一、前言:Agent 不是"会聊天",而是"会执行任务"
前面我学习 Agent 的时候,最容易关注的是:
模型能不能理解用户问题?
模型能不能调用工具?
模型能不能查知识库?
模型能不能多轮对话?
模型能不能让多个 Agent 协作?
这些当然很重要。
但是继续往后学就会发现,真正的 Agent 项目不能只看"能不能跑通一次"。
因为一个 Agent 系统如果只是 Demo,能跑通一次就够了;但如果想做成项目亮点,甚至以后真的往工程方向靠,就必须考虑:
任务执行到哪一步了?
工具调用失败怎么办?
模型判断错了怎么办?
多个 Agent 之间怎么交接?
怎么知道 Agent 为什么给出这个结果?
怎么排查 Agent 执行过程中的问题?
怎么避免重复调用、死循环、乱调用工具?
所以今天学习的重点,不再是某一个单独概念,而是 Agent 后半段必须面对的几个工程问题:
状态管理
任务编排
异常恢复
可观测性
安全边界
OpenAI 的 Agents SDK 文档里对 Agent 有一个比较关键的描述:Agent 应用会进行规划、调用工具、在不同专业 Agent 之间协作,并维护足够的状态来完成多步骤任务。这个定义其实已经说明,Agent 不只是一次 LLM 调用,而是一个有状态的任务执行系统。
二、第一层理解:普通 LLM 调用和 Agent 执行任务有什么区别?
普通 LLM 调用大概是这样:
用户输入问题
↓
模型生成答案
↓
返回结果
这种方式适合问答、总结、翻译、解释概念。
但是 Agent 不一样。
Agent 更像这样:
用户提出任务
↓
Agent 理解目标
↓
拆解步骤
↓
判断需要哪些工具
↓
调用工具
↓
观察工具结果
↓
决定是否继续执行
↓
汇总最终答案
比如用户说:
帮我分析一下昨天晚上服务为什么报错。
普通 LLM 只能根据已有上下文猜。
但是 Agent 应该做的是:
1. 识别服务名、时间范围、错误类型;
2. 如果信息缺失,先追问;
3. 查询日志;
4. 查询监控指标;
5. 分析错误堆栈;
6. 判断是否和数据库、缓存、下游接口有关;
7. 汇总证据;
8. 给出可能原因和处理建议。
这就不是一次回答,而是一个执行过程。
所以我现在对 Agent 的理解更清楚了:
LLM 是生成答案,Agent 是围绕目标执行任务。
三、Agent 后半段第一个重点:状态管理
Agent 如果只回答一次问题,状态管理好像不重要。
但是只要任务变复杂,状态就非常重要。
比如一个排障 Agent 正在执行任务:
用户目标:分析支付服务超时原因
当前步骤:已查询接口 RT,准备查询错误日志
已知信息:
- 服务名:payment-service
- 时间范围:22:00 - 22:30
- 接口:/pay/create
- 现象:RT 从 200ms 升到 3s
- 已查询工具:Prometheus
- 下一步:查询 CLS 日志
这些信息如果不保存下来,Agent 每一步都会"失忆"。
所以 Agent 需要维护一个任务状态。
可以简单理解成:
public class AgentTaskState {
private String taskId;
private String userGoal;
private String currentStep;
private List<String> completedSteps;
private List<String> pendingSteps;
private Map<String, Object> collectedEvidence;
private List<String> toolCallHistory;
private String finalConclusion;
}
这个状态不是普通聊天历史。
聊天历史只是:
用户说了什么
模型回答了什么
但是任务状态记录的是:
任务目标是什么
现在做到哪一步
已经拿到了哪些证据
下一步要做什么
工具调用结果是什么
是否需要重试
是否可以结束
所以我觉得 Agent 后半段第一个要学会的点就是:
不要只保存对话历史,还要保存任务执行状态。
这也是我之前理解多 Agent、Plan-Execute-Replan 时容易忽略的地方。
Planner 生成计划,Executor 执行步骤,Replanner 判断是否调整计划,本质上都依赖一个共享状态。
如果没有状态,所谓多 Agent 协作就会变成:
每个 Agent 都在各说各的
而不是围绕同一个任务不断推进。
四、Agent 后半段第二个重点:任务编排
Agent 项目真正难的地方,不是调用一个工具,而是把多个工具和多个步骤组织起来。
比如一个简单 Tool Use 是:
用户问天气
↓
调用 weatherTool
↓
返回天气结果
这个很简单。
但是复杂一点的任务就不一样了。
比如 AIOps 场景:
用户:帮我看看订单服务今天下午为什么大量报错。
Agent 可能要执行:
1. 识别时间范围:今天下午
2. 识别服务对象:订单服务
3. 查询告警信息
4. 查询 QPS / RT / Error Rate
5. 查询日志
6. 过滤 ERROR / WARN
7. 分析错误堆栈
8. 判断是否和数据库有关
9. 查询数据库慢 SQL
10. 生成排障报告
这个时候就需要任务编排。
任务编排可以分成两种:
1. 固定流程编排
固定流程适合比较确定的业务。
比如文件入库 RAG:
上传文件
↓
保存文件
↓
解析文本
↓
文本切分
↓
生成向量
↓
写入向量库
↓
返回完成结果
这种流程最好不要完全交给大模型自由发挥,而是应该用代码写死。
因为它确定、稳定、可控。
2. 动态任务编排
动态编排适合不确定任务。
比如排障、代码分析、复杂问答。
这类任务不能提前写死,因为每次问题都不一样。
例如:
如果错误率升高 → 查日志
如果 RT 升高但错误率没变 → 查慢查询或下游依赖
如果日志出现连接失败 → 查数据库或网络
如果证据不足 → 继续追问用户
这种场景更适合 Agent 来判断下一步。
所以可以这样区分:
确定流程:代码 Workflow 更适合
不确定流程:Agent 动态规划更适合
我觉得这是学习 Agent 时非常关键的一点。
不要什么都交给大模型。
真正工程化的 Agent,应该是:
能确定的部分用代码控制
不确定的部分交给模型判断
关键节点加规则兜底
五、Agent 后半段第三个重点:工具调用不是越多越好
我之前学习 Function Calling 的时候,会觉得 Agent 能调用工具就很厉害。
但现在继续往后看,发现工具调用也有风险。
比如:
用户只是问一个概念,Agent 却去查数据库;
用户问题不完整,Agent 直接调用日志工具;
工具返回空结果,Agent 还强行总结;
工具调用失败,Agent 假装查到了;
同一个工具重复调用很多次;
多个工具结果冲突,Agent 不说明不确定性。
这些都是 Agent 项目里很容易出现的问题。
MCP 官方文档里提到,Tools 是让模型和外部系统交互的能力,例如查询数据库、调用 API 或执行计算。
但注意,工具只是"能力入口",不是"正确答案"。
工具调用之后,还需要判断:
工具是否调用成功?
返回数据是否为空?
数据是否和用户问题相关?
是否需要二次查询?
是否存在多个解释?
是否可以支撑最终结论?
所以 Agent 工程里要有一个很重要的意识:
工具调用结果不是最终答案,它只是证据。
比如排障场景中,日志里出现:
Redis timeout
不能直接说:
故障原因就是 Redis。
更严谨的说法应该是:
从日志看,在用户提供的时间范围内出现了多次 Redis timeout,同时接口 RT 升高。当前证据说明 Redis 访问异常可能是主要原因之一,但还需要结合 Redis 监控指标进一步确认。
这才是 Agent 项目应该追求的输出。
六、Agent 后半段第四个重点:失败恢复机制
真实项目里,Agent 调工具失败很正常。
可能出现:
接口超时
权限不足
参数缺失
返回为空
服务不可用
结果格式异常
模型生成了错误参数
工具名称匹配失败
如果没有失败恢复机制,Agent 就会很脆弱。
比如:
查询日志失败
↓
Agent 直接回答:没有发现异常
这就很危险。
因为"查询失败"和"没有异常"完全不是一回事。
所以 Agent 需要区分:
没有查到结果
工具调用失败
参数不完整
权限不足
工具不可用
结果无法判断
我觉得可以设计一个统一的工具调用结果结构:
public class ToolResult<T> {
private boolean success;
private String errorCode;
private String errorMessage;
private T data;
private boolean retryable;
}
然后 Agent 根据不同情况处理:
success = true && data 不为空:
可以进入分析阶段
success = true && data 为空:
告诉用户当前范围内未检索到相关结果
success = false && retryable = true:
可以调整参数后重试
success = false && retryable = false:
明确说明工具不可用或权限不足
这样 Agent 的回答就不会混乱。
这一点也很适合写进项目亮点:
对工具调用结果进行了统一封装,区分成功、空结果、可重试失败和不可重试失败,避免 Agent 将工具异常误判为业务结论。
这个表达就比"我接了几个工具"高级很多。
七、Agent 后半段第五个重点:可观测性
Agent 项目和普通后端项目相比,有一个很大的问题:
结果是模型生成的,中间过程如果不记录,很难排查。
普通接口出问题,可以看:
请求参数
SQL
日志
异常堆栈
响应时间
但是 Agent 出问题时,如果没有记录执行过程,你只会看到最终回答。
比如用户说:
你为什么得出这个结论?
如果没有 trace,你可能根本不知道:
模型看了哪些上下文?
调用了哪些工具?
工具返回了什么?
哪一步开始判断错了?
有没有重复调用?
有没有跳过关键步骤?
OpenAI Agents SDK 的 Tracing 文档中提到,Tracing 可以记录 Agent run 中的 LLM 生成、工具调用、handoff、guardrail 以及自定义事件,并用于调试、可视化和监控工作流。
这说明 Agent 可观测性不是可有可无的功能,而是工程化必须考虑的能力。
我觉得一个 Agent 项目至少要记录这些信息:
taskId:任务 ID
sessionId:会话 ID
userInput:用户输入
agentName:当前执行的 Agent
stepName:当前步骤
modelInput:模型输入摘要
modelOutput:模型输出摘要
toolName:调用工具名称
toolParams:工具参数
toolResult:工具结果摘要
latency:耗时
status:成功 / 失败
errorMessage:错误信息
可以简单设计成:
AgentTrace
├── Task Started
├── Planner Output
├── Tool Call: queryMetrics
├── Tool Result: queryMetrics
├── Tool Call: queryLogs
├── Tool Result: queryLogs
├── Replanner Decision
└── Final Answer
这样一来,Agent 出错时就可以定位:
是用户问题没理解对?
是计划拆错了?
是工具参数生成错了?
是工具返回异常?
是模型总结错了?
是最终输出过度推断了?
这就是 Agent 可观测性的价值。
八、Agent 后半段第六个重点:多 Agent 不是简单"多写几个类"
之前我学习多 Agent 的时候,容易把它理解成:
写几个 Agent 类
让它们互相调用
但现在发现,多 Agent 真正难的是"协作边界"。
比如:
SupervisorAgent:负责判断任务交给谁
PlannerAgent:负责拆解计划
ExecutorAgent:负责执行工具调用
ReplannerAgent:负责判断是否需要调整计划
ReporterAgent:负责生成最终报告
看起来很清晰,但真正实现时会遇到几个问题:
谁拥有最终决策权?
Agent 之间传什么数据?
上一个 Agent 的结果如何约束下一个 Agent?
某个 Agent 失败后谁处理?
多个 Agent 结论冲突怎么办?
什么时候结束整个任务?
OpenAI Agents SDK 里也提到,Agent 可以配置 instructions、tools,以及 handoffs、guardrails、structured outputs 等运行时行为。
这里的 handoff 很关键。
它不是简单调用另一个对象,而是"任务控制权转移"。
比如:
Supervisor → Planner:
你负责把用户问题拆成可执行计划
Planner → Executor:
你根据计划调用工具
Executor → Replanner:
这是执行结果,请判断是否需要调整
Replanner → Reporter:
证据足够,可以生成最终报告
所以多 Agent 设计要关注的是:
角色边界
输入输出格式
状态共享方式
任务交接规则
失败处理策略
不是"Agent 越多越高级"。
有时候一个 Agent + 多个工具就够了。
只有当任务真的需要不同角色、不同判断逻辑、不同上下文控制时,才需要多 Agent。
九、结合我的项目,怎么把这部分说成面试亮点?
如果以后面试官问:
你这个 Agent 项目除了调用大模型和工具,还有什么工程化设计?
就不能只说:
我用了 Spring AI,接入了工具调用,实现了多 Agent。
这样太普通。
可以这样说:
我没有只停留在简单的 Function Calling,而是把 Agent 按任务执行系统来设计。
在执行过程中,我维护了任务状态,包括用户目标、当前步骤、已完成步骤、工具调用历史和中间证据;对于复杂任务,我采用 Planner-Executor-Replanner 的方式,让 Planner 先拆解计划,Executor 根据计划调用工具,Replanner 根据执行结果判断是否需要调整。
同时,我对工具调用结果做了统一封装,区分调用成功、空结果、可重试失败和不可重试失败,避免 Agent 把工具异常误判成业务结论。最后,通过日志记录 Agent 的执行步骤、工具参数、工具结果和最终输出,方便后续排查模型判断错误或工具调用异常的问题。
这段话的重点是:
状态管理
任务编排
失败处理
可观测性
这就比单纯说"我做了多 Agent"更像真实项目。
十、Agent 工程化执行链路
用户任务
↓
任务理解层
↓
状态管理层
↓
计划生成 Planner
↓
工具执行 Executor
↓
结果观察 Observation
↓
重规划 Replanner
↓
质量检查 Guardrail
↓
最终回答 Reporter
↓
Trace / Log / Monitor
┌──────────────────┐
│ User Task │
└─────────┬────────┘
↓
┌──────────────────┐
│ Agent Runtime │
└─────────┬────────┘
↓
┌────────────────────────────────┐
│ Task State / Memory / Context │
└────────────────────────────────┘
↓
┌──────────┬──────────┬──────────┐
│ Planner │ Executor │ Replanner│
└──────────┴──────────┴──────────┘
↓
┌────────────────────────────────┐
│ Tools / MCP / RAG / Database │
└────────────────────────────────┘
↓
┌────────────────────────────────┐
│ Trace / Logs / Metrics / Audit │
└────────────────────────────────┘