WRITING / 2026.09.23

AI Agent 是如何工作的:从一次模型调用到任务执行闭环

从一次虚构的服务超时排查出发,拆解目标、模型、工具、状态与环境反馈,说明 Agent 如何形成执行闭环、何时应该停止,以及 Java 后端经验如何迁移。提供可下载的 Python 离线示例,区分模拟决策与真实工具执行。

“结算服务为什么突然变慢了?”这句话看似适合交给模型回答,却没有提供足够的信息。是哪一个环境,什么时候开始变慢,慢的是数据库访问还是连接池等待,最近有没有上线?模型可以列举常见原因,但这些原因仍然只是候选解释。排障需要从现场取得证据,再根据证据决定下一步。

本系列围绕一个虚构的研发排障助手展开。服务名为 checkout,一次配置变更后出现超时。所有用户、指标、文档和事故均为教学数据;程序不会连接生产环境,也不会自动重启服务。我们要验证的是任务执行的机制,而不是某个模型的聪明程度。

六篇文章分别解决什么问题

第一篇建立执行循环,第二篇把业务能力封装为工具并接入 MCP,第三篇加入知识检索,第四篇管理上下文与记忆,第五篇处理复杂任务和恢复,第六篇建立评测与运行边界。你可以从第一篇顺序阅读,也可以通过专题入口找到需要的部分。

对 Java 开发者来说,理解这些机制不需要放弃已有经验。参数校验、状态机、事务、幂等和监控依然有用。变化主要发生在“谁来选择下一步”:以前由代码中的分支决定,现在有些选择可以由模型提出;执行权、授权和结果验证仍由应用承担。

示例采用 Python,是为了让控制流程保持简短。下载完整案例与测试后,在 Python 3.11 及以上环境运行。首次安装依赖需要网络,安装完成后的示例和测试不调用模型,也不访问外部业务服务。

python3 -m venv .venv
.venv/bin/python -m pip install -r requirements.txt
.venv/bin/python demo.py normal
.venv/bin/python run_tests.py

先区分三种任务执行方式

单次模型调用适合输入已经足够、结果可以一次形成的任务。例如,把一段已知故障记录整理为时间线。应用提交材料,模型返回整理结果,过程中不需要决定去哪里查询。此时加入循环不一定带来价值,反而增加停止控制和调试成本。

固定工作流适合步骤明确的过程。例如,先校验事件,再查询服务指标,随后读取最近发布记录,最后生成摘要。模型可以参与某些节点,但节点之间的顺序由应用定义。输入不满足条件时,也由预先定义的分支处理。它的优势是执行路径容易预测、测试和审计。

Agent 则把一部分运行时决策交给模型。模型可能先查询状态,发现 CPU 不高而连接等待增加,再要求检查连接池配置。下一步依赖之前取得的信息,无法完全通过一条固定链路表达。不过,动态选择并不意味着无限自由:工具集合、操作权限、时间和调用次数仍要有边界。

Anthropic 在架构讨论中区分预定义工作流与由模型动态决定过程的 Agent。这是一种有用的设计视角,而不是要求所有产品都采用同一个术语。本文沿用这个区分,并将其具体化为可观察的程序行为。

方式 下一步由谁选择 本案例中的适用场景
单次调用 调用前已经确定 总结已收集的事故材料
固定工作流 应用代码 每次都执行同一套健康检查
Agent 模型在约束内提出 根据现场证据继续追查原因

选择时可以先问三个问题:步骤是否已经稳定,外部信息是否需要动态补充,错误动作的代价是否可控。如果步骤稳定而且错误代价很高,固定流程通常更容易建立可靠性。若任务存在明显的信息缺口,而且后续查询取决于前一步结果,再考虑引入动态决策。

一个执行闭环包含哪些部分

目标描述任务要达到的状态。本例的目标是给出有依据的排查建议,而不是“无论如何修好服务”。后者把诊断、方案审批和生产变更混在一起,难以判断一次执行到底成功了没有。把目标写清楚,也意味着允许“现有证据不足”成为合理结果。

模型接收当前上下文,提出工具调用或者最终回答。工具将模型无法直接访问的能力转换成应用接口,例如读取状态快照。状态保存已经执行过的步骤、取得的证据与剩余预算。环境反馈则是工具的真实返回值,包括正常数据和明确的错误。

执行器负责把这些部分连接起来:接收决策,校验参数,检查权限,调用工具,记录观察,再决定是否进入下一轮。即使模型提出了一个语法正确的动作,执行器也不应把它等同于合法操作。调用一个不存在的工具、读取其他用户的数据,都必须在应用边界被拒绝。

Agent 执行循环:任务与状态进入决策,动作经过校验后调用工具,观察写回状态,并由预算与停止条件约束循环

这张图中,工具返回并不直接等于最终答案。一次查询可能发现连接池等待增加,但尚不能证明原因是连接池容量。需要结合变更记录或其他观测才能缩小假设范围。循环的价值在于允许补充信息,而不是把第一次返回包装得更像结论。

ReAct 与可观察轨迹

ReAct 论文研究了推理与行动相互配合的方式。应用工程中可以借鉴“提出下一步行动—获得观察—继续决策”的结构,但没有必要展示或猜测模型内部的完整思维过程。对排障系统而言,真正需要记录的是动作、参数、结果、时间和结果依据。

例如,一条轨迹可以包含“查询结算服务状态”“观察到等待连接数为六十四”“读取变更记录”“发现池上限从四十改为八”。这些都是可核对的事件。至于模型内部如何想到某个查询,不应由系统补写一段看似合理的独白,更不应该把那段独白当成审计证据。

示例中的 NORMAL_ACTIONS 是预置动作序列,明确标为 scripted_fixture。这使每次测试都会走相同路线,便于验证执行器。它不等同于真正能自主规划的模型,也不能证明真实模型能稳定选择这条路线。真实接入时,需要替换决策来源,而不是把预置数组的成功率当成模型成功率。

把循环写成可以检查的程序

案例的核心对象是 Toolsrun。前者封装当前身份可以执行的工具,后者消费决策、保留证据引用并产生结构化结果。决策的类型只有工具调用和结束回答两种,便于把异常路径暴露出来。

from demo import database, Tools, NORMAL_ACTIONS, run

with database() as db:
    output = run(NORMAL_ACTIONS, Tools(db), max_steps=6)
    print(output["stop_reason"])
    print(output["citations"])

这段代码真实执行本地工具,读取固定服务快照与文档。正常样例会返回 COMPLETED,引用状态快照、变更记录和排障文档三个标识。输出建议核对连接池等待指标并由人工评审回滚方案,程序不会声称已经完成根因确认,更不会进行实际回滚。

执行器维护一个证据集合。工具结果中出现的文档或快照标识可以进入集合,最终回答提供的引用必须来自已观察到的集合。模型不能凭空输出一个不存在的文档编号来通过检查。这个约束验证的是引用可追溯性,不是引用内容真的支持每句话;后者需要更细的评测。

状态中还维护已经出现过的动作指纹。相同工具和相同参数再次出现时,示例直接停止,以便清楚展示循环控制。真实业务可能允许周期性重查状态,此时应把观测时间、调用间隔或重试次数纳入策略,不能机械地禁止所有重复查询。

成功轨迹应当能够还原

正常路径先取得结算服务的固定快照:版本为第二版,九十五分位延迟为一千八百毫秒,连接等待数为六十四,CPU 使用率为百分之三十五。这里的数值是人为构造的案例输入,用来制造一个可讨论的诊断条件,不来自线上监控。

第二步返回配置变更:连接池上限由四十变为八。第三步检索与连接池等待相关的文档。最终回答将“配置变更”和“等待增加”表述为需要进一步核对的关联,而不是充分的因果证明。服务可能还受到流量变化、下游变慢或其他未采集因素影响。

读者应能从回答反向找到引用,再从引用找到工具观察。若只能看到一句“建议回滚”,却不知道使用了什么证据,就很难判断建议是否可靠。因此,回答与轨迹需要共同构成结果,而不是将轨迹视为开发阶段可以丢弃的调试日志。

执行耗时也由程序实际记录,但这里测到的是本地字典读取、检索和校验开销。不同机器上结果会变化,且不包含模型推理与网络耗时。文章只使用轨迹验证流程,不把毫秒数包装成 Agent 性能结论。

四种必须显式处理的停止情况

未知工具意味着决策与应用提供的能力不一致。系统应返回稳定的错误标识,并避免把工具名拼成系统命令执行。本例通过固定映射选择工具,没有动态执行模型生成的 Python 或 Shell。真实系统还需要确认工具集合与模型所见描述来自同一版本。

无效参数与工具故障是两类问题。前者例如服务名是数字,属于调用契约不满足;后者例如状态查询超时,属于执行过程失败。将它们都转成“查询结果为空”会丢失信息,后续决策也可能误以为服务没有异常。示例分别输出 INVALID_ARGUMENTSTOOL_TIMEOUT

重复动作往往意味着没有取得新信息,或者决策无法从失败中收敛。运行 python demo.py repeat 会看到 REPEATED_ACTION,轨迹里只保留一次真实调用。第二次相同动作在进入工具前被阻止,所以不会产生重复副作用。

达到步数预算时,系统输出 STEP_BUDGET。本例把每个决策都计入步数,包括最终回答,因此三次工具调用加一次回答需要四个决策额度。只统计工具数量会造成另一种定义,也可以采用,但应在代码、配置与监控中保持一致。

预算结束不能被当成任务完成。系统应说明已经取得什么、尚缺什么以及为什么停止;生产环境可以据此转交人工或进入新的任务,而不是让模型在最后一轮编造一个完整答案。停止原因首先服务于调用方的控制逻辑,其次才是面向人的提示。

状态应该保存到什么程度

最小循环把状态保存在内存中,适合教学与短任务。但执行时间变长后,应用进程可能重启,用户也可能关闭页面。此时需要明确哪些状态值得持久化:任务输入、已完成步骤、工具结果引用、剩余预算和当前阶段,通常比一整段未经整理的聊天记录更有用。

持久化不意味着把所有外部输出原样保存。原始日志可能含有个人信息、密钥或大体积内容。应该根据故障诊断需要保存可访问的证据位置与必要摘要,并在读取时重新验证权限。第五篇将进一步区分任务检查点与业务副作用,说明保存了状态为什么仍然可能重复执行。

状态也要有生命周期。一个已经结束的事故任务,不应该因为用户提出新的问题就自动继承全部旧判断。复用哪些信息、什么时候刷新服务状态,需要由会话与任务边界决定。否则系统会在形式上保持“记忆”,却在业务上持续使用过期事实。

用 Java 后端经验理解这些边界

可以把模型看成一个提出命令的外部调用方,把工具注册表看成允许访问的应用服务集合。命令到达后,仍要经历参数校验、身份检查、领域逻辑和错误映射。模型输出符合结构,只相当于请求格式正确,不能替代业务授权。

循环状态可以对应一个任务实体:拥有任务编号、阶段、版本和结果引用。工具调用记录类似可审计的执行日志;最终回答类似对执行结果的视图。保持这些概念分离,有助于避免把会话消息数组同时当成任务数据库、权限凭据和最终业务事实。

如果已有稳定的 Java 服务,工具层可以调用现有服务接口,继续复用它们的事务与鉴权。没有必要为了加入模型而把订单、库存或生产运维逻辑迁入提示词。模型擅长提出候选动作,业务系统负责确保这些动作只能在合法范围内发生。

如何验证自己理解了执行闭环

修改正常样例最后一步的引用,将其替换成一个不存在的文档编号,程序应返回 UNSUPPORTED_ANSWER。把工具名改成重启服务的命令,应得到 UNKNOWN_TOOL。把最大步数设为一,应在第一条观察之后停止。这些实验比仅看一次正常回答更能说明控制边界。

再做一个设计练习:如果状态查询第一次超时,第二次成功,是否应允许重试?答案取决于工具是否只读、是否存在截止时间,以及重试会不会挤占剩余预算。本篇选择遇错停止以保持机制清楚,第五篇再把可恢复失败与持续执行分开处理。

这一轮最小实现的验收重点是:工具是否真实执行、引用能否回溯、错误能否被调用方识别、循环是否有界。模型接入后还必须验证它能否选择正确工具、能否利用观察修正方向,以及面对不同问法时表现是否稳定。这些问题将由第六篇的评测框架承接。

还可以检查调用方如何消费结果。接口收到停止原因后,应先判断任务状态,再决定展示回答、要求补充材料还是转交人工。不要仅以回答字段非空判断成功,否则一次错误解释也可能被误计为完成。把这个判断写进调用方测试,才能让执行器的约束延续到产品行为。

参考资料

AI Agent 工程实践:从原理到可靠运行

  1. AI Agent 是如何工作的:从一次模型调用到任务执行闭环 · 当前文章
  2. 让 Agent 真正执行任务:Tool Calling 与 MCP 实战
  3. 从知识库问答到 Agentic RAG:让 Agent 找到可靠答案
  4. Agent 的上下文与记忆:如何记住有用信息
  5. 复杂任务如何编排:工作流、单 Agent 与多 Agent 的取舍
  6. Agent 从 Demo 到上线:评测、可观测性与安全边界

按时间浏览