Prompt学习三:从角色提示到结构化接口
前两篇我分别在讲两件事:第一,Prompt 不是聊天技巧,而是结构化输入设计;第二,想让模型真正接进系统,就必须让输出更稳定。但继续往下学,很快会遇到第三个问题:如果目标不只是“答得差不多”,而是“行为边界清楚、输出结构稳定、结果能被程序消费”,Prompt 应该怎么继续往前走?
这也是我学习 system prompt、角色结构、Function Calling(工具调用)和 JSON Schema 之后最大的认知变化:到了这一步,Prompt 已经越来越不像“提问方式”,而更像一层写给模型看的接口定义。
从“说清楚”到“约束清楚”
如果说第一篇解决的是“怎么把任务说清楚”,第二篇解决的是“怎么让结果更稳定”,那这一篇真正解决的是:
怎样让模型不只是看起来懂了,而是按你定义的边界来工作。
这一点在工程里非常关键。
因为很多 AI 应用并不是卡在模型完全不会,而是卡在下面这些更常见的问题上:
- system prompt 写了,但只是空泛角色扮演
- 用户任务和系统规则混在一起,边界不清
- 输出偶尔是 JSON,偶尔又夹杂解释文本
- 字段名字差不多,但结构并不稳定
- 人能看懂,程序却没法可靠接入
也就是说,继续往前学 Prompt,重点已经不再是“怎么写得更像人”,而是“怎么把行为约束和输出契约定义清楚”。
system / user / assistant 不是聊天标签,而是输入分层
刚接触聊天模型时,很容易把 system、user、assistant 理解成对话界面上的角色标签。但从工程角度看,它们更像是三层不同职责的输入:
system:定义整体行为边界user:提供当前任务和输入内容assistant:承载历史回答,或者作为 few-shot 示例的一部分
这三层拆开之后,一个很重要的变化是:你开始知道什么信息应该长期稳定存在,什么信息只属于当前请求,什么内容只是给模型看的示范。
所以 system prompt 真正的作用,并不是让模型“扮演一个专家”这么简单,而是给模型设定稳定的工作边界。比如:
- 回答时是否允许扩写
- 信息不足时是否必须明确说不知道
- 输出是否必须符合某种固定结构
- 是否只能依据提供的上下文作答
当这些边界不清楚时,模型其实是在替你做决定;而一旦模型开始替你做决定,下游系统的不确定性也会一起放大。
这时候,Prompt 开始像接口设计
学到这里,我对 Prompt 的理解又往前推进了一步。
之前我更愿意把 Prompt 看成一种结构化输入;现在我会更进一步,把它看成一种给模型看的接口说明。
这个接口里通常包含四类信息:
- 这个模型要完成什么任务
- 它可以基于哪些上下文做判断
- 它必须遵守哪些边界条件
- 它应该按什么结构返回结果
如果换成熟悉的软件工程语言,这其实已经非常像:
- 方法说明
- 调用上下文
- 参数约束
- 返回协议
Prompt 到这一层之后,已经不只是“让模型听懂”,而是在定义模型和系统之间怎么交互。
JSON Schema 为什么会突然变重要
学到结构化输出和 Function Calling 这一块时,我最大的一个体感是:
JSON Schema 并不是额外知识点,而是把 Prompt 从自然语言推进到工程契约的关键工具。
一开始看 schema,很容易把它当成“多写了一层格式说明”。但如果站在工程角度看,它做的是更本质的事情:
- 定义字段有哪些
- 定义字段类型是什么
- 定义哪些字段必须出现
- 定义哪些字段只能取固定值
- 定义是否允许多余字段存在
也就是说,它不是在帮你“美化 JSON”,而是在收紧模型的输出空间。
比如一个信息抽取任务,如果你只是写:
1 | 请提取文章中的人名、地点和组织名,并输出 JSON。 |
模型当然有机会输出一个能看的结果,但问题仍然很多:
- 字段名会不会漂移?
- 某个字段缺失时怎么办?
- 是返回字符串,还是数组?
- 会不会额外加一段解释?
如果继续把结构定义清楚,情况就完全不一样了。你开始告诉模型:
people是数组locations是数组organizations是数组- 这些字段必须全部出现
- 不允许额外字段
到这一步,Prompt 的含义就从“请帮我提取一下”变成了“请按这个返回协议交付结果”。
一个很有帮助的类比:配置文件为什么也要有 schema
这次学习里,我顺手还看了一类带 schema 的 CLI 配置系统,这反而让我更容易理解 schema 在 AI 里的意义。
配置文件为什么要有 schema?因为它解决的从来都不是“文件写得漂不漂亮”,而是:
- 哪些字段合法
- 字段类型对不对
- 枚举值是不是在允许范围内
- 编辑器能不能提示
- CLI 启动时能不能验证
换句话说,schema 的价值是:把自由文本变成可验证结构。
这个思路放到 AI 里几乎是一样的。
当你要求模型返回结构化输出时,本质上也是在做一件事:把模型原本可能高度发散的自然语言结果,收敛成一个可以校验、可以消费、可以接入流程的结构化对象。
所以 schema 不是 AI 特有的新概念,它只是第一次以非常直接的方式进入了 Prompt 学习过程。
Function Calling 让我重新理解“模型会调用工具”这句话
以前看到 Function Calling(工具调用),我会下意识理解成“模型可以调用函数”。但真正把文档看进去之后,会发现这句话有一点容易误导。
更准确的说法应该是:
模型负责判断要不要用工具,以及应该传什么参数;真正执行工具的,仍然是你的程序。
这件事非常关键。
因为一旦这么理解,Function Calling 就不再神秘了。它本质上是在做下面这条链路:
- 用户给出自然语言请求
- 模型根据 tool 描述和参数 schema,产出调用意图
- 你的程序解析 arguments
- 真实函数 / API / 数据库查询被执行
- 结果再回到模型,或者直接流入业务系统
这意味着,Function Calling 解决的核心问题并不是“让模型更聪明”,而是:
- 让模型知道有哪些外部能力可用
- 让模型按约定格式提供调用参数
- 让系统能稳定接住这些参数并继续执行
一旦理解到这一步,你会发现 tool description、parameter schema、字段约束这些东西,其实都已经属于接口设计范畴了。
Structured Outputs 真正解决的是“结果怎么落地”
如果说 Function Calling(工具调用)更像是在定义“调用接口”,那么 Structured Outputs(结构化输出)更像是在定义“返回接口”。
这个区别很重要。
有些任务,你需要模型决定是否调用工具;但另外一些任务,你只是想让它直接给你一个可消费的最终结构,比如:
- 实体抽取结果
- 分类标签
- 表单填充数据
- UI 渲染所需对象
- 后续工作流的输入参数
这时候,如果输出只是“看起来像 JSON”,还远远不够。
真正可用的要求通常是:
- 字段稳定
- 类型稳定
- 缺失规则明确
- 枚举范围明确
- 没有额外字段污染
这也是我这块学习里最强烈的一个感受:
结构化输出不是格式美化,而是系统边界的一部分。
当你开始这样理解时,就不会再把“输出 JSON”当成一个小技巧,而会把它看成系统设计中的交付协议。
一个足够说明问题的例子:让模型返回可校验的对象
这一块如果只停留在概念层,确实容易显得有点空。所以我更愿意用一个很小的例子把它讲透。
假设我们要从一段自然语言里提取用户信息,目标不是“总结一下”,而是得到一个后续程序能直接消费的对象。
如果按 Structured Outputs 的思路,约束会更像这样。下面这段不是 JSON Schema 裸结构本身,而是某类 API 在接收结构化输出约束时的配置包装;这里借它来说明 schema 是怎样进入模型调用的:
1 | { |
如果输入是:
1 | Jane, 54 years old |
那我们真正想要的,不是模型自由发挥出一段解释,而是得到类似这样的结果:
1 | { |
这个例子看起来很简单,但它已经把这一块最关键的东西都串起来了:
name和age是明确字段,不允许模型临时改名age是数字,不是字符串- 两个字段都必须出现
- 不允许多余字段污染结果
一旦你开始这样定义输出,模型返回的内容就不再只是“看着像 JSON”,而是一个可以继续做校验、渲染、入库或传给下游流程的结构化对象。
如果再往前一步接程序,这个链路就会变成:
- 模型按 schema 返回 JSON
- 程序解析结果
- 校验字段是否齐全、类型是否正确
- 校验通过后,再进入业务逻辑
所以这类例子真正要说明的,不是 API 长什么样,而是:
从这里开始,Prompt 已经不是单纯在“引导回答”,而是在“定义交付结果”。
返回值如何消费,才是这一块真正落地的地方
只学习“怎么让模型返回 JSON”其实还不够,因为真正的工程问题在下一步:这个 JSON 怎么被程序消费?
这里我对这块内容最重要的理解是:
LLM 输出本质上仍然是外部输入,不能因为它看起来合理,就跳过验证。
这和普通后端开发很像。你不会因为客户端传来的 JSON “像是对的”,就直接把它当成可信对象使用;同样地,也不应该因为模型“通常挺聪明”,就省掉解析和校验。
一个更可靠的链路应该是:
- 模型返回结构化结果
- 程序解析 JSON
- 用 schema 或 validator(校验器)做校验
- 校验通过后,再进入业务逻辑
也就是说,Prompt 到这一块,真正该建立的不是“更会写”,而是下面这个完整思维:
Prompt 负责定义输出契约,Schema 负责约束结果边界,Validator 负责保证程序消费安全。
这才是 AI 应用工程里真正稳定的输出链路。
我现在会怎样判断这一类 Prompt 是否已经可用
经过这块学习之后,我会用一组比以前更工程化的问题去验收 Prompt:
- system prompt 定义的是行为边界,还是只是角色口吻?
- 当前输入里,规则、上下文、示例、真实任务有没有分层?
- 输出结构是否已经明确到程序能稳定消费?
- 字段是否有缺失策略、类型约束和边界定义?
- 如果模型多返回内容,下游系统会不会出问题?
- 这个输出是“看起来像对”,还是“真的能接进后续流程”?
如果这些问题答不上来,那这个 Prompt 即使某一次结果不错,也更像 Demo,而不是可以长期维护的工程接口。
结语
第一篇我把 Prompt 理解成结构化表达,第二篇我开始理解怎样收窄输出空间,而到了第三篇,我真正开始意识到:
Prompt 的下一步,不是继续学会几种写法,而是学会定义模型和系统之间的接口。
system prompt 负责定义行为边界,system / user / assistant 这组消息角色负责组织输入分层,schema 负责约束参数和返回值,Function Calling 和 Structured Outputs 则分别把“调用工具”和“交付结果”变成可接入系统的结构。
到这一步,Prompt 学习就已经不只是“怎么和模型说话”,而是在进入 AI 应用开发最核心的一层:如何让模型产出的内容,真正成为系统里可以被校验、被安全消费的一部分。
参考文档
- Prompt Engineering Guide - 基本概念
- Prompt Engineering Guide - ChatGPT 提示工程
- Prompt Engineering Guide - Function Calling
- JSON Schema 官方文档
- Understanding JSON Schema
- OpenAI Function Calling Guide
- OpenAI Structured Outputs Guide
- OpenAI Responses vs Chat Completions
- Zod
- Pydantic