Trong các bài học trước, chúng ta thường viết một System Prompt cố định cho Agent. Tuy nhiên, trong thực tế sản xuất (Production), một Agent cần phải linh hoạt: phục vụ Giám đốc khác với phục vụ Nhân viên mới. Bài học này sẽ hướng dẫn bạn kỹ thuật Context Engineering để tự động biến đổi Agent dựa trên ngữ cảnh người dùng, sử dụng hệ thống middleware và tiện ích chuyên nghiệp từ Bài 1.
I. Tại sao cần Ngữ cảnh động?
Hãy tưởng tượng bạn xây dựng một trợ lý nội bộ cho công ty:
- Nếu là Admin: Agent có quyền truy cập công cụ "Xem doanh thu", trả lời ngắn gọn, quyết đoán.
- Nếu là Khách (Guest): Agent chỉ được xem "Tài liệu công khai", trả lời chi tiết, thân thiện và không được biết về các dữ liệu mật.
Thay vì tạo ra nhiều Agent riêng biệt, chúng ta sẽ tạo một Agent duy nhất có khả năng tự điều chỉnh danh sách công cụ và tính cách dựa trên thông tin định danh (Runtime Context).
II. Cơ chế Runtime Context và Middleware
LangChain cho phép bạn truyền một đối tượng context tùy chỉnh khi gọi hàm invoke. Middleware sẽ chặn yêu cầu này, đọc thông tin từ context và sửa đổi cấu hình Agent (Tools, System Message) ngay trước khi gửi dữ liệu tới bộ não AI.
III. Mã nguồn: 16-dynamic-context-engineering.ts
Chúng ta sử dụng createMiddleware để can thiệp vào luồng xử lý và createGeminiModel để khởi tạo Gemini 2.5 Flash.
// apps/langchain/scripts/16-dynamic-context-engineering.ts
// pnpm --filter=ai-notes-langchain run tsx scripts/16-dynamic-context-engineering.ts
import './env';
import { createGeminiModel, generateImage } from '@workspace/util-langchain';
import { createAgent, tool, createMiddleware } from 'langchain';
import { z } from 'zod';
// 1. Define tools with different permission levels
const readDocs = tool(
async () => 'Company Policy: Office hours are from 9 AM to 6 PM.',
{
name: 'read_public_documents',
description: 'Read general company documentation.',
schema: z.object({}),
},
);
const secretTool = tool(
async () => 'CONFIDENTIAL: Revenue increased by 200% this quarter.',
{
name: 'access_secret_data',
description: 'Access highly confidential company financial data.',
schema: z.object({}),
},
);
// 2. Define Middleware for Dynamic Context Engineering
const roleBasedSecurityMiddleware = createMiddleware({
name: 'RoleBasedSecurity',
wrapModelCall: async (request, handler) => {
// Extract role from the runtime context
const role = request.runtime.context?.userRole || 'guest';
let activeTools = [readDocs]; // Everyone can read docs
let customPrompt = 'You are a helpful office assistant.';
// If admin, grant extra tool and change persona
if (role === 'admin') {
activeTools.push(secretTool);
customPrompt =
'You are a Senior Executive Assistant. Be professional, concise, and direct.';
}
console.log(`[SYSTEM]: Configuring Agent for role: ${role.toUpperCase()}`);
// Inject dynamic changes into the request before it reaches the AI
return handler({
...request,
tools: activeTools,
systemMessage: request.systemMessage.concat(
`\n[CURRENT CONTEXT]: ${customPrompt}`,
),
});
},
});
async function main() {
const model = createGeminiModel();
const agent = createAgent({
model,
// We declare all possible tools here; the middleware will filter them
tools: [readDocs, secretTool],
middleware: [roleBasedSecurityMiddleware],
});
// Generate visual diagram
await generateImage(
agent,
'images/courses/langchain-course/scripts-16-dynamic-context.jpg',
);
// --- TEST 1: GUEST ROLE ---
console.log('\n--- SCENARIO 1: Guest User asking about secrets ---');
const res1 = await agent.invoke(
{ messages: [{ role: 'user', content: 'What is our company revenue?' }] },
{ context: { userRole: 'guest' } }, // Passing runtime context
);
console.log('AI Response (Guest):', res1.messages.at(-1).content);
// --- TEST 2: ADMIN ROLE ---
console.log('\n--- SCENARIO 2: Admin User asking about secrets ---');
const res2 = await agent.invoke(
{ messages: [{ role: 'user', content: 'Give me the revenue report.' }] },
{ context: { userRole: 'admin' } },
);
console.log('AI Response (Admin):', res2.messages.at(-1).content);
}
main().catch(console.error);IV. Phân tích kỹ thuật
- Bảo mật thực thi: Trong Test 1, AI thậm chí không biết sự tồn tại của
access_secret_data. Middleware đã loại bỏ nó hoàn toàn khỏi danh sách công cụ gửi đi. Điều này an toàn hơn nhiều so với việc chỉ dặn AI bằng câu chữ "đừng dùng tool này nhé". - Tiết kiệm chi phí: Bạn chỉ nạp các công cụ và chỉ dẫn thực sự cần thiết cho từng phiên làm việc, giúp tiết kiệm Token và giữ cho AI tập trung tối đa vào nhiệm vụ.
- Runtime Context: Đây là nơi chứa các biến "ngoại cảnh" không nằm trong lịch sử chat, giúp tách biệt giữa dữ liệu người dùng gửi và dữ liệu hệ thống xác thực.
V. Hình ảnh sơ đồ Graph
Kỹ thuật này cho phép một cấu trúc Graph duy nhất nhưng có khả năng thay đổi hành vi động:

VI. Tổng kết
- Context Engineering là đỉnh cao của việc tối ưu hóa Agent, giúp chuyển từ các Prompt tĩnh sang các hệ thống Agent thích nghi.
- Sử dụng Middleware để lọc Tools và Prompt dựa trên quyền hạn là phương pháp bảo mật tốt nhất.
- Hệ thống
util-langchainvàenv.tstiếp tục đóng vai trò nền tảng để triển khai các kỹ thuật nâng cao này một cách ổn định.
Trong bài học tiếp theo, chúng ta sẽ học về Store — cách giúp Agent có một "cuốn sổ tay" để lưu lại thông tin người dùng vĩnh viễn, kể cả khi họ quay lại sau nhiều tháng!