Skip to content

Chapter 6 · Prompt Engineering: instructions and the Description System ​

Learning objectives: Master the division of labor and priority among description, instructions, and system_message in Agno; learn to write stable, maintainable Agent prompts.

6.1 Agno's Three-Layer Prompt System ​

Agno doesn't ask you to hand-write one giant system prompt — it splits it into structured configuration items. Following the official Context Engineering model:

Config itemLayerRole
descriptionTopWhat the Agent is, overall responsibility — like a "job description"
instructionsMiddleList of specific behavioral rules — like an "operations manual"
system_messageOverrideFully custom system message; when set, replaces the auto-assembled combination of the above two
python
# prompt_layers.py —— Using the three-layer system
import os
from dotenv import load_dotenv
from agno.agent import Agent
from agno.models.openai import OpenAIChat

load_dotenv()
model = OpenAIChat(
    id="deepseek-chat",
    api_key=os.getenv("DEEPSEEK_API_KEY"),
    base_url="https://api.deepseek.com/v1",
)

agent = Agent(
    model=model,
    # Job description: one sentence clarifying role and boundaries
    description="You are a rigorous Python technical interviewer.",
    # Operations manual: list behavioral rules one per line; Agno numbers and injects them into the system message
    instructions=[
        "Ask one question at a time; wait for the candidate's answer before following up",
        "Follow-up questions should target weaknesses in the previous answer",
        "Use Chinese throughout; code examples may use English identifiers",
        "Don't give the answer directly — guide the candidate to think first",
    ],
    markdown=True,
)

agent.print_response("Start the interview")

Why a list instead of one big paragraph? A list puts each rule on its own line; models follow "itemized instructions" significantly better than large paragraphs. It also makes code review and version diffs cleaner — this is the key to treating prompts as "config" rather than "copywriting."

6.2 Template Variables in instructions ​

Instructions support placeholders that are filled at runtime from session state, enabling "one prompt serves many users":

python
# prompt_template.py —— Template variable injection
agent = Agent(
    model=model,
    db=None,
    instructions=[
        "The user's current membership tier is {plan}; adjust tone accordingly",
        "When recommending to free users, always note paid feature limitations",
    ],
    session_state={"plan": "pro"},     # Values from session state fill {plan}
)

# Same Agent serves different tiers with different session_state
agent.print_response("What features are available?", session_state={"plan": "free"})

This decouples "business data" from prompt text: changing membership rules only touches session_state, not the prompt.

6.3 When to Use system_message ​

In most scenarios, description + instructions is sufficient. Only use system_message when you need full control over the raw system message text — note that it bypasses auto-assembly:

python
# system_message_demo.py —— Fully custom system message
agent = Agent(
    model=model,
    description="This field is ignored",           # ⚠️ Ineffective once system_message is set
    instructions=["Neither is this"],
    system_message="You are a pirate translator. Translate user input into pirate-style English.",
)

Recommendation

Unless migrating a legacy system or conforming to a third-party prompt spec with hard constraints, don't use system_message. Abandoning structured layering means giving up Agno's fine-grained context management.

6.4 Few-shot Example Injection ​

The most effective way to make output format extremely stable is to provide examples in the instructions (few-shot). Combined with the structured output from Chapter 5, the format is guaranteed by the schema; style and judgment criteria are anchored by examples:

python
# few_shot.py —— Anchoring classification criteria with examples
from pydantic import BaseModel, Field
from typing import Literal

class TicketCategory(BaseModel):
    category: Literal["bug", "feature", "question", "complaint"]
    urgency: int = Field(ge=1, le=5, description="Urgency level")

support_router = Agent(
    model=model,
    description="Customer service ticket classifier.",
    instructions=[
        "Output category and urgency (1–5) based on ticket content",
        "Judgment criteria examples:",
        "- 'Login button does nothing' → bug, urgency=3",
        '- "Add dark mode support" → feature, urgency=2',
        '- "Will my data be leaked?" → question, urgency=1',
        '- "I will complain to the consumer association if this isn\'t fixed!" → complaint, urgency=5',
    ],
    output_schema=TicketCategory,
)

cases = ["App crashes on launch, can't open at all", "Can you add a WeChat mini-program version?"]
for c in cases:
    r = support_router.run(c)
    print(c, "->", r.content.category, r.content.urgency)

Examples eliminate ambiguity in standards: is a "crash" a bug or a complaint? With reference samples, the model's choice becomes much more stable.

6.5 Chinese Prompt Practical Tips ​

Since the tutorial uses Chinese models and Chinese scenarios, here are some empirically effective practices:

  1. Match instruction language to task language: When having DeepSeek handle Chinese tasks, write instructions in Chinese for better term comprehension;
  2. Avoid long negative sentences: "Don't use complex sentence structures" is worse than "Keep each sentence under 25 characters";
  3. Keep rule count under 10: Beyond that, middle rules get ignored; split into multiple specialized Agents instead;
  4. Put hard format constraints at the beginning and end: First and last items have the highest compliance rates;
  5. Use additional_guidance for temporary rules (when dynamically adding behavioral requirements); keep main instructions stable for easier comparison.

6.6 Iterative Method: Test Prompts Like Code ​

The correct approach to prompt engineering is not "tweak by feel" — build a small eval set for regression testing:

python
# prompt_eval.py —— Minimal prompt regression test
EVAL_CASES = [
    ("Login page white screen", "bug"),
    ("Want Excel export support", "feature"),
    ("How to change bound phone number", "question"),
]

def evaluate(agent) -> float:
    hits = sum(
        agent.run(q).content.category == expect
        for q, expect in EVAL_CASES
    )
    return hits / len(EVAL_CASES)

print(f"Accuracy: {evaluate(support_router):.0%}")
# Run before and after modifying instructions; adopt the version with better numbers

Scale this to dozens of cases and hook into CI for periodic runs — this is the precursor to the evaluation system (Evals) in Chapter 25.

6.7 Chapter Summary ​

  • description sets the role, instructions sets the rules, system_message overrides everything (use sparingly);
  • Instructions as an itemized list + template variables {placeholder} are maintainable "config," not copywriting;
  • Few-shot examples eliminate ambiguity in judgment criteria; pair well with output_schema;
  • Chinese prompts: same language as task, fewer negations, ≤10 rules, hard constraints at start/end;
  • Prompt changes must be validated with eval sets — never ship based on feeling alone.

🧪 随堂测验

点击你认为正确的选项。答错时会展示正确答案与原因解析。

1. After setting system_message, what happens to description and instructions?

2. Instructions contain "User plan is {plan}". How do you make the placeholder get replaced with an actual value?

3. Regarding the primary purpose of few-shot examples, which statement is correct?

4. According to the iterative methodology in this chapter, what is the correct workflow for improving a prompt?

🛠️ Hands-On Exercises ​

  1. Refactor the ticket classification Agent from Chapter 5's hands-on exercise into description + instructions layered style; add 3 boundary rules.
  2. Write 10 Chinese eval cases for your Agent; implement an upgraded prompt_eval.py that outputs a per-case expected-vs-actual comparison table.
  3. Experiment with rule ordering: place "output must be JSON" at the first position vs. the last position in instructions; run 5 times each and count format error frequency.

Beginner section complete. Proceed to Chapter 7: Tools — Built-in Toolkit Principles and Usage.