symfonic.core.learning.procedural_extractor_llm¶
procedural_extractor_llm ¶
LLM-backed procedural extractor (v7.6 — Jarvio Ask 9 Option A).
Single LLM call per tenant per nightly_nap. Reads recent episodic
entries enriched with the v7.4.7 metadata['tool_calls'] trace
producer, asks the model to identify durable action / preflight /
recovery / guardrail rules, and emits structured
:class:~symfonic.core.learning.procedural_extractor.ProceduralDraft
records that the dispatch site in
:mod:symfonic.core.learning.phases_procedural_promotion turns into
draft :class:~symfonic.memory.models.entry.MemoryEntry writes via
:func:~symfonic.memory.layers.procedural.layer.ProceduralLayer.store_skill.
Lift-and-shift from :mod:symfonic.core.learning.entity_extractor_llm
(v7.2) — same best-effort JSON parsing contract: malformed responses,
missing fields, and model-invocation exceptions all yield [] rather
than raise.
Default-off ship: the extractor is reachable only when
FrameworkConfig.phase_12_use_llm_extractor=True AND the consolidator
has a bound chat model. Either-or with the regex extractor (the eval
recommended this over a merged stream).
Design reference:
.claude/docs/2026-05-28-jarvio-llm-procedural-extractor-eval.md
§8.2 / §8.3.
LlmProceduralExtractor ¶
LlmProceduralExtractor(
chat_model: Any,
*,
max_drafts_per_run: int = 10,
max_episodes_per_run: int = 100,
observability_hook: ObservabilityHook | None = None,
)
LLM-backed Phase 12 procedural-draft extractor.
Implements :class:~symfonic.core.learning.procedural_extractor.ProceduralExtractor.
Construct the extractor.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
chat_model
|
Any
|
A LangChain-compatible chat model exposing
|
required |
max_drafts_per_run
|
int
|
Hard cap on drafts the extractor will
return per call. Default 5 mirrors the regex
extractor's |
10
|
max_episodes_per_run
|
int
|
Hard cap on episodes fed into the prompt. Excess episodes are dropped from the tail (older entries) so the freshest context wins. |
100
|
Source code in src/symfonic/core/learning/procedural_extractor_llm.py
extract
async
¶
extract(
episodes: list[MemoryEntry],
existing_skills: list[MemoryEntry],
scope: TenantScope,
*,
callback_manager: CallbackManager | None = None,
run_id: str = "",
model_name: str = "procedural_extractor_llm",
) -> list[ProceduralDraft]
Extract procedural drafts from recent episodes.
Returns [] rather than raising on every failure path: empty
input, None chat model, model exception, malformed JSON,
missing fields. The dispatch site logs the empty result via
the standard Phase 12 telemetry line.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
episodes
|
list[MemoryEntry]
|
Recent episodic entries (already filtered to
|
required |
existing_skills
|
list[MemoryEntry]
|
Procedural skills already in storage so the LLM can avoid duplicating them. |
required |
scope
|
TenantScope
|
Tenant scope -- forwarded for future per-tenant prompt customisation; not used by the default prompt. |
required |
callback_manager
|
CallbackManager | None
|
Optional v7.4.3 callback manager. When
provided, fires
|
None
|
run_id
|
str
|
Engine run identifier for callback correlation. |
''
|
model_name
|
str
|
Identifier stamped on the LLMEndEvent. Defaults to the module identifier; consumers that want to surface the actual SKU should pass the resolved model string. |
'procedural_extractor_llm'
|
Returns:
| Type | Description |
|---|---|
list[ProceduralDraft]
|
A list of :class: |
list[ProceduralDraft]
|
<= |
Source code in src/symfonic/core/learning/procedural_extractor_llm.py
draft_to_memory_entry ¶
Convert an :class:ProceduralDraft to a procedural MemoryEntry.
Used by the Phase 12 dispatch site in
:func:promote_episodic_to_procedural so the LLM and regex paths
share the same store_skill write contract. The draft's
structured fields land in the entry metadata under stable keys so
the v7.5 query_skills projection surfaces them:
precondition->properties['precondition'](v7.5)source->"phase_12_llm_extractor"(distinct from the regex extractor's"phase_12_promotion"so SRE telemetry can split adoption).kind/confidence/rationale/trigger/source_episode_idsare first-class properties for v7.7+ router enrichment.
The label field follows the v6.1 "Auto-learned: <name>"
convention so downstream label-prefix grouping (e.g. SemanticMerge
similarity, /procedures listing UI) keeps working unchanged.