7.2 KiB
7.2 KiB
1. Package scaffolding
- 1.1 Create
skills_learning/package with__init__.py,models.py,synthesis.py,versioning.py,embeddings.py,retrieval.py,config.py,store.py - 1.2 Add
skills_learning*topyproject.toml's[tool.setuptools.packages.find].includelist and add the embedding-provider SDK dependency - 1.3 Add
skills_learning/config.pywithSkillAuthoringConfig(enable flag defaultFalse, divergence tolerance, embedding model name, defaulttop_k) and a module-level accessor mirroringsemantic-scene-runtime's/world-model-runtime's config-module pattern - 1.4 Add
skills_learning/models.pydefining (or importing, ifskill-catalog-subscriptionis already implemented) the sharedSkill/FlowTemplateSkill/SkillMetadatadataclass shapes, plus this capability's ownsource,version,parent_version_idfields
2. Local skill store (skill-authoring, skill-versioning)
- 2.1 Implement
skills_learning/store.py: a local store for locally-synthesizedFlowTemplateSkillrecords, separate fromskill-catalog-subscription's synced catalog, withcreate_version(),get_by_id(),get_latest_by_name(),list_versions(name) - 2.2 Enforce
source = "local-synthesis"tagging on every record written by this store; add a guard/test that this store never writes into or imports a write-path ofskill-catalog-subscription's catalog module - 2.3 Write unit tests for the store's version-chain semantics: creating a new version does not delete/modify prior versions, and
get_latest_by_name()returns the highestversion
3. Timeline extraction and parameter abstraction (skill-authoring)
- 3.1 Implement
skills_learning/synthesis.py's tool-call extraction: given atask_id, readstorage.timeline.Timeline.read(task_id)and produce an ordered list of(tool_name, args)pairs, filtering out read-only tool names (describe_screen,screenshot,ui_tree,find_text,find_icon) - 3.2 Implement skeleton matching: given an extracted tool-name sequence, look up any stored skill (via
store.py) whose latest version has the identical tool-name sequence - 3.3 Implement cross-execution argument diffing: compare extracted argument values position-by-position against a matched stored version's steps, and promote any differing value into a named
{param}placeholder plus a corresponding entry in the skill'sparametersschema - 3.4 Implement parameter naming: prefer a name derived from the corresponding
SemanticScene.widgets[].purposelabel when available (optional dependency onsemantic/'s output, degrading gracefully when absent), else fall back to a positional name (e.g.param_2) - 3.5 Implement
synthesize_flow_skill(goal, timeline) -> FlowTemplateSkill: orchestrates extraction → skeleton match → diffing → parameter promotion → returns a candidate skill record (not yet persisted) - 3.6 Write unit tests for first-time synthesis (no prior match, zero parameters), second-execution parameter promotion, and identical-repeat synthesis (no spurious new parameters), using canned
TimelineRecordfixtures
4. Version divergence detection (skill-versioning)
- 4.1 Implement
skills_learning/versioning.py'sdiff_flow_versions(stored_steps, executed_steps) -> VersionDiff: detect tool-name-sequence insertion/deletion/reorder (structural divergence) versus argument-value-only differences - 4.2 Implement version-bump logic: on structural divergence, construct a new
FlowTemplateSkillversion with incrementedversionandparent_version_idset to the prior version's id; on argument-only divergence, update the existing version's parameters in place (no bump) - 4.3 Write unit tests: extra/missing/reordered step triggers a version bump; identical-sequence-different-values does not bump but does update parameters; assert prior version records remain retrievable and unmodified after a bump
5. Post-task synthesis hook wiring
- 5.1 Add an optional
on_task_succeeded: Callable[[str, str, Timeline], None] | None = Noneconstructor argument toTaskRunnerinruntime/task.py, invoked exactly once at the end ofrun()when the final status issucceeded - 5.2 Wire a default hook (when
on_task_succeededis not explicitly passed and Skill Authoring is enabled inskills_learning/config.py) that callssynthesis.synthesize_flow_skill(), runs versioning viaversioning.py, and persists the result viastore.py - 5.3 Verify that when Skill Authoring is disabled (default) or
on_task_succeededis leftNoneand disabled,TaskRunner.run()'s behavior and return value are byte-for-byte identical to before this change - 5.4 Write a unit test that runs a fake successful
TaskRunnerloop with Skill Authoring enabled and asserts a skill record is stored after completion, and a test that asserts no store write occurs when disabled
6. Embedding and retrieval (skill-embedding-retrieval)
- 6.1 Implement
skills_learning/embeddings.py's embedding client interface:embed_skill_text(text) -> list[float] | None, catching timeout/rate-limit/disabled-config/connection-error internally and returningNonerather than raising, mirroringsemantic/llm_client.py's degrade-safe contract - 6.2 Implement a local
skill_embeddingsindex (skill id + version → vector, model name,updated_at) inskills_learning/store.pyor a dedicatedskills_learning/embeddings_store.py - 6.3 Wire embedding computation into the post-synthesis/versioning path: call
embed_skill_text()onname + description + goalfor every newly stored skill version, storing the resulting vector (or leaving the skill un-embedded if the call returnsNone) - 6.4 Implement
skills_learning/retrieval.py'sretrieve_candidate_skills(goal, top_k) -> list[ScoredSkill]: embed the incoming goal, compute cosine similarity against every stored skill embedding, and return the toptop_kranked results, skipping skills with no stored embedding - 6.5 Write unit tests: ranked ordering for a goal similar to a stored skill's originating goal (using a fake/deterministic embedding function),
top_ktruncation, empty-result case when no skill has an embedding, and a case where an embedding call returnsNoneand the skill is stored but excluded from retrieval results
7. Integration tests and validation
- 7.1 Write an end-to-end test: run a fake successful task twice with slightly different goal text/argument values through
TaskRunner(Skill Authoring enabled, embedding client mocked), asserting the second run produces a new skill version with a promoted parameter and its own embedding - 7.2 Write an end-to-end test: run a fake successful task, then call
retrieve_candidate_skills()with a new, semantically similar goal string, asserting the synthesized skill is returned - 7.3 Run the full existing
pytestsuite and confirm zero existing test files require content changes (only newtests/test_skill_*.py-style files are added) - 7.4 Add a smoke test importing
skills_learningalongside existingtests/smoke coverage, confirming the package has no import-time dependency onskill-catalog-subscription's sync client (only, optionally, its shared model shapes if already implemented)