How to Build an AI Agent
How to Build an AI Agent
s01

エージェントループ

ツールと実行

Bash is All You Need

84 LOC1 ツールSingle-tool agent loop
The minimal agent kernel is a while loop + one tool

[ s01 ] s02 > s03 > s04 > s05 > s06 | s07 > s08 > s09 > s10 > s11 > s12

"One loop & Bash is all you need" -- 1つのツール + 1つのループ = エージェント。

問題

言語モデルはコードについて推論できるが、現実世界に触れられない。ファイルを読めず、テストを実行できず、エラーを確認できない。ループがなければ、ツール呼び出しのたびにユーザーが手動で結果をコピーペーストする必要がある。つまりユーザー自身がループになる。

解決策

+--------+      +-------+      +---------+
|  User  | ---> |  LLM  | ---> |  Tool   |
| prompt |      |       |      | execute |
+--------+      +---+---+      +----+----+
                    ^                |
                    |   tool_result  |
                    +----------------+
                    (loop until stop_reason != "tool_use")

1つの終了条件がフロー全体を制御する。モデルがツール呼び出しを止めるまでループが回り続ける。

仕組み

  1. ユーザーのプロンプトが最初のメッセージになる。
messages.append({"role": "user", "content": query})
  1. メッセージとツール定義をLLMに送信する。
response = client.messages.create(
    model=MODEL, system=SYSTEM, messages=messages,
    tools=TOOLS, max_tokens=8000,
)
  1. アシスタントのレスポンスを追加し、stop_reasonを確認する。ツールが呼ばれなければ終了。
messages.append({"role": "assistant", "content": response.content})
if response.stop_reason != "tool_use":
    return
  1. 各ツール呼び出しを実行し、結果を収集してuserメッセージとして追加。ステップ2に戻る。
results = []
for block in response.content:
    if block.type == "tool_use":
        output = run_bash(block.input["command"])
        results.append({
            "type": "tool_result",
            "tool_use_id": block.id,
            "content": output,
        })
messages.append({"role": "user", "content": results})

1つの関数にまとめると:

def agent_loop(query):
    messages = [{"role": "user", "content": query}]
    while True:
        response = client.messages.create(
            model=MODEL, system=SYSTEM, messages=messages,
            tools=TOOLS, max_tokens=8000,
        )
        messages.append({"role": "assistant", "content": response.content})

        if response.stop_reason != "tool_use":
            return

        results = []
        for block in response.content:
            if block.type == "tool_use":
                output = run_bash(block.input["command"])
                results.append({
                    "type": "tool_result",
                    "tool_use_id": block.id,
                    "content": output,
                })
        messages.append({"role": "user", "content": results})

これでエージェント全体が30行未満に収まる。本コースの残りはすべてこのループの上に積み重なる -- ループ自体は変わらない。

変更点

ComponentBeforeAfter
Agent loop(none)while True + stop_reason
Tools(none)bash (one tool)
Messages(none)Accumulating list
Control flow(none)stop_reason != "tool_use"

試してみる

python agents/s01_agent_loop.py
  1. Create a file called hello.py that prints "Hello, World!"
  2. List all Python files in this directory
  3. What is the current git branch?
  4. Create a directory called test_output and write 3 files in it