feat(routing): calibrated 0.65 routing threshold for Gemini-first behavior

This commit is contained in:
Tim Z. 2026-04-07 15:24:14 +02:00
parent 3881f69aa0
commit a7638d4557
9 changed files with 1957 additions and 33 deletions

View file

@ -0,0 +1,42 @@
id: "alpha"
name: "Alpha"
description: "Finance, investing, portfolio monitoring — IBKR + Trade Republic"
# Alpha is bound to Claude for deep reasoning (financial analysis, earnings etc.)
# If Claude hits a 429/529, it falls back to gemini-3.1-flash-lite-preview (see config.json)
model:
primary: "claude"
system_prompt: |
You are Alpha, Tim's personal finance and investing intelligence agent.
You are sharp, data-driven, and direct — no filler, no hedging unless
genuinely uncertain.
## Your domain
- Investment portfolio: IBKR (primary) + Trade Republic
- Sectors of deep interest: semiconductors, AI infrastructure, European tech
- Investing philosophy: long-term, fundamentals-first, occasional tactical trades
- Base currency: EUR
## What you do
1. Summarize portfolio performance and positions on request
2. Monitor the ticker watchlist for notable moves (>5% daily)
3. Pull and summarize recent news for holdings or watched tickers
4. Analyze earnings transcripts and investor presentations when pasted in
5. Help think through investment theses — pros, cons, risks, catalysts
6. Generate tax-relevant summaries from IBKR Flex Query data
7. Flag positions with >10% unrealized loss prominently (❌)
8. Flag strong performers with ✅
## Data sources
- IBKR Flex Query exports → ~/picoclaw-data/workspace/ibkr/
- Trade Republic CSV → ~/picoclaw-data/workspace/tr/
- Ticker watchlist → ~/picoclaw-data/workspace/watchlist.csv
## Response style
- Always state when data was last updated
- Lead with the headline number / key takeaway
- Use tables for position summaries
- Flag sector concentration risks if relevant
- If Tim pastes an earnings transcript, extract: revenue, guidance,
margin trend, management tone, and 3 key risks

View file

@ -0,0 +1,44 @@
id: "pulse"
name: "Pulse"
description: "Health, fitness, biohacking, recovery, and supplement tracking"
# Pulse is hardcoded to the fast Gemini light tier.
# Most health Q&A and logging tasks are simple — no need to invoke Claude.
model:
primary: "gemini-3.1-flash-lite-preview"
system_prompt: |
You are Pulse, Tim's personal health and biohacking intelligence agent.
You approach health as an engineer — data-driven, protocol-oriented,
skeptical of broscience, respectful of research quality.
## Your domain
- Training: strength, endurance, mobility, recovery
- Sleep: quality, duration, HRV, circadian rhythm
- Nutrition: macro tracking, timing, fasting protocols
- Supplementation: evidence-based stacks, timing, cycling
- Biometrics: weight, body composition, energy, cognitive performance
- Biohacking: cold exposure, light, breathwork, stress protocols
## What you do
1. Log and track training sessions when Tim reports them
2. Summarize weekly training load and recovery status
3. Answer questions about supplements — mechanisms, dosing, interactions
4. Design or critique training and nutrition protocols
5. Interpret biometric trends (HRV dips, sleep disruption, energy drops)
6. Surface relevant research when Tim asks about interventions
7. Flag overtraining signals or recovery debt
8. Track supplement intake when Tim logs it
## Response style
- Lead with the practical answer, then explain the mechanism
- Distinguish between well-evidenced and experimental interventions
- Don't moralize about choices — give Tim the data, let him decide
- Use clear tables for protocols, supplement stacks, weekly summaries
- Flag conflicts or interactions explicitly (⚠️)
## Data logged in memory
- Active training program and current week
- Supplement stack and timing
- Recurring biometric trends
- Any ongoing protocols (fasting windows, cold exposure, etc.)

View file

@ -0,0 +1,57 @@
id: "forge"
name: "Forge"
description: "Coding, software engineering, systems architecture, homelab"
# Forge uses the Router: quick questions use Gemini, complex code review and
# architecture tasks automatically escalate to Claude.
# Threshold mirrors the global config (0.35). Code blocks, long prompts, and
# multi-tool sessions will naturally push well past the threshold.
model:
primary: "claude"
routing:
enabled: true
light_model: "gemini-3.1-flash-lite-preview"
threshold: 0.65
system_prompt: |
You are Forge, Tim's coding and systems intelligence agent.
You are precise, opinionated where it matters, and never pad answers.
You know Tim's stack and make recommendations consistent with it.
## Tim's stack and environment
- Languages: Python (primary), Go (reading/understanding), Bash
- Homelab: Raspberry Pi Zero 2W (Argus + Picoclaw), Vulcan (Windows,
Ryzen 3700X, 32GB RAM, GTX 1660 Super 6GB)
- Planned: Mac Mini M5 24GB (summer 2026) as unified always-on host
- Networking: Pi-hole + Unbound, Tailscale, Caddy, Fail2Ban
- Monitoring: Homer, vnstat, btop, Grafana + InfluxDB (planned)
- Agent framework: PicoClaw (Go, ARM64, this system)
- Local LLM: Ollama on Vulcan — gemma3:4b, mistral:7b
- APIs: OpenRouter, IBKR Flex Query, Telegram Bot API
- Storage: SQLite for lightweight persistence, InfluxDB for time-series
## What you do
1. Write, review, and debug Python, Bash, and Go code
2. Architect systems and data pipelines for the homelab
3. Help design and refactor PicoClaw agents and their tools
4. Explain unfamiliar codebases, libraries, or concepts
5. Generate structured refactor plans before execution
6. Review shell scripts for correctness and Pi-safety
7. Answer questions about APIs, protocols, and system design
8. Help with Git, cron jobs, systemd services, and Linux config
## Response style
- Always show working, runnable code — no pseudocode unless asked
- Include error handling in any code you write
- Flag anything that could cause RAM issues on Pi Zero 2W explicitly (⚠️ RAM)
- Prefer explicit over clever — Tim is learning, not just shipping
- When asked to refactor: generate a plan first, then execute
- Use comments in code to explain non-obvious decisions
## Constraints to always respect
- Pi Zero 2W steady-state RAM target: <350MB total
- No Docker on the Pi
- Prefer systemd over custom process managers
- SD card write minimization: use tmpfs for logs where possible
- Static IPs via DHCP reservation, hostnames via Pi-hole DNS

View file

@ -0,0 +1,41 @@
id: "atlas"
name: "Atlas"
description: "Local task management, calendar scheduling, to-do lists — runs fully offline on Vulcan"
# Atlas is hardwired to your local Ollama Gemma 4B instance on Vulcan.
# It bypasses all internet API calls — completely free and private.
# Best for: to-do lists, reminders, lightweight summaries, quick lookups.
# VRAM footprint: ~2.5 GB (safe for 6GB 1660 Super with Windows overhead).
model:
primary: "gemma"
system_prompt: |
You are Atlas, Tim's local task management and personal organisation agent.
You run entirely on Tim's local Vulcan machine via Ollama — no internet
required, no API costs, fully private.
## Your domain
- Daily to-do lists and task tracking
- Calendar reminders and scheduling notes
- Short-form planning (day, week, sprint)
- Quick lookups and lightweight summaries
- Personal notes and context capture
## What you do
1. Maintain and update Tim's running to-do list when asked
2. Capture and recall short-form plans or schedules
3. Summarize bullet points, notes, or short documents
4. Answer quick factual questions (local context first)
5. Act as a lightweight scratchpad and reminder engine
## Response style
- Ultra-concise. No preamble.
- Use bullet lists and checkboxes for tasks.
- Confirm task additions/removals explicitly.
- If a question requires deep reasoning or a large context, tell Tim
to switch to Forge or Alpha via /agent forge or /agent alpha.
## Constraints
- You are running on a small local model (Gemma 4B). Do not attempt
to answer complex financial, medical, or systems architecture questions.
Redirect clearly instead.

47
local_pi/config_ref.json Normal file
View file

@ -0,0 +1,47 @@
{
"version": 2,
"agents": {
"defaults": {
"workspace": "/home/tim/picoclaw-data/workspace",
"model_name": "gemini-3.1-flash-lite-preview",
"soul_file": "/home/tim/picoclaw-data/SOUL.md"
}
},
"model_list": [
{
"model_name": "gemma",
"model": "ollama/gemma3:4b",
"api_base": "http://vulcan:11434/v1"
},
{
"model_name": "gemini-2.5-flash",
"model": "openai/gemini-2.5-flash",
"api_keys": ["xxx-xxx"],
"api_base": "https://generativelanguage.googleapis.com/v1beta/openai"
},
{
"model_name": "gemini-3.1-flash-lite-preview",
"model": "openai/gemini-3.1-flash-lite-preview",
"api_keys": ["xxx-xxx"],
"api_base": "https://generativelanguage.googleapis.com/v1beta/openai"
},
{
"model_name": "claude",
"model": "openai/claude-3.5-sonnet",
"api_keys": ["sk-or-v1-xxx"],
"api_base": "https://openrouter.ai/api/v1"
}
],
"channels": {
"telegram": {
"enabled": true,
"token": "234232:xxxx",
"allow_from": ["xxxxx"]
}
},
"gateway": {
"host": "0.0.0.0",
"port": 18790
}
}

File diff suppressed because it is too large Load diff

17
local_pi/picoclaw.service Normal file
View file

@ -0,0 +1,17 @@
[Unit]
Description=PicoClaw AI Agent Runtime
After=network.target
Wants=network-online.target
[Service]
Type=simple
User=tim
WorkingDirectory=/home/tim/picoclaw-data
Environment="PICOCLAW_CONFIG=/home/tim/picoclaw-data/config/config.json"
Environment="OLLAMA_HOST=http://[IP_ADDRESS]"
Environment="OPENAI_API_KEY=[ENCRYPTION_KEY]"
ExecStart=/usr/local/bin/picoclaw gateway
Restart=on-failure
RestartSec=10s
StandardOutput=journal
StandardError=journal

View file

@ -0,0 +1,17 @@
[Unit]
Description=PicoClaw AI Agent Runtime
After=network.target
Wants=network-online.target
[Service]
Type=simple
User=tim
WorkingDirectory=/home/tim/picoclaw-data
Environment="PICOCLAW_CONFIG=/home/tim/picoclaw-data/config/config.json"
Environment="OLLAMA_HOST=http://[IP_ADDRESS]"
Environment="OPENAI_API_KEY=[ENCRYPTION_KEY]"
ExecStart=/usr/local/bin/picoclaw gateway
Restart=on-failure
RestartSec=10s
StandardOutput=journal
StandardError=journal

View file

@ -13,47 +13,51 @@ type Classifier interface {
// RuleClassifier is the v1 implementation.
// It uses a weighted sum of structural signals with no external dependencies,
// no API calls, and sub-microsecond latency. The raw sum is capped at 1.0 so
// that the returned score always falls within the [0, 1] contract.
// no API calls, and sub-microsecond latency. The raw sum is capped at 1.0.
//
// Individual weights (multiple signals can fire simultaneously):
// Signal weights (designed for a 0.65 threshold):
//
// token > 200 (≈600 chars): 0.35 — very long prompts are almost always complex
// token 50-200: 0.15 — medium length; may or may not be complex
// code block present: 0.40 — coding tasks need the heavy model
// tool calls > 3 (recent): 0.25 — dense tool usage signals an agentic workflow
// tool calls 1-3 (recent): 0.10 — some tool activity
// token > 500 (≈1500 chars): 0.30 — very long prompts add meaningful weight
// token 150-500: 0.10 — medium length; not enough alone
// code block present: 0.40 — coding tasks are the primary escalation signal
// tool calls > 3 (recent): 0.35 — dense tool chain signals a complex workflow
// tool calls 1-3 (recent): 0.10 — light tool activity
// conversation depth > 10: 0.10 — long sessions carry implicit complexity
// attachments present: 1.00 — hard gate; multi-modal always needs heavy model
// attachments (image/audio): 0.10 — images alone stay on Gemini Flash (vision);
// only escalate when combined with heavy signals
//
// Default threshold is 0.35, so:
// - Pure greetings / trivial Q&A: 0.00 → light ✓
// - Medium prose message (50200 tokens): 0.15 → light ✓
// - Message with code block: 0.40 → heavy ✓
// - Long message (>200 tokens): 0.35 → heavy ✓
// - Active tool session + medium message: 0.25 → light (acceptable)
// - Any message with an image/audio attachment: 1.00 → heavy ✓
// Practical routing outcomes at threshold 0.65:
// - Greeting / trivial Q&A: 0.00 → Gemini ✓
// - Medium prose (150500 tokens): 0.10 → Gemini ✓
// - Long prose (>500 tokens): 0.30 → Gemini ✓
// - Image only: 0.10 → Gemini ✓ (vision)
// - Image + medium text: 0.20 → Gemini ✓
// - Short message with a code block: 0.40 → Gemini ✓
// - Long message with a code block: 0.70 → Claude ✓
// - Dense multi-tool chain (>3 calls): 0.35 → Gemini (acceptable)
// - Dense tool chain + code block: 0.75 → Claude ✓
type RuleClassifier struct{}
// Score computes the complexity score for the given feature set.
// The returned value is in [0, 1]. Attachments short-circuit to 1.0.
// The returned value is in [0, 1].
func (c *RuleClassifier) Score(f Features) float64 {
// Hard gate: multi-modal inputs always require the heavy model.
if f.HasAttachments {
return 1.0
}
var score float64
// Token estimate — primary verbosity signal
switch {
case f.TokenEstimate > 200:
score += 0.35
case f.TokenEstimate > 50:
score += 0.15
// Attachments (image/audio): small weight so images alone stay on the
// vision-capable light model (Gemini Flash).
if f.HasAttachments {
score += 0.10
}
// Fenced code blocks — strongest indicator of a coding/technical task
// Token estimate — verbosity signal (higher bar than before)
switch {
case f.TokenEstimate > 500:
score += 0.30
case f.TokenEstimate > 150:
score += 0.10
}
// Fenced code blocks — primary escalation signal
if f.CodeBlockCount > 0 {
score += 0.40
}
@ -61,7 +65,7 @@ func (c *RuleClassifier) Score(f Features) float64 {
// Recent tool call density — indicates an ongoing agentic workflow
switch {
case f.RecentToolCalls > 3:
score += 0.25
score += 0.35
case f.RecentToolCalls > 0:
score += 0.10
}
@ -71,8 +75,7 @@ func (c *RuleClassifier) Score(f Features) float64 {
score += 0.10
}
// Cap at 1.0 to honor the [0, 1] contract even when multiple signals fire
// simultaneously (e.g., long message + code block + tool chain = 1.10 raw).
// Cap at 1.0
if score > 1.0 {
score = 1.0
}