@whwrm/react-agent 1.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/README.md +413 -0
- package/dist/agent.d.ts +34 -0
- package/dist/agent.d.ts.map +1 -0
- package/dist/agent.js +337 -0
- package/dist/agent.js.map +1 -0
- package/dist/index.d.ts +16 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +52 -0
- package/dist/index.js.map +1 -0
- package/dist/providers.d.ts +22 -0
- package/dist/providers.d.ts.map +1 -0
- package/dist/providers.js +424 -0
- package/dist/providers.js.map +1 -0
- package/dist/registry.d.ts +47 -0
- package/dist/registry.d.ts.map +1 -0
- package/dist/registry.js +165 -0
- package/dist/registry.js.map +1 -0
- package/dist/types.d.ts +130 -0
- package/dist/types.d.ts.map +1 -0
- package/dist/types.js +2 -0
- package/dist/types.js.map +1 -0
- package/package.json +43 -0
package/README.md
ADDED
|
@@ -0,0 +1,413 @@
|
|
|
1
|
+
# React-Agent
|
|
2
|
+
|
|
3
|
+
一个轻量级的 ReAct (Reasoning + Acting) 框架,让 LLM 能够自主调用工具完成任务。
|
|
4
|
+
|
|
5
|
+
## 特性
|
|
6
|
+
|
|
7
|
+
- **极简调用** - 一行代码即可使用
|
|
8
|
+
- **自主决策** - LLM 自动决定调用哪些工具、调用顺序
|
|
9
|
+
- **链式依赖** - 自动处理 A→B→C 的依赖关系
|
|
10
|
+
- **并行优化** - 独立操作自动并行执行
|
|
11
|
+
- **多 LLM 支持** - 支持 OpenAI、DeepSeek、GLM、Qwen 等 15+ 平台
|
|
12
|
+
- **热加载配置** - JSON 配置文件,无需重新构建
|
|
13
|
+
|
|
14
|
+
## 安装
|
|
15
|
+
|
|
16
|
+
```bash
|
|
17
|
+
npm install react-agent
|
|
18
|
+
```
|
|
19
|
+
|
|
20
|
+
## 快速开始
|
|
21
|
+
|
|
22
|
+
### 最简使用(3行代码)
|
|
23
|
+
|
|
24
|
+
```typescript
|
|
25
|
+
import { ask, loadConfig } from 'react-agent';
|
|
26
|
+
|
|
27
|
+
const config = loadConfig('./config.json');
|
|
28
|
+
const answer = await ask(config, '你好');
|
|
29
|
+
```
|
|
30
|
+
|
|
31
|
+
### 带工具的完整示例
|
|
32
|
+
|
|
33
|
+
```typescript
|
|
34
|
+
import { ask, createToolServer, registerTool, getToolDefinitions } from 'react-agent';
|
|
35
|
+
|
|
36
|
+
// 1. 注册工具
|
|
37
|
+
registerTool('get_weather', async (args) => {
|
|
38
|
+
const res = await fetch(`https://wttr.in/${args.city}?format=j1`);
|
|
39
|
+
const data = await res.json();
|
|
40
|
+
return `${args.city}: ${data.current_condition[0].temp_C}°C`;
|
|
41
|
+
}, {
|
|
42
|
+
name: 'get_weather',
|
|
43
|
+
description: '获取城市天气',
|
|
44
|
+
parameters: {
|
|
45
|
+
type: 'object',
|
|
46
|
+
properties: { city: { type: 'string', description: '城市名称' } },
|
|
47
|
+
required: ['city']
|
|
48
|
+
}
|
|
49
|
+
});
|
|
50
|
+
|
|
51
|
+
// 2. 启动工具服务器
|
|
52
|
+
const server = await createToolServer({ port: 3000 });
|
|
53
|
+
|
|
54
|
+
// 3. 调用
|
|
55
|
+
const answer = await ask({
|
|
56
|
+
llm: {
|
|
57
|
+
model: 'glm-5',
|
|
58
|
+
apiKey: 'your-api-key',
|
|
59
|
+
baseUrl: 'https://coding.dashscope.aliyuncs.com/v1',
|
|
60
|
+
},
|
|
61
|
+
tools: {
|
|
62
|
+
url: 'http://localhost:3000',
|
|
63
|
+
definitions: getToolDefinitions(),
|
|
64
|
+
},
|
|
65
|
+
}, '北京今天天气怎么样?');
|
|
66
|
+
|
|
67
|
+
console.log(answer);
|
|
68
|
+
server.close();
|
|
69
|
+
```
|
|
70
|
+
|
|
71
|
+
## 配置说明
|
|
72
|
+
|
|
73
|
+
### config.json
|
|
74
|
+
|
|
75
|
+
```json
|
|
76
|
+
{
|
|
77
|
+
"llm": {
|
|
78
|
+
"model": "glm-5",
|
|
79
|
+
"apiKey": "${API_KEY}",
|
|
80
|
+
"baseUrl": "https://coding.dashscope.aliyuncs.com/v1"
|
|
81
|
+
},
|
|
82
|
+
"systemPrompt": "你是一个智能助手",
|
|
83
|
+
"maxIterations": 10,
|
|
84
|
+
"contextWindowTokens": 128000,
|
|
85
|
+
"tools": {
|
|
86
|
+
"url": "http://localhost:3000",
|
|
87
|
+
"definitionsFile": "./tools.json"
|
|
88
|
+
}
|
|
89
|
+
}
|
|
90
|
+
```
|
|
91
|
+
|
|
92
|
+
### tools.json
|
|
93
|
+
|
|
94
|
+
```json
|
|
95
|
+
[
|
|
96
|
+
{
|
|
97
|
+
"name": "get_weather",
|
|
98
|
+
"description": "获取城市天气信息",
|
|
99
|
+
"parameters": {
|
|
100
|
+
"type": "object",
|
|
101
|
+
"properties": {
|
|
102
|
+
"city": { "type": "string", "description": "城市名称" }
|
|
103
|
+
},
|
|
104
|
+
"required": ["city"]
|
|
105
|
+
}
|
|
106
|
+
},
|
|
107
|
+
{
|
|
108
|
+
"name": "search_web",
|
|
109
|
+
"description": "搜索网络信息",
|
|
110
|
+
"parameters": {
|
|
111
|
+
"type": "object",
|
|
112
|
+
"properties": {
|
|
113
|
+
"query": { "type": "string", "description": "搜索关键词" }
|
|
114
|
+
},
|
|
115
|
+
"required": ["query"]
|
|
116
|
+
}
|
|
117
|
+
}
|
|
118
|
+
]
|
|
119
|
+
```
|
|
120
|
+
|
|
121
|
+
## 支持的 LLM 平台
|
|
122
|
+
|
|
123
|
+
| 平台 | model 示例 | baseUrl |
|
|
124
|
+
|------|-----------|---------|
|
|
125
|
+
| OpenAI | gpt-4, gpt-3.5-turbo | https://api.openai.com |
|
|
126
|
+
| DeepSeek | deepseek-chat, deepseek-coder | https://api.deepseek.com |
|
|
127
|
+
| 智谱 GLM | glm-4, glm-5 | https://open.bigmodel.cn |
|
|
128
|
+
| 阿里 Qwen | qwen-plus, qwen-turbo | https://dashscope.aliyuncs.com |
|
|
129
|
+
| Moonshot | moonshot-v1-8k | https://api.moonshot.ai |
|
|
130
|
+
| MiniMax | abab6.5-chat | https://api.minimax.chat |
|
|
131
|
+
| 自定义 | 任意 | 你的 API 地址 |
|
|
132
|
+
|
|
133
|
+
### 自定义 API 端点
|
|
134
|
+
|
|
135
|
+
```typescript
|
|
136
|
+
const config = {
|
|
137
|
+
llm: {
|
|
138
|
+
model: 'your-model',
|
|
139
|
+
apiKey: 'your-key',
|
|
140
|
+
baseUrl: 'https://your-api-endpoint/v1', // 自定义 URL
|
|
141
|
+
},
|
|
142
|
+
};
|
|
143
|
+
```
|
|
144
|
+
|
|
145
|
+
## API 参考
|
|
146
|
+
|
|
147
|
+
### ask(config, message, callbacks?)
|
|
148
|
+
|
|
149
|
+
一行调用,返回最终答案。
|
|
150
|
+
|
|
151
|
+
```typescript
|
|
152
|
+
const answer = await ask(config, '你的问题');
|
|
153
|
+
```
|
|
154
|
+
|
|
155
|
+
### askFull(config, message, callbacks?)
|
|
156
|
+
|
|
157
|
+
返回完整结果,包含统计信息。
|
|
158
|
+
|
|
159
|
+
```typescript
|
|
160
|
+
const result = await askFull(config, '你的问题');
|
|
161
|
+
console.log(result.content); // 回答内容
|
|
162
|
+
console.log(result.toolCallsCount); // 工具调用次数
|
|
163
|
+
console.log(result.iterations); // 迭代次数
|
|
164
|
+
```
|
|
165
|
+
|
|
166
|
+
### registerTool(name, handler, definition)
|
|
167
|
+
|
|
168
|
+
注册工具。
|
|
169
|
+
|
|
170
|
+
```typescript
|
|
171
|
+
registerTool('tool_name', async (args) => {
|
|
172
|
+
// 工具逻辑
|
|
173
|
+
return '结果';
|
|
174
|
+
}, {
|
|
175
|
+
name: 'tool_name',
|
|
176
|
+
description: '工具描述',
|
|
177
|
+
parameters: {
|
|
178
|
+
type: 'object',
|
|
179
|
+
properties: {
|
|
180
|
+
param1: { type: 'string', description: '参数描述' }
|
|
181
|
+
},
|
|
182
|
+
required: ['param1']
|
|
183
|
+
}
|
|
184
|
+
});
|
|
185
|
+
```
|
|
186
|
+
|
|
187
|
+
### createToolServer(options)
|
|
188
|
+
|
|
189
|
+
启动工具服务器。
|
|
190
|
+
|
|
191
|
+
```typescript
|
|
192
|
+
const server = await createToolServer({
|
|
193
|
+
port: 3000,
|
|
194
|
+
tools: [{ name: 'xxx', handler: async () => '...' }] // 可选
|
|
195
|
+
});
|
|
196
|
+
// 完成后关闭
|
|
197
|
+
server.close();
|
|
198
|
+
```
|
|
199
|
+
|
|
200
|
+
### 回调函数
|
|
201
|
+
|
|
202
|
+
```typescript
|
|
203
|
+
const answer = await ask(config, '问题', {
|
|
204
|
+
onLLMResponse: (response) => {
|
|
205
|
+
console.log('LLM 响应:', response.finish_reason);
|
|
206
|
+
console.log('工具调用:', response.tool_calls);
|
|
207
|
+
},
|
|
208
|
+
onToolCall: (name, args) => {
|
|
209
|
+
console.log('调用工具:', name, args);
|
|
210
|
+
},
|
|
211
|
+
onToolResult: (name, result) => {
|
|
212
|
+
console.log('工具结果:', result);
|
|
213
|
+
},
|
|
214
|
+
onError: (error, context) => {
|
|
215
|
+
console.error('错误:', error);
|
|
216
|
+
},
|
|
217
|
+
});
|
|
218
|
+
```
|
|
219
|
+
|
|
220
|
+
## 高级用法
|
|
221
|
+
|
|
222
|
+
### Tools vs Skills
|
|
223
|
+
|
|
224
|
+
**Tool(工具)**:单一操作,获取/执行数据
|
|
225
|
+
- `get_weather(city)` - 查天气
|
|
226
|
+
- `run_command(cmd)` - 执行命令
|
|
227
|
+
|
|
228
|
+
**Skill(技能)**:固化的工作流程,包含知识背景
|
|
229
|
+
- `travel_planning` - 旅行规划(可能包含搜索、分析、生成报告)
|
|
230
|
+
- `report_writing` - 报告撰写(可能包含数据收集、分析、格式化)
|
|
231
|
+
|
|
232
|
+
```typescript
|
|
233
|
+
import { registerTool, registerSkill } from 'react-agent';
|
|
234
|
+
|
|
235
|
+
// Tool - 单一操作
|
|
236
|
+
registerTool('get_weather', async (args) => {
|
|
237
|
+
return `${args.city}: 25°C`;
|
|
238
|
+
}, { name: 'get_weather', description: '获取天气', parameters: {...} });
|
|
239
|
+
|
|
240
|
+
// Skill - 工作流程
|
|
241
|
+
registerSkill('travel_planning', async (args) => {
|
|
242
|
+
// 可以包含多个步骤、知识背景
|
|
243
|
+
return `行程规划...`;
|
|
244
|
+
}, {
|
|
245
|
+
name: 'travel_planning',
|
|
246
|
+
description: '旅行规划',
|
|
247
|
+
prompt: '你是一个旅行规划专家...', // 知识背景
|
|
248
|
+
parameters: {...}
|
|
249
|
+
});
|
|
250
|
+
```
|
|
251
|
+
|
|
252
|
+
### 链式依赖
|
|
253
|
+
|
|
254
|
+
Agent 会自动处理依赖关系:
|
|
255
|
+
|
|
256
|
+
```
|
|
257
|
+
用户: 查询张三的订单详情
|
|
258
|
+
|
|
259
|
+
执行过程:
|
|
260
|
+
1. get_user_id("张三") → user_001
|
|
261
|
+
2. get_orders("user_001") → [订单列表]
|
|
262
|
+
3. get_order_detail("ORD-001") → 订单详情
|
|
263
|
+
4. 生成最终回答
|
|
264
|
+
```
|
|
265
|
+
|
|
266
|
+
### 并行执行
|
|
267
|
+
|
|
268
|
+
独立操作自动并行:
|
|
269
|
+
|
|
270
|
+
```
|
|
271
|
+
用户: 查询北京和上海的天气
|
|
272
|
+
|
|
273
|
+
执行过程:
|
|
274
|
+
1. 并行调用:
|
|
275
|
+
- get_weather("北京")
|
|
276
|
+
- get_weather("上海")
|
|
277
|
+
2. 生成最终回答
|
|
278
|
+
```
|
|
279
|
+
|
|
280
|
+
### 自主纠错
|
|
281
|
+
|
|
282
|
+
Agent 会根据工具返回结果自主判断:
|
|
283
|
+
|
|
284
|
+
```
|
|
285
|
+
用户: 火星今天天气怎么样?
|
|
286
|
+
|
|
287
|
+
执行过程:
|
|
288
|
+
1. get_weather("火星") → 错误:无法获取火星天气
|
|
289
|
+
2. Agent 理解错误,解释原因,不再重复调用
|
|
290
|
+
```
|
|
291
|
+
|
|
292
|
+
## 项目结构
|
|
293
|
+
|
|
294
|
+
```
|
|
295
|
+
your-project/
|
|
296
|
+
├── config.json # Agent 配置
|
|
297
|
+
├── tools.json # 工具定义(可选)
|
|
298
|
+
├── tools/ # 工具实现
|
|
299
|
+
│ ├── weather.ts
|
|
300
|
+
│ ├── search.ts
|
|
301
|
+
│ └── index.ts
|
|
302
|
+
└── main.ts # 入口文件
|
|
303
|
+
```
|
|
304
|
+
|
|
305
|
+
## 示例:完整项目
|
|
306
|
+
|
|
307
|
+
### tools/index.ts
|
|
308
|
+
|
|
309
|
+
```typescript
|
|
310
|
+
import { registerTool } from 'react-agent';
|
|
311
|
+
|
|
312
|
+
registerTool('get_weather', async (args) => {
|
|
313
|
+
const res = await fetch(`https://wttr.in/${args.city}?format=j1`);
|
|
314
|
+
const data = await res.json();
|
|
315
|
+
const current = data.current_condition[0];
|
|
316
|
+
return `${args.city}: ${current.temp_C}°C, ${current.weatherDesc[0].value}`;
|
|
317
|
+
}, {
|
|
318
|
+
name: 'get_weather',
|
|
319
|
+
description: '获取城市天气',
|
|
320
|
+
parameters: {
|
|
321
|
+
type: 'object',
|
|
322
|
+
properties: { city: { type: 'string' } },
|
|
323
|
+
required: ['city']
|
|
324
|
+
}
|
|
325
|
+
});
|
|
326
|
+
|
|
327
|
+
registerTool('search', async (args) => {
|
|
328
|
+
// 搜索实现
|
|
329
|
+
return '搜索结果...';
|
|
330
|
+
}, {
|
|
331
|
+
name: 'search',
|
|
332
|
+
description: '搜索网络',
|
|
333
|
+
parameters: {
|
|
334
|
+
type: 'object',
|
|
335
|
+
properties: { query: { type: 'string' } },
|
|
336
|
+
required: ['query']
|
|
337
|
+
}
|
|
338
|
+
});
|
|
339
|
+
```
|
|
340
|
+
|
|
341
|
+
### main.ts
|
|
342
|
+
|
|
343
|
+
```typescript
|
|
344
|
+
import { ask, createToolServer, getToolDefinitions } from 'react-agent';
|
|
345
|
+
import './tools/index.js'; // 加载工具
|
|
346
|
+
|
|
347
|
+
async function main() {
|
|
348
|
+
const server = await createToolServer({ port: 3000 });
|
|
349
|
+
|
|
350
|
+
const answer = await ask({
|
|
351
|
+
llm: {
|
|
352
|
+
model: 'glm-5',
|
|
353
|
+
apiKey: process.env.API_KEY,
|
|
354
|
+
baseUrl: 'https://coding.dashscope.aliyuncs.com/v1',
|
|
355
|
+
},
|
|
356
|
+
tools: {
|
|
357
|
+
url: 'http://localhost:3000',
|
|
358
|
+
definitions: getToolDefinitions(),
|
|
359
|
+
},
|
|
360
|
+
}, '帮我查一下北京天气');
|
|
361
|
+
|
|
362
|
+
console.log(answer);
|
|
363
|
+
server.close();
|
|
364
|
+
}
|
|
365
|
+
|
|
366
|
+
main();
|
|
367
|
+
```
|
|
368
|
+
|
|
369
|
+
## 常见问题
|
|
370
|
+
|
|
371
|
+
### Q: 如何使用环境变量?
|
|
372
|
+
|
|
373
|
+
```json
|
|
374
|
+
{
|
|
375
|
+
"llm": {
|
|
376
|
+
"apiKey": "${OPENAI_API_KEY}"
|
|
377
|
+
}
|
|
378
|
+
}
|
|
379
|
+
```
|
|
380
|
+
|
|
381
|
+
### Q: 如何限制最大迭代次数?
|
|
382
|
+
|
|
383
|
+
```typescript
|
|
384
|
+
const config = {
|
|
385
|
+
maxIterations: 5, // 默认 20
|
|
386
|
+
};
|
|
387
|
+
```
|
|
388
|
+
|
|
389
|
+
### Q: 如何监控 token 使用?
|
|
390
|
+
|
|
391
|
+
```typescript
|
|
392
|
+
const agent = new ReactAgent(config);
|
|
393
|
+
const answer = await agent.run('问题');
|
|
394
|
+
|
|
395
|
+
const status = agent.getContextStatus();
|
|
396
|
+
console.log('估算 tokens:', status.estimatedTokens);
|
|
397
|
+
console.log('使用率:', status.usagePercent);
|
|
398
|
+
|
|
399
|
+
const usage = agent.getLastUsage();
|
|
400
|
+
console.log('实际 prompt tokens:', usage.promptTokens);
|
|
401
|
+
```
|
|
402
|
+
|
|
403
|
+
### Q: 如何处理超时?
|
|
404
|
+
|
|
405
|
+
```typescript
|
|
406
|
+
const config = {
|
|
407
|
+
maxIterations: 10, // 限制迭代次数
|
|
408
|
+
};
|
|
409
|
+
```
|
|
410
|
+
|
|
411
|
+
## License
|
|
412
|
+
|
|
413
|
+
MIT
|
package/dist/agent.d.ts
ADDED
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
import type { AgentConfig, AgentCallbacks, Message, ToolDefinition, SkillDefinition, AgentResult, ContextStatus } from './types.js';
|
|
2
|
+
export declare class ReactAgent {
|
|
3
|
+
private config;
|
|
4
|
+
private messages;
|
|
5
|
+
private toolDefinitions;
|
|
6
|
+
private skillDefinitions;
|
|
7
|
+
private toolCallsCount;
|
|
8
|
+
private iterations;
|
|
9
|
+
private lastUsage;
|
|
10
|
+
constructor(config: AgentConfig);
|
|
11
|
+
private loadDefinitions;
|
|
12
|
+
run(userMessage: string, callbacks?: AgentCallbacks): Promise<string>;
|
|
13
|
+
runFull(userMessage: string, callbacks?: AgentCallbacks): Promise<AgentResult>;
|
|
14
|
+
private callLLM;
|
|
15
|
+
private executeTool;
|
|
16
|
+
private callToolAPI;
|
|
17
|
+
private callSkillAPI;
|
|
18
|
+
private buildMessages;
|
|
19
|
+
private getToolDefinitions;
|
|
20
|
+
clear(): void;
|
|
21
|
+
reload(): void;
|
|
22
|
+
getMessages(): Message[];
|
|
23
|
+
getToolDefinitionsList(): ToolDefinition[];
|
|
24
|
+
getSkillDefinitionsList(): SkillDefinition[];
|
|
25
|
+
getEstimatedPromptTokens(): number;
|
|
26
|
+
getContextUsageRatio(): number;
|
|
27
|
+
getContextStatus(): ContextStatus;
|
|
28
|
+
getLastUsage(): {
|
|
29
|
+
promptTokens?: number;
|
|
30
|
+
completionTokens?: number;
|
|
31
|
+
};
|
|
32
|
+
isNearContextLimit(threshold?: number): boolean;
|
|
33
|
+
}
|
|
34
|
+
//# sourceMappingURL=agent.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"agent.d.ts","sourceRoot":"","sources":["../src/agent.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EACV,WAAW,EACX,cAAc,EACd,OAAO,EAEP,cAAc,EACd,eAAe,EAEf,WAAW,EACX,aAAa,EACd,MAAM,YAAY,CAAC;AA6CpB,qBAAa,UAAU;IACrB,OAAO,CAAC,MAAM,CAAqF;IACnG,OAAO,CAAC,QAAQ,CAAiB;IACjC,OAAO,CAAC,eAAe,CAAwB;IAC/C,OAAO,CAAC,gBAAgB,CAAyB;IACjD,OAAO,CAAC,cAAc,CAAa;IACnC,OAAO,CAAC,UAAU,CAAa;IAC/B,OAAO,CAAC,SAAS,CAA4D;gBAEjE,MAAM,EAAE,WAAW;IAS/B,OAAO,CAAC,eAAe;IA8BjB,GAAG,CAAC,WAAW,EAAE,MAAM,EAAE,SAAS,CAAC,EAAE,cAAc,GAAG,OAAO,CAAC,MAAM,CAAC;IAKrE,OAAO,CAAC,WAAW,EAAE,MAAM,EAAE,SAAS,CAAC,EAAE,cAAc,GAAG,OAAO,CAAC,WAAW,CAAC;YAsFtE,OAAO;YA4BP,WAAW;YASX,WAAW;YAgCX,YAAY;IAgC1B,OAAO,CAAC,aAAa;IAWrB,OAAO,CAAC,kBAAkB;IAwB1B,KAAK,IAAI,IAAI;IAOb,MAAM,IAAI,IAAI;IAId,WAAW,IAAI,OAAO,EAAE;IAIxB,sBAAsB,IAAI,cAAc,EAAE;IAI1C,uBAAuB,IAAI,eAAe,EAAE;IAI5C,wBAAwB,IAAI,MAAM;IAUlC,oBAAoB,IAAI,MAAM;IAK9B,gBAAgB,IAAI,aAAa;IAuBjC,YAAY,IAAI;QAAE,YAAY,CAAC,EAAE,MAAM,CAAC;QAAC,gBAAgB,CAAC,EAAE,MAAM,CAAA;KAAE;IAIpE,kBAAkB,CAAC,SAAS,SAAM,GAAG,OAAO;CAG7C"}
|