Trong bài học trước, chúng ta đã xây dựng một graph đơn giản chỉ với một Node duy nhất. Tuy nhiên, sức mạnh thực sự của LangGraph nằm ở khả năng kết nối nhiều Node lại với nhau để tạo thành một quy trình làm việc hoàn chỉnh. Thành phần đảm nhận vai trò này chính là Edges (Cạnh).
I. Khái niệm về Edges (Cạnh)
Nếu Nodes là các "nhân viên" thực hiện công việc, thì Edges chính là các "sợi dây" kết nối. Chúng định nghĩa Control Flow (luồng điều khiển) của ứng dụng.
Có một quy tắc vàng cần nhớ: Edges truyền quyền điều khiển, không truyền dữ liệu.
- Dữ liệu luôn nằm trong State dùng chung và được lưu trữ bởi runtime.
- Edge chỉ đơn giản bảo LangGraph rằng: "Sau khi Node A xong, hãy chuyển State này cho Node B xử lý tiếp".
II. Luồng thực thi tuần tự (Serial Flow)
Đây là dạng cơ bản nhất của Edge, nơi các Node chạy lần lượt theo một thứ tự xác định (A -> B -> C). Mỗi Node sẽ nhận State từ Node trước đó, thực hiện thay đổi và chuyển tiếp kết quả.
III. Mã nguồn: 03-edges-control-flow.ts
Dưới đây là mã nguồn minh họa một quy trình gồm 2 bước: Bước 1 nhận dữ liệu, Bước 2 xử lý thêm dữ liệu đó.
// path: 03-edges-control-flow.ts
import { StateGraph, START, END } from '@langchain/langgraph';
import { generateImage } from '@workspace/util-langchain';
import z from 'zod';
// 1. Define State
const StateDefinition = z.object({
nlist: z.array(z.string()),
});
type State = z.infer<typeof StateDefinition>;
// 2. Define Nodes
function step1(state: State) {
console.log('--- Executing Step 1 ---');
// Add a new string to the array
return { nlist: [...state.nlist, 'Data from Step 1'] };
}
function step2(state: State) {
console.log('--- Executing Step 2 ---');
// step2 sees the changes made by step1 in the state
return { nlist: [...state.nlist, 'Data from Step 2'] };
}
// 3. Build Graph with Serial Edges
export const serialGraph = new StateGraph(StateDefinition)
.addNode('node1', step1)
.addNode('node2', step2)
// Define the navigation flow
.addEdge(START, 'node1') // Start -> node1
.addEdge('node1', 'node2') // node1 -> node2
.addEdge('node2', END) // node2 -> End
.compile();
// 4. Execution
async function run() {
// Generate diagram
await generateImage(serialGraph, 'graph-ignore/scripts-03-serial-flow.jpg');
console.log('\n=== L3: Edges and Control Flow Example ===\n');
const initialState: State = { nlist: ['Initial Value'] };
const result = await serialGraph.invoke(initialState);
console.log('\n--- FINAL RESULT ---');
console.log(result.nlist);
// Expected: ["Initial Value", "Data from Step 1", "Data from Step 2"]
}
run();IV. Giải thích mục đích của Code
- Tính liên tục: Bạn có thể thấy
step2không hề nhận tham số trực tiếp từstep1. Nó nhận toàn bộstatetừ runtime. Vìstep1đã cập nhật mảngnlist, nênstep2sẽ nhìn thấy những thay đổi đó. - Định danh Node: Khi dùng
.addNode('node1', step1), chúng ta đặt tên định danh là'node1'. Tên này được dùng trong hàm.addEdge()để thiết lập đường đi. - Tách biệt logic: Mỗi Node chỉ tập trung vào một nhiệm vụ nhỏ. Điều này giúp code dễ đọc, dễ bảo trì và dễ kiểm thử.
Sơ đồ hoạt động:

V. Thử nghiệm: Lab 02
Để hiểu rõ hơn về cách luồng di chuyển:
- Tạo file
03-edges-control-flow.tsvà chạy bằng lệnhtsx. - Thử thêm một nút thứ 3 (
node3) vào quy trình:- Viết thêm hàm
step3. - Dùng
.addNodeđể thêm vào graph. - Sửa lại các Edge:
node1->node2->node3->END.
- Viết thêm hàm
- Chạy lại và quan sát mảng kết quả cuối cùng.
VI. Tổng kết
- Edges điều phối thứ tự thực hiện công việc giữa các nút.
- Trong luồng Tuần tự, các nút xếp hàng chờ đến lượt mình xử lý State.
- Việc chia nhỏ quy trình thành nhiều Node giúp hệ thống minh bạch và dễ kiểm soát lỗi.
Ở bài học tiếp theo, chúng ta sẽ học cách tối ưu tốc độ bằng cách cho nhiều Node chạy cùng một lúc với Parallel Execution và cách xử lý xung đột dữ liệu bằng Reducers!