A content writing agent for writing blog posts, LinkedIn posts, and tweets with cover images included.
This example demonstrates how to use Agent-RT with OpenBox governance through three filesystem primitives:
- Memory (
AGENTS.md) – persistent context like brand voice and style guidelines - Skills (
skills/*/SKILL.md) – workflows for specific tasks, loaded on demand - Subagents (
subagents.yaml) – specialized agents for delegated tasks like research
The content_writer.py script shows how to combine these into a working agent using Agent-RT with OpenBox governance.
# Set API keys
export OPENAI_API_KEY="..."
export OPENAI_MODEL="gpt-4o-mini"
export OPENBOX_API_URL="https://core.openbox.ai"
export OPENBOX_API_KEY="obx_live_..."
export OPENBOX_AGENT_DID="did:aip:..." # Required by default for newly registered agents
export OPENBOX_AGENT_PRIVATE_KEY="..." # Required by default for newly registered agents
export GOOGLE_API_KEY="..." # For image generation
export TAVILY_API_KEY="..." # For web search (optional)
# Run (uv automatically installs dependencies on first run)
cd examples/content-builder-agent
uv run python content_writer.py "Write a blog post about prompt engineering"More examples:
uv run python content_writer.py "Create a LinkedIn post about AI agents"
uv run python content_writer.py "Write a Twitter thread about the future of coding"OpenBox enables DID signing by default for newly registered agents. If signing
has been explicitly disabled for this agent in OpenBox, you can omit
OPENBOX_AGENT_DID and OPENBOX_AGENT_PRIVATE_KEY.
To use Anthropic instead of OpenAI, set ANTHROPIC_API_KEY and ANTHROPIC_MODEL
and install the matching Agent-RT provider extra. The example installs the local
SDK in editable mode through [tool.uv.sources].
The agent is configured by files on disk, not code:
content-builder-agent/
├── AGENTS.md # Brand voice & style guide
├── subagents.yaml # Subagent definitions
├── skills/
│ ├── blog-post/
│ │ └── SKILL.md # Blog writing workflow
│ └── social-media/
│ └── SKILL.md # Social media workflow
└── content_writer.py # Wires it together (includes tools)
| File | Purpose | When Loaded |
|---|---|---|
AGENTS.md |
Brand voice, tone, writing standards | Always (system prompt) |
subagents.yaml |
Research subagent config (prompt, model, tools) | When research tool runs |
skills/*/SKILL.md |
Content-specific workflows | Always (appended to system prompt) |
# Load memory + skills into system prompt
agents_md = (EXAMPLE_DIR / "AGENTS.md").read_text()
skills_text = load_skills(EXAMPLE_DIR / "skills")
# Create OpenBox governance
governance = await create_openbox_agentrt(
api_url=os.environ["OPENBOX_API_URL"],
api_key=os.environ["OPENBOX_API_KEY"],
agent_did=os.environ.get("OPENBOX_AGENT_DID"),
agent_private_key=os.environ.get("OPENBOX_AGENT_PRIVATE_KEY"),
agent_name="ContentWriter",
)
# Create a governed agent loop
loop = governance.govern_loop(AgentLoop(load_model(), tool_registry=registry))
# Run with governance applied automatically
result = await loop.run_streaming(agent, [user_message(task)], print_progress)Flow:
- Agent receives task → loads relevant skill (blog-post or social-media)
- Calls
researchtool → runs the researcher subagent as its own governed run → saves toresearch/ - Reads research findings → writes content → saves to
blogs/,linkedin/, ortweets/ - Generates cover image with Gemini → saves alongside content
File tools refuse paths that would escape the example directory.
blogs/
└── prompt-engineering/
├── post.md # Blog content
└── hero.png # Generated cover image
linkedin/
└── ai-agents/
├── post.md # Post content
└── image.png # Generated image
research/
└── prompt-engineering.md # Research notes
Change the voice: Edit AGENTS.md to modify brand tone and style.
Add a content type: Create skills/<name>/SKILL.md with YAML frontmatter:
---
name: newsletter
description: Use this skill when writing email newsletters
---
# Newsletter Skill
...Add a subagent: Add to subagents.yaml:
editor:
description: Review and improve drafted content
model: inherit
system_prompt: |
You are an editor. Review the content and suggest improvements...
tools: []Add a tool: Define an async handler and ToolDefinition in content_writer.py, add it to TOOLS, and include its name in the build_registry([...]) list. Classify it for policies through tool_type_map.
| Aspect | LangChain | Agent-RT |
|---|---|---|
| Agent creation | create_agent() from langchain |
AgentConfig + AgentLoop |
| Memory/Skills | Loaded manually into system prompt | Loaded manually into system prompt |
| Subagents | Custom research tool spawning sub-agent |
Custom research tool running a nested governed AgentLoop |
| Governance | create_openbox_langchain_middleware() via AgentMiddleware |
create_openbox_agentrt() + governance.govern_loop(...) |
| Execution | Sync (stream) |
Async (run_streaming) |
| Tools | @tool decorator |
ToolDefinition + async ToolRegistry handler |
- Python 3.11–3.14
OPENAI_API_KEY+OPENAI_MODEL- For the main agent and researcherOPENBOX_API_URL+OPENBOX_API_KEY- For OpenBox governanceGOOGLE_API_KEY- For image generation (Gemini)TAVILY_API_KEY- For web search (optional, research still works without it)