@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.
- package/dist/cjs/agent/engine.d.ts +12 -0
- package/dist/cjs/agent/engine.d.ts.map +1 -1
- package/dist/cjs/agent/engine.js +217 -25
- 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 +10 -0
- package/dist/cjs/agent/tool-executor.d.ts.map +1 -1
- package/dist/cjs/agent/tool-executor.js +114 -11
- package/dist/cjs/agent/tool-executor.js.map +1 -1
- package/dist/cjs/hooks/types.d.ts +8 -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 +64 -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/cjs/observability/types.d.ts +10 -0
- package/dist/cjs/observability/types.d.ts.map +1 -1
- package/dist/esm/agent/engine.d.ts +12 -0
- package/dist/esm/agent/engine.d.ts.map +1 -1
- package/dist/esm/agent/engine.js +217 -25
- 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 +10 -0
- package/dist/esm/agent/tool-executor.d.ts.map +1 -1
- package/dist/esm/agent/tool-executor.js +114 -11
- package/dist/esm/agent/tool-executor.js.map +1 -1
- package/dist/esm/hooks/types.d.ts +8 -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 +64 -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/dist/esm/observability/types.d.ts +10 -0
- package/dist/esm/observability/types.d.ts.map +1 -1
- 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":"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,
|
|
@@ -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
|
-
//
|
|
813
|
-
//
|
|
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
|
-
|
|
826
|
-
|
|
827
|
-
|
|
828
|
-
|
|
829
|
-
|
|
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 ||
|
|
1474
|
+
content: state.lastContent || this.generateFallbackSummary(state),
|
|
1283
1475
|
iterations: state.iteration,
|
|
1284
1476
|
usage: state.totalUsage,
|
|
1285
1477
|
});
|