BASICS · AI AGENT · 让模型自己动手

AI Agent:让模型从回答问题到完成任务

上一篇的模型只会「接话」,这一篇让它接活。拆开 Agent 的四个零件(LLM、工具、循环、目标),跑通一次工具调用,亲手写下 30 行最小 Agent,并拿到一张框架地图与风险清单。

agent — ~/project
> 帮我把仓库里所有 TODO 收集成一份清单
思考: 需要先搜代码 —— 调用 grep 工具
→ grep(pattern="TODO")
观察: 命中 7 处 — main.py:12 utils.py:3 api.py:2 …
思考: 信息够了,不需要再调工具
回答: 共找到 7 个 TODO,已按文件写入 TODO.md
本页目录

01

Agent 是什么:一个公式讲清楚

AI Agent(智能体)是让大模型在循环里自主使用工具、朝目标推进的系统。一个值得背下来的公式:Agent = LLM(大脑)+ 工具(手脚)+ 循环(心跳)+ 目标(方向)。LLM 负责判断下一步做什么,工具给它真正改变世界的能力,循环让「做一步、看结果、再决定」成为可能,目标则定义了「做完」长什么样。

它和上一篇的对话助手有什么不同?聊天机器人的世界是一问一答:你发消息,它回文本,交互结束。Agent 的世界是接活—干活—交差:你说目标,它自己拆步骤、调工具、看结果、修正方向,直到完成或需要你拍板。读文件、跑命令、查网页、写代码,都是它的「手」。用一张表看三种形态的边界:

形态特征
聊天机器人一问一答,只产出文本,不改任何东西
固定工作流(LLM 链)人预先写死步骤,模型只负责其中几步
Agent只给定目标与工具,步骤由模型现场决定

最后一行就是分水岭:步骤谁说了算。步骤是人写的叫工作流,步骤是模型看着办的就是 Agent。自由度更高,不可预测性也更高——第 07 节的所有刹车,都是为这一行字准备的。

为什么这件事最近两年才真正成立?2022 年 ReAct 论文给出「推理与行动交替」的经典循环;2023 年 6 月 OpenAI 给 API 加上 function calling,工具调用第一次有了标准接口;2024 年 11 月 Anthropic 开源 MCP 协议,工具生态开始互通;再加上 Claude Code、Codex 这类编程智能体把「循环+工具」打磨成了日用产品——四块地基同时就位,Agent 才从论文走进日常。

再泼一盆冷水:不是所有任务都该上 Agent。步骤固定、追求稳定的活(每天抓一次数据、固定格式翻译)用工作流更便宜、更可控;步骤没法预先写死的活(修一个还没定位的 bug、调研一个开放问题)才是 Agent 的主场。判断标准就一条:你能预先写出步骤清单吗?能,写工作流;不能,交给 Agent。

组成公式
LLM + 工具 + 循环 + 目标
标志性论文
ReAct(Yao et al., 2022 · ICLR 2023)
工具调用标准
function calling:OpenAI 2023-06 引入;Anthropic 称 tool use
工具互通协议
MCP:Anthropic 2024-11 开源
代表编程智能体
Claude Code · Codex · Kimi Code · OpenCode
与聊天机器人的区别
一次回答 vs 多步执行
自主档位
从「每步审批」到「全自动」连续可调
自我意识与自定目标
没有。它只优化你给定的目标
先把四个词焊在脑子里:大脑(LLM)决定下一步做什么,工具定义它能做什么,循环规定做到几时算完,目标说明为什么要做。本页后面每一个概念,都能挂回这四个词。

02

准备环境:三样东西,三条搭法

要亲手拆 Agent,只需要 Python、一个官方 SDK、一把 API Key。三种搭法按投入程度选:想读懂本页所有代码选 A 或 B,只想先体验选 C。

Terminal · Python 3.9+
# 先建独立虚拟环境,别污染系统 Python
python3 -m venv .venv
source .venv/bin/activate        # Windows: .venv\Scripts\activate

pip install anthropic
export ANTHROPIC_API_KEY="sk-ant-..."   # 控制台创建;Windows 用 setx

# 验证
python3 -c "import anthropic; print(anthropic.__version__)"
Anthropic 的工具结果以 tool_result 块回传,第 03 节演示;模型名以官方模型列表为准。
Key 等同钱包,而且 Agent 会自动花:一个失控的循环一晚上能烧掉不少额度。密钥只放环境变量,不进代码、不进 git;第 07 节专讲怎么省钱与止损。

三条路的共同前提只有一样:一把能用的 API Key(或一个已登录的终端 Agent)。环境就绪的标志是三件小事:Python 里 import 不报错、Key 在环境变量里、能用一句话换回模型的回答。

03

第一次工具调用:模型点菜,你来炒菜

Agent 与普通调用的全部区别,就在这一来一回:模型永远不执行工具,它只输出「我想调哪个工具、带什么参数」;真正动手的,是你的代码。先看 Anthropic 写法:

one_round.py · 一次工具调用的完整往返(示意)
import anthropic

client = anthropic.Anthropic()

# 1) 把"菜单"声明给模型:名字 + 描述 + 参数 JSON Schema
tools = [{
    "name": "get_weather",
    "description": "查询指定城市的当前天气",   # 这句是写给模型看的
    "input_schema": {
        "type": "object",
        "properties": {"city": {"type": "string"}},
        "required": ["city"],
    },
}]

messages = [{"role": "user", "content": "北京现在多少度?"}]

resp = client.messages.create(
    model="claude-sonnet-4-5",   # 以官方模型列表为准
    max_tokens=1024,
    tools=tools,
    messages=messages,
)

# 2) 模型只会"点菜",不会"炒菜"
print(resp.stop_reason)           # tool_use —— 它想调工具了
print(resp.content[0].name)       # get_weather
print(resp.content[0].input)      # {'city': '北京'}
  • 01

    声明菜单

    把每个工具的名字、用途、参数 Schema 随请求发给模型。description 写得越清楚,模型点菜越准——它是写给模型看的接口文档。

  • 02

    模型点菜

    返回里出现 tool_use 块:工具名 + 参数 JSON。注意此刻什么都没发生,模型只是「说」要调。

  • 03

    你来执行

    你的代码真的去调天气接口,把结果包成 tool_result(带上 tool_use_id)追加进对话。工具报错也如实回传——模型能根据报错改道。

  • 04

    循环到收工

    把更新后的 messages 再发给模型。它看完结果要么继续点菜,要么给出最终回答——stop_reason 变为 end_turn,循环结束。

OpenAI 兼容写法做同一件事,字段名不同、流程同构:

openai_style.py · 同构写法(节选示意)
r   = client.chat.completions.create(model=MODEL, messages=messages, tools=tools)
msg = r.choices[0].message

if msg.tool_calls:                        # 模型点了菜
    messages.append(msg)
    for call in msg.tool_calls:
        args = json.loads(call.function.arguments)
        result = get_weather(**args)      # 你来执行
        messages.append({"role": "tool",  # 回传结果
                         "tool_call_id": call.id,
                         "content": result})

两家的「菜名」对照着记,看任何框架源码都不慌:

角色字段名(Anthropic / OpenAI 兼容)
声明工具tools=... / tools=...(这一项两家几乎同名)
调用意图tool_use 块 / message.tool_calls
参数包裹block.input / call.function.arguments(都是 JSON)
结果回传tool_result 块(带 tool_use_id)/ role:"tool"(带 tool_call_id)
收工信号stop_reason="end_turn" / finish_reason="stop"
还有一条容易忽略:不是每个任务都需要工具。「总结这段文字」就不要给模型任何工具——能力越多,跑偏的姿势越多。工具菜单按任务配,这是第 05 节设计原则的第一条。

04

四大核心件:ReAct、工具调用、记忆、规划

4.1 ReAct:把「想」和「做」交替起来

ReAct = Reasoning + Acting。这篇 2022 年论文的核心发现是:让模型把推理写成文字(Thought)、再决定行动(Action)、然后观察结果(Observation),比一口气直接猜答案准确得多。原因不难理解:把「想」写出来,等于强迫模型分步检查自己的判断;而「观察」把真实世界的反馈接回上下文,模型不必靠想象答题。今天几乎所有 Agent 的主循环都是它的后代——包括 Claude Code 每次说「我来看一下这个文件」的那一刻。

react.log · 一次标准 ReAct 轨迹
Thought: 用户要找"处理登录的函数",应该先搜索代码
Action:  grep(pattern="def login")
Observation: auth/login.py:31  auth/oauth.py:88

Thought: 先读 auth/login.py
Action:  read_file(path="auth/login.py")
Observation: def login(user, pwd): ...

Thought: 找到了,可以回答
Answer: 登录逻辑在 auth/login.py 第 31 行,走的是表单校验……

对照第 03 节的 API 调用:Thought 和 Answer 是模型的文本输出,Action 对应 tool_use,Observation 就是你回传的 tool_result。论文里的三步,落在 API 上就是一次往返。

4.2 工具调用(function calling)怎么工作

模型在训练时见过海量「对话中插入工具调用」的样本,所以它天生会点菜。工程上要抓牢三件事:第一,工具由你执行,模型只输出调用意图;第二,参数是 JSON,用 Schema 约束格式;第三,工具描述是写给模型的接口文档——文档烂,点菜就烂。工具宜少而精,几十个语义重叠的工具会互相干扰。

descriptions.py · 同一个工具,两种文档
# 坏:模型猜不出什么时候用、参数什么格式
"description": "读文件"

# 好:说清用途、适用时机、限制、参数格式
"description": "读取本地文本文件内容。需要查看代码、配置或
    数据文件时使用;不支持列出目录;path 用相对路径"

MCP(Model Context Protocol)把「怎么声明、发现、调用工具」标准化:工具方实现一次 MCP server,任何支持 MCP 的 Agent 都能用。你在本站各工具专页里看到的 MCP 配置,背后就是这个协议。

顺带认识两个进阶词:并行工具调用——模型一次点几道互不依赖的菜,代码并发执行能省一半时间;结构化输出——让模型按固定 JSON 格式作答,是「调工具」之外另一种受控输出。两者在各家 API 文档里都有专门章节。

4.3 记忆:短期靠上下文,长期靠外存

短期记忆就是上下文窗口:本轮对话、每一步工具结果全在里面——这也是为什么 Agent 越跑越慢、越跑越贵(每一步都带着全部历史重新发送)。窗口逼近上限就要压缩(很多 CLI 提供 /compact 类命令,摘要折叠旧内容)。

长期记忆靠外部存储:把结论写进文件、数据库或向量库,下次需要时用 RAG 检索回来。规律一句话:上下文是易失的工作内存,文件与数据库才是持久的。

memory.txt · 记忆的三种落点
1. 上下文窗口    : 本会话有效;满了就压缩或截断
2. 项目文件      : AGENTS.md / TODO.md —— 重启之后还在
3. 向量库 + RAG  : 海量历史里按语义检索相关片段

# 压缩示例(各 CLI 命令名略有差异):
> /compact 保留架构决策与未完成的 TODO

工程上的对应物你已经见过:终端 Agent 的 /compact 是落点 1 的维护命令,AGENTS.md 是落点 2 的标准文件名,而落点 3 就是本站反复出现的 RAG——三个落点各管一段时效,混着用最稳。

4.4 规划:把大目标拆成小步骤

复杂任务直接开干容易翻车。主流做法是先规划再执行:让模型先产出步骤清单(写进 todo、或进入只读的 Plan 模式),人确认后再逐步执行,每完成一步更新状态。规划的本质,是把「一锤子买卖」变成「可检查的进度条」——错了能早发现,上下文也能省。

plan.log · Plan 模式下的任务分解(示意)
> 把认证模块从 session 换成 JWT
计划: 1. 通读 auth/ 现有实现(只读)
      2. 列出所有引用 session 的调用方
      3. 设计 JWT 签发与校验中间件
      4. 逐文件替换 + 补测试     ← 每步都要你点头才继续
四个零件合体:ReAct 是循环的骨架,工具调用是循环里唯一的「动作」,记忆决定每一步能看见什么,规划决定循环怎么分程。下一篇教程(ai-math.html)之外,本站各框架专页都是这四件事的不同封装。

05

速查表:概念、循环、框架与设计原则

下面这块表格能搜。忘词就搜 ReAct、压缩、审批,选框架就搜 多智能体。

概念一句话解释
Agent(智能体)LLM + 循环 + 工具 + 目标:能多步自主干活的系统
ReAct思考—行动—观察交替的经典循环范式(2022 论文)
Function Calling / Tool Use模型输出结构化调用意图,由你的代码执行工具
JSON Schema描述工具参数的结构,约束模型输出的格式
系统提示词对话最前面定角色、立规矩的指令,用户不可见
上下文窗口一次能塞给模型的最大 token 数,Agent 的工作内存
上下文压缩会话变长时摘要折叠旧内容,常见命令如 /compact
长期记忆文件、数据库或向量库;需要时用 RAG 取回
RAG 检索增强生成先检索资料再生成,给 Agent 接上私有知识
Plan 模式只读探索 + 出计划,批准后才动手的执行档位
Human-in-the-loop关键步骤回交人类审批的设计
Guardrails 护栏限制动作范围、步数与权限的防护机制
MCPAnthropic 2024-11 开源的模型上下文协议,工具生态的「USB 接口」
子代理(Subagent)主 Agent 派出去的独立 Agent:各自带干净上下文干子任务
上下文工程决定「每一步让模型看见什么」的工程——比提示词工程更进一步

06

实战:30 行写一个最小 Agent

下面这个文件是所有 Agent 的共同骨架——看懂它,Claude Code 和 LangChain 对你都不再是黑盒。写法是 OpenAI 兼容风格;Anthropic 原生 SDK 把工具结果包成 tool_result 块(见第 03 节),流程完全同构。

完整源码:mini_agent.py

mini_agent.py · 最小可用 Agent(示意)
# 跑通思路用:OpenAI 兼容写法,MODEL 换成你有权限的模型
import json
from openai import OpenAI

client = OpenAI()          # 自动读环境变量 OPENAI_API_KEY
MODEL  = "gpt-4o-mini"

# 1) 工具:先给模型一份"菜单"
def read_file(path: str) -> str:
    "读取本地文本文件的前 2000 字符"
    return open(path, encoding="utf-8").read()[:2000]

TOOLS = [{
    "type": "function",
    "function": {
        "name": "read_file",
        "description": "读取本地文本文件内容;查看代码/配置时用,不支持列目录",
        "parameters": {
            "type": "object",
            "properties": {"path": {"type": "string"}},
            "required": ["path"],
        },
    },
}]
EXEC = {"read_file": read_file}          # 菜名 → 真正的函数

# 2) 主循环:模型 → 工具 → 观察 → 再问模型,直到它不要工具
def run(goal: str, max_steps: int = 8) -> str:
    messages = [{"role": "user", "content": goal}]
    for _ in range(max_steps):             # 保险丝:防失控
        r = client.chat.completions.create(
            model=MODEL, messages=messages, tools=TOOLS)
        msg = r.choices[0].message
        if not msg.tool_calls:             # 不要工具了 = 最终回答
            return msg.content
        messages.append(msg)
        for call in msg.tool_calls:        # 执行每个工具并回传
            fn = EXEC.get(call.function.name)
            result = fn(**json.loads(call.function.arguments))
            messages.append({"role": "tool",
                             "tool_call_id": call.id,
                             "content": result})
    return "已达最大步数,强制停止。"

# 3) 下达目标
print(run("读一下 README.md,用三句话总结这个项目是做什么的"))

数一数:去掉注释与空行,核心逻辑三十行上下——声明工具、主循环、保险丝、目标,一个不少。所有 Agent 框架做的,都是给这个骨架加壳。

练手:五分钟给 Agent 装上第二只手

只改三处,就能让模型学会「先看目录、再挑文件」的组合拳——亲眼看一次工具组合,胜过十篇原理文章:

two_hands.py · 加一个 list_dir 工具(示意)
import os

def list_dir(path: str) -> str:
    "列出目录下的文件名,不递归"
    return "\n".join(os.listdir(path))

# 1) TOOLS 里照抄一份 schema:name 换成 "list_dir",
#    description 写 "不知道文件名时先用我列目录"
# 2) EXEC 注册真函数:
EXEC = {"read_file": read_file, "list_dir": list_dir}
# 3) 换个更开放的目标:
print(run("这个项目是干什么的?自己找入口文件研究"))
# 你会在日志里看到:先 list_dir,再 read_file(它挑中的那个)
加完跑一次:模型自己决定「先看目录、再挑文件读」,而你没有写任何一行 if 去安排这个顺序——这就是「步骤由模型现场决定」的体感。

拆解:循环里的三个关键点

  • 01

    声明工具

    TOOLS 里那段 JSON Schema 是给模型的接口文档:name 见名知义,description 写「何时该用我、有什么限制」。EXEC 把菜单名映射到真函数——菜单和后厨分开。

  • 02

    主循环

    create → 有 tool_calls 就执行并回传 → 再 create。模型不再要工具时返回 content,循环结束。整页第 04 节的 ReAct,落到代码里就是这个 for 循环。

  • 03

    加保险丝

    max_steps=8 防死循环。生产代码还要补:异常捕获、token 统计、每步日志——第 07 节逐条展开。

Terminal · 跑起来
$ python3 mini_agent.py
# 第 1 轮:模型点菜 read_file(README.md) → 你执行回传
# 第 2 轮:模型读完内容,不再点菜,输出三句话总结
这是一个面向初学者的 AI 教程站,共 21 门课程,按五大分类组织。
它提供终端实操风格的中文教程,附带可复制的命令与速查表。
内容依据官方文档整理,强调动手验证。

输出是示意口径——真实回答随模型与 README 内容而变;关键是过程:两轮循环、一次工具调用、零行 if-else 由你编写。

这是教学示意:它没有权限控制(能读你磁盘上任何路径)、没有重试、没有并发。真实产品至少要加目录白名单与敏感操作审批——这正是下一节的主题。

07

局限与风险:四个必须预装的「刹车」

Agent 的自由度比聊天机器人高一个数量级,出事的方式也多一个数量级。四个坑,每个都要在写第一行代码之前就想好刹车在哪。

坑一:循环失控

模型可能对同一个失败工具反复重试,或在两个工具之间来回打转;每转一圈,全部历史都重新发送一遍——错得越久,烧得越多。

brakes.py · 给循环上三道保险(节选)
for step in range(max_steps):            # 1. 步数上限
    ...
    if usage.total_tokens > budget:      # 2. 预算熔断
        raise BudgetExceeded(...)
    ...
# 3. 工具报错返回"可读"信息,引导模型换路而不是原路重试
return {"error": "文件不存在;检查路径,或改用 list_dir 查看目录"}

坑二:权限过大

Agent 拿到 shell 就等于拿到你的机器:删文件、改配置、外发数据都可能发生。默认最小权限——只读放行,写入与命令要审批;不可信任务跑在容器或沙箱里。各家 CLI 的 --yolo / --auto 档位都是「把判断权交出去」,用之前先 git status 看一眼,确保有退路。权限门的最小实现思路,其实只有一个白名单加一道人工闸:

perm.py · 权限门的最小实现思路(示意)
ALLOW_READ = {"./src", "./docs"}          # 只读白名单目录

def guard(tool: str, args: dict) -> bool:
    if tool == "read_file":
        return args["path"].startswith(tuple(ALLOW_READ))
    # 写入与 shell:一律回交人类审批
    return ask_human(f"允许 {tool}({args}) 吗? [y/N]")

把 guard() 插在第 06 节主循环「执行工具」之前,两行代码完成接入——所有成熟的 Agent 产品里,都有这个函数的复杂版本。

一条硬线:凡是全自动档,都等于授权模型替你做「能不能改」的判断。仓库有未提交改动、或脚本会碰线上环境时,退回「每步审批」。

坑三:成本失控

Agent 每一步都把「全部历史 + 工具结果」重新发给模型:上下文随步数增长,成本近似平方级爬升。长任务要压缩上下文、粗活用小模型、每步打印 usage。

省钱三招:粗活小模型、精活大模型(同一任务分程用不同模型);每步打印 usage,心里有数;长会话勤压缩——省下来的都是真金白银。
cost.log · 一次 10 步任务的 token 体感(示意)
第 1 步  请求约 2k tokens
第 5 步  请求约 15k tokens
第 10 步 请求约 30k tokens
# 整轮成本 ≈ Σ(每步全量上下文) —— 由"步数 × 上下文"决定

坑四:盲目信任执行结果

幻觉在 Agent 里没有消失,只是换了一身衣服:工具没搜到不代表不存在,网页内容错了模型会「自信地」接着编。对策三层:终稿必审 diff;关键事实让它给出处;重要任务导出会话留档。

Terminal · 收工三连
git diff                 # 1. 人工过一遍它到底改了什么
git add -p               # 2. 认可一块收一块,不整包吞下
# 3. 会话导出留档(如 Kimi Code 的 /export-md,见本站专页)
执行结果 ≠ 事实:grep 没搜到,可能只是关键词不对。人工看 diff 是最后一道、也是最便宜的一道防线。

08

进阶:多智能体协作,与精选资源

多智能体:把一个大脑变成一个团队

当单 Agent 的工具太多、上下文太挤时,可以把任务分给多个角色化 Agent:一个规划者拆解任务,几个执行者各管一段,一个审查者复核产出,最后由协调者汇总交差。AutoGen、CrewAI 把这套模式产品化;LangGraph 用「图」来编排这类流程。

team.log · 多智能体消息流(示意)
用户目标 → 规划者拆成 3 个子任务
  ├─ 执行者 A: 写代码     → 产出 patch
  ├─ 执行者 B: 写文档     → 产出 docs
  └─ 审查者 C: 复核 A/B   → 通过 / 打回
协调者汇总 → 交用户审批
规划者
拆任务、定每步的验收标准
执行者
干活;一人一域,工具集按角色裁剪
审查者
复核产出,敢于打回而不是放行
协调者
维护共享上下文,对外交差

但要泼一盆冷水:多智能体不是免费午餐——通信本身消耗 token,一个角色的错误会顺着消息流向下游放大。Anthropic 在《Building Effective Agents》里的建议是:能用单 Agent + 好工具解决的,就别上多 Agent;确要上,先让每个单 Agent 都可靠。什么信号说明值得上?任务能按专业域干净切分、单 Agent 上下文装不下、且各段之间只需要少量交接信息。

顺手指一条路:怎么读框架源码

任何框架、任何产品,先找主循环:在仓库里搜 stop_reason、finish_reason、tool_calls 这三个词,找到「调模型—判停—执行—回传」四步,剩下都是外围。第 03 节的同构对照表,就是你的寻宝图。

Terminal · 在任意框架仓库里定位主循环
grep -rn "stop_reason\|finish_reason\|tool_calls" src/ | head -20
# 命中的那几行,就是整个框架黑盒的开关

精选资源(真实链接)

下一步去哪:去 ai-math.html 补数学地基;想直接上手产品,从 claude-code.html 或 kimi-code.html 开始;想组 LLM 应用再去 langchain.html。

09

收尾:照着勾一遍

0 / 5

五个全勾,就进 ai-math.html 补地基;想边玩边学,开一个终端 Agent 对照本页第 04 节观察它的每一次「思考—行动—观察」。
已复制