Skip to main content

Prompt System

AgentFlow uses a modular prompt system where agent behavior is assembled from composable prompt blocks. Instead of writing monolithic system prompts, you select, order, override, and preview individual blocks — each controlling a specific aspect of agent behavior.

How it works

Every agent’s system prompt is built from an ordered list of blocks. Each block is an independent unit — a set of instructions for a specific concern (personality, tool usage, artifact formatting, error handling, etc.). Blocks can be:
  • Enabled or disabled per agent or per user
  • Reordered to change priority
  • Overridden with custom content
  • Previewed before saving to see the assembled prompt and token count

Block categories

CategoryBlocksWhat they control
Coresoul, role, constitution, communication, instructions, answer_formatAgent identity, personality, communication style, response format
Capabilitiestool_guidance, error_handling, cached_results, sub_agent_contextHow the agent uses tools, handles errors, leverages cached data
Artifactsartifact_draft_email, artifact_draft_meeting_invite, artifact_draft_task, artifact_draft_slack_messageOutput formatting for structured artifacts
Dynamicdatetime, available_skills, customInjected at request time — current timestamp, skill catalog, custom content
Different agents have different default block lists. MainAgent includes artifact blocks and skill awareness; EmailsAgent only includes email-specific artifact blocks; ResearchAgent has no artifact blocks at all.

Per-user prompt configuration

Users can customize which blocks are active for any agent, saving their configuration via the API. This enables power users to tune agent behavior without modifying the underlying agent code.

SDK

agent = await af.Agent.get("MainAgent")

# List available blocks
blocks = await agent.get_prompt_blocks()
for block in blocks:
    print(f"[{block['category']}] {block['id']}: {block['name']}")

# Get current configuration
config = await agent.get_prompt_config()
print(f"Active blocks: {config['selected_blocks']}")

# Save a custom configuration
await agent.save_prompt_config(
    selected_blocks=["soul", "role", "communication", "tool_guidance", "error_handling"],
    custom_instructions="Always respond in bullet points. Focus on actionable insights.",
)

# Preview the assembled prompt (without saving)
preview = await agent.preview_prompt(
    selected_blocks=["soul", "role", "communication", "tool_guidance"],
    custom_instructions="Be concise.",
)
print(f"Token count: {preview['estimated_tokens']}")
print(f"Prompt: {preview['full_prompt'][:500]}...")

# Reset to agent defaults
await agent.reset_prompt_config()

REST API

# List available blocks for an agent
GET /agent/{agent_id}/prompts/blocks

# Get block details (content, type, category)
GET /agent/{agent_id}/prompts/blocks/{block_id}

# Get current prompt configuration
GET /agent/{agent_id}/prompts/config

# Save prompt configuration
POST /agent/{agent_id}/prompts/config
{
  "selected_blocks": ["soul", "role", "communication", "tool_guidance"],
  "include_datetime": true,
  "custom_instructions": "Always respond in bullet points."
}

# Update prompt configuration
PUT /agent/{agent_id}/prompts/config

# Reset to defaults
DELETE /agent/{agent_id}/prompts/config

# Preview assembled prompt
POST /agent/{agent_id}/prompts/preview

# Preview with full dynamic context (KB results, attachments)
POST /agent/{agent_id}/prompts/full-preview

# Validate a configuration
POST /agent/{agent_id}/prompts/validate

# Analyze static vs dynamic content
POST /agent/{agent_id}/prompts/analyze
All prompt endpoints also have sub-agent variants at /agent/{id}/subagents/{sub_id}/prompts/*.

Block types

Static blocks

Static blocks contain fixed text — agent personality, communication rules, formatting instructions. Their content is defined at the framework level and doesn’t change between requests.

Dynamic blocks

Dynamic blocks generate content at request time:
  • datetime — injects the current date and time for temporal awareness
  • available_skills — injects the skill catalog (name + description for each available skill)
  • custom — injects user-provided custom context

Custom blocks

In addition to selecting from the built-in block library, you can inject entirely custom blocks:
await agent.save_prompt_config(
    selected_blocks=["soul", "role", "tool_guidance"],
    custom_blocks=[
        {
            "id": "company_context",
            "name": "Company Context",
            "content": "Our fiscal year ends in March. Q4 means Jan-Mar.",
            "priority": 10,
        }
    ],
)

Block overrides

Override the content of any built-in block without removing it from the block list:
await agent.save_prompt_config(
    selected_blocks=["soul", "role", "communication", "tool_guidance"],
    block_overrides={
        "communication": {
            "content": "Communicate in a formal, executive-briefing style."
        }
    },
)