MultiHead is a framework, not a product. This guide shows how to make it yours.
MultiHead has two layers:
- Framework — orchestration, knowledge store, adapters, CLI, shell (you keep these)
- Reference implementation — our extraction prompts, fusion tuning, pipeline stages (you customize these)
You don't fork the framework. You customize the implementation layer for your domain.
Heads are pluggable intelligence sources. Add any LLM, tool, or service.
Edit config/heads.yaml:
my-local-llm:
name: "My Fine-tuned Model"
adapter: ollama
model: "my-model:latest"
kind: llm
gpu_required: true
vram_hint_mb: 8000
my-api-model:
name: "GPT-4o via OpenAI"
adapter: openai
model_name: "gpt-4o"
kind: llm
gpu_required: false
extra:
api_key_env: "OPENAI_API_KEY"Available adapters: transformers, ollama, vllm, openai, anthropic, claude_agent_sdk, mock, deterministic
# src/multihead/adapters/my_adapter.py
from multihead.adapters.base import HeadAdapter
class MyAdapter(HeadAdapter):
async def load(self):
# Connect to your model/service
pass
async def generate(self, prompt: str) -> dict:
# Call your model
return {"text": response, "tokens_in": n, "tokens_out": m}
async def unload(self):
passRegister in heads.yaml:
my-head:
adapter: my_adapter
model: whateverThe claim extractor prompt determines what knowledge gets extracted from conversations. Ours is tuned for software engineering — yours should match your domain.
src/multihead/extractors/claim_extractor.py — the PROMPT_TEMPLATE variable.
Replace the prompt with your domain's knowledge types:
PROMPT_TEMPLATE = """Extract knowledge from this text about [YOUR DOMAIN].
DO extract:
- [Your knowledge type 1]: "description"
- [Your knowledge type 2]: "description"
- [Your knowledge type 3]: "description"
DO NOT extract:
- [Noise type 1]
- [Noise type 2]
Each claim must be a complete, self-contained sentence of at least 50 characters.
Output ONLY a JSON array:
[{{"claim_type":"<TYPE>","claim_key":"<KEY>","statement":"<SENTENCE>","confidence":0.8}}]
Text:
{text}
JSON:"""- Be specific about what "durable knowledge" means in your domain
- List explicit DO NOT extract items — reduces noise dramatically
- Set confidence rules (direct observation > inference > hearsay)
- Test with 10-20 sample texts before running on your full corpus
Fusion determines how claims from different sources are compared and verified.
src/multihead/claim_fusion.py — the ClaimFusion class.
Channel reliability — which observation methods are most trustworthy in YOUR domain:
# In _score_convergence():
# Our defaults (software engineering):
# code_read: 0.95, git_diff: 0.90, conversation: 0.50-0.80
#
# For medical domain you might use:
# clinical_trial: 0.95, peer_review: 0.90, patient_report: 0.60
#
# For legal domain:
# statute_text: 0.95, case_law: 0.85, legal_opinion: 0.70Convergence thresholds — when to corroborate vs contest:
# _fuse_topic():
if avg_convergence > 0.7: # Adjust: higher = stricter corroboration
action = "boost"
elif avg_convergence < -0.3: # Adjust: lower = stricter contradiction
action = "flag_contradiction"Sub-categories — what types of disagreement matter in your domain:
# _classify_divergence():
# We classify: TEMPORAL_DRIFT, CONFIG_DRIFT, IMPROVEMENT_UNVERIFIED
# You might add: JURISDICTION_CONFLICT, VERSION_MISMATCH, etc.The pipeline is a sequence of stages. Add, remove, or reorder them.
src/multihead/night_shift/models.py — the STAGES list:
STAGES = [
StageDefinition(0, "session_harvest", ...),
StageDefinition(1, "select_input_window", ...),
# ... add your own stages here
StageDefinition(N, "my_custom_stage", StageGate(on_fail="continue")),
]- Define the stage in
models.py - Add a handler method in
stages_early.py,stages_advanced.py, orstages_late.py:
async def _stage_my_custom_stage(self, context: dict) -> dict:
"""My custom extraction/analysis stage."""
# Query live DB
with self.knowledge._connect() as conn:
rows = conn.execute("SELECT ...").fetchall()
# Process
results = do_your_thing(rows)
# Return metrics (shown in stage summary)
return {"metrics": {"items_processed": len(results), "key_finding": "value"}}- The stage runner handles checkpointing, error recovery, and dependency management automatically.
Use depends_on to control execution order:
StageDefinition(10, "my_analysis", ..., depends_on=[7]), # runs after claim_extraction
StageDefinition(11, "my_report", ..., depends_on=[10]), # runs after my_analysisStages with no dependency on pending batch stages run in parallel.
The router decides which head handles each task.
src/multihead/router/_scoring.py
Adjust weights for your use case:
# Default weights (software engineering focus):
# active_state: 40 — prefer already-loaded head
# circuit_breaker: 30 — avoid failing heads
# vram_fit: 15 — must fit in available VRAM
# error_rate: 10 — historical success
# latency: 5 — speed preference
# For a cost-sensitive setup:
# cost: 40, capability: 30, latency: 20, error_rate: 10
# For a quality-focused setup:
# capability: 50, error_rate: 30, latency: 10, cost: 10MultiHead fuses knowledge from independent channels. Add channels for your domain.
code_read— AST analysis of source filescode_behavior_llm— LLM behavioral analysisgit_diff— git commit diffsgit_history— commit messagesci_test— CI/CD resultsuser_statement/assistant_statement— conversationagent_deposit— explicit agent deposits
Write an extractor that produces claims with your observation_method:
# src/multihead/extractors/my_channel.py
async def extract_from_my_source(source_path, adapter):
claims = []
for item in read_my_source(source_path):
claims.append({
"claim_key": f"my_domain.{item.topic}",
"statement": item.text,
"confidence": 0.85,
"observation_method": "my_custom_channel", # Your channel name
"file_path": item.source_file,
})
return claimsThen add a nightshift stage that calls it. Fusion will automatically compare your channel's claims against other channels for the same files.
The claims schema supports extension via JSON fields.
Use provenance_json to store domain-specific metadata:
provenance = Provenance(
produced_by={"kind": "agent", "id": "my-agent"},
observation_method="my_channel",
source_anchor={
"file_path": "path/to/file",
"custom_field": "your domain data",
},
evidence=[{"type": "my_evidence", "text": "supporting quote"}],
)SELECT claim_key, statement FROM claims
WHERE json_extract(provenance_json, '$.source_anchor.custom_field') = 'value';Add slash commands for your domain:
# src/multihead/slash_commands/_my_commands.py
from . import register_command
@register_command("/my-command")
async def my_command(args, context):
"""Description of what this does."""
# Your logic here
return "Output to display"| Keep as-is (framework) | Customize (implementation) |
|---|---|
| Orchestrator / DAG executor | Extraction prompts |
| Knowledge store schema | Fusion policy / thresholds |
| Consensus engine | Channel reliability hierarchy |
| Head manager / adapters | Router weights |
| CLI / Shell / API / MCP | Pipeline stages |
| Night Shift stage runner | Domain-specific channels |
| Batch recovery / checkpoint | Config (heads, solvers, recipes) |