چیت‌شیت

ماشین‌های وضعیت ایجنت و گراف‌های بادوام

Agent State Machines and Durable Graphs

وضعیت، نودها، یال‌ها، کاهنده‌ها، چک‌پوینت‌ها، وقفه‌ها و بازیابی وظایف اجاره‌شده. LangGraph و یک صف وظایف Go.

  • ۱۳ بخش
  • ۲ درس مرتبط
  • کد و دستورها عیناً از دوره‌ی اصلی
فهرست مطالب
  1. اجزای یک گراف وضعیت
  2. گراف چهار نودی ReAct
  3. حلقه‌ی ReAct با یک چک‌پوینتر
  4. وظایف گراف
  5. حالت‌های استریم
  6. وقفه، تأیید، ادامه‌ی کار
  7. گراف Stdlib: وقفه و ادامه‌ی کار
  8. وضعیت‌های وظیفه‌ی بادوام
  9. اجرای صف وظایف
  10. پیش‌فرض‌های ورکر
  11. عیب‌یابی
  12. پرسش و پاسخ
  13. نکات

اجزای یک گراف وضعیت

مفهوم‌ها
وضعیت
یک TypedDict که در گراف جریان دارد. هر نود وضعیت کامل را دریافت می‌کند.
نود
یک تابع state -> partial_state. یک گام گسسته.
یال
یال‌های ایستا به یک نود می‌روند. یال‌های شرطی یک تابع مسیریاب را فراخوانی می‌کنند.
کاهنده
(old, new) -> merged برای هر فیلد. حالت پیش‌فرض رونویسی است.
ترد
یک رشته‌ی thread_id که دامنه‌ی تمام چک‌پوینت‌های یک نشست را مشخص می‌کند.
چک‌پوینت
وضعیت کامل پس از اجرای یک نود، با کلید (thread_id, checkpoint_id).
ارسال
یک نود Send(node_name, state) را برمی‌گرداند تا نودها را موازی اجرا کند.
زیرگراف
یک گراف کامپایل‌شده که به‌عنوان یک نود در گرافی دیگر استفاده می‌شود.

گراف چهار نودی ReAct

کد
python
class State(TypedDict):
    messages: Annotated[list[AnyMessage], add_messages]  # الحاق، نه رونویسی
def should_continue(state: State) -> str:
    last = state["messages"][-1]
    return "tools" if getattr(last, "tool_calls", None) else END
graph = StateGraph(State)
graph.add_node("agent", agent_node)                      # فراخوانی LLM
graph.add_node("tools", ToolNode(TOOLS))                 # اجرای tool_calls
graph.set_entry_point("agent")
graph.add_conditional_edges("agent", should_continue, {"tools": "tools", END: END})
graph.add_edge("tools", "agent")                         # یال ایستای برگشت
app = graph.compile(checkpointer=MemorySaver(),
                    interrupt_before=["tools"])          # وقفه قبل از اثرات جانبی

حلقه‌ی ReAct با یک چک‌پوینتر

معماری
پیام‌هاtool_callsنتایج ابزارپایانذخیرهکاربرthread_idagentفراخوانی LLMtoolsToolNodeENDبدون tool_callsچک‌پوینتروضعیت بعد از هر نود

وظایف گراف

کار و کد
وظیفهکد
تنظیم ترد{"configurable": {"thread_id": "<id>"}}
استریم دلتاهاapp.stream(<state>, config, stream_mode="updates")
خواندن وضعیت متوقف‌شدهapp.get_state(config)
تأیید و ادامه‌ی کارapp.invoke(Command(resume=True), config)
رد کردن همراه با پیامapp.update_state(config, {"messages": [...]})
فهرست کردن چک‌پوینت‌هاlist(app.get_state_history(config))
بازپخش از یک چک‌پوینتapp.stream(None, <old_config>)
ذخیره‌ساز PostgresPostgresSaver.from_conn_string(<url>)

حالت‌های استریم

مقایسه
عاملupdatesmessagesvalues
خروجیدلتاهای وضعیتتوکن‌های LLMاسنپ‌شات‌های کامل
قالب داده{node_name: state_delta}توکن‌ها در نودهای مدلکل وضعیت
کاربرد دررابط کاربریاستریمینگ توکنارزیابی

وقفه، تأیید، ادامه‌ی کار

گام‌به‌گام
  1. کامپایلتنظیم interrupt_before=["tools"] و یک چک‌پوینتر.
  2. اجراگراف قبل از tools متوقف می‌شود. وضعیت پایدار باقی می‌ماند.
  3. APIپاسخ به کاربر با «در انتظار تأیید».
  4. بررسی‌کنندهخواندن tool_callsهای معلق از app.get_state(config).
  5. تأییدارسال Command(resume=True) روی همان thread_id.
  6. ردثبت پیام رد با app.update_state، سپس ادامه‌ی کار.

گراف Stdlib: وقفه و ادامه‌ی کار

گام‌به‌گام
  1. طبقه‌بندیتنظیم route به استرداد، باگ یا فروش.
  2. مسیریابadd_conditional_edges نود شاخه را بر اساس route انتخاب می‌کند.
  3. شاخهنوشتن یک ticket مثل BUG-....
  4. دروازه انسانیhuman_approval وجود ندارد، پس _pause_reason تنظیم می‌شود.
  5. اجراکنندهوضعیت را ذخیره کرده، سپس PausedAtNode را پرتاب می‌کند.
  6. انسانload_latest(session)، تنظیم human_approval به True.
  7. ادامه‌ی کارrunner.run(..., resume_from="send", state_override=...).

وضعیت‌های وظیفه‌ی بادوام

معماری
ClaimJobFinishReclaimسقفNewJobنسخه‌ی 0در صفدر انتظار ادعادر حال اجراLeaseUntil = now + ttlتکمیل‌شدهLeaseUntil 0ErrLimitتعداد تلاش‌ها در سقف

اجرای صف وظایف

دستورها
دستورکاری که انجام می‌دهد
go build -o /tmp/durable-jobs .ورکر مرجع را بیلد می‌کند
enqueue --store "$STORE" --input samples/jobs.jsonرکوردهای id/text اضافه می‌کند
worker --store "$STORE" --max-jobs 100وظایف را ادعا کرده و اجرا می‌کند
status --store "$STORE"وظایف، اثرات و دامنه را نمایش می‌دهد
worker ... --crash-after effectخروج با کد 86 پس از رسید
worker ... --now-ms 111 --lease-ms 10ادعای منقضی‌شده را بازیابی می‌کند
--crash-after claimکرش پیش از ایجاد هرگونه خروجی
go run . demoتوالی خودکار کرش اثرات جانبی

پیش‌فرض‌های ورکر

پیش‌فرض‌ها
تنظیمپیش‌فرضیادداشت
--lease-ms30000اجاره‌ی ادعا به میلی‌ثانیه
--max-attempts3وظیفه در سقف تلاش‌ها قابل مشاهده می‌ماند
--max-jobs1وظایف در هر فراخوانی ورکر
--delay-ms0محدوده 0..60000
--now-ms-1به معنی زمان واقعی جاری است
--storejobs-dataدایرکتوری محلی
Snapshot size4 MiBErrLimit در بالای آن

عیب‌یابی

عیب‌یابی
  • از دست رفتن نیمی از نوبت گفت‌وگو

    علت
    نبود کاهنده‌ی add_messages
    راه‌حل
    Annotated[list, add_messages]
  • نبود ادامه‌ی کار یا سفر در زمان

    علت
    نبود چک‌پوینتر
    راه‌حل
    MemorySaver در تست‌ها، ذخیره‌ساز واقعی در محیط پروداکشن
  • آسیب قبل از تأیید

    علت
    وقفه پس از اثر جانبی
    راه‌حل
    وقفه قبل از نود دارای اثر جانبی
  • از دست رفتن وضعیت ابزار در ادامه‌ی کار

    علت
    چک‌پوینت‌ها بسیار کوچک هستند
    راه‌حل
    سریالایز کردن کل وضعیت
  • نتیجه‌ی جدید در ادامه‌ی کار

    علت
    نود غیرقطعی
    راه‌حل
    ثبت سیدها، ساعت و پاسخ‌های API
  • شکست ورکر قدیمی در تکمیل کار

    علت
    تغییر نسخه یا اجاره
    راه‌حل
    مورد انتظار است. Finish ورکرهای قدیمی را محصور می‌کند
  • خطای Enqueue conflict

    علت
    تغییر متن تحت شناسه قدیمی
    راه‌حل
    استفاده از شناسه‌ی جدید
  • کد خروج 86

    علت
    تنظیم --crash-after
    راه‌حل
    مورد انتظار است. ورکر را دوباره اجرا کنید

پرسش و پاسخ

پرسش و پاسخ
ورودی None چه کاری انجام می‌دهد؟
از چک‌پوینت داده‌شده بازپخش می‌کند.
چرا v4 بعد از یک کرش تکمیل شد؟
ادعا، ادعای مجدد، ادعا و تکمیل هر کدام 1 واحد اضافه می‌کنند.
آیا اجرا دقیقاً یک‌بار (exactly-once) است؟
خیر. حداقل یک‌بار (at-least-once) است.
چرا رسید تکراری وجود ندارد؟
رسید محلی تغییرناپذیر اثر را بی‌اثر در تکرار می‌کند.
چه زمانی Reclaim فعال می‌شود؟
روی وظیفه‌ی در حال اجرا وقتی now به LeaseUntil رسیده یا گذشته باشد.
کدام سه توپولوژی؟
ناظر، ازدحام و سلسله‌مراتبی.

نکات

نکته‌ها
  • ابتدا نودها را نام‌گذاری کنید. هر تصمیم یا اثر جانبی یک نود است.
  • فیلدهای وظیفه مثل plan و budget را از messages بیرون بکشید.
  • زنجیره‌های خطی با انشعاب‌های گاه‌به‌گاه را ترجیح دهید.
  • تأییدیه‌ها را روی یال ورودی به نود دارای اثر جانبی بگذارید.
  • ذخیره‌ساز وظایف را روی NFS قرار ندهید.
  • برای هر تست کرش تکرارپذیر از یک ذخیره‌ساز جدید استفاده کنید.