使用参考示例
通常,通过向LLM提供参考例子,可以改善数LLM的据提取的质量。
from langchain_core.prompts import ChatPromptTemplate, MessagesPlaceholder
prompt = ChatPromptTemplate.from_messages(
[
(
"system",
"你是一种专家级的数据提取算法。"
"只从文本中提取相关信息。"
"如果您不知道被要求提取的属性的值,"
"则返回该属性值的null值。",
),
MessagesPlaceholder("examples"), # <-- 示例占位符!
("human", "{text}"),
]
)
测试模板:
from langchain_core.messages import (
HumanMessage,
)
prompt.invoke(
{"text": "这是一些文本", "examples": [HumanMessage(content="测试 1 2 3")]}
)
ChatPromptValue(messages=[SystemMessage(content="您是一种专家级的提取算法。只从文本中提取相关信息。如果您不知道被要求提取的属性的值,则返回该属性值的null值。"), HumanMessage(content='测试 1 2 3'), HumanMessage(content='这是一些文本')])
定义需要提取的数据模型
让我们从快速开始中重新使用人物模型。
from typing import List, Optional
from langchain_core.pydantic_v1 import BaseModel, Field
from langchain_openai import ChatOpenAI
# 定义我们需要提取的数据模型
class Person(BaseModel):
"""关于一个人的信息。"""
name: Optional[str] = Field(..., description="人的姓名")
hair_color: Optional[str] = Field(
..., description="如果已知,人的眼睛颜色"
)
height_in_meters: Optional[str] = Field(..., description="以米为单位的身高")
class Data(BaseModel):
"""关于人的提取数据。"""
people: List[Person]
定义参考示例
示例可以被定义为输入-输出对的列表。
每个示例包含一个示例输入
文本和一个示例输出
,显示应从文本中提取什么。
import uuid
from typing import Dict, List, TypedDict
from langchain_core.messages import (
AIMessage,
BaseMessage,
HumanMessage,
SystemMessage,
ToolMessage,
)
from langchain_core.pydantic_v1 import BaseModel, Field
# 定义参考例子的数据结构,用来保存例子数据样本
class Example(TypedDict):
"""每一个样本数据,由文本输入和预期的工具调用结果组成。
"""
input: str # 这是用户输入的问题样例
tool_calls: List[BaseModel] # 这里就是LLM模型应该返回的正确数据,我们这里使用 pydantic 模型的实例表示
def tool_example_to_messages(example: Example) -> List[BaseMessage]:
"""将示例转换为可以输入到 LLM 中的消息列表,目的就是格式化参考示例数据,方便传给LLM模型。
此代码是一个适配器,将我们的示例转换为可以输入到聊天模型中的消息列表。
每个示例的消息列表对应于:
1) HumanMessage:包含应该被提取的内容,也就是用户输入的问题。
2) AIMessage:包含模型中提取的信息,就是LLM应该返回的数据结构
3) ToolMessage:包含对模型的工具函数调用信息
ToolMessage 是必需的,因为一些聊天模型对代理进行了超优化,而不仅仅是为了提取数据。
"""
messages: List[BaseMessage] = [HumanMessage(content=example["input"])]
openai_tool_calls = []
for tool_call in example["tool_calls"]:
openai_tool_calls.append(
{
"id": str(uuid.uuid4()),
"type": "function",
"function": {
"name": tool_call.__class__.__name__,
"arguments": tool_call.json(),
},
}
)
messages.append(
AIMessage(content="", additional_kwargs={"tool_calls": openai_tool_calls})
)
tool_outputs = example.get("tool_outputs") or [
"您正确地调用了这个工具。"
] * len(openai_tool_calls)
for output, tool_call in zip(tool_outputs, openai_tool_calls):
messages.append(ToolMessage(content=output, tool_call_id=tool_call["id"]))
return messages
接下来让我们定义我们的示例,然后将它们转换为消息格式。
examples = [
(
"海洋广阔而蔚蓝。它超过了 20,000 英尺的深度。里面有很多鱼。",
Person(name=None, height_in_meters=None, hair_color=None),
),
(
"Fiona 远离法国到达了西班牙。",
Person(name="Fiona", height_in_meters=None, hair_color=None),
),
]
messages = []
for text, tool_call in examples:
messages.extend(
tool_example_to_messages({"input": text, "tool_calls": [tool_call]})
)
让我们测试一下提示
prompt.invoke({"text": "这是一些文本", "examples": messages})
ChatPromptValue(messages=[SystemMessage(content="您是一位专家级的提取算法。只需从文本中提取相关信息。如果您不知道要提取的属性的值,可以将该属性的值返回为null。"), HumanMessage(content="海洋辽阔而湛蓝。它的深度超过20,000英尺。里面有许多鱼。"), AIMessage(content='', additional_kwargs={'tool_calls': [{'id': 'c75e57cc-8212-4959-81e9-9477b0b79126', 'type': 'function', 'function': {'name': 'Person', 'arguments': '{"name": null, "hair_color": null, "height_in_meters": null}'}}]}), ToolMessage(content='您已正确调用了这个工具。', tool_call_id='c75e57cc-8212-4959-81e9-9477b0b79126'), HumanMessage(content='Fiona从法国远赴西班牙。'), AIMessage(content='', additional_kwargs={'tool_calls': [{'id': '69da50b5-e427-44be-b396-1e56d821c6b0', 'type': 'function', 'function': {'name': 'Person', 'arguments': '{"name": "Fiona", "hair_color": null, "height_in_meters": null}'}}]}), ToolMessage(content='您已正确调用了这个工具。', tool_call_id='69da50b5-e427-44be-b396-1e56d821c6b0'), HumanMessage(content='这是一些文本')])
创建提取器实现数据提取
在这里,我们将使用 gpt-4 来创建一个提取器。
llm = ChatOpenAI(
model="gpt-4-0125-preview",
temperature=0,
)
runnable = prompt | llm.with_structured_output(
schema=Data,
method="function_calling",
include_raw=False,
)
没有示例的情况
请注意,尽管我们在使用 gpt-4,但它在一个非常简单的测试案例上失败了,本例子不应该提取到数据!
for _ in range(5):
text = "太阳系很大,但地球只有一个月亮。"
print(runnable.invoke({"text": text, "examples": []}))
下面输出,表示提取了两条错误的数据
people=[]
people=[Person(name='earth', hair_color=None, height_in_meters=None)]
people=[Person(name='earth', hair_color=None, height_in_meters=None)]
people=[]
people=[]
带有示例
使用参考示例有助于修复失败!
for _ in range(5):
text = "太阳系很大,但地球只有一个月亮。"
print(runnable.invoke({"text": text, "examples": messages}))
这次修复了错误
people=[]
people=[]
people=[]
people=[]
people=[]
runnable.invoke(
{
"text": "我叫哈里森。我的头发是黑色的。",
"examples": messages,
}
)
Data(people=[Person(name='Harrison', hair_color='black', height_in_meters=None)])