Tôi đưa LangGraph vào với kỳ vọng rằng "quản lý trạng thái và điều khiển luồng sẽ gọn gàng hơn", nhưng ngược lại code lại trở nên phức tạp hơn.
Đây là bản ghi chép và chia sẻ về trải nghiệm tự mình chứng kiến rồi tổng kết rằng ngay cả khi đưa framework vào, cấu trúc vẫn có thể tệ đi.
Khi chẩn đoán lại thì tình trạng lúc đó như sau:
- Trong graph chỉ có đúng một node. Cấu trúc là
current_step → ENDnên không có conditional edge hay nhánh rẽ nào, vàgraph.invoke()cũng chẳng khác gì gọi trực tiếp một hàm. Việc quyết định chuyển Step lại do code dịch vụ bên ngoài graph đảm nhiệm. - Cùng một trạng thái được lưu ở hai nơi.
MemorySavercủa LangGraph (in-memory) và Checkpointer PostgreSQL tự xây đều lưu/khôi phục cùng một session theo kiểu kép, dẫn đến phát sinh sai lệch. - Có sự trùng lặp trong 3 đoạn code thực thi LangGraph (Executor) dài khoảng hơn 8.700 dòng. Logic cốt lõi thực chất chỉ là "gọi LLM + lắp ráp prompt", còn phần lớn còn lại là quản lý trạng thái, rẽ nhánh điều kiện và vá edge case.
Nhìn bề ngoài thì vấn đề là đã đưa framework vào chỉ như một lớp vỏ mà không dùng các tính năng thực sự của nó (conditional edge, checkpointer tích hợp sẵn, Human-in-the-Loop), nhưng khi đào sâu hơn thì nguyên nhân lại nằm ở chỗ khác.
- Phần lớn được tạo ra bằng vibe coding, nhưng trong quá trình xây dựng lại không thực sự xem xét sâu code nội bộ hay các nguyên tắc thiết kế, mà chỉ liên tục sắp xếp yêu cầu và ý đồ rồi tiếp tục triển khai.
- Vấn đề không nằm ở bản thân vibe coding, mà ở việc triển khai không qua kiểm chứng. Cách đó chỉ tối ưu chức năng trước mắt chứ không giữ được ranh giới trách nhiệm của toàn bộ cấu trúc. Checkpoint kép, trạng thái phân tán, logic trùng lặp — tất cả đều là kết quả của việc tích lũy các tối ưu cục bộ.
- Không phải là không có người thiết kế, mà nguyên nhân thật sự là ra chỉ thị ở bên trên một framework khi chưa hiểu đủ framework đó được thiết kế để chịu trách nhiệm cho điều gì.
Vì vậy tôi đã nhìn lại và đưa nó trở về như sau.
- Tách session bằng topology độc lập + cô lập
thread_id - Trong State chỉ giữ metadata, còn phần nội dung chính chuyển sang Store
- Dùng
tool_usenative thay cho phân tích cú pháp bằng regex - Tách node để có thể kiểm thử
- Chính bản thân tôi cần hiểu chính xác framework và cấu trúc cách vận dụng nó
Đây không phải là ghi chép/chia sẻ về "dùng như thế nào", mà là về "đã dùng sai như thế nào".
2 bình luận
Có vẻ mọi trường hợp sử dụng LangChain LangGraph đều có thể được thay thế bằng AI SDK.
Đúng vậy. Mình rất đồng cảm. Dù sẽ phát sinh phụ thuộc vào SDK, nhưng vì độ khó làm quen khá cao nên có lẽ như vậy còn tốt hơn.