大语言模型 Agent 原理与工程实践
这两年,大模型从对话、内容生成和辅助编程,快速走向工具调用和多步骤任务执行。对程序员来说,一个直观感受是:刚弄清 Prompt,又遇到 RAG、Tool Calling、MCP、Skill、Memory 和 Agent;概念越来越多,彼此的边界却越来越容易混在一起。
这些名词并不是同一层能力的不同叫法:有的组织输入,有的补充知识,有的连接外部系统,还有的负责任务编排和状态管理。本文从一次 LLM 调用出发,把它们串联起来,说明各自解决什么问题、如何协作,以及 Prompt、Workflow 和 Agent 分别适合什么场景。
一、从一次模型调用到 Agent
1.1 LLM 的能力与边界
大语言模型的一次调用,可以抽象为:
output = LLM(context)
模型接收当前 Context,根据训练得到的参数计算 Token 概率分布并逐步生成输出。随着模型规模、训练数据和训练方法演进,模型在指令遵循、复杂推理和代码生成等任务上的表现持续提升,但一次调用的基本形态并没有改变。
这意味着模型天然存在几个边界:
- 它只能基于当前 Context 生成结果;
- 它不会直接读取文件、运行命令或操作外部系统;
- 它不会天然保存跨请求、跨会话的任务状态;
- 它的输出具有概率性,无法仅凭自然语言保证结果正确;
- 它可以生成看似合理的答案,却无法仅凭自身输出确认外部任务是否真的完成。
今天常见的大模型应用能力,几乎都可以看作对这些边界的补充:
- Prompt:表达目标和约束;
- Context:提供模型本轮能够看到的信息;
- RAG / Memory:补充外部知识和历史信息;
- Tool:连接真实世界;
- Skill:封装可复用的方法;
- Workflow:固化确定性的执行流程;
- Agent:根据反馈动态决定下一步;
- Evaluation:判断结果是否真正满足目标。
模型提供智能,但一次模型调用本身还不是一个完整的应用。
1.2 Tools 与 Loop:Agent 如何从“想”到“做”
Agent 不是一种更聪明的模型,而是围绕模型构建的执行系统。不同框架对 Agent 的定义并不完全一致,本文采用下面的工程抽象(参见 Anthropic:Building effective agents):
最小 Agent = LLM + Tools + Loop
工程化 Agent = 最小 Agent
+ Context / State / Memory
+ Guardrails
Evaluation 是独立的质量验证层,也可以作为反馈机制接入 Agent Loop。Guardrails 用于检查输入、输出和工具调用;在具体框架中,它属于可选的运行时能力(参见 OpenAI Agents SDK:Guardrails)。
1.2.1 Tool 是如何工作的
模型可以生成“读取文件”“查询数据库”这样的文字,却不会真的执行这些操作。Tool 为模型提供了与外部系统交互的入口。
应用会把工具名称、用途和输入 Schema 等能力描述随请求提供给模型,让模型知道“可以做什么”以及“应该怎样调用”。当模型决定使用工具时,它返回的是结构化调用意图,而不是工具执行结果。
模型生成 tool_call
→ Tool Runtime 校验工具名、参数和权限
→ Tool Runtime 执行真实操作
→ tool_result 写回消息队列
→ 模型根据新结果继续判断
Tool Calling 只负责生成调用请求,并不执行工具。真实操作由应用、SDK Tool Runtime 或模型服务商托管的工具环境执行,执行结果随后重新返回模型。文件读写、命令执行、网络请求和数据库访问都发生在模型之外(参见 OpenAI:Function calling、Anthropic:Tool use)。
MCP 为 Prompts、Resources 和 Tools 提供标准化的发现与调用协议,减少不同 AI 应用重复集成的成本。但统一协议本身不会替代具体系统中的鉴权、用户授权、参数校验、危险操作防护和审计(参见 MCP 2025-06-18 版官方规范)。
模型负责提出操作意图,执行环境必须对真实操作负责。
1.2.2 一次完整的 Agent 交互
将模型调用和工具执行放在一起,一次完整的 Agent 交互会经历以下过程:
用户提出目标
→ Harness 组织 Prompt、历史消息、工具描述和当前状态
→ LLM 根据 Context 生成响应
→ 是否返回工具调用?
→ 是:生成结构化 tool_call
→ Tool Runtime 校验并执行工具
→ tool_result 写回消息队列
→ 再次调用 LLM
→ 否:返回最终结果,结束循环
模型始终只负责根据 Context 做出判断,并生成文本或结构化的工具调用意图。对于客户端工具,应用需要把工具结果写回消息队列;对于服务端工具,这一步可能由 SDK 或服务商自动完成。无论由谁编排,模型都必须在后续调用中获得工具结果,才能感知环境变化。
1.2.3 Agent Loop
把上述过程写成代码,一个最小 Agent Loop 可以表示为:
messages = [user_request]
for step in range(max_steps):
response = llm(messages, tools)
messages.append(response.message)
if response.has_tool_calls():
results = execute_tools(response.tool_calls)
messages.extend(results)
continue
return response.text
raise MaxStepsExceeded()
每次 LLM 调用仍然相互独立。所谓“Agent 正在持续工作”,来自外部系统不断重复“模型决策—工具执行—结果反馈”,并将新的环境状态交给下一次模型调用。
观察状态 → 做出决策 → 执行动作 → 获得反馈 → 更新状态
如果没有最大步数、停止条件和结果验证,Loop 不会带来自治,只会带来重复操作和失控的资源消耗。
二、组成大模型应用的基础能力
2.1 Prompt:表达目标,而不是替代程序
Prompt 是开发者和用户向模型表达任务的方式。它不仅是聊天框中的一句话,也可能包含长期规则、任务背景、示例和输出约束。
一次模型请求中常见的指令与约束包括:
- System Instruction:角色、长期规则、权限边界和行为要求;
- User Prompt:当前目标、输入数据和补充要求;
- Few-shot Examples:通过少量输入输出示例,说明任务边界和期望的输出模式;
- Structured Output / Output Schema:通过 API 参数或 Schema,约束模型返回 JSON 等结构化结果。
Structured Output 可以用于约束面向用户的响应,也可以配合 Function Calling 约束工具参数;两者用途不同(参见 OpenAI:Structured Outputs)。
好的 Prompt 能提高模型理解目标的概率,却不能把概率系统变成确定性程序。把“必须鉴权”“金额不能为负数”这类规则写入 Prompt,并不等于系统已经实现了鉴权和参数校验。
真正影响安全与正确性的规则,仍然需要由代码、权限系统、Schema 校验和测试兜底。
Prompt 用来引导模型,程序用来守住边界。
2.2 Context:模型这一轮真正看到的内容
Prompt 和 Context 经常被混为一谈。Prompt 是我们主动表达的指令,而 Context 是一次请求中模型能够看到的全部信息。
Context 可能包括:
- System Instruction 和用户输入;
- 历史对话;
- 工具名称、说明和参数 Schema;
- 工具执行结果;
- 检索到的文档;
- 已加载的 Skill;
- 文件内容、任务状态和历史摘要。
很多看起来像“模型拥有的新能力”,本质上是应用在调用模型之前,把更合适的信息组织进了 Context。模型不知道本地仓库的内容,但应用可以先读取文件,再把内容交给模型;模型不会永久记住用户偏好,但应用可以在下一次请求前重新检索并注入这些偏好。
要进一步理解 Agent 的连续性,还需要区分四种数据:
| 概念 | 回答的问题 |
|---|---|
| Context | 模型这一轮能看到什么 |
| State | 任务实际上进行到哪里 |
| Memory | 哪些信息值得未来任务复用 |
| Transcript | 过去发生过什么 |
Context Window 是有限资源。对话、工具结果和文件内容持续增长后,会带来成本上升、注意力分散,最终还可能超过模型能够处理的范围。
大模型应用设计的核心之一,是决定什么信息应该在什么时候进入 Context。
2.3 RAG 与 Memory:补充知识和历史信息
当模型参数中的知识不足,或任务需要使用历史信息时,可以通过 RAG 和 Memory 向当前 Context 补充内容。两者解决的问题并不相同。
| 能力 | 主要解决的问题 | 典型内容 |
|---|---|---|
| RAG | 当前问题需要哪些外部知识 | 文档、代码、数据库记录、动态资料 |
| Memory | 过去哪些信息值得在未来继续使用 | 用户偏好、项目约束、历史决策 |
RAG 的关键不是“拥有知识库”,而是根据当前问题找到合适内容,并把它放回 Context。Memory 的关键也不是永久保存所有对话,而是提取值得复用的信息,在后续任务中按需检索。
RAG 的原始定义结合参数化生成模型与可检索的非参数知识源(参见 Lewis 等:Retrieval-Augmented Generation for Knowledge-Intensive NLP Tasks)。
历史交互
→ 提取稳定信息
→ 分类和持久化
→ 根据新任务检索
→ 重新注入 Context
Memory 并没有让模型获得人类意义上的永久记忆。真正持久化的是外部存储,模型仍然只能看到本轮被重新注入的内容。
随着 Memory 增长,系统还需要处理重复、冲突、过期和检索噪声。记住得越多不一定越好,能否在正确时机找到正确信息才是关键。
RAG 和 Memory 都是在模型参数之外补充信息。如果仅靠 Prompt、RAG 和应用编排仍无法稳定达到目标行为,可以进一步评估 Fine-tuning;它属于模型适配层,本文不展开。
2.4 Skill:把经验封装成可复用能力
有了 Tool,模型知道自己“能做什么”;但面对一类复杂任务,它仍然需要知道“应该怎样做”。Skill 解决的是方法复用问题。
一个 Skill 通常可以包含:
- 适用场景和触发条件;
- 推荐的执行步骤;
- 领域知识和约束;
- 应该使用哪些工具;
- 输出格式和验收标准;
- 常见错误与风险边界。
例如,“运行测试”是一个 Tool;“发现构建失败后,读取日志、按文件归类错误、逐步修复并重新验证”则更接近一个 Skill。
Skill 并不是一种新的模型能力,也不是所有平台都遵循的统一标准。它更像一份可以被 Agent 按需加载的操作手册:把团队经验从临时 Prompt 中抽离出来,形成可维护、可复用、可演进的能力包。
为了节省 Context,Skill 通常适合渐进式加载:启动时只提供名称和简短描述,确定与当前任务相关后,再读取完整说明。
这种由元数据、指令以及可选脚本和资源组成,并按需逐步加载的组织方式,可参考 Anthropic:Agent Skills。
Tool 告诉模型“能做什么”,Skill 告诉模型“这类任务应该怎样做”。
2.5 Workflow 与 Agent:谁来决定下一步
当任务不止一个步骤时,可以选择 Workflow,也可以构建 Agent。两者最核心的区别,是下一步由程序预先确定,还是由模型根据当前状态动态决定。Anthropic 将 Workflow 定义为由预设代码路径编排 LLM 与工具,而 Agent 由模型动态决定过程和工具使用(参见 Anthropic:Building effective agents)。
| 方式 | 控制方式 | 适合场景 |
|---|---|---|
| 单次调用 | 应用发起一次请求 | 总结、分类、内容生成 |
| Workflow | 程序预先定义步骤和分支 | 流程稳定、结果需要可预测 |
| Agent | 模型根据执行结果决定下一步 | 路径不确定、需要动态探索 |
例如,一份代码提交必须依次执行格式检查、单元测试和构建,这更适合 Workflow。排查一个未知线上问题时,需要根据日志决定读取哪些文件、运行什么命令,则更适合 Agent。
Agent 并不天然优于 Workflow。它用灵活性换取了更高的不确定性、成本和治理难度。能够由固定流程可靠解决的问题,没有必要为了“智能化”强行改造成 Agent。
三、让 Agent 完成长任务
3.1 Plan、State 与后台任务
短任务可以主要依靠 Context。对于长任务,目标、进度和中间产物更适合外置;Plan 不宜只保留为模型生成的一段文字,而可以成为模型和运行时共同维护的任务状态。
一个任务可以保存:
id
subject / description
status
owner
blockedBy
artifacts
模型负责拆分任务、判断进度和提出更新,运行时负责持久化状态、检查依赖并调度执行。
典型状态可以表示为:
pending → in_progress → completed / failed
复杂任务还可以形成依赖图。只有前置任务完成后,后续任务才能开始。安装依赖、编译和测试等耗时操作则可以转为后台执行,完成后再把事件反馈给 Agent Loop。
状态持久化的价值在于:即使发生上下文压缩、模型切换或会话恢复,系统仍然可以从真实进度继续工作,而不是要求模型从历史消息中猜测。
如果一条信息会影响任务正确性,它就不应该只存在于消息历史中。
3.2 上下文压缩:按可恢复性管理信息
随着 Agent 不断读取文件和执行工具,Context 会持续膨胀。上下文压缩不应简单理解为“把旧消息总结一下”,而应该先判断信息能否恢复:
可以重新获取 → 保存引用,需要时重新读取
可以结构化表达 → 外置为任务状态
未来任务需要复用 → 写入 Memory
只能从对话中理解 → 最后再进行有损摘要
可以采用逐层处理的策略:
- 裁剪重复、低价值的历史内容;
- 将大段工具结果落盘,只保留预览和引用;
- 用结构化状态替代散落在对话中的进度描述;
- 保存完整 Transcript,再对较早历史生成摘要;
- 只有在接近或触及上下文限制时,才使用高损失的紧急压缩。
压缩的首要原则不是“尽量短”,而是“尽量可恢复”。目标、约束、关键证据和下一步计划一旦丢失,Agent 即使继续运行,也可能已经偏离原任务。
3.3 错误恢复与停止条件
真实系统需要面对模型输出截断、上下文超限、限流、服务过载和工具失败。不同错误的恢复方式不同,但都应该回到同一套主循环,避免产生难以追踪的旁路状态。
| 失败类型 | 常见处理方式 |
|---|---|
| 输出被截断 | 提高输出预算或续写,并限制重试次数 |
| 上下文超限 | 压缩历史、外置大结果、保留关键状态后重试 |
| 限流或过载 | 指数退避、随机抖动、模型降级或切换 |
| 工具失败 | 校验错误类型,决定重试、换参数或停止 |
| 重复调用 | 检测相同动作,达到阈值后熔断 |
所有重试都应该有上限,所有 Loop 都应该有停止条件。无法恢复时明确失败,比在错误状态下继续消耗资源更可靠。
Agent 的可靠性并不来自“从不失败”,而来自失败能够被识别、恢复,并且不会无限循环。
四、从单 Agent 到复杂系统
4.1 Multi-Agent:多开模型不等于协作
当任务规模继续扩大,可以把工作分配给多个 Agent。但多 Agent 首先增加的是协调成本,其次才是并行收益。
为便于讨论,本文按协作范围将多 Agent 系统分为以下三类。这是一种工程分类,不是行业统一标准。
| 模式 | 适用场景 | 关键机制 |
|---|---|---|
| SubAgent | 一次性探索、独立复核 | 通常使用独立或隔离的 Context,只把精炼结果返回主 Agent |
| Task Team | 有依赖的多步交付 | 共享任务图,通过领取、完成和依赖关系协调 |
| Agent Team | 跨角色、长周期协作 | 消息协议、独立生命周期和隔离工作区 |
多 Agent 要有效,需要满足几个前提:
- 任务能够被清晰拆分;
- 子任务可以相对独立地执行;
- 每份结果可以独立验证;
- 文件、分支或工作区能够隔离;
- 协调成本低于并行带来的收益。
一种常见实现是协调者—工作者模式。以 Anthropic Managed Agents 为例,协调者负责任务委派,各 Agent 使用隔离的上下文线程,同时共享 Sandbox 和文件系统;其他平台的隔离边界可能不同(参见 Anthropic:Multiagent orchestration)。
自然语言中的“我正在处理”不能代替任务所有权,“我已经完成”也不能代替验收状态。在需要可靠编排时,关机、审批等关键动作可以采用带请求标识的请求—响应协议,而不是只依赖口头约定。
并行不等于协作,共享 Context 也不等于共享 State。
4.2 Evaluation:判断任务是否真的完成
模型可以生成合理的解释,也可以声称已经完成任务,但声明本身不是证据。相比追问模型“为什么这么做”,更可靠的方式是检查系统能够提供的可观察事实:
- 实际读取了哪些文件;
- 调用了哪些工具;
- 工具返回了什么结果;
- 修改保存在哪里;
- 执行了哪些测试;
- 成功标准是否逐项满足。
生产系统通常需要保留模型生成、工具调用、Guardrails 和 Handoff 等完整 Trace,作为调试和评估依据(参见 OpenAI Agents SDK:Tracing)。
多轮 Review、独立评估器和多 Agent 对抗能够提高质量,也会增加 Token、时间和通信成本。只有当结果可以被稳定、重复地评价时,持续 Loop 才有可能形成真正的自动改进。
评估 Agent 时,不能只关注最终答案,还需要同时考虑:
- 结果质量;
- Token 消耗;
- 执行时间;
- 失败率;
- 可复现性;
- 人工介入成本。
没有客观评估,Loop 只是重复;没有稳定反馈,自我改进也可能只是自我强化。
4.3 持续沉淀:从会话中提取 Memory 与 Skill
Agent 在日常任务中会产生大量交互记录、工具结果和中间决策。将这些内容转化为可复用能力,可以形成一个持续沉淀过程:
记录任务过程
→ 提取稳定知识、用户偏好和操作方法
→ 分别写入知识库、Memory 或 Skill
→ 去重、合并冲突并清理过期内容
→ 在后续任务中按需检索和加载
这个过程可以理解为 Agent 的经验沉淀:它不改变模型参数,而是持续改进模型之外的知识、记忆和操作规范。
真正需要警惕的是错误经验被反复固化。只有经过验证、稳定且可判定的方法,才适合沉淀为 Skill;容易变化的事实,则更适合保存在可检索的数据源中。
五、回到工程决策
5.1 如何选择这些能力
面对一个具体需求,可以使用下面的判断路径:
只需要理解或生成内容
→ Prompt
需要私有知识或动态资料
→ RAG
需要操作文件、代码或外部系统
→ Tools
同类任务存在稳定、可复用的方法
→ Skill
步骤固定,可以提前编排
→ Workflow
下一步必须根据执行结果动态决定
→ Agent
任务可以独立拆分并独立验收
→ 考虑 Multi-Agent
需要跨任务保留稳定信息
→ Memory
这些能力不是互斥选项。一个完整 Agent 可以同时使用 Prompt、RAG、Memory、Tool 和 Skill,内部也可能包含确定性的 Workflow。
真正重要的不是使用了多少新概念,而是每一层是否解决了真实问题:
- 成功标准是否清楚;
- 模型需要看到哪些 Context;
- 哪些动作必须由真实工具执行;
- 哪些状态必须外置和持久化;
- 失败后能否恢复并安全停止;
- 最终结果是否有客观证据可以验证。
结语
一次 LLM 调用只负责基于 Context 生成结果。Prompt 表达目标,RAG 和 Memory 补充信息,Tool 连接外部操作,Skill 复用方法,Workflow 固化流程,Agent 根据反馈动态推进任务,Evaluation 验证结果。
这些概念最终落在五个工程问题上:模型看到什么、外部能力如何执行、状态保存在哪里、下一步由程序还是模型决定,以及结果如何验证。
参考资料
- Anthropic:Building effective agents
- OpenAI:Function calling
- Anthropic:Tool use with Claude
- Model Context Protocol 2025-06-18:Server Overview
- OpenAI:Structured Outputs
- Lewis 等:Retrieval-Augmented Generation for Knowledge-Intensive NLP Tasks
- Anthropic:Agent Skills
- Anthropic:Multiagent orchestration
- OpenAI Agents SDK:Guardrails
- OpenAI Agents SDK:Tracing