my-ai-chat-framework 1.0.2 → 2.0.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.
- package/.env +15 -0
- package/README.md +70 -268
- package/dist/my-ai-chat-framework.browser.es.js +427 -2627
- package/dist/my-ai-chat-framework.browser.es.js.map +1 -1
- package/dist/my-ai-chat-framework.browser.umd.js +442 -2633
- package/dist/my-ai-chat-framework.browser.umd.js.map +1 -1
- package/dist/my-ai-chat-framework.node.cjs.js +436 -1378
- package/dist/my-ai-chat-framework.node.cjs.js.map +1 -1
- package/package.json +15 -32
- package/src/adapters/openai.js +130 -0
- package/src/core/ChatService.js +110 -0
- package/src/core/EventEmitter.js +32 -126
- package/src/core/MessageStore.js +73 -0
- package/src/index.js +10 -414
- package/src/plugins/tool-calling.js +119 -0
- package/test.js +107 -0
- package/README_ZH.md +0 -401
- package/dist/my-ai-chat-framework.cjs.js +0 -2630
- package/dist/my-ai-chat-framework.cjs.js.map +0 -1
- package/dist/my-ai-chat-framework.es.js +0 -2630
- package/dist/my-ai-chat-framework.es.js.map +0 -1
- package/dist/my-ai-chat-framework.umd.js +0 -2634
- package/dist/my-ai-chat-framework.umd.js.map +0 -1
- package/src/core/ApiClient.js +0 -288
- package/src/core/MessageFormatter.js +0 -126
- package/src/core/Messages.js +0 -562
- package/src/core/RequestBuilder.js +0 -96
- package/src/core/ToolManager.js +0 -175
- package/src/utils/index.js +0 -21
|
@@ -0,0 +1,119 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* 工具调用插件
|
|
3
|
+
* 拦截消息中的 tool_calls,执行注册的工具,并将结果插入对话,然后继续请求
|
|
4
|
+
*/
|
|
5
|
+
export const toolCallingPlugin = {
|
|
6
|
+
name: 'tool-calling',
|
|
7
|
+
maxIterations: 5, // 防止无限循环
|
|
8
|
+
|
|
9
|
+
install(chatService) {
|
|
10
|
+
this.chatService = chatService;
|
|
11
|
+
this._tools = new Map(); // 工具名 -> { executor, definition }
|
|
12
|
+
|
|
13
|
+
// 覆盖 registerTool 方法
|
|
14
|
+
chatService.registerTool = (name, description, executor) => {
|
|
15
|
+
this._tools.set(name, { executor, description });
|
|
16
|
+
// 可选:同时生成工具定义,以便在请求中告知 AI
|
|
17
|
+
// 这里简化,依赖适配器在请求中自动包含工具定义(如果需要)
|
|
18
|
+
return chatService;
|
|
19
|
+
};
|
|
20
|
+
|
|
21
|
+
// 拦截发送流程:在发送前注入工具定义(如果需要)
|
|
22
|
+
// 在流式和非流式响应后检测 tool_calls
|
|
23
|
+
const originalSend = chatService.send;
|
|
24
|
+
chatService.send = async (input) => {
|
|
25
|
+
return this._handleWithTools(originalSend, input, false);
|
|
26
|
+
};
|
|
27
|
+
|
|
28
|
+
const originalStream = chatService.stream;
|
|
29
|
+
chatService.stream = async (input, onProgress, onDone) => {
|
|
30
|
+
return this._handleWithTools(originalStream, input, true, onProgress, onDone);
|
|
31
|
+
};
|
|
32
|
+
},
|
|
33
|
+
|
|
34
|
+
async _handleWithTools(originalMethod, input, isStream, onProgress, onDone) {
|
|
35
|
+
const self = this;
|
|
36
|
+
const chat = this.chatService;
|
|
37
|
+
let iteration = 0;
|
|
38
|
+
|
|
39
|
+
// 包装回调,用于递归处理
|
|
40
|
+
async function processResponse(initialResponse) {
|
|
41
|
+
let lastResponse = initialResponse;
|
|
42
|
+
while (iteration < self.maxIterations) {
|
|
43
|
+
// 检查是否有工具调用
|
|
44
|
+
const toolCalls = lastResponse?.toolCalls;
|
|
45
|
+
if (!toolCalls || toolCalls.length === 0) break;
|
|
46
|
+
|
|
47
|
+
iteration++;
|
|
48
|
+
// 执行所有工具
|
|
49
|
+
const toolResults = [];
|
|
50
|
+
for (const call of toolCalls) {
|
|
51
|
+
const toolName = call.function?.name;
|
|
52
|
+
const args = JSON.parse(call.function?.arguments || '{}');
|
|
53
|
+
const tool = self._tools.get(toolName);
|
|
54
|
+
if (!tool) {
|
|
55
|
+
console.warn(`未找到工具: ${toolName}`);
|
|
56
|
+
toolResults.push({ tool_call_id: call.id, error: `工具 ${toolName} 未注册`, success: false });
|
|
57
|
+
continue;
|
|
58
|
+
}
|
|
59
|
+
try {
|
|
60
|
+
const result = await tool.executor(args);
|
|
61
|
+
toolResults.push({
|
|
62
|
+
tool_call_id: call.id,
|
|
63
|
+
content: typeof result === 'string' ? result : JSON.stringify(result),
|
|
64
|
+
success: true
|
|
65
|
+
});
|
|
66
|
+
} catch (err) {
|
|
67
|
+
console.error(`工具 ${toolName} 执行失败:`, err);
|
|
68
|
+
toolResults.push({ tool_call_id: call.id, error: err.message, success: false });
|
|
69
|
+
}
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
// 添加工具结果消息
|
|
73
|
+
for (const tr of toolResults) {
|
|
74
|
+
chat.messages.addTool(tr.content || tr.error, tr.tool_call_id);
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
// 再次请求
|
|
78
|
+
if (isStream) {
|
|
79
|
+
// 流式重新请求:需要重新构建请求体
|
|
80
|
+
const userMsg = chat.messages.getLast(); // 实际上应该重新生成请求
|
|
81
|
+
// 这里简化:直接调用原 stream 方法,但需要避免循环
|
|
82
|
+
// 实际实现需更细致,我们这里仅示意
|
|
83
|
+
await new Promise((resolve) => {
|
|
84
|
+
originalStream.call(chat, null, (chunk) => {
|
|
85
|
+
if (onProgress) onProgress(chunk);
|
|
86
|
+
lastResponse = chunk;
|
|
87
|
+
}, (final) => {
|
|
88
|
+
lastResponse = final;
|
|
89
|
+
resolve();
|
|
90
|
+
});
|
|
91
|
+
});
|
|
92
|
+
} else {
|
|
93
|
+
const reqBody = chat._adapter.buildRequest(chat.messages.getAll(), chat.config);
|
|
94
|
+
const apiResp = await chat._adapter.send(reqBody, chat.config);
|
|
95
|
+
lastResponse = chat._adapter.parseResponse(apiResp);
|
|
96
|
+
}
|
|
97
|
+
}
|
|
98
|
+
return lastResponse;
|
|
99
|
+
}
|
|
100
|
+
|
|
101
|
+
if (isStream) {
|
|
102
|
+
// 流式模式
|
|
103
|
+
let finalMessage;
|
|
104
|
+
await originalMethod.call(chat, input,
|
|
105
|
+
(chunk) => {
|
|
106
|
+
if (onProgress) onProgress(chunk);
|
|
107
|
+
},
|
|
108
|
+
async (final) => {
|
|
109
|
+
finalMessage = await processResponse(final);
|
|
110
|
+
if (onDone) onDone(finalMessage);
|
|
111
|
+
}
|
|
112
|
+
);
|
|
113
|
+
} else {
|
|
114
|
+
const resp = await originalMethod.call(chat, input);
|
|
115
|
+
const final = await processResponse(resp);
|
|
116
|
+
return final;
|
|
117
|
+
}
|
|
118
|
+
}
|
|
119
|
+
};
|
package/test.js
ADDED
|
@@ -0,0 +1,107 @@
|
|
|
1
|
+
// test.js - 测试新框架核心功能
|
|
2
|
+
import 'dotenv/config'; // 如果已安装 dotenv,否则手动读取 .env
|
|
3
|
+
import { ChatService, openaiAdapter, toolCallingPlugin } from './src/index.js';
|
|
4
|
+
|
|
5
|
+
// 从环境变量读取配置(确保 .env 文件存在)
|
|
6
|
+
const apiKey = process.env.DEEPSEEK_API_KEY;
|
|
7
|
+
const apiUrl = process.env.DEEPSEEK_API_URL || 'https://api.deepseek.com/v1/chat/completions';
|
|
8
|
+
const model = process.env.TEST_MODEL || 'deepseek-chat';
|
|
9
|
+
|
|
10
|
+
if (!apiKey) {
|
|
11
|
+
console.error('❌ 未找到 API Key,请设置 DEEPSEEK_API_KEY 环境变量或创建 .env 文件');
|
|
12
|
+
process.exit(1);
|
|
13
|
+
}
|
|
14
|
+
|
|
15
|
+
console.log('🚀 开始测试新框架...\n');
|
|
16
|
+
|
|
17
|
+
// 创建聊天服务实例
|
|
18
|
+
const chat = new ChatService({
|
|
19
|
+
apiKey,
|
|
20
|
+
model,
|
|
21
|
+
apiUrl,
|
|
22
|
+
temperature: 0.7,
|
|
23
|
+
maxTokens: 2000
|
|
24
|
+
});
|
|
25
|
+
|
|
26
|
+
// 加载适配器和插件
|
|
27
|
+
chat.use(openaiAdapter);
|
|
28
|
+
chat.use(toolCallingPlugin);
|
|
29
|
+
|
|
30
|
+
// 监听事件(便于观察)
|
|
31
|
+
chat.on('sending', msg => {
|
|
32
|
+
console.log('📤 发送消息:', msg.content?.substring(0, 50) || msg);
|
|
33
|
+
});
|
|
34
|
+
chat.on('message', msg => {
|
|
35
|
+
console.log('📥 收到回复:', msg.content?.substring(0, 100) || msg);
|
|
36
|
+
});
|
|
37
|
+
chat.on('stream-progress', chunk => {
|
|
38
|
+
console.log('🌊 流式数据块:', chunk.content?.substring(0, 30) || '');
|
|
39
|
+
});
|
|
40
|
+
chat.on('error', err => {
|
|
41
|
+
console.error('❌ 错误:', err.message);
|
|
42
|
+
});
|
|
43
|
+
|
|
44
|
+
// 注册一个简单测试工具(不实际调用,只验证注册和工具调用流程)
|
|
45
|
+
chat.registerTool('greet', '打招呼工具', async (args) => {
|
|
46
|
+
console.log('🔧 工具被调用:', args);
|
|
47
|
+
return `你好,${args.name || '朋友'}!`;
|
|
48
|
+
});
|
|
49
|
+
|
|
50
|
+
// 辅助函数:等待指定毫秒
|
|
51
|
+
const sleep = ms => new Promise(resolve => setTimeout(resolve, ms));
|
|
52
|
+
|
|
53
|
+
// 测试 1:基础发送消息(非流式)
|
|
54
|
+
console.log('===== 测试1:基础发送消息 =====');
|
|
55
|
+
const response = await chat.send('请简单说一句:你好,世界!');
|
|
56
|
+
console.log('最终回复:', response.content);
|
|
57
|
+
console.log();
|
|
58
|
+
|
|
59
|
+
// 等待一下,避免请求过快
|
|
60
|
+
await sleep(1000);
|
|
61
|
+
|
|
62
|
+
// 测试 2:流式发送消息
|
|
63
|
+
console.log('===== 测试2:流式发送消息 =====');
|
|
64
|
+
let streamContent = '';
|
|
65
|
+
await chat.stream('请用流式方式回答:告诉我一个好消息。',
|
|
66
|
+
(chunk) => {
|
|
67
|
+
streamContent += chunk.content;
|
|
68
|
+
// 实时打印已接收内容(可选)
|
|
69
|
+
// process.stdout.write(chunk.content);
|
|
70
|
+
},
|
|
71
|
+
(final) => {
|
|
72
|
+
console.log('流式完成,完整内容:', final.content);
|
|
73
|
+
}
|
|
74
|
+
);
|
|
75
|
+
console.log();
|
|
76
|
+
|
|
77
|
+
await sleep(1000);
|
|
78
|
+
|
|
79
|
+
// 测试 3:工具调用
|
|
80
|
+
console.log('===== 测试3:工具调用 =====');
|
|
81
|
+
// 注意:需要让 AI 主动调用 greet 工具。由于 deepseek-chat 支持函数调用,我们可以在系统提示或用户消息中引导。
|
|
82
|
+
// 我们在用户消息中明确要求调用工具,但 AI 是否调用取决于模型。这里做一个简单引导。
|
|
83
|
+
const toolResponse = await chat.send('请使用 greet 工具,向 "小明" 打招呼。');
|
|
84
|
+
console.log('工具调用后的回复:', toolResponse.content);
|
|
85
|
+
console.log();
|
|
86
|
+
|
|
87
|
+
await sleep(1000);
|
|
88
|
+
|
|
89
|
+
// 测试 4:验证消息存储
|
|
90
|
+
console.log('===== 测试4:消息存储验证 =====');
|
|
91
|
+
const allMessages = chat.messages.getAll();
|
|
92
|
+
console.log(`当前消息总数: ${allMessages.length}`);
|
|
93
|
+
allMessages.forEach((msg, i) => {
|
|
94
|
+
console.log(`[${i}] ${msg.role}: ${msg.content?.substring(0, 40)}${msg.content?.length > 40 ? '...' : ''}`);
|
|
95
|
+
if (msg.toolCalls) console.log(' 工具调用:', msg.toolCalls);
|
|
96
|
+
if (msg.toolCallId) console.log(' 工具调用ID:', msg.toolCallId);
|
|
97
|
+
});
|
|
98
|
+
console.log();
|
|
99
|
+
|
|
100
|
+
// 测试 5:撤回功能(可选)
|
|
101
|
+
console.log('===== 测试5:撤回最后一条助手消息 =====');
|
|
102
|
+
const removed = chat.messages.undoToLastAssistant();
|
|
103
|
+
console.log(`撤回了 ${removed.length} 条消息`);
|
|
104
|
+
const afterUndo = chat.messages.getAll();
|
|
105
|
+
console.log(`剩余消息数: ${afterUndo.length}`);
|
|
106
|
+
|
|
107
|
+
console.log('\n✅ 所有测试完成!');
|
package/README_ZH.md
DELETED
|
@@ -1,401 +0,0 @@
|
|
|
1
|
-
# 🚀 我的AI聊天框架 - 中文文档
|
|
2
|
-
|
|
3
|
-
一个现代化的AI聊天框架,支持工具调用、流式传输和事件驱动架构。
|
|
4
|
-
|
|
5
|
-
## ✨ 核心特性
|
|
6
|
-
|
|
7
|
-
### ✅ 已完成功能
|
|
8
|
-
1. **完整的工具调用系统**
|
|
9
|
-
- 工具注册和管理
|
|
10
|
-
- 工具执行和错误处理
|
|
11
|
-
- 支持批量工具调用
|
|
12
|
-
- 流式传输中的工具调用支持
|
|
13
|
-
|
|
14
|
-
2. **流式传输支持**
|
|
15
|
-
- 实时数据流处理
|
|
16
|
-
- 进度回调支持
|
|
17
|
-
- 工具调用与流式传输的完美集成
|
|
18
|
-
|
|
19
|
-
3. **事件驱动架构**
|
|
20
|
-
- 基于EventEmitter的事件系统
|
|
21
|
-
- 丰富的生命周期事件
|
|
22
|
-
- 可扩展的事件监听
|
|
23
|
-
|
|
24
|
-
4. **模块化设计**
|
|
25
|
-
- 分离关注点:状态管理、格式转换、请求构建
|
|
26
|
-
- 易于扩展和维护
|
|
27
|
-
- 支持多种使用方式(ES模块、UMD、CommonJS)
|
|
28
|
-
|
|
29
|
-
### 🎯 设计理念
|
|
30
|
-
- **简单易用**: 提供直观的API接口
|
|
31
|
-
- **功能强大**: 支持复杂的AI交互场景
|
|
32
|
-
- **可扩展**: 易于添加新功能和工具
|
|
33
|
-
- **稳定可靠**: 完善的错误处理和类型检查
|
|
34
|
-
|
|
35
|
-
## 📦 快速开始
|
|
36
|
-
|
|
37
|
-
### 安装
|
|
38
|
-
```bash
|
|
39
|
-
cd my-ai-chat-framework
|
|
40
|
-
npm install
|
|
41
|
-
```
|
|
42
|
-
|
|
43
|
-
### 构建项目
|
|
44
|
-
```bash
|
|
45
|
-
npm run build
|
|
46
|
-
```
|
|
47
|
-
|
|
48
|
-
### 输出文件
|
|
49
|
-
构建后会在 `dist/` 目录生成以下文件:
|
|
50
|
-
- `my-ai-chat-framework.es.js` - ES模块格式(包含axios依赖)
|
|
51
|
-
- `my-ai-chat-framework.umd.js` - UMD格式(包含axios依赖)
|
|
52
|
-
- `my-ai-chat-framework.cjs.js` - CommonJS格式(包含axios依赖)
|
|
53
|
-
|
|
54
|
-
**重要**: axios已被打包进所有输出文件中,用户无需额外安装axios依赖。
|
|
55
|
-
|
|
56
|
-
## 🔧 核心组件
|
|
57
|
-
|
|
58
|
-
### 1. ChatService (主服务类)
|
|
59
|
-
```javascript
|
|
60
|
-
import { ChatService } from './src/index.js';
|
|
61
|
-
|
|
62
|
-
// 创建聊天服务
|
|
63
|
-
const chat = new ChatService('your-api-key', 'deepseek-chat');
|
|
64
|
-
|
|
65
|
-
// 注册工具
|
|
66
|
-
chat.registerTool(
|
|
67
|
-
'get_weather',
|
|
68
|
-
{
|
|
69
|
-
type: 'function',
|
|
70
|
-
function: {
|
|
71
|
-
name: 'get_weather',
|
|
72
|
-
description: '获取天气信息',
|
|
73
|
-
parameters: {
|
|
74
|
-
type: 'object',
|
|
75
|
-
properties: {
|
|
76
|
-
city: { type: 'string' }
|
|
77
|
-
},
|
|
78
|
-
required: ['city']
|
|
79
|
-
}
|
|
80
|
-
}
|
|
81
|
-
},
|
|
82
|
-
async (args) => {
|
|
83
|
-
// 工具执行逻辑
|
|
84
|
-
return { temperature: '25°C', condition: '晴朗' };
|
|
85
|
-
}
|
|
86
|
-
);
|
|
87
|
-
|
|
88
|
-
// 发送消息(支持工具调用)
|
|
89
|
-
const response = await chat.send('北京的天气怎么样?');
|
|
90
|
-
console.log('AI回复:', response);
|
|
91
|
-
|
|
92
|
-
// 流式传输
|
|
93
|
-
await chat.stream(
|
|
94
|
-
'请用流式方式告诉我一个故事',
|
|
95
|
-
(chunk) => console.log('收到数据:', chunk.content),
|
|
96
|
-
(final) => console.log('完成:', final.content)
|
|
97
|
-
);
|
|
98
|
-
```
|
|
99
|
-
|
|
100
|
-
### 2. 事件系统
|
|
101
|
-
```javascript
|
|
102
|
-
// 监听各种事件
|
|
103
|
-
chat.on('sending', (data) => {
|
|
104
|
-
console.log('发送消息:', data.content);
|
|
105
|
-
});
|
|
106
|
-
|
|
107
|
-
chat.on('message', (data) => {
|
|
108
|
-
console.log('收到回复:', data.content);
|
|
109
|
-
});
|
|
110
|
-
|
|
111
|
-
chat.on('tool-call-requested', (data) => {
|
|
112
|
-
console.log('AI请求工具调用:', data.tool_calls);
|
|
113
|
-
});
|
|
114
|
-
|
|
115
|
-
chat.on('tool-execute-success', (data) => {
|
|
116
|
-
console.log('工具执行成功:', data.name, data.result);
|
|
117
|
-
});
|
|
118
|
-
|
|
119
|
-
chat.on('stream-progress', (chunk) => {
|
|
120
|
-
console.log('流式进度:', chunk.content);
|
|
121
|
-
});
|
|
122
|
-
```
|
|
123
|
-
|
|
124
|
-
### 3. 消息管理
|
|
125
|
-
```javascript
|
|
126
|
-
import { Messages } from './src/index.js';
|
|
127
|
-
|
|
128
|
-
const messages = new Messages();
|
|
129
|
-
|
|
130
|
-
// 添加各种消息
|
|
131
|
-
messages.addUserMessage('你好');
|
|
132
|
-
messages.addAssistantMessage('你好!有什么可以帮助您的吗?');
|
|
133
|
-
messages.addSystemMessage('你是一个有帮助的AI助手');
|
|
134
|
-
|
|
135
|
-
// 工具调用消息
|
|
136
|
-
messages.addAssistantMessage('我来查询天气', {
|
|
137
|
-
tool_calls: [{
|
|
138
|
-
id: 'call_123',
|
|
139
|
-
type: 'function',
|
|
140
|
-
function: {
|
|
141
|
-
name: 'get_weather',
|
|
142
|
-
arguments: JSON.stringify({ city: '北京' })
|
|
143
|
-
}
|
|
144
|
-
}]
|
|
145
|
-
});
|
|
146
|
-
|
|
147
|
-
// 工具结果消息
|
|
148
|
-
messages.addToolMessage(JSON.stringify({ temp: '25°C' }), 'call_123');
|
|
149
|
-
```
|
|
150
|
-
|
|
151
|
-
## 🛠️ 工具调用详解
|
|
152
|
-
|
|
153
|
-
### 工具注册
|
|
154
|
-
```javascript
|
|
155
|
-
chat.registerTool(
|
|
156
|
-
'tool_name', // 工具名称
|
|
157
|
-
toolDefinition, // 工具定义(符合OpenAI格式)
|
|
158
|
-
async executor // 工具执行函数
|
|
159
|
-
);
|
|
160
|
-
```
|
|
161
|
-
|
|
162
|
-
### 工具定义格式
|
|
163
|
-
```javascript
|
|
164
|
-
const toolDefinition = {
|
|
165
|
-
type: 'function',
|
|
166
|
-
function: {
|
|
167
|
-
name: 'get_weather',
|
|
168
|
-
description: '获取天气信息',
|
|
169
|
-
parameters: {
|
|
170
|
-
type: 'object',
|
|
171
|
-
properties: {
|
|
172
|
-
city: {
|
|
173
|
-
type: 'string',
|
|
174
|
-
description: '城市名称'
|
|
175
|
-
}
|
|
176
|
-
},
|
|
177
|
-
required: ['city']
|
|
178
|
-
}
|
|
179
|
-
}
|
|
180
|
-
};
|
|
181
|
-
```
|
|
182
|
-
|
|
183
|
-
### 工具执行流程
|
|
184
|
-
1. **用户发送消息** → AI识别需要调用工具
|
|
185
|
-
2. **AI返回工具调用请求** → 包含 `tool_calls` 数组
|
|
186
|
-
3. **框架执行工具** → 调用注册的工具函数
|
|
187
|
-
4. **添加工具结果** → 将结果作为工具消息添加到对话
|
|
188
|
-
5. **再次请求AI** → 将工具结果发送给AI生成最终回复
|
|
189
|
-
|
|
190
|
-
## 🌊 流式传输
|
|
191
|
-
|
|
192
|
-
### 基本使用
|
|
193
|
-
```javascript
|
|
194
|
-
await chat.stream(
|
|
195
|
-
'用户消息',
|
|
196
|
-
(chunk) => {
|
|
197
|
-
// 实时处理流式数据
|
|
198
|
-
console.log('实时内容:', chunk.content);
|
|
199
|
-
},
|
|
200
|
-
(finalMessage) => {
|
|
201
|
-
// 流式传输完成
|
|
202
|
-
console.log('最终结果:', finalMessage);
|
|
203
|
-
}
|
|
204
|
-
);
|
|
205
|
-
```
|
|
206
|
-
|
|
207
|
-
### 流式传输中的工具调用
|
|
208
|
-
框架自动处理流式传输中的工具调用:
|
|
209
|
-
1. 流式传输过程中AI请求工具调用
|
|
210
|
-
2. 框架收集完整的工具调用信息
|
|
211
|
-
3. 执行工具并获取结果
|
|
212
|
-
4. 继续流式传输最终回复
|
|
213
|
-
|
|
214
|
-
## 📁 项目结构
|
|
215
|
-
|
|
216
|
-
```
|
|
217
|
-
my-ai-chat-framework/
|
|
218
|
-
├── src/
|
|
219
|
-
│ ├── core/
|
|
220
|
-
│ │ ├── EventEmitter.js # 事件系统
|
|
221
|
-
│ │ ├── Messages.js # 消息状态管理
|
|
222
|
-
│ │ ├── MessageFormatter.js # 消息格式转换
|
|
223
|
-
│ │ ├── RequestBuilder.js # 请求构建器
|
|
224
|
-
│ │ ├── ApiClient.js # API客户端(使用axios)
|
|
225
|
-
│ │ └── ToolManager.js # 工具管理器
|
|
226
|
-
│ ├── utils/
|
|
227
|
-
│ │ └── index.js # 工具函数
|
|
228
|
-
│ └── index.js # 主入口文件
|
|
229
|
-
├── dist/ # 打包输出
|
|
230
|
-
├── test-*.html # 各种测试页面
|
|
231
|
-
├── vite.config.js # 构建配置
|
|
232
|
-
└── package.json # 项目配置
|
|
233
|
-
```
|
|
234
|
-
|
|
235
|
-
## 🧪 测试页面
|
|
236
|
-
|
|
237
|
-
### 1. 完整功能测试
|
|
238
|
-
打开 `test-complete-tool-call.html` 进行完整的功能测试:
|
|
239
|
-
- 工具调用流程测试
|
|
240
|
-
- 流式传输测试
|
|
241
|
-
- 错误处理测试
|
|
242
|
-
|
|
243
|
-
### 2. 工具调用ID测试
|
|
244
|
-
打开 `test-tool-call-id.html` 验证 `tool_call_id` 的正确传递。
|
|
245
|
-
|
|
246
|
-
### 3. 事件系统测试
|
|
247
|
-
打开 `test-eventemitter.html` 测试事件系统功能。
|
|
248
|
-
|
|
249
|
-
## 🔍 调试技巧
|
|
250
|
-
|
|
251
|
-
### 启用详细日志
|
|
252
|
-
```javascript
|
|
253
|
-
// 在ChatService中启用详细日志
|
|
254
|
-
chat.on('sending', (data) => console.log('发送:', data));
|
|
255
|
-
chat.on('message', (data) => console.log('接收:', data));
|
|
256
|
-
chat.on('tool-call-requested', (data) => console.log('工具调用:', data));
|
|
257
|
-
```
|
|
258
|
-
|
|
259
|
-
### 检查消息状态
|
|
260
|
-
```javascript
|
|
261
|
-
// 查看当前所有消息
|
|
262
|
-
const allMessages = chat.messages.getMessages();
|
|
263
|
-
console.log('当前消息:', allMessages);
|
|
264
|
-
|
|
265
|
-
// 查看格式化后的消息
|
|
266
|
-
const formatted = chat.requestBuilder.toObject();
|
|
267
|
-
console.log('API请求体:', formatted);
|
|
268
|
-
```
|
|
269
|
-
|
|
270
|
-
## ⚠️ 注意事项
|
|
271
|
-
|
|
272
|
-
### 1. API Key 安全
|
|
273
|
-
- 不要在客户端代码中硬编码API Key
|
|
274
|
-
- 使用环境变量或后端代理
|
|
275
|
-
- 测试时可以使用临时Key
|
|
276
|
-
|
|
277
|
-
### 2. 工具调用限制
|
|
278
|
-
- 工具名称必须唯一
|
|
279
|
-
- 工具参数必须符合JSON Schema
|
|
280
|
-
- 工具执行函数应该是异步的
|
|
281
|
-
|
|
282
|
-
### 3. 流式传输
|
|
283
|
-
- 流式传输需要显式设置 `stream: true`
|
|
284
|
-
- 工具调用在流式传输中会自动处理
|
|
285
|
-
- 注意网络连接稳定性
|
|
286
|
-
|
|
287
|
-
### 4. 错误处理
|
|
288
|
-
- 所有工具调用都有错误处理
|
|
289
|
-
- 网络错误会自动重试(可配置)
|
|
290
|
-
- 详细的错误日志
|
|
291
|
-
|
|
292
|
-
## 🚀 高级用法
|
|
293
|
-
|
|
294
|
-
### 自定义事件处理器
|
|
295
|
-
```javascript
|
|
296
|
-
class MyChatService extends ChatService {
|
|
297
|
-
constructor(apiKey, model, config) {
|
|
298
|
-
super(apiKey, model, config);
|
|
299
|
-
this._setupCustomListeners();
|
|
300
|
-
}
|
|
301
|
-
|
|
302
|
-
_setupCustomListeners() {
|
|
303
|
-
this.on('tool-execute-start', this._onToolStart.bind(this));
|
|
304
|
-
this.on('tool-execute-success', this._onToolSuccess.bind(this));
|
|
305
|
-
}
|
|
306
|
-
|
|
307
|
-
_onToolStart(data) {
|
|
308
|
-
console.log(`工具 ${data.name} 开始执行`);
|
|
309
|
-
}
|
|
310
|
-
|
|
311
|
-
_onToolSuccess(data) {
|
|
312
|
-
console.log(`工具 ${data.name} 执行成功:`, data.result);
|
|
313
|
-
}
|
|
314
|
-
}
|
|
315
|
-
```
|
|
316
|
-
|
|
317
|
-
### 批量工具注册
|
|
318
|
-
```javascript
|
|
319
|
-
const tools = [
|
|
320
|
-
{
|
|
321
|
-
name: 'get_weather',
|
|
322
|
-
definition: { /* ... */ },
|
|
323
|
-
executor: async (args) => { /* ... */ }
|
|
324
|
-
},
|
|
325
|
-
{
|
|
326
|
-
name: 'get_time',
|
|
327
|
-
definition: { /* ... */ },
|
|
328
|
-
executor: async (args) => { /* ... */ }
|
|
329
|
-
}
|
|
330
|
-
];
|
|
331
|
-
|
|
332
|
-
tools.forEach(tool => {
|
|
333
|
-
chat.registerTool(tool.name, tool.definition, tool.executor);
|
|
334
|
-
});
|
|
335
|
-
```
|
|
336
|
-
|
|
337
|
-
### 消息持久化
|
|
338
|
-
```javascript
|
|
339
|
-
// 导出对话
|
|
340
|
-
const exportData = chat.export();
|
|
341
|
-
localStorage.setItem('chat_history', JSON.stringify(exportData));
|
|
342
|
-
|
|
343
|
-
// 导入对话
|
|
344
|
-
const savedData = JSON.parse(localStorage.getItem('chat_history'));
|
|
345
|
-
chat.import(savedData);
|
|
346
|
-
```
|
|
347
|
-
|
|
348
|
-
## 📚 学习资源
|
|
349
|
-
|
|
350
|
-
### 框架设计理念
|
|
351
|
-
1. **观察者模式**: EventEmitter实现事件系统
|
|
352
|
-
2. **工厂模式**: 消息对象创建
|
|
353
|
-
3. **策略模式**: 不同的API适配器
|
|
354
|
-
4. **外观模式**: ChatService作为统一接口
|
|
355
|
-
|
|
356
|
-
### 最佳实践
|
|
357
|
-
1. **单一职责**: 每个类只负责一个功能
|
|
358
|
-
2. **开放封闭**: 易于扩展,无需修改现有代码
|
|
359
|
-
3. **依赖倒置**: 高层模块不依赖低层模块
|
|
360
|
-
4. **接口隔离**: 客户端不应该依赖它不需要的接口
|
|
361
|
-
|
|
362
|
-
## 🤝 贡献指南
|
|
363
|
-
|
|
364
|
-
### 开发流程
|
|
365
|
-
1. Fork项目
|
|
366
|
-
2. 创建功能分支
|
|
367
|
-
3. 编写代码和测试
|
|
368
|
-
4. 提交Pull Request
|
|
369
|
-
|
|
370
|
-
### 代码规范
|
|
371
|
-
- 使用ES6+语法
|
|
372
|
-
- 添加JSDoc注释
|
|
373
|
-
- 编写单元测试
|
|
374
|
-
- 保持向后兼容性
|
|
375
|
-
|
|
376
|
-
### 测试要求
|
|
377
|
-
- 所有新功能都需要测试
|
|
378
|
-
- 保持测试覆盖率
|
|
379
|
-
- 测试应该独立且可重复
|
|
380
|
-
|
|
381
|
-
## 📄 许可证
|
|
382
|
-
|
|
383
|
-
MIT License - 详见LICENSE文件
|
|
384
|
-
|
|
385
|
-
## 🙏 致谢
|
|
386
|
-
|
|
387
|
-
感谢以下开源项目:
|
|
388
|
-
- [axios](https://github.com/axios/axios) - HTTP客户端
|
|
389
|
-
- [Vite](https://vitejs.dev/) - 构建工具
|
|
390
|
-
- [DeepSeek API](https://platform.deepseek.com/) - AI服务
|
|
391
|
-
|
|
392
|
-
---
|
|
393
|
-
|
|
394
|
-
**版本**: 1.0.0
|
|
395
|
-
**最后更新**: 2026年3月9日
|
|
396
|
-
**状态**: ✅ 生产就绪
|
|
397
|
-
**工具调用**: ✅ 完全支持
|
|
398
|
-
**流式传输**: ✅ 完全支持
|
|
399
|
-
**事件系统**: ✅ 完全支持
|
|
400
|
-
|
|
401
|
-
**提示**: 这是一个学习项目,旨在帮助理解AI聊天框架的设计和实现。欢迎反馈和建议!
|