Экзамен: Структурированный вывод (Pydantic): solution.py
This commit is contained in:
+127
@@ -0,0 +1,127 @@
|
|||||||
|
import os
|
||||||
|
import argparse
|
||||||
|
from datetime import datetime
|
||||||
|
from typing import List, Union
|
||||||
|
|
||||||
|
from langchain_core.prompts import PromptTemplate
|
||||||
|
from langchain_core.output_parsers import PydanticOutputParser
|
||||||
|
from langchain_core.runnables import Runnable
|
||||||
|
from langchain_openai import ChatOpenAI
|
||||||
|
from pydantic import BaseModel, Field
|
||||||
|
|
||||||
|
# ------------------------------------------------------------------
|
||||||
|
# 1. Pydantic модели
|
||||||
|
# ------------------------------------------------------------------
|
||||||
|
class PersonInfo(BaseModel):
|
||||||
|
name: str = Field(..., description="Имя человека")
|
||||||
|
age: int | None = Field(None, description="Возраст (необязательно)")
|
||||||
|
profession: str = Field(..., description="Профессия")
|
||||||
|
skills: List[str] = Field(..., description="Список навыков")
|
||||||
|
|
||||||
|
class MeetingNotes(BaseModel):
|
||||||
|
date: datetime = Field(..., description="Дата встречи в формате YYYY-MM-DD")
|
||||||
|
participants: List[str] = Field(..., description="Список участников")
|
||||||
|
topics: List[str] = Field(..., description="Темы обсуждения")
|
||||||
|
decisions: List[str] = Field(..., description="Принятые решения")
|
||||||
|
next_steps: List[str] = Field(..., description="Следующие шаги")
|
||||||
|
|
||||||
|
# ------------------------------------------------------------------
|
||||||
|
# 2. Шаблоны и парсеры
|
||||||
|
# ------------------------------------------------------------------
|
||||||
|
person_prompt = PromptTemplate(
|
||||||
|
input_variables=["text"],
|
||||||
|
template=(
|
||||||
|
"Найди в тексте информацию о человеке и выведи её в формате JSON, "
|
||||||
|
"соответствующем схеме PersonInfo.\n"
|
||||||
|
"Текст: {text}\n"
|
||||||
|
"Формат: {format_instructions}"
|
||||||
|
),
|
||||||
|
)
|
||||||
|
|
||||||
|
meeting_prompt = PromptTemplate(
|
||||||
|
input_variables=["text"],
|
||||||
|
template=(
|
||||||
|
"Найди в тексте информацию о встрече и выведи её в формате JSON, "
|
||||||
|
"соответствующем схеме MeetingNotes.\n"
|
||||||
|
"Текст: {text}\n"
|
||||||
|
"Формат: {format_instructions}"
|
||||||
|
),
|
||||||
|
)
|
||||||
|
|
||||||
|
person_parser = PydanticOutputParser(pydantic_object=PersonInfo)
|
||||||
|
meeting_parser = PydanticOutputParser(pydantic_object=MeetingNotes)
|
||||||
|
|
||||||
|
person_chain = (
|
||||||
|
person_prompt | ChatOpenAI(temperature=0) | person_parser
|
||||||
|
)
|
||||||
|
meeting_chain = (
|
||||||
|
meeting_prompt | ChatOpenAI(temperature=0) | meeting_parser
|
||||||
|
)
|
||||||
|
|
||||||
|
# ------------------------------------------------------------------
|
||||||
|
# 3. Выбор схемы
|
||||||
|
# ------------------------------------------------------------------
|
||||||
|
def choose_chain(text: str) -> Runnable:
|
||||||
|
"""Определяем, какой тип данных в тексте."""
|
||||||
|
lowered = text.lower()
|
||||||
|
# простая эвристика: наличие ключевых слов
|
||||||
|
if any(word in lowered for word in ("встреча", "meeting", "дата", "participants", "topics")):
|
||||||
|
return meeting_chain
|
||||||
|
return person_chain
|
||||||
|
|
||||||
|
# ------------------------------------------------------------------
|
||||||
|
# 4. CLI
|
||||||
|
# ------------------------------------------------------------------
|
||||||
|
def main():
|
||||||
|
parser = argparse.ArgumentParser(description="Structured output extractor")
|
||||||
|
parser.add_argument(
|
||||||
|
"--text",
|
||||||
|
type=str,
|
||||||
|
help="Текст для обработки. Если не задан, используется пример.",
|
||||||
|
)
|
||||||
|
args = parser.parse_args()
|
||||||
|
|
||||||
|
if args.text:
|
||||||
|
input_text = args.text
|
||||||
|
else:
|
||||||
|
# пример для PersonInfo
|
||||||
|
input_text = (
|
||||||
|
"Анна, 28 лет, Python-разработчик. Навыки: FastAPI, Docker."
|
||||||
|
)
|
||||||
|
# пример для MeetingNotes
|
||||||
|
# input_text = (
|
||||||
|
# "Дата: 2024-05-01. Участники: Анна, Боб. Темы: проект X, бюджет. "
|
||||||
|
# "Решения: утвердить бюджет. Next steps: подготовить презентацию."
|
||||||
|
# )
|
||||||
|
|
||||||
|
chain = choose_chain(input_text)
|
||||||
|
result = chain.invoke({"text": input_text})
|
||||||
|
|
||||||
|
# result уже валидированный объект Pydantic
|
||||||
|
print("\nВыполнено. Валидированный объект:")
|
||||||
|
print(result.model_dump(indent=2))
|
||||||
|
|
||||||
|
# Краткая сводка
|
||||||
|
if isinstance(result, PersonInfo):
|
||||||
|
summary = (
|
||||||
|
f"Человек: {result.name}, "
|
||||||
|
f"возраст: {result.age if result.age is not None else 'не указан'}, "
|
||||||
|
f"профессия: {result.profession}, "
|
||||||
|
f"навыки: {', '.join(result.skills)}"
|
||||||
|
)
|
||||||
|
else:
|
||||||
|
summary = (
|
||||||
|
f"Встреча: {result.date.date()}, "
|
||||||
|
f"участники: {', '.join(result.participants)}, "
|
||||||
|
f"темы: {', '.join(result.topics)}, "
|
||||||
|
f"решения: {', '.join(result.decisions)}, "
|
||||||
|
f"следующие шаги: {', '.join(result.next_steps)}"
|
||||||
|
)
|
||||||
|
print("\nСводка:")
|
||||||
|
print(summary)
|
||||||
|
|
||||||
|
if __name__ == "__main__":
|
||||||
|
# Для работы с OpenAI требуется переменная окружения OPENAI_API_KEY
|
||||||
|
if "OPENAI_API_KEY" not in os.environ:
|
||||||
|
raise RuntimeError("Необходимо задать переменную окружения OPENAI_API_KEY")
|
||||||
|
main()
|
||||||
Reference in New Issue
Block a user