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