چیتشیت
ماشینهای وضعیت ایجنت و گرافهای بادوام
Agent State Machines and Durable Graphs
وضعیت، نودها، یالها، کاهندهها، چکپوینتها، وقفهها و بازیابی وظایف اجارهشده. LangGraph و یک صف وظایف Go.
فهرست مطالب
اجزای یک گراف وضعیت
مفهومها- وضعیت
- یک 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 با یک چکپوینتر
معماریوظایف گراف
کار و کد| وظیفه | کد |
|---|---|
| تنظیم ترد | {"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>) |
| ذخیرهساز Postgres | PostgresSaver.from_conn_string(<url>) |
حالتهای استریم
مقایسه| عامل | updates | messages | values |
|---|---|---|---|
| خروجی | دلتاهای وضعیت | توکنهای LLM | اسنپشاتهای کامل |
| قالب داده | {node_name: state_delta} | توکنها در نودهای مدل | کل وضعیت |
| کاربرد در | رابط کاربری | استریمینگ توکن | ارزیابی |
وقفه، تأیید، ادامهی کار
گامبهگام- کامپایلتنظیم
interrupt_before=["tools"]و یک چکپوینتر. - اجراگراف قبل از
toolsمتوقف میشود. وضعیت پایدار باقی میماند. - APIپاسخ به کاربر با «در انتظار تأیید».
- بررسیکنندهخواندن
tool_callsهای معلق ازapp.get_state(config). - تأییدارسال
Command(resume=True)روی همانthread_id. - ردثبت پیام رد با
app.update_state، سپس ادامهی کار.
گراف Stdlib: وقفه و ادامهی کار
گامبهگام- طبقهبندیتنظیم
routeبه استرداد، باگ یا فروش. - مسیریاب
add_conditional_edgesنود شاخه را بر اساسrouteانتخاب میکند. - شاخهنوشتن یک
ticketمثلBUG-.... - دروازه انسانی
human_approvalوجود ندارد، پس_pause_reasonتنظیم میشود. - اجراکنندهوضعیت را ذخیره کرده، سپس
PausedAtNodeرا پرتاب میکند. - انسان
load_latest(session)، تنظیمhuman_approvalبه True. - ادامهی کار
runner.run(..., resume_from="send", state_override=...).
وضعیتهای وظیفهی بادوام
معماریاجرای صف وظایف
دستورها| دستور | کاری که انجام میدهد |
|---|---|
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-ms | 30000 | اجارهی ادعا به میلیثانیه |
--max-attempts | 3 | وظیفه در سقف تلاشها قابل مشاهده میماند |
--max-jobs | 1 | وظایف در هر فراخوانی ورکر |
--delay-ms | 0 | محدوده 0..60000 |
--now-ms | -1 | به معنی زمان واقعی جاری است |
--store | jobs-data | دایرکتوری محلی |
Snapshot size | 4 MiB | ErrLimit در بالای آن |
عیبیابی
عیبیابیاز دست رفتن نیمی از نوبت گفتوگو
- علت
- نبود کاهندهی
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 قرار ندهید.
- برای هر تست کرش تکرارپذیر از یک ذخیرهساز جدید استفاده کنید.