AI-Native Solutions Architect

Context Engineering: Kỹ thuật xây dựng kiến thức nền (file cấu hình rules, docs, codebase map) cho AI

7/19/2026 · 11p đọc


title: "Context Engineering: Kỹ thuật xây dựng kiến thức nền (file cấu hình rules, docs, codebase map) cho AI"
series: "AI-Native Solutions Architect: Từ Coder đến Kiến trúc sư AI"
season: "Season 1 — AI-Native Coding & Tooling"
order: 2
audience: "Software Engineer hướng tới Solutions Architect"
reading_time: "12 phút"
tags: ["context-engineering", "cursorrules", "codebase-map", "ai-coding", "solutions-architect", "developer-experience"]

Context Engineering: Kỹ thuật xây dựng kiến thức nền (file cấu hình rules, codebase map) cho AI

Bạn yêu cầu AI code assistant viết một endpoint mới. Nó trả về code chạy được, test pass, nhưng dùng axios trong khi cả team đã chuẩn hoá HttpService của NestJS từ hai năm nay. Biến được đặt tên userData trong khi convention của dự án là user_data cho DTO field và userData chỉ dùng ở tầng domain. Business logic tính discount nằm thẳng trong controller thay vì service — đúng thứ mà code review đã chặn không biết bao nhiêu lần.

Bạn sửa lại, review tiếp phần khác, và y hệt lỗi đó lặp lại ở PR sau. Không phải vì model "không đủ thông minh" — GPT-4 hay Claude hoàn toàn có khả năng viết Repository pattern đúng chuẩn nếu được yêu cầu. Vấn đề là mỗi lần bắt đầu một phiên chat mới, AI nhìn dự án của bạn như một người mới join ngày đầu tiên, không đọc README, không biết ai review code, không biết vì sao team quyết định không dùng axios. Nó chỉ có prompt của bạn và một vài file được mở trong context window.

Đây chính là lúc kỹ năng quan trọng nhất của một Solutions Architect trong kỷ nguyên AI xuất hiện: không phải viết prompt hay hơn, mà là thiết kế lại cách ngữ cảnh dự án được nạp vào AI một cách có hệ thống — gọi là Context Engineering.

Vấn đề

Có một ngộ nhận phổ biến: nghĩ rằng chất lượng output của AI code assistant tỷ lệ thuận với độ khéo léo của câu lệnh (prompt). Thực tế, với một dự án production-scale, biến số quyết định lớn nhất không phải là "prompt engineering" mà là lượng và chất lượng ngữ cảnh mà model nhìn thấy tại thời điểm sinh code.

Một AI code assistant không có trí nhớ dài hạn về dự án của bạn. Mỗi phiên làm việc, nó suy luận từ:

  1. System prompt / rules file mà công cụ nạp sẵn.
  2. Các file đang mở hoặc được đính kèm trong context.
  3. Câu lệnh bạn gõ trong khung chat.

Nếu ba nguồn này không chứa thông tin "dự án này dùng Repository pattern", "module X không được import trực tiếp từ module Y", "toàn bộ DTO phải validate bằng class-validator" — AI sẽ mặc định dùng kiến thức "trung bình cộng" từ dữ liệu huấn luyện, tức là pattern phổ biến nhất trên GitHub public, không phải pattern của riêng bạn.

Hệ quả kỹ thuật cụ thể:

  • Vi phạm kiến trúc âm thầm: AI viết logic nghiệp vụ trong Controller vì đó là cách viết nhanh nhất và phổ biến nhất trong tutorial, trong khi kiến trúc layered của bạn yêu cầu tách Service/Repository.
  • Convention trôi dạt (naming drift): mỗi thành viên dùng AI assistant với ngữ cảnh khác nhau, dẫn đến codebase có 3-4 kiểu đặt tên khác nhau chỉ sau vài sprint.
  • Tái phát minh bánh xe: AI đề xuất viết lại một helper đã tồn tại trong common/ chỉ vì nó không "nhìn thấy" thư mục đó trong context hiện tại.
  • Ảo giác về dependency: AI gợi ý thêm một thư viện mới (ví dụ lodash, moment) trong khi dự án đã có utility tương đương và team chủ động tránh thêm dependency.

Cách khắc phục không phải là "prompt kỹ hơn" mỗi lần — cách đó không scale với team nhiều người và nhiều phiên làm việc. Cách khắc phục đúng đắn là thiết kế các tầng ngữ cảnh persistent, để bất kỳ ai trong team, ở bất kỳ phiên chat nào, cũng nạp cùng một nền tảng kiến thức về dự án vào AI.

Kỹ thuật cốt lõi

Context Engineering chia ngữ cảnh AI cần thành ba tầng, khác nhau về độ bền (persistence), phạm vi (scope) và cách nạp vào công cụ.

flowchart TD
    A["Tầng 1: Rules File\n(.cursorrules / .clinerules / CLAUDE.md)\nBền vững toàn dự án"] --> D["AI Code Assistant"]
    B["Tầng 2: Codebase Map\n(CODEBASE_MAP.md)\nCập nhật theo kiến trúc"] --> D
    C["Tầng 3: Session Context\nFile đính kèm, đoạn code paste,\ncâu lệnh trong phiên chat"] --> D
    D --> E["Code output đúng convention"]

Tầng 1 — Rules file: đây là "hợp đồng lao động" giữa bạn và AI, được nạp tự động vào mọi phiên làm việc trong dự án đó. Tuỳ công cụ mà tên file khác nhau — .cursorrules (Cursor), .clinerules (Cline), CLAUDE.md (Claude Code), hoặc file system prompt tuỳ chỉnh — nhưng vai trò giống nhau: mô tả quy tắc bất biến của dự án. Rules file trả lời câu hỏi "làm cách nào" (coding convention, pattern bắt buộc) và đặc biệt là "không được làm gì" (điều cấm). Đây là nơi có đòn bẩy cao nhất vì một dòng rule đúng có thể chặn hàng chục lỗi lặp lại trong suốt vòng đời dự án.

Tầng 2 — Codebase map: rules file trả lời "quy tắc là gì", còn codebase map trả lời "dự án này được tổ chức ra sao". Đây là tài liệu tĩnh mô tả cấu trúc thư mục, luồng dữ liệu chính (request → controller → service → repository → DB), danh sách module quan trọng và trách nhiệm của từng module. Khác với rules file (nạp tự động), codebase map thường được đính kèm thủ công hoặc trỏ tới khi AI cần hiểu bức tranh tổng thể — ví dụ khi yêu cầu AI thiết kế một feature mới chạm nhiều module.

Tầng 3 — Session context: là phần "động" nhất — những gì bạn chủ động cung cấp trong từng cuộc trò chuyện cụ thể: file đang sửa, đoạn stack trace, schema bảng liên quan, hoặc một đoạn code mẫu để AI bắt chước style. Tầng này không thể (và không nên) chuẩn hoá thành file tĩnh, vì nó thay đổi theo từng task. Nhưng nó chỉ hiệu quả khi hai tầng dưới đã vững — nếu không, mỗi phiên chat bạn lại phải giải thích lại từ đầu những gì đáng lẽ rules file đã nên nói.

Điểm mấu chốt cho vai trò Solutions Architect: đây không phải việc của một cá nhân dev tự "tùy biến AI của mình" — đây là một artifact kiến trúc cần được review, version control, và bảo trì giống như bất kỳ tài liệu thiết kế nào khác. Rules file sai lệch với kiến trúc thực tế nguy hiểm không kém một ADR (Architecture Decision Record) lỗi thời.

Thực hành

1. File rules mẫu cho dự án NestJS

Dưới đây là ví dụ nội dung thật cho một file rules (đặt tên .cursorrules, .clinerules, hoặc phần "Quy ước" trong CLAUDE.md tuỳ công cụ) của một dự án backend NestJS giả định:

# Coding Rules — NestJS Backend

## Cấu trúc module
- Mỗi domain là MỘT module độc lập trong `src/modules/<domain>/`,
  gồm: `<domain>.controller.ts`, `<domain>.service.ts`,
  `<domain>.repository.ts`, `entities/`, `dto/`.
- KHÔNG import service của module khác bằng relative path dài
  (`../../other-module/other.service`). Phải export qua
  `<domain>.module.ts` và inject qua DI.

## Quy ước đặt tên
- Class: PascalCase (`InvoiceService`). File: kebab-case
  (`invoice.service.ts`). DB column & DTO field: snake_case
  (`created_at`, `user_id`).
- Tên method public phải là verb rõ nghĩa: `createInvoice()`,
  không dùng `handleInvoice()` hay `doInvoice()`.

## Pattern bắt buộc
- MỌI truy vấn DB phải đi qua Repository pattern
  (`<domain>.repository.ts`), KHÔNG gọi `this.dataSource.query()`
  trực tiếp trong Service.
- DTO bắt buộc dùng `class-validator` + `class-transformer`,
  KHÔNG validate thủ công bằng `if/else`.
- Business logic CHỈ được viết trong Service. Controller chỉ
  nhận request, gọi Service, trả response — KHÔNG chứa `if`
  liên quan đến nghiệp vụ (tính giá, kiểm tra quyền phức tạp...).

## Điều cấm
- KHÔNG thêm dependency mới (npm package) nếu chưa có trong
  `package.json` — đề xuất trước, không tự ý cài.
- KHÔNG dùng `any` cho type của DTO hoặc Entity.
- KHÔNG sửa schema DB trực tiếp — mọi thay đổi cột/bảng phải đi
  kèm migration file trong `src/migrations/`.

Ba vùng nội dung đáng chú ý trong ví dụ trên:

  • Cấu trúc + naming đặt ở đầu vì đây là thứ AI cần "nạp" trước tiên khi generate bất kỳ file nào.
  • Pattern bắt buộc viết dưới dạng câu mệnh lệnh ngắn, không giải thích dài dòng lý do — lý do nên nằm ở ADR riêng, rules file không phải chỗ tranh luận kiến trúc.
  • Điều cấm đặt riêng một mục, không trộn lẫn với "nên làm" — AI (và người review) cần scan nhanh phần này khi có PR đáng ngờ.

2. Cấu trúc file codebase map

File CODEBASE_MAP.md nên đặt ở root repo hoặc docs/, và giữ ngắn gọn — mục tiêu là bản đồ định hướng, không phải tài liệu đặc tả đầy đủ. Gợi ý mục lục:

# CODEBASE_MAP.md

## 1. Tổng quan kiến trúc
(1 đoạn + 1 sơ đồ: Client → API Gateway → Modules → DB/Cache/Queue)

## 2. Danh sách module chính và trách nhiệm
- `modules/auth` — xác thực, JWT, refresh token
- `modules/invoice` — hoá đơn, tính thuế, export PDF
- `modules/notification` — gửi email/push qua RabbitMQ
(chỉ liệt kê module lõi, không cần liệt kê hết)

## 3. Luồng dữ liệu chính (critical path)
- Luồng tạo đơn hàng: Controller → OrderService →
  OrderRepository → InventoryService (event) → NotificationService
- Luồng auth: Guard → AuthService → Redis session check

## 4. Dependency & tích hợp bên ngoài
- MySQL (TypeORM), Redis (session/cache), RabbitMQ (async job),
  Qdrant (vector search), Firebase (client auth)

## 5. Quy tắc migration & versioning DB
(trỏ tới CLAUDE.md hoặc CONTRIBUTING.md nếu đã có)

## 6. Liên kết tới rules file & ADR
- Rules file: `.cursorrules` / `CLAUDE.md`
- ADR: `docs/adr/`

File này khác README.md ở mục đích: README phục vụ người mới join team, còn CODEBASE_MAP.md được thiết kế để đính kèm trực tiếp vào context của AI khi bạn giao một task chạm nhiều module — ví dụ prompt: "Đây là CODEBASE_MAP.md của dự án, thiết kế cho tôi luồng đồng bộ dữ liệu giữa module Invoice và module Notification, tuân theo pattern hiện có ở mục 3."

3. Prompt mẫu tận dụng cả 3 tầng ngữ cảnh

Đọc .cursorrules và CODEBASE_MAP.md (đính kèm) trước khi trả lời.

Nhiệm vụ: Thêm tính năng "huỷ hoá đơn" (cancel invoice) vào
module invoice. Yêu cầu:
- Tuân thủ Repository pattern như rule đã định nghĩa.
- Không viết business logic trong Controller.
- Khi huỷ, phát event `invoice.cancelled` để module notification
  xử lý (xem luồng ở mục 3 CODEBASE_MAP.md).
- Cần migration nếu thêm cột `cancelled_at`, `cancel_reason`.

Trả lời gồm: entity thay đổi, service method, migration file,
và giải thích ngắn nếu có điểm nào rule chưa cover rõ.

Prompt này không "hay" ở câu từ — nó hiệu quả vì buộc AI tham chiếu rules file và codebase map một cách tường minh, thay vì hy vọng model tự suy luận đúng convention.

Cạm bẫy thường gặp

Rules file quá dài khiến AI "quên" quy tắc ở cuối. Context window có giới hạn hiệu dụng (effective context) nhỏ hơn nhiều so với giới hạn kỹ thuật (technical context) — model có xu hướng ưu tiên thông tin ở đầu và cuối, "lơ" phần giữa nếu file rules dài hàng trăm dòng. Một file rules 300 dòng liệt kê chi tiết từng edge case thực tế kém hiệu quả hơn một file 40-50 dòng tập trung vào pattern cốt lõi và điều cấm quan trọng nhất. Nếu cần chi tiết hoá, tách thành nhiều file nhỏ theo domain (rules/auth.md, rules/invoice.md) và chỉ đính kèm file liên quan vào session context thay vì nhồi tất cả vào một rules file toàn cục.

Không cập nhật rules file khi kiến trúc thay đổi. Đây là lỗi âm thầm và nguy hiểm hơn lỗi đầu tiên: team quyết định migrate từ REST sang GraphQL cho một domain, hoặc đổi từ Repository pattern thủ công sang dùng TypeORM QueryBuilder trực tiếp — nhưng rules file vẫn mô tả kiến trúc cũ. AI sẽ tiếp tục đề xuất pattern lỗi thời một cách rất tự tin, và vì nó "trích dẫn đúng rules file", reviewer dễ bị đánh lừa rằng code đó tuân thủ chuẩn. Rules file và codebase map cần nằm trong quy trình review PR giống như code — khi PR thay đổi kiến trúc, PR đó phải kèm cập nhật rules file tương ứng, không phải việc làm "sau này rảnh thì update".

Ảo tưởng rằng rules file thay thế được code review. Rules file giảm tần suất lỗi lặp lại, không loại bỏ nhu cầu review — AI vẫn có thể diễn giải sai một rule mơ hồ, hoặc áp dụng đúng rule nhưng sai ngữ cảnh nghiệp vụ.

🧭 Góc nhìn Solutions Architect
Rules file của team bạn có đang mô tả kiến trúc thực tế hiện tại, hay kiến trúc của 6 tháng trước? Ai là người chịu trách nhiệm cập nhật nó khi có quyết định kiến trúc mới — có nằm trong Definition of Done của một ADR không? Và nếu một dev mới dùng AI với rules file này trong tuần đầu tiên, liệu code họ tạo ra có "trông giống" codebase hiện tại, hay giống một dự án NestJS mẫu bất kỳ trên mạng?

🔗 Bài viết liên quan


Bài trước: AI-Native Workflow · Bài tiếp theo: Advanced Prompting

Context Engineering: Kỹ thuật xây dựng kiến thức nền (file cấu hình rules, docs, codebase map) cho AI