Factoryn tärkein säädin ei ole "parempi prompt". Se on policy — tarkka määrittely siitä, mitä rooli saa tehdä ja mitä ei.
Role-based policy
Role-based policy = määritelmä, joka kertoo yhdelle agentille: 1) mitkä työkalut sillä on käytössä, 2) mitä se EI saa tehdä, 3) mikä on sen output-muoto. Sama LLM-backend (esim. Claude Sonnet) tuottaa täysin eri käyttäytymistä eri policyilla. Tämä on factoryn vaikuttavin yksittäinen säädin — ei prompt-engineering.
Neljä perusroolia
| Rooli | Permissions | Output | Constraints |
|---|---|---|---|
| Analyzer | Read-only | Findings report | Ei modifikaatioita |
| Planner | Read-only | Step-by-step plan | Ei suoritusta |
| Fixer | Read + write targeted | Minimal diff | Vain scoped muutokset |
| Reviewer | Read-only | Pass/fail + rationale | Annetut kriteerit |
Miksi neljä, ei yksi?
Single Responsibility -periaate, sovellettuna agentteihin. Yksi agentti, yksi vastuu — ei sekoiteta:
- ❌ "Analyze and fix" → fixaa väärin, koska kiirehtii korjaukseen
- ✅ "Analyze" → "Plan" → "Fix" → "Review" → jokainen on hyvä omassaan
Pro tip
Anti-pattern: "Yksi älykäs super-agentti, joka tekee kaiken." Se on bag-of-agents -ongelma yhden agentin sisällä. Erottele rooleihin samalla logiikalla millä erottelet mikropalveluita: yksi vastuu, selkeä rajapinta, helppo testata.
Policy esimerkkinä: Analyzer
ANALYZER_POLICY = {
"role": "analyzer",
"system_prompt": """You are a code analyzer.
Your job: read the codebase and produce a findings report.
You CANNOT modify files. You CANNOT execute shell commands
that change state. Output a structured report only.""",
"allowed_tools": [
"read_file",
"grep",
"glob",
],
"forbidden_tools": [
"edit_file",
"write_file",
"bash", # No shell — can't risk side effects
"git",
],
"output_schema": {
"type": "object",
"required": ["findings", "severity_summary"],
"properties": {
"findings": {
"type": "array",
"items": {
"type": "object",
"required": ["file", "line", "severity", "description"],
"properties": {
"file": {"type": "string"},
"line": {"type": "integer"},
"severity": {"enum": ["info", "warn", "error"]},
"description": {"type": "string"},
},
},
},
"severity_summary": {"type": "object"},
},
},
"max_iterations": 5,
"max_cost_usd": 0.20,
}Minimal factory — alle 50 riviä
Pieni esimerkki: yksi rooli, yksi kutsu, jäsennelty I/O.
#!/usr/bin/env python3
"""Minimal factory: one agent, one call, structured output."""
import json
import sys
from anthropic import Anthropic
POLICY = {
"role": "summarizer",
"system": "You produce JSON summaries. Output ONLY valid JSON.",
"model": "claude-sonnet-4-6",
"max_tokens": 800,
"schema": {
"type": "object",
"required": ["title", "key_points", "risks"],
},
}
def run_factory(input_text: str) -> dict:
client = Anthropic()
response = client.messages.create(
model=POLICY["model"],
max_tokens=POLICY["max_tokens"],
system=POLICY["system"],
messages=[{
"role": "user",
"content": f"Summarize the following with schema {POLICY['schema']}:\n\n{input_text}",
}],
)
text = response.content[0].text
try:
result = json.loads(text)
except json.JSONDecodeError:
return {"status": "error", "reason": "invalid_json", "raw": text}
# Validate schema (simple check — use jsonschema for real)
missing = [k for k in POLICY["schema"]["required"] if k not in result]
if missing:
return {"status": "error", "reason": "schema_violation", "missing": missing}
return {"status": "success", "data": result}
if __name__ == "__main__":
text = sys.stdin.read()
print(json.dumps(run_factory(text), indent=2))Aja se:
echo "We're refactoring the auth module to use OAuth2..." \
| python 01_minimal_factory.pyOutput:
{
"status": "success",
"data": {
"title": "OAuth2 Migration for Auth Module",
"key_points": [
"Replacing legacy session-based auth",
"OAuth2 with PKCE flow"
],
"risks": [
"Existing sessions invalidated on cutover",
"Need backwards compat shim for 30 days"
]
}
}Mikä tekee tästä factoryn, ei vain LLM-kutsun?
| Ominaisuus | Vain LLM-kutsu | Minimal factory |
|---|---|---|
| Role/persona | Implisiittinen | Eksplisiittinen POLICY |
| Model lukittu | Ei | Kyllä (POLICY["model"]) |
| Output schema | Ei | Pakollinen JSON schema |
| Validaatio | Ei | Schema-check + virhe-status |
| Versioitavissa | Ei | Policy = commit hash |
Minimaalinen factory on jo voitto: voit testata sen, voit version-controlloida sen, voit ajaa sen rinnakkain CI:ssä.
Hands-on rehearsal: Three roles, same backend
# Saman backend, kolme roolia — eri käyttäytyminen
python 02_factory_catalog.py analyzer "Analyze backend_runner.py"
python 02_factory_catalog.py planner "Plan logging for backend_runner.py"
python 02_factory_catalog.py fixer "Add logging to backend_runner.py"| Rooli | Allowed tools | Mode | Tarkoitus |
|---|---|---|---|
| analyzer | Read, Glob, Grep | Read-only | Tarkasta ja selitä |
| planner | Read, Glob, Grep | Plan-only | Tuota suunnitelma |
| fixer | Read, Grep, Edit, Bash | Accept edits | Tee turvalliset muutokset |
Haaste: Suunnittele oma rooli
Suunnittele 5. rooli omasta tiimisi tarpeesta. Mitä se tekee, mitkä työkalut sillä on, mitkä on sen forbidden tools, mikä on output schema?
Session resume — pitkät factoryt
Real-world factory voi ajaa minuutteja. Tarvitset session resume:
# Aloita
python 03_resumable_factory.py start \
--backend opencode "Inspect auth subsystem"
# → tallentaa state /tmp/factory-session-abc123.json
# Jatka samasta kohtaa
python 03_resumable_factory.py resume \
--backend opencode "Now propose fixes"
# → lukee state, jatkaa kontekstistaSession resume
Session resume tallentaa agentin working memory levylle (esim. JSON-tiedostoon /tmp/-polkuun). Seuraava ajo lukee tämän tilan ja jatkaa siitä, mihin jäätiin. Tärkeää pitkille tehtäville (>5 min) ja kalliille LLM-kutsuille — älä menetä kontekstia jokaisella crashilla tai timeoutilla.
Yhteenveto
- 🎭 Roolit > yksi super-agentti — Single Responsibility myös agenteille
- 📋 Policy on factoryn säädin — sama backend, eri käyttäytyminen
- 📐 Schema pakollinen — validoi output, palauta virhe-status
- 💾 Session resume pitkille tehtäville
- 📦 Minimal factory < 50 riviä mutta jo version-controlable, testattavissa, ajettavissa
Seuraavassa oppitunnissa: Spec-driven delivery loops — miten factory ajaa Read Spec → Implement → Run Checks → Review -silmukkaa, kunnes hyväksytty.