MCPcopy Create free account
hub / github.com/MemTensor/MemOS / findRelatedSkill

Method findRelatedSkill

packages/memos-core/src/skill/evolver.ts:128–195  ·  view source on GitHub ↗

* Search for an existing skill that is HIGHLY related to the given task. * * 1. Collect top 50 skill candidates by FTS + vector similarity (relaxed thresholds). * 2. Call LLM with task title/summary and each skill's name/description; strict rule: * only output ONE skill index if the t

(task: Task)

Source from the content-addressed store, hash-verified

126 * otherwise output 0 (do not force a match).
127 */
128 private async findRelatedSkill(task: Task): Promise<Skill | null> {
129 const query = task.summary.slice(0, 600);
130 const owner = task.owner ?? "agent:main";
131 // Relaxed thresholds to gather a larger candidate pool; LLM will do strict filtering
132 const VEC_FLOOR = 0.35;
133 const TOP_N = SkillEvolver.RELATED_SKILL_CANDIDATE_TOP;
134
135 type Candidate = { skill: Skill; vecScore: number; ftsScore: number; combined: number };
136 const candidateMap = new Map<string, Candidate>();
137
138 // 1. FTS on skill name + description (take more candidates)
139 try {
140 const ftsHits = this.store.skillFtsSearch(query, TOP_N, "mix", owner);
141 for (const hit of ftsHits) {
142 const skill = this.store.getSkill(hit.skillId);
143 if (skill && (skill.status === "active" || skill.status === "draft")) {
144 candidateMap.set(skill.id, { skill, vecScore: 0, ftsScore: hit.score, combined: 0 });
145 }
146 }
147 } catch (err) {
148 this.ctx.log.warn(`SkillEvolver: skill FTS search failed: ${err}`);
149 }
150
151 // 2. Vector similarity: include all skills above a low floor to rank them
152 if (this.embedder) {
153 try {
154 const queryVec = await this.embedder.embedQuery(query);
155 const allSkillEmb = this.store.getSkillEmbeddings("mix", owner);
156 for (const row of allSkillEmb) {
157 const sim = cosineSimilarity(queryVec, row.vector);
158 if (sim >= VEC_FLOOR) {
159 const existing = candidateMap.get(row.skillId);
160 if (existing) {
161 existing.vecScore = sim;
162 } else {
163 const skill = this.store.getSkill(row.skillId);
164 if (skill && (skill.status === "active" || skill.status === "draft")) {
165 candidateMap.set(skill.id, { skill, vecScore: sim, ftsScore: 0, combined: 0 });
166 }
167 }
168 }
169 }
170 } catch (err) {
171 this.ctx.log.warn(`SkillEvolver: skill vector search failed: ${err}`);
172 }
173 }
174
175 if (candidateMap.size === 0) return null;
176
177 for (const c of candidateMap.values()) {
178 c.combined = c.vecScore * 0.7 + c.ftsScore * 0.3;
179 }
180
181 const sorted = [...candidateMap.values()]
182 .sort((a, b) => b.combined - a.combined)
183 .slice(0, TOP_N);
184
185 if (sorted.length === 0) return null;

Callers 1

processMethod · 0.95

Calls 11

cosineSimilarityFunction · 0.90
getSkillMethod · 0.65
setMethod · 0.65
warnMethod · 0.65
getMethod · 0.65
debugMethod · 0.65
skillFtsSearchMethod · 0.45
embedQueryMethod · 0.45
getSkillEmbeddingsMethod · 0.45
valuesMethod · 0.45

Tested by

no test coverage detected