This guide gets you to a working context build, a firewall-protected tool result, and a routed tool shortlist in under 10 minutes.
Time budget:
- Prerequisites: 30 seconds
- Install: 30 seconds
- Your first context build: 3 minutes
- Try the context firewall: 4 minutes
- Try tool routing: 2 minutes
- What to try next: 1 minute
contextweaver requires Python 3.10 or newer.
Check your Python version:
python --versionCreate and activate a virtual environment:
python -m venv .venvLinux and macOS:
source .venv/bin/activateWindows PowerShell:
.venv\Scripts\Activate.ps1If you see an error like running scripts is disabled on this system, either:
-
run the activation script from Command Prompt (cmd.exe) instead:
.venv\Scripts\activate.bat
-
or relax the execution policy for your current user in PowerShell (recommended only on machines you control):
Set-ExecutionPolicy -Scope CurrentUser -ExecutionPolicy RemoteSigned
Install from PyPI:
pip install contextweaverIf you are working from a repository checkout instead, install the package in editable mode:
pip install -e ".[dev]"Scenario: an agent receives a question, decides to query a database, and builds an answer-phase prompt from the conversation history.
Save this as first_agent.py:
"""Your first contextweaver context build."""
from contextweaver.context.manager import ContextManager
from contextweaver.types import ContextItem, ItemKind, Phase
mgr = ContextManager()
mgr.ingest(ContextItem(id="u1", kind=ItemKind.user_turn, text="How many active users do we have?"))
mgr.ingest(ContextItem(id="a1", kind=ItemKind.agent_msg, text="I'll check the database for you."))
mgr.ingest(
ContextItem(
id="tc1",
kind=ItemKind.tool_call,
text='db_query(sql="SELECT COUNT(*) FROM users WHERE active=true")',
parent_id="u1",
)
)
mgr.ingest(ContextItem(id="tr1", kind=ItemKind.tool_result, text="count: 1042", parent_id="tc1"))
pack = mgr.build_sync(phase=Phase.answer, query="active user count")
print("=== Compiled Context ===")
print(pack.prompt)
print("\n=== Build Stats ===")
print(f"Total candidates: {pack.stats.total_candidates}")
print(f"Included in prompt: {pack.stats.included_count}")
print(f"Dropped: {pack.stats.dropped_count}")
print(f"Deduplicated: {pack.stats.dedup_removed}")Run it:
python first_agent.pyExpected output excerpt:
=== Compiled Context ===
[TOOL RESULT [artifact:artifact:tr1]]
count: 1042
[TOOL CALL]
db_query(sql="SELECT COUNT(*) FROM users WHERE active=true")
[USER]
How many active users do we have?
[ASSISTANT]
I'll check the database for you.
=== Build Stats ===
Total candidates: 4
Included in prompt: 4
Dropped: 0
Deduplicated: 0
What just happened:
- You ingested four events into the event log.
build_sync()ran the context pipeline for theanswerphase.- The prompt was compiled from the most relevant items and returned with build stats.
Problem: a large tool result can dominate the prompt if you include it verbatim.
Save this as firewall_demo.py:
"""Show how the context firewall keeps prompts compact."""
from contextweaver.context.manager import ContextManager
from contextweaver.types import ContextItem, ItemKind, Phase
large_result = '{"users": [' + ', '.join(
[
f'{{"id": {i}, "name": "User{i}", "email": "user{i}@example.com"}}'
for i in range(1, 101)
]
) + ']}'
mgr = ContextManager()
mgr.ingest(ContextItem(id="u1", kind=ItemKind.user_turn, text="List all users"))
mgr.ingest(ContextItem(id="tc1", kind=ItemKind.tool_call, text="list_users()", parent_id="u1"))
mgr.ingest(ContextItem(id="tr1", kind=ItemKind.tool_result, text=large_result, parent_id="tc1"))
pack = mgr.build_sync(phase=Phase.answer, query="user list")
print(f"Raw tool result size: {len(large_result)} chars")
print("\n=== Compiled Context ===")
print(pack.prompt)
print("\n=== Firewall Impact ===")
print(f"Prompt size after firewall: {len(pack.prompt)} chars")
print(f"Artifacts stored: {len(mgr.artifact_store.list_refs())}")Run it:
python firewall_demo.pyExpected output excerpt:
Raw tool result size: 6087 chars
=== Compiled Context ===
[USER]
List all users
[TOOL RESULT [artifact:artifact:tr1]]
{"users": [{"id": 1, "name": "User1", "email": "[email protected]"}, ...
[TOOL CALL]
list_users()
=== Firewall Impact ===
Prompt size after firewall: ... chars
Artifacts stored: 1
What just happened:
- The tool result was processed by the firewall during context build (all
tool_resultitems go through it by default). contextweaverstored the raw result in the artifact store.- The prompt kept only a compact summary plus an artifact reference instead of the full payload.
Problem: when a catalog grows, the model should only see the most relevant tools.
Save this as routing_demo.py:
"""Route a natural-language request to a focused tool shortlist."""
from contextweaver.routing.catalog import Catalog
from contextweaver.routing.router import Router
from contextweaver.routing.tree import TreeBuilder
from contextweaver.types import SelectableItem
catalog = Catalog()
catalog.register(SelectableItem(id="t1", kind="tool", name="send_email", description="Send email to a recipient", tags=["notify", "team", "message"]))
catalog.register(SelectableItem(id="t2", kind="tool", name="db_query", description="Query the database", tags=["data"]))
catalog.register(SelectableItem(id="t3", kind="tool", name="create_ticket", description="Create support ticket", tags=["support"]))
catalog.register(SelectableItem(id="t4", kind="tool", name="send_sms", description="Send SMS message", tags=["notify", "team", "message"]))
catalog.register(SelectableItem(id="t5", kind="tool", name="schedule_meeting", description="Schedule a calendar meeting", tags=["calendar"]))
graph = TreeBuilder(max_children=3).build(catalog.all())
router = Router(graph, items=catalog.all(), beam_width=2, top_k=2)
result = router.route("notify the team about the deadline")
print("=== Query ===")
print("notify the team about the deadline")
print("\n=== Top Tools ===")
for item_id in result.candidate_ids:
item = catalog.get(item_id)
print(f"- {item.name}: {item.description}")Run it:
python routing_demo.pyExpected output:
=== Query ===
notify the team about the deadline
=== Top Tools ===
- send_sms: Send SMS message
- send_email: Send email to a recipient
What just happened:
TreeBuilderorganized the catalog into a bounded routing graph.Routerscored the query against that graph and returned the top two candidates.- Your model would now see a focused shortlist instead of the full catalog.
Available now:
- README for the top-level package overview
- Concepts for phases, the context firewall, and routing terms
- Architecture for the pipeline stages and module layout
- MCP Integration for MCP adapters and session ingestion
- A2A Integration for multi-agent adapter flows
- Examples directory for larger end-to-end demos
Planned separately:
- Framework-specific integration guides are tracked in separate issues and are not part of this quickstart.
If you want a deeper local smoke test after this guide, run:
python -m contextweaver demo