Галоўная / Артыкулы

Стандартная структура з сямі раздзелаў для кожнага агента LangGraph

Фіксаваная файлавая структура для агентаў LangGraph — ад імпартаў і стану да інструментаў, вузлоў, рэбраў, зборкі і выкліку — з шаблонамі і пашырэннем для шматагентных сістэм.

Выдавец: REACTAPP.TOP19 хв чытання4469 слоў

Крыніцы: The LangGraph Mental Model: A standardized architecture guide for every agent you’ll ever build

Кожны, хто якісь часы вівчаў LangGraph, знае гэта дзівнае адчуванне: ідэя здаецца логічной, а код так і не прыходзіць у галаву. Граф складаецца з вузлаў, вузлы выканаюць павінныя задачы, рэбра вялікія, куды працуваць далей, а стан храніць інфармацыю. Яго можна накрэсліць на дошцы зазначэнняў за пяць хвілін. А потым вы ачыняеце порожній файл, і нічога не прыходзіць. Вы забылі прыняць неабходныя імпорты, клас, які вам трэба, не мае явнага месца, і, здаецца, існуе пяць спосабаў напісаць тое ж самае.

Чаму код здаецца сложнейшым, чым сама концэпцыя

Асновная прычына тая, што LangGraph не накладае жадной супэўнай структуры. Ён дае вам спроможныя елементы для будавання і залішае рашэнне пра ўпорадкуванне вам. Кожны туторыял і кожны рэпазітарый практыкуюць розныя падходы, таму ваша памяць ніколі не зафіксуецца на аднай-едзінай схеме.

Практычны раён, які пропонуецца тут, — гэта выбір адзінаго стандартнага фармата і яго вікорыстоўванне для кожнага агента, незалежна ад таго, чы то маленькі чат-бот чы ланцоўка з многама саўместна працюючымі агентамі. Можна супрацаваць з яго як з шаблонам, які можна запам’ятаваць і выводзіць без зусіль, падобна да дыяграм, якія паказваюць, што агент ужо зрабіў, толькі ў даным случае гэта стосуецца коду, а не історыі выканання.

Весь файл за мгновеннае перагляданне

Кожны прыклад застосоўвання LangGraph можа быць напісаны як адзін файл з семью раздзеламі у фіксаванай парады, зверху вярху:

langgraph_agent.py
│
├── MODULE 1: IMPORTS & CONFIGURATION
│   └── All your libraries, API keys, model setup
│
├── MODULE 2: STATE
│   └── The TypedDict that defines your agent's memory
│
├── MODULE 3: TOOLS (optional, but common)
│   └── Functions decorated with @tool that the LLM can call
│
├── MODULE 4: NODES
│   └── Functions that do the actual work at each graph step
│
├── MODULE 5: EDGES & ROUTING
│   └── Functions that decide what happens next
│
├── MODULE 6: GRAPH ASSEMBLY
│   └── Where you build, wire, and compile the graph
│
└── MODULE 7: ENTRYPOINT
    └── The __main__ block or invoke() call that runs everything

Гэта ўсё сутнасць. Калі вы пачынаеце праект, ачыніце файл і запісайце семь заглаваў раздзялам як каментары, а пасля заповніце іх по аднаму. У наступных частках будзе рассказана пра кожны раздзял: для чаго ён, якія назвы трэба памяцаць, а таксама шаблон, які можна скопіюваць.

Раздзял 1: імпорты і налаштаванні

Для чаго ён

Спачатку вважайце гэты раздзял інструментамі. Тэпляр розкладае всі інструменты перад тым, як працаваць, а тут вы указвайце Python, якія зовнішнія можлівасці вам патрэбны, і налаштоввайце мадэль, якую будзе вікорыстоўваць агент.

Назвы, якія трэба памяцаць

  • ChatOpenAI або ChatAnthropic — гэта клас мовной мадэлі, «мозг» агента. Створыце яго тут раз і вікорыстоўвайце ў всіх месцах.
  • StateGraph — гэта основны клас, які стварае граф, падобны до плану, які вы рысуеце.
  • END — гэта спецыяльная сталя для пункта завершэння. Кант, які ведае да яго, зупіняе выконанне.
  • MemorySaver — гэта кантрольны пункт, які зберагае памяць працы ў разныя этапы дыялогу.

Шаблон

# --- Standard Library ---
import os
from typing import TypedDict, Annotated, Literal
# --- LangChain Core ---
from langchain_openai import ChatOpenAI          # or ChatAnthropic, ChatGroq, etc.
from langchain_core.messages import HumanMessage, AIMessage, SystemMessage, BaseMessage
from langchain_core.tools import tool
# --- LangGraph Core ---
from langgraph.graph import StateGraph, END
from langgraph.graph.message import add_messages  # The message reducer
from langgraph.prebuilt import ToolNode            # Pre-built node for tool execution
from langgraph.checkpoint.memory import MemorySaver
# --- Configuration ---
# Always name your model variable 'llm' - it's the standard in every node
llm = ChatOpenAI(
    model="gpt-4o",         # or "claude-3-5-sonnet-20241022", etc.
    temperature=0,          # 0 = deterministic; raise for creativity
    api_key=os.environ.get("OPENAI_API_KEY")
)

Чаму ўсё арганізавана так

Імпорты дзеляцца на чатыры групы: стандартная бібліятэка, LangChain (шар модэля), LangGraph (шар графа) і настройкі. Гэта адпавядае структуры самых бібліятэках, дзе LangChain забезпечвае прымітывы ШЫ, а LangGraph керуе оркестрацыяй. Раздзелэнне групаў дапамагае быстра павідоміць, што належыць куды, калі ўтворыцца проблема.

Найкорыстнейшыя прыкметы тут — гэта называнне модэля llm. Кожная функцыя вузла пазней будзе апісвацца на гэтыяя зменную, і калі адны праекты вжываюць model, а іншыя — chat, код перастае выглядаць аднородным і становіцца важкім для швыдкага аналізу.

Раздзіл 2: стан

Для чаго ён

State — гэта рабочая памяць агента, адна едынайшая адказвальная сурэч, якая пераходзіць праз увесь граф. Кожны вузел можа яе чытаць і запісваць у яе, тады як двіжак караце яе жыцёвы цыкл. З усьго, што ёсць у LangGraph, правільна рэалізацыя State, верагаць, ўсё найважлівейша.

Добрым прыкладам можа служыць дошка з мелам у офісе. Кожны, хто заходзіць у кімнату, тыя самыя кожны вузел, можа чытаць там напісанае і дадаць што-небудзь новае, а дошка застаецца на сваім месцы працягом усіх зустрэчаў, тыя самыя працягом усьго запуску графа.

Назвы, якія трэба ведаць

  • TypedDict — гэта клас у Python, які опісвае форму стану, падобна да схемы. Ваш клас State успадковуець ад яго.
  • Annotated — гэта обгортка для падказакаў пра тып. LangGraph выкарыстоўвае яе, каб прыкрэпіць reducer да поля, тыя самыя функцыю, якая вяршыць, як новая значэнне злучаецца з тым, што вялікі ўжо там.
  • add_messages — гэта вбудованы рэдзюсер для поля messages. Узел не заменяець весь спіс кожны раз, калі дадае новыя паведамленні, а дадае іх у канец ліста. Гэты рэдзюсер будзе вам выкарыстоўвацца чыстае.
  • operator.add — ўсё тады звычны рэдзюсер, які вікорыстоўваецца для полей, якія трэба нарабляць па прыему, напрыклад, для ліста рэзультатаў або лічыльніка.
  • Шаблон

    # ============================================================
    # MODULE 2: STATE
    # ============================================================
    class AgentState(TypedDict):
        # 'messages' is the heartbeat of almost every LangGraph agent.
        # Annotated[list, add_messages] means: "this is a list, and when
        # a node writes to it, append - don't replace."
        messages: Annotated[list[BaseMessage], add_messages]
    
        # Add custom fields below for your specific agent's needs.
        # Fields without a reducer are REPLACED each time a node writes to them.
    
        # Example: a simple string field (gets replaced each write)
        current_task: str
    
        # Example: a list you want to accumulate (use operator.add as reducer)
        # results: Annotated[list[str], operator.add]
    
        # Example: a counter
        # iteration_count: int
    

    Размышленні працэўнікіў

    Работэўнікі часта плутаюць пачатківцяў, таму прыведам аналагію. Уявіце стан як паперовы формуляр, які пераходзіць з рук у рукі ў офісе. Поле messages — гэта блок стыкерных змітакоў: кожны, хто працуе з формуляром, дадае змітак, а ніхто не вычырае пярэйшыя. Гэта і ёсць add_messages.

    Поле без работэўніка — цэлае тэкстовае поле. Кожны перазапісвае тое, што напісаў пярэйшы, што ўсталяванае як стандарт у LangGraph: першы запіс застаецца.

    Існуе простая правіла: ўсё часы прызначайце полю messages значэнне Annotated[list, add_messages], для кожнага дадатковага спіска, які павінен продавацца, выбірайце Annotated[list, operator.add], а всі іншы поля залішайце без змян.

    Раздзіл 3: інструменты

    Для чаго ён

    Інструменты — гэта рукі агента. LLM ёсць мозг, які рассуждае і прыме рашэнні, але сам па сабе не можа дзеяць у свеце. За дапамою інструментоў ён можа перэглядаць веб, запускаць API, выкананыць код, шукаць інфармацыю ў базе дадзенаў або запускаць будзь-яю функцыю на Python, якую вы задаеце.

    Гэты раздзіл ёсць необавязковым. Агент, які вырабляе толькі тэкст і ніколі не кантактуе з зовнішнімі сістэмамі, можа яго праігнораваць, хоць большасць рэальных агентоў патрабуе інструментоў.

    Назвы, якія трэба ведаць

    • @tool — гэта дэкаратар LangChain, які ператварае звычную функцыю на Python у інструмент, які розумее LangGraph. Докстрынг функцыі стае опісам, які чытае LLM пад час прымэння рашэння пра ўжыцце яе.
    • ToolNode — гэта готовы вузол, які знаходзіцца ў langgraph.prebuilt і запускае тыя інструменты, якія выбраў LLM, таму вам не трэба самім пісаць логіку выканання.
  • bind_tools — гэта метод у вашага экземпляра LLM, які прыўязвае да яго інструменты. Пасля чаго мадэль ведае, што існуе, і можа запрашваць выкананне задачы, выпускаючы спецыяльныя поведамленні tool_calls заместо звычнага тэксту.
  • Шаблон

    # ============================================================
    # MODULE 3: TOOLS
    # ============================================================
    @tool
    def search_web(query: str) -> str:
        """Search the web for current information about a topic.
    
        Use this when you need real-time information that is not in
        your training data, such as recent news or live prices.
    
        Args:
            query: The search query string.
    
        Returns:
            A string containing search results.
        """
        # Your actual implementation here (e.g., Tavily, SerpAPI, etc.)
        # Placeholder for illustration:
        return f"Search results for: {query}"
    
    @tool
    def calculate(expression: str) -> str:
        """Evaluate a mathematical expression and return the result.
    
        Use this for any arithmetic, algebra, or numerical computation.
    
        Args:
            expression: A valid Python math expression as a string, e.g. '2 + 2 * 10'
    
        Returns:
            The computed result as a string.
        """
        try:
            return str(eval(expression))
        except Exception as e:
            return f"Error: {e}"
    
    # Collect all tools into a list - this is the pattern you always follow
    tools = [search_web, calculate]
    # Bind tools to the LLM so it knows they exist and can choose to call them
    llm_with_tools = llm.bind_tools(tools)
    # Create the pre-built ToolNode that will execute tool calls automatically
    tool_node = ToolNode(tools)
    

    Докстрынг мае большое значэнне, чым можа здавацца

    Пачатківцы часта не разумеюць гэтага: мадэль паводлівае вашым докстрынгам, каб выбраць інструмент. Яна чытае тэкст унутроў тройных цітатаў. Калі докстрынг неясны або відсутны, LLM не будзе ведаць, калі ваш інструмент ёсць правым, таму пішыце чыстыя, конкрэтныя апісанні, якія пазначаюць, што робіць інструмент і калі яго вжываць.

    Раздзіл 4: вузлы

    Для чаго яны

    Вузлы — гэта «рабочыя елементы». Кожны з іх — гэта функцыя на Python, якая прыме якісь стан, выконвае якісь дзеянні, такія як вызов LLM, апрацоўка дадзэнняў або запуск інструмента, і вяртае словнік з апдэйтамі стану. Гэта і ёсць ўсё, што трэба ведаць пра ўзначэнне вузла.

    Вузел ніколі не вырашае, што будзе далей. Яны выкананяюць свою працу і апдэйтуюць стан, а наступная роутаванне обрабляецца ў раздзіле 5.

    Імены, якія трэба ведаць

    • state — гэты параметр, які атрымлеў кожны вузел, адпаведны словніку у фармаце AgentState. У яму можна прачытаць такія значэнні, як state["messages"] або state["current_task"].
    • HumanMessage, AIMessage і SystemMessage — гэтыя тры тыпы паведамленняя, якія вы будзеце часта вікорыстоўваць. Першыя — гэта тое, што сказаў корыстнік, другія — адпаведныя адпаведзі мадэлі, а трэція — вашы інструкцыі для мадэлі, тое самае, што ўзначае сістэмны прыказ.
    • invoke — гэта спосаб вызывання LLM, або llm_with_tools, каб атрымаць адпаведзь. Вы перадаеце спіс паведамленняў і атрымлеў AIMessage.
  • Значэнне, якое вяртаецца, завжды ў віглядзе словніку, калькі ключы якога збігаюцца з назвамі полей у AgentState. Яго значэнні — гэта апдэты, а не цэлы стан, і LangGraph з’едынае іх за дапамою рэдуктараў, якія вы задаўлі.
  • Шаблон

    # ============================================================
    # MODULE 4: NODES
    # ============================================================
    # ── Node: Agent (the reasoning brain) ───────────────────────
    def agent_node(state: AgentState) -> dict:
        """The central reasoning node. Calls the LLM and decides
        whether to respond or call a tool."""
    
        # Build the message list to send to the LLM.
        # Always include a system message to set behavior.
        system_prompt = SystemMessage(content=(
            "You are a helpful assistant. Use the available tools "
            "when you need real-time information or computation. "
            "Respond clearly and concisely."
        ))
    
        # The LLM receives the system prompt + all previous messages in state
        messages_to_send = [system_prompt] + state["messages"]
    
        # Call the LLM. Use llm_with_tools if you have tools; plain llm if not.
        response = llm_with_tools.invoke(messages_to_send)
    
        # Return the LLM's response as a state update.
        # add_messages will APPEND this AIMessage to state["messages"].
        return {"messages": [response]}
    
    # ── Node: Summarizer (example of a non-LLM processing node) ─
    def summarize_node(state: AgentState) -> dict:
        """Summarizes the conversation so far to keep context short.
        This shows that nodes don't have to call an LLM - they can
        do any Python processing."""
    
        all_messages = state["messages"]
    
        # Summarize with the LLM (a different prompt, same LLM)
        summary_prompt = [
            SystemMessage(content="Summarize the following conversation in 2-3 sentences."),
            HumanMessage(content=str(all_messages))
        ]
    
        summary_response = llm.invoke(summary_prompt)
    
        # Replace messages with a fresh start containing just the summary
        return {
            "messages": [AIMessage(content=f"[Summary] {summary_response.content}")]
        }
    

    Зяўрэк для вузлаў

    Уявіце вузел як бегуна-перадавальніка. Ён берае „бэйтан“ — тое ўзьме стан, праходзіць свой участак, тыято выканае працу, і перадае „бэйтан“ з апошнімі змянамі. Ён не выбірае наступнага бегуна. Гэта завада таго, хто керуе гонкай, тыято рэшэтаў.

    Кожны падпіс вузла выглядае аднакова: def my_node(state: AgentState) -> dict. Вхідным дадзенням ёсць весь стан, а выхідным — слоўнік, які містыць толькі тыя поля, якія вы хочаце змяніць.

    Раздзіл 5: рэшэты і маршрутызацыя

    Для чаго гэта

    Рэшэты — гэта логіка прыняцця рашэнняя графа. Яны з’яўляюць вузлы між сабою і вялікі, як праходзіць выкананне. У LangGraph існуе два відменнікі рэшэтаў.

    Статычныя рэшэты з’яўляюць адні вузлы з іншымі без жадных умов: „пасля сумарызатора — да агента“. Няма рашэння, толькі прамыя з’яўленні.

    Умовныя рэглі аналізуюць стан і вяртаюць строку з назвай наступнага вузла. Самэўсюды тут і знаходзіцца інтэлігэнція агента ўжо па сваёй схеме руху. Чы хаця бы ён вызваць інструмент, завершыць працу, чы зноў спробаваць?

    Назвы, якія трэба ведаць

    • add_edge(A, B) стварае статычны рэгл, тое значыць, кантроль завжды пераходзіць з A на B.
    • add_conditional_edges(source, routing_function, mapping) стварае галузь, якая выходзіць з source. Функцыя маршрутацыі прыме якісь стан і вяртае строку, а словнік mapping перакладае гэтую строку на рэальную назву наступнага вузла.
  • tools_condition — гэта готовая функцыя маршрутацыі з langgraph.prebuilt. Яна пераглядае апошнее сообшчэнне ў стане на наявнасць вызоў інструментаў. Калі такія ёсць, яна направляе трафік да "tools", а якшо няма — да END. Гэта стандартны шаблон для агентаў, якія выкарыстоўваюць інструменты, і яго варта запамятаць.
  • START — гэта сталя для пачатку графа. Установка точкі входу означае дадаць рэштку з START да першага вузла.
  • Шаблон

    # ============================================================
    # MODULE 5: EDGES & ROUTING
    # ============================================================
    # Import the pre-built tool routing function
    from langgraph.prebuilt import tools_condition
    # ── Custom Routing Function Example ─────────────────────────
    def should_continue(state: AgentState) -> Literal["tools", "summarize", "__end__"]:
        """Custom router for the agent node.
    
        Routing functions always:
        1. Receive the current state as input
        2. Return a string that maps to the next node (or END)
    
        The return values must match the keys in add_conditional_edges' mapping.
        """
    
        last_message = state["messages"][-1]  # Look at what the LLM just said
    
        # Case 1: The LLM decided to call a tool
        if hasattr(last_message, "tool_calls") and last_message.tool_calls:
            return "tools"
    
        # Case 2: The conversation is getting long - summarize before continuing
        if len(state["messages"]) > 20:
            return "summarize"
    
        # Case 3: The LLM gave a direct answer - we're done
        return "__end__"  # LangGraph's internal name for END
    

    Зявочка для маршрутацыі

    Уявіце функцыю маршрутацыі як аператара залізнадарожніх сігналів. Паўоз прыбывае на станцыю, калі завершаецца праца вузла. Аператар перакантруе інформацыю пра тэкушчы стан і пераключы правы сігнал, каб паўоз пайшоў правым шляхам. Аператар ніколі не перамішчае паўоза; гэта робіць LangGraph.

    Адна правіла, якое трэба памятаць: функцыі маршрутацыі ніколі не мяняюць стан. Яны толькі яго чытаюць. Будзь-яя змяна павінна выконвацца ў вузле, таму, калі вы бачыце, што хочаце апдэйтаваць стан пад час маршрутацыі, перакладзіце гэтую логіку ў вузел.

    Раздзіл 6: складанне графа

    Для чаго ён

    Тут усё з’еднаецца. Спрыйміце гэты раздзіл як дзень будавання. Раздзілы 1-5 былі падготовкай: былі візначаны матэрыялы, іх нарэзалі і расставілі, а цяпер вы складаеце структуру, з’ўязваючы елементы і компілюючы.

    Складанне завжоды выконваецца па тых самых пяць кроках, якія варта запам’ятаваць: ініцыялізацыя, рэгістрацыя вузлаў, заданне точкі входу, з’ѐеднанне краяў, компіляцыя.

    Назвы, якія трэба ведаць

    • StateGraph(AgentState) стварае інструмент для складання, выкарыстоўваючы ваш клас State як схему, таму кожны вузел павінен вяртаць абнаўленні, сумесныя з яю.
    • add_node(name, function) рэгіструе вузел. Назва — гэта строка, якая будзе выкарыстоўвана ў краях, а функцыя — гэта вузел з раздзела 4.
    • set_entry_point(name) вказывае, які вузел запускаецца першы, калі запускаецца граф.
    • add_edge(A, B) стварае прымітны, безусловны зв’язак з A на B.
    • add_conditional_edges(source, router, mapping) дадае умовную галузь з выходу, выкарыстоўваючы вашу функцыю маршрутызацыі і супорадкуванне ад вярнутых строкаў да назваў пунктавых прызначэнняў.
  • compile(checkpointer=...) закрывае граф у ўтварэнне, якое можна запусціць ( CompiledGraph ). Пасля гэтага не можна дадаць новыя вузлы і рэшткі. Апшаронны параметр checkpointer увёльняе можлівасць вікорыстоўвання стойкай памяці.
  • Шаблон

    # ============================================================
    # MODULE 6: GRAPH ASSEMBLY
    # ============================================================
    # ── Step 1: Initialize ──────────────────────────────────────
    # Always pass your State class to StateGraph
    graph_builder = StateGraph(AgentState)
    # ── Step 2: Register All Nodes ──────────────────────────────
    # Format: add_node("node_name_as_string", node_function)
    # The string name is what you use in ALL edge definitions
    graph_builder.add_node("agent", agent_node)
    graph_builder.add_node("tools", tool_node)      # The pre-built ToolNode from Module 3
    graph_builder.add_node("summarize", summarize_node)
    # ── Step 3: Set Entry Point ─────────────────────────────────
    # Which node runs first when we invoke the graph?
    graph_builder.set_entry_point("agent")
    # ── Step 4: Wire the Edges ──────────────────────────────────
    # Conditional edge from agent: check if we need tools, a summary, or we're done
    graph_builder.add_conditional_edges(
        "agent",          # Source node
        should_continue,  # Routing function from Module 5
        {
            "tools": "tools",           # If router returns "tools" → go to tools node
            "summarize": "summarize",   # If router returns "summarize" → go to summarize node
            "__end__": END,             # If router returns "__end__" → stop the graph
        }
    )
    # Static edge: after tools run, always go back to agent (the ReAct loop)
    graph_builder.add_edge("tools", "agent")
    # Static edge: after summarization, always return to agent
    graph_builder.add_edge("summarize", "agent")
    # ── Step 5: Compile ─────────────────────────────────────────
    # Without checkpointer: no persistent memory (stateless per invocation)
    # With checkpointer: memory persists across turns (stateful conversations)
    memory = MemorySaver()
    graph = graph_builder.compile(checkpointer=memory)
    

    Зявіце для складання

    Найлепшым аналагам ёст карточка схемы. Кожны вызов add_node размешчае компонент. Функцыі add_edge і add_conditional_edges з’ўязваюць дроты між компонентамі. Функцыя compile() адрабатвае заканчоўчыую перапачатку і гатавіць карточку. Пасля чаго яна гатовая да падключэння напаў, што вырабляецца за дапамогай invoke, і да выканання свайго завадзявання.

    Шаблон містыць фундаментальны цыкл ReAct: agent → tools → agent → tools і так далей, пакуль агент не вырашыць, што ўсё завершана, і не перэйдзе на END. Гэты цыкл «прычына-паслядкі-дзеянне» ёсць адной з асноваў практычна кожнага агента LangGraph, які выкарыстоўвае інструменты, і розуменне яго паяснюе большасць графаў, з якімі вы зустрэнетэся.

    Раздзіл 7: точка входу і вызыв

    Для чаго яна патрэбна

    Эта ўстановка для запуску. Усё, што было раней, — гэта вялікі план, а гэты раздзіл — адкліканне: код, які запускае граф, задае вхідныя даны і збірае выхідныя.

    Існуе два спосабы яго запуску: invoke для аднаго проходжэння і стрымінг для поступовага выдачы рэзультатаў. Таксама патрэбны config — слоўнік, які містыць параметры часу выканання, у тым часе thread_id, які вікорыстоўваецца для керування памяцюю.

    Назвы, якія трэба ведаць

    • graph.invoke(input, config) запускае весь граф і вяртае канечны стан. Паколькі гэта сінхронны працэс, вы нічога не отрымаеце, пакуль не завершыцца кожны крок.
    • graph.stream(input, config) таксама запускае граф, але вяртае частковыя абнаўленні стану па меры ўзнікнення, што ідзе на карысць дапрацоўкаў, якія павінны паказваць працэс у рэальны час.
  • config — гэта слоўнік, які пасылаецца праз кожны вызов. Яго ключавая елемента ў віглядзе {"configurable": {"thread_id": "some_id"}}. ID аддзеўкі працуе як ID сесіі, які паведамляе чэкпойнтэру, яку памяць трэба вярнуць і зберагчы. Два корыстнікі з разнымі ID аддзеўкі ніколі не дзеляцца памяцю.
  • HumanMessage(content=...) — гэта спосаб прыемкі вхідных дадзенняў корыстніка першым, прычым яныя падаюць у граф.
  • Шаблон

    # ============================================================
    # MODULE 7: ENTRYPOINT & INVOCATION
    # ============================================================
    if __name__ == "__main__":
    
        # ── Config: defines this conversation's memory session ──
        # Change thread_id to start a fresh conversation.
        # Keep the same thread_id to continue an existing one.
        config = {"configurable": {"thread_id": "user-session-001"}}
    
        # ── Single Invocation (synchronous) ─────────────────────
        user_input = "What is the current price of Bitcoin?"
    
        result = graph.invoke(
            input={"messages": [HumanMessage(content=user_input)]},
            config=config
        )
    
        # The result is the final state dictionary.
        # Access the last message to get the agent's final answer.
        final_answer = result["messages"][-1].content
        print(f"Agent: {final_answer}")
    
        # ── Streaming Invocation (for real-time output) ──────────
        for chunk in graph.stream(
            input={"messages": [HumanMessage(content=user_input)]},
            config=config,
            stream_mode="values"  # Yields the full state after each node runs
        ):
            # Each chunk is a state snapshot. The last message shows progress.
            latest = chunk["messages"][-1]
            if hasattr(latest, "content") and latest.content:
                print(f"[Streaming] {latest.content}")
    
        # ── Multi-turn Conversation Loop ─────────────────────────
        print("\n--- Starting Interactive Session ---")
        while True:
            user_text = input("You: ").strip()
            if user_text.lower() in ("exit", "quit", "bye"):
                break
    
            response = graph.invoke(
                input={"messages": [HumanMessage(content=user_text)]},
                config=config  # Same config = same memory thread
            )
    
            print(f"Agent: {response['messages'][-1].content}\n")
    

    Полная шаблон

    Чырвоны ёсць цэлы, мінімальны каркас, які трэба скопіюваць у новы праект. Оін являе сабою едын файл, які варта зберагчы: заменіце места для прадэманстрацый на сваю логіку, і у вас будзе база для будзь-каго агента LangGraph.

    # ============================================================
    # LANGGRAPH CANONICAL AGENT TEMPLATE
    # Modules: Imports → State → Tools → Nodes → Edges → Assembly → Entrypoint
    # ============================================================
    # ── MODULE 1: IMPORTS & CONFIGURATION ───────────────────────
    import os
    from typing import TypedDict, Annotated, Literal
    from langchain_openai import ChatOpenAI
    from langchain_core.messages import HumanMessage, AIMessage, SystemMessage, BaseMessage
    from langchain_core.tools import tool
    from langgraph.graph import StateGraph, END
    from langgraph.graph.message import add_messages
    from langgraph.prebuilt import ToolNode, tools_condition
    from langgraph.checkpoint.memory import MemorySaver
    llm = ChatOpenAI(model="gpt-4o", temperature=0)
    
    # ── MODULE 2: STATE ─────────────────────────────────────────
    class AgentState(TypedDict):
        messages: Annotated[list[BaseMessage], add_messages]
        # Add your custom fields here
    
    # ── MODULE 3: TOOLS ─────────────────────────────────────────
    @tool
    def my_tool(input: str) -> str:
        """Describe clearly what this tool does and when the LLM should use it."""
        return f"Result for: {input}"
    tools = [my_tool]
    llm_with_tools = llm.bind_tools(tools)
    tool_node = ToolNode(tools)
    
    # ── MODULE 4: NODES ─────────────────────────────────────────
    def agent_node(state: AgentState) -> dict:
        """The reasoning node. Calls the LLM, optionally triggers tool calls."""
        messages = [SystemMessage(content="You are a helpful assistant.")] + state["messages"]
        response = llm_with_tools.invoke(messages)
        return {"messages": [response]}
    
    # ── MODULE 5: EDGES & ROUTING ───────────────────────────────
    def should_continue(state: AgentState) -> Literal["tools", "__end__"]:
        """Decide: did the LLM call a tool, or did it give a final answer?"""
        last_message = state["messages"][-1]
        if hasattr(last_message, "tool_calls") and last_message.tool_calls:
            return "tools"
        return "__end__"
    
    # ── MODULE 6: GRAPH ASSEMBLY ────────────────────────────────
    graph_builder = StateGraph(AgentState)
    graph_builder.add_node("agent", agent_node)
    graph_builder.add_node("tools", tool_node)
    graph_builder.set_entry_point("agent")
    graph_builder.add_conditional_edges(
        "agent",
        should_continue,
        {"tools": "tools", "__end__": END}
    )
    graph_builder.add_edge("tools", "agent")
    memory = MemorySaver()
    graph = graph_builder.compile(checkpointer=memory)
    
    # ── MODULE 7: ENTRYPOINT ────────────────────────────────────
    if __name__ == "__main__":
        config = {"configurable": {"thread_id": "session-001"}}
    
        while True:
            user_text = input("You: ").strip()
            if not user_text or user_text.lower() in ("exit", "quit"):
                break
            response = graph.invoke(
                {"messages": [HumanMessage(content=user_text)]},
                config=config
            )
            print(f"Agent: {response['messages'][-1].content}\n")
    

    Далей: сістэмы з мнагае агентамі

    Калі падход з аднім агентам здаецца прыродным, сістэмы з мнагае агентамі перастаюць баяцца. Галоўная ідэя заключаецца у тым, што кожны агент — гэта проста скомпіляваны граф, а скомпіляваны граф сам можа быць вузлам у большым графе-кантролеры.

    Назвы, якія трэба ведаць для работы з мнагае агентамі

    • Вузел-кантролер — гэта агент, чыя задача не ў тым, каб адпавядаць на запыткі корыстніка, а ў тым, каб выбраць канкрэтнага спецыяліста, які павінен выконаць заданне. Ён направляе трафік між падагентамі.
    • Падграф — гэта скомпіляваны граф, зарэўнаваны як вузел у іншым графе. Ён прыймае і вяртае стан, як і будзь-яя звычайная функцыя вузла.
  • Command — гэта новейшы модэль, доступная з Лангграф 0.2, дзе адзін вузел вяртае як апдэйт стану, і інструкцыю паводзе ў адной значэнні. У дзеярых дызайнах з калькамі гэта пазбавляе ад патрэбы ў окалічных умовных рэштках.
  • Структурны шаблон

    # ── MULTI-AGENT PATTERN ─────────────────────────────────────
    # Each specialist is a compiled graph (a subgraph)
    
    # Sub-agent 1: A researcher
    researcher_graph = StateGraph(AgentState)
    # ... (built with its own nodes, edges, and tools)
    researcher = researcher_graph.compile()
    # Sub-agent 2: A writer
    writer_graph = StateGraph(AgentState)
    # ... (built with its own nodes, edges, and tools)
    writer = writer_graph.compile()
    
    # ── SUPERVISOR NODE ─────────────────────────────────────────
    def supervisor_node(state: AgentState) -> dict:
        """Decides which sub-agent should handle the current task."""
        # The supervisor LLM decides: "researcher" or "writer" or "FINISH"
        response = supervisor_llm.invoke(state["messages"])
        return {"messages": [response], "next_agent": response.content}
    
    def route_to_agent(state: AgentState) -> Literal["researcher", "writer", "__end__"]:
        """Routes to the appropriate sub-agent based on supervisor's decision."""
        return state.get("next_agent", "__end__")
    
    # ── SUPERVISOR GRAPH ────────────────────────────────────────
    supervisor_builder = StateGraph(AgentState)
    supervisor_builder.add_node("supervisor", supervisor_node)
    supervisor_builder.add_node("researcher", researcher)  # Subgraph as a node!
    supervisor_builder.add_node("writer", writer)          # Subgraph as a node!
    supervisor_builder.set_entry_point("supervisor")
    supervisor_builder.add_conditional_edges(
        "supervisor",
        route_to_agent,
        {"researcher": "researcher", "writer": "writer", "__end__": END}
    )
    supervisor_builder.add_edge("researcher", "supervisor")
    supervisor_builder.add_edge("writer", "supervisor")
    supervisor_graph = supervisor_builder.compile(checkpointer=MemorySaver())
    

    Версія з калькулярамі мае тую ж структуру, як і версія з адзентам, і прыменяюцца тыя ж сямь раздзелаў. Едыныя разлікі — гэта што дзеякія з „вузлаў“ ёсць цэлыя скомпіляваныя графы. Ментальны модэль расце без працы.

    Кароткая інфармацыя

    Ёсць стыснутая карточка, яю можна распрацаваць або закрэпіць, у якой сумараваны галоўныя ідэі.

    • Стан: TypedDict візначае яго форму. Annotated[T, reducer] прыўязвае рэдуктар да польа. add_messages — стандартны рэдуктар для спіса паведамленняў, які дадае ў спіс, а не заменяе яго. operator.add акумулюе спісы або цыфры.
    • LLM: ChatOpenAI або ChatAnthropic — гэта клас, які завжды асоціюецца з іменем llm. Функцыя bind_tools(tools) паведамляе модэлю пра наявныя інструменты. Функцыя invoke(messages) вызывае модэль і вяртае адпаведны адказ.
    • Паведамленні: HumanMessage несе даныя, якія вводзі корыстнік, AIMessage — рэзультаты працы модэля, а SystemMessage — вашы інструкцыі. Яны завжды перэдаюцца функцыі invoke у вачынку.
    • Інструменты: атрыбут @tool робіць функцыю на Python доступной для вызыву модэлем, пры чым докстрайнг служыць яе апісам. Функцыя ToolNode(tools) автаматычна выкананяе вызывы інструментаў. Атрыбут tools_condition пераканальваецца, чы не запрашваў модэль якога-небудзь інструмента.
    • Граф: StateGraph(State) ствараець канструкцыю, add_node(name, fn) рэгіструее вузел, set_entry_point(name) выбірае першы вузел, add_edge(A, B) стварае безусловную звязку, add_conditional_edges(source, router, mapping) стварае звязку, якая выбіраецца на адпаведнае значэнне, якое вяртае рутэр, compile(checkpointer=...) завершае працэс, а END пазначае канечную точку.
    • Выклік: graph.invoke(input, config) вяртае заканчоўны стан, graph.stream(input, config) паштоўна прадае апошнія змяны, config = {"configurable": {"thread_id": "..."}} ідэнтыфікуе сесію памяці, а MemorySaver() являе сабою чакпойнт у памяці.

    Ператворэнне ў інстынкты

    Мета не ў тым, каб навучыць LangGraph з нуля. Гэта ў тым, каб даць вашам мозгу стабільную структуру, яю можна будзе аўтаматычна выкарыстоўваць, так як музыкант берае звычную прогрэсію акордаў або кухар падготавляе інгрэдыянты перад пачаткам работы.

    Семь раздэлаў — імпорты, стан, інструменты, вузлы, рэшткі, складанне і точка входу — складаюць гэтую прагрэсію. Назвы ў кожным раздэле ўособліваюць ноты, а цэлы шаблон — партытуру.

    Як базовы чатбот, так і ланцюг з дзесяткамі вузлаў паслухоўваюцься той самый план. Складнасць зрастае, але структура — ні, і самэ гэта стабільнае падчыненне і ўчыніла яго стандартам.

    Розумны спосаб прыступу — пачаць з мінімума, з мінімальнага цыклу ReAct: адны вузел агента, адны вузел інструмента і адна умовная рэштка, якая выкарыстоўвае tools_condition. Спачатку заставіць гэта працаваць, потым дадаць ўтрохты ўжо адны вузел, а пазней — ўжо аднаго агента. План застаецца тым самым незалежна ад розмеру.

    Для задач, які выкарыстоўваюць бэкенды з можлівасцю зберагчыць даны, патэрны з удзелам чалавека і варыянты развяртання, офіцыйная дакументацыя LangGraph — гэта наступны істочнік інфармацыі.