autosnippet 3.2.9 → 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/lib/cli/deploy/FileManifest.js +1 -1
- package/lib/external/ai/providers/GoogleGeminiProvider.js +6 -4
- package/lib/external/mcp/handlers/bootstrap/pipeline/orchestrator.js +6 -2
- package/lib/http/routes/recipes.js +1 -5
- package/lib/service/agent/AgentFactory.js +137 -92
- package/lib/service/agent/AgentRuntime.js +43 -3
- package/lib/service/agent/capabilities.js +3 -2
- package/lib/service/agent/context/ExplorationTracker.js +87 -19
- package/lib/service/agent/core/ChatAgentPrompts.js +4 -2
- package/lib/service/agent/domain/ChatAgentTasks.js +2 -6
- package/lib/service/agent/domain/insight-producer.js +4 -1
- package/lib/service/agent/domain/scan-prompts.js +368 -29
- package/lib/service/agent/memory/ActiveContext.js +29 -1
- package/lib/service/agent/presets.js +3 -1
- package/lib/service/agent/strategies.js +41 -2
- package/package.json +1 -1
|
@@ -146,7 +146,7 @@ export const MANIFEST = [
|
|
|
146
146
|
src: 'pre-commit-guard.sh',
|
|
147
147
|
dest: null, // 动态决定:.husky/pre-commit 或 .git/hooks/pre-commit
|
|
148
148
|
strategy: 'create-only',
|
|
149
|
-
on: '
|
|
149
|
+
on: 'manual', // 暂不在 setup 中强制安装 git 工作流
|
|
150
150
|
category: 'pre-commit-hook',
|
|
151
151
|
chmod: true,
|
|
152
152
|
resolveDest: 'resolvePreCommitDest',
|
|
@@ -115,10 +115,12 @@ export class GoogleGeminiProvider extends AiProvider {
|
|
|
115
115
|
];
|
|
116
116
|
}
|
|
117
117
|
|
|
118
|
-
// toolChoice → Gemini mode
|
|
119
|
-
body.
|
|
120
|
-
|
|
121
|
-
|
|
118
|
+
// toolChoice → Gemini mode (仅在有工具声明时设置,无工具时设 toolConfig 可能导致空响应)
|
|
119
|
+
if (body.tools) {
|
|
120
|
+
body.toolConfig = {
|
|
121
|
+
functionCallingConfig: { mode: this.#toGeminiMode(toolChoice) },
|
|
122
|
+
};
|
|
123
|
+
}
|
|
122
124
|
|
|
123
125
|
// 系统指令
|
|
124
126
|
if (systemPrompt) {
|
|
@@ -24,6 +24,7 @@ import { SessionStore } from '../../../../../service/agent/memory/SessionStore.j
|
|
|
24
24
|
import { AgentMessage } from '../../../../../service/agent/AgentMessage.js';
|
|
25
25
|
import { BudgetPolicy } from '../../../../../service/agent/policies.js';
|
|
26
26
|
import { PRESETS } from '../../../../../service/agent/presets.js';
|
|
27
|
+
import { ANALYST_BUDGET } from '../../../../../service/agent/domain/insight-analyst.js';
|
|
27
28
|
import { ContextWindow } from '../../../../../service/agent/context/ContextWindow.js';
|
|
28
29
|
import { ExplorationTracker } from '../../../../../service/agent/context/ExplorationTracker.js';
|
|
29
30
|
import { clearCheckpoints, loadCheckpoints, saveDimensionCheckpoint } from './checkpoint.js';
|
|
@@ -607,9 +608,12 @@ export async function fillDimensionsV3(fillContext) {
|
|
|
607
608
|
outputType: dimConfig.outputType || 'analysis',
|
|
608
609
|
// ── 引擎增强参数 (PipelineStrategy → reactLoop 透传) ──
|
|
609
610
|
contextWindow: agentFactory.createContextWindow({ isSystem: true }),
|
|
611
|
+
// B1 fix: 分析阶段使用 analyst 策略 (SCAN→EXPLORE→VERIFY→SUMMARIZE)
|
|
612
|
+
// 而非 bootstrap (EXPLORE→PRODUCE→SUMMARIZE),避免 PRODUCE nudge 浪费轮次
|
|
613
|
+
// B3 fix: 透传完整 ANALYST_BUDGET (searchBudget/maxSubmits/softSubmitLimit/idleRoundsToExit)
|
|
610
614
|
tracker: ExplorationTracker.resolve(
|
|
611
|
-
{ source: 'system',
|
|
612
|
-
{
|
|
615
|
+
{ source: 'system', strategy: 'analyst' },
|
|
616
|
+
{ ...ANALYST_BUDGET },
|
|
613
617
|
),
|
|
614
618
|
trace: memoryCoordinator.getActiveContext(analystScopeId),
|
|
615
619
|
memoryCoordinator,
|
|
@@ -111,11 +111,7 @@ router.post(
|
|
|
111
111
|
// 异步执行,不 await
|
|
112
112
|
(async () => {
|
|
113
113
|
try {
|
|
114
|
-
const result = await agentFactory.
|
|
115
|
-
label: 'knowledge-graph',
|
|
116
|
-
files: [],
|
|
117
|
-
task: 'relations',
|
|
118
|
-
});
|
|
114
|
+
const result = await agentFactory.discoverRelations();
|
|
119
115
|
discoverTask.status = 'done';
|
|
120
116
|
discoverTask.finishedAt = new Date().toISOString();
|
|
121
117
|
discoverTask.discovered = result.discovered || 0;
|
|
@@ -23,8 +23,8 @@ import { BudgetPolicy, PolicyEngine } from './policies.js';
|
|
|
23
23
|
import { getPreset, resolveStrategy } from './presets.js';
|
|
24
24
|
import { ContextWindow } from './context/ContextWindow.js';
|
|
25
25
|
import { ExplorationTracker } from './context/ExplorationTracker.js';
|
|
26
|
-
import {
|
|
27
|
-
import { SCAN_TASK_CONFIGS } from './domain/scan-prompts.js';
|
|
26
|
+
import { MemoryCoordinator } from './memory/MemoryCoordinator.js';
|
|
27
|
+
import { SCAN_TASK_CONFIGS, buildScanPipelineStages, buildRelationsPipelineStages } from './domain/scan-prompts.js';
|
|
28
28
|
|
|
29
29
|
export class AgentFactory {
|
|
30
30
|
#container;
|
|
@@ -138,26 +138,63 @@ export class AgentFactory {
|
|
|
138
138
|
* 抽取 bootstrap orchestrator 中创建 ExplorationTracker / ContextWindow / source 的
|
|
139
139
|
* 通用逻辑,供 scanKnowledge 等系统场景共用完整的多轮 Agent 框架。
|
|
140
140
|
*
|
|
141
|
-
* bootstrap orchestrator
|
|
141
|
+
* 与 bootstrap orchestrator 保持一致的 MemoryCoordinator 管理模式:
|
|
142
|
+
* - 创建轻量级 MemoryCoordinator (无 PersistentMemory/SessionStore)
|
|
143
|
+
* - 通过 MC.createDimensionScope 创建并注册 ActiveContext
|
|
144
|
+
* - trace 从 MC.getActiveContext 获取 (统一生命周期管理)
|
|
145
|
+
* - memoryCoordinator 传入 strategyContext,供 reactLoop 每轮 buildDynamicMemoryPrompt
|
|
146
|
+
*
|
|
147
|
+
* 与 bootstrap orchestrator 对齐的关键字段:
|
|
148
|
+
* - activeContext: 与 trace 同一实例 — insightGateEvaluator 通过此字段
|
|
149
|
+
* 决定走 buildAnalysisArtifact (完整: findings/evidenceMap/negativeSignals)
|
|
150
|
+
* 还是 buildAnalysisReport (降级: 仅文本)
|
|
151
|
+
* - outputType: 'candidate' — 设置 quality_gate 的评判标准
|
|
152
|
+
* - dimId: 维度 ID — buildAnalysisArtifact 的 dimensionId 参数
|
|
153
|
+
*
|
|
154
|
+
* bootstrap orchestrator 不使用此方法(它还需要领域特定的 SessionStore / dimContext 等),
|
|
142
155
|
* 但引擎层基础设施是一致的。
|
|
143
156
|
*
|
|
144
157
|
* @param {Object} [opts]
|
|
145
158
|
* @param {Object} [opts.budget] — 预算覆盖 (透传给 ExplorationTracker)
|
|
146
159
|
* @param {string} [opts.trackerStrategy='analyst'] — tracker 策略名: 'analyst' | 'producer' | 'bootstrap'
|
|
147
|
-
* @
|
|
160
|
+
* @param {string} [opts.label='default'] — 作用域标签 (用于 scopeId 命名 + dimId)
|
|
161
|
+
* @param {string} [opts.lang] — 项目语言 (透传给 sharedState._projectLanguage)
|
|
162
|
+
* @returns {{ contextWindow: ContextWindow, tracker: ExplorationTracker, trace: import('./memory/ActiveContext.js').ActiveContext, activeContext: import('./memory/ActiveContext.js').ActiveContext, memoryCoordinator: MemoryCoordinator, outputType: string, dimId: string, sharedState: Object, source: string, scopeId: string }}
|
|
148
163
|
*/
|
|
149
|
-
buildSystemContext({ budget, trackerStrategy = 'analyst' } = {}) {
|
|
164
|
+
buildSystemContext({ budget, trackerStrategy = 'analyst', label = 'default', lang } = {}) {
|
|
165
|
+
// 创建轻量级 MemoryCoordinator (scan 场景无 PersistentMemory/SessionStore)
|
|
166
|
+
const mc = new MemoryCoordinator({ mode: 'bootstrap' });
|
|
167
|
+
const scopeId = `scan:${label}`;
|
|
168
|
+
mc.createDimensionScope(scopeId);
|
|
169
|
+
|
|
170
|
+
const activeContext = mc.getActiveContext(scopeId);
|
|
171
|
+
|
|
150
172
|
return {
|
|
151
173
|
contextWindow: this.createContextWindow({ isSystem: true }),
|
|
152
174
|
tracker: ExplorationTracker.resolve(
|
|
153
175
|
{ source: 'system', strategy: trackerStrategy },
|
|
154
176
|
budget || {},
|
|
155
177
|
),
|
|
178
|
+
// trace & activeContext 是同一个 ActiveContext 实例
|
|
179
|
+
// trace: AgentRuntime reactLoop 使用 (startRound/setThought/endRound)
|
|
180
|
+
// activeContext: insightGateEvaluator 检查此字段决定 artifact 路径
|
|
181
|
+
trace: activeContext,
|
|
182
|
+
activeContext,
|
|
183
|
+
memoryCoordinator: mc,
|
|
184
|
+
// outputType: bootstrap orchestrator 设为 'candidate'(insightGateEvaluator 的评判标准)
|
|
185
|
+
outputType: 'candidate',
|
|
186
|
+
// dimId: buildAnalysisArtifact 的 dimensionId 参数
|
|
187
|
+
dimId: label,
|
|
156
188
|
sharedState: {
|
|
157
189
|
submittedTitles: new Set(),
|
|
158
190
|
submittedPatterns: new Set(),
|
|
191
|
+
// G6: _projectLanguage — ToolExecutionPipeline 透传给工具 handler 上下文
|
|
192
|
+
_projectLanguage: lang || null,
|
|
193
|
+
// G7: _dimensionScopeId — ToolExecutionPipeline 透传给工具 handler (note_finding scope)
|
|
194
|
+
_dimensionScopeId: scopeId,
|
|
159
195
|
},
|
|
160
196
|
source: 'system',
|
|
197
|
+
scopeId,
|
|
161
198
|
};
|
|
162
199
|
}
|
|
163
200
|
|
|
@@ -209,15 +246,19 @@ export class AgentFactory {
|
|
|
209
246
|
// ─── 领域语义方法 (意图驱动, Agent 直接完成 AI 推理) ─────
|
|
210
247
|
|
|
211
248
|
/**
|
|
212
|
-
* 统一知识扫描 — 走 insight 管线 (Analyze → QualityGate → Produce)
|
|
249
|
+
* 统一知识扫描 — 走 insight 管线 (Analyze → QualityGate → Produce → RejectionGate)
|
|
250
|
+
*
|
|
251
|
+
* extract 和 summarize 共享工具驱动管线 (collect_scan_recipe),
|
|
252
|
+
* 仅 Produce 阶段的 systemPrompt 和预算不同:
|
|
253
|
+
* - extract: 多文件 target 扫描,24 iter analyze,24 iter produce
|
|
254
|
+
* - summarize: 单文件/代码片段,12 iter analyze,12 iter produce
|
|
213
255
|
*
|
|
214
|
-
*
|
|
215
|
-
* 替代: extractRecipes + summarizeCode + discoverRelations
|
|
256
|
+
* 关系发现请使用单独的 discoverRelations() 方法。
|
|
216
257
|
*
|
|
217
258
|
* @param {Object} opts
|
|
218
259
|
* @param {string} opts.label — 上下文标签(target 名 / 文件名)
|
|
219
260
|
* @param {Array<{name, content, language?}>} opts.files — 源文件
|
|
220
|
-
* @param {'extract'|'summarize'
|
|
261
|
+
* @param {'extract'|'summarize'} [opts.task='extract'] — 任务类型
|
|
221
262
|
* @param {string} [opts.lang] — 语言提示
|
|
222
263
|
* @param {boolean} [opts.comprehensive] — 深度扫描标志
|
|
223
264
|
* @returns {Promise<Object>} — task-specific JSON
|
|
@@ -229,60 +270,29 @@ export class AgentFactory {
|
|
|
229
270
|
}
|
|
230
271
|
const { producePrompt, fallback } = taskConfig;
|
|
231
272
|
|
|
232
|
-
//
|
|
233
|
-
const
|
|
234
|
-
|
|
235
|
-
|
|
236
|
-
|
|
237
|
-
//
|
|
238
|
-
|
|
239
|
-
const
|
|
240
|
-
|
|
241
|
-
|
|
242
|
-
|
|
243
|
-
|
|
244
|
-
|
|
245
|
-
|
|
246
|
-
|
|
247
|
-
name: 'analyze',
|
|
248
|
-
capabilities: caps,
|
|
249
|
-
budget: { maxIterations: analyzeMaxIter, maxTokens: 8192, temperature: 0.3 },
|
|
250
|
-
systemPrompt: ANALYST_SYSTEM_PROMPT,
|
|
251
|
-
},
|
|
252
|
-
{
|
|
253
|
-
name: 'produce',
|
|
254
|
-
capabilities: produceCaps,
|
|
255
|
-
budget: { maxIterations: 24, temperature: 0.2 },
|
|
256
|
-
systemPrompt: producePrompt,
|
|
257
|
-
promptTransform: (_input, prev) => {
|
|
258
|
-
const analysis = prev.analyze?.reply || '';
|
|
259
|
-
if (analysis.length >= 200) {
|
|
260
|
-
return `将以下代码分析转化为结构化输出。\n\n## 代码分析\n${analysis}`;
|
|
261
|
-
}
|
|
262
|
-
// Fallback: analyze reply 不足时直接提供源代码
|
|
263
|
-
const fileCtx = (files || []).slice(0, 15).map(f => {
|
|
264
|
-
const body = f.content.length > 1200
|
|
265
|
-
? f.content.slice(0, 1200) + '\n// ... (truncated)'
|
|
266
|
-
: f.content;
|
|
267
|
-
return `### ${f.relativePath}\n\`\`\`\n${body}\n\`\`\``;
|
|
268
|
-
}).join('\n\n');
|
|
269
|
-
const preamble = analysis
|
|
270
|
-
? `## 部分分析\n${analysis}\n\n`
|
|
271
|
-
: '';
|
|
272
|
-
return `${preamble}分析以下 ${files?.length || 0} 个源文件,提取知识 Recipe。\n\n${fileCtx}`;
|
|
273
|
-
},
|
|
274
|
-
},
|
|
275
|
-
];
|
|
273
|
+
// extract 和 summarize 都使用 code_analysis 分析 + scan_production 工具驱动
|
|
274
|
+
const analyzeCaps = ['code_analysis'];
|
|
275
|
+
const produceCaps = ['scan_production'];
|
|
276
|
+
|
|
277
|
+
// ── 统一 4 阶段 Pipeline (与冷启动 orchestrator 对齐) ──
|
|
278
|
+
// summarize (单文件) 使用较低预算
|
|
279
|
+
const analyzeMaxIter = task === 'summarize' ? 12 : 24;
|
|
280
|
+
const stages = buildScanPipelineStages({
|
|
281
|
+
task,
|
|
282
|
+
producePrompt,
|
|
283
|
+
analyzeCaps,
|
|
284
|
+
produceCaps,
|
|
285
|
+
files,
|
|
286
|
+
analyzeMaxIter,
|
|
287
|
+
});
|
|
276
288
|
|
|
277
289
|
// ── 创建 Runtime — 使用 insight preset + 对齐 policies ──
|
|
278
|
-
// policies override: 让 Runtime 级 BudgetPolicy 与 stage budget 对齐
|
|
279
|
-
// (避免 insight preset 的 maxIterations:24 与 stage 的 10 冲突)
|
|
280
290
|
const runtime = this.createRuntime(PresetName.INSIGHT, {
|
|
281
|
-
strategy: { type: 'pipeline', stages },
|
|
282
|
-
capabilities:
|
|
291
|
+
strategy: { type: 'pipeline', maxRetries: 1, stages },
|
|
292
|
+
capabilities: analyzeCaps,
|
|
283
293
|
policies: [
|
|
284
294
|
new BudgetPolicy({
|
|
285
|
-
maxIterations: 30, // 24 stage budget + 6 tracker grace
|
|
295
|
+
maxIterations: 30, // 24 stage budget + 6 tracker grace
|
|
286
296
|
maxTokens: 8192,
|
|
287
297
|
temperature: 0.3,
|
|
288
298
|
timeoutMs: 600_000,
|
|
@@ -295,20 +305,12 @@ export class AgentFactory {
|
|
|
295
305
|
runtime.setFileCache(files);
|
|
296
306
|
}
|
|
297
307
|
|
|
298
|
-
// ── 完整的系统级多轮基础设施 ──
|
|
299
|
-
// ExplorationTracker (analyst 策略) 提供:
|
|
300
|
-
// - 四阶段管理: SCAN → EXPLORE → VERIFY → SUMMARIZE
|
|
301
|
-
// - Nudge: budget_warning / convergence / reflection / force_exit
|
|
302
|
-
// - 阶段 toolChoice: SUMMARIZE='none' 强制 LLM 输出文本
|
|
303
|
-
// - Graceful exit: 终结阶段 2 轮 grace + 硬上限兜底
|
|
304
|
-
// ContextWindow 提供:
|
|
305
|
-
// - 自动三级递进上下文压缩
|
|
306
|
-
// source:'system' 提供:
|
|
307
|
-
// - 空响应自动重试 (2 次)
|
|
308
|
-
// - finalize 强制摘要兜底
|
|
308
|
+
// ── 完整的系统级多轮基础设施 (含 MemoryCoordinator 管理 ActiveContext) ──
|
|
309
309
|
const systemCtx = this.buildSystemContext({
|
|
310
310
|
budget: { maxIterations: analyzeMaxIter },
|
|
311
311
|
trackerStrategy: 'analyst',
|
|
312
|
+
label: `${task}:${label}`,
|
|
313
|
+
lang,
|
|
312
314
|
});
|
|
313
315
|
|
|
314
316
|
// ── 执行 ──
|
|
@@ -317,36 +319,79 @@ export class AgentFactory {
|
|
|
317
319
|
);
|
|
318
320
|
const result = await runtime.execute(message, { strategyContext: systemCtx });
|
|
319
321
|
|
|
320
|
-
// ── 提取结果 ──
|
|
321
|
-
|
|
322
|
-
|
|
323
|
-
|
|
324
|
-
|
|
325
|
-
|
|
326
|
-
|
|
327
|
-
|
|
328
|
-
|
|
329
|
-
|
|
330
|
-
|
|
331
|
-
|
|
332
|
-
|
|
333
|
-
|
|
334
|
-
|
|
335
|
-
|
|
336
|
-
|
|
337
|
-
return {
|
|
322
|
+
// ── 提取结果 — extract 和 summarize 统一从 toolCalls 提取 ──
|
|
323
|
+
const allToolCalls = result.toolCalls || [];
|
|
324
|
+
const recipes = allToolCalls
|
|
325
|
+
.filter(tc => (tc.tool || tc.name) === 'collect_scan_recipe')
|
|
326
|
+
.map(tc => {
|
|
327
|
+
const res = tc.result;
|
|
328
|
+
if (res && typeof res === 'object' && res.status === 'collected' && res.recipe) {
|
|
329
|
+
return res.recipe;
|
|
330
|
+
}
|
|
331
|
+
return null;
|
|
332
|
+
})
|
|
333
|
+
.filter(Boolean);
|
|
334
|
+
|
|
335
|
+
if (recipes.length > 0) {
|
|
336
|
+
// summarize 向后兼容: 扁平化首个 recipe 为 { title, summary, usageGuide, ... }
|
|
337
|
+
if (task === 'summarize') {
|
|
338
|
+
const first = recipes[0];
|
|
339
|
+
return {
|
|
340
|
+
title: first.title || '',
|
|
341
|
+
summary: first.description || first.summary || '',
|
|
342
|
+
usageGuide: first.usageGuide || '',
|
|
343
|
+
category: first.category || '',
|
|
344
|
+
headers: first.headers || [],
|
|
345
|
+
tags: first.tags || [],
|
|
346
|
+
trigger: first.trigger || '',
|
|
347
|
+
recipes,
|
|
348
|
+
extracted: recipes.length,
|
|
349
|
+
};
|
|
338
350
|
}
|
|
339
|
-
|
|
340
|
-
// Fallback: 工具未被调用时,尝试从文本解析
|
|
341
|
-
const produceReply = result.phases?.produce?.reply || result.reply;
|
|
342
|
-
return this.#parseJsonResponse(produceReply, fallback(label));
|
|
351
|
+
return { targetName: label, extracted: recipes.length, recipes };
|
|
343
352
|
}
|
|
344
353
|
|
|
345
|
-
//
|
|
354
|
+
// Fallback: 工具未被调用时,尝试从文本解析
|
|
346
355
|
const produceReply = result.phases?.produce?.reply || result.reply;
|
|
347
356
|
return this.#parseJsonResponse(produceReply, fallback(label));
|
|
348
357
|
}
|
|
349
358
|
|
|
359
|
+
/**
|
|
360
|
+
* 知识图谱关系发现 — 独立管线 (Explore → Synthesize)
|
|
361
|
+
*
|
|
362
|
+
* 与 scanKnowledge 不同,relations 不需要源文件输入,
|
|
363
|
+
* 而是通过查询知识库 + 读取源码发现知识条目间的语义关系。
|
|
364
|
+
*
|
|
365
|
+
* @param {Object} [opts]
|
|
366
|
+
* @param {number} [opts.batchSize=20] — 批次大小提示
|
|
367
|
+
* @returns {Promise<{ analyzed: number, relations: Array }>}
|
|
368
|
+
*/
|
|
369
|
+
async discoverRelations({ batchSize = 20 } = {}) {
|
|
370
|
+
const stages = buildRelationsPipelineStages();
|
|
371
|
+
|
|
372
|
+
const runtime = this.createRuntime(PresetName.INSIGHT, {
|
|
373
|
+
strategy: { type: 'pipeline', stages },
|
|
374
|
+
capabilities: ['knowledge_production', 'code_analysis'],
|
|
375
|
+
policies: [
|
|
376
|
+
new BudgetPolicy({
|
|
377
|
+
maxIterations: 28,
|
|
378
|
+
maxTokens: 8192,
|
|
379
|
+
temperature: 0.3,
|
|
380
|
+
timeoutMs: 420_000,
|
|
381
|
+
}),
|
|
382
|
+
],
|
|
383
|
+
memory: { enabled: false },
|
|
384
|
+
});
|
|
385
|
+
|
|
386
|
+
const message = AgentMessage.internal(
|
|
387
|
+
`探索知识库中所有知识条目之间的语义关系。每批分析约 ${batchSize} 条知识。`,
|
|
388
|
+
);
|
|
389
|
+
const result = await runtime.execute(message);
|
|
390
|
+
|
|
391
|
+
const synthesizeReply = result.phases?.synthesize?.reply || result.reply;
|
|
392
|
+
return this.#parseJsonResponse(synthesizeReply, { analyzed: 0, relations: [] });
|
|
393
|
+
}
|
|
394
|
+
|
|
350
395
|
/**
|
|
351
396
|
* AI 翻译 — chat 模式,单轮生成
|
|
352
397
|
*
|
|
@@ -334,7 +334,7 @@ export class AgentRuntime {
|
|
|
334
334
|
: this.capabilities;
|
|
335
335
|
|
|
336
336
|
// 构建基础系统提示词
|
|
337
|
-
|
|
337
|
+
let baseSystemPrompt = systemPromptOverride || this.#buildSystemPrompt(caps, context);
|
|
338
338
|
|
|
339
339
|
// 收集工具 (caps 为空数组时 = 明确无工具)
|
|
340
340
|
const allowedTools = this.#collectTools(caps);
|
|
@@ -361,6 +361,16 @@ export class AgentRuntime {
|
|
|
361
361
|
maxIterations: 20, maxTokens: 4096, temperature: 0.7,
|
|
362
362
|
};
|
|
363
363
|
|
|
364
|
+
// 系统源: 注入轮次预算 (先验锚定阶段节奏)
|
|
365
|
+
// 与旧版 ChatAgent 对齐: 60% 探索 → 80% 验证 → 最后 20% 输出总结
|
|
366
|
+
const isSystem = source === 'system';
|
|
367
|
+
if (isSystem && tracker && !baseSystemPrompt.includes('轮次预算')) {
|
|
368
|
+
const maxIter = budget.maxIterations || 24;
|
|
369
|
+
const exploreEnd = Math.floor(maxIter * 0.6);
|
|
370
|
+
const verifyEnd = Math.floor(maxIter * 0.8);
|
|
371
|
+
baseSystemPrompt += `\n\n## 轮次预算\n- 总轮次: **${maxIter} 轮**\n- 探索阶段: 第 1-${exploreEnd} 轮(搜索和结构化查询)\n- 验证阶段: 第 ${exploreEnd + 1}-${verifyEnd} 轮(读取关键文件确认细节)\n- 总结阶段: 第 ${verifyEnd + 1}-${maxIter} 轮(**停止工具调用,输出分析文本**)\n\n到达第 ${verifyEnd} 轮时你必须开始输出总结,不要继续搜索。`;
|
|
372
|
+
}
|
|
373
|
+
|
|
364
374
|
// 状态转移
|
|
365
375
|
this.#safeTransition('start', { prompt: prompt.slice(0, 100) });
|
|
366
376
|
this.#safeTransition('plan_ready');
|
|
@@ -534,10 +544,15 @@ export class AgentRuntime {
|
|
|
534
544
|
|
|
535
545
|
let llmResult;
|
|
536
546
|
try {
|
|
547
|
+
// toolChoice='none' 时不发送 toolSchemas —— 部分 LLM (Gemini) 在看到
|
|
548
|
+
// 工具定义但被禁止调用时会返回空内容,导致 SUMMARIZE 阶段失败
|
|
549
|
+
const effectiveToolSchemas = toolChoice === 'none'
|
|
550
|
+
? undefined
|
|
551
|
+
: (ctx.toolSchemas.length > 0 ? ctx.toolSchemas : undefined);
|
|
537
552
|
llmResult = await this.aiProvider.chatWithTools(effectivePrompt, {
|
|
538
553
|
messages: ctx.messages.toMessages(),
|
|
539
|
-
toolSchemas:
|
|
540
|
-
toolChoice,
|
|
554
|
+
toolSchemas: effectiveToolSchemas,
|
|
555
|
+
toolChoice: effectiveToolSchemas ? toolChoice : undefined,
|
|
541
556
|
systemPrompt: effectiveSystemPrompt,
|
|
542
557
|
temperature: ctx.budget.temperature ?? (ctx.isSystem ? 0.3 : 0.7),
|
|
543
558
|
maxTokens: ctx.budget.maxTokens ?? (ctx.isSystem ? 8192 : 4096),
|
|
@@ -563,6 +578,24 @@ export class AgentRuntime {
|
|
|
563
578
|
|
|
564
579
|
// 空响应重试
|
|
565
580
|
if (!llmResult.text && !llmResult.functionCalls?.length) {
|
|
581
|
+
// B4 fix: SUMMARIZE 阶段也允许重试 — force_exit nudge 刚注入时 LLM 可能
|
|
582
|
+
// 需要额外一轮才能生成有效输出。与 ExplorationTracker 的 2 轮 grace 对齐,
|
|
583
|
+
// 避免 grace 机制被架空。重试次数由 tracker.phaseRounds 控制而非独立计数。
|
|
584
|
+
const isTerminal = ctx.tracker && (ctx.tracker.phase === 'SUMMARIZE');
|
|
585
|
+
if (isTerminal) {
|
|
586
|
+
const phaseRounds = ctx.tracker.metrics?.phaseRounds ?? 0;
|
|
587
|
+
if (phaseRounds < 2) {
|
|
588
|
+
ctx.consecutiveEmptyResponses++;
|
|
589
|
+
this.logger.warn(
|
|
590
|
+
`[AgentRuntime] ⚠ empty response in SUMMARIZE — retrying (grace ${phaseRounds + 1}/2)`
|
|
591
|
+
);
|
|
592
|
+
// 不 rollbackTick: 让 tracker 计入 phaseRounds 以便到达 grace 上限退出
|
|
593
|
+
await new Promise(r => setTimeout(r, 1500));
|
|
594
|
+
return { __continue: true };
|
|
595
|
+
}
|
|
596
|
+
this.logger.warn('[AgentRuntime] ⚠ empty response in SUMMARIZE (grace exhausted) — proceeding to forced summary');
|
|
597
|
+
return null;
|
|
598
|
+
}
|
|
566
599
|
if (ctx.isSystem && ctx.consecutiveEmptyResponses < 2) {
|
|
567
600
|
ctx.consecutiveEmptyResponses++;
|
|
568
601
|
this.logger.warn(
|
|
@@ -843,6 +876,13 @@ export class AgentRuntime {
|
|
|
843
876
|
* @returns {Promise<Object>}
|
|
844
877
|
*/
|
|
845
878
|
async #finalize(ctx) {
|
|
879
|
+
// Scan pipeline: 所有结果在 toolCalls 中 (collect_scan_recipe),不需要文本回复
|
|
880
|
+
// 直接跳过 forced summary,避免浪费一次 LLM 调用
|
|
881
|
+
if (!ctx.lastReply && ctx.tracker?.submitToolName === 'collect_scan_recipe') {
|
|
882
|
+
const recipeCount = ctx.toolCalls.filter(tc => (tc.tool || tc.name) === 'collect_scan_recipe').length;
|
|
883
|
+
ctx.lastReply = `[scan complete: ${recipeCount} recipes collected]`;
|
|
884
|
+
}
|
|
885
|
+
|
|
846
886
|
// 强制摘要 (系统场景或 tracker 场景)
|
|
847
887
|
if (!ctx.lastReply && (ctx.tracker || ctx.isSystem)) {
|
|
848
888
|
const forcedResult = await produceForcedSummary({
|
|
@@ -239,11 +239,12 @@ export class KnowledgeProduction extends Capability {
|
|
|
239
239
|
}
|
|
240
240
|
|
|
241
241
|
get tools() {
|
|
242
|
+
// 与旧版 PRODUCER_TOOLS 保持一致: 仅 3 个核心工具
|
|
243
|
+
// guard_check_code / validate_candidate 不需要:提交时 UnifiedValidator 已自动校验
|
|
244
|
+
// 额外工具会分散 LLM 注意力,浪费 produce 轮次在验证而非提交上
|
|
242
245
|
return [
|
|
243
246
|
'submit_knowledge', 'submit_with_check',
|
|
244
247
|
'read_project_file', // 获取代码片段用于知识正文
|
|
245
|
-
'guard_check_code', // 提交前质量检查
|
|
246
|
-
'validate_candidate', // 校验候选质量
|
|
247
248
|
];
|
|
248
249
|
}
|
|
249
250
|
}
|