Галоўная / Артыкулы / Як агенты LangGraph выжываюць пасля перзапускаў: кэшпоінты, кэшпойнтары, ниткі

Як агенты LangGraph выжываюць пасля перзапускаў: кэшпоінты, кэшпойнтары, ниткі

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

4200 слоў

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

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

Чаму агентам патрэбны станы, якія перазьіхаюць час адбывання процесу

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

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

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

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

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

Што ламаецца, калі у агента няма перазбірання

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

Паўза напаўды длігай задачы

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

Стандартнае перзапусканне сервера

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

Збой у працэсе багатыякхадзінковай задачы

У рамках большой задачы агент запоўнюе зовнішні API, які выключаецца за часам, і процес на Python зупіняецца чераз неадрасаваную асаблівасць. Неудачны крок трапляецца, але таксама і все, што агент завершыў да таго момента.

Рабочыя практыкі, якія працуюць гады або дні

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

Пярэчанне чалавека, якое трывае гады

Агент стварае правовы дакумент, які адвокат павінен затвердзіць пры выклычэнні. Адвокат не можа абсалютна нічога пераглядаць прыблізна шасць гадзін. Затрымка процэсу і утриманне ресурсаў такі час є марнатрапствам, а якщо сервер перазапускаецца пад час чакання, задача зникае.

Багатыятковая логіка, якая злучваецца пазней

Складныя агенты часта дзеляць работу на цикл планавання, пошуку, пераканання і стварэння падсумку. Якщо 7-й з 10 крокаў не выйшае, павторны запуск крокаў 1-6 марнуе час, вызовы API і грошы.

Чаму „проста запусціць зноў“ не ёсць стратэгіяй

Перазапуск з нуля здаецца прыемным, пакуль не пасчытваеш затраты:

  • Грошы. Кожны вызов LLM спрацоўвае токены, таму павторны запуск шасці успешных крокаў з-за неудачы сьведамага кроку значыць платіць за іх два разы.
  • Час. Карыстальнікі вымушаны чакаць, пакуль будзе павтарацца работа, на якую ўжо чакалі.
  • Доверлівасць. Банкавы асистэнт, який забывае прасбу пра кредыт ў кожны раз, калі аднаўляецца сторанка, не ўражае як справжні продукт.
  • Небяспечныя наследкі. Якщо празьнейшы крок вже аправіў электранаўленне чы скасаваў платэж з карткі, його павтарэнне спрычынае рэальныя накладкі. Дзеянне дзеянняў не можна безпечна павтарыць.
  • Гэты последні пункт ёсць найважлівейшым. Мэта стойкасці — не толькі заўсёды захоўваць зусілля; гэта таксама можласць дазволіць прыкладнэй програме на час зупініцца, збіцца, пачаць зноў чы чакаць, а потым продаважыць з таго месца, дзе яна насправды зупінілася, так што завершаная работа застаецца завершанай.

    Точная дэфініцыя стойкасці

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

    У гэтым адзначэнні ёсць тры разныя можлівасці:

    1. Зберагчы стан: фіксацыя таго, што агент зараз ведае і што ён зрабіў.
    2. Вярнэнне да паканальнага выконання: чытанне гэтых зявіск паўтароць, нават пасля перзапуску.
    3. Продажчы роботу: продазваць работу з таго пункту, які быў вярнуты, а не з самага пачатку.

    Вялікочасовая память проты стойкага зберагчыка

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

    Што дае вам стойкасць у працэйнай средзе

    Пашчаўжна ад запобегання втраты роботы, стойкасць зменяе тое, якія системы вы можете стварыць.

    • Агенты, якія працуюць дзёўна. Агенты, якія пераглядаюць многа стораніц, обрабоўваюць вялікія наборы дадзеных або чакаюць на зовнішнія запускі, можна паставіць на паузу і продажыць у будзь-вялікі час, на будзь-якай машыне, якая можа з’явіцца да сховішча.
    • Толерантнасць да абяканняў. Система, якая толерантна да абяканняў, продовжае правільна працаваць, калі выходзяць з ладу, адбываюцца сіткавыя абякання або заканчваюцца таймауты. За дапамогою стойкасці абяканне каштуе толькі той крок, який быў у працэ, а не всю задачу.
    • Процесы затверджэння. Калі чалавек должен пераглянуць што-небудзь, агент можа зупініцца на месцы на столькі час, сколькі трэба, не втрачаючы нічога. Стварэнне такіх процесаў становіцца простым, калі стан є стойкім.
  • Вярнэнне без спецыяльнага коду. Вам не трэба пісаць спецыяльныя рутыны для вярнэння. Вы завантажваеце апошніяй збережаныя даны і продовжваеце работу; LangGraph адмашчае гэта за вас, калі ўвімкнута функцыя збережэння.
  • Надзеяныя продукты. Рэальныя корыстувачы не будуць схвалляць неабходнасць пачынання всёга з нуля пасля кожнай разбыранні. Функцыя збережэння — галоўная разліка межаючы нестойкай дэмаверсіяй і готовым продуктом.
  • Меньшыя виткі. Дорогія процесы, якія вже працавалі успешна, такія як длігія генерацыі або повольныя вызовы інструментаў, не платзяцца зноў.
  • Кращы досвід. Людзі чакаюць, што чат-асистэнт запамятае размову пасля таго, як яны закрыюць вікно і вернуцца. Гэтае запамятаванне — цэ ўжо функцыя збережэння.
  • Шырокі тэст, каб з’ясаваць, чы гэта трэба: якшо процес пачнётся занова зараз, чы будзе неспакою ў пользователя? Якшо адказ «так», то графу патрэбна стойкасць.

    Стан: тое, што насправды зберагаецца

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

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

    Стан зазвычай прызначаецца як слоўнік з типам. Першы крок — імпорт:

    from typing import TypedDict
    

    У схеме паказаны ключы, з якімі працюе граф, і ўсі їх типы. У даным прыкладзе стан фіксуея спіс паведамленняў, імя пользователя і лічылку крокаў:

    class State(TypedDict):
        messages: list
        user_name: str
        step_count: int
    

    Пакалькі State ёсць TypedDict, гэта проста слоўнік з фіксаваным наборам ключоў і заданымі типамі. Кожны вузол прымае текущы стан і вяртае частковае апдэйт, а LangGraph з’еднае гэты апдэйт у спільны стан.

    Чаму ўсё абоўязкова асоціюецца з станам

    Стан ёсць цэнтрам прыемплікацыі LangGraph:

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

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

    Фота стану пасля кожнага крока

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

    Контрольныя точкі: фота стану ў певны момент

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

    Што можа даць контрольныя точкі

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

    Контрольныя точкі ствараюцца самыя

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

    Што мае сабэкспаншн

    Сабэкспаншн зазвычай запісвае:

    • id: унікальны ідэнтыфікатор, аранжаваны так, каб пазнейшыя сабэкспаншныя запісы былі пасля ранейшых;
    • ts: час стварэння сабэкспаншна;
    • channel_values: самыя данні стану, такія як паведамлення і іншыя зменныя, у той момент;
    • channel_versions: внутрашнія лічыльнікі версій, якія викорыстоўвае LangGraph для стежэння за тым, якія часткі стану зменіліся;
    • metadata: інформацыя для бухгалтерскага утримання, такая як який вузол стварыў сабэкспаншн і номер крока.

    Няма прычыны запамятаваць гэтыя структуры. Дастатнека простая вялічына: пункт перапалоў — это кадры стану разам з дакументацыяй, запісаныя у конкрэтны момент. Якщо вы хочаце пазнакоміцца з тым, як усероўні ўтримваюцца гэтыя элементы, укладаючы ў сабе незавершаныя запісы і блокі дадзеных, там є болей шырокая інструкцыя у Inside LangGraph's InMemorySaver.

    Лічылка, па кроках

    Разглядзім граф з адным вузлом, які падвайшае цячэнне, які запускаецца тры разы паспяльна. Пасля першаго запуску захаваны стан мае форму {count: 1}, пасля другага — {count: 2}, а пасля трэцяга — {count: 3}; кожны з іх выступае як самастая точка контролю. Якщо програма зламваецца сяродзім пасля запісу другай точкі контролю, ўспрымленне можа продавацца з {count: 2}, не павтараючы першыя два падвайшэння.

    Чакпоінтары: компонент, який захоўвае і завантажае

    Якщо точка контролю — це знімак стану, то чакпоінтар — это компонент, який делае знімкі стану, захоўвае іх і выкарыстоўвае. Цэе шар перзістанная памяці LangGraph.

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

    Тры функцыі чэкпойнта

    Чэкпойнт адпавядае за:

    1. Зберагачча стану: запісваўце новага снэпшоту ў зберохват пасля кожнага крока.
    2. Чытанне стану: чытанне найсвежэйшага снэпшоту, калі граф зноў запускаецца для таго ж потока (потакі будуць рассмотраны ў наступным раздзеле).
    3. Вярнэнне выконання: перадача таго снэпшоту системе выканання, каб граф продаваў з таго месца, дзе ён завершыўся, а не пачынаў з нуля.

    Падключэнне чэкпойнта ў час компілявання

    Функцыя зберагча прынудна, калі граф компілюецца. Вы імпортуеце клас checkpointer і кампанеткі графа; у выкарыстоўваным фрагменте коду ён пазначаецца як JavaScript, але на самай працэ ён ў Python:

    from langgraph.checkpoint.memory import InMemorySaver
    from langgraph.graph import StateGraph
    

    Потым вы ствараеце об’ект checkpointer і перадаеце яго функцыі compile(). Заўважкайце, што ў выдрукаваным фрагменте коментар і прызначэнне checkpointer = InMemorySaver() з’едыналіся ў адну лінію, чыяму прызначэнне стае часткай коментара; у рэальным кодзе яны павінны быць на разных лінях:

    # ... assume `builder` is a StateGraph you've already defined ...checkpointer = InMemorySaver()
    graph = builder.compile(checkpointer=checkpointer)
    

    Этот аргумент checkpointer=checkpointer увёльняе можлівасць зберагча для всего графа. Без яго LangGraph нічога не зберагае, і кожны вызов invoke() пачынаецца з порожньяго стану.

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

    Є адна прычына, якую лёгка занедбаць. Пункт перапыткі захоўвае толькі тыя графы, якія ў яго былі переданы. Якщо той самы файл скомпілюе другі граф без пункта перапыткі, той другі граф узагалі не мае стойкасці.

    Ниткі: раздзелэнне розмов

    Значэння thread_id з’являецца ў всім коде LangGraph, і яго трэба чытка адказаць. Нітка прадставляе адну неперывную розмову чы задачу. Кожная нітка мае унікальны ID, і кожны пункт перапыткі, створаны для той розмовы, групуецца пад яй.

    Чаму кожная розмова патрэбна сваяя нітка

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

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

    Перадача ID тэадза ў коде

    Тэад выбіраецца за дапамогою слоўніка налаштаванняў. thread_id знаходзіцца пад ключам configurable (гэты і наступны фрагмент — у Python, а не просты текст):

    config = {"configurable": {"thread_id": "customer-a-session-101"}}
    

    Гэтыя налаштаванні перадаюцца разам з вхідным данным праз кожны вызов:

    result = graph.invoke({"messages": [{"role": "user", "content": "Where's my refund?"}]}, config)
    

    Кожны раз, калі вызываецца graph.invoke() або graph.stream(), пасылаецца параметр config, які мае значэнне thread_id. LangGraph выкарыстоўвае яго для:

    • пошуку ўжыткаваных пунктаў перапытку для данага потока, якщо існуе історыя, якая трэба завантажыць;
    • зберагчэння новых пунктаў перапытку пад тым самым ID, калі запуск продовжаецца.

    Якщо выскорыстаць іншы thread_id, будзе створана чыстая, порожняя розмова, нібы стан быў скасаваны, хоця скомпільваны граф і пункт перапытку ўсё тыя ж об’екты.

    Як потакі паўтараюцца ў рэальных продуктах

    • Інтерфейс чату: кожны чат, які вы ачыняеце, фактычна ёсць сваім сабой потокам, а змена чату падобная да змены ID потока. Тое, што вы сказалі ў адной розмове, не пратыкаецца ў іншую.
  • Служба падпіркі: кожны тыкет можа быть прыяжаны да аднаго ID-а задачі, чым історыя гэтага клёўента залічваецца адсакроўанай і лёгкая да выявлення пазней.
  • Асабісты асистэнт: асистэнт, які керуе календарым і спісам задач адной особы, можа выкарыстоўваць адны, давгавечны ID-задачы, прыяжаны да адначлена ўпынку, тады прывычкі пераходзяць праз многі дні.
  • Практычны наследак — ID-задачы трэба ствараць і зберагаць цярпліва, напрыклад, на адной з база дадзеных, выведаныя з номера тыкета або ID-а сесіі. Якщо ID згубіцца, контрольныя пункты застаюцца, але нічога не вядома пра іх.

    Адна скомпіляваная графіка, многі задачы

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

    Ад пачатку аж да збою і далей да продажу

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

    flowchart TD
        A[1. Graph starts with invoke] --> B[2. Checkpointer checks thread_id for existing State]
        B --> C{State exists for this thread?}
        C -->|Yes| D[3a. Load last saved State]
        C -->|No| E[3b. Start with fresh empty State]
        D --> F[4. Node executes]
        E --> F
        F --> G[5. State updates in memory]
        G --> H[6. Checkpoint saved to storage]
        H --> I{More nodes to run?}
        I -->|Yes| F
        I -->|No| J[7. Return final result to caller]
        H -.->|💥 Crash happens here| K[Process restarts]
        K --> B
    

    У прозе последовасць можна паказаць так:

    1. Пачынаецца графіка. Вы вызываеце graph.invoke(input, config) з адным конкрэтным thread_id.
  • Стан ствараецца або завантажваецца. Чэкпойнтар пераканальваець, чыяж тэад уже мае чэкпойнты. Якщо так, то найсвежэйшы з іх стае пачатковым станам; якщо ні, то выконанне пачынаецца з порожньага стану.
  • Вузел выконваецца на аднойчыні з станам, які ў яго быў переданы.
  • Стан апдэйтуецца. Усё, што вяртае вузел, з’едынаецца з станам.
  • Запісваецца чэкпойнт. Чэкпойнтар зберагае новы стан пад ID тэаду.
  • Выконваецца наступны вузел, і крокі 3 да 5 павтараюцца.
  • Адбываецца збой, скажамо, адразу пасля таго, як быў запісаны чэкпойнт для вузла 2, але перш чым пачаўся вузел 3.
  • Выкананне аднова пачынаецца. Прызыв графа зноў на той самый поток завантажвае пасляпэўні што успешна запісаны контрольны пункт, той, што настае пасля вузла 2, і выкананне продовжваецца з вузлам 3, а не з вузлам 1.
  • Няма неабходнасці реалізаваць окольны режым вяснавання. Дастатнека проста пракрыць граф зноў на той самый поток, каб LangGraph мог даць ведамасць, дзе продовжыць.

    Є адзін момант, працоўна дакладнасць у яму мае значэнне. Ёк хоча б запрацаваць даўжэй той процес, які быў перарваны або не выйшаў, трэба запускнуць граф з параметрам None як уводу та той самым настройкам, чыя мета — паведаміць LangGraph працаваць з захаваным станом, а не пачынаць новы процес. Падаўчы новы увод у вялікі ўжо запускаєцца процес, які будуе на захаваным стане: ключы з функцыяй агрэгаціі, такімі як спіс паведамленняў, накапліваюцца, тады як звычайныя ключы заменяюцца на новыя значэння. Можна пераглядзець, што было захавана, за дапамою graph.get_state(config) для апошньяго знімка стану, а graph.get_state_history(config) — для всей последовасці змян.

    Што самое, гэты механізм дазваляе агенту намеравана зупніцца, напрыклад, каб чакаць на рашынку чалавека, і пракраты свою дзеяльнасць за гады або дні пазней на іншай машыне, за ўмовы, што тая машына можа з’яўіцца да таго ж стойкага сховішча. Ёсць можлівасць практычна пазнакоміцца з гэтым прыемам у стацыяні «Паузаванне і пракратэнне агентаў LangGraph з выкарыстоўваннем перарываў і команд».

    Найпростейшы бэкенд: InMemorySaver

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

    Што ён і як зберагае контрольныя пункты

    InMemorySaver — это кэш-пункт, які зберагае кожны кэш-пункт у RAM чырговага процеса Python у звычным словніку ў памяці, індексаваным за ID аддзеўкі. Няма ні файлоў, ні баз дадзенаў — толькі об’ект Python.

    У першым прыкладзе выкарыстоўваецца стан з адной клучовай значэннем count і аднам вузлом, які яе збяльшае на адна. Вузел спаяны з START на END, граф скомпіляваны за дапамою InMemorySaver, і ён запускаецца на thread-1 з пачатковым значэнням count 1, што дае 2. У выкладзе це пазначаецца як JavaScript, але на самай працэ ўжо Python; таксама зазначыце, што тут прыпускаецца, што TypedDict, StateGraph, START, END і InMemorySaver былі ўжо імпортаваны ранейш:

    class StateInt(TypedDict):
        count: int
    
    def add_one(state: StateInt) -> dict:
        return {"count": state["count"] + 1}
    
    builder = StateGraph(StateInt)
    builder.add_node("add_one", add_one)
    builder.add_edge(START, "add_one")
    builder.add_edge("add_one", END)
    
    memory = InMemorySaver()
    graph = builder.compile(checkpointer=memory)
    
    config = {"configurable": {"thread_id": "thread-1"}}
    result = graph.invoke({"count": 1}, config)
    print(result)  # {'count': 2}
    

    Наступны ўрывак – это простая апісанне, якае праглытае мінімальную версію графа і поруч з яйным показвае болей рэалістычную версію:

    Two ways to write the same graph — minimal vs. real-world.
    

    Для мінімальной версіі патрэбны asyncio, а таксама клас checkpointer і калібратор графа:

    import asyncio
    from langgraph.checkpoint.memory import InMemorySaver
    from langgraph.graph import StateGraph
    

    Пасля чаго як весь стан выкарыстоўваецца звычайны int, як вузел рэгіструецца лямбда-функцыя, гэты вузел пазначаецца як тачка запуску і завершэння, а граф запускаецца асінхронна за дапамогою ainvoke. Як паказана, калькі лістынкі выконваюцца разам на адной лініі (на прыклад, вызов set_finish_point і прызначэнне InMemorySaver()), таму перад запуском іх трэба разлучыць; ўрывак таксама напісаны на Python, незважаючы на свой атрыбут:

    builder = StateGraph(int)
    builder.add_node("add_one", lambda x: x + 1)
    builder.set_entry_point("add_one")
    builder.set_finish_point("add_one")memory = InMemorySaver()
    graph = builder.compile(checkpointer=memory)config = {"configurable": {"thread_id": "thread-1"}}
    result = asyncio.run(graph.ainvoke(1, config))
    print(result)  # Output: 2
    

    Разгляд важлівых ліній:

    • InMemorySaver() стварае порожній хранальнік контрольных пунктав у памяці.
  • builder.compile(checkpointer=memory) прыяжоўвае гэты сховішча да графа, што увёльняе функцыю стойкага зберагання дадзеных.
  • config = {"configurable": {"thread_id": "thread-1"}} спаявае вызов з адной конкрэтнай розмовай.
  • graph.ainvoke(1, config) — гэта асінхронная точка входу; тут запуск пачынаецца з цэлага 1 як стану на нитку "thread-1".
  • Паўтарны вызов з тым самым thread_id працюе на адной з уже захаваных для той ніткі точак контролю, а не на порожней історыі. Памятаце пра разлік з паканальным раздзелам: у гэтым маленькім графе запуск вялічзеўся ўжо, і станам являецца адна перазапісаная значэнне, таму новы вхід проста яе заменяе, тады калі ж вхідным параметрам ўжо будзе None, запуск будзе продовжаны далей, незавершаны.

    Адрозніцы

    • Няма неабычайных налаштаванняў: не патрабуецца база дадзеных чы стороннія сервіс.
    • Вельмі шырока, адколькі няма затрымакаў дыска чы сеті.
    • Ідальна для тэстаў на елементы.
    • Зручная для навучэння чы эксперыментаў у зошыцях.

    Абмежэнні

    • Усё зникае, калі процес завершаецца. Цэя память — звычная RAM, што якраз і є проблемай, описанай у пачатку гіда.
    • Яе нельга деліць межы процэсамі чы серверамі, адколькі кожны процес мае свою память.
    • Яна небяспечная для прыменення ў рэальных умовах, адколькі звычны перзапуск стырае всі данні.

    Калі яе вжываць

    Падходячыя сферы вжывання:

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

    Калі ўсё, на чым залежаць рэальным пользователям, патрабуе контрольнага пункта, падтрымванага базай данных. У дасягледзе LangGraph чыста зазначаецца, што InMemorySaver прызначаны для дэбаггінгу і тэставання, і рекамендуецца викорыстанні надзейныя рэалізацыя, такая як PostgresSaver, для працы ў прымэтных умовах. Наступным логічным крокам є настройка такой рэалізацыі, а таксама іншых бэкендоў, як SQLite і Redis, асобістых контрольных пунктав і процэсаў затверджэння, створаных на аднойчыні interrupt() і Command; прыклад працы LangGraph на Postgres і Redis у прымэтных умовах прадставлены ў Самастойнае адключэнне сервера агента LangGraph.

    Ключовыя выводы

    • Функцыя перзберагання запісвае стан графа ў надзейныя сховішчы, таму запуск продаваецца пасля збоев, перзапускаў і дугіх чаканняў, замест таго каб пачынацца зноў.
  • Пачатак занова не толькі повільны: ён павтарае заплаты за викорыстанне LLM і можа спрычыніць паслядкі, такія як электраны чысткі або платежы.
  • Стан — это спяльныя даны, якія пераходзяць через граф, і самэе ўсё гэта зберагаецца.
  • Чекпоінт — это кадр гэтага стану пасля адзінаго супер-крока; чекпойнтар автаматычна створае, зберагае і перзавантажвае чекпоінты пасля таго, як будзе вызваны метод compile().
  • thread_id ізолюе адну розмову чыраз задачу, таму адзін скомпільаваны граф можа безбедна служыць для багоў корыстнікаў.
  • Аднова запуску, які быў перарваны, значыць вызваць той самы поток з параметрам None; новы вхід у існуючы поток запускае новы рун на адной зберажанай базе стану.
  • InMemorySaver ідеальна падходзіць для тэстаў і пратотыпаў, але так как яна знаходзіцца ў памяці процэса, у прыметных системах трэбуецца чекпойнтар, падтрымваны базай дадзенаў.