**摘要:**本文系统梳理了黑马程序员《大模型 RAG 与 Agent 智能体项目实战教程》中【提示词工程】模块的 6 集内容,围绕金融文本处理主线,从提示词设计的六大核心技巧出发,深入对比零样本(zero-shot)与少样本(few-shot)两种思想,并依次拆解金融文本分类、JSON 格式规范、金融信息抽取、金融文本匹配四个实战案例。文章还总结了常见踩坑与排错方法,并阐明该模块与后续 RAG、Agent 开发的前后衔接关系,帮助读者建立从提示词到结构化任务落地的完整认知。
提示词工程学习总结:用 Few-shot 把大模型调教成金融文本分类、抽取与匹配的自动化流水线
目录
- 一、模块定位与学习目标
- [二、第 1 集:大模型 prompt 工程指南------六大核心技巧](#二、第 1 集:大模型 prompt 工程指南——六大核心技巧)
- [三、第 2 集:零样本 zero-shot 与少样本 few-shot 的思想](#三、第 2 集:零样本 zero-shot 与少样本 few-shot 的思想)
- [3.1 zero-shot:完全信任模型的预训练知识](#3.1 zero-shot:完全信任模型的预训练知识)
- [3.2 few-shot:给少量示例来对齐输出](#3.2 few-shot:给少量示例来对齐输出)
- [3.3 zero-shot vs few-shot 对比](#3.3 zero-shot vs few-shot 对比)
- [四、第 3 集:金融文本分类------把分类任务写成 prompt](#四、第 3 集:金融文本分类——把分类任务写成 prompt)
- [4.1 任务与标签定义](#4.1 任务与标签定义)
- [4.2 system 角色与消息结构](#4.2 system 角色与消息结构)
- [4.3 调用代码](#4.3 调用代码)
- [五、第 4 集:JSON 数据格式------让大模型输出能被程序解析](#五、第 4 集:JSON 数据格式——让大模型输出能被程序解析)
- [5.1 为什么是 JSON](#5.1 为什么是 JSON)
- [5.2 Python 中与 JSON 互转](#5.2 Python 中与 JSON 互转)
- [六、第 5 集:金融文本信息抽取------从非结构化新闻里抽字段](#六、第 5 集:金融文本信息抽取——从非结构化新闻里抽字段)
- [6.1 任务与抽取字段](#6.1 任务与抽取字段)
- [6.2 prompt 与代码](#6.2 prompt 与代码)
- [七、第 6 集:金融文本匹配------判断两段文本是否语义相关](#七、第 6 集:金融文本匹配——判断两段文本是否语义相关)
- [7.1 任务定义](#7.1 任务定义)
- [7.2 prompt 与代码](#7.2 prompt 与代码)
- 八、踩坑与排错清单
- 九、与前后模块的衔接
- 十、面试与实战常考点
相关链接
李沐深度学习191集课程全解析:模块拆解、学习路径-CSDN博客
吴恩达《面向开发者的提示词工程》-CSDN博客
吴恩达 MCP 教程(Model Context Protocol)(一)-CSDN博客
多模态大模型教程学习笔记 --- ViT · CLIP · SAM · GLIP · Stable Diffusion
一句话总结: 提示词工程的本质就是"提问的工程"------在不动模型权重的前提下,靠角色设定、指令清晰、上下文、思维链分步、输出格式约束和少量示例(Few-shot)这六板斧,把一个通用大模型引导成能稳定完成金融文本分类、金融信息抽取、金融文本匹配这三类结构化任务的专用助手,而它正是后面 RAG 与 Agent 开发的地基。
本总结对应黑马程序员《大模型 RAG 与 Agent 智能体项目实战教程》中【提示词工程】模块的 6 集内容(编号 10~15)。这一模块没有讲空泛的 prompt 八股,而是用一条贯穿始终的金融文本处理主线把理论落地:先讲清提示词设计的核心原则与零样本/少样本两种思想,再用阿里云通义千问(qwen-max)与本地 Ollama 蒸馏模型(qwen3:4b)两套环境,依次跑完金融文本分类、JSON 格式规范、金融信息抽取、金融文本匹配四个真实案例。下面按"模块定位 → 逐集拆解 → 踩坑 → 前后衔接 → 面试考点"的顺序展开。
一、模块定位与学习目标
在整套课程里,【提示词工程】夹在"前置准备 + OpenAI 库基础使用"和"RAG 开发(LangChain)"之间。它的定位非常明确:大模型 本身的结构极简------用户输入、模型输出,输出我们无法直接控制,唯一能掌控的就是输入(也就是 prompt)。 因此课程反复强调一句结论:不管是 RAG 检索增强,还是 Agent 智能体,抑或是围绕大模型的各种复杂应用开发,本质上都是"在提示词上下功夫"。一个好的提示词,甚至能让基础模型的输出效果媲美经过微调的模型。
学完本模块,应当达到三个目标:
-
说清楚什么是提示词工程,并能熟练运用至少六种提示词设计技巧;
-
理解零样本(zero-shot)与少样本(few-shot)两种思想的区别、适用场景与优劣;
-
能独立用 OpenAI 兼容 SDK(
openai库)组装messages列表,完成金融文本分类、JSON 格式信息抽取、金融文本匹配三类任务,并能在云端 qwen-max 与本地 qwen3:4b 之间一键切换。
二、第 1 集:大模型 prompt 工程指南------六大核心技巧
第 1 集是纯概念课,不写代码。它先给提示词工程下定义:在不更新模型权重的前提下,探讨如何与 大模型 交互、引导其行为,从而获得所需结果的方法。 简化说就是"提问的工程"------你怎么问,决定了模型怎么答。
课上用"写贪吃蛇"做了对比演示:直接输入"写一个贪吃蛇程序",模型只能猜你的需求,默认用 Python 给一版;而把提示词写细------要求复古像素风格、用 HTML 和 JavaScript 实现、明确玩法(上下左右控制、随机生成食物且不重叠、得分增加)、视觉风格(蛇身、食物、背景、分数显示)、UI(边框、操作说明、标题)------模型就能精确产出符合预期的代码。结论是:你描述得越清晰、越详细,模型的回答就越贴合你的想法。
在此基础上,课程总结了六个可复用的提示词设计技巧:
| 技巧 | 核心做法 | 课程中的例子 |
|---|---|---|
| 详细描述 | 把需求、约束、风格、长度都写清楚 | "用温柔的语气写情书,500 字以上" |
| 角色设定 | 让模型扮演某个专业身份 | "你是面试官,给我出题,我答完再出下一题" |
| 分隔符区分输入 | 用三引号、中括号、XML 标签把待处理原文与指令隔开 | "用 20 个字总结由三引号包围的文本" |
| 指定任务步骤 | 把复杂任务拆成步骤一、步骤二,引导模型按逻辑执行 | 分步响应,增强逻辑性与指向性 |
| 提供示例 | 给一两个"输入→输出"样例,对齐风格与格式 | 给"听君一席话胜似一席话"再让模型仿写废话文学 |
| 使用参考文本 | 把标准答案/知识库塞进上下文,让模型从中抽取答案,降低幻觉 | "请在我给的参考资料内作答"------这正是 RAG 的雏形 |
其中第六条"使用参考文本"专门用来对付大模型的幻觉(一本正经地胡说八道):把标准答案给模型,要求它只在给定文本里挑选或抽取答案,它就不会自由发挥。课程预告:这套"给知识库、让模型在知识库内找答案"的做法,后面学 LangChain 时就是 RAG。
下面是一组"好 prompt vs 坏 prompt"的对照,体会一下同一件事两种问法的差别:
| 维度 | 坏 prompt(模糊) | 好 prompt(精准) |
|---|---|---|
| 任务 | 写个游戏 | 用 HTML+JavaScript 写复古像素风贪吃蛇,方向键控制、食物不与蛇身重叠、实时计分 |
| 角色 | (无) | 你是一名资深金融分析师,擅长把金融新闻归类 |
| 原文与指令 | 指令和待总结文字混在一起 | 用 """ 把待总结文本单独包围,再下总结指令 |
| 输出 | (无约束) | 只输出"是"或"不是",不要解释 |
| 缺失值 | (不说明) | 若原文未提及,一律填"原文未提及" |
三、第 2 集:零样本 zero-shot 与少样本 few-shot 的思想
从第 2 集开始进入金融实战。课程先交代了案例背景:要对金融领域数据做基本分析,落地为三个业务场景------金融文本分类、金融信息抽取、金融文本匹配。模型默认选用阿里云通义千问在线模型(课上用 qwen-max,即千问 3 max),每个模型都有 100 万 token 的免费额度,额度用完换一个同系列模型即可,因为对这类任务来说,千问系列模型效果基本相当。
设计提示词时,本案例统一借用两种思想:zero-shot(零样本)和 few-shot(少样本)。
3.1 zero-shot:完全信任模型的预训练知识
zero-shot 在提示词层面的含义是:不提供任何示例,仅用自然语言描述任务的要求、目标和约束,让模型直接出结果。 它依赖的是模型在预训练阶段学到的能力,把这种能力"属性迁移"到新任务上。
课程用"认动物"打比方:模型训练时认识马(四脚兽)、老虎(有条纹)、熊猫(黑白色),但没见过斑马。这时只要用语言告诉它"斑马是四脚兽、有黑白色条纹",模型就能把已知属性(四脚兽、条纹、黑白色)组合迁移,从而认出斑马。落到 prompt 上的真实例子是:
text
请判断双引号包围的用户评论中的情感倾向,只输出"正面"或"负面"。
评论:"这款代餐的饱腹感强,不柴,很推荐。"
请判断双引号包围的用户评论中的情感倾向,只输出"正面"或"负面"。 评论:"这款代餐的饱腹感强,不柴,很推荐。"
模型没有在训练里直接见过这句话,但它认识"饱腹感强""不柴""推荐"这些正向词,做属性迁移后输出"正面"。
3.2 few-shot:给少量示例来对齐输出
few-shot 则是:在提问时附带少量"输入→输出"示例,让模型照着示例的格式和判断标准处理新问题。 训练层面的比方是:给企鹅三张样本图,新图虽然不完全一样,模型通过相似度匹配,发现新图与企鹅样本得分最高,于是判为企鹅。
prompt 层面的真实例子(抽取产品名称和核心卖点,要求输出 JSON):
text
请从广告语中抽取"产品名称"和"核心卖点"两个字段,输出 JSON。
示例1:
输入:苹果电脑高效节能,性能强大,适合牛马工作使用。
输出:{"产品名称": "MacBook Pro", "核心卖点": "高效节能、性能强大"}
示例2:
输入:联想笔记本畅玩游戏无压力。
输出:{"产品名称": "联想笔记本", "核心卖点": "畅玩游戏无压力"}
现在请处理:
输入:华为 MatePad Pro 高清大屏,长效续航,你的好帮手。
请从广告语中抽取"产品名称"和"核心卖点"两个字段,输出 JSON。 示例1: 输入:苹果电脑高效节能,性能强大,适合牛马工作使用。 输出:{"产品名称": "MacBook Pro", "核心卖点": "高效节能、性能强大"} 示例2: 输入:联想笔记本畅玩游戏无压力。 输出:{"产品名称": "联想笔记本", "核心卖点": "畅玩游戏无压力"} 现在请处理: 输入:华为 MatePad Pro 高清大屏,长效续航,你的好帮手。
有了这两个示例,模型自然会输出 {"产品名称": "华为 MatePad Pro", "核心卖点": "高清大屏、长效续航"}。
3.3 zero-shot vs few-shot 对比
| 对比维度 | zero-shot(零样本) | few-shot(少样本) |
|---|---|---|
| 是否给示例 | 不给任何示例 | 给少量"输入→输出"样例 |
| 依赖什么 | 完全依赖模型预训练知识与属性迁移 | 预训练知识 + 示例对齐 |
| prompt 长度 | 短、省 token | 较长、示例占用上下文 |
| 优点 | 简洁、通用、无需准备标注数据 | 输出格式/标准更可控,对齐精度高 |
| 缺点 | 遇细分标签、特殊格式容易跑偏 | 示例不够或不典型时会误导模型 |
| 适用场景 | 模型熟悉的常见任务(如情感判断) | 自定义标签、自定义 JSON 字段、严格格式输出 |
一句话记忆:zero-shot 是"直接问,相信模型懂";few-shot 是"先做给你看,你照着办"。 后面三个金融案例,全部采用 few-shot------因为它们都涉及自定义标签和严格输出格式。
四、第 3 集:金融文本分类------把分类任务写成 prompt
4.1 任务与标签定义
任务是:给若干段来自金融领域的文本,让模型判断它属于哪一类。课程定义了四个主类别,外加一个兜底类别:
| 标签 | 含义 / 判定标准 |
|---|---|
| 新闻报告 | 对市场、行业、事件的新闻性报道 |
| 公司公告 | 上市公司对外发布的正式公告 |
| 财务公告(财务报告) | 涉及财务数据、财报、经营数据的公告 |
| 分析师报告 | 分析师出具的分析、点评、研报 |
| 不清楚(未知) | 无法归入上述四类时的兜底,不要硬猜 |
设计提示词要抓住两个点:第一,向模型解释"我们认为的文本分类是什么";第二,指定它按什么格式输出。 这两点都靠 few-shot 的对话历史来传递------与其口头解释,不如直接给几个"用户问→模型答"的样例。
4.2 system 角色与消息结构
课程的 system 提示词大意是:"你是金融专家,请把用户给的金融文本按【新闻报告、公司公告、财务公告、分析师报告】四类做分类;如果你判断不清楚,就回答'不清楚';下面我会给你一些示例。"然后在消息历史里塞入若干组示例(用户发一段文本,模型回一个标签),最后再放上真正要分类的问题。
messages 的目标结构如下:
python
[
{"role": "system", "content": "你是金融专家......按四个分类做分类,不清楚就回答不清楚,下面给你示例"},
{"role": "user", "content": "<示例文本1>"},
{"role": "assistant", "content": "新闻报告"},
{"role": "user", "content": "<示例文本2>"},
{"role": "assistant", "content": "财务报告"},
... 其余两组示例 ...
{"role": "user", "content": "按照示例回答这段文本的分类类别:<待分类文本>"}
]
[ {"role": "system", "content": "你是金融专家......按四个分类做分类,不清楚就回答不清楚,下面给你示例"}, {"role": "user", "content": "<示例文本1>"}, {"role": "assistant", "content": "新闻报告"}, {"role": "user", "content": "<示例文本2>"}, {"role": "assistant", "content": "财务报告"}, ... 其余两组示例 ... {"role": "user", "content": "按照示例回答这段文本的分类类别:<待分类文本>"} ]
4.3 调用代码
课程用 OpenAI 兼容 SDK 实现,核心是"先组装好 system + 示例历史,再循环把每个待分类文本拼到末尾提问":
python
from openai import OpenAI
client = OpenAI(
api_key="your-dashscope-api-key",
base_url="https://dashscope.aliyuncs.com/compatible-mode/v1",
)
示例数据:key 是标签,value 是该标签的示例文本
examples_data = {
"新闻报告": "示例:今日 A 股三大指数集体收涨,新能源板块领涨......",
"公司公告": "示例:本公司董事会决议召开临时股东大会......",
"财务报告": "示例:公司上半年营业收入同比增长 12%,净利润......",
"分析师报告": "示例:我们给予该公司'买入'评级,目标价......",
}
questions = ["待分类文本1......", "待分类文本2......", "待分类文本3......", "待分类文本4......", "待分类文本5......"]
组装 system + few-shot 历史
messages = [{"role": "system", "content": "你是金融专家,请把用户给的金融文本按【新闻报告、公司公告、财务报告、分析师报告】四类分类;若判断不清楚,直接回答'不清楚'。下面给你示例。"}]
for label, text in examples_data.items():
messages.append({"role": "user", "content": text})
messages.append({"role": "assistant", "content": label})
逐个提问
for q in questions:
resp = client.chat.completions.create(
model="qwen-max",
messages=messages + [{"role": "user", "content": f"按照示例回答这段文本的分类类别:{q}"}],
)
print(q, "=>", resp.choices[0].message.content)
from openai import OpenAI client = OpenAI( api_key="your-dashscope-api-key", base_url="https://dashscope.aliyuncs.com/compatible-mode/v1", ) # 示例数据:key 是标签,value 是该标签的示例文本 examples_data = { "新闻报告": "示例:今日 A 股三大指数集体收涨,新能源板块领涨......", "公司公告": "示例:本公司董事会决议召开临时股东大会......", "财务报告": "示例:公司上半年营业收入同比增长 12%,净利润......", "分析师报告": "示例:我们给予该公司'买入'评级,目标价......", } questions = ["待分类文本1......", "待分类文本2......", "待分类文本3......", "待分类文本4......", "待分类文本5......"] # 组装 system + few-shot 历史 messages = [{"role": "system", "content": "你是金融专家,请把用户给的金融文本按【新闻报告、公司公告、财务报告、分析师报告】四类分类;若判断不清楚,直接回答'不清楚'。下面给你示例。"}] for label, text in examples_data.items(): messages.append({"role": "user", "content": text}) messages.append({"role": "assistant", "content": label}) # 逐个提问 for q in questions: resp = client.chat.completions.create( model="qwen-max", messages=messages + [{"role": "user", "content": f"按照示例回答这段文本的分类类别:{q}"}], ) print(q, "=>", resp.choices[0].message.content)
课程运行结果:五个问题依次得到"新闻报告、公司公告、财务报告、分析师报告、不清楚"------最后一段不属于任何一类,模型如实在 system 指令约束下回了"不清楚",而不是硬猜一个标签。
切换本地 Ollama 模型 只需改两处:把 base_url 换成 http://localhost:11434/v1,把 model 换成本地已有的 qwen3:4b。注意运行前必须先启动 Ollama 服务。本地 4B 蒸馏模型跑得慢一些(跑的时候显卡占用率冲到 100%),但同样能正确完成分类任务------这说明小参数蒸馏模型把大模型的核心能力学到了手,这类任务完全够用。
五、第 4 集:JSON 数据格式------让大模型输出能被程序解析
为什么要单独插一集讲 JSON?因为后面的信息抽取案例要求模型输出可被程序直接解析的结构化数据,而不是一段自然语言。如果模型输出的是一段人话,Python 端还得靠字符串切分去抠字段,既脆弱又麻烦。
5.1 为什么是 JSON
| 数据形式 | 是否结构化 | 是否自带 schema(元数据) | 机器解析 | 主要缺点 |
|---|---|---|---|---|
| TXT 纯文本 | 否(非结构化) | 否 | 很难,靠人写规则切分 | 信息规整度差 |
| CSV | 是(逗号固定分隔) | 否 | 容易,按逗号切即可 | 列含义易丢失(孤立的"11、12、16"不知道是年龄还是身高) |
| JSON | 是 | 是,键值自带含义 | 容易,标准库直接解析 | 空间占用略大(现在硬盘便宜,用空间换可靠性很值) |
JSON 有两种结构:
-
JSON 对象 :用
{}包围,键值对(KV),键必须是字符串,值可以是数字、字符串、列表,甚至嵌套的对象或数组------对应 Python 的字典。 -
JSON 数组 :用
[]包围,里面是一堆 JSON 对象------对应 Python 的列表套字典。
5.2 Python 中与 JSON 互转
用 Python 内置的 json 库做双向转换,两个核心 API:
python
import json
d = {"name": "周杰伦", "age": 11, "hobby": ["唱歌", "打球"], "other": {"city": "台北"}}
1) Python 字典/列表 -> JSON 字符串
s = json.dumps(d, ensure_ascii=False) # ensure_ascii=False 否则中文变成 \uXXXX 乱码
print(s)
2) JSON 字符串 -> Python 字典/列表
back = json.loads(s)
print(type(back)) # <class 'dict'>
import json d = {"name": "周杰伦", "age": 11, "hobby": ["唱歌", "打球"], "other": {"city": "台北"}} # 1) Python 字典/列表 -> JSON 字符串 s = json.dumps(d, ensure_ascii=False) # ensure_ascii=False 否则中文变成 \uXXXX 乱码 print(s) # 2) JSON 字符串 -> Python 字典/列表 back = json.loads(s) print(type(back)) # <class 'dict'>
这里有一个极易踩的坑 :JSON 标准只认双引号 (为了跨语言兼容,因为很多语言里单引号和双引号是两回事)。如果你偷懒直接 str(d) 把字典转字符串,Python 会用单引号包键值,那不是合法 JSON,别的语言或 json.loads 都认不了。所以规范做法永远是 json.dumps。
六、第 5 集:金融文本信息抽取------从非结构化新闻里抽字段
6.1 任务与抽取字段
信息抽取(Information Extraction)的任务是:给一段非结构化的金融口头报告/新闻文本,让模型把指定字段抽出来,组装成 JSON(字典)。课程定义了五个字段:
| 字段名 | 说明 | 示例值 |
|---|---|---|
| 日期 | 行情对应的交易日 | 2025 年 6 月 6 日 |
| 股票名称 | 标的证券名称(含市场) | 传智教育 A 股 |
| 开盘价 | 当日开盘价格 | 66 |
| 收盘价 | 当日收盘价格 | 68 |
| 成交量 | 成交股数/手数 | 123000 |
关键设计点有两个:一是向模型解释"我们要抽哪几个字段";二是要求它按 JSON 字符串输出。 再补一条容错规则:某个字段原文没提,就填"原文未提及"------这样既不会漏键,也不会让模型瞎编。
6.2 prompt 与代码
system 提示词大意是:"请帮我完成信息抽取,我给你句子,你从句子中抽取【日期、股票名称、开盘价、收盘价、成交量】这五个字段,按 JSON 字符串输出;如果某个信息原文未提及,就用'原文未提及'表示。请参考如下示例。"
示例数据是一个列表,每项是 {"content": 句子, "answer": 结果字典}。注意:塞进消息历史的 answer 必须先用 json.dumps 转成 JSON 字符串,因为 content 只能是字符串。
import json from openai import OpenAI client = OpenAI(api_key="your-key", base_url="https://dashscope.aliyuncs.com/compatible-mode/v1") schema = ["日期", "股票名称", "开盘价", "收盘价", "成交量"] examples_data = [ {"content": "2025 年 6 月 6 日,传智教育 A 股开盘 66 元,收盘 68 元,成交 123000 手。", "answer": {"日期": "2025年6月6日", "股票名称": "传智教育A股", "开盘价": "66", "收盘价": "68", "成交量": "123000"}}, {"content": "另一份示例句子......", "answer": {"日期": "......", "股票名称": "......", "开盘价": "......", "收盘价": "......", "成交量": "......"}}, ] questions = [ "2025 年 6 月 6 日,传智教育 A 股开盘 66 元,收盘 68 元,成交 123000 手。", "2025 年 6 月 6 日,黑马程序员 A 股开盘 200 元人民币,收盘 206 元。", ] messages = [{"role": "system", "content": "请完成信息抽取:从用户句子中抽取【日期、股票名称、开盘价、收盘价、成交量】五个字段," "按 JSON 字符串输出;若某信息原文未提及,一律填\"原文未提及\"。请参考如下示例。"}] for ex in examples_data: messages.append({"role": "user", "content": ex["content"]}) messages.append({"role": "assistant", "content": json.dumps(ex["answer"], ensure_ascii=False)}) for q in questions: resp = client.chat.completions.create( model="qwen-max", messages=messages + [{"role": "user", "content": f"按照上述示例,现在抽取这个句子的信息:{q}"}], ) print(resp.choices[0].message.content)
运行结果验证:第一个句子五个字段全部抽对(日期 2025 年 6 月 6 日、传智教育 A 股、开盘 66、收盘 68、成交量 123000);第二个句子里压根没提成交量,模型严格按指令把"成交量"填成了"原文未提及"。这就是大模型处理非结构化文本的价值------靠写死的字符串切分根本抠不出来,交给模型却又快又准。同样地,切到本地 qwen3:4b(base_url 改为 http://localhost:11434/v1)也能跑对,只是慢一些。
七、第 6 集:金融文本匹配------判断两段文本是否语义相关
7.1 任务定义
文本匹配(语义相似度判断)任务:给成对的短文本,让模型判断两句话是否说的是同一件事/有关联,只输出"是"或"不是"。课程示例:
-
"股票大涨,投资者乐观" ↔ "上涨的市场让投资者满意" → 是(都讲股票上涨带来乐观情绪)
-
"油价" ↔ "智能城市" → 不是(两个领域无关联)
-
"房地产" ↔ "房地产" → 是(同主题)
提示词设计仍抓两点:解释什么叫"匹配"、约束输出只能是"是/不是"。这次用了正反两类示例 ------"是"给两对、"不是"给两对,共四个示例,让模型同时学到正例和反例的边界。还用了第 1 集讲的"分隔符"技巧:把两个待判断的句子都用中括号 [] 包起来,避免模型混淆。
7.2 prompt 与代码
examples_data 是一个字典:键是答案("是"/"不是"),值是该答案下的句子对列表。双层循环把它们展开成"用户给两句→模型回是/不是"的对话历史。
bash
from openai import OpenAI client = OpenAI(api_key="your-key", base_url="https://dashscope.aliyuncs.com/compatible-mode/v1") examples_data = { "是": [ ("股票大涨,投资者乐观", "上涨的市场让投资者满意"), ("房地产市场回暖", "楼市成交量回升"), ], "不是": [ ("国际油价波动", "智能城市建设"), ("央行加息", "某地新能源汽车销量"), ], } questions = [ ("利润上升影响房地产市场", "高利率对房地产有冲击"), ("国际油价上涨", "智慧城市试点落地"), ("股票普涨", "投资者情绪高涨"), ] messages = [{"role": "system", "content": "请完成文本匹配:用户会给出两个用中括号包围的句子,判断它们是否语义相关/同属一类," "只回答\"是\"或\"不是\",不要解释。请参考示例。"}]
for label, pairs in examples_data.items():
for t in pairs: messages.append({"role": "user", "content": f"句子一:[{t[0]}],句子二:[{t[1]}]"}) messages.append({"role": "assistant", "content": label})
for q in questions: resp = client.chat.completions.create( model="qwen-max", messages=messages + [{"role": "user", "content": f"句子一:[{q[0]}],句子二:[{q[1]}]"}], ) print(q, "=>", resp.choices[0].message.content)
运行结果为"是、不是、是",与人工判断完全一致:前一句房地产高利率相关、中间油价与智慧城市无关、最后股票普涨与投资者情绪高涨相关。切到本地 qwen3:4b 同样能给出正确结果。这个任务本质上就是后面 RAG 里"召回片段是否与问题相关"的判分雏形。
八、踩坑与排错清单
把这几个案例连起来做,最容易踩下面几个坑:
-
模型不按格式输出。 表现:让它输出 JSON,它却写成"好的,结果如下:......"再讲一段话。对策:①在 system 里明确"只输出 JSON,不要任何解释";②用 few-shot 给一个标准答案示范;③信息抽取这类强格式任务,把
answer用json.dumps(..., ensure_ascii=False)标准化后再塞进示例历史。 -
JSON 被 markdown 代码块包裹,
json.loads直接报错。 大模型经常返回json ...。对策:解析前先清洗------去掉首尾的json 和,再json.loads;或在 prompt 里加一句"直接输出 JSON,不要用代码块包裹"。 -
JSON 引号不对。 模型偶尔用单引号,或 trailing comma。对策:优先用支持容错的解析,或在 prompt 强调"必须使用双引号、标准 JSON"。中文记得
ensure_ascii=False,否则json.dumps出来全是\uXXXX。 -
少样本示例不够或不典型。 表现:分类时把"财务报告"和"公司公告"混为一谈。对策:①每个标签至少给一个示例,边界模糊的标签(公司公告 vs 财务公告)要各给一个区分度高的例子;②匹配任务要正反例都给,否则模型只会顺着一个方向答。
-
标签混淆 / 乱猜。 表现:遇到不认识的文本硬塞进某个类别。对策:在 system 里设兜底类------分类任务说"不清楚就回答不清楚",抽取任务说"没提到就填'原文未提及'",给模型一条体面的"弃权通道"。
-
切换本地模型时连不上。 表现:
Connection refused。对策:确认 Ollama 已启动、base_url是http://localhost:11434/v1、model名字与本地ollama list里完全一致(如qwen3:4b)。 -
忘记
messages是"列表套字典"。 few-shot 必须靠对话历史实现:system 打头,后面一组组user/assistant交替,最后再补一个待回答的user。结构预想要画清楚再动手 append。
九、与前后模块的衔接
-
向前衔接: 本模块建立在"前置准备(通义千问接入、环境变量保管 API Key、Ollama 本地部署)"和"OpenAI 库基础使用(客户端创建、流式输出、带历史消息调用)"之上。你会发现三个案例用的都是同一套
client.chat.completions.create(model=..., messages=...)写法,区别只在messages怎么组装。 -
向后衔接: 本模块是 RAG 与 Agent 的提示词地基。第 1 集讲的"使用参考文本、降低幻觉"就是 RAG 的思想内核------后面 LangChain 的通用提示词模板、
FewShotPromptTemplate、ChatPromptTemplate,本质上就是把本集手写的messages组装过程模板化、可复用化;信息抽取的 JSON 结构化输出、文本匹配的相关性判断,更是 RAG 召回与 Agent 工具调用里天天要用的能力。
十、面试与实战常考点
-
什么是提示词工程? 在不更新模型权重的前提下,通过设计输入(提问)来引导大模型输出预期结果的方法,俗称"提问的工程"。
-
常用提示词技巧有哪些? 详细描述、角色设定、分隔符区分输入、指定任务步骤(思维链)、提供示例(few-shot)、使用参考文本降低幻觉。
-
zero-shot 和 few-shot 的区别? 前者不给示例、纯靠语言描述和模型预训练知识;后者给少量输入→输出样例来对齐格式与判断标准,格式要求高时优先 few-shot。
-
如何让大模型稳定输出 JSON? system 强约束 + few-shot 示例 +
json.dumps(answer, ensure_ascii=False)标准化示例 + 输出端清洗 markdown 代码块后再json.loads。 -
金融文本分类怎么落地? system 设"金融专家"角色并枚举标签与兜底规则,few-shot 用对话历史给"文本→标签"样例,最后逐个待分类文本拼到
messages末尾调用。 -
信息抽取中字段缺失怎么办? 在 prompt 里约定"原文未提及"的占位符,保证 JSON 键完整、不瞎编。
-
云端模型和本地蒸馏模型怎么选? 额度充足优先云端(qwen-max,快);离线/隐私/成本敏感时用 Ollama 本地 qwen3:4b,改
base_url和model两处即可,小模型这类结构化任务也能胜任。
结语: 这一模块表面上在调金融文本分类、信息抽取、文本匹配三个小案例,实际上是在练一件事------把模糊的业务需求,翻译成一份结构清晰、带角色、带示例、带格式约束的 messages 。 把这套功夫练熟,后面学 LangChain 模板、做 RAG 检索增强、搭 Agent 智能体时,你会发现框架只是把你手写的 messages 变得更优雅,而真正决定效果上限的,永远是你提示词里的那几个字。