AIGC标识 Responses API 介绍与 deepseek-harness 应用分析报告

Responses API 介绍与 deepseek-harness 应用分析报告

分析对象:@deepseek-harness/(DeepSeek Harness,dsh
报告内容:Responses API 概述、输入/输出消息示例、"卡片"与结构化输入支持、presentCall/presentResult 声明示例、在 harness 中的应用。


目录

  1. 什么是 Responses API
  2. 与 Chat Completions 的对比
  3. 输入(请求)消息示例
  4. 输出(响应)消息示例
  5. 流式 SSE 事件示例
  6. "卡片"消息与结构化用户输入
  7. presentCall / presentResult 声明示例
  8. 在 deepseek-harness 中的应用
  9. 小结

1. 什么是 Responses API

OpenAI Responses APIPOST /v1/responses)是 OpenAI 于 2025 年推出的统一模型接口,用一个端点整合 Chat Completions、工具调用、推理(reasoning)、结构化输出、文件搜索、computer use 等能力。

它与旧的 Chat Completions 最本质的差别在数据结构上:

维度 说明
请求 input[](消息条目数组)+ 独立的 instructions(系统指令),取代 messages[] + system 角色
响应 output[](条目数组:message / function_call / reasoning…)取代 choices[].message
工具调用 是 output 里的一等 function_call 条目,而非 assistant 消息内嵌的 tool_calls
推理 用原生 reasoning: { effort } 表达
状态/缓存 支持 previous_response_idstoreprompt_cache_key

2. 与 Chat Completions 的对比

维度 Chat Completions Responses API
端点 /chat/completions /responses
请求结构 messages[]role 区分 system/user/assistant/tool) input[] + 独立 instructions
输出结构 choices[].message output[] 条目数组
工具调用 assistant 消息内嵌 tool_calls output 中的 function_call 条目
推理 各家私有字段 原生 reasoning: { effort }
结构化输出 response_format: json_schema text.format: { type: "json_schema" }
状态/缓存 previous_response_id / store / prompt_cache_key

3. 输入(请求)消息示例

3.1 最简文本请求

// POST /v1/responses
{
  "model": "gpt-4o",
  "instructions": "You are a helpful assistant.",   // 系统指令(独立于 input)
  "input": [
    { "role": "user", "content": [ { "type": "input_text", "text": "2 + 2 等于几?" } ] }
  ],
  "temperature": 1.0,
  "max_output_tokens": 1024
}

3.2 带工具 + 推理 + 结构化输出

{
  "model": "gpt-4o",
  "instructions": "查询天气并以 JSON 回答。",
  "input": [
    { "role": "user", "content": [ { "type": "input_text", "text": "北京今天天气如何?" } ] }
  ],
  // —— 工具(函数调用,JSON Schema 定义参数)——
  "tools": [
    {
      "type": "function",
      "name": "get_weather",
      "description": "Get current weather for a city",
      "parameters": {
        "type": "object",
        "properties": { "city": { "type": "string" } },
        "required": ["city"]
      }
    }
  ],
  // —— 推理强度 ——
  "reasoning": { "effort": "medium" },
  // —— 结构化输出(强制 JSON Schema 约束)——
  "text": {
    "format": {
      "type": "json_schema",
      "name": "weather_report",
      "strict": true,
      "schema": {
        "type": "object",
        "properties": {
          "city": { "type": "string" },
          "temp_c": { "type": "number" }
        },
        "required": ["city", "temp_c"],
        "additionalProperties": false
      }
    }
  }
}

3.3 多轮会话(带工具调用结果的 input 条目)

第二轮请求把上一轮的 function_call 和工具返回的 function_call_output 作为 input 条目回填——这是 Responses API 与 Chat Completions 最大的结构差异之一:

{
  "model": "gpt-4o",
  "instructions": "You are a helpful assistant.",
  "previous_response_id": "resp_9xK...",   // 有状态会话:引用上一轮响应
  "input": [
    { "role": "user", "content": [ { "type": "input_text", "text": "北京今天天气如何?" } ] },
    { "role": "assistant", "content": [ { "type": "output_text", "text": "我来查一下。" } ] },
    // 模型请求调用工具 —— 一个一等 output 条目,而不是消息内嵌字段
    {
      "type": "function_call",
      "name": "get_weather",
      "arguments": "{\"city\":\"北京\"}",   // 原始 JSON 字符串
      "call_id": "call_abc123"
    },
    // 工具执行结果回填 —— 通过 call_id 与上面的调用关联
    {
      "type": "function_call_output",
      "call_id": "call_abc123",
      "output": "{\"temp_c\": 22, \"condition\": \"晴\"}"
    }
  ]
}

4. 输出(响应)消息示例

4.1 非流式完整响应对象

字段形状以 subagent-codex/tests/responses-fixture.tsresponseObject 为准。

{
  "id": "resp_fixture",
  "object": "response",
  "created_at": 1,
  "status": "completed",
  "model": "gpt-4o",
  "error": null,
  "incomplete_details": null,
  "instructions": null,
  "output": [                                   // 输出条目数组
    {
      "id": "msg_fixture",
      "type": "message",                    // 条目类型:message / function_call / reasoning ...
      "status": "completed",
      "role": "assistant",
      "content": [ { "type": "output_text", "text": "2 + 2 = 4", "annotations": [], "logprobs": [] } ]
    }
  ],
  "usage": {
    "input_tokens": 10,
    "input_tokens_details": { "cached_tokens": 0 },
    "output_tokens": 1,
    "output_tokens_details": { "reasoning_tokens": 0 },
    "total_tokens": 11
  },
  "reasoning": { "effort": null, "summary": null },
  "text": { "format": { "type": "text" }, "verbosity": "medium" },
  "tool_choice": "auto",
  "tools": [],
  "parallel_tool_calls": true,
  "temperature": null,
  "max_output_tokens": null,
  "previous_response_id": null,
  "prompt_cache_key": null,
  "prompt_cache_retention": null,
  "store": false,
  "service_tier": "default",
  "background": false,
  "safety_identifier": null,
  "truncation": "disabled",
  "top_logprobs": 0,
  "top_p": null,
  "user": null,
  "metadata": {}
}

4.2 函数调用条目(output 中的 function_call

{
  "type": "function_call",
  "id": "fc_fixture",
  "call_id": "call_fixture",      // 与 function_call_output 关联
  "name": "get_weather",
  "arguments": "{\"city\":\"北京\"}",   // 原始 JSON 字符串
  "status": "completed"
}

5. 流式 SSE 事件示例

流式响应的 content-type: text/event-stream,事件序列(取自 completeResponsesEvents):

response.created → response.output_item.added → response.content_part.added
  → response.output_text.delta → response.output_text.done
  → response.content_part.done → response.output_item.done → response.completed
// 每条 data: 一行 JSON
data: {"type":"response.created","response":{ ...status: in_progress, output: [] }}
data: {"type":"response.output_item.added","output_index":0,"item":{ id, type: message, status: in_progress, content: [] }}
data: {"type":"response.content_part.added","item_id":"msg","output_index":0,"content_index":0,"part":{ type: output_text, text: '' }}
data: {"type":"response.output_text.delta","item_id":"msg","delta":"2 + 2 "}
data: {"type":"response.output_text.delta","item_id":"msg","delta":"= 4"}
data: {"type":"response.output_text.done","text":"2 + 2 = 4"}
data: {"type":"response.content_part.done","part":{ ...output_text }}
data: {"type":"response.output_item.done","item":{ ...message completed }}
data: {"type":"response.completed","response":{ ...完整对象 }}
data: [DONE]

函数调用参数的增量事件用 response.function_call_arguments.delta / .done 代替上面的 output_text 系列。


6. "卡片"消息与结构化用户输入

结论一:Responses API 本身没有"卡片"(card)这种消息类型。
它的输入条目 content 只有 input_text / input_image / input_file 等,输出条目是 message / function_call / reasoning 等。没有"表单卡片""widget"这类供用户填写的结构化控件。

Responses API 的结构化输入机制是下面三件事,而不是"卡片":

机制 说明 示例字段
函数调用 模型通过 function_call 条目向外部要结构化参数,参数由 JSON Schema 约束 tools[]function_call / function_call_output
结构化输出 强制模型输出符合 JSON Schema(strict 模式保证 100% 合法) text.format = { type: "json_schema", strict: true }
文本输入条目 用户结构化的最小载体仍是文本块 content: [{ type: "input_text" }]

结论二:deepseek-harness 有"卡片"概念,但它是工具调用的 UI 展示层,不是用户填表用的结构化输入控件。
位于 packages/core/tools/src/presentation.tsprovider-neutral 工具渲染意图词汇表,由工具通过 presentCall / presentResult 声明,UI 桥接层据此渲染。它与 Responses API 的 output 条目是两层:Responses 条目是模型→服务端的协议,卡片是工具→前端 UI的展示投影。

harness 中的卡片类型(presentation.ts)

卡片 说明
card: 'generic' 默认卡片:标题 + 类别图标 + 原始输入 + 跟随文件位置
card: 'terminal' shell 命令卡片:cwd 头部 + 命令标题 + 输出 + 退出码
card: 'diff' 文件改动内联 diff 卡片(create/edit/overwrite)
card: 'search' 搜索卡片:shape: matches(按文件分组)/ shape: paths
card: 'read' 文件读取卡片:行号 + 语法高亮 + totalLines
card: 'web' Web 检索卡片:kind: search(引用源)/ kind: fetch(URL+状态码)

harness 中的"结构化输入"如何实现

harness 用带 JSON Schema 的工具实现结构化,而非"卡片"。最典型的是子代理的结构化输出工具(subagent-in-process-driver/src/structured.ts):

// 工具名与提示词指令
export const STRUCTURED_OUTPUT_TOOL = 'structured_output'
// "你必须调用 structured_output 工具、参数严格匹配 schema,纯文本答案不算结果"
{
  "type": "function",
  "name": "structured_output",
  "parameters": { "... 用户的 JSON Schema ..." }   // 结构约束在这里
}

也就是说:"需要用户结构化输入"在 harness 里 = 一个 function 工具的 JSON Schema 参数,它在 Responses API 侧对应 tools[] + function_call/function_call_output 条目,在前端侧才可能被渲染成一张"卡片/表单"UI——而那个 UI 形态不属于 Responses API 协议本身。


7. presentCall / presentResult 声明示例

工具在 defineTool({...}) 里声明两个纯函数展示器——它们不依赖执行、不读环境,UI 可在实时流和回放两条路径上调用:

// ToolDefinition 中的两个可选字段(packages/core/tools/src/schema.ts)
presentCall?(args: InferArgs<S>): ToolCallView | undefined
    // 待执行态:调用已发出、结果未回 —— 返回通用/终端/diff 卡片
presentResult?(args: InferArgs<S>, result: ToolResult): ToolResultView | undefined
    // 完成态:execute 已返回 —— 返回结果卡片;返回 undefined 时降级为通用卡片

示例 1 · web_search:generic 调用卡片 → web 结果卡片

来源:packages/web/tool-web/src/search.ts。调用态是带 kind: 'search' 的通用卡片;结果态是携带结构化引用源的 card: 'web' 卡片,其 sourcesoutput.presentationMeta 投影而来(有损渲染文本无法无损携带)。

export function presentSearchCall(args: { query: string }): GenericCallView {
  return { card: 'generic', title: args.query, kind: 'search', rawInput: args.query }
}

export function presentSearchResult(args: { query: string }, result: ToolResult): WebSearchResultView | undefined {
  if (result.isError) return undefined            // 失败 → 降级为通用卡片
  const meta = searchMetaFromResult(result.meta)   // 从 meta 窄化出结构化 sources
  if (meta === undefined) return undefined
  return {
    card: 'web',
    kind: 'search',
    title: args.query,
    sources: meta.sources,        // WebSource[]:url/title/snippet/publishedAt
    truncated: meta.truncated,
    ...meta.answer !== undefined ? { answer: meta.answer } : {},
  }
}

// defineTool 里挂载:
presentCall: presentSearchCall,
presentResult: (args, result) => presentSearchResult(args, result),

示例 2 · bash:terminal 调用卡片 → terminal 结果卡片

来源:packages/shell/tool-bash/src/index.ts。前台命令渲染为终端卡片(后台启动降级为通用卡片);结果态把退出标记拆成退出码胶囊,输出体单独成 output

function presentBashCall(args: BashCallArgs): GenericCallView | TerminalCallView {
  if (args.run_in_background === true) {
    return {                       // 后台:通用卡片
      card: 'generic',
      title: args.command,
      kind: 'execute',
      rawInput: args.command,
      content: [{ type: 'text', text: args.description }],
    }
  }
  return {                       // 前台:终端卡片
    card: 'terminal',
    title: args.command,
    description: args.description,
    ...args.workdir !== undefined ? { cwd: args.workdir } : {},
  }
}

function presentBashResult(args: unknown, result: ToolResult): ToolResultView | undefined {
  const block = result.content.length === 1 ? result.content[0] : undefined
  if (block === undefined || block.type !== 'text') return undefined
  const raw = block.text
  // 后台确认/错误:无退出码,降级为 fenced 通用卡片
  if (isBackground || result.isError) {
    return { card: 'generic', content: [{ type: 'text', text: `\`\`\`console\n${raw}\n\`\`\`` }] }
  }
  const { body, ...exit } = parseExitStatus(raw)   // 退出标记 → 退出码胶囊,离开输出体
  return { card: 'terminal', output: body, ...exit }
}

示例 3 · write:diff 调用卡片 → diff 结果卡片

来源:packages/fs/tool-fs/src/write.ts。调用态只有参数,新建文件 oldText: null;结果态重复 diff(因为完成态视图会替换待执行态视图),优先用 result.meta 里的应用后元数据。

// 待执行态:无旧内容,oldText 为 null(新建/覆盖)
presentCall(args): DiffCallView {
  return {
    card: 'diff',
    title: `Write ${args.file_path}`,
    diffs: [{ path: args.file_path, oldText: null, newText: args.content }],
    locations: [{ path: args.file_path }],
  }
},

// 完成态:结果视图替换待执行视图,因此重复 diff
presentResult(args, result: ToolResult): DiffResultView | undefined {
  if (result.isError) return undefined
  const diffs = diffsFromMeta(result.meta)
    ?? [{ path: args.file_path, oldText: null, newText: args.content }]
  return { card: 'diff', title: `Write ${args.file_path}`, diffs }
},

声明要点

  • 纯函数、无 I/O——展示器只吃 args(已校验的 typed 参数)和 result,UI 可在实时流与回放两条路径复用。
  • 返回 undefined 即降级——用通用卡片渲染,常用于 result.isErrormeta 畸形(回放安全,不抛异常)。
  • 完成态替换待执行态——所以 presentResult 常"重复"调用态内容(diff 卡片尤其如此),否则原始结果文本会覆盖卡片。
  • 结构化字段走 presentationMeta——无法从有损渲染文本重建的字段(搜索源、diff、read 行号)由 output.presentationMeta 随会话日志持久化,presentResult 再窄化回来。
  • 卡片是 UI 展示层,不是 Responses API 协议——ToolCallView/ToolResultView 与 Responses 的 output 条目分属两个层次,前者由 UI 桥接层消费。

8. 在 deepseek-harness 中的应用

deepseek-harnessdsh)是 DeepSeek AI 的 agent 框架,LLM 层刻意做成供应商中立:核心 dsh-llm 只定义统一词汇(GenerateOptions / StreamChunk / ContentBlock),各 adapter 翻译厂商协议。

Responses API 在三处出现:

位置 角色
packages/llm/llm-pi-ai 主入口:集成 @earendil-works/pi-ai,协议表 PROTOCOLS'openai-responses' → openAIResponsesApi;路由的 api 字段可指向 Responses;OpenAI 目录模型描述符自带 Responses 协议;replay state 恢复 response id。
packages/llm/llm-deepseek 对照:DeepSeek 官方直连走 Chat Completions(/chat/completions),不走 Responses。
packages/subagent/subagent-codex 测试侧:Codex 原生说 Responses API;responses-fixture.ts 复刻完整 SSE 事件与 response 对象,deepseek-responses-bridge.ts 做 Responses→DeepSeek Chat Completions 的桥接翻译。

9. 小结

  • Responses APIinput[] + instructions / output[] 取代 Chat Completions 的 messages[],工具调用是一等条目,原生支持推理、结构化输出、状态与缓存。
  • 输入示例:文本请求、带工具+推理+结构化输出、多轮含 function_call/function_call_output 条目。
  • 输出示例:完整 response 对象、function_call 条目、SSE 流式事件序列。
  • "卡片":Responses API 无原生卡片类型;结构化输入靠函数调用 + json_schema。deepseek-harness 的"卡片"是工具展示层(generic/terminal/diff/search/read/web),结构化输入靠带 JSON Schema 的工具(如 structured_output)。
  • presentCall / presentResult:两个纯函数展示器,声明工具在待执行态/完成态的 UI 渲染意图,返回 undefined 即降级为通用卡片。
  • 在 harness 中:Responses 作为 llm-pi-ai 的三种可配置线路协议之一接入;官方 DeepSeek 直连仍走 Chat Completions。

本报告示例的 wire 格式以 deepseek-harness 仓库 packages/subagent/subagent-codex/tests/responses-fixture.ts
packages/llm/llm-pi-ai/src/*packages/core/tools/src/presentation.ts 等源码为依据整理。

posted on 2026-08-20 09:20  Gary Zhang  阅读(52)  评论(0)    收藏  举报

导航