Context Blocks
Context blocks are dynamic prompt blocks that fetch live data before an agent request. They are useful for stable conversation/session context such as CRM profile data, task counts, field mappings, personalization, user memory, and catalogs of available KBs, skills, and artifacts.
Most built-in context blocks are:
message=system
type=dynamic
mode=cached
scope=conversation
Turn-specific context, such as selected records or current page state, should usually be sent as inline prompt_blocks or context_refs on the chat request. AgentFlow persists those as hidden system_context with the turn.
How It Works
prompts/<name>/PROMPT.py registers a local async function with @prompt_block.
- AgentFlow builds a
PromptContext with tenant, user, agent, request scope, auth, and safe API helpers.
- Eligible dynamic blocks run in parallel.
- Each block returns body text, or
None / blank text to skip.
- AgentFlow wraps the body in
<name>...</name>.
- Cacheable blocks are stored in the process-local prompt-block cache.
- Wrapped blocks are ordered and injected into the model request.
Dynamic block functions return inner body text, not the outer XML tag.
from framework.prompts.block_registry import PromptContext, prompt_block
@prompt_block(scope="conversation", tags=["tasks", "context"], ttl=300)
async def tasks_overview(ctx: PromptContext) -> str | None:
"""Outstanding and overdue task counts."""
overview = await load_task_overview(ctx)
if overview.total == 0:
return None
return f"open_tasks: {overview.total}\noverdue_tasks: {overview.overdue}"
AgentFlow injects:
<tasks_overview>
open_tasks: 12
overdue_tasks: 3
</tasks_overview>
Built-In Blocks
| Block | Scope | Typical TTL | What it provides |
|---|
available_knowledge_bases | conversation | 5 min | Assigned KB catalog, not full KB contents |
available_artifacts | conversation | 5 min | Assigned artifact type catalog and how to use artifacts |
available_skills | conversation | 5 min | Assigned skill catalog, not full skill files |
crm_context | conversation | 30 min | CRM user/account context from GraphQL-backed sources |
crm_field_mappings | conversation | 60 min | CRM object field-to-locator mappings |
personalization | conversation | 10 min | Profile, custom instructions, style/tone preferences |
tasks_overview | conversation | 5 min | Outstanding and overdue task counts |
user_memory | conversation | 10 min | Core user/agent memory |
Catalog blocks use progressive disclosure: they explain what exists and how to fetch more detail, rather than dumping full content.
Progressive Disclosure Shape
Catalog items should stay small:
name: renewal_playbook
description: Renewal risk and expansion workflow guidance.
when_to_use: Use when planning next steps for an account renewal.
how_to_use: Search this KB before making policy-specific recommendations.
Use the same shape for KBs, skills, artifacts, and future asset/file catalogs. For memory, use it for a high-level memory index; inject specific recalled archival memory as current-turn system_reminders only when relevant.
Request Scope
Chat requests can narrow catalogs:
| Request field | Affects |
|---|
knowledge_bases | available_knowledge_bases |
skills | available_skills |
artifacts | available_artifacts |
These fields narrow what the agent sees; they do not grant access to resources not assigned to the agent.
Preloading
Preloading means calling the same cacheable block builders before the user sends a prompt, usually when the chat UI opens.
from agentflow import AsyncAgentFlow
async with AsyncAgentFlow.from_profile("prod") as client:
agent_id = (await client.agents.by_name("MainAgent")).id
await client.agents.preload_context(agent_id)
response = await client.agents.run(agent_id=agent_id, message="What's my pipeline look like?")
The REST route is still:
POST /api/v1/agent/{agent_id}/context/preload
The context cache is process-local. A preload request benefits later requests that land on the same API worker. Use session affinity if cross-worker warmth matters.
Inspecting Blocks
blocks = await client.agents.list_context_blocks(agent_id)
for block in blocks:
print(block.name, block.tags, block.ttl)
rendered = await client.agents.build_context(agent_id, include_content=True)
for name, body in rendered.content_by_name.items():
print(f"--- {name} ---")
print(body[:200])
| SDK method | REST endpoint |
|---|
client.agents.list_context_blocks(agent_id) | GET /api/v1/agent/{agent_id}/context/blocks |
client.agents.build_context(agent_id) | POST /api/v1/agent/{agent_id}/context/blocks/build |
client.agents.preload_context(agent_id) | POST /api/v1/agent/{agent_id}/context/preload |
Turn Context
Not every dynamic context source should be a conversation-scoped context block.
| Source | Recommended placement |
|---|
| Current page | Inline turn prompt block or built-in page context |
| Current selection | Inline turn prompt block |
| Datetime/timezone | Built-in current-turn system context |
| Resolved CRM/Slack refs | context_refs, resolved into turn system_context |
| KB retrieval snippets | Turn system_context |
| Attachments | Turn system_context |
| Archival memory recall | Turn system_reminders |
| Artifact actions/summaries | system_events |
| Conversation compaction notice | system_events |
Use context blocks for stable, reusable conversation context. Use turn system_context for details tied to the current message. Use turn system_reminders for bounded system reminders like automatic archival recall. Use system_events for durable system actions that must replay across future turns.
Cache Behavior
| Property | Value |
|---|
| Key | {tenant_id}:{user_id}:{agent_name}:{block_name} plus scope inputs when configured |
| Storage | Process-local in-memory cache |
| TTL | Per block; defaults are intentionally short for live catalogs |
| Empty result | Skipped and not injected |
| Error/timeout | Skipped for the request and not cached |
| Invalidation | POST /api/v1/prompt-blocks/{name}/invalidate-cache |