Prompt của bạn có thể đúng ngữ pháp, đúng ví dụ, đúng few-shot, và model vẫn trả lời sai. Nguyên nhân thường không nằm ở câu chữ mà ở thứ được đưa vào ngữ cảnh: một tài liệu cũ, một tool schema thừa, một ký ức sai từ lượt trước, hoặc đơn giản là quá nhiều thứ đến mức chỉ dẫn quan trọng bị chôn giữa khối văn bản.
Context engineering là việc thiết kế toàn bộ ngữ cảnh đó — nguồn nào được lấy, đặt ở đâu, giữ bao lâu, cắt khi nào, và cô lập ra sao. Bài này đi từ mô hình tư duy đến một ngân sách context cụ thể và checklist trước khi ship.
Mục lục
Cập nhật 2026: context engineering đã thành một tầng riêng
- Roadmap AI Engineer 2026 tách context engineering thành nhóm topic riêng: context window, context sources, compaction, isolation, failure modes, security, evaluation, long-context processing.
- Cửa sổ ngữ cảnh của các model flagship đã lên khoảng 1 triệu token, nhưng giá vẫn tính theo token và vượt 272K trên OpenAI bị phụ phí. Context rẻ hơn không có nghĩa là nên nhồi nhiều hơn.
- Prompt caching buộc bạn giữ prefix byte-stable. Anthropic yêu cầu prefix tối thiểu 512 token mới cache được; OpenAI đọc cache khoảng 5–10% giá gốc nhưng ghi cache vẫn tính tiền.
- Agent nhiều bước làm lộ failure mode mới: context poisoning, distraction, clash và bloat. Chúng không xuất hiện trong một lượt chat, chỉ xuất hiện sau vài vòng tool.
Context engineering và prompt engineering khác nhau ở đâu
Prompt engineering là cách diễn đạt yêu cầu: chọn từ, đặt ví dụ, định nghĩa output. Context engineering là cách lắp ráp mọi thứ model thấy tại thời điểm gọi: instructions, tool definitions, tài liệu truy xuất, memory, lịch sử hội thoại, trạng thái runtime, và cả thứ tự của chúng. Prompt là một section trong context, không phải toàn bộ.
Hệ quả thực tế: tối ưu prompt mà không nhìn context thường chỉ dịch chuyển lỗi. Bạn thêm một câu “hãy trả lời dựa trên tài liệu” nhưng tài liệu truy xuất đã sai phiên bản; bạn thêm few-shot nhưng lịch sử 20 lượt trước mâu thuẫn với few-shot. Model không có cách nào biết cái nào đúng — nó chỉ thấy một khối văn bản.
Sáu nguồn context và rủi ro của từng nguồn
| Nguồn | Nên chứa gì | Rủi ro chính |
|---|---|---|
| Instructions | Vai trò, policy, định dạng output, giới hạn quyền | Dài dòng, mâu thuẫn nội bộ, chứa secret |
| Tool schemas | Tool thật sự cần cho tác vụ hiện tại | Quá nhiều tool, mô tả mơ hồ, schema lệch thực tế |
| Retrieval | Đoạn tài liệu liên quan, có nguồn và ngày | Tài liệu cũ, trùng lặp, chứa chỉ dẫn độc hại |
| Memory | Sở thích, quyết định, ràng buộc đã xác nhận | Ký ức sai được lưu vĩnh viễn, nhiễm độc về sau |
| User history | Lượt gần đây, trạng thái tác vụ đang mở | Phình token, thông tin cũ không còn đúng |
| Runtime state | ID, trạng thái đơn, kết quả tool vừa chạy | Dữ liệu nhạy cảm, format không ổn định |
Mỗi nguồn cần một chính sách riêng: ai được ghi, ai được đọc, TTL bao lâu, có được đưa vào prompt của tenant khác không. Nếu bạn không trả lời được bốn câu đó cho memory và retrieval, bạn chưa có context layer — bạn có một hàm nối chuỗi.
Thứ tự và prefix ổn định: nơi tiền được tiết kiệm
Model đọc tuần tự, nhưng cache thì không. Prompt caching so khớp prefix từ đầu request; một byte đổi ở vị trí đầu làm phần sau mất cache. Vì vậy thứ tự chuẩn là: instructions và policy trước, tool definitions tiếp, memory dài hạn sau, rồi tài liệu truy xuất, lịch sử hội thoại, và câu hỏi hiện tại ở cuối.
Ba thói quen phá cache thường gặp: chèn datetime.now() vào system prompt; serialize tool schema với thứ tự key ngẫu nhiên; đưa ID phiên hoặc tên người dùng lên đầu instructions. Tất cả đều làm prefix đổi mỗi request. Hãy đặt phần biến động xuống cuối và giữ phần tĩnh byte-identical, kể cả khoảng trắng.
# File: app/context.py
SECTIONS = ["policy", "tools", "memory", "retrieved", "history", "question"]
def assemble(sections: dict[str, str]) -> str:
missing = [name for name in SECTIONS if name not in sections]
if missing:
raise ValueError(f"thiếu section: {missing}")
parts = []
for name in SECTIONS:
body = sections[name].strip()
if body:
parts.append(f"<{name}>\n{body}\n</{name}>")
return "\n\n".join(parts)
Hàm trên không tự động cache, nhưng nó buộc thứ tự và buộc mọi caller điền đủ section. Khi policy và tools không đổi, prefix ổn định; chỉ phần đuôi thay đổi. Với Anthropic, prefix phải đủ 512 token mới có tác dụng; với OpenAI, đọc cache rẻ hơn nhưng ghi cache vẫn là một dòng chi phí.
Đừng tối ưu cache bằng cách nhồi thêm nội dung tĩnh chỉ để đạt ngưỡng. Token thêm vào luôn có giá; cache chỉ giảm giá phần đọc lại. Đo tỉ lệ cache hit thật trong usage trước khi kết luận.
Compaction: cắt ngữ cảnh mà không cắt sự thật
Compaction là thay lịch sử dài bằng bản tóm tắt có cấu trúc, giữ lại thứ cần cho quyết định tiếp theo. Trigger nên dựa trên ngân sách của bạn, không dựa trên cửa sổ model: khi context đã dùng vượt khoảng 70% ngân sách, bắt đầu tóm tắt. Con số này là điểm khởi đầu, không phải chuẩn; hãy chỉnh theo eval.
Bản tóm tắt tốt có schema, không phải một đoạn văn:
{
"decisions": ["chọn gói B", "hoàn tiền theo chính sách 30 ngày"],
"constraints": ["không gửi email trước khi xác minh đơn"],
"open_questions": ["khách chưa cung cấp mã đơn"],
"entities": {"order_id": "12345", "tenant": "acme"},
"dropped": ["chào hỏi", "tool output trùng lặp", "suy đoán chưa xác nhận"]
}
Giữ quyết định, ràng buộc, ID và câu hỏi mở. Bỏ chào hỏi, văn bản lặp, và suy đoán chưa được xác nhận. Điều quan trọng nhất: không đưa suy đoán của model vào memory như sự thật — đó là con đường ngắn nhất đến context poisoning.
Compaction tốn thêm một lần gọi model, nên nó không miễn phí. Nó có lợi khi phần bị cắt lớn hơn nhiều so với bản tóm tắt và khi các lượt sau thực sự cần lịch sử. Với tác vụ một lượt, đừng tóm tắt gì cả. Và sau khi thay context, phải chạy lại eval: tóm tắt hay làm mất một ràng buộc mà không ai biết.
Isolation: context nào thuộc về ai
Cô lập là tầng bảo vệ rẻ nhất và hiệu quả nhất. Bốn ranh giới cần có:
- Theo task. Mỗi subagent nhận một lát context đủ cho việc của nó, không kèm toàn bộ lịch sử của agent cha. Ít tool hơn, ít quyền hơn, ít thông tin hơn.
- Theo tenant. Memory, vector index, semantic cache và log đều phải key theo tenant. Không dùng chung cache giữa khách hàng; câu trả lời phụ thuộc quyền truy cập không được cache toàn cục.
- Theo request. Không tái sử dụng object context giữa hai người dùng. Nếu bạn dùng
previous_response_id, ID đó thuộc phiên nào thì chỉ phiên đó được dùng. - Theo môi trường. Context eval không được lẫn dữ liệu production; ngược lại, eval không được ghi vào memory thật.
Trong agent, hãy tách planner khỏi executor: planner không đọc nội dung không tin cậy, executor không có quyền rộng. Đây là cách giảm thiệt hại khi một tài liệu truy xuất chứa chỉ dẫn độc hại — indirect prompt injection không cần người dùng làm gì sai.
Bốn failure mode của context
1. Context poisoning
Một thông tin sai lọt vào memory hoặc tài liệu nguồn, được model lặp lại, rồi được lưu tiếp như sự thật. Sau vài vòng, không ai truy được nó đến từ đâu. Phòng ngừa: chỉ ghi vào memory thứ đã xác minh; gắn nguồn và thời điểm cho mọi entry; có đường xoá và sửa; không cho model tự ghi memory mà không qua kiểm tra.
2. Context distraction
Tài liệu liên quan nhưng không cần thiết kéo model sang hướng khác. Triệu chứng điển hình: câu trả lời đúng nhưng lan man, hoặc trích dẫn một đoạn không liên quan đến câu hỏi. Phòng ngừa: retrieval theo tác vụ, không theo “có thể hữu ích”; giới hạn số đoạn; rerank và cắt theo ngưỡng điểm; đặt tài liệu ngay trước câu hỏi thay vì rải khắp nơi.
3. Context clash
Hai nguồn nói ngược nhau: policy cũ và policy mới, memory cá nhân và quy định tenant, few-shot và lịch sử. Model không có cơ chế phân xử trừ khi bạn nói rõ thứ tự ưu tiên. Phòng ngừa: một nguồn chân lý cho mỗi loại sự thật; ghi ngày hiệu lực; loại bỏ bản cũ khỏi corpus thay vì chỉ thêm bản mới; nói rõ trong instructions: “policy hiện hành thắng memory”.
4. Context bloat
Nhồi mọi thứ vì “model có thể cần”. Hệ quả kép: chi phí tăng và chất lượng giảm vì thông tin quan trọng bị chôn. Dấu hiệu: token vào tăng mỗi tuần, tỉ lệ trả lời đúng không tăng, p95 tăng. Phòng ngừa: ngân sách theo section, ablation từng nguồn, và một quy tắc đơn giản — nếu bỏ một section mà eval không giảm, đừng giữ nó.
Đánh giá context như đánh giá code
Ba phép đo đủ để biết context layer có tốt lên hay không:
- Ablation. Chạy cùng bộ case với từng nguồn bị bỏ. Nguồn nào bỏ đi mà điểm không đổi là nguồn đang đốt tiền.
- Token trên mỗi task thành công. Không chỉ đo điểm trung bình; đo chi phí để đạt một kết quả đúng. Một cấu hình đúng 90% nhưng tốn gấp đôi token có thể tệ hơn cấu hình 85% rẻ hơn nhiều.
- Groundedness và format validity. Câu trả lời có được tài liệu truy xuất hậu thuẫn không, và có đúng schema không. Hai lỗi này thường đi cùng context sai chứ không phải prompt sai.
Ghi lại hash của context đã dùng cho mỗi lần chạy. Khi điểm tụt, bạn cần biết nó tụt vì model, vì prompt, hay vì bộ tài liệu đã đổi. Không có hash thì mọi so sánh đều là phỏng đoán.
Ngân sách context thực dụng
Đây là một ngân sách mẫu cho trợ lý hỗ trợ khách hàng, không phải chuẩn cố định. Mục đích là buộc mỗi section có hạn mức và buộc bạn phải cắt trước khi gửi.
| Section | Ngân sách ví dụ | Khi vượt |
|---|---|---|
| Instructions + policy | 2.000–4.000 token | Cắt ví dụ, giữ quy tắc |
| Tool schemas | 2.000–6.000 token | Bỏ tool không dùng cho tác vụ |
| Retrieval | 8.000–20.000 token | Rerank, cắt theo ngưỡng, giảm số đoạn |
| History / memory | 4.000–8.000 token | Compaction có schema |
| Output reserve | 2.000–4.000 token | Giảm max_output_tokens hoặc chia tác vụ |
Tổng ví dụ khoảng 20.000–40.000 token — rất xa cửa sổ 1 triệu. Đó là chủ ý: cửa sổ là giới hạn kỹ thuật, ngân sách là quyết định sản phẩm. Bạn không cần dùng hết cửa sổ, và mỗi token thêm vào phải chứng minh được giá trị bằng eval.
Checklist trước khi ship
- Mỗi section có owner, TTL và giới hạn token; không section nào là “phần còn lại”.
- Prefix tĩnh byte-identical giữa các request; mọi thứ biến động nằm cuối.
- Tool list được lọc theo tác vụ, không phải toàn bộ tool của hệ thống.
- Memory chỉ ghi dữ liệu đã xác minh, có nguồn và ngày; có API xoá.
- Context key theo tenant; semantic cache không dùng cho câu trả lời phụ thuộc quyền.
- Compaction có schema và có eval sau compaction.
- Có ablation cho từng nguồn và log hash context theo từng lần chạy.
Nhìn tới 2027
Context layer sẽ dần bị framework hấp thụ: middleware compaction, memory có scope, và policy engine cho tool. Nhưng quyết định ai được thấy gì vẫn là quyết định của bạn, không phải của framework. Đầu tư sớm vào ba thứ — ngân sách theo section, cô lập theo tenant, và eval có ablation — sẽ sống lâu hơn bất kỳ API cụ thể nào.
Nền tảng của mọi context layer vẫn là prompt rõ ràng. Đọc Nghệ Thuật Viết Prompt Hiệu Quả để chắc phần chỉ dẫn không phải nút thắt. Bài Tokens, Cửa Sổ Ngữ Cảnh và Chi Phí API giải thích phần kế toán token. Và Xây Dựng Pipeline RAG là nơi context retrieval được đo bằng recall và precision.
