一份请求,如何变成模型的回答?

用大白话讲清楚 ChatGPT 背后的 Chat API:系统提示词、历史消息、工具调用,以及一串 HTTP 请求里的文字,是怎么一步步变成「Token 数字」、再变成最终回复的。

📨 请求结构 🎭 系统提示词 🔧 工具调用 🔢 Prompt → Token 🧠 Tokenizer

0 先建立一个直觉

模型眼里没有「网页、JSON、函数」,只有一串数字。

你可以把一次 API 调用想象成给一个「只会算数字的翻译官」下达任务:

你写的 JSON 请求 拼成一段话(Prompt) 切成数字(Token) 模型算下一个数字 翻译回文字回答
一句话原理:大模型不会「执行你的函数」,也不会「理解」JSON。它做的唯一一件事,是根据前面所有的 Token,预测「下一个最可能的 Token」。你看到的函数调用、工具执行,都是外面程序配合完成的。

1 一次请求长什么样

核心就一个 messages 数组,外加几个「旋钮」参数。

📨 Chat Completions 接口的最小示例

HTTP POST /v1/chat/completions{ "model": "gpt-4.1", "messages": [ { "role": "system", "content": "你是一个耐心的Java老师" }, { "role": "user", "content": "解释一下Spring Boot" } ], "temperature": 0.7, "max_tokens": 1024 }

拆开看,每个字段各司其职:

字段作用通俗理解
model指定用哪个模型「叫哪位员工来干活」
messages完整的对话上下文「把之前说过的话都给他看」
temperature回答的随机程度「是死板照搬,还是天马行空」
max_tokens最多输出多少字「回答长度的上限」
关键认知:messages没有所谓「会话状态」。模型是无记忆的 —— 每次调用你都得把整段历史重新发一遍,它才能「记得」前面聊了什么。

🧪 动手试试:实时生成 JSON 请求

在下面填内容,右侧会实时拼出真实的请求体:

2 系统提示词(System Prompt)

给模型「戴上的角色面具」和「立下的规矩」,优先级最高。

system 角色放在消息列表最前面,用来告诉模型三件事:

🎭 你是谁
(角色设定)
📏 什么能做
(规则边界)
📋 按什么格式答
(输出规范)

一个「真能干活」的系统提示词示例

system content你是「AI自动化讲师」,负责用通俗语言讲解技术概念。 规则: 1. 先打比方,再讲原理,最后给代码。 2. 涉及代码时,用 '''java 和 ''' 包裹。 3. 如果用户提问与课程无关,礼貌地拉回主题。 输出格式: 【一句话结论】→【生活化比喻】→【核心原理】→【示例代码】
为什么系统提示词有效?因为模型是「预测下一个词」,而系统提示词是每次都在最前面反复出现的强上下文,等于持续告诉模型「你要用这个身份和风格来说话」。它不神秘,就是「被放进 Prompt 里的一段文字」。
请注意:不同厂商写法不同。Anthropic 的 Claude 把 system 单独作为一个顶层字段("system": "..."),而 OpenAI 是放进 messages 里用 role="system"。本质一样 —— 都是最高优先级的指令。

3 历史消息:模型为什么「记得」你

记忆是「假象」,真相是每次都把聊天记录重新喂回去。

一次多轮对话,请求体是滚雪球式增长的:

第 3 轮对话的完整 messages[ { "role":"system", "content":"你是Java老师" }, { "role":"user", "content":"什么是JVM?" }, { "role":"assistant", "content":"JVM是运行Java字节码的虚拟机..." }, { "role":"user", "content":"那它和JDK什么关系?" } ← 新问题 ]
role是谁说的话作用
system你给模型的设定立规矩、定人设
user用户说的话问题本身
assistant模型之前说的话让模型「接得上」上下文
代价很直接:历史越久,messages 越长,消耗的 Token 越多(也就越贵、越慢)。所以工程上才会有「截断旧对话」「做摘要压缩」这些优化手段。

4 工具调用(Tool Calling / Function Calling)

模型不会自己「打电话查天气」,它只会说「我想调用这个函数,参数是……」,真正执行靠你。

🔧 完整流程(四步四角色)

1️⃣ 你告诉模型
有哪些工具可用
2️⃣ 模型返回
要调哪个函数+参数
3️⃣ 你的程序
真正执行函数
4️⃣ 把结果回填
再问一次模型

第 1 步:在请求里声明工具(tools 字段)

告诉你模型有哪些工具{ "tools": [{ "type": "function", "function": { "name": "get_weather", "description": "查询指定城市的天气", "parameters": { "type": "object", "properties": { "city": { "type": "string", "description": "城市名" } }, "required": ["city"] } } }] }

第 2 步:模型的回答不是文字,而是一个「调用请求」

模型的原始返回(本质仍是Token){ "tool_calls": [{ "function": { "name": "get_weather", "arguments": "{\"city\":\"武汉\"}" } }] }

第 3、4 步:你执行后,把结果当作一条消息回填

把工具结果回填给模型[ ...之前的所有消息, { "role":"assistant", "tool_calls":[{"function":{...}}] }, { "role":"tool", "tool_call_id":"call_abc", "content":"武汉今天 32℃,晴" } ]
别误解:「工具调用」并不是模型在运行函数,而是模型在生成一串格式像 JSON 的 Token。你的应用解析这串 Token、去调真正的 get_weather(),再带着真实结果回来。这叫「模型动嘴,程序跑腿」。

🧪 动画演示:一个「查天气」的完整回合

5 HTTP 请求里的内容,如何进入模型

不是所有字段都会进模型。有些是「给模型看的话」,有些只是「给推理引擎的旋钮」。

HTTP 请求 推理服务解析
messages/tools/参数
Chat 模板拼接
拼成一段Prompt
Tokenizer 分词
文字→数字ID
Transformer 计算
预测下一个Token

哪些字段「真的进了模型」?

内容是否进入模型计算说明
system 系统提示词✔ 进变成上下文 Token
user 用户消息✔ 进变成上下文 Token
assistant 历史回复✔ 进变成上下文 Token
RAG 检索到的知识✔ 进拼进 Prompt 一起处理
tools 工具定义✔ 通常进进入上下文,占用 Token
temperature✘ 不进采样参数,改的是选词随机性
max_tokens✘ 不进只是限制输出长度
一句话区分:「话」(messages / tools 里的文字)会变成 Token 交给模型;「旋钮」(temperature / max_tokens 等)只被外面推理引擎读取,用来控制「怎么挑下一个词」,模型本身看不到。

6 Prompt → Token:文字如何变成数字

模型不识字,只认数字。Tokenizer 就是那个「翻译成数字」的分词器。

🧪 输入一句话,看看它怎么被「切+编号」

⚠️ 这是教学用简化演示(按常见子词规则模拟分割并伪造 ID)。真实的 Tokenizer(如 GPT 的 BPE)更细:常见词可能 1 个词 = 1 个 Token,生僻词会被拆成多个小片段。

真实的转换链路长这样

Prompt → Token → ID → 向量"你好,世界" ← 原文 ↓ Tokenizer [ "你", "好", ",", "世", "界" ] ← 切成词元(示意) ↓ 查词表 [ 108386, 11, 99873 ] ← 每个Token对应一个数字ID ↓ Embedding [ [0.23,-0.51,...,0.18], ... ] ← 再映射成浮点向量

为什么「中文字符数 ≠ Token 数」?

每个模型自带一套「词表 + 切分规则」。同一个词,在 GPT、Qwen、DeepSeek 里的切法和 ID 可能完全不同。所以计算长度、显存、费用时,只能按 Token 数,不能按字数估算

Token 数的实际意义

  • 💸 计费:按输入/输出的 Token 数收费
  • 🧠 显存:上下文越长,显存占用越大
  • ⏱️ 速度:Token 越多,生成越慢
  • 📐 上限:输入+输出 ≤ 上下文窗口

输入 / 输出 / 上下文窗口,三个别搞混

概念含义通俗理解
输入长度 Input Tokens进入模型的 Token 数(system+历史+问题+RAG+工具)「他看了多少」
输出长度 Output Tokens模型新生成的 Token 数「他说了多少」
上下文窗口 Context Window模型能处理的最大 Token 数「他的脑容量上限」
约束关系输入长度 + 输出长度 ≤ 上下文窗口 例:某模型窗口 = 32768 输入占 20000 tokens → 最多还能输出 12768 tokens

Prefill 与 Decode:一次算完 vs 一吐一个

🔵 Prefill(预填充)

处理输入阶段。你输入的 1 万个 Token 一次性并行算完,同时生成缓存(KV Cache)。主要影响首字延迟(TTFT)和长文本处理速度。

🟢 Decode(解码生成)

生成阶段。借助 KV Cache,每次只算一个新 Token,一个一个往外吐,循环直到结束。这就是为什么回答长内容像「逐字打印」而不是「瞬间整段弹出」。

Transformer 的本质任务:根据已有 Token x₁, x₂, …, xₜ,预测下一个 Token 的概率分布 P(xₜ₊₁ | x₁…xₜ),再按采样策略(这就是 temperature 起作用的地方)挑出一个,如此反复。

7 常用参数速查表

面试和实操都常被问到的几个「旋钮」。

参数作用取值范围 / 典型值调整方向
temperature采样随机性0 ~ 2,常见 0.7↓ 更稳定死板,↑ 更多样发散
max_tokens输出上限正整数决定回答最长长度
top_p核采样,只在累计概率内的词里选0 ~ 1,常见 0.9与 temperature 二选一微调
n一次返回几个候选回答正整数需要多选一/对比时用
stop遇到指定字符串就停止生成字符串数组控制生成到哪收尾
tools声明可用工具函数定义数组开启工具调用能力
stream是否流式返回(打字机效果)true / false体验好就开 true
记住一个原则:temperaturetop_p 别同时调大,二选一。需要严谨准确(代码、数学、JSON 输出)就调低 temperature;需要创意发散(文案、脑暴)就调高。

8 一张图记住全部

从 HTTP 到回答,完整链路在大脑里过一遍。

📨 HTTP 请求 🧵 Prompt 组装
system+user+RAG+工具
🔢 Tokenizer
文字 → ID
🧠 Transformer
预测下一个 Token
🔄 工具调用 或 文本输出 ✅ 最终结果

三句必须记住的话

① 模型无状态:每次调用都要把整段对话重新发一遍,记忆是「拼出来的」,不是「存下来的」。
② 模型只认 Token:系统提示词、历史消息、工具定义,最终都是被 Tokenizer 切成数字后进入模型;温度、长度这些旋钮不进模型。
③ 模型不执行工具:工具调用是模型「生成一段像 JSON 的文字」,真正跑函数的是你的程序,跑完再把结果喂回模型。

对比速记:OpenAI vs Anthropic vs 通用 Agent 生态

能力/概念OpenAIAnthropic (Claude)更高层的封装
消息接口Chat / ResponsesMessagesLangChain / LangGraph 等
系统提示词role: "system"顶层 system 字段PromptTemplate
工具调用tool_callstool_useTool / MCP / Skill
工具结果回填role: "tool"tool_result由框架自动编排

LangGraph(状态图 Agent 编排)、AgentScope(多智能体)、Spring AI Alibaba(Java 生态)这些框架,干的都是同一件事:帮你组织上下文、保存状态、调用工具、控制流程——而底层的「上下文 → Token → 预测下一个 Token」始终没变。