@zhin.js/agent 0.1.0 → 0.1.3
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/lib/cron-engine.d.ts +20 -2
- package/lib/cron-engine.d.ts.map +1 -1
- package/lib/cron-engine.js +59 -18
- package/lib/cron-engine.js.map +1 -1
- package/lib/discover-skills.d.ts +3 -1
- package/lib/discover-skills.d.ts.map +1 -1
- package/lib/discover-skills.js +7 -9
- package/lib/discover-skills.js.map +1 -1
- package/lib/discover-tools.d.ts +1 -6
- package/lib/discover-tools.d.ts.map +1 -1
- package/lib/discover-tools.js +2 -6
- package/lib/discover-tools.js.map +1 -1
- package/lib/index.d.ts +2 -4
- package/lib/index.d.ts.map +1 -1
- package/lib/index.js +1 -2
- package/lib/index.js.map +1 -1
- package/lib/init/create-zhin-agent.d.ts.map +1 -1
- package/lib/init/create-zhin-agent.js +42 -20
- package/lib/init/create-zhin-agent.js.map +1 -1
- package/lib/init/register-ai-trigger.d.ts.map +1 -1
- package/lib/init/register-ai-trigger.js +10 -3
- package/lib/init/register-ai-trigger.js.map +1 -1
- package/lib/init/register-builtin-tools.d.ts.map +1 -1
- package/lib/init/register-builtin-tools.js +85 -15
- package/lib/init/register-builtin-tools.js.map +1 -1
- package/lib/init/register-db-models.d.ts.map +1 -1
- package/lib/init/register-db-models.js +1 -3
- package/lib/init/register-db-models.js.map +1 -1
- package/lib/init/register-db-upgrade.d.ts.map +1 -1
- package/lib/init/register-db-upgrade.js +1 -8
- package/lib/init/register-db-upgrade.js.map +1 -1
- package/lib/init/register-management-tools.d.ts.map +1 -1
- package/lib/init/register-management-tools.js +33 -20
- package/lib/init/register-management-tools.js.map +1 -1
- package/lib/service.d.ts.map +1 -1
- package/lib/service.js +0 -8
- package/lib/service.js.map +1 -1
- package/lib/zhin-agent/builtin-tools.d.ts +0 -2
- package/lib/zhin-agent/builtin-tools.d.ts.map +1 -1
- package/lib/zhin-agent/builtin-tools.js +0 -55
- package/lib/zhin-agent/builtin-tools.js.map +1 -1
- package/lib/zhin-agent/config.d.ts +2 -1
- package/lib/zhin-agent/config.d.ts.map +1 -1
- package/lib/zhin-agent/config.js +1 -1
- package/lib/zhin-agent/config.js.map +1 -1
- package/lib/zhin-agent/index.d.ts +1 -6
- package/lib/zhin-agent/index.d.ts.map +1 -1
- package/lib/zhin-agent/index.js +26 -34
- package/lib/zhin-agent/index.js.map +1 -1
- package/lib/zhin-agent/prompt.d.ts.map +1 -1
- package/lib/zhin-agent/prompt.js +31 -76
- package/lib/zhin-agent/prompt.js.map +1 -1
- package/lib/zhin-agent/tool-collector.d.ts.map +1 -1
- package/lib/zhin-agent/tool-collector.js +7 -7
- package/lib/zhin-agent/tool-collector.js.map +1 -1
- package/package.json +7 -4
- package/CHANGELOG.md +0 -190
- package/lib/follow-up.d.ts +0 -131
- package/lib/follow-up.d.ts.map +0 -1
- package/lib/follow-up.js +0 -265
- package/lib/follow-up.js.map +0 -1
- package/src/agent.ts +0 -6
- package/src/bootstrap.ts +0 -309
- package/src/builtin-tools.ts +0 -958
- package/src/compaction.ts +0 -28
- package/src/context-manager.ts +0 -15
- package/src/conversation-memory.ts +0 -5
- package/src/cron-engine.ts +0 -338
- package/src/discover-agents.ts +0 -138
- package/src/discover-skills.ts +0 -325
- package/src/discover-tools.ts +0 -302
- package/src/discovery-utils.ts +0 -96
- package/src/file-policy.ts +0 -333
- package/src/follow-up.ts +0 -357
- package/src/hooks.ts +0 -223
- package/src/index.ts +0 -183
- package/src/init/create-zhin-agent.ts +0 -161
- package/src/init/register-ai-service.ts +0 -53
- package/src/init/register-ai-trigger.ts +0 -253
- package/src/init/register-builtin-tools.ts +0 -308
- package/src/init/register-db-models.ts +0 -31
- package/src/init/register-db-upgrade.ts +0 -77
- package/src/init/register-management-tools.ts +0 -71
- package/src/init/register-message-recorder.ts +0 -31
- package/src/init/register-tool-service.ts +0 -9
- package/src/init/shared-refs.ts +0 -20
- package/src/init/types.ts +0 -18
- package/src/init.ts +0 -50
- package/src/output.ts +0 -15
- package/src/rate-limiter.ts +0 -5
- package/src/service.ts +0 -228
- package/src/session.ts +0 -13
- package/src/storage.ts +0 -9
- package/src/subagent.ts +0 -209
- package/src/tone-detector.ts +0 -5
- package/src/tools.ts +0 -214
- package/src/user-profile.ts +0 -182
- package/src/zhin-agent/builtin-tools.ts +0 -247
- package/src/zhin-agent/config.ts +0 -124
- package/src/zhin-agent/exec-policy.ts +0 -285
- package/src/zhin-agent/index.ts +0 -633
- package/src/zhin-agent/prompt.ts +0 -305
- package/src/zhin-agent/tool-collector.ts +0 -249
- package/tests/ai/follow-up.test.ts +0 -175
- package/tests/ai/integration.test.ts +0 -582
- package/tests/ai/multimodal.test.ts +0 -106
- package/tests/ai/setup.ts +0 -186
- package/tests/ai/subagent.test.ts +0 -270
- package/tests/ai/tools-builtin.test.ts +0 -310
- package/tests/ai/user-profile.test.ts +0 -73
- package/tests/ai/zhin-agent.test.ts +0 -306
- package/tests/exec-policy.test.ts +0 -355
- package/tests/file-policy.test.ts +0 -405
- package/tsconfig.json +0 -22
package/src/user-profile.ts
DELETED
|
@@ -1,182 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* UserProfileStore — 用户画像存储
|
|
3
|
-
*
|
|
4
|
-
* 持久化存储用户偏好和特征,让 AI 跨会话记住用户的个性化信息。
|
|
5
|
-
*
|
|
6
|
-
* ai_user_profiles 表:
|
|
7
|
-
* ┌──────────────────────────────────────────────────────────┐
|
|
8
|
-
* │ user_id | key | value | updated_at │
|
|
9
|
-
* │ u1 | name | 小明 | 1700000000 │
|
|
10
|
-
* │ u1 | style | 简洁正式 | 1700000001 │
|
|
11
|
-
* │ u1 | interests | 编程,天气 | 1700000010 │
|
|
12
|
-
* └──────────────────────────────────────────────────────────┘
|
|
13
|
-
*/
|
|
14
|
-
|
|
15
|
-
import { Logger } from '@zhin.js/core';
|
|
16
|
-
|
|
17
|
-
const logger = new Logger(null, 'UserProfile');
|
|
18
|
-
|
|
19
|
-
// ============================================================================
|
|
20
|
-
// 数据库模型
|
|
21
|
-
// ============================================================================
|
|
22
|
-
|
|
23
|
-
export const AI_USER_PROFILE_MODEL = {
|
|
24
|
-
user_id: { type: 'text' as const, nullable: false },
|
|
25
|
-
key: { type: 'text' as const, nullable: false },
|
|
26
|
-
value: { type: 'text' as const, nullable: false },
|
|
27
|
-
updated_at: { type: 'integer' as const, default: 0 },
|
|
28
|
-
};
|
|
29
|
-
|
|
30
|
-
// ============================================================================
|
|
31
|
-
// 类型
|
|
32
|
-
// ============================================================================
|
|
33
|
-
|
|
34
|
-
interface ProfileRecord {
|
|
35
|
-
id?: number;
|
|
36
|
-
user_id: string;
|
|
37
|
-
key: string;
|
|
38
|
-
value: string;
|
|
39
|
-
updated_at: number;
|
|
40
|
-
}
|
|
41
|
-
|
|
42
|
-
/**
|
|
43
|
-
* 数据库模型接口(与 RelatedModel 的链式查询 API 对齐)
|
|
44
|
-
*/
|
|
45
|
-
interface DbModel {
|
|
46
|
-
select(...fields: string[]): any; // 返回 Selection (thenable, 支持 .where())
|
|
47
|
-
create(data: Record<string, any>): Promise<any>;
|
|
48
|
-
update(data: Partial<any>): any; // 返回 Updation (thenable, 支持 .where())
|
|
49
|
-
delete(condition: Record<string, any>): any; // 返回 Deletion (thenable, 支持 .where())
|
|
50
|
-
}
|
|
51
|
-
|
|
52
|
-
// ============================================================================
|
|
53
|
-
// Store 接口
|
|
54
|
-
// ============================================================================
|
|
55
|
-
|
|
56
|
-
interface IProfileStore {
|
|
57
|
-
get(userId: string, key: string): Promise<string | null>;
|
|
58
|
-
getAll(userId: string): Promise<Record<string, string>>;
|
|
59
|
-
set(userId: string, key: string, value: string): Promise<void>;
|
|
60
|
-
delete(userId: string, key: string): Promise<boolean>;
|
|
61
|
-
dispose(): void;
|
|
62
|
-
}
|
|
63
|
-
|
|
64
|
-
// ============================================================================
|
|
65
|
-
// 内存实现
|
|
66
|
-
// ============================================================================
|
|
67
|
-
|
|
68
|
-
class MemoryProfileStore implements IProfileStore {
|
|
69
|
-
private data: Map<string, Map<string, string>> = new Map();
|
|
70
|
-
|
|
71
|
-
async get(userId: string, key: string): Promise<string | null> {
|
|
72
|
-
return this.data.get(userId)?.get(key) ?? null;
|
|
73
|
-
}
|
|
74
|
-
|
|
75
|
-
async getAll(userId: string): Promise<Record<string, string>> {
|
|
76
|
-
const map = this.data.get(userId);
|
|
77
|
-
if (!map) return {};
|
|
78
|
-
return Object.fromEntries(map);
|
|
79
|
-
}
|
|
80
|
-
|
|
81
|
-
async set(userId: string, key: string, value: string): Promise<void> {
|
|
82
|
-
let map = this.data.get(userId);
|
|
83
|
-
if (!map) { map = new Map(); this.data.set(userId, map); }
|
|
84
|
-
map.set(key, value);
|
|
85
|
-
}
|
|
86
|
-
|
|
87
|
-
async delete(userId: string, key: string): Promise<boolean> {
|
|
88
|
-
return this.data.get(userId)?.delete(key) ?? false;
|
|
89
|
-
}
|
|
90
|
-
|
|
91
|
-
dispose(): void { this.data.clear(); }
|
|
92
|
-
}
|
|
93
|
-
|
|
94
|
-
// ============================================================================
|
|
95
|
-
// 数据库实现
|
|
96
|
-
// ============================================================================
|
|
97
|
-
|
|
98
|
-
class DatabaseProfileStore implements IProfileStore {
|
|
99
|
-
constructor(private model: DbModel) {}
|
|
100
|
-
|
|
101
|
-
async get(userId: string, key: string): Promise<string | null> {
|
|
102
|
-
const records = await this.model.select().where({ user_id: userId, key }) as ProfileRecord[];
|
|
103
|
-
return records.length > 0 ? records[0].value : null;
|
|
104
|
-
}
|
|
105
|
-
|
|
106
|
-
async getAll(userId: string): Promise<Record<string, string>> {
|
|
107
|
-
const records = await this.model.select().where({ user_id: userId }) as ProfileRecord[];
|
|
108
|
-
const result: Record<string, string> = {};
|
|
109
|
-
for (const r of records) result[r.key] = r.value;
|
|
110
|
-
return result;
|
|
111
|
-
}
|
|
112
|
-
|
|
113
|
-
async set(userId: string, key: string, value: string): Promise<void> {
|
|
114
|
-
const existing = await this.model.select().where({ user_id: userId, key });
|
|
115
|
-
if (existing.length > 0) {
|
|
116
|
-
await this.model.update({ value, updated_at: Date.now() }).where({ user_id: userId, key });
|
|
117
|
-
} else {
|
|
118
|
-
await this.model.create({ user_id: userId, key, value, updated_at: Date.now() });
|
|
119
|
-
}
|
|
120
|
-
}
|
|
121
|
-
|
|
122
|
-
async delete(userId: string, key: string): Promise<boolean> {
|
|
123
|
-
const existing = await this.model.select().where({ user_id: userId, key });
|
|
124
|
-
if (existing.length === 0) return false;
|
|
125
|
-
await this.model.delete({ user_id: userId, key });
|
|
126
|
-
return true;
|
|
127
|
-
}
|
|
128
|
-
|
|
129
|
-
dispose(): void {}
|
|
130
|
-
}
|
|
131
|
-
|
|
132
|
-
// ============================================================================
|
|
133
|
-
// UserProfileStore
|
|
134
|
-
// ============================================================================
|
|
135
|
-
|
|
136
|
-
export class UserProfileStore {
|
|
137
|
-
private store: IProfileStore;
|
|
138
|
-
|
|
139
|
-
constructor() {
|
|
140
|
-
this.store = new MemoryProfileStore();
|
|
141
|
-
}
|
|
142
|
-
|
|
143
|
-
upgradeToDatabase(model: DbModel): void {
|
|
144
|
-
const old = this.store;
|
|
145
|
-
this.store = new DatabaseProfileStore(model);
|
|
146
|
-
old.dispose();
|
|
147
|
-
logger.debug('UserProfileStore: upgraded to database storage');
|
|
148
|
-
}
|
|
149
|
-
|
|
150
|
-
async get(userId: string, key: string): Promise<string | null> {
|
|
151
|
-
return this.store.get(userId, key);
|
|
152
|
-
}
|
|
153
|
-
|
|
154
|
-
async getAll(userId: string): Promise<Record<string, string>> {
|
|
155
|
-
return this.store.getAll(userId);
|
|
156
|
-
}
|
|
157
|
-
|
|
158
|
-
async set(userId: string, key: string, value: string): Promise<void> {
|
|
159
|
-
return this.store.set(userId, key, value);
|
|
160
|
-
}
|
|
161
|
-
|
|
162
|
-
async delete(userId: string, key: string): Promise<boolean> {
|
|
163
|
-
return this.store.delete(userId, key);
|
|
164
|
-
}
|
|
165
|
-
|
|
166
|
-
/**
|
|
167
|
-
* Build user profile summary for system prompt. Keys like "language" or "preferred_language"
|
|
168
|
-
* define the reply language preference; persona instructions use this for "Reply in the language specified in [User profile]".
|
|
169
|
-
*/
|
|
170
|
-
async buildProfileSummary(userId: string): Promise<string> {
|
|
171
|
-
const profile = await this.store.getAll(userId);
|
|
172
|
-
const entries = Object.entries(profile);
|
|
173
|
-
if (entries.length === 0) return '';
|
|
174
|
-
|
|
175
|
-
const lines = entries.map(([k, v]) => `- ${k}: ${v}`).join('\n');
|
|
176
|
-
return `[User profile]\n${lines}`;
|
|
177
|
-
}
|
|
178
|
-
|
|
179
|
-
dispose(): void {
|
|
180
|
-
this.store.dispose();
|
|
181
|
-
}
|
|
182
|
-
}
|
|
@@ -1,247 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* ZhinAgent 内置上下文工具工厂
|
|
3
|
-
*
|
|
4
|
-
* 这些工具依赖运行时上下文(sessionId / userId / context),
|
|
5
|
-
* 由 ZhinAgent.process() 按需创建并注入到工具列表中。
|
|
6
|
-
*/
|
|
7
|
-
|
|
8
|
-
import type { ToolContext } from '@zhin.js/core';
|
|
9
|
-
import type { AgentTool } from '@zhin.js/core';
|
|
10
|
-
import type { ConversationMemory } from '@zhin.js/ai';
|
|
11
|
-
import type { UserProfileStore } from '../user-profile.js';
|
|
12
|
-
import type { FollowUpManager } from '../follow-up.js';
|
|
13
|
-
import type { SubagentManager, SubagentOrigin } from '../subagent.js';
|
|
14
|
-
|
|
15
|
-
export function createChatHistoryTool(sessionId: string, memory: ConversationMemory): AgentTool {
|
|
16
|
-
return {
|
|
17
|
-
name: 'chat_history',
|
|
18
|
-
description: '搜索与用户的历史聊天记录。可以按关键词搜索,也可以按对话轮次范围查询。当用户问到"之前聊过什么""我们讨论过什么"等回忆类问题时使用。',
|
|
19
|
-
parameters: {
|
|
20
|
-
type: 'object',
|
|
21
|
-
properties: {
|
|
22
|
-
keyword: {
|
|
23
|
-
type: 'string',
|
|
24
|
-
description: '搜索关键词(模糊匹配消息内容和摘要)。留空则返回最近几轮记录',
|
|
25
|
-
},
|
|
26
|
-
from_round: {
|
|
27
|
-
type: 'number',
|
|
28
|
-
description: '起始轮次(与 to_round 配合使用,精确查询某段对话)',
|
|
29
|
-
},
|
|
30
|
-
to_round: {
|
|
31
|
-
type: 'number',
|
|
32
|
-
description: '结束轮次',
|
|
33
|
-
},
|
|
34
|
-
},
|
|
35
|
-
required: ['keyword'],
|
|
36
|
-
},
|
|
37
|
-
tags: ['memory', 'history', '聊天记录', '回忆', '之前'],
|
|
38
|
-
keywords: ['之前', '历史', '聊过', '讨论过', '记得', '上次', '以前', '回忆'],
|
|
39
|
-
async execute(args: Record<string, any>) {
|
|
40
|
-
const { keyword, from_round, to_round } = args;
|
|
41
|
-
const currentRound = await memory.getCurrentRound(sessionId);
|
|
42
|
-
|
|
43
|
-
if (keyword) {
|
|
44
|
-
const result = await memory.traceByKeyword(sessionId, keyword);
|
|
45
|
-
const msgs = result.messages.map(m => {
|
|
46
|
-
const role = m.role === 'user' ? '用户' : '助手';
|
|
47
|
-
const time = new Date(m.time).toLocaleString('zh-CN');
|
|
48
|
-
return `[第${m.round}轮 ${time}] ${role}: ${m.content}`;
|
|
49
|
-
}).join('\n');
|
|
50
|
-
|
|
51
|
-
let output = `当前是第 ${currentRound} 轮对话。\n\n`;
|
|
52
|
-
if (result.summary) {
|
|
53
|
-
output += `📋 找到相关摘要(覆盖第${result.summary.fromRound}-${result.summary.toRound}轮):\n${result.summary.summary}\n\n`;
|
|
54
|
-
}
|
|
55
|
-
output += msgs ? `💬 相关聊天记录:\n${msgs}` : '未找到包含该关键词的聊天记录。';
|
|
56
|
-
return output;
|
|
57
|
-
}
|
|
58
|
-
|
|
59
|
-
if (from_round != null && to_round != null) {
|
|
60
|
-
const messages = await memory.getMessagesByRound(sessionId, from_round, to_round);
|
|
61
|
-
if (messages.length === 0) {
|
|
62
|
-
return `第 ${from_round}-${to_round} 轮没有聊天记录。当前是第 ${currentRound} 轮。`;
|
|
63
|
-
}
|
|
64
|
-
const msgs = messages.map(m => {
|
|
65
|
-
const role = m.role === 'user' ? '用户' : '助手';
|
|
66
|
-
const time = new Date(m.time).toLocaleString('zh-CN');
|
|
67
|
-
return `[第${m.round}轮 ${time}] ${role}: ${m.content}`;
|
|
68
|
-
}).join('\n');
|
|
69
|
-
return `第 ${from_round}-${to_round} 轮聊天记录(当前第 ${currentRound} 轮):\n${msgs}`;
|
|
70
|
-
}
|
|
71
|
-
|
|
72
|
-
const messages = await memory.getMessagesByRound(
|
|
73
|
-
sessionId,
|
|
74
|
-
Math.max(1, currentRound - 4),
|
|
75
|
-
currentRound,
|
|
76
|
-
);
|
|
77
|
-
if (messages.length === 0) {
|
|
78
|
-
return '暂无聊天记录。';
|
|
79
|
-
}
|
|
80
|
-
const msgs = messages.map(m => {
|
|
81
|
-
const role = m.role === 'user' ? '用户' : '助手';
|
|
82
|
-
return `[第${m.round}轮] ${role}: ${m.content}`;
|
|
83
|
-
}).join('\n');
|
|
84
|
-
return `最近的聊天记录(当前第 ${currentRound} 轮):\n${msgs}`;
|
|
85
|
-
},
|
|
86
|
-
};
|
|
87
|
-
}
|
|
88
|
-
|
|
89
|
-
export function createUserProfileTool(userId: string, profiles: UserProfileStore): AgentTool {
|
|
90
|
-
return {
|
|
91
|
-
name: 'user_profile',
|
|
92
|
-
description: '读取或保存用户的个人偏好和信息。当用户告诉你他的名字、偏好、兴趣、习惯等个人信息时,用 set 操作保存。当需要了解用户偏好时,用 get 操作读取。',
|
|
93
|
-
parameters: {
|
|
94
|
-
type: 'object',
|
|
95
|
-
properties: {
|
|
96
|
-
action: {
|
|
97
|
-
type: 'string',
|
|
98
|
-
description: '操作类型: get(读取所有偏好), set(保存偏好), delete(删除偏好)',
|
|
99
|
-
enum: ['get', 'set', 'delete'],
|
|
100
|
-
},
|
|
101
|
-
key: {
|
|
102
|
-
type: 'string',
|
|
103
|
-
description: '偏好名称,如: name, style, interests, timezone, language 等',
|
|
104
|
-
},
|
|
105
|
-
value: {
|
|
106
|
-
type: 'string',
|
|
107
|
-
description: '偏好值(仅 set 操作需要)',
|
|
108
|
-
},
|
|
109
|
-
},
|
|
110
|
-
required: ['action'],
|
|
111
|
-
},
|
|
112
|
-
tags: ['profile', '偏好', '用户', '个性化', '记住'],
|
|
113
|
-
keywords: ['我叫', '我的名字', '记住我', '我喜欢', '我偏好', '我习惯', '叫我', '我是'],
|
|
114
|
-
async execute(args: Record<string, any>) {
|
|
115
|
-
const { action, key, value } = args;
|
|
116
|
-
|
|
117
|
-
switch (action) {
|
|
118
|
-
case 'get': {
|
|
119
|
-
const all = await profiles.getAll(userId);
|
|
120
|
-
const entries = Object.entries(all);
|
|
121
|
-
if (entries.length === 0) return '暂无保存的用户偏好。';
|
|
122
|
-
return '用户偏好:\n' + entries.map(([k, v]) => ` ${k}: ${v}`).join('\n');
|
|
123
|
-
}
|
|
124
|
-
case 'set': {
|
|
125
|
-
if (!key || !value) return '需要提供 key 和 value';
|
|
126
|
-
await profiles.set(userId, key, value);
|
|
127
|
-
return `已保存: ${key} = ${value}`;
|
|
128
|
-
}
|
|
129
|
-
case 'delete': {
|
|
130
|
-
if (!key) return '需要提供 key';
|
|
131
|
-
const deleted = await profiles.delete(userId, key);
|
|
132
|
-
return deleted ? `已删除: ${key}` : `未找到偏好: ${key}`;
|
|
133
|
-
}
|
|
134
|
-
default:
|
|
135
|
-
return '不支持的操作,请使用 get/set/delete';
|
|
136
|
-
}
|
|
137
|
-
},
|
|
138
|
-
};
|
|
139
|
-
}
|
|
140
|
-
|
|
141
|
-
export function createScheduleFollowUpTool(
|
|
142
|
-
sessionId: string,
|
|
143
|
-
context: ToolContext,
|
|
144
|
-
followUps: FollowUpManager,
|
|
145
|
-
): AgentTool {
|
|
146
|
-
const platform = context.platform || '';
|
|
147
|
-
const botId = context.botId || '';
|
|
148
|
-
const senderId = context.senderId || '';
|
|
149
|
-
const sceneId = context.sceneId || '';
|
|
150
|
-
const sceneType = context.message?.$channel?.type || 'private';
|
|
151
|
-
|
|
152
|
-
return {
|
|
153
|
-
name: 'schedule_followup',
|
|
154
|
-
description: '安排或取消定时跟进提醒。创建新提醒会自动取消之前的提醒。提醒持久保存,重启不丢失。',
|
|
155
|
-
parameters: {
|
|
156
|
-
type: 'object',
|
|
157
|
-
properties: {
|
|
158
|
-
action: {
|
|
159
|
-
type: 'string',
|
|
160
|
-
description: '操作类型: create(创建提醒,默认)或 cancel(取消当前会话所有提醒)',
|
|
161
|
-
enum: ['create', 'cancel'],
|
|
162
|
-
},
|
|
163
|
-
delay_minutes: {
|
|
164
|
-
type: 'number',
|
|
165
|
-
description: '延迟时间,单位是分钟。注意:3 就是 3 分钟,不是 3 小时。举例: 3 = 3分钟后, 60 = 1小时后, 1440 = 1天后',
|
|
166
|
-
},
|
|
167
|
-
message: {
|
|
168
|
-
type: 'string',
|
|
169
|
-
description: '提醒消息内容',
|
|
170
|
-
},
|
|
171
|
-
},
|
|
172
|
-
required: ['action'],
|
|
173
|
-
},
|
|
174
|
-
tags: ['reminder', '提醒', '跟进', '定时'],
|
|
175
|
-
keywords: ['提醒', '提醒我', '过一会', '过一小时', '明天', '跟进', '别忘了', '记得提醒', '取消提醒'],
|
|
176
|
-
async execute(args: Record<string, any>) {
|
|
177
|
-
const { action = 'create', delay_minutes, message: msg } = args;
|
|
178
|
-
|
|
179
|
-
if (action === 'cancel') {
|
|
180
|
-
const count = await followUps.cancelBySession(sessionId);
|
|
181
|
-
return count > 0
|
|
182
|
-
? `✅ 已取消 ${count} 个待执行的提醒`
|
|
183
|
-
: '当前没有待执行的提醒';
|
|
184
|
-
}
|
|
185
|
-
|
|
186
|
-
if (!delay_minutes || delay_minutes <= 0) return '延迟时间必须大于 0 分钟';
|
|
187
|
-
if (!msg) return '请提供提醒内容';
|
|
188
|
-
|
|
189
|
-
return followUps.schedule({
|
|
190
|
-
sessionId,
|
|
191
|
-
platform,
|
|
192
|
-
botId,
|
|
193
|
-
senderId,
|
|
194
|
-
sceneId,
|
|
195
|
-
sceneType,
|
|
196
|
-
message: msg,
|
|
197
|
-
delayMinutes: delay_minutes,
|
|
198
|
-
});
|
|
199
|
-
},
|
|
200
|
-
};
|
|
201
|
-
}
|
|
202
|
-
|
|
203
|
-
export function createSpawnTaskTool(
|
|
204
|
-
context: ToolContext,
|
|
205
|
-
manager: SubagentManager,
|
|
206
|
-
): AgentTool {
|
|
207
|
-
const platform = context.platform || '';
|
|
208
|
-
const botId = context.botId || '';
|
|
209
|
-
const senderId = context.senderId || '';
|
|
210
|
-
const sceneId = context.sceneId || '';
|
|
211
|
-
const sceneType = context.message?.$channel?.type || 'private';
|
|
212
|
-
|
|
213
|
-
return {
|
|
214
|
-
name: 'spawn_task',
|
|
215
|
-
description: '将复杂或耗时的任务交给后台子 agent 异步处理。子 agent 拥有文件读写、Shell、网络搜索等能力,完成后会自动通知用户。适用于需要多步操作的文件处理、代码分析、数据收集等任务。',
|
|
216
|
-
parameters: {
|
|
217
|
-
type: 'object',
|
|
218
|
-
properties: {
|
|
219
|
-
task: {
|
|
220
|
-
type: 'string',
|
|
221
|
-
description: '要交给子 agent 完成的任务描述(尽量详细,包含目标、范围、期望输出)',
|
|
222
|
-
},
|
|
223
|
-
label: {
|
|
224
|
-
type: 'string',
|
|
225
|
-
description: '任务的简短标签(用于显示,可选)',
|
|
226
|
-
},
|
|
227
|
-
},
|
|
228
|
-
required: ['task'],
|
|
229
|
-
},
|
|
230
|
-
tags: ['agent', 'async', 'task', '后台', '子任务'],
|
|
231
|
-
keywords: ['后台', '异步', '子任务', 'spawn', 'background', '并行', '独立处理'],
|
|
232
|
-
async execute(args: Record<string, any>) {
|
|
233
|
-
const { task, label } = args;
|
|
234
|
-
if (!task) return '请提供任务描述';
|
|
235
|
-
|
|
236
|
-
const origin: SubagentOrigin = {
|
|
237
|
-
platform,
|
|
238
|
-
botId,
|
|
239
|
-
sceneId,
|
|
240
|
-
senderId,
|
|
241
|
-
sceneType,
|
|
242
|
-
};
|
|
243
|
-
|
|
244
|
-
return manager.spawn({ task, label, origin });
|
|
245
|
-
},
|
|
246
|
-
};
|
|
247
|
-
}
|
package/src/zhin-agent/config.ts
DELETED
|
@@ -1,124 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* ZhinAgent 配置、常量、类型定义
|
|
3
|
-
*/
|
|
4
|
-
|
|
5
|
-
import type { RateLimitConfig } from '@zhin.js/ai';
|
|
6
|
-
import { DEFAULT_CONTEXT_TOKENS } from '@zhin.js/ai';
|
|
7
|
-
|
|
8
|
-
export type ModelSizeHint = 'small' | 'medium' | 'large';
|
|
9
|
-
|
|
10
|
-
const SMALL_MODEL_RE = /[:\-_](0\.5|1\.?[58]?|[3-8])b\b/i;
|
|
11
|
-
const MEDIUM_MODEL_RE = /[:\-_](14|[12][0-9]|32)b\b/i;
|
|
12
|
-
|
|
13
|
-
/**
|
|
14
|
-
* Infer model size from model name string.
|
|
15
|
-
* Pattern: `:8b` → small, `:14b` → medium, else large.
|
|
16
|
-
*/
|
|
17
|
-
export function inferModelSize(modelName: string): ModelSizeHint {
|
|
18
|
-
if (SMALL_MODEL_RE.test(modelName)) return 'small';
|
|
19
|
-
if (MEDIUM_MODEL_RE.test(modelName)) return 'medium';
|
|
20
|
-
return 'large';
|
|
21
|
-
}
|
|
22
|
-
|
|
23
|
-
/**
|
|
24
|
-
* Resolve the effective model size hint.
|
|
25
|
-
* Priority: explicit config > model name inference.
|
|
26
|
-
*/
|
|
27
|
-
export function resolveModelSize(config: Required<ZhinAgentConfig>, modelName: string): ModelSizeHint {
|
|
28
|
-
if (config.modelSizeHint && (config.modelSizeHint as string) !== '') return config.modelSizeHint as ModelSizeHint;
|
|
29
|
-
return inferModelSize(modelName);
|
|
30
|
-
}
|
|
31
|
-
|
|
32
|
-
/**
|
|
33
|
-
* Resolve the effective skill instruction max chars based on model size.
|
|
34
|
-
*/
|
|
35
|
-
export function resolveSkillInstructionMaxChars(config: Required<ZhinAgentConfig>, modelName: string): number {
|
|
36
|
-
if (config.skillInstructionMaxChars && config.skillInstructionMaxChars > 0) return config.skillInstructionMaxChars;
|
|
37
|
-
const size = resolveModelSize(config, modelName);
|
|
38
|
-
switch (size) {
|
|
39
|
-
case 'small': return 1500;
|
|
40
|
-
case 'medium': return 4000;
|
|
41
|
-
case 'large': return 8000;
|
|
42
|
-
}
|
|
43
|
-
}
|
|
44
|
-
|
|
45
|
-
export const SECTION_SEP = '\n\n---\n\n';
|
|
46
|
-
export const HISTORY_CONTEXT_MARKER = '[Chat messages since your last reply - for context]';
|
|
47
|
-
export const CURRENT_MESSAGE_MARKER = '[Current message - respond to this]';
|
|
48
|
-
|
|
49
|
-
export const PERM_MAP: Record<string, number> = {
|
|
50
|
-
user: 0,
|
|
51
|
-
group_admin: 1,
|
|
52
|
-
group_owner: 2,
|
|
53
|
-
bot_admin: 3,
|
|
54
|
-
owner: 4,
|
|
55
|
-
};
|
|
56
|
-
|
|
57
|
-
export type OnChunkCallback = (chunk: string, full: string) => void;
|
|
58
|
-
|
|
59
|
-
/** 上下文感知内置工具的关键词触发正则 */
|
|
60
|
-
export const KEYWORD_TRIGGERS = {
|
|
61
|
-
chatHistory: /之前|上次|历史|回忆|聊过|记录|还记得|曾经/i,
|
|
62
|
-
userProfile: /偏好|设置|配置|档案|资料|时区|timezone|profile|喜好|我叫|叫我|记住我/i,
|
|
63
|
-
scheduleFollowUp: /提醒|定时|过一会|跟进|别忘|取消提醒|reminder|分钟后|小时后/i,
|
|
64
|
-
spawnTask: /后台|子任务|spawn|异步|background|并行|独立处理/i,
|
|
65
|
-
} as const;
|
|
66
|
-
|
|
67
|
-
export interface ZhinAgentConfig {
|
|
68
|
-
persona?: string;
|
|
69
|
-
maxIterations?: number;
|
|
70
|
-
timeout?: number;
|
|
71
|
-
preExecTimeout?: number;
|
|
72
|
-
maxSkills?: number;
|
|
73
|
-
maxTools?: number;
|
|
74
|
-
minTopicRounds?: number;
|
|
75
|
-
slidingWindowSize?: number;
|
|
76
|
-
topicChangeThreshold?: number;
|
|
77
|
-
rateLimit?: RateLimitConfig;
|
|
78
|
-
toneAwareness?: boolean;
|
|
79
|
-
/** 聊天任务使用的模型(覆盖自动选择) */
|
|
80
|
-
chatModel?: string;
|
|
81
|
-
visionModel?: string;
|
|
82
|
-
contextTokens?: number;
|
|
83
|
-
maxHistoryShare?: number;
|
|
84
|
-
disabledTools?: string[];
|
|
85
|
-
allowedTools?: string[];
|
|
86
|
-
execSecurity?: 'deny' | 'allowlist' | 'full';
|
|
87
|
-
execPreset?: 'readonly' | 'network' | 'development' | 'custom';
|
|
88
|
-
execAllowlist?: string[];
|
|
89
|
-
execAsk?: boolean;
|
|
90
|
-
maxSubagentIterations?: number;
|
|
91
|
-
subagentTools?: string[];
|
|
92
|
-
/** 模型大小提示,影响技能指令截断长度。留空则根据模型名自动推断 */
|
|
93
|
-
modelSizeHint?: '' | 'small' | 'medium' | 'large';
|
|
94
|
-
/** 技能指令最大字符数(覆盖 modelSizeHint 推断值) */
|
|
95
|
-
skillInstructionMaxChars?: number;
|
|
96
|
-
}
|
|
97
|
-
|
|
98
|
-
export const DEFAULT_CONFIG: Required<ZhinAgentConfig> = {
|
|
99
|
-
persona: 'You are Zhin, an intelligent IM bot assistant that helps users with tasks through conversation. Use tools available to you to assist the user. You are running inside the Zhin.js framework.',
|
|
100
|
-
maxIterations: 5,
|
|
101
|
-
timeout: 60_000,
|
|
102
|
-
preExecTimeout: 10_000,
|
|
103
|
-
maxSkills: 3,
|
|
104
|
-
maxTools: 8,
|
|
105
|
-
minTopicRounds: 5,
|
|
106
|
-
slidingWindowSize: 5,
|
|
107
|
-
topicChangeThreshold: 0.15,
|
|
108
|
-
rateLimit: {},
|
|
109
|
-
toneAwareness: true,
|
|
110
|
-
chatModel: '',
|
|
111
|
-
visionModel: '',
|
|
112
|
-
contextTokens: DEFAULT_CONTEXT_TOKENS,
|
|
113
|
-
maxHistoryShare: 0.5,
|
|
114
|
-
disabledTools: [],
|
|
115
|
-
allowedTools: [],
|
|
116
|
-
execSecurity: 'deny',
|
|
117
|
-
execPreset: 'custom',
|
|
118
|
-
execAllowlist: [],
|
|
119
|
-
execAsk: false,
|
|
120
|
-
maxSubagentIterations: 15,
|
|
121
|
-
subagentTools: [],
|
|
122
|
-
modelSizeHint: '',
|
|
123
|
-
skillInstructionMaxChars: 0,
|
|
124
|
-
};
|