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()