Files
task-69dd4221f309a98be0006b2e/main.py
T

114 lines
4.0 KiB
Python

"""Сырой текст задания → плоская карточка (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
load_dotenv()
BROJS_INFERENCE_URL = "https://platform.brojs.ru/jrnl-bh/api/inference/v1"
DEFAULT_MODEL = "openai/gpt-oss-20b:free"
DEFAULT_INPUT = (
"Сдайте к пятнице мини-отчёт по LangChain: 2 страницы, упор на агентов. "
"Оценка: за полноту и за пример кода."
)
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="Список критериев или упоминаний про оценку"
)
def _api_key() -> str:
return (
os.getenv("OPENAI_API_KEY")
or os.getenv("JOURNAL_MCP_PAT")
or os.getenv("JOURNAL_TOKEN")
or ""
)
def _base_url() -> str:
if os.getenv("OPENAI_BASE_URL"):
return os.environ["OPENAI_BASE_URL"]
if os.getenv("OPENAI_API_KEY"):
return os.getenv("OPENROUTER_BASE_URL", "https://openrouter.ai/api/v1")
return BROJS_INFERENCE_URL
def _model() -> str:
return os.getenv("OPENAI_MODEL") or os.getenv("OPENROUTER_MODEL") or DEFAULT_MODEL
def build_chain():
"""Цепочка prompt | llm | parser — точка входа для автопроверки."""
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()},
)
llm = ChatOpenAI(
model=_model(),
base_url=_base_url(),
api_key=_api_key(),
temperature=0.0,
)
return prompt | llm | parser
def parse_assignment_text(assignment_text: str) -> AssignmentCard:
return build_chain().invoke({"assignment_text": assignment_text})
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 = parse_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()