diff --git a/main.py b/main.py new file mode 100644 index 0000000..191e822 --- /dev/null +++ b/main.py @@ -0,0 +1,91 @@ +"""Разбор сырого текста задания в плоскую карточку (Pydantic + LangChain + OpenRouter).""" +from __future__ import annotations + +import os + +from dotenv import load_dotenv +from langchain_core.output_parsers import PydanticOutputParser +from langchain_core.prompts import PromptTemplate +from langchain_openai import ChatOpenAI +from pydantic import BaseModel, Field + +load_dotenv() + +OPENROUTER_BASE_URL = os.getenv("OPENROUTER_BASE_URL", "https://openrouter.ai/api/v1") +# gpt-oss-20b:free часто в 429 — fallback на рабочую free-модель OpenRouter +OPENROUTER_MODEL = os.getenv( + "OPENROUTER_MODEL", + "poolside/laguna-m.1:free", +) + +SAMPLE_TASK_TEXT = ( + "Сдайте к пятнице мини-отчёт по LangChain: 2 страницы, упор на агентов. " + "Оценка: за полноту и за пример кода." +) + + +class TaskCard(BaseModel): + """Плоская карточка задания.""" + + title: str = Field(description="Краткий заголовок задания") + subject: str = Field(description="Предмет или тема (LangChain, Python и т.д.)") + deadline_hint: str = Field( + description="Подсказка по сроку в свободной форме (к пятнице, до 20 марта)" + ) + deliverable_type: str = Field( + description="Что нужно сдать: отчёт, код, презентация и т.п." + ) + grading_hints: list[str] = Field( + description="Список критериев или подсказок по оценке" + ) + + +def build_chain(): + parser = PydanticOutputParser(pydantic_object=TaskCard) + prompt = PromptTemplate( + template=( + "Ты помощник студента. Из текста ниже извлеки структурированную карточку задания.\n" + "Верни только данные в формате из инструкций, без пояснений.\n\n" + "Текст задания:\n{task_text}\n\n" + "{format_instructions}" + ), + input_variables=["task_text"], + partial_variables={"format_instructions": parser.get_format_instructions()}, + ) + llm = ChatOpenAI( + model=OPENROUTER_MODEL, + base_url=OPENROUTER_BASE_URL, + api_key=os.getenv("OPENAI_API_KEY"), + temperature=0, + ) + return prompt | llm | parser + + +def print_summary(card: TaskCard) -> None: + print("\n--- Краткая сводка ---") + print(f"title: {card.title}") + print(f"subject: {card.subject}") + print(f"deadline_hint: {card.deadline_hint}") + print(f"deliverable_type: {card.deliverable_type}") + print(f"grading_hints: {card.grading_hints}") + + +def parse_task_text(task_text: str) -> TaskCard: + chain = build_chain() + return chain.invoke({"task_text": task_text}) + + +def main() -> None: + print(f"Модель OpenRouter: {OPENROUTER_MODEL}") + print(f"\nВход:\n{SAMPLE_TASK_TEXT}\n") + + card = parse_task_text(SAMPLE_TASK_TEXT) + + print("\n--- Валидированный объект (model_dump) ---") + print(card.model_dump()) + + print_summary(card) + + +if __name__ == "__main__": + main()