Langchian提取结构化的数据
构建一个链来从:非结构化的文本中提取结构化信息。
任务介绍
本练习演示如何利用 LangChain 的 with_structured_output 能力,将自然语言中的人物信息结构化为符合 Pydantic 模型 Manypeople 的 JSON。通过"模型 + 提示 + 结构化解析"流水线,我们可以稳定地从任意文本中提取多个角色的姓名、发色和身高。
背景与需求
在多人物叙述里,简单的单实体抽取已经无法满足需求。我们需要:
- 支持同一段文本中出现的多个角色;
- 保证输出严格符合
{"people": [...]}的结构,以方便下游系统消费; - 在模型返回前就校验数据格式,避免脏数据流入。
为此,选用 Qwen 兼容 OpenAI 接口,并结合 LangChain 的 structured output 功能与 Pydantic 校验,把所有环节串联起来。
解决逻辑与关键代码
整体链路:输入文本 → 构造提示 → LLM 生成结构化 JSON → Pydantic 校验 → 输出 Manypeople。
关键逻辑如下:
chain = {
'text': RunnablePassthrough()
} | prompt | model.with_structured_output(schema=Manypeople)
RunnablePassthrough将原始文本透传给提示;ChatPromptTemplate指定 system + human 消息,告知模型必须返回 JSON,并在未知时写null;with_structured_output绑定Manypeopleschema,让模型直接生成符合 Pydantic 的 JSON;解析失败会立刻抛错。
文字流程图
- 接收输入文本,描述若干人物。
- 通过 Prompt 模板注入 system 说明,要求输出 JSON。
- Qwen LLM 根据提示返回结构化内容。
- LangChain 将响应按照
Manypeopleschema 解析;若缺字段则报错,提醒用户调整。 - 校验通过后,得到一个
people列表,每项都是Person对象,可直接在 Python 中使用。
总结
- 通过 Pydantic + LangChain 的组合,可以快速构建可靠的结构化抽取能力。
- 多人物场景的核心在于提示里明确
people列表结构,并让模型严格输出 JSON。 - 解析层自带校验,既能早发现问题,也方便调试提示或后处理。
完整代码
python
from typing import List, Optional
from langchain_openai import ChatOpenAI
from pydantic import BaseModel, Field
# Qwen(通义千问)API Key
qwen_api_key = 'TripleH' # 请替换为您的 DashScope API Key
# 创建 Qwen LLM 模型
# 可选模型:qwen-turbo, qwen-plus, qwen-max, qwen-max-longcontext
model = ChatOpenAI(
model='qwen-turbo', # 可以根据需要改为 qwen-plus 或 qwen-max
api_key=qwen_api_key,
base_url='https://dashscope.aliyuncs.com/compatible-mode/v1'
)
from langchain_core.prompts import ChatPromptTemplate
# 定义自定义提示以提供指令和任何其他上下文。
# 1) 你可以在提示模板中添加示例以提高提取质量
# 2) 引入额外的参数以考虑上下文(例如,包括有关提取文本的文档的元数据。)
prompt = ChatPromptTemplate.from_messages(
[
(
"system",
"你是一个专业的提取算法。只从未结构化文本中提取相关信息,必须以JSON格式返回结果。如果你不知道要提取的属性的值,返回该属性的值为null。",
),
# 请参阅有关如何使用参考记录消息历史的案例
# MessagesPlaceholder('examples'),
("human", "{text}"),
]
)
# pydantic: 处理数据,验证数据, 定义数据的格式, 虚拟化和反虚拟化,类型转换等等。
class Person(BaseModel):
"""
关于一个人的数据模型
"""
name: Optional[str] = Field(default=None,description='表示人的名字')
hair_color: Optional[str] = Field(default=None, description='如果知道的话,这个人的头发颜色')
height_in_meters: Optional[str] = Field(default=None, description="以米为单位测量的高度")
class Manypeople(BaseModel):
people: List[Person]
from langchain_core.runnables import RunnablePassthrough
chain = {'text' : RunnablePassthrough()} | prompt | model.with_structured_output(schema=Manypeople)
# text = '马路上走来一个女生,长长的黑头发披在肩上,大概1米7左右,'
# text = "马路上走来一个女生,长长的黑头发披在肩上,大概1米7左右。走在她旁边的是她的男朋友,叫:刘海;比她高10厘米。"
text = "My name is Jeff, my hair is black and i am 6 feet tall. Anna has the same color hair as me."
resp = chain.invoke(text)
print(resp)
# name='Jeff' hair_color='black' height_in_meters=None