コーディングエージェントとは?仕組みと構築方法

コーディングエージェントとは?仕組みと構築方法

コーディングエージェントとは、大規模言語モデルを推論の中核として使用し、自律的にコードの記述、実行、反復を行うAIシステムです。エディタで補完を提案するコードアシスタントとは異なり、コーディングエージェントは「観察→判断→実行」の完全なループを実行します。つまり、ファイルを読む、変更を書き込む、コマンドを実行する、出力を確認する、そしてタスクが完了するまで修正を繰り返します。

この記事では、そのループの仕組み(プランナー、LLM推論層、ツール、サンドボックス実行環境)を説明し、その後、NovitaのLLM APIとAgent Sandboxを使用して実際に組み立てる方法を紹介します。

コーディングエージェントを定義するもの

コードアシスタントとコーディングエージェントの違いは実行にあります。コードアシスタントは候補を生成して停止します。一方、AIコーディングエージェントはコードを生成し、実行し、結果を読み、目標を達成するか行き詰まるまで継続します。

その実行能力には、具体的に3つの要件があります。

  • ツールへのアクセス — ファイルの読み取り、ファイルの書き込み、シェルコマンドの実行機能
  • サンドボックス環境 — 問題が発生してもホストシステムに損害を与えない、コードを実行するための隔離された場所
  • 永続的なコンテキスト — ツールの出力がモデルのコンテキストにフィードバックされ、何が起こったかを推論できる

これら3つが揃わなければ、コードを書けるチャットボットにすぎません。3つすべてが揃って初めてエージェントとなります。

「コードエージェント」という用語は、IDEのインライン提案から、曖昧に指定されたタスクを受け取り、どのファイルが関係するかを把握し、変更を加え、各ステップで人間が介入することなく動作を確認できる完全自律型システムまで、幅広く使われています。開発者が「最高のコードエージェント」を比較する場合、通常は後者、つまり最小限のサポートでマルチステップのコーディングタスクを確実に完了するシステムを指します。

コーディングエージェントの4層構造

プロダクション品質のAIコーディングエージェントには、すべて認識可能な4つのコンポーネントがあります。実装の詳細(フレームワーク、LLM、サンドボックスプロバイダー)は異なりますが、アーキテクチャは一貫しています。

1. プランナー

プランナーはタスクの説明を受け取り、エージェントが実行するステップに分割します。単純なタスクでは、これはモデルの推論内部で暗黙的に行われます。複雑なタスク(「このサービスを新しい認証ライブラリを使用するように移行する」など)では、明示的な計画ステップにより、モデルが各ステップの後に状態を更新しながら処理する番号付きタスクリストが生成されます。

計画はいつ停止するかも決定します。完了基準のないエージェントは、際限なく改良を加え続けたり、さらに悪いことに、失敗したステップでループしたりします。ほとんどの実装では、システムプロンプトにタスク成功条件をエンコードし、モデルに完了を判断させます。

2. LLM推論層

LLMは、あらゆるAIコーディングエージェントの中核となる推論エンジンです。次にどのツールを呼び出すか、どの引数を渡すか、結果をどのように解釈するかを決定します。この決定は、構造化されたツールコール(関数名とパラメータを含むJSONオブジェクト)として表現され、フレームワークが実際の実行層にディスパッチします。

コードエージェントの場合、LLMには以下の能力が求められます。長いコンテキストを確実に処理する(ツールの結果が急速に蓄積される)、一貫して適切にフォーマットされたツールコールを返す(10ステップのワークフローの6ステップ目で構造が壊れたJSONは実行全体を破綻させる)、そして多くの連続したツールコールにわたって状態変化を推論する。

ここで推論プロバイダーが重要になります。OpenAI互換形式での関数呼び出しをサポートし、モデルレベルで有効なJSONを強制する構造化出力、そして並列サブタスクを生成するエージェントワークロードに十分な同時実行制限を持つAPIが必要です。Novita AIのLLM APIは、OpenAI互換のエンドポイントでこれら3つすべてをカバーしており、ツールコール解析ロジックを書き直すことなくモデルを交換できます。

3. ツール層

ツールはエージェントと世界とのインターフェースです。最小限のコーディングエージェントには4つのツールが必要です。

ツール 機能
read_file 指定されたパスのファイルの内容を返す
write_file ファイルパスに文字列を書き込む
run_command シェルコマンドを実行し、stdout + stderrを返す
list_directory パス上のファイルとディレクトリを一覧表示する

各ツールは完全な出力を返さなければなりません。結果が切り詰められたり、サイレントに失敗したりすると、エージェントのコードベースモデルが破損し、後で複合的なエラーを引き起こします。特に run_command ツールは、stdout と stderr の両方をキャプチャする必要があります。エージェントは成功出力よりもエラー出力から多くを学ぶことがよくあります。

エージェントによっては、コードベース全体のgrepスタイルの検索のために search_files ツールや、外部ドキュメントを読むための fetch_url ツールを追加するものもあります。適切なセットはタスクドメインによって異なります。純粋なコード作業の場合、上記の4つでほとんどのケースをカバーできます。

4. サンドボックス

サンドボックスは、エージェントのコマンドが実際に実行される、完全に孤立したLinux環境です。これは2つの理由で重要です。

第一に、セキュリティです。エージェントは、ユーザープロンプト、取得したドキュメント、推測されたパターンからコードを生成します。善意のエージェントでも、ファイルを削除したり、ネットワーク接続を開いたり、無制限にリソースを消費したりするコードを生成する可能性があります。サンドボックスは、いかなる損害も隔離された環境内に封じ込めます。

第二に、状態管理です。適切なサンドボックスは、セッション内のツールコール間でファイルシステムの状態を保持します。エージェントがステップ2でファイルを作成した場合、ステップ8でもそれが存在している必要があります。各コマンドが新しい環境で実行されるステートレスなコンテナアプローチは、実際のコーディングタスクには機能しません。

Novita Agent SandboxはFirecrackerマイクロVM上に構築されており、標準的なコンテナよりも強力なカーネルレベルの分離を提供します。セッションは最大24時間実行でき、ファイルシステムの状態はコマンド間で保持され、コールドスタートは200ms未満です。これは、サンドボックスの起動を待つことがインタラクティブなワークフローを中断しないほど十分な速さです。

実行ループの仕組み

具体的な例を使うと、ループを理解しやすくなります。タスクが「/login エンドポイントにレート制限を追加する」だとします。

  1. 計画 — モデルがタスクを読み、何が必要かを特定する:ログインルートを見つける、現在のハンドラを理解する、レート制限ミドルウェアを追加する、テスト実行で確認する。
  2. 観察 — エージェントが list_directory を呼び出してルートファイルを見つけ、次に read_file でログインハンドラを読み込む。ファイルの内容がモデルコンテキストに追加される。
  3. 判断 — モデルが現在のコードを推論し、何をするかを決定する:レート制限ライブラリをインストールする、ハンドラを修正する、テストを追加する。
  4. 実行 — エージェントが run_command("pip install slowapi") を呼び出し、次に変更したハンドラで write_file を実行し、さらに run_command("pytest tests/test_login.py") を実行する。
  5. 再観察 — テスト出力がコンテキストにフィードバックされる。テストが失敗した場合、モデルはトレースバックを読み、エラーを特定し、修正したファイルを書き込む。
  6. 完了 — テストが成功し、モデルに保留中のステップがなければ、最終的なサマリーを返す。

このループは単一セッション内で実行されます。コンテキストウィンドウはエージェントのワーキングメモリであり、読み取られたすべてのファイル、すべてのコマンド出力、すべてのツールコールがそこに蓄積されます。これが、コーディングエージェントにとってコンテキスト長が非常に重要である理由です。実際のリファクタリングタスクでは、ステップ15までに簡単に10万トークンを消費します。エージェントが1ターンチャットとは異なる形で推論プロバイダーに負荷をかける仕組みをご覧ください。

Novitaを使ったコーディングエージェントの構築

次の例は、NovitaのLLM APIとAgent Sandboxを連携させます。Novitaのエンドポイントを指すPython OpenAI SDKを使用します。NovitaのモデルはOpenAIのAPIと同じ関数呼び出しインターフェースを使用するため、統合にカスタム解析は必要ありません。

import os
import json
from openai import OpenAI
from novita_sandbox.code_interpreter import Sandbox

client = OpenAI(
    base_url="https://api.novita.ai/openai",
    api_key=os.environ["NOVITA_API_KEY"],
)

sandbox = Sandbox.create(timeout=1800)


def read_file(path: str) -> str:
    try:
        return sandbox.files.read(path)
    except Exception as e:
        return f"Error: {e}"


def write_file(path: str, content: str) -> str:
    try:
        sandbox.files.write(path, content)
        return f"Written to {path}"
    except Exception as e:
        return f"Error: {e}"


def run_command(cmd: str) -> str:
    try:
        result = sandbox.commands.run(cmd)
        return str(result)
    except Exception as e:
        return f"Error: {e}"


tools = [
    {
        "type": "function",
        "function": {
            "name": "read_file",
            "description": "Read the contents of a file",
            "parameters": {
                "type": "object",
                "properties": {"path": {"type": "string"}},
                "required": ["path"],
            },
        },
    },
    {
        "type": "function",
        "function": {
            "name": "write_file",
            "description": "Write content to a file",
            "parameters": {
                "type": "object",
                "properties": {
                    "path": {"type": "string"},
                    "content": {"type": "string"},
                },
                "required": ["path", "content"],
            },
        },
    },
    {
        "type": "function",
        "function": {
            "name": "run_command",
            "description": "Run a shell command in the sandbox and return output",
            "parameters": {
                "type": "object",
                "properties": {"cmd": {"type": "string"}},
                "required": ["cmd"],
            },
        },
    },
]

dispatch = {
    "read_file": read_file,
    "write_file": write_file,
    "run_command": run_command,
}


def run_agent(task: str, model: str) -> str:
    messages = [
        {
            "role": "system",
            "content": (
                "You are a coding agent with access to a Linux sandbox. "
                "Complete tasks by calling tools. When done, return a plain-text summary."
            ),
        },
        {"role": "user", "content": task},
    ]

    while True:
        response = client.chat.completions.create(
            model=model,
            messages=messages,
            tools=tools,
            tool_choice="auto",
        )
        msg = response.choices[0].message
        messages.append(msg)

        if not msg.tool_calls:
            return msg.content

        for call in msg.tool_calls:
            fn = dispatch[call.function.name]
            args = json.loads(call.function.arguments)
            result = fn(**args)
            messages.append(
                {
                    "role": "tool",
                    "tool_call_id": call.id,
                    "content": result,
                }
            )


# Replace <model-id> with a function-calling model from novita.ai/docs
result = run_agent(
    task="Write a Python script that counts words in a text file and run it on a sample input",
    model="<model-id>",
)
print(result)
sandbox.kill()

この実装についていくつか注意点があります。

  • while True ループは、モデルがツールコールなしのメッセージを返すまで実行されます。これがエージェントがタスク完了と見なすシグナルです。
  • ツールの結果は role: tool エントリとして messages に追加されます。これにより、ステップ間で共有コンテキストが構築されます。
  • sandbox.kill() はコンピューティングリソースを解放します。セッションが終了したら必ず呼び出してください。

サポートされている関数呼び出しモデルIDについては、Novita関数呼び出しドキュメントをご確認ください。Gradio UIを含むより完全なウォークスルーについては、Building a Coding Agent with Novita’s Agent Sandbox をご覧ください。

コードエージェントに適したLLMの選び方

HumanEvalやSWE-benchは、1ターンのコード生成を測定します。エージェントのワークロードは異なります。プロダクションのコードエージェントを実際に壊すのは、ツールコールのフォーマット失敗です。ベンチマークで良いスコアを獲得していても、複雑なマルチターンセッションで時折不正なJSONを返すモデルは、デバッグが困難な形で失敗します。

AIコーディングエージェントの実用的な評価基準は以下の通りです。

  • ツールコールの信頼性 — 20ステップ以上のセッションで、モデルがどれだけ一貫して適切にフォーマットされたツールコールを返すか?
  • コンテキスト保持 — モデルが40ステップ前に読んだファイルを正しく参照できるか?
  • 指示追従 — エージェントはタスクに留まるか、それとも無関係なファイルの修正を始めるか?
  • コードの正確性 — 生成されたコードは実際に実行されるか、それとも複数の修正ループが必要か?

代表的な実際のコーディングタスクを実行し、タスク完了率を測定することは、どの公開ベンチマークよりも有益です。自身のコードベースから20〜30のタスクを選び、候補モデルで実行し、人間の介入なしで完了する数を数えてください。

推論の価格設定は、エージェントスケールでは急速に影響します。1回のセッションで、全ターンにわたって20万〜50万トークンを消費する可能性があります。プロンプトキャッシングと競争力のあるトークン単価を提供するプロバイダーは、1日に数百のエージェントセッションを実行する場合、経済性を大きく変えます。

コスト効率の良い選択肢としてのオープンソースモデル

クローズドソースのフロンティアモデルはコーディングベンチマークでリードしてきましたが、トップのオープンソースモデルとの差は大幅に縮まっています。DeepSeek V3Qwen3 などのモデルは、現在、コード生成とツール使用タスクで競争力のあるパフォーマンスを発揮しており、OpenAI互換のAPIを通じて提供されているため、切り替えは model パラメータの1行の変更で済みます。

どちらもNovitaのLLM APIを通じて利用可能です。同じエンドポイント、同じ関数呼び出しインターフェース、同じAgent Sandbox統合を、GPUインフラを自分で管理することなく利用できます。GPUオーケストレーション、バッチ処理、信頼性エンジニアリングは簡単ではないため、これらをマネージドAPIに委任することで、エージェントロジックに集中できます。

これがコーディングエージェントにとって特に重要な理由は、セッションあたりのトークンコストがライセンス料よりもエージェントワークロードの経済性を左右するからです。1日あたり200のコーディングエージェントセッションを実行しているチームが、オープンソースモデルで同等のタスク完了率を達成できれば、統合コードを一切変更することなく、推論コストを大幅に削減できます。

実用的なテスト:ターゲットモデルで50の代表的なコーディングタスクを実行し、ツールコール成功率とタスク完了率を測定し、セッションあたりのコストと比較します。ベンチマーク数値はこの質問に答えてくれません。実際のワークロードで判断する必要があります。

FAQ

コーディングエージェントとコードアシスタントの違いは何ですか?

コードアシスタント(GitHub Copilotのインライン提案など)は補完を生成して停止します。コーディングエージェントはコードを実行し、出力を読み、反復します。定義的な特徴は実行ループです:読み取り、判断、実行、観察、繰り返し。異なるエージェントフォームファクターがこのループをどのように使用するかの比較については、CLI vs IDE Coding Agent をご覧ください。

コーディングエージェントを構築するにはサンドボックスが必要ですか?

はい、エージェントがユーザー入力や外部ソースから生成されたコードを実行する場合は必要です。分離がなければ、バグのあるコード生成がホストのファイルシステムを破損したり、無制限にリソースを消費したりする可能性があります。内部使用のみの場合でも、サンドボックスは暴走プロセスがホストに影響を与えるのを防ぎます。コンテナは基本的な分離を提供します。NovitaのようなマイクロVMベースのサンドボックスは、マルチテナントやセキュリティ重視のワークロードに対して、より強力なカーネルレベルの分離を提供します。

コーディングエージェントはインターネットアクセスなしで動作できますか?

ほとんどの純粋なコーディングタスクでは、はい。ファイルの読み取り/書き込みとローカルコマンドの実行で、ワークフローの大部分をカバーできます。実際、サンドボックス内の外部への通信を制限することは良いデフォルト設定です。生成されたコードが予期しない外部リクエストを行うことを防ぎ、脅威モデルを単純化します。

特定のタスクに最適なコードエージェントは何によって決まりますか?

実際のワークロードにおけるツールコールの信頼性とタスク完了率です。公開ベンチマークランキングはモデルを選別するための出発点であり、最終的な答えではありません。代表的なタスクを実行し、完了率を測定し、セッションあたりのトークンコストを考慮してください。軽量なリファクタリングを行う小規模スタートアップにとって最適なコードエージェントは、大規模な自動PRレビューを実行するエンタープライズチームにとって最適な選択肢とは大きく異なる場合があります。

コーディングエージェントのセッションはどのくらいの期間実行できますか?

それはサンドボックスプロバイダーによります。Novita Agent Sandboxは、コマンド間でファイルシステムの状態を保持しながら最大24時間のセッションをサポートしており、エージェントコードにチェックポイント/リストアロジックを必要とせずに、長時間のリファクタリングや移行タスクにも対応できます。


おすすめ記事