Example marketing content agent demonstrating the full prompt-manager stack
Project description
Marketing Content Agent Example
A demo showing how the full prompt-manager stack works together to build a marketing content agent whose prompts get better over time.
Architecture
+--------------------------+
| marketing-agent | <-- this example
| (ShonkuAgent) |
+----------+---------------+
|
| resolve_prompt / report_metric
v
+----------+---------------+
| prompt-manager | API + client SDK
| (FastAPI + PostgreSQL) |
+----------+---------------+
|
| analyse metrics, propose improvements
v
+----------+---------------+
| autoresearcher-shonku | autonomous prompt optimiser
| (ShonkuAgent) |
+----------+---------------+
|
| agent primitives
v
+----------+---------------+
| shonku | declarative agent framework
| (wraps agno) |
+--------------------------+
What it demonstrates
- Prompt resolution -- the agent asks prompt-manager for the best template for a given content type.
- Experiment-aware routing -- if an A/B experiment is running, prompt-manager routes to the right variant based on
session_idusing MurmurHash3 deterministic hashing. - Quality metrics -- after generating content the agent self-evaluates quality and reports the score back to prompt-manager.
- Autonomous optimisation -- autoresearcher-shonku analyses collected metrics, proposes improved prompt versions, validates safety, deploys a new experiment, and adjusts routing weights.
Setup
1. Start PostgreSQL
# From the repo root
docker compose up -d
2. Start the prompt-manager API
PM_DATABASE_URL=postgresql://prompt_manager:prompt_manager@localhost:15432/prompt_manager \
python -m prompt_manager.api.main
The API starts on http://localhost:8910 by default.
3. Install this example
cd packages/example
pip install -e ".[dev]"
Running
Basic demo (generate content)
python -m marketing_agent.main
Seeds four prompt templates and generates content for each.
Full loop demo (experiment + optimisation)
python -m marketing_agent.demo_full_loop
This runs the complete autoresearch loop end-to-end. See below for details.
Full Loop Demo: What Happens
The demo_full_loop.py script runs 6 steps that demonstrate the entire system:
Step 1: Create prompt with 2 versions
Two versions of a welcome email are created:
| Version | Style | Content |
|---|---|---|
| v1 | Formal | "Dear {name}, We are pleased to inform you..." |
| v2 | Casual | "Hey {name}! We're SO excited you're here..." |
Step 2: Create A/B experiment (50/50 routing)
An experiment is created with two arms:
- formal arm (v1): 50% of traffic
- casual arm (v2): 50% of traffic
Routing is deterministic per session_id using MurmurHash3 -- the same user always sees the same version.
Step 3: Marketing agent runs 4 times
The marketing agent runs with different session IDs. Each run:
- Calls
resolve_prompt("welcome-demo", session_id=...)-- prompt-manager routes to v1 or v2 based on the experiment - The LLM (gpt-oss-120b on Groq) generates personalised content from the template
- Calls
rate_content(content, "email")-- agent's built-in quality scorer - Calls
report_metric(slug, version_id, "quality_score", score)-- metric saved to PostgreSQL
Example output:
demo-user-0 → v2 (casual), quality: 6.5
demo-user-1 → v2 (casual), quality: 6.5
demo-user-2 → v1 (formal), quality: 6.0
demo-user-3 → v1 (formal), quality: 6.0
Step 4: Check metrics per version
Metrics are aggregated from PostgreSQL:
v1 (formal): count=2, mean=6.00
v2 (casual): count=2, mean=6.50
Both versions score mediocre. The casual version is slightly better.
Step 5: Autoresearcher proposes v3 and adjusts routing
This is where the autoresearch loop runs. The AutoResearcherAgent (from autoresearcher-shonku) is given tools that wrap the prompt-manager API:
tools = [get_prompt, get_metrics, get_sample_interactions,
create_version, create_experiment, conclude_experiment]
The autoresearcher has no built-in knowledge of your prompts. It learns everything through tool calls:
get_prompt("welcome-demo")-- reads current prompt textget_metrics(prompt_id, ...)-- sees v1=6.0, v2=6.5get_sample_interactions(prompt_id)-- sees both version texts- LLM proposes v3 -- combines formal professionalism with casual warmth
create_version("welcome-demo", "Hello {name}, Welcome to {company}! We're delighted...")-- saves to DBcheck_safety_rails(original, proposed, ...)-- validates (similarity=0.376, all checks pass)conclude_experiment(old_exp_id)-- stops the old 50/50 experimentcreate_experiment(...)-- creates new experiment with adjusted weights:
| Version | Style | Weight |
|---|---|---|
| v1 | Formal | 30% |
| v2 | Casual | 30% |
| v3 | Optimised (new) | 40% |
The optimised version gets the highest weight because it was designed to improve on both.
Step 6: Verify new routing
Traffic now routes across all 3 versions:
verify-user-0 → v2
verify-user-1 → v3 ← new optimised version
verify-user-2 → v1
verify-user-3 → v1
verify-user-4 → v2
verify-user-5 → v2
All 3 versions receiving traffic. The system is now collecting metrics on v3 alongside v1 and v2.
How tools flow through the stack
demo_full_loop.py (example layer)
│
│ defines 6 tool functions (closures over httpx client)
│ get_prompt() → GET /prompts/{slug}
│ get_metrics() → GET /metrics/aggregate
│ get_sample_interactions() → GET /prompts/{slug}/versions
│ create_version() → POST /prompts/{slug}/versions
│ create_experiment() → POST /experiments
│ conclude_experiment() → PATCH /experiments/{id}/status
│
└─→ AutoResearcherAgent.run(tools=[...], input="Optimize...")
│
│ autoresearcher-shonku adds its own tool:
│ check_safety_rails() (validates similarity, length, budget)
│
│ shonku merges: 6 external + 1 agent-owned = 7 tools
│ shonku validates: all required_tools present
│
└─→ agno.Agent(tools=[7 callables], model=Groq("gpt-oss-120b"))
│
└─→ LLM decides which tools to call based on reasoning
The autoresearcher is completely reusable -- swap the tools and it optimises different things. The domain knowledge lives in the tools (which wrap the prompt-manager API), not in the agent.
Seed prompts
The file prompts/seed_prompts.json contains four starter templates:
| Slug | Type | Tags |
|---|---|---|
welcome-email |
email, onboarding | |
social-post |
Social | social, engagement |
ad-copy |
Ad | ad, conversion |
product-description |
Product | product, ecommerce |
Templates use {{variable}} placeholders that the agent fills in at generation time.
Running tests
pytest
Configuration
export PM_DATABASE_URL=postgresql://prompt_manager:prompt_manager@localhost:15432/prompt_manager
export PM_LLM_PROVIDER=groq # or: anthropic, openai, gemini, openrouter
export PM_LLM_MODEL=openai/gpt-oss-120b # or: claude-sonnet-4-20250514, gpt-4o, etc.
export PM_LLM_API_KEY=your-api-key # required for optimization + content generation
| Env Var | Default | Description |
|---|---|---|
PM_DATABASE_URL |
postgresql://localhost:5432/prompt_manager |
PostgreSQL connection |
PM_API_URL |
http://localhost:8910 |
Prompt Manager API URL |
PM_LLM_PROVIDER |
groq |
LLM provider |
PM_LLM_MODEL |
openai/gpt-oss-120b |
Model ID |
PM_LLM_API_KEY / GROQ_API_KEY |
-- | API key (required) |
Project details
Download files
Download the file for your platform. If you're not sure which to choose, learn more about installing packages.
Source Distribution
Built Distribution
Filter files by name, interpreter, ABI, and platform.
If you're not sure about the file name format, learn more about wheel file names.
Copy a direct link to the current filters
File details
Details for the file marketing_agent_example-0.1.3.tar.gz.
File metadata
- Download URL: marketing_agent_example-0.1.3.tar.gz
- Upload date:
- Size: 13.7 kB
- Tags: Source
- Uploaded using Trusted Publishing? No
- Uploaded via: twine/6.1.0 CPython/3.13.7
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
3bf9bfc132b0cebcc6c9cdafcc50f7cbe542929131ba8419ad0d8b3fccaec7b3
|
|
| MD5 |
55caea4e9b5862dc492877748bd52c4a
|
|
| BLAKE2b-256 |
c99a8d00bbd55408f4049e176a58f679f79beaaf7ecd398022d62aec1e674adf
|
File details
Details for the file marketing_agent_example-0.1.3-py3-none-any.whl.
File metadata
- Download URL: marketing_agent_example-0.1.3-py3-none-any.whl
- Upload date:
- Size: 15.7 kB
- Tags: Python 3
- Uploaded using Trusted Publishing? No
- Uploaded via: twine/6.1.0 CPython/3.13.7
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
f5e6f986954ea43e4cc54c71b2bf4a8470cd5ab155ce9ae7172f6f5d021ba022
|
|
| MD5 |
b75b0fea042c16a3c3fb94614933e008
|
|
| BLAKE2b-256 |
15ce2174739456733c0871ad4f5e933183205c8835dd5f8351c1316230b67ebc
|