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.
|
// RuleClassifier is the v1 implementation.
|
||||||
// It uses a weighted sum of structural signals with no external dependencies,
|
// 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
|
// no API calls, and sub-microsecond latency. The raw sum is capped at 1.0.
|
||||||
// that the returned score always falls within the [0, 1] contract.
|
|
||||||
//
|
//
|
||||||
// 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 > 500 (≈1500 chars): 0.30 — very long prompts add meaningful weight
|
||||||
// token 50-200: 0.15 — medium length; may or may not be complex
|
// token 150-500: 0.10 — medium length; not enough alone
|
||||||
// code block present: 0.40 — coding tasks need the heavy model
|
// code block present: 0.40 — coding tasks are the primary escalation signal
|
||||||
// tool calls > 3 (recent): 0.25 — dense tool usage signals an agentic workflow
|
// tool calls > 3 (recent): 0.35 — dense tool chain signals a complex workflow
|
||||||
// tool calls 1-3 (recent): 0.10 — some tool activity
|
// tool calls 1-3 (recent): 0.10 — light tool activity
|
||||||
// conversation depth > 10: 0.10 — long sessions carry implicit complexity
|
// 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:
|
// Practical routing outcomes at threshold 0.65:
|
||||||
// - Pure greetings / trivial Q&A: 0.00 → light ✓
|
// - Greeting / trivial Q&A: 0.00 → Gemini ✓
|
||||||
// - Medium prose message (50–200 tokens): 0.15 → light ✓
|
// - Medium prose (150–500 tokens): 0.10 → Gemini ✓
|
||||||
// - Message with code block: 0.40 → heavy ✓
|
// - Long prose (>500 tokens): 0.30 → Gemini ✓
|
||||||
// - Long message (>200 tokens): 0.35 → heavy ✓
|
// - Image only: 0.10 → Gemini ✓ (vision)
|
||||||
// - Active tool session + medium message: 0.25 → light (acceptable)
|
// - Image + medium text: 0.20 → Gemini ✓
|
||||||
// - Any message with an image/audio attachment: 1.00 → heavy ✓
|
// - 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{}
|
type RuleClassifier struct{}
|
||||||
|
|
||||||
// Score computes the complexity score for the given feature set.
|
// 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 {
|
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
|
var score float64
|
||||||
|
|
||||||
// Token estimate — primary verbosity signal
|
// Attachments (image/audio): small weight so images alone stay on the
|
||||||
switch {
|
// vision-capable light model (Gemini Flash).
|
||||||
case f.TokenEstimate > 200:
|
if f.HasAttachments {
|
||||||
score += 0.35
|
score += 0.10
|
||||||
case f.TokenEstimate > 50:
|
|
||||||
score += 0.15
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// 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 {
|
if f.CodeBlockCount > 0 {
|
||||||
score += 0.40
|
score += 0.40
|
||||||
}
|
}
|
||||||
|
|
@ -61,7 +65,7 @@ func (c *RuleClassifier) Score(f Features) float64 {
|
||||||
// Recent tool call density — indicates an ongoing agentic workflow
|
// Recent tool call density — indicates an ongoing agentic workflow
|
||||||
switch {
|
switch {
|
||||||
case f.RecentToolCalls > 3:
|
case f.RecentToolCalls > 3:
|
||||||
score += 0.25
|
score += 0.35
|
||||||
case f.RecentToolCalls > 0:
|
case f.RecentToolCalls > 0:
|
||||||
score += 0.10
|
score += 0.10
|
||||||
}
|
}
|
||||||
|
|
@ -71,8 +75,7 @@ func (c *RuleClassifier) Score(f Features) float64 {
|
||||||
score += 0.10
|
score += 0.10
|
||||||
}
|
}
|
||||||
|
|
||||||
// Cap at 1.0 to honor the [0, 1] contract even when multiple signals fire
|
// Cap at 1.0
|
||||||
// simultaneously (e.g., long message + code block + tool chain = 1.10 raw).
|
|
||||||
if score > 1.0 {
|
if score > 1.0 {
|
||||||
score = 1.0
|
score = 1.0
|
||||||
}
|
}
|
||||||
|
|
|
||||||
Loading…
Add table
Reference in a new issue