目录
[1.1 项目背景](#1.1 项目背景)
[1.2 项目方案](#1.2 项目方案)
[1.3 技术选型](#1.3 技术选型)
[二、前置知识:Prompt 工程基础](#二、前置知识:Prompt 工程基础)
[2.1 什么是 Prompt 工程](#2.1 什么是 Prompt 工程)
[2.2 Zero-Shot 与 Few-Shot](#2.2 Zero-Shot 与 Few-Shot)
[2.3 标准 Prompt 设计公式:角色 + 任务 + 输出](#2.3 标准 Prompt 设计公式:角色 + 任务 + 输出)
[2.4 Prompt 设计的常见技巧](#2.4 Prompt 设计的常见技巧)
[3.1 依赖安装](#3.1 依赖安装)
[3.2 配置 .env 文件](#3.2 配置 .env 文件)
[3.3 初始化大模型(通用模块)](#3.3 初始化大模型(通用模块))
[4.1 需求分析](#4.1 需求分析)
[4.2 Prompt 设计策略](#4.2 Prompt 设计策略)
[4.3 完整代码实现](#4.3 完整代码实现)
[4.4 运行结果与分析](#4.4 运行结果与分析)
[5.1 需求分析](#5.1 需求分析)
[5.2 Prompt 设计策略](#5.2 Prompt 设计策略)
[5.3 完整代码实现](#5.3 完整代码实现)
[5.4 运行结果与分析](#5.4 运行结果与分析)
[6.1 需求分析](#6.1 需求分析)
[6.2 Prompt 设计策略](#6.2 Prompt 设计策略)
[6.3 完整代码实现](#6.3 完整代码实现)
[6.4 运行结果与分析](#6.4 运行结果与分析)
[八、Prompt 设计最佳实践](#八、Prompt 设计最佳实践)
[8.1 结构化组织](#8.1 结构化组织)
[8.2 输出稳定性技巧](#8.2 输出稳定性技巧)
[8.3 Few-Shot 示例选择原则](#8.3 Few-Shot 示例选择原则)
[8.4 常见问题与应对](#8.4 常见问题与应对)
[9.1 本章总结](#9.1 本章总结)
[9.2 方法优势](#9.2 方法优势)
[9.3 局限性](#9.3 局限性)
[9.4 后续优化方向](#9.4 后续优化方向)
一、项目概述
1.1 项目背景
随着金融行业信息化的深入发展,金融数据呈爆发式增长,涵盖新闻资讯、交易数据、财务报表、用户行为等多种形态。这些数据中蕴含着大量有价值的信息,但人工处理面临效率低、成本高、一致性差等问题。
因此,金融行业开始大量引入人工智能技术来提升数据处理效率与决策质量。AI 在金融领域的应用可归纳为三大方向:
| 方向 | 核心问题 | 典型场景 |
|---|---|---|
| 风险评估 | 是否可靠? | 信用评分、反欺诈检测、风险预警 |
| 投资决策 | 是否值得投资? | 行情分析、财报解读、投资建议 |
| 客户服务 | 响应够快够准吗? | 智能客服、自动问答、工单分类 |
1.2 项目方案
本项目采用大模型 + Prompt 工程的方案,无需训练模型,通过精心设计的提示词直接引导大模型完成三类核心任务:
- 1.金融文本分类 --- 判断文本属于哪种类别
- 2.金融信息抽取 --- 从文本中提取结构化字段
- 3.金融文本匹配 --- 判断两段文本语义是否相似
1.3 技术选型
- 大模型:DeepSeek 或 Qwen(通义千问),通过 OpenAI 兼容 API 调用
- 开发框架 :LangChain(
langchain_openai) - Prompt 方法:基于 Zero-Shot / Few-Shot 的提示词设计
二、前置知识:Prompt 工程基础
2.1 什么是 Prompt 工程
Prompt 工程(Prompt Engineering)是指通过设计、优化输入给大语言模型(LLM)的提示文本,引导模型生成符合预期的输出。它是使用大模型的核心技能之一。
其核心思想是:大模型的能力是通用的,但输出质量取决于你如何"提问"。
一个好的 Prompt 应当具备以下要素:
┌──────────────────────────────────────────────┐
│ Prompt 组成要素 │
├──────────────────────────────────────────────┤
│ 1. 角色设定(Role) → 你是谁? │
│ 2. 任务定义(Task) → 你要做什么? │
│ 3. 上下文信息(Context)→ 背景知识是什么? │
│ 4. 输入数据(Input) → 具体内容是什么? │
│ 5. 输出格式(Format) → 结果长什么样? │
│ 6. 示例(Examples) → 参考标准是什么? │
└──────────────────────────────────────────────┘
2.2 Zero-Shot 与 Few-Shot
根据 Prompt 中是否包含示例,可以将提示策略分为两类:
Zero-Shot(零样本):不提供任何示例,直接描述任务要求,依赖模型自身知识完成任务。
示例 Prompt:
"请判断以下文本属于哪种金融文本类型:新闻报道、公司公告、财务报告、分析师报告。
文本:今日央行宣布降息......
请只输出类别名称。"
Few-Shot(少样本):在 Prompt 中提供若干"输入-输出"示例对,让模型通过类比来完成任务。
示例 Prompt:
"请判断文本类型。
示例1:文本:公司发布季度财报...... 类别:财务报告
示例2:文本:分析师指出...... 类别:分析师报告
现在请判断:文本:今日央行宣布降息...... 类别:?"
对比:
| 维度 | Zero-Shot | Few-Shot |
|---|---|---|
| 是否需要示例 | 不需要 | 需要 2~5 个示例 |
| 适用场景 | 模型已有足够先验知识 | 任务定义模糊、需要明确边界 |
| Prompt 长度 | 短 | 较长 |
| 输出稳定性 | 较低 | 较高 |
| Token 消耗 | 少 | 多 |
实践建议:对于分类、抽取等有明确格式要求的任务,推荐使用 Few-Shot,能显著提高输出的稳定性和准确性。
2.3 标准 Prompt 设计公式:角色 + 任务 + 输出
一个通用的 Prompt 设计公式为:
Prompt = 角色设定 + 任务描述 + 输出约束 [+ 示例]
以金融文本分类为例:
【角色】你是一个金融领域的文本分类专家。
【任务】请分析以下金融文本,将其归类为四种类别之一:
新闻报道、公司公告、财务报告、分析师报告。
【输出】请只输出一个类别名称,不要添加任何解释。
【示例】
文本:本公司年度报告显示盈利增长...... → 类别:财务报告
文本:分析师预计科技行业将持续增长...... → 类别:分析师报告
2.4 Prompt 设计的常见技巧
在实际应用中,以下技巧可以有效提升 Prompt 的效果:
(1)明确指令(Be Specific)
❌ 差:"分析一下这段话"
✅ 好:"请从以下文本中提取日期、公司名称、交易金额三个字段,以 JSON 格式输出"
(2)设定边界(Set Boundaries)
"请只输出类别名称,不要添加任何解释或多余文字。"
"若文本不属于以上类别,请输出'不清楚类型'。"
(3)分隔符(Delimiters)
使用明确的分隔符将指令与输入内容区分开,避免模型混淆:
"请对以下【】中的文本进行分类:
【今日央行宣布降息......】"
(4)思维链(Chain of Thought, CoT)
对于复杂推理任务,可以引导模型"一步一步思考":
"请先分析文本中的关键信息,再判断其所属类别。"
本项目中的三个任务主要运用 Few-Shot + 角色设定 + 输出约束的组合策略。
三、环境准备
3.1 依赖安装
pip install langchain langchain-openai python-dotenv
各依赖的作用:
| 包名 | 作用 |
|---|---|
langchain |
LangChain 核心框架,提供消息类型等基础组件 |
langchain-openai |
提供 ChatOpenAI 类,支持 OpenAI 兼容接口调用 |
python-dotenv |
从 .env 文件加载环境变量,避免硬编码密钥 |
3.2 配置 .env 文件
在项目根目录下创建 .env 文件,存放模型 API 的连接信息:
MODEL_API_KEY=your_api_key_here
MODEL_BASE_URL=https://api.deepseek.com
MODEL_NAME=deepseek-chat
说明:
- 如果使用通义千问,
MODEL_BASE_URL可设置为https://dashscope.aliyuncs.com/compatible-mode/v1,MODEL_NAME设置为qwen-plus或qwen-turbo。- 如果使用 DeepSeek,
MODEL_BASE_URL设置为https://api.deepseek.com,MODEL_NAME设置为deepseek-chat。- 以上 API 地址和模型名称请以官方最新文档为准。
3.3 初始化大模型(通用模块)
将大模型初始化逻辑封装为独立模块 llm_client.py,后续三个任务共用:
python
# llm_client.py
import os
from dotenv import load_dotenv
from langchain_openai import ChatOpenAI
# 定位项目根目录下的 .env 文件
env_path = os.path.join(
os.path.dirname(os.path.dirname(os.path.abspath(__file__))),
".env"
)
load_dotenv(env_path)
MODEL_API_KEY = os.getenv("MODEL_API_KEY")
MODEL_BASE_URL = os.getenv("MODEL_BASE_URL")
MODEL_NAME = os.getenv("MODEL_NAME")
my_llm = ChatOpenAI(
api_key=MODEL_API_KEY,
base_url=MODEL_BASE_URL,
model=MODEL_NAME,
# temperature 控制输出的随机性,0 表示确定性最高,分类任务建议设为 0
temperature=0
)
参数说明:
temperature:取值范围 0~2,值越小输出越确定、越保守;值越大输出越随机、越有创造性。对于分类和抽取等需要精确结果的任务,建议设为 0 或接近 0 的值。
四、任务一:金融文本分类
4.1 需求分析
目标:对单条金融文本进行类别判定,实现信息的自动归类与分流。
类别定义:
| 类别 | 说明 | 典型特征 |
|---|---|---|
| 新闻报道 | 媒体对金融事件的报道 | 包含"今日"、"据报道"等,客观叙述 |
| 公司公告 | 公司发布的官方声明 | 包含"本公司宣布"、"公告"等,正式语气 |
| 财务报告 | 公司财务数据的陈述 | 包含"资产负债表"、"盈利"、"现金流"等 |
| 分析师报告 | 分析师对行业/公司的研究判断 | 包含"预计"、"投资者应关注"等,带预测性 |
待分类文本:
python
test_sentences = [
"今日,央行发布公告宣布降低利率,以刺激经济增长。这一降息举措将影响贷款利率,并在未来几个季度内对金融市场产生影响。",
"ABC公司今日发布公告称,已成功完成对XYZ公司股权的收购交易。本次交易是ABC公司在扩大业务范围、加强市场竞争力方面的重要举措。据悉,此次收购将进一步巩固ABC公司在行业中的地位,并为未来业务发展提供更广阔的发展空间。详情请见公司官方网站公告栏",
"公司资产负债表显示,公司偿债能力强劲,现金流充足,为未来投资和扩张提供了坚实的财务基础。",
"最新的分析报告指出,可再生能源行业预计将在未来几年经历持续增长,投资者应该关注这一领域的投资机会",
]
期望输出 :['新闻报道', '公司公告', '财务报告', '分析师报告']
4.2 Prompt 设计策略
本任务采用 Few-Shot Learning 策略。核心思路是在 Prompt 中为每个类别提供一个标准示例,让模型"照葫芦画瓢"。
Prompt 结构如下:
python
┌──────────────────────────────────────────┐
│ 1. 角色设定:你是一个金融专家 │
│ 2. 任务说明:将文本归类为以下类别之一 │
│ 3. 类别列表:新闻报道/公司公告/... │
│ 4. 边界条件:不属于以上类别则输出"不清楚类型"│
│ 5. Few-Shot 示例:每类一个标准示例 │
│ 6. 待分类文本:用户输入 │
│ 7. 输出约束:只输出类别名称 │
└──────────────────────────────────────────┘
设计要点:
- 每个类别提供一个典型示例,帮助模型建立判断标准
- 示例文本应具有该类别的明显特征,避免歧义
- 明确输出格式约束,防止模型输出多余解释
4.3 完整代码实现
python
# task1_text_classification.py
import os
from dotenv import load_dotenv
from langchain_openai import ChatOpenAI
# ========== 1. 初始化大模型 ==========
env_path = os.path.join(
os.path.dirname(os.path.dirname(os.path.abspath(__file__))),
".env"
)
load_dotenv(env_path)
my_llm = ChatOpenAI(
api_key=os.getenv("MODEL_API_KEY"),
base_url=os.getenv("MODEL_BASE_URL"),
model=os.getenv("MODEL_NAME"),
temperature=0
)
# ========== 2. 定义 Few-Shot 示例 ==========
class_examples = {
'新闻报道': (
'今日,股市经历了一轮震荡,受到宏观经济数据和全球贸易紧张局势的影响。'
'投资者密切关注美联储可能的政策调整,以适应市场的不确定性。'
),
'财务报告': (
'本公司年度财务报告显示,去年公司实现了稳步增长的盈利,'
'同时资产负债表呈现强劲的状况。经济环境的稳定和管理层的有效战略执行'
'为公司的健康发展奠定了基础。'
),
'公司公告': (
'本公司高兴地宣布成功完成最新一轮并购交易,收购了一家在人工智能领域领先的公司。'
'这一战略举措将有助于扩大我们的业务领域,提高市场竞争力。'
),
'分析师报告': (
'最新的行业分析报告指出,科技公司的创新将成为未来增长的主要推动力。'
'云计算、人工智能和数字化转型被认为是引领行业发展的关键因素,'
'投资者应关注这些趋势。'
),
}
# ========== 3. 构建 Prompt ==========
def build_classification_prompt(sentence: str) -> str:
"""
构建金融文本分类的 Few-Shot Prompt。
Args:
sentence: 待分类的金融文本
Returns:
完整的 Prompt 字符串
"""
class_list = list(class_examples.keys())
class_line = "、".join(class_list)
n = len(class_list)
# 角色设定 + 任务说明 + 类别列表
prompt = (
"你是一个金融领域的文本分类专家。"
"请分析用户给出的金融领域文本,并将其归类为下列类型之一:\n\n"
f"{class_line}\n\n"
f"若文本明显不属于以上{n}类,请仅输出:不清楚类型\n\n"
"------ 参考示例 ------"
)
# 添加 Few-Shot 示例
for i, (class_type, example) in enumerate(class_examples.items(), start=1):
prompt += (
f"\n\n【示例 {i}】\n"
f"文本:\n{example}\n\n"
f"类别:{class_type}"
)
# 添加待分类文本 + 输出约束
prompt += (
"\n\n------ 待分类文本 ------\n"
f"{sentence}\n\n"
"请只输出一个类别名称(须为上述类型之一,或「不清楚类型」),不要其它解释。"
)
return prompt
# ========== 4. 定义分类函数 ==========
def classify_finance_text(sentence: str) -> str:
"""
调用大模型对金融文本进行分类。
Args:
sentence: 待分类的金融文本
Returns:
分类结果(类别名称字符串)
"""
prompt = build_classification_prompt(sentence)
response = my_llm.invoke(prompt)
return response.content.strip()
# ========== 5. 测试 ==========
if __name__ == "__main__":
test_sentences = [
"今日,央行发布公告宣布降低利率,以刺激经济增长。这一降息举措将影响贷款利率,并在未来几个季度内对金融市场产生影响。",
"ABC公司今日发布公告称,已成功完成对XYZ公司股权的收购交易。本次交易是ABC公司在扩大业务范围、加强市场竞争力方面的重要举措。据悉,此次收购将进一步巩固ABC公司在行业中的地位,并为未来业务发展提供更广阔的发展空间。详情请见公司官方网站公告栏",
"公司资产负债表显示,公司偿债能力强劲,现金流充足,为未来投资和扩张提供了坚实的财务基础。",
"最新的分析报告指出,可再生能源行业预计将在未来几年经历持续增长,投资者应该关注这一领域的投资机会",
]
print("=" * 60)
print("金融文本分类测试")
print("=" * 60)
for i, sentence in enumerate(test_sentences, 1):
result = classify_finance_text(sentence)
print(f"\n【文本 {i}】{sentence[:50]}...")
print(f"【分类结果】{result}")
print("\n" + "=" * 60)
4.4 运行结果与分析
预期输出:
python
============================================================
金融文本分类测试
============================================================
【文本 1】今日,央行发布公告宣布降低利率,以刺激经济增长。这一降息举措将影响...
【分类结果】新闻报道
【文本 2】ABC公司今日发布公告称,已成功完成对XYZ公司股权的收购交易。本次交...
【分类结果】公司公告
【文本 3】公司资产负债表显示,公司偿债能力强劲,现金流充足,为未来投资和扩...
【分类结果】财务报告
【文本 4】最新的分析报告指出,可再生能源行业预计将在未来几年经历持续增长,...
【分类结果】分析师报告
============================================================
分析:
- 文本 1 包含"央行发布公告"、"降息举措"等关键词,属于对政策事件的客观报道 → 新闻报道
- 文本 2 包含"ABC公司发布公告"、"收购交易"、"详情请见公司官方网站公告栏" → 公司公告
- 文本 3 包含"资产负债表"、"偿债能力"、"现金流"等财务术语 → 财务报告
- 文本 4 包含"分析报告指出"、"预计"、"投资者应该关注"等分析性表述 → 分析师报告
五、任务二:金融信息抽取
5.1 需求分析
目标:从非结构化金融文本中提取关键字段,将自然语言转化为机器可读的结构化数据(键值对)。
输入文本示例:
2025-02-15,股票佰笃[BD]美股开盘价10美元...最终以13美元收盘,成交量460,000。
抽取 Schema 定义:
Schema 是告诉模型"要找什么"的说明书。这里定义了 5 个关键字段:
python
schema = {
'金融': ['日期', '股票名称', '开盘价', '收盘价', '成交量']
}
| 字段 | 说明 | 示例 |
|---|---|---|
| 日期 | 交易日期 | 2025-02-15 |
| 股票名称 | 股票全称(含代码和市场) | 佰笃[BD]美股 |
| 开盘价 | 当日开盘价格 | 10美元 |
| 收盘价 | 当日收盘价格 | 13美元 |
| 成交量 | 当日成交量 | 460,000 |
期望输出(JSON 格式):
python
{
"日期": ["2025-02-15"],
"股票名称": ["佰笃[BD]美股"],
"开盘价": ["10美元"],
"收盘价": ["13美元"],
"成交量": ["460,000"]
}
设计说明:所有字段值使用列表(list)格式,是为了兼容同一字段可能出现多个值的情况(例如文本中提到多只股票)。
5.2 Prompt 设计策略
与分类任务不同,信息抽取对输出格式有严格要求(必须是 JSON)。因此我们采用 In-context Learning with Message History 的方式,利用 LangChain 的消息类型来构建对话历史:
python
System Message → 角色设定(信息提取专家)
Assistant → 示例输入文本
Assistant → 示例标准答案(JSON)
Human → 待抽取文本
为什么用消息列表而非纯文本 Prompt?
- 多数大模型 API 对
System、User、Assistant三种角色的消息有不同的处理权重 System Message用于设定全局行为规则,优先级最高- 通过
User → Assistant的示例对,模型能更清晰地学习输出格式 - 相比纯文本 Prompt,消息列表方式的输出格式稳定性更高
5.3 完整代码实现
python
# task2_information_extraction.py
import json
import os
from dotenv import load_dotenv
from langchain_openai import ChatOpenAI
from langchain_core.messages import SystemMessage, HumanMessage, AIMessage
# ========== 1. 初始化大模型 ==========
env_path = os.path.join(
os.path.dirname(os.path.dirname(os.path.abspath(__file__))),
".env"
)
load_dotenv(env_path)
my_llm = ChatOpenAI(
api_key=os.getenv("MODEL_API_KEY"),
base_url=os.getenv("MODEL_BASE_URL"),
model=os.getenv("MODEL_NAME"),
temperature=0
)
# ========== 2. 定义抽取 Schema ==========
schema = {
'金融': ['日期', '股票名称', '开盘价', '收盘价', '成交量']
}
# ========== 3. 定义 Few-Shot 示例 ==========
ie_examples = [
{
'content': (
'2023-01-10,股市震荡。股票古哥-D[EOOE]美股今日开盘价100美元,'
'一度飙升至105美元,随后回落至98美元,最终以102美元收盘,'
'成交量达到520000。'
),
'answers': {
'日期': ['2023-01-10'],
'股票名称': ['古哥-D[EOOE]美股'],
'开盘价': ['100美元'],
'收盘价': ['102美元'],
'成交量': ['520000'],
}
}
]
# ========== 4. 构建 Prompt(消息列表形式) ==========
def build_extraction_messages(sentence: str) -> list:
"""
构建信息抽取的消息列表。
Args:
sentence: 待抽取的金融文本
Returns:
包含 System、示例对话、待处理文本的消息列表
"""
messages = []
# System Message:设定角色和行为规则
system_prompt = (
"你是一位资深的金融信息提取专家。\n"
"请严格按照用户的指令,从文本中抽取指定实体,\n"
"并以标准JSON格式输出。\n"
f"需要抽取的字段:{json.dumps(schema, ensure_ascii=False)}\n"
"所有字段的值必须为列表格式,即使只有一个值。\n"
"如果某个字段在文本中找不到对应信息,其值设为空列表 []。"
)
messages.append(SystemMessage(system_prompt))
# Few-Shot 示例:User 文本 → Assistant JSON
for example in ie_examples:
messages.append(HumanMessage(example['content']))
messages.append(AIMessage(
json.dumps(example['answers'], ensure_ascii=False, indent=2)
))
# 待抽取文本
messages.append(HumanMessage(sentence))
return messages
# ========== 5. 定义信息抽取函数 ==========
def finance_info_extract(sentence: str) -> str:
"""
调用大模型从金融文本中抽取结构化信息。
Args:
sentence: 待抽取的金融文本
Returns:
JSON 格式的抽取结果字符串
"""
messages = build_extraction_messages(sentence)
response = my_llm.invoke(messages)
return response.content.strip()
# ========== 6. 测试 ==========
if __name__ == "__main__":
test_sentences = [
(
'2025-02-15,寓意吉祥的节日,股票佰笃[BD]美股开盘价10美元,'
'虽然经历了波动,但最终以13美元收盘,'
'成交量微幅增加至460,000,投资者情绪较为平稳。'
),
(
'2025-04-05,市场迎来轻松氛围,股票盘古(0021)开盘价23元,'
'尽管经历了波动,但最终以26美元收盘,'
'成交量缩小至310,000,投资者保持观望态度。'
),
]
print("=" * 60)
print("金融信息抽取测试")
print("=" * 60)
for i, sentence in enumerate(test_sentences, 1):
result = finance_info_extract(sentence)
print(f"\n【文本 {i}】{sentence}")
print(f"【抽取结果】\n{result}")
print("\n" + "=" * 60)
5.4 运行结果与分析
预期输出:
python
============================================================
金融信息抽取测试
============================================================
【文本 1】2025-02-15,寓意吉祥的节日,股票佰笃[BD]美股开盘价10美元...
【抽取结果】
{
"日期": ["2025-02-15"],
"股票名称": ["佰笃[BD]美股"],
"开盘价": ["10美元"],
"收盘价": ["13美元"],
"成交量": ["460,000"]
}
【文本 2】2025-04-05,市场迎来轻松氛围,股票盘古(0021)开盘价23元...
【抽取结果】
{
"日期": ["2025-04-05"],
"股票名称": ["盘古(0021)"],
"开盘价": ["23元"],
"收盘价": ["26美元"],
"成交量": ["310,000"]
}
============================================================
分析:
- 模型成功从自然语言文本中识别并提取了 5 个关键字段
- 文本中存在干扰信息(如"寓意吉祥的节日"、"虽然经历了波动"),模型能够正确过滤
- 输出为标准 JSON 格式,可直接被程序解析使用
后续优化方向:
- 可对模型返回的 JSON 字符串做二次解析和校验,确保格式合法
- 可增加更多 Few-Shot 示例以覆盖更多文本模式
- 可将抽取结果直接存入数据库或 DataFrame 进行后续分析
六、任务三:金融文本语义匹配
6.1 需求分析
目标:判断两段金融文本在语义层面是否表达了相同或相近的含义。
输入文本对示例:
| 编号 | 句1 | 句2 | 期望结果 |
|---|---|---|---|
| Pair 1 | 股票市场今日大涨,投资者乐观。 | 持续上涨的市场让投资者感到满意。 | 相似 |
| Pair 2 | 油价大幅下跌,能源公司面临挑战。 | 未来智能城市的建设趋势愈发明显。 | 不相似 |
分析:
- Pair 1 中,"大涨"与"持续上涨"、"乐观"与"满意"是同义替换,描述的是同一类事件 → 相似
- Pair 2 中,虽然两个句子都涉及经济/商业话题,但主题完全不同(石油 vs 智能城市) → 不相似
6.2 Prompt 设计策略
本任务采用 Message History + 二元指令 的方式:
python
System Message → "你是文本匹配助手,只回答'相似'或'不相似'"
Human + AI → 正例(相似)的 Few-Shot 示例
Human + AI → 负例(不相似)的 Few-Shot 示例
Human → 待匹配的文本对
设计要点:
- System Message 明确限定输出范围(二元选择),减少模型"话多"的问题
- 同时提供正例和负例,帮助模型理解"相似"与"不相似"的判定边界
- 负例选取需要注意:应选择表面相关但实质不同的样本,而非完全无关的样本,这样更有区分度
6.3 完整代码实现
python
# task3_text_matching.py
import os
from dotenv import load_dotenv
from langchain_openai import ChatOpenAI
from langchain_core.messages import SystemMessage, HumanMessage, AIMessage
# ========== 1. 初始化大模型 ==========
env_path = os.path.join(
os.path.dirname(os.path.dirname(os.path.abspath(__file__))),
".env"
)
load_dotenv(env_path)
my_llm = ChatOpenAI(
api_key=os.getenv("MODEL_API_KEY"),
base_url=os.getenv("MODEL_BASE_URL"),
model=os.getenv("MODEL_NAME"),
temperature=0
)
# ========== 2. 定义 Few-Shot 示例(正例 + 负例) ==========
examples = {
'相似': [
(
'公司ABC发布了季度财报,显示盈利增长。',
'财报披露,公司ABC利润上升。'
),
],
'不相似': [
(
'黄金价格下跌,投资者抛售。',
'外汇市场交易额创下新高。'
),
(
'央行降息,刺激经济增长。',
'新能源技术的创新。'
),
]
}
# ========== 3. 构建 Prompt(消息列表形式) ==========
def build_matching_messages(sentence1: str, sentence2: str) -> list:
"""
构建文本匹配的消息列表。
Args:
sentence1: 第一段文本
sentence2: 第二段文本
Returns:
包含 System、示例对话、待匹配文本对的消息列表
"""
messages = []
# System Message:设定角色和输出约束
system_prompt = (
"你是一个文本匹配助手。\n"
"现在你需要帮助我完成文本匹配任务。\n"
"当我给你两个句子时,你需要判断这两句话的语义是否相似。\n"
"只需要回答「相似」或「不相似」,不要做多余的回答。"
)
messages.append(SystemMessage(system_prompt))
# Few-Shot 示例
for label, sentence_pairs in examples.items():
for pair in sentence_pairs:
example_text = f"句1:{pair[0]}\n句2:{pair[1]}"
messages.append(HumanMessage(example_text))
messages.append(AIMessage(label))
# 待匹配文本对
input_text = f"句1:{sentence1}\n句2:{sentence2}"
messages.append(HumanMessage(input_text))
return messages
# ========== 4. 定义匹配函数 ==========
def finance_text_match(sentence1: str, sentence2: str) -> str:
"""
调用大模型判断两段金融文本是否语义相似。
Args:
sentence1: 第一段文本
sentence2: 第二段文本
Returns:
"相似" 或 "不相似"
"""
messages = build_matching_messages(sentence1, sentence2)
response = my_llm.invoke(messages)
return response.content.strip()
# ========== 5. 测试 ==========
if __name__ == "__main__":
test_pairs = [
(
'股票市场今日大涨,投资者乐观。',
'持续上涨的市场让投资者感到满意。'
),
(
'油价大幅下跌,能源公司面临挑战。',
'未来智能城市的建设趋势愈发明显。'
),
(
'利率上升,影响房地产市场。',
'高利率对房地产有一定冲击。'
),
]
print("=" * 60)
print("金融文本语义匹配测试")
print("=" * 60)
for i, (s1, s2) in enumerate(test_pairs, 1):
result = finance_text_match(s1, s2)
print(f"\n【Pair {i}】")
print(f" 句1:{s1}")
print(f" 句2:{s2}")
print(f" 匹配结果:{result}")
print("\n" + "=" * 60)
6.4 运行结果与分析
预期输出:
python
============================================================
金融文本语义匹配测试
============================================================
【Pair 1】
句1:股票市场今日大涨,投资者乐观。
句2:持续上涨的市场让投资者感到满意。
匹配结果:相似
【Pair 2】
句1:油价大幅下跌,能源公司面临挑战。
句2:未来智能城市的建设趋势愈发明显。
匹配结果:不相似
【Pair 3】
句1:利率上升,影响房地产市场。
句2:高利率对房地产有一定冲击。
匹配结果:相似
============================================================
分析:
- Pair 1:虽然用词不同,但核心语义一致(股市上涨 + 投资者正面情绪) → 相似
- Pair 2:主题完全不同(石油 vs 智能城市),尽管都属于经济领域 → 不相似
- Pair 3:"利率上升"与"高利率"、"影响"与"冲击"构成语义等价 → 相似
七、三种任务对比总结
| 维度 | 文本分类 | 信息抽取 | 文本匹配 |
|---|---|---|---|
| 输入 | 单条文本 | 单条文本 + Schema | 文本对 |
| 输出 | 类别标签 | JSON 键值对 | 相似/不相似 |
| Prompt 策略 | Few-Shot(纯文本) | Few-Shot(消息列表) | Few-Shot(消息列表) |
| 输出格式要求 | 中等(类别名) | 高(标准 JSON) | 低(二元选择) |
| 核心难点 | 类别边界模糊 | 字段识别准确 | 语义理解深度 |
| 推荐 temperature | 0 | 0 | 0 |
| 消息类型 | 单条 HumanMessage | System + Human/AI 交替 | System + Human/AI 交替 |
Prompt 设计方法对应关系:
| 方法 | 说明 | 本项目应用 |
|---|---|---|
| Zero-Shot | 无示例,直接指令 | 未单独使用(可作为基线对比) |
| Few-Shot | 提供少量示例 | 文本分类(纯文本形式) |
| In-context Learning | 通过对话历史提供上下文 | 信息抽取、文本匹配(消息列表形式) |
八、Prompt 设计最佳实践
基于本项目的三个任务,总结以下 Prompt 设计经验:
8.1 结构化组织
python
好的 Prompt 结构:
┌─ 角色设定 ──── 明确模型身份
├─ 任务说明 ──── 清晰描述目标
├─ 约束条件 ──── 限定输出范围
├─ 示例参考 ──── Few-Shot 引导
└─ 输出格式 ──── 明确期望结构
8.2 输出稳定性技巧
| 技巧 | 说明 |
|---|---|
设置 temperature=0 |
降低随机性,提高输出一致性 |
| 明确输出约束 | 如"只输出类别名称"、"以 JSON 格式输出" |
| 提供兜底选项 | 如"若不属于以上类别,请输出'不清楚类型'" |
| 使用分隔符 | 用 ------、【】 等符号将指令与内容区分开 |
8.3 Few-Shot 示例选择原则
- 1.代表性:示例应覆盖该类别的典型特征
- 2.多样性:不同示例之间应有差异,避免模型学到错误的捷径
- 3.无歧义:示例的标签应明确无争议,避免误导模型
- 4.适量:一般每个类别 1~3 个示例即可,过多会增加 Token 消耗且可能引入噪声
8.4 常见问题与应对
| 问题 | 原因 | 解决方案 |
|---|---|---|
| 输出格式不稳定 | 模型自由发挥 | 加强输出约束,提供更多格式示例 |
| 分类边界模糊 | 类别定义不清晰 | 优化类别描述,增加边界案例示例 |
| 抽取字段缺失 | 文本表述多样 | 增加更多表述方式的示例 |
| Token 超限 | Prompt 过长 | 精简示例,移除冗余描述 |
九、总结与展望
9.1 本章总结
本项目围绕金融行业的三大核心 NLP 任务,基于大模型 + Prompt 工程的方法,完成了以下工作:
| 任务 | 方法 | 核心 Prompt 策略 | 关键技术点 |
|---|---|---|---|
| 文本分类 | Few-Shot | 纯文本 Prompt + 示例 | 角色设定 + 类别示例 + 输出约束 |
| 信息抽取 | Few-Shot | 消息列表 + JSON 示例 | Schema 定义 + System/AI 消息 |
| 文本匹配 | Few-Shot | 消息列表 + 正负例 | 二元指令 + 正负例对比 |
9.2 方法优势
- 无需训练:不需要标注大量数据和训练模型,直接利用大模型的预训练知识
- 灵活可调:修改 Prompt 即可调整任务行为,迭代速度快
- 通用性强:同一套框架可迁移到其他领域的文本处理任务
9.3 局限性
- 依赖模型能力:任务效果受限于底层大模型的理解能力
- Token 限制:Few-Shot 示例占用上下文窗口,长文本场景需注意
- 输出不保证 100% 稳定:即使 temperature=0,模型输出仍可能存在偶发偏差
- 成本考量:每次调用均消耗 API Token,大规模应用需考虑成本
9.4 后续优化方向
- 1.Prompt 优化:通过 A/B 测试对比不同 Prompt 版本的效果
- 2.输出解析:增加后处理逻辑(正则匹配、JSON 校验等),提高结果可用性
- 3.批量处理:结合异步调用,实现大批量文本的高效处理
- 4.评估体系:建立准确率、召回率等指标,量化评估 Prompt 效果
- 5.RAG 增强:结合检索增强生成(Retrieval-Augmented Generation),为模型提供更多领域知识