@cicctencent/agent-core 0.1.98 → 0.1.100

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.
Files changed (55) hide show
  1. package/dist/cjs/agent/engine.d.ts +12 -0
  2. package/dist/cjs/agent/engine.d.ts.map +1 -1
  3. package/dist/cjs/agent/engine.js +217 -25
  4. package/dist/cjs/agent/engine.js.map +1 -1
  5. package/dist/cjs/agent/history-manager.d.ts +21 -0
  6. package/dist/cjs/agent/history-manager.d.ts.map +1 -1
  7. package/dist/cjs/agent/history-manager.js +72 -0
  8. package/dist/cjs/agent/history-manager.js.map +1 -1
  9. package/dist/cjs/agent/llm-caller.d.ts +5 -7
  10. package/dist/cjs/agent/llm-caller.d.ts.map +1 -1
  11. package/dist/cjs/agent/llm-caller.js +110 -20
  12. package/dist/cjs/agent/llm-caller.js.map +1 -1
  13. package/dist/cjs/agent/tool-executor.d.ts +10 -0
  14. package/dist/cjs/agent/tool-executor.d.ts.map +1 -1
  15. package/dist/cjs/agent/tool-executor.js +114 -11
  16. package/dist/cjs/agent/tool-executor.js.map +1 -1
  17. package/dist/cjs/hooks/types.d.ts +8 -1
  18. package/dist/cjs/hooks/types.d.ts.map +1 -1
  19. package/dist/cjs/observability/middleware.d.ts.map +1 -1
  20. package/dist/cjs/observability/middleware.js +64 -1
  21. package/dist/cjs/observability/middleware.js.map +1 -1
  22. package/dist/cjs/observability/sink.d.ts +4 -0
  23. package/dist/cjs/observability/sink.d.ts.map +1 -1
  24. package/dist/cjs/observability/sink.js +26 -3
  25. package/dist/cjs/observability/sink.js.map +1 -1
  26. package/dist/cjs/observability/types.d.ts +10 -0
  27. package/dist/cjs/observability/types.d.ts.map +1 -1
  28. package/dist/esm/agent/engine.d.ts +12 -0
  29. package/dist/esm/agent/engine.d.ts.map +1 -1
  30. package/dist/esm/agent/engine.js +217 -25
  31. package/dist/esm/agent/engine.js.map +1 -1
  32. package/dist/esm/agent/history-manager.d.ts +21 -0
  33. package/dist/esm/agent/history-manager.d.ts.map +1 -1
  34. package/dist/esm/agent/history-manager.js +72 -0
  35. package/dist/esm/agent/history-manager.js.map +1 -1
  36. package/dist/esm/agent/llm-caller.d.ts +5 -7
  37. package/dist/esm/agent/llm-caller.d.ts.map +1 -1
  38. package/dist/esm/agent/llm-caller.js +110 -20
  39. package/dist/esm/agent/llm-caller.js.map +1 -1
  40. package/dist/esm/agent/tool-executor.d.ts +10 -0
  41. package/dist/esm/agent/tool-executor.d.ts.map +1 -1
  42. package/dist/esm/agent/tool-executor.js +114 -11
  43. package/dist/esm/agent/tool-executor.js.map +1 -1
  44. package/dist/esm/hooks/types.d.ts +8 -1
  45. package/dist/esm/hooks/types.d.ts.map +1 -1
  46. package/dist/esm/observability/middleware.d.ts.map +1 -1
  47. package/dist/esm/observability/middleware.js +64 -1
  48. package/dist/esm/observability/middleware.js.map +1 -1
  49. package/dist/esm/observability/sink.d.ts +4 -0
  50. package/dist/esm/observability/sink.d.ts.map +1 -1
  51. package/dist/esm/observability/sink.js +26 -3
  52. package/dist/esm/observability/sink.js.map +1 -1
  53. package/dist/esm/observability/types.d.ts +10 -0
  54. package/dist/esm/observability/types.d.ts.map +1 -1
  55. package/package.json +1 -1
@@ -148,6 +148,12 @@ export declare class AgentEngine {
148
148
  private prepareRun;
149
149
  /** 主循环:迭代调用 LLM → 执行工具 → 直到完成或达到上限 */
150
150
  private agentLoop;
151
+ /**
152
+ * 生成兜底总结(当 LLM 调用失败或无内容输出时使用)。
153
+ * 从工具调用记录中提取摘要信息,确保用户始终能收到有意义的回复,
154
+ * 而非仅看到 "Max iterations reached"。
155
+ */
156
+ private generateFallbackSummary;
151
157
  /**
152
158
  * 向运行中的 Agent 注入 steering 消息
153
159
  * 消息将在下次 LLM 调用前注入对话上下文
@@ -167,6 +173,12 @@ export declare class AgentEngine {
167
173
  * 解决 agent 在数据查询循环中耗尽迭代、永远无法进入文档生成/委托阶段的问题。
168
174
  */
169
175
  private injectIterationGuard;
176
+ /**
177
+ * 从可用工具列表中移除数据获取类工具,强制 LLM 转向产出。
178
+ * 在 iterationGuard / dataFetchGuard 触发后调用。
179
+ * 仅移除尚未执行的 toolDefs,不影响正在执行的工具。
180
+ */
181
+ private disableDataFetchTools;
170
182
  /**
171
183
  * 数据获取循环检测 + 连续失败熔断
172
184
  *
@@ -1 +1 @@
1
- {"version":3,"file":"engine.d.ts","sourceRoot":"","sources":["../../../src/agent/engine.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"engine.d.ts","sourceRoot":"","sources":["../../../src/agent/engine.ts"],"names":[],"mappings":"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"}
@@ -173,7 +173,8 @@ class AgentEngine {
173
173
  });
174
174
  // 初始化工具分类集合(可通过 config 覆盖默认值)
175
175
  this.dataFetchTools = new Set(config.dataFetchTools ?? [
176
- 'web_fetch', 'execute_command', 'write_file', 'append_file',
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
- toolRegistry: custom?.toolRegistry ?? this.config.toolRegistry,
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,
@@ -726,12 +738,16 @@ ${textHistory.join('\n')}`;
726
738
  lastContent: '',
727
739
  toolCallRecords: [],
728
740
  consecutiveToolFailures: new Map(),
741
+ blockedTools: new Set(),
729
742
  iterationGuardInjected: false,
730
743
  consecutiveSilentIterations: 0,
731
744
  lastIterationHadToolCalls: false,
732
745
  continuationNudges: 0,
733
746
  dataFetchGuardInjected: false,
734
747
  consecutiveAllFailIterations: 0,
748
+ finalSummaryInjected: false,
749
+ hadFatalError: false,
750
+ fatalErrorMsg: undefined,
735
751
  };
736
752
  }
737
753
  // ---- Agent Loop ----
@@ -741,6 +757,11 @@ ${textHistory.join('\n')}`;
741
757
  state.iteration++;
742
758
  // 中断检查
743
759
  if (state.internalAbort.signal.aborted) {
760
+ // 外部中断(客户端断开 / 用户主动 abort / 看门狗超时):标记致命错误,
761
+ // 确保 emitDone → on_complete 将 trace 状态写为 error,而非默认的 ok,
762
+ // 避免 trace 卡在 active 或被误判为正常完成。
763
+ state.hadFatalError = true;
764
+ state.fatalErrorMsg = state.fatalErrorMsg || 'Run aborted before completion';
744
765
  yield this.emitDone(state);
745
766
  return;
746
767
  }
@@ -764,11 +785,28 @@ ${textHistory.join('\n')}`;
764
785
  timestamp: Date.now(),
765
786
  iteration: state.iteration,
766
787
  });
788
+ // 记录可观测 error 事件:让 trace 时间线解释"为何熔断",
789
+ // 与 on_complete(runStatus='error') 闭环,确保异常被统计进可观测。
790
+ await this.runHooks('on_error', {
791
+ sessionId: state.sessionId,
792
+ iteration: state.iteration,
793
+ error: budgetErrMsg,
794
+ recoverable: false,
795
+ });
796
+ state.hadFatalError = true;
797
+ state.fatalErrorMsg = budgetErrMsg;
767
798
  yield this.emitDone(state);
768
799
  return;
769
800
  }
770
801
  else {
771
802
  (0, logger_ts_1.logEvent)({ event: 'engine.token_budget_warning', sessionId: state.sessionId, level: 'warn', data: { current: budgetCheck.currentTokens, max: budgetCheck.maxTokens } });
803
+ await this.runHooks('on_budget_warning', {
804
+ sessionId: state.sessionId,
805
+ iteration: state.iteration,
806
+ currentTokens: budgetCheck.currentTokens,
807
+ maxTokens: budgetCheck.maxTokens,
808
+ traceId: state.toolContext?.custom?.traceId,
809
+ });
772
810
  }
773
811
  }
774
812
  }
@@ -786,6 +824,12 @@ ${textHistory.join('\n')}`;
786
824
  messages: steeringResult.messages,
787
825
  count: steeringResult.count,
788
826
  });
827
+ await this.runHooks('on_steering', {
828
+ sessionId: state.sessionId,
829
+ iteration: state.iteration,
830
+ steeringMessages: steeringResult.messages,
831
+ traceId: state.toolContext?.custom?.traceId,
832
+ });
789
833
  }
790
834
  // 迭代预算提醒:接近上限时注入提醒,防止 agent 耗尽迭代仍在收集数据
791
835
  this.injectIterationGuard(state);
@@ -808,9 +852,12 @@ ${textHistory.join('\n')}`;
808
852
  state.totalUsage.totalTokens += usage.totalTokens || 0;
809
853
  }
810
854
  if (!assistant) {
811
- // 所有 LLM provider 均已失败(callWithRetry 内部已重试+降级),
812
- // 不再发 done 事件(callWithRetry 已发出带详细错误信息的 error 事件),
813
- // 仅完成清理,避免 done 事件覆盖前端已展示的错误消息
855
+ // 所有 LLM provider 均已失败(callWithRetry 内部已重试+降级)。
856
+ // callWithRetry 已发出 error 事件,此处补充 done 事件以正确关闭 SSE 流。
857
+ // 不发 done 事件会导致:SSE 流不关闭、DB 消息状态卡在 streaming、
858
+ // 前端 onDone 回调不触发(无法更新 UI 状态)。
859
+ // error 和 done 是不同的 SSE 事件类型,前端分别用 onError 和 onDone 处理,
860
+ // done 事件携带 lastContent(之前成功迭代的输出)或兜底总结,不会覆盖 error 消息。
814
861
  const errorMsg = 'All LLM providers exhausted (all calls failed including retries and fallback)';
815
862
  (0, logger_ts_1.logEvent)({ event: 'engine.llm_exhausted', sessionId: state.sessionId, level: 'warn', data: { iteration: state.iteration } });
816
863
  this.statusMap.set(state.sessionId, 'idle');
@@ -822,24 +869,11 @@ ${textHistory.join('\n')}`;
822
869
  timestamp: Date.now(),
823
870
  iteration: state.iteration,
824
871
  });
825
- // 记录 Token 用量到 UsageTracker
826
- if (this.config.usageTracker && state.totalUsage) {
827
- const modelInfo = this.config.llmProvider.getModel();
828
- this.config.usageTracker.record({
829
- sessionId: state.sessionId,
830
- provider: modelInfo.provider,
831
- model: modelInfo.model,
832
- usage: state.totalUsage,
833
- iterations: state.iteration,
834
- });
835
- }
836
- this.runHooks('on_complete', {
837
- sessionId: state.sessionId,
838
- iteration: state.iteration,
839
- totalIterations: state.iteration,
840
- usage: state.totalUsage,
841
- traceId: state.toolContext?.custom?.traceId,
842
- }).catch(() => { });
872
+ state.hadFatalError = true;
873
+ state.fatalErrorMsg = errorMsg;
874
+ // 发出 done 事件,确保 SSE 流正确关闭、DB 状态正确更新
875
+ // emitDone 内部会记录 Token 用量、调用 on_complete hook 并生成兜底总结
876
+ yield this.emitDone(state);
843
877
  return;
844
878
  }
845
879
  // Hook: after_ll_call
@@ -875,6 +909,12 @@ ${textHistory.join('\n')}`;
875
909
  state.lastIterationHadToolCalls = false; // 已消费标记,避免重复注入
876
910
  (0, logger_ts_1.logEvent)({ event: 'engine.continuation_nudge', sessionId: state.sessionId, data: { nudge: state.continuationNudges, iteration: state.iteration } });
877
911
  this.injectContinuationNudge(state);
912
+ await this.runHooks('on_continuation_nudge', {
913
+ sessionId: state.sessionId,
914
+ iteration: state.iteration,
915
+ nudgeCount: state.continuationNudges,
916
+ traceId: state.toolContext?.custom?.traceId,
917
+ });
878
918
  // 不退出,继续下一轮迭代让 LLM 继续执行任务
879
919
  continue;
880
920
  }
@@ -911,6 +951,16 @@ ${textHistory.join('\n')}`;
911
951
  timestamp: Date.now(),
912
952
  iteration: state.iteration,
913
953
  });
954
+ // 记录可观测 error 事件:让 trace 时间线解释"为何熔断",
955
+ // 与 on_complete(runStatus='error') 闭环,确保异常被统计进可观测。
956
+ await this.runHooks('on_error', {
957
+ sessionId: state.sessionId,
958
+ iteration: state.iteration,
959
+ error: failMsg,
960
+ recoverable: false,
961
+ });
962
+ state.hadFatalError = true;
963
+ state.fatalErrorMsg = failMsg;
914
964
  yield this.emitDone(state);
915
965
  return;
916
966
  }
@@ -920,15 +970,118 @@ ${textHistory.join('\n')}`;
920
970
  }
921
971
  }
922
972
  // 渐进式结果交付:并行分发多个 delegate_task 后,提示 LLM 逐步呈现结果
973
+ const delegateCount = assistant.tool_calls?.filter(tc => tc.function.name === 'delegate_task').length ?? 0;
923
974
  this.injectProgressiveNudge(state, assistant.tool_calls);
975
+ if (delegateCount >= 2) {
976
+ await this.runHooks('on_progressive_nudge', {
977
+ sessionId: state.sessionId,
978
+ iteration: state.iteration,
979
+ traceId: state.toolContext?.custom?.traceId,
980
+ });
981
+ }
924
982
  // 标记本轮有工具调用(用于下一轮检测 LLM 是否提前停止)
925
983
  state.lastIterationHadToolCalls = true;
926
984
  // Middleware.after
927
985
  await this.runMiddlewarePhase('after', state);
928
986
  }
929
- // 达到最大迭代
987
+ // 达到最大迭代 - 注入最终总结提示,给 LLM 最后一次机会输出结果
988
+ if (!state.finalSummaryInjected) {
989
+ state.finalSummaryInjected = true;
990
+ // 禁用所有数据获取工具,确保 LLM 只能产出结果
991
+ this.disableDataFetchTools(state);
992
+ const maxIter = this.config.maxIterations;
993
+ const summaryMsg = {
994
+ role: 'user',
995
+ content: [
996
+ `[SYSTEM: MAX ITERATIONS REACHED - FINAL OUTPUT REQUIRED]`,
997
+ '',
998
+ `You have reached the maximum number of iterations (${maxIter}). Your task is now OVER.`,
999
+ '',
1000
+ '**You MUST produce your final output NOW.**',
1001
+ '',
1002
+ 'Rules:',
1003
+ '1. Do NOT attempt any more tool calls - data gathering tools have been disabled.',
1004
+ '2. If you have data from previous tool calls, present it directly in text.',
1005
+ ...(this.hasTool('show_widget') ? ['3. Use `show_widget` to visualize any data you have.'] : []),
1006
+ ...(this.hasTool('create_document') ? ['4. Use `create_document` to create a structured report.'] : []),
1007
+ '5. If you do NOT have sufficient data, clearly explain to the user:',
1008
+ ' - What you were trying to find',
1009
+ ' - Which data sources you tried and why they failed',
1010
+ ' - What the user can do to get the data they need',
1011
+ '',
1012
+ 'The user is waiting for a response. Provide it NOW.',
1013
+ ].join('\n'),
1014
+ };
1015
+ state.messages.push(summaryMsg);
1016
+ this.historyManager.addHistory(state.sessionId, summaryMsg);
1017
+ // 主动压缩上下文:防止累积的工具结果导致 prompt 过大、LLM 调用失败
1018
+ const maxContextTokens = this.config.contextBudget?.maxContextTokens ?? 100000;
1019
+ const compressedTarget = Math.floor(maxContextTokens * 0.6);
1020
+ const tokensAfter = this.historyManager.compressMessages(state.messages, compressedTarget);
1021
+ (0, logger_ts_1.logEvent)({ event: 'engine.final_summary_injected', sessionId: state.sessionId, data: { iteration: state.iteration, max: maxIter, compressedTokens: tokensAfter } });
1022
+ // 最后一次 LLM 调用(不再执行工具,直接产出文本)
1023
+ this.statusMap.set(state.sessionId, 'thinking');
1024
+ const { assistant: finalAssistant, usage: finalUsage } = yield* this.llmCaller.callWithRetry(state.sessionId, state.messages, state.toolDefs, state.internalAbort.signal);
1025
+ if (finalUsage) {
1026
+ if (!state.totalUsage) {
1027
+ state.totalUsage = { promptTokens: 0, completionTokens: 0, totalTokens: 0 };
1028
+ }
1029
+ state.totalUsage.promptTokens += finalUsage.promptTokens || 0;
1030
+ state.totalUsage.completionTokens += finalUsage.completionTokens || 0;
1031
+ state.totalUsage.totalTokens += finalUsage.totalTokens || 0;
1032
+ }
1033
+ if (finalAssistant) {
1034
+ state.messages.push(finalAssistant);
1035
+ this.historyManager.addHistory(state.sessionId, finalAssistant);
1036
+ if (finalAssistant.content) {
1037
+ state.lastContent = (0, types_ts_1.extractTextFromContent)(finalAssistant.content);
1038
+ }
1039
+ }
1040
+ else {
1041
+ // LLM 调用失败:生成兜底总结,确保用户始终能收到有意义的回复
1042
+ state.lastContent = this.generateFallbackSummary(state);
1043
+ (0, logger_ts_1.logEvent)({ event: 'engine.fallback_summary', sessionId: state.sessionId, level: 'warn', data: { iteration: state.iteration, toolCalls: state.toolCallRecords.length } });
1044
+ }
1045
+ this.statusMap.set(state.sessionId, 'idle');
1046
+ }
930
1047
  yield this.emitDone(state);
931
1048
  }
1049
+ /**
1050
+ * 生成兜底总结(当 LLM 调用失败或无内容输出时使用)。
1051
+ * 从工具调用记录中提取摘要信息,确保用户始终能收到有意义的回复,
1052
+ * 而非仅看到 "Max iterations reached"。
1053
+ */
1054
+ generateFallbackSummary(state) {
1055
+ const records = state.toolCallRecords;
1056
+ if (records.length === 0) {
1057
+ return '任务未能完成:未执行任何工具调用,可能由于配置问题或会话被中断。请重试或联系管理员。';
1058
+ }
1059
+ // 按工具名分组统计
1060
+ const byTool = new Map();
1061
+ for (const r of records) {
1062
+ const entry = byTool.get(r.tool) || { total: 0, failed: 0 };
1063
+ entry.total++;
1064
+ if (r.isError)
1065
+ entry.failed++;
1066
+ byTool.set(r.tool, entry);
1067
+ }
1068
+ const lines = [
1069
+ '抱歉,任务未能完成。以下是执行摘要:',
1070
+ '',
1071
+ `**执行轮次**: ${state.iteration} 轮`,
1072
+ `**工具调用**: ${records.length} 次`,
1073
+ '',
1074
+ '**尝试的工具及结果**:',
1075
+ ];
1076
+ for (const [tool, info] of byTool) {
1077
+ const status = info.failed === info.total
1078
+ ? '全部失败'
1079
+ : info.failed > 0 ? `${info.failed}/${info.total} 失败` : '成功但未获得有效数据';
1080
+ lines.push(`- \`${tool}\`: ${info.total} 次调用 (${status})`);
1081
+ }
1082
+ lines.push('', '**可能的原因**:', '- 外部数据源无法访问(网络限制/API密钥/数据源变更)', '', '**建议**:', '1. 提供具体的数据文件或 API 密钥', '2. 手动提供数据让我进行分析', '3. 稍后重试或更换网络环境');
1083
+ return lines.join('\n');
1084
+ }
932
1085
  // ---- Live Steering(实时转向)----
933
1086
  /**
934
1087
  * 向运行中的 Agent 注入 steering 消息
@@ -1040,6 +1193,29 @@ ${textHistory.join('\n')}`;
1040
1193
  state.messages.push(msg);
1041
1194
  this.historyManager.addHistory(state.sessionId, msg);
1042
1195
  (0, logger_ts_1.logEvent)({ event: 'engine.iteration_guard', sessionId: state.sessionId, data: { iteration: cur, max } });
1196
+ // 硬性禁用数据获取工具,防止 LLM 无视文字提示继续调用
1197
+ this.disableDataFetchTools(state);
1198
+ }
1199
+ }
1200
+ /**
1201
+ * 从可用工具列表中移除数据获取类工具,强制 LLM 转向产出。
1202
+ * 在 iterationGuard / dataFetchGuard 触发后调用。
1203
+ * 仅移除尚未执行的 toolDefs,不影响正在执行的工具。
1204
+ */
1205
+ disableDataFetchTools(state) {
1206
+ const before = state.toolDefs.length;
1207
+ state.toolDefs = state.toolDefs.filter(t => !this.dataFetchTools.has(t.function.name));
1208
+ const removed = before - state.toolDefs.length;
1209
+ // 同步将禁用的工具名加入 blockedTools 集合,供 tool executor 在执行层拦截
1210
+ // LLM 基于对话历史的幻觉调用(toolDefs 仅控制 LLM 可见的工具列表,
1211
+ // 但 tool executor 检查的是 toolRegistry,不会拦截已注册但被禁用的工具)
1212
+ if (!state.blockedTools)
1213
+ state.blockedTools = new Set();
1214
+ for (const name of this.dataFetchTools) {
1215
+ state.blockedTools.add(name);
1216
+ }
1217
+ if (removed > 0) {
1218
+ (0, logger_ts_1.logEvent)({ event: 'engine.tools_disabled', sessionId: state.sessionId, data: { removed, remaining: state.toolDefs.length, blocked: state.blockedTools.size } });
1043
1219
  }
1044
1220
  }
1045
1221
  /**
@@ -1079,6 +1255,17 @@ ${textHistory.join('\n')}`;
1079
1255
  return;
1080
1256
  }
1081
1257
  }
1258
+ // --- 条件 3:跨迭代累计失败(总调用 >=8 且失败率 >=70%) ---
1259
+ // 解决 LLM 在不同迭代中切换工具类型(web_fetch -> http_request -> browser_navigate)
1260
+ // 导致每种工具的连续失败计数都不够触发条件 2 的问题。
1261
+ const totalCalls = state.toolCallRecords.length;
1262
+ if (totalCalls >= 8) {
1263
+ const totalErrors = state.toolCallRecords.filter(r => r.isError).length;
1264
+ if (totalErrors >= Math.floor(totalCalls * 0.7)) {
1265
+ this.injectDataFetchGuard(state, state.toolCallRecords.slice(-6), 'cumulative_failures');
1266
+ return;
1267
+ }
1268
+ }
1082
1269
  }
1083
1270
  /** 注入数据获取循环/连续失败熔断提示 */
1084
1271
  injectDataFetchGuard(state, recent, reason) {
@@ -1115,6 +1302,8 @@ ${textHistory.join('\n')}`;
1115
1302
  state.messages.push(msg);
1116
1303
  this.historyManager.addHistory(state.sessionId, msg);
1117
1304
  (0, logger_ts_1.logEvent)({ event: 'engine.data_fetch_guard', sessionId: state.sessionId, data: { reason, iteration: state.iteration, recent: toolSummary } });
1305
+ // 硬性禁用数据获取工具,防止 LLM 无视文字提示继续调用
1306
+ this.disableDataFetchTools(state);
1118
1307
  }
1119
1308
  /**
1120
1309
  * 任务继续提示
@@ -1276,10 +1465,13 @@ ${textHistory.join('\n')}`;
1276
1465
  totalIterations: state.iteration,
1277
1466
  usage: state.totalUsage,
1278
1467
  traceId: state.toolContext?.custom?.traceId,
1468
+ output: state.lastContent || undefined,
1469
+ runStatus: state.hadFatalError ? 'error' : 'ok',
1470
+ error: state.fatalErrorMsg,
1279
1471
  }).catch(() => { });
1280
1472
  return this.createEvent(state.sessionId, {
1281
1473
  type: 'done',
1282
- content: state.lastContent || 'Max iterations reached',
1474
+ content: state.lastContent || this.generateFallbackSummary(state),
1283
1475
  iterations: state.iteration,
1284
1476
  usage: state.totalUsage,
1285
1477
  });