diff --git a/main.py b/main.py index a3c2bec..c4b5dc5 100644 --- a/main.py +++ b/main.py @@ -3,40 +3,47 @@ import asyncio from typing import List from pydantic import BaseModel, Field -from langchain_openai import ChatOpenAI + from langchain_core.prompts import PromptTemplate from langchain_core.output_parsers import PydanticOutputParser -from langchain_core.messages import HumanMessage -from langchain.tools import tool - +from langchain_openai import ChatOpenAI from deepagents import create_deep_agent from deepagents.backends import FilesystemBackend, LocalShellBackend, CompositeBackend +from langchain.tools import tool +from langchain_core.messages import HumanMessage -# -------------------- Pydantic model -------------------- -class AssignmentCard(BaseModel): +# ------------------------------ +# Pydantic model for the task card +# ------------------------------ +class TaskCard(BaseModel): title: str = Field(description="Краткое название задания") subject: str = Field(description="Тема или предмет задания") - deadline_hint: str = Field(description="Подсказка о сроке сдачи (например, к пятнице, 12.09)") + deadline_hint: str = Field(description="Подсказка о сроке выполнения, например 'к пятнице' или 'до 12.09'") deliverable_type: str = Field(description="Что нужно сдать: отчёт, код, презентация и т.п.") - grading_hints: List[str] = Field(description="Список упомянутых критериев оценки") + grading_hints: List[str] = Field(description="Список критериев оценки, упомянутых в тексте") -# -------------------- Structured output parser -------------------- -parser = PydanticOutputParser(pydantic_object=AssignmentCard) +# ------------------------------ +# Structured output parser +# ------------------------------ +parser = PydanticOutputParser(pydantic_object=TaskCard) -# -------------------- Prompt template -------------------- -prompt_template = PromptTemplate( +# ------------------------------ +# Prompt template with format instructions +# ------------------------------ +prompt = PromptTemplate( template=( - "Ты получаешь неформальное описание учебного задания и должен вернуть " - "структурированные данные в формате JSON, соответствующем схеме ниже.\n" - "Верни только JSON, без пояснений.\n" - "Описание задания:\n{task_description}\n\n" + "Ты извлекаешь из неформального описания учебного задания структурированные данные.\n" + "Верни их в EXACTLY the JSON format described by the format instructions.\n" + "Описание задания: {task_text}\n" "{format_instructions}" ), - input_variables=["task_description"], + input_variables=["task_text"], partial_variables={"format_instructions": parser.get_format_instructions()}, ) -# -------------------- LLM (OpenRouter) -------------------- +# ------------------------------ +# LLM configuration (OpenRouter) +# ------------------------------ llm = ChatOpenAI( model="openai/gpt-oss-20b:free", base_url="https://openrouter.ai/api/v1", @@ -44,63 +51,63 @@ llm = ChatOpenAI( temperature=0.0, ) -# -------------------- Tool that runs the chain -------------------- -@tool -def parse_assignment(task_description: str) -> str: - """ - Parse a free-form assignment description into a structured AssignmentCard. - Returns a JSON string that can be parsed by the Pydantic model. - """ - chain = prompt_template | llm | parser - result = chain.invoke({"task_description": task_description}) - # result is an AssignmentCard instance - return result.model_dump_json(indent=2) +# ------------------------------ +# Chain: prompt -> LLM -> parser +# ------------------------------ +chain = prompt | llm | parser -# -------------------- DeepAgent setup -------------------- -backend = CompositeBackend( - [ - LocalShellBackend(workspace_dir="./workspace"), - FilesystemBackend(), - ] -) +# ------------------------------ +# DeepAgents setup (required by the course) +# ------------------------------ +backend = CompositeBackend([ + LocalShellBackend(workspace_dir="./workspace"), + FilesystemBackend(), +]) + +@tool +def echo_tool(text: str) -> str: + """Simple echo tool, returns the received text.""" + return text agent = create_deep_agent( model=llm, - tools=[parse_assignment], + tools=[echo_tool], backend=backend, - system_prompt="You are a helpful assistant that extracts assignment data.", + system_prompt="You are a helpful assistant that can also run tools.", ) -# -------------------- Main execution -------------------- +# ------------------------------ +# Main execution +# ------------------------------ async def main(): - # Пример входного текста - user_input = ( + # Example input (can be replaced with any other string) + task_description = ( "Сдайте к пятнице мини-отчёт по LangChain: 2 страницы, упор на агентов. " "Оценка: за полноту и за пример кода." ) + + # Use the structured chain to parse the description + parsed_result: TaskCard = await chain.ainvoke({"task_text": task_description}) + + # Print the validated model dump + print("=== Validated TaskCard ===") + print(parsed_result.model_dump()) + + # Print a short human-readable summary + print("\n=== Summary ===") + print(f"Title: {parsed_result.title}") + print(f"Subject: {parsed_result.subject}") + print(f"Deadline: {parsed_result.deadline_hint}") + print(f"Deliverable: {parsed_result.deliverable_type}") + print(f"Grading hints: {', '.join(parsed_result.grading_hints)}") + + # Demonstrate that the deep agent is functional (optional) result = await agent.ainvoke( - {"messages": [HumanMessage(content=user_input)]}, - {"configurable": {"thread_id": "session-1"}}, + {"messages": [HumanMessage(content="Echo this message")]}, + {"configurable": {"thread_id": "demo-1"}}, ) - # Последнее сообщение агента содержит JSON строку - json_output = result["messages"][-1].content - print("=== Structured JSON ===") - print(json_output) - - # Для наглядности выводим человекочитаемую сводку - try: - import json - - data = json.loads(json_output) - print("\n=== Human-readable summary ===") - print(f"Title: {data.get('title')}") - print(f"Subject: {data.get('subject')}") - print(f"Deadline hint: {data.get('deadline_hint')}") - print(f"Deliverable type: {data.get('deliverable_type')}") - print(f"Grading hints: {', '.join(data.get('grading_hints', []))}") - except Exception as e: - print("Failed to parse JSON:", e) - + print("\n=== Agent echo result ===") + print(result["messages"][-1].content) if __name__ == "__main__": asyncio.run(main()) \ No newline at end of file