knight-agent 1.2.0 → 1.2.2
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/base/AbstractAgent.d.ts +2 -2
- package/base/AbstractLLM.d.ts +0 -2
- package/docs/architecture-manual.md +978 -0
- package/index.js +1 -1
- package/module/AgentModule/Agent.d.ts +2 -2
- package/package.json +1 -1
- package/readme.md +9 -9
- package/utility/AgentUtility.d.ts +49 -5
- package/utility/LLMUtility.d.ts +0 -11
|
@@ -0,0 +1,978 @@
|
|
|
1
|
+
# Knight Agent 2.0 — 架构使用手册
|
|
2
|
+
|
|
3
|
+
## 目录
|
|
4
|
+
|
|
5
|
+
- [1. 项目概述](#1-项目概述)
|
|
6
|
+
- [2. 核心架构](#2-核心架构)
|
|
7
|
+
- [3. 快速开始](#3-快速开始)
|
|
8
|
+
- [4. Host 宿主容器](#4-host-宿主容器)
|
|
9
|
+
- [5. LLM 模块](#5-llm-模块)
|
|
10
|
+
- [6. Tool 工具模块](#6-tool-工具模块)
|
|
11
|
+
- [7. Skill 技能模块](#7-skill-技能模块)
|
|
12
|
+
- [8. Agent 代理模块](#8-agent-代理模块)
|
|
13
|
+
- [9. 快照与恢复](#9-快照与恢复)
|
|
14
|
+
- [10. 流式响应](#10-流式响应)
|
|
15
|
+
- [11. 工具调用循环](#11-工具调用循环)
|
|
16
|
+
- [12. 扩展自定义 LLM](#12-扩展自定义-llm)
|
|
17
|
+
- [13. 类型参考](#13-类型参考)
|
|
18
|
+
|
|
19
|
+
---
|
|
20
|
+
|
|
21
|
+
## 1. 项目概述
|
|
22
|
+
|
|
23
|
+
Knight Agent 2.0(包名 `knight-agent`)是一个 **TypeScript 多 LLM Agent 编排框架**。它提供统一的抽象层,封装了多个大模型平台(Claude、DeepSeek、OpenAI、Gemini、QWen),并内置工具调用(Tool Calling)、流式响应(Streaming)、基于意图匹配的技能路由(Skill Routing)、Agent 对话管理以及快照持久化能力。
|
|
24
|
+
|
|
25
|
+
### 核心能力
|
|
26
|
+
|
|
27
|
+
- **多平台统一接口**:一套 API 调用 5 个 LLM 平台,自动处理请求体/响应体格式差异
|
|
28
|
+
- **工具调用循环**:自动执行 LLM 返回的 tool_calls,将结果回传,循环直到 LLM 产出最终文本
|
|
29
|
+
- **技能意图匹配**:根据用户输入自动匹配最合适的 Skill,组装提示词和工具集
|
|
30
|
+
- **消息缓存管理**:自动 Trim 历史消息、Compact 工具输出,防止超出上下文窗口
|
|
31
|
+
- **快照/恢复**:Agent 和 LLM 的完整状态可序列化、可恢复
|
|
32
|
+
- **流式响应**:所有平台均支持 SSE 流式解析
|
|
33
|
+
|
|
34
|
+
---
|
|
35
|
+
|
|
36
|
+
## 2. 核心架构
|
|
37
|
+
|
|
38
|
+
```
|
|
39
|
+
┌──────────────────────────────────────────────────┐
|
|
40
|
+
│ User Code │
|
|
41
|
+
└──────────────────────┬───────────────────────────┘
|
|
42
|
+
│
|
|
43
|
+
▼
|
|
44
|
+
┌─────────────────┐
|
|
45
|
+
│ Host (Singleton) │
|
|
46
|
+
│ ┌─────────────┐ │
|
|
47
|
+
│ │ LLMModule │ │ ← LLM 工厂 + 注册表
|
|
48
|
+
│ │ ToolModule │ │ ← 工具注册表 + 执行器
|
|
49
|
+
│ │ SkillModule │ │ ← 技能注册表 + 意图匹配
|
|
50
|
+
│ │ AgentModule │ │ ← Agent 工厂 + 注册表
|
|
51
|
+
│ └─────────────┘ │
|
|
52
|
+
└─────────────────┘
|
|
53
|
+
│
|
|
54
|
+
▼
|
|
55
|
+
┌─────────────────┐
|
|
56
|
+
│ Agent.send() │
|
|
57
|
+
│ ┌─────────────┐ │
|
|
58
|
+
│ │ 意图匹配 │ │ ← SkillModule.matchBest()
|
|
59
|
+
│ │ 提示词组装 │ │ ← SkillModule.buildPrompt()
|
|
60
|
+
│ │ 工具解析 │ │ ← ToolModule.resolve()
|
|
61
|
+
│ │ LLM 调用 │ │ ← AbstractLLM.send()
|
|
62
|
+
│ │ 对话记录 │ │ ← Agent.record()
|
|
63
|
+
│ └─────────────┘ │
|
|
64
|
+
└─────────────────┘
|
|
65
|
+
```
|
|
66
|
+
|
|
67
|
+
### 类继承体系
|
|
68
|
+
|
|
69
|
+
```
|
|
70
|
+
AbstractObject ← 全局 ID + 注册表 + OnDestroy
|
|
71
|
+
├── AbstractModule ← 模块基类(LLMModule / ToolModule / SkillModule / AgentModule)
|
|
72
|
+
├── AbstractLLM ← LLM 基类:消息缓存、send()、工具循环、快照
|
|
73
|
+
│ ├── ClaudeLLM
|
|
74
|
+
│ ├── DeepSeekLLM
|
|
75
|
+
│ ├── OpenAILLM
|
|
76
|
+
│ ├── GeminiLLM
|
|
77
|
+
│ └── QWenLLM
|
|
78
|
+
└── AbstractAgent ← Agent 基类:包装 LLM、turn 记录、快照
|
|
79
|
+
└── Agent ← 默认实现:意图匹配 → 提示词组装 → 工具解析 → LLM 调用
|
|
80
|
+
```
|
|
81
|
+
|
|
82
|
+
---
|
|
83
|
+
|
|
84
|
+
## 3. 快速开始
|
|
85
|
+
|
|
86
|
+
### 安装
|
|
87
|
+
|
|
88
|
+
```bash
|
|
89
|
+
npm install knight-agent
|
|
90
|
+
```
|
|
91
|
+
|
|
92
|
+
### 最简示例
|
|
93
|
+
|
|
94
|
+
```typescript
|
|
95
|
+
import { Host } from "knight-agent";
|
|
96
|
+
|
|
97
|
+
// 1. 注册工具
|
|
98
|
+
Host.Instance.toolModule.register({
|
|
99
|
+
name: "get_weather",
|
|
100
|
+
description: "获取指定城市的天气",
|
|
101
|
+
inputSchema: {
|
|
102
|
+
type: "object",
|
|
103
|
+
properties: {
|
|
104
|
+
city: { type: "string", description: "城市名称" }
|
|
105
|
+
},
|
|
106
|
+
required: ["city"]
|
|
107
|
+
},
|
|
108
|
+
handler: async (args) => {
|
|
109
|
+
return `城市 ${args.city} 的天气:晴,25°C`;
|
|
110
|
+
}
|
|
111
|
+
});
|
|
112
|
+
|
|
113
|
+
// 2. 注册技能
|
|
114
|
+
Host.Instance.skillModule.register({
|
|
115
|
+
key: "weather_skill",
|
|
116
|
+
name: "天气查询",
|
|
117
|
+
description: "查询城市天气信息",
|
|
118
|
+
trigger: {
|
|
119
|
+
type: "intent_match",
|
|
120
|
+
patterns: ["天气", "气温", "下雨"]
|
|
121
|
+
},
|
|
122
|
+
toolKeys: ["get_weather"],
|
|
123
|
+
examples: [
|
|
124
|
+
{ input: "北京今天天气怎么样", output: "北京今天晴,25°C" }
|
|
125
|
+
]
|
|
126
|
+
});
|
|
127
|
+
|
|
128
|
+
// 3. 创建 Agent
|
|
129
|
+
const agent = await Host.Instance.agentModule.create({
|
|
130
|
+
type: "DeepSeek",
|
|
131
|
+
model: "deepseek-chat",
|
|
132
|
+
apiKey: "sk-xxxxxxxx"
|
|
133
|
+
});
|
|
134
|
+
|
|
135
|
+
// 4. 发送消息
|
|
136
|
+
const response = await agent.send("北京今天天气怎么样");
|
|
137
|
+
console.log(response.content);
|
|
138
|
+
```
|
|
139
|
+
|
|
140
|
+
---
|
|
141
|
+
|
|
142
|
+
## 4. Host 宿主容器
|
|
143
|
+
|
|
144
|
+
`Host` 是单例组合根,持有四个子模块的引用。所有模块通过 `Host.Instance` 访问,不需要手动创建。
|
|
145
|
+
|
|
146
|
+
```typescript
|
|
147
|
+
import { Host } from "knight-agent";
|
|
148
|
+
|
|
149
|
+
// 访问四个子模块
|
|
150
|
+
const llmModule = Host.Instance.llmModule; // LLMModule
|
|
151
|
+
const toolModule = Host.Instance.toolModule; // ToolModule
|
|
152
|
+
const skillModule = Host.Instance.skillModule; // SkillModule
|
|
153
|
+
const agentModule = Host.Instance.agentModule; // AgentModule
|
|
154
|
+
```
|
|
155
|
+
|
|
156
|
+
---
|
|
157
|
+
|
|
158
|
+
## 5. LLM 模块
|
|
159
|
+
|
|
160
|
+
### 5.1 支持的平台
|
|
161
|
+
|
|
162
|
+
| 平台 | type 值 | 类名 |
|
|
163
|
+
| -------- | ----------- | ------------- |
|
|
164
|
+
| Claude | `"Claude"` | `ClaudeLLM` |
|
|
165
|
+
| DeepSeek | `"DeepSeek"`| `DeepSeekLLM` |
|
|
166
|
+
| OpenAI | `"OpenAI"` | `OpenAILLM` |
|
|
167
|
+
| Gemini | `"Gemini"` | `GeminiLLM` |
|
|
168
|
+
| QWen | `"QWen"` | `QWenLLM` |
|
|
169
|
+
|
|
170
|
+
### 5.2 创建 LLM 实例
|
|
171
|
+
|
|
172
|
+
```typescript
|
|
173
|
+
import { LLMUtility } from "knight-agent";
|
|
174
|
+
|
|
175
|
+
// 基础配置(适用于所有平台)
|
|
176
|
+
const config: LLMUtility.Config = {
|
|
177
|
+
type: "DeepSeek",
|
|
178
|
+
model: "deepseek-chat",
|
|
179
|
+
apiKey: "sk-xxxxxxxx",
|
|
180
|
+
systemPrompt: "你是一个有用的助手",
|
|
181
|
+
temperature: 0.7,
|
|
182
|
+
maxTokens: 4096,
|
|
183
|
+
timeout: 60000, // 毫秒
|
|
184
|
+
stream: true, // 是否启用流式
|
|
185
|
+
maxMessageCache: 50, // 最大缓存消息数
|
|
186
|
+
maxToolOutput: 2000 // 工具输出最大字符数
|
|
187
|
+
};
|
|
188
|
+
|
|
189
|
+
const llm = await Host.Instance.llmModule.add(config);
|
|
190
|
+
```
|
|
191
|
+
|
|
192
|
+
### 5.3 平台特有配置
|
|
193
|
+
|
|
194
|
+
```typescript
|
|
195
|
+
// Claude — 支持 thinking 模式
|
|
196
|
+
const claudeConfig: LLMUtility.Claude.Config = {
|
|
197
|
+
type: "Claude",
|
|
198
|
+
model: "claude-sonnet-4-6",
|
|
199
|
+
apiKey: "sk-ant-xxx",
|
|
200
|
+
thinking: true,
|
|
201
|
+
speed: "fast", // "fast" | "default"
|
|
202
|
+
outputConfig: {
|
|
203
|
+
thinking: { budgetTokens: 4000 }
|
|
204
|
+
}
|
|
205
|
+
};
|
|
206
|
+
|
|
207
|
+
// OpenAI — 支持 reasoning_effort 和 web_search
|
|
208
|
+
const openaiConfig: LLMUtility.OpenAI.Config = {
|
|
209
|
+
type: "OpenAI",
|
|
210
|
+
model: "o4-mini",
|
|
211
|
+
apiKey: "sk-xxx",
|
|
212
|
+
reasoningEffort: "medium", // "low" | "medium" | "high"
|
|
213
|
+
webSearchOptions: {
|
|
214
|
+
search_context_size: "medium"
|
|
215
|
+
}
|
|
216
|
+
};
|
|
217
|
+
|
|
218
|
+
// Gemini — 支持 safetySettings 和 thinkingConfig
|
|
219
|
+
const geminiConfig: LLMUtility.Gemini.Config = {
|
|
220
|
+
type: "Gemini",
|
|
221
|
+
model: "gemini-2.5-flash",
|
|
222
|
+
apiKey: "xxx",
|
|
223
|
+
topK: 40,
|
|
224
|
+
thinkingConfig: {
|
|
225
|
+
thinkingBudget: 1024
|
|
226
|
+
}
|
|
227
|
+
};
|
|
228
|
+
|
|
229
|
+
// DeepSeek — 支持 thinking 模式
|
|
230
|
+
const deepSeekConfig: LLMUtility.DeepSeek.Config = {
|
|
231
|
+
type: "DeepSeek",
|
|
232
|
+
model: "deepseek-reasoner",
|
|
233
|
+
apiKey: "sk-xxx",
|
|
234
|
+
thinking: true
|
|
235
|
+
};
|
|
236
|
+
|
|
237
|
+
// QWen — 支持联网搜索
|
|
238
|
+
const qwenConfig: LLMUtility.QWen.Config = {
|
|
239
|
+
type: "QWen",
|
|
240
|
+
model: "qwen-max",
|
|
241
|
+
apiKey: "sk-xxx",
|
|
242
|
+
enableSearch: true
|
|
243
|
+
};
|
|
244
|
+
```
|
|
245
|
+
|
|
246
|
+
### 5.4 直接使用 LLM
|
|
247
|
+
|
|
248
|
+
```typescript
|
|
249
|
+
const llm = await Host.Instance.llmModule.add({
|
|
250
|
+
type: "DeepSeek",
|
|
251
|
+
model: "deepseek-chat",
|
|
252
|
+
apiKey: "sk-xxx"
|
|
253
|
+
});
|
|
254
|
+
|
|
255
|
+
// 发送消息(带工具)
|
|
256
|
+
const response = await llm.send(
|
|
257
|
+
"北京今天天气怎么样?",
|
|
258
|
+
[weatherTool], // ToolDefinition[],可选
|
|
259
|
+
(usage) => { // onUsage 回调
|
|
260
|
+
console.log("输入:", usage.input, "输出:", usage.output);
|
|
261
|
+
},
|
|
262
|
+
(chunk) => { // onChunk 回调(流式)
|
|
263
|
+
process.stdout.write(chunk.content || "");
|
|
264
|
+
}
|
|
265
|
+
);
|
|
266
|
+
|
|
267
|
+
console.log("完整回复:", response.content);
|
|
268
|
+
console.log("Token 用量:", response.usage);
|
|
269
|
+
|
|
270
|
+
// 获取 / 移除
|
|
271
|
+
const same = Host.Instance.llmModule.get(llm.id);
|
|
272
|
+
Host.Instance.llmModule.remove(llm.id);
|
|
273
|
+
Host.Instance.llmModule.removeAll();
|
|
274
|
+
```
|
|
275
|
+
|
|
276
|
+
---
|
|
277
|
+
|
|
278
|
+
## 6. Tool 工具模块
|
|
279
|
+
|
|
280
|
+
### 6.1 定义工具
|
|
281
|
+
|
|
282
|
+
```typescript
|
|
283
|
+
import { LLMUtility } from "knight-agent";
|
|
284
|
+
|
|
285
|
+
const databaseTool: LLMUtility.ToolDefinition = {
|
|
286
|
+
name: "query_database",
|
|
287
|
+
description: "执行 SQL 查询并返回结果",
|
|
288
|
+
inputSchema: {
|
|
289
|
+
type: "object",
|
|
290
|
+
properties: {
|
|
291
|
+
sql: { type: "string", description: "要执行的 SQL 语句" }
|
|
292
|
+
},
|
|
293
|
+
required: ["sql"]
|
|
294
|
+
},
|
|
295
|
+
handler: async (args: Record<string, any>) => {
|
|
296
|
+
// 在这里实现实际的数据库查询逻辑
|
|
297
|
+
const result = await db.query(args.sql);
|
|
298
|
+
return JSON.stringify(result);
|
|
299
|
+
}
|
|
300
|
+
};
|
|
301
|
+
```
|
|
302
|
+
|
|
303
|
+
### 6.2 注册与管理工具
|
|
304
|
+
|
|
305
|
+
```typescript
|
|
306
|
+
const tm = Host.Instance.toolModule;
|
|
307
|
+
|
|
308
|
+
// 注册单个
|
|
309
|
+
tm.register(databaseTool);
|
|
310
|
+
|
|
311
|
+
// 批量注册
|
|
312
|
+
tm.registerAll([toolA, toolB, toolC]);
|
|
313
|
+
|
|
314
|
+
// 查询
|
|
315
|
+
const tool = tm.get("query_database");
|
|
316
|
+
const allTools = tm.getAll();
|
|
317
|
+
const exists = tm.has("query_database");
|
|
318
|
+
|
|
319
|
+
// 按名称批量解析(用于 Skills)
|
|
320
|
+
const resolved = tm.resolve(["tool_a", "tool_b"]);
|
|
321
|
+
// → ToolDefinition[]
|
|
322
|
+
|
|
323
|
+
// 注销
|
|
324
|
+
tm.unregister("query_database");
|
|
325
|
+
```
|
|
326
|
+
|
|
327
|
+
### 6.3 工具执行
|
|
328
|
+
|
|
329
|
+
```typescript
|
|
330
|
+
// 模拟 LLM 返回的工具调用
|
|
331
|
+
const toolCalls: LLMUtility.ToolCall[] = [
|
|
332
|
+
{
|
|
333
|
+
id: "call_001",
|
|
334
|
+
type: "function",
|
|
335
|
+
function: {
|
|
336
|
+
name: "query_database",
|
|
337
|
+
arguments: JSON.stringify({ sql: "SELECT * FROM users" })
|
|
338
|
+
}
|
|
339
|
+
}
|
|
340
|
+
];
|
|
341
|
+
|
|
342
|
+
// 执行工具
|
|
343
|
+
const results: LLMUtility.ToolResult[] = await tm.execute(toolCalls);
|
|
344
|
+
// → [{ tool_call_id: "call_001", role: "tool", content: "..." }]
|
|
345
|
+
```
|
|
346
|
+
|
|
347
|
+
---
|
|
348
|
+
|
|
349
|
+
## 7. Skill 技能模块
|
|
350
|
+
|
|
351
|
+
Skill 是工具 + 提示词 + 触发规则的组合。它定义了一个领域能力,当用户输入匹配到某个 Skill 时,Agent 会自动组装相关工具和提示词。
|
|
352
|
+
|
|
353
|
+
### 7.1 定义技能
|
|
354
|
+
|
|
355
|
+
```typescript
|
|
356
|
+
import { LLMUtility } from "knight-agent";
|
|
357
|
+
|
|
358
|
+
const weatherSkill: LLMUtility.SkillData = {
|
|
359
|
+
key: "weather_skill",
|
|
360
|
+
name: "天气查询",
|
|
361
|
+
description: "你可以查询城市的天气信息。回复时请使用友好的语气。",
|
|
362
|
+
tags: ["weather", "utility"],
|
|
363
|
+
trigger: {
|
|
364
|
+
type: "intent_match", // 基于关键词的意图匹配
|
|
365
|
+
patterns: ["天气", "气温", "下雨", "下雪", "台风"]
|
|
366
|
+
},
|
|
367
|
+
toolKeys: ["get_weather", "get_forecast"],
|
|
368
|
+
dependencies: ["location_skill"],
|
|
369
|
+
examples: [
|
|
370
|
+
{ input: "北京天气怎么样", output: "北京今天晴,25°C" },
|
|
371
|
+
{ input: "明天上海会下雨吗", output: "明天上海有小雨,记得带伞" }
|
|
372
|
+
]
|
|
373
|
+
};
|
|
374
|
+
```
|
|
375
|
+
|
|
376
|
+
### 7.2 注册与查询技能
|
|
377
|
+
|
|
378
|
+
```typescript
|
|
379
|
+
const sm = Host.Instance.skillModule;
|
|
380
|
+
|
|
381
|
+
// 注册
|
|
382
|
+
sm.register(weatherSkill);
|
|
383
|
+
sm.registerAll([skillA, skillB]);
|
|
384
|
+
|
|
385
|
+
// 按条件查询
|
|
386
|
+
sm.findByCategory("utility");
|
|
387
|
+
sm.findByTag("weather");
|
|
388
|
+
sm.findByIntent("天气");
|
|
389
|
+
sm.findByTriggerType("intent_match");
|
|
390
|
+
|
|
391
|
+
// 解析技能的依赖
|
|
392
|
+
const deps = sm.resolveDependencies("weather_skill"); // → SkillData[]
|
|
393
|
+
const tools = sm.resolveTools("weather_skill"); // → ToolDefinition[]
|
|
394
|
+
```
|
|
395
|
+
|
|
396
|
+
### 7.3 意图匹配原理
|
|
397
|
+
|
|
398
|
+
`SkillModule.matchBest(prompt)` 遍历所有 `trigger.type === "intent_match"` 的技能,计算每个技能的 `patterns` 中与用户输入最长子串匹配的字符数,返回得分最高的技能。
|
|
399
|
+
|
|
400
|
+
```typescript
|
|
401
|
+
// 内部机制演示
|
|
402
|
+
const skill = sm.matchBest("我想查一下北京今天天气");
|
|
403
|
+
// → weatherSkill(因为 "天气" pattern 匹配成功)
|
|
404
|
+
```
|
|
405
|
+
|
|
406
|
+
---
|
|
407
|
+
|
|
408
|
+
## 8. Agent 代理模块
|
|
409
|
+
|
|
410
|
+
Agent 是用户交互的顶层入口,它将 Skill 路由、Tool 装配、LLM 调用串联起来。
|
|
411
|
+
|
|
412
|
+
### 8.1 创建 Agent
|
|
413
|
+
|
|
414
|
+
```typescript
|
|
415
|
+
const am = Host.Instance.agentModule;
|
|
416
|
+
|
|
417
|
+
const agent = await am.create({
|
|
418
|
+
type: "DeepSeek",
|
|
419
|
+
model: "deepseek-chat",
|
|
420
|
+
apiKey: "sk-xxx",
|
|
421
|
+
systemPrompt: "你是一个专业的助手"
|
|
422
|
+
});
|
|
423
|
+
|
|
424
|
+
// 或注入已有 LLM
|
|
425
|
+
const existingLLM = Host.Instance.llmModule.add(config);
|
|
426
|
+
const agent2 = await am.create({
|
|
427
|
+
type: "DeepSeek",
|
|
428
|
+
model: "deepseek-chat",
|
|
429
|
+
apiKey: "sk-xxx"
|
|
430
|
+
}, existingLLM); // 第二个参数可选
|
|
431
|
+
```
|
|
432
|
+
|
|
433
|
+
### 8.2 Agent.send() 完整流程
|
|
434
|
+
|
|
435
|
+
```typescript
|
|
436
|
+
// agent.send() 内部执行以下步骤:
|
|
437
|
+
//
|
|
438
|
+
// 1. SkillModule.matchBest(prompt) → 意图匹配
|
|
439
|
+
// 2. SkillModule.buildPrompt(skill, p) → 组装提示词
|
|
440
|
+
// 3. ToolModule.resolve(skill.toolKeys) → 解析工具
|
|
441
|
+
// 4. LLM.send(fullPrompt, tools, ...) → 调用大模型
|
|
442
|
+
// 5. Agent.record(input, output, usage) → 记录上下文
|
|
443
|
+
|
|
444
|
+
const response = await agent.send("北京今天天气怎么样?", (chunk) => {
|
|
445
|
+
// 可选:流式回调
|
|
446
|
+
console.log(chunk.content);
|
|
447
|
+
});
|
|
448
|
+
```
|
|
449
|
+
|
|
450
|
+
### 8.3 查询与管理 Agent
|
|
451
|
+
|
|
452
|
+
```typescript
|
|
453
|
+
const agent = am.get(agentId); // 按 ID 获取
|
|
454
|
+
const all = am.list(); // 列出所有 Agent
|
|
455
|
+
|
|
456
|
+
am.remove(agentId); // 移除单个(同时销毁其 LLM)
|
|
457
|
+
am.removeAll(); // 移除全部
|
|
458
|
+
```
|
|
459
|
+
|
|
460
|
+
### 8.4 Agent 上下文记录
|
|
461
|
+
|
|
462
|
+
每个 Agent 内部维护一个 `turns: SessionData[]` 数组,记录每次对话的输入/输出/用量/时间戳:
|
|
463
|
+
|
|
464
|
+
```typescript
|
|
465
|
+
// AgentUtility.SessionData 结构
|
|
466
|
+
{
|
|
467
|
+
input: "用户原始输入",
|
|
468
|
+
output: { id: "resp_01", model: "deepseek-chat", content: "...", usage: {...} },
|
|
469
|
+
usage: { input: 120, output: 80, total: 200 },
|
|
470
|
+
timestamp: "2026-06-28T12:00:00.000Z"
|
|
471
|
+
}
|
|
472
|
+
```
|
|
473
|
+
|
|
474
|
+
---
|
|
475
|
+
|
|
476
|
+
## 9. 快照与恢复
|
|
477
|
+
|
|
478
|
+
框架支持完整的 Agent/LLM 状态序列化与恢复,适用于跨会话持久化。
|
|
479
|
+
|
|
480
|
+
### 9.1 导出快照
|
|
481
|
+
|
|
482
|
+
```typescript
|
|
483
|
+
const agent = await am.create(config);
|
|
484
|
+
|
|
485
|
+
// 发送一些消息...
|
|
486
|
+
await agent.send("你好");
|
|
487
|
+
|
|
488
|
+
// 导出快照
|
|
489
|
+
const snapshot = agent.exportSnapshot();
|
|
490
|
+
// → AgentUtility.AgentSnapshot {
|
|
491
|
+
// id: "agent-uuid",
|
|
492
|
+
// llmSnapshot: { id, config, messages, timestamp },
|
|
493
|
+
// turns: [...],
|
|
494
|
+
// createdAt: "...",
|
|
495
|
+
// exportAt: "..."
|
|
496
|
+
// }
|
|
497
|
+
|
|
498
|
+
// 序列化为 JSON 存储
|
|
499
|
+
const json = JSON.stringify(snapshot);
|
|
500
|
+
fs.writeFileSync("agent-state.json", json);
|
|
501
|
+
```
|
|
502
|
+
|
|
503
|
+
### 9.2 从快照恢复
|
|
504
|
+
|
|
505
|
+
```typescript
|
|
506
|
+
const json = fs.readFileSync("agent-state.json", "utf-8");
|
|
507
|
+
const snapshot = JSON.parse(json);
|
|
508
|
+
|
|
509
|
+
// 恢复 Agent(包含 LLM 状态 + 对话历史)
|
|
510
|
+
const restoredAgent = await am.restore(snapshot);
|
|
511
|
+
|
|
512
|
+
// 继续之前的对话
|
|
513
|
+
await restoredAgent.send("继续刚才的话题");
|
|
514
|
+
```
|
|
515
|
+
|
|
516
|
+
### 9.3 仅 LLM 快照
|
|
517
|
+
|
|
518
|
+
```typescript
|
|
519
|
+
// 导出 LLM 快照
|
|
520
|
+
const llmSnapshot = llm.exportSnapshot();
|
|
521
|
+
|
|
522
|
+
// 从快照恢复 LLM
|
|
523
|
+
const restoredLLM = Host.Instance.llmModule.restore(llmSnapshot);
|
|
524
|
+
```
|
|
525
|
+
|
|
526
|
+
---
|
|
527
|
+
|
|
528
|
+
## 10. 流式响应
|
|
529
|
+
|
|
530
|
+
所有平台均支持 SSE(Server-Sent Events)流式响应,通过 `onChunk` 回调接收增量数据。
|
|
531
|
+
|
|
532
|
+
### 10.1 LLM 层流式调用
|
|
533
|
+
|
|
534
|
+
```typescript
|
|
535
|
+
const llm = await Host.Instance.llmModule.add({
|
|
536
|
+
type: "Claude",
|
|
537
|
+
model: "claude-sonnet-4-6",
|
|
538
|
+
apiKey: "sk-ant-xxx",
|
|
539
|
+
stream: true // 启用流式
|
|
540
|
+
});
|
|
541
|
+
|
|
542
|
+
const response = await llm.send(
|
|
543
|
+
"写一首关于夏天的诗",
|
|
544
|
+
undefined,
|
|
545
|
+
(usage) => console.log("用量:", usage),
|
|
546
|
+
(chunk: LLMUtility.StreamChunk) => {
|
|
547
|
+
// chunk 结构: { content, delta, toolCalls, finishReason, usage, raw }
|
|
548
|
+
if (chunk.content) {
|
|
549
|
+
process.stdout.write(chunk.content);
|
|
550
|
+
}
|
|
551
|
+
}
|
|
552
|
+
);
|
|
553
|
+
|
|
554
|
+
// response 包含完整聚合结果
|
|
555
|
+
console.log("\n\n完整:", response.content);
|
|
556
|
+
```
|
|
557
|
+
|
|
558
|
+
### 10.2 Agent 层流式调用
|
|
559
|
+
|
|
560
|
+
```typescript
|
|
561
|
+
const agent = await Host.Instance.agentModule.create({
|
|
562
|
+
type: "DeepSeek",
|
|
563
|
+
model: "deepseek-chat",
|
|
564
|
+
apiKey: "sk-xxx"
|
|
565
|
+
});
|
|
566
|
+
|
|
567
|
+
await agent.send("写一首诗", (chunk) => {
|
|
568
|
+
if (chunk.content) {
|
|
569
|
+
process.stdout.write(chunk.content);
|
|
570
|
+
}
|
|
571
|
+
});
|
|
572
|
+
```
|
|
573
|
+
|
|
574
|
+
### 10.3 各平台 SSE 事件差异
|
|
575
|
+
|
|
576
|
+
框架内部处理了不同平台的 SSE 格式:
|
|
577
|
+
|
|
578
|
+
| 平台 | SSE 事件格式 |
|
|
579
|
+
| -------- | --------------------------------------------------------------------------- |
|
|
580
|
+
| Claude | `message_start` / `content_block_start` / `content_block_delta` / `message_delta` |
|
|
581
|
+
| Gemini | `candidates` 数组 + `parts` 增量 |
|
|
582
|
+
| DeepSeek | OpenAI 兼容 delta 格式 |
|
|
583
|
+
| OpenAI | OpenAI 兼容 delta 格式 |
|
|
584
|
+
| QWen | OpenAI 兼容 delta 格式 |
|
|
585
|
+
|
|
586
|
+
---
|
|
587
|
+
|
|
588
|
+
## 11. 工具调用循环
|
|
589
|
+
|
|
590
|
+
当 LLM 返回 `finishReason === "tool_calls"` 时,框架自动进入工具调用循环:
|
|
591
|
+
|
|
592
|
+
```
|
|
593
|
+
用户提示词
|
|
594
|
+
│
|
|
595
|
+
▼
|
|
596
|
+
LLM.send() ──→ LLM 返回 tool_calls
|
|
597
|
+
│ │
|
|
598
|
+
│ ▼
|
|
599
|
+
│ executeTools() ← 调用 handler
|
|
600
|
+
│ │
|
|
601
|
+
│ ▼
|
|
602
|
+
│ 将 ToolResult 传回 LLM
|
|
603
|
+
│ │
|
|
604
|
+
│ ▼
|
|
605
|
+
│ LLM 再次返回...
|
|
606
|
+
│ ├── tool_calls → 继续循环(最多 100 轮)
|
|
607
|
+
│ └── stop → 返回最终 Response
|
|
608
|
+
│
|
|
609
|
+
▼
|
|
610
|
+
返回给调用方
|
|
611
|
+
```
|
|
612
|
+
|
|
613
|
+
### 关键配置
|
|
614
|
+
|
|
615
|
+
```typescript
|
|
616
|
+
const config: LLMUtility.Config = {
|
|
617
|
+
// ...
|
|
618
|
+
maxToolOutput: 2000, // 单次工具输出被截断的最大字符数,防止超出上下文
|
|
619
|
+
maxMessageCache: 50 // 消息缓存上限,超出后按 turn 裁剪
|
|
620
|
+
};
|
|
621
|
+
```
|
|
622
|
+
|
|
623
|
+
`compactMessages()` 会在每次 `send()` 前执行,将消息缓存中的工具结果截断到 `maxToolOutput`。`messages.Trim()` 按 turn 数裁剪,保留 system 消息。
|
|
624
|
+
|
|
625
|
+
---
|
|
626
|
+
|
|
627
|
+
## 12. 扩展自定义 LLM
|
|
628
|
+
|
|
629
|
+
要接入新的 LLM 平台,继承 `AbstractLLM` 并实现 7 个抽象方法:
|
|
630
|
+
|
|
631
|
+
```typescript
|
|
632
|
+
import { AbstractLLM, LLMUtility } from "knight-agent";
|
|
633
|
+
|
|
634
|
+
interface MyPlatformConfig extends LLMUtility.Config {
|
|
635
|
+
type: "MyPlatform";
|
|
636
|
+
customOption: string;
|
|
637
|
+
}
|
|
638
|
+
|
|
639
|
+
interface MyMessageData extends LLMUtility.MessageData {
|
|
640
|
+
role: "system" | "user" | "assistant" | "tool";
|
|
641
|
+
content: string;
|
|
642
|
+
}
|
|
643
|
+
|
|
644
|
+
class MyPlatformLLM extends AbstractLLM<MyPlatformConfig, MyMessageData> {
|
|
645
|
+
|
|
646
|
+
addSystemMessage(config: MyPlatformConfig): void {
|
|
647
|
+
this.messages.Add({ role: "system", content: config.systemPrompt, preserve: true });
|
|
648
|
+
}
|
|
649
|
+
|
|
650
|
+
addUserMessage(prompt: string): void {
|
|
651
|
+
this.messages.Add({ role: "user", content: prompt, preserve: false });
|
|
652
|
+
}
|
|
653
|
+
|
|
654
|
+
addAssistantMessage(response: LLMUtility.Response): void {
|
|
655
|
+
// 需要同时记录工具调用和文本内容
|
|
656
|
+
this.messages.Add({
|
|
657
|
+
role: "assistant",
|
|
658
|
+
content: response.content,
|
|
659
|
+
preserve: false
|
|
660
|
+
});
|
|
661
|
+
}
|
|
662
|
+
|
|
663
|
+
addToolMessage(result: LLMUtility.ToolResult): void {
|
|
664
|
+
this.messages.Add({
|
|
665
|
+
role: "tool",
|
|
666
|
+
content: result.content,
|
|
667
|
+
preserve: false
|
|
668
|
+
});
|
|
669
|
+
}
|
|
670
|
+
|
|
671
|
+
compactMessages(maxOutput: number): void {
|
|
672
|
+
// 遍历消息,截断超长工具输出
|
|
673
|
+
for (const msg of this.messages.All()) {
|
|
674
|
+
if (msg.role === "tool" && msg.content.length > maxOutput) {
|
|
675
|
+
msg.content = msg.content.substring(0, maxOutput) + "...[截断]";
|
|
676
|
+
}
|
|
677
|
+
}
|
|
678
|
+
}
|
|
679
|
+
|
|
680
|
+
stopReason(reason: string): boolean {
|
|
681
|
+
return reason === "stop" || reason === "end_turn";
|
|
682
|
+
}
|
|
683
|
+
|
|
684
|
+
async request(
|
|
685
|
+
tools: LLMUtility.ToolDefinition[] | undefined,
|
|
686
|
+
onUsage: (usage: LLMUtility.Usage) => void,
|
|
687
|
+
onChunk: (chunk: LLMUtility.StreamChunk) => void
|
|
688
|
+
): Promise<LLMUtility.Response> {
|
|
689
|
+
if (this.config.stream) {
|
|
690
|
+
return this.requestStream(tools, onUsage, onChunk);
|
|
691
|
+
}
|
|
692
|
+
return this.requestSync(tools, onUsage);
|
|
693
|
+
}
|
|
694
|
+
|
|
695
|
+
private async requestSync(tools: any[], onUsage: any): Promise<LLMUtility.Response> {
|
|
696
|
+
// 1. 构建请求体(平台特有格式)
|
|
697
|
+
// 2. 发送 HTTP POST
|
|
698
|
+
// 3. 解析响应 → LLMUtility.Response
|
|
699
|
+
// 4. 调用 onUsage(usage)
|
|
700
|
+
// 5. 返回 Response
|
|
701
|
+
throw new Error("未实现");
|
|
702
|
+
}
|
|
703
|
+
|
|
704
|
+
private async requestStream(tools: any[], onUsage: any, onChunk: any): Promise<LLMUtility.Response> {
|
|
705
|
+
// 1. 构建请求体
|
|
706
|
+
// 2. 使用 HTTP.onStreamRequest() 发起 SSE 连接
|
|
707
|
+
// 3. 逐块解析 → 调用 onChunk(chunk)
|
|
708
|
+
// 4. 聚合完整响应
|
|
709
|
+
// 5. 调用 onUsage(usage)
|
|
710
|
+
// 6. 返回聚合 Response
|
|
711
|
+
throw new Error("未实现");
|
|
712
|
+
}
|
|
713
|
+
}
|
|
714
|
+
```
|
|
715
|
+
|
|
716
|
+
然后在 `LLMModule.add()` 的 switch 语句中注册新平台即可。
|
|
717
|
+
|
|
718
|
+
---
|
|
719
|
+
|
|
720
|
+
## 13. 类型参考
|
|
721
|
+
|
|
722
|
+
### AgentUtility
|
|
723
|
+
|
|
724
|
+
```typescript
|
|
725
|
+
// Token 用量
|
|
726
|
+
interface Usage {
|
|
727
|
+
input: number; // 输入 token
|
|
728
|
+
output: number; // 输出 token
|
|
729
|
+
total: number; // 总计 token
|
|
730
|
+
}
|
|
731
|
+
|
|
732
|
+
// 会话回合
|
|
733
|
+
interface SessionData {
|
|
734
|
+
input: string;
|
|
735
|
+
output: LLMUtility.Response;
|
|
736
|
+
usage: Usage;
|
|
737
|
+
timestamp: string;
|
|
738
|
+
}
|
|
739
|
+
|
|
740
|
+
// Agent 快照
|
|
741
|
+
interface AgentSnapshot {
|
|
742
|
+
id: string;
|
|
743
|
+
llmSnapshot: LLMUtility.Snapshot;
|
|
744
|
+
turns: SessionData[];
|
|
745
|
+
createdAt: string;
|
|
746
|
+
exportAt: string;
|
|
747
|
+
}
|
|
748
|
+
```
|
|
749
|
+
|
|
750
|
+
### LLMUtility 核心类型
|
|
751
|
+
|
|
752
|
+
```typescript
|
|
753
|
+
// 工具定义
|
|
754
|
+
interface ToolDefinition {
|
|
755
|
+
name: string;
|
|
756
|
+
description: string;
|
|
757
|
+
inputSchema: JSONSchema;
|
|
758
|
+
handler: (args: Record<string, any>) => Promise<string>;
|
|
759
|
+
}
|
|
760
|
+
|
|
761
|
+
// 工具调用
|
|
762
|
+
interface ToolCall {
|
|
763
|
+
id: string;
|
|
764
|
+
type: "function";
|
|
765
|
+
function: {
|
|
766
|
+
name: string;
|
|
767
|
+
arguments: string; // JSON 字符串
|
|
768
|
+
};
|
|
769
|
+
}
|
|
770
|
+
|
|
771
|
+
// 工具结果
|
|
772
|
+
interface ToolResult {
|
|
773
|
+
tool_call_id: string;
|
|
774
|
+
role: "tool";
|
|
775
|
+
content: string;
|
|
776
|
+
}
|
|
777
|
+
|
|
778
|
+
// 统一响应
|
|
779
|
+
interface Response {
|
|
780
|
+
id: string;
|
|
781
|
+
model: string;
|
|
782
|
+
content: string | null;
|
|
783
|
+
toolCalls: ToolCall[];
|
|
784
|
+
finishReason: string;
|
|
785
|
+
usage: Usage;
|
|
786
|
+
raw: any; // 平台原始响应
|
|
787
|
+
}
|
|
788
|
+
|
|
789
|
+
// 流式块
|
|
790
|
+
interface StreamChunk {
|
|
791
|
+
content: string | null;
|
|
792
|
+
delta: any;
|
|
793
|
+
toolCalls: ToolCall[];
|
|
794
|
+
finishReason: string | null;
|
|
795
|
+
usage: Usage | null;
|
|
796
|
+
raw: any;
|
|
797
|
+
}
|
|
798
|
+
|
|
799
|
+
// 技能定义
|
|
800
|
+
interface SkillData {
|
|
801
|
+
key: string;
|
|
802
|
+
name: string;
|
|
803
|
+
description: string;
|
|
804
|
+
tags?: string[];
|
|
805
|
+
trigger: SkillTrigger;
|
|
806
|
+
toolKeys?: string[];
|
|
807
|
+
dependencies?: string[];
|
|
808
|
+
examples?: SkillExample[];
|
|
809
|
+
}
|
|
810
|
+
|
|
811
|
+
interface SkillTrigger {
|
|
812
|
+
type: "intent_match" | "event" | "manual";
|
|
813
|
+
patterns?: string[];
|
|
814
|
+
event?: string;
|
|
815
|
+
}
|
|
816
|
+
|
|
817
|
+
interface SkillExample {
|
|
818
|
+
input: string;
|
|
819
|
+
output: string;
|
|
820
|
+
}
|
|
821
|
+
|
|
822
|
+
// 快照
|
|
823
|
+
interface Snapshot {
|
|
824
|
+
id: string;
|
|
825
|
+
config: Config;
|
|
826
|
+
messages: MessageData[];
|
|
827
|
+
timestamp: string;
|
|
828
|
+
}
|
|
829
|
+
```
|
|
830
|
+
|
|
831
|
+
### 工具类
|
|
832
|
+
|
|
833
|
+
```typescript
|
|
834
|
+
// ObjectUtility 中的数据结构
|
|
835
|
+
class ArrayPool<T> // 基于数组的对象池:Get / Add / Remove / Exist
|
|
836
|
+
class MapPool<T> // 基于 Map 的对象池:Get / Add / Set / Remove / KeysToArray / ValuesToArray
|
|
837
|
+
class QueuePool<T> // 队列:Enqueue / Dequeue / Peek / IsEmpty / Size / Clear / Clone
|
|
838
|
+
|
|
839
|
+
// LogUtility
|
|
840
|
+
function LogTip(msg: string): void; // 灰色
|
|
841
|
+
function LogInfo(msg: string): void; // 白色
|
|
842
|
+
function LogWarning(msg: string): void; // 黄色
|
|
843
|
+
function LogError(msg: string): void; // 红色
|
|
844
|
+
|
|
845
|
+
// NetworkUtility
|
|
846
|
+
async function HTTP.OnRequest(req: Request): Promise<Response>; // 标准 HTTP
|
|
847
|
+
async function HTTP.onStreamRequest(req: Request): AsyncGenerator<any>; // SSE 流
|
|
848
|
+
|
|
849
|
+
// 其他工具
|
|
850
|
+
function StringUtility.empty(str: string | null | undefined): boolean;
|
|
851
|
+
function StringUtility.GetHash(str: string): number;
|
|
852
|
+
function StringUtility.GetGUID(): string;
|
|
853
|
+
function TimeUtility.Now(): number;
|
|
854
|
+
function TimeUtility.NowString(): string;
|
|
855
|
+
function TimeUtility.NowHEX(): string;
|
|
856
|
+
function PromiseUtility.Wait(ms: number): Promise<void>;
|
|
857
|
+
```
|
|
858
|
+
|
|
859
|
+
---
|
|
860
|
+
|
|
861
|
+
## 附录:完整示例 — 构建一个数据库查询 Agent
|
|
862
|
+
|
|
863
|
+
```typescript
|
|
864
|
+
import { Host, LLMUtility } from "knight-agent";
|
|
865
|
+
|
|
866
|
+
async function main() {
|
|
867
|
+
// === 注册工具 ===
|
|
868
|
+
const tm = Host.Instance.toolModule;
|
|
869
|
+
|
|
870
|
+
tm.register({
|
|
871
|
+
name: "execute_sql",
|
|
872
|
+
description: "执行 SQL 查询。仅支持 SELECT 语句。",
|
|
873
|
+
inputSchema: {
|
|
874
|
+
type: "object",
|
|
875
|
+
properties: {
|
|
876
|
+
sql: { type: "string", description: "SQL SELECT 语句" }
|
|
877
|
+
},
|
|
878
|
+
required: ["sql"]
|
|
879
|
+
},
|
|
880
|
+
handler: async (args) => {
|
|
881
|
+
// 安全校验
|
|
882
|
+
const sql: string = args.sql.trim().toUpperCase();
|
|
883
|
+
if (!sql.startsWith("SELECT")) {
|
|
884
|
+
return "错误:仅允许 SELECT 查询";
|
|
885
|
+
}
|
|
886
|
+
// 模拟数据库查询
|
|
887
|
+
const mockData = [
|
|
888
|
+
{ id: 1, name: "Alice", role: "工程师" },
|
|
889
|
+
{ id: 2, name: "Bob", role: "设计师" },
|
|
890
|
+
{ id: 3, name: "Carol", role: "产品经理" }
|
|
891
|
+
];
|
|
892
|
+
return JSON.stringify(mockData, null, 2);
|
|
893
|
+
}
|
|
894
|
+
});
|
|
895
|
+
|
|
896
|
+
tm.register({
|
|
897
|
+
name: "get_schema",
|
|
898
|
+
description: "获取数据库表结构信息",
|
|
899
|
+
inputSchema: {
|
|
900
|
+
type: "object",
|
|
901
|
+
properties: {
|
|
902
|
+
table: { type: "string", description: "表名,留空返回所有表" }
|
|
903
|
+
},
|
|
904
|
+
required: []
|
|
905
|
+
},
|
|
906
|
+
handler: async (args) => {
|
|
907
|
+
const schemas: Record<string, any> = {
|
|
908
|
+
"users": {
|
|
909
|
+
columns: [
|
|
910
|
+
{ name: "id", type: "INTEGER", primaryKey: true },
|
|
911
|
+
{ name: "name", type: "VARCHAR(100)" },
|
|
912
|
+
{ name: "role", type: "VARCHAR(50)" },
|
|
913
|
+
{ name: "created_at", type: "DATETIME" }
|
|
914
|
+
],
|
|
915
|
+
rowCount: 1024
|
|
916
|
+
},
|
|
917
|
+
"orders": {
|
|
918
|
+
columns: [
|
|
919
|
+
{ name: "id", type: "INTEGER", primaryKey: true },
|
|
920
|
+
{ name: "user_id", type: "INTEGER" },
|
|
921
|
+
{ name: "amount", type: "DECIMAL(10,2)" },
|
|
922
|
+
{ name: "status", type: "VARCHAR(20)" }
|
|
923
|
+
],
|
|
924
|
+
rowCount: 5120
|
|
925
|
+
}
|
|
926
|
+
};
|
|
927
|
+
|
|
928
|
+
if (args.table && schemas[args.table]) {
|
|
929
|
+
return JSON.stringify(schemas[args.table], null, 2);
|
|
930
|
+
}
|
|
931
|
+
return JSON.stringify(Object.keys(schemas), null, 2);
|
|
932
|
+
}
|
|
933
|
+
});
|
|
934
|
+
|
|
935
|
+
// === 注册技能 ===
|
|
936
|
+
Host.Instance.skillModule.register({
|
|
937
|
+
key: "db_skill",
|
|
938
|
+
name: "数据库查询助手",
|
|
939
|
+
description: "你是一个数据库查询助手。在查询前,先使用 get_schema 了解表结构,再用 execute_sql 执行查询。回复时请将结果格式化输出。",
|
|
940
|
+
trigger: {
|
|
941
|
+
type: "intent_match",
|
|
942
|
+
patterns: ["查询", "数据", "SQL", "数据库", "表", "统计"]
|
|
943
|
+
},
|
|
944
|
+
toolKeys: ["get_schema", "execute_sql"],
|
|
945
|
+
examples: [
|
|
946
|
+
{
|
|
947
|
+
input: "查询所有用户",
|
|
948
|
+
output: "我先查看 users 表结构,然后执行查询。结果如下:\n1. Alice - 工程师\n2. Bob - 设计师\n3. Carol - 产品经理"
|
|
949
|
+
}
|
|
950
|
+
]
|
|
951
|
+
});
|
|
952
|
+
|
|
953
|
+
// === 创建 Agent ===
|
|
954
|
+
const agent = await Host.Instance.agentModule.create({
|
|
955
|
+
type: "DeepSeek",
|
|
956
|
+
model: "deepseek-chat",
|
|
957
|
+
apiKey: process.env.DEEPSEEK_API_KEY!,
|
|
958
|
+
systemPrompt: "你是一个数据库查询助手。请使用提供的工具来帮助用户查询数据。",
|
|
959
|
+
temperature: 0.3,
|
|
960
|
+
maxTokens: 4096
|
|
961
|
+
});
|
|
962
|
+
|
|
963
|
+
// === 交互 ===
|
|
964
|
+
const response = await agent.send("查询 users 表中所有用户的姓名和角色");
|
|
965
|
+
console.log(response.content);
|
|
966
|
+
|
|
967
|
+
// === 导出快照 ===
|
|
968
|
+
const snapshot = agent.exportSnapshot();
|
|
969
|
+
console.log("快照已生成,turns 数:", snapshot.turns.length);
|
|
970
|
+
|
|
971
|
+
// === 清理 ===
|
|
972
|
+
Host.Instance.agentModule.removeAll();
|
|
973
|
+
Host.Instance.toolModule.unregister("execute_sql");
|
|
974
|
+
Host.Instance.toolModule.unregister("get_schema");
|
|
975
|
+
}
|
|
976
|
+
|
|
977
|
+
main().catch(console.error);
|
|
978
|
+
```
|