Aklman · Library

02 / 第二章

最小循环 · 一个 Agent 只需要什么.

20 行代码就能写出一个能工作的 Agent —— prompt、LLM、工具调用、结果回传,循环直到完成。


第一章说 agent 是一个循环。这一章把那个循环写出来 —— 一个真能跑的最小 agent,不到 30 行,没有任何框架。写完你会有两个收获:一个能改的骨架(就是那个研究助手的种子),和一双能看穿框架的眼睛。

I

一个 Agent 只需要四样东西.

剥到最小,一个 agent 就是:一个目标、一个会调工具的模型、一组工具、一个带停止条件的循环。其余都是便利,不是本质。 这四样里,模型那一项有个硬要求 —— 它得支持 tool use:能在该调工具时返回 stop_reason: "tool_use",而不是硬编一段文本让你自己解析。1注 1Anthropic 文档 · Tool use overview —— client tool 的循环:Claude 返回 stop_reason: tool_use 与 tool_use 块(id / name / input),你执行后回传 tool_result(带 tool_use_id)。示例模型 claude-opus-4-8,anthropic-version 2023-06-01。截至 2026-05。剩下三样都是你写的代码:目标塞进 messages,工具用 JSON Schema 描述,循环就是一个 while 框架(LangChain 这类)不是错,它只是把这四样藏在抽象后面,让 agent 看起来很复杂。2注 2Anthropic · 「Building Effective Agents」(2024-12-19)—— augmented LLM 作为基本积木;agent 是在循环里用工具、读环境反馈、自己决定下一步。先手写一遍,你才知道框架在替你做什么 —— 以及它什么时候碍事。

II

把循环写出来.

下面是研究助手的种子 —— 它现在只会一件事:搜索。看长度,一个能跑的 agent 没你想象的那么复杂。
import anthropic
 
client = anthropic.Anthropic()   # 读环境变量 ANTHROPIC_API_KEY
 
TOOLS = [{
    "name": "web_search",
    "description": "按查询搜索网页,返回标题和摘要的列表。要查资料时调用。",
    "input_schema": {
        "type": "object",
        "properties": {"query": {"type": "string"}},
        "required": ["query"],
    },
}]
 
def run_tool(name, args):                       # 工具的真正执行
    if name == "web_search":
        return search_web(args["query"])        # 真实场景接一个搜索 API
    return f"未知工具: {name}"
 
def agent(question, max_steps=10):
    messages = [{"role": "user", "content": question}]
    for _ in range(max_steps):                  # 停止条件:步数上限
        resp = client.messages.create(
            model="claude-opus-4-8", max_tokens=1024,
            tools=TOOLS, messages=messages,
        )
        messages.append({"role": "assistant", "content": resp.content})
        if resp.stop_reason != "tool_use":      # 模型给出最终回答
            return "".join(b.text for b in resp.content if b.type == "text")
        results = [{
            "type": "tool_result", "tool_use_id": b.id,
            "content": run_tool(b.name, b.input),
        } for b in resp.content if b.type == "tool_use"]
        messages.append({"role": "user", "content": results})
    return "stopped: 达到 max_steps"

研究助手的种子:一个能搜索的最小 agent(Anthropic Messages API)

整个循环就三步:调模型 → 看 stop_reason → 执行工具并回传,直到模型不再要工具,或撞上步数上限。1注 1Anthropic 文档 · Tool use overview —— client tool 的循环:Claude 返回 stop_reason: tool_use 与 tool_use 块(id / name / input),你执行后回传 tool_result(带 tool_use_id)。示例模型 claude-opus-4-8,anthropic-version 2023-06-01。截至 2026-05。这就是 agent 的全部骨架。 它能跑,但它很笨:重启就忘光(没记忆)、工具报错它不会处理、你要是不设 max_steps 它能无限烧钱。这些不是缺陷,是后面章节的入口 —— 记忆是第 4 章,错误处理是第 3 章,停止条件与成本是第 11 章。

III

最小循环里已经埋着所有难题.

这 30 行不是玩具,是后面每一章的地图。循环里的每一格,都对应一个还没解决的问题。
TOOLS 的定义
模型靠 description 决定调不调 —— 写不好它就调错。第 3 章。
messages 越滚越长
上下文窗口会满,记忆要分层。第 4 章。
run_tool 直接执行
它在你机器上跑代码 —— 沙箱与权限。第 7、9 章。
max_steps 与停止
跑偏了怎么发现、怎么度量。第 10、11 章。
所以别急着加。先让这个最小循环在你自己的一个真任务上跑通 —— 它在哪卡住,哪一章就是你最该先读的。 动手 · 让最小循环跑你自己的一个真活:
01

接一个你已有的只读工具

run_tool 里的 web_search 接到一个真实搜索 API(先返回假结果也行)—— 研究助手就从这一步开始。
02

给它一个真目标,跑起来

用一个你平时要上网查的小问题当 question,看它搜几轮、搜对了没有。
03

记下它第一个卡住的地方

忘了上文?工具报错崩了?绕圈停不下来?把这个卡点对照上面的地图 —— 那就是你下一章。

框架把循环藏起来, 手写一遍才看清它有多小。.

Frameworks hide the loop; writing it once shows how small it is..

Aklman Library

讨论

讨论.

评论区初始化中…