构建提取链
在本教程中,我们将使用 工具调用 功能,结合 聊天模型,从非结构化文本中提取结构化信息。我们还将演示如何在此场景中使用 少样本提示(few-shot prompting) 来提升性能。
本教程需要 langchain-core>=0.3.20,并且仅适用于支持 工具调用 的模型。
设置
Jupyter Notebook
本教程及其他教程最方便在 Jupyter notebooks 中运行。在交互式环境中逐步学习指南是更好地理解它们的绝佳方式。有关安装说明,请参阅 此处。
安装
安装 LangChain 请运行:
- Pip
- Conda
pip install --upgrade langchain-core
conda install langchain-core -c conda-forge
有关更多详细信息,请参阅我们的 安装指南。
LangSmith
您使用 LangChain 构建的许多应用程序将包含多个步骤以及多次 LLM 调用。 随着这些应用程序变得越来越复杂,能够检查您的链或代理内部究竟发生了什么变得至关重要。 实现这一点的最佳方式是使用 LangSmith。
在通过上述链接注册后,请确保设置您的环境变量以开始记录追踪:
export LANGSMITH_TRACING="true"
export LANGSMITH_API_KEY="..."
或者,如果在笔记本中,您可以这样设置:
import getpass
import os
os.environ["LANGSMITH_TRACING"] = "true"
os.environ["LANGSMITH_API_KEY"] = getpass.getpass()
模式
首先,我们需要描述我们希望从文本中提取什么信息。
我们将使用 Pydantic 定义一个示例模式,用于提取个人信息。
from typing import Optional
from pydantic import BaseModel, Field
class Person(BaseModel):
"""Information about a person."""
# ^ Doc-string for the entity Person.
# This doc-string is sent to the LLM as the description of the schema Person,
# and it can help to improve extraction results.
# Note that:
# 1. Each field is an `optional` -- this allows the model to decline to extract it!
# 2. Each field has a `description` -- this description is used by the LLM.
# Having a good description can help improve extraction results.
name: Optional[str] = Field(default=None, description="The name of the person")
hair_color: Optional[str] = Field(
default=None, description="The color of the person's hair if known"
)
height_in_meters: Optional[str] = Field(
default=None, description="Height measured in meters"
)
定义模式时有两个最佳实践:
- 记录 属性 和 模式 本身:此信息将发送给大语言模型(LLM),用于提高信息提取的质量。
- 不要强迫大语言模型编造信息!上面我们使用
Optional作为属性值,允许大语言模型在不知道答案时输出None。
为了获得最佳性能,请充分记录架构,并确保模型在没有可提取信息时不会被强制返回结果。
提取器
让我们使用上述定义的架构创建一个信息提取器。
from langchain_core.prompts import ChatPromptTemplate, MessagesPlaceholder
# Define a custom prompt to provide instructions and any additional context.
# 1) You can add examples into the prompt template to improve extraction quality
# 2) Introduce additional parameters to take context into account (e.g., include metadata
# about the document from which the text was extracted.)
prompt_template = ChatPromptTemplate.from_messages(
[
(
"system",
"You are an expert extraction algorithm. "
"Only extract relevant information from the text. "
"If you do not know the value of an attribute asked to extract, "
"return null for the attribute's value.",
),
# Please see the how-to about improving performance with
# reference examples.
# MessagesPlaceholder('examples'),
("human", "{text}"),
]
)
我们需要使用一个支持函数/工具调用的模型。
请查阅文档以了解所有可与此 API 配合使用的模型。
pip install -qU "langchain[openai]"
import getpass
import os
if not os.environ.get("OPENAI_API_KEY"):
os.environ["OPENAI_API_KEY"] = getpass.getpass("Enter API key for OpenAI: ")
from langchain.chat_models import init_chat_model
llm = init_chat_model("gpt-4o-mini", model_provider="openai")
structured_llm = llm.with_structured_output(schema=Person)
让我们测试一下:
text = "Alan Smith is 6 feet tall and has blond hair."
prompt = prompt_template.invoke({"text": text})
structured_llm.invoke(prompt)
Person(name='Alan Smith', hair_color='blond', height_in_meters='1.83')
提取即生成 🤯
LLM是生成式模型,因此它们可以完成一些非常酷的事情,比如即使身高是以英尺为单位提供的,也能正确提取出以米为单位的数值。
我们可以看到 LangSmith 追踪 此处。请注意,追踪中的聊天模型部分揭示了发送给模型的精确消息序列、调用的工具以及其他元数据。
多个实体
在大多数情况下,您应该提取实体列表,而不是单个实体。
这可以通过在 Pydantic 中将模型嵌套在一起轻松实现。
from typing import List, Optional
from pydantic import BaseModel, Field
class Person(BaseModel):
"""Information about a person."""
# ^ Doc-string for the entity Person.
# This doc-string is sent to the LLM as the description of the schema Person,
# and it can help to improve extraction results.
# Note that:
# 1. Each field is an `optional` -- this allows the model to decline to extract it!
# 2. Each field has a `description` -- this description is used by the LLM.
# Having a good description can help improve extraction results.
name: Optional[str] = Field(default=None, description="The name of the person")
hair_color: Optional[str] = Field(
default=None, description="The color of the person's hair if known"
)
height_in_meters: Optional[str] = Field(
default=None, description="Height measured in meters"
)
class Data(BaseModel):
"""Extracted data about people."""
# Creates a model so that we can extract multiple entities.
people: List[Person]
提取结果在此可能不够完美。继续阅读以了解如何使用参考示例来提升提取质量,并查看我们的提取操操作指南以获取更详细的说明。
structured_llm = llm.with_structured_output(schema=Data)
text = "My name is Jeff, my hair is black and i am 6 feet tall. Anna has the same color hair as me."
prompt = prompt_template.invoke({"text": text})
structured_llm.invoke(prompt)
Data(people=[Person(name='Jeff', hair_color='black', height_in_meters='1.83'), Person(name='Anna', hair_color='black', height_in_meters=None)])
当模式支持提取多个实体时,它还能通过在文本中无相关信息时提供一个空列表,允许模型提取零个实体。
这通常是一件好事!它允许在实体上指定必需属性,而无需强制模型检测该实体。
我们可以看到 LangSmith 的追踪 此处。
参考示例
LLM 应用程序的行为可以通过少样本提示来引导。对于聊天模型,这可以采取为一系列输入和响应消息对的形式,以展示期望的行为。
例如,我们可以通过交替的user和assistant消息来传达符号的含义:
messages = [
{"role": "user", "content": "2 🦜 2"},
{"role": "assistant", "content": "4"},
{"role": "user", "content": "2 🦜 3"},
{"role": "assistant", "content": "5"},
{"role": "user", "content": "3 🦜 4"},
]
response = llm.invoke(messages)
print(response.content)
7
结构化输出 通常在后台使用 工具调用。这通常涉及生成包含工具调用的 AI 消息,以及包含工具调用结果的 工具消息。在这种情况下,消息序列应该是什么样子?
不同的 聊天模型提供商 对有效的消息序列有不同的要求。有些会接受形式为:(重复的) 消息序列:
- 用户消息
- 带有工具调用的 AI 消息
- 工具消息与结果
其他方法需要一条包含某种响应的最终 AI 消息。
LangChain 包含一个实用函数 tool_example_to_messages,可为大多数模型提供商生成有效的序列。它通过仅需提供相应工具调用的 Pydantic 表示形式,简化了结构化少样本示例的生成过程。
让我们尝试一下。我们可以将输入字符串对和所需的 Pydantic 对象转换为一系列消息,这些消息可以提供给聊天模型。在底层,LangChain 会将工具调用格式化为每个提供者所需的格式。
注意:此版本的 tool_example_to_messages 需要 langchain-core>=0.3.20。
from langchain_core.utils.function_calling import tool_example_to_messages
examples = [
(
"The ocean is vast and blue. It's more than 20,000 feet deep.",
Data(people=[]),
),
(
"Fiona traveled far from France to Spain.",
Data(people=[Person(name="Fiona", height_in_meters=None, hair_color=None)]),
),
]
messages = []
for txt, tool_call in examples:
if tool_call.people:
# This final message is optional for some providers
ai_response = "Detected people."
else:
ai_response = "Detected no people."
messages.extend(tool_example_to_messages(txt, [tool_call], ai_response=ai_response))
检查结果显示,我们看到了这两组示例生成了八条消息:
for message in messages:
message.pretty_print()
================================[1m Human Message [0m=================================
The ocean is vast and blue. It's more than 20,000 feet deep.
==================================[1m Ai Message [0m==================================
Tool Calls:
Data (d8f2e054-7fb9-417f-b28f-0447a775b2c3)
Call ID: d8f2e054-7fb9-417f-b28f-0447a775b2c3
Args:
people: []
=================================[1m Tool Message [0m=================================
You have correctly called this tool.
==================================[1m Ai Message [0m==================================
Detected no people.
================================[1m Human Message [0m=================================
Fiona traveled far from France to Spain.
==================================[1m Ai Message [0m==================================
Tool Calls:
Data (0178939e-a4b1-4d2a-a93e-b87f665cdfd6)
Call ID: 0178939e-a4b1-4d2a-a93e-b87f665cdfd6
Args:
people: [{'name': 'Fiona', 'hair_color': None, 'height_in_meters': None}]
=================================[1m Tool Message [0m=================================
You have correctly called this tool.
==================================[1m Ai Message [0m==================================
Detected people.
让我们比较一下有无这些消息时的性能。例如,让我们传递一条我们打算不提取任何人的消息:
message_no_extraction = {
"role": "user",
"content": "The solar system is large, but earth has only 1 moon.",
}
structured_llm = llm.with_structured_output(schema=Data)
structured_llm.invoke([message_no_extraction])
Data(people=[Person(name='Earth', hair_color='None', height_in_meters='0.00')])
在此示例中,该模型可能会错误地生成人员记录。
由于我们的少样本示例包含“负例”示例,我们鼓励模型在这种情况下表现正确:
structured_llm.invoke(messages + [message_no_extraction])
Data(people=[])
该 LangSmith 运行追踪揭示了发送给聊天模型的精确消息序列、生成的工具调用、延迟、令牌计数以及其他元数据。
有关提取工作流的更多详细信息和参考示例(包括如何整合提示模板以及自定义示例消息的生成),请参阅 本指南。
下一步
现在您已经了解了使用 LangChain 进行提取的基础知识,接下来就可以继续学习其余的操操作指南了: