0
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

自然言語からシェルコマンドを生成し「ローカルで安全検査してから実行」するCLIを作った

0
Posted at
  • 自然言語の依頼を Google Gemini / OpenAI に渡してシェルコマンドを1つ生成する CLI ツールです。
  • AI の「安全そう」という判断は信用せず、AI とは独立した決定論的なローカルポリシーで危険度(low / medium / high / blocked)を再判定します。
  • 既定では 確認プロンプトを経て実行します。--dry-run を付ければ検査だけで止められます。
  • blocked(sudo、リモートスクリプト実行、ディスク消去、ルート削除など)は実行できません。
  • リポジトリ: https://github.com/softjapan/ai-shell-agent

⚠️ 重要: このツールの安全性検査は補助的なガードレールであり、OS サンドボックスではありません。表示されたコマンド・作業ディレクトリ・危険度を必ず確認してください。


こんな課題を解きたい

「あのコマンドのオプション、なんだっけ」を毎回ぐぐるのは地味に時間を食います。かといって、AI が出したコマンドをそのままシェルに貼って実行するのは怖い。特に rm や権限変更のような取り返しのつかない操作は事故のもとです。

そこで、次の2つを両立させることを目標にしました。

  1. 自然言語からコマンドを手軽に生成する
  2. 生成物をそのまま鵜呑みにせず、実行前にローカルで危険度を判定して歯止めをかける

ポイントは「AI に安全性を判断させない」ことです。LLM に「これは安全ですか?」と聞くのではなく、生成されたコマンド文字列を、AI とは無関係な決定論的ルールで検査します。


使い方

インストール

uv を使うのが簡単です。

git clone https://github.com/softjapan/ai-shell-agent.git
cd ai-shell-agent
uv sync --locked

利用するプロバイダーの API キーを設定します(両方設定する必要はありません)。

# Google(既定プロバイダー)
export GEMINI_API_KEY="your-gemini-api-key"

# OpenAI
export OPENAI_API_KEY="your-openai-api-key"

まずは検査だけ(--dry-run)

いきなり実行せず、生成と危険度判定だけを見たいときは --dry-run を付けます。

uv run ai-shell-agent --dry-run "現在のディレクトリにあるPythonファイルを表示"

既定はシンプルな出力です。

[AI Answer]: find . -maxdepth 1 -name '*.py' -print
Dry run only. Re-run without --dry-run to allow confirmation and execution.

--verbose を付けると、プロバイダー・モデル・危険度・作業ディレクトリ・シェルなどの詳細も表示されます。

Command   find . -maxdepth 1 -name '*.py' -print
Details   現在のディレクトリ直下にあるPythonファイルを表示します。
Provider  openai  (gpt-4o-mini)
Risk      low
          ↳ matches a recognized read-only command
Workdir   /path/to/project
Shell     /bin/zsh
Dry run only. Re-run without --dry-run to allow confirmation and execution.

実行(既定)

--dry-run を外すと、生成・検査のあと確認プロンプトに進みます。

uv run ai-shell-agent "現在のディレクトリを表示"
[AI Answer]: ls
Execute? Y/N: y
(ls の結果)

high(削除・権限変更・プロセス終了など)のときは、実行前に警告と理由が表示されます。

[AI Answer]: rm old.txt
⚠ High-risk command: it may delete data, change permissions, or otherwise be hard to undo.
  - deletes files or directories
Execute? Y/N:

blocked は同意しても実行できません。


設計:AI 生成と安全判定を分離する

このツールの肝は、AI によるコマンド生成とローカルの安全判定を完全に分けていることです。

ユーザー入力 + provider
    ↓
ai_shell_agent.generator   Google/OpenAIによるCommandPlan生成
    ↓
ai_shell_agent.models      Pydanticによる構造検証
    ↓
ai_shell_agent.policy      プロバイダー非依存のローカル危険度判定
    ↓
ai_shell_agent.cli         dry-run・危険度別の対話確認
    ↓
ai_shell_agent.executor    subprocess・timeout・終了コード

生成とプロセス実行が分離されているので、テストでは両方の境界をモックでき、外部 API や実コマンドを一切呼ばずに検証できます。

1. 構造化出力で受け取る

LLM の出力は Pydantic モデル CommandPlan として受け取り、「成功なら単一行コマンドのみ」「失敗なら理由のみ」といった整合性を検証します。改行や NUL 文字を含むコマンドは弾きます。プロンプトの段階でも、Markdown フェンスを付けない・単一行にする・むやみに sudo を付けないといった方針を明示しています。

2. AI とは独立したローカルポリシー

生成されたコマンド文字列を、決定論的なルールで4段階に分類します。

危険度 例 動作
low pwd, ls, git status Y/N確認後に実行可能
medium ファイル作成、ネットワーク、未知のコマンド Y/N確認後に実行可能
high ファイル削除、権限変更、プロセス終了 警告と理由を表示し、Y/N確認後に実行可能
blocked sudo、リモートスクリプト実行、ディスク消去、ルート削除 実行不可

blocked の代表例は次のようなものです。

  • curl ... | sh のようなリモートスクリプト実行
  • sudo による権限昇格
  • mkfs / diskutil erase / dd of=/dev/... のようなディスク破壊
  • rm -rf / のようなルートからの広域削除
  • fork bomb

文字列解析ベースなので誤検出・見逃しの可能性はあります。ドキュメントでもその旨を明記し、機密環境ではコンテナや専用ユーザー、OS サンドボックスなど追加の隔離を推奨しています。あくまで「補助的なガードレール」という位置づけです。

3. 制御されたプロセス実行

実行は os.system ではなく subprocess を使い、次を制御します。

  • 実行に使うシェルを検証(sh, bash, zsh, dash, ksh のみ許可)
  • 作業ディレクトリの存在確認
  • タイムアウト(既定30秒)
  • 終了コードの伝播

プロバイダーの切り替え

Google Gemini(既定)と OpenAI を --provider で切り替えられます。

# Google(既定)
uv run ai-shell-agent "現在のディレクトリを表示"

# OpenAI
uv run ai-shell-agent --provider openai "現在のディレクトリを表示"

# モデル指定も可能
uv run ai-shell-agent --provider openai --model gpt-4o "Gitの変更内容を表示"

既定モデルは Google が gemini-2.5-flash、OpenAI が gpt-4o-mini です。

毎回 --provider を付けたくない場合は、環境変数で既定を変更できます。CLI の --provider は常に優先されます。

export AI_SHELL_AGENT_PROVIDER="openai"
uv run ai-shell-agent "現在のディレクトリを表示"                 # OpenAI を使用
uv run ai-shell-agent --provider google "現在のディレクトリを表示"  # 明示指定が優先

優先順位は次のとおりです。

  1. CLI の --provider(最優先)
  2. 環境変数 AI_SHELL_AGENT_PROVIDER
  3. 既定(google)

API キーの扱い:環境変数を最優先

API キーは環境変数が最優先です。任意で .env を使う場合も、環境変数に未設定のキーだけを補完し、既存の環境変数を上書きしません。秘密情報の管理はシェル・CI・シークレットマネージャに委ね、ツールは生成・検査・実行に専念する設計です。

cp .env.example .env
# .env に使うプロバイダーのキーを設定
  • .env の場所は --env-file PATH で変更可能
  • --env-file /dev/null で .env 読み込みを無効化
  • .env は Git 管理から除外(.env.example のみコミット対象)

.env の自動読み込みには専用ライブラリを追加せず、依存を増やさない小さなパーサで実装しています(export KEY=value 形式やクォート、行末コメントに対応)。


終了コード

スクリプトや CI から扱いやすいように、終了コードを整理しています。

コード 意味
0 dry-run完了、キャンセル、またはコマンド成功
2 引数、APIキー、生成処理のエラー
3 ローカル安全ポリシーによるブロック
4 コマンド開始前の実行エラー
124 コマンドタイムアウト
130 Ctrl+Cによる中断
その他 実行したコマンドの終了コード

テストと CI

外部 API と実コマンドの境界をモックしているため、テストは決定論的です。API キーを消費せず、実際に破壊的コマンドを走らせることもありません。

  • 危険コマンドのブロック、高リスク判定、読み取り専用コマンドの許可
  • dry-run で実行されないこと、確認プロンプトの挙動
  • プロバイダー選択・既定解決・API エラーの秘匿処理
  • .env 補完と環境変数優先の優先順位
  • 終了コードの伝播、タイムアウト

CI(GitHub Actions)では、Python 3.10〜3.13 で以下を回しています。

uv run black --check .
uv run ruff check .
uv run pyright
uv run pytest
uv build

プロジェクト構成

ai-shell-agent/
├── ai_shell_agent/
│   ├── __init__.py     # バージョン情報
│   ├── cli.py          # provider選択、dry-run、確認、終了コード
│   ├── config.py       # 環境変数優先の任意.env補完
│   ├── executor.py     # 制御されたサブプロセス実行
│   ├── formatting.py   # 依存なしのANSIカラー補助
│   ├── generator.py    # Google/OpenAI連携とAPIエラー処理
│   ├── models.py       # 構造化出力とドメイン型
│   └── policy.py       # ローカル安全ポリシー
├── tests/              # API・プロセスをモックした単体テスト
├── .github/workflows/  # CI
├── .env.example        # キー名のみのテンプレート
├── as.py               # 後方互換ラッパー
├── pyproject.toml
├── requirements.txt
├── uv.lock
└── LICENSE

設計上のこだわりどころ

  • AI に安全性を判断させない: 生成と検査を分離し、検査は決定論的ルールで行う。
  • 既定は実行だが、必ず確認を挟む: 手軽さと安全のバランス。破壊的操作は警告、危険すぎるものはブロック。
  • 秘密情報を漏らさない: API エラー本文や作業ディレクトリのフルパスを AI へのプロンプトに含めない。環境変数を最優先し、.env は上書きしない。
  • 依存は最小限・固定: 使うプロバイダーの依存だけに絞り、バージョンをピン留め。
  • テスト可能な境界: 生成もプロセス実行もモックできる構造。

まとめ

「AI が出したコマンドを、AI に安全確認させずローカルで再検査してから実行する」というシンプルな方針で、自然言語シェルコマンド生成の実用性と安全性を両立させました。

とはいえ、繰り返しになりますが、これはサンドボックスではなく補助的なガードレールです。最終的な実行判断は利用者の責任で行ってください。

興味があればリポジトリをのぞいてみてください。フィードバックや Issue も歓迎です。


参考

0
0
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
0
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?