The system prompt was 4,000 tokens. Nobody remembered what each paragraph did. Half of it was out of date. The "Available tools" section listed a tool that had been removed six months ago.
The problem: system prompts are often assembled as one giant string concatenation. You cannot tell which part is stale. You cannot enable or disable sections based on context. You cannot test sections independently.
agent-context-builder lets you build system prompts from named, independently-managed sections.
The Shape of the Fix
from agent_context_builder import ContextBuilder
builder = ContextBuilder()
builder.add("role", "You are a customer support agent for Acme Corp.")
builder.add("capabilities", """
You can:
- Look up order status
- Process refunds for orders under $50
- Escalate complex issues to a human agent
""")
builder.add("constraints", """
Do not:
- Promise specific delivery dates
- Approve refunds over $50 without manager approval
- Share other customers' order information
""")
builder.add("tone", "Be helpful, concise, and empathetic. Use plain language.")
# Build the full system prompt
system = builder.build()
# Conditionally include sections
if user.is_premium:
builder.add("premium_tools", "You also have access to the priority queue tool.")
# Remove outdated sections
builder.remove("old_feature_section")
# Inspect what's in the prompt
print(builder.sections()) # ["role", "capabilities", "constraints", "tone"]
Named sections. Explicit inclusion. Clear ownership. Each section is independently testable.
What It Does NOT Do
agent-context-builder does not format sections automatically. You control the content of each section. The builder handles concatenation and ordering.
It does not validate that sections produce a coherent system prompt. Contradicting sections (one says "always be brief", another says "always be comprehensive") will both be included without warning.
It does not track token counts by section. For token budget management across sections, pair with prompt-token-counter.
Inside the Library
The builder is an ordered dict of sections:
class ContextBuilder:
def __init__(self, separator: str = "\n\n"):
self._sections: dict[str, str] = {}
self._order: list[str] = []
self._separator = separator
def add(self, name: str, content: str, position: int | None = None) -> None:
if name in self._sections:
raise DuplicateSectionError(name)
self._sections[name] = content
if position is not None:
self._order.insert(position, name)
else:
self._order.append(name)
def update(self, name: str, content: str) -> None:
if name not in self._sections:
raise UnknownSectionError(name)
self._sections[name] = content
def remove(self, name: str) -> None:
self._sections.pop(name, None)
self._order.remove(name)
def build(self) -> str:
return self._separator.join(
self._sections[n] for n in self._order if n in self._sections
)
def sections(self) -> list[str]:
return list(self._order)
The separator between sections is configurable. Default is two newlines. For Anthropic's recommended XML-tagged prompts, use separator="" and wrap each section content in XML tags yourself.
position parameter: insert a section at a specific index rather than appending. Useful for adding context-specific sections that need to appear before the general instructions.
Templates: builder.add("role", "You are a {role} agent for {company}.") combined with builder.build(role="support", company="Acme") fills the placeholders. Implemented with .format(**kwargs) on each section string.
When to Use It
Use it for any agent with a system prompt longer than a few sentences. The section pattern pays off when:
- Multiple team members own different parts of the system prompt
- You need to enable/disable sections based on user permissions or context
- You want to test sections independently (each section is a string you can assert on)
- The system prompt changes frequently and you need to track what changed and why
Skip it for simple agents with short, stable system prompts. Two sentences do not need a section manager.
Install
pip install git+https://github.com/MukundaKatta/agent-context-builder
from agent_context_builder import ContextBuilder
import os
def build_system_prompt(user_role: str, feature_flags: dict) -> str:
builder = ContextBuilder()
builder.add("role", f"You are a {user_role} assistant.")
builder.add("base_capabilities", STANDARD_CAPABILITIES)
builder.add("constraints", BASE_CONSTRAINTS)
if feature_flags.get("web_search"):
builder.add("web_search", WEB_SEARCH_INSTRUCTIONS)
if feature_flags.get("code_execution"):
builder.add("code_execution", CODE_EXECUTION_INSTRUCTIONS)
if user_role == "admin":
builder.add("admin_tools", ADMIN_TOOL_INSTRUCTIONS)
builder.add("response_format", RESPONSE_FORMAT_INSTRUCTIONS)
return builder.build()
# Test sections independently
def test_admin_section():
builder = ContextBuilder()
builder.add("admin_tools", ADMIN_TOOL_INSTRUCTIONS)
assert "approve_refund" in builder.build()
assert "delete_user" in builder.build()
Sibling Libraries
| Library | What it solves |
|---|---|
prompt-token-counter |
Count tokens per section for budget management |
prompt-template-version |
Version and hash system prompt templates |
cachebench |
Measure cache hit rate for your assembled prompts |
prompt-cache-warmer |
Pre-warm the Anthropic cache for assembled prompts |
agent-fn-registry |
Registry for tools that pair with the system prompt |
The pairing: agent-context-builder assembles the prompt, prompt-template-version fingerprints it for change tracking, prompt-cache-warmer keeps it warm in Anthropic's cache.
What's Next
Inheritance: ChildBuilder(parent=builder) that starts with the parent's sections and can override or add sections. Useful for building specialized agents from a base agent definition.
Conditional sections with predicate functions: builder.add_if("premium_tools", lambda ctx: ctx.is_premium, content). This would make the conditional logic part of the builder definition rather than scattered in the calling code.
YAML/JSON configuration: load section definitions from a file instead of code. This would let non-engineers manage system prompts through configuration files while engineers manage the builder logic.
Built as part of the agent-stack family: composable Python primitives for production LLM agents.
Top comments (0)