autosnippet 3.0.0 → 3.0.1
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/service/chat/ChatAgent.js +142 -21
- package/package.json +1 -1
|
@@ -939,7 +939,7 @@ export class ChatAgent {
|
|
|
939
939
|
// ── 循环退出: 产出 dimensionDigest 总结 ──
|
|
940
940
|
reasoning.afterRound();
|
|
941
941
|
const forcedResult = await this.#produceForcedSummary({
|
|
942
|
-
source, toolCalls, toolSchemas, ctx, phaseRouter, execStartTime,
|
|
942
|
+
source, toolCalls, toolSchemas, ctx, phaseRouter, execStartTime, prompt,
|
|
943
943
|
});
|
|
944
944
|
forcedResult.reasoningTrace = reasoning.trace;
|
|
945
945
|
forcedResult.reasoningQuality = reasoning.getQualityMetrics();
|
|
@@ -950,9 +950,10 @@ export class ChatAgent {
|
|
|
950
950
|
* 强制退出后的摘要生成 — 独立方法,避免主循环代码膨胀
|
|
951
951
|
* @private
|
|
952
952
|
*/
|
|
953
|
-
async #produceForcedSummary({ source, toolCalls, toolSchemas, ctx, phaseRouter, execStartTime }) {
|
|
953
|
+
async #produceForcedSummary({ source, toolCalls, toolSchemas, ctx, phaseRouter, execStartTime, prompt }) {
|
|
954
954
|
const iterations = phaseRouter?.totalIterations || 0;
|
|
955
|
-
|
|
955
|
+
const isSystem = source === 'system';
|
|
956
|
+
this.#logger.info(`[ChatAgent] ⚠ producing forced summary (${iterations} iters, ${toolCalls.length} calls, source=${source})`);
|
|
956
957
|
|
|
957
958
|
const candidateCount = toolCalls.filter(tc =>
|
|
958
959
|
tc.tool === 'submit_knowledge' || tc.tool === 'submit_with_check'
|
|
@@ -960,22 +961,30 @@ export class ChatAgent {
|
|
|
960
961
|
|
|
961
962
|
let finalReply;
|
|
962
963
|
|
|
963
|
-
// 如果熔断器已打开,跳过 AI
|
|
964
|
+
// 如果熔断器已打开,跳过 AI 调用直接合成摘要(避免无用的失败 + 计数累积)
|
|
964
965
|
const isCircuitOpen = this.#aiProvider._circuitState === 'OPEN';
|
|
965
966
|
if (isCircuitOpen) {
|
|
966
|
-
this.#logger.warn(`[ChatAgent] circuit breaker is OPEN — skipping AI summary, using synthetic digest`);
|
|
967
|
+
this.#logger.warn(`[ChatAgent] circuit breaker is OPEN — skipping AI summary, using synthetic ${isSystem ? 'digest' : 'summary'}`);
|
|
967
968
|
}
|
|
968
969
|
|
|
970
|
+
// ── 收集工具调用摘要(user / system 共用) ──
|
|
971
|
+
const submitSummary = toolCalls
|
|
972
|
+
.filter(tc => tc.tool === 'submit_knowledge' || tc.tool === 'submit_with_check')
|
|
973
|
+
.map((tc, i) => `${i + 1}. ${tc.params?.title || tc.params?.category || 'untitled'}`)
|
|
974
|
+
.join('\n');
|
|
975
|
+
|
|
976
|
+
// ── 收集工具调用上下文(user 源需要更丰富的上下文来生成自然语言总结) ──
|
|
977
|
+
const toolContextSummary = isSystem ? '' : this.#buildToolContextForUserSummary(toolCalls);
|
|
978
|
+
|
|
969
979
|
try {
|
|
970
|
-
if (isCircuitOpen) throw new Error('circuit open — skip to synthetic
|
|
980
|
+
if (isCircuitOpen) throw new Error('circuit open — skip to synthetic summary');
|
|
971
981
|
|
|
972
|
-
|
|
973
|
-
|
|
974
|
-
.map((tc, i) => `${i + 1}. ${tc.params?.title || tc.params?.category || 'untitled'}`)
|
|
975
|
-
.join('\n');
|
|
982
|
+
let summaryPrompt;
|
|
983
|
+
let systemPrompt;
|
|
976
984
|
|
|
977
|
-
|
|
978
|
-
|
|
985
|
+
if (isSystem) {
|
|
986
|
+
// ── system 源: 输出 dimensionDigest JSON(供 Bootstrap 管线消费) ──
|
|
987
|
+
summaryPrompt = `你已完成 ${iterations} 轮工具调用(共 ${toolCalls.length} 次),提交了 ${candidateCount} 个候选。
|
|
979
988
|
${submitSummary ? `已提交候选:\n${submitSummary}\n` : ''}
|
|
980
989
|
**必须**输出 dimensionDigest JSON(用 \`\`\`json 包裹):
|
|
981
990
|
\`\`\`json
|
|
@@ -992,8 +1001,25 @@ ${submitSummary ? `已提交候选:\n${submitSummary}\n` : ''}
|
|
|
992
1001
|
}
|
|
993
1002
|
}
|
|
994
1003
|
\`\`\`
|
|
995
|
-
> remainingTasks: 列出本次未来得及处理的信号/主题。已全部覆盖则留空 \`[]
|
|
996
|
-
|
|
1004
|
+
> remainingTasks: 列出本次未来得及处理的信号/主题。已全部覆盖则留空 \`[]\`。`;
|
|
1005
|
+
systemPrompt = '直接输出 dimensionDigest JSON 总结,不要调用工具。';
|
|
1006
|
+
} else {
|
|
1007
|
+
// ── user 源: 输出人类可读的 Markdown 结构化总结(前端 AI Chat 展示) ──
|
|
1008
|
+
const userQuestion = prompt ? `用户的原始问题:「${prompt.slice(0, 500)}」\n\n` : '';
|
|
1009
|
+
summaryPrompt = `${userQuestion}你刚才通过 ${toolCalls.length} 次工具调用分析了项目代码。以下是你调用过的工具和获取到的关键信息:
|
|
1010
|
+
|
|
1011
|
+
${toolContextSummary}
|
|
1012
|
+
|
|
1013
|
+
请基于以上收集到的信息,用**清晰易读的 Markdown** 格式撰写分析总结,直接回答用户的问题。
|
|
1014
|
+
|
|
1015
|
+
要求:
|
|
1016
|
+
- 使用二级/三级标题组织内容
|
|
1017
|
+
- 要有具体的代码文件路径、类名、模式名称等细节
|
|
1018
|
+
- 关键发现用列表项罗列
|
|
1019
|
+
- 如果发现了架构模式或最佳实践,用简短代码块举例
|
|
1020
|
+
- 语言自然流畅,像一份技术分析报告`;
|
|
1021
|
+
systemPrompt = '你是项目分析助手。请用纯 Markdown 格式输出结构清晰的分析总结,只输出人类可读的自然语言文档,不要输出 JSON 格式的数据。';
|
|
1022
|
+
}
|
|
997
1023
|
|
|
998
1024
|
// 用空 messages 避免累积上下文导致 400
|
|
999
1025
|
const summaryResult = await this.#aiProvider.chatWithTools(
|
|
@@ -1002,8 +1028,8 @@ ${submitSummary ? `已提交候选:\n${submitSummary}\n` : ''}
|
|
|
1002
1028
|
messages: [],
|
|
1003
1029
|
toolSchemas,
|
|
1004
1030
|
toolChoice: 'none',
|
|
1005
|
-
systemPrompt
|
|
1006
|
-
temperature: 0.3,
|
|
1031
|
+
systemPrompt,
|
|
1032
|
+
temperature: isSystem ? 0.3 : 0.5,
|
|
1007
1033
|
maxTokens: 8192,
|
|
1008
1034
|
},
|
|
1009
1035
|
);
|
|
@@ -1015,11 +1041,13 @@ ${submitSummary ? `已提交候选:\n${submitSummary}\n` : ''}
|
|
|
1015
1041
|
finalReply = this.#cleanFinalAnswer(summaryResult.text || '');
|
|
1016
1042
|
} catch (err) {
|
|
1017
1043
|
this.#logger.warn(`[ChatAgent] forced summary AI call failed: ${err.message}`);
|
|
1018
|
-
|
|
1019
|
-
|
|
1020
|
-
|
|
1021
|
-
|
|
1022
|
-
|
|
1044
|
+
|
|
1045
|
+
if (isSystem) {
|
|
1046
|
+
// ── system 源兜底: 合成 dimensionDigest JSON ──
|
|
1047
|
+
const titles = toolCalls
|
|
1048
|
+
.filter(tc => tc.tool === 'submit_knowledge' || tc.tool === 'submit_with_check')
|
|
1049
|
+
.map(tc => tc.params?.title || 'untitled');
|
|
1050
|
+
finalReply = `\`\`\`json
|
|
1023
1051
|
{
|
|
1024
1052
|
"dimensionDigest": {
|
|
1025
1053
|
"summary": "通过 ${toolCalls.length} 次工具调用分析了项目代码,提交了 ${candidateCount} 个候选。",
|
|
@@ -1030,6 +1058,33 @@ ${submitSummary ? `已提交候选:\n${submitSummary}\n` : ''}
|
|
|
1030
1058
|
}
|
|
1031
1059
|
}
|
|
1032
1060
|
\`\`\``;
|
|
1061
|
+
} else {
|
|
1062
|
+
// ── user 源兜底: 合成人类可读的 Markdown 摘要 ──
|
|
1063
|
+
const toolNames = [...new Set(toolCalls.map(tc => tc.tool))];
|
|
1064
|
+
const filesRead = toolCalls
|
|
1065
|
+
.filter(tc => tc.tool === 'read_project_file')
|
|
1066
|
+
.flatMap(tc => {
|
|
1067
|
+
if (tc.params?.filePaths) return tc.params.filePaths;
|
|
1068
|
+
if (tc.params?.filePath) return [tc.params.filePath];
|
|
1069
|
+
return [];
|
|
1070
|
+
})
|
|
1071
|
+
.slice(0, 10);
|
|
1072
|
+
const searches = toolCalls
|
|
1073
|
+
.filter(tc => tc.tool === 'search_project_code' || tc.tool === 'semantic_search_code')
|
|
1074
|
+
.map(tc => tc.params?.patterns?.[0] || tc.params?.query || tc.params?.pattern)
|
|
1075
|
+
.filter(Boolean)
|
|
1076
|
+
.slice(0, 5);
|
|
1077
|
+
|
|
1078
|
+
finalReply = `## 分析总结\n\n通过 **${toolCalls.length} 次工具调用**探索了项目代码。\n\n`;
|
|
1079
|
+
if (searches.length > 0) {
|
|
1080
|
+
finalReply += `### 搜索的关键词\n${searches.map(s => `- \`${s}\``).join('\n')}\n\n`;
|
|
1081
|
+
}
|
|
1082
|
+
if (filesRead.length > 0) {
|
|
1083
|
+
finalReply += `### 读取的文件\n${filesRead.map(f => `- \`${f}\``).join('\n')}\n\n`;
|
|
1084
|
+
}
|
|
1085
|
+
finalReply += `### 使用的工具\n${toolNames.map(t => `- ${t}`).join('\n')}\n\n`;
|
|
1086
|
+
finalReply += `> ⚠️ AI 服务异常,未能生成完整分析。请稍后重试或缩小分析范围。`;
|
|
1087
|
+
}
|
|
1033
1088
|
}
|
|
1034
1089
|
|
|
1035
1090
|
const totalDuration = Date.now() - execStartTime;
|
|
@@ -1037,6 +1092,72 @@ ${submitSummary ? `已提交候选:\n${submitSummary}\n` : ''}
|
|
|
1037
1092
|
return { reply: finalReply, toolCalls, hasContext: toolCalls.length > 0 };
|
|
1038
1093
|
}
|
|
1039
1094
|
|
|
1095
|
+
/**
|
|
1096
|
+
* 从工具调用记录中提取上下文摘要(供 user 源强制总结使用)
|
|
1097
|
+
* @private
|
|
1098
|
+
*/
|
|
1099
|
+
#buildToolContextForUserSummary(toolCalls) {
|
|
1100
|
+
const sections = [];
|
|
1101
|
+
|
|
1102
|
+
// 目录结构探索
|
|
1103
|
+
const structureCalls = toolCalls.filter(tc => tc.tool === 'list_project_structure');
|
|
1104
|
+
if (structureCalls.length > 0) {
|
|
1105
|
+
const dirs = structureCalls.map(tc => tc.params?.directory || '/').slice(0, 5);
|
|
1106
|
+
sections.push(`**目录探索**: ${dirs.map(d => `\`${d}\``).join(', ')}`);
|
|
1107
|
+
}
|
|
1108
|
+
|
|
1109
|
+
// 项目概况
|
|
1110
|
+
const overviewCalls = toolCalls.filter(tc => tc.tool === 'get_project_overview');
|
|
1111
|
+
if (overviewCalls.length > 0) {
|
|
1112
|
+
sections.push('**项目概况**: 已获取');
|
|
1113
|
+
}
|
|
1114
|
+
|
|
1115
|
+
// 代码搜索
|
|
1116
|
+
const searchCalls = toolCalls.filter(tc => tc.tool === 'search_project_code' || tc.tool === 'semantic_search_code');
|
|
1117
|
+
if (searchCalls.length > 0) {
|
|
1118
|
+
const queries = searchCalls
|
|
1119
|
+
.map(tc => tc.params?.patterns?.[0] || tc.params?.query || tc.params?.pattern)
|
|
1120
|
+
.filter(Boolean)
|
|
1121
|
+
.slice(0, 8);
|
|
1122
|
+
sections.push(`**代码搜索** (${searchCalls.length} 次): ${queries.map(q => `\`${q}\``).join(', ')}`);
|
|
1123
|
+
}
|
|
1124
|
+
|
|
1125
|
+
// 文件读取
|
|
1126
|
+
const readCalls = toolCalls.filter(tc => tc.tool === 'read_project_file');
|
|
1127
|
+
if (readCalls.length > 0) {
|
|
1128
|
+
const files = readCalls
|
|
1129
|
+
.flatMap(tc => {
|
|
1130
|
+
if (tc.params?.filePaths) return tc.params.filePaths;
|
|
1131
|
+
if (tc.params?.filePath) return [tc.params.filePath];
|
|
1132
|
+
return [];
|
|
1133
|
+
})
|
|
1134
|
+
.slice(0, 10);
|
|
1135
|
+
sections.push(`**文件读取** (${readCalls.length} 次): ${files.map(f => `\`${f}\``).join(', ')}`);
|
|
1136
|
+
}
|
|
1137
|
+
|
|
1138
|
+
// AST 分析
|
|
1139
|
+
const astCalls = toolCalls.filter(tc =>
|
|
1140
|
+
['get_class_hierarchy', 'get_class_info', 'get_protocol_info', 'get_method_overrides', 'get_category_map'].includes(tc.tool)
|
|
1141
|
+
);
|
|
1142
|
+
if (astCalls.length > 0) {
|
|
1143
|
+
const entities = astCalls
|
|
1144
|
+
.map(tc => tc.params?.className || tc.params?.name || tc.params?.protocolName || tc.params?.rootClass)
|
|
1145
|
+
.filter(Boolean)
|
|
1146
|
+
.slice(0, 5);
|
|
1147
|
+
sections.push(`**AST 结构分析** (${astCalls.length} 次): ${entities.map(e => `\`${e}\``).join(', ')}`);
|
|
1148
|
+
}
|
|
1149
|
+
|
|
1150
|
+
// 知识库搜索
|
|
1151
|
+
const kbCalls = toolCalls.filter(tc =>
|
|
1152
|
+
['search_knowledge', 'search_recipes', 'knowledge_overview'].includes(tc.tool)
|
|
1153
|
+
);
|
|
1154
|
+
if (kbCalls.length > 0) {
|
|
1155
|
+
sections.push(`**知识库查询**: ${kbCalls.length} 次`);
|
|
1156
|
+
}
|
|
1157
|
+
|
|
1158
|
+
return sections.length > 0 ? sections.join('\n') : '(工具调用记录为空)';
|
|
1159
|
+
}
|
|
1160
|
+
|
|
1040
1161
|
// ─── Text Parsing 已移除 (v5.0) ────────────────────────
|
|
1041
1162
|
// 所有 Provider 统一走 chatWithTools() 原生函数调用路径。
|
|
1042
1163
|
// 不支持 native tool calling 的 Provider 在基类 chatWithTools()
|