大语言模型 Agent 原理与工程实践
随着 AI 快速发展,越来越多的技术被提出。一个直观感受是:刚弄清 Prompt,又遇到 MCP、Skill、和 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
1.2.1 Tool 是如何工作的
模型可以生成“读取文件”“查询数据库”这样的文字,却不会真的执行这些操作。Tool 为模型提供了与外部系统交互的入口。应用会把工具名称、用途和输入 Schema 等能力描述随请求提供给模型,让模型知道“可以做什么”以及“应该怎样调用”。当模型决定使用工具时,它会返回一个包含工具名和参数的结构化调用请求;这种由模型提出工具调用请求的机制,就叫作工具调用(Tool Calling)。
模型生成 tool_call
→ Tool Runtime 校验工具名、参数和权限
→ Tool Runtime 执行真实操作
→ tool_result 写回消息队列
→ 模型根据新结果继续判断
需要特别注意:工具调用只表示模型“请求使用某个工具”,并不意味着模型亲自执行了它。真实操作由应用、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 等结构化结果。
例如,一个电商退款助手收到“帮我退货退款”时,一次模型请求可以包含以下四部分:
- System Instruction(应用开发者预先设置的长期规则):
你是电商售后助手。先收集订单号、退款金额和退款原因;信息不完整时应继续询问;调用退款工具前必须取得用户确认。 - User Prompt(用户这一轮提出的具体需求):
订单 20250801001 收到时已经破损,请退回 99 元,我确认退款。 - Few-shot Examples(开发者提供的少量示范):例如先给模型一组“用户只说商品坏了 → 继续询问订单号”“用户提供完整信息并确认 → 生成退款工具调用”的输入输出样例,让模型模仿合适的处理方式。这些示例不会在真实业务中执行。
- Structured Output / Function Calling Schema(由程序随请求提供的结构约束):规定调用
create_refund时必须返回order_id、refund_amount和reason,并限制各字段的数据类型。
Structured Output 可以用于约束面向用户的响应,也可以配合 Function Calling 约束工具参数;两者用途不同(参见 OpenAI:Structured Outputs)。好的 Prompt 能提高模型理解目标的概率,却不能把概率系统变成确定性程序。把“必须鉴权”“金额不能为负数”这类规则写入 Prompt,并不等于系统已经实现了鉴权和参数校验。真正影响安全与正确性的规则,仍然需要由代码、权限系统、Schema 校验和测试兜底。
2.2 Context:模型这一轮真正看到的内容
Prompt 和 Context 经常被混为一谈。Prompt 是我们主动表达的指令,而 Context 是一次请求中模型能够看到的全部信息。Context 可能包括:
- System Instruction 和用户输入;
- 历史对话;
- 工具名称、说明和参数 Schema;
- 工具执行结果;
- 检索到的文档;
- 已加载的 Skill;
- 文件内容、任务状态和历史摘要。
继续以电商退款助手为例。在准备处理用户的退款确认时,应用可能把下面这些内容共同组织进一次模型请求。以下只是概念示意,不代表某个 API 的固定格式:
[System Instruction]
你是电商售后助手。调用退款工具前必须取得用户确认。
[历史对话]
用户:订单 20250801001 收到时已经破损,我想退款。
助手:我先为你查询订单和退款规则。
[检索到的售后政策]
签收后 7 天内发现商品质量问题,可以申请退款;审核通过后退回原支付渠道。
[可用工具]
get_order(order_id):查询订单归属、实付金额和当前状态
create_refund(order_id, refund_amount, reason):创建退款申请
[工具执行结果]
get_order → 订单属于当前用户,实付 99 元,状态为“已签收”,尚未退款
[当前用户输入]
退回 99 元吧,我确认退款。
这些内容合在一起,才是模型在这一轮能够看到的 Context。聊天框里的最后一句只是其中一部分;售后政策、工具说明或订单查询结果如果没有被应用放进这次请求,模型就无法凭空知道它们。
很多看起来像“模型拥有的新能力”,本质上是应用在调用模型之前,把更合适的信息组织进了 Context。模型不知道本地仓库的内容,但应用可以先读取文件,再把内容交给模型;模型不会永久记住用户偏好,但应用可以在下一次请求前重新检索并注入这些偏好。
2.3 RAG 与 Memory:信息从哪里进入 Context
上一节示例中的售后政策和用户偏好,并不是模型凭空知道的。应用需要先从外部存储中找到它们,再放入当前 Context。RAG 和 Memory 都完成了这个“找回并注入”的过程,但两者寻找的内容不同:
| 能力 | 回答的问题 | 典型来源 | 典型内容 |
|---|---|---|---|
| RAG | 当前任务需要哪些外部知识 | 知识库、代码库、数据库、实时数据 | 产品文档、业务规则、代码、订单记录 |
| Memory | 过去有哪些信息值得继续使用 | 历史对话和历史任务 | 用户偏好、项目约束、已确认的决策 |
例如,用户问:“破损商品退款时,运费由谁承担?”应用根据这个问题检索最新售后政策,把相关条款放入 Context,再让模型回答。这是 RAG:它为当前问题寻找外部知识。政策发生变化后,下一次检索到的内容也会随之变化。
如果用户曾经说过:“退款默认退回原支付渠道,不要退到平台余额。”系统可以将其提取为用户偏好。几个月后再次处理退款时,应用按需找回这条偏好并放入 Context。这是 Memory:它复用的是从过去交互中提取出的稳定信息。
Memory 并没有让模型获得人类意义上的永久记忆。真正保存信息的是模型之外的存储系统;没有被重新放入 Context 的记忆,模型这一轮仍然看不到。一个简化的 Memory 流程是:
历史交互
→ 提取值得复用的信息
→ 保存并更新
→ 根据新任务检索
→ 注入当前 Context
因此,RAG 的重点是“当前问题需要什么知识”,Memory 的重点是“过去有什么信息值得带到现在”。两者的效果都不只取决于存了多少内容,更取决于能否检索到正确、相关且没有过期的内容。RAG 的经典定义可参见 Lewis 等:Retrieval-Augmented Generation for Knowledge-Intensive NLP Tasks。
2.4 Skill:把处理方法带入 Context
RAG 和 Memory 解决了“模型需要知道什么”,但知道相关信息并不等于知道应该怎样完成任务。Tool 和 Skill 的区别可以概括为:
| 能力 | 提供什么 | 退款场景中的例子 |
|---|---|---|
| Tool | 一项可以真实执行的操作 | 查询订单、搜索政策、创建退款 |
| Skill | 完成一类任务的方法 | 先核对订单,再查询政策,取得确认后创建退款 |
继续以电商售后为例,get_order、search_policy 和 create_refund 都是 Tool。它们分别完成一次操作,却不说明应该先调用哪个、缺少信息时怎么办,以及什么情况下必须停止。团队可以把这些经验整理成“破损商品退款” Skill:
适用场景:用户反馈商品破损并要求退款
1. 收集订单号、破损情况和退款诉求
2. 使用 get_order 核对订单归属、状态和实付金额
3. 使用 search_policy 查询当前适用的售后规则
4. 信息不足时向用户补充询问
5. 说明退款金额和渠道,并取得明确确认
6. 调用 create_refund
7. 再次查询退款状态,返回真实处理结果
停止条件:订单不属于当前用户、超过退款期限或校验结果冲突时,
不得继续退款,应说明原因或转交人工客服
这份 Skill 没有增加新的底层操作能力,而是告诉 Agent 如何组合已有 Tool。它更像一份可复用的操作手册,通常包含适用场景、执行步骤、领域约束、可用工具、停止条件和验收标准。
Skill 不是所有平台都遵循的统一标准。工程上常采用渐进式加载:启动时只让模型看到 Skill 的名称和简介,确定任务相关后,再加载完整说明以及可选的脚本和资源。这样既能复用方法,也能避免无关说明占满 Context(参见 Anthropic:Agent Skills)。
三、让 Agent 可靠地完成长任务
前面的退款示例只需要少量步骤,相关信息可以放进当前 Context。任务持续几十分钟甚至几天后,消息和工具结果会越来越多,后台操作也可能在模型调用结束后才完成。此时仅靠对话历史已经不足以维持任务,需要在模型之外保存真实进度,并为失败和恢复设计机制。
3.1 Plan 与 State:记录任务真正进行到哪里
讨论长任务时,几个概念很容易混在一起:
| 概念 | 回答的问题 | 是否必须长期保存 |
|---|---|---|
| Context | 模型这一轮看到了什么 | 否,每次调用都会重新组织 |
| State | 任务实际上进行到哪里 | 是,应该由运行时持久化 |
| Transcript | 过去具体发生了什么 | 按需保存,用于恢复、审计和调试 |
| Memory | 哪些历史信息值得未来任务复用 | 按价值保存,不等于完整历史 |
Plan 可以看作 State 的一部分。对于长任务,它不宜只是模型临时生成的一段文字,而应该成为模型和运行时共同维护的结构化数据。例如:
id
description
status: pending / in_progress / completed / failed
blocked_by
artifacts
verification
模型可以提出任务拆分和状态更新,运行时则负责保存状态、检查依赖和调度执行。安装依赖、编译、测试等耗时操作可以在后台运行;完成后,运行时再把结果作为新事件交给 Agent Loop。
State 外置之后,即使发生上下文压缩、模型切换或会话恢复,系统也可以从真实进度继续工作,而不是要求模型从一长串历史消息中猜测任务做到哪里。
3.2 上下文压缩:保留信息,而不是保留所有文字
随着 Agent 不断读取文件和执行工具,Context 会持续膨胀。全部保留会增加成本并分散模型注意力,直接删除又可能丢失目标和关键证据。处理这些信息时,可以先判断它们能否恢复:
可以重新获取 → 保存位置或标识,需要时重新读取
可以结构化表达 → 写入 State
未来任务仍有价值 → 写入 Memory
只能从对话中理解 → 保留原文或进行有损摘要
例如,完整测试日志可以保存到文件,Context 中只保留失败摘要和文件路径;任务进度可以写入 State,不必反复携带所有讨论过程;已经确认的长期编码约束可以进入 Memory。只有无法从其他位置恢复的对话信息,才需要谨慎摘要。
因此,上下文压缩的首要目标不是“尽量短”,而是“压缩后仍然可以继续正确执行”。目标、约束、关键证据和下一步计划一旦丢失,Agent 即使继续运行,也可能已经偏离原任务。
3.3 错误恢复与停止条件
真实系统需要面对模型输出截断、上下文超限、限流、服务过载和工具失败。不同错误的处理方式不同:
| 失败类型 | 常见处理方式 |
|---|---|
| 输出被截断 | 提高输出预算或续写,并限制重试次数 |
| 上下文超限 | 外置大结果、压缩历史、保留关键 State 后重试 |
| 限流或过载 | 指数退避、随机抖动、模型降级或切换 |
| 工具失败 | 根据错误类型决定重试、修改参数或停止 |
| 重复调用 | 检测相同动作,达到阈值后熔断 |
所有重试都应该有上限,所有 Loop 都应该有停止条件。权限不足、关键输入缺失、结果相互冲突时,Agent 应明确暂停并请求人工处理,而不是不断换一种说法重复尝试。
Agent 的可靠性并不来自“从不失败”,而来自失败能够被识别、记录和恢复,并且系统能在无法安全继续时停止。
四、从单 Agent 到复杂系统
4.1 Multi-Agent:只有可拆分的任务才值得并行
当一个 Agent 已经能够可靠完成任务后,才有必要考虑 Multi-Agent。增加 Agent 并不会自动提高质量;它首先带来更多 Context、通信、任务分配和结果合并成本。
Multi-Agent 比较适合以下情况:
- 任务可以拆成边界清晰的子任务;
- 子任务之间依赖较少,可以并行执行;
- 每份结果都有独立的验收方式;
- 文件、分支或工作区能够避免写入冲突;
- 节省的时间或获得的独立视角大于协调成本。
一种常见结构是协调者—工作者模式:主 Agent 拆分任务,多个工作 Agent 在隔离的 Context 中执行,主 Agent 收集结果并统一验收。
用户目标
→ 主 Agent 拆分并分配任务
→ Agent A:查资料
→ Agent B:实现代码
→ Agent C:独立检查
→ 主 Agent 汇总、处理冲突并验收
真正需要共享的是结构化的任务状态和产物,而不是让所有 Agent 看见同一份超长对话。任务所有权、依赖关系和完成状态也应该由运行时记录,不能只依赖 Agent 在自然语言中声称“正在处理”或“已经完成”。
4.2 持续沉淀:从任务中提取 Memory 与 Skill
Agent 在执行任务时会产生大量对话、工具结果和中间决策。其中一部分可以转化为以后继续使用的能力:
记录任务过程
→ 提取稳定事实、用户偏好和操作方法
→ 分别写入知识库、Memory 或 Skill
→ 去重、处理冲突并清理过期内容
→ 在后续任务中按需检索和加载
三类内容应该分开处理:容易变化的业务事实进入可更新的知识库;跨任务仍然有效的偏好和决策进入 Memory;经过验证、可以重复执行的方法进入 Skill。
这个过程不会改变模型参数,改进的是模型之外的知识、记忆和操作规范。需要警惕的是错误经验被反复固化:只有已经验证、相对稳定并且具备验收标准的方法,才适合沉淀为 Skill。
结语
一次 LLM 调用只负责根据当前 Context 生成文本或工具调用意图。围绕这次调用,Prompt 表达目标,RAG 和 Memory 补充信息,Tool 连接外部系统,Skill 复用方法,Workflow 固化流程,Agent 根据反馈动态推进,State 保存真实进度,Evaluation 验证结果。
Agent 因此不是一种脱离大语言模型的新技术,而是一套围绕模型能力边界建立起来的执行系统。模型能力还会继续演进,但工程上的基本问题不会消失:如何提供恰当的信息,如何约束真实操作,如何维护任务状态,以及如何让系统在失败时安全停止。把这些边界设计清楚,比给系统贴上“Agent”标签更重要。