02. Prompting as an Engineering Discipline¶
Why does this matter?¶
A prompt is code for a neural network. The difference from ordinary code is that it works by steering probabilities.
Without proper prompting an agent will:
- Act randomly, without logic
- Skip important analysis steps
- Perform dangerous actions without confirmation
- Reply in the wrong format
In this chapter we'll look at how to write prompts that actually control agent behavior.
A real-world case¶
Situation: You built an incident-response agent. The user writes: "Service is down, fix it"
Problem: The agent immediately restarts the service without checking the logs. Or the opposite: it spends a long time analyzing and never applies a fix.
Solution: A proper System Prompt with an SOP (Standard Operating Procedure) defines the order of actions: status → logs → analysis → fix → verification.
TL;DR: What to Remember¶
- The System Prompt is a specification of agent behavior. It consists of: Role, Goal, Constraints, Format, SOP.
- In-Context Learning (ICL): Zero-shot (instruction-only) — compact and flexible, but the model may misinterpret the format. Few-shot (demonstration) — instruction plus examples, more accurate for complex formats. In practice — a combination.
- CoT (Chain-of-Thought): makes the model think step by step. Critical for agents.
- SOP: an action algorithm encoded in the prompt. It defines the process; CoT helps the model follow it.
- Task Decomposition: break complex tasks into subtasks.
- The Tools Schema is passed in a separate
tools[]field, not inside the System Prompt. - Few-shot examples must be consistent — one format, otherwise the model gets confused.
Structure of the System Prompt¶
A good System Prompt consists of five blocks:
1. Role (Persona) — who you are
2. Goal — what you need to achieve
3. Constraints — what you must not do
4. Format — how to format your reply
5. SOP — the action algorithm
System prompt template¶
You are [Role] with [experience/qualifications].
Your goal is [Goal].
Constraints:
- [Constraint 1]
- [Constraint 2]
Reply format:
- [Format rule 1]
- [Format rule 2]
SOP for [task type]:
1. [Step 1]
2. [Step 2]
3. [Step 3]
A DevOps example¶
You are a Senior DevOps Engineer. Your goal is to restore service
health without breaking what still works.
Invariants:
- Destructive actions (restart, rollback, data deletion) — only after
confirmation: the system may actually be alive and the alert false,
and a restart would make things worse.
- Do not run commands that wipe data outside the incident zone:
recovering from them costs more than the incident itself.
Usual incident investigation path (not a rigid order — if there is not
enough data, gather more; the main rule is: never apply a fix before
analysis):
- check the service status (check_http)
- if the status is not 200 — read the latest logs (read_logs)
- use the logs to identify the likely cause (deploy, DB, resources)
- apply the minimal fix (restart/rollback)
- check the status again (check_http)
Important: delimiter strings in a prompt are a hint to the model, not protection. The model can ignore them or interpret them differently. The real boundary is enforced by code: the tool allowlist, access rights, and mandatory confirmation of dangerous actions via a code mechanism (a Confirm callback or a required tool argument). The prompt explains to the model why the invariant exists — this lowers the chance of spontaneous violations, but it does not replace enforcement. How this is done — see Chapter 05: Safety and HITL and Chapter 17: Security and Governance.
A support example¶
You are a Tier 2 Customer Support Agent.
Your goal is to resolve the user's problem quickly and politely.
Constraints:
- Always stay polite
- If the problem is complex — escalate
- Do not give technical details if the user is not technical
Reply format:
- Use a structured format: {"action": "...", "user_id": "..."}
SOP for handling a ticket:
1. Read the ticket in full (get_ticket)
2. Collect context (software version, OS, browser)
3. Search the knowledge base (search_kb)
4. If a solution is found — compose the answer
5. If not — escalate (escalate_ticket)
A data analytics example¶
You are a Data Analyst with experience in SQL and BI tools.
Your goal is to provide accurate data and analytics.
Constraints:
- Work only with read-only SQL (SELECT): a mutating query can corrupt production data, and it cannot be restored from an analytics session
- Check data quality before analysis: on dirty data your conclusions will mislead
- If the data is wrong — say so instead of "fudging" conclusions
Reply format:
- First show the SQL query
- Then the results and the analysis
SOP for analysis:
1. Understand the user's question
2. Formulate the SQL query
3. Verify the table schema (describe_table)
4. Run the query (sql_select)
5. Analyze the results
6. Generate the report
The system prompt is a cacheable prefix¶
LLM APIs have a prompt cache: an unchanging prefix of the request is cached and is noticeably cheaper and faster. This gives us practical rules:
- The system prompt must be immutable from request to request. Any change — a timestamp, a counter, a "personalized" insertion — breaks the cache and makes every request more expensive.
- All dynamics (current time, tool results, new facts) go at the end of the
messageshistory — via tool results and appended messages. Never mutatemessages[0]. time.Now()and other changing values in a system prompt are an anti-pattern.- Don't pile bulk knowledge into the prompt: large instructions live in skill files that the model reads itself as needed (progressive disclosure) — 10 skills take ~10 lines in the prompt, not 10 pages.
More detail in Chapter 12: Agent Memory and Chapter 13: Context Engineering.
In-Context Learning (ICL): Zero-shot and Few-shot¶
Now that we've defined the System Prompt structure, the question arises: what is the best way to convey the desired behavior to the model?
In-Context Learning (ICL) is the model's ability to adapt its behavior based on examples inside the prompt, without changing the model's weights.
ICL works in two modes:
Zero-shot (instruction only)¶
We give the model only the instruction, no examples:
System Prompt: "You are a DevOps engineer. When the user asks you to check logs, use the read_logs tool."
User: "Check the nginx logs"
Assistant: [The model performs the task based on the instruction]
When to use:
- Simple tasks where the model understands instructions well
- Limited context (tokens need to be saved)
- The model is already trained on similar tasks
Few-shot (instruction + examples)¶
We give the model the instruction plus examples of the desired behavior:
System Prompt: "You are a DevOps engineer. Examples:
Example 1:
User: "Check the nginx logs"
Assistant: {"tool": "read_logs", "args": {"service": "nginx"}}
Example 2:
User: "Restart the web-01 server"
Assistant: {"tool": "restart", "args": {"name": "web-01"}}
User: "Check the server status"
Assistant: {"tool": "check_status", "args": {"hostname": "web-01"}}
Important: teaching the model the tool-call format via text in the prompt is a historical technique (recovery). The mainstream approach today is native tool-calling: the model is pre-trained on tool calls, and it is enough to pass the JSON Schema in a separate
tools[]field (see Chapter 03: Tools) — the call comes back in a separatetool_callsfield, not as text. Few-shot with textual{"tool": ...}examples remains useful in two cases: non-standard textual reply formats (not tool calls) and recovery when the model or the environment does not support native tool-calling.
When to use:
- A complex reply format (JSON, structured data)
- High format-compliance accuracy is required
- The model may misinterpret the instruction
Terminology: instruction vs demonstration¶
The terms Instruction-only and Demonstration (or Few-shot) are alternative ways of describing the same two ICL modes:
- Instruction-only = Zero-shot: we describe to the model what to do (compact, flexible, but the model may misinterpret the format)
- Demonstration/Few-shot = Few-shot: we show the model examples of the desired behavior (the model copies the format precisely, but it takes more tokens)
Default rule: use a combination — instruction + 1–2 examples for complex formats or edge cases.
Table for choosing an ICL mode¶
| Mode | What we give the model | When to use | Example |
|---|---|---|---|
| Zero-shot (Instruction-only) | Instruction only | Simple tasks, the model understands instructions well, limited context, saving tokens | "You are a DevOps engineer. Check logs via read_logs." |
| Few-shot (Demonstration) | Instruction + examples | Complex reply format (JSON, structured data), high format-compliance accuracy required | Instruction + 2–3 examples of the reply format |
A practical example: Few-shot for support¶
// System Prompt with Few-shot examples
systemPrompt := `You are a Support agent.
Examples of the reply format:
Example 1:
User: "My account is locked"
Assistant: {"action": "check_account", "user_id": "extract_from_ticket"}
Example 2:
User: "I can't log in"
Assistant: {"action": "check_login", "user_id": "extract_from_ticket"}`
messages := []Message{
{Role: "system", Content: systemPrompt},
{Role: "user", Content: "Access problem"},
}
// Message — the neutral struct from Chapter 03:
// type Message struct {
// Role string
// Content string
// ToolCalls []ToolCall
// }
// The model sees the examples in the System Prompt and generates a reply in the same format:
// {"action": "check_account", "user_id": "..."}
Anti-example: inconsistent Few-shot examples¶
Bad: examples in different formats
Example 1:
User: "Check the logs"
Assistant: {"tool": "read_logs", "service": "nginx"}
Example 2:
User: "Restart the server"
Assistant: {"action": "restart", "target": "web-01"} // A different format!
Example 3:
User: "Status"
Assistant: check_status("web-01") // Yet another format!
Problem: The model sees three different formats and doesn't know which one to use. The result is unpredictable.
Good: all examples in one format
Example 1:
User: "Check the logs"
Assistant: {"tool": "read_logs", "args": {"service": "nginx"}}
Example 2:
User: "Restart the server"
Assistant: {"tool": "restart", "args": {"name": "web-01"}} // Same format
Example 3:
User: "Status"
Assistant: {"tool": "check_status", "args": {"hostname": "web-01"}} // Same format
Result: The model clearly sees the pattern and follows it.
Important: few-shot sets not only the format but also hidden hints. Few-shot examples can do more than show the reply format — they can implicitly suggest the desired output. The model may "pick up" a pattern from the examples and drift toward what the demonstrations show, even when that doesn't match the real data or the task.
Problem: If the correct answer is always in position "A" in the examples, or all examples lead to one type of solution, the model may adopt that positional or topical bias.
Solution: Use diverse examples, shuffle the positions of correct answers, include counterexamples. See the section "How to Avoid Suggesting the Answer to the Model" below.
How ICL relates to other techniques¶
- ICL → CoT: Demonstrations can define not only the reply format but also the format of reasoning. For example, examples with "Thought: ... Action: ... Observation: ..." (the historical ReAct format — more in the CoT section below).
- CoT → SOP: An SOP is a process fixed by instructions and/or examples. CoT helps the model follow this process step by step.
See also:
- Lab 01: Basics — working with context and memory
- Lab 02: Tools — reply format via Function Calling
- Lab 06: Incident (SOP) — an SOP as an action algorithm
End-to-end Example: What Exactly the Agent Sends to the LLM¶
In brief — what lives where in a request to the LLM API:
- System Prompt — the text in
messages[0]withrole = "system": instructions (Role, Goal, Constraints), the SOP, and few-shot examples, if used. - Tools Schema — a separate
tools[]field (NOT inside the prompt): JSON Schema descriptions of the tools. The model sees them together with the prompt. - User Input —
role = "user"messages in themessageshistory: the current request plus previous user/assistant messages. - Tool Results — your runtime appends a
role = "tool"message to the end of the history with atool_call_idlinking the result to the call.
A compact request skeleton in the middle of a loop (one tool call has already happened). The shape matches the course's neutral Go structs (Request/Message/ToolCall, ch. 04); the exact wire format of the HTTP request ("type": "function" + the function wrapper) is in Chapter 03:
{
"model": "glm-5.3",
"messages": [
{ "role": "system", "content": "You are a DevOps engineer..." },
{ "role": "user", "content": "Check the nginx status" },
{ "role": "assistant", "content": "",
"tool_calls": [
{ "id": "call_1", "name": "check_status", "arguments": "{\"service\": \"nginx\"}" }
] },
{ "role": "tool", "tool_call_id": "call_1", "content": "nginx is ONLINE" }
],
"tools": [
{ "name": "check_status", "description": "Check service status",
"parameters": {
"type": "object",
"properties": { "service": { "type": "string" } },
"required": ["service"]
} }
]
}
The loop: the model returns tool_calls → your code (the runtime) validates the call, executes the tool and appends the result to the end of messages → a new request is sent with the extended history. No magic: the model simply sees the results of previous tools in its context and generates the next step based on them.
A full walkthrough of the tool-calling protocol — formats, argument validation, error handling — in Chapter 03: Tools and Function Calling.
Chain-of-Thought (CoT): Think Step by Step¶
Chain-of-Thought (CoT) is the "think step by step" technique. For agents, CoT is critical.
When is CoT needed?¶
Bad: "Fix the server" (one step)
Good: "Analyze the situation, form a hypothesis, test it, propose a solution" (a chain)
CoT is needed for:
- Complex tasks that require analysis
- Tasks with multiple steps
- Tasks where a hypothesis must be tested before acting
CoT is not needed for:
- Simple one-step tasks
- Tasks where the reply format matters more than the process
How do you set CoT?¶
CoT can be set via an instruction (Zero-shot) or via examples (Few-shot):
Zero-shot CoT:
Think step by step:
1. Analyze the situation
2. Form a hypothesis
3. Test the hypothesis
4. Apply the solution
Few-shot CoT (the historical ReAct format):
Example of reasoning (this shows how the agent works in a loop):
Thought: The user complains about slowness. I'll start by checking the metrics.
Action: get_cpu_metrics()
Observation: CPU 95%, process: ffmpeg
Thought: ffmpeg is eating resources. I'll check what this process is.
Action: get_process_info(pid=12345)
The model sees an example of the reasoning format and follows it. In practice this happens in a loop: each Observation is appended to the history by the runtime (your code), and the model sees it on the next iteration.
Context: where this format comes from. "Thought / Action / Observation" is the ReAct text protocol (2022): back then models could not call tools natively, and the whole agent loop had to be expressed as text in the prompt. Today a separate textual format is unnecessary: "Thought" is simply the
contentof the model's reply alongsidetool_calls, and "Observation" is arole = "tool"message. Knowing ReAct is useful as the origin of the agent loop and as a recovery technique when the model or the environment does not support native tool-calling — but not as the primary protocol.
CoT and the agent loop¶
The "Thought-Action-Observation" format is a formalization of the Agent Loop (the agent's iterative process) proposed in the ReAct paper. Back then the whole loop lived in text; today the same loop is expressed through native API fields.
How it works (mapped to the ReAct format):
- Thought — the
contentof the model's reply: a brief analysis of the situation and the plan for the next step, written by the model alongsidetool_calls - Action — the
tool_calls: the model picks a tool and its parameters (generates atool_call) - Observation — the runtime (your code) executes the tool and appends the result to the history (
role = "tool") - The loop repeats: the model sees the Observation in its context, thinks again (Thought), and picks the next Action
Example of an iteration:
Iteration 1:
Thought: "I need to check CPU metrics"
Action: get_cpu_metrics()
Observation: "CPU 95%, process: ffmpeg" ← added to history by the runtime
Iteration 2 (the model sees the Observation in context):
Thought: "ffmpeg is eating resources. Let me check the process details"
Action: get_process_info(pid=12345)
Observation: "It's a video conversion" ← added to history by the runtime
Iteration 3:
Thought: "Legitimate process, but it's choking the system. I'll suggest throttling its priority"
Action: [final answer to the user]
Important: There is no magic here — the model simply sees the results of previous tool calls in its context (messages[]) and generates the next step based on that context. (The notation above is an educational pseudo-notation for ReAct; in a real model response the same steps appear as content + tool_calls.)
Important: CoT can be post-hoc. The chain of thought (CoT) a model generates does not necessarily reflect its actual inference process. The model may first "solve" the task (via its probabilistic mechanism) and only then produce a "nice explanation" of that solution.
The problem: Polished CoT is not proof that the answer is correct. The model can confidently rationalize a wrong conclusion.
The solution: Don't rely on CoT as the sole source of truth. Verify answers with tools, evals, and runtime validation. For critical decisions, use Human-in-the-Loop regardless of how good the explanation sounds.
See also: Chapter 04: Autonomy and Loops — how the ReAct loop and the agent's iterative process work.
CoT examples in different domains¶
Below are educational reasoning trajectories in ReAct notation. Read them as an illustration of the agent's train of thought; in a real loop with native tool calling, the same steps are content (the reasoning) and tool_calls (the action) in the model's responses.
DevOps¶
Thought: The user complains about slowness. I'll start by checking metrics.
Action: get_cpu_metrics()
Observation: CPU 95%, process: ffmpeg
Thought: ffmpeg is eating resources. Let me check what this process is.
Action: get_process_info(pid=12345)
Observation: It's a video conversion started by the user
Thought: This is a legitimate process, but it's blocking the system. I'll suggest throttling its priority.
Security¶
Thought: A suspicious-activity alert came in. I'll start with triage.
Action: query_siem(query="host=192.168.1.10 AND time>now-1h")
Observation: Many failed login attempts
Thought: This looks like brute force. Let me check the source.
Action: get_source_ip()
Observation: IP from an unfamiliar country
Thought: High risk. I'll isolate the host, but first I'll request confirmation.
Data analytics¶
Thought: The user is asking about sales. I need to see what data is available.
Action: describe_table("sales")
Observation: The table contains fields: date, region, amount
Thought: Now I'll write an SQL query for last month's sales.
Action: sql_select("SELECT region, SUM(amount) FROM sales WHERE date > NOW() - INTERVAL '1 month' GROUP BY region")
Observation: Results: Region A: 100k, Region B: 150k
Thought: I'll analyze the results and draw conclusions.
SOP and Task Decomposition: The Agent's Process¶
An SOP (Standard Operating Procedure) is an algorithm of actions encoded in a prompt. It defines the process; CoT helps the model follow it step by step.
The chain: goal → constraints → SOP → CoT¶
- Goal/Constraints set the frame (what to achieve, what must not be done)
- SOP sets the process (which steps to perform)
- CoT helps follow the steps (thinking at each stage)
Why is this necessary?¶
Without an SOP, the agent can "flail": restart immediately, then read logs, then restart again.
Why an SOP matters: it directs the model's attention
Without an SOP, the model sees: User: Fix it. Its probabilistic mechanism may output: Call: restart_service. That's the most "popular" action.
With an SOP, the model is forced to generate text like:
- "Step 1: I need to check the HTTP status." → This raises the probability of calling
check_http - "HTTP is 502. Step 2: I need to check logs." → This raises the probability of calling
read_logs
We steer the model's attention down the right channel by explicitly spelling out the steps in the prompt.
Example SOP for incidents (DevOps)¶
SOP for a service outage:
1. Check Status: Check the HTTP response code
2. Check Logs: If 500/502 — read the last 20 lines of logs
3. Analyze: Look for keywords:
- "Syntax error" → Rollback
- "Connection refused" → Check Database
- "Out of memory" → Restart
4. Action: Apply the fix according to your analysis
5. Verify: Check the HTTP status again
Example SOP for support¶
SOP for handling a ticket:
1. Read: Read the ticket in full
2. Context: Gather context (version, OS, browser)
3. Search: Look for similar cases in the knowledge base
4. Decide:
- If a solution is found → Draft reply
- If it's a complex issue → Escalate
5. Respond: Send the answer to the user
Example SOP for security¶
SOP for alert triage:
1. Severity: Determine the criticality (Low/Medium/High/Critical)
2. Evidence: Gather evidence (logs, metrics, network traffic)
3. Triage:
- If False Positive → Close alert
- If True Positive → Containment (with confirmation!)
4. Report: Generate a report for the SOC
Structuring tasks (task decomposition)¶
Complex tasks should be broken into subtasks.
Without decomposition:
With decomposition:
User: "Sort out the database problem"
Assistant:
1. Check DB availability (ping_db)
2. Check metrics (cpu, memory, connections)
3. Read the logs (read_db_logs)
4. Analyze the errors
5. Propose a solution
Common Prompting Mistakes¶
Mistake 1: A prompt that is too generic¶
Symptom: The model doesn't understand what to do and answers in generalities.
Cause: The prompt doesn't define a specific role, goal, or process.
Solution:
// BAD:
You are a helpful assistant.
// GOOD:
You are a Senior DevOps Engineer with 10 years of experience.
Your goal is to restore services to a working state.
Follow the SOP: 1. Check Status 2. Check Logs 3. Analyze 4. Fix 5. Verify
Mistake 2: No CoT¶
Symptom: The model tries to do everything at once and skips analysis steps.
Cause: The prompt doesn't specify a reasoning format (Chain-of-Thought), so the model doesn't "think" step by step.
Solution:
// BAD:
Fix the server.
// GOOD:
Think step by step:
1. Analyze the situation
2. Form a hypothesis
3. Test the hypothesis
4. Apply the fix
Mistake 3: No constraints¶
Symptom: The model may perform dangerous actions without confirmation.
Cause: The prompt doesn't constrain the agent's actions — so the model follows the "fix it fast" principle.
Solution:
// BAD:
You are a DevOps engineer. Do whatever is needed.
// GOOD:
You are a DevOps engineer. Your goal is to restore the service without creating new damage.
Constraints:
- Delete data only after confirmation: the system may still be alive
and the alert may be false, and deleted data cannot be rolled back
- Gather facts (status, logs) before any action: acting without a diagnosis
usually hurts more than it helps
Remember: these lines are hints to the model that reduce the probability of a mistake. The guarantee comes from code: a tool allowlist and confirmation of dangerous actions as a runtime mechanism (ch. 05).
Mistake 4: A suggested answer (anchoring bias)¶
Symptom: The model biases its answer toward whatever is explicitly suggested in the prompt or the user's request, even if it's wrong.
Cause: The prompt or request contains explicit hints about the desired answer ("I think the correct answer is A", "it's probably X"). A model trained to maximize human preferences (RLHF) may "flatter" and agree.
Solution:
// BAD:
User: "I think the problem is in the database. Check the logs."
System Prompt: "The user believes the problem is in the DB. Check the logs."
// GOOD:
User: "I think the problem is in the database. Check the logs."
System Prompt: "Check the logs and analyze all possible causes. Don't limit yourself to the user's assumptions."
Practice: Separate facts (data, logs, metrics) from the user's preferences and hypotheses. Don't put preferences into the context as facts.
Mistake 5: Few-shot bias ("the answer is always a")¶
Symptom: The model picks up a positional pattern from the examples: if the correct answer in few-shot examples is always in position "A", the model starts choosing "A" more often than it should.
Cause: The few-shot examples lack variety; correct answers always sit in the same position or follow the same pattern.
Solution:
// BAD:
Example 1: Question → Answer A (correct)
Example 2: Question → Answer A (correct)
Example 3: Question → Answer A (correct)
// GOOD:
Example 1: Question → Answer A (correct)
Example 2: Question → Answer B (correct)
Example 3: Question → Answer C (correct)
// Or shuffle the positions and add counterexamples
Practice: Shuffle the positions of correct answers, include varied examples, and add counterexamples (wrong answers with an explanation of why they are wrong).
Mistake 6: Blind faith in CoT¶
Symptom: The developer takes the model's "nice explanation" as proof that the answer is correct, without checking the answer itself.
Cause: The CoT looks logical and convincing, but may be a post-hoc rationalization of a wrong conclusion.
Solution:
// BAD:
Model: "Thought: The problem is in the DB. Action: restart_database()"
Developer: "Great, that's logical!" → Executes the action without checking
// GOOD:
Model: "Thought: The problem is in the DB. Action: restart_database()"
Developer: Verifies with tools (check_db_status, read_logs),
validates with evals, requests confirmation for critical actions
Practice: CoT is a tool for improving the model's reasoning, not a source of truth. Always verify answers with tools and evals, especially for critical actions.
How to Avoid Suggesting the Answer to the Model¶
Few-shot and CoT can inadvertently "suggest" the desired conclusion to the model. Here is a checklist for avoiding this:
Checklist: Neutral wording¶
Good:
- Use neutral wording in requests
- Separate facts from the user's preferences
- Don't include assumptions as facts in the context
Bad:
- "I think the correct answer is A, verify it"
- "The user believes X; treat it as a fact"
- "The problem is most likely Y; check only Y"
Checklist: Variety in Few-shot examples¶
Good:
- Shuffle the positions of correct answers (A, B, C, D)
- Include diverse task types
- Add counterexamples (wrong answers with an explanation)
Bad:
- All correct answers in position "A"
- All examples of the same type
- Only positive examples
Checklist: Separating preferences from facts¶
Good:
Facts (include in context):
- Logs show a 500 error
- CPU metrics: 95%
Preferences/hypotheses (separately, not as facts):
- The user suspects a DB problem
- Hypothesis: the problem is in the network
Bad:
Facts:
- The problem is in the database (this is an assumption, not a fact!)
- Logs show a 500 error
Checklist: Verification via tools¶
Good:
- Verify answers with tools (read_logs, check_status)
- Use evals to check resistance to leading hints
- Don't rely on CoT alone as proof
Bad:
- Accepting the model's answer just because the CoT looks logical
- Not checking answers with tools
- No evals for anchoring resistance
Example: An Anti-Prompt and a fixed version¶
Bad (suggests the answer):
You are a DevOps engineer. The user believes the problem is in the database.
Check the logs and confirm that the problem is in the DB.
Problems:
- Includes the user's assumption as a fact
- Suggests the desired conclusion ("confirm that the problem is in the DB")
- The model may agree even if the problem is not in the DB
Good (neutral):
You are a DevOps engineer. The user reported an issue.
Check the logs, metrics, and all possible causes.
Analyze the data objectively, without limiting yourself to assumptions.
Improvements:
- Doesn't include the assumption as a fact
- Doesn't suggest the desired conclusion
- Requires objective analysis of all causes
Acceptance Criteria / Checklist¶
Passed:
- Role (Persona) is clearly defined
- Goal is specific and measurable
- Constraints are stated explicitly
- Response format is described
- SOP (if applicable) is spelled out in detail
- CoT is enabled for complex tasks
- Few-shot examples are added (if a complex format is needed)
- Few-shot examples are consistent (one format)
- Few-shot examples are varied (no positional bias)
- The prompt doesn't suggest the desired answer (neutral wording)
- The user's preferences are separated from facts
- Answers are verified with tools and evals, not just via CoT
Not passed:
- The prompt is too generic (no specific role or goal)
- No CoT for complex tasks
- No constraints on dangerous actions
- Few-shot examples use different formats
- Few-shot examples have positional bias (all answers in position "A")
- The prompt suggests the desired answer ("confirm that X")
- The user's preferences are included as facts
- Blind faith in CoT without verification via tools
Exercises¶
Exercise 1: Write a system prompt¶
Write a System Prompt for an agent that handles support tickets:
Expected result:
- Role: Support Agent
- Goal: resolve the user's issue
- Constraints: politeness, escalation of complex cases
- Format: structured JSON with
actionanduser_idfields - SOP: Read → Context → Search → Decide → Respond
Exercise 2: Add CoT¶
Improve the prompt from Exercise 1 by adding Chain-of-Thought:
Expected result:
- A reasoning format has been added: the model first analyzes and formulates a hypothesis, then acts
- In native tool calling, the reasoning lives in the
contentof the response next totool_calls— a separate text format (Thought/Action/Observation) is not needed - Or an instruction to "Think step by step" (if zero-shot)
For the curious¶
This section explains the mechanics of how few-shot prompting and CoT work at the model level. Feel free to skip it if you only care about the practice.
Why Few-shot works¶
The self-attention mechanism¶
The self-attention mechanism in a transformer lets the model "see" connections between tokens in the context. When the model sees examples:
- Intuitively: the model "adapts" to the pattern it sees in the context
- Mechanically: the vector representations of tokens shift toward the expected format, because the preceding tokens (the examples) have set that context
- Practically: the quality of the result depends heavily on the quality of the examples and their consistency
Why CoT works¶
Imagine the task: "What is 23 * 41 + 12?"
Without CoT:
- The model has to produce the answer "955" immediately
- That requires an enormous amount of computation in a single step
- The probability of error is high (the model may "guess" wrong)
With CoT:
- The model generates: "23 * 40 = 920... 23 * 1 = 23... sum 943... plus 12... answer 955"
- By generating intermediate tokens ("920", "943"), the model offloads computation into the context
- The next token is predicted from the extended context, which now contains the intermediate results
- This turns a hard problem into a series of easy ones
For agents, CoT is critical, because agents solve complex multi-step problems where each step depends on the previous one.
Relation to Other Chapters¶
- LLM physics: understanding the probabilistic nature of LLMs helps you write effective prompts — see Chapter 01: LLM Physics
- Tools: how the prompt affects tool selection — see Chapter 03: Tools
- Autonomy: how SOPs and CoT work in the agent loop — see Chapter 04: Autonomy
What's Next?¶
Once you've finished with prompting, move on to:
- 03. Tools and Function Calling — how the agent interacts with the real world