feat(genome): HypothesisAgentGenome with deterministic id and serde

Dataclass per genoma agente ipotesi con campi prompt/feature/temperature/
top_p/model_tier/lookback/style + parent_ids/generation. Id sha1[:16]
deterministico su contenuto canonico (feature_access ordinate, float
arrotondati). to_dict/from_dict per persistenza.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This commit is contained in:
2026-05-09 19:45:07 +02:00
parent 26c328d541
commit 42a95a52b5
3 changed files with 122 additions and 0 deletions
View File
+72
View File
@@ -0,0 +1,72 @@
from __future__ import annotations
import hashlib
import json
from dataclasses import dataclass, field
from enum import StrEnum
from typing import Any
class ModelTier(StrEnum):
B = "B" # Sonnet 4.6 via Anthropic
C = "C" # Qwen 2.5 72B via OpenRouter
@dataclass
class HypothesisAgentGenome:
system_prompt: str
feature_access: list[str]
temperature: float
top_p: float
model_tier: ModelTier
lookback_window: int
cognitive_style: str
parent_ids: list[str] = field(default_factory=list)
generation: int = 0
id: str = ""
def __post_init__(self) -> None:
if not self.id:
self.id = self._compute_id()
def _compute_id(self) -> str:
payload = {
"system_prompt": self.system_prompt,
"feature_access": sorted(self.feature_access),
"temperature": round(self.temperature, 4),
"top_p": round(self.top_p, 4),
"model_tier": self.model_tier.value,
"lookback_window": self.lookback_window,
"cognitive_style": self.cognitive_style,
}
s = json.dumps(payload, sort_keys=True)
return hashlib.sha1(s.encode()).hexdigest()[:16]
def to_dict(self) -> dict[str, Any]:
return {
"id": self.id,
"system_prompt": self.system_prompt,
"feature_access": self.feature_access,
"temperature": self.temperature,
"top_p": self.top_p,
"model_tier": self.model_tier.value,
"lookback_window": self.lookback_window,
"cognitive_style": self.cognitive_style,
"parent_ids": self.parent_ids,
"generation": self.generation,
}
@classmethod
def from_dict(cls, data: dict[str, Any]) -> HypothesisAgentGenome:
return cls(
system_prompt=data["system_prompt"],
feature_access=list(data["feature_access"]),
temperature=float(data["temperature"]),
top_p=float(data["top_p"]),
model_tier=ModelTier(data["model_tier"]),
lookback_window=int(data["lookback_window"]),
cognitive_style=data["cognitive_style"],
parent_ids=list(data.get("parent_ids", [])),
generation=int(data.get("generation", 0)),
id=data.get("id", ""),
)
+50
View File
@@ -0,0 +1,50 @@
from multi_swarm.genome.hypothesis import HypothesisAgentGenome, ModelTier
def test_genome_creation_defaults():
g = HypothesisAgentGenome(
system_prompt="Pensa come un fisico.",
feature_access=["close", "volume"],
temperature=0.9,
top_p=0.95,
model_tier=ModelTier.C,
lookback_window=200,
cognitive_style="physicist",
)
assert g.id is not None
assert g.parent_ids == []
assert g.generation == 0
def test_genome_serialization_roundtrip():
g = HypothesisAgentGenome(
system_prompt="Pensa come un biologo.",
feature_access=["close", "high", "low"],
temperature=1.1,
top_p=0.9,
model_tier=ModelTier.C,
lookback_window=300,
cognitive_style="biologist",
parent_ids=["abc"],
generation=5,
)
payload = g.to_dict()
g2 = HypothesisAgentGenome.from_dict(payload)
assert g2.system_prompt == g.system_prompt
assert g2.feature_access == g.feature_access
assert g2.temperature == g.temperature
assert g2.parent_ids == g.parent_ids
assert g2.generation == g.generation
assert g2.id == g.id
def test_genome_id_is_deterministic_on_content():
g1 = HypothesisAgentGenome(
system_prompt="X", feature_access=["close"], temperature=0.5,
top_p=0.9, model_tier=ModelTier.C, lookback_window=100, cognitive_style="x",
)
g2 = HypothesisAgentGenome(
system_prompt="X", feature_access=["close"], temperature=0.5,
top_p=0.9, model_tier=ModelTier.C, lookback_window=100, cognitive_style="x",
)
assert g1.id == g2.id