jvinhit//lab

Search posts

Type to search across journal entries.

navigate open esc close

Prompting Fundamentals — Từ câu hỏi mơ hồ đến instruction LLM thực sự hiểu

3 tầng của prompt (system/user/assistant), 6 nguyên tắc viết prompt hiệu quả, sampling parameters (temperature, top-p, top-k, stopping criteria), personalization qua system prompt, multi-turn strategy, và template tái dùng cho dev.

“Prompt engineering” nghe như nghề thời thượng 2023 giờ nhiều người cười: “cần gì engineer, gõ câu hỏi là xong”. Sự thật ở giữa. Gõ câu hỏi là đủ cho task đơn giản. Nhưng với task khó — code nghiêm túc, debug sâu, architecture decision — cách bạn viết prompt quyết định 50% chất lượng output.

Bài này là nền tảng. Không có mẹo “10 prompt magic”. Chỉ có nguyên tắc, parameter, template — đủ để bạn thoát khỏi giai đoạn “đoán prompt”.

Mental model giữ trong đầu cả bài: coi LLM như một đồng nghiệp cực giỏi nhưng đọc lướt, quên sạch sau mỗi câu, và nếu thiếu thông tin sẽ tự tin bịa ra. Mọi nguyên tắc bên dưới thực chất chỉ là cách giao việc cho một đồng nghiệp như vậy: nói rõ, đưa đủ ngữ cảnh, và chốt “thế nào là xong”.


1. Prompt không phải chỉ text input

Với API LLM, 1 request gồm 3 roles của message:

┌─────────────────────────────────────────────────┐
│  System message                                 │
│  Set context, personality, constraint của model  │
├─────────────────────────────────────────────────┤
│  User message 1                                 │
│  Câu hỏi đầu tiên                                │
├─────────────────────────────────────────────────┤
│  Assistant message 1                            │
│  Response của model                              │
├─────────────────────────────────────────────────┤
│  User message 2                                 │
│  Câu hỏi tiếp theo (có context 2 lượt trên)     │
└─────────────────────────────────────────────────┘

System message — bộ khung bất biến

Set 1 lần, áp dụng toàn conversation. Dùng cho:

  • Vai trò model (You are a senior TypeScript engineer).
  • Constraint (Never suggest deprecated APIs).
  • Format output (Always respond in markdown).
  • Persona (Use concise technical tone).

Trong Cursor, Rules + Skills được inject thành system message. Đó là lý do chúng “bền” qua mọi chat.

User message — câu hỏi thật

Phần biến đổi mỗi lượt. Nơi bạn hỏi, mô tả task, paste code, attach file.

Assistant message — context memory

Model không “nhớ” bạn là ai. Mọi context là text trong message history. Mỗi response cũ của model trở thành 1 assistant message, được replay lại với request mới.

Conversation dài → history dài → cost scale theo.


2. 6 nguyên tắc viết prompt hiệu quả

2.1. Specificity — càng cụ thể càng tốt

❌ "Refactor hàm này cho tốt hơn"
   → Model đoán "tốt hơn" nghĩa là gì → random direction

✅ "Refactor hàm X theo 3 tiêu chí:
    1. Extract validation logic thành helper riêng
    2. Early return khi invalid thay vì nested if
    3. Add JSDoc với @param và @returns"
   → Model có tiêu chí objective → output predictable

2.2. Context — cung cấp đủ, không thiếu không thừa

❌ "Code này có bug không?"
   + [paste 500 dòng]

→ Model search bug ngẫu nhiên, miss vì context quá rộng.

✅ "Function calculateTax (file @tax.ts line 45-67) trả về wrong value
    cho input negative. Test fail: @tax.test.ts:23.
    Expected behavior: throw on negative input.
    Check function này có validate input đúng không?"

Scope hẹp → AI focus đúng chỗ.

2.3. Output format — define trước khi gen

❌ "List các framework CSS"

✅ "List 5 framework CSS, format:
    | Framework | Use case | Bundle size | Maturity |
    |-----------|----------|-------------|----------|
    Không text giải thích ngoài bảng."

Define format = tiết kiệm token + ít rewrite.

2.4. Examples — show don’t tell

Với task không chuẩn (format mới, style riêng), cho 1-2 example:

"Chuyển commit message sang conventional format.

Input:  'Added login button to header'
Output: 'feat(header): add login button'

Input:  'Fixed date display in reports'
Output: 'fix(reports): correct date display'

Now convert: 'Updated README with setup instructions'"

Đây là few-shot learning — kỹ thuật cơ bản nhưng hiệu quả nhất.

2.5. Constraint — nói rõ KHÔNG làm gì

"Refactor component này.

CONSTRAINTS:
- KHÔNG đổi public props API
- KHÔNG add dependency mới
- KHÔNG đụng file test hiện có
- KHÔNG dùng useEffect với empty deps (codebase ban)

AI thường “helpful” quá mức — tự ý mở rộng scope. Constraint cứng chặn việc này.

2.6. Role — gán vai phù hợp

"You are a security engineer reviewing code for production banking app.
Review @login.ts with focus on:
- Authentication bypass
- Timing attack
- Token leakage
- Error handling revealing info"

Role kích hoạt pattern phù hợp trong training data. “Security engineer” → model output attention đến vulnerability chi tiết hơn so với prompt chung chung.


3. Sampling parameters — cách model “chọn” token

Model output là phân phối xác suất. Sampling parameter quyết định cách chọn token tiếp theo.

3.1. Temperature

Scale độ “sharp” của phân phối xác suất.

  • T = 0 → luôn chọn token xác suất cao nhất → deterministic, repetitive.
  • T = 0.3 → focused, ít variance.
  • T = 0.7 (default thường) → cân bằng.
  • T = 1.0 → nhiều random, sáng tạo.
  • T > 1.5 → chaotic, dễ nonsense.

3.2. Top-p (nucleus sampling)

Chỉ sample từ tập token có cumulative probability ≤ p.

  • p = 0.1 → focus top 10%, predictable.
  • p = 0.9 (default) → bao gồm phần lớn khả năng.
  • p = 1.0 → mọi token đều có thể.

3.3. Top-k

Chỉ sample từ k token xác suất cao nhất.

  • k = 1 → greedy (tương đương T=0).
  • k = 40 → moderate diversity.
  • k = ∞ → không giới hạn.

3.4. Chọn combo nào cho task nào

TaskTTop-pNote
Code refactor, translation01Reproducible, ít hallucinate
Bug fix, debugging0 - 0.20.9Cần chính xác
Code review0.2 - 0.40.9Cân bằng
Brainstorm, naming0.7 - 1.00.95Variety
Creative writing0.9 - 1.20.95Diverse output
JSON / structured output01Deterministic

Cursor / Claude.ai / ChatGPT mặc định T ≈ 0.7. Trong API call, bạn set được. Cursor cho chỉnh qua advanced settings.

3.5. Deterministic hay không?

  • T = 0 + top-p = 1 → gần như deterministic. Nhưng vẫn có variation nhỏ do floating point + batching trên GPU.
  • Muốn absolute reproducible → thêm seed parameter (OpenAI support).

Cho test suite / reproducible result: luôn T=0 + seed cố định.


4. Stopping criteria — khi nào model dừng

Model không tự biết “đủ rồi, dừng”. Nó dừng khi:

4.1. End-of-sequence token (EOS)

Token đặc biệt trong vocabulary (<|endoftext|> cho GPT). Model học từ training khi nào nên output EOS.

4.2. Max tokens (hard cap)

Parameter API. Ví dụ max_tokens=1000 → model dừng sau 1000 token dù chưa hết.

Bẫy phổ biến: set max_tokens quá thấp → response bị cắt ngang.

4.3. Stop sequences (custom)

Chỉ định chuỗi làm trigger dừng:

client.chat.completions.create(
    model="gpt-4o",
    messages=[...],
    stop=["\n\n", "---", "###"]
)

Dùng khi muốn model dừng trước 1 section cố định. Ví dụ:

System: "Trả lời, kết thúc bằng ---END---"
Stop: ["---END---"]

4.4. Programmatic stop (streaming)

Khi streaming, client có thể stop giữa chừng:

Model đang gen: "Để giải quyết vấn đề, bạn có thể dùng axios..."
User nhấn stop → request aborted → không tốn tiếp output tokens.

Cursor dùng pattern này khi bạn thấy AI đi sai hướng → stop ngay → save cost.


5. Personalization qua System Prompt

System prompt là công cụ mạnh nhất để “chỉnh” personality / behavior của model. 3 tầng:

5.1. Global — đổi mọi chat

ChatGPT “Custom Instructions”, Claude “Projects”, Cursor rule always-on.

You are helping a senior frontend engineer working with TypeScript,
Astro, and Tailwind CSS.

- Default to TypeScript strict mode
- Prefer composition over inheritance
- Use path alias ~/ (not ../..)
- Suggest existing patterns from codebase before adding dependencies
- When explaining, be concise. Use Vietnamese for prose, English for
  technical terms.

5.2. Project — đổi theo repo

Cursor .cursor/rules/*.mdc, Claude Projects, ChatGPT GPTs:

Stack for this repo: Astro 5, TypeScript strict, Tailwind v4
Tokens: --color-bg, --color-fg, --color-accent
Content: MDX với schema Zod @content.config.ts
Deploy: Cloudflare Pages

5.3. Task — đổi per chat

Inline ở user message đầu tiên:

For this conversation only: switch to pair-programming mode.
Explain reasoning step-by-step before code.
After each code block, wait for my feedback before next step.

Hiểu và dùng đúng 3 tầng → agent đúng ý 90% lần đầu, không phải correct 10 lần.


6. Multi-turn conversation — nghệ thuật duy trì context

6.1. Context accumulate theo lượt

Mỗi lượt chat, toàn bộ history (system + mọi user + assistant trước) được gửi lại. Nghĩa là:

  • Conversation 20 lượt = prompt input 20 lượt size.
  • Cost tăng tuyến tính theo độ dài.

6.2. Khi nào nên start new chat

  • Chủ đề thay đổi rõ rệt.
  • Đã có quyết định xong, bắt đầu task mới.
  • Context dính rác từ attempt thất bại.
  • Đã đạt ~50% context window.

6.3. Pattern handoff giữa chat

Kết thúc chat cũ:

"Tổng kết conversation này thành 1 artifact:
- Quyết định đã chốt
- Code đã implement
- Open question còn lại

Format: markdown, tôi sẽ paste vào chat mới."

Paste summary vào chat mới → context gọn, không mang rác.

6.4. Anti-pattern: conversation zombie

Chat 3 ngày trước, đã cố fix bug qua 30 lượt, không ra. Đừng tiếp tục — zombie context sẽ dắt mũi session mới vào cùng dead-end.

Fix: start new chat, paste problem statement clean, để AI approach fresh.


7. Prompt templates tái dùng

Template: Plan before code

Task: <mô tả>
Code context: <file list>

Trước khi code, plan:
1. Understanding: bạn hiểu task thế nào
2. Files: create / edit / read-only
3. 2-3 approach options + pros/cons
4. Risk + open questions

KHÔNG code. Hỏi tôi nếu có gì chưa rõ.

Template: Explain-then-ask

Đoạn code sau:
[paste]

1. Giải thích từng bước nó làm gì (giả sử tôi là junior dev)
2. Indentify 3 câu hỏi tôi nên hỏi về nó (performance, correctness,
   maintainability)
3. Gợi ý 1 cải tiến có impact nhất

KHÔNG refactor tự động.

Template: Structured review

Review @<file> theo 3 lớp:

**Correctness:**
- Logic đúng spec?
- Edge cases: null, empty, boundary
- Error handling

**Quality:**
- Security risk
- Performance concern
- Maintainability

**Fit:**
- Match convention @<reference file>?
- Abstraction level phù hợp?

Report issue theo priority. KHÔNG tự fix.

Template: Comparison decision

Tôi cần chọn giữa <option A> và <option B> cho <context>.

Analyze:
1. So sánh cụ thể 5 dimension: A, B, C, D, E
2. Trade-off chính
3. Gợi ý lựa chọn với lý do
4. Case nào option khác lại tốt hơn

Format: markdown table + 1 đoạn kết luận.

Template: Learn a concept

Giải thích concept X cho tôi (background: <kinh nghiệm của bạn>).

1. Analogy đời thường
2. Definition kỹ thuật (chính xác)
3. 1 ví dụ minimal code
4. 2 common pitfall khi dùng
5. Reference để đọc sâu thêm

Length: ~400 chữ. Tiếng Việt + technical English terms.

Lưu vào Cursor Skills, snippet manager, hoặc ~/.cursor/prompts/. Tái dùng sau không phải tự gõ lại mỗi lần.


8. Anti-patterns phổ biến

❌ 8.1. “Please” / “thank you” verbose

Model không được reward thêm khi bạn lịch sự. “Please” đầu prompt = đốt 3-5 token thừa × mỗi request × số dev × số năm = số lớn. Nếu xài API trả tiền.

Với subscription thì không quan trọng. Nhưng build habit concise vẫn tốt.

❌ 8.2. Hỏi mơ hồ rồi correct nhiều lần


Turn 1: "Viết API route"
Turn 2: "Không, dùng Hono chứ không Express"
Turn 3: "Path alias là ~/, không @/"
Turn 4: "Validator dùng Zod"
→ 4 round trip, 4x cost
✅ 1 prompt đầy đủ:
"Viết API route POST /users với:
- Framework: Hono
- Validator: Zod
- Path alias: ~/
- Return 201 + user object, 400 nếu email duplicate"
→ 1 round trip, output đúng lần đầu

❌ 8.3. Nhét cả codebase

❌ "@src/" để hỏi 1 chi tiết nhỏ
   → 100K+ token attach, đốt tiền, model "lost in middle"

✅ Attach đúng 2-3 file liên quan

❌ 8.4. Không define success

❌ "Improve this code"
✅ "Improve for readability. Metric: comment ratio ≥ 10%, max function
    length 20 lines, consistent naming."

❌ 8.5. Ignore format output

Model output markdown mà bạn cần JSON → copy sang, parse, fix. Waste.

Define format trong prompt ngay từ đầu: "Output: JSON with schema {...}".


9. Debugging prompt không hiệu quả

Output không như ý? Kiểm tra theo thứ tự:

  1. Model có đủ info không? → thiếu context thêm file/docs.
  2. Constraint có rõ không? → liệt kê rõ “không được làm X”.
  3. Format output có define không? → template cụ thể.
  4. Temperature phù hợp? → code task T quá cao gây random.
  5. Role prompt có match không? → “security engineer” cho security task, “senior engineer” cho code review.
  6. Context có quá dài? → chia nhỏ, start new chat.

Nếu đã check 6 điểm trên mà vẫn fail → có thể task vượt khả năng model hiện tại. Thử model khác (Claude vs GPT vs Gemini — mỗi model có strength khác), hoặc thử thinking model.


10. Tổng kết

Prompt engineering không phải magic. Nó là kỹ năng communication với máy — giống communication với người, nhưng người đó đọc rất nhanh, quên rất nhanh, và nếu thiếu info sẽ tự bịa.

5 điều quan trọng nhất:

  1. Specific > vague. Define rõ input, output format, constraint.
  2. Context right-sized. Đủ để AI làm đúng, không thừa.
  3. Parameters matter. T=0 cho code, T=0.8 cho brainstorm.
  4. System prompt = bộ khung. Dùng tốt → agent “hiểu” bạn 10x.
  5. Template tái dùng > viết lại prompt mỗi lần.

Bài tiếp theo nâng cấp những nguyên tắc này lên cấp độ agent: messages array, persona vs policy, few-shot có chọn lọc, và structured output cho vòng lặp tự trị.

Prompt Engineering for Agents


Đọc thêm

Reference

  • OpenAI Prompt Engineering guide (platform.openai.com)
  • Anthropic Prompting docs (docs.anthropic.com)
  • “Prompt Engineering Guide” — promptingguide.ai