autosnippet 2.9.0 → 2.10.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +4 -4
- package/bin/cli.js +5 -33
- package/config/constitution.yaml +9 -2
- package/dashboard/dist/assets/{icons-CH-H9x0E.js → icons-BkT3XrKf.js} +105 -100
- package/dashboard/dist/assets/index-BsB7DzW4.css +1 -0
- package/dashboard/dist/assets/index-DdmQMrJJ.js +155 -0
- package/dashboard/dist/index.html +3 -3
- package/lib/cli/AiScanService.js +13 -11
- package/lib/cli/KnowledgeSyncService.js +343 -0
- package/lib/cli/SetupService.js +8 -26
- package/lib/core/gateway/GatewayActionRegistry.js +48 -58
- package/lib/domain/index.js +16 -11
- package/lib/domain/knowledge/KnowledgeEntry.js +351 -0
- package/lib/domain/knowledge/KnowledgeRepository.js +123 -0
- package/lib/domain/knowledge/Lifecycle.js +109 -0
- package/lib/domain/knowledge/index.js +27 -0
- package/lib/domain/knowledge/values/Constraints.js +125 -0
- package/lib/domain/knowledge/values/Content.js +86 -0
- package/lib/domain/knowledge/values/Quality.js +93 -0
- package/lib/domain/knowledge/values/Reasoning.js +69 -0
- package/lib/domain/knowledge/values/Relations.js +168 -0
- package/lib/domain/knowledge/values/Stats.js +87 -0
- package/lib/domain/knowledge/values/index.js +9 -0
- package/lib/external/ai/AiProvider.js +48 -0
- package/lib/external/mcp/McpServer.js +7 -5
- package/lib/external/mcp/handlers/bootstrap/pipeline/orchestrator.js +2 -2
- package/lib/external/mcp/handlers/bootstrap.js +116 -11
- package/lib/external/mcp/handlers/browse.js +77 -73
- package/lib/external/mcp/handlers/candidate.js +29 -276
- package/lib/external/mcp/handlers/guard.js +2 -0
- package/lib/external/mcp/handlers/knowledge.js +205 -0
- package/lib/external/mcp/handlers/structure.js +25 -23
- package/lib/external/mcp/handlers/system.js +10 -12
- package/lib/external/mcp/tools.js +125 -138
- package/lib/http/HttpServer.js +4 -8
- package/lib/http/routes/extract.js +48 -4
- package/lib/http/routes/knowledge.js +246 -0
- package/lib/http/routes/search.js +12 -17
- package/lib/infrastructure/database/migrations/016_unified_knowledge_entries.js +395 -0
- package/lib/infrastructure/external/XcodeAutomation.js +187 -103
- package/lib/injection/ServiceContainer.js +49 -60
- package/lib/repository/knowledge/KnowledgeRepository.impl.js +373 -0
- package/lib/service/automation/DirectiveDetector.js +2 -3
- package/lib/service/automation/FileWatcher.js +67 -28
- package/lib/service/automation/XcodeIntegration.js +931 -156
- package/lib/service/automation/handlers/AlinkHandler.js +6 -4
- package/lib/service/automation/handlers/CreateHandler.js +53 -18
- package/lib/service/automation/handlers/GuardHandler.js +183 -20
- package/lib/service/automation/handlers/SearchHandler.js +35 -17
- package/lib/service/chat/CandidateGuardrail.js +1 -1
- package/lib/service/chat/ChatAgent.js +46 -45
- package/lib/service/chat/ContextWindow.js +5 -5
- package/lib/service/chat/ProducerAgent.js +7 -7
- package/lib/service/chat/tools.js +130 -123
- package/lib/service/guard/GuardCheckEngine.js +114 -10
- package/lib/service/guard/GuardService.js +59 -48
- package/lib/service/knowledge/ConfidenceRouter.js +159 -0
- package/lib/service/knowledge/KnowledgeFileWriter.js +602 -0
- package/lib/service/knowledge/KnowledgeService.js +725 -0
- package/lib/service/search/SearchEngine.js +92 -19
- package/lib/service/skills/SignalCollector.js +12 -7
- package/lib/service/skills/SkillAdvisor.js +13 -11
- package/lib/service/snippet/SnippetFactory.js +5 -5
- package/package.json +1 -1
- package/scripts/install-cursor-skill.js +0 -6
- package/scripts/migrate-md-to-knowledge.mjs +364 -0
- package/skills/autosnippet-analysis/SKILL.md +15 -7
- package/skills/autosnippet-candidates/SKILL.md +6 -6
- package/skills/autosnippet-coldstart/SKILL.md +7 -3
- package/skills/autosnippet-concepts/SKILL.md +7 -6
- package/skills/autosnippet-create/SKILL.md +13 -13
- package/skills/autosnippet-intent/SKILL.md +3 -2
- package/skills/autosnippet-lifecycle/SKILL.md +5 -5
- package/skills/autosnippet-recipes/SKILL.md +16 -4
- package/templates/constitution.yaml +1 -1
- package/templates/copilot-instructions.md +6 -6
- package/templates/recipes-setup/README.md +3 -3
- package/dashboard/dist/assets/index-CqJRvYRL.js +0 -197
- package/dashboard/dist/assets/index-DICm9PNa.css +0 -1
- package/lib/cli/CandidateSyncService.js +0 -261
- package/lib/cli/SyncService.js +0 -356
- package/lib/domain/candidate/Candidate.js +0 -196
- package/lib/domain/candidate/CandidateRepository.js +0 -107
- package/lib/domain/candidate/Reasoning.js +0 -52
- package/lib/domain/recipe/Recipe.js +0 -421
- package/lib/domain/recipe/RecipeRepository.js +0 -54
- package/lib/domain/types/CandidateStatus.js +0 -52
- package/lib/http/routes/candidates.js +0 -559
- package/lib/http/routes/recipes.js +0 -397
- package/lib/repository/candidate/CandidateRepository.impl.js +0 -230
- package/lib/repository/recipe/RecipeRepository.impl.js +0 -498
- package/lib/service/candidate/CandidateAggregator.js +0 -52
- package/lib/service/candidate/CandidateFileWriter.js +0 -383
- package/lib/service/candidate/CandidateService.js +0 -1001
- package/lib/service/recipe/RecipeFileWriter.js +0 -514
- package/lib/service/recipe/RecipeService.js +0 -786
- package/lib/service/recipe/RecipeStatsTracker.js +0 -148
|
@@ -36,7 +36,7 @@ const PROJECT_ROOT = path.resolve(__dirname, '../../..');
|
|
|
36
36
|
const SKILLS_DIR = path.resolve(PROJECT_ROOT, 'skills');
|
|
37
37
|
const SOUL_PATH = path.resolve(PROJECT_ROOT, 'SOUL.md');
|
|
38
38
|
const MAX_ITERATIONS = 6;
|
|
39
|
-
/** 系统调用 (如 bootstrap) 允许更多迭代,因为每维度需要多次
|
|
39
|
+
/** 系统调用 (如 bootstrap) 允许更多迭代,因为每维度需要多次 submit_knowledge */
|
|
40
40
|
const MAX_ITERATIONS_SYSTEM = 30;
|
|
41
41
|
/** 原生函数调用模式下,已提交 ≥ MIN_SUBMITS_FOR_EARLY_EXIT 个候选后,连续 N 轮无新提交则提前退出 */
|
|
42
42
|
const MIN_SUBMITS_FOR_EARLY_EXIT = 1;
|
|
@@ -69,23 +69,23 @@ const SYSTEM_CONTINUATION_PROMPT = `你的分析计划很好。但你需要 **
|
|
|
69
69
|
请现在开始执行:
|
|
70
70
|
1. 用 \`search_project_code\` 搜索项目代码获取真实示例
|
|
71
71
|
2. 用 \`read_project_file\` 查看完整文件内容
|
|
72
|
-
3. 对每个值得保留的信号,用 \`
|
|
72
|
+
3. 对每个值得保留的信号,用 \`submit_knowledge\` 提交候选
|
|
73
73
|
|
|
74
74
|
⚡ 推荐使用 batch_actions 一次提交多条候选:
|
|
75
75
|
\`\`\`batch_actions
|
|
76
76
|
[
|
|
77
|
-
{"tool": "
|
|
78
|
-
{"tool": "
|
|
77
|
+
{"tool": "submit_knowledge", "params": {"title": "[Bootstrap] xxx/子主题", "code": "# 标题 — 项目特写\\n\\n> 摘要...\\n\\n描述和代码交织...", "language": "objectivec", "category": "Service", "summary": "...", "tags": ["bootstrap"], "source": "bootstrap", "reasoning": {"whyStandard": "...", "sources": ["file1"], "confidence": 0.7}}},
|
|
78
|
+
{"tool": "submit_knowledge", "params": {"title": "...", "code": "...", ...}}
|
|
79
79
|
]
|
|
80
80
|
\`\`\`
|
|
81
81
|
|
|
82
82
|
请立即开始执行,不要再输出分析文字。`;
|
|
83
83
|
|
|
84
84
|
/**
|
|
85
|
-
* 系统调用提交提示 — 当 AI 做了工具调用(search/read)、写了分析文本,但没调
|
|
86
|
-
* 引导 AI 将已有分析转化为实际的
|
|
85
|
+
* 系统调用提交提示 — 当 AI 做了工具调用(search/read)、写了分析文本,但没调 submit_knowledge 时注入
|
|
86
|
+
* 引导 AI 将已有分析转化为实际的 submit_knowledge 调用
|
|
87
87
|
*/
|
|
88
|
-
const SYSTEM_SUBMIT_PROMPT = `你的分析很好,已经获取了足够的项目信息。但你还没有调用 \`
|
|
88
|
+
const SYSTEM_SUBMIT_PROMPT = `你的分析很好,已经获取了足够的项目信息。但你还没有调用 \`submit_knowledge\` 提交任何候选。
|
|
89
89
|
|
|
90
90
|
**你的分析不能只停留在文字描述层面** — 必须通过工具调用将分析结果持久化。
|
|
91
91
|
|
|
@@ -93,7 +93,7 @@ const SYSTEM_SUBMIT_PROMPT = `你的分析很好,已经获取了足够的项
|
|
|
93
93
|
|
|
94
94
|
\`\`\`batch_actions
|
|
95
95
|
[
|
|
96
|
-
{"tool": "
|
|
96
|
+
{"tool": "submit_knowledge", "params": {
|
|
97
97
|
"title": "[Bootstrap] 维度/子主题",
|
|
98
98
|
"code": "# 标题 — 项目特写\\n\\n> 本项目使用 XX 模式, N 个文件采用此写法\\n\\n描述...\\n\\n\`\`\`objc\\n// 真实代码示例\\n\`\`\`\\n\\n要点说明...",
|
|
99
99
|
"language": "objectivec",
|
|
@@ -103,11 +103,11 @@ const SYSTEM_SUBMIT_PROMPT = `你的分析很好,已经获取了足够的项
|
|
|
103
103
|
"source": "bootstrap",
|
|
104
104
|
"reasoning": {"whyStandard": "为什么值得保留", "sources": ["真实文件名"], "confidence": 0.7}
|
|
105
105
|
}},
|
|
106
|
-
{"tool": "
|
|
106
|
+
{"tool": "submit_knowledge", "params": {...}}
|
|
107
107
|
]
|
|
108
108
|
\`\`\`
|
|
109
109
|
|
|
110
|
-
将你上面分析出的每个有价值的发现都转化为一条
|
|
110
|
+
将你上面分析出的每个有价值的发现都转化为一条 submit_knowledge 调用。code 字段写「项目特写」风格: 描述和代码交织,用项目真实类名和代码。`;
|
|
111
111
|
|
|
112
112
|
export class ChatAgent {
|
|
113
113
|
#toolRegistry;
|
|
@@ -342,7 +342,7 @@ export class ChatAgent {
|
|
|
342
342
|
// Bootstrap 场景限制可用工具集 (支持外部覆盖)
|
|
343
343
|
const effectiveAllowedTools = allowedTools || (isSystem ? [
|
|
344
344
|
'search_project_code', 'read_project_file',
|
|
345
|
-
'
|
|
345
|
+
'submit_knowledge', 'submit_with_check',
|
|
346
346
|
'list_project_structure', 'get_file_summary', 'semantic_search_code',
|
|
347
347
|
// AST 结构化分析工具
|
|
348
348
|
'get_project_overview', 'get_class_hierarchy', 'get_class_info',
|
|
@@ -384,7 +384,7 @@ export class ChatAgent {
|
|
|
384
384
|
}
|
|
385
385
|
// PhaseRouter 因 maxIterations 强制转入 SUMMARIZE → 注入收尾 nudge
|
|
386
386
|
if (phaseRouter.consumeForcedSummarize()) {
|
|
387
|
-
const submitCount = toolCalls.filter(tc => tc.tool === '
|
|
387
|
+
const submitCount = toolCalls.filter(tc => tc.tool === 'submit_knowledge' || tc.tool === 'submit_with_check').length;
|
|
388
388
|
ctx.appendUserNudge(
|
|
389
389
|
`⚠️ 轮次即将耗尽 (${phaseRouter.totalIterations}/${budget.maxIterations}),**必须立即结束**。请在回复中直接输出 dimensionDigest JSON 总结(用 \`\`\`json 包裹),不要再调用任何工具。\n` +
|
|
390
390
|
`\`\`\`json\n{"dimensionDigest":{"summary":"分析总结","candidateCount":${submitCount},"keyFindings":["发现"],"crossRefs":{},"gaps":["缺口"],"remainingTasks":[{"signal":"未处理信号","reason":"轮次耗尽","priority":"high","searchHints":["搜索词"]}]}}\n\`\`\`\n> remainingTasks: 列出未来得及处理的信号。已覆盖则留空 \`[]\`。`
|
|
@@ -418,7 +418,7 @@ export class ChatAgent {
|
|
|
418
418
|
} else if (isSystem && iterationCount >= maxIter && !ctx.__gracefulExitInjected) {
|
|
419
419
|
// 达到上限 → 注入收尾消息让 AI 快速总结(而非硬中断)
|
|
420
420
|
this.#logger.info(`[ChatAgent] Iteration cap reached (${iterationCount}/${maxIter}) — injecting graceful exit nudge`);
|
|
421
|
-
const submitCount = toolCalls.filter(tc => tc.tool === '
|
|
421
|
+
const submitCount = toolCalls.filter(tc => tc.tool === 'submit_knowledge' || tc.tool === 'submit_with_check').length;
|
|
422
422
|
ctx.appendUserNudge(
|
|
423
423
|
`⚠️ 你已使用 ${iterationCount}/${maxIter} 轮次,**必须立即结束**。请在回复中直接输出 dimensionDigest JSON 总结(用 \`\`\`json 包裹),不要再调用任何工具。\n` +
|
|
424
424
|
`\`\`\`json\n{"dimensionDigest":{"summary":"分析总结","candidateCount":${submitCount},"keyFindings":["发现"],"crossRefs":{},"gaps":["缺口"],"remainingTasks":[{"signal":"未处理信号","reason":"轮次耗尽","priority":"high","searchHints":["搜索词"]}]}}\n\`\`\`\n> remainingTasks: 列出未来得及处理的信号。已覆盖则留空 \`[]\`。`
|
|
@@ -671,7 +671,7 @@ export class ChatAgent {
|
|
|
671
671
|
explorationMetrics.uniqueQueries.add(qKey);
|
|
672
672
|
foundNewInfo = true;
|
|
673
673
|
}
|
|
674
|
-
} else if (fc.name !== '
|
|
674
|
+
} else if (fc.name !== 'submit_knowledge' && fc.name !== 'submit_with_check') {
|
|
675
675
|
// 其他未分类工具 — 首次算新信息,之后同工具名+同参数去重
|
|
676
676
|
const qKey = `${fc.name}:${JSON.stringify(fc.args || {}).substring(0, 80)}`;
|
|
677
677
|
if (!explorationMetrics.uniqueQueries.has(qKey)) {
|
|
@@ -692,7 +692,7 @@ export class ChatAgent {
|
|
|
692
692
|
let resultStr = limitToolResult(fc.name, toolResult, quota);
|
|
693
693
|
|
|
694
694
|
// ── 重复提交 / 维度范围校验 ──
|
|
695
|
-
if (fc.name === '
|
|
695
|
+
if (fc.name === 'submit_knowledge' || fc.name === 'submit_with_check') {
|
|
696
696
|
const title = fc.args?.title || fc.args?.category || '';
|
|
697
697
|
const isRejected = typeof toolResult === 'object' && toolResult?.status === 'rejected';
|
|
698
698
|
const isError = typeof toolResult === 'object' && (toolResult?.error || toolResult?.status === 'error');
|
|
@@ -734,7 +734,7 @@ export class ChatAgent {
|
|
|
734
734
|
// 需要一条 user 消息明确告知 AI 切换到提交模式
|
|
735
735
|
if (transition.transitioned && transition.newPhase === 'PRODUCE') {
|
|
736
736
|
ctx.appendUserNudge(
|
|
737
|
-
'你已充分探索了项目代码,现在请开始调用
|
|
737
|
+
'你已充分探索了项目代码,现在请开始调用 submit_knowledge 工具来提交你发现的知识候选。不要再搜索,直接提交。'
|
|
738
738
|
);
|
|
739
739
|
this.#logger.info('[ChatAgent] 📝 injected PRODUCE transition nudge');
|
|
740
740
|
}
|
|
@@ -743,7 +743,7 @@ export class ChatAgent {
|
|
|
743
743
|
// skill-only 维度从 EXPLORE 直接进入 SUMMARIZE (跳过 PRODUCE),
|
|
744
744
|
// 需要明确告知 AI 输出 dimensionDigest JSON
|
|
745
745
|
if (transition.transitioned && transition.newPhase === 'SUMMARIZE') {
|
|
746
|
-
const submitCount = toolCalls.filter(tc => tc.tool === '
|
|
746
|
+
const submitCount = toolCalls.filter(tc => tc.tool === 'submit_knowledge' || tc.tool === 'submit_with_check').length;
|
|
747
747
|
ctx.appendUserNudge(
|
|
748
748
|
`你已完成分析探索。请在回复中直接输出 dimensionDigest JSON(用 \`\`\`json 包裹),包含以下字段:\n\`\`\`json\n{"dimensionDigest":{"summary":"分析总结(100-200字)","candidateCount":${submitCount},"keyFindings":["关键发现"],"crossRefs":{},"gaps":["未覆盖方面"],"remainingTasks":[{"signal":"未处理的信号/主题","reason":"未完成原因(如:提交上限已达)","priority":"high|medium|low","searchHints":["建议搜索词"]}]}}\n\`\`\`\n> 如果所有信号都已覆盖,remainingTasks 留空数组 \`[]\`。如果有未来得及处理的信号,请在此标记,系统会在下次运行时续传。`
|
|
749
749
|
);
|
|
@@ -781,7 +781,7 @@ export class ChatAgent {
|
|
|
781
781
|
// 注入 nudge 让 AI 再输出一次 digest JSON
|
|
782
782
|
if (transition.transitioned) {
|
|
783
783
|
ctx.appendAssistantText(aiResult.text || '');
|
|
784
|
-
const submitCount = toolCalls.filter(tc => tc.tool === '
|
|
784
|
+
const submitCount = toolCalls.filter(tc => tc.tool === 'submit_knowledge' || tc.tool === 'submit_with_check').length;
|
|
785
785
|
ctx.appendUserNudge(
|
|
786
786
|
`请在回复中直接输出 dimensionDigest JSON 总结(用 \`\`\`json 包裹):\n\`\`\`json\n{"dimensionDigest":{"summary":"分析总结","candidateCount":${submitCount},"keyFindings":["发现"],"crossRefs":{},"gaps":["缺口"],"remainingTasks":[{"signal":"未处理的信号","reason":"原因","priority":"high","searchHints":["搜索词"]}]}}\n\`\`\`\n> remainingTasks: 记录未来得及处理的信号。已全部覆盖则留空 \`[]\`。`
|
|
787
787
|
);
|
|
@@ -802,7 +802,7 @@ export class ChatAgent {
|
|
|
802
802
|
ctx.appendAssistantText(aiResult.text || '');
|
|
803
803
|
if (phaseRouter.phase === 'PRODUCE') {
|
|
804
804
|
ctx.appendUserNudge(
|
|
805
|
-
'你的分析很好。请继续调用
|
|
805
|
+
'你的分析很好。请继续调用 submit_knowledge 提交你发现的知识候选,每个值得记录的模式/实践都应该提交。'
|
|
806
806
|
);
|
|
807
807
|
this.#logger.info('[ChatAgent] 📝 injected submit nudge (text in PRODUCE, not transitioning)');
|
|
808
808
|
}
|
|
@@ -850,7 +850,7 @@ export class ChatAgent {
|
|
|
850
850
|
this.#logger.info(`[ChatAgent] ⚠ producing forced summary (${iterations} iters, ${toolCalls.length} calls)`);
|
|
851
851
|
|
|
852
852
|
const candidateCount = toolCalls.filter(tc =>
|
|
853
|
-
tc.tool === '
|
|
853
|
+
tc.tool === 'submit_knowledge' || tc.tool === 'submit_with_check'
|
|
854
854
|
).length;
|
|
855
855
|
|
|
856
856
|
let finalReply;
|
|
@@ -865,7 +865,7 @@ export class ChatAgent {
|
|
|
865
865
|
if (isCircuitOpen) throw new Error('circuit open — skip to synthetic digest');
|
|
866
866
|
|
|
867
867
|
const submitSummary = toolCalls
|
|
868
|
-
.filter(tc => tc.tool === '
|
|
868
|
+
.filter(tc => tc.tool === 'submit_knowledge' || tc.tool === 'submit_with_check')
|
|
869
869
|
.map((tc, i) => `${i + 1}. ${tc.params?.title || tc.params?.category || 'untitled'}`)
|
|
870
870
|
.join('\n');
|
|
871
871
|
|
|
@@ -912,7 +912,7 @@ ${submitSummary ? `已提交候选:\n${submitSummary}\n` : ''}
|
|
|
912
912
|
this.#logger.warn(`[ChatAgent] forced summary AI call failed: ${err.message}`);
|
|
913
913
|
// 合成 digest 兜底
|
|
914
914
|
const titles = toolCalls
|
|
915
|
-
.filter(tc => tc.tool === '
|
|
915
|
+
.filter(tc => tc.tool === 'submit_knowledge' || tc.tool === 'submit_with_check')
|
|
916
916
|
.map(tc => tc.params?.title || 'untitled');
|
|
917
917
|
finalReply = `\`\`\`json
|
|
918
918
|
{
|
|
@@ -997,7 +997,7 @@ ${submitSummary ? `已提交候选:\n${submitSummary}\n` : ''}
|
|
|
997
997
|
|
|
998
998
|
if (!actions) {
|
|
999
999
|
// ── 系统调用自动续跑 ──
|
|
1000
|
-
const hasSubmits = toolCalls.some(tc => tc.tool === '
|
|
1000
|
+
const hasSubmits = toolCalls.some(tc => tc.tool === 'submit_knowledge' || tc.tool === 'submit_with_check');
|
|
1001
1001
|
if (source === 'system' && iterations < maxIter && !hasSubmits) {
|
|
1002
1002
|
if (this.#looksLikeIncompleteStep(response)) {
|
|
1003
1003
|
this.#logger.info(`[ChatAgent] 🔄 detected planning-only response at iteration ${iterations}, injecting continuation prompt`);
|
|
@@ -1272,13 +1272,13 @@ ${highSim.map(s => `- ${s.title} (相似度: ${s.similarity})`).join('\n')}
|
|
|
1272
1272
|
*/
|
|
1273
1273
|
async #taskDiscoverAllRelations({ batchSize = 20 } = {}) {
|
|
1274
1274
|
const ctx = this.#getToolContext();
|
|
1275
|
-
const
|
|
1276
|
-
if (!
|
|
1275
|
+
const knowledgeService = ctx.container.get('knowledgeService');
|
|
1276
|
+
if (!knowledgeService) throw new Error('KnowledgeService 不可用');
|
|
1277
1277
|
|
|
1278
1278
|
if (!ctx.aiProvider) throw new Error('AI Provider 未配置,请先设置 API Key');
|
|
1279
1279
|
|
|
1280
|
-
//
|
|
1281
|
-
const { items = [], data = [] } = await
|
|
1280
|
+
// 获取所有活跃知识条目
|
|
1281
|
+
const { items = [], data = [] } = await knowledgeService.list({ lifecycle: 'active' }, { page: 1, pageSize: 500 });
|
|
1282
1282
|
const recipes = items.length > 0 ? items : data;
|
|
1283
1283
|
if (recipes.length < 2) return { discovered: 0, totalPairs: 0, message: `只有 ${recipes.length} 条 Recipe,至少需要 2 条` };
|
|
1284
1284
|
|
|
@@ -1334,10 +1334,10 @@ ${highSim.map(s => `- ${s.title} (相似度: ${s.similarity})`).join('\n')}
|
|
|
1334
1334
|
*/
|
|
1335
1335
|
async #taskFullEnrich({ status = 'pending', maxCount = 50 } = {}) {
|
|
1336
1336
|
const ctx = this.#getToolContext();
|
|
1337
|
-
const
|
|
1337
|
+
const knowledgeService = ctx.container.get('knowledgeService');
|
|
1338
1338
|
|
|
1339
|
-
const { items = [], data = [] } = await
|
|
1340
|
-
{ status }, { page: 1, pageSize: maxCount }
|
|
1339
|
+
const { items = [], data = [] } = await knowledgeService.list(
|
|
1340
|
+
{ lifecycle: status }, { page: 1, pageSize: maxCount }
|
|
1341
1341
|
);
|
|
1342
1342
|
const candidates = items.length > 0 ? items : data;
|
|
1343
1343
|
if (candidates.length === 0) return { enriched: 0, message: 'No candidates to enrich' };
|
|
@@ -1363,10 +1363,10 @@ ${highSim.map(s => `- ${s.title} (相似度: ${s.similarity})`).join('\n')}
|
|
|
1363
1363
|
*/
|
|
1364
1364
|
async #taskQualityAudit({ threshold = 0.6, maxCount = 100 } = {}) {
|
|
1365
1365
|
const ctx = this.#getToolContext();
|
|
1366
|
-
const
|
|
1366
|
+
const knowledgeService = ctx.container.get('knowledgeService');
|
|
1367
1367
|
|
|
1368
|
-
const { items = [], data = [] } = await
|
|
1369
|
-
{
|
|
1368
|
+
const { items = [], data = [] } = await knowledgeService.list(
|
|
1369
|
+
{ lifecycle: 'active' }, { page: 1, pageSize: maxCount }
|
|
1370
1370
|
);
|
|
1371
1371
|
const recipes = items.length > 0 ? items : data;
|
|
1372
1372
|
if (recipes.length === 0) return { total: 0, lowQuality: [], message: 'No active recipes' };
|
|
@@ -1493,7 +1493,7 @@ ${code.substring(0, 3000)}
|
|
|
1493
1493
|
// 核心工具 — 使用最频繁,直接展示完整 schema
|
|
1494
1494
|
const coreTools = new Set([
|
|
1495
1495
|
'search_project_code', 'read_project_file',
|
|
1496
|
-
'search_knowledge', '
|
|
1496
|
+
'search_knowledge', 'submit_knowledge', 'submit_with_check', 'analyze_code',
|
|
1497
1497
|
'bootstrap_knowledge', 'load_skill', 'suggest_skills',
|
|
1498
1498
|
'create_skill', 'knowledge_overview', 'get_tool_details',
|
|
1499
1499
|
'plan_task', 'review_my_output',
|
|
@@ -1549,8 +1549,8 @@ ${skillSection}
|
|
|
1549
1549
|
|
|
1550
1550
|
\`\`\`batch_actions
|
|
1551
1551
|
[
|
|
1552
|
-
{"tool": "
|
|
1553
|
-
{"tool": "
|
|
1552
|
+
{"tool": "submit_knowledge", "params": {"title": "...", "code": "..."}},
|
|
1553
|
+
{"tool": "submit_knowledge", "params": {"title": "...", "code": "..."}}
|
|
1554
1554
|
]
|
|
1555
1555
|
\`\`\`
|
|
1556
1556
|
|
|
@@ -1616,7 +1616,7 @@ ${this.#projectBriefingCache}
|
|
|
1616
1616
|
2. **定向探索** → get_file_summary 快速了解文件角色
|
|
1617
1617
|
3. **深入研读** → search_project_code / read_project_file 获取真实代码
|
|
1618
1618
|
4. **语义发现** → semantic_search_code 在知识库查找相关知识
|
|
1619
|
-
5. **知识产出** →
|
|
1619
|
+
5. **知识产出** → submit_knowledge 提交有价值的发现
|
|
1620
1620
|
|
|
1621
1621
|
## 高效使用工具(节省轮次)
|
|
1622
1622
|
- **批量搜索**: search_project_code({ patterns: ["keywordA", "keywordB", "keywordC"] })
|
|
@@ -1624,7 +1624,7 @@ ${this.#projectBriefingCache}
|
|
|
1624
1624
|
- 合并同类请求为一次调用,避免逐个搜索/读取浪费轮次。
|
|
1625
1625
|
|
|
1626
1626
|
## 「项目特写」= 基本用法 + 项目特征融合
|
|
1627
|
-
|
|
1627
|
+
submit_knowledge 的 code 字段必须是「项目特写」— 将技术的基本用法与本项目的特征融合为一体:
|
|
1628
1628
|
1. **项目选择了什么**: 采用了哪种写法/模式/约定
|
|
1629
1629
|
2. **为什么这样选**: 统计数据(N 个文件、占比 M%)
|
|
1630
1630
|
3. **项目禁止什么**: 被放弃的写法、反模式、显式禁用标记
|
|
@@ -2002,15 +2002,16 @@ submit_candidate 的 code 字段必须是「项目特写」— 将技术的基
|
|
|
2002
2002
|
// rule: code-standard, code-style, best-practice, boundary-constraint
|
|
2003
2003
|
// pattern: code-pattern, architecture, solution
|
|
2004
2004
|
// fact: code-relation, inheritance, call-chain, data-flow, module-dependency
|
|
2005
|
+
// V3: knowledge_entries 统一表(candidates 已合并,lifecycle 替代 status)
|
|
2005
2006
|
const stats = db.prepare(`
|
|
2006
2007
|
SELECT
|
|
2007
|
-
(SELECT COUNT(*) FROM
|
|
2008
|
-
(SELECT COUNT(*) FROM
|
|
2009
|
-
(SELECT COUNT(*) FROM
|
|
2010
|
-
(SELECT COUNT(*) FROM
|
|
2011
|
-
(SELECT COUNT(*) FROM
|
|
2012
|
-
(SELECT COUNT(*) FROM
|
|
2013
|
-
(SELECT COUNT(*) FROM
|
|
2008
|
+
(SELECT COUNT(*) FROM knowledge_entries WHERE lifecycle = 'active') as recipeCount,
|
|
2009
|
+
(SELECT COUNT(*) FROM knowledge_entries WHERE lifecycle = 'active' AND knowledge_type IN ('code-standard','code-style','best-practice','boundary-constraint')) as ruleCount,
|
|
2010
|
+
(SELECT COUNT(*) FROM knowledge_entries WHERE lifecycle = 'active' AND knowledge_type IN ('code-pattern','architecture','solution')) as patternCount,
|
|
2011
|
+
(SELECT COUNT(*) FROM knowledge_entries WHERE lifecycle = 'active' AND knowledge_type IN ('code-relation','inheritance','call-chain','data-flow','module-dependency')) as factCount,
|
|
2012
|
+
(SELECT COUNT(*) FROM knowledge_entries WHERE lifecycle = 'active' AND knowledge_type = 'boundary-constraint') as guardRuleCount,
|
|
2013
|
+
(SELECT COUNT(*) FROM knowledge_entries WHERE lifecycle = 'pending') as pendingCandidates,
|
|
2014
|
+
(SELECT COUNT(*) FROM knowledge_entries) as totalCandidates
|
|
2014
2015
|
`).get();
|
|
2015
2016
|
if (!stats || stats.recipeCount === 0) {
|
|
2016
2017
|
return '\n## 项目状态\n⚠️ 知识库为空。建议先执行冷启动(bootstrap_knowledge)。\n';
|
|
@@ -218,7 +218,7 @@ export class ContextWindow {
|
|
|
218
218
|
for (const m of removed) {
|
|
219
219
|
if (m.role === 'assistant' && m.toolCalls) {
|
|
220
220
|
for (const tc of m.toolCalls) {
|
|
221
|
-
if (tc.name === '
|
|
221
|
+
if (tc.name === 'submit_knowledge' || tc.name === 'submit_with_check') {
|
|
222
222
|
this.#compactedSubmits.add(tc.args?.title || tc.args?.category || 'untitled');
|
|
223
223
|
}
|
|
224
224
|
}
|
|
@@ -339,7 +339,7 @@ export class ContextWindow {
|
|
|
339
339
|
const m = this.#messages[i];
|
|
340
340
|
if (m.role === 'assistant' && m.toolCalls) {
|
|
341
341
|
for (const tc of m.toolCalls) {
|
|
342
|
-
if (tc.name === '
|
|
342
|
+
if (tc.name === 'submit_knowledge' || tc.name === 'submit_with_check') {
|
|
343
343
|
this.#compactedSubmits.add(tc.args?.title || tc.args?.category || 'untitled');
|
|
344
344
|
}
|
|
345
345
|
}
|
|
@@ -393,8 +393,8 @@ export class ContextWindow {
|
|
|
393
393
|
export function limitToolResult(toolName, result, quota) {
|
|
394
394
|
const { maxChars = 4000, maxMatches = 10 } = quota;
|
|
395
395
|
|
|
396
|
-
//
|
|
397
|
-
if (toolName === '
|
|
396
|
+
// submit_knowledge / submit_with_check 结果很短,不截断
|
|
397
|
+
if (toolName === 'submit_knowledge' || toolName === 'submit_with_check') {
|
|
398
398
|
const raw = typeof result === 'string' ? result : JSON.stringify(result);
|
|
399
399
|
return raw.length > 500 ? raw.substring(0, 500) : raw;
|
|
400
400
|
}
|
|
@@ -783,7 +783,7 @@ export class PhaseRouter {
|
|
|
783
783
|
if (this.#totalSubmits === 0 && this.#phaseRounds >= 1) {
|
|
784
784
|
return this.#isSkillOnly
|
|
785
785
|
? '你已收集足够信息,请在回复中直接输出 dimensionDigest JSON。'
|
|
786
|
-
: '⚠️ 探索阶段已结束。你已收集了足够的项目信息,请 **立即** 调用
|
|
786
|
+
: '⚠️ 探索阶段已结束。你已收集了足够的项目信息,请 **立即** 调用 submit_knowledge 提交候选。不要继续搜索,直接提交。';
|
|
787
787
|
}
|
|
788
788
|
if (this.#totalSubmits >= this.#budget.softSubmitLimit && this.#budget.softSubmitLimit > 0) {
|
|
789
789
|
const remaining = this.#budget.maxSubmits - this.#totalSubmits;
|
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
* ProducerAgent.js — v3.0 生产者 Agent
|
|
3
3
|
*
|
|
4
4
|
* 职责:
|
|
5
|
-
* - 将 Analyst 的分析文本转换为结构化的
|
|
5
|
+
* - 将 Analyst 的分析文本转换为结构化的 submit_knowledge 调用
|
|
6
6
|
* - 遵循 PROJECT_SNAPSHOT_STYLE_GUIDE 格式
|
|
7
7
|
* - 使用 read_project_file 获取代码片段
|
|
8
8
|
* - CandidateGuardrail 验证每次提交
|
|
@@ -21,7 +21,7 @@ import Logger from '../../infrastructure/logging/Logger.js';
|
|
|
21
21
|
|
|
22
22
|
const PRODUCER_SYSTEM_PROMPT = `你是知识管理专家。你会收到一段代码分析文本,需要将其中的知识点转化为结构化的知识候选。
|
|
23
23
|
|
|
24
|
-
核心原则: 分析文本已经包含了所有发现,你的唯一工作是将它们格式化为
|
|
24
|
+
核心原则: 分析文本已经包含了所有发现,你的唯一工作是将它们格式化为 submit_knowledge 调用。
|
|
25
25
|
|
|
26
26
|
每个候选必须:
|
|
27
27
|
1. 有清晰的标题 (描述知识点的核心,使用项目真实类名)
|
|
@@ -33,7 +33,7 @@ const PRODUCER_SYSTEM_PROMPT = `你是知识管理专家。你会收到一段代
|
|
|
33
33
|
1. 阅读分析文本,识别每个独立的知识点/发现
|
|
34
34
|
2. 用 read_project_file 批量获取关键代码片段:
|
|
35
35
|
read_project_file({ filePaths: ["FileA.m", "FileB.m"], maxLines: 80 })
|
|
36
|
-
3. 立刻调用
|
|
36
|
+
3. 立刻调用 submit_knowledge 提交
|
|
37
37
|
4. 重复直到分析中的所有知识点都已提交
|
|
38
38
|
|
|
39
39
|
关键规则:
|
|
@@ -55,7 +55,7 @@ const PRODUCER_SYSTEM_PROMPT = `你是知识管理专家。你会收到一段代
|
|
|
55
55
|
// ──────────────────────────────────────────────────────────────────
|
|
56
56
|
|
|
57
57
|
const PRODUCER_TOOLS = [
|
|
58
|
-
'
|
|
58
|
+
'submit_knowledge',
|
|
59
59
|
'submit_with_check',
|
|
60
60
|
'read_project_file',
|
|
61
61
|
];
|
|
@@ -79,7 +79,7 @@ const PRODUCER_BUDGET = {
|
|
|
79
79
|
|
|
80
80
|
const STYLE_GUIDE = `# 「项目特写」写作要求
|
|
81
81
|
|
|
82
|
-
|
|
82
|
+
submit_knowledge 的 code 字段必须是「项目特写」。
|
|
83
83
|
|
|
84
84
|
## 什么是「项目特写」
|
|
85
85
|
将一种技术的**基本用法**与**本项目的具体特征**融合为一体。
|
|
@@ -207,7 +207,7 @@ export class ProducerAgent {
|
|
|
207
207
|
|
|
208
208
|
// 统计提交 (区分成功/失败)
|
|
209
209
|
const submitCalls = (result.toolCalls || []).filter(
|
|
210
|
-
tc => (tc.tool || tc.name) === '
|
|
210
|
+
tc => (tc.tool || tc.name) === 'submit_knowledge' || (tc.tool || tc.name) === 'submit_with_check'
|
|
211
211
|
);
|
|
212
212
|
const successCount = submitCalls.filter(tc => {
|
|
213
213
|
const res = tc.result;
|
|
@@ -238,7 +238,7 @@ export class ProducerAgent {
|
|
|
238
238
|
});
|
|
239
239
|
|
|
240
240
|
const retrySubmits = (retryResult.toolCalls || []).filter(
|
|
241
|
-
tc => (tc.tool || tc.name) === '
|
|
241
|
+
tc => (tc.tool || tc.name) === 'submit_knowledge' || (tc.tool || tc.name) === 'submit_with_check'
|
|
242
242
|
);
|
|
243
243
|
const retrySuccess = retrySubmits.filter(tc => {
|
|
244
244
|
const res = tc.result;
|