手把手教你用大模型实现结构化信息提取:从 Pydantic 建模到少样本提示实战

在大模型应用开发中,从非结构化文本中提取结构化信息是高频需求。今天我们就来聊聊如何通过 LangChain 与 DeepSeek 模型的结合,实现精准的信息提取,全程附带代码实战,新手也能轻松上手。

一、定义清晰的提取 schema:用 Pydantic 构建数据模型

我们先需要告诉模型 “要提取什么”。通过 Pydantic 定义数据模型是最佳实践,来看这个 Person 类的设计:

python

from typing import Optional
from pydantic import BaseModel, Field

class Person(BaseModel):
    """人物信息提取模型。  
    用于从文本中提取人物相关属性,若信息未知或无法提取,字段值可为空。  
    DeepSeek模型请严格按照以下字段结构返回JSON,允许字段值为null。"""

    name: Optional[str] = Field(
        default=None,
        description="人物的姓名(如:张三、Alice),若文本未提及则返回null"
    )
    hair_color: Optional[str] = Field(
        default=None,
        description="人物的头发颜色(如:黑色、金色、棕色),若文本未提及头发颜色则返回null"
    )
    height_in_meters: Optional[str] = Field(
        default=None,
        description="人物的身高(单位:米,如:1.75、1.80),需保留两位小数,若未提及身高则返回null"
    )

这里有几个关键设计点:

  1. 使用Optional类型允许字段为空,避免模型编造信息
  2. Field描述中用具体示例(如 “1.75 米”)明确格式要求
  3. 保留两位小数的细节规定,确保输出一致性

当需要提取多个实体时,我们可以用容器模型封装:

python

from typing import List, Optional

class ExtractedData(BaseModel):
    """多人物信息提取容器  
    用于封装多个Person实体,形成结构化列表输出  
    DeepSeek模型请严格按照此结构返回JSON数组"""

    people: List[Person] = Field(
        description="从文本中提取的人物信息列表,每个元素需符合Person模型结构,无匹配信息时返回空数组"
    )

这种层级结构让模型清楚:输出应该是包含多个 Person 对象的列表,没有匹配时返回空数组。

二、构建提示模板:让模型明确任务边界

接下来需要告诉模型 “如何处理输入”。我们通过ChatPromptTemplate构建系统提示:

python

from langchain_core.prompts import ChatPromptTemplate, MessagesPlaceholder

prompt_template = ChatPromptTemplate.from_messages(
    [
        (
            "system",
            "你是一个专业的信息提取算法。"
            "仅从文本中提取相关信息。"
            "如果你不知道被要求提取的属性的值,请将该属性的值设为 null。"
        ),
        ("human", "{text}")
    ]
)

这里的 system prompt 做了三个关键约束:

  1. 明确角色定位:专业的信息提取工具
  2. 划定任务边界:仅从输入文本提取,不做额外联想
  3. 错误处理规范:未知信息设为 null

这种明确的指令能有效减少模型的 “幻觉” 问题,比如不会把 “身高八尺” 强行转换为米制单位,而是返回 null。

三、工具调用实战:从单实体到多实体提取

初始化 LLM 时我们指定 DeepSeek 模型,并通过with_structured_output绑定数据 schema:

python

from langchain_deepseek import ChatDeepSeek

llm = ChatDeepSeek(
    model="deepseek-chat",
    temperature=0,  # 固定输出,避免随机性
    max_tokens=None,
    timeout=None,
    max_retries=2,
    api_key="sk-***",
)

structured_llm = llm.with_structured_output(schema=ExtractedData)

来看实际提取效果:
当输入 “张飞身高 1.8 米,有一头黑发。” 时,模型能准确提取:

python

{
  "people": [
    {
      "name": "张飞",
      "hair_color": "黑色",
      "height_in_meters": "1.80"
    }
  ]
}

处理多实体场景 “张飞的头发是黑色的,身高 1.8 米。李逵的头发和张飞的颜色一样。” 时,模型会:

  1. 提取张飞的完整信息
  2. 推断李逵的头发颜色(通过 “颜色一样”)
  3. 身高字段因未提及设为 null

最终输出符合预期的结构化列表,这说明模型能理解上下文关联信息。

四、少样本提示:用示例教会模型理解规则

当模型对复杂逻辑或特定格式的理解不够精准时,少样本提示就像 “教学手册”—— 通过少量「用户输入 - 工具输出 - 最终反馈」的完整示例,让模型快速掌握业务规则。我们结合具体实现,拆解这个关键技术环节。

4.1 消息序列的完整构建过程

我们先定义两个典型示例:

python

examples = [  
    (  
        "txt",  # 空文本输入,无人物信息  
        ExtractedData(people=[]),   
    ),  
    (  
        "张飞的头发是黑色的,身高1.8米。李逵的头发和张飞的颜色一样。",  # 多人物文本,包含隐含信息  
        ExtractedData(people=[  
            Person(name='张飞', hair_color='黑色', height_in_meters='1.80'),  
            Person(name='李逵', hair_color='黑色', height_in_meters=None)  
        ]),    
    ),  
]  

通过tool_example_to_messages生成的完整消息序列(以 DeepSeek 格式为例),实际是这样的 “教学对话”:

json

[  
    {  
        "role": "user",  
        "content": "txt"  // 第一条用户输入:空文本,测试无信息场景  
    },  
    {  
        "role": "assistant",  
        "tool_call": {  
            "tool": {  
                "name": "extract_person",  // 工具名由schema自动生成,固定为模型可识别的处理函数  
                "parameters": {"text": "txt"}  // 传入用户输入文本作为处理参数  
            }  
        }  
    },  
    {  
        "role": "tool",  
        "content": "{\"people\": []}"  // 工具返回空数组,明确“无匹配信息时输出空列表”  
    },  
    {  
        "role": "assistant",  
        "content": "未检测到人物信息."  // 助手总结,建立“空数据→明确反馈”的映射  
    },  
]  

4.2 消息序列的三层核心教学目标

第一层:教会模型 “如何调用工具”(格式标准化)
  • 痛点:不同模型的工具调用语法不同(如 OpenAI 用function call,DeepSeek 用tool字段),手动编写易出错。
  • 解决方案
    tool_example_to_messages自动根据所选模型(此处为 DeepSeek)生成合规的tool_call结构:

    python

    # DeepSeek要求的工具调用格式  
    {  
        "tool": {  
            "name": "extract_person",  // 工具名由schema名称自动生成,无需手动定义  
            "parameters": {"text": 输入文本}  // 固定传入待处理的text参数  
        }  
    }  
    

    开发者无需记忆复杂格式,模型也能通过示例明确 “遇到用户文本,就调用 extract_person 工具,传入 text 参数”。
第二层:教会模型 “如何输出数据”(结构严格化)
  • 关键设计:通过 Pydantic 模型ExtractedDataPerson,给模型划定 “输出红线”:
    • 必须包含people字段,且是Person对象的列表(如[][{...}, {...}]
    • 每个Person必须包含namehair_colorheight_in_meters,允许null但不能缺失字段
  • 示例作用
    第二个示例的工具输出中,李逵的height_in_metersnull而非忽略,明确 “未提及字段必须保留并设为 null”;张飞的身高是"1.80"而非"1.8",强调 “必须保留两位小数”。这些细节通过具体案例传递,比文字描述更直观。
第三层:教会模型 “如何反馈结果”(逻辑链条化)
  • 消息序列的隐性逻辑

    plaintext

    用户输入文本 → 助手调用工具处理 → 工具返回结构化数据 → 助手根据数据有无给出总结  
    

    第一层示例(空文本)展示 “无数据时,助手回复‘未检测到人物信息’”;
    第二层示例(多人物)展示 “有数据时,助手回复‘检测到人物信息’”。
    这种 “输入 - 处理 - 反馈” 的完整链路,让模型学会在实际处理时,自动遵循 “先工具提取,再总结反馈” 的流程,避免跳过关键步骤。

4.3 少样本提示的核心优势:从 “模糊指令” 到 “具体示范”

假设我们只用文字指令告诉模型:“遇到人物信息,提取姓名、发色、身高,未提及的设为 null”,模型可能会困惑:

  • “身高的单位是米还是厘米?需要保留几位小数?”
  • “‘发色相同’这种隐含关系要不要处理?怎么处理?”

而少样本提示通过具体示例,让模型 “看到” 正确做法:

  1. 格式规范:身高必须是字符串类型,保留两位小数(如"1.80"
  2. 逻辑处理:通过 “颜色一样” 推断李逵的发色,但身高未提及时严格设为null
  3. 边界情况:无匹配信息时返回空数组,而非报错或乱填

这种 “示例驱动” 的学习方式,比单纯依赖自然语言指令更高效,尤其适合处理:

  • 字段格式有严格要求(如日期、金额)
  • 存在上下文关联(如代词指代、隐含关系)
  • 模型容易产生 “幻觉”(如编造未提及的信息)

4.4 示例设计的黄金法则

  1. 覆盖核心场景:至少包含 “单实体”“多实体”“无匹配” 三类示例,确保模型能处理 80% 的常见情况。
  2. 突出差异点:每个示例重点展示一个规则,如示例 1 强调 “空输入→空数组”,示例 2 强调 “隐含信息提取 + 字段完整性”。
  3. 控制数量:2-3 个高质量示例优于 10 个重复示例,避免模型因信息过载导致规则混淆。

五、避坑指南:确保提取准确性的关键细节

在实践中需要注意这些细节:

  1. 数据类型一致性:身高字段定义为字符串而非浮点型,避免模型返回科学计数法等格式
  2. 单位严格性:明确要求 “米” 为单位,防止将 “180cm” 直接转换导致的精度问题
  3. 未知处理:所有字段默认值设为 None,配合 system prompt 的 null 返回要求,避免模型猜测
  4. 温度控制:设置 temperature=0,确保输出确定性,避免同一输入产生不同结果

当遇到复杂文本时,建议分步骤处理:

  1. 先通过简单示例验证单个字段提取
  2. 逐步增加实体数量和关联关系
  3. 对边界情况(如完全无匹配文本)进行鲁棒性测试

总结:打造可靠的信息提取流水线

通过今天的实践,我们掌握了从数据建模到模型调用的完整流程:

  1. 用 Pydantic 定义清晰的提取 schema,明确 “要什么”
  2. 通过提示模板约束模型行为,规范 “怎么做”
  3. 利用少样本示例强化特定规则,解决 “怎么做好”

这套方法适用于各类垂直领域的信息提取任务,无论是简历解析、医疗文本处理还是电商商品信息抓取,核心思路都是相通的。关键是要:

  • 提前定义好完整的数据模型
  • 用明确的指令约束模型行为
  • 通过示例让模型理解业务规则

现在就可以将这些代码整合到你的项目中,试试从自己的文本数据中提取结构化信息吧!如果在实践中遇到问题,欢迎在评论区交流 —— 记得点击关注,后续会分享更多大模型应用开发的实战技巧~

觉得有帮助的话,别忘了收藏这篇文章,方便后续查阅。也欢迎点赞让更多开发者看到

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

佑瞻

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值