Skip to content

模型调用与消息结构入门

版本:v1.1

最后更新:2026-07-08

适用对象:刚开始接触大模型 API、已经会发请求但还没有稳定工程心智模型、准备继续进入结构化输出 / 工具调用 / Agent 的读者

很多人一开始学 AI 应用,最容易误判的一件事是:

以为自己在学“神秘模型技巧”,其实真正需要先搞清楚的是一次调用里到底有哪些层。

如果这一层没有建立起来,后面看 Prompt、RAG、Agents、协议、平台工程时,常常会把本来属于不同层的问题混在一起。

这篇文档的目标很明确:

  1. 帮你先看懂一次模型调用真正由哪些部分组成。
  2. 帮你分清 Prompt、上下文、结构化输出、工具调用各自负责什么。
  3. 帮你建立一个最小但可扩展的工程心智模型。
  4. 帮你把 OpenAI、Anthropic、Gemini 这些官方资料里的共性原则串起来。

1. 先建立一个最小心智模型

你可以先把一次大模型调用理解成下面这条链路:

  1. 你定义任务目标。
  2. 你组织规则、输入和上下文。
  3. 模型在给定上下文里生成结果。
  4. 应用侧校验结果。
  5. 需要时再决定是否调用工具、检索知识或进入下一步。

所以,一个 AI 应用从来不只是“发一段 Prompt 给模型”。

更完整地看,通常至少有 7 个组成部分:

  • 任务规格:这次到底要解决什么问题。
  • 指令层:稳定规则、边界约束、角色设定、输出协议。
  • 动态输入:用户本次真正带来的内容。
  • 上下文:历史消息、检索内容、工具结果、状态摘要。
  • 执行能力:模型本身、工具调用、检索系统、工作流编排。
  • 结果校验:schema 校验、风险检查、人工确认。
  • 结果消费:展示给人、写回数据库、触发系统动作或进入下游流程。

只要你先把这几层分开,很多问题就容易定位得多。

2. 一次模型调用里,到底发生了什么

2.1 你不是在“提问”,而是在定义任务

在工程里,我们更关心的是:

  • 输入字段是否完整
  • 任务边界是否清晰
  • 输出协议是否稳定
  • 失败时能否回放和修复

比如“帮我总结这段文本”看起来很简单,但在工程里通常还要补这些信息:

  • 总结给谁看
  • 输出多长
  • 是否保留关键数字
  • 是否允许模型补充外部知识
  • 是否必须输出固定结构

这也是为什么同样一句自然语言,在演示环境能跑,在系统里却可能不稳定。

2.2 指令层和用户输入,最好分开

不同平台对消息结构的命名不完全一样,但核心原则一致:

  • 把长期稳定的规则单独放在上层指令里
  • 把用户每次变化的输入单独放在动态输入里
  • 把外部资料、检索结果、工具输出单独标成上下文

这样做的价值是:

  • 规则更容易复用
  • 历史更容易回放
  • 变更更容易评测
  • 安全边界更容易标注

如果把所有内容糊成一大段文本,后面通常会遇到这些问题:

  • 不知道到底是哪一层改动导致效果变化
  • 难以做版本治理
  • 工具结果和用户原话混在一起
  • 安全审查时难以区分可信与不可信输入

2.3 模型看到的,不只是“你写的 Prompt”

很多应用最终送进模型的上下文,往往来自多部分拼接:

  • 固定行为规则
  • 任务描述
  • 用户输入
  • 检索回来的知识片段
  • 工具执行结果
  • 历史摘要
  • 输出格式说明

所以,输出不稳定时,根因可能在很多地方:

  • 用户输入本身含糊
  • 上下文太长,被挤掉关键内容
  • 检索证据质量差
  • 输出字段定义不清
  • 工具返回结果不可直接消费

3. 先搞清楚 6 个最基础名词

3.1 token

token 可以粗略理解成模型处理内容时使用的最小计量单位。它不是“字数”,也不是“单词数”,而是模型分词后的计算单位。

你需要关心 token,不是为了背定义,而是因为它会直接影响:

  • 上下文窗口能装多少内容
  • 请求成本
  • 响应延迟
  • 历史消息保留策略

OpenAI 当前文档特别强调,真实请求的 token 统计不只包含文本本身,还会包含消息结构、角色边界、工具与 schema 等格式开销,所以不能只靠“字符数 / 4”这种估算方式。Gemini 文档也明确把图片、文件、思考 token、工具 token 分开计量;Anthropic 则把工具定义、消息内容和输出一起纳入上下文预算。

3.2 上下文窗口

上下文窗口不是“无限聊天记录”,而是这次推理能看到的总预算。

通常会包含:

  • 当前输入
  • 历史消息
  • 系统规则
  • 检索材料
  • 工具返回内容
  • 本轮要生成的输出

这意味着只要你不断追加内容,总有一天会遇到:

  • 关键信息被截断
  • 历史污染当前任务
  • 成本和延迟上升
  • 长上下文带来的召回下降

Anthropic 的最新文档把这种现象明确提醒为长上下文下的精度衰减问题;这也解释了为什么“上下文越大”不等于“效果越稳定”。

3.3 结构化输出

结构化输出的核心价值不是“格式好看”,而是让结果可以稳定进入程序。

例如下面两种输出相比:

  • 自由文本:适合给人看
  • 固定字段:适合给系统消费

如果你的结果要进入数据库、工作流、审批流、任务路由、统计报表,通常优先考虑结构化输出,而不是只让模型“尽量返回 JSON”。

3.4 工具调用

工具调用的本质不是“让模型更聪明”,而是让模型把某一步交给外部系统执行。

常见工具包括:

  • 搜索
  • 数据库查询
  • 发邮件
  • 调 CRM
  • 调内部审批接口

一旦进入工具调用,问题就不再只是 Prompt 问题,还会变成:

  • 参数定义是否清晰
  • 调用条件是否合理
  • 权限是否越界
  • 结果是否需要二次校验

3.5 会话状态

多轮对话的难点不是“消息越来越多”,而是如何保留真正有价值的状态。

常见状态包括:

  • 用户目标
  • 当前步骤
  • 已经确认的事实
  • 已经执行过的工具结果
  • 待用户补充的信息

如果不做状态治理,多轮应用很容易越聊越乱。

3.6 schema

schema 可以理解成你希望模型输出或工具入参满足的结构约束。

它常见于两类场景:

  • 结构化输出
  • function / tool calling

schema 的价值是把“请尽量按这个格式返回”升级成“结果必须满足这个结构约束,再进入下游程序”。

4. 不同 API 里的消息结构,核心共性是什么

虽然不同平台的字段名不完全一样,但工程上的共性很强。

4.1 OpenAI 视角

OpenAI 当前文档强调:

  • 单次请求本身是无状态的
  • 多轮对话可以手动传历史
  • 也可以用 previous_response_idconversation 持续状态
  • 响应里既可以生成文本,也可以生成结构化输出或工具调用

所以在 OpenAI 体系里,你更应该从“请求由哪些块组成”来理解,而不是只盯着一段 prompt string。

4.2 Anthropic 视角

Anthropic 的 Messages 文档更突出“消息块”的概念:

  • system
  • messages
  • tools
  • tool_result

它的文档也反复强调上下文窗口里不仅有文本,还有工具定义、工具结果、图片、文档,以及在特定模式下的 thinking 块。

4.3 Gemini 视角

Gemini 文档对结构化输出、函数调用、token 计数和工具能力拆得比较清楚:

  • Structured Outputs 用来约束最终答案格式
  • Function Calling 用来请求外部动作
  • Token guide 用来估算输入、输出、thinking、cache、tool use 的预算

4.4 你真正该记住的共识

比起死记每家字段名,更重要的是记住下面这几个稳定原则:

  1. 稳定规则要和动态输入分开。
  2. 用户原话要和外部上下文分开。
  3. 模型输出要和工具执行结果分开。
  4. 给人看的答案要和给系统消费的结构分开。
  5. 历史消息不是越全越好,而是要治理。

5. 一个最小但健康的消息分层方式

你可以把一次调用拆成 5 块:

5.1 稳定规则

适合放:

  • 角色设定
  • 不允许做什么
  • 输出风格
  • 输出字段要求
  • 高风险动作的审批规则

这部分通常更新频率最低。

5.2 当前任务

适合放:

  • 本次到底要做什么
  • 任务成功标准
  • 任务边界

这部分是“这轮为什么发请求”的核心。

5.3 用户输入

适合放:

  • 用户原话
  • 表单字段
  • 上传文本
  • 用户本轮新增说明

这一层要尽量保持原始,不要混入系统解释。

5.4 外部上下文

适合放:

  • 检索证据
  • 历史摘要
  • 工具查询结果
  • 任务状态

这部分最容易导致上下文膨胀,也最需要治理。

5.5 输出协议

适合放:

  • 返回字段
  • 类型要求
  • 不确定时如何填
  • 引用来源是否必须

很多项目失败,就失败在这一层没有明确下来。

6. 为什么“分层”比“写长 Prompt”更重要

当你只是把 Prompt 写得很长,却没有把结构分层,通常会遇到下面这些问题:

6.1 不知道为什么效果变了

你改了一句指令,还是加了一段资料,还是历史消息变了?

如果没有分层,根本没法定位。

6.2 版本治理困难

你以后想做:

  • Prompt 版本管理
  • 回归评测
  • 问题复盘
  • 跨团队协作

都会很痛苦。

6.3 安全边界不清

如果用户输入、检索结果、工具结果都混成一段,后面就很难回答:

  • 哪些信息是可信的
  • 哪些信息可能被 prompt injection 污染
  • 哪些内容应该只读不能执行

6.4 程序消费不稳定

如果没有清楚的输出协议,程序端就只能赌模型“这次刚好输出得比较像你要的格式”。

7. token 预算为什么会比你想的复杂

OpenAI 当前的 token counting 文档已经把一个很关键的误区说得很清楚:

  • 真实请求的 token 不是只看文本
  • 消息角色和结构会消耗 token
  • 工具定义与 schema 会增加 token
  • 图片、文件、缓存和推理模式也会改变真实计数

Gemini 也提供 count_tokens 以及响应里的 usage 字段来分别看输入、输出、thinking、cache、tool use。

Anthropic 则明确指出:

  • system prompt 算
  • messages 算
  • tool results 算
  • tool definitions 算
  • output 和某些 thinking token 也算

7.1 所以你要避免什么

  • 用字符数粗估成本
  • 不算工具 schema 的上下文开销
  • 不算图片和文件的输入预算
  • 不看输出 token,只盯输入

7.2 更稳的做法

  1. 在核心链路上做真实 token 计数。
  2. 把工具定义、schema、上下文块拆开观察。
  3. 给不同任务设置不同的上下文预算。
  4. 设计超限时的裁剪、摘要、降级或回退策略。

8. 多轮对话为什么不能只靠“把历史一直追加”

这是很多新手最容易踩的坑之一。

8.1 单次请求是无状态的

OpenAI 当前文档明确说明,单次文本生成请求本身是无状态的;多轮效果来自你是否在下一轮把历史或前一轮响应继续带进去,或者通过 previous_response_id / conversation 这样的机制持久化状态。

也就是说:

  • “会话”是你设计出来的
  • 不是模型天然无成本替你记住的

8.2 把历史一直追加的问题

如果你机械地保留完整历史,常见后果包括:

  • 上下文越来越贵
  • 当前任务被旧信息污染
  • 关键内容被淹没
  • 系统性能越来越差

8.3 更稳的状态管理思路

至少要开始区分:

  • 原始消息历史
  • 长期稳定事实
  • 当前任务状态
  • 本轮临时工具结果
  • 可丢弃的中间思考

8.4 新手可先采用的最小方案

  1. 保留最近几轮原始消息。
  2. 把更早历史压成摘要。
  3. 单独维护“已经确认的事实”。
  4. 工具结果按任务阶段保留,不要永久堆积。

9. 结构化输出、Function Calling、自由文本,到底怎么选

这是一个非常常见的混淆点。

9.1 自由文本

适合:

  • 面向人阅读
  • 创意表达
  • 长段解释

不适合:

  • 直接进入数据库
  • 直接驱动程序逻辑
  • 高一致性要求的流程节点

9.2 结构化输出

适合:

  • 结果抽取
  • 分类标签
  • 路由决策
  • 报表字段
  • 审批建议

Google 的 Structured Outputs 文档当前就把它定义为“控制最终答案格式”的能力,而不是执行外部动作。

9.3 Function / Tool Calling

适合:

  • 要查系统数据
  • 要写业务系统
  • 要调用外部能力
  • 要把决策和执行拆开

OpenAI 和 Gemini 当前文档都明确把 Function Calling 指向“连接外部函数、系统与动作”,而不是仅仅让答案看起来更像 JSON。

9.4 一个简单判断原则

如果你要的是:

  • 给人看:先考虑自由文本
  • 给系统消费:先考虑结构化输出
  • 让系统执行动作:先考虑 function/tool calling

10. 工具调用真正的生命周期是什么

很多人把工具调用理解成“模型自己去调接口”,但更准确的理解是:

  1. 你定义工具及参数 schema。
  2. 模型根据任务决定是否请求某个工具。
  3. 应用侧执行该工具。
  4. 应用侧把工具结果传回模型。
  5. 模型基于结果继续输出最终答案或下一步动作。

也就是说,真正拥有执行权的是你的应用,不是模型。

10.1 这意味着什么

  • 工具调用结果要做权限控制
  • 入参要做校验
  • 执行动作要有审计
  • 高风险动作要有人审或双确认

10.2 什么场景不该急着上工具调用

  • 只是普通文本改写
  • 只是摘要和分类
  • 只是离线知识解释

这类任务很多时候单次调用加结构化输出就够了。

11. 为什么很多问题其实不是“模型太弱”

下面这张表很适合新手排查:

现象更可能的问题层优先检查什么
输出很散任务规格层目标、边界、输出字段是否写清
JSON 不稳定输出协议层是否使用结构化输出、程序端是否校验
答非所问输入组织层用户输入是否含糊、示例是否偏移
明明资料里有却答不出来检索层chunk、召回、重排、证据装配
多轮越聊越乱状态层历史裁剪、状态摘要、记忆分层
明明只是查数据却做成超复杂 Agent架构层是否本来只需要工具调用

只要你开始按“层”排查,很多问题就不会一直靠感觉调。

12. 一个最小可落地的工程模板

如果你现在要做第一个正式一点的 AI 功能,建议至少具备下面这些元素:

12.1 输入定义

  • 用户输入字段
  • 必填项
  • 非法值处理

12.2 规则定义

  • 稳定行为规则
  • 输出格式要求
  • 风险边界

12.3 上下文定义

  • 需要哪些历史
  • 需要哪些知识
  • 需要哪些工具结果

12.4 输出定义

  • 文本输出还是结构化输出
  • 是否需要引用
  • 不确定时怎么表达

12.5 校验定义

  • schema 校验
  • 敏感字段校验
  • 风险检查
  • 是否需要人工确认

12.6 回放定义

  • 保存输入
  • 保存输出
  • 保存失败样例
  • 保存版本信息

13. 给零基础读者的 7 天最小实践路线

第 1 天

第 2 天

  • 做一个摘要任务
  • 明确输入字段、输出字段
  • 保存 3 条成功样例和 3 条失败样例

第 3 天

  • 做一个信息抽取任务
  • 把结果固定成字段而不是自由文本
  • 开始建立“输出协议”意识

第 4 天

  • 继续练一个分类或路由任务
  • 定义“不确定时”的处理策略
  • 尝试做最简单的程序端校验

第 5 天

第 6 天

第 7 天

  • 用自己的话回答 5 个问题:
    1. 什么是消息分层?
    2. 为什么结构化输出比“返回 JSON”更稳?
    3. 工具调用和自由文本输出的边界是什么?
    4. 为什么历史消息不能一直无脑追加?
    5. 为什么失败样例要比“继续拍脑袋调 Prompt”更重要?

14. 学到这里,先别急着跳的 5 个坑

  • 还没把消息结构看懂,就直接上多智能体框架。
  • 还没做输出校验,就把模型结果直接写进业务系统。
  • 还没做失败样例沉淀,就不断重写 Prompt。
  • 还没建立 token 和上下文预算意识,就盲目叠资料。
  • 还没分清单次调用和 Agent 的边界,就上复杂编排。

15. 看完这篇后,下一步该读什么

如果你现在最缺的是:

16. 官方资料入口

以下入口在 2026-07-08 检查时可访问,适合和本页对照阅读:

17. 最后给一句判断标准

如果你现在已经能做到下面这些事,说明“模型调用与消息结构”这一关基本算过了:

  1. 能把一次调用拆成规则、输入、上下文、输出协议几个层次。
  2. 能区分自由文本、结构化输出、工具调用各自适用的场景。
  3. 知道上下文窗口为什么是预算问题,不是“能塞多少塞多少”。
  4. 遇到效果差时,能先按层排查,而不是只怪模型不聪明。
  5. 知道模型输出只是推理结果,不等于系统可直接执行的事实。

当你能做到这些,再去看 RAG、Agent、协议和平台工程,后面很多内容就会一下子顺起来。