← Tất cả bài viết
Codemap: Bản Đồ Codebase Cho AI Coding Agent
Mỗi session AI agent mới lại grep repo từ số 0. Một bản đồ codebase generate tự động cộng file patterns giảm 58% tool call — số liệu benchmark thật.

Nhìn một AI coding agent mở repo của bạn lần đầu, bạn sẽ thấy đúng một nghi thức lặp lại mỗi session: glob cây thư mục, grep tên component, đọc ba file, grep tiếp. Nó tự suy lại đúng những kiến thức định hướng — route nằm đâu, folder nào sở hữu hero, convention styling là gì — mà chính nó (hoặc anh em nó) đã suy ra hôm qua. Spawn subagent còn tệ hơn: mỗi con khởi đầu với context bằng 0, nên chi phí khám phá lặp lại theo từng agent, không phải theo project. Bài này là cách tôi sửa chuyện đó bằng một bản đồ codebase cho AI coding agent — một file CODEMAP.md generate tự động cộng một file công thức tên PATTERNS.md — kèm benchmark A/B trung thực về thứ nó thực sự mua được: giảm 58% tool call, với vài caveat sắc cạnh.
Vì sao agent khám phá lại repo mỗi session
Context window của agent khởi đầu trống rỗng. Mọi thứ nó biết về repo, nó phải mua bằng tool call — liệt kê thư mục, grep symbol, đọc file — và mỗi call là một vòng round-trip mạng đầy đủ cộng token của những gì trả về. Với câu hỏi một lần thì ổn. Nhưng kiến thức định hướng ("X ở đâu?", "cái gì xử lý Y?") ổn định giữa các session, và agent mua lại nó mỗi lần vì không có gì lưu nó lại.
Insight thì nhàm và cũ: đó chính là một cái index. Database không table-scan hai lần; repo cũng không nên bị khám phá lại hai lần. Nên tôi build cái index thành hai file commit thẳng vào repo, để agent đọc đầu tiên.
Phần 1: CODEMAP.md — index được generate, không bao giờ viết tay
CODEMAP.md được sinh bởi một script Node ~700 dòng, zero dependency — chỉ fs, path, child_process, không LLM nào trong vòng lặp. Nó chạy dưới một giây và được gắn làm pre-commit hook, nên bản đồ tái sinh ở mọi commit và không bao giờ lệch khỏi code. Nó phủ: routes, feature modules, shared components, hooks/lib, animation runtime, WebGL, các lane styling, scripts trong package.json, và chỉ TÊN biến env (không bao giờ có giá trị).
Bốn ràng buộc thiết kế hóa ra quan trọng hơn cả danh sách section:
1. Ngân sách cứng ≤18.000 ký tự. Các host agent thường cap một tool result inline quanh ~25K ký tự; bản đồ tràn cap sẽ bị platform cắt cụt, trong im lặng, và agent không biết mình mất gì. Nên generator tự ép ngân sách và cắt một cách trung thực — mỗi section có thể kết bằng dòng … +7 more (not shown), để agent biết còn nữa và fallback sang grep cho phần đuôi. (Ý tưởng budget tiering tôi học từ CodeGraph, vốn cap response retrieval đúng kiểu này.)
2. Mỗi dòng viết đúng thanh ghi của câu hỏi. Agent hỏi "X ở đâu?" và "cái gì xử lý X?" — nên mỗi dòng trả lời đúng cái đó, một dòng cho một thứ:
## Feature modules
- src/modules/hero/ — intro full viewport, timeline scrub theo scroll? (Hero.tsx + Hero.module.scss)
- src/modules/services/ — grid dịch vụ chạy từ CMS (ServicesSection.tsx)
… +7 more (not shown)
3. Đánh dấu nguồn gốc. Mô tả nào script suy luận ra (từ tên, import, vị trí folder) mang dấu ? ở cuối; fact đọc nguyên văn (tên script, một export) thì không. Agent tin thẳng các dòng không dấu, và verify dòng ? trước khi đặt cược.
4. Banner độ tươi. Dòng đầu ghi ngày generate, commit, và lệnh tái sinh — agent (hay người) liếc một cái là biết bản đồ có mới không và refresh bằng gì.
Phần 2: PATTERNS.md — công thức agent viết, script kiểm soát
Bản đồ trả lời "X ở đâu." Nó không trả lời "thêm một X thì làm thế nào" — đó là kiến thức quy trình. PATTERNS.md chứa các công thức do agent viết cho những việc lặp lại của repo (thêm section CMS-editable, thêm API route…), với luật nghiêm: công thức được trích từ code thật, không bao giờ bịa — mỗi cái phải verify trên ít nhất hai instance thật trong repo, nêu đích danh bài mẫu, và nói rõ nó cố tình không đụng vào đâu:
### Thêm một section CMS-editable
Verified against: src/modules/services/, src/modules/faq/ (2 instances)
Mirror: src/modules/services/ — copy đúng cấu trúc module này
1. Tạo src/modules/<name>/<Name>Section.tsx + <Name>.module.scss
2. Đăng ký section type trong CMS schema và switch của section-renderer
3. Thêm reader query cạnh các section reader sẵn có
Not touched: src/lib/cms/client.ts — tầng reader là generic; section mới không cần sửa gì
Công thức sẽ mục, nên generator của codemap kiểm soát chúng: mỗi lần regen nó validate mọi path trong backtick của mọi công thức so với cây thư mục, và đóng dấu công thức nào có cấu trúc đã dời đi:
### Thêm một section CMS-editable ⚠ STALE (missing: src/modules/faq/FaqSection.tsx)
Dấu stale không xóa công thức — nó bảo agent kế tiếp "verify trước khi mirror." Tự giương cờ tốt hơn sai trong im lặng. (Ý tưởng thư viện công thức bê thẳng từ skill library của Voyager: lưu quy trình đã verify, truy xuất bằng khóa ngôn ngữ tự nhiên, thay vì suy lại từ đầu.)
Benchmark: cùng repo, cùng model, bật/tắt bản đồ
Tôi A/B trên template site Next.js của mình: câu hỏi giống hệt cho các session agent mới tinh, cùng một model xuyên suốt, có và không có hai file. Khai thật giới hạn ngay từ đầu — n=3 câu hỏi, mỗi ô chạy một lần, và câu hỏi do tôi tự soạn — nên hãy đọc như một tín hiệu mạnh, không phải một paper.
| Câu hỏi | Setup | Tool call | Thời gian | Token |
|---|---|---|---|---|
| Q1 — định hướng (file render hero, SCSS module của nó, dep animation, runtime smooth-scroll, lệnh verify) | không map | 6 | 31.2s | 35.6K |
| CODEMAP | 1 | 8.7s | 37.5K | |
| Q2 — thiết kế ("mọi file phải đụng để thêm một section CMS-editable mới") | không map | 14 | 95.4s | 57.4K |
| chỉ CODEMAP | 9 | 85.5s | 61.5K | |
| CODEMAP + PATTERNS | 2 | 21.6s | 40.8K | |
| Q3 — pipeline (những script nào hợp thành pipeline blog-cover + tên env) | không map | 13 | 43.3s | 51.3K |
| CODEMAP | 4 | 40.0s | 39.7K |
Mọi câu trả lời đều đúng ở mọi ô (Q2 khớp nhau từng file; ở Q3 bản chạy có map trả lời đúng phần lõi pipeline, còn baseline đào thêm được vài thứ ngoại vi). Tổng hợp: tool call −58%, thời gian −21%, token xấp xỉ đi ngang. Q2 với tầng patterns là ngôi sao: call −86%, thời gian −77%, token −29%.
Ba cách đọc trung thực, vì cái dòng token đi ngang xứng đáng được giải thích:
- Token ≈ đi ngang trên từng câu hỏi đơn lẻ vì đọc một bản đồ 17.7K ký tự cũng tốn token — bạn đang đổi kết quả grep lấy chữ của bản đồ. Cái thắng thật nằm ở round-trip (mỗi tool call là vài giây latency) và ở flow orchestrator: dán đúng một section bản đồ vào brief của subagent, thay vì để mỗi subagent tự khám phá lại.
- Câu hỏi vị trí thắng ngay; câu hỏi thiết kế sâu chỉ thắng khi có tầng công thức. CODEMAP một mình gần như không xê dịch Q2 (14→9 call) — phải có PATTERNS mới sập nó xuống (→2).
- Bản đồ thay thế việc khám phá, không thay thế việc hiểu. Agent vẫn nên mở source thật cho việc cần phán đoán — bản đồ chỉ nó file nào, còn dấu
?/⚠ STALEbảo nó khi nào phải nghi ngờ chính bản đồ.
Mang đi nơi khác: ba repo, không đổi một dòng code
Bài test của một generator kiểu này là repo thứ hai. Tôi copy script nguyên văn vào ba repo hình dạng rất khác nhau — template site Next.js của tôi, package CMS tự host của tôi, và một prototype mới — và nó không cần sửa dòng code nào. Mẹo nằm ở section detection có cổng tồn tại: mỗi section tự dò bằng chứng của chính nó và báo cáo đúng những gì tìm thấy, kể cả không có gì. Repo không có thư mục WebGL riêng báo đúng là không có — và các công thức PATTERNS của nó phủ scene React Three Fiber thay vào, vì instance thật của nó là thứ đó.
Đó cũng là lý lẽ của tôi cho việc generate thay vì viết tay: file context viết tay (một CLAUDE.md, một trang wiki) rất tốt cho luật lệ, nhưng phần dữ kiện của nó mục ngay tuần bạn refactor. Một script chạy dưới một giây gắn pre-commit hook thì không thể quên tự cập nhật.
FAQ
Khác gì một file CLAUDE.md / instructions cho agent?
Chúng bổ trợ nhau, không cạnh tranh. File instructions mang luật và convention ("dùng SCSS module", "đừng commit X") — ổn định, viết tay, có chính kiến. Codemap mang dữ kiện về cây thư mục — dễ biến động, được generate, có đánh dấu nguồn gốc. Dữ kiện trong file viết tay mục trong im lặng; dữ kiện trong file generate tái sinh ở mọi commit.
Sao không dùng embeddings / RAG trên repo?
Với bài toán này, một script 700 dòng thắng vector store trên mọi trục tôi quan tâm: không hạ tầng, không dependency, output deterministic, diff được trong code review, và miễn phí. Semantic search đáng tiền trên monorepo đa ngôn ngữ khổng lồ; trên repo cỡ dự án, một index 18K ký tự đúng hình dạng trả lời cùng những câu hỏi đó mà không có bộ phận chuyển động nào.
Sao không để agent tự khám phá — nó sinh ra để làm thế mà?
Vì tốn tiền thật và thời gian thật, theo từng session và từng subagent, để suy lại những dữ kiện chưa hề đổi. Q1 ở trên là ca thường ngày: sáu tool call và 31 giây để học thứ mà một lần đọc bản đồ trả lời trong 8.7 giây. Khám phá dành cho câu hỏi index không trả lời được — vì thế bản đồ tự đánh dấu suy luận và độ mục của chính nó, thay vì giả vờ hoàn chỉnh.
Codemap có giúp developer con người không?
Có, như một tác dụng phụ — nó là một cái README onboarding tử tế không thể mục. Nhưng con người không phải đối tượng thiết kế: ngân sách ký tự, dòng viết theo thanh ghi câu hỏi, và dấu nguồn gốc tồn tại vì cách agent tiêu thụ tool result.
Nếu bạn đang đánh giá một developer thực sự làm việc với AI tooling thế nào trong 2026, đây là loại hạ tầng tôi muốn nói tới — cùng một sự kỹ tính như khi làm tầng dữ liệu Next.js cache đúng cách, áp vào chính workflow của agent. Nó là một nửa nghĩa của chữ "engineering" trong creative developer đối với tôi. Trọn bộ setup — generator, hook, công thức — đi theo mọi dự án tôi ship qua dịch vụ của mình.


