diff --git a/main.py b/main.py new file mode 100644 index 0000000..4beab7d --- /dev/null +++ b/main.py @@ -0,0 +1,87 @@ +"""Сырой текст задания → плоская карточка (LangChain structured output).""" +from __future__ import annotations + +import os +import sys + +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, SecretStr + +load_dotenv() + +# Модель: LM Studio локально или любой OpenAI-совместимый API через .env +llm = ChatOpenAI( + model=os.getenv("OPENAI_MODEL", "local-model"), + base_url=os.getenv("OPENAI_BASE_URL", "http://localhost:1234/v1"), + api_key=SecretStr(os.getenv("OPENAI_API_KEY", "fake")), + temperature=0.0, +) + + +class AssignmentCard(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="Список критериев или упоминаний про оценку" + ) + + +parser = PydanticOutputParser(pydantic_object=AssignmentCard) + +prompt = PromptTemplate( + template=( + "Ты модуль разбора учебных заданий. Из одной формулировки преподавателя " + "извлеки поля карточки. Не задавай уточняющих вопросов — только структурированный ответ.\n\n" + "Формулировка задания:\n{assignment_text}\n\n" + "{format_instructions}" + ), + input_variables=["assignment_text"], + partial_variables={"format_instructions": parser.get_format_instructions()}, +) + +chain = prompt | llm | parser + +DEFAULT_INPUT = ( + "Сдайте к пятнице мини-отчёт по LangChain: 2 страницы, упор на агентов. " + "Оценка: за полноту и за пример кода." +) + + +def human_summary(card: AssignmentCard) -> str: + """Краткая человекочитаемая сводка.""" + hints = ", ".join(card.grading_hints) if card.grading_hints else "—" + return ( + f"Заголовок: {card.title}\n" + f"Тема: {card.subject}\n" + f"Срок: {card.deadline_hint}\n" + f"Что сдать: {card.deliverable_type}\n" + f"Оценка (подсказки): {hints}" + ) + + +def main() -> None: + assignment_text = " ".join(sys.argv[1:]).strip() or DEFAULT_INPUT + card: AssignmentCard = chain.invoke({"assignment_text": assignment_text}) + + print("=== Валидированный объект (model_dump) ===") + dumped = card.model_dump() + for key, value in dumped.items(): + print(f"{key}: {value!r}") + + print("\n=== Краткая сводка ===") + print(human_summary(card)) + + +if __name__ == "__main__": + main()