feat: solution for 'Практическое задание №3: Память и подтверждение действий'

This commit is contained in:
2026-06-24 15:17:01 +03:00
commit 4f5c7becbd
4 changed files with 202 additions and 0 deletions
+5
View File
@@ -0,0 +1,5 @@
node_modules/
.env
dist/
build/
*.log
+33
View File
@@ -0,0 +1,33 @@
# Практическое задание №3: Память и подтверждение действий
Главная
Мои задания
Практическое задание №3: Память и подтверждение действий
EN
Практическое задание №3: Память и подтверждение действий
Зачёт
Версия 1
Дедлайн сдачи: 31.08.2026
В работе
Редактирование ответа
Заполните ответ и отправьте работу на проверку преподавателю.
Тип ответа
Текст
Ссылка
Файлы
Текст ответа
Прикреплённые файлы
Загрузить файл
Отправить на проверку
Отменить
Задание
Цель
Доработать агента из предыдущих заданий: добавить память разговора и механизм подтверждения каждо
+4
View File
@@ -0,0 +1,4 @@
rich
langgraph
langchain
openai
+160
View File
@@ -0,0 +1,160 @@
#!/usr/bin/env python3
"""
A simple LangGraph agent with memory and user confirmation before tool calls.
"""
import os
import sys
from typing import Any, Dict, Optional
from langgraph import create_agent
from langgraph.checkpoint.memory import MemorySaver
from langgraph.tools import Tool
from langchain.chat_models import ChatOpenAI
from rich.console import Console
# --------------------------------------------------------------------------- #
# Configuration
# --------------------------------------------------------------------------- #
# Ensure the OpenAI API key is set
OPENAI_API_KEY = os.getenv("OPENAI_API_KEY")
if not OPENAI_API_KEY:
print("Error: OPENAI_API_KEY environment variable not set.")
sys.exit(1)
# Rich console for pretty output
console = Console()
# LLM model
llm = ChatOpenAI(temperature=0, openai_api_key=OPENAI_API_KEY)
# Memory saver for conversation persistence
memory = MemorySaver()
# --------------------------------------------------------------------------- #
# Tool definition
# --------------------------------------------------------------------------- #
def echo_tool(message: str) -> str:
"""
A simple echo tool that returns the message back to the user.
"""
return f"Echo: {message}"
# Wrap the function as a LangGraph Tool
echo = Tool.from_function(
fn=echo_tool,
name="echo",
description="Echoes back the provided message."
)
# --------------------------------------------------------------------------- #
# Agent creation
# --------------------------------------------------------------------------- #
system_prompt = """
You are a helpful assistant. When you need to use a tool, you will call it.
"""
agent = create_agent(
model=llm,
tools=[echo],
system_prompt=system_prompt,
checkpointer=memory, # Enable memory
interrupt_before=["tools"], # Pause before any tool call
)
# --------------------------------------------------------------------------- #
# Conversation loop
# --------------------------------------------------------------------------- #
def ask_and_run(user_input: Optional[str], config: Dict[str, Any]) -> None:
"""
Handles streaming from the agent, pauses before tool calls, and asks the user
for confirmation before executing the tool.
"""
# If user_input is provided, send it as a new message
if user_input is not None:
# The agent expects a dict with a "messages" key
input_payload = {"messages": [{"role": "user", "content": user_input}]}
else:
# None means resume from the paused state
input_payload = {}
# Stream the agent's response
for chunk_type, chunk_data in agent.stream(
input_payload,
config=config,
stream_mode=["messages", "updates"],
):
# Handle message chunks
if chunk_type == "messages":
# chunk_data is a list of messages; print the last one
if isinstance(chunk_data, list) and chunk_data:
last_msg = chunk_data[-1]
if last_msg.get("role") == "assistant":
console.print(f"[bold cyan]Assistant:[/bold cyan] {last_msg.get('content', '')}")
elif last_msg.get("role") == "tool":
console.print(f"[bold magenta]Tool Output:[/bold magenta] {last_msg.get('content', '')}")
else:
console.print(last_msg.get("content", ""))
else:
console.print(chunk_data)
# Detect interrupt before tool call
if "__interrupt__" in chunk_data and agent.get_state(config).next == ("tools",):
# Retrieve the pending tool call
state = agent.get_state(config)
try:
last_message = state.values["messages"][-1]
tool_call = last_message.tool_calls[0]
tool_name = tool_call["name"]
tool_args = tool_call["args"]
console.print(f"[yellow]Agent wants to call tool:[/yellow] {tool_name}({tool_args})")
except Exception as e:
console.print(f"[red]Error retrieving tool call: {e}[/red]")
break
# Ask user for confirmation
console.print("[bold]Allow tool execution? (Y/n):[/bold] ", end="")
answer = input().strip().lower()
if answer in ("", "y", "yes"):
console.print("[green]Executing tool...[/green]")
# Recursively resume the agent
ask_and_run(None, config)
else:
console.print("[red]Tool execution cancelled by user.[/red]")
break
# Handle updates (optional)
if chunk_type == "updates":
# For this simple example, we ignore updates
pass
def main() -> None:
"""
Main conversation loop.
"""
# Use a fixed thread ID for this session
config = {"configurable": {"thread_id": "thread-1"}}
console.print("[bold green]Welcome to the LangGraph Agent![/bold green]")
console.print("Type your messages below. Press Ctrl+C to exit.\n")
while True:
try:
user_input = input("[bold]You:[/bold] ")
if not user_input:
continue
ask_and_run(user_input, config)
except KeyboardInterrupt:
console.print("\n[bold red]Exiting...[/bold red]")
break
except Exception as e:
console.print(f"[red]Unexpected error: {e}[/red]")
if __name__ == "__main__":
main()