Một prompt được kiến trúc tốt không phải là "viết văn hay" — đó là kỹ thuật thiết kế context có chủ đích, biến AI từ một cỗ máy đoán chữ thành một cộng sự kỹ thuật đáng tin cậy.
System Prompt vs. User Prompt — Vì Sao Sự Phân Biệt Này Quan Trọng
Khi làm việc với LLM (Large Language Model) qua API hoặc qua các công cụ như Claude Code, Cursor, hay bất kỳ AI coding assistant nào, bạn thực chất đang gửi đi ít nhất hai luồng thông tin khác nhau: system prompt và user prompt. Nhiều kỹ sư — kể cả những người đã dùng ChatGPT hay Claude hàng ngày — vẫn nhầm lẫn hai khái niệm này, và hậu quả là họ nhồi nhét mọi thứ vào một chỗ, khiến model khó phân biệt đâu là quy tắc cố định và đâu là yêu cầu tình huống.
System prompt là lớp chỉ dẫn nền, được thiết lập một lần và áp dụng xuyên suốt cho toàn bộ phiên làm việc (session). Nó định nghĩa "bản chất" của AI trong ngữ cảnh cuộc trò chuyện: vai trò, phạm vi trách nhiệm, các ràng buộc không đổi, quy ước coding style, cách xử lý các tình huống biên (edge case). Trong Claude Code, đây chính là vai trò của file CLAUDE.md — nó được nạp vào context như một system-level instruction, áp dụng cho mọi turn tiếp theo mà không cần lặp lại.
User prompt, ngược lại, là yêu cầu cụ thể cho tác vụ hiện tại — "sửa bug này", "viết test cho function kia", "refactor class nọ". Nó thay đổi liên tục theo từng turn.
Vì sao phải tách bạch? Ba lý do kỹ thuật cụ thể:
-
Độ ưu tiên (priority weighting). Hầu hết các LLM hiện đại — bao gồm dòng Claude — được huấn luyện (fine-tune) để đối xử với system prompt như một nguồn chỉ dẫn có trọng số ưu tiên cao hơn user prompt thông thường. Nếu bạn đặt một quy tắc quan trọng ("luôn viết test trước khi sửa code") vào system prompt, model có xu hướng tuân thủ nhất quán hơn là khi bạn nhắc lại nó lẫn lộn giữa hàng chục dòng yêu cầu nghiệp vụ trong user prompt.
-
Tránh lặp lại và tiết kiệm context window (không gian ngữ cảnh — phần bộ nhớ hữu hạn mà model có thể "nhìn thấy" trong một lần xử lý). Nếu mọi convention của team (naming convention, cách viết commit message, quy tắc bảo mật) đều nằm trong system prompt, bạn không cần gõ lại chúng ở mỗi câu hỏi. Đây chính là lý do
CLAUDE.mdtồn tại: nó là system prompt "sống" của một dự án. -
Tách biệt mối quan tâm (separation of concerns). Khi debug một prompt không hoạt động đúng, việc tách system/user giúp bạn nhanh chóng xác định vấn đề nằm ở "quy tắc nền" hay ở "yêu cầu cụ thể" — giống hệt tư duy tách config khỏi business logic trong phần mềm truyền thống.
Một ví dụ thực tế. Giả sử team bạn luôn muốn AI review code theo chuẩn OWASP Top 10 và không bao giờ đề xuất bỏ qua validation input. Đặt trong system prompt:
You are a code review assistant for a fintech backend team.
Always evaluate security implications against OWASP Top 10.
Never suggest skipping input validation, even for "internal-only" endpoints.
Flag any use of string concatenation in SQL queries as a blocking issue.
Sau đó, user prompt cho từng tác vụ chỉ cần ngắn gọn:
Review the PR diff below for the /api/transfer endpoint.
Nếu không tách bạch, bạn sẽ phải nhắc lại toàn bộ đoạn OWASP mỗi lần hỏi — vừa tốn token, vừa dễ quên, vừa khiến model khó phân biệt cái gì là "luật bất biến" và cái gì là "yêu cầu một lần".
Mẹo: Nếu bạn thấy mình copy-paste cùng một đoạn hướng dẫn vào nhiều prompt khác nhau, đó là dấu hiệu nó nên chuyển vào system prompt (hoặc file cấu hình tương đương như
CLAUDE.md), không phải nằm rải rác trong user prompt.
Role Framing — Ví Dụ Before/After Cụ Thể
Role framing (đóng khung vai trò) là kỹ thuật gán cho AI một persona chuyên môn cụ thể trước khi giao nhiệm vụ. Nghe có vẻ như một mẹo tâm lý học vô thưởng vô phạt, nhưng thực chất nó có cơ sở kỹ thuật vững chắc: LLM được huấn luyện trên một lượng văn bản khổng lồ, trong đó có sự tương quan mạnh giữa "giọng văn của người viết" và "độ sâu, độ chính xác của nội dung". Khi bạn framing "You are a senior staff engineer", bạn đang kích hoạt (activate) vùng phân bố xác suất của model nghiêng về các pattern ngôn ngữ, mức độ chi tiết, và tiêu chuẩn chất lượng gắn với vai trò đó trong dữ liệu huấn luyện.
Before (không có role framing)
Prompt yếu, không có role framing:
Review this code:
function calculateDiscount(price, user) {
if (user.type == "vip") {
return price * 0.8;
} else if (user.type == "regular") {
return price * 0.95;
}
return price;
}
Với prompt kiểu này, output thường khá hời hợt — model có xu hướng liệt kê những nhận xét bề mặt, kiểu: "Nên dùng === thay vì ==", "Nên thêm comment giải thích logic", "Hàm này trông ổn". Nó dừng ở mức cú pháp (syntax), không đào sâu vào các vấn đề thiết kế, tính mở rộng (extensibility), hay rủi ro nghiệp vụ. Model không biết bạn kỳ vọng mức độ nghiêm khắc nào, nên nó mặc định về mức "trợ giúp chung chung, dễ chịu" — an toàn nhưng không có giá trị thực sự cho một kỹ sư senior.
After (có role framing)
Cùng đoạn code, nhưng với role framing rõ ràng và yêu cầu cụ thể hơn:
You are a senior staff engineer specializing in e-commerce pricing systems,
with a strong bias toward catching subtle business-logic bugs and
scalability issues before they reach production. You have reviewed
hundreds of PRs that introduced silent pricing bugs, and you know these
bugs are expensive because they usually aren't caught by unit tests.
Review the following function with that lens. Specifically call out:
1. Any missing discount tiers or fallback behavior that could silently
undercharge or overcharge a customer.
2. Whether the discount logic will scale if we add 5 more user tiers
next quarter.
3. Any comparison or type-coercion pitfalls in JavaScript.
function calculateDiscount(price, user) {
if (user.type == "vip") {
return price * 0.8;
} else if (user.type == "regular") {
return price * 0.95;
}
return price;
}
Với prompt này, output thường sâu và cụ thể hơn hẳn. Model sẽ chỉ ra: hàm không xử lý trường hợp user.type là null/undefined hoặc một giá trị tier mới chưa được định nghĩa (silent fallback về giá gốc — một bug nghiệp vụ nghiêm trọng vì khách hàng "premium" mới có thể bị tính giá thường mà không ai biết); cấu trúc if/else if không mở rộng tốt khi có thêm tier (nên chuyển sang lookup table hoặc strategy pattern); và == thay vì === là rủi ro type-coercion thực sự trong JavaScript, không chỉ là vấn đề style. Đây là sự khác biệt giữa một checklist linter và một code review có tư duy kỹ sư thật sự.
Điểm mấu chốt: role framing hiệu quả nhất khi nó đi kèm ngữ cảnh cụ thể về việc gì cần soi kỹ (domain, loại bug thường gặp, mức độ nghiêm khắc), chứ không chỉ là một câu "You are an expert" chung chung — câu đó gần như không tạo khác biệt vì nó không thu hẹp không gian phân bố xác suất của model.
Mẹo: Đừng chỉ viết "You are an expert developer" — hãy cụ thể hóa domain, loại lỗi bạn lo ngại, và tiêu chuẩn bạn kỳ vọng. Role framing càng cụ thể, output càng có giá trị thực tế.
Structured Instructions — XML Tags, JSON Schema, và Danh Sách Đánh Số
Khi prompt có nhiều phần khác nhau — ngữ cảnh, yêu cầu, ràng buộc, ví dụ — việc trộn lẫn chúng thành một đoạn văn dài khiến model (và cả bạn, khi đọc lại sau này) khó phân định ranh giới giữa các phần. Structured instructions (chỉ dẫn có cấu trúc) giải quyết vấn đề này bằng cách áp một bộ khung rõ ràng lên prompt.
XML Tags cho Prompt Nhiều Phần
Claude đặc biệt được huấn luyện để nhận diện tốt các cặp thẻ XML trong prompt — đây không phải yêu cầu bắt buộc về mặt cú pháp, nhưng nó giúp model phân tách rõ ràng "đây là dữ liệu nền" khỏi "đây là việc cần làm" khỏi "đây là giới hạn không được vi phạm". Ví dụ:
<context>
The codebase uses a repository pattern for data access. All repositories
implement the `IRepository<T>` interface defined in `src/data/IRepository.ts`.
We use PostgreSQL via the `pg` driver, no ORM.
</context>
<task>
Add a `findByEmailDomain(domain: string)` method to `UserRepository` that
returns all users whose email ends with the given domain.
</task>
<constraints>
- Must use parameterized queries (no string concatenation into SQL).
- Must follow the existing error-handling pattern in `UserRepository`
(wrap DB errors in `RepositoryError`).
- Do not modify the `IRepository<T>` interface.
- Add a corresponding unit test in `UserRepository.test.ts`.
</constraints>
Cách tổ chức này giúp model xử lý chính xác hơn khi prompt dài, đặc biệt khi bạn nhúng thêm đoạn code, log lỗi, hay nhiều ví dụ trong cùng một request. Nó cũng giúp bạn tái sử dụng: mai này chỉ cần đổi nội dung trong <task>, giữ nguyên <context> và <constraints>.
JSON Schema cho Structured Output
Khi bạn cần AI trả về dữ liệu để hệ thống khác (script, CI pipeline, một agent khác) xử lý tiếp — chứ không phải để con người đọc — hãy yêu cầu output theo JSON schema tường minh. Điều này loại bỏ sự mơ hồ trong việc parse output tự do bằng ngôn ngữ tự nhiên.
Ví dụ, yêu cầu AI trả về một kế hoạch thay đổi code (change plan) dưới dạng JSON:
Analyze the request below and return a JSON object matching this schema
(no prose, no markdown fences, just the raw JSON):
{
"summary": "string, one sentence describing the change",
"affected_files": ["array of file paths that will be modified or created"],
"steps": [
{
"order": "number",
"file": "string, path to the file",
"action": "one of: create | modify | delete",
"description": "string, what changes in this file"
}
],
"risks": ["array of strings describing potential risks or breaking changes"],
"requires_migration": "boolean"
}
Request: Split the monolithic `UserRepository` class into `UserReadRepository`
and `UserWriteRepository`, both implementing a shared `IUserRepository`
interface, to reduce the blast radius of write-path bugs.
Với schema tường minh như trên, output có thể được JSON.parse() trực tiếp và đưa vào bước tiếp theo của pipeline (ví dụ: một script tự động tạo file skeleton, hoặc một agent khác đọc steps để thực thi tuần tự). Đây là kỹ thuật cốt lõi khi bạn xây dựng agentic workflow nhiều bước, nơi output của một lời gọi AI là input có cấu trúc cho lời gọi kế tiếp.
Danh Sách Đánh Số cho Tác Vụ Tuần Tự
Khi tác vụ có nhiều bước phải thực hiện theo đúng thứ tự — và thứ tự đó quan trọng về mặt logic (ví dụ: phải viết test trước khi refactor, phải backup trước khi migrate) — danh sách đánh số buộc model tuân theo trình tự thay vì tự ý sắp xếp lại hoặc gộp các bước.
Perform the following steps in exact order. Do not skip or reorder any step.
After each step, report what changed before moving to the next step.
1. Read `src/data/UserRepository.ts` and list all public methods currently exposed.
2. Write failing unit tests for each existing method in a new file
`UserRepository.legacy.test.ts`, asserting current behavior (characterization tests).
3. Run the test suite and confirm all new tests pass against the current
implementation before any refactor begins.
4. Only after step 3 passes, propose the split into `UserReadRepository`
and `UserWriteRepository`.
Danh sách đánh số đặc biệt hữu ích khi kết hợp với các AI coding agent có khả năng thực thi lệnh thật (chạy test, đọc file, ghi file) — nó đóng vai trò như một "runbook" ép agent tuân thủ quy trình an toàn, thay vì nhảy thẳng đến bước cuối vì đó là phần "thú vị" nhất của yêu cầu.
Mẹo: Kết hợp cả ba kỹ thuật khi cần: dùng XML tags để phân vùng context/task/constraints ở tầng ngoài, rồi dùng numbered list bên trong
<task>khi tác vụ đó tự nó là một chuỗi bước tuần tự.
Viết Ràng Buộc và Tiêu Chí Chấp Nhận (Acceptance Criteria) Trong Prompt
Một trong những nguyên nhân phổ biến nhất khiến output của AI "gần đúng nhưng không dùng được" là vì prompt thiếu tiêu chí thành công đo lường được (measurable acceptance criteria). Nói "viết code sạch, hiệu quả" không phải là ràng buộc — đó là mong muốn mơ hồ mà model không có cách nào kiểm chứng được liệu nó đã đáp ứng hay chưa.
Ràng buộc tốt phải kiểm chứng được (verifiable) — bằng công cụ tự động (test, linter, type-checker) hoặc bằng tiêu chí định lượng rõ ràng. So sánh:
Ràng buộc mơ hồ (tránh dùng):
Refactor this function to be cleaner and more maintainable.
Ràng buộc đo lường được (nên dùng):
Refactor `processOrder()` with these acceptance criteria:
- All 14 existing tests in `order.test.ts` must still pass unchanged.
- Cyclomatic complexity must drop from 12 to under 6 (no nested conditionals
beyond 2 levels).
- The function signature must not change (it's called from 8 other places).
- No new external dependencies may be introduced.
- The diff must stay under 150 lines; if a larger refactor is warranted,
propose it separately instead of doing it inline.
Một số dạng ràng buộc/acceptance criteria hữu ích nên cân nhắc đưa vào prompt thường xuyên:
- Ràng buộc về hành vi: "Must not break existing tests", "Must preserve the existing public API", "Output must remain backward-compatible with API version v1".
- Ràng buộc về phạm vi (scope): "Only touch files under
src/payments/", "Do not modify database migrations", "Response under 200 lines of code". - Ràng buộc về hiệu năng: "The query must not introduce an N+1 problem", "Must complete under 100ms for a table with 1M rows".
- Ràng buộc về định dạng đầu ra: "Return only the diff, no explanation", "Respond in valid JSON matching the schema above, no markdown".
- Ràng buộc về quy trình: "Ask clarifying questions before writing code if the requirement is ambiguous", "Stop and report back after step 3, do not proceed to deployment".
Việc viết ràng buộc theo dạng có thể kiểm chứng (test pass/fail, số dòng, độ phức tạp, danh sách file được phép chạm vào) biến prompt từ một yêu cầu chủ quan thành một hợp đồng (contract) mà cả bạn và AI đều có thể đối chiếu để biết công việc đã "xong" hay chưa — giống hệt tinh thần Definition of Done trong Agile.
Mẹo: Trước khi gửi bất kỳ prompt yêu cầu code change nào, tự hỏi: "Nếu AI trả về output, làm sao tôi biết nó đúng mà không cần đọc lại toàn bộ diff?" Nếu không trả lời được, ràng buộc của bạn chưa đủ cụ thể.
Các Anti-Pattern Thường Gặp Khi Viết Prompt
Ngay cả kỹ sư giàu kinh nghiệm cũng dễ mắc phải những lỗi lặp đi lặp lại khi giao tiếp với AI. Dưới đây là những anti-pattern phổ biến nhất, cùng lý do vì sao chúng làm giảm chất lượng output:
-
Yêu cầu mơ hồ (vague asks). "Làm cho code này tốt hơn", "sửa cái bug này" mà không kèm log lỗi, bước tái hiện (reproduction steps), hay tiêu chí thành công. Model buộc phải đoán, và với vấn đề mơ hồ, nó thường chọn hướng diễn giải phổ biến nhất — chưa chắc là hướng bạn cần.
-
Chỉ dẫn mâu thuẫn nhau (contradictory instructions). Ví dụ vừa yêu cầu "giữ nguyên toàn bộ API hiện tại" vừa yêu cầu "đơn giản hóa interface bằng cách gộp các method". Khi hai chỉ dẫn xung đột, model phải tự chọn cái nào ưu tiên hơn — và lựa chọn đó có thể không khớp với ý bạn.
-
Không có ví dụ (no examples). Với các tác vụ có định dạng output đặc thù (một convention đặt tên riêng, một cấu trúc response API riêng của team), việc mô tả bằng lời thường kém hiệu quả hơn nhiều so với đưa ra 1-2 ví dụ cụ thể (few-shot prompting). "Viết theo style giống hàm
formatUserResponsebên dưới" hiệu quả hơn nhiều so với việc mô tả style đó bằng văn xuôi. -
Nhét toàn bộ file không cần thiết (dumping entire files unnecessarily). Copy-paste cả một file 2000 dòng khi vấn đề chỉ nằm ở một hàm 20 dòng vừa lãng phí context window, vừa "pha loãng" tín hiệu quan trọng — model phải tìm kim trong đống rơm thay vì tập trung vào đoạn code liên quan. Nguyên tắc: chỉ đưa vào context những gì thực sự cần cho quyết định hiện tại (xem thêm ở bài "How LLMs Process Context" trong module này).
-
Không có tiêu chí thành công (no success criteria). Đã bàn kỹ ở phần trên — đây là anti-pattern đủ nghiêm trọng để lặp lại: không có acceptance criteria nghĩa là không có cách nào để cả bạn lẫn AI biết khi nào công việc thực sự hoàn thành.
-
Đặt câu hỏi kép (double-barreled prompt) mà không tách turn. Hỏi "sửa bug A, đồng thời viết thêm test cho toàn bộ module B, và refactor luôn cả C" trong một prompt duy nhất khiến model phải chia sẻ "ngân sách chú ý" (attention budget) cho ba việc không liên quan, kết quả thường là cả ba đều làm hời hợt. Tách thành nhiều turn tuần tự (xem phần Hands-On bên dưới) cho kết quả tốt hơn hẳn.
-
Giả định model "nhớ" ngữ cảnh không được cung cấp. Nhiều kỹ sư hỏi "sửa lỗi giống lần trước" hoặc tham chiếu một quyết định đã bàn ở một session khác mà không đưa lại context — với hầu hết các công cụ, mỗi session là độc lập trừ khi có cơ chế duy trì bộ nhớ rõ ràng (như
CLAUDE.mdhoặc file plan).
Mẹo: Trước khi bấm gửi một prompt dài, đọc lại và tự hỏi: "Nếu tôi là một đồng nghiệp mới, chưa biết gì về context của tôi, tôi có đủ thông tin để làm đúng việc này không?" Đây là phép thử nhanh để phát hiện phần lớn anti-pattern ở trên.
Thực Hành: Xây Dựng Prompt Nhiều Lượt (Multi-Turn) Cho Tác Vụ Phức Tạp
Với các tác vụ phức tạp — đặc biệt là refactor có phạm vi rộng, ảnh hưởng đến nhiều file — gửi một prompt "khổng lồ" duy nhất yêu cầu AI làm mọi thứ trong một lần thường cho kết quả kém hơn nhiều so với chia nhỏ thành chuỗi prompt nhiều lượt (multi-turn), trong đó mỗi turn xây dựng trên kết quả đã được xác nhận của turn trước. Cách tiếp cận này giống hệt cách một kỹ sư senior thực sự làm việc: khảo sát trước, lên kế hoạch, thực thi từng bước có kiểm chứng, không nhảy thẳng vào code.
Lợi ích của multi-turn:
- Bạn có điểm dừng để review và điều chỉnh trước khi AI đi quá xa theo hướng sai.
- Mỗi turn có context tập trung, giảm rủi ro model bị "quá tải" bởi quá nhiều yêu cầu cùng lúc.
- Nếu turn 2 cho kết quả không như ý, bạn chỉ cần sửa turn 2 mà không phải làm lại từ đầu.
Bài Tập: Refactor Data Access Layer Bằng Multi-Turn Prompt
Tình huống: bạn có một class UserRepository kiểu "God-class" (lớp ôm đồm quá nhiều trách nhiệm) — nó xử lý cả đọc, ghi, cache, và gửi event, tổng cộng hơn 600 dòng. Mục tiêu: tách thành các repository nhỏ hơn, đứng sau một interface chung, không làm hỏng bất kỳ chức năng hiện có nào.
Turn 1 — Khảo sát và lên kế hoạch (không viết code ngay):
<context>
`src/data/UserRepository.ts` is a 600-line class handling: reads, writes,
in-memory caching, and publishing domain events on user changes. It's
called from 11 different services across the codebase.
</context>
<task>
Before writing any code, analyze `UserRepository.ts` and produce a plan
to split it into smaller repositories:
- `UserReadRepository` (queries only)
- `UserWriteRepository` (inserts/updates/deletes)
- `UserCache` (caching, extracted as a separate concern, not a repository)
- `UserEventPublisher` (event publishing, extracted as a separate concern)
All repositories should implement a shared `IUserRepository` interface
that the 11 calling services can continue to depend on, so we can do
this migration without touching all 11 call sites at once.
</task>
<constraints>
- Do not write implementation code yet. Only return the plan.
- List every public method in the current class and which new class it
maps to.
- Flag any method that mixes concerns (e.g., a "write" method that also
invalidates cache and publishes an event) and propose how to
decompose it.
- Identify any behavior that depends on execution order between the
current mixed responsibilities (e.g., cache invalidated before or
after the event is published).
</constraints>
Turn 2 — Viết characterization test trước khi refactor (an toàn lưới đỡ):
Good, the plan looks correct. Before implementing the split, write
characterization tests that lock in the CURRENT observable behavior of
`UserRepository`, including the order-of-operations edge cases you
flagged (cache invalidation vs. event publishing order).
Requirements:
1. Create `UserRepository.characterization.test.ts`.
2. Cover every public method identified in your plan.
3. These tests must pass against the CURRENT implementation, unmodified.
4. Do not modify `UserRepository.ts` in this turn — only add tests.
5. After writing the tests, run them and report the pass/fail result
before we proceed.
Turn 3 — Thực thi refactor từng phần, có kiểm chứng theo bước:
All characterization tests pass. Now implement the split, one class at
a time, in this exact order:
1. Create `IUserRepository` interface based on the plan from turn 1.
2. Implement `UserCache` and `UserEventPublisher` as standalone classes,
extracted from the mixed methods you flagged. Run the
characterization tests after this step and report results.
3. Implement `UserReadRepository` implementing the read half of
`IUserRepository`. Run tests, report results.
4. Implement `UserWriteRepository` implementing the write half,
composing `UserCache` and `UserEventPublisher` internally to
preserve the exact order-of-operations from the original class.
Run tests, report results.
5. Do NOT touch any of the 11 calling services in this turn — that's a
separate follow-up task. Only leave `UserRepository.ts` as a thin
facade that delegates to the new classes, so existing call sites
keep working unchanged.
Acceptance criteria for this turn: all characterization tests must pass
after every step, and the public interface consumed by the 11 existing
call sites must remain unchanged.
Chuỗi ba turn này minh họa nguyên tắc cốt lõi: khảo sát → bảo vệ bằng test → thực thi có kiểm chứng từng bước. Mỗi turn có acceptance criteria riêng, và turn sau chỉ bắt đầu khi turn trước đã được xác nhận đạt. Đây chính là cách áp dụng tư duy kỹ sư — không phải "hy vọng AI làm đúng trong một lần" — vào việc điều khiển một AI coding agent cho các thay đổi có rủi ro cao.
Mẹo: Với bất kỳ refactor nào chạm vào logic nghiệp vụ quan trọng, luôn chèn một turn "viết characterization test trước" giữa bước lên kế hoạch và bước thực thi — đây là network an toàn rẻ nhất bạn có thể thêm vào quy trình.
Những điểm chính
- System prompt chứa quy tắc bất biến, xuyên suốt session (như
CLAUDE.md); user prompt chứa yêu cầu cụ thể theo từng turn — tách bạch hai lớp này giúp model ưu tiên đúng và tránh lặp lại chỉ dẫn. - Role framing hiệu quả khi cụ thể hóa domain, loại vấn đề cần soi kỹ, và tiêu chuẩn kỳ vọng — không chỉ là một câu "You are an expert" chung chung.
- XML tags giúp phân vùng context/task/constraints trong prompt phức tạp; JSON schema đảm bảo output có thể parse được cho pipeline tự động; danh sách đánh số ép model tuân thủ đúng trình tự các bước quan trọng.
- Ràng buộc và acceptance criteria phải đo lường được (test pass, số dòng, độ phức tạp, phạm vi file) — không phải mong muốn mơ hồ như "code sạch hơn".
- Tránh các anti-pattern: yêu cầu mơ hồ, chỉ dẫn mâu thuẫn, thiếu ví dụ, nhồi nhét context không cần thiết, không có tiêu chí thành công.
- Với tác vụ phức tạp, chia thành prompt nhiều lượt (multi-turn): khảo sát và lên kế hoạch → viết test bảo vệ hành vi hiện tại → thực thi từng bước có kiểm chứng — thay vì gửi một prompt khổng lồ yêu cầu làm mọi thứ cùng lúc.