сырой текст задания → плоская карточка: solution.py
This commit is contained in:
+146
@@ -0,0 +1,146 @@
|
|||||||
|
#!/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()
|
||||||
Reference in New Issue
Block a user