symfonic.capabilities.memory.policy¶
policy ¶
The learning policy a maintenance run reads, owned by whoever runs it.
The generated worker reads eighteen fields off agent._config with
getattr(name, default). That is the deployment's learning policy, which the
agent holds only because the compatibility configuration carries everything.
Three consequences, and the third is the one that bites:
- it is the last engine private in the generated app;
- every default is written twice -- once in the framework, once in the template -- and nothing checks the two agree;
- the worker cannot run without an agent. It takes no turns and needs no model, so building one in order to read eighteen numbers makes a maintenance job fail whenever the invocation stack does.
So the policy is a value. Defaults live in one place, and the agent stops being a configuration transport.
Moved into the capability with the roster it configures. It was the legacy
consolidator's knob contract and it is now both routes' -- as_kwargs still
answers SleepConsolidator, and :meth:LearningPolicy.as_phase_kwargs
answers the phase factories. One value object, two spellings of the same
nineteen numbers, so a deployment that tunes a threshold tunes it for whichever
route runs.
LearningPolicy
dataclass
¶
LearningPolicy(lookback_hours: int = 24, promotion_min_pattern_count: int = 3, promotion_recency_days: int = 30, promotion_max_drafts_per_run: int = 5, promotion_use_tool_calls_fallback: bool = False, promotion_promote_assistant_content: bool = False, phase_12_use_llm_extractor: bool = False, phase_12_llm_model: str = 'claude-haiku-4-5', phase_12_llm_max_episodes_per_run: int = 100, phase_12_llm_max_drafts_per_run: int = 5, episodic_summarization_max_entries: int = 100, episodic_summarization_batch_size: int = 50, phase1_spreading_weight: float = 0.5, synthetic_link_min_co_count: int = 2, enable_entity_linker: bool = False, entity_linker_extractor_kind: str = 'regex', entity_linker_min_mention_count: int = 2, entity_linker_max_episodics_per_run: int = 200, entity_linker_confidence_threshold: float = 0.5)
What a consolidation run should promote, summarise and link.
Frozen: a run reads its policy once, and one that could change mid-pass would produce a result nobody can reproduce.
as_kwargs ¶
The keyword arguments a consolidation run takes.
One call site instead of nineteen. Every
getattr(config, name, default) it replaces was a place the
framework's default and the template's copy could drift apart with
nothing to notice.
Source code in src/symfonic/capabilities/memory/policy.py
as_phase_kwargs ¶
The same knobs, named as the phase factories name them.
A translation table rather than a rename, because the two vocabularies are genuinely different: the shipped consolidator takes nineteen keyword arguments on one constructor, and the factories take them where the phase that reads each one is built. Written once, here, next to the fields it maps -- a worker doing this inline would be a second copy of every default in the place this class exists to remove them from.
enable_entity_linker and phase_12_use_llm_extractor are absent
on purpose: in the factories a phase runs when it was given the
collaborator it needs, so "enabled" is not a flag but the presence of
an extractor. A deployment that set the flag and composed nothing would
otherwise have a phase that reports zero rather than declining.
Source code in src/symfonic/capabilities/memory/policy.py
from_settings
classmethod
¶
Read a deployment's own settings object, falling back per field.
Absent names take the shipped default rather than raising: a settings object carries what that deployment chose to configure and nothing else, and requiring all nineteen would put a second copy of every default back in the place this removes it from.