MCPcopy
hub / github.com/aiming-lab/MetaClaw / SkillManager

Class SkillManager

metaclaw/skill_manager.py:142–587  ·  view source on GitHub ↗

Retrieves skills from a directory of Markdown skill files (Claude skills format). Supports two retrieval modes: * ``"template"`` – keyword-based task-type detection, zero latency * ``"embedding"`` – cosine similarity via SentenceTransformer

Source from the content-addressed store, hash-verified

140# ------------------------------------------------------------------ #
141
142class SkillManager:
143 """
144 Retrieves skills from a directory of Markdown skill files (Claude skills format).
145
146 Supports two retrieval modes:
147 * ``"template"`` – keyword-based task-type detection, zero latency
148 * ``"embedding"`` – cosine similarity via SentenceTransformer
149 """
150
151 def __init__(
152 self,
153 skills_dir: str,
154 retrieval_mode: str = "template",
155 embedding_model_path: Optional[str] = None,
156 task_specific_top_k: Optional[int] = None,
157 ):
158 if retrieval_mode not in ("template", "embedding"):
159 raise ValueError(
160 f"retrieval_mode must be 'template' or 'embedding', got '{retrieval_mode}'"
161 )
162 if not os.path.isdir(skills_dir):
163 raise FileNotFoundError(f"Skills directory not found: {skills_dir}")
164
165 self._skills_dir = skills_dir
166 self.retrieval_mode = retrieval_mode
167 self.embedding_model_path = embedding_model_path or "Qwen/Qwen3-Embedding-0.6B"
168 self.task_specific_top_k = task_specific_top_k
169
170 self._embedding_model = None
171 self._skill_embeddings_cache: Optional[Dict] = None
172
173 # Monotonically-increasing counter. Incremented each time new skills are
174 # successfully added via add_skills(). Used by the RL trainer to discard
175 # training samples collected before a skill evolution (MAML support/query
176 # separation: samples that triggered evolution are not reused for RL updates).
177 self.generation: int = 0
178
179 self.skills = self._load_skills()
180
181 n_gen = len(self.skills.get("general_skills", []))
182 n_task = sum(len(v) for v in self.skills.get("task_specific_skills", {}).values())
183 n_mistakes = len(self.skills.get("common_mistakes", []))
184 logger.info(
185 "[SkillManager] loaded %d general + %d task-specific + %d mistakes "
186 "from %s | mode=%s",
187 n_gen, n_task, n_mistakes, skills_dir, retrieval_mode,
188 )
189
190 if retrieval_mode == "embedding":
191 self._compute_skill_embeddings()
192
193 # ------------------------------------------------------------------ #
194 # Loading #
195 # ------------------------------------------------------------------ #
196
197 def _load_skills(self) -> Dict[str, Any]:
198 """Scan skills_dir for */SKILL.md files and parse each into the internal dict."""
199 result: Dict[str, Any] = {

Callers 6

run_conditionFunction · 0.90
setup_skill_managerFunction · 0.90
setup_skill_managerFunction · 0.90
_start_skills_onlyMethod · 0.85
setupMethod · 0.85

Calls

no outgoing calls

Tested by 2

setup_skill_managerFunction · 0.72