symfonic.core.learning.phases_procedural_promotion¶
phases_procedural_promotion ¶
Phase 12: Promote episodic patterns to draft procedural skills.
Scans episodic memory for repeated action sequences. When a pattern
appears min_pattern_count or more times across recent episodes,
extracts it as a candidate procedural skill stored in draft status.
Human reviewers approve/reject via /procedures/{id}/approve or
/procedures/{id}/reject (shipped in Sprint 1).
Why always draft? Phase 12 is the offline learning path. It runs
unattended during consolidation and should never auto-activate skills --
the real-time extraction path in AgentEngine._consolidate() is where
MetacognitiveMiddleware gates live usage. Here, human review is the
quality gate.
Pattern extraction is intentionally simple for v1:
- Entries whose
metadata['action_type']matches group by that key. - Entries whose content starts with an action verb ("I ran", "I used",
"Then I
") group by the verb + object phrase. - (v7.4.7, opt-in via
use_tool_calls_fallback) Entries whosemetadata['tool_calls']list is non-empty group bytool_calls[0]['name']. This lets the regex extractor produce signal for tool-call-heavy agents whose narrated content begins with"User: ..."(the engine's auto-turn summary shape) and therefore never matches the action-verb anchor.
If none of the heuristics fire, the episodic entry is skipped. A
future ticket will upgrade this with an LLM-based extractor (see
.claude/docs/2026-05-28-jarvio-llm-procedural-extractor-eval.md).
promote_episodic_to_procedural
async
¶
promote_episodic_to_procedural(
episodic_layer: Any,
procedural_layer: ProceduralLayer,
scope: TenantScope,
*,
min_pattern_count: int = DEFAULT_MIN_PATTERN_COUNT,
recency_days: int = DEFAULT_RECENCY_DAYS,
max_drafts_per_run: int = DEFAULT_MAX_DRAFTS_PER_RUN,
use_tool_calls_fallback: bool = False,
llm_extractor: Any | None = None,
llm_model_name: str = "",
promote_assistant_content: bool = False,
gate: DurabilityGate | None = None,
) -> int
Phase 12: extract repeated episodic patterns into draft skills.
Algorithm
- Fetch recent episodic entries (
recency_dayslookback). - Extract action patterns via
_extract_pattern_key. - Keep patterns with
count >= min_pattern_count. - Rank by frequency descending; cap at
max_drafts_per_run. - For each, call
procedural_layer.store_skill(..., status='draft'). SemanticMerge dedup insidestore_skillhandles near-duplicates against existing procedural nodes (draft or approved). - Return count of new draft nodes written this run.
Resilient to backends that don't populate created_at on episodic
retrieval and to missing optional parameters (max_drafts_per_run=0
short-circuits).
Source code in src/symfonic/core/learning/phases_procedural_promotion.py
416 417 418 419 420 421 422 423 424 425 426 427 428 429 430 431 432 433 434 435 436 437 438 439 440 441 442 443 444 445 446 447 448 449 450 451 452 453 454 455 456 457 458 459 460 461 462 463 464 465 466 467 468 469 470 471 472 473 474 475 476 477 478 479 480 481 482 483 484 485 486 487 488 489 490 491 492 493 494 495 496 497 498 499 500 501 502 503 504 505 506 507 508 509 510 511 512 513 514 515 516 517 518 519 520 521 522 523 524 525 526 527 528 529 530 531 532 533 534 535 536 537 538 539 540 541 542 543 544 545 546 547 548 549 550 551 552 553 554 555 556 557 | |