3 Penulisan Semula LangGraph: Apa yang Diajar oleh Checkpointing Produksi kepada Saya
Selama tiga minggu, ejen onboarding kami kehilangan setiap tugasan yang sepatutnya disimpan. Log tidak menunjukkan apa-apa. Tiada ralat. Tiada amaran. Checkpointer melaporkan kejayaan, tetapi state hilang apabila disambung semula.
Saya menulis semula pipeline LangGraph yang sama sebanyak tiga kali sebelum saya memahami kebenarannya. Framework tersebut melakukan tepat apa yang saya arahkan. Saya hanya memberikan arahan yang salah.
Jika anda membina stateful agents untuk produksi, jangan anggap tetapan lalai (defaults) akan menyelamatkan anda. Berikut adalah apa yang rosak dan bagaimana saya memperbaikinya.
The First Break: Kehilangan State Secara Senyap
Ejen saya mengendalikan aliran onboarding lima langkah. Saya menggunakan Postgres untuk menyimpan kemajuan supaya pengguna boleh menyambung semula kemudian. Tetapi setiap kali disambung semula, ia bermula dari langkah pertama.
Puncanya adalah skema state saya. Dalam LangGraph, setiap nod mengembalikan kemas kini yang digabungkan ke dalam state. Jika anda tidak menyatakan cara untuk menggabungkannya, tetapan lalainya adalah untuk menulis semula (overwrite).
Saya menyangka senarai mesej saya akan ditambah (append). Sebaliknya, setiap nod baharu menggantikan keseluruhan sejarah dengan hanya satu mesej. Checkpoint tersebut menyimpan data yang salah dengan sempurna.
Penyelesaiannya: Gunakan medan Annotated dengan reducer yang eksplisit.
• Gunakan operator add untuk senarai mesej.
• Gunakan fungsi merge tersuai untuk dictionary.
• Gunakan overwrite lalai hanya untuk nilai tunggal seperti "step".
The Second Break: Deserialization dan Concurrency
Penulisan semula kedua menghadapi dua isu baharu:
- Baris yang rosak (Corrupt rows): Saya menyimpan objek tersuai dalam state. Serializer tidak dapat mengendalikannya. Ini menghasilkan baris yang wujud tetapi tidak boleh digunakan.
- Kunci pendua (Duplicate keys): WhatsApp akan mencuba semula webhook jika anda tidak bertindak balas dengan cepat. Jika dua mesej tiba serentak, dua larian graf cuba menulis ke thread yang sama. Ini menyebabkan perlanggaran pangkalan data (database collisions).
Penyelesaiannya: • Buang objek tersuai. Gunakan dict biasa dan jenis LangChain standard sahaja. • Kendalikan webhook di luar graf. Gunakan queue dan kunci idempotensi untuk membuang pendua. • Tambah database lock. Pastikan hanya satu larian berlaku bagi setiap thread pada satu-satu masa.
The Third Rewrite: Corak yang Stabil
Versi akhir memfokuskan kepada tiga prinsip:
• Small graphs: I broke one massive graph into three smaller subgraphs. This reduced the blast radius of bugs. • Explicit checkpoints: I stopped checkpointing after every single node. I only checkpoint at meaningful resume points. This cut database writes by 60%. • Idempotent nodes: This is vital. Every node must produce the same result if it runs twice. If a node sees that a task is already done in the state, it should return immediately. This prevents double-charging for expensive model calls.
𝗟𝗲𝘀𝘀𝗼𝗻𝘀 𝗳𝗼𝗿 𝘆𝗼𝘂:
- Read reducer semantics before you write code.
- Do not store custom objects in state.
- Move concurrency control outside the graph.
- Make every node idempotent.
The framework did not fail. My assumptions did.
Optional learning community: https://t.me/GyaanSetuAi