Камені будавання LangGraph: стан, вузлы, рэбра і вызыванне
Словнік LangGraph пры падзеце стварэння: запісаны стан, маленькія вузлы, умовныя рэшткі, вызовы інструментаў і паведамленні як стан, паказаныя з аднім простым агентам.
Калі модэлі навучыліся выкарыстоўваць інструменты і працаваць па колькашагоэтапнаму логічнаму расчытку, стала складнаю ў тэставанні і пачатку выканання спецыяльныя ціклы. LangGraph чытра адзначае прабег кантролю: стан, вузлы, рэшткі і вызывы — такім чынам агенты стаюць графамі, якія можна адзірнуць, а не хаотычным наборам заповедзей.
Найпростышы граф
Мінімальны прабег агента выглядае так: пачатак → вузлы для расчытку/дзеяння → канец, пры чым стан перадаецца між этапамі.
Створыце першага простога агента
Receive Order
Prepare Food
Deliver Food
Receive Order
↓
Prepare Food
↓
Deliver Food
Pizza
Address: 123 Main Street
Customer: John
Receive Order
↓
Prepare Food
↓
Deliver Food
from typing_extensions import TypedDict
class State(TypedDict):
graph_state: strNodes
def node_1(state):
print("---Node 1---")
return {"graph_state": state['graph_state'] + "I am"}
def node_2(state):
print("---Node 2---")
return {"graph_state": state['graph_state'] + "happy!"}
def node_3(state):
print("---Node 3---")
return {"graph_state": state['graph_state'] + "sad!"}
import random
from typing import Literal
def decide_mood(state) -> Literal["node_2", "node_3"]:
# Often, we will use state to decide on the next node to visit
user_input = state['graph_state']
# Here, let's just do a 50 / 50 split between nodes 2, 3
if random.random() < 0.5
# 50% of the time, we return Node 2
return "node_2"
# 50% of the time, we return Node 3
return "node_3"
from IPython.display import Image, display
from langgraph.graph import StateGraph, START, END
#Build Graph
builder = StateGraph(state)
builder.add_node("node_1", node_1)
builder.add_node("node_2", node_2)
builder.add_node("node_3", node_3)
#Logic
builder.add_edge(START, "node_1")
builder.add_conditional_edges("node_1", decide_mood)
builder.add_edge("node_2", END)
builder.add_edge("node_3", END)
#Add
graph = builder.compile()
#View
display(Image(graph.get_graph().draw_mermaid_png()))
graph.invoke({"graph_state" : "Hi, this is Lance."})
###OUTPUT
---Node 1---
---Node 3---
{'graph_state': 'Hi, this is Lance. I am sad!'}
{'graph_state': 'Hi, this is Lance. I am sad!'}
%%capture --no-stderr
%pip install --quiet -U langchain_openai langchain_core langgraph
from pprint import pprint
from langchain_core.messages import AIMessage, HumanMessage
messages = [AIMessage(content=f"So you said you were researching ocean mammals?", name="Model")]
messages.append(HumanMessage(content=f"Yes, that's right.",name="Lance"))
messages.append(AIMessage(content=f"Great, what would you like to learn about.", name="Model"))
messages.append(HumanMessage(content=f"I want to learn about the best place to see Orcas in the US.", name="Lance"))
for m in messages:
m.pretty_print()
#OUTPUT
================================== Ai Message ==================================
Name: Model
So you said you were researching ocean mammals?
================================ Human Message =================================
Name: Lance
Yes, that's right.
================================== Ai Message ==================================
Name: Model
Great, what would you like to learn about.
================================ Human Message =================================
Name: Lance
I want to learn about the best place to see Orcas in the US.
from langchain_openai import ChatOpenAI
llm = ChatOpenAI(model="gpt-4o")
result = llm.invoke(messages)
type(result)
result
#OUTPUT
langchain_core.messages.ai.AIMessage
result
AIMessage(content='One of the best places to see orcas in the United States is the Pacific Northwest, particularly around the San Juan Islands in Washington State. Here are some details:\n\n1. **San Juan Islands, Washington**: These islands are a renowned spot for whale watching, with orcas frequently spotted between late spring and early fall. The waters around the San Juan Islands are home to both resident and transient orca pods, making it an excellent location for sightings.\n\n2. **Puget Sound, Washington**: This area, including places like Seattle and the surrounding waters, offers additional opportunities to see orcas, particularly the Southern Resident killer whale population.\n\n3. **Olympic National Park, Washington**: The coastal areas of the park provide a stunning backdrop for spotting orcas, especially during their migration periods.\n\nWhen planning a trip for whale watching, consider peak seasons for orca activity and book tours with reputable operators who adhere to responsible wildlife viewing practices. Additionally, land-based spots like Lime Kiln Point State Park, also known as “Whale Watch Park,” on San Juan Island, offer great opportunities for orca watching from shore.', additional_kwargs={'refusal': None}, response_metadata={'token_usage': {'completion_tokens': 228, 'prompt_tokens': 67, 'total_tokens': 295, 'completion_tokens_details': {'accepted_prediction_tokens': 0, 'audio_tokens': 0, 'reasoning_tokens': 0, 'rejected_prediction_tokens': 0}, 'prompt_tokens_details': {'audio_tokens': 0, 'cached_tokens': 0}}, 'model_name': 'gpt-4o-2024-08-06', 'system_fingerprint': 'fp_50cad350e4', 'finish_reason': 'stop', 'logprobs': None}, id='run-57ed2891-c426-4452-b44b-15d0a5c3f225-0', usage_metadata={'input_tokens': 67, 'output_tokens': 228, 'total_tokens': 295, 'input_token_details': {'audio': 0, 'cache_read': 0}, 'output_token_details': {'audio': 0, 'reasoning': 0}})
def multiply(a:int, b:int)-> int:
return a*b
llm_with_tools = llm.bind_tools([multiply])
tool_call = llm_with_tools.invoke([HumanMessage(content=f"What is 2 multiplied by 3
", name="Lance")])
tool_call.tool_calls
#OUTPUT
[{'name': 'multiply',
'args': {'a': 2, 'b': 3},
'id': 'call_lBBBNo5oYpHGRqwxNaNRbsiT',
'type': 'tool_call'}]
from typing_extentions import TypedDict
from langchain_core.messges import AnyMessage
class MessagesState(TypedDict):
messages : list[AnyMessage]
from typing import Annotated
from langgraph.graph.message import add_messages
class MessagesState(TypedDict):
messages: Annotated[list[AnyMessage], add_messages]
from langgraph.graph import MessagesState
class MessagesState(MessagesState):
# Add any keys needed beyond messages, which is pre-built
pass
# Initial state
initial_messages = [AIMessage(content="Hello! How can I assist you?", name="Model"),
HumanMessage(content="I'm looking for information on marine biology.", name="Lance")
]
# New message to add
new_message = AIMessage(content="Sure, I can help with that. What specifically are you interested in?", name="Model")
# Test
add_messages(initial_messages , new_message)
from IPython.display import Image, display
from langgraph.graph import StateGraph, START, END
# Node
def tool_calling_llm(state: MessagesState):
return {"messages": [llm_with_tools.invoke(state["messages"])]}
# Build graph
builder = StateGraph(MessagesState)
builder.add_node("tool_calling_llm", tool_calling_llm)
builder.add_edge(START, "tool_calling_llm")
builder.add_edge("tool_calling_llm", END)
graph = builder.compile()
# View
display(Image(graph.get_graph().draw_mermaid_png()))
messages = graph.invoke({"messages": HumanMessage(content="Hello!")})
for m in messages['messages']:
m.pretty_print()
#OUTPUT
================================ Human Message =================================
Hello!
================================== Ai Message ==================================
Hi there! How can I assist you today?
messages = graph.invoke({"messages": HumanMessage(content="Multiply 2 and 3")})
for m in messages['messages']:
m.pretty_print()
OUTPUT
================================ Human Message =================================
Multiply 2 and 3!
================================== Ai Message ==================================
Tool Calls:
multiply (call_Er4gChFoSGzU7lsuaGzfSGTQ)
Call ID: call_Er4gChFoSGzU7lsuaGzfSGTQ
Args:
a: 2
b: 3
Стан
Запісаны стан (частаючы TypedDict або reducers) храняць паведамленні, флагі і проміжныя рэзультаты. Выбірайце reducers адказова — дадзіце append для спісоў паведамленняў, replace для скалярных значэнняў.
Вузлы
Вузлы — гэта функціі: яны чытаюць стан, выконваюць задачу (LLM, інструмент, чысты Python) і вяртаюць часткова апдэйтаваны стан. Робіце іх маленькімі і прыемнымі для тэставання.
Рэшты
Статычныя рэглі ўсталяваюць парадак; умовныя рэглі ствараюць галузі залежна ад стану. Графы стаюць корыснымі, калі з’яўляюцца галузі і цыклы.
Стварэнне графа
Зарэўнавайце вузлы, пад’якніце рэглі, скомпілюйце з неабяжным чэкпойнтам для адзінства дадзеных.
Выклік графа
Выклікайце граф з пачатковым станам і настройкамі (ID адэнты, калі ствараеце чэкпойнт). Трансляюцьце запускі падзей для павышэння корыстнасці.
Ланцюг проты графа
Лінейныя ланцюгі все ўсё падходзяць для працы за адну пераходжэння. Графы дазволяюць паузаваць/працяваць далей, ствараць галузі і выкорыстоўваць спакойны стан.
Паведамленні і мадэлі чату
Спісы паведамленняў ёсць стандартным станом дыялогу. Прыўяжыце мадэлі чату ўнутрь вузлаў, а не хавайце I/O ў логіке рэглаў.
Інструменты
Адкройце інструменты для вузлаў, якія выклікаюць мадэлі; апрацаваўце выклікі інструментаў; запісваце спостэрэнні назад у стан.
Выкорыстоўванне паведамленняў як стану
Калі паведамленні являюцца станом, рэдактары должны безпечна дадаць іх да стану, а скрашчэнне/сумарызація становіцца явным вузлам — а не прыгожданнем.
Заключэнне
Частка 0 — гэта слоўнік: стан, вузлы, рэшткі, кампайляванне, вызыванне. У наступных частках дадаюцца трывалыя ніткі, перэрывы HITL і шаблоны з удзелам калькі агентаў на той жа базе.
Называйце вузлы па бізнес-кроках (classify, retrieve, draft) адносна node1. Чытаемыя графы ёсць графы, якія можна выкарыстоўваць.
Дадзіце тэст на елементы, які вызывае скомпіляваны граф з фіксованным станам і пераконваецца, якія вузлы запрацавалі. Сама функцыя compile() не дазволяе падтвердзіць полнасцю бізнес-процэса.
Называйце вузлы па бізнес-кроках (classify, retrieve, draft) адносна node1. Чытаемыя графы ёсць графы, якія можна выкарыстоўваць.
Дадзіце тэст на елементах, які запускаюць скомпіляваны граф у стане фікстуры і пераканаюцца, якія вузлы працавалі. Сама функцыя compile() не падтверджвае полнасць бізнес-процэса.
Называйце вузлы па бізнес-кроках (classify, retrieve, draft) адзін у адзін, а не node1. Чытальныя графы — гэта графы, якія можна выкарыстоўваць у праце.
Дадзіце тэст на елементах, які запускаюць скомпіляваны граф у стане фікстуры і пераканаюцца, якія вузлы працавалі. Сама функцыя compile() не падтверджвае полнасць бізнес-процэса.
Называйце вузлы па бізнес-кроках (classify, retrieve, draft) адзін у адзін, а не node1. Чытальныя графы — гэта графы, якія можна выкарыстоўваць у праце.
Дадзіце тэст на елементах, які запускаюць скомпіляваны граф у стане фікстуры і пераканаюцца, якія вузлы працавалі. Сама функцыя compile() не падтверджвае полнасць бізнес-процэса.
Называйце вузлы па шагах бізнес-процэсу (classify, retrieve, draft) а не node1. Чытальныя графы — гэтае графы, якія можна выкарыстоўваць у практыцы.
Дадзіце тэст на елементарную перацэю, які запускае скомпіляваны граф у стане фікстуры і пераконваецца, якія вузлы былі запрацоўваны. Сама функцыя compile() не дазволяе падтвердзіць полнасцю бізнес-процэсу.
Называйце вузлы па шагах бізнес-процэсу (classify, retrieve, draft) а не node1. Чытальныя графы — гэтае графы, якія можна выкарыстоўваць у практыцы.
Дадзіце тэст на елементарную перацэю, які запускае скомпіляваны граф у стане фікстуры і пераконваецца, якія вузлы былі запрацоўваны. Сама функцыя compile() не дазволяе падтвердзіць полнасцю бізнес-процэсу.
Называйце вузлы па шагах бізнес-процэсу (classify, retrieve, draft) а не node1. Чытальныя графы — гэтае графы, якія можна выкарыстоўваць у практыцы.
Дадзіце тэст на елементах, які запускаюць скомпіляваны граф у стане фікстуры і пераканаюцца, якія вузлы працавалі. Сама функцыя compile() не падтверджвае полнасць бізнес-процэса.
Называйце вузлы па бізнес-кроках (classify, retrieve, draft) адзін у адзін, а не node1. Чытабельныя графы — гэта графы, якія можна выкарыстоўваць у праце.
Дадзіце тэст на елементах, які запускаюць скомпіляваны граф у стане фікстуры і пераканаюцца, якія вузлы працавалі. Сама функцыя compile() не падтверджвае полнасць бізнес-процэса.
Называйце вузлы па бізнес-кроках (classify, retrieve, draft) адзін у адзін, а не node1. Чытабельныя графы — гэта графы, якія можна выкарыстоўваць у праце.
Дадзіце тэст на елементах, які запускаюць скомпіляваны граф у стане фікстуры і пераканаюцца, якія вузлы працавалі. Сама функцыя compile() не падтверджвае полнасць бізнес-процэса.
Называйце вузлы па шагах бізнес-процэсу (classify, retrieve, draft) а не node1. Чытальныя графы — гэтае графы, якія можна выкарыстоўваць у практыцы.
Дадзіце тэст на елементарную перацэю, які запускае скомпіляваны граф у стане фікстуры і пераконваецца, якія вузлы былі запрацоўваны. Толькі функцыя compile() не дазволяе падтвердзіць полнасцю бізнес-процэсу.
Называйце вузлы па шагах бізнес-процэсу (classify, retrieve, draft) а не node1. Чытальныя графы — гэтае графы, якія можна выкарыстоўваць у практыцы.
Дадзіце тэст на елементарную перацэю, які запускае скомпіляваны граф у стане фікстуры і пераконваецца, якія вузлы былі запрацоўваны. Толькі функцыя compile() не дазволяе падтвердзіць полнасцю бізнес-процэсу.
Называйце вузлы па шагах бізнес-процэсу (classify, retrieve, draft) а не node1. Чытальныя графы — гэтае графы, якія можна выкарыстоўваць у практыцы.
Дадзіце тэст на елементах, які запускаюць скомпіляваны граф у стане фікстуры і пераканаюцца, якія вузлы працавалі. Сама функцыя compile() не падтверджвае полнасць бізнес-процэса.
Называйце вузлы па бізнес-кроках (classify, retrieve, draft) адзін у адзін, а не node1. Чытабельныя графы — гэта графы, якія можна выкарыстоўваць у праце.
Дадзіце тэст на елементах, які запускаюць скомпіляваны граф у стане фікстуры і пераканаюцца, якія вузлы працавалі. Сама функцыя compile() не падтверджвае полнасць бізнес-процэса.
Называйце вузлы па бізнес-кроках (classify, retrieve, draft) адзін у адзін, а не node1. Чытабельныя графы — гэта графы, якія можна выкарыстоўваць у праце.
Дадзіце тэст на елементах, які запускаюць скомпіляваны граф у стане фікстуры і пераканаюцца, якія вузлы працавалі. Сама функцыя compile() не падтверджвае полнасць бізнес-процэса.
Называйце вузлы па шагах бізнес-процэсу (classify, retrieve, draft) а не node1. Чытальныя графы — гэтае графы, якія можна выкарыстоўваць у практыцы.
Дадзіце тэст на елементарную перацэю, які запускае скомпіляваны граф у стане фікстуры і пераконваецца, якія вузлы былі запрацоўваны. Сама функцыя compile() не дазволяе падтвердзіць полнасць бізнес-процэсу.
Называйце вузлы па шагах бізнес-процэсу (classify, retrieve, draft) а не node1. Чытальныя графы — гэтае графы, якія можна выкарыстоўваць у практыцы.
Дадзіце тэст на елементарную перацэю, які запускае скомпіляваны граф у стане фікстуры і пераконваецца, якія вузлы былі запрацоўваны. Сама функцыя compile() не дазволяе падтвердзіць полнасць бізнес-процэсу.
Называйце вузлы па шагах бізнес-процэсу (classify, retrieve, draft) а не node1. Чытальныя графы — гэтае графы, якія можна выкарыстоўваць у практыцы.
Дадзіце тэст на елементах, які запускаюць скомпіляваны граф у стане фікстуры і пераканаюцца, якія вузлы працавалі. Сама функцыя compile() не падтверджвае полнасць бізнес-процэса.
Называйце вузлы па бізнес-кроках (classify, retrieve, draft) адзін у адзін, а не node1. Чытабельныя графы — гэта графы, якія можна выкарыстоўваць у праце.
Дадзіце тэст на елементах, які запускаюць скомпіляваны граф у стане фікстуры і пераканаюцца, якія вузлы працавалі. Сама функцыя compile() не падтверджвае полнасць бізнес-процэса.
Называйце вузлы па бізнес-кроках (classify, retrieve, draft) адзін у адзін, а не node1. Чытабельныя графы — гэта графы, якія можна выкарыстоўваць у праце.
Дадзіце тэст на елементах, які запускаюць скомпіляваны граф у стане фікстуры і пераканаюцца, якія вузлы працавалі. Сама функцыя compile() не падтверджвае полнасць бізнес-процэса.
Называйце вузлы па названнях бізнес-крокаў (classify, retrieve, draft) а не node1. Чытальныя графы — гэта графы, якія можна выкарыстоўваць у працы.
Дадзіце тэст на аддыт, які запускае скомпіляваны граф у стане фікстуры і пераканаецца, якія вузлы былі запрацоўваны. Сама функцыя compile() не падтверджвае повнасць бізнес-процэса.
Для тэстаў маршрутацыі лепей выбіраць дэтэрміністычныя вузлы: увядзіце фальшывы LLM, які вяртае фіксаваныя выклікі інструментаў, каб CI не патрабавала доступу да сеткі.
З самага пачатку задокументавайце правіла автарызаціі thread_id: наяўнасць чэкпоінта не значыць права на яго чытанне.
Інструменты, якія вызываюць побачныя эфекты, трэба залічваць за межамі рэбер затварэння, нават у протатыпах часткі 0 — звычкі формуюцца быстра.
Для тэстаў маршрутацыі лепей выбіраць дэтэрміністычныя вузлы: увядзіце фальшывы LLM, які вяртае фіксаваныя выклікі інструментаў, каб CI не патрабавала доступу да сеткі.
Заўсёды дакументавайце правілы автарызаціі thread_id з самага пачатку: адналёгчэнне пашукі контрольнай точкі не значыць права на яе чытанне.
Нават у протатаях часткі 0 трэба залічваць інструменты, якія вызываюць побачныя эфекты, за межамі процэса затверджэння — прычыны формуюцца быстра.
Для тэстав маршрутызаціі лепей выбіраць дэтэрміністычныя вузлы: ін’ектавайце фальшывы LLM, які вяртае фіксаваныя вызовы інструментаў, каб CI не патрабавала доступу да сеткі.
Заўсёды документавайце правілы автарызаціі thread_id з самага пачатку: адналёгчэнне пашукі контрольнай точкі не значыць права на яе чытанне.
Нават у протатаях часткі 0 трэба залічваць інструменты, якія вызываюць побачныя эфекты, за межамі процэса затверджэння — прычыны формуюцца быстра.
Для тэстав маршрутызаціі лепей выбіраць дэтэрміністычныя вузлы: ін’ектавайце фальшывы LLM, які вяртае фіксаваныя вызовы інструментаў, каб CI не патрабавала доступу да сеткі.