Raw LLM 생성에서 구조화된 출력을 얻는 것은 어렵습니다.
예를 들어, 특정 스키마로 포맷된 모델 출력이 필요하다고 가정해 보겠습니다:
이를 위한 두 가지 주요 접근 방식이 있습니다:
Functions: 일부 LLM은 functions를 호출하여 LLM 응답에서 임의의 엔티티를 추출할 수 있습니다.
Parsing: 출력 구문 분석기는 LLM 응답을 구조화하는 클래스입니다.
일부 LLM만 함수(예: OpenAI)를 지원하며, 파서보다 더 일반적입니다.
구문 분석기는 제공된 스키마에 열거된 내용(예: 사람의 특정 속성)을 정확하게 추출합니다.
함수는 제공된 스키마 이외의 것(예: 사용자가 요청하지 않은 사람에 대한 속성)을 추론할 수 있습니다.
OpenAI 함수는 추출을 시작하는 한 가지 방법입니다.
LLM 출력에서 추출할 속성을 지정하는 스키마를 정의합니다.
그런 다음 create_extraction_chain을 사용하여 OpenAI 함수 호출을 통해 원하는 스키마를 추출할 수 있습니다.
!pip install langchain openai
Collecting langchain
Downloading langchain-0.1.3-py3-none-any.whl (803 kB)
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ 803.6/803.6 kB 5.5 MB/s eta 0:00:00
Collecting openai
Downloading openai-1.9.0-py3-none-any.whl (223 kB)
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ 223.4/223.4 kB 5.3 MB/s eta 0:00:00
import getpass
import os
os.environ["OPENAI_API_KEY"] = getpass.getpass() #"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"
from langchain.chains import create_extraction_chain
from langchain.chat_models import ChatOpenAI
# Schema
schema = {
"properties": {
"name": {"type": "string"},
"height": {"type": "integer"},
"hair_color": {"type": "string"},
},
"required": ["name", "height"],
}
# Input
inp = """Alex is 5 feet tall. Claudia is 1 feet taller Alex and jumps higher than him. Claudia is a brunette and Alex is blonde."""
# Run chain
llm = ChatOpenAI(temperature=0.7, model="gpt-3.5-turbo")
chain = create_extraction_chain(schema, llm)
chain.run(inp)
/usr/local/lib/python3.10/dist-packages/langchain_core/_api/deprecation.py:117: LangChainDeprecationWarning: The class `langchain_community.chat_models.openai.ChatOpenAI` was deprecated in langchain-community 0.0.10 and will be removed in 0.2.0. An updated version of the class exists in the langchain-openai package and should be used instead. To use it run `pip install -U langchain-openai` and import as `from langchain_openai import ChatOpenAI`.
warn_deprecated(
/usr/local/lib/python3.10/dist-packages/langchain_core/_api/deprecation.py:117: LangChainDeprecationWarning: The function `run` was deprecated in LangChain 0.1.0 and will be removed in 0.2.0. Use invoke instead.
warn_deprecated(
[{'name': 'Alex', 'height': 5, 'hair_color': 'blonde'},
{'name': 'Claudia', 'height': 6, 'hair_color': 'brunette'}]
create_extraction_chain
호출할 때 어떤 일이 일어나는지 자세히 살펴봅시다.
이 정보_추출 함수는 여기에 정의되어 있으며 딕셔너리를 반환합니다.
모델 출력에서 dict를 볼 수 있습니다:
{
"info": [
{
"name": "Alex",
"height": 5,
"hair_color": "blonde"
},
{
"name": "Claudia",
"height": 6,
"hair_color": "brunette"
}
]
}
그런 다음 create_extraction_chain
은 JsonKeyOutputFunctionsParser
를 사용하여 원시 LLM 출력을 파싱합니다.
그 결과 위의 체인에서 반환된 JSON 객체 목록이 생성됩니다:
[{'name': 'Alex', 'height': 5, 'hair_color': 'blonde'},
{'name': 'Claudia', 'height': 6, 'hair_color': 'brunette'}]
이를 더 확장할 수 있습니다.
개와 사람을 구분하고 싶다고 가정해 봅시다.
각 속성에 person_
및 dog_
접두사를 추가하면 됩니다.
schema = {
"properties": {
"person_name": {"type": "string"},
"person_height": {"type": "integer"},
"person_hair_color": {"type": "string"},
"dog_name": {"type": "string"},
"dog_breed": {"type": "string"},
},
"required": ["person_name", "person_height"],
}
chain = create_extraction_chain(schema, llm)
inp = """Alex is 5 feet tall. Claudia is 1 feet taller Alex and jumps higher than him. Claudia is a brunette and Alex is blonde.
Alex's dog Frosty is a labrador and likes to play hide and seek."""
chain.run(inp)
[{'person_name': 'Alex',
'person_height': 5,
'person_hair_color': 'blonde',
'dog_name': 'Frosty',
'dog_breed': 'labrador'},
{'person_name': 'Claudia',
'person_height': 6,
'person_hair_color': 'brunette'}]
필수: []
를 사용하면 모델이 단일 엔티티(사람 또는 개)에 대해 사람 속성만 또는 개 속성만을 반환하도록 허용합니다.
schema = {
"properties": {
"person_name": {"type": "string"},
"person_height": {"type": "integer"},
"person_hair_color": {"type": "string"},
"dog_name": {"type": "string"},
"dog_breed": {"type": "string"},
},
"required": [],
}
chain = create_extraction_chain(schema, llm)
inp = """Alex is 5 feet tall. Claudia is 1 feet taller Alex and jumps higher than him. Claudia is a brunette and Alex is blonde.
Willow is a German Shepherd that likes to play with other dogs and can always be found playing with Milo, a border collie that lives close by."""
chain.run(inp)
[{'person_name': 'Alex', 'person_height': 5, 'person_hair_color': 'blonde'},
{'person_name': 'Claudia',
'person_height': 6,
'person_hair_color': 'brunette'},
{'dog_name': 'Willow', 'dog_breed': 'German Shepherd'},
{'dog_name': 'Milo', 'dog_breed': 'Border Collie'}]
The power of functions (파서만 사용할 때와 비교했을 때) 의미 추출을 수행할 수 있는 능력에 있습니다.
특히 '스키마에 명시적으로 열거되지 않은 것을 요청할 수 있다'는 점입니다.
개에 대한 지정되지 않은 추가 정보를 원한다고 가정해 봅시다.
비정형 추출을 위한 자리 표시자인 dog_extra_info
를 추가할 수 있습니다.
schema = {
"properties": {
"person_name": {"type": "string"},
"person_height": {"type": "integer"},
"person_hair_color": {"type": "string"},
"dog_name": {"type": "string"},
"dog_breed": {"type": "string"},
"dog_extra_info": {"type": "string"},
},
}
chain = create_extraction_chain(schema, llm)
chain.run(inp)
[{'person_name': 'Alex', 'person_height': 5, 'person_hair_color': 'blonde'},
{'person_name': 'Claudia',
'person_height': 6,
'person_hair_color': 'brunette'},
{'dog_name': 'Willow',
'dog_breed': 'German Shepherd',
'dog_extra_info': 'likes to play with other dogs'},
{'dog_name': 'Milo',
'dog_breed': 'border collie',
'dog_extra_info': 'lives close by'}]
이를 통해 개에 대한 추가 정보를 얻을 수 있습니다.
Pydantic은 Python용 데이터 유효성 검사 및 설정 관리 라이브러리입니다.
이 라이브러리를 사용하면 객체를 인스턴스화할 때 자동으로 유효성이 검사되는 속성을 가진 데이터 클래스를 만들 수 있습니다.
유형으로 주석이 달린 속성을 가진 클래스를 정의할 수 있습니다.
from typing import Optional
from langchain.chains import create_extraction_chain_pydantic
from langchain.pydantic_v1 import BaseModel
# Pydantic data class
class Properties(BaseModel):
person_name: str
person_height: int
person_hair_color: str
dog_breed: Optional[str]
dog_name: Optional[str]
# Extraction
chain = create_extraction_chain_pydantic(pydantic_schema=Properties, llm=llm)
# Run
inp = """Alex is 5 feet tall. Claudia is 1 feet taller Alex and jumps higher than him. Claudia is a brunette and Alex is blonde."""
chain.run(inp)
[Properties(person_name='Alex', person_height=5, person_hair_color='blonde', dog_breed=None, dog_name=None),
Properties(person_name='Claudia', person_height=6, person_hair_color='brunette', dog_breed=None, dog_name=None)]
trace에서 볼 수 있듯이, 위와 같이 Pydantic 스키마와 함께 information_extraction
함수를 사용합니다.
출력 구문 분석기는 언어 모델 응답을 구조화하는 데 도움이 되는 클래스입니다.
위에 표시된 것처럼 create_extraction_chain
에서 OpenAI 함수 호출의 출력을 구문 분석하는 데 사용됩니다.
하지만 함수와 독립적으로 사용할 수도 있습니다.
위와 마찬가지로 Pydantic 데이터 클래스를 기반으로 한 세대를 파싱해 보겠습니다.
from typing import Optional, Sequence
from langchain.llms import OpenAI
from langchain.output_parsers import PydanticOutputParser
from langchain.prompts import (
PromptTemplate,
)
from pydantic import BaseModel, Field, validator
class Person(BaseModel):
person_name: str
person_height: int
person_hair_color: str
dog_breed: Optional[str]
dog_name: Optional[str]
class People(BaseModel):
"""Identifying information about all people in a text."""
people: Sequence[Person]
# Run
query = """Alex is 5 feet tall. Claudia is 1 feet taller Alex and jumps higher than him. Claudia is a brunette and Alex is blonde."""
# Set up a parser + inject instructions into the prompt template.
parser = PydanticOutputParser(pydantic_object=People)
# print(parser.get_format_instructions())
# Prompt
prompt = PromptTemplate(
template="Answer the user query.\n{format_instructions}\n{query}\n",
input_variables=["query"],
partial_variables={"format_instructions": parser.get_format_instructions()},
)
# Run
_input = prompt.format_prompt(query=query)
# print(_input)
model = OpenAI(temperature=0)
output = model(_input.to_string())
parser.parse(output)
/usr/local/lib/python3.10/dist-packages/langchain_core/_api/deprecation.py:117: LangChainDeprecationWarning: The class `langchain_community.llms.openai.OpenAI` was deprecated in langchain-community 0.0.10 and will be removed in 0.2.0. An updated version of the class exists in the langchain-openai package and should be used instead. To use it run `pip install -U langchain-openai` and import as `from langchain_openai import OpenAI`.
warn_deprecated(
/usr/local/lib/python3.10/dist-packages/langchain_core/_api/deprecation.py:117: LangChainDeprecationWarning: The function `__call__` was deprecated in LangChain 0.1.7 and will be removed in 0.2.0. Use invoke instead.
warn_deprecated(
People(people=[Person(person_name='Alex', person_height=5, person_hair_color='blonde', dog_breed=None, dog_name=None), Person(person_name='Claudia', person_height=6, person_hair_color='brunette', dog_breed=None, dog_name=None)])
LLM이 원하는 형식으로 출력하도록하기 위해 투샷 프롬프트를 제공합니다.
그리고 조금 더 작업해야 합니다:
다른 경우에도 이와 같은 결과를 볼 수 있습니다.
from langchain.llms import OpenAI
from langchain.output_parsers import PydanticOutputParser
from langchain.prompts import (
PromptTemplate,
)
from pydantic import BaseModel, Field, validator
# Define your desired data structure.
class Joke(BaseModel):
setup: str = Field(description="question to set up a joke")
punchline: str = Field(description="answer to resolve the joke")
# You can add custom validation logic easily with Pydantic.
@validator("setup")
def question_ends_with_question_mark(cls, field):
if field[-1] != "?":
raise ValueError("Badly formed question!")
return field
# And a query intended to prompt a language model to populate the data structure.
joke_query = "Tell me a joke."
# Set up a parser + inject instructions into the prompt template.
parser = PydanticOutputParser(pydantic_object=Joke)
print(parser.get_format_instructions())
# Prompt
prompt = PromptTemplate(
template="Answer the user query.\n{format_instructions}\n{query}\n",
input_variables=["query"],
partial_variables={"format_instructions": parser.get_format_instructions()},
)
# Run
_input = prompt.format_prompt(query=joke_query)
print(_input)
model = OpenAI(temperature=0)
output = model(_input.to_string())
parser.parse(output)
The output should be formatted as a JSON instance that conforms to the JSON schema below.
As an example, for the schema {"properties": {"foo": {"title": "Foo", "description": "a list of strings", "type": "array", "items": {"type": "string"}}}, "required": ["foo"]}
the object {"foo": ["bar", "baz"]} is a well-formatted instance of the schema. The object {"properties": {"foo": ["bar", "baz"]}} is not well-formatted.
Here is the output schema:
{"properties": {"setup": {"title": "Setup", "description": "question to set up a joke", "type": "string"}, "punchline": {"title": "Punchline", "description": "answer to resolve the joke", "type": "string"}}, "required": ["setup", "punchline"]}
text='Answer the user query.\nThe output should be formatted as a JSON instance that conforms to the JSON schema below.\n\nAs an example, for the schema {"properties": {"foo": {"title": "Foo", "description": "a list of strings", "type": "array", "items": {"type": "string"}}}, "required": ["foo"]}\nthe object {"foo": ["bar", "baz"]} is a well-formatted instance of the schema. The object {"properties": {"foo": ["bar", "baz"]}} is not well-formatted.\n\nHere is the output schema:\n```\n{"properties": {"setup": {"title": "Setup", "description": "question to set up a joke", "type": "string"}, "punchline": {"title": "Punchline", "description": "answer to resolve the joke", "type": "string"}}, "required": ["setup", "punchline"]}\n```\nTell me a joke.\n'
Joke(setup='Why did the tomato turn red?', punchline='Because it saw the salad dressing!')
보시다시피, 우리가 원래 원했던 스키마를 따르는 Joke 클래스의 출력을 얻습니다: setup
, punchline
.
- 스키마 정의: 사람의 이름과 나이를 추출하기 위한 스키마를 정의합니다. 이 스키마는 이름을 문자열로, 나이를 정수로 갖는 구조를 가집니다.
- 텍스트 입력: 추출할 정보를 포함하는 샘플 텍스트를 준비합니다.
- 추출 체인 생성: langchain 라이브러리를 사용하여 추출 체인을 생성합니다. 이 체인은 입력된 텍스트에서 이름과 나이 정보를 추출합니다.
- 추출 실행 및 결과 확인: 생성된 체인을 실행하여 텍스트에서 정보를 추출하고, 결과를 확인합니다.
# 스키마 정의
schema = {
"properties": {
"name": {"type": "string"},
"age": {"type": "integer"}
},
"required": ["name", "age"]
}
# 텍스트 입력
input_text = "John is 30 years old. Mary is two years younger than John."
# 추출 체인 생성
llm = ChatOpenAI(temperature=0, model="gpt-3.5-turbo")
chain = create_extraction_chain(schema, llm)
# 추출 실행
result = chain.run(input_text)
# 결과 출력
print(result)
# [{'name': 'John', 'age': 30}, {'name': 'Mary', 'age': 28}]
# from langchain.chains import create_extraction_chain_pydantic
class Properties(BaseModel):
name: str
age: int
# 텍스트 입력
input_text = "John is 30 years old. Mary is two years younger than John."
# 추출 체인 생성
llm = ChatOpenAI(temperature=0, model="gpt-3.5-turbo")
chain = create_extraction_chain_pydantic(pydantic_schema=Properties, llm=llm)
# 추출 실행
result = chain.run(input_text)
# 결과 출력
print(result)
# [Properties(name='John', age=30), Properties(name='Mary', age=28)]
- 이벤트 정보 스키마 정의: 이벤트 이름, 날짜, 위치, 참가자 등의 정보를 포함하는 스키마를 정의합니다.
- 텍스트 입력: 추출할 정보를 포함하는 복잡한 서술 문단을 준비합니다
- 추출 체인 생성 및 실행: langchain 라이브러리를 사용하여 추출 체인을 생성하고, 준비된 텍스트에서 정보를 추출합니다.
- 결과 확인 및 분석: 추출된 결과를 확인하고, 스크립트가 복잡한 쿼리를 어떻게 처리하는지 분석합니다.
schema = {
"properties": {
"event_name": {"type": "string"},
"date": {"type": "string"}, # 날짜검증이 필요할 수도
"location": {"type": "string"},
"participants": {"type": "array", "items":{"type": "string"}},
},
"required": ["event_name", "date", "location"]
}
# 텍스트 입력
input_text = "The annual science fair, which will be held on March 15th at the city hall, is expected to attract many notable scientists including Dr. Jane Doe and Prof. John Smith."
# 추출 체인 생성
llm = ChatOpenAI(temperature=0, model="gpt-3.5-turbo")
chain = create_extraction_chain(schema, llm)
# 추출 실행
result = chain.run(input_text)
# 결과 출력
print(result)
#[{'event_name': 'annual science fair', 'date': 'March 15th', 'location': 'city hall', 'participants': ['Dr. Jane Doe', 'Prof. John Smith']}]
class Event(BaseModel):
event_name: str
date: str
location: str
participants: list
query = "The annual science fair, which will be held on March 15th at the city hall, is expected to attract many notable scientists including Dr. Jane Doe and Prof. John Smith."
# Set Parser
parser = PydanticOutputParser(pydantic_object=Event)
# Prompt template
prompt = PromptTemplate(
template="Answer the query. \n {format_instructions} \m {query} \n",
input_variables=["query"],
partial_variables={"format_instructions": parser.get_format_instructions()}
)
# run chain
_input = prompt.format_prompt(query=query)
model = OpenAI(temperature=0)
outputs = model(_input.to_string())
parser.parse(outputs)
#Event(event_name='Annual Science Fair', date='March 15th', location='City Hall', participants=['Dr. Jane Doe', 'Prof. John Smith'])
class Event(BaseModel):
name: str
event_name: str
date: str
location: str
class People(BaseModel):
"""Event Participants"""
participants: Sequence[Event]
query = "The annual science fair, which will be held on March 15th at the city hall, is expected to attract many notable scientists including Dr. Jane Doe and Prof. John Smith."
# Set Parser
parser = PydanticOutputParser(pydantic_object=People)
# Prompt template
prompt = PromptTemplate(
template="Answer the query. \n {format_instructions} \m {query} \n",
input_variables=["query"],
partial_variables={"format_instructions": parser.get_format_instructions()}
)
# run chain
_input = prompt.format_prompt(query=query)
model = OpenAI(temperature=0)
outputs = model(_input.to_string())
parser.parse(outputs)
#People(participants=[Event(name='Dr. Jane Doe', event_name='Annual
#Science Fair', date='March 15th', location='City Hall'), Event(name='Prof. John Smith', event_name='Annual Science Fair', date='March 15th', location='City Hall')])
- 사용자 입력 처리: 사용자가 추출하고자 하는 정보 유형을 입력할 수 있는 인터페이스를 구현합니다. 예를 들어, 사용자가 "이름", "나이", "직업" 등을 입력할 수 있습니다.
- 동적 스키마 생성: 사용자의 입력을 기반으로 동적으로 스키마를 생성하는 함수를 구현합니다. 이 함수는 사용자가 입력한 정보 유형에 따라 적절한 스키마를 만듭니다.
- 추출 체인 설정 및 실행: 생성된 스키마를 사용하여 langchain 라이브러리의 추출 체인을 설정하고 실행합니다.
- 결과 출력 및 검증: 추출된 결과를 출력하고, 스크립트가 정확하게 사용자 요구 사항에 맞는 스키마를 생성했는지 검증합니다.
def create_dynamic_schema(user_inputs):
schema = {"properties": {}, "required": []}
for input in user_inputs:
# 여기에서 각 입력 유형에 대한 스키마 정의를 추가합니다.
schema["properties"][input] = {"type": "string"} # 예시: 모든 필드를 문자열로 처리
schema["required"].append(input)
return schema
# 사용자 입력
user_inputs = ["name", "age"] # 예시: 사용자가 입력한 필드
# 스키마 생성
dynamic_schema = create_dynamic_schema(user_inputs)
# 텍스트 입력
input_text = "John, a 30-year-old engineer, works at Acme Corp."
# 추출 체인 생성
llm = ChatOpenAI(temperature=0, model="gpt-3.5-turbo")
chain = create_extraction_chain(dynamic_schema, llm)
# 추출 실행
result = chain.run(input_text)
# 결과 출력
print(result)
# [{'name': 'John', 'age': '30'}, {'name': 'engineer'}, {'name': 'Acme Corp.'}]