from __future__ import annotations import os from typing import List from langchain_core.prompts import PromptTemplate from langchain_openai import ChatOpenAI from langchain_core.output_parsers import PydanticOutputParser from pydantic import BaseModel, Field class AssignmentCard(BaseModel): """Структурированная карточка задания.""" title: str = Field(..., description="Краткое название задачи") subject: str = Field(..., description="Область знаний или предмет") deadline_hint: str | None = Field( None, description=( "Указание срока сдачи в свободной форме (например, «к пятнице»). " "Если не найдено – None" ), ) deliverable_type: str = Field(..., description="Тип сдаваемого материала") grading_hints: List[str] = Field( default_factory=list, description=( "Ключевые критерии оценки, перечисленные в тексте. " "Если не найдено – пустой список" ), ) def build_chain() -> PromptTemplate: """Создаёт цепочку Prompt → LLM → Pydantic парсер.""" parser = PydanticOutputParser(pydantic_object=AssignmentCard) prompt_template = ( "Ниже приведено описание задания от преподавателя. " "Ваша задача – извлечь из него следующую информацию:\n" "- title: название задачи\n" "- subject: предмет/область знаний\n" "- deadline_hint: указание срока сдачи (если есть)\n" "- deliverable_type: что сдать (отчёт, код, презентация и т.д.)\n" "- grading_hints: список критериев оценки\n\n" "Ответ должен быть строго в формате JSON, соответствующем модели " f"{AssignmentCard.__name__}.\n" "{format_instructions}\n\n" "Текст задания:\n{assignment_text}" ) prompt = PromptTemplate( template=prompt_template, input_variables=["assignment_text"], partial_variables={"format_instructions": parser.get_format_instructions()}, ) return prompt def main() -> None: """Пример использования цепочки.""" # Получаем ключ API из переменной окружения api_key = os.getenv("OPENAI_API_KEY") if not api_key: raise RuntimeError( "Переменная окружения OPENAI_API_KEY не установлена. " "Установите её перед запуском." ) llm = ChatOpenAI(api_key=api_key, temperature=0) prompt = build_chain() parser = PydanticOutputParser(pydantic_object=AssignmentCard) chain = prompt | llm | parser # Пример входного текста assignment_text = ( "Сдайте к пятнице мини-отчёт по LangChain: 2 страницы, упор на агентов. " "Оценка: за полноту и за пример кода." ) result = chain.invoke({"assignment_text": assignment_text}) # Выводим валидированный объект print("=== Валидированная карточка ===") print(result.model_dump()) # Краткая человекочитаемая сводка summary = ( f"Задача: {result.title}\n" f"Предмет: {result.subject}\n" f"Срок сдачи: {result.deadline_hint or 'не указан'}\n" f"Тип сдаваемого материала: {result.deliverable_type}\n" f"Критерии оценки: {', '.join(result.grading_hints) if result.grading_hints else 'нет'}" ) print("\n=== Сводка ===") print(summary) if __name__ == "__main__": main()