William 的技术笔记

搭建 Agent 的基础知识:从一个循环说起

2026-10-22 · #Agent#LLM#架构

“Agent”这个词这两年被用得太多了,几乎任何接了大模型的产品都会自称 Agent。但如果你真的想动手搭一个,会发现它的核心远比宣传里简单,而难点也不在大家通常以为的地方。

这篇文章想做两件事:先用最少的概念把 Agent 讲清楚,再把搭建时绕不开的几块基础知识(上下文、工具、记忆、规划、多 Agent、评估、安全)逐一过一遍。前面写过的 Agent 沙箱 和 MCP 还是 CLI 可以看作这篇里两个小节的展开。

什么是 Agent

一个比较实用的定义是:

Agent = 一个大模型,在一个循环里,通过调用工具来完成目标,并根据工具返回的结果决定下一步。

拆开看有三个要素:

  • 模型:负责理解目标、做决策、生成下一步动作;
  • 工具:模型影响外部世界、获取信息的唯一途径,比如搜索、读写文件、执行命令、调用 API;
  • 循环:模型不是只回答一次,而是“行动 → 观察结果 → 再行动”,直到它认为任务完成。

第三点是关键。普通的聊天机器人是“一问一答”,控制流在人手里;Agent 的控制流在模型手里,由模型决定要走几步、每一步做什么。这就是 Agent 和“调用了一次 LLM 的程序”之间的本质区别。

Workflow 和 Agent

Anthropic 在《Building Effective Agents》里做过一个很有用的区分:

  • Workflow(工作流):LLM 和工具按照代码里预先写好的路径编排,比如“先分类,再根据类别走不同的提示词,最后汇总”;
  • Agent:LLM 自己动态决定流程和工具的使用方式。

两者不是好坏之分,而是自主程度的光谱。任务路径固定、可预测的场景,Workflow 更便宜、更稳定、更容易调试;任务开放、步骤数量无法事先确定的场景(比如修一个 bug、做一次调研),才需要真正的 Agent。

一个朴素但重要的建议:能用 Workflow 解决的,就别上 Agent;能用一次 LLM 调用解决的,就别上 Workflow。 自主性是有成本的:更多 token、更高延迟、更难预测的行为。

最小的 Agent 循环

把上面的定义翻译成代码,核心不过二十行:

messages = [{"role": "user", "content": task}]

while True:
    response = llm.chat(messages=messages, tools=TOOLS)
    messages.append({"role": "assistant", "content": response.content})

    tool_calls = [b for b in response.content if b.type == "tool_use"]
    if not tool_calls:
        break  # 模型不再调用工具,认为任务完成

    results = []
    for call in tool_calls:
        output = run_tool(call.name, call.input)   # 真正执行工具
        results.append({"type": "tool_result",
                        "tool_use_id": call.id,
                        "content": output})
    messages.append({"role": "user", "content": results})

这就是几乎所有 Agent 框架的内核。现代 LLM API 原生支持“工具调用”(Tool Use / Function Calling):你把工具的名字、描述和参数的 JSON Schema 传给模型,模型在需要时输出一个结构化的调用请求,你的代码负责执行并把结果塞回对话里。

这个循环有个学术上的前身:2022 年的 ReAct(Reasoning + Acting)论文,让模型交替输出“思考”和“行动”,再读取“观察”。今天的工具调用 API 和模型内置的推理能力,本质上是把 ReAct 做成了基础设施。

在这个循环之外,一个能用的 Agent 还需要一层“外壳”,业界常叫它 Harness:

  • 最大步数、超时、预算上限,防止无限循环;
  • 工具执行的错误处理、重试和输出截断;
  • 权限检查,以及需要人类确认的操作拦截;
  • 日志与追踪。

模型决定“做什么”,Harness 决定“能做什么、做到什么程度停下”。很多 Agent 产品之间的差距,与其说在模型,不如说在 Harness。

上下文:Agent 的工作台

对模型来说,上下文窗口就是它所知道的全部世界。它看不到你的数据库、你的代码仓库、你上周说过的话,除非这些东西以某种方式进了上下文。

一次请求里的上下文大致由这几部分组成:

  1. 系统提示词:角色、目标、规则、输出格式;
  2. 工具定义:每个工具的名字、描述、参数 Schema;
  3. 对话历史:用户消息、模型回复、工具调用及其结果;
  4. 检索进来的外部信息:文档片段、搜索结果、文件内容。

因此搭 Agent 时有一个越来越被重视的说法:上下文工程(Context Engineering),比“提示词工程”更进一步,关心的是“在每一步,给模型放进哪些信息、以什么形式、放多少”。

几个基本事实:

  • 上下文是有限且昂贵的。窗口再大也会满,而且每一步循环都要把整个历史重新发一遍,token 成本和延迟随步数增长;
  • 更多不等于更好。上下文里无关信息越多,模型越容易分心、遗漏关键内容,这种现象常被称为“上下文腐烂”(Context Rot);
  • 工具结果是最大的变量。一次 cat 一个大文件、一次返回几百条记录的查询,就可能吃掉几万 token。

常见的应对手段:

  • 截断和摘要:工具输出超过一定长度就截断,并告诉模型如何获取更多(比如分页、按行读取);
  • 压缩(Compaction):对话过长时,把早期历史总结成一段摘要,保留关键决策和未完成事项;
  • 按需加载:不把所有资料一股脑塞进去,而是给模型检索工具,让它需要时自己去拿;
  • 结构化笔记:让 Agent 把进度、待办写到外部文件里,必要时再读回来。

工具:Agent 的手和眼

模型本身只会生成文本,工具决定了 Agent 的能力边界。工具设计得好不好,对效果的影响常常比换一个更强的模型还大。

工具设计的原则

把工具当成给一个聪明但对你的系统一无所知的新同事写的接口:

  • 按任务设计,而不是按 API 映射。不要把 REST API 的几十个端点原样暴露成几十个工具;一个 search_orders(customer, status, date_range) 往往比 list_orders + get_order + filter_orders 好用;
  • 名字和描述写给模型看。说清楚这个工具做什么、什么时候该用、什么时候不该用,参数的格式和示例;
  • 返回对模型有意义的内容。返回人类可读的名字而不是内部 UUID,返回精简后的字段而不是完整原始 JSON;
  • 错误信息要可操作。“date 应为 YYYY-MM-DD 格式,你传入的是 ‘昨天’“能让模型自己纠正,“400 Bad Request”只会让它瞎猜;
  • 数量适中。工具越多,定义越占上下文,模型也越容易选错。

两种接法

给 Agent 接工具有两条主要路线:一是用 MCP 这样的协议把能力包装成结构化工具,二是直接给 Agent 一个 shell,让它跑现成的命令行工具。两者的取舍我在 MCP 还是 CLI:给 Agent 接工具的两条路 里专门聊过,这里只说结论:有 shell、面向开发者的场景,CLI 通常更省更灵活;没有 shell、多用户、需要严格限定能力的场景,MCP 更合适;真实系统大多混用。

一个特殊的工具:代码执行

如果只能给 Agent 一个工具,很多人会选“执行代码”。有了它,Agent 可以自己算数、处理数据、调用库、甚至临时写一个小工具来用。编码 Agent 之所以是目前最成熟的一类 Agent,很大程度上就是因为它们天然拥有这个万能工具。

代价也很明显:能执行任意代码,就意味着需要沙箱。

记忆:跨越上下文窗口

上下文窗口是 Agent 的“工作记忆”,一次会话结束就清空。要让 Agent 记住更久的东西,需要额外的机制。一个常见的分类方式是借用认知科学的说法:

类型 内容 常见实现
短期 / 工作记忆 当前任务的对话与中间结果 上下文窗口本身
情景记忆 过去发生过的具体事件、对话 对话日志 + 检索
语义记忆 事实、偏好、领域知识 知识库、向量检索、结构化存储
程序记忆 “怎么做某件事”的经验 提示词里的规则、技能文档、示例

实现上,最常见的是 RAG(检索增强生成):把资料切块、做向量化存入数据库,需要时按语义相似度检索出相关片段放进上下文。不过对 Agent 来说,RAG 不一定是第一选择:

  • 如果资料本身有良好的结构(代码仓库、文档目录),直接给 Agent grep、find、读文件的工具,让它像人一样去翻,往往比向量检索更准;
  • 如果需要记住的是少量关键事实(用户偏好、项目约定),一个简单的 Markdown 文件,让 Agent 在会话开始时读、在学到新东西时写,就已经很好用。

记忆系统最难的部分不是“存”,而是**“什么时候该写、写什么、什么时候该读、读多少”**。存得太多会把噪音带进上下文,存得太少又会让 Agent 反复问同样的问题。

规划与推理

面对复杂任务,Agent 需要某种形式的规划。常见的几种做法:

  • 边想边做(ReAct 式):不做全局计划,每一步根据当前观察决定下一步。灵活,适合环境反馈丰富的任务;
  • 先计划后执行(Plan-and-Execute):先让模型列出步骤,再逐步执行,必要时修订计划。适合步骤较多、需要全局视角的任务;
  • 待办清单:让 Agent 维护一个显式的 TODO 列表,完成一项勾一项。这个朴素的做法在长任务里效果出奇地好,因为它把“计划”从模型的脑子里搬到了上下文里,不容易忘;
  • 反思与自我检查:执行完成后让模型对照目标检查结果,或者运行测试、校验输出格式,发现问题再修。

现在的主流模型大多内置了“思考”能力(Extended Thinking / Reasoning),可以在给出动作之前先进行较长的推理。这让很多过去需要靠框架来实现的规划逻辑,变成了模型自己就能做的事。框架层面的规划越来越轻,更多的工作是给模型提供好的反馈信号:测试结果、编译错误、校验器输出。可验证的反馈,是 Agent 能自我纠错的前提。

多 Agent:什么时候值得

让多个 Agent 协作,听起来很诱人。常见的模式有:

  • 编排者-执行者(Orchestrator-Workers):一个主 Agent 拆分任务,分派给多个子 Agent 并行处理,再汇总结果;
  • 生成者-评审者(Evaluator-Optimizer):一个 Agent 产出,另一个 Agent 评审并给出修改意见,循环往复;
  • 路由(Routing):根据输入类型分发给不同的专用 Agent。

多 Agent 的真正价值主要在两点:

  1. 并行:调研类任务可以同时搜索多个方向,缩短总耗时;
  2. 上下文隔离:子 Agent 在自己独立的上下文里做大量探索,只把精炼后的结论交回给主 Agent,避免主 Agent 的上下文被中间过程淹没。

但它的代价同样真实:token 消耗成倍增长,Agent 之间的信息传递会丢失细节,出了问题也更难排查。如果单个 Agent 加上好的工具和上下文管理就能完成任务,就不要急着拆成多个。 多 Agent 更适合可以清晰拆分、子任务之间依赖少的场景,比如广度优先的调研;而像写代码这种需要所有部分紧密一致的任务,拆分往往得不偿失。

评估:你怎么知道它变好了

Agent 的行为路径不确定,改一句提示词、换一个工具描述,效果可能变好也可能变差。没有评估,你所有的优化都是在凭感觉。

几个基本做法:

  • 尽早建一个小评估集。不需要几千条,二三十个有代表性的真实任务就能发现大部分明显问题;
  • 评估结果,也评估过程。任务是否完成是第一位的,但也要看用了多少步、多少 token、有没有调用不该调用的工具;
  • 能用代码判断的就用代码。测试是否通过、输出是否符合格式、最终状态是否正确,这些比让 LLM 打分可靠得多;
  • LLM 评审作为补充。对开放式输出(报告质量、回答是否切题),可以用另一个模型按明确的评分标准打分,但要定期抽查它的判断;
  • 认真读轨迹。看 Agent 实际做了什么,比看任何汇总指标都更能发现问题:它在哪里误解了工具、在哪里陷入了循环、在哪里漏看了关键信息。

另外,同一个任务要多跑几次。Agent 有随机性,一次成功不代表稳定成功。

安全:边界放在模型之外

Agent 能做的事越多,可能闯的祸就越大。几类主要风险:

  • 模型犯错:误解意图、操作失误、过度发挥;
  • 提示词注入:Agent 读到的网页、文档、邮件里藏着恶意指令,模型无法可靠地区分“数据”和“指令”;
  • 权限滥用:拿到的凭证和权限超出任务需要。

应对的核心原则只有一条:不要指望模型守规矩,安全边界必须由模型无法绕过的机制来强制执行。 具体包括:

  • 用沙箱隔离代码执行,网络出口默认拒绝;
  • 凭证不进入 Agent 能读到的环境,或只给短期、最小权限的凭证;
  • 不可逆或对外的操作(删除数据、部署、发消息、付款)需要人类确认,且确认由系统强制;
  • 记录 Agent 的每一步操作,可审计、可回放。

这部分在 Agent 沙箱:原理与几个真正重要的地方 里有更详细的展开。

动手时的几条建议

最后,把上面的内容收拢成几条实际搭建时的建议:

  1. 从最简单的方案开始。先试一次 LLM 调用,不行再上 Workflow,再不行才上 Agent;
  2. 直接用 API 写一个最小循环,理解清楚每一步发生了什么,再决定要不要引入框架。框架能省事,但也会把关键细节藏起来;
  3. 在工具上投入时间。好的工具名、描述、返回格式和错误信息,是性价比最高的优化;
  4. 管理好上下文。截断、摘要、按需加载,别让上下文被无关信息塞满;
  5. 给模型可验证的反馈。测试、校验器、明确的成功标准,让 Agent 能知道自己做得对不对;
  6. 先建评估,再做优化。哪怕只有二三十个用例;
  7. 安全从第一天就考虑,而不是上线前再补。

小结

  1. Agent 的本质是“模型 + 工具 + 循环”,控制流在模型手里;
  2. Workflow 和 Agent 是自主程度的光谱,按任务的确定性来选;
  3. 上下文是 Agent 最宝贵的资源,上下文工程比提示词技巧更重要;
  4. 工具决定能力边界,设计工具要站在模型的角度;
  5. 记忆、规划、多 Agent 都是在应对上下文的有限性,按需引入,不要为了架构而架构;
  6. 评估让优化有方向,安全边界放在模型之外。

模型能力每隔几个月就会上一个台阶,今天需要框架费力实现的东西,明天可能模型自己就会了。但“给它合适的信息、合适的工具、合适的边界,并且知道怎么衡量它做得好不好”,这几件事大概不会过时。