@cicctencent/agent-core 0.1.20 → 0.1.22

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 (153) hide show
  1. package/dist/cjs/a2a/index.d.ts +4 -0
  2. package/dist/cjs/a2a/index.d.ts.map +1 -1
  3. package/dist/cjs/a2a/index.js +6 -1
  4. package/dist/cjs/a2a/index.js.map +1 -1
  5. package/dist/cjs/a2a/rest-client.d.ts +111 -0
  6. package/dist/cjs/a2a/rest-client.d.ts.map +1 -0
  7. package/dist/cjs/a2a/rest-client.js +342 -0
  8. package/dist/cjs/a2a/rest-client.js.map +1 -0
  9. package/dist/cjs/a2a/rest-runner.d.ts +41 -0
  10. package/dist/cjs/a2a/rest-runner.d.ts.map +1 -0
  11. package/dist/cjs/a2a/rest-runner.js +188 -0
  12. package/dist/cjs/a2a/rest-runner.js.map +1 -0
  13. package/dist/cjs/agent/engine.d.ts +17 -0
  14. package/dist/cjs/agent/engine.d.ts.map +1 -1
  15. package/dist/cjs/agent/engine.js +208 -13
  16. package/dist/cjs/agent/engine.js.map +1 -1
  17. package/dist/cjs/agent/llm-caller.d.ts +12 -1
  18. package/dist/cjs/agent/llm-caller.d.ts.map +1 -1
  19. package/dist/cjs/agent/llm-caller.js +101 -32
  20. package/dist/cjs/agent/llm-caller.js.map +1 -1
  21. package/dist/cjs/agent/types.d.ts +1 -1
  22. package/dist/cjs/agent/types.d.ts.map +1 -1
  23. package/dist/cjs/index.d.ts +3 -0
  24. package/dist/cjs/index.d.ts.map +1 -1
  25. package/dist/cjs/index.js +8 -1
  26. package/dist/cjs/index.js.map +1 -1
  27. package/dist/cjs/kb/in-memory-kb.d.ts.map +1 -1
  28. package/dist/cjs/kb/in-memory-kb.js +2 -1
  29. package/dist/cjs/kb/in-memory-kb.js.map +1 -1
  30. package/dist/cjs/llm/anthropic-provider.d.ts.map +1 -1
  31. package/dist/cjs/llm/anthropic-provider.js +1 -0
  32. package/dist/cjs/llm/anthropic-provider.js.map +1 -1
  33. package/dist/cjs/llm/ollama-provider.d.ts.map +1 -1
  34. package/dist/cjs/llm/ollama-provider.js +1 -0
  35. package/dist/cjs/llm/ollama-provider.js.map +1 -1
  36. package/dist/cjs/llm/openai-provider.d.ts.map +1 -1
  37. package/dist/cjs/llm/openai-provider.js +17 -12
  38. package/dist/cjs/llm/openai-provider.js.map +1 -1
  39. package/dist/cjs/llm/types.d.ts +4 -0
  40. package/dist/cjs/llm/types.d.ts.map +1 -1
  41. package/dist/cjs/llm/types.js.map +1 -1
  42. package/dist/cjs/memory/checkpoint.d.ts.map +1 -1
  43. package/dist/cjs/memory/checkpoint.js +3 -2
  44. package/dist/cjs/memory/checkpoint.js.map +1 -1
  45. package/dist/cjs/memory/extractor.d.ts +7 -0
  46. package/dist/cjs/memory/extractor.d.ts.map +1 -1
  47. package/dist/cjs/memory/extractor.js +18 -1
  48. package/dist/cjs/memory/extractor.js.map +1 -1
  49. package/dist/cjs/memory/persona.d.ts +4 -0
  50. package/dist/cjs/memory/persona.d.ts.map +1 -1
  51. package/dist/cjs/memory/persona.js +15 -1
  52. package/dist/cjs/memory/persona.js.map +1 -1
  53. package/dist/cjs/memory/pipeline.d.ts +3 -0
  54. package/dist/cjs/memory/pipeline.d.ts.map +1 -1
  55. package/dist/cjs/memory/pipeline.js +7 -4
  56. package/dist/cjs/memory/pipeline.js.map +1 -1
  57. package/dist/cjs/memory/scene.d.ts +4 -0
  58. package/dist/cjs/memory/scene.d.ts.map +1 -1
  59. package/dist/cjs/memory/scene.js +15 -1
  60. package/dist/cjs/memory/scene.js.map +1 -1
  61. package/dist/cjs/middleware/audit-log.d.ts.map +1 -1
  62. package/dist/cjs/middleware/audit-log.js +3 -2
  63. package/dist/cjs/middleware/audit-log.js.map +1 -1
  64. package/dist/cjs/store/file-tool-result-store.d.ts.map +1 -1
  65. package/dist/cjs/store/file-tool-result-store.js +2 -1
  66. package/dist/cjs/store/file-tool-result-store.js.map +1 -1
  67. package/dist/cjs/store/json-store.d.ts +5 -1
  68. package/dist/cjs/store/json-store.d.ts.map +1 -1
  69. package/dist/cjs/store/json-store.js +5 -1
  70. package/dist/cjs/store/json-store.js.map +1 -1
  71. package/dist/cjs/tool/delegate.js +1 -1
  72. package/dist/cjs/tool/delegate.js.map +1 -1
  73. package/dist/cjs/utils/date.d.ts +36 -0
  74. package/dist/cjs/utils/date.d.ts.map +1 -0
  75. package/dist/cjs/utils/date.js +52 -0
  76. package/dist/cjs/utils/date.js.map +1 -0
  77. package/dist/esm/a2a/index.d.ts +4 -0
  78. package/dist/esm/a2a/index.d.ts.map +1 -1
  79. package/dist/esm/a2a/index.js +3 -0
  80. package/dist/esm/a2a/index.js.map +1 -1
  81. package/dist/esm/a2a/rest-client.d.ts +111 -0
  82. package/dist/esm/a2a/rest-client.d.ts.map +1 -0
  83. package/dist/esm/a2a/rest-client.js +338 -0
  84. package/dist/esm/a2a/rest-client.js.map +1 -0
  85. package/dist/esm/a2a/rest-runner.d.ts +41 -0
  86. package/dist/esm/a2a/rest-runner.d.ts.map +1 -0
  87. package/dist/esm/a2a/rest-runner.js +185 -0
  88. package/dist/esm/a2a/rest-runner.js.map +1 -0
  89. package/dist/esm/agent/engine.d.ts +17 -0
  90. package/dist/esm/agent/engine.d.ts.map +1 -1
  91. package/dist/esm/agent/engine.js +208 -13
  92. package/dist/esm/agent/engine.js.map +1 -1
  93. package/dist/esm/agent/llm-caller.d.ts +12 -1
  94. package/dist/esm/agent/llm-caller.d.ts.map +1 -1
  95. package/dist/esm/agent/llm-caller.js +101 -32
  96. package/dist/esm/agent/llm-caller.js.map +1 -1
  97. package/dist/esm/agent/types.d.ts +1 -1
  98. package/dist/esm/agent/types.d.ts.map +1 -1
  99. package/dist/esm/index.d.ts +3 -0
  100. package/dist/esm/index.d.ts.map +1 -1
  101. package/dist/esm/index.js +3 -0
  102. package/dist/esm/index.js.map +1 -1
  103. package/dist/esm/kb/in-memory-kb.d.ts.map +1 -1
  104. package/dist/esm/kb/in-memory-kb.js +2 -1
  105. package/dist/esm/kb/in-memory-kb.js.map +1 -1
  106. package/dist/esm/llm/anthropic-provider.d.ts.map +1 -1
  107. package/dist/esm/llm/anthropic-provider.js +1 -0
  108. package/dist/esm/llm/anthropic-provider.js.map +1 -1
  109. package/dist/esm/llm/ollama-provider.d.ts.map +1 -1
  110. package/dist/esm/llm/ollama-provider.js +1 -0
  111. package/dist/esm/llm/ollama-provider.js.map +1 -1
  112. package/dist/esm/llm/openai-provider.d.ts.map +1 -1
  113. package/dist/esm/llm/openai-provider.js +17 -12
  114. package/dist/esm/llm/openai-provider.js.map +1 -1
  115. package/dist/esm/llm/types.d.ts +4 -0
  116. package/dist/esm/llm/types.d.ts.map +1 -1
  117. package/dist/esm/llm/types.js.map +1 -1
  118. package/dist/esm/memory/checkpoint.d.ts.map +1 -1
  119. package/dist/esm/memory/checkpoint.js +3 -2
  120. package/dist/esm/memory/checkpoint.js.map +1 -1
  121. package/dist/esm/memory/extractor.d.ts +7 -0
  122. package/dist/esm/memory/extractor.d.ts.map +1 -1
  123. package/dist/esm/memory/extractor.js +18 -1
  124. package/dist/esm/memory/extractor.js.map +1 -1
  125. package/dist/esm/memory/persona.d.ts +4 -0
  126. package/dist/esm/memory/persona.d.ts.map +1 -1
  127. package/dist/esm/memory/persona.js +15 -1
  128. package/dist/esm/memory/persona.js.map +1 -1
  129. package/dist/esm/memory/pipeline.d.ts +3 -0
  130. package/dist/esm/memory/pipeline.d.ts.map +1 -1
  131. package/dist/esm/memory/pipeline.js +7 -4
  132. package/dist/esm/memory/pipeline.js.map +1 -1
  133. package/dist/esm/memory/scene.d.ts +4 -0
  134. package/dist/esm/memory/scene.d.ts.map +1 -1
  135. package/dist/esm/memory/scene.js +15 -1
  136. package/dist/esm/memory/scene.js.map +1 -1
  137. package/dist/esm/middleware/audit-log.d.ts.map +1 -1
  138. package/dist/esm/middleware/audit-log.js +3 -2
  139. package/dist/esm/middleware/audit-log.js.map +1 -1
  140. package/dist/esm/store/file-tool-result-store.d.ts.map +1 -1
  141. package/dist/esm/store/file-tool-result-store.js +2 -1
  142. package/dist/esm/store/file-tool-result-store.js.map +1 -1
  143. package/dist/esm/store/json-store.d.ts +5 -1
  144. package/dist/esm/store/json-store.d.ts.map +1 -1
  145. package/dist/esm/store/json-store.js +5 -1
  146. package/dist/esm/store/json-store.js.map +1 -1
  147. package/dist/esm/tool/delegate.js +1 -1
  148. package/dist/esm/tool/delegate.js.map +1 -1
  149. package/dist/esm/utils/date.d.ts +36 -0
  150. package/dist/esm/utils/date.d.ts.map +1 -0
  151. package/dist/esm/utils/date.js +48 -0
  152. package/dist/esm/utils/date.js.map +1 -0
  153. package/package.json +2 -2
@@ -0,0 +1,185 @@
1
+ // ============================================================
2
+ // REST 风格 A2A Remote Runner — 将 REST 风格远程 Agent 包装为 SubAgentRunner
3
+ // 供 delegate_task 使用,与标准 JSON-RPC A2A Runner 并行存在
4
+ // ============================================================
5
+ /**
6
+ * 创建 REST 风格远程 A2A Agent 的 SubAgentRunner
7
+ *
8
+ * 核心逻辑:
9
+ * - streaming=true && ctx.emitSubEvent 存在 → invokeStreaming + 事件转发
10
+ * - 否则 → invoke 同步阻塞
11
+ *
12
+ * 事件映射:
13
+ * - status(running) → start
14
+ * - stage → progress
15
+ * - tool_call → tool_call
16
+ * - tool_result → tool_result
17
+ * - message_chunk → content
18
+ * - result → done(由 runStreaming 提取最终文本)
19
+ * - error → error
20
+ */
21
+ export function createRestA2ARemoteRunner(config) {
22
+ const { baseUrl, capabilityId, name: runnerName, description: runnerDesc, client, staticFields = {}, taskField = 'analysis_focus', streaming = true, fallbackToSync = true, } = config;
23
+ /** 将 task 字符串构建为请求输入 */
24
+ function buildInput(task) {
25
+ return { ...staticFields, [taskField]: task };
26
+ }
27
+ /** 从 RestCapabilityResult 中提取最终文本 */
28
+ function extractResultText(result) {
29
+ if (result.result?.final_text)
30
+ return result.result.final_text;
31
+ if (result.result?.summary)
32
+ return result.result.summary;
33
+ // 降级:JSON 序列化整个 result
34
+ return JSON.stringify(result.result || result, null, 2);
35
+ }
36
+ // ---- 同步执行 ----
37
+ const runSync = async (task) => {
38
+ try {
39
+ const result = await client.invoke(baseUrl, capabilityId, buildInput(task));
40
+ if (result.status === 'failed' || result.error) {
41
+ const errMsg = result.error || 'Unknown error';
42
+ console.error(`[REST-A2A] '${runnerName}' sync failed: ${errMsg}`);
43
+ return `[REST A2A '${runnerName}'] Error: ${errMsg}`;
44
+ }
45
+ return extractResultText(result);
46
+ }
47
+ catch (err) {
48
+ console.error(`[REST-A2A] '${runnerName}' sync error: ${err?.message || err}`);
49
+ return `[REST A2A '${runnerName}'] Error: ${err.message || err}`;
50
+ }
51
+ };
52
+ // ---- 流式执行 ----
53
+ const runStreaming = async (task, ctx) => {
54
+ const emit = ctx.emitSubEvent;
55
+ const parentCallId = ctx.currentToolCallId || `rest_a2a_${Date.now()}`;
56
+ emitStart(emit, parentCallId, runnerName, task);
57
+ try {
58
+ const finalResult = await client.invokeStreaming(baseUrl, capabilityId, buildInput(task), (event) => forwardRestSSEEvent(emit, parentCallId, runnerName, event), ctx.abortSignal);
59
+ if (finalResult?.status === 'failed' || finalResult?.error) {
60
+ const errMsg = finalResult.error || 'Remote agent returned error';
61
+ console.error(`[REST-A2A] '${runnerName}' failed: ${errMsg}`);
62
+ emitError(emit, parentCallId, runnerName, errMsg);
63
+ return `[REST A2A '${runnerName}'] Error: ${errMsg}`;
64
+ }
65
+ const resultText = finalResult
66
+ ? extractResultText(finalResult)
67
+ : 'Task completed with no output.';
68
+ emitDone(emit, parentCallId, runnerName, resultText.length);
69
+ return resultText;
70
+ }
71
+ catch (err) {
72
+ const errMsg = err?.message || String(err);
73
+ console.error(`[REST-A2A] '${runnerName}' streaming error: ${errMsg}`);
74
+ // 流式失败,自动回退到同步模式
75
+ if (fallbackToSync) {
76
+ console.log(`[REST-A2A] '${runnerName}' falling back to sync`);
77
+ return runSync(task);
78
+ }
79
+ emitError(emit, parentCallId, runnerName, errMsg);
80
+ return `[REST A2A '${runnerName}'] Error: ${errMsg}`;
81
+ }
82
+ };
83
+ return {
84
+ name: runnerName,
85
+ description: runnerDesc,
86
+ run: async (task, ctx, _options) => {
87
+ return streaming && ctx?.emitSubEvent
88
+ ? runStreaming(task, ctx)
89
+ : runSync(task);
90
+ },
91
+ };
92
+ }
93
+ // ---- 事件转发辅助函数 ----
94
+ function emitStart(emit, parentCallId, agentName, task) {
95
+ emit?.({
96
+ parentToolCallId: parentCallId,
97
+ agentName,
98
+ eventType: 'start',
99
+ data: { agentName, task, source: 'rest-a2a-streaming' },
100
+ });
101
+ }
102
+ function emitDone(emit, parentCallId, agentName, contentLength) {
103
+ emit?.({
104
+ parentToolCallId: parentCallId,
105
+ agentName,
106
+ eventType: 'done',
107
+ data: { contentLength },
108
+ });
109
+ }
110
+ function emitError(emit, parentCallId, agentName, error) {
111
+ emit?.({
112
+ parentToolCallId: parentCallId,
113
+ agentName,
114
+ eventType: 'error',
115
+ data: { error },
116
+ });
117
+ }
118
+ /**
119
+ * 将 REST 风格 SSE 事件转发为 SubAgentEvent
120
+ *
121
+ * 事件映射:
122
+ * status(running) → progress
123
+ * stage → progress
124
+ * tool_call → tool_call
125
+ * tool_result → tool_result
126
+ * message_chunk → content
127
+ * result → (由 runStreaming 处理,不转发)
128
+ * error → error
129
+ */
130
+ function forwardRestSSEEvent(emit, parentCallId, agentName, event) {
131
+ const { event: eventType, data } = event;
132
+ switch (eventType) {
133
+ case 'status':
134
+ // running → progress;completed → 由 result 事件处理
135
+ if (data.status === 'running') {
136
+ emit?.({
137
+ parentToolCallId: parentCallId,
138
+ agentName,
139
+ eventType: 'progress',
140
+ data: { status: 'running', trace_id: data.trace_id },
141
+ });
142
+ }
143
+ break;
144
+ case 'stage':
145
+ emit?.({
146
+ parentToolCallId: parentCallId,
147
+ agentName,
148
+ eventType: 'progress',
149
+ data: { stage: data.stage, trace_id: data.trace_id },
150
+ });
151
+ break;
152
+ case 'tool_call':
153
+ emit?.({
154
+ parentToolCallId: parentCallId,
155
+ agentName,
156
+ eventType: 'tool_call',
157
+ data,
158
+ });
159
+ break;
160
+ case 'tool_result':
161
+ emit?.({
162
+ parentToolCallId: parentCallId,
163
+ agentName,
164
+ eventType: 'tool_result',
165
+ data,
166
+ });
167
+ break;
168
+ case 'message_chunk':
169
+ emit?.({
170
+ parentToolCallId: parentCallId,
171
+ agentName,
172
+ eventType: 'content',
173
+ data: { content: data.content },
174
+ });
175
+ break;
176
+ // result 和 error 由 runStreaming 的 finalResult 处理,不在此转发
177
+ case 'result':
178
+ case 'error':
179
+ break;
180
+ default:
181
+ // 未知事件类型 → 丢弃
182
+ break;
183
+ }
184
+ }
185
+ //# sourceMappingURL=rest-runner.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"rest-runner.js","sourceRoot":"","sources":["../../../src/a2a/rest-runner.ts"],"names":[],"mappings":"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"}
@@ -68,6 +68,23 @@ export declare class AgentEngine {
68
68
  private resolveSkill;
69
69
  /** 构建初始消息列表(system + history + user) */
70
70
  private buildInitialMessages;
71
+ /**
72
+ * 将对话历史压缩为摘要,用 <conversation_history> XML 标签包裹,
73
+ * 注入为 user 消息 + assistant 确认对。
74
+ *
75
+ * 摘要是陈述句,当前消息是指令句,语义上天然区分。
76
+ * ≤2 条消息时直接拼接,省去 LLM 调用;LLM 失败时回退到简单拼接。
77
+ *
78
+ * @param history 历史消息列表
79
+ * @param currentText 当前用户消息文本(用于排除最后一条匹配的 user 消息,null 表示全部纳入摘要)
80
+ * @returns 摘要消息对 [user: <conversation_history>..., assistant: 确认]
81
+ */
82
+ private buildHistoryMessages;
83
+ /**
84
+ * 通过轻量 LLM 调用将多轮对话历史压缩为 ≤800 字的陈述句摘要。
85
+ * LLM 失败时回退到简单拼接。
86
+ */
87
+ private summarizeHistory;
71
88
  /** 获取工具定义列表(含预过滤 + 渐进式发现) */
72
89
  private resolveToolDefs;
73
90
  /** 构建 ToolContext */
@@ -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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"}
@@ -36,6 +36,43 @@ function buildSkillCatalog(skillManager) {
36
36
  const lines = manifests.map(m => `- \`${m.id}\`: ${m.name} — ${m.description || '(no description)'}`);
37
37
  return `## Available Skills\n\nYou have access to the following skills. Use \`load_skill(skill_id="<id>")\` to load full instructions when a task matches a skill's purpose.\n\n${lines.join('\n')}`;
38
38
  }
39
+ /**
40
+ * 生成工具调用的简短摘要(用于历史摘要)。
41
+ * 保留工具名和关键参数,让 LLM 在后续轮次中能复用之前的调用参数。
42
+ */
43
+ function summarizeToolCall(name, args) {
44
+ switch (name) {
45
+ case 'send_email': {
46
+ const to = Array.isArray(args.to) ? args.to.join(',') : (args.to || '');
47
+ const subject = (args.subject || '').slice(0, 60);
48
+ const hasAttachments = Array.isArray(args.attachments) && args.attachments.length > 0;
49
+ return `send_email(to=${to}, subject="${subject}"${hasAttachments ? ', with_attachments' : ''})`;
50
+ }
51
+ case 'delegate_task': {
52
+ const agent = args.agent || '';
53
+ const task = typeof args.task === 'string' ? args.task.slice(0, 80) : '';
54
+ return `delegate_task(agent=${agent}, task="${task}...")`;
55
+ }
56
+ case 'create_report':
57
+ case 'create_presentation':
58
+ case 'create_document':
59
+ case 'create_pdf':
60
+ case 'save_artifact': {
61
+ const filename = (args.filename || args.title || '').slice(0, 60);
62
+ return `${name}(filename="${filename}")`;
63
+ }
64
+ default: {
65
+ // 通用:展示前 3 个参数的键名和值摘要
66
+ const keys = Object.keys(args).slice(0, 3);
67
+ const parts = keys.map(k => {
68
+ const v = args[k];
69
+ const s = typeof v === 'string' ? v.slice(0, 50) : JSON.stringify(v)?.slice(0, 50);
70
+ return `${k}=${s}`;
71
+ });
72
+ return `${name}(${parts.join(', ')})`;
73
+ }
74
+ }
75
+ }
39
76
  export class AgentEngine {
40
77
  config;
41
78
  /** sessionId → AgentStatus */
@@ -214,7 +251,7 @@ export class AgentEngine {
214
251
  return { actualMessage: options.message, skillMatched: false };
215
252
  }
216
253
  /** 构建初始消息列表(system + history + user) */
217
- buildInitialMessages(options, skillContent, actualMessage, skillMatched) {
254
+ async buildInitialMessages(options, skillContent, actualMessage, skillMatched) {
218
255
  const { sessionId } = options;
219
256
  let systemPrompt = this.config.basePrompt || '';
220
257
  if (options.systemPromptOverride) {
@@ -257,8 +294,9 @@ export class AgentEngine {
257
294
  systemPrompt += PROGRESS_NARRATION_RULES;
258
295
  }
259
296
  }
260
- // 去重:如果历史最后一条已经是当前用户消息(由 buildThreadHistory 从 DB 恢复),
261
- // 则不再重复追加,避免 LLM 看到两条连续相同的 user 消息导致意图理解偏差
297
+ // 去重检测:如果历史最后一条已经是当前用户消息(由 buildThreadHistory 从 DB 恢复),
298
+ // 则在构建历史摘要时排除该消息,但仍需将当前用户消息作为 userMessage 传入。
299
+ // 之前的实现将 userMessage 设为 undefined,导致 LLM 收不到用户实际请求 → 空响应。
262
300
  const history = this.historyManager.getHistory(sessionId);
263
301
  const lastMsg = history.length > 0 ? history[history.length - 1] : null;
264
302
  // 多模态消息去重:比较文本内容
@@ -266,17 +304,158 @@ export class AgentEngine {
266
304
  const isDuplicateUserMessage = lastMsg?.role === 'user'
267
305
  && typeof lastMsg.content === 'string'
268
306
  && lastMsg.content === actualText;
307
+ if (isDuplicateUserMessage) {
308
+ console.log(`[Engine] Detected duplicate user message in history (likely restored from DB), excluding from summary and re-appending as userMessage`);
309
+ }
269
310
  // 非破坏性上下文投影(Context Collapse):
270
311
  // 当历史 token 超过预算时,使用投影而非破坏性压缩,保留原始历史完整
271
312
  const maxContextTokens = this.config.contextBudget?.maxContextTokens ?? 100000;
272
313
  const projectedHistory = this.historyManager.getProjectedHistory
273
314
  ? this.historyManager.getProjectedHistory(sessionId, maxContextTokens)
274
315
  : history;
316
+ // 历史摘要 + XML 标签:将多轮对话历史压缩为 ≤300 字摘要,
317
+ // 用 <conversation_history> 标签包裹注入为 user+assistant 消息对。
318
+ // 摘要是陈述句,当前消息是指令句,语义上天然区分,从结构上消除混淆。
319
+ // 当 isDuplicateUserMessage 时,buildHistoryMessages 会从摘要中排除当前消息,
320
+ // 然后通过 userMessage 参数重新追加,确保 LLM 能看到用户的实际请求。
321
+ const historyMessages = isDuplicateUserMessage
322
+ ? await this.buildHistoryMessages(projectedHistory, actualText)
323
+ : await this.buildHistoryMessages(projectedHistory, null);
275
324
  return this.config.contextManager.buildMessages({
276
325
  systemPrompt,
277
- history: projectedHistory,
278
- userMessage: isDuplicateUserMessage ? undefined : actualMessage,
326
+ history: historyMessages,
327
+ userMessage: actualMessage,
328
+ });
329
+ }
330
+ /**
331
+ * 将对话历史压缩为摘要,用 <conversation_history> XML 标签包裹,
332
+ * 注入为 user 消息 + assistant 确认对。
333
+ *
334
+ * 摘要是陈述句,当前消息是指令句,语义上天然区分。
335
+ * ≤2 条消息时直接拼接,省去 LLM 调用;LLM 失败时回退到简单拼接。
336
+ *
337
+ * @param history 历史消息列表
338
+ * @param currentText 当前用户消息文本(用于排除最后一条匹配的 user 消息,null 表示全部纳入摘要)
339
+ * @returns 摘要消息对 [user: <conversation_history>..., assistant: 确认]
340
+ */
341
+ async buildHistoryMessages(history, currentText) {
342
+ if (history.length === 0)
343
+ return [];
344
+ // 排除最后一条与当前消息匹配的 user 消息(避免重复)
345
+ let effectiveHistory = history;
346
+ if (currentText) {
347
+ for (let i = history.length - 1; i >= 0; i--) {
348
+ if (history[i].role === 'user') {
349
+ const text = typeof history[i].content === 'string'
350
+ ? history[i].content
351
+ : extractTextFromContent(history[i].content);
352
+ if (text === currentText) {
353
+ effectiveHistory = history.slice(0, i);
354
+ }
355
+ break;
356
+ }
357
+ }
358
+ }
359
+ if (effectiveHistory.length === 0)
360
+ return [];
361
+ // 提取纯文本用于摘要(包含工具调用和工具结果,否则 LLM 丢失关键上下文)
362
+ const textHistory = effectiveHistory
363
+ .map(m => {
364
+ if (m.role === 'tool') {
365
+ // 工具结果消息:保留状态和摘要
366
+ const text = typeof m.content === 'string'
367
+ ? m.content
368
+ : extractTextFromContent(m.content);
369
+ // 保留 artifact URL(关键!否则后续 send_email 无法引用已生成的报告)
370
+ const urlMatch = text.match(/(?:URL|url|artifact)[::]\s*(\/api\/[^\s"']+)/i);
371
+ const urlInfo = urlMatch ? ` [artifact_url: ${urlMatch[1]}]` : '';
372
+ const truncated = text.length > 150
373
+ ? text.slice(0, 150) + '...' + urlInfo
374
+ : text + urlInfo;
375
+ return `[工具结果]: ${truncated}`;
376
+ }
377
+ const text = typeof m.content === 'string'
378
+ ? m.content
379
+ : extractTextFromContent(m.content);
380
+ // 为 assistant 消息附加工具调用摘要(关键!否则 send_email 等参数完全丢失)
381
+ let toolInfo = '';
382
+ if (m.role === 'assistant' && m.tool_calls?.length) {
383
+ const callSummaries = m.tool_calls.map(tc => {
384
+ let args = {};
385
+ try {
386
+ args = JSON.parse(tc.function.arguments);
387
+ }
388
+ catch { /* ignore */ }
389
+ return summarizeToolCall(tc.function.name, args);
390
+ });
391
+ toolInfo = ` [调用: ${callSummaries.join('; ')}]`;
392
+ }
393
+ // 截断过长的内容(assistant 保留较短,因为工具调用信息更重要)
394
+ const maxLen = m.role === 'assistant' ? 150 : 200;
395
+ const truncated = text.length > maxLen
396
+ ? text.slice(0, maxLen) + '...'
397
+ : text;
398
+ return `${m.role === 'user' ? '用户' : '助手'}: ${truncated}${toolInfo}`;
279
399
  });
400
+ if (textHistory.length === 0)
401
+ return [];
402
+ // ≤2 条消息直接拼接,省去 LLM 调用
403
+ let summary;
404
+ if (textHistory.length <= 2) {
405
+ summary = textHistory.join('\n');
406
+ }
407
+ else {
408
+ summary = await this.summarizeHistory(textHistory);
409
+ }
410
+ // 用 XML 标签包裹,注入为 user + assistant 确认对
411
+ return [
412
+ {
413
+ role: 'user',
414
+ content: `<conversation_history>\n${summary}\n</conversation_history>`,
415
+ },
416
+ {
417
+ role: 'assistant',
418
+ content: '好的,我已了解之前的对话内容。请告诉我您现在的需求。',
419
+ },
420
+ ];
421
+ }
422
+ /**
423
+ * 通过轻量 LLM 调用将多轮对话历史压缩为 ≤800 字的陈述句摘要。
424
+ * LLM 失败时回退到简单拼接。
425
+ */
426
+ async summarizeHistory(textHistory) {
427
+ try {
428
+ const provider = this.config.llmProvider;
429
+ const prompt = `请将以下对话历史压缩为不超过800字的陈述句摘要。必须保留:
430
+ 1. 用户的核心请求和意图
431
+ 2. 助手调用了哪些工具(工具名+关键参数,如收件人邮箱、邮件主题等)
432
+ 3. 工具调用的结果(成功/失败)
433
+ 4. 已生成的内容摘要(如报告主题、数据要点)
434
+ 5. **artifact URL**(格式如 /api/work/artifacts/...),这是后续发送邮件等操作的关键引用
435
+
436
+ 不要添加任何评价或建议,只做客观压缩:
437
+
438
+ ${textHistory.join('\n')}`;
439
+ const response = await provider.chat({
440
+ messages: [
441
+ { role: 'system', content: '你是一个对话摘要助手。将对话历史压缩为简洁的陈述句摘要,不超过800字。务必保留工具调用名称和关键参数(如邮箱地址、邮件主题等),因为这些是后续操作的重要上下文。' },
442
+ { role: 'user', content: prompt },
443
+ ],
444
+ temperature: 0.3,
445
+ maxTokens: 1000,
446
+ });
447
+ const rawContent = response.choices?.[0]?.message?.content;
448
+ const summary = typeof rawContent === 'string' ? rawContent.trim() : '';
449
+ if (summary && summary.length > 0) {
450
+ return summary.length > 800 ? summary.slice(0, 800) + '...' : summary;
451
+ }
452
+ }
453
+ catch (e) {
454
+ console.warn('[Engine] summarizeHistory failed, falling back to concatenation:', e?.message || e);
455
+ }
456
+ // 回退:简单拼接,截断到 800 字
457
+ const fallback = textHistory.join('\n');
458
+ return fallback.length > 800 ? fallback.slice(0, 800) + '...' : fallback;
280
459
  }
281
460
  // ---- 工具定义 ----
282
461
  /** 获取工具定义列表(含预过滤 + 渐进式发现) */
@@ -342,7 +521,7 @@ export class AgentEngine {
342
521
  console.log(`[Engine] session=${options.sessionId} tools(${toolDefs.length}): ${toolDefs.map(t => t.function.name).join(', ') || '(none)'}`);
343
522
  return {
344
523
  sessionId: options.sessionId,
345
- messages: this.buildInitialMessages(options, skillContent, actualMessage, skillMatched),
524
+ messages: await this.buildInitialMessages(options, skillContent, actualMessage, skillMatched),
346
525
  toolDefs,
347
526
  toolContext: this.buildToolContext(options, internalAbort),
348
527
  internalAbort,
@@ -363,7 +542,6 @@ export class AgentEngine {
363
542
  async *agentLoop(state) {
364
543
  while (state.iteration < this.config.maxIterations) {
365
544
  state.iteration++;
366
- state.lastIterationHadToolCalls = false; // 每轮迭代重置,工具执行后才置 true
367
545
  // 中断检查
368
546
  if (state.internalAbort.signal.aborted) {
369
547
  yield this.emitDone(state);
@@ -422,13 +600,29 @@ export class AgentEngine {
422
600
  state.totalUsage.totalTokens += usage.totalTokens || 0;
423
601
  }
424
602
  if (!assistant) {
425
- // LLM 返回 null(上下文过长/错误)后,如果上一轮有工具调用,注入提示重试
426
- if (state.lastIterationHadToolCalls && state.continuationNudges < 2) {
427
- state.continuationNudges++;
428
- console.log(`[Engine] LLM returned null after tool calls, injecting continuation nudge #${state.continuationNudges} at iteration ${state.iteration}`);
429
- this.injectContinuationNudge(state);
603
+ // 所有 LLM provider 均已失败(callWithRetry 内部已重试+降级),
604
+ // 不再发 done 事件(callWithRetry 已发出带详细错误信息的 error 事件),
605
+ // 仅完成清理,避免 done 事件覆盖前端已展示的错误消息
606
+ console.warn(`[Engine] All LLM providers exhausted at iteration ${state.iteration}, stopping agent loop`);
607
+ this.statusMap.set(state.sessionId, 'idle');
608
+ // 记录 Token 用量到 UsageTracker
609
+ if (this.config.usageTracker && state.totalUsage) {
610
+ const modelInfo = this.config.llmProvider.getModel();
611
+ this.config.usageTracker.record({
612
+ sessionId: state.sessionId,
613
+ provider: modelInfo.provider,
614
+ model: modelInfo.model,
615
+ usage: state.totalUsage,
616
+ iterations: state.iteration,
617
+ });
430
618
  }
431
- continue;
619
+ this.runHooks('on_complete', {
620
+ sessionId: state.sessionId,
621
+ iteration: state.iteration,
622
+ totalIterations: state.iteration,
623
+ usage: state.totalUsage,
624
+ }).catch(() => { });
625
+ return;
432
626
  }
433
627
  // Hook: after_ll_call
434
628
  await this.runHooks('after_ll_call', {
@@ -454,6 +648,7 @@ export class AgentEngine {
454
648
  if (!assistant.tool_calls?.length) {
455
649
  if (state.lastIterationHadToolCalls && state.continuationNudges < 2) {
456
650
  state.continuationNudges++;
651
+ state.lastIterationHadToolCalls = false; // 已消费标记,避免重复注入
457
652
  console.log(`[Engine] LLM stopped after tool calls without new tool_calls, injecting continuation nudge #${state.continuationNudges} at iteration ${state.iteration}`);
458
653
  this.injectContinuationNudge(state);
459
654
  // 不退出,继续下一轮迭代让 LLM 继续执行任务