flowmind 1.0.1 → 1.1.0

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.
@@ -0,0 +1,320 @@
1
+ /**
2
+ * Model Manager - AI 模型管理器
3
+ * 管理多个模型 Provider,提供统一的调用接口
4
+ */
5
+
6
+ const OpenAIProvider = require('./providers/openai');
7
+ const AnthropicProvider = require('./providers/anthropic');
8
+ const OllamaProvider = require('./providers/ollama');
9
+ const GLMProvider = require('./providers/glm');
10
+ const MiMoProvider = require('./providers/mimo');
11
+ const QwenProvider = require('./providers/qwen');
12
+ const ERNIEProvider = require('./providers/ernie');
13
+ const DeepSeekProvider = require('./providers/deepseek');
14
+ const prompts = require('./prompts');
15
+
16
+ class ModelManager {
17
+ constructor(config = {}) {
18
+ this.config = config;
19
+ this.providers = new Map();
20
+ this.defaultProvider = config.defaultProvider || 'openai';
21
+ this.features = {
22
+ intentUnderstanding: true,
23
+ parameterExtraction: true,
24
+ skillSelection: true,
25
+ resultSummary: true,
26
+ learningFeedback: true,
27
+ ...config.features
28
+ };
29
+ this.fallbackToRules = config.fallbackToRules !== false;
30
+ this.initialized = false;
31
+ }
32
+
33
+ /**
34
+ * 初始化模型管理器
35
+ */
36
+ async init() {
37
+ // 注册内置 Provider
38
+ this._registerBuiltinProviders();
39
+
40
+ // 初始化配置的 Provider
41
+ const providersConfig = this.config.providers || {};
42
+ for (const [name, providerConfig] of Object.entries(providersConfig)) {
43
+ try {
44
+ await this.initProvider(name, providerConfig);
45
+ } catch (error) {
46
+ console.warn(`Failed to initialize provider ${name}: ${error.message}`);
47
+ }
48
+ }
49
+
50
+ this.initialized = true;
51
+ return this;
52
+ }
53
+
54
+ /**
55
+ * 注册内置 Provider
56
+ * @private
57
+ */
58
+ _registerBuiltinProviders() {
59
+ this.providerFactories = {
60
+ openai: (config) => new OpenAIProvider(config),
61
+ anthropic: (config) => new AnthropicProvider(config),
62
+ ollama: (config) => new OllamaProvider(config),
63
+ glm: (config) => new GLMProvider(config),
64
+ mimo: (config) => new MiMoProvider(config),
65
+ qwen: (config) => new QwenProvider(config),
66
+ ernie: (config) => new ERNIEProvider(config),
67
+ deepseek: (config) => new DeepSeekProvider(config)
68
+ };
69
+ }
70
+
71
+ /**
72
+ * 初始化指定 Provider
73
+ */
74
+ async initProvider(name, config = {}) {
75
+ const factory = this.providerFactories[name];
76
+ if (!factory) {
77
+ throw new Error(`Unknown provider: ${name}`);
78
+ }
79
+
80
+ const provider = factory(config);
81
+ await provider.init();
82
+ this.providers.set(name, provider);
83
+ return provider;
84
+ }
85
+
86
+ /**
87
+ * 注册自定义 Provider
88
+ */
89
+ registerProvider(name, provider) {
90
+ this.providers.set(name, provider);
91
+ }
92
+
93
+ /**
94
+ * 获取 Provider
95
+ */
96
+ getProvider(name) {
97
+ return this.providers.get(name || this.defaultProvider);
98
+ }
99
+
100
+ /**
101
+ * 获取默认 Provider
102
+ */
103
+ getDefaultProvider() {
104
+ const provider = this.getProvider(this.defaultProvider);
105
+ if (!provider) {
106
+ // 尝试获取任意可用的 Provider
107
+ for (const [name, p] of this.providers) {
108
+ if (p.initialized) return p;
109
+ }
110
+ return null;
111
+ }
112
+ return provider;
113
+ }
114
+
115
+ /**
116
+ * 理解用户意图
117
+ * @param {string} input - 用户输入
118
+ * @param {Object} context - 上下文
119
+ * @returns {Promise<Object>} 意图分析结果
120
+ */
121
+ async understandIntent(input, context = {}) {
122
+ if (!this.features.intentUnderstanding) {
123
+ return null;
124
+ }
125
+
126
+ const provider = this.getDefaultProvider();
127
+ if (!provider) {
128
+ return this._fallback('intentUnderstanding', input, context);
129
+ }
130
+
131
+ try {
132
+ const messages = [
133
+ { role: 'system', content: prompts.intent.system },
134
+ { role: 'user', content: prompts.intent.user(input, context) }
135
+ ];
136
+
137
+ const response = await provider.chat(messages, {
138
+ responseFormat: { type: 'json_object' }
139
+ });
140
+
141
+ return JSON.parse(response);
142
+ } catch (error) {
143
+ console.warn(`AI intent understanding failed: ${error.message}`);
144
+ return this._fallback('intentUnderstanding', input, context);
145
+ }
146
+ }
147
+
148
+ /**
149
+ * 提取参数
150
+ * @param {string} input - 用户输入
151
+ * @param {string} skillName - 技能名称
152
+ * @param {Object} skillSchema - 参数 schema
153
+ * @returns {Promise<Object>} 提取的参数
154
+ */
155
+ async extractParameters(input, skillName, skillSchema = {}) {
156
+ if (!this.features.parameterExtraction) {
157
+ return {};
158
+ }
159
+
160
+ const provider = this.getDefaultProvider();
161
+ if (!provider) {
162
+ return this._fallback('parameterExtraction', input, { skillName });
163
+ }
164
+
165
+ try {
166
+ const messages = [
167
+ { role: 'system', content: prompts.extraction.system },
168
+ { role: 'user', content: prompts.extraction.user(input, skillName, skillSchema) }
169
+ ];
170
+
171
+ const response = await provider.chat(messages, {
172
+ responseFormat: { type: 'json_object' }
173
+ });
174
+
175
+ return JSON.parse(response);
176
+ } catch (error) {
177
+ console.warn(`AI parameter extraction failed: ${error.message}`);
178
+ return this._fallback('parameterExtraction', input, { skillName });
179
+ }
180
+ }
181
+
182
+ /**
183
+ * 选择技能
184
+ * @param {string} input - 用户输入
185
+ * @param {Array} candidates - 候选技能列表
186
+ * @returns {Promise<Object>} 选择的技能
187
+ */
188
+ async selectSkill(input, candidates) {
189
+ if (!this.features.skillSelection) {
190
+ return null;
191
+ }
192
+
193
+ const provider = this.getDefaultProvider();
194
+ if (!provider) {
195
+ return this._fallback('skillSelection', input, { candidates });
196
+ }
197
+
198
+ try {
199
+ const messages = [
200
+ { role: 'system', content: prompts.selection.system },
201
+ { role: 'user', content: prompts.selection.user(input, candidates) }
202
+ ];
203
+
204
+ const response = await provider.chat(messages, {
205
+ responseFormat: { type: 'json_object' }
206
+ });
207
+
208
+ return JSON.parse(response);
209
+ } catch (error) {
210
+ console.warn(`AI skill selection failed: ${error.message}`);
211
+ return this._fallback('skillSelection', input, { candidates });
212
+ }
213
+ }
214
+
215
+ /**
216
+ * 生成结果摘要
217
+ * @param {Object} result - 执行结果
218
+ * @param {Object} context - 上下文
219
+ * @returns {Promise<string>} 摘要文本
220
+ */
221
+ async summarizeResult(result, context = {}) {
222
+ if (!this.features.resultSummary) {
223
+ return null;
224
+ }
225
+
226
+ const provider = this.getDefaultProvider();
227
+ if (!provider) {
228
+ return this._fallback('resultSummary', result, context);
229
+ }
230
+
231
+ try {
232
+ const messages = [
233
+ { role: 'system', content: prompts.summary.system },
234
+ { role: 'user', content: prompts.summary.user(result, context) }
235
+ ];
236
+
237
+ return await provider.chat(messages);
238
+ } catch (error) {
239
+ console.warn(`AI result summary failed: ${error.message}`);
240
+ return this._fallback('resultSummary', result, context);
241
+ }
242
+ }
243
+
244
+ /**
245
+ * 分析学习反馈
246
+ * @param {string} input - 用户输入
247
+ * @param {Object} context - 上下文
248
+ * @returns {Promise<Object>} 学习分析结果
249
+ */
250
+ async analyzeLearningFeedback(input, context = {}) {
251
+ if (!this.features.learningFeedback) {
252
+ return null;
253
+ }
254
+
255
+ const provider = this.getDefaultProvider();
256
+ if (!provider) {
257
+ return this._fallback('learningFeedback', input, context);
258
+ }
259
+
260
+ try {
261
+ const messages = [
262
+ { role: 'system', content: prompts.learning.system },
263
+ { role: 'user', content: prompts.learning.user(input, context) }
264
+ ];
265
+
266
+ const response = await provider.chat(messages, {
267
+ responseFormat: { type: 'json_object' }
268
+ });
269
+
270
+ return JSON.parse(response);
271
+ } catch (error) {
272
+ console.warn(`AI learning feedback analysis failed: ${error.message}`);
273
+ return this._fallback('learningFeedback', input, context);
274
+ }
275
+ }
276
+
277
+ /**
278
+ * 降级处理
279
+ * @private
280
+ */
281
+ _fallback(feature, input, context) {
282
+ if (!this.fallbackToRules) {
283
+ return null;
284
+ }
285
+ // 返回 null,让调用方使用规则引擎
286
+ return null;
287
+ }
288
+
289
+ /**
290
+ * 检查是否有可用的 Provider
291
+ */
292
+ hasAvailableProvider() {
293
+ for (const [name, provider] of this.providers) {
294
+ if (provider.initialized) return true;
295
+ }
296
+ return false;
297
+ }
298
+
299
+ /**
300
+ * 获取状态信息
301
+ */
302
+ getStatus() {
303
+ const providers = {};
304
+ for (const [name, provider] of this.providers) {
305
+ providers[name] = {
306
+ initialized: provider.initialized,
307
+ info: provider.getInfo()
308
+ };
309
+ }
310
+
311
+ return {
312
+ initialized: this.initialized,
313
+ defaultProvider: this.defaultProvider,
314
+ features: this.features,
315
+ providers: providers
316
+ };
317
+ }
318
+ }
319
+
320
+ module.exports = ModelManager;
@@ -0,0 +1,38 @@
1
+ /**
2
+ * 参数提取提示词
3
+ */
4
+
5
+ module.exports = {
6
+ system: `你是一个参数提取专家。你的任务是从用户的自然语言输入中提取结构化参数。
7
+
8
+ 你需要返回一个 JSON 对象,包含提取的参数。
9
+
10
+ 常见参数类型:
11
+ - traceId: 链路追踪ID,通常为字母数字组合
12
+ - timeRange: 时间范围,如"最近1小时"、"今天"、"2024-01-01到2024-01-02"
13
+ - keywords: 关键词列表
14
+ - filters: 过滤条件
15
+ - format: 输出格式(table/list/json/detail)
16
+ - limit: 数量限制
17
+ - database: 数据库名称
18
+ - table: 表名
19
+ - sql: SQL语句
20
+ - apiPath: API路径
21
+ - repository: 仓库名称
22
+ - branch: 分支名称
23
+ - filePath: 文件路径
24
+ - serviceName: 服务名称
25
+ - logLevel: 日志级别(error/warn/info/debug)
26
+
27
+ 请只返回 JSON,不要返回其他内容。如果没有找到参数,返回空对象 {}。`,
28
+
29
+ user: (input, skillName, skillSchema) => {
30
+ let prompt = `请从以下输入中提取参数:\n\n输入: "${input}"\n\n技能: ${skillName}`;
31
+
32
+ if (skillSchema && Object.keys(skillSchema).length > 0) {
33
+ prompt += `\n\n参数 Schema:\n${JSON.stringify(skillSchema, null, 2)}`;
34
+ }
35
+
36
+ return prompt;
37
+ }
38
+ };
@@ -0,0 +1,11 @@
1
+ /**
2
+ * AI 提示词模板
3
+ */
4
+
5
+ module.exports = {
6
+ intent: require('./intent'),
7
+ extraction: require('./extraction'),
8
+ selection: require('./selection'),
9
+ summary: require('./summary'),
10
+ learning: require('./learning')
11
+ };
@@ -0,0 +1,43 @@
1
+ /**
2
+ * 意图理解提示词
3
+ */
4
+
5
+ module.exports = {
6
+ system: `你是一个意图理解专家。你的任务是分析用户的输入,理解他们的真实意图。
7
+
8
+ 你需要返回一个 JSON 对象,包含以下字段:
9
+ {
10
+ "intent": "主要意图类型",
11
+ "action": "具体操作",
12
+ "entities": ["提取的实体列表"],
13
+ "confidence": 0.0-1.0,
14
+ "language": "zh/en"
15
+ }
16
+
17
+ 意图类型包括:
18
+ - query: 查询操作(查询日志、查询数据、查询状态等)
19
+ - analyze: 分析操作(分析日志、分析性能、分析代码等)
20
+ - create: 创建操作(创建文档、创建任务、创建流程等)
21
+ - update: 更新操作(更新配置、更新文档、更新状态等)
22
+ - delete: 删除操作(删除资源、删除配置等)
23
+ - review: 审查操作(代码审查、PR审查、项目审查等)
24
+ - validate: 验证操作(数据验证、逻辑验证、配置验证等)
25
+ - sync: 同步操作(同步文档、同步接口、同步配置等)
26
+ - deploy: 部署操作(部署应用、部署配置等)
27
+ - learn: 学习操作(记住偏好、记录纠正等)
28
+ - help: 帮助操作(查看帮助、查看文档等)
29
+ - unknown: 无法识别的意图
30
+
31
+ 请只返回 JSON,不要返回其他内容。`,
32
+
33
+ user: (input, context) => {
34
+ let prompt = `用户输入: "${input}"`;
35
+ if (context.previousIntent) {
36
+ prompt += `\n之前的意图: ${context.previousIntent}`;
37
+ }
38
+ if (context.currentSkill) {
39
+ prompt += `\n当前正在使用的技能: ${context.currentSkill}`;
40
+ }
41
+ return prompt;
42
+ }
43
+ };
@@ -0,0 +1,46 @@
1
+ /**
2
+ * 学习反馈提示词
3
+ */
4
+
5
+ module.exports = {
6
+ system: `你是一个学习反馈分析专家。你的任务是分析用户的反馈,提取学习点。
7
+
8
+ 你需要返回一个 JSON 对象:
9
+ {
10
+ "isLearning": true/false,
11
+ "learningType": "correction/preference/scene_mapping",
12
+ "originalInput": "原始输入",
13
+ "correction": "纠正内容",
14
+ "preference": "偏好设置",
15
+ "sceneMapping": {
16
+ "trigger": "触发条件",
17
+ "workflow": "工作流步骤"
18
+ },
19
+ "confidence": 0.0-1.0
20
+ }
21
+
22
+ 学习类型:
23
+ - correction: 纠正之前的错误(如"不对,应该用表格格式")
24
+ - preference: 记录用户偏好(如"以后都用中文回复")
25
+ - scene_mapping: 记录场景到工作流的映射(如"排查问题先查错误日志再查链路")
26
+
27
+ 识别模式:
28
+ - 纠正模式: "不对"、"错了"、"应该是"、"不要"、"改为"
29
+ - 偏好模式: "以后"、"都用"、"总是"、"默认"
30
+ - 场景模式: "先...再..."、"首先...然后..."、"...的时候...就..."
31
+
32
+ 请只返回 JSON,不要返回其他内容。`,
33
+
34
+ user: (input, context) => {
35
+ let prompt = `用户输入: "${input}"`;
36
+
37
+ if (context.previousInteraction) {
38
+ prompt += `\n之前的交互: ${JSON.stringify(context.previousInteraction)}`;
39
+ }
40
+ if (context.currentSkill) {
41
+ prompt += `\n当前技能: ${context.currentSkill}`;
42
+ }
43
+
44
+ return prompt;
45
+ }
46
+ };
@@ -0,0 +1,38 @@
1
+ /**
2
+ * 技能选择提示词
3
+ */
4
+
5
+ module.exports = {
6
+ system: `你是一个技能选择专家。你的任务是根据用户的输入,从候选技能列表中选择最合适的技能。
7
+
8
+ 你需要返回一个 JSON 对象:
9
+ {
10
+ "selectedSkill": "选中的技能名称",
11
+ "confidence": 0.0-1.0,
12
+ "reason": "选择原因"
13
+ }
14
+
15
+ 选择标准:
16
+ 1. 技能的触发词是否匹配用户输入
17
+ 2. 技能的描述是否符合用户意图
18
+ 3. 技能的参数是否能满足用户需求
19
+ 4. 优先选择更具体的技能(如"sls-log-audit"优于"log-audit")
20
+
21
+ 请只返回 JSON,不要返回其他内容。`,
22
+
23
+ user: (input, candidates) => {
24
+ const candidateList = candidates.map(c => ({
25
+ name: c.name,
26
+ description: c.description,
27
+ triggers: c.triggers,
28
+ category: c.category
29
+ }));
30
+
31
+ return `用户输入: "${input}"
32
+
33
+ 候选技能列表:
34
+ ${JSON.stringify(candidateList, null, 2)}
35
+
36
+ 请选择最合适的技能。`;
37
+ }
38
+ };
@@ -0,0 +1,35 @@
1
+ /**
2
+ * 结果摘要提示词
3
+ */
4
+
5
+ module.exports = {
6
+ system: `你是一个结果摘要专家。你的任务是将技术性的执行结果转换为用户友好的自然语言摘要。
7
+
8
+ 摘要要求:
9
+ 1. 简洁明了,突出关键信息
10
+ 2. 使用用户能理解的语言
11
+ 3. 如果有错误,清楚地说明问题所在
12
+ 4. 如果有数据,提取关键指标和趋势
13
+ 5. 如果有建议,给出明确的下一步操作
14
+
15
+ 格式要求:
16
+ - 使用中文回复
17
+ - 使用 markdown 格式
18
+ - 重要信息使用加粗
19
+ - 列表使用有序或无序列表`,
20
+
21
+ user: (result, context) => {
22
+ let prompt = `请为以下执行结果生成摘要:\n\n`;
23
+
24
+ if (context.skill) {
25
+ prompt += `使用的技能: ${context.skill}\n`;
26
+ }
27
+ if (context.intent) {
28
+ prompt += `用户意图: ${context.intent}\n`;
29
+ }
30
+
31
+ prompt += `\n执行结果:\n${JSON.stringify(result, null, 2)}`;
32
+
33
+ return prompt;
34
+ }
35
+ };
@@ -0,0 +1,93 @@
1
+ /**
2
+ * Anthropic Provider - Claude 模型接入
3
+ */
4
+
5
+ const BaseModel = require('../base-model');
6
+
7
+ class AnthropicProvider extends BaseModel {
8
+ constructor(config = {}) {
9
+ super('anthropic', config);
10
+ this.apiKey = config.apiKey || process.env.ANTHROPIC_API_KEY;
11
+ this.model = config.model || 'claude-3-sonnet-20240229';
12
+ this.baseUrl = config.baseUrl || 'https://api.anthropic.com';
13
+ this.maxTokens = config.maxTokens ?? 2000;
14
+ this.temperature = config.temperature ?? 0.3;
15
+ }
16
+
17
+ async init() {
18
+ if (!this.apiKey) {
19
+ throw new Error('Anthropic API key is required. Set ANTHROPIC_API_KEY environment variable or provide in config.');
20
+ }
21
+ this.initialized = true;
22
+ }
23
+
24
+ validateConfig() {
25
+ return !!this.apiKey;
26
+ }
27
+
28
+ async chat(messages, options = {}) {
29
+ if (!this.initialized) {
30
+ await this.init();
31
+ }
32
+
33
+ // 转换消息格式:提取 system 消息
34
+ let systemPrompt = '';
35
+ const userMessages = [];
36
+
37
+ for (const msg of messages) {
38
+ if (msg.role === 'system') {
39
+ systemPrompt = msg.content;
40
+ } else {
41
+ userMessages.push(msg);
42
+ }
43
+ }
44
+
45
+ const response = await fetch(`${this.baseUrl}/v1/messages`, {
46
+ method: 'POST',
47
+ headers: {
48
+ 'Content-Type': 'application/json',
49
+ 'x-api-key': this.apiKey,
50
+ 'anthropic-version': '2023-06-01'
51
+ },
52
+ body: JSON.stringify({
53
+ model: options.model || this.model,
54
+ max_tokens: options.maxTokens ?? this.maxTokens,
55
+ system: systemPrompt || undefined,
56
+ messages: userMessages,
57
+ temperature: options.temperature ?? this.temperature
58
+ })
59
+ });
60
+
61
+ if (!response.ok) {
62
+ const error = await response.text();
63
+ throw new Error(`Anthropic API error: ${response.status} - ${error}`);
64
+ }
65
+
66
+ const data = await response.json();
67
+ return data.content[0].text;
68
+ }
69
+
70
+ async complete(prompt, options = {}) {
71
+ return this.chat([{ role: 'user', content: prompt }], options);
72
+ }
73
+
74
+ async isAvailable() {
75
+ try {
76
+ if (!this.apiKey) return false;
77
+ // Anthropic 没有 models 端点,直接尝试调用
78
+ return true;
79
+ } catch {
80
+ return false;
81
+ }
82
+ }
83
+
84
+ getInfo() {
85
+ return {
86
+ ...super.getInfo(),
87
+ model: this.model,
88
+ baseUrl: this.baseUrl
89
+ };
90
+ }
91
+ }
92
+
93
+ module.exports = AnthropicProvider;
@@ -0,0 +1,80 @@
1
+ /**
2
+ * DeepSeek Provider - DeepSeek 模型接入
3
+ * 支持 DeepSeek-V3、DeepSeek-R1 等模型
4
+ */
5
+
6
+ const BaseModel = require('../base-model');
7
+
8
+ class DeepSeekProvider extends BaseModel {
9
+ constructor(config = {}) {
10
+ super('deepseek', config);
11
+ this.apiKey = config.apiKey || process.env.DEEPSEEK_API_KEY;
12
+ this.model = config.model || 'deepseek-chat';
13
+ this.baseUrl = config.baseUrl || 'https://api.deepseek.com/v1';
14
+ this.temperature = config.temperature ?? 0.3;
15
+ this.maxTokens = config.maxTokens ?? 2000;
16
+ }
17
+
18
+ async init() {
19
+ if (!this.apiKey) {
20
+ throw new Error('DeepSeek API key is required. Set DEEPSEEK_API_KEY environment variable or provide in config.');
21
+ }
22
+ this.initialized = true;
23
+ }
24
+
25
+ validateConfig() {
26
+ return !!this.apiKey;
27
+ }
28
+
29
+ async chat(messages, options = {}) {
30
+ if (!this.initialized) {
31
+ await this.init();
32
+ }
33
+
34
+ const response = await fetch(`${this.baseUrl}/chat/completions`, {
35
+ method: 'POST',
36
+ headers: {
37
+ 'Content-Type': 'application/json',
38
+ 'Authorization': `Bearer ${this.apiKey}`
39
+ },
40
+ body: JSON.stringify({
41
+ model: options.model || this.model,
42
+ messages: messages,
43
+ temperature: options.temperature ?? this.temperature,
44
+ max_tokens: options.maxTokens ?? this.maxTokens
45
+ })
46
+ });
47
+
48
+ if (!response.ok) {
49
+ const error = await response.text();
50
+ throw new Error(`DeepSeek API error: ${response.status} - ${error}`);
51
+ }
52
+
53
+ const data = await response.json();
54
+ return data.choices[0].message.content;
55
+ }
56
+
57
+ async complete(prompt, options = {}) {
58
+ return this.chat([{ role: 'user', content: prompt }], options);
59
+ }
60
+
61
+ async isAvailable() {
62
+ try {
63
+ if (!this.apiKey) return false;
64
+ return true;
65
+ } catch {
66
+ return false;
67
+ }
68
+ }
69
+
70
+ getInfo() {
71
+ return {
72
+ ...super.getInfo(),
73
+ model: this.model,
74
+ baseUrl: this.baseUrl,
75
+ provider: 'DeepSeek'
76
+ };
77
+ }
78
+ }
79
+
80
+ module.exports = DeepSeekProvider;