autosnippet 2.9.0 → 2.11.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 +12 -12
- package/bin/cli.js +53 -40
- package/config/constitution.yaml +9 -2
- package/dashboard/dist/assets/{icons-CH-H9x0E.js → icons-D4IWpDIk.js} +105 -100
- package/dashboard/dist/assets/index-CWBNcF9z.css +1 -0
- package/dashboard/dist/assets/index-DHtzhbuG.js +120 -0
- package/dashboard/dist/index.html +3 -3
- package/lib/cli/AiScanService.js +35 -36
- package/lib/cli/KnowledgeSyncService.js +345 -0
- package/lib/cli/SetupService.js +8 -26
- package/lib/cli/UpgradeService.js +28 -0
- package/lib/core/gateway/GatewayActionRegistry.js +48 -58
- package/lib/domain/index.js +16 -11
- package/lib/domain/knowledge/KnowledgeEntry.js +289 -0
- package/lib/domain/knowledge/KnowledgeRepository.js +123 -0
- package/lib/domain/knowledge/Lifecycle.js +99 -0
- package/lib/domain/knowledge/index.js +27 -0
- package/lib/domain/knowledge/values/Constraints.js +128 -0
- package/lib/domain/knowledge/values/Content.js +69 -0
- package/lib/domain/knowledge/values/Quality.js +81 -0
- package/lib/domain/knowledge/values/Reasoning.js +70 -0
- package/lib/domain/knowledge/values/Relations.js +142 -0
- package/lib/domain/knowledge/values/Stats.js +72 -0
- package/lib/domain/knowledge/values/index.js +9 -0
- package/lib/external/ai/AiProvider.js +85 -11
- package/lib/external/mcp/McpServer.js +7 -5
- package/lib/external/mcp/handlers/bootstrap/pipeline/orchestrator.js +18 -2
- package/lib/external/mcp/handlers/bootstrap.js +116 -11
- package/lib/external/mcp/handlers/browse.js +76 -73
- package/lib/external/mcp/handlers/candidate.js +26 -275
- package/lib/external/mcp/handlers/guard.js +2 -0
- package/lib/external/mcp/handlers/knowledge.js +267 -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 +134 -140
- package/lib/http/HttpServer.js +14 -8
- package/lib/http/routes/ai.js +4 -3
- 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/database/migrations/017_camelcase_knowledge_entries.js +107 -0
- package/lib/infrastructure/external/XcodeAutomation.js +187 -103
- package/lib/injection/ServiceContainer.js +69 -60
- package/lib/repository/knowledge/KnowledgeRepository.impl.js +338 -0
- package/lib/service/automation/DirectiveDetector.js +2 -3
- package/lib/service/automation/FileWatcher.js +59 -28
- package/lib/service/automation/XcodeIntegration.js +931 -156
- package/lib/service/automation/handlers/AlinkHandler.js +5 -4
- package/lib/service/automation/handlers/CreateHandler.js +53 -19
- package/lib/service/automation/handlers/DraftHandler.js +1 -1
- package/lib/service/automation/handlers/GuardHandler.js +183 -20
- package/lib/service/automation/handlers/SearchHandler.js +25 -22
- package/lib/service/candidate/SimilarityService.js +2 -2
- package/lib/service/chat/AnalystAgent.js +9 -0
- package/lib/service/chat/CandidateGuardrail.js +22 -11
- package/lib/service/chat/ChatAgent.js +132 -54
- package/lib/service/chat/ContextWindow.js +5 -5
- package/lib/service/chat/HandoffProtocol.js +1 -0
- package/lib/service/chat/ProducerAgent.js +40 -13
- package/lib/service/chat/ReasoningLayer.js +854 -0
- package/lib/service/chat/ReasoningTrace.js +329 -0
- package/lib/service/chat/tools.js +308 -205
- package/lib/service/cursor/CursorDeliveryPipeline.js +279 -0
- package/lib/service/cursor/KnowledgeCompressor.js +87 -0
- package/lib/service/cursor/RulesGenerator.js +168 -0
- package/lib/service/cursor/SkillsSyncer.js +268 -0
- package/lib/service/cursor/TokenBudget.js +58 -0
- package/lib/service/cursor/TopicClassifier.js +141 -0
- package/lib/service/guard/GuardCheckEngine.js +99 -10
- package/lib/service/guard/GuardService.js +57 -46
- package/lib/service/knowledge/ConfidenceRouter.js +159 -0
- package/lib/service/knowledge/KnowledgeFileWriter.js +595 -0
- package/lib/service/knowledge/KnowledgeService.js +802 -0
- package/lib/service/recipe/RecipeParser.js +3 -12
- package/lib/service/search/SearchEngine.js +67 -22
- package/lib/service/skills/SignalCollector.js +14 -9
- package/lib/service/skills/SkillAdvisor.js +13 -11
- package/lib/service/snippet/SnippetFactory.js +5 -5
- package/lib/service/spm/SpmService.js +15 -48
- package/lib/shared/RecipeReadinessChecker.js +6 -11
- 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 +8 -8
- package/skills/autosnippet-coldstart/SKILL.md +8 -4
- 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 +18 -6
- 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
|
@@ -30,13 +30,14 @@ import { TaskPipeline } from './TaskPipeline.js';
|
|
|
30
30
|
import { Memory } from './Memory.js';
|
|
31
31
|
import { ConversationStore } from './ConversationStore.js';
|
|
32
32
|
import { ContextWindow, PhaseRouter, limitToolResult } from './ContextWindow.js';
|
|
33
|
+
import { ReasoningLayer } from './ReasoningLayer.js';
|
|
33
34
|
|
|
34
35
|
const __dirname = path.dirname(fileURLToPath(import.meta.url));
|
|
35
36
|
const PROJECT_ROOT = path.resolve(__dirname, '../../..');
|
|
36
37
|
const SKILLS_DIR = path.resolve(PROJECT_ROOT, 'skills');
|
|
37
38
|
const SOUL_PATH = path.resolve(PROJECT_ROOT, 'SOUL.md');
|
|
38
39
|
const MAX_ITERATIONS = 6;
|
|
39
|
-
/** 系统调用 (如 bootstrap) 允许更多迭代,因为每维度需要多次
|
|
40
|
+
/** 系统调用 (如 bootstrap) 允许更多迭代,因为每维度需要多次 submit_knowledge */
|
|
40
41
|
const MAX_ITERATIONS_SYSTEM = 30;
|
|
41
42
|
/** 原生函数调用模式下,已提交 ≥ MIN_SUBMITS_FOR_EARLY_EXIT 个候选后,连续 N 轮无新提交则提前退出 */
|
|
42
43
|
const MIN_SUBMITS_FOR_EARLY_EXIT = 1;
|
|
@@ -69,23 +70,23 @@ const SYSTEM_CONTINUATION_PROMPT = `你的分析计划很好。但你需要 **
|
|
|
69
70
|
请现在开始执行:
|
|
70
71
|
1. 用 \`search_project_code\` 搜索项目代码获取真实示例
|
|
71
72
|
2. 用 \`read_project_file\` 查看完整文件内容
|
|
72
|
-
3. 对每个值得保留的信号,用 \`
|
|
73
|
+
3. 对每个值得保留的信号,用 \`submit_knowledge\` 提交候选
|
|
73
74
|
|
|
74
75
|
⚡ 推荐使用 batch_actions 一次提交多条候选:
|
|
75
76
|
\`\`\`batch_actions
|
|
76
77
|
[
|
|
77
|
-
{"tool": "
|
|
78
|
-
{"tool": "
|
|
78
|
+
{"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}}},
|
|
79
|
+
{"tool": "submit_knowledge", "params": {"title": "...", "code": "...", ...}}
|
|
79
80
|
]
|
|
80
81
|
\`\`\`
|
|
81
82
|
|
|
82
83
|
请立即开始执行,不要再输出分析文字。`;
|
|
83
84
|
|
|
84
85
|
/**
|
|
85
|
-
* 系统调用提交提示 — 当 AI 做了工具调用(search/read)、写了分析文本,但没调
|
|
86
|
-
* 引导 AI 将已有分析转化为实际的
|
|
86
|
+
* 系统调用提交提示 — 当 AI 做了工具调用(search/read)、写了分析文本,但没调 submit_knowledge 时注入
|
|
87
|
+
* 引导 AI 将已有分析转化为实际的 submit_knowledge 调用
|
|
87
88
|
*/
|
|
88
|
-
const SYSTEM_SUBMIT_PROMPT = `你的分析很好,已经获取了足够的项目信息。但你还没有调用 \`
|
|
89
|
+
const SYSTEM_SUBMIT_PROMPT = `你的分析很好,已经获取了足够的项目信息。但你还没有调用 \`submit_knowledge\` 提交任何候选。
|
|
89
90
|
|
|
90
91
|
**你的分析不能只停留在文字描述层面** — 必须通过工具调用将分析结果持久化。
|
|
91
92
|
|
|
@@ -93,7 +94,7 @@ const SYSTEM_SUBMIT_PROMPT = `你的分析很好,已经获取了足够的项
|
|
|
93
94
|
|
|
94
95
|
\`\`\`batch_actions
|
|
95
96
|
[
|
|
96
|
-
{"tool": "
|
|
97
|
+
{"tool": "submit_knowledge", "params": {
|
|
97
98
|
"title": "[Bootstrap] 维度/子主题",
|
|
98
99
|
"code": "# 标题 — 项目特写\\n\\n> 本项目使用 XX 模式, N 个文件采用此写法\\n\\n描述...\\n\\n\`\`\`objc\\n// 真实代码示例\\n\`\`\`\\n\\n要点说明...",
|
|
99
100
|
"language": "objectivec",
|
|
@@ -103,11 +104,11 @@ const SYSTEM_SUBMIT_PROMPT = `你的分析很好,已经获取了足够的项
|
|
|
103
104
|
"source": "bootstrap",
|
|
104
105
|
"reasoning": {"whyStandard": "为什么值得保留", "sources": ["真实文件名"], "confidence": 0.7}
|
|
105
106
|
}},
|
|
106
|
-
{"tool": "
|
|
107
|
+
{"tool": "submit_knowledge", "params": {...}}
|
|
107
108
|
]
|
|
108
109
|
\`\`\`
|
|
109
110
|
|
|
110
|
-
将你上面分析出的每个有价值的发现都转化为一条
|
|
111
|
+
将你上面分析出的每个有价值的发现都转化为一条 submit_knowledge 调用。code 字段写「项目特写」风格: 描述和代码交织,用项目真实类名和代码。`;
|
|
111
112
|
|
|
112
113
|
export class ChatAgent {
|
|
113
114
|
#toolRegistry;
|
|
@@ -342,7 +343,7 @@ export class ChatAgent {
|
|
|
342
343
|
// Bootstrap 场景限制可用工具集 (支持外部覆盖)
|
|
343
344
|
const effectiveAllowedTools = allowedTools || (isSystem ? [
|
|
344
345
|
'search_project_code', 'read_project_file',
|
|
345
|
-
'
|
|
346
|
+
'submit_knowledge', 'submit_with_check',
|
|
346
347
|
'list_project_structure', 'get_file_summary', 'semantic_search_code',
|
|
347
348
|
// AST 结构化分析工具
|
|
348
349
|
'get_project_overview', 'get_class_hierarchy', 'get_class_info',
|
|
@@ -373,6 +374,16 @@ export class ChatAgent {
|
|
|
373
374
|
const MIN_EXPLORE_ITERS = 16; // 最少探索轮次(冷启动质量保障)
|
|
374
375
|
const STALE_THRESHOLD = 3; // 连续无新信息轮次触发收敛
|
|
375
376
|
|
|
377
|
+
// ── Layer 4: ReasoningLayer — 推理链采集 + 结构化观察 + 反思 + 规划 ──
|
|
378
|
+
const reasoning = new ReasoningLayer({
|
|
379
|
+
enabled: true,
|
|
380
|
+
reflectionEnabled: isSystem, // 仅 system 模式启用反思
|
|
381
|
+
reflectionInterval: isSystem ? 5 : 0,
|
|
382
|
+
planningEnabled: isSystem, // 仅 system 模式启用规划
|
|
383
|
+
replanInterval: isSystem ? 8 : 0,
|
|
384
|
+
deviationThreshold: 0.6,
|
|
385
|
+
});
|
|
386
|
+
|
|
376
387
|
// ── 主循环 ──
|
|
377
388
|
while (true) {
|
|
378
389
|
// PhaseRouter tick + 退出检查
|
|
@@ -384,7 +395,7 @@ export class ChatAgent {
|
|
|
384
395
|
}
|
|
385
396
|
// PhaseRouter 因 maxIterations 强制转入 SUMMARIZE → 注入收尾 nudge
|
|
386
397
|
if (phaseRouter.consumeForcedSummarize()) {
|
|
387
|
-
const submitCount = toolCalls.filter(tc => tc.tool === '
|
|
398
|
+
const submitCount = toolCalls.filter(tc => tc.tool === 'submit_knowledge' || tc.tool === 'submit_with_check').length;
|
|
388
399
|
ctx.appendUserNudge(
|
|
389
400
|
`⚠️ 轮次即将耗尽 (${phaseRouter.totalIterations}/${budget.maxIterations}),**必须立即结束**。请在回复中直接输出 dimensionDigest JSON 总结(用 \`\`\`json 包裹),不要再调用任何工具。\n` +
|
|
390
401
|
`\`\`\`json\n{"dimensionDigest":{"summary":"分析总结","candidateCount":${submitCount},"keyFindings":["发现"],"crossRefs":{},"gaps":["缺口"],"remainingTasks":[{"signal":"未处理信号","reason":"轮次耗尽","priority":"high","searchHints":["搜索词"]}]}}\n\`\`\`\n> remainingTasks: 列出未来得及处理的信号。已覆盖则留空 \`[]\`。`
|
|
@@ -418,7 +429,7 @@ export class ChatAgent {
|
|
|
418
429
|
} else if (isSystem && iterationCount >= maxIter && !ctx.__gracefulExitInjected) {
|
|
419
430
|
// 达到上限 → 注入收尾消息让 AI 快速总结(而非硬中断)
|
|
420
431
|
this.#logger.info(`[ChatAgent] Iteration cap reached (${iterationCount}/${maxIter}) — injecting graceful exit nudge`);
|
|
421
|
-
const submitCount = toolCalls.filter(tc => tc.tool === '
|
|
432
|
+
const submitCount = toolCalls.filter(tc => tc.tool === 'submit_knowledge' || tc.tool === 'submit_with_check').length;
|
|
422
433
|
ctx.appendUserNudge(
|
|
423
434
|
`⚠️ 你已使用 ${iterationCount}/${maxIter} 轮次,**必须立即结束**。请在回复中直接输出 dimensionDigest JSON 总结(用 \`\`\`json 包裹),不要再调用任何工具。\n` +
|
|
424
435
|
`\`\`\`json\n{"dimensionDigest":{"summary":"分析总结","candidateCount":${submitCount},"keyFindings":["发现"],"crossRefs":{},"gaps":["缺口"],"remainingTasks":[{"signal":"未处理信号","reason":"轮次耗尽","priority":"high","searchHints":["搜索词"]}]}}\n\`\`\`\n> remainingTasks: 列出未来得及处理的信号。已覆盖则留空 \`[]\`。`
|
|
@@ -450,6 +461,15 @@ export class ChatAgent {
|
|
|
450
461
|
currentChoice = 'auto';
|
|
451
462
|
}
|
|
452
463
|
|
|
464
|
+
// ── ReasoningLayer Hook 1: beforeAICall — 开始新轮次 + 反思检查 ──
|
|
465
|
+
const reflectionNudge = reasoning.beforeAICall(iterationCount, {
|
|
466
|
+
explorationMetrics, budget, phase: phaseRouter?.phase,
|
|
467
|
+
});
|
|
468
|
+
if (reflectionNudge) {
|
|
469
|
+
ctx.appendUserNudge(reflectionNudge);
|
|
470
|
+
this.#logger.info(`[ChatAgent] 💭 injected reflection at iteration ${iterationCount}`);
|
|
471
|
+
}
|
|
472
|
+
|
|
453
473
|
// ── 压缩检查 (每次 AI 调用前) ──
|
|
454
474
|
const compactResult = ctx.compactIfNeeded();
|
|
455
475
|
if (compactResult.level > 0) {
|
|
@@ -508,6 +528,9 @@ export class ChatAgent {
|
|
|
508
528
|
this.#logger.info(`[ChatAgent] ✓ AI returned text in ${aiDuration}ms (${(aiResult.text || '').length} chars) — "${textPreview}…"`);
|
|
509
529
|
}
|
|
510
530
|
consecutiveAiErrors = 0;
|
|
531
|
+
|
|
532
|
+
// ── ReasoningLayer Hook 2: afterAICall — 提取 Thought ──
|
|
533
|
+
reasoning.afterAICall(aiResult, 'native');
|
|
511
534
|
} catch (aiErr) {
|
|
512
535
|
consecutiveAiErrors++;
|
|
513
536
|
this.#logger.warn(`[ChatAgent] AI call failed (attempt ${consecutiveAiErrors}): ${aiErr.message}`);
|
|
@@ -518,10 +541,13 @@ export class ChatAgent {
|
|
|
518
541
|
this.#logger.warn(`[ChatAgent] 🛑 circuit breaker is OPEN — skipping to summary`);
|
|
519
542
|
break;
|
|
520
543
|
}
|
|
544
|
+
reasoning.afterRound();
|
|
521
545
|
return {
|
|
522
546
|
reply: `抱歉,AI 服务暂时不可用(${aiErr.message})。请稍后重试,或检查 API 配置。`,
|
|
523
547
|
toolCalls,
|
|
524
548
|
hasContext: toolCalls.length > 0,
|
|
549
|
+
reasoningTrace: reasoning.trace,
|
|
550
|
+
reasoningQuality: reasoning.getQualityMetrics(),
|
|
525
551
|
};
|
|
526
552
|
}
|
|
527
553
|
|
|
@@ -531,10 +557,13 @@ export class ChatAgent {
|
|
|
531
557
|
ctx.resetToPromptOnly();
|
|
532
558
|
break;
|
|
533
559
|
}
|
|
560
|
+
reasoning.afterRound();
|
|
534
561
|
return {
|
|
535
562
|
reply: `抱歉,AI 服务暂时不可用(${aiErr.message})。请稍后重试,或检查 API 配置。`,
|
|
536
563
|
toolCalls,
|
|
537
564
|
hasContext: toolCalls.length > 0,
|
|
565
|
+
reasoningTrace: reasoning.trace,
|
|
566
|
+
reasoningQuality: reasoning.getQualityMetrics(),
|
|
538
567
|
};
|
|
539
568
|
}
|
|
540
569
|
await new Promise(r => setTimeout(r, 2000));
|
|
@@ -550,8 +579,9 @@ export class ChatAgent {
|
|
|
550
579
|
if (aiResult.text) {
|
|
551
580
|
const reply = this.#cleanFinalAnswer(aiResult.text);
|
|
552
581
|
const totalDuration = Date.now() - execStartTime;
|
|
582
|
+
reasoning.afterRound();
|
|
553
583
|
this.#logger.info(`[ChatAgent] ✅ final answer (graceful exit, forced) — ${reply.length} chars, ${toolCalls.length} tool calls, ${totalDuration}ms`);
|
|
554
|
-
return { reply, toolCalls, hasContext: toolCalls.length > 0 };
|
|
584
|
+
return { reply, toolCalls, hasContext: toolCalls.length > 0, reasoningTrace: reasoning.trace, reasoningQuality: reasoning.getQualityMetrics() };
|
|
555
585
|
}
|
|
556
586
|
// 无文本时继续循环,下一轮硬上限兜底
|
|
557
587
|
continue;
|
|
@@ -594,6 +624,9 @@ export class ChatAgent {
|
|
|
594
624
|
const summarized = this.#summarizeResult(toolResult);
|
|
595
625
|
toolCalls.push({ tool: fc.name, params: fc.args, result: summarized });
|
|
596
626
|
|
|
627
|
+
// ── ReasoningLayer Hook 3: afterToolExec — 记录 Action + 构建 Observation ──
|
|
628
|
+
reasoning.afterToolExec(fc.name, fc.args, toolResult, explorationMetrics);
|
|
629
|
+
|
|
597
630
|
// ── 探索进度追踪 (非 PhaseRouter 路径) ──
|
|
598
631
|
if (!phaseRouter && isSystem) {
|
|
599
632
|
explorationMetrics.totalToolCalls++;
|
|
@@ -671,7 +704,7 @@ export class ChatAgent {
|
|
|
671
704
|
explorationMetrics.uniqueQueries.add(qKey);
|
|
672
705
|
foundNewInfo = true;
|
|
673
706
|
}
|
|
674
|
-
} else if (fc.name !== '
|
|
707
|
+
} else if (fc.name !== 'submit_knowledge' && fc.name !== 'submit_with_check') {
|
|
675
708
|
// 其他未分类工具 — 首次算新信息,之后同工具名+同参数去重
|
|
676
709
|
const qKey = `${fc.name}:${JSON.stringify(fc.args || {}).substring(0, 80)}`;
|
|
677
710
|
if (!explorationMetrics.uniqueQueries.has(qKey)) {
|
|
@@ -692,7 +725,7 @@ export class ChatAgent {
|
|
|
692
725
|
let resultStr = limitToolResult(fc.name, toolResult, quota);
|
|
693
726
|
|
|
694
727
|
// ── 重复提交 / 维度范围校验 ──
|
|
695
|
-
if (fc.name === '
|
|
728
|
+
if (fc.name === 'submit_knowledge' || fc.name === 'submit_with_check') {
|
|
696
729
|
const title = fc.args?.title || fc.args?.category || '';
|
|
697
730
|
const isRejected = typeof toolResult === 'object' && toolResult?.status === 'rejected';
|
|
698
731
|
const isError = typeof toolResult === 'object' && (toolResult?.error || toolResult?.status === 'error');
|
|
@@ -721,6 +754,14 @@ export class ChatAgent {
|
|
|
721
754
|
explorationMetrics.staleRounds++;
|
|
722
755
|
}
|
|
723
756
|
|
|
757
|
+
// ── ReasoningLayer Hook 4: afterRound — 关闭轮次 + 写入摘要 ──
|
|
758
|
+
reasoning.afterRound({
|
|
759
|
+
newInfoCount: roundHasNewInfo ? 1 : 0,
|
|
760
|
+
totalCalls: activeCalls.length,
|
|
761
|
+
submitCount: roundSubmitCount,
|
|
762
|
+
explorationMetrics,
|
|
763
|
+
});
|
|
764
|
+
|
|
724
765
|
// ── PhaseRouter 更新 ──
|
|
725
766
|
if (phaseRouter) {
|
|
726
767
|
const transition = phaseRouter.update({
|
|
@@ -734,7 +775,7 @@ export class ChatAgent {
|
|
|
734
775
|
// 需要一条 user 消息明确告知 AI 切换到提交模式
|
|
735
776
|
if (transition.transitioned && transition.newPhase === 'PRODUCE') {
|
|
736
777
|
ctx.appendUserNudge(
|
|
737
|
-
'你已充分探索了项目代码,现在请开始调用
|
|
778
|
+
'你已充分探索了项目代码,现在请开始调用 submit_knowledge 工具来提交你发现的知识候选。不要再搜索,直接提交。'
|
|
738
779
|
);
|
|
739
780
|
this.#logger.info('[ChatAgent] 📝 injected PRODUCE transition nudge');
|
|
740
781
|
}
|
|
@@ -743,7 +784,7 @@ export class ChatAgent {
|
|
|
743
784
|
// skill-only 维度从 EXPLORE 直接进入 SUMMARIZE (跳过 PRODUCE),
|
|
744
785
|
// 需要明确告知 AI 输出 dimensionDigest JSON
|
|
745
786
|
if (transition.transitioned && transition.newPhase === 'SUMMARIZE') {
|
|
746
|
-
const submitCount = toolCalls.filter(tc => tc.tool === '
|
|
787
|
+
const submitCount = toolCalls.filter(tc => tc.tool === 'submit_knowledge' || tc.tool === 'submit_with_check').length;
|
|
747
788
|
ctx.appendUserNudge(
|
|
748
789
|
`你已完成分析探索。请在回复中直接输出 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
790
|
);
|
|
@@ -781,7 +822,7 @@ export class ChatAgent {
|
|
|
781
822
|
// 注入 nudge 让 AI 再输出一次 digest JSON
|
|
782
823
|
if (transition.transitioned) {
|
|
783
824
|
ctx.appendAssistantText(aiResult.text || '');
|
|
784
|
-
const submitCount = toolCalls.filter(tc => tc.tool === '
|
|
825
|
+
const submitCount = toolCalls.filter(tc => tc.tool === 'submit_knowledge' || tc.tool === 'submit_with_check').length;
|
|
785
826
|
ctx.appendUserNudge(
|
|
786
827
|
`请在回复中直接输出 dimensionDigest JSON 总结(用 \`\`\`json 包裹):\n\`\`\`json\n{"dimensionDigest":{"summary":"分析总结","candidateCount":${submitCount},"keyFindings":["发现"],"crossRefs":{},"gaps":["缺口"],"remainingTasks":[{"signal":"未处理的信号","reason":"原因","priority":"high","searchHints":["搜索词"]}]}}\n\`\`\`\n> remainingTasks: 记录未来得及处理的信号。已全部覆盖则留空 \`[]\`。`
|
|
787
828
|
);
|
|
@@ -791,8 +832,9 @@ export class ChatAgent {
|
|
|
791
832
|
// 已在 SUMMARIZE 阶段 — 这就是最终回答
|
|
792
833
|
const reply = this.#cleanFinalAnswer(aiResult.text || '');
|
|
793
834
|
const totalDuration = Date.now() - execStartTime;
|
|
835
|
+
reasoning.afterRound();
|
|
794
836
|
this.#logger.info(`[ChatAgent] ✅ final answer — ${reply.length} chars, ${phaseRouter.totalIterations} iters, ${toolCalls.length} tool calls, ${totalDuration}ms`);
|
|
795
|
-
return { reply, toolCalls, hasContext: toolCalls.length > 0 };
|
|
837
|
+
return { reply, toolCalls, hasContext: toolCalls.length > 0, reasoningTrace: reasoning.trace, reasoningQuality: reasoning.getQualityMetrics() };
|
|
796
838
|
}
|
|
797
839
|
|
|
798
840
|
if (!transition.transitioned) {
|
|
@@ -802,7 +844,7 @@ export class ChatAgent {
|
|
|
802
844
|
ctx.appendAssistantText(aiResult.text || '');
|
|
803
845
|
if (phaseRouter.phase === 'PRODUCE') {
|
|
804
846
|
ctx.appendUserNudge(
|
|
805
|
-
'你的分析很好。请继续调用
|
|
847
|
+
'你的分析很好。请继续调用 submit_knowledge 提交你发现的知识候选,每个值得记录的模式/实践都应该提交。'
|
|
806
848
|
);
|
|
807
849
|
this.#logger.info('[ChatAgent] 📝 injected submit nudge (text in PRODUCE, not transitioning)');
|
|
808
850
|
}
|
|
@@ -811,8 +853,9 @@ export class ChatAgent {
|
|
|
811
853
|
// 非生产阶段的未转换文本 = 最终回答
|
|
812
854
|
const reply = this.#cleanFinalAnswer(aiResult.text || '');
|
|
813
855
|
const totalDuration = Date.now() - execStartTime;
|
|
856
|
+
reasoning.afterRound();
|
|
814
857
|
this.#logger.info(`[ChatAgent] ✅ final answer — ${reply.length} chars, ${phaseRouter.totalIterations} iters, ${toolCalls.length} tool calls, ${totalDuration}ms`);
|
|
815
|
-
return { reply, toolCalls, hasContext: toolCalls.length > 0 };
|
|
858
|
+
return { reply, toolCalls, hasContext: toolCalls.length > 0, reasoningTrace: reasoning.trace, reasoningQuality: reasoning.getQualityMetrics() };
|
|
816
859
|
}
|
|
817
860
|
|
|
818
861
|
// 其他阶段的文字回答 → 继续循环(PhaseRouter 已自动转换阶段)
|
|
@@ -824,21 +867,27 @@ export class ChatAgent {
|
|
|
824
867
|
if (ctx.__gracefulExitInjected || !isSystem) {
|
|
825
868
|
const reply = this.#cleanFinalAnswer(aiResult.text || '');
|
|
826
869
|
const totalDuration = Date.now() - execStartTime;
|
|
870
|
+
reasoning.afterRound();
|
|
827
871
|
this.#logger.info(`[ChatAgent] ✅ final answer (${ctx.__gracefulExitInjected ? 'graceful exit' : 'user'}) — ${reply.length} chars, ${toolCalls.length} tool calls, ${totalDuration}ms`);
|
|
828
|
-
return { reply, toolCalls, hasContext: toolCalls.length > 0 };
|
|
872
|
+
return { reply, toolCalls, hasContext: toolCalls.length > 0, reasoningTrace: reasoning.trace, reasoningQuality: reasoning.getQualityMetrics() };
|
|
829
873
|
}
|
|
830
874
|
|
|
831
875
|
// system 模式非 graceful exit 的文字回答(理论上不应到这里)
|
|
832
876
|
const reply = this.#cleanFinalAnswer(aiResult.text || '');
|
|
833
877
|
const totalDuration = Date.now() - execStartTime;
|
|
878
|
+
reasoning.afterRound();
|
|
834
879
|
this.#logger.info(`[ChatAgent] ✅ final answer — ${reply.length} chars, ${toolCalls.length} tool calls, ${totalDuration}ms`);
|
|
835
|
-
return { reply, toolCalls, hasContext: toolCalls.length > 0 };
|
|
880
|
+
return { reply, toolCalls, hasContext: toolCalls.length > 0, reasoningTrace: reasoning.trace, reasoningQuality: reasoning.getQualityMetrics() };
|
|
836
881
|
}
|
|
837
882
|
|
|
838
883
|
// ── 循环退出: 产出 dimensionDigest 总结 ──
|
|
839
|
-
|
|
884
|
+
reasoning.afterRound();
|
|
885
|
+
const forcedResult = await this.#produceForcedSummary({
|
|
840
886
|
source, toolCalls, toolSchemas, ctx, phaseRouter, execStartTime,
|
|
841
887
|
});
|
|
888
|
+
forcedResult.reasoningTrace = reasoning.trace;
|
|
889
|
+
forcedResult.reasoningQuality = reasoning.getQualityMetrics();
|
|
890
|
+
return forcedResult;
|
|
842
891
|
}
|
|
843
892
|
|
|
844
893
|
/**
|
|
@@ -850,7 +899,7 @@ export class ChatAgent {
|
|
|
850
899
|
this.#logger.info(`[ChatAgent] ⚠ producing forced summary (${iterations} iters, ${toolCalls.length} calls)`);
|
|
851
900
|
|
|
852
901
|
const candidateCount = toolCalls.filter(tc =>
|
|
853
|
-
tc.tool === '
|
|
902
|
+
tc.tool === 'submit_knowledge' || tc.tool === 'submit_with_check'
|
|
854
903
|
).length;
|
|
855
904
|
|
|
856
905
|
let finalReply;
|
|
@@ -865,7 +914,7 @@ export class ChatAgent {
|
|
|
865
914
|
if (isCircuitOpen) throw new Error('circuit open — skip to synthetic digest');
|
|
866
915
|
|
|
867
916
|
const submitSummary = toolCalls
|
|
868
|
-
.filter(tc => tc.tool === '
|
|
917
|
+
.filter(tc => tc.tool === 'submit_knowledge' || tc.tool === 'submit_with_check')
|
|
869
918
|
.map((tc, i) => `${i + 1}. ${tc.params?.title || tc.params?.category || 'untitled'}`)
|
|
870
919
|
.join('\n');
|
|
871
920
|
|
|
@@ -912,7 +961,7 @@ ${submitSummary ? `已提交候选:\n${submitSummary}\n` : ''}
|
|
|
912
961
|
this.#logger.warn(`[ChatAgent] forced summary AI call failed: ${err.message}`);
|
|
913
962
|
// 合成 digest 兜底
|
|
914
963
|
const titles = toolCalls
|
|
915
|
-
.filter(tc => tc.tool === '
|
|
964
|
+
.filter(tc => tc.tool === 'submit_knowledge' || tc.tool === 'submit_with_check')
|
|
916
965
|
.map(tc => tc.params?.title || 'untitled');
|
|
917
966
|
finalReply = `\`\`\`json
|
|
918
967
|
{
|
|
@@ -954,10 +1003,19 @@ ${submitSummary ? `已提交候选:\n${submitSummary}\n` : ''}
|
|
|
954
1003
|
let consecutiveAiErrors = 0;
|
|
955
1004
|
const maxIter = source === 'system' ? MAX_ITERATIONS_SYSTEM : MAX_ITERATIONS;
|
|
956
1005
|
|
|
1006
|
+
// ── ReasoningLayer (文本解析模式) ──
|
|
1007
|
+
const reasoning = new ReasoningLayer({
|
|
1008
|
+
enabled: true,
|
|
1009
|
+
reflectionEnabled: false, // 文本模式暂不启用反思(避免复杂度)
|
|
1010
|
+
});
|
|
1011
|
+
|
|
957
1012
|
while (iterations < maxIter) {
|
|
958
1013
|
iterations++;
|
|
959
1014
|
const iterStartTime = Date.now();
|
|
960
1015
|
|
|
1016
|
+
// ── ReasoningLayer Hook 1: beforeAICall ──
|
|
1017
|
+
reasoning.beforeAICall(iterations);
|
|
1018
|
+
|
|
961
1019
|
let response;
|
|
962
1020
|
try {
|
|
963
1021
|
this.#logger.info(`[ChatAgent] 🔄 text iteration ${iterations}/${maxIter} — calling AI (${messages.length} messages)`);
|
|
@@ -969,24 +1027,33 @@ ${submitSummary ? `已提交候选:\n${submitSummary}\n` : ''}
|
|
|
969
1027
|
const responsePreview = (response || '').substring(0, 120).replace(/\n/g, '↵');
|
|
970
1028
|
this.#logger.info(`[ChatAgent] ✓ AI responded in ${aiDuration}ms (${(response || '').length} chars) — "${responsePreview}…"`);
|
|
971
1029
|
consecutiveAiErrors = 0;
|
|
1030
|
+
|
|
1031
|
+
// ── ReasoningLayer Hook 2: afterAICall ──
|
|
1032
|
+
reasoning.afterAICall(response, 'text');
|
|
972
1033
|
} catch (aiErr) {
|
|
973
1034
|
consecutiveAiErrors++;
|
|
974
1035
|
this.#logger.warn(`[ChatAgent] AI call failed (attempt ${consecutiveAiErrors}): ${aiErr.message}`);
|
|
975
1036
|
|
|
976
1037
|
// 熔断器已打开 → 立即返回
|
|
977
1038
|
if (aiErr.code === 'CIRCUIT_OPEN') {
|
|
1039
|
+
reasoning.afterRound();
|
|
978
1040
|
return {
|
|
979
1041
|
reply: `抱歉,AI 服务暂时不可用(${aiErr.message})。请稍后重试,或检查 API 配置。`,
|
|
980
1042
|
toolCalls,
|
|
981
1043
|
hasContext: toolCalls.length > 0,
|
|
1044
|
+
reasoningTrace: reasoning.trace,
|
|
1045
|
+
reasoningQuality: reasoning.getQualityMetrics(),
|
|
982
1046
|
};
|
|
983
1047
|
}
|
|
984
1048
|
|
|
985
1049
|
if (consecutiveAiErrors >= 2) {
|
|
1050
|
+
reasoning.afterRound();
|
|
986
1051
|
return {
|
|
987
1052
|
reply: `抱歉,AI 服务暂时不可用(${aiErr.message})。请稍后重试,或检查 API 配置。`,
|
|
988
1053
|
toolCalls,
|
|
989
1054
|
hasContext: toolCalls.length > 0,
|
|
1055
|
+
reasoningTrace: reasoning.trace,
|
|
1056
|
+
reasoningQuality: reasoning.getQualityMetrics(),
|
|
990
1057
|
};
|
|
991
1058
|
}
|
|
992
1059
|
await new Promise(r => setTimeout(r, 2000));
|
|
@@ -997,7 +1064,7 @@ ${submitSummary ? `已提交候选:\n${submitSummary}\n` : ''}
|
|
|
997
1064
|
|
|
998
1065
|
if (!actions) {
|
|
999
1066
|
// ── 系统调用自动续跑 ──
|
|
1000
|
-
const hasSubmits = toolCalls.some(tc => tc.tool === '
|
|
1067
|
+
const hasSubmits = toolCalls.some(tc => tc.tool === 'submit_knowledge' || tc.tool === 'submit_with_check');
|
|
1001
1068
|
if (source === 'system' && iterations < maxIter && !hasSubmits) {
|
|
1002
1069
|
if (this.#looksLikeIncompleteStep(response)) {
|
|
1003
1070
|
this.#logger.info(`[ChatAgent] 🔄 detected planning-only response at iteration ${iterations}, injecting continuation prompt`);
|
|
@@ -1017,9 +1084,10 @@ ${submitSummary ? `已提交候选:\n${submitSummary}\n` : ''}
|
|
|
1017
1084
|
|
|
1018
1085
|
const reply = this.#cleanFinalAnswer(response);
|
|
1019
1086
|
const totalDuration = Date.now() - execStartTime;
|
|
1087
|
+
reasoning.afterRound();
|
|
1020
1088
|
this.#logger.info(`[ChatAgent] ✅ text final answer — ${reply.length} chars, ${iterations} iterations, ${toolCalls.length} tool calls, ${totalDuration}ms total`);
|
|
1021
1089
|
|
|
1022
|
-
return { reply, toolCalls, hasContext: toolCalls.length > 0 };
|
|
1090
|
+
return { reply, toolCalls, hasContext: toolCalls.length > 0, reasoningTrace: reasoning.trace, reasoningQuality: reasoning.getQualityMetrics() };
|
|
1023
1091
|
}
|
|
1024
1092
|
|
|
1025
1093
|
// 执行工具
|
|
@@ -1058,8 +1126,17 @@ ${submitSummary ? `已提交候选:\n${submitSummary}\n` : ''}
|
|
|
1058
1126
|
result: summarized,
|
|
1059
1127
|
});
|
|
1060
1128
|
batchResults.push({ tool: action.tool, result: toolResult });
|
|
1129
|
+
|
|
1130
|
+
// ── ReasoningLayer Hook 3: afterToolExec ──
|
|
1131
|
+
reasoning.afterToolExec(action.tool, action.params, toolResult);
|
|
1061
1132
|
}
|
|
1062
1133
|
|
|
1134
|
+
// ── ReasoningLayer Hook 4: afterRound ──
|
|
1135
|
+
reasoning.afterRound({
|
|
1136
|
+
totalCalls: actions.length,
|
|
1137
|
+
submitCount: 0,
|
|
1138
|
+
});
|
|
1139
|
+
|
|
1063
1140
|
// 将工具结果注入为下一轮 prompt
|
|
1064
1141
|
let observation;
|
|
1065
1142
|
if (batchResults.length === 1) {
|
|
@@ -1098,7 +1175,7 @@ ${submitSummary ? `已提交候选:\n${submitSummary}\n` : ''}
|
|
|
1098
1175
|
}
|
|
1099
1176
|
|
|
1100
1177
|
const finalReply = this.#cleanFinalAnswer(finalResponse);
|
|
1101
|
-
return { reply: finalReply, toolCalls, hasContext: toolCalls.length > 0 };
|
|
1178
|
+
return { reply: finalReply, toolCalls, hasContext: toolCalls.length > 0, reasoningTrace: reasoning.trace, reasoningQuality: reasoning.getQualityMetrics() };
|
|
1102
1179
|
}
|
|
1103
1180
|
|
|
1104
1181
|
/**
|
|
@@ -1272,13 +1349,13 @@ ${highSim.map(s => `- ${s.title} (相似度: ${s.similarity})`).join('\n')}
|
|
|
1272
1349
|
*/
|
|
1273
1350
|
async #taskDiscoverAllRelations({ batchSize = 20 } = {}) {
|
|
1274
1351
|
const ctx = this.#getToolContext();
|
|
1275
|
-
const
|
|
1276
|
-
if (!
|
|
1352
|
+
const knowledgeService = ctx.container.get('knowledgeService');
|
|
1353
|
+
if (!knowledgeService) throw new Error('KnowledgeService 不可用');
|
|
1277
1354
|
|
|
1278
1355
|
if (!ctx.aiProvider) throw new Error('AI Provider 未配置,请先设置 API Key');
|
|
1279
1356
|
|
|
1280
|
-
//
|
|
1281
|
-
const { items = [], data = [] } = await
|
|
1357
|
+
// 获取所有活跃知识条目
|
|
1358
|
+
const { items = [], data = [] } = await knowledgeService.list({ lifecycle: 'active' }, { page: 1, pageSize: 500 });
|
|
1282
1359
|
const recipes = items.length > 0 ? items : data;
|
|
1283
1360
|
if (recipes.length < 2) return { discovered: 0, totalPairs: 0, message: `只有 ${recipes.length} 条 Recipe,至少需要 2 条` };
|
|
1284
1361
|
|
|
@@ -1334,10 +1411,10 @@ ${highSim.map(s => `- ${s.title} (相似度: ${s.similarity})`).join('\n')}
|
|
|
1334
1411
|
*/
|
|
1335
1412
|
async #taskFullEnrich({ status = 'pending', maxCount = 50 } = {}) {
|
|
1336
1413
|
const ctx = this.#getToolContext();
|
|
1337
|
-
const
|
|
1414
|
+
const knowledgeService = ctx.container.get('knowledgeService');
|
|
1338
1415
|
|
|
1339
|
-
const { items = [], data = [] } = await
|
|
1340
|
-
{ status }, { page: 1, pageSize: maxCount }
|
|
1416
|
+
const { items = [], data = [] } = await knowledgeService.list(
|
|
1417
|
+
{ lifecycle: status }, { page: 1, pageSize: maxCount }
|
|
1341
1418
|
);
|
|
1342
1419
|
const candidates = items.length > 0 ? items : data;
|
|
1343
1420
|
if (candidates.length === 0) return { enriched: 0, message: 'No candidates to enrich' };
|
|
@@ -1363,10 +1440,10 @@ ${highSim.map(s => `- ${s.title} (相似度: ${s.similarity})`).join('\n')}
|
|
|
1363
1440
|
*/
|
|
1364
1441
|
async #taskQualityAudit({ threshold = 0.6, maxCount = 100 } = {}) {
|
|
1365
1442
|
const ctx = this.#getToolContext();
|
|
1366
|
-
const
|
|
1443
|
+
const knowledgeService = ctx.container.get('knowledgeService');
|
|
1367
1444
|
|
|
1368
|
-
const { items = [], data = [] } = await
|
|
1369
|
-
{
|
|
1445
|
+
const { items = [], data = [] } = await knowledgeService.list(
|
|
1446
|
+
{ lifecycle: 'active' }, { page: 1, pageSize: maxCount }
|
|
1370
1447
|
);
|
|
1371
1448
|
const recipes = items.length > 0 ? items : data;
|
|
1372
1449
|
if (recipes.length === 0) return { total: 0, lowQuality: [], message: 'No active recipes' };
|
|
@@ -1493,7 +1570,7 @@ ${code.substring(0, 3000)}
|
|
|
1493
1570
|
// 核心工具 — 使用最频繁,直接展示完整 schema
|
|
1494
1571
|
const coreTools = new Set([
|
|
1495
1572
|
'search_project_code', 'read_project_file',
|
|
1496
|
-
'search_knowledge', '
|
|
1573
|
+
'search_knowledge', 'submit_knowledge', 'submit_with_check', 'analyze_code',
|
|
1497
1574
|
'bootstrap_knowledge', 'load_skill', 'suggest_skills',
|
|
1498
1575
|
'create_skill', 'knowledge_overview', 'get_tool_details',
|
|
1499
1576
|
'plan_task', 'review_my_output',
|
|
@@ -1549,8 +1626,8 @@ ${skillSection}
|
|
|
1549
1626
|
|
|
1550
1627
|
\`\`\`batch_actions
|
|
1551
1628
|
[
|
|
1552
|
-
{"tool": "
|
|
1553
|
-
{"tool": "
|
|
1629
|
+
{"tool": "submit_knowledge", "params": {"title": "...", "code": "..."}},
|
|
1630
|
+
{"tool": "submit_knowledge", "params": {"title": "...", "code": "..."}}
|
|
1554
1631
|
]
|
|
1555
1632
|
\`\`\`
|
|
1556
1633
|
|
|
@@ -1616,7 +1693,7 @@ ${this.#projectBriefingCache}
|
|
|
1616
1693
|
2. **定向探索** → get_file_summary 快速了解文件角色
|
|
1617
1694
|
3. **深入研读** → search_project_code / read_project_file 获取真实代码
|
|
1618
1695
|
4. **语义发现** → semantic_search_code 在知识库查找相关知识
|
|
1619
|
-
5. **知识产出** →
|
|
1696
|
+
5. **知识产出** → submit_knowledge 提交有价值的发现
|
|
1620
1697
|
|
|
1621
1698
|
## 高效使用工具(节省轮次)
|
|
1622
1699
|
- **批量搜索**: search_project_code({ patterns: ["keywordA", "keywordB", "keywordC"] })
|
|
@@ -1624,7 +1701,7 @@ ${this.#projectBriefingCache}
|
|
|
1624
1701
|
- 合并同类请求为一次调用,避免逐个搜索/读取浪费轮次。
|
|
1625
1702
|
|
|
1626
1703
|
## 「项目特写」= 基本用法 + 项目特征融合
|
|
1627
|
-
|
|
1704
|
+
submit_knowledge 的 code 字段必须是「项目特写」— 将技术的基本用法与本项目的特征融合为一体:
|
|
1628
1705
|
1. **项目选择了什么**: 采用了哪种写法/模式/约定
|
|
1629
1706
|
2. **为什么这样选**: 统计数据(N 个文件、占比 M%)
|
|
1630
1707
|
3. **项目禁止什么**: 被放弃的写法、反模式、显式禁用标记
|
|
@@ -1998,19 +2075,20 @@ submit_candidate 的 code 字段必须是「项目特写」— 将技术的基
|
|
|
1998
2075
|
try {
|
|
1999
2076
|
const db = this.#container?.get('database');
|
|
2000
2077
|
if (!db) return '';
|
|
2001
|
-
//
|
|
2078
|
+
// knowledgeType → kind 映射:
|
|
2002
2079
|
// rule: code-standard, code-style, best-practice, boundary-constraint
|
|
2003
2080
|
// pattern: code-pattern, architecture, solution
|
|
2004
2081
|
// fact: code-relation, inheritance, call-chain, data-flow, module-dependency
|
|
2082
|
+
// V3: knowledge_entries 统一表(candidates 已合并,lifecycle 替代 status)
|
|
2005
2083
|
const stats = db.prepare(`
|
|
2006
2084
|
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
|
|
2085
|
+
(SELECT COUNT(*) FROM knowledge_entries WHERE lifecycle = 'active') as recipeCount,
|
|
2086
|
+
(SELECT COUNT(*) FROM knowledge_entries WHERE lifecycle = 'active' AND knowledgeType IN ('code-standard','code-style','best-practice','boundary-constraint')) as ruleCount,
|
|
2087
|
+
(SELECT COUNT(*) FROM knowledge_entries WHERE lifecycle = 'active' AND knowledgeType IN ('code-pattern','architecture','solution')) as patternCount,
|
|
2088
|
+
(SELECT COUNT(*) FROM knowledge_entries WHERE lifecycle = 'active' AND knowledgeType IN ('code-relation','inheritance','call-chain','data-flow','module-dependency')) as factCount,
|
|
2089
|
+
(SELECT COUNT(*) FROM knowledge_entries WHERE lifecycle = 'active' AND knowledgeType = 'boundary-constraint') as guardRuleCount,
|
|
2090
|
+
(SELECT COUNT(*) FROM knowledge_entries WHERE lifecycle = 'pending') as pendingCandidates,
|
|
2091
|
+
(SELECT COUNT(*) FROM knowledge_entries) as totalCandidates
|
|
2014
2092
|
`).get();
|
|
2015
2093
|
if (!stats || stats.recipeCount === 0) {
|
|
2016
2094
|
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;
|
|
@@ -118,6 +118,7 @@ export function buildAnalysisReport(analystResult, dimensionId, projectGraph = n
|
|
|
118
118
|
iterations: analystResult.toolCalls?.length || 0,
|
|
119
119
|
toolCallCount: analystResult.toolCalls?.length || 0,
|
|
120
120
|
tokenUsage: analystResult.tokenUsage || null,
|
|
121
|
+
reasoningQuality: analystResult.reasoningQuality || null,
|
|
121
122
|
},
|
|
122
123
|
};
|
|
123
124
|
}
|