autosnippet 2.8.3 → 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 +5 -5
- package/bin/cli.js +5 -33
- package/config/constitution.yaml +9 -2
- package/dashboard/dist/assets/{icons-B_Xg4B-s.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 +9 -27
- package/lib/core/ast/ProjectGraph.js +160 -0
- 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/ai/providers/GoogleGeminiProvider.js +12 -3
- package/lib/external/mcp/McpServer.js +7 -5
- package/lib/external/mcp/handlers/bootstrap/pipeline/orchestrator.js +3 -2
- package/lib/external/mcp/handlers/bootstrap.js +121 -12
- 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/skill.js +4 -2
- 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/middleware/requestLogger.js +3 -3
- package/lib/http/routes/ai.js +17 -1
- 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/http/routes/skills.js +44 -1
- package/lib/infrastructure/cache/GraphCache.js +143 -0
- package/lib/infrastructure/database/migrations/015_create_token_usage.js +27 -0
- package/lib/infrastructure/database/migrations/016_unified_knowledge_entries.js +395 -0
- package/lib/infrastructure/external/XcodeAutomation.js +187 -103
- package/lib/infrastructure/realtime/RealtimeService.js +14 -2
- package/lib/injection/ServiceContainer.js +164 -63
- package/lib/repository/knowledge/KnowledgeRepository.impl.js +373 -0
- package/lib/repository/token/TokenUsageStore.js +162 -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/AnalystAgent.js +25 -14
- package/lib/service/chat/CandidateGuardrail.js +1 -1
- package/lib/service/chat/ChatAgent.js +280 -48
- package/lib/service/chat/ContextWindow.js +92 -8
- package/lib/service/chat/HandoffProtocol.js +26 -1
- package/lib/service/chat/ProducerAgent.js +11 -9
- package/lib/service/chat/tools.js +298 -194
- 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 +15 -9
- package/lib/service/skills/SkillAdvisor.js +13 -11
- package/lib/service/snippet/SnippetFactory.js +5 -5
- package/lib/service/spm/SpmService.js +119 -18
- 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-CkIih2CC.css +0 -1
- package/dashboard/dist/assets/index-Duc8Qk-c.js +0 -197
- 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 -973
- package/lib/service/recipe/RecipeFileWriter.js +0 -514
- package/lib/service/recipe/RecipeService.js +0 -786
- package/lib/service/recipe/RecipeStatsTracker.js +0 -148
|
@@ -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,19 +393,33 @@ 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
|
}
|
|
401
401
|
|
|
402
|
-
// search_project_code: 限制匹配数 +
|
|
402
|
+
// search_project_code: 限制匹配数 + 截断上下文(支持批量模式)
|
|
403
403
|
if (toolName === 'search_project_code') {
|
|
404
|
+
if (result && typeof result === 'object' && result.batchResults) {
|
|
405
|
+
// 批量模式:对每个 pattern 的结果独立限制(直接操作对象,避免 stringify→parse 往返)
|
|
406
|
+
const limited = { ...result };
|
|
407
|
+
const perKeyChars = Math.floor(maxChars / Object.keys(limited.batchResults).length);
|
|
408
|
+
for (const [key, sub] of Object.entries(limited.batchResults)) {
|
|
409
|
+
limited.batchResults[key] = limitSearchResultObj(sub, Math.min(maxMatches, 3), perKeyChars);
|
|
410
|
+
}
|
|
411
|
+
const raw = JSON.stringify(limited);
|
|
412
|
+
return raw.length > maxChars ? raw.substring(0, maxChars) + '\n... [batch truncated]' : raw;
|
|
413
|
+
}
|
|
404
414
|
return limitSearchResult(result, maxMatches, maxChars);
|
|
405
415
|
}
|
|
406
416
|
|
|
407
|
-
// read_project_file:
|
|
417
|
+
// read_project_file: 限制字符数(支持批量模式)
|
|
408
418
|
if (toolName === 'read_project_file') {
|
|
419
|
+
if (result && typeof result === 'object' && result.batchResults) {
|
|
420
|
+
const raw = JSON.stringify(result);
|
|
421
|
+
return raw.length > maxChars ? raw.substring(0, maxChars) + '\n... [batch truncated]' : raw;
|
|
422
|
+
}
|
|
409
423
|
return limitFileContent(result, maxChars);
|
|
410
424
|
}
|
|
411
425
|
|
|
@@ -465,6 +479,41 @@ function limitSearchResult(result, maxMatches, maxChars) {
|
|
|
465
479
|
return str;
|
|
466
480
|
}
|
|
467
481
|
|
|
482
|
+
/**
|
|
483
|
+
* 限制搜索结果(返回对象) — 用于批量模式,避免 JSON.stringify → JSON.parse 往返
|
|
484
|
+
* 当源码含控制字符时,stringify→substring 截断会破坏 JSON 结构导致 parse 失败
|
|
485
|
+
*/
|
|
486
|
+
function limitSearchResultObj(result, maxMatches, maxChars) {
|
|
487
|
+
if (!result || typeof result !== 'object') return result || {};
|
|
488
|
+
if (typeof result === 'string') return { _raw: result.substring(0, maxChars) };
|
|
489
|
+
|
|
490
|
+
const limited = { ...result };
|
|
491
|
+
if (Array.isArray(limited.matches)) {
|
|
492
|
+
limited.matches = limited.matches.slice(0, maxMatches).map(m => {
|
|
493
|
+
const copy = { ...m };
|
|
494
|
+
if (copy.context && typeof copy.context === 'string') {
|
|
495
|
+
const contextLines = copy.context.split('\n');
|
|
496
|
+
if (contextLines.length > 7) {
|
|
497
|
+
copy.context = contextLines.slice(0, 7).join('\n') + '\n... [truncated]';
|
|
498
|
+
}
|
|
499
|
+
// 按字符上限截断 context(防止单个代码块过大)
|
|
500
|
+
if (copy.context.length > 500) {
|
|
501
|
+
copy.context = copy.context.substring(0, 500) + '\n... [truncated]';
|
|
502
|
+
}
|
|
503
|
+
}
|
|
504
|
+
if (Array.isArray(copy.lines) && copy.lines.length > 5) {
|
|
505
|
+
copy.lines = copy.lines.slice(0, 5);
|
|
506
|
+
copy._truncated = true;
|
|
507
|
+
}
|
|
508
|
+
return copy;
|
|
509
|
+
});
|
|
510
|
+
if (result.matches.length > maxMatches) {
|
|
511
|
+
limited._note = `Showing ${maxMatches} of ${result.matches.length} matches`;
|
|
512
|
+
}
|
|
513
|
+
}
|
|
514
|
+
return limited;
|
|
515
|
+
}
|
|
516
|
+
|
|
468
517
|
/**
|
|
469
518
|
* 限制文件内容 — 截断 content 字段
|
|
470
519
|
*/
|
|
@@ -526,6 +575,9 @@ export class PhaseRouter {
|
|
|
526
575
|
/** @type {Object} 日志器 */
|
|
527
576
|
#logger;
|
|
528
577
|
|
|
578
|
+
/** @type {boolean} 是否因 maxIterations 强制进入 SUMMARIZE */
|
|
579
|
+
#forcedSummarize = false;
|
|
580
|
+
|
|
529
581
|
/**
|
|
530
582
|
* @param {Object} budget — 预算配置
|
|
531
583
|
* @param {boolean} isSkillOnly — 是否为 skill-only 维度
|
|
@@ -561,6 +613,19 @@ export class PhaseRouter {
|
|
|
561
613
|
return this.#totalSubmits;
|
|
562
614
|
}
|
|
563
615
|
|
|
616
|
+
/**
|
|
617
|
+
* 是否因 maxIterations 触顶而强制进入 SUMMARIZE(一次性标记)
|
|
618
|
+
* 调用后自动复位
|
|
619
|
+
* @returns {boolean}
|
|
620
|
+
*/
|
|
621
|
+
consumeForcedSummarize() {
|
|
622
|
+
if (this.#forcedSummarize) {
|
|
623
|
+
this.#forcedSummarize = false;
|
|
624
|
+
return true;
|
|
625
|
+
}
|
|
626
|
+
return false;
|
|
627
|
+
}
|
|
628
|
+
|
|
564
629
|
/**
|
|
565
630
|
* 获取当前阶段的 toolChoice
|
|
566
631
|
* @returns {'required'|'auto'|'none'}
|
|
@@ -585,8 +650,21 @@ export class PhaseRouter {
|
|
|
585
650
|
* @returns {boolean}
|
|
586
651
|
*/
|
|
587
652
|
shouldExit() {
|
|
588
|
-
|
|
653
|
+
// 已在 SUMMARIZE 阶段 → 给 2 轮输出总结后退出
|
|
589
654
|
if (this.#phase === 'SUMMARIZE' && this.#phaseRounds >= 2) return true;
|
|
655
|
+
|
|
656
|
+
// 达到 maxIterations → 不硬退出,而是强制转入 SUMMARIZE 让 AI 在完整上下文中收尾
|
|
657
|
+
if (this.#totalIterations >= this.#budget.maxIterations && this.#phase !== 'SUMMARIZE') {
|
|
658
|
+
this.#logger.info(`[PhaseRouter] maxIterations reached (${this.#totalIterations}/${this.#budget.maxIterations}), forcing → SUMMARIZE for graceful exit`);
|
|
659
|
+
this.#forcedSummarize = true;
|
|
660
|
+
this.#transitionTo('SUMMARIZE');
|
|
661
|
+
// 返回 false 让主循环继续运行 SUMMARIZE 阶段的 2 轮收尾
|
|
662
|
+
return false;
|
|
663
|
+
}
|
|
664
|
+
|
|
665
|
+
// SUMMARIZE 阶段超限兜底(maxIterations + 2 轮 grace)
|
|
666
|
+
if (this.#totalIterations >= this.#budget.maxIterations + 2) return true;
|
|
667
|
+
|
|
590
668
|
return false;
|
|
591
669
|
}
|
|
592
670
|
|
|
@@ -688,6 +766,12 @@ export class PhaseRouter {
|
|
|
688
766
|
* @returns {string|null}
|
|
689
767
|
*/
|
|
690
768
|
getPhaseHint() {
|
|
769
|
+
// 接近 maxIterations 上限时,无论处于哪个阶段都警告 AI
|
|
770
|
+
const remaining = this.#budget.maxIterations - this.#totalIterations;
|
|
771
|
+
if (remaining <= 2 && remaining > 0 && this.#phase !== 'SUMMARIZE') {
|
|
772
|
+
return `⚠️ 仅剩 ${remaining} 轮次即达上限,请尽快完成当前工作并准备输出总结。`;
|
|
773
|
+
}
|
|
774
|
+
|
|
691
775
|
switch (this.#phase) {
|
|
692
776
|
case 'EXPLORE':
|
|
693
777
|
if (this.#phaseRounds >= this.#budget.searchBudget - 2) {
|
|
@@ -699,7 +783,7 @@ export class PhaseRouter {
|
|
|
699
783
|
if (this.#totalSubmits === 0 && this.#phaseRounds >= 1) {
|
|
700
784
|
return this.#isSkillOnly
|
|
701
785
|
? '你已收集足够信息,请在回复中直接输出 dimensionDigest JSON。'
|
|
702
|
-
: '⚠️ 探索阶段已结束。你已收集了足够的项目信息,请 **立即** 调用
|
|
786
|
+
: '⚠️ 探索阶段已结束。你已收集了足够的项目信息,请 **立即** 调用 submit_knowledge 提交候选。不要继续搜索,直接提交。';
|
|
703
787
|
}
|
|
704
788
|
if (this.#totalSubmits >= this.#budget.softSubmitLimit && this.#budget.softSubmitLimit > 0) {
|
|
705
789
|
const remaining = this.#budget.maxSubmits - this.#totalSubmits;
|
|
@@ -13,6 +13,31 @@
|
|
|
13
13
|
// AnalysisReport 构建
|
|
14
14
|
// ──────────────────────────────────────────────────────────────────
|
|
15
15
|
|
|
16
|
+
/**
|
|
17
|
+
* 清理 Analyst 分析文本中可能泄漏的系统 nudge / graceful exit 指令。
|
|
18
|
+
* 这些内容如果传给 Producer,会干扰其正常工作流。
|
|
19
|
+
*/
|
|
20
|
+
function sanitizeAnalysisText(text) {
|
|
21
|
+
if (!text) return '';
|
|
22
|
+
// 移除 graceful exit nudge 及 digest 模板指令
|
|
23
|
+
const patterns = [
|
|
24
|
+
/\*{0,2}⚠️?\s*(?:你已使用|轮次即将耗尽|仅剩|请立即停止|必须立即结束)[^\n]*\n?/gi,
|
|
25
|
+
/\*{0,2}请立即停止所有工具调用[^\n]*\*{0,2}\n?/gi,
|
|
26
|
+
/请在回复中直接输出\s*dimensionDigest\s*JSON[^\n]*\n?/gi,
|
|
27
|
+
/> ?(?:remainingTasks|如果所有信号都已覆盖)[^\n]*\n?/gi,
|
|
28
|
+
/> ?⚠️ 严禁输出任何非 JSON 内容[^\n]*\n?/gi,
|
|
29
|
+
// 移除 AI 回显的 dimensionDigest JSON 块(对 Producer 无价值且会干扰)
|
|
30
|
+
/```json\s*\n\s*\{\s*"dimensionDigest"\s*:[\s\S]*?\n```/g,
|
|
31
|
+
];
|
|
32
|
+
let cleaned = text;
|
|
33
|
+
for (const pat of patterns) {
|
|
34
|
+
cleaned = cleaned.replace(pat, '');
|
|
35
|
+
}
|
|
36
|
+
// 移除可能残留的空行堆积
|
|
37
|
+
cleaned = cleaned.replace(/\n{3,}/g, '\n\n').trim();
|
|
38
|
+
return cleaned;
|
|
39
|
+
}
|
|
40
|
+
|
|
16
41
|
/**
|
|
17
42
|
* 从 Analyst 的执行结果构建 AnalysisReport
|
|
18
43
|
*
|
|
@@ -73,7 +98,7 @@ export function buildAnalysisReport(analystResult, dimensionId, projectGraph = n
|
|
|
73
98
|
}
|
|
74
99
|
|
|
75
100
|
// 从分析文本中提取文件路径
|
|
76
|
-
const text = analystResult.reply || '';
|
|
101
|
+
const text = sanitizeAnalysisText(analystResult.reply || '');
|
|
77
102
|
const textFileRefs = text.match(/[\w/.-]+\.[mhswift]+/g);
|
|
78
103
|
if (textFileRefs) {
|
|
79
104
|
for (const f of textFileRefs) {
|
|
@@ -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. 有清晰的标题 (描述知识点的核心,使用项目真实类名)
|
|
@@ -31,12 +31,14 @@ const PRODUCER_SYSTEM_PROMPT = `你是知识管理专家。你会收到一段代
|
|
|
31
31
|
|
|
32
32
|
工作流程:
|
|
33
33
|
1. 阅读分析文本,识别每个独立的知识点/发现
|
|
34
|
-
2.
|
|
35
|
-
|
|
34
|
+
2. 用 read_project_file 批量获取关键代码片段:
|
|
35
|
+
read_project_file({ filePaths: ["FileA.m", "FileB.m"], maxLines: 80 })
|
|
36
|
+
3. 立刻调用 submit_knowledge 提交
|
|
36
37
|
4. 重复直到分析中的所有知识点都已提交
|
|
37
38
|
|
|
38
39
|
关键规则:
|
|
39
40
|
- 分析中的每个要点/段落都应转化为至少一个候选
|
|
41
|
+
- read_project_file 支持 filePaths 数组批量读取多个文件,一次调用完成
|
|
40
42
|
- read_project_file 时读取足够多的行数(startLine + maxLines 至少 30 行)
|
|
41
43
|
- reasoning.sources 必须是非空数组,填写相关文件路径如 ["FileName.m"]
|
|
42
44
|
- 如果分析提到了 3 个模式,就应该提交 3 个候选,不要合并
|
|
@@ -53,7 +55,7 @@ const PRODUCER_SYSTEM_PROMPT = `你是知识管理专家。你会收到一段代
|
|
|
53
55
|
// ──────────────────────────────────────────────────────────────────
|
|
54
56
|
|
|
55
57
|
const PRODUCER_TOOLS = [
|
|
56
|
-
'
|
|
58
|
+
'submit_knowledge',
|
|
57
59
|
'submit_with_check',
|
|
58
60
|
'read_project_file',
|
|
59
61
|
];
|
|
@@ -63,7 +65,7 @@ const PRODUCER_TOOLS = [
|
|
|
63
65
|
// ──────────────────────────────────────────────────────────────────
|
|
64
66
|
|
|
65
67
|
const PRODUCER_BUDGET = {
|
|
66
|
-
maxIterations:
|
|
68
|
+
maxIterations: 24, // was 18 — 与 Analyst 统一
|
|
67
69
|
searchBudget: 4,
|
|
68
70
|
searchBudgetGrace: 3,
|
|
69
71
|
maxSubmits: 10,
|
|
@@ -77,7 +79,7 @@ const PRODUCER_BUDGET = {
|
|
|
77
79
|
|
|
78
80
|
const STYLE_GUIDE = `# 「项目特写」写作要求
|
|
79
81
|
|
|
80
|
-
|
|
82
|
+
submit_knowledge 的 code 字段必须是「项目特写」。
|
|
81
83
|
|
|
82
84
|
## 什么是「项目特写」
|
|
83
85
|
将一种技术的**基本用法**与**本项目的具体特征**融合为一体。
|
|
@@ -205,7 +207,7 @@ export class ProducerAgent {
|
|
|
205
207
|
|
|
206
208
|
// 统计提交 (区分成功/失败)
|
|
207
209
|
const submitCalls = (result.toolCalls || []).filter(
|
|
208
|
-
tc => (tc.tool || tc.name) === '
|
|
210
|
+
tc => (tc.tool || tc.name) === 'submit_knowledge' || (tc.tool || tc.name) === 'submit_with_check'
|
|
209
211
|
);
|
|
210
212
|
const successCount = submitCalls.filter(tc => {
|
|
211
213
|
const res = tc.result;
|
|
@@ -236,7 +238,7 @@ export class ProducerAgent {
|
|
|
236
238
|
});
|
|
237
239
|
|
|
238
240
|
const retrySubmits = (retryResult.toolCalls || []).filter(
|
|
239
|
-
tc => (tc.tool || tc.name) === '
|
|
241
|
+
tc => (tc.tool || tc.name) === 'submit_knowledge' || (tc.tool || tc.name) === 'submit_with_check'
|
|
240
242
|
);
|
|
241
243
|
const retrySuccess = retrySubmits.filter(tc => {
|
|
242
244
|
const res = tc.result;
|