@cicctencent/agent-core 0.1.99 → 0.1.101
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/dist/cjs/agent/engine.d.ts +11 -0
- package/dist/cjs/agent/engine.d.ts.map +1 -1
- package/dist/cjs/agent/engine.js +191 -29
- package/dist/cjs/agent/engine.js.map +1 -1
- package/dist/cjs/agent/history-manager.d.ts +21 -0
- package/dist/cjs/agent/history-manager.d.ts.map +1 -1
- package/dist/cjs/agent/history-manager.js +72 -0
- package/dist/cjs/agent/history-manager.js.map +1 -1
- package/dist/cjs/agent/llm-caller.d.ts +5 -7
- package/dist/cjs/agent/llm-caller.d.ts.map +1 -1
- package/dist/cjs/agent/llm-caller.js +110 -20
- package/dist/cjs/agent/llm-caller.js.map +1 -1
- package/dist/cjs/agent/tool-executor.d.ts +6 -3
- package/dist/cjs/agent/tool-executor.d.ts.map +1 -1
- package/dist/cjs/agent/tool-executor.js +95 -26
- package/dist/cjs/agent/tool-executor.js.map +1 -1
- package/dist/cjs/agent/types.d.ts +5 -0
- package/dist/cjs/agent/types.d.ts.map +1 -1
- package/dist/cjs/agent/types.js.map +1 -1
- package/dist/cjs/hooks/types.d.ts +6 -1
- package/dist/cjs/hooks/types.d.ts.map +1 -1
- package/dist/cjs/observability/middleware.d.ts.map +1 -1
- package/dist/cjs/observability/middleware.js +63 -1
- package/dist/cjs/observability/middleware.js.map +1 -1
- package/dist/cjs/observability/sink.d.ts +4 -0
- package/dist/cjs/observability/sink.d.ts.map +1 -1
- package/dist/cjs/observability/sink.js +26 -3
- package/dist/cjs/observability/sink.js.map +1 -1
- package/dist/esm/agent/engine.d.ts +11 -0
- package/dist/esm/agent/engine.d.ts.map +1 -1
- package/dist/esm/agent/engine.js +191 -29
- package/dist/esm/agent/engine.js.map +1 -1
- package/dist/esm/agent/history-manager.d.ts +21 -0
- package/dist/esm/agent/history-manager.d.ts.map +1 -1
- package/dist/esm/agent/history-manager.js +72 -0
- package/dist/esm/agent/history-manager.js.map +1 -1
- package/dist/esm/agent/llm-caller.d.ts +5 -7
- package/dist/esm/agent/llm-caller.d.ts.map +1 -1
- package/dist/esm/agent/llm-caller.js +110 -20
- package/dist/esm/agent/llm-caller.js.map +1 -1
- package/dist/esm/agent/tool-executor.d.ts +6 -3
- package/dist/esm/agent/tool-executor.d.ts.map +1 -1
- package/dist/esm/agent/tool-executor.js +95 -26
- package/dist/esm/agent/tool-executor.js.map +1 -1
- package/dist/esm/agent/types.d.ts +5 -0
- package/dist/esm/agent/types.d.ts.map +1 -1
- package/dist/esm/agent/types.js.map +1 -1
- package/dist/esm/hooks/types.d.ts +6 -1
- package/dist/esm/hooks/types.d.ts.map +1 -1
- package/dist/esm/observability/middleware.d.ts.map +1 -1
- package/dist/esm/observability/middleware.js +63 -1
- package/dist/esm/observability/middleware.js.map +1 -1
- package/dist/esm/observability/sink.d.ts +4 -0
- package/dist/esm/observability/sink.d.ts.map +1 -1
- package/dist/esm/observability/sink.js +26 -3
- package/dist/esm/observability/sink.js.map +1 -1
- package/package.json +1 -1
|
@@ -146,8 +146,19 @@ export declare class AgentEngine {
|
|
|
146
146
|
private buildToolContext;
|
|
147
147
|
/** 汇总准备阶段,生成 LoopState */
|
|
148
148
|
private prepareRun;
|
|
149
|
+
/** 动态构建工具区 prompt(Available Tools + Tool Usage Guidelines)。
|
|
150
|
+
* 从 state.toolDefs 现场拼装,确保与可执行工具集一致,消除静态穿透。 */
|
|
151
|
+
private buildDynamicToolSection;
|
|
152
|
+
/** 刷新 system prompt 中的工具区,确保与 state.toolDefs 一致 */
|
|
153
|
+
private refreshToolSection;
|
|
149
154
|
/** 主循环:迭代调用 LLM → 执行工具 → 直到完成或达到上限 */
|
|
150
155
|
private agentLoop;
|
|
156
|
+
/**
|
|
157
|
+
* 生成兜底总结(当 LLM 调用失败或无内容输出时使用)。
|
|
158
|
+
* 从工具调用记录中提取摘要信息,确保用户始终能收到有意义的回复,
|
|
159
|
+
* 而非仅看到 "Max iterations reached"。
|
|
160
|
+
*/
|
|
161
|
+
private generateFallbackSummary;
|
|
151
162
|
/**
|
|
152
163
|
* 向运行中的 Agent 注入 steering 消息
|
|
153
164
|
* 消息将在下次 LLM 调用前注入对话上下文
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"engine.d.ts","sourceRoot":"","sources":["../../../src/agent/engine.ts"],"names":[],"mappings":"AAMA,OAAO,KAAK,EAAE,UAAU,EAAE,WAAW,EAAE,OAAO,EAAoE,MAAM,aAAa,CAAC;AAItI,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,qBAAqB,CAAC;AACxD,OAAO,KAAK,EAAE,WAAW,EAAE,UAAU,EAAoD,MAAM,YAAY,CAAC;AAE5G,OAAO,KAAK,EAAE,SAAS,EAAE,WAAW,EAAE,UAAU,EAAE,MAAM,mBAAmB,CAAC;
|
|
1
|
+
{"version":3,"file":"engine.d.ts","sourceRoot":"","sources":["../../../src/agent/engine.ts"],"names":[],"mappings":"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"}
|
package/dist/cjs/agent/engine.js
CHANGED
|
@@ -173,7 +173,8 @@ class AgentEngine {
|
|
|
173
173
|
});
|
|
174
174
|
// 初始化工具分类集合(可通过 config 覆盖默认值)
|
|
175
175
|
this.dataFetchTools = new Set(config.dataFetchTools ?? [
|
|
176
|
-
'web_fetch', '
|
|
176
|
+
'web_fetch', 'http_request', 'browser_navigate', 'browser_click',
|
|
177
|
+
'execute_command', 'write_file', 'append_file',
|
|
177
178
|
'run_script', 'read_file', 'read_tool_doc',
|
|
178
179
|
]);
|
|
179
180
|
this.outputTools = new Set(config.outputTools ?? [
|
|
@@ -351,6 +352,15 @@ class AgentEngine {
|
|
|
351
352
|
iteration: 0,
|
|
352
353
|
rawError: err,
|
|
353
354
|
});
|
|
355
|
+
// 异常终止时以 error 状态结束 trace(正常路径由 emitDone 触发 on_complete,
|
|
356
|
+
// 此处显式调用确保 trace 不会卡在 active,也不会被 runWithTrace 覆盖为 ok)
|
|
357
|
+
this.runHooks('on_complete', {
|
|
358
|
+
sessionId,
|
|
359
|
+
iteration: 0,
|
|
360
|
+
totalIterations: 0,
|
|
361
|
+
runStatus: 'error',
|
|
362
|
+
error: errorMsg,
|
|
363
|
+
}).catch(() => { });
|
|
354
364
|
this.statusMap.set(sessionId, 'idle');
|
|
355
365
|
}
|
|
356
366
|
finally {
|
|
@@ -703,7 +713,9 @@ ${textHistory.join('\n')}`;
|
|
|
703
713
|
abortSignal: internalAbort.signal,
|
|
704
714
|
custom: {
|
|
705
715
|
...custom,
|
|
706
|
-
|
|
716
|
+
// Fix A:始终使用引擎授权注册表(已按 selectedTools/disabledTools 过滤),
|
|
717
|
+
// 不接受 custom.toolRegistry 覆盖——防止 read_tool_doc/diagnose 泄漏被禁工具 schema。
|
|
718
|
+
toolRegistry: this.config.toolRegistry,
|
|
707
719
|
skillManager: custom?.skillManager ?? this.config.skillManager,
|
|
708
720
|
},
|
|
709
721
|
emitSubEvent: custom?.emitSubEvent,
|
|
@@ -716,9 +728,15 @@ ${textHistory.join('\n')}`;
|
|
|
716
728
|
const { skillContent, actualMessage, skillMatched } = await this.resolveSkill(options, options.sessionId);
|
|
717
729
|
const toolDefs = this.resolveToolDefs(options);
|
|
718
730
|
(0, logger_ts_1.logEvent)({ event: 'engine.prepare', sessionId: options.sessionId, data: { toolCount: toolDefs.length, tools: toolDefs.map(t => t.function.name) } });
|
|
731
|
+
const messages = await this.buildInitialMessages(options, skillContent, actualMessage, skillMatched);
|
|
732
|
+
// 存储不含工具区的稳定系统提示词,首次注入动态工具区
|
|
733
|
+
const stableSystemPrompt = messages[0]?.content || '';
|
|
734
|
+
if (messages[0]) {
|
|
735
|
+
messages[0].content = stableSystemPrompt + this.buildDynamicToolSection(toolDefs);
|
|
736
|
+
}
|
|
719
737
|
return {
|
|
720
738
|
sessionId: options.sessionId,
|
|
721
|
-
messages
|
|
739
|
+
messages,
|
|
722
740
|
toolDefs,
|
|
723
741
|
toolContext: this.buildToolContext(options, internalAbort),
|
|
724
742
|
internalAbort,
|
|
@@ -726,6 +744,7 @@ ${textHistory.join('\n')}`;
|
|
|
726
744
|
lastContent: '',
|
|
727
745
|
toolCallRecords: [],
|
|
728
746
|
consecutiveToolFailures: new Map(),
|
|
747
|
+
blockedTools: new Set(),
|
|
729
748
|
iterationGuardInjected: false,
|
|
730
749
|
consecutiveSilentIterations: 0,
|
|
731
750
|
lastIterationHadToolCalls: false,
|
|
@@ -733,8 +752,39 @@ ${textHistory.join('\n')}`;
|
|
|
733
752
|
dataFetchGuardInjected: false,
|
|
734
753
|
consecutiveAllFailIterations: 0,
|
|
735
754
|
finalSummaryInjected: false,
|
|
755
|
+
hadFatalError: false,
|
|
756
|
+
fatalErrorMsg: undefined,
|
|
757
|
+
stableSystemPrompt,
|
|
736
758
|
};
|
|
737
759
|
}
|
|
760
|
+
/** 动态构建工具区 prompt(Available Tools + Tool Usage Guidelines)。
|
|
761
|
+
* 从 state.toolDefs 现场拼装,确保与可执行工具集一致,消除静态穿透。 */
|
|
762
|
+
buildDynamicToolSection(toolDefs) {
|
|
763
|
+
if (this.config.buildToolSection) {
|
|
764
|
+
return this.config.buildToolSection(toolDefs, this.config.toolRegistry);
|
|
765
|
+
}
|
|
766
|
+
if (toolDefs.length === 0)
|
|
767
|
+
return '';
|
|
768
|
+
const lines = toolDefs.map(d => `- \`${d.function.name}\`: ${d.function.description || ''}`);
|
|
769
|
+
let section = `\n\n## Available Tools\n${lines.join('\n')}`;
|
|
770
|
+
// 收集工具自带的使用引导(tool.prompt)
|
|
771
|
+
const prompts = [];
|
|
772
|
+
for (const def of toolDefs) {
|
|
773
|
+
const tool = this.config.toolRegistry.get(def.function.name);
|
|
774
|
+
if (tool?.prompt)
|
|
775
|
+
prompts.push(tool.prompt);
|
|
776
|
+
}
|
|
777
|
+
if (prompts.length > 0) {
|
|
778
|
+
section += `\n\n## Tool Usage Guidelines\n\n${prompts.join('\n\n')}`;
|
|
779
|
+
}
|
|
780
|
+
return section;
|
|
781
|
+
}
|
|
782
|
+
/** 刷新 system prompt 中的工具区,确保与 state.toolDefs 一致 */
|
|
783
|
+
refreshToolSection(state) {
|
|
784
|
+
if (state.messages[0] && state.stableSystemPrompt != null) {
|
|
785
|
+
state.messages[0].content = state.stableSystemPrompt + this.buildDynamicToolSection(state.toolDefs);
|
|
786
|
+
}
|
|
787
|
+
}
|
|
738
788
|
// ---- Agent Loop ----
|
|
739
789
|
/** 主循环:迭代调用 LLM → 执行工具 → 直到完成或达到上限 */
|
|
740
790
|
async *agentLoop(state) {
|
|
@@ -742,6 +792,11 @@ ${textHistory.join('\n')}`;
|
|
|
742
792
|
state.iteration++;
|
|
743
793
|
// 中断检查
|
|
744
794
|
if (state.internalAbort.signal.aborted) {
|
|
795
|
+
// 外部中断(客户端断开 / 用户主动 abort / 看门狗超时):标记致命错误,
|
|
796
|
+
// 确保 emitDone → on_complete 将 trace 状态写为 error,而非默认的 ok,
|
|
797
|
+
// 避免 trace 卡在 active 或被误判为正常完成。
|
|
798
|
+
state.hadFatalError = true;
|
|
799
|
+
state.fatalErrorMsg = state.fatalErrorMsg || 'Run aborted before completion';
|
|
745
800
|
yield this.emitDone(state);
|
|
746
801
|
return;
|
|
747
802
|
}
|
|
@@ -765,11 +820,28 @@ ${textHistory.join('\n')}`;
|
|
|
765
820
|
timestamp: Date.now(),
|
|
766
821
|
iteration: state.iteration,
|
|
767
822
|
});
|
|
823
|
+
// 记录可观测 error 事件:让 trace 时间线解释"为何熔断",
|
|
824
|
+
// 与 on_complete(runStatus='error') 闭环,确保异常被统计进可观测。
|
|
825
|
+
await this.runHooks('on_error', {
|
|
826
|
+
sessionId: state.sessionId,
|
|
827
|
+
iteration: state.iteration,
|
|
828
|
+
error: budgetErrMsg,
|
|
829
|
+
recoverable: false,
|
|
830
|
+
});
|
|
831
|
+
state.hadFatalError = true;
|
|
832
|
+
state.fatalErrorMsg = budgetErrMsg;
|
|
768
833
|
yield this.emitDone(state);
|
|
769
834
|
return;
|
|
770
835
|
}
|
|
771
836
|
else {
|
|
772
837
|
(0, logger_ts_1.logEvent)({ event: 'engine.token_budget_warning', sessionId: state.sessionId, level: 'warn', data: { current: budgetCheck.currentTokens, max: budgetCheck.maxTokens } });
|
|
838
|
+
await this.runHooks('on_budget_warning', {
|
|
839
|
+
sessionId: state.sessionId,
|
|
840
|
+
iteration: state.iteration,
|
|
841
|
+
currentTokens: budgetCheck.currentTokens,
|
|
842
|
+
maxTokens: budgetCheck.maxTokens,
|
|
843
|
+
traceId: state.toolContext?.custom?.traceId,
|
|
844
|
+
});
|
|
773
845
|
}
|
|
774
846
|
}
|
|
775
847
|
}
|
|
@@ -787,6 +859,12 @@ ${textHistory.join('\n')}`;
|
|
|
787
859
|
messages: steeringResult.messages,
|
|
788
860
|
count: steeringResult.count,
|
|
789
861
|
});
|
|
862
|
+
await this.runHooks('on_steering', {
|
|
863
|
+
sessionId: state.sessionId,
|
|
864
|
+
iteration: state.iteration,
|
|
865
|
+
steeringMessages: steeringResult.messages,
|
|
866
|
+
traceId: state.toolContext?.custom?.traceId,
|
|
867
|
+
});
|
|
790
868
|
}
|
|
791
869
|
// 迭代预算提醒:接近上限时注入提醒,防止 agent 耗尽迭代仍在收集数据
|
|
792
870
|
this.injectIterationGuard(state);
|
|
@@ -794,6 +872,8 @@ ${textHistory.join('\n')}`;
|
|
|
794
872
|
// 首次触发:注入 guard 消息让 LLM 下轮产出总结;不立即终止,给 LLM 一次产出机会
|
|
795
873
|
// 若 LLM 继续失败,全局连续失败熔断器(consecutiveAllFailIterations >= 3)会兜底
|
|
796
874
|
this.checkDataFetchLoop(state);
|
|
875
|
+
// 动态拼装工具区,确保 system prompt 工具列表与 state.toolDefs 一致
|
|
876
|
+
this.refreshToolSection(state);
|
|
797
877
|
// 调用 LLM(流式,含重试与降级)
|
|
798
878
|
this.statusMap.set(state.sessionId, 'thinking');
|
|
799
879
|
const llmStart = Date.now();
|
|
@@ -809,9 +889,12 @@ ${textHistory.join('\n')}`;
|
|
|
809
889
|
state.totalUsage.totalTokens += usage.totalTokens || 0;
|
|
810
890
|
}
|
|
811
891
|
if (!assistant) {
|
|
812
|
-
// 所有 LLM provider 均已失败(callWithRetry
|
|
813
|
-
//
|
|
814
|
-
//
|
|
892
|
+
// 所有 LLM provider 均已失败(callWithRetry 内部已重试+降级)。
|
|
893
|
+
// callWithRetry 已发出 error 事件,此处补充 done 事件以正确关闭 SSE 流。
|
|
894
|
+
// 不发 done 事件会导致:SSE 流不关闭、DB 消息状态卡在 streaming、
|
|
895
|
+
// 前端 onDone 回调不触发(无法更新 UI 状态)。
|
|
896
|
+
// error 和 done 是不同的 SSE 事件类型,前端分别用 onError 和 onDone 处理,
|
|
897
|
+
// done 事件携带 lastContent(之前成功迭代的输出)或兜底总结,不会覆盖 error 消息。
|
|
815
898
|
const errorMsg = 'All LLM providers exhausted (all calls failed including retries and fallback)';
|
|
816
899
|
(0, logger_ts_1.logEvent)({ event: 'engine.llm_exhausted', sessionId: state.sessionId, level: 'warn', data: { iteration: state.iteration } });
|
|
817
900
|
this.statusMap.set(state.sessionId, 'idle');
|
|
@@ -823,25 +906,11 @@ ${textHistory.join('\n')}`;
|
|
|
823
906
|
timestamp: Date.now(),
|
|
824
907
|
iteration: state.iteration,
|
|
825
908
|
});
|
|
826
|
-
|
|
827
|
-
|
|
828
|
-
|
|
829
|
-
|
|
830
|
-
|
|
831
|
-
provider: modelInfo.provider,
|
|
832
|
-
model: modelInfo.model,
|
|
833
|
-
usage: state.totalUsage,
|
|
834
|
-
iterations: state.iteration,
|
|
835
|
-
});
|
|
836
|
-
}
|
|
837
|
-
this.runHooks('on_complete', {
|
|
838
|
-
sessionId: state.sessionId,
|
|
839
|
-
iteration: state.iteration,
|
|
840
|
-
totalIterations: state.iteration,
|
|
841
|
-
usage: state.totalUsage,
|
|
842
|
-
traceId: state.toolContext?.custom?.traceId,
|
|
843
|
-
output: state.lastContent || undefined,
|
|
844
|
-
}).catch(() => { });
|
|
909
|
+
state.hadFatalError = true;
|
|
910
|
+
state.fatalErrorMsg = errorMsg;
|
|
911
|
+
// 发出 done 事件,确保 SSE 流正确关闭、DB 状态正确更新
|
|
912
|
+
// emitDone 内部会记录 Token 用量、调用 on_complete hook 并生成兜底总结
|
|
913
|
+
yield this.emitDone(state);
|
|
845
914
|
return;
|
|
846
915
|
}
|
|
847
916
|
// Hook: after_ll_call
|
|
@@ -877,6 +946,12 @@ ${textHistory.join('\n')}`;
|
|
|
877
946
|
state.lastIterationHadToolCalls = false; // 已消费标记,避免重复注入
|
|
878
947
|
(0, logger_ts_1.logEvent)({ event: 'engine.continuation_nudge', sessionId: state.sessionId, data: { nudge: state.continuationNudges, iteration: state.iteration } });
|
|
879
948
|
this.injectContinuationNudge(state);
|
|
949
|
+
await this.runHooks('on_continuation_nudge', {
|
|
950
|
+
sessionId: state.sessionId,
|
|
951
|
+
iteration: state.iteration,
|
|
952
|
+
nudgeCount: state.continuationNudges,
|
|
953
|
+
traceId: state.toolContext?.custom?.traceId,
|
|
954
|
+
});
|
|
880
955
|
// 不退出,继续下一轮迭代让 LLM 继续执行任务
|
|
881
956
|
continue;
|
|
882
957
|
}
|
|
@@ -913,6 +988,17 @@ ${textHistory.join('\n')}`;
|
|
|
913
988
|
timestamp: Date.now(),
|
|
914
989
|
iteration: state.iteration,
|
|
915
990
|
});
|
|
991
|
+
// 记录可观测 error 事件:让 trace 时间线解释"为何熔断",
|
|
992
|
+
// 与 on_complete(runStatus='error') 闭环,确保异常被统计进可观测。
|
|
993
|
+
await this.runHooks('on_error', {
|
|
994
|
+
sessionId: state.sessionId,
|
|
995
|
+
iteration: state.iteration,
|
|
996
|
+
error: failMsg,
|
|
997
|
+
recoverable: false,
|
|
998
|
+
});
|
|
999
|
+
state.hadFatalError = true;
|
|
1000
|
+
state.fatalErrorMsg = failMsg;
|
|
1001
|
+
state.lastContent = this.generateFallbackSummary(state);
|
|
916
1002
|
yield this.emitDone(state);
|
|
917
1003
|
return;
|
|
918
1004
|
}
|
|
@@ -922,7 +1008,15 @@ ${textHistory.join('\n')}`;
|
|
|
922
1008
|
}
|
|
923
1009
|
}
|
|
924
1010
|
// 渐进式结果交付:并行分发多个 delegate_task 后,提示 LLM 逐步呈现结果
|
|
1011
|
+
const delegateCount = assistant.tool_calls?.filter(tc => tc.function.name === 'delegate_task').length ?? 0;
|
|
925
1012
|
this.injectProgressiveNudge(state, assistant.tool_calls);
|
|
1013
|
+
if (delegateCount >= 2) {
|
|
1014
|
+
await this.runHooks('on_progressive_nudge', {
|
|
1015
|
+
sessionId: state.sessionId,
|
|
1016
|
+
iteration: state.iteration,
|
|
1017
|
+
traceId: state.toolContext?.custom?.traceId,
|
|
1018
|
+
});
|
|
1019
|
+
}
|
|
926
1020
|
// 标记本轮有工具调用(用于下一轮检测 LLM 是否提前停止)
|
|
927
1021
|
state.lastIterationHadToolCalls = true;
|
|
928
1022
|
// Middleware.after
|
|
@@ -958,8 +1052,13 @@ ${textHistory.join('\n')}`;
|
|
|
958
1052
|
};
|
|
959
1053
|
state.messages.push(summaryMsg);
|
|
960
1054
|
this.historyManager.addHistory(state.sessionId, summaryMsg);
|
|
961
|
-
|
|
1055
|
+
// 主动压缩上下文:防止累积的工具结果导致 prompt 过大、LLM 调用失败
|
|
1056
|
+
const maxContextTokens = this.config.contextBudget?.maxContextTokens ?? 100000;
|
|
1057
|
+
const compressedTarget = Math.floor(maxContextTokens * 0.6);
|
|
1058
|
+
const tokensAfter = this.historyManager.compressMessages(state.messages, compressedTarget);
|
|
1059
|
+
(0, logger_ts_1.logEvent)({ event: 'engine.final_summary_injected', sessionId: state.sessionId, data: { iteration: state.iteration, max: maxIter, compressedTokens: tokensAfter } });
|
|
962
1060
|
// 最后一次 LLM 调用(不再执行工具,直接产出文本)
|
|
1061
|
+
this.refreshToolSection(state);
|
|
963
1062
|
this.statusMap.set(state.sessionId, 'thinking');
|
|
964
1063
|
const { assistant: finalAssistant, usage: finalUsage } = yield* this.llmCaller.callWithRetry(state.sessionId, state.messages, state.toolDefs, state.internalAbort.signal);
|
|
965
1064
|
if (finalUsage) {
|
|
@@ -977,10 +1076,51 @@ ${textHistory.join('\n')}`;
|
|
|
977
1076
|
state.lastContent = (0, types_ts_1.extractTextFromContent)(finalAssistant.content);
|
|
978
1077
|
}
|
|
979
1078
|
}
|
|
1079
|
+
else {
|
|
1080
|
+
// LLM 调用失败:生成兜底总结,确保用户始终能收到有意义的回复
|
|
1081
|
+
state.lastContent = this.generateFallbackSummary(state);
|
|
1082
|
+
(0, logger_ts_1.logEvent)({ event: 'engine.fallback_summary', sessionId: state.sessionId, level: 'warn', data: { iteration: state.iteration, toolCalls: state.toolCallRecords.length } });
|
|
1083
|
+
}
|
|
980
1084
|
this.statusMap.set(state.sessionId, 'idle');
|
|
981
1085
|
}
|
|
982
1086
|
yield this.emitDone(state);
|
|
983
1087
|
}
|
|
1088
|
+
/**
|
|
1089
|
+
* 生成兜底总结(当 LLM 调用失败或无内容输出时使用)。
|
|
1090
|
+
* 从工具调用记录中提取摘要信息,确保用户始终能收到有意义的回复,
|
|
1091
|
+
* 而非仅看到 "Max iterations reached"。
|
|
1092
|
+
*/
|
|
1093
|
+
generateFallbackSummary(state) {
|
|
1094
|
+
const records = state.toolCallRecords;
|
|
1095
|
+
if (records.length === 0) {
|
|
1096
|
+
return '任务未能完成:未执行任何工具调用,可能由于配置问题或会话被中断。请重试或联系管理员。';
|
|
1097
|
+
}
|
|
1098
|
+
// 按工具名分组统计
|
|
1099
|
+
const byTool = new Map();
|
|
1100
|
+
for (const r of records) {
|
|
1101
|
+
const entry = byTool.get(r.tool) || { total: 0, failed: 0 };
|
|
1102
|
+
entry.total++;
|
|
1103
|
+
if (r.isError)
|
|
1104
|
+
entry.failed++;
|
|
1105
|
+
byTool.set(r.tool, entry);
|
|
1106
|
+
}
|
|
1107
|
+
const lines = [
|
|
1108
|
+
'抱歉,任务未能完成。以下是执行摘要:',
|
|
1109
|
+
'',
|
|
1110
|
+
`**执行轮次**: ${state.iteration} 轮`,
|
|
1111
|
+
`**工具调用**: ${records.length} 次`,
|
|
1112
|
+
'',
|
|
1113
|
+
'**尝试的工具及结果**:',
|
|
1114
|
+
];
|
|
1115
|
+
for (const [tool, info] of byTool) {
|
|
1116
|
+
const status = info.failed === info.total
|
|
1117
|
+
? '全部失败'
|
|
1118
|
+
: info.failed > 0 ? `${info.failed}/${info.total} 失败` : '成功但未获得有效数据';
|
|
1119
|
+
lines.push(`- \`${tool}\`: ${info.total} 次调用 (${status})`);
|
|
1120
|
+
}
|
|
1121
|
+
lines.push('', '**可能的原因**:', '- 外部数据源无法访问(网络限制/API密钥/数据源变更)', '', '**建议**:', '1. 提供具体的数据文件或 API 密钥', '2. 手动提供数据让我进行分析', '3. 稍后重试或更换网络环境');
|
|
1122
|
+
return lines.join('\n');
|
|
1123
|
+
}
|
|
984
1124
|
// ---- Live Steering(实时转向)----
|
|
985
1125
|
/**
|
|
986
1126
|
* 向运行中的 Agent 注入 steering 消息
|
|
@@ -1105,8 +1245,16 @@ ${textHistory.join('\n')}`;
|
|
|
1105
1245
|
const before = state.toolDefs.length;
|
|
1106
1246
|
state.toolDefs = state.toolDefs.filter(t => !this.dataFetchTools.has(t.function.name));
|
|
1107
1247
|
const removed = before - state.toolDefs.length;
|
|
1248
|
+
// 同步将禁用的工具名加入 blockedTools 集合,供 tool executor 在执行层拦截
|
|
1249
|
+
// LLM 基于对话历史的幻觉调用(toolDefs 仅控制 LLM 可见的工具列表,
|
|
1250
|
+
// 但 tool executor 检查的是 toolRegistry,不会拦截已注册但被禁用的工具)
|
|
1251
|
+
if (!state.blockedTools)
|
|
1252
|
+
state.blockedTools = new Set();
|
|
1253
|
+
for (const name of this.dataFetchTools) {
|
|
1254
|
+
state.blockedTools.add(name);
|
|
1255
|
+
}
|
|
1108
1256
|
if (removed > 0) {
|
|
1109
|
-
(0, logger_ts_1.logEvent)({ event: 'engine.tools_disabled', sessionId: state.sessionId, data: { removed, remaining: state.toolDefs.length } });
|
|
1257
|
+
(0, logger_ts_1.logEvent)({ event: 'engine.tools_disabled', sessionId: state.sessionId, data: { removed, remaining: state.toolDefs.length, blocked: state.blockedTools.size } });
|
|
1110
1258
|
}
|
|
1111
1259
|
}
|
|
1112
1260
|
/**
|
|
@@ -1146,6 +1294,17 @@ ${textHistory.join('\n')}`;
|
|
|
1146
1294
|
return;
|
|
1147
1295
|
}
|
|
1148
1296
|
}
|
|
1297
|
+
// --- 条件 3:跨迭代累计失败(总调用 >=8 且失败率 >=70%) ---
|
|
1298
|
+
// 解决 LLM 在不同迭代中切换工具类型(web_fetch -> http_request -> browser_navigate)
|
|
1299
|
+
// 导致每种工具的连续失败计数都不够触发条件 2 的问题。
|
|
1300
|
+
const totalCalls = state.toolCallRecords.length;
|
|
1301
|
+
if (totalCalls >= 8) {
|
|
1302
|
+
const totalErrors = state.toolCallRecords.filter(r => r.isError).length;
|
|
1303
|
+
if (totalErrors >= Math.floor(totalCalls * 0.7)) {
|
|
1304
|
+
this.injectDataFetchGuard(state, state.toolCallRecords.slice(-6), 'cumulative_failures');
|
|
1305
|
+
return;
|
|
1306
|
+
}
|
|
1307
|
+
}
|
|
1149
1308
|
}
|
|
1150
1309
|
/** 注入数据获取循环/连续失败熔断提示 */
|
|
1151
1310
|
injectDataFetchGuard(state, recent, reason) {
|
|
@@ -1339,17 +1498,20 @@ ${textHistory.join('\n')}`;
|
|
|
1339
1498
|
totalUsage: state.totalUsage,
|
|
1340
1499
|
});
|
|
1341
1500
|
}
|
|
1501
|
+
const finalContent = state.lastContent || this.generateFallbackSummary(state);
|
|
1342
1502
|
this.runHooks('on_complete', {
|
|
1343
1503
|
sessionId: state.sessionId,
|
|
1344
1504
|
iteration: state.iteration,
|
|
1345
1505
|
totalIterations: state.iteration,
|
|
1346
1506
|
usage: state.totalUsage,
|
|
1347
1507
|
traceId: state.toolContext?.custom?.traceId,
|
|
1348
|
-
output:
|
|
1508
|
+
output: finalContent,
|
|
1509
|
+
runStatus: state.hadFatalError ? 'error' : 'ok',
|
|
1510
|
+
error: state.fatalErrorMsg,
|
|
1349
1511
|
}).catch(() => { });
|
|
1350
1512
|
return this.createEvent(state.sessionId, {
|
|
1351
1513
|
type: 'done',
|
|
1352
|
-
content:
|
|
1514
|
+
content: finalContent,
|
|
1353
1515
|
iterations: state.iteration,
|
|
1354
1516
|
usage: state.totalUsage,
|
|
1355
1517
|
});
|