Экзамен: Структурированный вывод (Pydantic): solution.py
This commit is contained in:
+149
@@ -0,0 +1,149 @@
|
||||
import os
|
||||
import sys
|
||||
from typing import List, Optional
|
||||
|
||||
from langchain_core.output_parsers import PydanticOutputParser
|
||||
from langchain_core.prompts import PromptTemplate
|
||||
from langchain_openai import ChatOpenAI
|
||||
from pydantic import BaseModel, Field
|
||||
|
||||
# ------------------------------------------------------------------
|
||||
# 1. Pydantic модели
|
||||
# ------------------------------------------------------------------
|
||||
|
||||
|
||||
class PersonInfo(BaseModel):
|
||||
name: str = Field(..., description="Имя человека")
|
||||
age: Optional[int] = Field(None, description="Возраст (если известен)")
|
||||
profession: str = Field(..., description="Профессия или должность")
|
||||
skills: List[str] = Field(..., description="Список навыков")
|
||||
|
||||
|
||||
class MeetingNotes(BaseModel):
|
||||
date: str = 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. Создание парсера
|
||||
# ------------------------------------------------------------------
|
||||
|
||||
|
||||
def get_parser(model_cls):
|
||||
return PydanticOutputParser(pydantic_object=model_cls)
|
||||
|
||||
|
||||
person_parser = get_parser(PersonInfo)
|
||||
meeting_parser = get_parser(MeetingNotes)
|
||||
|
||||
# ------------------------------------------------------------------
|
||||
# 3. Prompt шаблоны (один общий, но с разными инструкциями)
|
||||
# ------------------------------------------------------------------
|
||||
|
||||
|
||||
prompt_template = """
|
||||
{format_instructions}
|
||||
|
||||
Входной текст:
|
||||
"{text}"
|
||||
"""
|
||||
|
||||
prompt = PromptTemplate(
|
||||
input_variables=["text", "format_instructions"],
|
||||
template=prompt_template,
|
||||
)
|
||||
|
||||
# ------------------------------------------------------------------
|
||||
# 4. LLM
|
||||
# ------------------------------------------------------------------
|
||||
# Для работы нужен API‑ключ OpenAI в переменной окружения OPENAI_API_KEY
|
||||
llm = ChatOpenAI(temperature=0, model_name="gpt-3.5-turbo")
|
||||
|
||||
# ------------------------------------------------------------------
|
||||
# 5. Функция выбора схемы и выполнения цепочки
|
||||
# ------------------------------------------------------------------
|
||||
|
||||
|
||||
def extract_structured(text: str):
|
||||
"""
|
||||
Определяем тип текста (person vs meeting) по эвристике
|
||||
и возвращаем валидированный объект Pydantic.
|
||||
"""
|
||||
# простая эвристика: наличие слова "встреча" или "meeting"
|
||||
if any(word in text.lower() for word in ["встреча", "meeting"]):
|
||||
parser = meeting_parser
|
||||
else:
|
||||
parser = person_parser
|
||||
|
||||
chain = prompt | llm | parser
|
||||
result = chain.invoke(
|
||||
{
|
||||
"text": text,
|
||||
"format_instructions": parser.get_format_instructions(),
|
||||
}
|
||||
)
|
||||
return result
|
||||
|
||||
|
||||
# ------------------------------------------------------------------
|
||||
# 6. CLI
|
||||
# ------------------------------------------------------------------
|
||||
|
||||
|
||||
def main():
|
||||
examples = [
|
||||
(
|
||||
"Анна, 28 лет, Python-разработчик. Навыки: FastAPI, Docker.",
|
||||
PersonInfo,
|
||||
),
|
||||
(
|
||||
"""Встреча 2023-08-15
|
||||
|
||||
Участники:
|
||||
- Иванов И.И.
|
||||
- Петров П.П.
|
||||
|
||||
Темы:
|
||||
1. Обновление проекта
|
||||
2. Распределение задач
|
||||
|
||||
Решения:
|
||||
- Завершить модуль X к 01/09
|
||||
- Назначить ответственных за Y
|
||||
|
||||
Следующие шаги:
|
||||
- Подготовить презентацию
|
||||
- Отправить отчёт команде""",
|
||||
MeetingNotes,
|
||||
),
|
||||
]
|
||||
|
||||
if len(sys.argv) > 1 and sys.argv[1] == "--example":
|
||||
for idx, (txt, model_cls) in enumerate(examples, start=1):
|
||||
print(f"\n=== Пример {idx} ===")
|
||||
obj = extract_structured(txt)
|
||||
print(obj.model_dump(indent=2))
|
||||
else:
|
||||
# пользовательский ввод
|
||||
print("Введите текст (Ctrl-D/Enter для завершения):")
|
||||
user_text = sys.stdin.read().strip()
|
||||
if not user_text:
|
||||
print("Пустой ввод.")
|
||||
return
|
||||
obj = extract_structured(user_text)
|
||||
print("\nРезультат:")
|
||||
print(obj.model_dump(indent=2))
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
main()
|
||||
Reference in New Issue
Block a user