阶段 2:LLM 应用基础学习日志

主题:模型 API、消息结构、流式输出、Prompt、Structured Outputs、Function Calling、参数校验与错误兜底
适用阶段:RAG / Tool Calling / Agent 之前的关键基础阶段
学习目标:从“会写提示词”升级到“能稳定构建 LLM 应用”


一、这一阶段到底在学什么

阶段 2 的核心,不是“会聊天”,而是“会把模型接入程序系统”。


二、这一阶段的总认知框架

把一次 LLM 请求理解成下面这个结构:

用户任务
  ↓
指令(instructions / system behavior)
  ↓
输入(input / messages / items)
  ↓
模型生成
  ↓
输出约束(文本 / JSON / schema / tool call)
  ↓
程序解析
  ↓
工具执行 / 数据查询 / 结果展示
  ↓
错误处理 / 重试 / fallback

所以,真正的工程化 LLM 应用,不只有一个 prompt,至少包含:

  • 指令设计
  • 上下文管理
  • 输出格式约束
  • 工具协议
  • 参数校验
  • 异常处理
  • 状态管理

三、第一部分:模型 API、消息结构、流式输出

3.1 Responses API 是当前主线

当前学习 OpenAI API 时,建议把 Responses API 当成主线来学。

它可以统一处理:

  • 普通文本生成
  • 多轮上下文
  • structured outputs
  • function calling
  • 内置工具
  • conversation state

你不要再把“LLM 调用”只理解成旧式的 messages=[...] 聊天接口。

在新的接口思路里,更应该理解为:

  • instructions:本轮高层行为约束
  • input:当前输入内容
  • tools:模型可调用的能力
  • stream:是否流式返回
  • output:模型真正的输出项集合
  • output_text:方便读取最终文本的快捷字段

3.2 最小可运行调用

Python 示例

from openai import OpenAI

client = OpenAI()

response = client.responses.create(
    model="gpt-5.4",
    instructions="你是一个简洁的技术助教。",
    input="请用一句话解释什么是 HTTP。"
)

print(response.output_text)

你要理解的点

1)instructions

它是高层指令,决定模型这次“以什么身份、什么原则、什么风格”回答。

适合放:

  • 角色
  • 行为边界
  • 风格要求
  • 输出原则

例如:

  • 你是一个严谨的技术助教
  • 回答要分点但不能过度啰嗦
  • 如果信息不足要明确说明不确定
2)input

它是当前轮实际处理的内容。

适合放:

  • 用户问题
  • 当前任务描述
  • 当前轮上下文
  • 需要分析的数据
3)response.output_text

这是最方便的读取方式,适合“只关心最终文本”的场景。

但你必须知道:

底层真正的返回不是一段字符串,而是 output items

因为模型的输出未来可能不只是文本,还可能是:

  • 消息内容
  • function call
  • tool output
  • reasoning item

这点是你进入 Agent 学习前必须先建立的思维。


3.3 不要把“模型输出”只当成一段文字

初学者常见误区:

模型输入是一段 prompt,模型输出是一段文本。

这在最简单 demo 里没问题,但一旦进入工程就不够用了。

你要逐渐建立下面这个理解:

模型输出 ≠ 单纯文本
模型输出 = 一组可被程序消费的结果项

例如在一个工具调用场景里,模型输出可能是:

  1. “我需要调用天气工具”
  2. 工具名:get_weather
  3. 参数:{"city": "Hangzhou"}
  4. 工具执行完后,模型再基于结果生成最终回答

也就是说,模型输出要能驱动程序动作,而不是只让人阅读。


3.4 消息结构:为什么要分层

初学时最容易犯的错误是把下面这些东西全糊在一个 prompt 里:

  • 系统指令
  • 用户输入
  • 历史对话
  • 工具结果
  • 输出格式要求
  • 边界规则

这会造成两个问题:

问题 1:可维护性差

你后续修改任何一个部分,都容易破坏整体行为。

问题 2:职责不清

程序分不清:

  • 哪部分是规则
  • 哪部分是任务
  • 哪部分是历史
  • 哪部分是工具结果

因此,工程化思路一定是分层:

推荐的输入分层

第 1 层:行为指令

例如:

  • 你是一个代码审查助手
  • 优先指出严重 bug
  • 输出先给结论,再给理由

第 2 层:当前任务输入

例如:

  • 下面是用户的代码
  • 请检查可能的空指针问题

第 3 层:历史上下文

例如:

  • 用户之前说系统运行在 FastAPI 中
  • 用户更关注性能,不太关注代码风格

第 4 层:工具结果

例如:

  • 数据库查询结果
  • 搜索结果
  • 文件内容

第 5 层:输出要求

例如:

  • 必须输出 JSON
  • 字段包括 risk_level, issues, suggestions

3.5 多轮上下文的本质

多轮对话不是“聊天记录滚动显示”这么简单。

它的本质是:

当前这一轮,要不要把前面的内容继续带入模型上下文。

你要解决的核心问题

  • 带哪些历史?
  • 带多少历史?
  • 历史里哪些信息才真正重要?
  • 如何降低 token 成本?
  • 如何避免旧上下文污染当前任务?

3.6 多轮上下文的两种基本思路

方式 1:手动拼接上下文

最传统的方法是自己保存聊天记录,然后下一轮再拼回去。

示意:

history = [
    {"role": "user", "content": "什么是 HTTP?"},
    {"role": "assistant", "content": "HTTP 是一种应用层协议。"},
    {"role": "user", "content": "那 404 是什么?"}
]

这种方式的优点:

  • 可控性强
  • 兼容性高
  • 好理解

缺点:

  • 容易越拼越长
  • token 成本越来越高
  • 程序端要自己管理摘要、裁剪、截断

方式 2:使用 previous_response_id

Responses API 支持把上一轮 response 作为下一轮上下文链起来。

示意:

from openai import OpenAI

client = OpenAI()

r1 = client.responses.create(
    model="gpt-5.4",
    instructions="你是一个耐心的助教。",
    input="什么是 HTTP?"
)

r2 = client.responses.create(
    model="gpt-5.4",
    previous_response_id=r1.id,
    instructions="你是一个耐心的助教。",
    input="那 404 状态码又是什么意思?"
)

print(r2.output_text)

这里有一个非常容易忽略的点

使用 previous_response_id 时,上一轮的 instructions 不会自动继承。

所以你不能误以为“上一轮设过系统行为,这一轮就自动还在”。

如果你希望行为持续稳定,通常要在新一轮里继续提供对应指令。


3.7 会话状态管理:为什么不能无脑带全量历史

上下文不是越多越好。

无脑带全量历史的问题

1)成本上升

输入 token 变多,请求更贵。

2)延迟变高

模型读更长上下文,首字节响应通常会更慢。

3)噪声变多

旧历史可能和当前问题无关,反而干扰当前回答。

4)注意力污染

模型可能抓住旧约束,忽略当前更重要的任务。

正确做法

上下文管理要做选择:

  • 保留当前任务强相关历史
  • 对长对话做摘要
  • 只保留关键事实、用户偏好、未完成事项
  • 工具输出尽量结构化保存,而不是原样塞回全部文本

你后面做 Agent 时,所谓“记忆”,本质上就是这个问题的升级版。


3.8 流式输出是什么

默认响应模式:

  • 模型生成完整回答
  • 服务端一次性返回
  • 前端再统一显示

流式输出模式:

  • 模型边生成
  • 服务端边返回增量事件
  • 前端边展示内容

用户体验上的差异非常大。

适合流式输出的场景

  • 聊天产品
  • 回答较长
  • 用户希望尽快看到首字
  • 你想提升“系统正在工作”的感知
  • 你需要边生成边渲染

不一定非流式的场景

  • 结果很短
  • 结果必须严格校验后才能展示
  • 后端流程里还要先调用多个工具
  • 更关注稳定入库而不是交互体验

3.9 流式输出不是“print 一下就行”

这是一个很常见误解。

流式输出真正会遇到的问题包括:

  • 增量内容如何拼接
  • 如何识别结束事件
  • 中途断流如何处理
  • 前端取消请求怎么办
  • 工具调用过程中如何切换展示状态
  • 结构化输出场景里是否适合边展示边解析

所以流式输出属于:

交互体验能力 + 工程处理能力

不是简单的语法问题。


3.10 流式输出的学习重点

你不需要一开始就死扣事件名细节,但要搞懂下面几个点:

1)为什么要流式

为了降低用户等待焦虑,提升首字节体验。

2)流式的本质

返回的是事件流,不是一次性完整文本。

3)程序端要做什么

  • 接收事件
  • 增量拼接
  • 判断结束
  • 处理异常
  • 将结果推送到前端

4)什么情况下不适合流式

  • 结果必须先整体校验
  • 工具链很长,中间不适合直接向用户暴露过程
  • 输出非常短,流式收益小

3.11 什么时候直接 prompt,什么时候接 tool

这个是 LLM 应用开发最重要的判断之一。

适合直接 prompt 的情况

模型自己就能完成任务,并且不需要访问外部世界。

例如:

  • 翻译
  • 改写
  • 总结
  • 提取关键词
  • 对一段文本分类
  • 解释一个稳定概念

适合接 tool 的情况

任务依赖外部数据、外部动作或确定性系统能力。

例如:

  • 查最新天气
  • 查数据库订单
  • 查用户账户余额
  • 搜索知识库
  • 调用公司内部接口
  • 发邮件
  • 创建日历事件

一句话判断原则

只靠模型记忆能完成的,优先直接 prompt。
凡是需要访问外部世界的,就应该考虑 tool。


3.12 为什么“能不用 tool 就别用”也是错的

有些初学者会想:

tool 太麻烦了,我都让模型自己回答算了。

这会导致两个大问题:

问题 1:时效性错误

比如你问:

  • 今天杭州天气怎么样
  • 我最近一笔订单状态如何

模型如果不调工具,只能“猜”。

问题 2:确定性不足

比如:

  • 做复杂数学计算
  • 查具体数据库记录
  • 执行某个业务动作

这些事情应该由程序系统完成,而不是由模型凭概率生成。

结论

LLM 负责理解与决策。
工具负责连接真实世界与确定性系统。


四、第二部分:Prompt、结构化输出、函数调用基础

4.1 为什么只会写 prompt 远远不够

很多初学者学大模型时,只关注“怎么把 prompt 写得更像咒语”。

这在 demo 阶段还凑合,但到了实际系统里不够。

因为真正要解决的问题是:

  • 输出能不能稳定解析
  • 字段能不能固定
  • 参数能不能可信
  • 工具调用能不能被程序安全执行
  • 错误时能不能自动兜底

所以你必须从“prompt 工程”升级成:

Prompt + Schema + Validation + Fallback

这才是 LLM 应用工程的基础组合拳。


4.2 什么是 Structured Outputs

Structured Outputs 的核心目标是:

让模型输出严格符合你定义的结构,而不是只“看起来像 JSON”。

这是非常关键的差别。

普通“请输出 JSON”

模型可能:

  • 漏字段
  • 多字段
  • 类型错误
  • 枚举值写错
  • 在 JSON 外面再加一段解释

Structured Outputs

你提供 schema 后,模型输出要满足这个结构要求。

这样程序才能稳定消费结果。


4.3 JSON mode 和 Structured Outputs 的区别

这是面试高频题。

JSON mode

只能尽量保证输出是合法 JSON。

它不保证:

  • 字段齐全
  • 字段名正确
  • 字段类型正确
  • 枚举范围正确
  • 嵌套结构符合预期

Structured Outputs

不仅要求是 JSON,还要求符合你定义的 JSON Schema

结论

  • 只想让输出长得像 JSON:JSON mode
  • 真正需要程序稳定解析:Structured Outputs

在工程里,优先级明显是后者更重要。


4.4 为什么 Agent 强依赖结构化输出

Agent 不是为了“说得像人”,而是为了“驱动程序继续行动”。

例如一个 Agent 系统可能要做下面这些事:

  • 判断当前任务属于哪种类型
  • 决定是否调用工具
  • 决定调用哪个工具
  • 生成工具参数
  • 接收工具结果
  • 更新状态
  • 决定是否结束循环

如果模型输出不稳定,比如:

  • 有时返回字符串
  • 有时返回半结构化文本
  • 有时字段名不一致
  • 有时漏必要字段

那整个系统的控制流就会变得非常脆弱。

所以你可以记住一句话:

对聊天产品来说,结构化输出是加分项;对 Agent 来说,结构化输出是基础设施。


4.5 JSON Schema 是什么

JSON Schema 可以理解为:

你给模型和程序共同约定的一份“数据合同”。

它定义了:

  • 允许有哪些字段
  • 每个字段是什么类型
  • 哪些字段必填
  • 枚举值有哪些
  • 是否允许多余字段
  • 嵌套对象长什么样

例如:

{
  "type": "object",
  "properties": {
    "intent": {"type": "string", "enum": ["qa", "search", "action"]},
    "confidence": {"type": "number"},
    "need_tool": {"type": "boolean"}
  },
  "required": ["intent", "confidence", "need_tool"],
  "additionalProperties": false
}

这个 schema 表达的意思

模型必须返回一个对象,并且:

  • intent 只能是 qa/search/action
  • confidence 必须是数字
  • need_tool 必须是布尔值
  • 不能偷偷返回别的字段

这就是“结构化”的真正含义。


4.6 结构化输出的典型使用场景

场景 1:信息抽取

比如从自然语言里提取:

  • 时间
  • 地点
  • 人物
  • 事件

场景 2:任务分类

比如把用户请求分类为:

  • 问答
  • 检索
  • 执行操作
  • 投诉

场景 3:前端渲染

让模型直接返回卡片渲染需要的字段。

场景 4:Agent 状态流转

让模型输出:

  • 当前状态
  • 下一步动作
  • 是否结束
  • 需要调用的工具类别

场景 5:报告结构化摘要

把总结结果固定成:

  • 核心结论
  • 风险点
  • 建议项
  • 优先级

4.7 Structured Outputs 示例思路

你不必死记所有 SDK 细节,但一定要懂下面这个模式:

目标

让模型把一句话中的事件信息抽取成结构化对象。

示例结构

from pydantic import BaseModel
from openai import OpenAI

client = OpenAI()

class CalendarEvent(BaseModel):
    name: str
    date: str
    participants: list[str]

response = client.responses.parse(
    model="gpt-4o-2024-08-06",
    input=[
        {"role": "system", "content": "Extract the event information."},
        {"role": "user", "content": "Alice and Bob are going to a science fair on Friday."}
    ],
    text_format=CalendarEvent,
)

print(response.output_parsed)

你真正要学到的东西

不是这几行代码,而是这套工程思路:

  1. 先定义 schema / Pydantic 模型
  2. 再让模型按这个结构输出
  3. 再由程序直接解析成对象
  4. 然后安全地进入后续业务逻辑

4.8 Function Calling / Tool Calling 的本质

Function calling 的本质不是“让模型会写函数”,而是:

让模型告诉你的程序:现在该调用哪个工具,以及调用参数是什么。

这个流程一般分为几步:

第 1 步:你把工具定义告诉模型

包括:

  • 工具名
  • 工具描述
  • 参数 schema

第 2 步:模型决定是否调用工具

如果当前任务需要外部能力,模型会产出一个工具调用请求。

第 3 步:你的程序执行工具

比如:

  • 查数据库
  • 调用天气 API
  • 搜索内部知识库

第 4 步:把工具结果回传给模型

模型基于工具结果继续生成。

第 5 步:模型输出最终结果

或者继续进入下一轮工具调用。

所以本质上:

模型负责决策
程序负责执行

4.9 Function Calling 和 Structured Outputs 的区别

这个一定要分清。

Structured Outputs 关注的是

最终给程序消费的回答结构

例如:

{
  "summary": "...",
  "risk_level": "high",
  "suggestions": ["...", "..."]
}

Function Calling 关注的是

要调用哪个工具,以及参数是什么

例如:

{
  "name": "get_weather",
  "arguments": {
    "city": "Hangzhou",
    "date": "today"
  }
}

一句话记忆

  • Structured Outputs:约束“回答长什么样”
  • Function Calling:约束“动作怎么执行”

4.10 工具 schema 为什么是核心能力

很多人学习 tool calling,只停留在“哦,模型能调函数”。

真正难的不是“会不会调”,而是:

你能不能把工具定义得清晰、稳定、可控。

一个好的工具 schema 至少应该包含:

  • 工具名清晰
  • 描述明确
  • 参数字段语义清楚
  • 枚举范围明确
  • 必填项明确
  • 禁止额外字段

差的工具定义会导致什么

  • 模型不知道什么时候该调用
  • 模型参数经常写错
  • 工具职责重叠,模型选错工具
  • 工具输出无法被下游稳定消费

例子:一个更合理的工具定义

{
  "type": "function",
  "name": "get_weather",
  "description": "查询指定城市某天的天气信息",
  "strict": true,
  "parameters": {
    "type": "object",
    "properties": {
      "city": {
        "type": "string",
        "description": "城市名称,例如 Hangzhou"
      },
      "date": {
        "type": "string",
        "enum": ["today", "tomorrow"]
      }
    },
    "required": ["city", "date"],
    "additionalProperties": false
  }
}

这个工具定义表达得很清楚:

  • 查询什么:天气
  • 需要什么参数:城市、日期
  • 日期允许哪些值:today/tomorrow
  • 不允许额外字段

4.11 strict: true 为什么重要

在工具调用和结构化输出中,strict 都是非常重要的设计点。

它的核心目的就是:

尽量让模型输出严格遵守你定义的 schema。

开启 strict 的意义

  • 参数更稳定
  • 字段更规范
  • 减少解析失败
  • 减少模型乱补字段
  • 降低工具误调用风险

但你也要注意

严格模式不是“万能保险”。

你程序端仍然要做:

  • 参数校验
  • 类型检查
  • 安全边界控制
  • 异常处理

因为:

模型再强,也不能取代业务代码的防线。


4.12 Prompting best practices:现阶段最实用的原则

这一阶段你不需要追求“写出多华丽的提示词”,而是要追求:

  • 任务清晰
  • 约束清晰
  • 输出清晰
  • 边界清晰

建议记住的几条原则

1)先说任务,再说要求

错误写法:

  • 说了一大堆背景,最后才告诉模型要干什么

更好的写法:

任务:请把下面这段文本总结为 3 条要点。
要求:
1. 每条不超过 20 字
2. 不要输出额外解释
3. 输出 JSON 数组

2)重要约束写明确,不要含糊

不要写:

  • 尽量简洁
  • 稍微正式一点
  • 最好是 JSON

而要写:

  • 必须输出合法 JSON
  • 只允许包含 titlesummary 两个字段
  • 不要输出 Markdown

3)把上下文和任务分开

推荐格式:

任务:...
输出要求:...
上下文:
"""
...
"""

4)负向约束不要太多

与其写一堆:

  • 不要这样
  • 不要那样
  • 不要废话
  • 不要解释

不如直接写:

  • 输出格式固定为 …
  • 仅输出字段 …
  • 缺失信息时返回 null

5)给例子往往比抽象要求更有效

特别是以下任务:

  • 分类
  • 抽取
  • 改写
  • 固定格式输出

给 1~2 个正例,通常比写很多抽象描述更稳。


4.13 一个好 prompt 的推荐模板

你是一个____助手。

任务:
请完成以下工作:____

输出要求:
1. 输出格式必须为 ____
2. 字段包括 ____
3. 不要输出 ____
4. 如果信息不足,返回 ____

上下文:
"""
____
"""

这个模板为什么有用

因为它天然把内容拆成了:

  • 角色
  • 任务
  • 输出要求
  • 上下文

这比把所有东西挤成一大段自然语言稳定得多。


五、参数校验与错误兜底

5.1 为什么这部分极其重要

很多人做 LLM demo 时,最容易忽略的就是:

  • 解析失败怎么办
  • 参数不合法怎么办
  • 工具报错怎么办
  • 超时怎么办
  • 模型拒绝怎么办
  • 结果字段不全怎么办

但到了工程里,这些才是系统能不能稳定运行的关键。

真正的 LLM 应用,不是“成功时多厉害”,而是“失败时能不能体面地活下来”。


5.2 你至少要做的三层防线

第一层:模型侧约束

用:

  • 明确 prompt
  • Structured Outputs
  • strict schema

目的:减少错误发生概率。

第二层:程序侧校验

用:

  • Pydantic
  • JSON Schema 校验
  • 类型检查
  • 业务规则检查

目的:不要盲信模型输出。

第三层:失败兜底

用:

  • 重试
  • 默认值
  • fallback 提示
  • 降级逻辑
  • 记录日志

目的:错误发生时系统不要直接崩。


5.3 常见错误类型与处理思路

1)JSON 解析失败

可能原因

  • 模型混入解释性文本
  • 括号不闭合
  • 引号格式不对
  • 输出被截断

处理思路

  • 先记录原始输出
  • 做一次轻量重试
  • 再次失败则 fallback
  • 不要 silently ignore

2)字段缺失 / 类型错误

可能原因

  • prompt 约束不够清晰
  • schema 不完整
  • 模型理解偏差

处理思路

  • Pydantic 二次校验
  • 缺必要字段则判失败
  • 非关键字段可给默认值

3)工具参数不合法

可能原因

  • 模型字段名写错
  • 枚举值不在范围内
  • 参数逻辑冲突

处理思路

  • 先校验,不通过不执行
  • 返回明确错误结果
  • 让模型或程序重新决策

4)工具执行失败

可能原因

  • 外部 API 超时
  • 网络错误
  • 数据库异常
  • 权限不足

处理思路

  • 捕获异常
  • 返回结构化错误信息
  • 允许模型基于错误决定降级回复

5)模型拒答或结果不可信

处理思路

  • 明确识别 refusal
  • 必要时改为安全提示
  • 不要强行用不可信输出继续驱动高风险动作

5.4 一个最小兜底流程模板

try:
    response = call_model(...)
    parsed = parse_output(response)
    validate(parsed)
except ParseError:
    # 解析失败,考虑重试一次
    ...
except ValidationError:
    # 参数或结构不合法,不执行敏感动作
    ...
except ToolError:
    # 工具执行失败,记录日志并降级
    ...
else:
    # 成功才进入后续业务逻辑
    ...

学习重点

你不用一开始就把兜底写得很复杂,但你要先建立思维:

模型输出必须先被“怀疑”,再被“验证”,最后才能“执行”。


六、第二阶段最核心的知识点清单

下面这份可以直接拿去复习。

6.1 模型 API

  • 知道 Responses API 是当前主线
  • 知道 instructionsinput 的职责区别
  • 知道 output_text 是快捷字段
  • 知道底层返回的是 output items

6.2 消息结构

  • 知道输入要分层
  • 知道系统指令、任务输入、历史上下文、工具结果不能乱糊
  • 知道多轮上下文本质是“带哪些历史”

6.3 上下文管理

  • 知道 previous_response_id 的基本作用
  • 知道 instructions 不会自动继承
  • 知道上下文不是越多越好
  • 知道长对话需要摘要、裁剪、筛选

6.4 流式输出

  • 知道流式是增量事件流
  • 知道它主要改善交互体验
  • 知道流式需要处理拼接、结束、异常
  • 知道并非所有场景都适合流式

6.5 Tool Calling

  • 知道什么时候该接工具
  • 知道工具调用的本质是“模型决策,程序执行”
  • 知道工具定义依赖 JSON Schema
  • 知道参数设计质量决定工具调用质量

6.6 Structured Outputs

  • 知道 JSON mode 和 Structured Outputs 的区别
  • 知道结构化输出是 Agent 的重要基础
  • 知道 schema 是数据合同
  • 知道程序端仍然要继续校验

6.7 错误处理

  • 知道模型输出不能直接信
  • 知道至少要做解析、校验、兜底
  • 知道工具失败后要能降级
  • 知道敏感动作不能因一次错误输出就直接执行

七、第二阶段的难点总结

难点 1:从“聊天思维”切到“系统思维”

初学者最难的一步,是从:

  • 我怎么问模型

切换到:

  • 我的程序怎么和模型协作

难点 2:理解“文本输出”和“结构化输出”不是一回事

模型说得通顺,不代表程序能消费。

难点 3:理解 Function Calling 和 Structured Outputs 是两种不同问题

  • 一个管“动作参数”
  • 一个管“最终结果结构”

难点 4:理解上下文管理不是简单地拼聊天记录

而是:

  • 记什么
  • 忘什么
  • 压缩什么
  • 保留什么约束

难点 5:真正重视校验和兜底

很多 demo 成功,是因为测试样例太理想。
一旦输入变脏、接口变慢、工具失败,没有兜底的系统会非常脆弱。


八、常见误区

误区 1:会写 prompt 就算会做 LLM 应用

错。提示词只是入口,不是完整系统能力。

误区 2:只要让模型输出 JSON 就稳了

错。合法 JSON 不等于符合业务 schema。

误区 3:模型输出严格了,就不需要程序校验

错。程序校验永远不能省。

误区 4:上下文越长越好

错。上下文越长,成本越高,噪声越大,污染风险越高。

误区 5:工具调用就是“模型自己执行函数”

错。模型只是生成调用请求,真正执行的是你的程序。

误区 6:流式输出只是为了酷炫

错。它主要服务于用户体验,但也会带来更复杂的工程处理。


九、面试高频问题与回答思路

9.1 什么是 Responses API?为什么它重要?

回答思路

Responses API 可以理解为当前更统一的模型调用接口。它不仅能做文本生成,还能处理多轮上下文、结构化输出和工具调用。相比只把模型当聊天接口来看,Responses API 更适合构建完整的 LLM 应用和 Agent 系统。


9.2 instructionsinput 有什么区别?

回答思路

instructions 负责高层行为约束,比如角色、风格、规则;input 负责当前轮实际任务内容。前者更像行为配置,后者更像本次请求的数据输入。


9.3 多轮上下文管理要注意什么?

回答思路

重点不是简单保存历史,而是选择性保留高价值上下文。要考虑 token 成本、噪声污染、长期记忆压缩和历史约束是否仍然有效。使用 previous_response_id 可以方便串联多轮,但也要注意上一轮的 instructions 不会自动继承。


9.4 什么时候应该用 Tool Calling?

回答思路

当任务需要访问外部实时数据、私有数据或执行外部动作时,就应该考虑 Tool Calling,比如查数据库、查天气、搜索知识库、调用内部系统。纯文本改写、总结、翻译这类任务通常直接 prompt 就够了。


9.5 Structured Outputs 和 Function Calling 有什么区别?

回答思路

Structured Outputs 用来约束模型最终输出结果的结构,方便程序解析和下游消费;Function Calling 用来约束模型调用工具时的参数结构,方便程序执行具体动作。一个偏“结果结构”,一个偏“动作协议”。


9.6 为什么不能只要求“输出 JSON”?

回答思路

因为“输出 JSON”只能保证形式上更像 JSON,不保证字段完整、类型正确、枚举合法、嵌套结构符合预期。真正要做稳定系统,需要 schema 级约束和程序端二次校验。


9.7 为什么程序端还要做校验?

回答思路

因为模型输出本质上仍然是概率生成,不能直接视为可信输入。尤其是涉及敏感动作、工具执行、状态流转时,程序必须进行类型、字段、业务规则和安全边界校验。


9.8 流式输出的价值是什么?

回答思路

流式输出的主要价值是提升交互体验,缩短用户感知等待时间,让用户尽快看到生成结果。但它也会引入增量处理、断流恢复、前后端协同等工程复杂度。


官方资料速记(便于后续复习)

建议后续复习时优先看这些官方文档:

  1. Responses API Overview
  2. Text generation guide
  3. Conversation state guide
  4. Streaming responses guide
  5. Structured outputs guide
  6. Function calling guide
  7. Using tools guide
  8. Migrate to the Responses API
  9. Prompt engineering best practices

学习建议:以后每学完一个知识点,都补一条“我自己理解的关键结论”和“一段最小可运行代码”,这样学习日志才会真正变成你的面试资产。


参考链接(官方)

  • Responses Overview: https://developers.openai.com/api/reference/responses/overview/
  • Text generation guide: https://developers.openai.com/api/docs/guides/text
  • Conversation state guide: https://developers.openai.com/api/docs/guides/conversation-state
  • Function calling guide: https://developers.openai.com/api/docs/guides/function-calling
  • Using tools guide: https://developers.openai.com/api/docs/guides/tools
  • Migrate to the Responses API: https://developers.openai.com/api/docs/guides/migrate-to-responses
  • OpenAI API docs home: https://developers.openai.com/api/docs
  • Prompt engineering best practices: https://help.openai.com/en/articles/6654000-best-practices-for-prompt-engineering-with-the-openai-api
Logo

AtomGit 是由开放原子开源基金会联合 CSDN 等生态伙伴共同推出的新一代开源与人工智能协作平台。平台坚持“开放、中立、公益”的理念,把代码托管、模型共享、数据集托管、智能体开发体验和算力服务整合在一起,为开发者提供从开发、训练到部署的一站式体验。

更多推荐