·

Một trong những sai lầm phổ biến nhất khi làm việc với AI agent trong codebase không phải là chọn sai model, mà là chọn sai lượng context (ngữ cảnh) đưa vào. Rất nhiều kỹ sư — từ junior đến senior — vẫn có phản xạ: gặp bug thì paste nguyên file, thậm chí nguyên vài chục file, vào cửa sổ chat với AI rồi hy vọng model "tự tìm ra". Cách làm này có vẻ an toàn (đưa dư còn hơn thiếu), nhưng thực tế nó vừa đốt tiền vừa làm chất lượng câu trả lời tệ đi. Bài viết này sẽ phân tích chi tiết vì sao "naive code inclusion" (đưa code vào ngữ cảnh một cách ngây thơ) là một anti-pattern tốn kém, có công thức tính toán cụ thể, và hướng dẫn bạn tự đo lường mức lãng phí trong chính workflow của mình.

Hiểu Thế Nào Là "Đưa Code Vào Ngữ Cảnh Một Cách Ngây Thơ" Trong Thực Tế

"Naive code inclusion" là thuật ngữ mô tả hành vi đưa code vào context window (cửa sổ ngữ cảnh) của LLM mà không cân nhắc file đó có thực sự cần thiết cho tác vụ hiện tại hay không. Nó không phải là hành vi hiếm gặp — nó là mặc định của rất nhiều người mới bắt đầu dùng AI agent, và thậm chí một số senior engineer vẫn mắc phải khi đang vội.

Vài tình huống rất thực tế mà bạn có thể đã từng gặp hoặc chính mình từng làm:

  • Dump cả repo vào Claude Project / Custom GPT: Một developer cần sửa một function tính giá đơn hàng trong file pricing.service.ts, nhưng vì "sợ AI thiếu context" nên họ kéo toàn bộ thư mục src/ — hàng trăm file TypeScript, bao gồm cả các module không liên quan như auth, notification, admin-dashboard — vào một context duy nhất.
  • Copy-paste nguyên file thay vì đoạn code: Chỉ cần sửa 10 dòng trong một file 2.000 dòng, nhưng developer paste cả file vì "để AI hiểu bối cảnh xung quanh", trong khi 1.990 dòng còn lại không liên quan gì đến logic cần sửa.
  • Đưa cả lịch sử git log, changelog, hoặc toàn bộ README dài dằng dặc vào mỗi câu hỏi, dù câu hỏi chỉ liên quan đến một endpoint API cụ thể.
  • Coding agent (Claude Code, Cursor, Windsurf...) tự động quét toàn bộ workspace khi không có cấu hình include/exclude rõ ràng, khiến agent đọc luôn cả node_modules được commit nhầm, file build, hoặc asset nhị phân được encode base64.
  • Đính kèm toàn bộ test suite khi chỉ có một test case bị fail, vì nghĩ "AI cần thấy hết các test khác để hiểu pattern".

Vì sao hành vi này lại phổ biến đến vậy? Có ba lý do tâm lý chính:

  1. Tâm lý "an toàn hơn là thiếu": Nhiều người tin rằng thà đưa dư thông tin còn hơn để AI thiếu context và trả lời sai. Đây là suy nghĩ hợp lý với con người (đọc lướt không tốn gì), nhưng sai với LLM — vì mỗi token đưa vào đều được tính phí và đều ảnh hưởng đến khả năng "chú ý" (attention) của model.
  2. Thiếu công cụ hoặc kỹ năng để trích xuất phần liên quan: Nhiều người chưa quen với các công cụ như repo map, semantic search, hay grep có định hướng, nên cách nhanh nhất họ biết là "chọn hết".
  3. Áp lực thời gian: Khi đang deadline gấp, việc dừng lại để suy nghĩ "file nào thực sự cần" tốn thời gian hơn việc bấm chọn tất cả rồi paste.

Mẹo

  • Trước khi đưa bất kỳ file nào vào AI, tự hỏi: "Nếu tôi đưa câu hỏi này cho một đồng nghiệp mới join team, họ cần đọc bao nhiêu file để trả lời được?" Số đó thường nhỏ hơn nhiều so với những gì bạn định paste.
  • Tập thói quen copy đoạn code cụ thể (function, class, hoặc vài dòng quanh lỗi) kèm chú thích đường dẫn file, thay vì paste cả file.
  • Với coding agent có khả năng tự đọc file (agentic tool use), hãy để agent tự quyết định đọc file nào qua tool call, thay vì bạn chủ động nhồi sẵn mọi thứ vào system prompt hay câu hỏi đầu tiên.

Bài Toán Token: Chi Phí Thực Sự Của Việc Đưa Code Ngây Thơ

Để thấy rõ mức độ lãng phí, chúng ta cần quy đổi ra số cụ thể. Một quy tắc ước lượng phổ biến (rule of thumb) cho code và văn bản tiếng Anh là khoảng 1 token ≈ 4 ký tự, hoặc trung bình một file code 100 dòng (giả sử mỗi dòng khoảng 40 ký tự) sẽ tốn khoảng 1.000–1.300 token tùy vào độ dày đặc của cú pháp (code có nhiều symbol, dấu ngoặc, khoảng trắng lặp lại thường "tốn" token khác với văn xuôi thông thường).

Giờ hãy thử tính một tình huống thực tế: một developer cần sửa một hàm nhỏ nhưng lại paste toàn bộ 80 file trong module src/services/ vào context, mỗi file trung bình 150 dòng (~1.800 token/file).

Bảng chi phí minh họa (giả định giá $3 / triệu input token — mức giá phổ biến của các model hạng "mid-tier" hiện nay):

Khoản mục Số lượng Token ước tính Chi phí ước tính
Số file đưa vào context 80 file
Token trung bình mỗi file ~1.800 token/file 144.000 token $0.432
Token thực sự cần (1 file liên quan + 2 file phụ thuộc trực tiếp) 3 file 5.400 token $0.016
Token lãng phí mỗi lần hỏi 77 file dư 138.600 token $0.416

Chỉ với một câu hỏi, bạn đã lãng phí khoảng 96% lượng token đưa vào — và đây là con số mỗi lần hỏi. Nếu một developer lặp lại việc này 15–20 lần/ngày (điều rất bình thường khi debug hoặc pair-code với AI), số tiền lãng phí có thể lên tới $6–8/ngày chỉ riêng một người, và nhân với cả team 10-20 người thì con số hàng tháng có thể lên đến hàng nghìn đô la — chưa kể chi phí "ẩn" về thời gian chờ phản hồi lâu hơn vì phải xử lý context dài.

Bạn có thể tự ước lượng nhanh bằng đoạn script Python sau, dùng thư viện tiktoken (hoặc bất kỳ tokenizer tương ứng với model bạn dùng):

import tiktoken
from pathlib import Path

encoding = tiktoken.get_encoding("cl100k_base")

def count_tokens(file_path: str) -> int:
    text = Path(file_path).read_text(errors="ignore")
    return len(encoding.encode(text))

files = list(Path("src/services").rglob("*.ts"))
total_tokens = sum(count_tokens(str(f)) for f in files)

PRICE_PER_MILLION_INPUT = 3.0  # USD, adjust to your model's pricing
estimated_cost = (total_tokens / 1_000_000) * PRICE_PER_MILLION_INPUT

print(f"Files scanned: {len(files)}")
print(f"Total tokens if dumped naively: {total_tokens:,}")
print(f"Estimated cost per request: ${estimated_cost:.4f}")

Chạy script này trên một thư mục thực tế của bạn sẽ cho ra số liệu "sốc" hơn bạn nghĩ — nhiều team lần đầu chạy thử đã phát hiện ra một request đơn lẻ tốn tới hàng trăm nghìn token dù task chỉ liên quan đến một file nhỏ.

Mẹo

  • Luôn quy đổi ra tiền thật (không chỉ số token) khi trình bày vấn đề này với quản lý hoặc đồng nghiệp — con số "$400/tháng lãng phí" thuyết phục hơn nhiều so với "chúng ta đang dùng dư token".
  • Nếu dùng model có context window lớn (128K, 200K, hay 1M token), đừng coi đó là lý do để nhồi nhét — context window lớn giải quyết vấn đề "không đủ chỗ chứa", không giải quyết vấn đề "chất lượng và chi phí".
  • Thiết lập một ngưỡng cảnh báo (ví dụ nếu một request vượt 20.000 token input) để tự động nhắc bạn xem lại có đang đưa dư context không.

Ngữ Cảnh Không Liên Quan Làm Giảm Chất Lượng Của Model Như Thế Nào

Đây là phần thường bị bỏ qua nhất: nhiều người nghĩ đưa dư context "chỉ tốn tiền nhưng không hại gì", nhưng thực tế nó làm giảm chất lượng câu trả lời. Hiện tượng này được gọi là "lost in the middle" (lạc mất thông tin ở giữa) — một phát hiện đã được nhiều nghiên cứu về long-context LLM ghi nhận: các model có xu hướng chú ý tốt nhất đến thông tin ở đầucuối của context, còn thông tin nằm ở giữa một context rất dài dễ bị "lướt qua" hoặc đánh giá thấp trọng số, dù về mặt kỹ thuật model vẫn "đọc" được toàn bộ.

Hệ quả thực tế của lost-in-the-middle khi làm việc với codebase:

  • Model trả lời dựa trên phần sai của codebase: Nếu function bạn cần sửa nằm ở giữa một context 100 file, model có thể "nhặt nhầm" một function tên tương tự ở một file khác (nằm gần đầu hoặc cuối context) để làm cơ sở suy luận, dẫn đến code sửa sai vị trí hoặc sai logic.
  • Tăng khả năng hallucination (bịa đặt): Khi có quá nhiều thông tin không liên quan, model dễ "trộn" các pattern từ nhiều file khác nhau để tạo ra một đoạn code nghe hợp lý nhưng không tồn tại trong codebase thật — ví dụ gọi một hàm helper không có thật vì nó "nhìn giống" các helper khác trong context.
  • Agent mất tập trung vào task chính: Với AI agent thực hiện nhiều bước (agentic loop), nếu system prompt hoặc bước đầu tiên đã nhồi quá nhiều file, agent có xu hướng "lạc đề" — đi sửa hoặc bình luận về những phần code không được yêu cầu, vì chúng đang nằm ngay trong tầm nhìn của nó.
  • Giảm độ chính xác của các câu trả lời cần độ chi tiết cao: Các tác vụ như review logic bảo mật, tính toán số liệu tài chính, hay refactor có yêu cầu chặt chẽ về tính đúng đắn sẽ bị ảnh hưởng nhiều hơn so với các câu hỏi mở, chung chung.

Ví dụ cụ thể: Giả sử một QA engineer cần AI phân tích vì sao một test case về tính thuế (calculateTax) bị fail. Nếu họ đưa nguyên module src/finance/ (30 file, bao gồm cả calculateShipping, calculateDiscount, calculateTax, formatCurrency...) vào context, model có xác suất cao sẽ lẫn logic giữa calculateDiscountcalculateTax vì cả hai đều dùng chung pattern early-return và nằm gần nhau trong "khối giữa" của context. Kết quả: AI đề xuất một fix sai chỗ, hoặc tệ hơn, tự tin khẳng định nguyên nhân sai mà QA engineer — nếu không kiểm tra kỹ — có thể tin theo và áp dụng nhầm.

Ngược lại, nếu chỉ đưa đúng file calculateTax.ts, file test tương ứng, và có thể 1 file config thuế liên quan, model sẽ có ít "nhiễu" hơn để suy luận đúng vào đúng vấn đề.

Mẹo

  • Với các tác vụ cần độ chính xác cao (bảo mật, tài chính, logic nghiệp vụ phức tạp), hãy chủ động giảm số file đưa vào ngay cả khi context window còn dư chỗ — đừng lấy "còn chỗ" làm lý do để nhồi thêm.
  • Nếu buộc phải đưa nhiều file, hãy đặt file quan trọng nhất ở đầu hoặc cuối của context (lợi dụng chính hiện tượng lost-in-the-middle theo hướng có lợi).
  • Luôn yêu cầu model trích dẫn chính xác đường dẫn file và số dòng nó dựa vào để trả lời — nếu model trích dẫn sai hoặc mơ hồ, đó là dấu hiệu context đang bị quá tải hoặc không liên quan.

Các Anti-Pattern Phổ Biến và Vì Sao Chúng Có Vẻ Hợp Lý

Dưới đây là những anti-pattern (khuôn mẫu hành vi sai nhưng lặp lại) phổ biến nhất khi teams làm việc với AI agent trên codebase — kèm lý do vì sao mỗi cái nghe "có lý" ở góc nhìn cá nhân, nhưng gây lãng phí ở quy mô team/tháng.

  • "Cứ paste cả repo cho chắc": Cảm thấy hợp lý vì loại bỏ hoàn toàn rủi ro "thiếu context". Nhưng thực tế gây lãng phí token khổng lồ và kích hoạt lost-in-the-middle như đã phân tích ở trên.
  • Luôn include package.json, lockfile (package-lock.json, yarn.lock, pnpm-lock.yaml): Nhiều người nghĩ "AI cần biết version dependency". Đúng là đôi khi cần biết version của một package cụ thể, nhưng lockfile có thể dài hàng chục nghìn dòng chỉ để trả lời một câu hỏi liên quan đến 1 dòng version — quá lãng phí. Chỉ cần grep đúng entry cần thiết.
  • Không có file exclude kiểu .aiderignore / .gitignore-style cho AI context: Nhiều team dùng .gitignore để loại node_modules, dist, .next, file build... khỏi git, nhưng lại quên rằng coding agent cũng cần một cấu hình tương tự để không tự động quét các thư mục này. Không có exclude rõ ràng, agent có thể tự "ăn" hàng chục MB code sinh tự động.
  • Đưa cả file/thư mục generated hoặc build (dist/, .next/, coverage/, file minified, snapshot test tự sinh): Cảm thấy "vô hại" vì nghĩ AI sẽ tự bỏ qua, nhưng thực tế các file này thường chiếm token nhiều nhất (code minified rất dày token) mà không mang giá trị ngữ nghĩa nào.
  • Đưa toàn bộ test suite khi chỉ có một test bị fail: Lý do "hợp lý" là muốn AI thấy pattern chung của test. Nhưng nếu chỉ 1 trong 200 test fail, 199 file test còn lại là nhiễu thuần túy, làm loãng attention của model khỏi đúng test đang cần fix.
  • Copy nguyên function log hoặc stack trace dài hàng nghìn dòng: Cảm thấy "an toàn" vì sợ bỏ lỡ dòng log quan trọng, nhưng phần lớn stack trace log lặp lại (retry loop, framework boilerplate) không đóng góp thông tin mới.
  • Giữ toàn bộ lịch sử chat/agent session dài trong một conversation thay vì tách task mới thành session mới: Mỗi lượt hỏi sau lại kéo theo toàn bộ ngữ cảnh cũ không còn liên quan.

Một ví dụ cấu hình loại trừ (tương tự file .aiderignore dùng cho công cụ Aider, hoặc có thể áp dụng ý tưởng tương tự cho các coding agent khác):

node_modules/
dist/
build/
.next/
coverage/
*.min.js
*.min.css
*.lock
package-lock.json
pnpm-lock.yaml
yarn.lock
**/*.snap
**/__generated__/**
**/*.generated.ts
.env
.env.*
*.log

Việc duy trì một file cấu hình như trên (áp dụng tương tự với .cursorignore, .codeiumignore, hoặc cấu hình include/exclude tùy theo công cụ bạn đang dùng) giúp giảm đáng kể lượng "nhiễu" bị agent tự động cuốn vào context mà bạn không hề chủ ý.

Mẹo

  • Coi việc thiết lập ignore-file cho AI context như một phần của onboarding dự án — cùng cấp độ ưu tiên với .gitignore.
  • Định kỳ (mỗi quý) rà soát lại ignore-file này, vì codebase thay đổi, có thể sinh ra thư mục generated mới cần loại trừ.
  • Với lockfile, nếu cần thông tin version, chỉ trích một dòng cụ thể (grep "react-router" package-lock.json) thay vì đưa cả file.

Đo Lường Mức Độ Lãng Phí Hiện Tại: Một Cuộc Kiểm Toán Chẩn Đoán

Lý thuyết là một chuyện, nhưng để thuyết phục bản thân (và team) thay đổi hành vi, bạn cần số liệu thực tế từ chính workflow của mình. Dưới đây là quy trình kiểm toán (audit) bạn có thể áp dụng ngay trong tuần này, không cần công cụ phức tạp.

Bước 1 — Bật logging token cho mọi request AI trong 3-5 ngày làm việc.

Nếu bạn dùng API trực tiếp (qua Anthropic SDK, OpenAI SDK...), hầu hết response đều trả về số liệu usage (input_tokens, output_tokens). Nếu dùng qua IDE/tool có sẵn (Claude Code, Cursor...), nhiều công cụ có dashboard hoặc log usage tích hợp — hãy bật nó lên.

Ví dụ đoạn wrapper Python đơn giản để log lại mỗi lần gọi API:

import json
import time
from pathlib import Path

LOG_FILE = Path("ai_usage_log.jsonl")

def log_request(task_description: str, input_tokens: int, output_tokens: int,
                 files_included: int, files_actually_relevant: int):
    entry = {
        "timestamp": time.time(),
        "task": task_description,
        "input_tokens": input_tokens,
        "output_tokens": output_tokens,
        "files_included": files_included,
        "files_actually_relevant": files_actually_relevant,
        "estimated_waste_tokens": input_tokens * (
            1 - (files_actually_relevant / max(files_included, 1))
        ),
    }
    with LOG_FILE.open("a") as f:
        f.write(json.dumps(entry) + "\n")

log_request(
    task_description="fix calculateTax rounding bug",
    input_tokens=42_000,
    output_tokens=800,
    files_included=25,
    files_actually_relevant=2,
)

Bước 2 — Sau mỗi request, tự đánh giá lại (retro nhanh 10 giây): "Trong số file/đoạn code tôi vừa đưa vào, bao nhiêu thực sự được model dùng để trả lời đúng?"

Ghi số này vào cột files_actually_relevant. Đừng làm việc này quá kỹ càng khiến bạn nản — mục tiêu là có số liệu tương đối, không cần tuyệt đối chính xác.

Bước 3 — Cuối tuần, tổng hợp log bằng một script phân tích nhanh:

import json
from pathlib import Path

entries = [json.loads(l) for l in Path("ai_usage_log.jsonl").read_text().splitlines()]

total_input_tokens = sum(e["input_tokens"] for e in entries)
total_waste_tokens = sum(e["estimated_waste_tokens"] for e in entries)
waste_ratio = total_waste_tokens / total_input_tokens if total_input_tokens else 0

PRICE_PER_MILLION_INPUT = 3.0
monthly_multiplier = 20  # approximate working days per month

print(f"Requests logged: {len(entries)}")
print(f"Total input tokens: {total_input_tokens:,}")
print(f"Estimated wasted tokens: {total_waste_tokens:,.0f} ({waste_ratio:.1%})")
print(f"Estimated weekly waste cost: ${(total_waste_tokens/1_000_000)*PRICE_PER_MILLION_INPUT:.2f}")
print(f"Projected monthly waste (x{monthly_multiplier} scale): "
      f"${(total_waste_tokens/1_000_000)*PRICE_PER_MILLION_INPUT*4.3:.2f}")

Bước 4 — Nhân con số lãng phí cá nhân với số lượng thành viên team đang dùng AI hàng ngày để có ước tính lãng phí cấp team/tháng — đây là số liệu hữu ích để trình bày với quản lý khi đề xuất đầu tư thời gian vào cải thiện context engineering (kỹ thuật thiết kế ngữ cảnh), ví dụ xây repo map, thiết lập RAG cho code, hay đào tạo lại thói quen include file cho cả team.

Bước 5 — Lặp lại audit này sau 2-4 tuần khi bạn đã áp dụng các kỹ thuật targeted file inclusion (đưa đúng file cần thiết) được nói ở các bài tiếp theo trong module này, để so sánh mức cải thiện thực tế — cả về token lẫn về chất lượng câu trả lời (tỷ lệ phải hỏi lại/sửa lại giảm bao nhiêu).

Mẹo

  • Nếu công ty có billing dashboard của provider AI (Anthropic Console, OpenAI dashboard...), đối chiếu ngay số liệu log cá nhân với hóa đơn thực tế — hai con số nên khớp tương đối để bạn tin vào phương pháp đo.
  • Đừng chỉ đo token — đo luôn "số lần phải hỏi lại vì câu trả lời sai/lạc đề", đây là chi phí ẩn (thời gian) thường lớn hơn cả chi phí token.
  • Biến việc audit này thành một checklist ngắn dùng chung cho cả team, không chỉ làm một mình rồi bỏ qua.

Mối Quan Hệ Giữa Chất Lượng và Chi Phí: Vì Sao Ít Hơn Lại Tốt Hơn

Điểm mấu chốt cần khắc sâu: giảm context một cách có chủ đích không phải đánh đổi giữa "tiết kiệm tiền" và "chất lượng câu trả lời" — trong phần lớn trường hợp thực tế với codebase, giảm context đúng cách sẽ cải thiện cả hai cùng lúc. Đây là điều phản trực giác với nhiều người mới, nhưng lại là nguyên lý cốt lõi của context engineering hiện đại.

Logic ở đây khá đơn giản khi tách ra từng phần:

  • Ít token hơn → chi phí thấp hơn: Điều này hiển nhiên, vì phần lớn model tính phí theo input token.
  • Ít nhiễu hơn → attention của model tập trung hơn vào phần thực sự liên quan → giảm hallucination, giảm lost-in-the-middle, tăng độ chính xác trích dẫn code.
  • Context ngắn hơn → model xử lý nhanh hơn → thời gian phản hồi (latency) thấp hơn → developer/QA/PM chờ ít hơn, năng suất cao hơn.
  • Context có chọn lọc buộc người dùng phải hiểu rõ vấn đề trước khi hỏi — bản thân quá trình chọn "file nào thực sự liên quan" đã giúp người hỏi định hình vấn đề rõ ràng hơn, điều này gián tiếp cải thiện chất lượng của cả câu hỏi (prompt) chứ không chỉ context.

Điều quan trọng là "ít hơn" ở đây không có nghĩa là "thiếu" — mục tiêu không phải là cắt giảm mù quáng, mà là chọn lọc có chủ đích (targeted inclusion): đưa đúng file, đúng hàm, đúng đoạn liên quan, kèm đủ thông tin phụ trợ cần thiết (ví dụ type definition, interface mà function đó phụ thuộc). Đây chính là lằn ranh giữa "tối ưu token" và "cắt xén context đến mức model không đủ thông tin để trả lời đúng" — hai điều hoàn toàn khác nhau.

Trong các bài tiếp theo của module này, bạn sẽ học các kỹ thuật cụ thể để đạt được sự chọn lọc đó: xây dựng repo map và file summary để model có bản đồ tổng quan mà không cần đọc hết code, kỹ thuật targeted file inclusion dựa trên phân tích dependency, dùng file cấu hình như CLAUDE.md/.cursorrules để định hướng agent, và cách áp dụng RAG (retrieval-augmented generation — sinh câu trả lời có truy xuất) cho codebase lớn. Nhưng trước khi đến các kỹ thuật đó, điều quan trọng nhất là thay đổi tư duy: "đưa nhiều hơn cho chắc" không phải là chiến lược an toàn — nó là một anti-pattern có thể đo lường được bằng tiền và bằng chất lượng.

Mẹo

  • Khi review lại một AI session không đạt kết quả tốt, thử hỏi ngược lại: "Nếu tôi cắt bớt một nửa context vừa đưa, liệu câu trả lời có tốt hơn không?" — nhiều khi câu trả lời là có.
  • Đặt mục tiêu nhóm (team KPI nhẹ nhàng, không cần quá cứng nhắc): giảm token trung bình mỗi request AI mà không giảm tỷ lệ câu trả lời đúng/hữu ích — theo dõi cả hai chỉ số cùng lúc để tránh cắt giảm quá tay.
  • Nhắc nhở team rằng context window lớn (100K, 200K, 1M token) là công cụ dự phòng cho các tác vụ thực sự cần phạm vi rộng (ví dụ phân tích toàn bộ architecture), không phải lý do để bỏ qua việc chọn lọc context ở các tác vụ hàng ngày.