假设我们请求 LLM Agent 创建一个 Issue。回答中写着“已创建 Issue”,但项目中并没有新的 Issue。再次询问时,Agent 解释称其已使用工具。
在更换模型或添加“绝对不要撒谎”这样的 Prompt 之前,我们需要先确认一件事:执行过程实际上进行到了哪个阶段?
仅凭 Agent 的文本,我们无法得知答案。必须区分以下情况:是否生成了工具调用请求、执行器是否处理了该请求、外部服务中是否留下了结果。
1. “调用了”这一表述混杂了不同的事件
使用工具的 Agent 的工作大致经过以下过程。
- 模型选择要使用的工具和参数。
- 运行时解析调用请求并验证权限。
- 执行器实际调用函数或外部 API。
- 工具结果传递给模型。
- 判断是否达到用户请求的状态。
每个步骤都可能单独失败。
即使模型正确生成了调用请求,执行器也可能抛出异常。即使 API 已受理请求,异步任务可能仍在执行中。即使执行已结束,也可能在错误的项目中创建了 Issue。
因此,在调查故障时,最好不要用“工具调用成功”这一单一状态来覆盖所有步骤。
2. 区分普通文本和调用事件
以 OpenAI Responses API 中的自定义函数调用为例。以下是调用项主要字段的简化示例。
{
"type": "function_call",
"call_id": "call_42",
"name": "create_ticket",
"arguments": "{\"title\":\"로그인 오류 점검\"}"
}应用程序接收此项并执行相应函数,将结果关联到同一个 call_id。
{
"type": "function_call_output",
"call_id": "call_42",
"output": "{\"status\":\"created\",\"ticket_id\":\"T-731\"}"
}这里的 status 和 ticket_id 是示例应用程序定义的结果格式。它们并非由 OpenAI 保证 Issue 创建成功的标准字段。
这一结构与出现在普通回答中的以下句子有明显的不同。
我调用了 create_ticket 创建了 Issue。
这句话是模型生成的文本。即使代码块中写有工具名称或调用形式,如果执行器没有将其作为调用请求进行处理的实现,执行就不会发生。
自定义函数由应用程序执行,但由提供商运行的服务器端工具,其执行位置可能不同。因此,首先必须确认所使用的工具在哪里执行,以及会留下哪些事件。OpenAI 函数调用文档和 Claude 工具使用文档描述了这些执行边界。
3. 症状相同,原因不同
没有调用请求,只生成了完成句
如果模型输出中只有普通消息而没有调用事件,需确认模型是否在没有执行证据的情况下声称已完成。
可能的原因包括:该请求未暴露该工具、工具描述含糊、未选择调用等。首先必须比较实际请求中包含的工具定义与模型的原始输出。
但是,不能仅因为追踪界面中没有记录就确定未执行。还需检查采集缺失、采样情况以及独立的执行路径。
有调用请求,但执行器漏掉了
在这种情况下,需要查看应用程序的控制流,而不仅仅是模型。
- 是否只读取了响应中的文本而丢弃了调用项?
- 是否只处理了多个调用中的第一个?
- 工具名称与实际处理程序名称是否匹配?
- 执行循环是否在等待批准状态下结束?
流式传输也是检查对象。如果将参数片段处理为完成的请求,或漏掉调用完成事件,可能会导致执行遗漏。response.function_call_arguments.done 是参数生成结束的信号,而不是外部函数执行结束的信号。
执行失败被转换为成功并传递
异常处理代码有时也会抹去失败。
try:
result = execute_tool(arguments)
except Exception:
result = {"message": "처리했습니다"}这段代码甚至消除了调查原因的线索。传递给模型的结果中也应保留失败及其范围。
在 MCP 中,收到响应的事实与工具操作的成功也是区分开的。例如,以下响应表示工具错误。
{
"jsonrpc": "2.0",
"id": 17,
"result": {
"content": [
{
"type": "text",
"text": "프로젝트에 이슈를 생성할 권한이 없습니다."
}
],
"isError": true
}
}如果适配器仅传递 content 而丢弃错误标志,关键信息将会丢失。MCP 工具规范分别处理协议错误和工具执行错误。
将请求受理解释为工作完成
在部署、文件转换、电子邮件发送等具有多个阶段的工作中,尤其需要小心。
如果部署 API 返回了作业 ID,可能仅确认了“部署请求已受理”。即使邮件服务接受了消息,也不能确认其已到达收件人的收件箱。
必须为每个工具定义产品将哪个状态称为“完成”。
4. 区分 Prompt 和 Schema 解决的范围
“在收到执行结果之前,不要说已完成”这样的指示可能会有所帮助。但它无法恢复执行器的异常处理或中断的工作流。
配置也有其应用范围。
tool_choice: "required"要求工具调用。strict: true有助于生成符合支持 Schema 约束的参数。
两者都不能保证外部操作的成功。格式正确的项目 ID 是否实际存在、用户是否有权限在该项目中工作,也需要单独检查。
强制调用的配置应应用于必要步骤,并能在描述结果的步骤中终止。如果在所有回合中无条件强制调用,可能会导致不必要的重复。
5. 完成状态由执行记录生成
在实际操作中,独立于模型回答管理工作状态的方法很有用。例如,可以区分以下状态。
requested:收到了调用请求。waiting_for_approval:等待执行批准。running:开始执行。succeeded:工具返回了成功结果。verified:确认了定义的完成条件。failed:确认了失败。unknown:尚无法判断执行结果。
这些名称是设计示例。重要的是,不要让模型任意声明状态,而是由执行器和验证代码基于实际事件进行更新。
Issue 创建的完成条件可以定义如下。
- 创建 API 返回了 Issue ID。
- 通过该 ID 查询到的 Issue 存在于请求的项目中。
- 标题等必要字段与请求一致。
并非所有工具都需要额外查询。这取决于原始响应保证了什么,以及工作风险和成本有多大。如果服务在存储后立即查询时存在延迟,也需要考虑这一特性。
为了安全起见,诸如完成徽章之类的重要 UI 最好直接从这些状态渲染。模型应描述经过验证的结果,但不应成为决定完成与否的唯一主体。
6. 遇到超时需在重试前确认
假设外部 API 请求以超时结束。服务器可能已经创建了 Issue,只是响应丢失了。
如果将此状态立即视为失败并重新创建,就会产生重复 Issue。首先需要记录为 unknown,并需要通过查询或提供商的请求标识符来验证结果。
如果服务支持幂等性键,则在同一逻辑操作的多次尝试中使用相同的键。实际的保证范围和保留时间需遵循服务文档。Stripe 的幂等请求文档是一个具体的例子。
模型的 call_id 和业务操作的幂等性键也需要区分。如果模型再次请求相同的操作,可能会生成新的调用 ID,因此仅依靠调用 ID 来防止重复执行的设计可能不够充分。
7. 在回答评估中附加执行证据
在 Agent 测试中,必须同时验证回答文本和实际状态。
至少可以构造以下情况。
- 在未调用工具时,是否不声称已完成?
- 是否不将权限拒绝描述为成功?
- 工具错误是否原样传递给模型?
- 是否将异步任务标记为执行中?
- 是否在超时后未验证结果就直接重复执行?
- 是否将多个调用的结果正确关联到相应的请求?
- 是否不将错误目标上生成的结果确认为完成?
追踪日志中最好有能将模型调用、工具请求、执行开始与结束、错误、验证结果关联起来的标识符。可以利用如 OpenAI Agents SDK 中的 tracing 这样的功能来收集这些信息。
但是,不应无条件地在操作日志中留下密钥值或个人信息。应优先记录追踪问题所需的标识符和状态,并对参数和结果应用掩码和访问控制。
总结
当 Agent 说“已调用”时,请确认以下三点。
- 实际是否存在调用请求?
- 执行器记录了什么结果?
- 该结果是否满足用户请求的完成条件?
区分这三个问题,可以更准确地诊断模型错误的主张和运行时的执行失败。
值得信赖的 Agent 不仅仅由流畅的完成句构成。必须设计支撑这些句子的执行证据和验证路径。




