@zhin.js/core 1.0.26 → 1.0.28
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/CHANGELOG.md +18 -0
- package/lib/ai/agent.d.ts.map +1 -1
- package/lib/ai/agent.js +4 -0
- package/lib/ai/agent.js.map +1 -1
- package/lib/ai/bootstrap.d.ts +82 -0
- package/lib/ai/bootstrap.d.ts.map +1 -0
- package/lib/ai/bootstrap.js +199 -0
- package/lib/ai/bootstrap.js.map +1 -0
- package/lib/ai/builtin-tools.d.ts +36 -0
- package/lib/ai/builtin-tools.d.ts.map +1 -0
- package/lib/ai/builtin-tools.js +509 -0
- package/lib/ai/builtin-tools.js.map +1 -0
- package/lib/ai/compaction.d.ts +132 -0
- package/lib/ai/compaction.d.ts.map +1 -0
- package/lib/ai/compaction.js +370 -0
- package/lib/ai/compaction.js.map +1 -0
- package/lib/ai/hooks.d.ts +143 -0
- package/lib/ai/hooks.d.ts.map +1 -0
- package/lib/ai/hooks.js +108 -0
- package/lib/ai/hooks.js.map +1 -0
- package/lib/ai/index.d.ts +6 -0
- package/lib/ai/index.d.ts.map +1 -1
- package/lib/ai/index.js +6 -0
- package/lib/ai/index.js.map +1 -1
- package/lib/ai/init.d.ts.map +1 -1
- package/lib/ai/init.js +120 -3
- package/lib/ai/init.js.map +1 -1
- package/lib/ai/types.d.ts +2 -0
- package/lib/ai/types.d.ts.map +1 -1
- package/lib/ai/zhin-agent.d.ts +28 -1
- package/lib/ai/zhin-agent.d.ts.map +1 -1
- package/lib/ai/zhin-agent.js +196 -57
- package/lib/ai/zhin-agent.js.map +1 -1
- package/lib/built/config.d.ts +10 -0
- package/lib/built/config.d.ts.map +1 -1
- package/lib/built/config.js +54 -3
- package/lib/built/config.js.map +1 -1
- package/lib/built/tool.d.ts +6 -0
- package/lib/built/tool.d.ts.map +1 -1
- package/lib/built/tool.js +12 -0
- package/lib/built/tool.js.map +1 -1
- package/lib/cron.d.ts +0 -27
- package/lib/cron.d.ts.map +1 -1
- package/lib/cron.js +28 -27
- package/lib/cron.js.map +1 -1
- package/lib/types-generator.js +1 -1
- package/lib/types-generator.js.map +1 -1
- package/lib/types.d.ts +7 -0
- package/lib/types.d.ts.map +1 -1
- package/package.json +6 -6
- package/src/ai/agent.ts +6 -0
- package/src/ai/bootstrap.ts +263 -0
- package/src/ai/builtin-tools.ts +569 -0
- package/src/ai/compaction.ts +529 -0
- package/src/ai/hooks.ts +223 -0
- package/src/ai/index.ts +58 -0
- package/src/ai/init.ts +127 -3
- package/src/ai/types.ts +2 -0
- package/src/ai/zhin-agent.ts +226 -54
- package/src/built/config.ts +53 -3
- package/src/built/tool.ts +12 -0
- package/src/cron.ts +28 -27
- package/src/types-generator.ts +1 -1
- package/src/types.ts +8 -0
- package/tests/adapter.test.ts +1 -1
- package/tests/config.test.ts +2 -2
- package/test/minimal-bot.ts +0 -31
- package/test/stress-test.ts +0 -123
|
@@ -0,0 +1,132 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Session Compaction — 会话压缩
|
|
3
|
+
*
|
|
4
|
+
* 借鉴 OpenClaw 的 compaction 设计,当对话历史超过上下文窗口限制时,
|
|
5
|
+
* 通过 LLM 生成摘要来压缩早期消息,保留最近的消息完整性。
|
|
6
|
+
*
|
|
7
|
+
* 核心理念:
|
|
8
|
+
* 1. Token 估算:用 chars/4 粗略估算 token 数
|
|
9
|
+
* 2. 分块(Chunk):按 token 预算将消息分成多块
|
|
10
|
+
* 3. 渐进式摘要:对每块分别生成摘要,再合并
|
|
11
|
+
* 4. 安全裕度:估算偏低时留 20% buffer
|
|
12
|
+
* 5. 降级策略:摘要失败时使用更粗糙的摘要
|
|
13
|
+
*/
|
|
14
|
+
import type { AIProvider, ChatMessage } from './types.js';
|
|
15
|
+
/** 默认上下文窗口大小(tokens) */
|
|
16
|
+
export declare const DEFAULT_CONTEXT_TOKENS = 128000;
|
|
17
|
+
/** 上下文窗口最低阈值 */
|
|
18
|
+
export declare const CONTEXT_WINDOW_HARD_MIN_TOKENS = 16000;
|
|
19
|
+
/** 上下文窗口警告阈值 */
|
|
20
|
+
export declare const CONTEXT_WINDOW_WARN_BELOW_TOKENS = 32000;
|
|
21
|
+
/** 基础分块比例 — 每块最多占上下文的 40% */
|
|
22
|
+
export declare const BASE_CHUNK_RATIO = 0.4;
|
|
23
|
+
/** 最小分块比例 */
|
|
24
|
+
export declare const MIN_CHUNK_RATIO = 0.15;
|
|
25
|
+
/** 安全裕度系数 — 20% buffer 补偿 estimateTokens 的低估 */
|
|
26
|
+
export declare const SAFETY_MARGIN = 1.2;
|
|
27
|
+
/**
|
|
28
|
+
* 估算单条消息的 token 数
|
|
29
|
+
* 使用 chars/4 粗略估算(对中文偏高,对英文偏低,但足够用于分块)
|
|
30
|
+
*/
|
|
31
|
+
export declare function estimateTokens(message: ChatMessage): number;
|
|
32
|
+
/**
|
|
33
|
+
* 估算消息列表的总 token 数
|
|
34
|
+
*/
|
|
35
|
+
export declare function estimateMessagesTokens(messages: ChatMessage[]): number;
|
|
36
|
+
/**
|
|
37
|
+
* 按 token 份额拆分消息
|
|
38
|
+
*/
|
|
39
|
+
export declare function splitMessagesByTokenShare(messages: ChatMessage[], parts?: number): ChatMessage[][];
|
|
40
|
+
/**
|
|
41
|
+
* 按最大 token 数拆分消息
|
|
42
|
+
*/
|
|
43
|
+
export declare function chunkMessagesByMaxTokens(messages: ChatMessage[], maxTokens: number): ChatMessage[][];
|
|
44
|
+
/**
|
|
45
|
+
* 计算自适应分块比例
|
|
46
|
+
* 消息越大,分块比例越小,避免超出模型限制
|
|
47
|
+
*/
|
|
48
|
+
export declare function computeAdaptiveChunkRatio(messages: ChatMessage[], contextWindow: number): number;
|
|
49
|
+
export type ContextWindowSource = 'config' | 'model' | 'default';
|
|
50
|
+
export interface ContextWindowInfo {
|
|
51
|
+
tokens: number;
|
|
52
|
+
source: ContextWindowSource;
|
|
53
|
+
}
|
|
54
|
+
export interface ContextWindowGuardResult extends ContextWindowInfo {
|
|
55
|
+
shouldWarn: boolean;
|
|
56
|
+
shouldBlock: boolean;
|
|
57
|
+
}
|
|
58
|
+
/**
|
|
59
|
+
* 解析上下文窗口大小
|
|
60
|
+
*/
|
|
61
|
+
export declare function resolveContextWindowTokens(configTokens?: number, modelContextWindow?: number): ContextWindowInfo;
|
|
62
|
+
/**
|
|
63
|
+
* 评估上下文窗口安全性
|
|
64
|
+
*/
|
|
65
|
+
export declare function evaluateContextWindowGuard(info: ContextWindowInfo): ContextWindowGuardResult;
|
|
66
|
+
/**
|
|
67
|
+
* 带降级的摘要生成
|
|
68
|
+
*
|
|
69
|
+
* 完整摘要失败时,尝试排除超大消息再摘要
|
|
70
|
+
*/
|
|
71
|
+
export declare function summarizeWithFallback(params: {
|
|
72
|
+
provider: AIProvider;
|
|
73
|
+
messages: ChatMessage[];
|
|
74
|
+
maxChunkTokens: number;
|
|
75
|
+
contextWindow: number;
|
|
76
|
+
previousSummary?: string;
|
|
77
|
+
customInstructions?: string;
|
|
78
|
+
}): Promise<string>;
|
|
79
|
+
/**
|
|
80
|
+
* 分阶段摘要 — 先将消息拆为多段分别摘要,再合并
|
|
81
|
+
*/
|
|
82
|
+
export declare function summarizeInStages(params: {
|
|
83
|
+
provider: AIProvider;
|
|
84
|
+
messages: ChatMessage[];
|
|
85
|
+
maxChunkTokens: number;
|
|
86
|
+
contextWindow: number;
|
|
87
|
+
previousSummary?: string;
|
|
88
|
+
customInstructions?: string;
|
|
89
|
+
parts?: number;
|
|
90
|
+
}): Promise<string>;
|
|
91
|
+
export interface PruneResult {
|
|
92
|
+
/** 保留的消息 */
|
|
93
|
+
messages: ChatMessage[];
|
|
94
|
+
/** 被丢弃的消息 */
|
|
95
|
+
droppedMessages: ChatMessage[];
|
|
96
|
+
/** 丢弃的块数 */
|
|
97
|
+
droppedChunks: number;
|
|
98
|
+
/** 丢弃的消息数 */
|
|
99
|
+
droppedCount: number;
|
|
100
|
+
/** 丢弃的 token 数 */
|
|
101
|
+
droppedTokens: number;
|
|
102
|
+
/** 保留的 token 数 */
|
|
103
|
+
keptTokens: number;
|
|
104
|
+
/** 预算 token 数 */
|
|
105
|
+
budgetTokens: number;
|
|
106
|
+
}
|
|
107
|
+
/**
|
|
108
|
+
* 剪裁历史消息,使其不超过上下文预算
|
|
109
|
+
*
|
|
110
|
+
* 策略:从最旧的消息开始丢弃,直到 token 总量在预算内
|
|
111
|
+
*/
|
|
112
|
+
export declare function pruneHistoryForContext(params: {
|
|
113
|
+
messages: ChatMessage[];
|
|
114
|
+
maxContextTokens: number;
|
|
115
|
+
maxHistoryShare?: number;
|
|
116
|
+
parts?: number;
|
|
117
|
+
}): PruneResult;
|
|
118
|
+
/**
|
|
119
|
+
* 对给定消息列表执行一次就地压缩,返回压缩后的摘要和保留消息
|
|
120
|
+
*/
|
|
121
|
+
export declare function compactSession(params: {
|
|
122
|
+
provider: AIProvider;
|
|
123
|
+
messages: ChatMessage[];
|
|
124
|
+
contextWindow?: number;
|
|
125
|
+
keepRecentCount?: number;
|
|
126
|
+
}): Promise<{
|
|
127
|
+
summary: string;
|
|
128
|
+
keptMessages: ChatMessage[];
|
|
129
|
+
compactedCount: number;
|
|
130
|
+
savedTokens: number;
|
|
131
|
+
}>;
|
|
132
|
+
//# sourceMappingURL=compaction.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"compaction.d.ts","sourceRoot":"","sources":["../../src/ai/compaction.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,370 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Session Compaction — 会话压缩
|
|
3
|
+
*
|
|
4
|
+
* 借鉴 OpenClaw 的 compaction 设计,当对话历史超过上下文窗口限制时,
|
|
5
|
+
* 通过 LLM 生成摘要来压缩早期消息,保留最近的消息完整性。
|
|
6
|
+
*
|
|
7
|
+
* 核心理念:
|
|
8
|
+
* 1. Token 估算:用 chars/4 粗略估算 token 数
|
|
9
|
+
* 2. 分块(Chunk):按 token 预算将消息分成多块
|
|
10
|
+
* 3. 渐进式摘要:对每块分别生成摘要,再合并
|
|
11
|
+
* 4. 安全裕度:估算偏低时留 20% buffer
|
|
12
|
+
* 5. 降级策略:摘要失败时使用更粗糙的摘要
|
|
13
|
+
*/
|
|
14
|
+
import { Logger } from '@zhin.js/logger';
|
|
15
|
+
const logger = new Logger(null, 'Compaction');
|
|
16
|
+
// ============================================================================
|
|
17
|
+
// 常量
|
|
18
|
+
// ============================================================================
|
|
19
|
+
/** 默认上下文窗口大小(tokens) */
|
|
20
|
+
export const DEFAULT_CONTEXT_TOKENS = 128_000;
|
|
21
|
+
/** 上下文窗口最低阈值 */
|
|
22
|
+
export const CONTEXT_WINDOW_HARD_MIN_TOKENS = 16_000;
|
|
23
|
+
/** 上下文窗口警告阈值 */
|
|
24
|
+
export const CONTEXT_WINDOW_WARN_BELOW_TOKENS = 32_000;
|
|
25
|
+
/** 基础分块比例 — 每块最多占上下文的 40% */
|
|
26
|
+
export const BASE_CHUNK_RATIO = 0.4;
|
|
27
|
+
/** 最小分块比例 */
|
|
28
|
+
export const MIN_CHUNK_RATIO = 0.15;
|
|
29
|
+
/** 安全裕度系数 — 20% buffer 补偿 estimateTokens 的低估 */
|
|
30
|
+
export const SAFETY_MARGIN = 1.2;
|
|
31
|
+
/** 默认摘要回退文本 */
|
|
32
|
+
const DEFAULT_SUMMARY_FALLBACK = '无历史记录。';
|
|
33
|
+
/** 摘要合并指令 */
|
|
34
|
+
const MERGE_SUMMARIES_INSTRUCTIONS = '将下面这些部分摘要合并为一份连贯的完整摘要。保留关键决定、TODO、未解决的问题和所有约束。';
|
|
35
|
+
// ============================================================================
|
|
36
|
+
// Token 估算
|
|
37
|
+
// ============================================================================
|
|
38
|
+
/**
|
|
39
|
+
* 估算单条消息的 token 数
|
|
40
|
+
* 使用 chars/4 粗略估算(对中文偏高,对英文偏低,但足够用于分块)
|
|
41
|
+
*/
|
|
42
|
+
export function estimateTokens(message) {
|
|
43
|
+
const content = typeof message.content === 'string'
|
|
44
|
+
? message.content
|
|
45
|
+
: JSON.stringify(message.content);
|
|
46
|
+
// 消息角色和格式开销约 4 tokens
|
|
47
|
+
return Math.ceil(content.length / 4) + 4;
|
|
48
|
+
}
|
|
49
|
+
/**
|
|
50
|
+
* 估算消息列表的总 token 数
|
|
51
|
+
*/
|
|
52
|
+
export function estimateMessagesTokens(messages) {
|
|
53
|
+
return messages.reduce((sum, m) => sum + estimateTokens(m), 0);
|
|
54
|
+
}
|
|
55
|
+
// ============================================================================
|
|
56
|
+
// 分块
|
|
57
|
+
// ============================================================================
|
|
58
|
+
/**
|
|
59
|
+
* 按 token 份额拆分消息
|
|
60
|
+
*/
|
|
61
|
+
export function splitMessagesByTokenShare(messages, parts = 2) {
|
|
62
|
+
if (messages.length === 0)
|
|
63
|
+
return [];
|
|
64
|
+
const normalizedParts = Math.min(Math.max(1, Math.floor(parts)), messages.length);
|
|
65
|
+
if (normalizedParts <= 1)
|
|
66
|
+
return [messages];
|
|
67
|
+
const totalTokens = estimateMessagesTokens(messages);
|
|
68
|
+
const targetTokens = totalTokens / normalizedParts;
|
|
69
|
+
const chunks = [];
|
|
70
|
+
let current = [];
|
|
71
|
+
let currentTokens = 0;
|
|
72
|
+
for (const message of messages) {
|
|
73
|
+
const msgTokens = estimateTokens(message);
|
|
74
|
+
if (chunks.length < normalizedParts - 1 &&
|
|
75
|
+
current.length > 0 &&
|
|
76
|
+
currentTokens + msgTokens > targetTokens) {
|
|
77
|
+
chunks.push(current);
|
|
78
|
+
current = [];
|
|
79
|
+
currentTokens = 0;
|
|
80
|
+
}
|
|
81
|
+
current.push(message);
|
|
82
|
+
currentTokens += msgTokens;
|
|
83
|
+
}
|
|
84
|
+
if (current.length > 0)
|
|
85
|
+
chunks.push(current);
|
|
86
|
+
return chunks;
|
|
87
|
+
}
|
|
88
|
+
/**
|
|
89
|
+
* 按最大 token 数拆分消息
|
|
90
|
+
*/
|
|
91
|
+
export function chunkMessagesByMaxTokens(messages, maxTokens) {
|
|
92
|
+
if (messages.length === 0)
|
|
93
|
+
return [];
|
|
94
|
+
const effectiveMax = Math.max(1, Math.floor(maxTokens / SAFETY_MARGIN));
|
|
95
|
+
const chunks = [];
|
|
96
|
+
let currentChunk = [];
|
|
97
|
+
let currentTokens = 0;
|
|
98
|
+
for (const message of messages) {
|
|
99
|
+
const msgTokens = estimateTokens(message);
|
|
100
|
+
if (currentChunk.length > 0 && currentTokens + msgTokens > effectiveMax) {
|
|
101
|
+
chunks.push(currentChunk);
|
|
102
|
+
currentChunk = [];
|
|
103
|
+
currentTokens = 0;
|
|
104
|
+
}
|
|
105
|
+
currentChunk.push(message);
|
|
106
|
+
currentTokens += msgTokens;
|
|
107
|
+
// 超大单条消息也要拆
|
|
108
|
+
if (msgTokens > effectiveMax) {
|
|
109
|
+
chunks.push(currentChunk);
|
|
110
|
+
currentChunk = [];
|
|
111
|
+
currentTokens = 0;
|
|
112
|
+
}
|
|
113
|
+
}
|
|
114
|
+
if (currentChunk.length > 0)
|
|
115
|
+
chunks.push(currentChunk);
|
|
116
|
+
return chunks;
|
|
117
|
+
}
|
|
118
|
+
/**
|
|
119
|
+
* 计算自适应分块比例
|
|
120
|
+
* 消息越大,分块比例越小,避免超出模型限制
|
|
121
|
+
*/
|
|
122
|
+
export function computeAdaptiveChunkRatio(messages, contextWindow) {
|
|
123
|
+
if (messages.length === 0)
|
|
124
|
+
return BASE_CHUNK_RATIO;
|
|
125
|
+
const totalTokens = estimateMessagesTokens(messages);
|
|
126
|
+
const avgTokens = totalTokens / messages.length;
|
|
127
|
+
const safeAvgTokens = avgTokens * SAFETY_MARGIN;
|
|
128
|
+
const avgRatio = safeAvgTokens / contextWindow;
|
|
129
|
+
if (avgRatio > 0.1) {
|
|
130
|
+
const reduction = Math.min(avgRatio * 2, BASE_CHUNK_RATIO - MIN_CHUNK_RATIO);
|
|
131
|
+
return Math.max(MIN_CHUNK_RATIO, BASE_CHUNK_RATIO - reduction);
|
|
132
|
+
}
|
|
133
|
+
return BASE_CHUNK_RATIO;
|
|
134
|
+
}
|
|
135
|
+
/**
|
|
136
|
+
* 解析上下文窗口大小
|
|
137
|
+
*/
|
|
138
|
+
export function resolveContextWindowTokens(configTokens, modelContextWindow) {
|
|
139
|
+
if (configTokens && configTokens > 0) {
|
|
140
|
+
return { tokens: Math.floor(configTokens), source: 'config' };
|
|
141
|
+
}
|
|
142
|
+
if (modelContextWindow && modelContextWindow > 0) {
|
|
143
|
+
return { tokens: Math.floor(modelContextWindow), source: 'model' };
|
|
144
|
+
}
|
|
145
|
+
return { tokens: DEFAULT_CONTEXT_TOKENS, source: 'default' };
|
|
146
|
+
}
|
|
147
|
+
/**
|
|
148
|
+
* 评估上下文窗口安全性
|
|
149
|
+
*/
|
|
150
|
+
export function evaluateContextWindowGuard(info) {
|
|
151
|
+
const tokens = Math.max(0, Math.floor(info.tokens));
|
|
152
|
+
return {
|
|
153
|
+
...info,
|
|
154
|
+
tokens,
|
|
155
|
+
shouldWarn: tokens > 0 && tokens < CONTEXT_WINDOW_WARN_BELOW_TOKENS,
|
|
156
|
+
shouldBlock: tokens > 0 && tokens < CONTEXT_WINDOW_HARD_MIN_TOKENS,
|
|
157
|
+
};
|
|
158
|
+
}
|
|
159
|
+
// ============================================================================
|
|
160
|
+
// 摘要生成
|
|
161
|
+
// ============================================================================
|
|
162
|
+
/**
|
|
163
|
+
* 通过 LLM 对消息进行摘要
|
|
164
|
+
*/
|
|
165
|
+
async function generateSummary(provider, messages, maxChunkTokens, previousSummary, customInstructions) {
|
|
166
|
+
const conversation = messages.map(m => {
|
|
167
|
+
const role = m.role === 'user' ? '用户' : m.role === 'assistant' ? '助手' : '系统';
|
|
168
|
+
const content = typeof m.content === 'string' ? m.content : JSON.stringify(m.content);
|
|
169
|
+
return `[${role}] ${content}`;
|
|
170
|
+
}).join('\n');
|
|
171
|
+
let systemPrompt = `你是一个对话摘要助手。请将以下对话压缩为简洁的摘要,保留:
|
|
172
|
+
- 关键决定和结论
|
|
173
|
+
- 未完成的 TODO 和待解决的问题
|
|
174
|
+
- 重要的用户偏好和约束
|
|
175
|
+
- 讨论的核心主题
|
|
176
|
+
|
|
177
|
+
摘要应该简洁但信息量大,便于后续对话能快速了解上下文。`;
|
|
178
|
+
if (customInstructions) {
|
|
179
|
+
systemPrompt += `\n\n额外要求:${customInstructions}`;
|
|
180
|
+
}
|
|
181
|
+
let userContent = '';
|
|
182
|
+
if (previousSummary) {
|
|
183
|
+
userContent += `之前的摘要:\n${previousSummary}\n\n`;
|
|
184
|
+
}
|
|
185
|
+
userContent += `新的对话内容:\n${conversation}\n\n请生成更新后的完整摘要。`;
|
|
186
|
+
try {
|
|
187
|
+
const response = await provider.chat({
|
|
188
|
+
model: provider.models[0],
|
|
189
|
+
messages: [
|
|
190
|
+
{ role: 'system', content: systemPrompt },
|
|
191
|
+
{ role: 'user', content: userContent },
|
|
192
|
+
],
|
|
193
|
+
});
|
|
194
|
+
const result = response.choices?.[0]?.message?.content;
|
|
195
|
+
return typeof result === 'string' ? result : DEFAULT_SUMMARY_FALLBACK;
|
|
196
|
+
}
|
|
197
|
+
catch (e) {
|
|
198
|
+
logger.warn(`摘要生成失败: ${e.message}`);
|
|
199
|
+
return DEFAULT_SUMMARY_FALLBACK;
|
|
200
|
+
}
|
|
201
|
+
}
|
|
202
|
+
/**
|
|
203
|
+
* 对消息分块摘要,支持降级
|
|
204
|
+
*/
|
|
205
|
+
async function summarizeChunks(params) {
|
|
206
|
+
if (params.messages.length === 0) {
|
|
207
|
+
return params.previousSummary ?? DEFAULT_SUMMARY_FALLBACK;
|
|
208
|
+
}
|
|
209
|
+
const chunks = chunkMessagesByMaxTokens(params.messages, params.maxChunkTokens);
|
|
210
|
+
let summary = params.previousSummary;
|
|
211
|
+
for (const chunk of chunks) {
|
|
212
|
+
summary = await generateSummary(params.provider, chunk, params.maxChunkTokens, summary, params.customInstructions);
|
|
213
|
+
}
|
|
214
|
+
return summary ?? DEFAULT_SUMMARY_FALLBACK;
|
|
215
|
+
}
|
|
216
|
+
/**
|
|
217
|
+
* 带降级的摘要生成
|
|
218
|
+
*
|
|
219
|
+
* 完整摘要失败时,尝试排除超大消息再摘要
|
|
220
|
+
*/
|
|
221
|
+
export async function summarizeWithFallback(params) {
|
|
222
|
+
if (params.messages.length === 0) {
|
|
223
|
+
return params.previousSummary ?? DEFAULT_SUMMARY_FALLBACK;
|
|
224
|
+
}
|
|
225
|
+
// 尝试完整摘要
|
|
226
|
+
try {
|
|
227
|
+
return await summarizeChunks(params);
|
|
228
|
+
}
|
|
229
|
+
catch (fullError) {
|
|
230
|
+
logger.warn(`完整摘要失败,尝试部分摘要: ${fullError.message}`);
|
|
231
|
+
}
|
|
232
|
+
// 降级:排除超大消息
|
|
233
|
+
const smallMessages = [];
|
|
234
|
+
const oversizedNotes = [];
|
|
235
|
+
for (const msg of params.messages) {
|
|
236
|
+
const tokens = estimateTokens(msg) * SAFETY_MARGIN;
|
|
237
|
+
if (tokens > params.contextWindow * 0.5) {
|
|
238
|
+
oversizedNotes.push(`[大型 ${msg.role} 消息 (~${Math.round(tokens / 1000)}K tokens) 已从摘要中省略]`);
|
|
239
|
+
}
|
|
240
|
+
else {
|
|
241
|
+
smallMessages.push(msg);
|
|
242
|
+
}
|
|
243
|
+
}
|
|
244
|
+
if (smallMessages.length > 0) {
|
|
245
|
+
try {
|
|
246
|
+
const partial = await summarizeChunks({ ...params, messages: smallMessages });
|
|
247
|
+
const notes = oversizedNotes.length > 0 ? `\n\n${oversizedNotes.join('\n')}` : '';
|
|
248
|
+
return partial + notes;
|
|
249
|
+
}
|
|
250
|
+
catch (partialError) {
|
|
251
|
+
logger.warn(`部分摘要也失败: ${partialError.message}`);
|
|
252
|
+
}
|
|
253
|
+
}
|
|
254
|
+
// 最终降级
|
|
255
|
+
return `上下文包含 ${params.messages.length} 条消息(${oversizedNotes.length} 条超大)。由于大小限制,摘要不可用。`;
|
|
256
|
+
}
|
|
257
|
+
/**
|
|
258
|
+
* 分阶段摘要 — 先将消息拆为多段分别摘要,再合并
|
|
259
|
+
*/
|
|
260
|
+
export async function summarizeInStages(params) {
|
|
261
|
+
const { messages } = params;
|
|
262
|
+
if (messages.length === 0) {
|
|
263
|
+
return params.previousSummary ?? DEFAULT_SUMMARY_FALLBACK;
|
|
264
|
+
}
|
|
265
|
+
const parts = Math.min(Math.max(1, params.parts ?? 2), messages.length);
|
|
266
|
+
const totalTokens = estimateMessagesTokens(messages);
|
|
267
|
+
// 消息太少,或 token 没超限,直接走单次摘要
|
|
268
|
+
if (parts <= 1 || messages.length < 4 || totalTokens <= params.maxChunkTokens) {
|
|
269
|
+
return summarizeWithFallback(params);
|
|
270
|
+
}
|
|
271
|
+
const splits = splitMessagesByTokenShare(messages, parts).filter(c => c.length > 0);
|
|
272
|
+
if (splits.length <= 1)
|
|
273
|
+
return summarizeWithFallback(params);
|
|
274
|
+
// 每段分别摘要
|
|
275
|
+
const partialSummaries = [];
|
|
276
|
+
for (const chunk of splits) {
|
|
277
|
+
partialSummaries.push(await summarizeWithFallback({
|
|
278
|
+
...params,
|
|
279
|
+
messages: chunk,
|
|
280
|
+
previousSummary: undefined,
|
|
281
|
+
}));
|
|
282
|
+
}
|
|
283
|
+
if (partialSummaries.length === 1)
|
|
284
|
+
return partialSummaries[0];
|
|
285
|
+
// 合并多段摘要
|
|
286
|
+
const summaryMessages = partialSummaries.map(s => ({
|
|
287
|
+
role: 'user',
|
|
288
|
+
content: s,
|
|
289
|
+
}));
|
|
290
|
+
const mergeInstructions = params.customInstructions
|
|
291
|
+
? `${MERGE_SUMMARIES_INSTRUCTIONS}\n\n额外要求:${params.customInstructions}`
|
|
292
|
+
: MERGE_SUMMARIES_INSTRUCTIONS;
|
|
293
|
+
return summarizeWithFallback({
|
|
294
|
+
...params,
|
|
295
|
+
messages: summaryMessages,
|
|
296
|
+
customInstructions: mergeInstructions,
|
|
297
|
+
});
|
|
298
|
+
}
|
|
299
|
+
/**
|
|
300
|
+
* 剪裁历史消息,使其不超过上下文预算
|
|
301
|
+
*
|
|
302
|
+
* 策略:从最旧的消息开始丢弃,直到 token 总量在预算内
|
|
303
|
+
*/
|
|
304
|
+
export function pruneHistoryForContext(params) {
|
|
305
|
+
const maxHistoryShare = params.maxHistoryShare ?? 0.5;
|
|
306
|
+
const budgetTokens = Math.max(1, Math.floor(params.maxContextTokens * maxHistoryShare));
|
|
307
|
+
let keptMessages = params.messages;
|
|
308
|
+
const allDropped = [];
|
|
309
|
+
let droppedChunks = 0;
|
|
310
|
+
let droppedCount = 0;
|
|
311
|
+
let droppedTokens = 0;
|
|
312
|
+
const parts = Math.min(Math.max(1, params.parts ?? 2), keptMessages.length);
|
|
313
|
+
while (keptMessages.length > 0 && estimateMessagesTokens(keptMessages) > budgetTokens) {
|
|
314
|
+
const chunks = splitMessagesByTokenShare(keptMessages, parts);
|
|
315
|
+
if (chunks.length <= 1)
|
|
316
|
+
break;
|
|
317
|
+
const [dropped, ...rest] = chunks;
|
|
318
|
+
keptMessages = rest.flat();
|
|
319
|
+
droppedChunks += 1;
|
|
320
|
+
droppedCount += dropped.length;
|
|
321
|
+
droppedTokens += estimateMessagesTokens(dropped);
|
|
322
|
+
allDropped.push(...dropped);
|
|
323
|
+
}
|
|
324
|
+
return {
|
|
325
|
+
messages: keptMessages,
|
|
326
|
+
droppedMessages: allDropped,
|
|
327
|
+
droppedChunks,
|
|
328
|
+
droppedCount,
|
|
329
|
+
droppedTokens,
|
|
330
|
+
keptTokens: estimateMessagesTokens(keptMessages),
|
|
331
|
+
budgetTokens,
|
|
332
|
+
};
|
|
333
|
+
}
|
|
334
|
+
// ============================================================================
|
|
335
|
+
// /compact 命令 — 主动压缩当前会话
|
|
336
|
+
// ============================================================================
|
|
337
|
+
/**
|
|
338
|
+
* 对给定消息列表执行一次就地压缩,返回压缩后的摘要和保留消息
|
|
339
|
+
*/
|
|
340
|
+
export async function compactSession(params) {
|
|
341
|
+
const contextWindow = params.contextWindow ?? DEFAULT_CONTEXT_TOKENS;
|
|
342
|
+
const keepRecentCount = params.keepRecentCount ?? 6;
|
|
343
|
+
const messages = params.messages;
|
|
344
|
+
if (messages.length <= keepRecentCount) {
|
|
345
|
+
return {
|
|
346
|
+
summary: '',
|
|
347
|
+
keptMessages: messages,
|
|
348
|
+
compactedCount: 0,
|
|
349
|
+
savedTokens: 0,
|
|
350
|
+
};
|
|
351
|
+
}
|
|
352
|
+
// 保留最近 N 条,其余摘要
|
|
353
|
+
const toCompact = messages.slice(0, messages.length - keepRecentCount);
|
|
354
|
+
const toKeep = messages.slice(messages.length - keepRecentCount);
|
|
355
|
+
const beforeTokens = estimateMessagesTokens(toCompact);
|
|
356
|
+
const maxChunkTokens = Math.floor(contextWindow * computeAdaptiveChunkRatio(toCompact, contextWindow));
|
|
357
|
+
const summary = await summarizeInStages({
|
|
358
|
+
provider: params.provider,
|
|
359
|
+
messages: toCompact,
|
|
360
|
+
maxChunkTokens,
|
|
361
|
+
contextWindow,
|
|
362
|
+
});
|
|
363
|
+
return {
|
|
364
|
+
summary,
|
|
365
|
+
keptMessages: toKeep,
|
|
366
|
+
compactedCount: toCompact.length,
|
|
367
|
+
savedTokens: beforeTokens - estimateTokens({ role: 'system', content: summary }),
|
|
368
|
+
};
|
|
369
|
+
}
|
|
370
|
+
//# sourceMappingURL=compaction.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"compaction.js","sourceRoot":"","sources":["../../src/ai/compaction.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,143 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* AI Hooks — AI 生命周期事件钩子系统
|
|
3
|
+
*
|
|
4
|
+
* 借鉴 OpenClaw 的 Hooks 设计,提供可扩展的事件驱动钩子:
|
|
5
|
+
*
|
|
6
|
+
* - message:received — 收到消息时触发
|
|
7
|
+
* - message:sent — 发送消息时触发
|
|
8
|
+
* - session:compact — 会话压缩时触发
|
|
9
|
+
* - session:new — 新建会话时触发
|
|
10
|
+
* - agent:bootstrap — Agent 初始化时触发
|
|
11
|
+
* - agent:tool-call — 工具调用时触发
|
|
12
|
+
*
|
|
13
|
+
* 钩子按注册顺序执行,错误不会中断其他钩子。
|
|
14
|
+
*/
|
|
15
|
+
/** 事件类型 */
|
|
16
|
+
export type AIHookEventType = 'message' | 'session' | 'agent' | 'tool';
|
|
17
|
+
/** Hook 事件基础接口 */
|
|
18
|
+
export interface AIHookEvent {
|
|
19
|
+
/** 事件类型 */
|
|
20
|
+
type: AIHookEventType;
|
|
21
|
+
/** 具体动作 */
|
|
22
|
+
action: string;
|
|
23
|
+
/** 会话标识 */
|
|
24
|
+
sessionId?: string;
|
|
25
|
+
/** 附加上下文 */
|
|
26
|
+
context: Record<string, unknown>;
|
|
27
|
+
/** 事件时间戳 */
|
|
28
|
+
timestamp: Date;
|
|
29
|
+
/** 钩子可以推送消息到这个数组 */
|
|
30
|
+
messages: string[];
|
|
31
|
+
}
|
|
32
|
+
/** 消息接收事件 */
|
|
33
|
+
export interface MessageReceivedEvent extends AIHookEvent {
|
|
34
|
+
type: 'message';
|
|
35
|
+
action: 'received';
|
|
36
|
+
context: {
|
|
37
|
+
from: string;
|
|
38
|
+
content: string;
|
|
39
|
+
platform: string;
|
|
40
|
+
channelId?: string;
|
|
41
|
+
messageId?: string;
|
|
42
|
+
};
|
|
43
|
+
}
|
|
44
|
+
/** 消息发送事件 */
|
|
45
|
+
export interface MessageSentEvent extends AIHookEvent {
|
|
46
|
+
type: 'message';
|
|
47
|
+
action: 'sent';
|
|
48
|
+
context: {
|
|
49
|
+
to: string;
|
|
50
|
+
content: string;
|
|
51
|
+
success: boolean;
|
|
52
|
+
error?: string;
|
|
53
|
+
platform: string;
|
|
54
|
+
};
|
|
55
|
+
}
|
|
56
|
+
/** 会话压缩事件 */
|
|
57
|
+
export interface SessionCompactEvent extends AIHookEvent {
|
|
58
|
+
type: 'session';
|
|
59
|
+
action: 'compact';
|
|
60
|
+
context: {
|
|
61
|
+
compactedCount: number;
|
|
62
|
+
savedTokens: number;
|
|
63
|
+
summary: string;
|
|
64
|
+
};
|
|
65
|
+
}
|
|
66
|
+
/** 会话新建事件 */
|
|
67
|
+
export interface SessionNewEvent extends AIHookEvent {
|
|
68
|
+
type: 'session';
|
|
69
|
+
action: 'new';
|
|
70
|
+
context: {
|
|
71
|
+
previousSessionId?: string;
|
|
72
|
+
};
|
|
73
|
+
}
|
|
74
|
+
/** Agent 初始化事件 */
|
|
75
|
+
export interface AgentBootstrapEvent extends AIHookEvent {
|
|
76
|
+
type: 'agent';
|
|
77
|
+
action: 'bootstrap';
|
|
78
|
+
context: {
|
|
79
|
+
workspaceDir: string;
|
|
80
|
+
toolCount: number;
|
|
81
|
+
skillCount: number;
|
|
82
|
+
bootstrapFiles: string[];
|
|
83
|
+
};
|
|
84
|
+
}
|
|
85
|
+
/** 工具调用事件 */
|
|
86
|
+
export interface ToolCallEvent extends AIHookEvent {
|
|
87
|
+
type: 'tool';
|
|
88
|
+
action: 'call';
|
|
89
|
+
context: {
|
|
90
|
+
toolName: string;
|
|
91
|
+
args: Record<string, unknown>;
|
|
92
|
+
result?: string;
|
|
93
|
+
durationMs?: number;
|
|
94
|
+
success: boolean;
|
|
95
|
+
error?: string;
|
|
96
|
+
};
|
|
97
|
+
}
|
|
98
|
+
/** Hook 处理函数 */
|
|
99
|
+
export type AIHookHandler = (event: AIHookEvent) => Promise<void> | void;
|
|
100
|
+
/**
|
|
101
|
+
* 注册 Hook 处理函数
|
|
102
|
+
*
|
|
103
|
+
* @param eventKey - 事件类型(如 'message')或具体动作(如 'message:received')
|
|
104
|
+
* @param handler - 处理函数
|
|
105
|
+
*
|
|
106
|
+
* @example
|
|
107
|
+
* ```ts
|
|
108
|
+
* // 监听所有消息事件
|
|
109
|
+
* registerAIHook('message', async (event) => {
|
|
110
|
+
* console.log('消息事件:', event.action);
|
|
111
|
+
* });
|
|
112
|
+
*
|
|
113
|
+
* // 只监听消息接收
|
|
114
|
+
* registerAIHook('message:received', async (event) => {
|
|
115
|
+
* console.log('收到消息:', event.context.content);
|
|
116
|
+
* });
|
|
117
|
+
* ```
|
|
118
|
+
*/
|
|
119
|
+
export declare function registerAIHook(eventKey: string, handler: AIHookHandler): () => void;
|
|
120
|
+
/**
|
|
121
|
+
* 注销 Hook 处理函数
|
|
122
|
+
*/
|
|
123
|
+
export declare function unregisterAIHook(eventKey: string, handler: AIHookHandler): void;
|
|
124
|
+
/**
|
|
125
|
+
* 清除所有 Hook(测试用)
|
|
126
|
+
*/
|
|
127
|
+
export declare function clearAIHooks(): void;
|
|
128
|
+
/**
|
|
129
|
+
* 获取已注册的事件 key 列表(调试用)
|
|
130
|
+
*/
|
|
131
|
+
export declare function getRegisteredAIHookKeys(): string[];
|
|
132
|
+
/**
|
|
133
|
+
* 触发 Hook 事件
|
|
134
|
+
*
|
|
135
|
+
* 同时调用通用类型(如 'message')和具体动作(如 'message:received')的处理函数。
|
|
136
|
+
* 处理函数按注册顺序执行,错误被捕获并记录,不影响其他处理函数。
|
|
137
|
+
*/
|
|
138
|
+
export declare function triggerAIHook(event: AIHookEvent): Promise<void>;
|
|
139
|
+
/**
|
|
140
|
+
* 创建 Hook 事件(辅助函数)
|
|
141
|
+
*/
|
|
142
|
+
export declare function createAIHookEvent(type: AIHookEventType, action: string, sessionId?: string, context?: Record<string, unknown>): AIHookEvent;
|
|
143
|
+
//# sourceMappingURL=hooks.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"hooks.d.ts","sourceRoot":"","sources":["../../src/ai/hooks.ts"],"names":[],"mappings":"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"}
|