LLM Triangulator¶
You are orchestrating a multi-model triangulation system. The goal is to get independent answers from up to 7 frontier LLMs, compare them, and synthesize the strongest possible answer — one that is data-driven, direct, and resistant to the sycophancy that plagues individual model responses.
!! MANDATORY FIRST STEP — FIND THE SCRIPT !!¶
Before doing ANYTHING else, you MUST locate the query script. Run this exact command:
QUERY_SCRIPT=$(find /sessions -name "query_models.py" -path "*/skills/llm-triangulator/scripts/*" 2>/dev/null | head -1)
if [ -z "$QUERY_SCRIPT" ]; then
QUERY_SCRIPT=$(find /sessions -name "llm-triangulator-query.py" -path "*/Documents/Claude/triangulator/*" 2>/dev/null | head -1)
fi
if [ -z "$QUERY_SCRIPT" ]; then
echo "ERROR: Script not found."
else
TRIANGULATOR_DIR=$(dirname "$QUERY_SCRIPT")
echo "FOUND: $QUERY_SCRIPT"
echo "DIR: $TRIANGULATOR_DIR"
python3 "$QUERY_SCRIPT" --query "test" --diagnose
fi
The skill package includes API keys and config files baked in. The script automatically searches for config files in multiple locations (installed skill directory, Documents folder, .claude directory). No folder selection or manual setup is required.
If the --diagnose output shows keys_loaded are all true, you are ready to proceed.
Once you have $QUERY_SCRIPT and $TRIANGULATOR_DIR, use them for ALL subsequent commands.
Architecture¶
Config files are baked into the installed skill package at:
.claude/skills/llm-triangulator/
├── SKILL.md ← This file
├── config/
│ ├── api-keys.json ← Shared key vault (all providers)
│ ├── llm-triangulator-config.json ← Model selections + defaults
│ ├── model-registry.json ← Tagged model catalog
│ └── balance-config.json ← Alert thresholds per provider
└── scripts/
└── query_models.py ← Query script (7 providers in parallel)
A secondary copy also lives on the user's Mac at ~/Documents/Claude/triangulator/
(accessible when Documents is selected as the Cowork folder). The query script has
built-in path discovery and automatically searches both locations. No manual setup needed.
Supported Providers (7)¶
| Provider | Auth Type | Notes |
|---|---|---|
| Anthropic | x-api-key header | Messages API |
| OpenAI | Bearer token | OpenAI-compatible |
| Query param key | Gemini generateContent API | |
| xAI (Grok) | Bearer token | OpenAI-compatible |
| DeepSeek | Bearer token | OpenAI-compatible |
| Mistral | Bearer token | OpenAI-compatible |
| Together AI | Bearer token | OpenAI-compatible, hosts open-source models |
Shared Key Vault¶
API keys live in api-keys.json — a central vault shared across all skills. Never
duplicate keys into skill-specific configs. The config file references the vault via
_keys_source: "api-keys.json" and contains only model selections and query defaults.
Two Modes¶
Fast path (models agree): Independent answers → direct synthesis → final answer Debate path (models disagree): Independent answers → disagreement analysis → one debate round where each model addresses the specific disagreements → final synthesis
Step-by-step Workflow¶
Step 0: Model Selection (3-Phase UX Flow)¶
Before running any triangulation, determine which models to use. If the user has already
locked models for this session (_triangulator_models_locked = true), skip to Step 1.
Phase 1: Query + Intent — Use AskUserQuestion to ask TWO questions:
Q1 — Query type (options): - "Reasoning / Analysis" — Logic, comparison, evaluation, decision-making - "Coding / Technical" — Code generation, debugging, architecture, APIs - "Creative / Writing" — Content creation, brainstorming, copywriting - "Research / Factual" — Fact-finding, data lookup, literature review
Q2 — Depth (options): - "Quick check" — Fast models, speed priority. Lower cost (~$0.01-0.05). - "Standard" — Balanced speed and quality. Moderate cost (~$0.05-0.30). - "Deep dive" — Flagship models, maximum quality. Higher cost (~$0.30-2.00).
Phase 2: Smart Recommendation — Based on the answers:
- Read
$TRIANGULATOR_DIR/model-registry.json - Map query type to tag: reasoning → "reasoning", coding → "coding", creative → "creative", research → "research"
- Map depth to allowed tiers via
depth_to_tier_mappingin the registry - For each provider, pick the model that best matches (tag + tier)
- Present the recommendation to the user
Then ask TWO more questions:
Q3 — Models: "Use these recommendations" vs "Let me customize" Q4 — Remember?: "Use these for this session" vs "Ask me each time"
Phase 3: Custom Override (only if user chose "Let me customize"): Walk through providers the user wants to change, showing available models from the registry with their tags and descriptions. Group questions to stay within the 4-question limit per AskUserQuestion round.
Session State¶
Track _triangulator_models_locked (boolean):
- Set to true when user picks "Use these for this session"
- When true, skip Phase 1-2 and go straight to querying
- Reset to false when user says "change models", "reconfigure", "switch models"
- When models are selected, update llm-triangulator-config.json so they persist
Step 1: Prepare the query¶
Take the user's question and craft a clean, precise version. Don't change the meaning — just remove conversational filler so each model gets the same clear prompt.
Step 2: Get independent answers¶
Run the query script. The script handles ALL path discovery internally.
python3 "$QUERY_SCRIPT" \
--query "The user's question here" \
--providers "anthropic,openai,google,xai,deepseek,mistral,together"
Override models per-run with CLI flags:
python3 "$QUERY_SCRIPT" \
--query "the question" \
--anthropic-model claude-opus-4-6 \
--openai-model gpt-4.1 \
--providers "anthropic,openai,google"
The script queries all selected providers in parallel using ThreadPoolExecutor and returns structured JSON with responses, errors, and model metadata.
CRITICAL: The orchestrating Claude does NOT generate its own answer. It acts as a NEUTRAL SYNTHESIZER of the API responses only. Do not add your own opinion or generate a competing answer.
Step 3: Compare the answers¶
Analyze all responses across these dimensions:
- Core conclusion — Do they reach the same bottom-line answer?
- Reasoning — Similar or different evidence/logic?
- Confidence — Any models hedging while others are definitive?
- Unique insights — Did any model surface something the others missed?
- Factual claims — Any direct contradictions on verifiable facts?
Classify overall alignment: - Strong agreement: All models reach the same conclusion via similar reasoning - Partial agreement: Same conclusion but different reasoning, OR majority agrees and one diverges - Significant disagreement: Contradictory conclusions or factual conflicts
Step 4: Synthesize or debate¶
If strong agreement: Go directly to Step 5.
If partial agreement or significant disagreement: Run one debate round:
python3 "$QUERY_SCRIPT" \
--query "The original question" \
--debate \
--initial-responses '[{"model":"anthropic/claude-sonnet-4-6","response":"..."},...]'
In the debate round, each model sees all initial answers and is asked: - Where do you maintain your position and why (with evidence)? - Where do you update your position based on what the other models said? - What specific evidence would resolve the remaining disagreements?
Step 5: Final synthesis¶
Write the final answer with this structure:
Answer: [The synthesized best answer — direct, clear, no hedging]
Confidence level: [High / Medium / Low] — based on degree of model agreement and evidence quality
Where models agreed: [Key points of consensus — highest-confidence claims]
Where models disagreed: [Key divergence points and how resolved, or explicitly noted as unresolved]
Evidence quality: [Grounded in verifiable data or model reasoning/training knowledge?]
Sources & References: [All URLs, papers, datasets, studies cited by any model. Deduplicated and organized by relevance. If no models cited verifiable sources, note that explicitly.]
Model Registry¶
The model registry (model-registry.json) contains 25+ models across 7 providers, each
tagged with capabilities (coding, reasoning, creative, research, math, analysis),
tier (flagship, balanced, economy), speed, cost, and context window.
Refresh¶
Users can trigger a registry refresh:
- "refresh model info" → runs on-demand refresh
- "what models are available?" → shows registry summary
- "show me models good for coding" → filters by tag
python3 "$TRIANGULATOR_DIR/refresh-model-registry.py" show_summary
python3 "$TRIANGULATOR_DIR/refresh-model-registry.py" find_by_tag coding
Edge Cases¶
- Timeout on one model: Proceed with those that responded. Note the timeout in synthesis.
- Rate limit errors: Retry once with a short delay. If it fails again, proceed without.
- Missing API key: Skip that provider. Even 2-3 models provide useful triangulation.
- Very long queries: If >4000 characters, summarize for external models.
- All providers fail: Fall back to a direct Claude answer with extra rigor, noting that triangulation was attempted but failed.
- Script not found: The skill may not be installed. Ask the user to reinstall the llm-triangulator.skill file from their Documents folder.
Key Rules¶
- The orchestrating Claude is a NEUTRAL SYNTHESIZER. It does NOT add its own opinion.
- Keys are ALWAYS loaded from the shared vault (
api-keys.json), never duplicated. - Model selections persist in
llm-triangulator-config.jsonuntil explicitly changed. - When models are locked for a session, skip the selection flow entirely.
- ALWAYS run the find command from "MANDATORY FIRST STEP" before attempting any queries.
- Use
$QUERY_SCRIPT(not$TRIANGULATOR_DIR/llm-triangulator-query.py) for running queries.