使用OpenAI函数
本教程演示了如何在代码中使用OpenAI函数调用API。我们将介绍以下内容:
- 如何使用函数从ChatOpenAI获取结构化输出
- 如何创建一个使用(多个)函数的通用链
- 如何创建一个实际执行所选函数的链
from typing import Optional
from langchain.chains.openai_functions import (
create_openai_fn_chain,
create_structured_output_chain,
)
from langchain.chat_models import ChatOpenAI
from langchain.prompts import ChatPromptTemplate, HumanMessagePromptTemplate
from langchain.schema import HumanMessage, SystemMessage
API参考:
- create_openai_fn_chain 来自
langchain.chains.openai_functions
- create_structured_output_chain 来自
langchain.chains.openai_functions
- ChatOpenAI 来自
langchain.chat_models
- ChatPromptTemplate 来自
langchain.prompts
- HumanMessagePromptTemplate 来自
langchain.prompts
- HumanMessage 来自
langchain.schema
- SystemMessage 来自
langchain.schema
获取结构化输出
我们可以利用OpenAI函数来尝试强制模型返回特定类型的结构化输出。我们将使用create_structured_output_chain
来创建我们的链,该函数接受所需的结构化输出作为Pydantic类或JsonSchema。
有关相关的参考文档,请参阅此处。
使用Pydantic类
当我们在代码中传递Pydantic类来构造文本时,我们需要确保为类添加文档字符串描述。同时,为每个类属性添加描述也是有帮助的。
from pydantic import BaseModel, Field
class Person(BaseModel):
"""关于一个人的身份信息。"""
name: str = Field(..., description="人的姓名")
age: int = Field(..., description="人的年龄")
fav_food: Optional[str] = Field(None, description="人的最喜欢的食物")
如果我们明确传递一个模型,我们需要确保它支持OpenAI的函数调用API。
llm = ChatOpenAI(model="gpt-4", temperature=0)
prompt_msgs = [
SystemMessage(
content="您是一个世界级的算法,可以从结构化格式中提取信息。"
),
HumanMessage(
content="使用给定的格式从以下输入中提取信息:"
),
HumanMessagePromptTemplate.from_template("{input}"),
HumanMessage(content="提示:确保以正确的格式回答"),
]
prompt = ChatPromptTemplate(messages=prompt_msgs)
chain = create_structured_output_chain(Person, llm, prompt, verbose=True)
chain.run("Sally is 13")
> 进入新的LLMChain链...
格式化后的提示:
系统:您是一个世界级的算法,可以从结构化格式中提取信息。
人类:使用给定的格式从以下输入中提取信息:
人类:Sally is 13
人类:提示:确保以正确的格式回答
{'function_call': {'name': '_OutputFormatter', 'arguments': '{\n "output": {\n "name": "Sally",\n "age": 13,\n "fav_food": "Unknown"\n }\n}'}}
> 完成链。
Person(name='Sally', age=13, fav_food='Unknown')
要提取任意数量的给定格式的结构化输出,我们可以创建一个包装器Pydantic类,该类接受原始类的序列。
from typing import Sequence
class People(BaseModel):
"""关于文本中所有人的身份信息。"""
people: Sequence[Person] = Field(..., description="文本中的人")
chain = create_structured_output_chain(People, llm, prompt, verbose=True)
chain.run(
"Sally is 13, Joey just turned 12 and loves spinach. Caroline is 10 years older than Sally, so she's 23."
)
> 进入新的LLMChain链...
格式化后的提示:
系统:您是一个世界级的算法,可以从结构化格式中提取信息。
人类:使用给定的格式从以下输入中提取信息:
人类:Sally is 13, Joey just turned 12 and loves spinach. Caroline is 10 years older than Sally, so she's 23.
人类:提示:确保以正确的格式回答
{'function_call': {'name': '_OutputFormatter', 'arguments': '{\n "output": {\n "people": [\n {\n "name": "Sally",\n "age": 13,\n "fav_food": ""\n },\n {\n "name": "Joey",\n "age": 12,\n "fav_food": "spinach"\n },\n {\n "name": "Caroline",\n "age": 23,\n "fav_food": ""\n }\n ]\n }\n}'}}
> 完成链。
People(people=[Person(name='Sally', age=13, fav_food=''), Person(name='Joey', age=12, fav_food='spinach'), Person(name='Caroline', age=23, fav_food='')])
使用JsonSchema
我们还可以传入JsonSchema而不是Pydantic类来指定所需的结构。当我们这样做时,我们的链将输出与JsonSchema中描述的属性相对应的json,而不是Pydantic类。
json_schema = {
"title": "Person",
"description": "关于一个人的身份信息",
"type": "object",
"properties": {
"name": {"title": "姓名", "description": "人的姓名", "type": "string"},
"age": {"title": "年龄", "description": "人的年龄", "type": "integer"},
"fav_food": {
"title": "喜爱的食物",
"description": "人的喜爱食物",
"type": "string",
},
},
"required": ["name", "age"],
}
chain = create_structured_output_chain(json_schema, llm, prompt, verbose=True)
chain.run("Sally is 13")
> 进入新的LLMChain链...
格式化后的提示:
系统:您是一个世界级的算法,可以从结构化格式中提取信息。
人:使用给定的格式从以下输入中提取信息:
人:Sally is 13
人:提示:确保以正确的格式回答
{'function_call': {'name': 'output_formatter', 'arguments': '{\n "name": "Sally",\n "age": 13\n}'}}
> 链结束。
{'name': 'Sally', 'age': 13}
创建通用的OpenAI函数链
要创建通用的OpenAI函数链,我们可以使用create_openai_fn_chain
方法。这与create_structured_output_chain
相同,只是它接受一系列函数定义,而不是单个输出模式。
函数可以传入以下形式:
- 符合OpenAI函数规范的字典,
- Pydantic类,其中它们应该具有表示函数的docstring描述以及每个参数的描述,
- Python函数,其中它们应该具有函数和参数的docstring描述,以及类型提示。
有关相关的参考文档,请参阅此处。
class RecordPerson(BaseModel):
"""记录有关人的一些身份信息。"""
name: str = Field(..., description="人的姓名")
age: int = Field(..., description="人的年龄")
fav_food: Optional[str] = Field(None, description="人喜欢的食物")
class RecordDog(BaseModel):
"""记录有关狗的一些身份信息。"""
name: str = Field(..., description="狗的姓名")
color: str = Field(..., description="狗的颜色")
fav_food: Optional[str] = Field(None, description="狗喜欢的食物")
prompt_msgs = [
SystemMessage(content="您是一个世界级的记录实体算法"),
HumanMessage(
content="调用相关函数来记录以下输入中的实体:"
),
HumanMessagePromptTemplate.from_template("{input}"),
HumanMessage(content="提示:确保以正确的格式回答"),
]
prompt = ChatPromptTemplate(messages=prompt_msgs)
chain = create_openai_fn_chain([RecordPerson, RecordDog], llm, prompt, verbose=True)
chain.run("Harry是一只胖胖的棕色比格犬,喜欢吃鸡肉")
> 进入新的LLMChain链...
格式化后的提示:
系统:您是一个世界级的记录实体算法
人:调用相关函数来记录以下输入中的实体:
人:Harry是一只胖胖的棕色比格犬,喜欢吃鸡肉
人:提示:确保以正确的格式回答
{'function_call': {'name': 'RecordDog', 'arguments': '{\n "name": "Harry",\n "color": "brown",\n "fav_food": "chicken"\n}'}}
> 链执行完毕。
RecordDog(name='Harry', color='brown', fav_food='chicken')
使用python函数
我们可以将函数作为Pydantic类、直接作为OpenAI函数字典或Python函数传递。要直接传递Python函数,我们需要确保参数具有类型提示,有一个文档字符串,并且使用Google Python风格的文档字符串来描述参数。
注意:要使用Python函数,请确保函数参数是原始类型(str、float、int、bool)或Pydantic对象。
class OptionalFavFood(BaseModel):
"""食物或null。"""
food: Optional[str] = Field(
None,
description="食物的名称或null。如果不知道食物,则应为null。",
)
def record_person(name: str, age: int, fav_food: OptionalFavFood) -> str:
"""记录一个人的一些基本身份信息。
Args:
name: 人的姓名。
age: 人的年龄。
fav_food: 一个OptionalFavFood对象,其中包含人的最喜欢的食物或null值。如果不知道食物,则应为null。
"""
return f"记录人员{name},年龄为{age},最喜欢的食物是{fav_food.food}!"
chain = create_openai_fn_chain([record_person], llm, prompt, verbose=True)
chain.run(
"关于我12岁的汤米,最重要的是他会为了苹果派而做任何事情。"
)
> 进入新的LLMChain链...
格式化后的提示:
系统:您是一个世界级的记录实体的算法
人类:调用相关函数以记录以下输入中的实体
人类:关于我12岁的汤米,最重要的是他会为了苹果派而做任何事情。
人类:提示:确保以正确的格式回答
{'function_call': {'name': 'record_person', 'arguments': '{\n "name": "Tommy",\n "age": 12,\n "fav_food": {\n "food": "apple pie"\n }\n}'}}
> 完成链。
{'name': 'Tommy', 'age': 12, 'fav_food': {'food': 'apple pie'}}
如果我们传入多个Python函数或OpenAI函数,则返回的输出将采用以下形式:
{"name": "<<function_name>>", "arguments": {<<function_arguments>>}}
def record_dog(name: str, color: str, fav_food: OptionalFavFood) -> str:
"""记录一些关于狗的基本身份信息。
Args:
name: 狗的名字。
color: 狗的颜色。
fav_food: 一个OptionalFavFood对象,其中包含狗的最喜欢的食物或null值。如果不知道食物,则应为null。
"""
return f"记录狗{name},颜色为{color},最喜欢的食物是{fav_food}!"
chain = create_openai_fn_chain([record_person, record_dog], llm, prompt, verbose=True)
chain.run(
"我找不到我的狗亨利,他是一只小棕色的比格犬。你能给他发个消息吗?"
)
> 进入新的LLMChain链...
格式化后的提示:
系统:您是一个世界级的记录实体的算法
人类:调用相关函数以记录以下输入中的实体
人类:我找不到我的狗亨利,他是一只小棕色的比格犬。你能给他发个消息吗?
人类:提示:确保以正确的格式回答
{'function_call': {'name': 'record_dog', 'arguments': '{\n "name": "Henry",\n "color": "brown",\n "fav_food": {\n "food": null\n }\n}'}}
> 完成链。
{'name': 'record_dog',
'arguments': {'name': 'Henry', 'color': 'brown', 'fav_food': {'food': None}}}
使用OpenAI函数的其他链
有许多更具体的链使用OpenAI函数。