重生之我在 Vibe Coding 时代当程序员:第十二课,Prompt 不是咒语,是可以沉淀的业务接口
上一节课我刚把变量提升里最绕的部分捋清楚:
var、let、const、函数声明、函数表达式,为什么有时候是undefined,有时候是ReferenceError,有时候又是TypeError。这一篇我继续往下走,结合线上课程资料、官方文档风格的 API 示例、技术网站里的 prompt 写法,以及我自己的复盘,开始理解 Prompt 设计和 LLM API 调用。刚开始我以为这只是"怎么把一句话问得更清楚",但复盘之后我发现,Prompt Engineering 不是写一句神奇咒语,而是在把业务任务变成稳定、可复用、可调用的接口。
这份笔记的标题叫 Prompt 设计规则 ,但里面不只有 prompt。它还穿插了 Python 的 dict、哈希表、set、hashable,以及 JavaScript 里的函数提升。刚整理这些资料的时候我有点散,感觉自己在不同知识点之间跳来跳去。后来我把它们串起来,才发现这一节其实在讲同一件事:
text
如果我要在 Vibe Coding 时代真正使用 AI,
我不能只会"问 AI 一句话",
我还要知道数据怎么组织、接口怎么封装、prompt 怎么稳定复用、代码怎么判断是否真的运行正确。
从"最好的数据可能是 prompt 设计"说起
笔记最后有一句我后来补充过的话:
text
现在公司内部真正有价值的数据,可能不只是某段代码设计、某份功能设计书,甚至也不只是原始用户数据。
在 LLM 应用里,越来越重要的一类资产是:针对某个稳定、常用业务功能,经过反复验证和优化后沉淀下来的固定 prompt 设计。
这类 prompt 本质上是把业务经验、输出标准、边界条件和执行流程压缩成可复用的指令模板。谁能把常用场景的 prompt 设计好、维护好,谁就更容易稳定地调用模型完成业务任务。
这句话是我这次复盘之后最想保留下来的主线。
以前我会觉得公司里重要的数据是代码、需求文档、用户数据、业务流程图。但在 LLM 应用里,还有一种东西会越来越重要:一个具体业务功能背后反复打磨过的 prompt 模板。
比如"分析用户反馈"不是随口问一句:
text
帮我分析这段用户反馈
更稳定的做法是把任务、输入范围、输出格式、字段约束、无法判断时的处理方式都写进去:
text
请分析三引号中的用户反馈。
要求:
1. 只分析三引号中的文本,不要添加文本外的信息。
2. 只输出一个 JSON 对象,不要输出 JSON 以外的解释文字。
3. JSON 对象必须包含以下字段:
- main_issue:用户主要问题
- emotion:用户情绪
- urgency:紧急程度,值只能是 "low"、"medium"、"high"
- suggested_reply:客服回复建议
4. 如果无法判断某个字段,该字段值填 null。
用户反馈:
"""
这里放用户反馈
"""
这已经不是普通聊天了,更像是一个"自然语言接口"。只要这个接口稳定,后面就可以把它放进代码、工作流、Agent,变成业务系统的一部分。
先补一块底层:dict、hash table 和 set
这份资料一开始不是直接进入 prompt,而是先补了 Python 的 dict。
笔记里写到:
text
Dict 字典(python)
与 JS 中的对象字面量 key : value { } 类似
es6 新增的数组结构
- Set
- Map HashMap 哈希表 hash
Python 的 dict 可以理解成字典,也就是通过 key 找 value。它和 JS 里的对象字面量有相似之处:都是用某个名字或 key 去对应一个值。
资料里的例子是:假设我要根据同学的名字查找成绩。如果不用字典,可以用两个列表:
text
一个列表存储名字,一个列表存储成绩,根据两者下标一一对应。
比如:
python
names = ['小刘', '小王']
scores = [95, 88]
如果我要找"小刘"的成绩,就要先在 names 里找到"小刘"在哪个位置,再去 scores 里拿同一个下标的值。这个方法能做,但它的问题是查找过程不直接。
用 dict 就更自然:
python
scores = {
'小刘': 95,
'小王': 88
}
scores['小刘']
它表达的就是:我知道学生姓名这个 key,要查对应成绩这个 value。
原笔记里写 dict 的特点是:
text
查找和插入、删除的速度极快,不会随 key 的增加而变慢
需要占据大量的内存,内存消费多
这里我后来补了一个更准确的理解:dict 平均查找很快,但不能死记成"永远 O(1)、永远不会变慢"。真实的哈希表要处理哈希冲突,极端情况下性能也可能受影响。更稳的说法是:在常见情况下,dict 能提供非常快的 key -> value 查找。
哈希表不是两个并排列表
笔记里对 HashTable 的描述是:
text
哈希表是一个基于键值对存储数据的数据结构
key 唯一,进行哈希(计算函数)运算,得到一个唯一索引
索引指向一个存储位置,存储着对应的 value
当需要根据 key 查找 value 时,直接根据 key 进行哈希运算,得到索引,再根据索引从存储位置找到对应的 value
这个描述适合入门,但我后来要修正一个点:不要把它理解成"hash 一定得到唯一索引"。真实世界里会有哈希碰撞,也就是不同 key 可能算到同一个位置附近。哈希表真正厉害的地方不是"永远没有冲突",而是:
text
通过 hash 快速定位,再通过冲突处理保证最终能找到正确的 value。
所以我现在会这样记:
text
dict 是基于哈希表的映射结构。
key 经过 hash 快速定位到存储位置,再找到对应 value。
真实哈希表需要处理哈希碰撞,所以不能简单说 hash 一定得到唯一索引。
这也解释了为什么 dict 用在"需要高速查找元素"的地方很合适。
set:只关心有没有,不关心对应值
笔记里接着讲了 set:
text
和 dict 类似,也是一组 key 的集合,但不存储 value
由于 key 不能重复,所以在 Set 中没有重复的元素
set 和 dict 唯一的区别在于没有存储对应的 value,set 的原理和 dict 一样
这部分我现在会用一句话区分:
text
dict 管"key 到 value 的映射"。
set 管"某个元素是否存在"。
list 管"有顺序的一串元素"。
比如一个学生系统里有三个需求:
text
1. 根据学生姓名查成绩
2. 判断某个学生是否已经报名
3. 保存学生报名顺序
对应选择就是:
text
1. 根据学生姓名查成绩 -> dict
2. 判断某个学生是否已经报名 -> set
3. 保存学生报名顺序 -> list
这比背概念更有用。只要先问"我现在要解决哪类问题",数据结构就能自然选出来。
hashable:变量重新赋值,不等于对象本身可变
原笔记里有一个关键词:unhashable type。
原笔记写到:
text
dict 的 key 必须可哈希的(hashable),也就是不可变的类型
key[1,2,3] 可变的,内容可以随意添加
hash 的 key 必须要唯一,字符串
dict 靠 key 计算 value 的存储位置,如果 key 可变,每次计算出来的结果可能不同,dict 就会混乱
通过 key 计算位置的算法,称为哈希算法(hash)
这里我后来在练习里暴露了一个混淆:我一开始差点以为 123 这个数字可能"能被修改"。后来才分清楚:
python
x = 123
x = 456
这不是把数字对象 123 改成了 456,而是让变量 x 从指向 123 改成指向 456。数字对象本身没有被原地修改。
所以这些可以作为 dict 的 key:
python
"a" # str,不可变,可以
123 # int,不可变,可以
(1, 2) # tuple,且内部元素都 hashable,可以
这些不可以:
python
[1, 2] # list 可变,不可以
{"name": "小刘"} # dict 可变,不可以
(1, [2, 3]) # 外层是 tuple,但里面有 list,不可以
注意 (1, [2, 3]) 这个例子很关键。它说明不是看到外层是 tuple 就一定能做 key,还要看 tuple 里面的元素是不是也 hashable。
原笔记里还用字符串举了一个例子:str 是不可变对象。比如对字符串调用 replace,并不是把原来的字符串对象原地改掉,而是返回一个新的字符串对象。
python
s = 'abc'
new_s = s.replace('a', 'A')
s # 'abc'
new_s # 'Abc'
所以"变量可以重新赋值"和"对象本身可变"不是一回事。变量名可以改为指向新对象,但 str、int 这类对象本身不会被原地修改。
我现在会这样记 hashable:
text
能做 dict key 的对象,必须让 hash 值在生命周期内稳定,并且能配合相等性比较。
这比"不可变"更准确一点。
回到 Prompt:清晰不等于短
笔记里的 Prompt 规则是:
text
编写清晰、具体的指令
让大模型的回答符合预期,不偏离主题或出错
具体一些,为大模型提供完整的说明和背景信息(上下文)
引导大模型逐步推理
清晰具体,有什么要求
输出格式要求
分布规则
我一开始容易把"清晰"理解成"简洁",好像 prompt 越短越好。但结合课程资料和课后练习后,这个误区被纠正了:清晰不是短,清晰是可执行、可检查。
一个 prompt 是否清晰,我现在会看四件事:
text
1. 要完成什么任务
2. 输入范围在哪里
3. 输出格式是什么
4. 有什么边界、限制或判断标准
比如坏 prompt:
text
帮我总结一下这段话
更稳定的版本是:
text
请将三引号中的文本总结成一句话。
要求:
1. 只输出一句话。
2. 不要添加三引号文本中没有的信息。
3. 不要输出解释、标题或项目符号。
文本:
"""
这里放原文
"""
这里的关键不是"写得更长",而是多写出来的每一行都在降低歧义:只总结哪里?输出几句话?能不能添加背景?能不能输出解释?
第一种 prompt:圈定输入范围
notebook 里第一个 prompt 优化例子是总结文本。
原始文本是:
python
text = f"""
你应当通过尽可能清晰、具体的指令,来明确你希望模型完成的任务。
这能引导模型产出符合预期的结果,同时降低回复内容偏离主题或出现错误的概率。
不要把编写清晰的提示词和精简提示词混为一谈。
很多时候,篇幅更长的提示词能为模型提供更完整的说明与背景信息,进而让输出内容更加详实、贴合需求。
"""
notebook 里写的 prompt 是:
python
prompt = f"""
将三个反引号之间的文本总结成一句话
'''{text}'''
"""
这个例子要表达的是:prompt 里要把输入范围圈出来。模型不是只看"总结一句话",还要知道到底总结哪一段。
不过这里也有一个课后要修正的小细节:代码里用了中文弯引号 ''',它看起来像三引号,但不是稳定的 Python/Markdown 分隔符。后面我自己写 f-string prompt 时,也犯过类似问题。更稳的写法是:
python
prompt = f"""
请将三引号中的文本总结成一句话。
要求:
1. 只输出一句话。
2. 不要添加三引号文本中没有的信息。
文本:
\"\"\"
{text}
\"\"\"
"""
如果外层已经是 f"""...""",里面想显示三引号,就可以写成 \"\"\"。变量要用 {text} 插进去。
第二种 prompt:把输出格式说死
notebook 里的第二个例子是列出四大名著:
python
prompt = f"""
请列出四大名著并标注对应的作者与书籍类型
使用 json 格式呈现,需要包含以下字段:book_id(书籍编号), book_name(书籍名称),
author(作者), book_type(书籍类型),desc(书籍描述)
"""
print(get_completion(prompt))
这个例子对应的技巧是:描述输出格式。
如果只是写:
text
列出四大名著
模型可能输出自然语言段落,也可能输出项目符号。它不一定知道我要的是结构化数据。
更稳的写法是:
text
请帮我列出中国四大名著的信息。
要求:
1. 输出 JSON 数组。
2. 每本书是数组中的一个对象。
3. 每个对象必须包含以下字段:book_id、book_name、author、book_type、desc。
4. 不要输出 JSON 以外的解释文字。
课后练习里我一开始能写出"输出 json 格式的数据",但还漏了"JSON 数组还是 JSON 对象""不要输出 JSON 以外的解释文字"。这两个约束看起来小,但对于真正接 API 很重要。
如果后续代码要 json.loads(),模型多输出一句"当然可以,以下是......"都可能让解析失败。
第三种 prompt:把任务拆成步骤
notebook 里还有一个泡茶文本:
python
text = f"""
泡一杯茶其实很简单!首先把水烧开。烧水的同时,拿出茶杯,放入
茶包。水烧开后,将热水冲入杯中浸泡茶包。静置片刻让茶香析出。
几分钟后,根据个人口味,还可以加入糖或牛奶。这样一杯美味的
茶饮就泡好了。
"""
print(get_completion(text))
如果直接把文本丢给模型,它可能只是普通总结。但这个练习想解决的是:把一段自然语言里的操作指令重新整理成步骤。
notebook 里的 prompt 是:
python
prompt = f"""
你将收到由三引号包裹的文本,若文本中包含一系列操作指令,请按照下述格式重新整理这些指令
步骤一:...
步骤二:...
步骤三:...
...
步骤N:...
如果文本中没有操作指引,直接输出**"没有提供步骤。"**
\"\"\"{text}\"\"\"
"""
print(get_completion(prompt))
这个例子里有两个重点。
第一个是格式:
text
步骤一:...
步骤二:...
步骤三:...
...
步骤N:...
第二个是分支:如果没有步骤,就不要硬编步骤,而是输出:
text
没有提供步骤。
notebook 后面马上用另一段没有操作步骤的文本做了测试:
python
text_2 = f"""
今日阳光明媚,鸟儿欢唱。这样的好天气很适合去公园散步。
花儿竞相绽放,树木在微风中轻轻摇曳。
人们纷纷出门,享受这宜人的天气。
有人野餐,有人嬉戏,还有人悠闲地躺在草地上休憩。
这是亲近户外、领略自然之美的绝佳时刻。
"""
这个文本里没有明确操作步骤,所以正确输出应该是"没有提供步骤。"。这件事让我意识到:prompt 不是只告诉模型"有东西时怎么做",也要告诉它"没有东西时怎么做"。
我现在会写成:
text
请整理三引号中的文本。
要求:
1. 只处理三引号中的文本。
2. 不要添加文本中没有的步骤。
3. 如果文本中包含操作步骤,请按以下格式输出:
步骤一:...
步骤二:...
步骤三:...
...
步骤N:...
4. 如果文本中不包含操作步骤,只输出:没有提供步骤。
文本:
"""
这里放原文
"""
第四种 prompt:few-shot 不是解释规则,而是给样子
notebook 里还有一个 few-shot 例子:
python
# LLM 工作的随机性
# prompt优化技巧:Few Shots 少量的样本提示
prompt = f"""
你的任务是保持统一的行文风格作答。
提问:请讲讲何为耐心。
回答:能凿出幽深峡谷的江河,源自涓涓细流;
恢弘壮阔的交响乐,起于单个音符;
精美繁复的织锦,始于一缕丝绒。
请问:请讲讲何为韧性。
"""
print(get_completion(prompt))
这个例子不是用一堆规则描述"请写得有诗意",而是直接给了一个样本。模型看到"耐心"的回答方式,就更容易模仿这种句式和风格来回答"韧性"。
我现在理解 few-shot 的作用是:
text
当一个任务很难用规则说清楚,或者风格比逻辑更重要时,给模型样例,比空讲要求更有效。
这也和"固定 prompt 是企业资产"连起来了。很多业务场景里的好 prompt,不只是几条规则,而是包含了高质量示例。示例越贴近业务,模型越容易稳定复现那个业务风格。
一次多步骤任务:总结、翻译、抽取 JSON
notebook 里第 7 个 cell 是一个综合任务:
python
text = f"""
在一座风光宜人的小村庄里,姐弟俩杰克和吉尔动身前往山顶的水井取水。
两人一路欢歌向上攀登,不料意外突生
------ 杰克被石头绊倒,滚下山坡,吉尔也跟着摔了下去。
二人虽受了些轻伤,还是回到了家中,得到家人温柔的安抚。
这场小意外并未磨灭他们冒险的兴致,此后他们依旧满心欢喜地四处游玩。
"""
prompt = f"""
执行以下操作:
将三个反引号内的文本概括为一句话
把这句摘要翻译成法语。
列出法语摘要中出现的所有人名
输出 json 对象,,包含字段 french_summary、num_names
答案分行展示
文本:
```{text}```
"""
print(get_completion(prompt))
这个例子把多个动作串起来:
text
1. 概括中文文本
2. 把摘要翻译成法语
3. 从法语摘要里找人名
4. 输出 JSON 对象
这就不是单一问答了,而是一个小工作流。这里我也看到了 prompt 设计中的一个问题:如果我要让输出稳定,应该把字段和格式写得更严格。比如 num_names 这个字段名看起来像"人名数量",但任务描述说"列出所有人名"。如果真要列出人名,字段可能应该叫 names;如果只要数量,才是 num_names。
这类细节在练习里看起来只是小问题,但到了真正做业务系统时很重要。字段名、字段含义、输出结构不一致,会直接影响后面的程序解析。
把模型调用封装成 get_completion
这份笔记的另一条主线是 LLM API 调用。notebook 开头先初始化 client:
python
# prompt 的模块化引入
# import OpenAi form openai
# js 抄 python
# gemini claude 接口不同
from openai import OpenAI
client = OpenAI(
api_key = '这里填自己的 API Key',
base_url = 'https://api.deepseek.com/v1'
)
client
原始 notebook 里这里写了真实 key。整理成文章时我不保留真实 key,因为 API Key 不应该进入公开文章或长期笔记里。真正要保留的是这段代码的结构:用 OpenAI SDK 的 OpenAI 创建 client,并把 base_url 指向 DeepSeek 的接口。
然后是封装函数:
python
# 参数默认值,es6 借鉴了,函数灵活
# llm 很强大,封装的特别好,就是几个接口
# completion 接口 aigc prompt 生成接口
# chat 聊天接口 要多一个参数 messages 多轮对话的信息
# chat 分角色 我们是user,llm是assistant
def get_completion(prompt , model = 'deepseek-chat'):
response = client.chat.completions.create(
model = model,
messages = [
{ "role":"user" , "content":prompt }
],
# 自由度 0:严谨 2:嘻哈
temperature = 0.5 ,
# 问题 + 回复的 token 的最大数量
max_tokens = 1024,
# 给出的回复的数量
n = 1
)
# print(response)
return response.choices[0].message.content
print(get_completion('给我写一首诗'))
这段代码里我现在会重点看 5 步数据流:
text
1. 用已经创建好的 client 调用聊天补全接口。
2. 给 create() 传入 model、messages、temperature、max_tokens、n 等参数。
3. 把普通字符串 prompt 包装进 messages:
{"role": "user", "content": prompt}
4. API 返回 response 对象,里面包含模型生成结果。
5. 取第一个候选回复的 message.content 作为最终文本返回。
以前我会以为 prompt 就是直接扔给模型的一段字符串。但 chat 接口里,它要被放进 messages:
python
messages = [
{ "role":"user" , "content":prompt }
]
也就是说,prompt 在 API 里不是裸字符串,而是一次对话消息。这个消息有角色:user。模型的返回也会以 assistant 的消息形式回来。
这也是为什么后面做多轮对话时,messages 会比单个字符串重要得多。
temperature、max_tokens、n:三个参数不要背错
原笔记里写:
text
temperature 控制模型的随机性
max_tokens 控制模型的输出长度
n 模型给出的回复的数量
课后练习里,我一开始把 max_tokens 理解成"传入 token 上限",后来纠正了:这里更应该记成模型最多能生成多少 token,也就是输出长度上限。
现在我会这样记:
text
temperature:
控制输出随机性。越高越发散,越低越稳定。
结构化 JSON、严谨任务通常调低;创意标题、发散想法可以调高。
max_tokens:
控制模型这次最多能生成多少 token,也就是输出长度上限。
如果回答中途断掉,优先检查 max_tokens 是否太低。
n:
控制一次请求返回几个候选答案。
n=1 表示只返回一个结果。
比如让我生成严格 JSON,我会让 temperature 偏低,n 通常设 1,max_tokens 要给够,避免 JSON 输出到一半被截断。
如果让我想 30 个短视频标题,temperature 可以高一点。n 可以设 1,让模型一次生成 30 个;也可以设 2-3 拿几个候选批次。但不能盲目把 n 调很大,因为多个候选之间也可能重复。
如果我让模型写 3000 字学习总结,结果总是中途断掉,最先要看的就是 max_tokens。
f-string prompt:变量要用大括号插进去
资料里还提到 Python 的:
text
f"""""" 支持多行字符,嵌套变量,可以看做字符串模板
我后来练习时写过一个不太稳的版本,大概是:
python
prompt = f'''
读取下放三引号包裹的feedback文本,也就是用户的反馈
整合用户反馈中的信息,返回一个json格式的对象
要求:
1. 只能读取三引号中feedback中的文本信息,不能加入额外的其他信息
2.只能输出json格式的对象
3.json对象中需要包含以下字段
main_issue、emotion、urgency、suggested_reply
4.字段中的urgency 只能是 "low"、"medium"、"high"
5.字段中存有无法判断的,填null
'''
...
''' feedback '''
这个版本的问题很多:
text
1. "下放"应该是"下方"。
2. 外层用了 f'''...''',里面又想用三引号包 feedback,容易把字符串边界搞乱。
3. ' ' 是中文弯引号,不是 Python 稳定识别的引号。
4. feedback 没有用 {feedback} 插入。
5. 没有明确"不要输出 JSON 以外的解释文字"。
更稳的写法是:
python
prompt = f"""
请读取下方三引号中的用户反馈,并从中抽取结构化信息。
要求:
1. 只分析三引号中的文本,不要添加文本外的信息。
2. 只输出一个 JSON 对象,不要输出 JSON 以外的解释文字。
3. JSON 对象必须包含以下字段:
- main_issue
- emotion
- urgency
- suggested_reply
4. urgency 的值只能是 "low"、"medium"、"high"。
5. 如果无法判断某个字段,该字段值填 null。
用户反馈:
\"\"\"
{feedback}
\"\"\"
"""
这里最关键的是 {feedback}。因为外层用了 f"""...""",所以变量要用 {} 插进去。
这其实就是把 prompt 模板变成了代码里的可复用结构。业务文本不同,但指令、字段、边界可以保持一致。
回到 JS:函数声明、函数表达式和同名覆盖
这个目录里还有两个 JS 小文件,正好接上上一篇变量提升。
1.js 是:
js
showName();
var showName = function() {
console.log(2);
}
function showName() {
console.log(1);
}
这段代码的关键是:
text
准备阶段,function showName() 让 showName 绑定到完整函数对象。
var showName 这个声明也被看见了,但同名 var 声明不会把已有函数绑定改成 undefined。
执行到 showName() 时,showName 还是函数声明那个函数对象,所以输出 1。
执行到 var showName = function(){...} 后,showName 才被改成输出 2 的新函数。
2.js 是:
js
function showName() {
console.log('极客帮');
}
showName();
function showName() {
console.log('极客时间');
}
showName();
// 编译阶段,会先编译函数声明,再编译函数调用,还会有同名函数覆盖
这里是两个同名函数声明。后面的函数声明会覆盖前面的函数声明,所以两次 showName() 调到的都是后面那个函数,输出应该都是:
text
极客时间
极客时间
这部分放在 prompt/API 主题旁边,看起来有点跳。但我现在能理解它为什么重要:写 API 封装时,函数声明、函数表达式、同名覆盖都会影响代码实际执行。如果我只会让 AI 生成代码,却判断不了这些基础行为,就会很容易被"看起来能跑"的代码骗过去。
我现在怎么理解 Prompt Engineering
notebook 最后一个 cell 是:
python
prompt = f"""
介绍一下prompt engineering
"""
print(get_completion(prompt))
如果是在这次复盘前,我可能会觉得 Prompt Engineering 就是"怎么问 AI"。现在我会把它说得更具体一点:
text
Prompt Engineering 是把一个模糊需求,转化成模型可以稳定执行的任务接口。
这个接口至少包含:
text
任务:要模型做什么
输入:模型只能看哪里
格式:模型必须怎么输出
边界:什么不能做,无法判断时怎么办
示例:如果规则不好描述,就给 few-shot 样例
调用:把 prompt 放进 API 的 messages 结构里
解析:用 response.choices[0].message.content 拿到结果
所以这份笔记不是只讲了 prompt。它其实把几条线放到了一起:
text
数据结构:我怎么组织 key/value、集合、列表
Prompt:我怎么组织自然语言任务
API:我怎么把 prompt 变成一次模型调用
Hoisting:我怎么判断封装代码实际怎么执行
这几条线加起来,才是 Vibe Coding 时代真正需要的能力。
AI 可以帮我生成代码,但不能替我判断代码是否符合业务目标、运行逻辑和系统约束。学习 dict、hashable、prompt 模板、API 参数、函数提升,不是为了让我手写更多代码,而是为了让我能看懂、调试、约束和改造 AI 生成的代码。
到这里,我对这份笔记的掌握程度大概是:主线已经能串起来,但还需要继续练两个地方。
第一是 f-string 里的 prompt 模板。尤其是三引号、变量插入、JSON 输出约束,这些细节要练到不用想。
第二是结构化 prompt 的最后一圈约束。以后只要我要求 JSON,就要下意识补上:
text
只输出一个 JSON 对象 / JSON 数组
不要输出 JSON 以外的解释文字
字段必须包含哪些
字段取值范围是什么
无法判断时填 null
如果说上一篇变量提升解决的是"JS 到底怎么执行",那这一篇解决的是另一个问题:我怎么把一个业务需求,变成一个可以被模型稳定执行、被代码稳定调用的 prompt 接口。