很多人觉得 AI Agent 很神秘,其实它的内核非常朴素:让大模型在一个循环里反复思考、调用工具、观察结果,直到完成任务。这套范式最经典的表述就是 ReAct(Reasoning + Acting)。
Agent 的本质是一个循环
while 未完成:
思考(Thought) ← 模型决定下一步做什么
行动(Action) ← 调用某个工具,带上参数
观察(Observation) ← 把工具结果喂回模型
模型每一轮都能看到之前所有的思考与观察,于是它可以“边做边调整”。这就是 Agent 比单轮问答强大的地方。
什么时候该用 Agent
适合:
- 需要查资料 / 算数 / 调 API 才能答的问题
- 多步骤任务(先搜再汇总再落库)
- 结果可验证(工具返回客观事实)
不适合(至少一开始):
- 纯创作、纯闲聊(浪费步数与成本)
- 工具不可靠且没有校验层
- 你还说不清成功标准
定义工具
工具本质上是一个带描述的函数。描述写得越清楚,模型调用得越准:
tools = {
"search": {
"desc": "在知识库中检索资料,输入为查询关键词",
"run": lambda q: knowledge_base.search(q),
},
"calc": {
"desc": "计算数学表达式,输入为合法表达式字符串",
"run": safe_eval, # 白名单实现,绝非裸 eval
},
}
安全提醒:真实项目里绝不要直接
eval用户输入。工具边界就是攻击面,务必做好白名单、超时、鉴权与审计日志。
工具设计清单
- 输入输出 schema 固定(JSON Schema 更佳)
- 超时与重试策略明确
- 错误信息对模型可读(它要靠错误自我纠正)
- 危险操作(删库、付款)必须二次确认或人工闸门
最小 ReAct 循环
def run_agent(question: str, max_steps: int = 6) -> str:
history = [system_prompt(tools), user(question)]
for _ in range(max_steps):
step = llm(history) # 模型输出 thought + action
if step.action == "final":
return step.answer
result = tools[step.action]["run"](step.input)
history.append(observation(result)) # 把结果喂回去
return "已达最大步数,未能完成任务"
关键点在于:每次把工具的真实结果追加回历史,模型才能基于事实继续推理,而不是凭空想象。
结构化输出更稳
与其让模型自由发挥自然语言,不如要求 JSON:
{
"thought": "需要先查资料",
"action": "search",
"input": "RAG 重排是什么"
}
或结束时:
{
"thought": "信息已足够",
"action": "final",
"answer": "……"
}
解析失败时,把错误再喂回模型重试一轮即可。
让 Agent 更可靠的几个技巧
- 限制步数:设置
max_steps,避免死循环烧钱。 - 失败可恢复:工具报错时,把错误信息也当作 observation 返回。
- 可观测:每一步 thought/action/observation 落日志,支持回放。
- 预算:限制单次任务的 tokens / 工具调用次数。
- 沙箱:对代码执行类工具隔离文件系统与网络。
从单 Agent 到多 Agent
当任务复杂到单个 Agent 难以驾驭时,可以拆成多个角色协作:一个负责规划、一个负责执行、一个负责校验。但请记住一条原则:
能用一个可靠的循环解决的问题,就不要引入多个不可靠的循环。
先把单 Agent 打磨扎实(工具稳、日志全、评测在),再考虑编排。这比一上来就堆架构要务实得多。
最小上线标准
- 工具白名单 + 超时
- max_steps + 费用上限
- 全链路日志
- 失败样本回归集
- 人工接管入口(高风险动作)
