跳到主要内容

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

· 阅读需 9 分钟

更新日期:2026-10-05
适用场景:理解 agentforge-model-openai 所对接的 OpenAI Chat Completions wire protocol
维护者:changlu

一句话结论:AgentForge 的 OpenAI Provider 对接的是 Chat Completions(POST {baseUrl}/chat/completions)。本文只讲"协议本身长什么样";AgentForge 如何把它映射成统一 ChatRequest / ChatResponse,见《OpenAI协议02、AgentForge OpenAI接入核心实践》。


一、背景与问题引入​

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、协议定位​

POST {baseUrl}/chat/completions

默认:

baseUrl = https://api.openai.com/v1
→ 实际地址 https://api.openai.com/v1/chat/completions

官方参考:


二、请求协议​

2.1、Headers​

Content-Type: application/json
Accept: application/json
Authorization: Bearer ${apiKey}

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


2.2、请求体:messages(核心)​

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

纯文本示例:

{
"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、完整非流式请求示例​

{
"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​

{
"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)​

请求时增加:

{ "stream": true, "stream_options": { "include_usage": true } }

响应为 SSE:

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 聚合:

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 发起调用​

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

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


5.2、tool 结果回填​

{ "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​

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

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

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):

{
"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、非流式 · 第二轮:回填工具结果,得到最终答案​

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):

{
"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)​

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(按到达顺序):

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 追加):

index=0 → id=call_1, name=get_weather, arguments={"city":"杭州"}

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

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

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.contentdelta.content 逐段回调
usage响应体 usage末尾 choices=[] 的 usage chunk
结束—data: [DONE]

6.7、真实测试验证​

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

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 接入核心实践](/blog/01、model模型协议层/chatmodel/openai/OpenAI协议02、AgentForge 适配OpenAI接入核心实践)


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