Nếu bài học trước giúp bạn xây dựng những quy trình cố định, thì bài học này sẽ giúp bạn xây dựng những Agent linh hoạt. Chúng ta sẽ làm quen với hai mẫu thiết kế cấp cao: Orchestrator-Worker (Người điều phối) và Evaluator-Optimizer (Người tối ưu).
I. Orchestrator-Worker: Lập kế hoạch động
Trong mẫu thiết kế này, thay vì định nghĩa sẵn 3 hay 4 node chạy song song, chúng ta để một Node gọi là Orchestrator tự quyết định cần bao nhiêu bước.
- Orchestrator: Nhận yêu cầu và tạo ra một bản kế hoạch (ví dụ: chia bài báo thành 5 chương).
- Workers: LangGraph sẽ thực thi các tác vụ con một cách độc lập (thường sử dụng API
Sendđể sinh node động). - Synthesizer: Thu thập kết quả từ tất cả các worker để gộp thành kết quả cuối cùng.
II. Evaluator-Optimizer: Vòng lặp tự sửa lỗi
Đây là mẫu thiết kế "Writer - Editor" (Người viết - Biên tập viên). LLM đôi khi không tạo ra kết quả tốt ngay lần đầu, vì vậy chúng ta tạo ra một vòng lặp phản hồi:
- Generator (Người viết): Tạo ra kết quả ban đầu dựa trên yêu cầu.
- Evaluator (Người duyệt): Kiểm tra kết quả dựa trên các tiêu chí cụ thể (ví dụ: đúng định dạng, đủ ý, giọng văn phù hợp).
- Loop: Nếu chưa đạt, Evaluator gửi phản hồi ngược lại cho Generator để sửa lại. Quá trình này lặp lại cho đến khi đạt yêu cầu hoặc chạm giới hạn số lần thử (iterations).
III. Mã nguồn: 08-patterns-orchestrator-optimizer.ts
Ví dụ dưới đây minh họa mẫu Evaluator-Optimizer sử dụng Gemini model. Agent sẽ cố gắng viết một bài thơ hài hước và bắt buộc phải có từ "robot".
// path: 08-patterns-orchestrator-optimizer.ts
import { END, START, StateGraph } from '@langchain/langgraph';
import { createGeminiModel, generateImage } from '@workspace/util-langchain';
import z from 'zod';
// 1. Define Evaluation structure for structured output
const EvaluationSchema = z.object({
grade: z.enum(['accepted', 'rejected']),
feedback: z
.string()
.describe('Reason for rejection or suggestions for revision'),
});
// 2. Define State
const StateDefinition = z.object({
topic: z.string(),
content: z.string().optional(),
feedback: z.string().optional(),
iterations: z.number().default(0),
status: z.string().optional(),
});
type State = z.infer<typeof StateDefinition>;
const llm = createGeminiModel();
// 3. Logic nodes
async function generator(state: State) {
console.log(`--- NODE: Generator (Attempt ${state.iterations + 1}) ---`);
const prompt = state.feedback
? `Revise the poem about ${state.topic} based on the feedback: ${state.feedback}. Previous version: ${state.content}`
: `Write a short 4-line poem about ${state.topic}.`;
const response = await llm.invoke(prompt);
return {
content: response.content as string,
iterations: state.iterations + 1,
};
}
async function evaluator(state: State) {
console.log('--- NODE: Evaluator (Reviewing) ---');
// Use structured output to force LLM to return valid JSON
const critic = llm.withStructuredOutput(EvaluationSchema);
const result = await critic.invoke(
`Evaluate this poem: ${state.content}.
Criteria: The poem MUST be humorous and MUST contain the word 'robot'.
If it does not meet the criteria, reject it and provide feedback.`,
);
return { status: result.grade, feedback: result.feedback };
}
// 4. Router to control the loop
function controlLoop(state: State) {
// Terminate if accepted or if we hit the limit of 3 tries
if (state.status === 'accepted' || state.iterations >= 3) {
return END;
}
return 'generator'; // Back to revision
}
// 5. Build Graph
export const optimizerGraph = new StateGraph(StateDefinition)
.addNode('generator', generator)
.addNode('evaluator', evaluator)
.addEdge(START, 'generator')
.addEdge('generator', 'evaluator')
.addConditionalEdges('evaluator', controlLoop, {
generator: 'generator',
[END]: END,
})
.compile();
// 6. Execution
async function run() {
await generateImage(
optimizerGraph,
'graph-ignore/scripts-08-patterns-orchestrator-optimizer.jpg',
);
const result = await optimizerGraph.invoke({ topic: 'fast food' });
console.log('\n=== FINAL RESULT ===\n', result.content);
console.log('Number of revisions:', result.iterations);
}
run();IV. Hình ảnh sơ đồ Graph
Mẫu thiết kế này tạo ra một vòng lặp giữa Node Generator và Evaluator cho đến khi thỏa mãn điều kiện:

V. Các điểm mấu chốt (Takeaways)
- Đảm bảo chất lượng (Self-Correction): Thay vì hy vọng LLM ra kết quả đúng ngay lập tức, bạn xây dựng một hệ thống kiểm soát chất lượng tự động.
- Sử dụng linh hoạt Model: Bạn có thể dùng model nhỏ/rẻ để viết (Generator) và model mạnh hơn để chấm điểm (Evaluator) để tối ưu chi phí.
- Giới hạn vòng lặp (Safety Break): Luôn cần đặt một giới hạn
iterations(ví dụ: tối đa 3 lần) trong hàm Router để tránh việc Agent bị lặp vô tận và làm tiêu tốn ngân sách API.
Chúc mừng bạn đã làm chủ được các luồng xử lý dữ liệu phức tạp nhất! Ở bài sau, chúng ta sẽ bắt đầu tìm hiểu về Persistence, nền tảng kỹ thuật giúp Agent có thể ghi nhớ và lưu trữ trạng thái lâu dài.