快速入门
本教程主要介绍借用大模型的函数/工具调用能力,实现结构化数据提取功能。
以OpenAI的GPT模型为例,模型支持传递一组函数或者工具调用声明(包括函数名、函数说明、参数说明)给大模型,大模型根据用户问题,自动选择调用那个函数/工具,然后从用户的问题中提取相关信息自动填充函数的调用参数。
我们就是利用这个特性实现结构化数据提取。
说明:只有选择支持函数/工具调用的模型才能使用这个方法。
设置
这里选择OpenAI模型,安装它的依赖项,并设置API密钥!
# 安装框架
pip install langchain
# 安装模型依赖
pip install langchain-openai
设置KEY
export OPENAI_API_KEY="..."
或者
from langchain_openai import ChatOpenAI
llm = ChatOpenAI(openai_api_key="...")
定义需要提取的数据结构
首先,我们需要描述我们想要从文本中提取的信息。
我们将使用Pydantic来定义一个数据结构,用来代表,我们需要提取的个人信息。
from typing import Optional
from langchain_core.pydantic_v1 import BaseModel, Field
# 定义一个Person类,类属性就是我们希望提取的字段
# 注意:类的注释、字段的描述都会传给GPT模型,告诉我们我们需要提取什么信息,非常重要。
class Person(BaseModel):
"""个人信息。"""
name: Optional[str] = Field(..., description="姓名")
hair_color: Optional[str] = Field(
..., description="如果已知,表示人的发色"
)
height_in_meters: Optional[str] = Field(
..., description="以米为单位的身高"
)
在上述代码中,我们使用了Pydantic库定义了一个名为Person的模式,用于提取个人信息。这个模式包括了个人的姓名、头发颜色和身高。
在定义模式时有两个最佳实践:
- 类注释、字段描述:这些信息要写清楚,这些描述信息会发给大模型,指导大模型提取什么数据。
- 不要强迫LLM编造信息!上面我们对属性使用了
Optional
,如果LLM不知道答案,允许LLM输出None
。
数据提取
让我们使用上面定义的数据结构,实现一个数据提取逻辑。
from typing import Optional
from langchain_core.prompts import ChatPromptTemplate, MessagesPlaceholder
from langchain_core.pydantic_v1 import BaseModel, Field
from langchain_openai import ChatOpenAI
# 设置提示词
prompt = ChatPromptTemplate.from_messages(
[
(
"system",
"你是一位数据提取专家。"
"只能从文本中提取相关信息。"
"如果你不知道要提取的属性的值,"
"请返回该属性的值为null。",
),
("human", "{text}"),
]
)
初始化OpenAI模型
from langchain_openai import ChatOpenAI
llm = ChatOpenAI()
# 通过LCEL表达式,定一个Chain,把提示词传给模型
# 注意模型使用with_structured_output函数,将我们前面定义的数据结构传递给模型
runnable = prompt | llm.with_structured_output(schema=Person)
让我们来测试一下
text = "Alan Smith身高6英尺,金发。"
runnable.invoke({"text": text})
看下面输出例子,模型返回了一个Person对象,里面包含了我们希望提取的数据
Person(name='Alan Smith', hair_color='金发', height_in_meters='1.8288')
如何提取多条数据
很多时间,我们想提取一个数据列表,而不仅仅是一条数据。
这可以通过在pydantic中嵌套模型来轻松实现。
from typing import List, Optional
from langchain_core.pydantic_v1 import BaseModel, Field
class Person(BaseModel):
"""关于一个人的信息。"""
name: Optional[str] = Field(..., description="人的姓名")
hair_color: Optional[str] = Field(
..., description="如果已知,人的头发颜色"
)
height_in_meters: Optional[str] = Field(
..., description="以米为单位的身高"
)
# 定义了一个Data类,包含了people数组
class Data(BaseModel):
"""有关人的提取数据。"""
people: List[Person]
下面将Data数据结构传递给模型,进行测试。
runnable = prompt | llm.with_structured_output(schema=Data)
text = "我的名字是杰夫,我的头发是黑色的,我身高6英尺。安娜的头发颜色和我一样。"
runnable.invoke({"text": text})
下面测试输出例子
Data(people=[Person(name='杰夫', hair_color=None, height_in_meters=None), Person(name='安娜', hair_color=None, height_in_meters=None)])
如果文本中没有相关信息,模型也可以提取零个实体,从而允许返回一个空的列表。