grasys blog

AIエージェント用開発環境を作ってみた

*geminiに文体を整えてもらっています。個性が消えた感じがしますが、読みやすくなった気がする。。。

はじめに

AIエージェントを実行する際の環境構築について、皆さんはどのように工夫されていますか?

Codex CLIやClaude Codeなど、各エージェントの実行環境(エージェントハーネス)側でもセキュリティ対策は行われているものの、ローカル環境でそのまま動かすのはまだ少し不安が残ります。 かといって、コマンドを実行するたびにいちいち許可を求められるのは手間ですよね。「自由にやって! あとでまとめて確認するから!」というスタイルで進めたいのが本音です。

そこで、AIエージェント自体をコンテナに閉じ込めて運用することにしました。 今回は、私が実践してみて良かった「AIエージェント用開発環境のTips」を共有したいと思います。

エージェント環境のリポジトリ構造

私は普段の業務でもDev ContainersとVS Codeを使用しているため、Dev Container内にハーネスをインストールし、その中ではエージェントにフルコントロールを与えています。 プロンプトインジェクションや意図しない破壊的なコマンドが実行されるリスクはありますが、影響範囲をコンテナ内部に留められるため、ローカルで実行するより大幅に安全性が高まります

さらに、Dev Containerの設定(ハーネス環境)とプロジェクト本体のGitリポジトリを分けて管理するのがおすすめです(通常、Dev Containerの設定はプロジェクト直下に配置しますが、あえて分離します)。 これにより、エージェントのカスタマイズ内容を他のプロジェクトへ横展開しやすいという大きなメリットが得られます

現在は様々なエージェントツールが鎬を削っていますが、それぞれのGitリポジトリを用意しておけば、用途や気分に合わせて環境を切り替えることも簡単です。 (CLIでの利用になるため、デスクトップアプリ版のリッチな機能が使えなくなるのは少し悲しいところですが……)

実際のディレクトリ構成は以下のようになっています

project_root
├── agent_harness (git repo)
│   ├── .agents
│   │   └── skills
│   ├── AGENTS.md
│   └── docs
│       └── AGENTS.md
└── project (git repo)
    ├── .agents
    │   └── skills
    ├── AGENTS.md
    ├── docs
    │   └── AGENTS.md
    └── src

プロジェクトからハーネスへ知識を蒸留

ハーネス側の AGENTS.md.agents ディレクトリ(.codex.claude など)は、コンテナ内ユーザーのホームディレクトリにマウントして使用します。これにより、コンテナ全体に共通の設定を適用できます

一方で、プロジェクト側にも .agents ディレクトリを置いておくことで、両方の設定が読み込まれます。 これにより、「プロジェクト固有の知識はプロジェクト側のリポジトリに」「汎用的な知識はハーネス側のリポジトリに」といった使い分けが可能になります。

ハーネスの設定読み込み順はdir > project > home となっているため、この構造は理にかなっていると考えています。

運用フローとしては以下のようになります

  1. エージェントの実行はプロジェクト配下で行い、プロジェクト固有のSkillや AGENTS.md を育てていく。
  2. その中で汎用的に使える便利な知識が見つかったら、ハーネス側に移設する。

このように、プロジェクトを跨いだ知識の蒸留ができる点が利点です。 チームメンバーにこのワークフローを共有すれば、ハーネスに知識が集約され、より効率的な運用ができるはずです。

gemini3.1proくんがnanobanana2で記事から生成してくれた画像。文字の破綻がないのすごい。

セキュリティ上の注意点と今後の課題

注意点として、コンテナ内の情報はすべてエージェントが触れられる状態にあるため、環境変数などにAPIキーを直接読み込ませてしまうと、情報漏洩のリスクがあります

解決策としては、エージェントにはダミーの認証情報を渡しておき、APIリクエストをプロキシ経由にして、そこで実際の認証情報に置換するなどのアプローチがある様です。OneCLIというOSSが有望そうなので、試してみようと思っています!

おまけ

以下はdevcontainer.jsonになります。ポイントはinitializeCommandでCOMPOSE_PROJECT_NAMEとしてproject_rootの名前を.envに書き出しているところです。これがないと、vscodeが同じDev Containerとして認識してしまうので、設定ファイルを固定できませんでした。

{
  "name": "opencode",
  "service": "agent_harness",
  "dockerComposeFile": [
    "./compose.yaml"
  ],
  "workspaceFolder": "/home/vscode/workspace",
  "features": {
    "ghcr.io/devcontainers/features/node:1": {},
    "ghcr.io/devcontainers/features/python:1": {},
    "ghcr.io/devcontainers-extra/features/pre-commit:2": {},
    "ghcr.io/devcontainers-extra/features/terraform-docs:1": {},
    "ghcr.io/dhoeric/features/hadolint:1": {},
    "ghcr.io/devcontainers/features/docker-outside-of-docker:1": {
      "moby": false
    },
    "ghcr.io/devcontainers/features/github-cli:1": {},
    "ghcr.io/devcontainers/features/aws-cli:1": {},
    "ghcr.io/joshuanianji/devcontainer-features/gcloud-cli-persistence:1": {}
  },
  "customizations": {
    "vscode": {
      "extensions": [
        "exiasr.hadolint",
        "hashicorp.terraform",
        "mosapride.zenkaku",
        "ms-azuretools.vscode-docker",
        "redhat.vscode-yaml",
        "shardulm94.trailing-spaces",
        "sst-dev.opencode-v2",
        "sst-dev.opencode",
        "bierner.markdown-mermaid"
      ]
    }
  },
  "remoteEnv": {
    "PATH": "${containerEnv:PATH}:/home/vscode/.bun/bin",
    "COMPOSE_PROJECT_NAME": "${localWorkspaceFolderBasename}"
  },
  "containerEnv": {
    "TENV_AUTO_INSTALL": "True",
    "VERTEX_LOCATION": "global",
    "GOOGLE_CLOUD_PROJECT": "PROJECT_NAME",
    "GOOGLE_CLOUD_LOCATION": "global"
  },
  "initializeCommand": "touch .devcontainer/.env && grep -v '^COMPOSE_PROJECT_NAME=' .devcontainer/.env > .devcontainer/.env.tmp && echo COMPOSE_PROJECT_NAME=$(basename $(dirname \"${localWorkspaceFolder}\")) >> .devcontainer/.env.tmp && mv .devcontainer/.env.tmp .devcontainer/.env",
  "postCreateCommand": "agent_harness/.devcontainer/postCreateCommand.sh",
  "postStartCommand": "agent_harness/.devcontainer/postStartCommand.sh",
  "remoteUser": "vscode"
}

以下はcompose.yamlです。opencodeで使用しているものですが、設定ファイルの置き場所が独特でした。~/.localのshare・stateにopencodeの状態が記録されるので、これもマウントしておくと状態を永続化できます。

services:
 agent_harness:
    build:
      context: .
      dockerfile: ./Dockerfile
    tty: true
    volumes:
      - type: bind
        source: ../../src
        target: $DEVCONTAINER_HOME/workspace/project
      - type: bind
        source: ../
        target: $DEVCONTAINER_HOME/workspace/agent_harness
      - type: bind
        source: ../.opencode/config
        target: $DEVCONTAINER_HOME/.config/opencode
      - type: bind
        source: ../.opencode/local/share
        target: $DEVCONTAINER_HOME/.local/share/opencode
      - type: bind
        source: ../.opencode/local/state
        target: $DEVCONTAINER_HOME/.local/state/opencode
    restart: unless-stopped

採用情報
お問い合わせ