智能体任务完成,要由业务系统确认
文章指出,智能体声称完成不代表工单、邮件或部署已真正落地,误判可能造成重复操作和审计问题。它主张由业务记录系统提供完成证据,模型负责提议行动、调用工具和解释结果。
AI 深度解读
Agent 说「完成了」,工单却没创建,邮件还在发件箱,部署也没通过健康检查。原文讨论的就是这类落差:模型生成了成功的叙述,应用却没有拿到成功的证据。作者的结论很明确:是否完成,必须由承载业务结果的系统确认。
这篇没有提供模型跑分或团队实测数据,更像一份生产环境设计清单。它值得读的地方,是把工具调用、权限、状态、重试和结果核验串到一起:模型负责理解与规划,应用负责决定能不能做,以及到底做成了没有。
工具调用成功,为什么还不能直接说完成
作者举了一个维修场景:用户要求检查订单 A20261024 是否在保修期内,如果符合条件,就创建维修工单。
模型适合从自然语言里提取订单号、识别故障、选择查询工具。但从「决定创建工单」到「工单确实存在」,中间还隔着身份校验、权限判断、实际写入和结果读取。
合理的流程是:模型提出工具调用,应用检查身份、参数与业务规则,工具执行操作,应用读取结果,最后让模型解释已经核实的状态。
模型提出的动作是意图,业务系统返回的状态才是证据。
假设工单 API 返回 201 Created,并给出编号 CS-48271,这是有用的成功证据。但如果请求发出去后超时,结果就不确定了。服务端可能已经创建工单,只是响应没送回来。此时直接重试,可能重复建单;直接宣告失败,也可能掩盖已经发生的操作。
作者建议用幂等键或业务标识查询状态。幂等键可以理解为一次业务操作的固定编号,让重试请求有机会被识别为同一件事。不过,原文没有展开服务端如何落实去重;仅仅生成一个字符串,并不能自动防止重复写入。
核验的力度还要匹配最终声明。邮件服务返回 message ID,能支持服务已受理的判断,却不能直接证明收件人已经收到。构建成功也不能证明部署可用,还要看部署状态与健康检查。
工具越通用,应用越难守住权限边界
原文反对一上来就向业务 Agent 暴露 execute_sql、run_shell、call_api 这类通用工具。它们能让演示快速跑起来,也让权限范围变得难以判断。
相比之下,get_order_status(order_id) 的用途明确,参数容易检查,返回结构也容易固定。写操作同样应该表达业务意图。例如 create_repair_ticket 接收 order_id、issue_code 和 summary;示例对应订单 A20261024、故障码 DEVICE_NO_POWER,摘要描述设备完成基础供电检查后仍无法启动。
接到调用后,应用仍要核实:订单是否属于当前用户、产品是否在保修期内、是否已有同类工单,以及调用者是否有创建权限。
Function calling 只是把模型的意图组织成结构化参数,并没有完成授权。
这也解释了为什么外部内容不能获得指挥权。网页、邮件、Issue 描述和上传文件里,都可能出现要求忽略规则或泄露秘密的文字。应用需要把这些内容与系统规则、用户目标分开,避免将工具凭证放入模型上下文,并让敏感操作经过独立校验。
作者没有声称一句提示词能解决提示注入。他依赖的是隔离、最小权限、明确的工具结构和参数验证,同时记录外部内容来源,便于追查异常运行。
长任务要记住已经发生的事,还要允许结果未知
聊天历史无法可靠承担工作流状态。任务跨越多个服务,遇到限流、重启或人工审核后,应用不能靠模型重读对话,猜测哪些副作用已经发生。
下面这个状态对象解决的是恢复执行时的定位问题:
{
"run_id": "run_7f2a",
"goal": "open_repair_ticket",
"order_id": "A20261024",
"step": "ticket_creation",
"completed_steps": [
"ownership_checked",
"warranty_checked",
"troubleshooting_searched"
],
"idempotency_key": "repair-A20261024-device-no-power",
"status": "in_progress"
}
要关注 completed_steps 与 idempotency_key:前者记录已完成的检查,后者帮助识别同一次业务操作。作者认为,数据库记录、事件日志和一个小状态机,通常就能带来价值,不一定需要复杂的自主 Agent 框架。
结果核验也应该由代码表达,而不是留给模型临场判断。下面的函数读取工单后,才决定流程是否完成:
def finish_ticket_workflow(ticket_id, ticket_client):
ticket = ticket_client.get(ticket_id)
if not ticket:
return {"status": "unknown", "reason": "ticket_not_found"}
if ticket["state"] not in {"open", "queued"}:
return {
"status": "not_completed",
"reason": f"unexpected_state:{ticket['state']}"
}
return {
"status": "completed",
"ticket_id": ticket["id"],
"state": ticket["state"]
}
关键是 unknown 这个分支:没有查到工单,就保留不确定性。模型可以把结果说得易懂,但不能自行把 unknown 改写成 completed。
失败处理也要分类。临时读取失败可以考虑指数退避;参数校验失败需要补充信息;权限错误应立即停止;写入结果不明确时先查状态;影响较大且状态不确定的操作应暂停审核。
此外,还要限制执行时间、工具调用次数、重试次数和花费,识别参数完全相同却没有新增证据的重复调用,并把停止原因写进状态。
换模型之前,先确认工作流还能稳定完成
作者建议将模型配置从业务逻辑中抽出。示例使用 OpenAI 客户端,通过 os.environ 读取 MODEL_API_KEY 和 MODEL_BASE_URL,分别传入 api_key、base_url;再调用 client.chat.completions.create,用 AGENT_MODEL 设置 model,并传入 messages 与 tools。
兼容 API 能降低接入成本,却不能保证更换模型后行为一致。
评估需要使用真实工作流轨迹,观察任务完成情况、无效工具参数、不必要的调用、延迟、成本和人工接管率。作者建议先跑影子模式,也就是观察候选模型的决策但不允许真实写入,再逐步增加低风险流量,并保留回滚能力。
最终回复则应该像一张回执:执行了什么操作、核实到哪个标识、当前处于什么状态、还有什么需要处理。对用户而言,这些信息比一段流畅的成功宣言更有用。
我的判断
我赞同这套分工。尤其是「写入超时先查询」和「未知状态不能包装成完成」,它们把传统业务系统里处理重试与一致性的经验,放回了 Agent 应用。
但标题里的「数据库确认」应理解为业务权威来源确认。工单系统、邮件服务、测试运行器、部署平台,各自只能证明其覆盖范围内的事实。读到一条数据库记录,也未必代表后续通知、异步处理或外部交付已经完成。
原文的核验函数同样带着业务前提:open、queued 被视为创建完成,只适用于目标是「创建维修请求」的流程,不能据此宣称维修已经完成。实际系统还要处理查询异常和状态可见性延迟,示例并未展开。
通用工具也并非一概不适用。业务客服 Agent 适合窄工具;编程 Agent 往往需要 Shell、文件系统和测试命令,此时应约束运行环境、凭证与执行范围。原文没有提供不同工具设计的对照数据,因此它是一套有工程依据的经验建议,不能当作已经量化验证的收益结论。
能拿来做什么
- 给前端任务进度区增加「执行中」「结果待确认」「已完成」等业务状态,由后端核验结果驱动展示;完成时显示工单编号、部署标识等可追查信息。
- 在 Node 服务中为会产生副作用的工具配套查询能力,把写入超时后的状态查询、幂等处理和停止原因放进编排代码。
- 调整 AI 编程任务的完成标准:修复缺陷要报告实际运行的相关测试及结果,部署任务要读取部署状态与健康检查,尚未验证的部分直接说明。
- 更换模型时复用真实任务轨迹,先观察工具选择和参数质量,再逐步开放低风险写入,避免只凭聊天效果决定切换。