从0到1构建像Claude Code那样的Agent(二):工具

文章目录

1.Debug位置

s02_tool_use.py#120 #132

python 复制代码
def agent_loop(messages: list):
    while True:
        response = client.messages.create(
            model=MODEL, system=SYSTEM, messages=messages,
            tools=TOOLS, max_tokens=8000,
        )
        print("assistant response:", response.to_json())
        messages.append({"role": "assistant", "content": [b.to_dict() for b in response.content]})
        if response.stop_reason != "tool_use":
            return
        results = []
        for block in response.content:
            if block.type == "tool_use":
                handler = TOOL_HANDLERS.get(block.name)
                output = handler(**block.input) if handler else f"Unknown tool: {block.name}"
                print(f"> {block.name}: {output[:200]}")
                results.append({"type": "tool_result", "tool_use_id": block.id, "content": output})
        messages.append({"role": "user", "content": results})
        print("messages:", json.dumps(messages, indent=2, ensure_ascii=False))

2.工具

One Handler Per Tool 每个工具一个处理器

The loop stays the same; new tools register into the dispatch map

循环保持不变;新工具注册到调度映射

"加一个工具, 只加一个 handler" -- 循环不用动, 新工具注册进 dispatch map 就行。

Harness 层: 工具分发 -- 扩展模型能触达的边界。

3.问题

只有 bash 时, 所有操作都走 shell。cat 截断不可预测, sed 遇到特殊字符就崩, 每次 bash

调用都是不受约束的安全面。专用工具 (read_file, write_file) 可以在工具层面做路径沙箱。

关键洞察: 加工具不需要改循环。

4.设计决策

  1. 为什么恰好四个工具?

四个工具分别是 bash、read_file、write_file 和 edit_file,覆盖了大约 95% 的编程任务。Bash 处理执行和任意命令;read_file

提供带行号的精确文件读取;write_file 创建或覆盖文件;edit_file

做精确的字符串替换。工具越多,模型的认知负担越重------它必须在更多选项中做选择,选错的概率也随之增加。更少的工具也意味着更少的

schema 需要维护、更少的边界情况需要处理。

我们可以添加专门的工具(list_directory、search_files、http_request),后续版本也确实添加了。但在这个阶段,bash 已经涵盖了这些用例。从

v0 的单个工具到 v1 的四个工具的分离,特别是为了给模型提供结构化的文件操作 I/O,而 bash 的引号和转义常常让模型出错。

  1. 模型本身就是代理

核心 agent 循环极其简单:不断调用 LLM,如果返回 tool_use

块就执行并回传结果,如果只返回文本就停止。没有路由器,没有决策树,没有工作流引擎。模型自己决定做什么、何时停止、如何从错误中恢复。代码只是连接模型和工具的管道。这是一种设计哲学:agent

行为从模型中涌现,而非由框架定义。

许多代理框架添加了复杂的编排层:带有明确的思想/行动/观察解析的 ReAct 循环、LangChain 风格的链、AutoGPT

风格的目标分解。这些框架假设模型需要脚手架才能表现得像代理。我们的方法假设模型已经知道如何成为代理------它只需要工具来作用于世界。

  1. 每个工具都有 JSON Schema

每个工具都为输入参数定义了严格的 JSON schema。例如,edit_file 要求 old_string 和 new_string

是精确的字符串,而非正则表达式。这消除了一整类错误:模型无法传递格式错误的输入,因为 API 会在执行前校验

schema。这也使模型的意图变得明确------当它用特定字符串调用 edit_file 时,不存在关于它想修改什么的解析歧义。

有些代理系统允许模型输出自由形式的文本,这些文本通过正则表达式或启发式方法进行解析(例如,从 markdown

代码块中提取代码)。这是脆弱的------模型可能会稍微改变输出格式并破坏解析器。JSON 模式用灵活性换取了可靠性。

5.解决方案

markdown 复制代码
    +----------+      +-------+      +------------------+
    |   User   | ---> |  LLM  | ---> | Tool Dispatch    |
    |  prompt  |      |       |      | {                |
    +----------+      +---+---+      |   bash: run_bash |
                          ^          |   read: run_read |
                          |          |   write: run_wr  |
                          +----------+   edit: run_edit |
                          tool_result| }                |
                                     +------------------+

The dispatch map is a dict: {tool_name: handler_function}.
One lookup replaces any if/elif chain.

6.工作原理

  1. 每个工具有一个处理函数。路径沙箱防止逃逸工作区。
python 复制代码
def safe_path(p: str) -> Path:
    path = (WORKDIR / p).resolve()
    if not path.is_relative_to(WORKDIR):
        raise ValueError(f"Path escapes workspace: {p}")
    return path


def run_read(path: str, limit: int = None) -> str:
    try:
        text = safe_path(path).read_text()
        lines = text.splitlines()
        if limit and limit < len(lines):
            lines = lines[:limit] + [f"... ({len(lines) - limit} more lines)"]
        return "\n".join(lines)[:50000]
    except Exception as e:
        return f"Error: {e}"
  1. dispatch map 将工具名映射到处理函数。
python 复制代码
TOOL_HANDLERS = {
    "bash": lambda **kw: run_bash(kw["command"]),
    "read_file": lambda **kw: run_read(kw["path"], kw.get("limit")),
    "write_file": lambda **kw: run_write(kw["path"], kw["content"]),
    "edit_file": lambda **kw: run_edit(kw["path"], kw["old_text"], kw["new_text"]),
}
  1. 循环中按名称查找处理函数。循环体本身与 s01 完全一致。
python 复制代码
for block in response.content:
    if block.type == "tool_use":
        handler = TOOL_HANDLERS.get(block.name)
        output = handler(**block.input) if handler else f"Unknown tool: {block.name}"
        print(f"> {block.name}: {output[:200]}")
        results.append({"type": "tool_result", "tool_use_id": block.id, "content": output})

加工具 = 加 handler + 加 schema。循环永远不变。

7.相对 s01 的变更

组件 之前 (s01) 之后 (s02)
Tools 工具 1 (仅 bash) 4 (bash, read, write, edit)
Dispatch 硬编码 bash 调用 TOOL_HANDLERS 字典
路径安全 safe_path()沙箱
Agent loop代理循环 不变 不变

8.试一试

bash 复制代码
cd learn-claude-code
python agents/s02_tool_use.py

试试这些 prompt (英文 prompt 对 LLM 效果更好, 也可以用中文):

  • Add a function called greet(name) to hello.py
  • Read the file requirements.txt
  • Create a file called greet.py with a greet(name) function
  • Edit greet.py to add a docstring to the function
  • Read greet.py to verify the edit worked

9.示例

9.1 Add a function called greet(name) to hello.py

hello.py 中添加一个名为 greet(name)的函数

  1. assistant request:
    用户请求代码修改
python 复制代码
from agents.s02_tool_use import TOOLS

request = {
    "max_tokens": 8000,
    "messages": [
        {
            "role": "user",
            "content": "Add a function called greet(name) to hello.py"
        }
    ],
    "model": "aws-claude-sonnet-4-6",
    "system": "You are a coding agent at /home/xiangli/SourceCode/learn-claude-code. Use tools to solve tasks. Act, don't explain.",
    "tools": TOOLS
}
  1. assistant response:
    模型选择了 read_file 而不是 bash cat
json 复制代码
{
  "id": "msg_bdrk_017U4mxo8ja6xitbbZvMYyC8",
  "content": [
    {
      "text": "Let me first check if `hello.py` exists and review its current contents.",
      "type": "text"
    },
    {
      "id": "toolu_bdrk_01AQKsfx69xNnaeSBdbbG7uv",
      "input": {
        "path": "/home/xiangli/SourceCode/learn-claude-code/hello.py"
      },
      "name": "read_file",
      "type": "tool_use"
    }
  ],
  "model": "claude-sonnet-4-6",
  "role": "assistant",
  "stop_reason": "tool_use",
  "type": "message",
  "usage": {
    "cache_creation": {
      "ephemeral_1h_input_tokens": 0,
      "ephemeral_5m_input_tokens": 0
    },
    "cache_creation_input_tokens": 0,
    "cache_read_input_tokens": 0,
    "input_tokens": 816,
    "output_tokens": 90
  }
}

read_file: print("Hello, World!")

  1. messages:
    专用的读取工具提供更干净的文件访问
json 复制代码
[
  {
    "role": "user",
    "content": "Add a function called greet(name) to hello.py"
  },
  {
    "role": "assistant",
    "content": [
      {
        "text": "Let me first check if `hello.py` exists and review its current contents.",
        "type": "text"
      },
      {
        "id": "toolu_bdrk_01AQKsfx69xNnaeSBdbbG7uv",
        "input": {
          "path": "/home/xiangli/SourceCode/learn-claude-code/hello.py"
        },
        "name": "read_file",
        "type": "tool_use"
      }
    ]
  },
  {
    "role": "user",
    "content": [
      {
        "type": "tool_result",
        "tool_use_id": "toolu_bdrk_01AQKsfx69xNnaeSBdbbG7uv",
        "content": "print(\"Hello, World!\")"
      }
    ]
  }
]
  1. assistant request:
    文件内容返回给模型
python 复制代码
from agents.s02_tool_use import TOOLS

request = {
    "max_tokens": 8000,
    "messages": [
        {
            "role": "user",
            "content": "Add a function called greet(name) to hello.py"
        },
        {
            "role": "assistant",
            "content": [
                {
                    "text": "Let me first check if `hello.py` exists and review its current contents.",
                    "type": "text"
                },
                {
                    "id": "toolu_bdrk_01AQKsfx69xNnaeSBdbbG7uv",
                    "input": {
                        "path": "/home/xiangli/SourceCode/learn-claude-code/hello.py"
                    },
                    "name": "read_file",
                    "type": "tool_use"
                }
            ]
        },
        {
            "role": "user",
            "content": [
                {
                    "type": "tool_result",
                    "tool_use_id": "toolu_bdrk_01AQKsfx69xNnaeSBdbbG7uv",
                    "content": "print(\"Hello, World!\")"
                }
            ]
        }
    ],
    "model": "aws-claude-sonnet-4-6",
    "system": "You are a coding agent at /home/xiangli/SourceCode/learn-claude-code. Use tools to solve tasks. Act, don't explain.",
    "tools": TOOLS
}
  1. assistant response:
    write_file 替换整个文件内容
json 复制代码
{
  "id": "msg_bdrk_01UFq2BGC9buDtuSAzy9wEU9",
  "content": [
    {
      "text": "Now I'll add the `greet(name)` function to the file.",
      "type": "text"
    },
    {
      "id": "toolu_bdrk_01VJcPaD7M7nRsahJFStqss2",
      "input": {
        "path": "/home/xiangli/SourceCode/learn-claude-code/hello.py",
        "old_text": "print(\"Hello, World!\")",
        "new_text": "print(\"Hello, World!\")\n\n\ndef greet(name):\n    \"\"\"Greet a person by name.\"\"\"\n    print(f\"Hello, {name}!\")\n"
      },
      "name": "edit_file",
      "type": "tool_use"
    }
  ],
  "model": "claude-sonnet-4-6",
  "role": "assistant",
  "stop_reason": "tool_use",
  "type": "message",
  "usage": {
    "cache_creation": {
      "ephemeral_1h_input_tokens": 0,
      "ephemeral_5m_input_tokens": 0
    },
    "cache_creation_input_tokens": 0,
    "cache_read_input_tokens": 0,
    "input_tokens": 924,
    "output_tokens": 169
  }
}

edit_file: Edited /home/xiangli/SourceCode/learn-claude-code/hello.py

  1. messages:
    Write confirmed 写入确认
json 复制代码
[
  {
    "role": "user",
    "content": "Add a function called greet(name) to hello.py"
  },
  {
    "role": "assistant",
    "content": [
      {
        "text": "Let me first check if `hello.py` exists and review its current contents.",
        "type": "text"
      },
      {
        "id": "toolu_bdrk_01AQKsfx69xNnaeSBdbbG7uv",
        "input": {
          "path": "/home/xiangli/SourceCode/learn-claude-code/hello.py"
        },
        "name": "read_file",
        "type": "tool_use"
      }
    ]
  },
  {
    "role": "user",
    "content": [
      {
        "type": "tool_result",
        "tool_use_id": "toolu_bdrk_01AQKsfx69xNnaeSBdbbG7uv",
        "content": "print(\"Hello, World!\")"
      }
    ]
  },
  {
    "role": "assistant",
    "content": [
      {
        "text": "Now I'll add the `greet(name)` function to the file.",
        "type": "text"
      },
      {
        "id": "toolu_bdrk_01VJcPaD7M7nRsahJFStqss2",
        "input": {
          "path": "/home/xiangli/SourceCode/learn-claude-code/hello.py",
          "old_text": "print(\"Hello, World!\")",
          "new_text": "print(\"Hello, World!\")\n\n\ndef greet(name):\n    \"\"\"Greet a person by name.\"\"\"\n    print(f\"Hello, {name}!\")\n"
        },
        "name": "edit_file",
        "type": "tool_use"
      }
    ]
  },
  {
    "role": "user",
    "content": [
      {
        "type": "tool_result",
        "tool_use_id": "toolu_bdrk_01VJcPaD7M7nRsahJFStqss2",
        "content": "Edited /home/xiangli/SourceCode/learn-claude-code/hello.py"
      }
    ]
  }
]
  1. assistant request:
python 复制代码
from agents.s02_tool_use import TOOLS

request = {
    "max_tokens": 8000,
    "messages": [
        {
            "role": "user",
            "content": "Add a function called greet(name) to hello.py"
        },
        {
            "role": "assistant",
            "content": [
                {
                    "text": "Let me first check if `hello.py` exists and review its current contents.",
                    "type": "text"
                },
                {
                    "id": "toolu_bdrk_01AQKsfx69xNnaeSBdbbG7uv",
                    "input": {
                        "path": "/home/xiangli/SourceCode/learn-claude-code/hello.py"
                    },
                    "name": "read_file",
                    "type": "tool_use"
                }
            ]
        },
        {
            "role": "user",
            "content": [
                {
                    "type": "tool_result",
                    "tool_use_id": "toolu_bdrk_01AQKsfx69xNnaeSBdbbG7uv",
                    "content": "print(\"Hello, World!\")"
                }
            ]
        },
        {
            "role": "assistant",
            "content": [
                {
                    "text": "Now I'll add the `greet(name)` function to the file.",
                    "type": "text"
                },
                {
                    "id": "toolu_bdrk_01VJcPaD7M7nRsahJFStqss2",
                    "input": {
                        "path": "/home/xiangli/SourceCode/learn-claude-code/hello.py",
                        "old_text": "print(\"Hello, World!\")",
                        "new_text": "print(\"Hello, World!\")\n\n\ndef greet(name):\n    \"\"\"Greet a person by name.\"\"\"\n    print(f\"Hello, {name}!\")\n"
                    },
                    "name": "edit_file",
                    "type": "tool_use"
                }
            ]
        },
        {
            "role": "user",
            "content": [
                {
                    "type": "tool_result",
                    "tool_use_id": "toolu_bdrk_01VJcPaD7M7nRsahJFStqss2",
                    "content": "Edited /home/xiangli/SourceCode/learn-claude-code/hello.py"
                }
            ]
        }
    ],
    "model": "aws-claude-sonnet-4-6",
    "system": "You are a coding agent at /home/xiangli/SourceCode/learn-claude-code. Use tools to solve tasks. Act, don't explain.",
    "tools": TOOLS
}
  1. assistant response:
    I've updated hello.py with a greet(name) function that takes a name parameter.
    我已更新 hello.py,添加了一个 greet(name)函数,该函数接受一个 name 参数。
    4 个工具胜过 1 个:每个工具都有明确、专注的目的
json 复制代码
{
  "id": "msg_bdrk_0163TdXNMpLi2Qb2mJVitr3J",
  "content": [
    {
      "text": "The `greet(name)` function has been added to `hello.py`. Here's a summary of the change:\n\n- **`greet(name)`** --- Takes a single `name` parameter and prints a personalized greeting, e.g., calling `greet(\"Alice\")` will output `Hello, Alice!`.",
      "type": "text"
    }
  ],
  "model": "claude-sonnet-4-6",
  "role": "assistant",
  "stop_reason": "end_turn",
  "type": "message",
  "usage": {
    "cache_creation": {
      "ephemeral_1h_input_tokens": 0,
      "ephemeral_5m_input_tokens": 0
    },
    "cache_creation_input_tokens": 0,
    "cache_read_input_tokens": 0,
    "input_tokens": 1127,
    "output_tokens": 74
  }
}

9.2 Read the file requirements.txt

  1. assistant request
python 复制代码
from agents.s02_tool_use import TOOLS

request = {
    "max_tokens": 8000,
    "messages": [
        {
            "role": "user",
            "content": "Read the file requirements.txt"
        }
    ],
    "model": "aws-claude-sonnet-4-6",
    "system": "You are a coding agent at /home/xiangli/SourceCode/learn-claude-code/agents. Use tools to solve tasks. Act, don't explain.",
    "tools": TOOLS
}
  1. assistant response
json 复制代码
{
  "id": "msg_bdrk_01NvuCmmn2VXafLXeJ924krY",
  "content": [
    {
      "text": "I'll read the requirements.txt file for you right away!",
      "type": "text"
    },
    {
      "id": "toolu_bdrk_015TrEn3nLoNATsbbwY69x4D",
      "input": {
        "path": "requirements.txt"
      },
      "name": "read_file",
      "type": "tool_use"
    }
  ],
  "model": "claude-sonnet-4-6",
  "role": "assistant",
  "stop_reason": "tool_use",
  "type": "message",
  "usage": {
    "cache_creation": {
      "ephemeral_1h_input_tokens": 0,
      "ephemeral_5m_input_tokens": 0
    },
    "cache_creation_input_tokens": 0,
    "cache_read_input_tokens": 0,
    "input_tokens": 811,
    "output_tokens": 69
  }
}
  1. messages append
json 复制代码
 [
  {
    "role": "user",
    "content": "Read the file requirements.txt"
  },
  {
    "role": "assistant",
    "content": [
      {
        "text": "I'll read the file right away!",
        "type": "text"
      },
      {
        "id": "toolu_bdrk_01HkLDUs8cSRneny1ZMt7Vbq",
        "input": {
          "path": "/home/xiangli/SourceCode/learn-claude-code/requirements.txt"
        },
        "name": "read_file",
        "type": "tool_use"
      }
    ]
  },
  {
    "role": "user",
    "content": [
      {
        "type": "tool_result",
        "tool_use_id": "toolu_bdrk_01HkLDUs8cSRneny1ZMt7Vbq",
        "content": "anthropic>=0.25.0\npython-dotenv>=1.0.0"
      }
    ]
  }
]
  1. assistant request
python 复制代码
from agents.s02_tool_use import TOOLS

request = {
    "max_tokens": 8000,
    "messages": [
        {
            "role": "user",
            "content": "Read the file requirements.txt"
        },
        {
            "role": "assistant",
            "content": [
                {
                    "text": "I'll read the file right away!",
                    "type": "text"
                },
                {
                    "id": "toolu_bdrk_01HkLDUs8cSRneny1ZMt7Vbq",
                    "input": {
                        "path": "/home/xiangli/SourceCode/learn-claude-code/requirements.txt"
                    },
                    "name": "read_file",
                    "type": "tool_use"
                }
            ]
        },
        {
            "role": "user",
            "content": [
                {
                    "type": "tool_result",
                    "tool_use_id": "toolu_bdrk_01HkLDUs8cSRneny1ZMt7Vbq",
                    "content": "anthropic>=0.25.0\npython-dotenv>=1.0.0"
                }
            ]
        }
    ],
    "model": "aws-claude-sonnet-4-6",
    "system": "You are a coding agent at /home/xiangli/SourceCode/learn-claude-code. Use tools to solve tasks. Act, don't explain.",
    "tools": TOOLS
}
  1. assistant response
json 复制代码
{
  "id": "msg_bdrk_01UGMYjZfW4kFCDhuyK7412k",
  "content": [
    {
      "text": "Here are the contents of `requirements.txt`:\n\n| Package | Version Requirement |\n|---|---|\n| `anthropic` | `>= 0.25.0` |\n| `python-dotenv` | `>= 1.0.0` |\n\nThe file lists **two dependencies**:\n1. **`anthropic`** -- The official Anthropic Python SDK (version 0.25.0 or higher), used for interacting with Claude's API.\n2. **`python-dotenv`** -- A package for loading environment variables from `.env` files (version 1.0.0 or higher), commonly used to manage API keys and configuration securely.",
      "type": "text"
    }
  ],
  "model": "claude-sonnet-4-6",
  "role": "assistant",
  "stop_reason": "end_turn",
  "type": "message",
  "usage": {
    "cache_creation": {
      "ephemeral_1h_input_tokens": 0,
      "ephemeral_5m_input_tokens": 0
    },
    "cache_creation_input_tokens": 0,
    "cache_read_input_tokens": 0,
    "input_tokens": 921,
    "output_tokens": 156
  }
}

10.完整代码

python 复制代码
#!/usr/bin/env python3
# Harness: tool dispatch -- expanding what the model can reach.
"""
s02_tool_use.py - Tools

The agent loop from s01 didn't change. We just added tools to the array
and a dispatch map to route calls.

    +----------+      +-------+      +------------------+
    |   User   | ---> |  LLM  | ---> | Tool Dispatch    |
    |  prompt  |      |       |      | {                |
    +----------+      +---+---+      |   bash: run_bash |
                          ^          |   read: run_read |
                          |          |   write: run_wr  |
                          +----------+   edit: run_edit |
                          tool_result| }                |
                                     +------------------+

Key insight: "The loop didn't change at all. I just added tools."
"""
import json
import os
import subprocess
from pathlib import Path

from anthropic import Anthropic
from dotenv import load_dotenv

load_dotenv(override=True)

if os.getenv("ANTHROPIC_BASE_URL"):
    os.environ.pop("ANTHROPIC_AUTH_TOKEN", None)

WORKDIR = Path.cwd()
client = Anthropic(base_url=os.getenv("ANTHROPIC_BASE_URL"))
MODEL = os.environ["MODEL_ID"]

SYSTEM = f"You are a coding agent at {WORKDIR}. Use tools to solve tasks. Act, don't explain."


def safe_path(p: str) -> Path:
    path = (WORKDIR / p).resolve()
    if not path.is_relative_to(WORKDIR):
        raise ValueError(f"Path escapes workspace: {p}")
    return path


def run_bash(command: str) -> str:
    dangerous = ["rm -rf /", "sudo", "shutdown", "reboot", "> /dev/"]
    if any(d in command for d in dangerous):
        return "Error: Dangerous command blocked"
    try:
        r = subprocess.run(command, shell=True, cwd=WORKDIR,
                           capture_output=True, text=True, timeout=120)
        out = (r.stdout + r.stderr).strip()
        return out[:50000] if out else "(no output)"
    except subprocess.TimeoutExpired:
        return "Error: Timeout (120s)"


def run_read(path: str, limit: int = None) -> str:
    try:
        text = safe_path(path).read_text()
        lines = text.splitlines()
        if limit and limit < len(lines):
            lines = lines[:limit] + [f"... ({len(lines) - limit} more lines)"]
        return "\n".join(lines)[:50000]
    except Exception as e:
        return f"Error: {e}"


def run_write(path: str, content: str) -> str:
    try:
        fp = safe_path(path)
        fp.parent.mkdir(parents=True, exist_ok=True)
        fp.write_text(content)
        return f"Wrote {len(content)} bytes to {path}"
    except Exception as e:
        return f"Error: {e}"


def run_edit(path: str, old_text: str, new_text: str) -> str:
    try:
        fp = safe_path(path)
        content = fp.read_text()
        if old_text not in content:
            return f"Error: Text not found in {path}"
        fp.write_text(content.replace(old_text, new_text, 1))
        return f"Edited {path}"
    except Exception as e:
        return f"Error: {e}"


# -- The dispatch map: {tool_name: handler} --
TOOL_HANDLERS = {
    "bash":       lambda **kw: run_bash(kw["command"]),
    "read_file":  lambda **kw: run_read(kw["path"], kw.get("limit")),
    "write_file": lambda **kw: run_write(kw["path"], kw["content"]),
    "edit_file":  lambda **kw: run_edit(kw["path"], kw["old_text"], kw["new_text"]),
}

TOOLS = [
    {"name": "bash", "description": "Run a shell command.",
     "input_schema": {"type": "object", "properties": {"command": {"type": "string"}}, "required": ["command"]}},
    {"name": "read_file", "description": "Read file contents.",
     "input_schema": {"type": "object", "properties": {"path": {"type": "string"}, "limit": {"type": "integer"}}, "required": ["path"]}},
    {"name": "write_file", "description": "Write content to file.",
     "input_schema": {"type": "object", "properties": {"path": {"type": "string"}, "content": {"type": "string"}}, "required": ["path", "content"]}},
    {"name": "edit_file", "description": "Replace exact text in file.",
     "input_schema": {"type": "object", "properties": {"path": {"type": "string"}, "old_text": {"type": "string"}, "new_text": {"type": "string"}}, "required": ["path", "old_text", "new_text"]}},
]


def agent_loop(messages: list):
    while True:
        response = client.messages.create(
            model=MODEL, system=SYSTEM, messages=messages,
            tools=TOOLS, max_tokens=8000,
        )
        print("assistant response:", response.to_json())
        messages.append({"role": "assistant", "content": [b.to_dict() for b in response.content]})
        if response.stop_reason != "tool_use":
            return
        results = []
        for block in response.content:
            if block.type == "tool_use":
                handler = TOOL_HANDLERS.get(block.name)
                output = handler(**block.input) if handler else f"Unknown tool: {block.name}"
                print(f"> {block.name}: {output[:200]}")
                results.append({"type": "tool_result", "tool_use_id": block.id, "content": output})
        messages.append({"role": "user", "content": results})
        print("messages:", json.dumps(messages, indent=2, ensure_ascii=False))

if __name__ == "__main__":
    history = []
    while True:
        try:
            query = input("\033[36ms02 >> \033[0m")
        except (EOFError, KeyboardInterrupt):
            break
        if query.strip().lower() in ("q", "exit", ""):
            break
        history.append({"role": "user", "content": query})
        agent_loop(history)
        response_content = history[-1]["content"]
        if isinstance(response_content, list):
            for block in response_content:
                if hasattr(block, "text"):
                    print(block.text)
        print()
相关推荐
一拳不是超人2 小时前
半年AI编程实战总结:从工具到心法,让AI成为你的超能力
前端·人工智能·ai编程
cramer_50h2 小时前
Python的web开发框架Django再次更新
前端·python·django
weixin_6682 小时前
Clawith 大模型设计逻辑与前端接口架构分析 -AI分析
前端·人工智能·架构
x-cmd2 小时前
[x-cmd] Chrome DevTools MCP 更新:支持 coding agent 直接接管当前的浏览器窗口
前端·chrome·ai·agent·chrome devtools·x-cmd·mcp
Never_Satisfied2 小时前
在HTML & CSS中,user-select属性详解
前端·css·html
怕浪猫2 小时前
第4章 提示工程基础:Prompt Templates
langchain·llm·ai编程
arvin_xiaoting2 小时前
AI Agent行为约束失效深度分析:为何SOUL.md无法完全控制Agent行为
人工智能·ai·agent·soul·约束系统·行为约束·偏离分析
ethan.Yin2 小时前
AI编程 | 概念一览
ai编程
毛飞龙2 小时前
Chrome浏览器整页截图方法(MacOS)
chrome·长截图·滚动截图