From 1a1d57057ea612e8af5d8d62bae8f414e9156680 Mon Sep 17 00:00:00 2001 From: claude-code-best Date: Tue, 28 Apr 2026 08:47:37 +0800 Subject: [PATCH] =?UTF-8?q?fix:=20=E9=99=90=E5=88=B6=20skill-learning=20ev?= =?UTF-8?q?idence=20=E6=97=A0=E9=99=90=E5=A2=9E=E9=95=BF=E5=AF=BC=E8=87=B4?= =?UTF-8?q?=E5=85=A8=E5=B1=80=20skill=20=E6=96=87=E4=BB=B6=E8=86=A8?= =?UTF-8?q?=E8=83=80?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit evidence 数组和追加块缺少大小限制,导致 skill 文件(如 sdd-brainstorming)在短时间内膨胀至 21K+ 行/78 个 evidence 块。 三处修复: - instinctParser: evidence 数组 cap 10 条, observationIds cap 20 条 - skillGenerator: 追加块每次最多 20 行, 文件总大小上限 50KB, 生成 skill 的 evidence 段限制 20 行 - agentGenerator: 生成 agent 的 evidence 段限制 20 行 Co-Authored-By: Claude Opus 4.7 --- src/services/skillLearning/agentGenerator.ts | 1 + src/services/skillLearning/instinctParser.ts | 7 +++-- src/services/skillLearning/skillGenerator.ts | 33 +++++++++++++++++--- 3 files changed, 35 insertions(+), 6 deletions(-) diff --git a/src/services/skillLearning/agentGenerator.ts b/src/services/skillLearning/agentGenerator.ts index 032180686..4876c4b86 100644 --- a/src/services/skillLearning/agentGenerator.ts +++ b/src/services/skillLearning/agentGenerator.ts @@ -122,6 +122,7 @@ function buildAgentContent(params: { '', instincts .flatMap(instinct => instinct.evidence.map(evidence => `- ${evidence}`)) + .slice(0, 20) .join('\n'), '', ].join('\n') diff --git a/src/services/skillLearning/instinctParser.ts b/src/services/skillLearning/instinctParser.ts index c61a49961..3fb08213f 100644 --- a/src/services/skillLearning/instinctParser.ts +++ b/src/services/skillLearning/instinctParser.ts @@ -35,15 +35,18 @@ export function createInstinct( }) } +const MAX_EVIDENCE_ENTRIES = 10 + export function normalizeInstinct(instinct: StoredInstinct): StoredInstinct { + const uniqueEvidence = Array.from(new Set(instinct.evidence.filter(Boolean))) return { ...instinct, id: instinct.id || buildInstinctId(instinct.trigger, instinct.action), confidence: clampConfidence(instinct.confidence), - evidence: Array.from(new Set(instinct.evidence.filter(Boolean))), + evidence: uniqueEvidence.slice(-MAX_EVIDENCE_ENTRIES), evidenceOutcome: instinct.evidenceOutcome, observationIds: instinct.observationIds - ? Array.from(new Set(instinct.observationIds)) + ? Array.from(new Set(instinct.observationIds)).slice(-20) : undefined, } } diff --git a/src/services/skillLearning/skillGenerator.ts b/src/services/skillLearning/skillGenerator.ts index 1091cfefc..860519a86 100644 --- a/src/services/skillLearning/skillGenerator.ts +++ b/src/services/skillLearning/skillGenerator.ts @@ -12,6 +12,9 @@ import { import type { LearnedSkillDraft, SkillLearningScope } from './types.js' export const DUPLICATE_SKILL_OVERLAP_THRESHOLD = 0.8 +const MAX_EVIDENCE_LINES_PER_APPEND = 20 +const MAX_EVIDENCE_LINES_IN_SKILL = 20 +const MAX_SKILL_FILE_BYTES = 50_000 export type SkillGeneratorOptions = { cwd?: string @@ -101,20 +104,41 @@ export async function appendInstinctEvidenceToSkill( const existing = await readFile(target.path, 'utf8').catch( () => target.content, ) + + // Skip if the file already exceeds the size cap + if (Buffer.byteLength(existing, 'utf8') >= MAX_SKILL_FILE_BYTES) { + return target.path + } + + const allEvidence = instincts.flatMap(instinct => + instinct.evidence.map(evidence => `- ${evidence}`), + ) + const evidenceLines = allEvidence.slice(0, MAX_EVIDENCE_LINES_PER_APPEND) + if (evidenceLines.length < allEvidence.length) { + evidenceLines.push( + `- [... ${allEvidence.length - evidenceLines.length} more evidence entries omitted]`, + ) + } + const now = new Date().toISOString() const block = [ '', `## Learned evidence (${now})`, '', - ...instincts.flatMap(instinct => - instinct.evidence.map(evidence => `- ${evidence}`), - ), + ...evidenceLines, '', ].join('\n') const merged = existing.endsWith('\n') ? existing + block : `${existing}\n${block}` - await writeFile(target.path, merged, 'utf8') + + // Final guard: truncate if merged exceeds size cap + const finalContent = + Buffer.byteLength(merged, 'utf8') > MAX_SKILL_FILE_BYTES + ? merged.slice(0, MAX_SKILL_FILE_BYTES) + : merged + + await writeFile(target.path, finalContent, 'utf8') clearSkillIndexCache() return target.path } @@ -191,6 +215,7 @@ function buildSkillContent(params: { '', instincts .flatMap(instinct => instinct.evidence.map(evidence => `- ${evidence}`)) + .slice(0, MAX_EVIDENCE_LINES_IN_SKILL) .join('\n'), '', ]