Responses API 会把消息、函数调用和函数结果表达为不同 Item。协议结构更清楚,但审计不会自动完成:业务仍需回答哪位调用者发起任务、模型提出了哪个函数、控制层是否批准、工具实际执行了什么,以及哪个结果被传回后续响应。若只保存最终文本,故障发生时无法还原工具链;若完整记录提示词和参数,又可能复制敏感数据。本文设计一条以 request_idresponse.idcall_id 为骨架的最小审计链。

四种标识各自解决什么

标识 生成方 用途
request_id 业务应用 关联一次用户操作和内部日志
response.id OpenAI Responses API 标识一次模型响应
Item id Responses API 标识某个输出 Item
call_id 函数调用 Item 关联函数调用与 function_call_output

call_id 不是用户身份,也不替代业务授权记录。应用先生成 request_id,再把收到的 API 与工具标识挂到同一业务任务上。

OpenAI 当前迁移文档确认 Responses 使用类型化 output Items,函数调用和结果通过 call_id 关联;具体结构参见 Migrate to the Responses API

建立三段式事件链

1. 模型请求事件

发送请求前记录:

request_id
actor_id_hash
project_id
operation
model_requested
prompt_version
toolset_version
started_at

响应到达后追加:

response_id
model_returned
response_status
output_item_types
usage_fields
completed_at

actor_id_hash 使用组织认可的稳定匿名标识,不发送或记录用户邮箱等直接身份信息。

2. 工具决策与执行事件

遍历 response.output,遇到 function_call 时记录:

request_id
response_id
item_id
call_id
tool_name
argument_schema_version
argument_digest
authorization_decision
approval_id

argument_digest 用于判断两次调用参数是否一致,不能代替实际参数审查。工具控制层仍需解析参数、执行 Schema 校验、检查调用者权限和资源范围。

执行后记录:

execution_id
started_at
completed_at
result_status
error_category
side_effect_reference
output_digest

外部写入使用幂等标识,并在 side_effect_reference 保存业务资源 ID,而不是完整写入内容。

3. 工具结果回传事件

构造 function_call_output 时记录它使用的同一个 call_id、关联 execution_id 和下一次业务 request_id。不要生成新的 call_id 代替模型返回值。

OpenAI Function calling guide 给出了函数调用循环。应用需要在每轮继续维护自己的业务关联标识。

一个最小处理骨架

const response = await client.responses.create(request);

for (const item of response.output) {
  if (item.type !== "function_call") continue;

  const auditContext = {
    requestId,
    responseId: response.id,
    itemId: item.id,
    callId: item.call_id,
    toolName: item.name
  };

  const args = validateArguments(item.name, item.arguments);
  await authorizeTool(actor, item.name, args);
  const result = await executeTool(item.name, args, auditContext);

  await client.responses.create({
    model,
    previous_response_id: response.id,
    input: [{
      type: "function_call_output",
      call_id: item.call_id,
      output: JSON.stringify(result)
    }]
  });
}

示例省略了并行调用、错误、重试和多轮循环,不能直接作为完整生产实现。关键点是审计上下文在工具执行前建立,并沿相同 call_id 传到结果回传。

并行调用不能只取第一个 Item

一次响应可能包含多个函数调用。不能用 find() 取第一个后忽略其余 Items。为每个 function_call 创建独立执行记录,再按应用策略并行或串行处理。

若并行工具有相互依赖,控制层必须明确顺序。模型返回多个调用不等于它们可以安全并行执行。

参数日志采用分级策略

默认记录:

工具名
Schema 版本
字段名列表
参数大小
参数摘要或哈希
权限结果
错误类别

默认不记录:

完整提示词与用户输入
文件正文和数据库结果
访问令牌与 Cookie
个人信息和客户标识
工具返回的完整敏感内容

确需保留原始参数进行安全调查时,使用隔离存储、严格访问控制、明确用途和删除期限。普通应用日志只保存指向受控证据的标识。

记录 requested 与 returned model

请求模型可能是可变别名,代理也可能执行路由。保存请求值和 Responses 返回的实际模型值。若中间层替换模型,额外记录 effective_model 与策略标识,不能让审计者从名称猜测。

同样,保存所用端点与 store 设置。状态管理方式影响调查时可从服务端取得什么,不能在事故后才推断。

错误处理保持 call_id 语义

工具执行失败时,不要悄悄丢弃调用。记录稳定错误类别和副作用状态,再决定是否把错误结果作为工具输出交回模型、请求人工处理或结束任务。

超时尤其需要区分:

确认未执行
确认执行失败
执行成功但响应丢失
状态未知

状态未知时先用业务幂等标识查询,不能直接重复写入。模型无法仅凭超时文本判断外部副作用。

用完整性检查发现断链

定期检查:

缺少工具结果不一定是系统错误,可能是调用被拒绝或任务中断;审计链必须记录对应原因。

结论与限制

Responses 的类型化 Items 和 call_id 提供了关联函数调用与结果的协议键,但业务审计仍需应用自己实现。以 request_id 连接业务身份,以 response.id 和 Item ID 定位模型输出,以 call_id 连接工具执行和回传,才能还原一次 Agent 任务。

本文依据 2026 年 7 月 27 日访问的 OpenAI 官方迁移与函数调用文档,只覆盖函数工具的通用审计结构。内置工具、远程 MCP、状态存储和数据保留有各自字段与政策,实施前需要按当前官方文档和组织要求单独核对。