Parsowanie
LLM-ki, które potrafią dobrze wykonywać polecenia, mogą być zadane z zadaniem przedstawienia informacji w określonym formacie.
Metoda ta polega na zaprojektowaniu dobrych poleceń, a następnie parsowaniu wyników LLM-ek, aby wyciągnęły informacje skutecznie.
Tutaj użyjemy Claude'a, który świetnie radzi sobie z wykonywaniem poleceń! Zobacz Modele antropogenne.
from langchain_anthropic.chat_models import ChatAnthropic
model = ChatAnthropic(model_name="claude-3-sonnet-20240229", temperature=0)
wskazówka: Wszystkie te same uwagi dotyczące jakości wyodrębniania mają zastosowanie do metody parsowania. Przeczytaj wytyczne dotyczące jakości wyodrębniania.
Ten sam poradnik ma na celu być prosty, ale zazwyczaj powinien zawierać przykłady odniesienia, aby wycisnąć jak najlepszą wydajność!
Użycie PydanticOutputParser
W poniższym przykładzie użyto wbudowanego PydanticOutputParser
do parsowania wyniku modelu czatu.
from typing import List, Optional
from langchain.output_parsers import PydanticOutputParser
from langchain_core.prompts import ChatPromptTemplate
from langchain_core.pydantic_v1 import BaseModel, Field, validator
class Person(BaseModel):
"""Informacje o osobie."""
name: str = Field(..., description="Imię osoby")
height_in_meters: float = Field(
..., description="Wzrost osoby wyrażony w metrach."
)
class People(BaseModel):
"""Informacje identyfikacyjne o wszystkich osobach w tekście."""
people: List[Person]
parser = PydanticOutputParser(pydantic_object=People)
prompt = ChatPromptTemplate.from_messages(
[
(
"system",
"Odpowiedz na zapytanie użytkownika. Obejmij wynik tagami `json`\n{format_instructions}",
),
("human", "{query}"),
]
).partial(format_instructions=parser.get_format_instructions())
Spójrzmy, jakie informacje są wysyłane do modelu.
query = "Anna ma 23 lata i ma 183 cm wzrostu"
print(prompt.format_prompt(query=query).to_string())
System: Odpowiedz na zapytanie użytkownika. Obejmij wynik tagami `json`
Wyjście powinno być sformatowane jako instancja JSON zgodna ze schematem JSON poniżej.
Jako przykład, dla schematu {"properties": {"foo": {"title": "Foo", "description": "a list of strings", "type": "array", "items": {"type": "string"}}}, "required": ["foo"]}
obiekt {"foo": ["bar", "baz"]} jest dobrze sformatowaną instancją schematu. Obiekt {"properties": {"foo": ["bar", "baz"]}} nie jest właściwie sformatowany.
Oto schemat wyjściowy:
{"description": "Informacje identyfikacyjne o wszystkich osobach w tekście.", "properties": {"people": {"title": "People", "type": "array", "items": {"$ref": "#/definitions/Person"}}}, "required": ["people"], "definitions": {"Person": {"title": "Person", "description": "Informacje o osobie.", "type": "object", "properties": {"name": {"title": "Name", "description": "Imię osoby", "type": "string"}, "height_in_meters": {"title": "Height In Meters", "description": "Wzrost osoby wyrażony w metrach.", "type": "number"}}, "required": ["name", "height_in_meters"]}}}
Człowiek: Anna ma 23 lata i ma 183 cm wzrostu
chain = prompt | model | parser
chain.invoke({"query": query})
People(people=[Person(name='Anna', height_in_meters=1.83)])
Własne parsowanie
Stworzenie własnej prośby i parsera jest proste z użyciem LangChain
i LCEL
.
Możesz wykorzystać prostą funkcję do parsowania wyników z modelu!
import json
import re
from typing import List, Optional
from langchain_anthropic.chat_models import ChatAnthropic
from langchain_core.messages import AIMessage
from langchain_core.prompts import ChatPromptTemplate
from langchain_core.pydantic_v1 import BaseModel, Field, validator
class Osoba(BaseModel):
"""Informacje o osobie."""
name: str = Field(..., description="Imię osoby")
height_in_meters: float = Field(
..., description="Wzrost osoby wyrażony w metrach."
)
class Ludzie(BaseModel):
"""Identyfikujące informacje dotyczące wszystkich osób w tekście."""
people: List[Osoba]
prompt = ChatPromptTemplate.from_messages(
[
(
"system",
"Odpowiedz na zapytanie użytkownika. Wygeneruj odpowiedź w formacie JSON zgodnym "
"ze wskazanym schematem: ```json\n{schema}\n```. Upewnij się, że odpowiedź jest "
"umieszczona pomiędzy znacznikami ```json oraz ```",
),
("human", "{query}"),
]
).partial(schema=Ludzie.schema())
def extract_json(message: AIMessage) -> List[dict]:
"""Wyodrębnia zawartość JSON z ciągu, w którym JSON jest osadzony pomiędzy znacznikami ```json oraz ```.
Parametry:
text (str): Tekst zawierający zawartość JSON.
Zwraca:
list: Lista wyodrębnionych ciągów JSON.
"""
text = message.content
pattern = r"```json(.*?)```"
matches = re.findall(pattern, text, re.DOTALL)
try:
return [json.loads(match.strip()) for match in matches]
except Exception:
raise ValueError(f"Nie udało się sparsować: {message}")
query = "Anna ma 23 lata i ma 1,83 metra wzrostu"
print(prompt.format_prompt(query=query).to_string())
System: Odpowiedz na zapytanie użytkownika. Wygeneruj odpowiedź w formacie JSON zgodnym ze wskazanym schematem: \`\`\`json
{'title': 'Ludzie', 'description': 'Identyfikujące informacje dotyczące wszystkich osób w tekście.', 'type': 'object', 'properties': {'people': {'title': 'People', 'type': 'array', 'items': {'$ref': '#/definitions/Person'}}}, 'required': ['people'], 'definitions': {'Person': {'title': 'Osoba', 'description': 'Informacje o osobie.', 'type': 'object', 'properties': {'name': {'title': 'Name', 'description': 'Imię osoby', 'type': 'string'}, 'height_in_meters': {'title': 'Height In Meters', 'description': 'Wzrost osoby wyrażony w metrach.', 'type': 'number'}}, 'required': ['name', 'height_in_meters']}}}
\`\`\`. Upewnij się, że odpowiedź jest umieszczona pomiędzy znacznikami \`\`\`json oraz \`\`\`
Human: Anna ma 23 lata i ma 1,83 metra wzrostu
chain = prompt | model | extract_json
chain.invoke({"query": query})
[{'people': [{'name': 'Anna', 'height_in_meters': 1.83}]}]
Inne biblioteki
Jeśli rozważasz podejście parsowania, sprawdź bibliotekę Kor. Jest napisana przez jednego z opiekunów LangChain
i pomaga tworzyć prośby uwzględniając przykłady, umożliwia kontrolowanie formatów (np. JSON lub CSV) oraz wyraża schemat w TypeScript. Wygląda na to, że działa świetnie!