Tworzenie Systemu opartego na Agentach AI z LangGraph: Integracja Człowieka w Proces Decyzyjny
Tworzenie Systemu Agentów AI z Możliwością Interakcji z Człowiekiem
W poprzednim poradniku omówiliśmy budowę agenta AI, który potrafił odpowiadać na zapytania, przeszukując internet oraz przechowywać swój stan między kolejnymi interakcjami. Jednak w wielu przypadkach konieczne jest wprowadzenie człowieka do procesu decyzyjnego, aby monitorować i zatwierdzać działania agenta. Dzięki LangGraph można to łatwo osiągnąć. W tym artykule omówimy, jak dodać człowieka do pętli decyzyjnej i umożliwić mu kontrolowanie działań agenta.
—
Konfiguracja Środowiska
Zaczniemy od przygotowania środowiska, definiując zmienne środowiskowe, importując niezbędne biblioteki oraz konfigurując mechanizm zapisu stanu agenta.
Instalacja wymaganych pakietów
Najpierw należy zainstalować niezbędne biblioteki:
bash
pip install langgraph==0.2.53 langgraph-checkpoint==2.0.6 langgraph-sdk==0.1.36 langchain-groq langchain-community langgraph-checkpoint-sqlite==2.0.1
Konfiguracja kluczy API
Aby działać z niektórymi usługami, potrzebujemy kluczy API:
python
import os
os.environ['TAVILY_API_KEY'] = ""
os.environ['GROQ_API_KEY'] = ""
Ustawienie mechanizmu zapisu stanu
Zapisywanie stanu agenta w bazie SQLite pozwala na trwałość jego działań i przerywanie oraz wznawianie procesów:
python
from langgraph.checkpoint.sqlite import SqliteSaver
import sqlite3
sqlite_conn = sqlite3.connect("checkpoints.sqlite", check_same_thread=False)
memory = SqliteSaver(sqlite_conn)
Definiowanie Agenta
Agent AI będzie operował na podstawie modelu językowego i wykorzystywał narzędzia do wyszukiwania informacji.
python
from langgraph.graph import StateGraph, END
from typing import TypedDict, Annotated
import operator
from langchain_core.messages import AnyMessage, SystemMessage, HumanMessage, ToolMessage, AIMessage
from langchain_groq import ChatGroq
from langchain_community.tools.tavily_search import TavilySearchResults
Następnie definiujemy klasę agenta:
python
class Agent:
def __init__(self, model, tools, checkpointer, system=""):
self.system = system
graph = StateGraph(AgentState)
graph.add_node("llm", self.call_openai)
graph.add_node("action", self.take_action)
graph.add_conditional_edges("llm", self.exists_action, {True: "action", False: END})
graph.add_edge("action", "llm")
graph.set_entry_point("llm")
self.graph = graph.compile(checkpointer=checkpointer)
self.tools = {t.name: t for t in tools}
self.model = model.bind_tools(tools)
def call_openai(self, state: AgentState):
messages = state['messages']
if self.system:
messages = [SystemMessage(content=self.system)] + messages
message = self.model.invoke(messages)
return {'messages': [message]}
def exists_action(self, state: AgentState):
result = state['messages'][-1]
return len(result.tool_calls) > 0
def take_action(self, state: AgentState):
tool_calls = state['messages'][-1].tool_calls
results = []
for t in tool_calls:
result = self.tools[t['name']].invoke(t['args'])
results.append(ToolMessage(tool_call_id=t['id'], name=t['name'], content=str(result)))
return {'messages': results}
—
Konfiguracja Stanu Agenta
Agent przechowuje historię rozmów i wyników, ale w tym przypadku chcemy umożliwić użytkownikowi edytowanie wiadomości zamiast jedynie ich dodawania.
python
from uuid import uuid4
def reduce_messages(left: list[AnyMessage], right: list[AnyMessage]) -> list[AnyMessage]:
for message in right:
if not message.id:
message.id = str(uuid4())
merged = left.copy()
for message in right:
for i, existing in enumerate(merged):
if existing.id == message.id:
merged[i] = message
break
else:
merged.append(message)
return merged
class AgentState(TypedDict):
messages: Annotated[list[AnyMessage], reduce_messages]
—
Dodanie Człowieka do Procesu Decyzyjnego
Aby umożliwić człowiekowi zatwierdzanie decyzji agenta, dodajemy mechanizm przerywania przed wykonaniem konkretnej akcji.
python
class Agent:
def __init__(self, model, tools, checkpointer, system=""):
self.graph = graph.compile(checkpointer=checkpointer, interrupt_before=["action"])
—
Uruchamianie Agenta
Teraz możemy uruchomić agenta, wykorzystując model językowy i narzędzie do wyszukiwania informacji.
python
prompt = """Jesteś inteligentnym asystentem badawczym. Korzystaj z wyszukiwarki internetowej, aby znaleźć informacje.
Możesz wykonywać wiele zapytań w razie potrzeby. Wyszukuj informacje tylko wtedy, gdy masz pewność, czego szukasz."""
model = ChatGroq(model="Llama-3.3-70b-Specdec")
tool = TavilySearchResults(max_results=2)
abot = Agent(model, [tool], system=prompt, checkpointer=memory)
messages = [HumanMessage(content="Jaka jest pogoda w Warszawie?")]
thread = {"configurable": {"thread_id": "1"}}
for event in abot.graph.stream({"messages": messages}, thread):
for v in event.values():
print(v)
Podczas działania agenta użytkownik może przerywać proces i zatwierdzać kolejne kroki ręcznie.
—
Interaktywne Zatwierdzanie przez Człowieka
Wprowadzamy pętlę, która pozwala użytkownikowi ręcznie akceptować lub odrzucać decyzje agenta:
python
messages = [HumanMessage("Jaka jest pogoda w Krakowie?")]
thread = {"configurable": {"thread_id": "2"}}
for event in abot.graph.stream({"messages": messages}, thread):
for v in event.values():
print(v)
while abot.graph.get_state(thread).next:
print("n", abot.graph.get_state(thread), "n")
_input = input("Kontynuować? (t/n): ")
if _input.lower() != "t":
print("Przerwano")
break
for event in abot.graph.stream(None, thread):
for v in event.values():
print(v)
—
Podsumowanie
Dzięki LangGraph możemy nie tylko stworzyć inteligentnego agenta AI, ale także umożliwić człowiekowi nadzorowanie jego działań. Taki system może być wykorzystywany w różnych zastosowaniach, od obsługi klienta po analizy danych i badania naukowe. Spróbuj eksperymentować z różnymi punktami przerwania i sprawdź, jak zmienia się zachowanie agenta w zależności od ingerencji użytkownika.