Agent 3 — Context Engineering: nuôi cửa sổ context đúng cách
Context là tài nguyên hữu hạn
Có một hiểu lầm nguy hiểm: "cửa sổ context giờ tới 1 triệu token rồi, cứ nhồi hết vào cho chắc". Sai. Cửa sổ context lớn không có nghĩa là bạn nên dùng hết. Theo bài "Effective context engineering for AI agents" của Anthropic, context là một tài nguyên hữu hạn phải được quản lý cẩn thận — giống như bạn không đổ toàn bộ ổ cứng vào RAM chỉ vì RAM đủ chỗ. Mỗi token bạn đưa vào là một token model phải "để mắt tới", và sự chú ý của model bị pha loãng khi context phình to.
Đây là chỗ phân biệt hai khái niệm hay bị nhập nhằng:
- Prompt engineering: nghệ thuật soạn một lời nhắc tốt cho một lượt gọi. Bạn viết system prompt, câu hỏi, vài ví dụ few-shot — xong, gửi đi, nhận kết quả. Đây là công việc "tĩnh", một lượt.
- Context engineering: nghệ thuật quản lý trạng thái context qua nhiều lượt và nhiều tool call. Agent chạy vòng lặp: gọi tool, nhận kết quả, gọi tiếp, tóm tắt, kéo tài liệu về... Cửa sổ context liên tục thay đổi. Câu hỏi không còn là "viết prompt gì" mà là "ở mỗi bước, cái gì nên nằm trong cửa sổ, cái gì nên bị bỏ ra".
Prompt engineering là tập con. Với agent chạy dài, phần khó và quyết định chất lượng là context engineering.
Trong cửa sổ context có gì
Trước khi quản, phải biết mình đang quản cái gì. Một cửa sổ context của agent điển hình chứa các "hộ dân" sau, tất cả cùng cạnh tranh chỗ:
Điểm mấu chốt về system prompt: Anthropic nhấn mạnh phải đặt nó ở "đúng độ cao" (the right altitude). Có hai thái cực đều tệ:
- Quá cứng (quá thấp): nhồi hàng loạt logic if-else, liệt kê mọi tình huống cạnh, ép model theo kịch bản cứng nhắc. Kết quả: prompt phình to, giòn, thêm case mới lại phải sửa, và model mất khả năng suy luận linh hoạt.
- Quá mơ hồ (quá cao): chỉ nói "hãy là trợ lý hữu ích" rồi phó mặc. Model không có đủ tín hiệu để hành động nhất quán.
"Đúng độ cao" là điểm cân bằng: đủ cụ thể để định hướng hành vi, nhưng đủ tổng quát để model tự suy luận trong tình huống mới. Nêu nguyên tắc và tín hiệu, đừng viết cả cẩm nang.
Context rot: vì sao "ít mà đúng" thắng "nhiều mà nhiễu"
Hiện tượng cốt lõi cần thuộc là context rot: khi context càng dài và càng nhiều thông tin không liên quan, chất lượng đầu ra của model giảm — độ chính xác tụt, model bỏ sót chi tiết, bám vào thứ vô nghĩa. Nhồi 200 trang tài liệu vào không làm model "nhớ" 200 trang; nó làm loãng sự chú ý đến mức phần thực sự quan trọng bị chìm.
Đây là lý do triết lý của context engineering là tìm tập token nhỏ nhất, tín hiệu cao nhất để đạt kết quả mong muốn — chứ không phải tập lớn nhất. Mỗi kết quả tool thô, mỗi lượt hội thoại đã hết tác dụng, mỗi đoạn tài liệu "nạp cho chắc" đều là nhiễu cạnh tranh với thứ quan trọng. "Ít mà đúng" thắng "nhiều mà nhiễu" không phải vì tiết kiệm tiền (dù có), mà vì nó cho kết quả tốt hơn.
Năm kỹ thuật cốt lõi
1. Đặt phần ổn định lên đầu — tận dụng prompt caching
Trong một phiên agent, phần ổn định gần như không đổi giữa các lượt: system prompt, định nghĩa tool, có thể cả tài liệu chính sách nền. Phần biến động là lịch sử hội thoại và kết quả tool mới.
Nguyên tắc vàng: đặt phần ổn định lên đầu, phần biến động ở sau, rồi đánh dấu ranh giới ổn định bằng cache_control. Đây là prompt caching của Anthropic: phần đầu được cache lại; các lượt sau, thay vì xử lý lại toàn bộ phần đầu, model đọc từ cache — giảm chi phí và độ trễ khi lặp nhiều lượt. TTL của cache khoảng 5 phút (làm mới mỗi lần trúng cache), nên nó hợp với phiên agent liên tục. (Đừng gán con số "% giảm" cụ thể — mức lợi phụ thuộc tỷ lệ phần ổn định trên tổng và tần suất tái sử dụng.)
Hệ quả thiết kế: đừng chèn nội dung biến động vào giữa phần ổn định. Một biến số nhỏ (ví dụ timestamp) nhét vào đầu system prompt sẽ phá vỡ cache cho toàn bộ phần sau nó. Giữ ranh giới sạch.
2. Just-in-time retrieval — nạp đúng lúc, không nhồi trước
Cách cũ (RAG kiểu "nhồi trước"): trước khi agent chạy, truy hồi sẵn một đống đoạn tài liệu rồi dồn hết vào context. Cách này đơn giản nhưng tốn token và dễ gây context rot nếu truy hồi sai.
Cách Anthropic khuyến nghị cho agent: just-in-time retrieval — thay vì nhồi trước, cho agent tool tìm kiếm và để nó tự kéo tài liệu về đúng lúc cần. Agent giữ các "định danh nhẹ" (đường dẫn file, ID tài liệu, truy vấn) và chỉ nạp nội dung đầy đủ khi thực sự dùng. Lợi ích: tiết kiệm context, và dữ liệu luôn mới (tài liệu đổi thì lần kéo sau đã cập nhật, không cần nạp lại).
Nền tảng RAG và cách xây pipeline truy hồi xem LLM 4 — RAG và Vector 7 — RAG & Retrieval.
3. Compaction — nén khi gần đầy
Với nhiệm vụ dài hơn một cửa sổ context, phải có cơ chế compaction: khi context chạm ngưỡng (ví dụ 70–80% cửa sổ), tóm tắt phần cũ thành một khối súc tích rồi tiếp tục từ đó. Đây là cách agent làm được việc kéo dài hàng trăm lượt mà không tràn context.
Điều then chốt: compaction phải giữ lại quyết định đã chốt, trạng thái hiện tại, dữ kiện số quan trọng, việc đang dở — và bỏ chi tiết thừa (kết quả tool thô đã dùng xong, các lượt qua lại không còn tác dụng). Compaction làm ẩu (tóm tắt chung chung) sẽ đánh mất chính thứ agent cần để làm tiếp — dẫn tới sai lầm sau khi nén.
4. Sub-agent — cô lập context
Một kỹ thuật mạnh: giao việc con nặng context cho một sub-agent có cửa sổ riêng. Sub-agent "đọc 40 file để tìm chỗ tính lãi" tiêu tốn hàng chục nghìn token trong context của chính nó, rồi chỉ trả về kết quả gọn vài dòng cho agent chính. Context chính vẫn sạch, không bị nhiễu bởi 40 file thô.
Đây là nguyên tắc "chia để trị sự nhiễu": mỗi agent một khoang context. Chi tiết kiến trúc orchestrator–worker và cách điều phối nhiều agent xem Agent 7 — Multi-agent orchestration.
5. Tỉa kết quả tool
Kết quả tool là nguồn phình context lớn nhất và dễ kiểm soát nhất. Đừng để tool trả 5.000 dòng JSON thô vào context. Hai cách tỉa:
- Truncation (cắt): giới hạn số dòng/ký tự, và báo rõ cho model biết đã bị cắt ("...(còn 4.980 dòng, dùng tool X để lọc)") để model biết đường xử lý tiếp thay vì tưởng đó là toàn bộ.
- Summary (tóm tắt tại nguồn): tool trả bảng tổng hợp/thống kê thay vì dữ liệu thô. Ví dụ tool truy vấn giao dịch nên trả "tổng, trung bình, số dòng, top-5" thay vì cả vạn bản ghi.
Cân bằng: nạp trước vs just-in-time
Không phải lúc nào just-in-time cũng thắng. Hai chiến lược có đánh đổi rõ ràng:
| Tiêu chí | Nạp trước (pre-load) | Just-in-time |
|---|---|---|
| Độ phức tạp | Đơn giản, ít vòng lặp tool | Phức tạp hơn, cần tool tìm kiếm |
| Prompt caching | Dễ cache (phần đầu ổn định) | Khó cache phần kéo động |
| Token tiêu thụ | Cao (nạp cả thứ không dùng) | Thấp (chỉ nạp thứ cần) |
| Độ mới dữ liệu | Có thể cũ | Luôn mới |
| Rủi ro context rot | Cao nếu nạp thừa | Thấp |
Quy tắc thực dụng:
- Nạp trước khi tập tài liệu nhỏ, ổn định, gần như luôn cần (ví dụ: bảng thuật ngữ, chính sách lõi ít đổi). Nó ổn định nên cache tốt.
- Just-in-time khi kho tài liệu lớn, hay đổi, chỉ một phần nhỏ liên quan mỗi truy vấn (ví dụ: hàng nghìn văn bản chính sách, sao kê từng khách hàng).
- Thực tế thường là lai (hybrid): nạp trước phần lõi ổn định (và cache nó), just-in-time phần dài đuôi.
Code minh hoạ (Python, Anthropic SDK)
Đoạn code dưới đây là minh hoạ để làm rõ cấu trúc, không phải thư viện dựng-sẵn. Model dùng
claude-opus-4-8; extended thinking bật ở chế độadaptive.
Đặt phần ổn định trước + đánh dấu cache
import anthropic
client = anthropic.Anthropic()
# Phần ỔN ĐỊNH: system prompt + tài liệu chính sách lõi.
# Đánh dấu cache_control ở CUỐI khối ổn định -> mọi thứ trước nó được cache.
SYSTEM = [
{
"type": "text",
"text": (
"Bạn là trợ lý phân tích tín dụng của NCB. Nêu nguyên tắc, "
"trích dẫn nguồn chính sách, không tự bịa số. Khi cần dữ liệu "
"khách hàng, GỌI TOOL để kéo về đúng lúc — không giả định."
),
},
{
"type": "text",
"text": POLICY_CORE_TEXT, # chính sách lõi, ít đổi -> hợp để cache
"cache_control": {"type": "ephemeral"}, # ranh giới cache (TTL ~5 phút)
},
]
def call_model(messages):
return client.messages.create(
model="claude-opus-4-8",
max_tokens=4096,
system=SYSTEM, # phần ổn định, đặt trước
messages=messages, # phần biến động, đặt sau
tools=TOOLS,
thinking={"type": "adaptive"}, # KHÔNG dùng budget_tokens
)
Lưu ý: khi nối hội thoại nhiều lượt, phải giữ nguyên các block model trả về (kể cả block thinking) khi append lại vào messages — không được lược bỏ, nếu không lượt sau sẽ hỏng.
Vòng nén khi vượt ngưỡng token
MODEL_WINDOW = 200_000 # cửa sổ mục tiêu (minh hoạ)
COMPACT_AT = 0.75 # nén khi ước lượng dùng quá 75%
def maybe_compact(messages):
if estimate_tokens(messages) < MODEL_WINDOW * COMPACT_AT:
return messages
# Giữ đầu (mục tiêu) + đuôi (ngữ cảnh còn nóng), nén khúc giữa.
head, tail = messages[:2], messages[-6:]
middle = messages[2:-6]
resp = client.messages.create(
model="claude-opus-4-8",
max_tokens=1024,
messages=[{
"role": "user",
"content": (
"Tóm tắt đoạn hội thoại sau, GIỮ NGUYÊN: mục tiêu, các "
"quyết định đã chốt, dữ kiện số quan trọng, việc đang dở. "
"BỎ chi tiết thừa và output tool đã dùng xong.\n\n"
+ serialize(middle)
),
}],
thinking={"type": "adaptive"},
)
summary = resp.content[-1].text
return head + [{"role": "user",
"content": f"[TÓM TẮT PHIÊN TRƯỚC]\n{summary}"}] + tail
def agent_loop(user_task):
messages = [{"role": "user", "content": user_task}]
while True:
messages = maybe_compact(messages) # nén nếu gần đầy
resp = call_model(messages)
messages.append({"role": "assistant", "content": resp.content})
if resp.stop_reason == "end_turn":
return resp
if resp.stop_reason == "tool_use":
results = run_tools(resp.content) # đã tỉa gọn tại nguồn
messages.append({"role": "user", "content": results})
Ba chi tiết đáng khắc: (1) maybe_compact giữ đầu + đuôi, nén giữa — đúng chỗ context rot hay nuốt mất; (2) câu lệnh tóm tắt chỉ định rõ giữ gì, không tóm tắt chung chung; (3) vòng lặp bám đúng stop_reason (tool_use → chạy tool và trả tool_result; end_turn → dừng).
Use case thực tế
Bài toán NCB — trợ lý phân tích tín dụng phiên dài. Cán bộ thẩm định mở một phiên hỏi đáp kéo dài để soi một hồ sơ vay: đọc chính sách tín dụng, sao kê 12 tháng, tóm tắt CIC, rồi hỏi qua lại hàng chục lượt trước khi ra khuyến nghị. Đây là kịch bản ngốn context điển hình, và gom đủ cả năm kỹ thuật:
- Cache phần ổn định: system prompt + bộ chính sách tín dụng lõi (giả sử ~15.000 token) đặt lên đầu, đánh dấu
cache_control. Cán bộ hỏi 30 lượt trong một phiên 20 phút → phần 15k token này chỉ xử lý đầy đủ ở lượt đầu, các lượt sau (trong TTL ~5 phút, được làm mới mỗi lượt) đọc từ cache → giảm độ trễ và chi phí phần lặp lại. - Just-in-time retrieval trên tài liệu chính sách: NCB có hàng nghìn văn bản chính sách/hướng dẫn; không nạp hết. Agent dùng tool
search_policy(query)kéo về đúng 2–3 điều khoản liên quan mỗi khi cần — thay vì nhồi cả kho. - Tỉa kết quả tool: tool
get_statement(customer_id)truy sao kê từ read-replica không trả 4.000 dòng giao dịch thô, mà trả bảng tổng hợp (thu nhập TB tháng, tổng chi, số lần trễ hạn, DTI) — vài trăm token thay vì hàng chục nghìn. - Compaction khi hội thoại dài: sau ~25 lượt, ước lượng context chạm 75% ngưỡng → agent nén phần giữa thành một khối "đã chốt: thu nhập TB 45tr, DTI 38%, 1 lần trễ tháng 3, tài sản đảm bảo đủ", giữ đầu (mục tiêu) và 6 lượt cuối. Nhờ giữ đúng quyết định + số liệu, agent không "quên" giữa chừng.
- Sub-agent cô lập cho việc nặng: khi cần đối chiếu 40 hợp đồng liên quan cùng nhóm khách, giao cho một sub-agent riêng; nó đọc hết trong context của nó rồi chỉ trả về "3 hợp đồng có dấu hiệu cùng dòng tiền" — context chính không bị 40 hợp đồng làm nhiễu.
Ước lượng (minh hoạ, không phải số đo thật): một phiên 30 lượt kiểu "nhồi tất": mỗi lượt tái xử lý ~15k token chính sách + ~40k token sao kê thô + lịch sử phình dần → dễ chạm hàng triệu token xử lý và context rot rõ rệt ở lượt sau. Cùng phiên đó áp năm kỹ thuật trên: phần 15k chính sách được cache (không tái xử lý đầy đủ mỗi lượt), sao kê chỉ còn ~vài trăm token nhờ tỉa tại nguồn, và compaction chặn lịch sử phình — ước lượng cắt phần lớn token phải xử lý và giữ chất lượng ổn định về cuối phiên. Con số cụ thể tùy tỷ lệ cache-hit và độ dài phiên; hãy đo bằng usage thực tế trước khi cam kết KPI.
Toàn bộ vẫn tuân thủ nguyên tắc ngân hàng: đọc trên read-replica, mọi truy vấn dữ liệu khách qua tool có audit log, và khuyến nghị cuối cần human-in-the-loop duyệt.
Ghi nhớ
- Context là tài nguyên hữu hạn dù cửa sổ tới 1M token — mục tiêu là tập token nhỏ nhất, tín hiệu cao nhất, không phải nhiều nhất.
- Prompt engineering = soạn một lượt; context engineering = quản trạng thái context qua nhiều lượt và nhiều tool. Agent chạy dài sống bằng cái sau.
- Trong context có: system prompt, định nghĩa tool, lịch sử, kết quả tool, tài liệu retrieval, memory — tất cả cạnh tranh chỗ. Luôn chừa headroom cho đầu ra.
- System prompt đặt "đúng độ cao": đủ cụ thể để định hướng, đủ tổng quát để model suy luận — không quá cứng (if-else), không quá mơ hồ.
- Context rot: context dài/nhiễu làm chất lượng giảm. "Ít mà đúng" thắng "nhiều mà nhiễu" vì kết quả tốt hơn, không chỉ rẻ hơn.
- Prompt caching: đặt phần ổn định lên đầu +
cache_control, TTL ~5 phút, giảm chi phí/độ trễ khi lặp. Đừng chèn biến động vào giữa phần ổn định (phá cache). Không tự bịa % giảm. - Just-in-time retrieval: cho tool tìm kiếm, kéo tài liệu đúng lúc thay vì nhồi trước — tiết kiệm token, dữ liệu luôn mới.
- Compaction: nén khi gần đầy (≈70–80%), giữ quyết định/trạng thái/số liệu/việc dở, bỏ chi tiết thừa.
- Sub-agent: cô lập việc nặng context, chỉ trả kết quả gọn về agent chính.
- Tỉa kết quả tool: truncation có báo hoặc summary tại nguồn — đừng để JSON khổng lồ tràn context.
- Cân bằng nạp trước (đơn giản, cache tốt) vs just-in-time (linh hoạt, ít token); thực tế thường lai.
- Ví dụ dùng
claude-opus-4-8,thinking={"type":"adaptive"}(khôngbudget_tokens), giữ nguyên block model trả về khi nối hội thoại.
Nguồn tham khảo
- Anthropic Engineering — "Effective context engineering for AI agents" (bài gốc được trích dẫn xuyên suốt: context là tài nguyên hữu hạn, context rot, just-in-time retrieval, compaction, sub-agent).
- Anthropic Engineering — "Building effective agents" (nguyên tắc thiết kế agent, vòng lặp tool, orchestrator–worker).
- Anthropic Documentation — "Prompt caching" (cơ chế
cache_control, khối ổn định, TTL cache). - Anthropic Documentation — "Extended thinking" (chế độ thinking, cách giữ block thinking khi nối nhiều lượt).
- Anthropic Documentation — "Context windows" (cách các thành phần chiếm chỗ trong cửa sổ context và quản lý token).
- Anthropic Documentation — "Tool use (function calling)" (định nghĩa tool,
stop_reason,tool_use/tool_result).
Bài viết liên quan
Đặt nền cho chuỗi AI: phân biệt ba vòng tròn lồng nhau AI ⊃ ML ⊃ DL và khác biệt bản chất giữa lập trình truyền thống với học từ dữ liệu. Giới thiệu ba kiểu học máy (supervised, unsupervised, reinforcement), phân loại descriptive/predictive/prescriptive, quy trình ML end-to-end, chia train/validation/test, overfitting/underfitting và các thuật ngữ nền tảng, gắn với ứng dụng ngân hàng NCB.
Harness — lớp scaffolding quanh model (vòng lặp, tool, context, memory, verify, sub-agent) — mới là thứ quyết định agent chạy được hay chỉ là demo. Bài này mổ xẻ giải phẫu một harness, 7 kỹ năng cốt lõi khi xây agent, single vs multi-agent (kèm số liệu hiệu quả/chi phí), các repo nên dùng, và một quickstart Python dựng-là-chạy cho bối cảnh ngân hàng.
Hiểu LLM từ gốc: bản chất dự đoán token, ba giai đoạn huấn luyện (pretraining, fine-tuning, RLHF), token, context window và các tham số sinh (temperature, top-p). Nắm hiện tượng hallucination và kỹ thuật prompt engineering (vai trò, few-shot, chain-of-thought, ràng buộc đầu ra), kèm ví dụ gọi API model Claude mới nhất với adaptive thinking.
Vì sao dữ liệu quyết định chất lượng mô hình hơn cả thuật toán. Bài này đi qua toàn bộ pipeline chuẩn bị dữ liệu: phân loại dữ liệu, làm sạch (thiếu/ngoại lai/trùng lặp), mã hoá hạng mục, scaling, feature engineering, giảm chiều, và cách phòng data leakage — soi qua bài toán chấm điểm tín dụng.
Cảm nhận của bạn
Bình luận
Chưa có bình luận. Hãy là người đầu tiên chia sẻ!