feat(routing): calibrated 0.65 routing threshold for Gemini-first behavior
This commit is contained in:
parent
3881f69aa0
commit
a7638d4557
9 changed files with 1957 additions and 33 deletions
42
local_pi/agent-01-alpha/agent.yaml
Normal file
42
local_pi/agent-01-alpha/agent.yaml
Normal 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
|
||||
44
local_pi/agent-02-pulse/agent.yaml
Normal file
44
local_pi/agent-02-pulse/agent.yaml
Normal 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.)
|
||||
57
local_pi/agent-03-forge/agent.yaml
Normal file
57
local_pi/agent-03-forge/agent.yaml
Normal 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
|
||||
41
local_pi/agent-04-atlas/agent.yaml
Normal file
41
local_pi/agent-04-atlas/agent.yaml
Normal 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
47
local_pi/config_ref.json
Normal 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
|
||||
}
|
||||
}
|
||||
|
||||
1656
local_pi/picoclaw-setup-guide_1.md
Normal file
1656
local_pi/picoclaw-setup-guide_1.md
Normal file
File diff suppressed because it is too large
Load diff
17
local_pi/picoclaw.service
Normal file
17
local_pi/picoclaw.service
Normal 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
|
||||
17
local_pi/picoclaw_ref.service
Normal file
17
local_pi/picoclaw_ref.service
Normal 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
|
||||
|
|
@ -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
|
||||
// conversation depth > 10: 0.10 — long sessions carry implicit complexity
|
||||
// attachments present: 1.00 — hard gate; multi-modal always needs heavy model
|
||||
// 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 (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 (50–200 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 (150–500 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
|
||||
}
|
||||
|
|
|
|||
Loading…
Add table
Reference in a new issue