agent 到底是什么:一个 while 循环
去掉包装,agent 就是「让模型自己决定下一步调什么工具,直到它说完事了」。这个定义决定了后面所有设计取舍。
学完这节你能做到
- 用一句话说清 agent、chatbot、workflow 三者的界线,并判断手头需求属于哪一类
- 指出 agent 的自主性来自哪里,以及代价是什么(不可预测、成本、越权)
- 识别「其实不需要 agent」的场景,避免拿循环去做 if-else 的活
"agent" 这个词现在什么都能指:一个会调 API 的聊天框、一条 LangChain 流水线、一个自动回邮件的脚本。
定义混乱的直接后果是选型混乱 —— 明明一个 if/else 能解决的事,非要塞进一个会自己乱跑的循环里。
所以先把定义钉死。
从一次普通调用说起
最朴素的用法是一问一答:
resp = client.messages.create(
model="claude-sonnet-5",
messages=[{"role": "user", "content": "帮我看看这个函数有什么问题"}],
)
print(resp.content[0].text)
这里模型能做的只有一件事:产出文本。它猜不到你的函数长什么样,也改不了你的文件。 输出好不好,全看你在 prompt 里塞了多少上下文。
加上工具:它开始能改变世界
给它一组工具,模型就能要求你替它执行动作 —— 读文件、跑命令、查数据库:
tools = [
{"name": "read_file", "description": "读取一个文件的内容", "input_schema": {...}},
{"name": "bash", "description": "在项目目录里执行 shell 命令", "input_schema": {...}},
]
resp = client.messages.create(model=..., messages=msgs, tools=tools)
# resp.stop_reason == "tool_use" —— 它想读文件
注意这里的分工:模型只是提出请求,执行永远发生在你的代码里。
这一点是后面所有安全边界的基础 —— 它想 rm -rf /,跑不跑得起来是你决定的。
加上循环:谁来决定「还要不要继续」
一次工具调用不够用。真实任务是:读文件 → 发现要看另一个文件 → 改代码 → 跑测试 → 测试挂了 → 再改。 把它包进循环,就成了 agent:
while True:
resp = call_model(msgs, tools)
if resp.stop_reason != "tool_use":
break # 模型认为完事了
results = run_tools(resp.tool_calls)
msgs.append(resp)
msgs.append(results) # 结果(包括报错)回灌
这就是 agent 的全部。 剩下的都是工程:上下文放不下怎么办、工具报错怎么说、 人想插一句话怎么插、跑飞了谁来拦。
上面这段代码里,「下一步做什么」是模型用 tool_use 决定的,代码只负责执行和回灌。
这个位置的移交,就是 agent 与工作流的分水岭。
workflow 与 agent 的分界线
| 控制流 | 可预测性 | 出错形态 | 适合 | |
|---|---|---|---|---|
| workflow | 写在代码里 | 高,路径可枚举 | 某一步失败,位置明确 | 步骤固定的批处理、抽取、分类 |
| agent | 由模型决定 | 低,每次都可能不同 | 绕圈、走偏、越权 | 步骤取决于中途发现的任务 |
判断标准很实用:你能不能提前画出流程图? 能画出来的就别用 agent —— 「把这批 PDF 里的金额抽出来入库」是 workflow;「查清楚这个线上故障的根因」是 agent, 因为下一步查什么取决于上一步看到了什么。
拿 agent 去做只有一条路径的事。结果是慢十倍、贵二十倍,还多出一个「它偶尔会自己发挥」的风险。 既有的判断逻辑该写成代码,别指望模型每次都推理正确。
harness 是什么
那段 while 循环谁都写得出来。真正麻烦的是外面那一圈:
- 上下文窗口满了,压缩哪一段?压缩之后 prompt 缓存全失效,这笔钱谁付?
- 工具输出十万行,全塞回去还是截断?截断了模型会不会看不到关键那行?
- 人在它跑到一半时想改主意,怎么让这句话插进循环,还不留下半截状态?
- 它要执行
git push --force,谁来拦?拦的规则写在哪一层? - 跑了两小时没进展,凭什么判定「该停了」?
把这一圈做好并且做得可改的那层软件,就叫 harness。Pi、Claude Code、Codex 都是 harness。 这门课选 Pi,是因为它的核心刻意做得极小 —— sub-agent、plan mode、权限确认、沙箱在它这里 全都是扩展,不是内置黑盒。能拆开的东西才讲得清。
Pi 官网有一节「What we didn't build」,明确列出不做 MCP、不做 sub-agent、不做权限弹窗, 每条都给了替代做法。看一个项目决定不做什么,比看它做了什么更能学到东西。
什么时候不该用 agent
- 路径固定 —— 写成脚本,快且便宜。
- 一次调用就够 —— 翻译、摘要、分类,不需要循环。
- 错了不可逆且没人复核 —— 转账、发布、删数据,先把确认门建好再说。
- 延迟敏感 —— agent 动辄几十秒到几分钟,用户在等的场景撑不住。
下面哪些需求更适合做成 agent(而不是固定工作流)?
这一节的结论
- agent = 工具 + 循环 + 由模型决定下一步。
- 执行永远在你的代码里,所以边界也永远由你定。
- 难的不是循环,是循环外面那一圈;那一圈就是 harness。
- 能写成流程图的,别用 agent。
下一节把 Pi 装上手,四种运行形态各跑一遍 —— 先有体感,再拆内部。