一次 Agent Run 是怎么发生的:从用户目标到工具调用、状态更新和风险拦截
前两篇我分别讲了两个问题。
第一篇讲:Agent 不是多个 AI 角色互相聊天,而是让大模型在边界内完成任务。
第二篇讲:ReAct、Tool Calling、State、Memory、Guardrails、Tracing 这些术语,在 Agent 的任务执行循环里分别负责什么。
这篇文章只拆一件事:
一次 Agent Run 是怎么从“用户问题”,一步步走到“工具调用、状态更新、风险拦截和最终回答”的。
真实的一次 Agent Run 更像这样:
用户目标 → 当前状态 → 模型判断下一步 → 请求调用工具 → 权限检查 → 执行工具 → 返回观察结果 → 更新状态 → 风险拦截 → 最终回答 / 人工确认 / 继续执行 → 记录运行轨迹如果再压缩一点,就是:
看目标 → 看状态 → 决定下一步 → 调工具 → 看结果 → 更新状态 → 判断风险 → 继续或停止一次 Agent Run 不是“模型调用了一个工具”,而是模型、工具、状态、权限、反馈和日志共同完成的一次任务执行过程。
我更愿意把一次 Agent Run 理解成:
从用户提出目标开始,到系统完成任务、失败停止、进入人工确认,或者输出最终结果为止的一次完整执行过程。
一、Step 1:把用户问题变成任务目标
链路位置:
用户目标用户原话是:
我的订单签收三天了,商品破损,可以退吗?Agent 首先要做的,不是回答,而是识别目标。
这个问题背后的任务目标是:
判断当前订单是否可以申请售后 / 退货,并给出下一步处理建议。同时,系统还要识别几个关键信号:
问题类型:售后 / 退款 / 商品破损 用户声称:签收三天 已知信息:商品破损 未知信息:订单状态、商品类目、售后政策、凭证要求 潜在风险:不能直接承诺退款,不能直接提交退款申请这一步看起来简单,但非常重要。
如果目标识别错了,后面的工具调用都会错。用户问退款,系统却误判成物流查询,就会去查物流;用户问售后政策,系统却误判成投诉处理,就会进入工单流程。
ReAct 论文的核心思想也是让模型在任务过程中交替进行 reasoning 和 acting:先判断下一步,再采取动作,通过外部知识源或环境获得反馈,然后继续调整后续动作。放到工程里,就是 Agent 不能一次性拍脑袋给答案,而要根据目标和反馈持续推进任务。
二、Step 2:初始化 State,记录已知信息和缺失信息
链路位置:
用户目标 → 当前状态识别目标之后,系统要初始化 Agent State。
State 不是普通聊天记录。它更像这次任务的运行上下文。
比如:
Run ID:run_001 Goal: 判断订单是否可以退货。 Known Facts: - 用户说订单已签收三天 - 用户说商品破损 Missing Info: - 真实订单状态 - 订单是否属于当前用户 - 商品类目 - 售后政策 - 是否需要图片凭证 Risk Flags: - 不能直接承诺退款 - 不能自动提交退款申请它把“用户已经说了什么”“系统还不知道什么”“当前有哪些风险”先记录下来。
为什么不能跳过?
因为 Agent 不是单轮问答。它后面会查订单、查政策、更新事实、判断风险。如果没有 State,模型下一轮可能忘记刚刚查到的信息,也可能重复调用同一个工具。
从LangGraph 文档中可以理解 Agent 里的 State 和 Memory:State 管当前任务,Memory 管长期上下文。
所以,一次 Agent Run 的第一层工程基础是:
先有状态,再谈行动。
三、Step 3:模型第一次判断:当前信息不足,先查订单
链路位置:
用户目标 → 当前状态 → 模型判断下一步现在 Agent 已经知道:
用户想判断是否能退货。但真实订单状态未知。商品类目未知。售后政策未知。这时模型不应该直接回答,而应该做第一次判断:
当前信息不足,需要先查询订单。这一步可以叫 Reasoning,也可以叫 Planning。
但这里要注意:工程系统不需要把模型完整思维链展示给用户。真正需要记录的是可审计的决策摘要。
比如:
Decision #1:需要查询订单,因为当前无法确认订单状态、签收时间和商品类目。然后模型提出工具调用请求:
{ "type": "tool_call", "name": "get_order", "arguments": { "orderId": "A1001" } }注意:这一步还没有真正查数据库。
它只是一个 Tool Call Request。
四、Step 4:Tool Call 不是执行结果,只是动作请求
链路位置:
用户目标 → 当前状态 → 模型判断下一步 → 请求调用工具这是 Tool Calling 最容易被误解的地方。
很多人会说:
模型调用了工具。
更准确的说法是:
模型请求调用工具,应用程序决定是否执行。
Spring AI 官方文档对这个边界说得很清楚:虽然通常说 Tool Calling 是模型能力,但工具调用逻辑由客户端应用提供;模型只能请求工具调用并提供输入参数,应用负责执行工具调用并返回结果,模型不会直接访问工具背后的 API。
所以刚才这个 JSON:
{ "type": "tool_call", "name": "get_order", "arguments": { "orderId": "A1001" } }不是订单结果。
不是数据库查询。
不是系统操作。
它只是模型在说:
我认为现在应该调用 get_order。真正的执行要交给应用程序。
这一步的工程边界非常重要:
模型提出动作意图→ 应用验证动作是否合法→ 应用执行工具→ 工具结果返回给模型如果把这一步理解错,就会误以为“模型拥有了系统权限”。
实际上,权限应该始终掌握在应用层。
五、Step 5:Policy Gate 先检查权限,再执行工具
链路位置:
用户目标 → 当前状态 → 模型判断下一步 → 请求调用工具 → 权限检查收到 tool call 后,应用不能马上执行。
它至少要检查几个问题:
这个工具是否存在? 这个工具是否允许当前场景调用? 当前用户是否有权限? orderId 是否属于当前用户? 参数格式是否正确? 这个工具是只读、写操作,还是高风险动作?比如get_order是一个只读工具,但它仍然不能随便执行。
如果用户传入了别人的订单号,系统必须拦截。
可以把这一层叫做 Policy Gate:
Tool Call: get_order(orderId=A1001) Policy Gate: - get_order 是否存在:是 - 是否只读工具:是 - 当前用户是否能访问 A1001:需要校验 - 校验通过后,才执行工具这就是为什么 Agent 安全不能只靠 prompt。
不能只写:
不要越权查询。不要查别人的订单。不要直接退款。真正可靠的做法,是让系统层做拦截。
所以,工具调用前必须有系统层边界。
六、Step 6:工具返回 Observation,State 更新
链路位置:
权限检查 → 执行工具 → 返回观察结果 → 更新状态权限检查通过后,应用执行get_order。
工具返回:
{ "orderId": "A1001", "status": "SIGNED", "signedDaysAgo": 3, "productCategory": "electronics" }这就是 Observation。
Observation 不是最终答案,而是外部系统给 Agent 的反馈。
收到 Observation 后,系统要更新 State:
Known Facts 新增: - 订单存在 - 订单属于当前用户 - 订单状态:SIGNED - 签收时间:3 天前 - 商品类目:electronics Missing Info 更新: - 还缺 electronics 类目的售后政策 - 还缺破损凭证要求 Risk Flags: - 仍不能直接承诺退款这一步决定了 Agent 能否连续推进。
如果工具结果没有写回 State,模型后面可能会忘记刚才查到的信息。
如果 Observation 没有结构化,模型可能会误读工具返回。
如果 Observation 没有记录,后面就无法复盘。
所以一次 Agent Run 里,Observation 和 State Update 是成对出现的。
工具返回结果 → 写入当前状态 → 变成下一轮判断的依据七、Step 7:模型第二次判断:继续查售后政策
链路位置:
更新状态 → 模型继续判断下一步现在 State 已经更新。
Agent 知道订单已签收 3 天,也知道商品类目是 electronics。
但它仍然不能回答“可以退”。
因为还缺售后政策。
所以模型做第二次判断:
Decision #2:已确认订单状态和商品类目,但还需要查询 electronics 类目的售后政策。然后提出第二个工具调用:
{ "type": "tool_call", "name": "get_refund_policy", "arguments": { "category": "electronics" } }应用再次检查工具是否允许、参数是否有效、返回结果是否可信。
工具返回:
{ "category": "electronics", "returnWindowDays": 7, "needDamagePhoto": true, "autoRefundAllowed": false }State 继续更新:
Known Facts 新增: - electronics 类目售后窗口:7 天 - 破损需要上传图片凭证 - 不允许自动退款 Risk Flags 新增: - 可以生成申请说明 - 不能自动提交退款 - 如果用户要提交,需要人工确认到这里,Agent 才有足够信息生成一个相对靠谱的回答。
八、Step 8:Guardrails 判断是否允许自动退款
链路位置:
更新状态 → 风险拦截这时有一个关键分叉。
用户问的是:
可以退吗?
如果模型直接说:
可以,我帮你提交退款。
这就是风险。
因为工具返回里已经明确:
autoRefundAllowed = false而且退款提交是写操作,会改变系统状态。
这时 Guardrails 应该介入。
Guardrails 不是 prompt 里一句“请谨慎”。
它应该包括:
工具权限分类 参数校验 输出检查 敏感信息过滤 高风险动作拦截 人工确认 审计日志在这个退款场景里,Guardrails 可以这样判断:
submit_refund 属于 WRITE_REVIEW。 当前政策不允许自动退款。 需要图片凭证。 所以不能自动提交退款。 只能生成退款申请说明,等待用户确认或人工客服审核。OpenAI Agents SDK 的 tracing 文档也说明,agent run 中会收集 LLM generations、tool calls、handoffs、guardrails 和自定义事件,方便调试、可视化和监控 agentic workflows。
这说明生产级 Agent 不只是“能调工具”,还要知道每一步有没有触发风险边界。
Agent 的价值不是它能不能自己做更多事,而是它能不能在该停下来的地方停下来。
九、Step 9:需要 Human Review 的动作必须暂停
链路位置:
风险拦截 → 人工确认 / 继续执行 / 停止如果用户继续说:
那你帮我直接提交吧。
系统不能直接提交。
因为这是高风险写操作。
这时 Agent Run 应该进入 Human Review:
Action: 准备提交退款申请 Risk: 写操作,会改变订单售后状态 Decision: 暂停,等待用户确认或人工客服审核LangGraph 的 interrupts 文档描述了类似机制:interrupt 可以在指定点暂停 graph execution,等待外部输入后再继续;触发中断时,LangGraph 会通过 persistence layer 保存当前 graph state,直到恢复执行。
LangChain 的 Human-in-the-loop 文档也提到,当模型提出可能需要审查的动作,比如写文件或执行 SQL,middleware 可以暂停执行并等待人类决策;图状态会被保存,之后可以 approve、edit、reject 或 respond。
这说明 Human Review 不是“弹窗确认”那么简单。
它背后需要:
暂停当前 Run 保存当前 State 展示待执行动作 展示风险原因 等待用户或人工审核 根据审核结果继续、修改或终止没有状态保存,就无法安全暂停。
没有状态恢复,就无法从人工确认后继续执行。
十、Step 10:生成最终回答,但保留证据和限制条件
链路位置:
最终回答 / 人工确认 / 继续执行在信息足够、风险判断完成后,Agent 才能生成最终回答。
一个更合理的回答是:
你的订单已签收 3 天,仍在 7 天售后窗口内。 但因为商品破损,需要上传图片凭证。当前政策不支持自动退款,我可以先帮你生成退款申请说明,提交前需要你确认,或者转人工客服审核。这个回答和普通模型回答的区别是:
它没有凭常识直接说“可以退”。 它基于订单状态和售后政策。 它说明了限制条件。 它没有越权执行退款。 它把高风险动作交给用户确认或人工审核。这就是 Agent Run 的结果。
不是“模型答得更像客服”,而是系统在边界内完成了任务推进。
十一、Step 11:Trace Log 记录整次执行过程
链路位置:
记录运行轨迹最后,一次 Agent Run 必须被记录。
你至少要能复盘这些信息:
Run ID 是什么? 用户原始问题是什么? 识别出的 Goal 是什么? 初始 State 是什么? 模型做了哪些决策? 调用了哪些工具? 工具参数是什么? 工具返回了什么? State 如何更新? Guardrails 是否触发? 是否进入 Human Review? 最终回答用了哪些证据? 整个过程花了多少 token、成本和时间?如果没有 Trace Log,Agent 出错后你只能看到最终回答。
有了 Trace Log,你才能判断:
错在目标识别? 错在工具选择? 错在工具参数? 错在权限检查? 错在工具返回? 错在 State Update? 错在 Guardrails? 错在最终生成?真实业务不只关心“这次回答对不对”。
更重要的是:
如果错了,我能不能知道错在哪里。
十二、一次 Agent Run 最容易断在哪些地方
第一,目标识别错误。
用户问的是退款,系统误判成物流查询,后面所有步骤都会偏。
第二,工具选择错误。
明明应该先查订单,却先查售后政策,导致回答缺少订单上下文。
第三,权限检查缺失。
模型请求查询订单,应用没有检查订单是否属于当前用户,这是典型越权风险。
第四,Observation 不可信。
工具返回字段不完整、错误不明确、结果未脱敏,都会影响后续判断。
第五,State 没有更新。
工具结果没有进入状态,模型下一轮可能重复调用工具,或者忘记刚刚查到的信息。
第六,Guardrails 太弱。
系统只靠 prompt 限制模型,没有在工具权限和业务规则层做硬拦截。
第七,高风险动作没有人工确认。
提交退款、发邮件、修改用户资料、执行 SQL,这些都不应该被模型自动完成。
第八,没有 Trace Log。
一旦出错,无法复盘,也无法改进。
这些问题不是“模型不够聪明”导致的,而是 Agent Run 的工程链路没有设计好。
十三、最后总结
一次 Agent Run 不是一次模型调用。
也不是一次 Tool Calling。
它是一条完整的任务执行链路:
用户目标 → 当前状态 → 模型判断下一步 → 请求调用工具 → 权限检查 → 执行工具 → 返回观察结果 → 更新状态 → 风险拦截 → 最终回答 / 人工确认 / 继续执行 → 记录运行轨迹这条链路里,每一层都有自己的职责。
模型负责判断下一步。
工具负责连接外部系统。
State 负责记录当前任务。
Observation 负责反馈真实结果。
Guardrails 负责限制风险。
Human Review 负责接管高风险动作。
Tracing 负责让过程可复盘。
所以,Agent 的价值不是“会调用工具”。
真正的价值是:
它能在状态、工具、反馈和权限边界内,把一个多步骤任务稳定推进到可交付结果。
参考资料
[1] OpenAI, Running agents - OpenAI Agents SDK.
[2] Spring AI Reference, Tool Calling.
[3] Yao et al., ReAct: Synergizing Reasoning and Acting in Language Models.
[4] LangGraph Docs, Interrupts.
[5] LangGraph Docs, Persistence.
[6] LangChain Docs, Human-in-the-loop.
[7] OpenAI Agents SDK, Tracing.
[8] OWASP, LLM06:2025 Excessive Agency.
我是Ryan,一个专注于可信AI应用工程的开发者,我的个人技术博客,研究如何让AI生成从“外观”走向“有证据、可追溯、可验证”。
