跳转到内容

OpenAI Chat Completions 完整交互示例

OpenAI Chat Completions 完整交互示例

Section titled “OpenAI Chat Completions 完整交互示例”

返回:OpenAI Chat Completions 协议

下面用同一个跑步建议场景展示 Chat Completions 的工具调用过程。

用户问:“北京今天适合跑步吗?如果空气质量不好,请参考公开信息给建议。”

Chat Completions 的工具调用核心是本地工具:模型返回 tool_calls,客户端执行工具后用下一轮 tool message 回填。这个协议没有 Responses/Anthropic 那样的服务端工具 item/block 生命周期;web_search_options 这类能力是请求级选项,只影响上游服务行为,不会在 SSE 中形成独立的 server-tool-use/result 事件。

Client
|
| 1. messages + tools(function get_weather) + web_search_options + stream=true
v
proxai
|
| 2. 原样转发 openai_chat_completions
v
OpenAI-compatible upstream
|
| 3. SSE: delta.tool_calls(function arguments)
v
proxai
|
| 4. 原始 SSE bytes 透传,Chat observer 记录 finish_reason/usage
v
Client
|
| 5. 本地执行 get_weather
v
Local tool runtime
|
| 6. 下一轮 messages 带 tool role 结果
v
proxai -> upstream -> proxai -> Client
{
"model": "gpt-5.4",
"stream": true,
"messages": [
{
"role": "system",
"content": "你是一个简洁的出行建议助手。"
},
{
"role": "user",
"content": "北京今天适合跑步吗?如果空气质量不好,请参考公开信息给建议。"
}
],
"tools": [
{
"type": "function",
"function": {
"name": "get_weather",
"description": "查询指定城市的天气和空气质量摘要。",
"parameters": {
"type": "object",
"properties": {
"city": { "type": "string" },
"date": { "type": "string" }
},
"required": ["city", "date"]
},
"strict": true
}
}
],
"tool_choice": "auto",
"parallel_tool_calls": false,
"web_search_options": {
"search_context_size": "low",
"user_location": {
"type": "approximate",
"approximate": {
"city": "Beijing",
"country": "CN",
"timezone": "Asia/Shanghai"
}
}
}
}

对应结构映射。这里是字段映射伪代码,json! 表示 serde_json::json!... 表示其余可选字段省略:

CreateChatCompletionRequest {
model: "gpt-5.4".to_string(),
stream: Some(true),
messages: vec![
ChatCompletionRequestMessage::System(ChatCompletionRequestSystemMessage {
content: ChatCompletionRequestSystemMessageContent::Text(
"你是一个简洁的出行建议助手。".to_string(),
),
...
}),
ChatCompletionRequestMessage::User(ChatCompletionRequestUserMessage {
content: ChatCompletionRequestUserMessageContent::Text(
"北京今天适合跑步吗?如果空气质量不好,请参考公开信息给建议。".to_string(),
),
...
}),
],
tools: Some(vec![
ChatCompletionTools::Function(ChatCompletionTool {
function: FunctionObject {
name: "get_weather".to_string(),
description: Some("查询指定城市的天气和空气质量摘要。".to_string()),
parameters: Some(json!({
"type": "object",
"properties": {
"city": { "type": "string" },
"date": { "type": "string" }
},
"required": ["city", "date"]
})),
strict: Some(true),
},
}),
]),
tool_choice: Some(ChatCompletionToolChoiceOption::Mode(ToolChoiceOptions::Auto)),
parallel_tool_calls: Some(false),
web_search_options: Some(WebSearchOptions {
search_context_size: Some(WebSearchContextSize::Low),
user_location: Some(WebSearchUserLocation {
r#type: WebSearchUserLocationType::Approximate,
approximate: WebSearchLocation {
city: Some("Beijing".to_string()),
country: Some("CN".to_string()),
timezone: Some("Asia/Shanghai".to_string()),
...
},
}),
}),
...
}

Chat Completions 流式响应里,工具调用参数在 choices[].delta.tool_calls[].function.arguments 中增量到达。

data: {
"id": "chatcmpl_01",
"object": "chat.completion.chunk",
"created": 1770000000,
"model": "gpt-5.4",
"choices": [
{
"index": 0,
"delta": { "role": "assistant" },
"finish_reason": null
}
]
}
data: {
"id": "chatcmpl_01",
"object": "chat.completion.chunk",
"created": 1770000000,
"model": "gpt-5.4",
"choices": [
{
"index": 0,
"delta": {
"tool_calls": [
{
"index": 0,
"id": "call_weather_01",
"type": "function",
"function": {
"name": "get_weather",
"arguments": "{\"city\":\"北京\""
}
}
]
},
"finish_reason": null
}
]
}
data: {
"id": "chatcmpl_01",
"object": "chat.completion.chunk",
"created": 1770000000,
"model": "gpt-5.4",
"choices": [
{
"index": 0,
"delta": {
"tool_calls": [
{
"index": 0,
"function": {
"arguments": ",\"date\":\"today\"}"
}
}
]
},
"finish_reason": null
}
]
}
data: {
"id": "chatcmpl_01",
"object": "chat.completion.chunk",
"created": 1770000000,
"model": "gpt-5.4",
"choices": [
{
"index": 0,
"delta": {},
"finish_reason": "tool_calls"
}
],
"usage": {
"prompt_tokens": 140,
"completion_tokens": 24,
"total_tokens": 164
}
}
data: [DONE]

对应结构映射:

CreateChatCompletionStreamResponse {
id: "chatcmpl_01".to_string(),
object: "chat.completion.chunk".to_string(),
created: 1770000000,
model: "gpt-5.4".to_string(),
choices: vec![ChatChoiceStream {
index: 0,
delta: ChatCompletionStreamResponseDelta {
role: Some(Role::Assistant),
...
},
...
}],
...
}
CreateChatCompletionStreamResponse {
choices: vec![ChatChoiceStream {
index: 0,
delta: ChatCompletionStreamResponseDelta {
tool_calls: Some(vec![
ChatCompletionMessageToolCallChunk {
index: 0,
id: Some("call_weather_01".to_string()),
r#type: Some(FunctionType::Function),
function: Some(FunctionCallStream {
name: Some("get_weather".to_string()),
arguments: Some("{\"city\":\"北京\"".to_string()),
}),
},
]),
...
},
...
}],
...
}
CreateChatCompletionStreamResponse {
choices: vec![ChatChoiceStream {
index: 0,
delta: ChatCompletionStreamResponseDelta {
tool_calls: Some(vec![
ChatCompletionMessageToolCallChunk {
index: 0,
function: Some(FunctionCallStream {
arguments: Some(",\"date\":\"today\"}".to_string()),
...
}),
...
},
]),
...
},
...
}],
...
}
CreateChatCompletionStreamResponse {
choices: vec![ChatChoiceStream {
index: 0,
delta: ChatCompletionStreamResponseDelta { ... },
finish_reason: Some(FinishReason::ToolCalls),
...
}],
usage: Some(CompletionUsage { ... }),
...
}

客户端聚合规则:

tool_calls[(choice.index=0, tool.index=0)].id = "call_weather_01"
tool_calls[(0, 0)].name = "get_weather"
tool_calls[(0, 0)].arguments += "{\"city\":\"北京\""
tool_calls[(0, 0)].arguments += ",\"date\":\"today\"}"

finish_reason = "tool_calls" 到达时,arguments 才可以作为完整 JSON 解析。

客户端执行本地 get_weather 后,下一轮请求需要带上 assistant 的 tool_calls 和对应的 tool message。

{
"model": "gpt-5.4",
"stream": true,
"messages": [
{
"role": "user",
"content": "北京今天适合跑步吗?如果空气质量不好,请参考公开信息给建议。"
},
{
"role": "assistant",
"tool_calls": [
{
"id": "call_weather_01",
"type": "function",
"function": {
"name": "get_weather",
"arguments": "{\"city\":\"北京\",\"date\":\"today\"}"
}
}
]
},
{
"role": "tool",
"tool_call_id": "call_weather_01",
"content": "北京今天气温 18-27C,轻度污染,PM2.5 约 85,傍晚有风。"
}
]
}

结构映射:

CreateChatCompletionRequest {
model: "gpt-5.4".to_string(),
stream: Some(true),
messages: vec![
ChatCompletionRequestMessage::User(ChatCompletionRequestUserMessage {
content: ChatCompletionRequestUserMessageContent::Text(
"北京今天适合跑步吗?如果空气质量不好,请参考公开信息给建议。".to_string(),
),
...
}),
ChatCompletionRequestMessage::Assistant(ChatCompletionRequestAssistantMessage {
tool_calls: Some(vec![
ChatCompletionMessageToolCalls::Function(ChatCompletionMessageToolCall {
id: "call_weather_01".to_string(),
function: FunctionCall {
name: "get_weather".to_string(),
arguments: "{\"city\":\"北京\",\"date\":\"today\"}".to_string(),
},
}),
]),
...
}),
ChatCompletionRequestMessage::Tool(ChatCompletionRequestToolMessage {
tool_call_id: "call_weather_01".to_string(),
content: ChatCompletionRequestToolMessageContent::Text(
"北京今天气温 18-27C,轻度污染,PM2.5 约 85,傍晚有风。".to_string(),
),
}),
],
...
}
data: {
"id": "chatcmpl_02",
"object": "chat.completion.chunk",
"created": 1770000010,
"model": "gpt-5.4",
"choices": [
{
"index": 0,
"delta": {
"role": "assistant",
"content": "今天北京不太适合高强度户外跑步。"
},
"finish_reason": null
}
]
}
data: {
"id": "chatcmpl_02",
"object": "chat.completion.chunk",
"created": 1770000010,
"model": "gpt-5.4",
"choices": [
{
"index": 0,
"delta": {
"content": "空气质量为轻度污染,建议改为低强度慢跑或室内训练。"
},
"finish_reason": null
}
]
}
data: {
"id": "chatcmpl_02",
"object": "chat.completion.chunk",
"created": 1770000010,
"model": "gpt-5.4",
"choices": [
{
"index": 0,
"delta": {},
"finish_reason": "stop"
}
]
}
data: [DONE]

结构映射和聚合规则:

CreateChatCompletionStreamResponse {
choices: vec![ChatChoiceStream {
index: 0,
delta: ChatCompletionStreamResponseDelta {
role: Some(Role::Assistant),
content: Some("今天北京不太适合高强度户外跑步。".to_string()),
...
},
...
}],
...
}
CreateChatCompletionStreamResponse {
choices: vec![ChatChoiceStream {
index: 0,
delta: ChatCompletionStreamResponseDelta {
content: Some("空气质量为轻度污染,建议改为低强度慢跑或室内训练。".to_string()),
...
},
...
}],
...
}
assistant_text[0] = ""
assistant_text[0] += "今天北京不太适合高强度户外跑步。"
assistant_text[0] += "空气质量为轻度污染,建议改为低强度慢跑或室内训练。"

delta.content 是新增文本片段,不是完整文本。finish_reason = "stop" 表示该 choice 的最终回答结束。