Files

146 lines
5.6 KiB
Python

#!/usr/bin/env python3
# -*- coding: utf-8 -*-
"""
solution.py
Простой скрипт, который принимает одну строку с описанием задания,
передаёт её модели OpenAI через LangChain и получает валидированный объект
TaskCard (Pydantic). Затем выводит сериализованную форму и краткую сводку.
"""
import os
import sys
from typing import List
from pydantic import BaseModel, Field
from langchain_core.prompts import PromptTemplate
from langchain_openai import ChatOpenAI
from langchain_core.output_parsers import PydanticOutputParser
# --------------------------------------------------------------------------- #
# 1. Модель данных карточки задания
# --------------------------------------------------------------------------- #
class TaskCard(BaseModel):
"""
Карточка задачи, которую генерирует модель.
"""
title: str = Field(
...,
description="Краткое название задачи (например, «Мини‑отчёт по LangChain»).",
)
subject: str | None = Field(
None,
description="Тема или предмет, к которому относится задача.",
)
deadline_hint: str | None = Field(
None,
description=(
"Неформальное указание срока сдачи (например, «к пятнице»)."
),
)
deliverable_type: str | None = Field(
None,
description="Тип сдаваемого материала (отчёт, код, презентация и т.п.).",
)
grading_hints: List[str] | None = Field(
None,
description=(
"Ключевые критерии оценки. Каждый элемент списка – отдельный пункт."
),
)
# --------------------------------------------------------------------------- #
# 2. Настройка LLM и парсера
# --------------------------------------------------------------------------- #
# Параметры модели можно менять через переменные окружения.
OPENAI_MODEL = os.getenv("OPENAI_MODEL", "gpt-4o-mini")
TEMPERATURE = float(os.getenv("OPENAI_TEMPERATURE", "0"))
llm = ChatOpenAI(
model=OPENAI_MODEL,
temperature=TEMPERATURE,
)
parser = PydanticOutputParser(pydantic_object=TaskCard)
# --------------------------------------------------------------------------- #
# 3. Шаблон промпта
# --------------------------------------------------------------------------- #
prompt_template = PromptTemplate(
template="""
Вы — помощник, который преобразует неформальное описание задания в структурированный формат.
Пожалуйста, выведите только JSON‑объект с полями, которые описаны ниже.
{format_instructions}
Входное описание:
"{input_text}"
""",
input_variables=["input_text"],
partial_variables={"format_instructions": parser.get_format_instructions()},
)
# --------------------------------------------------------------------------- #
# 4. Цепочка (prompt → LLM → парсер)
# --------------------------------------------------------------------------- #
chain = prompt_template | llm | parser
# --------------------------------------------------------------------------- #
# 5. Вспомогательная функция для вывода сводки
# --------------------------------------------------------------------------- #
def summarize(card: TaskCard) -> str:
"""
Формирует краткую человекочитаемую сводку из объекта TaskCard.
"""
parts = [f"**{card.title}**"]
if card.subject:
parts.append(f"(предмет: {card.subject})")
if card.deadline_hint:
parts.append(f"Срок сдачи: {card.deadline_hint}")
if card.deliverable_type:
parts.append(f"Тип сдаваемого материала: {card.deliverable_type}")
if card.grading_hints:
hints = ", ".join(card.grading_hints)
parts.append(f"Критерии оценки: {hints}")
return " | ".join(parts)
# --------------------------------------------------------------------------- #
# 6. Точка входа
# --------------------------------------------------------------------------- #
def main() -> None:
if len(sys.argv) > 1:
# Если передан аргумент, считаем его описанием задания
input_text = " ".join(sys.argv[1:])
else:
# Иначе читаем из stdin (первую строку)
print("Введите описание задания:")
input_text = sys.stdin.readline().strip()
if not input_text:
print("Ошибка: входной текст пуст.")
sys.exit(1)
try:
card: TaskCard = chain.invoke({"input_text": input_text})
except Exception as exc:
print(f"Не удалось получить структуру от модели: {exc}")
sys.exit(1)
# Вывод валидированного объекта
print("\n=== Валидированный объект ===")
print(card.model_dump(indent=2))
# Краткая сводка
print("\n=== Сводка задачи ===")
print(summarize(card))
if __name__ == "__main__":
main()