autosnippet 3.2.8 → 3.2.10
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/bin/cli.js +6 -5
- package/dashboard/dist/assets/index-BTAsOZv2.js +128 -0
- package/dashboard/dist/assets/index-C_72Ct98.css +1 -0
- package/dashboard/dist/index.html +2 -2
- package/lib/cli/AiScanService.js +23 -26
- package/lib/cli/SetupService.js +1 -1
- package/lib/cli/deploy/FileManifest.js +1 -1
- package/lib/core/AstAnalyzer.js +1 -1
- package/lib/core/discovery/index.js +2 -2
- package/lib/external/ai/AiProvider.js +66 -172
- package/lib/external/ai/providers/GoogleGeminiProvider.js +29 -5
- package/lib/external/mcp/handlers/bootstrap/BootstrapSession.js +1 -1
- package/lib/external/mcp/handlers/bootstrap/ExternalSubmissionTracker.js +3 -3
- package/lib/external/mcp/handlers/bootstrap/MissionBriefingBuilder.js +1 -1
- package/lib/external/mcp/handlers/bootstrap/pipeline/IncrementalBootstrap.js +1 -1
- package/lib/external/mcp/handlers/bootstrap/pipeline/dimension-context.js +8 -8
- package/lib/external/mcp/handlers/bootstrap/pipeline/noAiFallback.js +1 -1
- package/lib/external/mcp/handlers/bootstrap/pipeline/orchestrator.js +291 -204
- package/lib/external/mcp/handlers/bootstrap/shared/bootstrap-phases.js +7 -6
- package/lib/external/mcp/handlers/bootstrap/shared/dimension-sop.js +1 -1
- package/lib/external/mcp/handlers/bootstrap-internal.js +2 -2
- package/lib/external/mcp/handlers/dimension-complete-external.js +6 -6
- package/lib/http/HttpServer.js +1 -1
- package/lib/http/middleware/requestLogger.js +1 -0
- package/lib/http/routes/ai.js +240 -35
- package/lib/http/routes/candidates.js +2 -3
- package/lib/http/routes/extract.js +13 -11
- package/lib/http/routes/modules.js +2 -2
- package/lib/http/routes/recipes.js +5 -5
- package/lib/http/routes/remote.js +134 -255
- package/lib/http/routes/violations.js +0 -54
- package/lib/http/utils/sse-sessions.js +1 -1
- package/lib/infrastructure/logging/Logger.js +5 -4
- package/lib/infrastructure/monitoring/PerformanceMonitor.js +3 -2
- package/lib/injection/ServiceContainer.js +64 -17
- package/lib/platform/ScreenCaptureService.js +177 -0
- package/lib/platform/ios/routes/spm.js +2 -2
- package/lib/service/agent/AgentEventBus.js +207 -0
- package/lib/service/agent/AgentFactory.js +535 -0
- package/lib/service/agent/AgentMessage.js +240 -0
- package/lib/service/agent/AgentRouter.js +228 -0
- package/lib/service/agent/AgentRuntime.js +1056 -0
- package/lib/service/agent/AgentState.js +217 -0
- package/lib/service/agent/IntentClassifier.js +331 -0
- package/lib/service/agent/LarkTransport.js +389 -0
- package/lib/service/agent/capabilities.js +409 -0
- package/lib/service/{chat → agent/context}/ContextWindow.js +37 -12
- package/lib/service/{chat → agent/context}/ExplorationTracker.js +112 -33
- package/lib/service/{chat → agent/core}/ChatAgentPrompts.js +5 -3
- package/lib/service/agent/core/LoopContext.js +170 -0
- package/lib/service/agent/core/MessageAdapter.js +223 -0
- package/lib/service/agent/core/ToolExecutionPipeline.js +376 -0
- package/lib/service/{chat → agent/domain}/ChatAgentTasks.js +15 -98
- package/lib/service/{chat → agent/domain}/EpisodicConsolidator.js +7 -7
- package/lib/service/{chat → agent/domain}/EvidenceCollector.js +4 -2
- package/lib/service/{chat/AnalystAgent.js → agent/domain/insight-analyst.js} +37 -172
- package/lib/service/{chat/HandoffProtocol.js → agent/domain/insight-gate.js} +85 -135
- package/lib/service/agent/domain/insight-producer.js +270 -0
- package/lib/service/agent/domain/scan-prompts.js +444 -0
- package/lib/service/agent/forced-summary.js +266 -0
- package/lib/service/agent/index.js +91 -0
- package/lib/service/{chat → agent}/memory/ActiveContext.js +29 -1
- package/lib/service/{chat → agent}/memory/MemoryCoordinator.js +7 -7
- package/lib/service/{chat/ProjectSemanticMemory.js → agent/memory/PersistentMemory.js} +359 -89
- package/lib/service/{chat → agent}/memory/SessionStore.js +1 -1
- package/lib/service/{chat → agent}/memory/index.js +1 -1
- package/lib/service/agent/policies.js +442 -0
- package/lib/service/agent/presets.js +305 -0
- package/lib/service/agent/strategies.js +756 -0
- package/lib/service/{chat → agent/tools}/ToolRegistry.js +3 -3
- package/lib/service/agent/tools/ai-analysis.js +75 -0
- package/lib/service/{chat → agent}/tools/composite.js +2 -1
- package/lib/service/{chat → agent}/tools/guard.js +1 -121
- package/lib/service/{chat → agent}/tools/index.js +27 -21
- package/lib/service/{chat → agent}/tools/infrastructure.js +1 -1
- package/lib/service/agent/tools/knowledge-graph.js +112 -0
- package/lib/service/agent/tools/scan-recipe.js +189 -0
- package/lib/service/agent/tools/system-interaction.js +476 -0
- package/lib/service/automation/DirectiveDetector.js +0 -1
- package/lib/service/automation/FileWatcher.js +0 -8
- package/lib/service/automation/handlers/CreateHandler.js +7 -3
- package/lib/service/automation/handlers/DraftHandler.js +7 -6
- package/lib/service/module/ModuleService.js +40 -73
- package/lib/service/skills/SignalCollector.js +26 -19
- package/lib/service/snippet/codecs/VSCodeCodec.js +1 -1
- package/lib/shared/FieldSpec.js +1 -1
- package/lib/shared/StyleGuide.js +1 -1
- package/package.json +4 -1
- package/resources/native-ui/screenshot.swift +228 -0
- package/dashboard/dist/assets/index-D5jiDBQG.css +0 -1
- package/dashboard/dist/assets/index-e5OKj-Ni.js +0 -128
- package/lib/core/discovery/SpmDiscoverer.js +0 -5
- package/lib/external/mcp/handlers/bootstrap/pipeline/EpisodicMemory.js +0 -750
- package/lib/external/mcp/handlers/bootstrap/pipeline/ToolResultCache.js +0 -277
- package/lib/http/routes/spm.js +0 -5
- package/lib/infrastructure/external/XcodeAutomation.js +0 -15
- package/lib/service/chat/ChatAgent.js +0 -1602
- package/lib/service/chat/Memory.js +0 -161
- package/lib/service/chat/ProducerAgent.js +0 -431
- package/lib/service/chat/ReasoningTrace.js +0 -523
- package/lib/service/chat/TaskPipeline.js +0 -357
- package/lib/service/chat/WorkingMemory.js +0 -359
- package/lib/service/chat/memory/PersistentMemory.js +0 -450
- package/lib/service/chat/tools/ai-analysis.js +0 -267
- package/lib/service/chat/tools/knowledge-graph.js +0 -234
- package/lib/service/chat/tools.js +0 -18
- package/lib/service/snippet/PlaceholderConverter.js +0 -5
- package/lib/service/snippet/codecs/XcodeCodec.js +0 -5
- /package/lib/service/{chat → agent}/ConversationStore.js +0 -0
- /package/lib/service/{chat → agent}/tools/_shared.js +0 -0
- /package/lib/service/{chat → agent}/tools/ast-graph.js +0 -0
- /package/lib/service/{chat → agent}/tools/lifecycle.js +0 -0
- /package/lib/service/{chat → agent}/tools/project-access.js +0 -0
- /package/lib/service/{chat → agent}/tools/query.js +0 -0
|
@@ -0,0 +1,444 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* scan-prompts.js — scanKnowledge 任务配置 + 统一管线工厂 + 关系发现管线
|
|
3
|
+
*
|
|
4
|
+
* scan pipeline (extract/summarize):
|
|
5
|
+
* 共享 Insight Pipeline (Analyze → QualityGate → Produce → RejectionGate),
|
|
6
|
+
* Analyze 使用与冷启动一致的 ANALYST_SYSTEM_PROMPT + ExplorationTracker 四阶段管理,
|
|
7
|
+
* Produce 阶段均为工具驱动 (collect_scan_recipe),与冷启动 submit_knowledge 对齐。
|
|
8
|
+
*
|
|
9
|
+
* relations pipeline (独立):
|
|
10
|
+
* 知识图谱关系发现: Explore → Synthesize 两阶段,
|
|
11
|
+
* 通过查询知识库 + 读取源码发现条目间语义关系。
|
|
12
|
+
*
|
|
13
|
+
* @module scan-prompts
|
|
14
|
+
*/
|
|
15
|
+
|
|
16
|
+
import { insightGateEvaluator, buildRetryPrompt } from './insight-gate.js';
|
|
17
|
+
import { producerRejectionGateEvaluator, buildCodeContextSection } from './insight-producer.js';
|
|
18
|
+
import { ANALYST_SYSTEM_PROMPT } from './insight-analyst.js';
|
|
19
|
+
|
|
20
|
+
/**
|
|
21
|
+
* @typedef {Object} ScanTaskConfig
|
|
22
|
+
* @property {string} producePrompt — Produce 阶段的 systemPrompt
|
|
23
|
+
* @property {(label: string) => Object} fallback — 解析失败时的默认值工厂
|
|
24
|
+
*/
|
|
25
|
+
|
|
26
|
+
/**
|
|
27
|
+
* task → Produce 阶段配置 (extract + summarize)
|
|
28
|
+
*
|
|
29
|
+
* 两种 task 均为工具驱动 (collect_scan_recipe),Recipe 格式与冷启动 submit_knowledge 对齐:
|
|
30
|
+
* - extract: 多文件 target 扫描 → 多个 Recipe
|
|
31
|
+
* - summarize: 单文件/代码片段 → 1~2 个 Recipe
|
|
32
|
+
*
|
|
33
|
+
* @type {Record<string, ScanTaskConfig>}
|
|
34
|
+
*/
|
|
35
|
+
export const SCAN_TASK_CONFIGS = {
|
|
36
|
+
|
|
37
|
+
// ─── extract: Recipe 提取(工具驱动,与冷启动 submit_knowledge 字段对齐) ─────
|
|
38
|
+
|
|
39
|
+
extract: {
|
|
40
|
+
producePrompt: `你是知识管理专家。你会收到一段代码分析文本,需要将其中的知识点转化为结构化的知识候选。
|
|
41
|
+
|
|
42
|
+
核心原则: 分析文本已经包含了所有发现,你的唯一工作是将它们格式化为 collect_scan_recipe 调用。
|
|
43
|
+
|
|
44
|
+
每个候选必须:
|
|
45
|
+
1. 有清晰的标题 (描述知识点的核心,使用项目真实类名)
|
|
46
|
+
2. 有项目特写风格的正文 (content.markdown 字段,结合代码展示)
|
|
47
|
+
3. 标注相关文件路径 (reasoning.sources)
|
|
48
|
+
4. 选择正确的 kind (rule/pattern/fact)
|
|
49
|
+
5. 提供完整的 Cursor 交付字段 (trigger, doClause, whenClause 等)
|
|
50
|
+
|
|
51
|
+
## 「项目特写」写作要求(content.markdown)
|
|
52
|
+
content.markdown 字段必须是「项目特写」:
|
|
53
|
+
1. **项目选择了什么** — 采用了哪种写法/模式/约定
|
|
54
|
+
2. **为什么这样选** — 统计分布、占比、历史决策
|
|
55
|
+
3. **项目禁止什么** — 反模式、已废弃写法
|
|
56
|
+
4. **新代码怎么写** — 可直接复制使用的代码模板 + 来源标注 (来源: FileName.ext:行号)
|
|
57
|
+
|
|
58
|
+
## 工作流程
|
|
59
|
+
1. 阅读分析文本,识别每个独立的知识点/发现
|
|
60
|
+
2. 用 read_project_file 批量获取关键代码片段:
|
|
61
|
+
read_project_file({ filePaths: ["FileA.m", "FileB.m"], maxLines: 80 })
|
|
62
|
+
3. 立刻调用 collect_scan_recipe 提交
|
|
63
|
+
4. 重复直到分析中的所有知识点都已提交
|
|
64
|
+
|
|
65
|
+
## 关键规则
|
|
66
|
+
- 分析中的每个要点/段落都应转化为至少一个候选
|
|
67
|
+
- read_project_file 支持 filePaths 数组批量读取多个文件,一次调用完成
|
|
68
|
+
- reasoning.sources 必须是非空数组,填写相关文件路径如 ["FileName.m"]
|
|
69
|
+
- 如果分析提到了 3 个模式,就应该提交 3 个候选,不要合并
|
|
70
|
+
- 禁止: 不要搜索新文件、不要做额外分析,专注于格式化和提交
|
|
71
|
+
|
|
72
|
+
容错规则:
|
|
73
|
+
- 如果 read_project_file 返回"文件不存在"或错误,不要重试同一文件的其他路径变体
|
|
74
|
+
- 文件读取失败时,直接使用分析文本中已有的代码和描述来提交候选
|
|
75
|
+
- 永远不要因为文件读取失败而跳过知识点 — 分析文本已经包含足够信息
|
|
76
|
+
- 先提交候选,再考虑是否需要读取更多代码(提交优先于验证)`,
|
|
77
|
+
fallback: (label) => ({ targetName: label, extracted: 0, recipes: [] }),
|
|
78
|
+
},
|
|
79
|
+
|
|
80
|
+
// ─── summarize: 代码摘要(工具驱动,与 extract 管线对齐) ──────
|
|
81
|
+
|
|
82
|
+
summarize: {
|
|
83
|
+
producePrompt: `你是技术文档专家。你会收到一段代码分析文本,需要将其转化为高质量的知识候选。
|
|
84
|
+
|
|
85
|
+
核心原则: 分析文本已经包含了所有发现,你的唯一工作是将它们格式化为 collect_scan_recipe 调用。
|
|
86
|
+
|
|
87
|
+
这是单文件/代码片段的深度分析,提交一个(或少量)高质量的知识候选:
|
|
88
|
+
1. 清晰的标题(描述代码的核心功能,使用项目真实类名)
|
|
89
|
+
2. 完整的技术文档正文(content.markdown 字段,≥200 字符)
|
|
90
|
+
3. 实用的使用指南(usageGuide 字段,含示例)
|
|
91
|
+
4. 准确的分类(category)和标签(tags)
|
|
92
|
+
|
|
93
|
+
## content.markdown 写作要求
|
|
94
|
+
1. **功能概述** — 这段代码做什么,解决什么问题
|
|
95
|
+
2. **核心实现** — 关键代码逻辑,含代码块 (\`\`\`)
|
|
96
|
+
3. **使用方式** — 如何调用/集成,含示例代码
|
|
97
|
+
4. **注意事项** — 边界条件、性能考量、已知限制
|
|
98
|
+
|
|
99
|
+
## 工作流程
|
|
100
|
+
1. 阅读分析文本,理解代码的核心功能和设计决策
|
|
101
|
+
2. 如需验证细节,用 read_project_file 获取代码片段
|
|
102
|
+
3. 调用 collect_scan_recipe 提交知识候选
|
|
103
|
+
|
|
104
|
+
## 关键规则
|
|
105
|
+
- 单文件通常提交 1 个候选,除非代码明确包含多个独立知识点
|
|
106
|
+
- reasoning.sources 必须是非空数组,填写源文件路径
|
|
107
|
+
- kind 选择: 优先 pattern(代码模式)或 fact(技术事实)
|
|
108
|
+
- 必填: trigger (@kebab-case)、doClause (英文祈使句)、content.rationale
|
|
109
|
+
- content.markdown 必须包含代码块,展示核心实现`,
|
|
110
|
+
fallback: (label) => ({ targetName: label, extracted: 0, recipes: [] }),
|
|
111
|
+
},
|
|
112
|
+
|
|
113
|
+
};
|
|
114
|
+
|
|
115
|
+
// ──────────────────────────────────────────────────────────────────
|
|
116
|
+
// 统一管线工厂 — 生成标准 4 阶段 Pipeline (与冷启动对齐)
|
|
117
|
+
// ──────────────────────────────────────────────────────────────────
|
|
118
|
+
|
|
119
|
+
/**
|
|
120
|
+
* 构建 scanKnowledge 的标准 4 阶段 Pipeline stages
|
|
121
|
+
*
|
|
122
|
+
* 与冷启动 orchestrator 完全对齐:
|
|
123
|
+
* 1. analyze — 代码分析 (ExplorationTracker 四阶段管理)
|
|
124
|
+
* 2. quality_gate — 分析质量门控 (insightGateEvaluator + buildAnalysisArtifact)
|
|
125
|
+
* 3. produce — 知识生产 (artifact-aware promptBuilder + 工具驱动提交)
|
|
126
|
+
* 4. rejection_gate — 拒绝率门控 (producerRejectionGateEvaluator)
|
|
127
|
+
*
|
|
128
|
+
* 与冷启动对齐的关键节点:
|
|
129
|
+
* - quality_gate 通过 strategyContext.activeContext 走 buildAnalysisArtifact
|
|
130
|
+
* (而非降级的 buildAnalysisReport),保留 findings/evidenceMap/negativeSignals
|
|
131
|
+
* - produce 使用 promptBuilder (而非 promptTransform),
|
|
132
|
+
* 从 gateArtifact 注入结构化发现和代码证据到 prompt
|
|
133
|
+
* - strategyContext 需要包含 activeContext / outputType / dimId
|
|
134
|
+
* (由 AgentFactory.buildSystemContext 设置)
|
|
135
|
+
*
|
|
136
|
+
* @param {Object} opts
|
|
137
|
+
* @param {'extract'|'summarize'} opts.task — 任务类型
|
|
138
|
+
* @param {string} opts.producePrompt — Produce 阶段 systemPrompt
|
|
139
|
+
* @param {string[]} opts.analyzeCaps — Analyze 阶段 capabilities
|
|
140
|
+
* @param {string[]} opts.produceCaps — Produce 阶段 capabilities
|
|
141
|
+
* @param {Array} [opts.files] — 源文件 (fallback prompt 用)
|
|
142
|
+
* @param {number} [opts.analyzeMaxIter=24] — Analyze 最大迭代
|
|
143
|
+
* @returns {Object[]} PipelineStrategy stages 数组
|
|
144
|
+
*/
|
|
145
|
+
export function buildScanPipelineStages({
|
|
146
|
+
task,
|
|
147
|
+
producePrompt,
|
|
148
|
+
analyzeCaps,
|
|
149
|
+
produceCaps,
|
|
150
|
+
files,
|
|
151
|
+
analyzeMaxIter = 24,
|
|
152
|
+
} = {}) {
|
|
153
|
+
// ── Stage 1: Analyze ──
|
|
154
|
+
const analyzeStage = {
|
|
155
|
+
name: 'analyze',
|
|
156
|
+
capabilities: analyzeCaps,
|
|
157
|
+
budget: {
|
|
158
|
+
maxIterations: analyzeMaxIter,
|
|
159
|
+
maxTokens: 8192,
|
|
160
|
+
temperature: 0.3,
|
|
161
|
+
timeoutMs: 300_000, // 5 min (与冷启动对齐)
|
|
162
|
+
},
|
|
163
|
+
systemPrompt: ANALYST_SYSTEM_PROMPT,
|
|
164
|
+
retryPromptBuilder: (retryCtx, _origPrompt, prev) => {
|
|
165
|
+
const prevAnalysis = prev.analyze?.reply || '';
|
|
166
|
+
const retryHint = buildRetryPrompt(retryCtx.reason);
|
|
167
|
+
return `${prevAnalysis}\n\n⚠️ 上述分析未通过质量检查: ${retryCtx.reason}\n\n${retryHint}`;
|
|
168
|
+
},
|
|
169
|
+
};
|
|
170
|
+
|
|
171
|
+
// ── Stage 2: Quality Gate ──
|
|
172
|
+
// insightGateEvaluator 读取 strategyContext.activeContext:
|
|
173
|
+
// - 有 activeContext → buildAnalysisArtifact (完整: findings/evidenceMap/negativeSignals)
|
|
174
|
+
// - 无 activeContext → buildAnalysisReport (降级: 仅文本 + 文件列表)
|
|
175
|
+
// buildSystemContext 通过 trace 设置 activeContext,确保走完整路径
|
|
176
|
+
const qualityGateStage = {
|
|
177
|
+
name: 'quality_gate',
|
|
178
|
+
gate: {
|
|
179
|
+
evaluator: insightGateEvaluator,
|
|
180
|
+
maxRetries: 1,
|
|
181
|
+
},
|
|
182
|
+
};
|
|
183
|
+
|
|
184
|
+
// ── Stage 3: Produce ──
|
|
185
|
+
// extract 和 summarize 都是工具驱动 (collect_scan_recipe)
|
|
186
|
+
const isToolDriven = task === 'extract' || task === 'summarize';
|
|
187
|
+
const isSummarize = task === 'summarize';
|
|
188
|
+
const submitToolNames = isToolDriven ? ['collect_scan_recipe'] : [];
|
|
189
|
+
|
|
190
|
+
const produceStage = {
|
|
191
|
+
name: 'produce',
|
|
192
|
+
submitToolName: 'collect_scan_recipe', // 透传给 ExplorationTracker nudge 文本
|
|
193
|
+
capabilities: produceCaps,
|
|
194
|
+
budget: {
|
|
195
|
+
maxIterations: isSummarize ? 12 : 24,
|
|
196
|
+
temperature: 0.2,
|
|
197
|
+
timeoutMs: isSummarize ? 120_000 : 180_000,
|
|
198
|
+
// 显式传入 tracker 阈值,避免依赖默认值 (softSubmitLimit:8) 导致转换不触发
|
|
199
|
+
maxSubmits: isSummarize ? 3 : 10,
|
|
200
|
+
softSubmitLimit: isSummarize ? 2 : 8,
|
|
201
|
+
idleRoundsToExit: 2,
|
|
202
|
+
},
|
|
203
|
+
systemPrompt: producePrompt,
|
|
204
|
+
// 使用 promptBuilder (而非 promptTransform) — 与冷启动对齐
|
|
205
|
+
// promptBuilder 接收 gateArtifact (来自 quality_gate 的 AnalysisArtifact),
|
|
206
|
+
// 注入结构化 findings + 代码证据到 prompt,而非仅传入 analyze.reply 纯文本
|
|
207
|
+
promptBuilder: (ctx) => {
|
|
208
|
+
return buildScanProducerPrompt(ctx, files, task);
|
|
209
|
+
},
|
|
210
|
+
// retry 配置 (拒绝率过高时缩减预算)
|
|
211
|
+
...(isToolDriven ? {
|
|
212
|
+
retryBudget: { maxIterations: isSummarize ? 3 : 5, temperature: 0.3, timeoutMs: isSummarize ? 60_000 : 120_000 },
|
|
213
|
+
retryPromptBuilder: (retryCtx, _origPrompt, prev) => {
|
|
214
|
+
const prevProduce = prev.produce;
|
|
215
|
+
const submitCalls = (prevProduce?.toolCalls || []).filter(tc =>
|
|
216
|
+
submitToolNames.includes(tc.tool || tc.name));
|
|
217
|
+
const rejected = submitCalls.filter(tc => {
|
|
218
|
+
const res = tc.result;
|
|
219
|
+
if (!res) return false;
|
|
220
|
+
if (typeof res === 'string') return res.includes('rejected') || res.includes('error');
|
|
221
|
+
return res.status === 'rejected' || res.status === 'error';
|
|
222
|
+
}).length;
|
|
223
|
+
return `你的 ${rejected} 个提交被拒绝了。请根据拒绝原因改进后重新提交,确保:
|
|
224
|
+
1. content 必须是对象: { markdown: "...", rationale: "...", pattern: "..." }
|
|
225
|
+
2. content.markdown 字段 ≥ 200 字符,含代码块 (\`\`\`)
|
|
226
|
+
3. content.rationale 必填 — 设计原理说明
|
|
227
|
+
4. reasoning.sources 必须是非空数组
|
|
228
|
+
5. 标题使用项目真实类名
|
|
229
|
+
6. 必填: trigger (@kebab-case)、kind (rule/pattern/fact)、doClause (英文祈使句)`;
|
|
230
|
+
},
|
|
231
|
+
skipOnDegrade: true,
|
|
232
|
+
} : {
|
|
233
|
+
skipOnDegrade: true,
|
|
234
|
+
}),
|
|
235
|
+
};
|
|
236
|
+
|
|
237
|
+
const stages = [analyzeStage, qualityGateStage, produceStage];
|
|
238
|
+
|
|
239
|
+
// ── Stage 4: Rejection Gate (仅工具驱动模式) ──
|
|
240
|
+
if (isToolDriven) {
|
|
241
|
+
stages.push({
|
|
242
|
+
name: 'rejection_gate',
|
|
243
|
+
gate: {
|
|
244
|
+
evaluator: (source, phaseResults, ctx) =>
|
|
245
|
+
producerRejectionGateEvaluator(source, phaseResults, {
|
|
246
|
+
...ctx,
|
|
247
|
+
submitToolNames,
|
|
248
|
+
}),
|
|
249
|
+
maxRetries: 1,
|
|
250
|
+
},
|
|
251
|
+
skipOnDegrade: true,
|
|
252
|
+
});
|
|
253
|
+
}
|
|
254
|
+
|
|
255
|
+
return stages;
|
|
256
|
+
}
|
|
257
|
+
|
|
258
|
+
// ──────────────────────────────────────────────────────────────────
|
|
259
|
+
// Scan Producer Prompt Builder — artifact-aware (与冷启动 buildProducerPromptV2 对齐)
|
|
260
|
+
// ──────────────────────────────────────────────────────────────────
|
|
261
|
+
|
|
262
|
+
/**
|
|
263
|
+
* 构建 scan produce 阶段的 prompt — 从 gateArtifact 注入结构化信息
|
|
264
|
+
*
|
|
265
|
+
* 与冷启动 buildProducerPromptV2 对齐的关键:
|
|
266
|
+
* - 优先使用 gateArtifact 中的 findings (结构化发现)
|
|
267
|
+
* - 注入 evidenceMap (Analyst 已读取的代码证据)
|
|
268
|
+
* - 注入 negativeSignals (搜索但未找到的模式)
|
|
269
|
+
* - 当 artifact 不可用时 fallback 到 analyze.reply 纯文本
|
|
270
|
+
*
|
|
271
|
+
* @param {Object} ctx — promptBuilder 上下文 (含 gateArtifact, phaseResults, ...)
|
|
272
|
+
* @param {Array} [files] — 源文件 (fallback 用)
|
|
273
|
+
* @param {'extract'|'summarize'} task — 任务类型
|
|
274
|
+
* @returns {string}
|
|
275
|
+
*/
|
|
276
|
+
function buildScanProducerPrompt(ctx, files, task) {
|
|
277
|
+
const artifact = ctx.gateArtifact;
|
|
278
|
+
const analysis = ctx.phaseResults?.analyze?.reply || '';
|
|
279
|
+
|
|
280
|
+
// ── 有完整 artifact 时 (走 buildAnalysisArtifact 路径) ──
|
|
281
|
+
if (artifact && artifact.analysisText) {
|
|
282
|
+
const parts = [];
|
|
283
|
+
|
|
284
|
+
// §1 分析文本
|
|
285
|
+
parts.push(`将以下代码分析转化为 collect_scan_recipe 调用。\n\n---\n${artifact.analysisText}\n---`);
|
|
286
|
+
|
|
287
|
+
// §2 结构化发现 (来自 ActiveContext scratchpad)
|
|
288
|
+
if (artifact.findings?.length > 0) {
|
|
289
|
+
const findingLines = ['## 关键发现 (Analyst 已确认)'];
|
|
290
|
+
const sorted = [...artifact.findings].sort((a, b) => (b.importance || 0) - (a.importance || 0));
|
|
291
|
+
for (const f of sorted) {
|
|
292
|
+
const badge = (f.importance || 0) >= 8 ? '⚠️' : '📋';
|
|
293
|
+
findingLines.push(`${badge} **[${f.importance || 5}/10]** ${f.finding}`);
|
|
294
|
+
if (f.evidence) {
|
|
295
|
+
findingLines.push(` 证据: ${f.evidence}`);
|
|
296
|
+
}
|
|
297
|
+
}
|
|
298
|
+
findingLines.push('');
|
|
299
|
+
findingLines.push('☝️ 上述每个发现都应至少转化为一个候选。');
|
|
300
|
+
parts.push(findingLines.join('\n'));
|
|
301
|
+
}
|
|
302
|
+
|
|
303
|
+
// §3 代码证据 (来自 EvidenceCollector)
|
|
304
|
+
if (artifact.evidenceMap && artifact.evidenceMap.size > 0) {
|
|
305
|
+
const codeContext = buildCodeContextSection(artifact.evidenceMap);
|
|
306
|
+
if (codeContext) {
|
|
307
|
+
parts.push(codeContext);
|
|
308
|
+
}
|
|
309
|
+
}
|
|
310
|
+
|
|
311
|
+
// §4 负空间信号 (搜索但未找到的模式 — 不要猜测)
|
|
312
|
+
if (artifact.negativeSignals?.length > 0) {
|
|
313
|
+
const nsLines = ['## ⛔ 不存在的模式 (不要猜测)'];
|
|
314
|
+
for (const ns of artifact.negativeSignals.slice(0, 5)) {
|
|
315
|
+
nsLines.push(`- "${ns.searchPattern}" — ${ns.implication}`);
|
|
316
|
+
}
|
|
317
|
+
parts.push(nsLines.join('\n'));
|
|
318
|
+
}
|
|
319
|
+
|
|
320
|
+
// §5 引用文件
|
|
321
|
+
if (artifact.referencedFiles?.length > 0) {
|
|
322
|
+
parts.push(`分析中引用的关键文件: ${artifact.referencedFiles.slice(0, 15).join(', ')}`);
|
|
323
|
+
}
|
|
324
|
+
|
|
325
|
+
return parts.join('\n\n');
|
|
326
|
+
}
|
|
327
|
+
|
|
328
|
+
// ── Fallback: 无 artifact 时退回纯文本 (不应该发生,但防御性保留) ──
|
|
329
|
+
if (analysis.length >= 200) {
|
|
330
|
+
return `将以下代码分析转化为结构化输出。\n\n## 代码分析\n${analysis}`;
|
|
331
|
+
}
|
|
332
|
+
|
|
333
|
+
// Fallback: analyze reply 不足时直接提供源代码
|
|
334
|
+
const fileCtx = (files || []).slice(0, 15).map(f => {
|
|
335
|
+
const body = (f.content || '').length > 1200
|
|
336
|
+
? f.content.slice(0, 1200) + '\n// ... (truncated)'
|
|
337
|
+
: (f.content || '');
|
|
338
|
+
return `### ${f.relativePath || f.name}\n\`\`\`\n${body}\n\`\`\``;
|
|
339
|
+
}).join('\n\n');
|
|
340
|
+
const preamble = analysis
|
|
341
|
+
? `## 部分分析\n${analysis}\n\n`
|
|
342
|
+
: '';
|
|
343
|
+
return `${preamble}分析以下 ${files?.length || 0} 个源文件,提取知识 Recipe。\n\n${fileCtx}`;
|
|
344
|
+
}
|
|
345
|
+
|
|
346
|
+
// ──────────────────────────────────────────────────────────────────
|
|
347
|
+
// Relations Pipeline — 知识图谱关系发现(独立管线)
|
|
348
|
+
// ──────────────────────────────────────────────────────────────────
|
|
349
|
+
|
|
350
|
+
/** Explore 阶段: 查询知识库,分析条目间关联 */
|
|
351
|
+
const RELATIONS_EXPLORE_PROMPT = `你是知识图谱架构师。你的任务是探索项目知识库中的知识条目,发现它们之间的语义关系。
|
|
352
|
+
|
|
353
|
+
## 工作流程
|
|
354
|
+
1. 使用 search_knowledge 查询知识库中的条目分类
|
|
355
|
+
2. 逐组分析相关知识条目的内容、依赖、关联代码
|
|
356
|
+
3. 使用 read_project_file 验证跨条目的代码引用关系
|
|
357
|
+
4. 详细记录发现的所有关系及其代码证据
|
|
358
|
+
|
|
359
|
+
## 关系类型
|
|
360
|
+
- requires: A 需要 B 才能正常工作
|
|
361
|
+
- extends: A 扩展了 B 的功能
|
|
362
|
+
- enforces: A 强制规范了 B 的使用方式
|
|
363
|
+
- depends_on: A 依赖 B
|
|
364
|
+
- inherits: A 继承自 B
|
|
365
|
+
- implements: A 实现了 B 的接口/协议
|
|
366
|
+
- calls: A 调用了 B
|
|
367
|
+
- prerequisite: 理解 A 之前需要先了解 B
|
|
368
|
+
|
|
369
|
+
## 分析要求
|
|
370
|
+
- 每个关系必须有明确的代码证据(文件名 + 代码片段)
|
|
371
|
+
- 不要臆造不存在的关系
|
|
372
|
+
- 优先发现强关联(requires, implements, inherits),再发现弱关联(calls, prerequisite)
|
|
373
|
+
- 将发现以结构化文本记录: "FromTitle → ToTitle (type): evidence"`;
|
|
374
|
+
|
|
375
|
+
/** Synthesize 阶段: 将探索结果转化为 JSON */
|
|
376
|
+
const RELATIONS_SYNTHESIZE_PROMPT = `你是结构化数据专家。将知识图谱探索结果转化为 JSON 格式的关系列表。
|
|
377
|
+
|
|
378
|
+
## 输出格式(纯 JSON,不含 markdown 包装)
|
|
379
|
+
{
|
|
380
|
+
"analyzed": 知识条目数量,
|
|
381
|
+
"relations": [
|
|
382
|
+
{
|
|
383
|
+
"from": "知识条目A精确标题",
|
|
384
|
+
"to": "知识条目B精确标题",
|
|
385
|
+
"type": "关系类型",
|
|
386
|
+
"evidence": "具体代码证据描述"
|
|
387
|
+
}
|
|
388
|
+
]
|
|
389
|
+
}
|
|
390
|
+
|
|
391
|
+
## 规则
|
|
392
|
+
- 严格对照探索阶段的发现,不添加未被提及的关系
|
|
393
|
+
- type 必须是: requires / extends / enforces / depends_on / inherits / implements / calls / prerequisite
|
|
394
|
+
- evidence 必须引用具体的代码或文件
|
|
395
|
+
- from 和 to 使用知识条目的精确标题`;
|
|
396
|
+
|
|
397
|
+
/**
|
|
398
|
+
* 构建知识图谱关系发现的独立 Pipeline stages
|
|
399
|
+
*
|
|
400
|
+
* 与 scan pipeline 不同,relations pipeline:
|
|
401
|
+
* - 不需要源文件输入 (从知识库查询)
|
|
402
|
+
* - 2 阶段: explore (工具驱动) → synthesize (文本输出)
|
|
403
|
+
* - 无质量门控 (探索结果质量由工具返回保证)
|
|
404
|
+
*
|
|
405
|
+
* @param {Object} [opts]
|
|
406
|
+
* @param {string[]} [opts.exploreCaps] — Explore 阶段 capabilities
|
|
407
|
+
* @param {number} [opts.exploreMaxIter=20] — Explore 最大迭代
|
|
408
|
+
* @returns {Object[]} PipelineStrategy stages 数组
|
|
409
|
+
*/
|
|
410
|
+
export function buildRelationsPipelineStages({
|
|
411
|
+
exploreCaps = ['knowledge_production', 'code_analysis'],
|
|
412
|
+
exploreMaxIter = 20,
|
|
413
|
+
} = {}) {
|
|
414
|
+
return [
|
|
415
|
+
{
|
|
416
|
+
name: 'explore',
|
|
417
|
+
capabilities: exploreCaps,
|
|
418
|
+
budget: {
|
|
419
|
+
maxIterations: exploreMaxIter,
|
|
420
|
+
maxTokens: 8192,
|
|
421
|
+
temperature: 0.3,
|
|
422
|
+
timeoutMs: 300_000,
|
|
423
|
+
},
|
|
424
|
+
systemPrompt: RELATIONS_EXPLORE_PROMPT,
|
|
425
|
+
},
|
|
426
|
+
{
|
|
427
|
+
name: 'synthesize',
|
|
428
|
+
capabilities: [],
|
|
429
|
+
budget: {
|
|
430
|
+
maxIterations: 4,
|
|
431
|
+
maxTokens: 8192,
|
|
432
|
+
temperature: 0.2,
|
|
433
|
+
timeoutMs: 60_000,
|
|
434
|
+
},
|
|
435
|
+
systemPrompt: RELATIONS_SYNTHESIZE_PROMPT,
|
|
436
|
+
promptTransform: (_input, prev) => {
|
|
437
|
+
const exploration = prev.explore?.reply || '';
|
|
438
|
+
return `基于以下知识图谱探索结果,输出结构化关系 JSON。\n\n## 探索结果\n${exploration}`;
|
|
439
|
+
},
|
|
440
|
+
},
|
|
441
|
+
];
|
|
442
|
+
}
|
|
443
|
+
|
|
444
|
+
export default SCAN_TASK_CONFIGS;
|