ContextPilot acts as a transparent HTTP proxy or a native plugin. In either mode, it deduplicates shared content across tool results, reorders documents, and maximizes cache hits.
The native OpenClaw plugin runs in-process with zero external dependencies.
- Install the plugin:
openclaw plugins install @contextpilot-ai/contextpilot- Enable in
~/.openclaw/openclaw.json:
{
"plugins": {
"slots": { "contextEngine": "contextpilot" },
"entries": { "contextpilot": { "enabled": true } }
}
}- Restart OpenClaw. ContextPilot now intercepts every LLM call, optimizes the context, and injects cache control markers (for Anthropic) automatically.
# Clone and run
git clone https://github.com/EfficientContext/ContextPilot.git
cd ContextPilot/examples/openclaw
bash setup.sh anthropic # or: bash setup.sh openaiThe script installs ContextPilot, generates a config, and starts the proxy.
cd ContextPilot/examples/openclaw
docker compose up -d
# OpenAI instead of Anthropic:
CONTEXTPILOT_BACKEND_URL=https://api.openai.com docker compose up -dpip install contextpilot
python -m contextpilot.server.http_server \
--port 8765 \
--infer-api-url https://api.anthropic.com- Open OpenClaw
- Go to Settings → Models
- Add a custom provider:
| Field | Value |
|---|---|
| Name | contextpilot-anthropic |
| Base URL | http://localhost:8765/v1 |
| API Key | your Anthropic API key |
| API | anthropic-messages |
| Model ID | claude-opus-4-6 |
- Select the model and start chatting
Merge into ~/.openclaw/openclaw.json:
{
"models": {
"providers": {
"contextpilot-anthropic": {
"baseUrl": "http://localhost:8765/v1",
"apiKey": "${ANTHROPIC_API_KEY}",
"api": "anthropic-messages",
"headers": {
"X-ContextPilot-Scope": "all"
},
"models": [
{
"id": "claude-opus-4-6",
"name": "Claude Opus 4.6 (via ContextPilot)",
"reasoning": false,
"input": ["text"],
"contextWindow": 200000,
"maxTokens": 32000
}
]
}
}
}
}For OpenAI, use api: "openai-completions" and point --infer-api-url to https://api.openai.com. See examples/openclaw/openclaw.json.example for both providers.
For self-hosted models, ContextPilot proxies between OpenClaw and SGLang:
OpenClaw ──▶ ContextPilot Proxy (server:8765) ──▶ SGLang (server:30000)
Start SGLang with tool calling support:
python -m sglang.launch_server \
--model-path Qwen/Qwen3.5-27B \
--tool-call-parser qwen3_coder \
--port 30000Start ContextPilot proxy:
python -m contextpilot.server.http_server \
--port 8765 \
--infer-api-url http://localhost:30000 \
--model Qwen/Qwen3.5-27BConfigure OpenClaw (replace <server-ip> with your server's IP):
# Requires jq (install: sudo apt install jq / brew install jq)
jq '
.agents.defaults.model.primary = "contextpilot-sglang/Qwen/Qwen3.5-27B" |
.models = {
"mode": "merge",
"providers": {
"contextpilot-sglang": {
"baseUrl": "http://<server-ip>:8765/v1",
"apiKey": "placeholder",
"api": "openai-completions",
"headers": {"X-ContextPilot-Scope": "all"},
"models": [{
"id": "Qwen/Qwen3.5-27B",
"name": "Qwen 3.5 27B (SGLang via ContextPilot)",
"reasoning": false,
"input": ["text"],
"contextWindow": 131072,
"maxTokens": 8192
}]
}
}
}
' ~/.openclaw/openclaw.json > /tmp/oc.json && mv /tmp/oc.json ~/.openclaw/openclaw.jsonThen restart:
pkill -f openclaw && openclaw gateway start && openclaw tuiWithout jq: manually edit ~/.openclaw/openclaw.json
- Change
agents.defaults.model.primaryto"contextpilot-sglang/Qwen/Qwen3.5-27B" - Add a
"models"key at the top level:
"models": {
"mode": "merge",
"providers": {
"contextpilot-sglang": {
"baseUrl": "http://<server-ip>:8765/v1",
"apiKey": "placeholder",
"api": "openai-completions",
"headers": { "X-ContextPilot-Scope": "all" },
"models": [{
"id": "Qwen/Qwen3.5-27B",
"name": "Qwen 3.5 27B (SGLang via ContextPilot)",
"reasoning": false,
"input": ["text"],
"contextWindow": 131072,
"maxTokens": 8192
}]
}
}
}Important: Use the server's IP address (not hostname) in
baseUrlto avoid IPv6 DNS resolution issues in Node.js/WSL environments.--tool-call-parseris required for OpenClaw's tool loop to work.
Check the X-ContextPilot-Result response header:
X-ContextPilot-Result: {"intercepted":true,"documents_reordered":true,"total_documents":8,"sources":{"system":1,"tool_results":2}}
If the header is absent, the request had fewer than 2 extractable documents (nothing to reorder).
ContextPilot auto-detects these formats in both system prompts and tool results:
| Format | Pattern | Typical Source |
|---|---|---|
| XML tags | <documents><document>...</document></documents> |
RAG systems |
| File tags | <files><file>...</file></files> |
Code search |
| Numbered | [1] doc [2] doc |
Search rankings |
| Separator | docs split by --- or === |
Text chunking |
| Markdown headers | sections split by #/## |
Structured docs |
Auto-detection priority: XML > Numbered > Separator > Markdown headers.
X-ContextPilot-Scope |
System Prompt | Tool Results |
|---|---|---|
all (default) |
reordered | reordered |
system |
reordered | untouched |
tool_results |
untouched | reordered |
Set via headers in the OpenClaw provider config, or per-request.
| Header | Description | Default |
|---|---|---|
X-ContextPilot-Enabled |
Enable/disable | true |
X-ContextPilot-Mode |
Extraction mode | auto |
X-ContextPilot-Scope |
Which messages to process | all |
X-ContextPilot-Tag |
Custom XML tag name | document |
X-ContextPilot-Separator |
Custom separator | --- |
X-ContextPilot-Alpha |
Clustering distance parameter | 0.001 |
X-ContextPilot-Linkage |
Clustering linkage method | average |
For details on how reorder and dedup work, see How It Works.
Tested on claw-tasks — 60 enterprise document analysis tasks, 22 documents (490 KB), ~250 turns.
Avg P50 P99
Prompt Tokens
OpenClaw + SGLang 45,771 44,570 92,785
OpenClaw + ContextPilot + SGLang 33,622 32,526 51,581
Δ -26.5% -27.0% -44.4%
Wall Time (s)
OpenClaw + SGLang 26.1 25.2 68.8
OpenClaw + ContextPilot + SGLang 20.8 21.8 50.4
Δ -20.4% -13.3% -26.6%
Accuracy 245/245 245/245
See docs/benchmarks/openclaw.md for details.
No X-ContextPilot-Result header — Request had < 2 extractable documents. Check that search/memory tools are returning multiple results.
Connection refused — Proxy not running. Check curl http://localhost:8765/health.
Connection error. from OpenClaw (Node.js) — IPv6 DNS resolution failure. Use IP address in baseUrl, or export NODE_OPTIONS="--dns-result-order=ipv4first".
401/403 from backend — API key not set or invalid. The proxy forwards auth headers as-is.
Tool call appears as XML text, agent stops — SGLang not parsing tool calls into structured tool_calls. Add --tool-call-parser qwen3_coder (or the appropriate parser for your model) to SGLang launch command.
Tool results not reordered — Check scope is all or tool_results. Verify tool results use a supported format.
