OpenAI协议01、OpenAI底层协议快速理解

前言

在进入正文之前,先交代一下这些文章的来龙去脉。

AgentForge 是一个面向 Java 开发者、从 LLM 最底层能力开始构建 的开源 Agent 框架。它不从高度封装的 Agent API 起步,而是先建立稳定、统一、可扩展的模型抽象,再逐层向上锻造 Tool、Memory、Middleware、Reasoning 与 Agent Runtime 等能力。

AgentForge = Agent + Forge:Agent 代表能理解目标、进行推理、调用工具并完成任务的智能体,Forge 则强调把原始智能持续加工、塑形、强化,最终锻造成真正可用的产品。

本系列《AgentForge 核心模块设计原理》沿着这条自底向上的路径,逐个模块拆解它的设计原理与实现细节。本文聚焦 OpenAI Chat Completions 的底层协议 (请求 / 响应 / 流式 SSE / Function Calling 的 wire 细节),对应模块 agentforge-model-openai。

bash 复制代码
git clone https://github.com/changluya/AgentForge.git
cd AgentForge
mvn clean install -DskipTests

如果这套「自底向上」的设计对你有帮助,欢迎到 GitHub 给 AgentForge 点一个 Star。


一、背景与问题引入

1.1、场景驱动:为什么第一版选 Chat Completions

在为 AgentForge 统一接入多家大模型时,我们遇到一个现实问题:OpenAI 官方一边推荐新的 Responses API ,一边又有大量 OpenAI-compatible 服务(自建网关、国产模型)只兼容 /chat/completions。

因此 AgentForge release_1.x 选择 Chat Completions 作为第一版统一接入协议:它仍是明确存在的 API,且兼容面最广。

1.2、问题引导:一次对话调用到底发了什么?

问题:当 Agent 调用一次 OpenAI 模型,请求体、响应体、流式 SSE 分别长什么样?工具调用(function calling)在 wire 上如何表达与回填?

本文按"请求 → 响应 → 流式 → 工具调用"的顺序把协议讲清。

1.3、协议定位

http 复制代码
POST {baseUrl}/chat/completions

默认:

text 复制代码
baseUrl = https://api.openai.com/v1
→ 实际地址 https://api.openai.com/v1/chat/completions

官方参考:


二、请求协议

2.1、Headers

http 复制代码
Content-Type: application/json
Accept: application/json
Authorization: Bearer ${apiKey}

重点 :鉴权用标准 Authorization: Bearer;企业网关可追加自定义 Header(trace / 租户 / 路由)。

2.2、请求体:messages(核心)

role 关键字段 含义
system content 系统提示
user content 或 content[] 用户输入(多模态时为数组)
assistant content / tool_calls[] 模型回复,可携带工具调用
tool tool_call_id + content 工具执行结果回填

纯文本示例:

json 复制代码
{
  "model": "your-model",
  "messages": [
    { "role": "system", "content": "You are a concise Java assistant." },
    { "role": "user", "content": "What is CAS?" }
  ]
}

2.3、请求体:采样与工具参数

字段 说明
temperature 采样温度
max_tokens 最大生成 token
top_p 核采样
stop 停止序列
tools[] 工具声明:{"type":"function","function":{name,description,parameters,strict}}
tool_choice "auto" / "none" / "required" / {"type":"function","function":{"name":X}}

2.4、完整非流式请求示例

json 复制代码
{
  "model": "your-model",
  "messages": [
    { "role": "system", "content": "You are a concise Java assistant." },
    { "role": "user", "content": "What is CAS?" }
  ],
  "temperature": 0.2,
  "max_tokens": 1024,
  "top_p": 0.9
}

三、响应协议

3.1、非流式 chat.completion

json 复制代码
{
  "id": "chatcmpl_xxx",
  "object": "chat.completion",
  "created": 1780000000,
  "model": "your-model",
  "choices": [
    {
      "index": 0,
      "message": { "role": "assistant", "content": "CAS means Compare-And-Swap." },
      "finish_reason": "stop"
    }
  ],
  "usage": { "prompt_tokens": 20, "completion_tokens": 10, "total_tokens": 30 }
}

注意 :choices 是数组,消费方通常只取 choices[0]。

3.2、finish_reason

值 含义
stop 正常结束
length 达到 max_tokens
tool_calls 需要执行工具
function_call 旧字段(deprecated)
content_filter 内容被过滤

四、流式协议(SSE)

请求时增加:

json 复制代码
{ "stream": true, "stream_options": { "include_usage": true } }

响应为 SSE:

text 复制代码
data: {"choices":[{"index":0,"delta":{"content":"Hel"},"finish_reason":null}]}

data: {"choices":[{"index":0,"delta":{"content":"lo"}}]}

data: {"choices":[],"usage":{"prompt_tokens":20,"completion_tokens":2,"total_tokens":22}}

data: [DONE]

重点 :stream_options.include_usage=true 时,[DONE] 之前会出现一个 choices=[] 且携带整体 usage 的额外 chunk;解析时应先读 usage,再判断 choices 是否为空。

4.1、工具调用的流式增量(按 index 聚合)

delta.tool_calls[] 是分片下发的,必须按 index 聚合:

text 复制代码
chunk: {"index":0,"id":"call_1","function":{"name":"getWeather","arguments":""}}
chunk: {"index":0,"function":{"arguments":"{\"city\":"}}
chunk: {"index":0,"function":{"arguments":"\"hangzhou\"}"}}
        │
        ▼ merge by index
ToolRequest(id=call_1, name=getWeather, arguments={"city":"hangzhou"})
delta 字段 聚合方式
index 分桶键
id 覆盖式写入当前桶(首个 chunk 携带完整 id)
function.name 追加
function.arguments 追加原文,保留 JSON 文本

注意 :部分兼容网关不下发 index,此时以"出现一个新的非空 id"作为新一次调用的起点(fallback 计数器分桶),避免把同一次调用的参数增量错拆成多次调用。


五、工具调用(Function Calling)wire 细节

5.1、assistant 发起调用

json 复制代码
{
  "role": "assistant",
  "content": null,
  "tool_calls": [
    {
      "id": "call_1",
      "type": "function",
      "function": { "name": "getWeather", "arguments": "{\"city\":\"hangzhou\"}" }
    }
  ]
}

重点 :arguments 是字符串(JSON 文本),不是对象------这是后续 AgentForge 归一化与再解析的关键。

5.2、tool 结果回填

json 复制代码
{ "role": "tool", "tool_call_id": "call_1", "content": "{\"temperature\":22}" }

tool_call_id 必须与发起时的 id 一一对应。

5.3、多工具调用

一次响应可返回多个 tool_calls,需逐个执行并分别以 tool role 回填,id 一一对应。


六、完整调用演练:user prompt + function calls(curl)

场景:用户问 "杭州今天天气怎么样?" ,并声明一个 get_weather 工具。下面从 curl 构造到返回,非流式 / 流式都演示一遍(含工具结果回填的第二轮)。

6.1、声明 tools

json 复制代码
"tools": [
  {
    "type": "function",
    "function": {
      "name": "get_weather",
      "description": "查询指定城市的当前天气",
      "parameters": {
        "type": "object",
        "properties": { "city": { "type": "string", "description": "城市名" } },
        "required": ["city"]
      }
    }
  }
]

6.2、非流式 · 第一轮:模型决定调用工具

bash 复制代码
curl https://api.openai.com/v1/chat/completions \
  -H "Authorization: Bearer $OPENAI_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "model": "gpt-4o-mini",
    "messages": [
      { "role": "user", "content": "杭州今天天气怎么样?" }
    ],
    "tools": [
      { "type": "function", "function": {
          "name": "get_weather",
          "description": "查询指定城市的当前天气",
          "parameters": { "type": "object",
            "properties": { "city": { "type": "string", "description": "城市名" } },
            "required": ["city"] } } }
    ],
    "tool_choice": "auto"
  }'

返回(注意 finish_reason=tool_calls、content=null):

json 复制代码
{
  "id": "chatcmpl_abc",
  "object": "chat.completion",
  "created": 1780000000,
  "model": "gpt-4o-mini",
  "choices": [
    {
      "index": 0,
      "message": {
        "role": "assistant",
        "content": null,
        "tool_calls": [
          {
            "id": "call_1",
            "type": "function",
            "function": { "name": "get_weather", "arguments": "{\"city\":\"杭州\"}" }
          }
        ]
      },
      "finish_reason": "tool_calls"
    }
  ],
  "usage": { "prompt_tokens": 62, "completion_tokens": 15, "total_tokens": 77 }
}

重点 :arguments 是字符串 "{\"city\":\"杭州\"}",本地解析后执行 get_weather("杭州")。

6.3、非流式 · 第二轮:回填工具结果,得到最终答案

bash 复制代码
curl https://api.openai.com/v1/chat/completions \
  -H "Authorization: Bearer $OPENAI_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "model": "gpt-4o-mini",
    "messages": [
      { "role": "user", "content": "杭州今天天气怎么样?" },
      { "role": "assistant", "content": null,
        "tool_calls": [
          { "id": "call_1", "type": "function",
            "function": { "name": "get_weather", "arguments": "{\"city\":\"杭州\"}" } } ] },
      { "role": "tool", "tool_call_id": "call_1",
        "content": "{\"temperature\":26,\"text\":\"晴\"}" }
    ]
  }'

返回(finish_reason=stop):

json 复制代码
{
  "id": "chatcmpl_def",
  "choices": [
    {
      "index": 0,
      "message": { "role": "assistant", "content": "杭州今天 26℃,天气晴。" },
      "finish_reason": "stop"
    }
  ],
  "usage": { "prompt_tokens": 85, "completion_tokens": 12, "total_tokens": 97 }
}

6.4、流式 · 第一轮:tool_calls 增量(stream:true)

bash 复制代码
curl https://api.openai.com/v1/chat/completions \
  -H "Authorization: Bearer $OPENAI_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "model": "gpt-4o-mini",
    "stream": true,
    "stream_options": { "include_usage": true },
    "messages": [ { "role": "user", "content": "杭州今天天气怎么样?" } ],
    "tools": [
      { "type": "function", "function": {
          "name": "get_weather", "description": "查询指定城市的当前天气",
          "parameters": { "type": "object",
            "properties": { "city": { "type": "string" } }, "required": ["city"] } } }
    ],
    "tool_choice": "auto"
  }'

SSE(按到达顺序):

text 复制代码
data: {"choices":[{"index":0,"delta":{"role":"assistant","content":null,"tool_calls":[{"index":0,"id":"call_1","type":"function","function":{"name":"get_weather","arguments":""}}]},"finish_reason":null}]}

data: {"choices":[{"index":0,"delta":{"tool_calls":[{"index":0,"function":{"arguments":"{\"city\":"}}]},"finish_reason":null}]}

data: {"choices":[{"index":0,"delta":{"tool_calls":[{"index":0,"function":{"arguments":"\"杭州\"}"}}]},"finish_reason":null}]}

data: {"choices":[{"index":0,"delta":{},"finish_reason":"tool_calls"}]}

data: {"choices":[],"usage":{"prompt_tokens":62,"completion_tokens":15,"total_tokens":77}}

data: [DONE]

聚合 (按 tool_calls[].index 分桶;id 覆盖、name/arguments 追加):

text 复制代码
index=0 → id=call_1, name=get_weather, arguments={"city":"杭州"}

6.5、流式 · 第二轮:回填后生成最终文本

请求体同 6.3,追加 "stream": true, "stream_options": {"include_usage": true}。

text 复制代码
data: {"choices":[{"index":0,"delta":{"role":"assistant","content":"杭州"},"finish_reason":null}]}

data: {"choices":[{"index":0,"delta":{"content":"今天 26℃"},"finish_reason":null}]}

data: {"choices":[{"index":0,"delta":{"content":",天气晴。"},"finish_reason":null}]}

data: {"choices":[{"index":0,"delta":{},"finish_reason":"stop"}]}

data: {"choices":[],"usage":{"prompt_tokens":85,"completion_tokens":12,"total_tokens":97}}

data: [DONE]

聚合 :delta.content 顺序拼接 → "杭州今天 26℃,天气晴。"。

6.6、演练要点小结

观测点 非流式 流式
工具参数 message.tool_calls[].function.arguments(字符串) delta.tool_calls[].function.arguments 分片,按 index 聚合
是否发工具 finish_reason == "tool_calls" 最后一个 chunk 的 finish_reason == "tool_calls"
文本 message.content delta.content 逐段回调
usage 响应体 usage 末尾 choices=[] 的 usage chunk
结束 --- data: [DONE]

6.7、真实测试验证

可直接运行仓库内脚本(需设置 API Key):

bash 复制代码
export OPENAI_API_KEY=sk-...
bash docs/dev/backend/AgentForge核心模块设计原理/01、model模型协议层/chatmodel/openai/verify-openai-chat.sh

脚本依次执行:① 非流式工具调用请求;② 用 jq 打印 tool_calls;③ 流式请求打印 SSE。可选环境变量 OPENAI_BASE_URL / OPENAI_MODEL 覆盖默认值。

注意:真实调用会产生费用;请勿把 Key 写入脚本或提交到仓库。


七、总结

  1. Endpoint :POST {baseUrl}/chat/completions,Authorization: Bearer 鉴权。
  2. 请求 :messages(system / user / assistant / tool)+ 采样参数 + tools / tool_choice。
  3. 响应 :choices[0].message(content 或 tool_calls)+ usage + finish_reason。
  4. 流式 :SSE data: chunk;delta.content 文本、delta.tool_calls 按 index 聚合;[DONE] 结束。
  5. 工具 :assistant.tool_calls(arguments 为字符串)→ tool role 按 tool_call_id 回填。

AgentForge 如何把这些字段映射为统一类型,见《OpenAI协议02、AgentForge OpenAI接入核心实践》。


参考资料

1. OpenAI Chat Completions API(官方参考)

2. OpenAI 文本生成指南

3. Server-Sent Events(MDN)

4. 相关内部文档:AgentForge OpenAI 接入核心实践

整理者:长路 创建时间:2026.10.5 更新时间:2026.10.5

相关推荐
鱼宵8 小时前
Spring AI 工具调用:@Tool 让大模型自己查订单查库存
人工智能·spring·工具调用·functioncalling·springai
浪淘沙jkp19 小时前
【ComfyUI 国产模型炼丹记】第一篇:V100 16G 部署 Wan2.2:从环境配置到首次出片
文生视频·comfyui·ai模型·wan2.2·wan2.1·v100 16g
制造数据与AI践行者老蒋1 天前
排坑笔记:LangChain 多工具 Agent 完整性校验 return_intermediate_steps 事后核对方案
langchain·ai agent·工具调用·agent开发·排坑笔记·多工具协同·工程化 质量保障
可乐ea6 天前
从第一性原理构建 AI Agent:提示词、工具、技能与记忆全解剖
数据库·人工智能·工具调用·ai智能体·提示词工程·agent开发·智能体记忆
可乐ea6 天前
AI Agent 工具调用准确性评测:选择错误与参数错误分开测
大数据·人工智能·算法·大模型·工具调用·ai智能体·agent评测
漂着的圆木6 天前
Agent监控:Copilot托管设置启用OTel的配置核对
agent·可观测性·github copilot·工具调用·opentelemetry
浅安的邂逅19 天前
260918-白帽把 OpenAI 论坛“打穿“了:攻防赛漏洞、账户被 Claude 入侵、模型偷偷掩盖不当行为
人工智能·大模型·ai编程·ai模型·行业动态
Fang_YuanAI20 天前
OST传媒提出“AIGC原生IP”:AI内容正在进入IP时代
ai·aigc·短视频·ai视频·ai模型·ai漫剧·ai短剧
deepseek231 个月前
MCP 2026-07-28 Tasks扩展深度解析:从无状态协议到长时运行任务的架构演进
ai agent·分布式系统·协议设计·mcp·无状态协议·tasks扩展