在金融领域的 RAG(检索增强生成)和 Agent
应用中,数据预处理 是至关重要的一环。我们需要将海量的非结构化文本(新闻、公告、研报)自动分类,以便存入不同的知识库或触发不同的处理流程。
然而,通用大模型在面对专业术语时可能会产生幻觉或分类错误。今天,我们将通过一个金融文本分类 的实战案例,演示如何通过
Few-Shot Prompting(少样本提示) 和 动态消息构建 技巧,显著提升模型的分类准确率和鲁棒性。
🎯 案例背景与目标
任务:将一段金融相关的文本自动分类为以下四类之一:
- 新闻报道 (News)
- 财务报告 (Financial Report)
- 公司公告 (Company Announcement)
- 分析师报告 (Analyst Report)
挑战:
- 金融文本专业性强,语境复杂。
- 用户输入可能包含无关内容(噪音),模型需要能够识别并拒绝分类。
- 需要模型严格遵循输出格式,便于后续代码处理。
💡 核心技术点
1. Few-Shot Learning (少样本学习)
与其花费大量时间编写复杂的规则描述什么是"财务报告",不如直接给模型看几个高质量的示例(Example)。模型具有强大的模式匹配能力,看到示例后能迅速模仿分类逻辑。
2. 动态构建 Messages 列表
代码中没有将所有示例硬编码在 prompt 字符串中,而是利用 Python 循环动态构建 messages 列表。
- 优势:易于维护,可以随时从数据库加载示例;结构清晰,符合 Chat Completion API 的多轮对话格式。
3. 防御性提示 (Defensive Prompting)
在 System Prompt 中明确加入:"不清楚的分类为'不清楚'"。
- 作用:防止模型强行对无关文本(如"小明喜欢小新")进行分类,提高系统的鲁棒性。
📝 完整代码解析
python
import os
from openai import OpenAI
# 1. 初始化客户端
client = OpenAI(
api_key=os.getenv("DASHSCOPE_API_KEY"),
base_url="https://dashscope.aliyuncs.com/compatible-mode/v1",
)
# 2. 准备 Few-Shot 示例数据 (金库)
# 键(Key)是分类标签,值(Value)是典型的文本内容
examples_data = {
'新闻报道': '今日,股市经历了一轮震荡,受到宏观经济数据和全球贸易紧张局势的影响。投资者密切关注美联储可能的政策调整,以适应市场的不确定性。',
'财务报告': '本公司年度财务报告显示,去年公司实现了稳步增长的盈利,同时资产负债表呈现强劲的状况。经济环境的稳定和管理层的有效战略执行为公司的健康发展奠定了基础。',
'公司公告': '本公司高兴地宣布成功完成最新一轮并购交易,收购了一家在人工智能领域领先的公司。这一战略举措将有助于扩大我们的业务领域,提高市场竞争力。',
'分析师报告': '最新的行业分析报告指出,科技公司的创新将成为未来增长的主要推动力。云计算、人工智能和数字化转型被认为是引领行业发展的关键因素,投资者应关注这些领域的投资机会。'
}
# 3. 待测试的提问数据 (包含正常样本和噪音样本)
questions = [
"今日,央行发布公告宣布降低利率,以刺激经济增长。这一降息举措将影响贷款利率,并在未来几个季度内对金融市场产生影响。", # 预期:新闻报道
"ABC公司今日发布公告称,已成功完成对XYZ公司股权的收购交易。本次交易是ABC公司在扩大业务范围、加强市场竞争力方面的重要举措。据悉,此次收购将进一步巩固ABC公司在行业内的领先地位。", # 预期:公司公告
"公司资产负债表显示,公司偿债能力强劲,现金流充足,为未来投资和扩张提供了坚实的财务基础。", # 预期:财务报告
"最新的分析报告指出,可再生能源行业预计将在未来几年经历持续增长,投资者应该关注这一领域的投资机会", # 预期:分析师报告
"小明喜欢小新哟" # 预期:不清楚 (噪音测试)
]
# 4. 构建基础 Prompt 结构
# System Prompt: 设定角色和规则,特别是"不清楚"的处理逻辑
messages = [
{"role": "system", "content": "你是金融专家,将文本分类为['新闻报道', '财务报告', '公司公告', '分析师报告'],不清楚的分类为'不清楚'"},
]
# 5. 动态注入 Few-Shot 示例
# 遍历示例数据,交替添加 user (文本) 和 assistant (标签) 消息
# 这种结构让模型清晰地看到"输入->输出"的映射关系
for key, value in examples_data.items():
messages.append({"role": "user", "content": value})
messages.append({"role": "assistant", "content": key})
# 此时 messages 列表结构如下:
# [System, User(例1), Assistant(标签1), User(例2), Assistant(标签2), ...]
print("🚀 开始金融文本分类测试...\n")
# 6. 循环测试每一个问题
for i, q in enumerate(questions, 1):
# 构造当前请求的消息列表
# 技巧:使用 messages + [新user消息],保留所有历史示例作为上下文
current_messages = messages + [{"role": "user", "content": f"按照示例,回答这段文本的分类类别:{q}"}]
try:
response = client.chat.completions.create(
model="qwen-plus",
messages=current_messages,
temperature=0.0 # 🔥 关键参数:设置为0以获得确定性最高的输出,适合分类任务
)
result = response.choices[0].message.content.strip()
print(f"[测试 {i}] 输入:{q[:30]}...")
print(f" 分类结果:✅ {result}\n")
except Exception as e:
print(f"[测试 {i}] 发生错误:{e}\n")
结果

🔍 深度原理解析
为什么这样做比直接问效果好?
❌ 错误做法 (Zero-shot)
直接问:"请把这段话分类:'央行宣布降息...'"。
- 风险:模型可能输出"宏观经济新闻"、"货币政策"等不在我们预设列表中的标签,导致后续代码报错。或者对于"小明喜欢小新"这种话,模型可能会强行编造一个分类。
✅ 正确做法 (Few-shot + System Constraint)
- System Prompt 约束 :明确限定输出空间
['新闻报道', '财务报告', '公司公告', '分析师报告', '不清楚']。 - 示例引导 :通过 4 个高质量示例,让模型学习:
- 提到"股市"、"美联储" ->
新闻报道 - 提到"资产负债表"、"盈利" ->
财务报告 - 提到"宣布"、"并购" ->
公司公告 - 提到"行业分析"、"未来增长" ->
分析师报告
- 提到"股市"、"美联储" ->
- 边界测试:示例中隐含了"只有金融相关才分类"的逻辑,配合 System Prompt 的"不清楚"指令,完美处理了第 5 个测试用例。
关键参数:temperature=0.0
在代码中(建议添加),我们将 temperature 设置为 0.0。
- 原因 :分类任务是确定性任务,不需要模型的创造力。
- 效果:设置越低,模型输出越稳定,重复运行结果一致,且更严格遵守指令。
🚀 总结
本案例展示了提示词工程在垂直领域应用中的巨大威力:
- Few-Shot 是解决特定领域分类任务的最快路径。
- System Prompt 是控制模型行为边界的护栏。
- 动态构建上下文 让代码灵活且可扩展。
- Temperature 调优 确保了生产环境的稳定性。