@saber2pr/ai-agent 0.0.64 → 0.0.65
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/core/agent-graph.d.ts +4 -4
- package/lib/core/agent-graph.js +98 -87
- package/lib/types/type.d.ts +5 -4
- package/package.json +1 -1
|
@@ -1,6 +1,6 @@
|
|
|
1
|
-
import { AIMessage, BaseMessage } from
|
|
2
|
-
import { GraphAgentOptions } from
|
|
3
|
-
import { AgentGraphModel } from
|
|
1
|
+
import { AIMessage, BaseMessage } from "@langchain/core/messages";
|
|
2
|
+
import { GraphAgentOptions } from "../types/type";
|
|
3
|
+
import { AgentGraphModel } from "../model/AgentGraphModel";
|
|
4
4
|
interface TokenUsage {
|
|
5
5
|
total: number;
|
|
6
6
|
}
|
|
@@ -30,7 +30,7 @@ export default class McpGraphAgent<T extends AgentGraphModel = any> {
|
|
|
30
30
|
* 设置外部流式输出回调(如 VS Code Webview)。
|
|
31
31
|
* 设置后,callModel 的流式输出将通过回调发送而非写入 stdout。
|
|
32
32
|
*/
|
|
33
|
-
setStreamOutput(callback: (chunk: string, type:
|
|
33
|
+
setStreamOutput(callback: (chunk: string, type: "think" | "text") => void): void;
|
|
34
34
|
/**
|
|
35
35
|
* 清除外部流式输出回调,恢复默认的 stdout 输出。
|
|
36
36
|
*/
|
package/lib/core/agent-graph.js
CHANGED
|
@@ -76,11 +76,11 @@ class McpGraphAgent {
|
|
|
76
76
|
const cleanup = async () => {
|
|
77
77
|
this.stopLoading();
|
|
78
78
|
await this.closeMcpClients(); // 清理 MCP 连接
|
|
79
|
-
process.stdout.write(
|
|
79
|
+
process.stdout.write("\u001B[?25h");
|
|
80
80
|
process.exit(0);
|
|
81
81
|
};
|
|
82
|
-
process.on(
|
|
83
|
-
process.on(
|
|
82
|
+
process.on("SIGINT", cleanup);
|
|
83
|
+
process.on("SIGTERM", cleanup);
|
|
84
84
|
}
|
|
85
85
|
/**
|
|
86
86
|
* 设置外部流式输出回调(如 VS Code Webview)。
|
|
@@ -96,7 +96,7 @@ class McpGraphAgent {
|
|
|
96
96
|
this.streamOutputCallback = null;
|
|
97
97
|
}
|
|
98
98
|
printLoadedTools() {
|
|
99
|
-
console.log(
|
|
99
|
+
console.log("\n🛠️ [Graph] 正在加载工具节点...");
|
|
100
100
|
this.langchainTools.forEach((tool) => {
|
|
101
101
|
// 工具名称
|
|
102
102
|
console.log(`\n🧰 工具名: ${tool.name}`);
|
|
@@ -120,12 +120,12 @@ class McpGraphAgent {
|
|
|
120
120
|
loadMcpConfigs() {
|
|
121
121
|
const combined = { mcpServers: {} };
|
|
122
122
|
const paths = [
|
|
123
|
-
path_1.default.join(os_1.default.homedir(),
|
|
124
|
-
path_1.default.join(os_1.default.homedir(),
|
|
123
|
+
path_1.default.join(os_1.default.homedir(), ".cursor", "mcp.json"),
|
|
124
|
+
path_1.default.join(os_1.default.homedir(), ".vscode", "mcp.json"),
|
|
125
125
|
];
|
|
126
|
-
paths.forEach(p => {
|
|
126
|
+
paths.forEach((p) => {
|
|
127
127
|
if (fs_1.default.existsSync(p)) {
|
|
128
|
-
const content = JSON.parse(fs_1.default.readFileSync(p,
|
|
128
|
+
const content = JSON.parse(fs_1.default.readFileSync(p, "utf-8"));
|
|
129
129
|
Object.assign(combined.mcpServers, content.mcpServers);
|
|
130
130
|
}
|
|
131
131
|
});
|
|
@@ -142,13 +142,13 @@ class McpGraphAgent {
|
|
|
142
142
|
args: config.args,
|
|
143
143
|
env: { ...process.env, ...(config.env || {}) },
|
|
144
144
|
});
|
|
145
|
-
const client = new index_js_1.Client({ name:
|
|
145
|
+
const client = new index_js_1.Client({ name: "mcp-graph-client", version: "1.0.0" }, { capabilities: {} });
|
|
146
146
|
await client.connect(transport);
|
|
147
147
|
this.mcpClients.push(client);
|
|
148
148
|
const { tools } = await client.listTools();
|
|
149
|
-
tools.forEach(tool => {
|
|
149
|
+
tools.forEach((tool) => {
|
|
150
150
|
mcpToolInfos.push({
|
|
151
|
-
type:
|
|
151
|
+
type: "function",
|
|
152
152
|
function: {
|
|
153
153
|
name: tool.name,
|
|
154
154
|
description: tool.description,
|
|
@@ -172,11 +172,20 @@ class McpGraphAgent {
|
|
|
172
172
|
return mcpToolInfos;
|
|
173
173
|
}
|
|
174
174
|
async prepareTools() {
|
|
175
|
-
const builtinToolInfos = (0, builtin_1.createDefaultBuiltinTools)({
|
|
175
|
+
const builtinToolInfos = (0, builtin_1.createDefaultBuiltinTools)({
|
|
176
|
+
options: this.options,
|
|
177
|
+
});
|
|
176
178
|
const mcpToolInfos = await this.initMcpTools();
|
|
177
179
|
// 合并内置、手动传入和 MCP 工具
|
|
178
|
-
|
|
179
|
-
|
|
180
|
+
let allToolInfos = [
|
|
181
|
+
...builtinToolInfos,
|
|
182
|
+
...(this.options.tools || []),
|
|
183
|
+
...mcpToolInfos,
|
|
184
|
+
];
|
|
185
|
+
if (this.options.filterTools) {
|
|
186
|
+
allToolInfos = allToolInfos.filter(this.options.filterTools);
|
|
187
|
+
}
|
|
188
|
+
this.langchainTools = allToolInfos.map((t) => (0, convertToLangChainTool_1.convertToLangChainTool)(t, { allTools: allToolInfos }));
|
|
180
189
|
this.toolNode = new prebuilt_1.ToolNode(this.langchainTools);
|
|
181
190
|
return {
|
|
182
191
|
builtinToolInfos,
|
|
@@ -190,7 +199,6 @@ class McpGraphAgent {
|
|
|
190
199
|
if (this.model && this.langchainTools.length > 0) {
|
|
191
200
|
return this.getModel();
|
|
192
201
|
}
|
|
193
|
-
;
|
|
194
202
|
// 1. 加载所有工具(含 MCP)
|
|
195
203
|
const toolsInfo = await this.prepareTools();
|
|
196
204
|
// 2. 初始化模型
|
|
@@ -213,17 +221,17 @@ class McpGraphAgent {
|
|
|
213
221
|
this.mcpClients = [];
|
|
214
222
|
}
|
|
215
223
|
showLoading(text) {
|
|
216
|
-
const chars = [
|
|
224
|
+
const chars = ["⠋", "⠙", "⠹", "⠸", "⠼", "⠴", "⠦", "⠧", "⠇", "⠏"];
|
|
217
225
|
let i = 0;
|
|
218
|
-
process.stdout.write(
|
|
226
|
+
process.stdout.write("\u001B[?25l");
|
|
219
227
|
const timer = setInterval(() => {
|
|
220
228
|
process.stdout.write(`\r\x1b[36m${chars[i]}\x1b[0m ${text}`);
|
|
221
229
|
i = (i + 1) % chars.length;
|
|
222
230
|
}, 80);
|
|
223
231
|
return () => {
|
|
224
232
|
clearInterval(timer);
|
|
225
|
-
process.stdout.write(
|
|
226
|
-
process.stdout.write(
|
|
233
|
+
process.stdout.write("\r\x1b[K");
|
|
234
|
+
process.stdout.write("\u001B[?25h");
|
|
227
235
|
};
|
|
228
236
|
}
|
|
229
237
|
startLoading(text) {
|
|
@@ -260,29 +268,33 @@ class McpGraphAgent {
|
|
|
260
268
|
let config = {};
|
|
261
269
|
if (fs_1.default.existsSync(config_1.CONFIG_FILE)) {
|
|
262
270
|
try {
|
|
263
|
-
config = JSON.parse(fs_1.default.readFileSync(config_1.CONFIG_FILE,
|
|
271
|
+
config = JSON.parse(fs_1.default.readFileSync(config_1.CONFIG_FILE, "utf-8"));
|
|
264
272
|
}
|
|
265
273
|
catch (e) { }
|
|
266
274
|
}
|
|
267
275
|
if (!config.baseURL || !config.apiKey) {
|
|
268
|
-
const rl = readline_1.default.createInterface({
|
|
269
|
-
|
|
276
|
+
const rl = readline_1.default.createInterface({
|
|
277
|
+
input: process.stdin,
|
|
278
|
+
output: process.stdout,
|
|
279
|
+
});
|
|
280
|
+
const question = (q) => new Promise((res) => rl.question(q, res));
|
|
270
281
|
config.baseURL = config.baseURL || (await question(`? API Base URL: `));
|
|
271
282
|
config.apiKey = config.apiKey || (await question(`? API Key: `));
|
|
272
|
-
config.model =
|
|
283
|
+
config.model =
|
|
284
|
+
config.model || (await question(`? Model Name: `)) || "gpt-4o";
|
|
273
285
|
fs_1.default.writeFileSync(config_1.CONFIG_FILE, JSON.stringify(config, null, 2));
|
|
274
286
|
rl.close();
|
|
275
287
|
}
|
|
276
288
|
return config;
|
|
277
289
|
}
|
|
278
|
-
async chat(query =
|
|
290
|
+
async chat(query = "开始代码审计") {
|
|
279
291
|
try {
|
|
280
292
|
await this.ensureInitialized();
|
|
281
293
|
const app = await this.createGraph();
|
|
282
294
|
const graphStream = await app.stream({
|
|
283
295
|
messages: [new messages_1.HumanMessage(query)],
|
|
284
296
|
}, {
|
|
285
|
-
configurable: { thread_id:
|
|
297
|
+
configurable: { thread_id: "auto_worker" },
|
|
286
298
|
recursionLimit: this.recursionLimit,
|
|
287
299
|
debug: this.verbose,
|
|
288
300
|
});
|
|
@@ -290,7 +302,7 @@ class McpGraphAgent {
|
|
|
290
302
|
this.renderOutput(output, this.streamEnabled);
|
|
291
303
|
}
|
|
292
304
|
catch (error) {
|
|
293
|
-
console.error(
|
|
305
|
+
console.error("\n❌ Chat 过程中发生错误:", error);
|
|
294
306
|
}
|
|
295
307
|
finally {
|
|
296
308
|
await this.closeMcpClients();
|
|
@@ -300,7 +312,7 @@ class McpGraphAgent {
|
|
|
300
312
|
* 流式执行单次查询(编程式 API)。
|
|
301
313
|
* 无论 options.stream 是否开启,此方法始终以流式方式输出。
|
|
302
314
|
*/
|
|
303
|
-
async stream(query =
|
|
315
|
+
async stream(query = "开始代码审计") {
|
|
304
316
|
const prevStream = this.streamEnabled;
|
|
305
317
|
this.streamEnabled = true;
|
|
306
318
|
try {
|
|
@@ -309,7 +321,7 @@ class McpGraphAgent {
|
|
|
309
321
|
const graphStream = await app.stream({
|
|
310
322
|
messages: [new messages_1.HumanMessage(query)],
|
|
311
323
|
}, {
|
|
312
|
-
configurable: { thread_id:
|
|
324
|
+
configurable: { thread_id: "stream_worker" },
|
|
313
325
|
recursionLimit: this.recursionLimit,
|
|
314
326
|
debug: this.verbose,
|
|
315
327
|
});
|
|
@@ -317,7 +329,7 @@ class McpGraphAgent {
|
|
|
317
329
|
this.renderOutput(output, true);
|
|
318
330
|
}
|
|
319
331
|
catch (error) {
|
|
320
|
-
console.error(
|
|
332
|
+
console.error("\n❌ Stream 过程中发生错误:", error);
|
|
321
333
|
}
|
|
322
334
|
finally {
|
|
323
335
|
this.streamEnabled = prevStream;
|
|
@@ -327,21 +339,24 @@ class McpGraphAgent {
|
|
|
327
339
|
async start() {
|
|
328
340
|
await this.ensureInitialized();
|
|
329
341
|
const app = await this.createGraph();
|
|
330
|
-
const rl = readline_1.default.createInterface({
|
|
331
|
-
|
|
342
|
+
const rl = readline_1.default.createInterface({
|
|
343
|
+
input: process.stdin,
|
|
344
|
+
output: process.stdout,
|
|
345
|
+
});
|
|
346
|
+
rl.on("SIGINT", () => {
|
|
332
347
|
this.stopLoading();
|
|
333
348
|
rl.close();
|
|
334
|
-
process.stdout.write(
|
|
349
|
+
process.stdout.write("\u001B[?25h");
|
|
335
350
|
process.exit(0);
|
|
336
351
|
});
|
|
337
352
|
const ask = () => {
|
|
338
|
-
rl.question(
|
|
339
|
-
if (input.toLowerCase() ===
|
|
353
|
+
rl.question("> ", async (input) => {
|
|
354
|
+
if (input.toLowerCase() === "exit") {
|
|
340
355
|
rl.close();
|
|
341
356
|
return;
|
|
342
357
|
}
|
|
343
358
|
const graphStream = await app.stream({ messages: [new messages_1.HumanMessage(input)] }, {
|
|
344
|
-
configurable: { thread_id:
|
|
359
|
+
configurable: { thread_id: "session" },
|
|
345
360
|
recursionLimit: this.recursionLimit,
|
|
346
361
|
debug: this.verbose,
|
|
347
362
|
});
|
|
@@ -358,7 +373,9 @@ class McpGraphAgent {
|
|
|
358
373
|
// ✅ 打印工具执行结果(tools 节点的输出)
|
|
359
374
|
const toolsNode = output.tools;
|
|
360
375
|
if (toolsNode && toolsNode.messages) {
|
|
361
|
-
const toolMessages = Array.isArray(toolsNode.messages)
|
|
376
|
+
const toolMessages = Array.isArray(toolsNode.messages)
|
|
377
|
+
? toolsNode.messages
|
|
378
|
+
: [];
|
|
362
379
|
// 获取最近的 AI 消息以匹配 tool_call_id
|
|
363
380
|
const lastAiMsg = agentNode?.messages?.[agentNode.messages.length - 1];
|
|
364
381
|
const toolCallMap = new Map();
|
|
@@ -372,10 +389,12 @@ class McpGraphAgent {
|
|
|
372
389
|
// ToolMessage 有 tool_call_id 字段
|
|
373
390
|
const toolCallId = msg.tool_call_id || msg.id;
|
|
374
391
|
if (toolCallId) {
|
|
375
|
-
const toolName = toolCallMap.get(toolCallId) || msg.name ||
|
|
376
|
-
const content = typeof msg.content ===
|
|
392
|
+
const toolName = toolCallMap.get(toolCallId) || msg.name || "unknown";
|
|
393
|
+
const content = typeof msg.content === "string"
|
|
394
|
+
? msg.content
|
|
395
|
+
: JSON.stringify(msg.content);
|
|
377
396
|
// 如果内容太长,截断显示
|
|
378
|
-
const displayContent = content.length > 500 ? content.substring(0, 500) +
|
|
397
|
+
const displayContent = content.length > 500 ? content.substring(0, 500) + "..." : content;
|
|
379
398
|
console.log(`✅ [工具结果] ${toolName}: ${displayContent}`);
|
|
380
399
|
}
|
|
381
400
|
});
|
|
@@ -402,7 +421,7 @@ class McpGraphAgent {
|
|
|
402
421
|
}
|
|
403
422
|
// 4. 打印工具调用情况
|
|
404
423
|
if (msg.tool_calls?.length) {
|
|
405
|
-
msg.tool_calls.forEach(call => {
|
|
424
|
+
msg.tool_calls.forEach((call) => {
|
|
406
425
|
console.log(`🛠️ [调用工具]: ${call.name} 📦 参数: ${JSON.stringify(call.args)}`);
|
|
407
426
|
});
|
|
408
427
|
}
|
|
@@ -412,9 +431,9 @@ class McpGraphAgent {
|
|
|
412
431
|
const recentToolCalls = this.getRecentToolCalls(state.messages);
|
|
413
432
|
const recentToolCallsStr = recentToolCalls.length > 0
|
|
414
433
|
? `\n\n⚠️ 最近调用的工具(避免重复调用相同工具和参数):\n${recentToolCalls
|
|
415
|
-
.map(tc => ` - ${tc.name}(${JSON.stringify(tc.args)})`)
|
|
416
|
-
.join(
|
|
417
|
-
:
|
|
434
|
+
.map((tc) => ` - ${tc.name}(${JSON.stringify(tc.args)})`)
|
|
435
|
+
.join("\n")}`
|
|
436
|
+
: "";
|
|
418
437
|
// 1. 构建当前的系统提示词模板
|
|
419
438
|
const systemPromptTemplate = `
|
|
420
439
|
${(0, getSystemPromptTemplate_1.getSystemPromptTemplate)(this.targetDir)}
|
|
@@ -425,32 +444,32 @@ ${(0, getSystemPromptTemplate_1.getSystemPromptTemplate)(this.targetDir)}
|
|
|
425
444
|
// ✅ 检查 options 中的 alwaysSystem 参数 (默认为 true 或根据你的需求设置)
|
|
426
445
|
// 如果不希望每次都携带(即只在首轮携带),则过滤掉历史消息里的 SystemMessage
|
|
427
446
|
if (this.options.alwaysSystem === false) {
|
|
428
|
-
inputMessages = state.messages.filter(msg => msg._getType() !==
|
|
447
|
+
inputMessages = state.messages.filter((msg) => msg._getType() !== "system");
|
|
429
448
|
}
|
|
430
449
|
else {
|
|
431
450
|
// 默认模式:保持干净,由 PromptTemplate 重新生成最新的 System 状态
|
|
432
|
-
inputMessages = state.messages.filter(msg => msg._getType() !==
|
|
451
|
+
inputMessages = state.messages.filter((msg) => msg._getType() !== "system");
|
|
433
452
|
}
|
|
434
453
|
const prompt = prompts_1.ChatPromptTemplate.fromMessages([
|
|
435
|
-
[
|
|
436
|
-
new prompts_1.MessagesPlaceholder(
|
|
454
|
+
["system", systemPromptTemplate],
|
|
455
|
+
new prompts_1.MessagesPlaceholder("messages"),
|
|
437
456
|
]);
|
|
438
|
-
this.startLoading(
|
|
457
|
+
this.startLoading("AI 正在分析并思考中");
|
|
439
458
|
try {
|
|
440
459
|
const promptParams = {
|
|
441
460
|
messages: inputMessages,
|
|
442
461
|
recentToolCalls: recentToolCallsStr,
|
|
443
|
-
extraPrompt: this.options.extraSystemPrompt ||
|
|
462
|
+
extraPrompt: this.options.extraSystemPrompt || "",
|
|
444
463
|
};
|
|
445
464
|
if (this.streamEnabled) {
|
|
446
465
|
// ✅ 流式模式:通过 AgentGraphModel.streamGenerate 进行流式输出
|
|
447
466
|
const formattedMessages = await prompt.formatMessages(promptParams);
|
|
448
467
|
this.stopLoading();
|
|
449
468
|
// --- 流式 <think> 标签实时过滤 + 流式打印思考内容 ---
|
|
450
|
-
const THINK_OPEN =
|
|
451
|
-
const THINK_CLOSE =
|
|
469
|
+
const THINK_OPEN = "<think>";
|
|
470
|
+
const THINK_CLOSE = "</think>";
|
|
452
471
|
let inThink = false;
|
|
453
|
-
let textBuffer =
|
|
472
|
+
let textBuffer = "";
|
|
454
473
|
let aiHeaderPrinted = false;
|
|
455
474
|
let thinkHeaderPrinted = false;
|
|
456
475
|
const hasExternalHandler = !!this.streamOutputCallback;
|
|
@@ -458,11 +477,11 @@ ${(0, getSystemPromptTemplate_1.getSystemPromptTemplate)(this.targetDir)}
|
|
|
458
477
|
if (!text)
|
|
459
478
|
return;
|
|
460
479
|
if (hasExternalHandler) {
|
|
461
|
-
this.streamOutputCallback(text,
|
|
480
|
+
this.streamOutputCallback(text, "text");
|
|
462
481
|
}
|
|
463
482
|
else {
|
|
464
483
|
if (!aiHeaderPrinted) {
|
|
465
|
-
process.stdout.write(
|
|
484
|
+
process.stdout.write("🤖 [AI]: ");
|
|
466
485
|
aiHeaderPrinted = true;
|
|
467
486
|
}
|
|
468
487
|
process.stdout.write(text);
|
|
@@ -472,11 +491,11 @@ ${(0, getSystemPromptTemplate_1.getSystemPromptTemplate)(this.targetDir)}
|
|
|
472
491
|
if (!text)
|
|
473
492
|
return;
|
|
474
493
|
if (hasExternalHandler) {
|
|
475
|
-
this.streamOutputCallback(text,
|
|
494
|
+
this.streamOutputCallback(text, "think");
|
|
476
495
|
}
|
|
477
496
|
else {
|
|
478
497
|
if (!thinkHeaderPrinted) {
|
|
479
|
-
process.stdout.write(
|
|
498
|
+
process.stdout.write("\x1b[2m🧠 [思考]: ");
|
|
480
499
|
thinkHeaderPrinted = true;
|
|
481
500
|
}
|
|
482
501
|
process.stdout.write(text);
|
|
@@ -496,7 +515,7 @@ ${(0, getSystemPromptTemplate_1.getSystemPromptTemplate)(this.targetDir)}
|
|
|
496
515
|
inThink = false;
|
|
497
516
|
// 思考块结束:stdout 模式下换行 + 重置样式
|
|
498
517
|
if (!hasExternalHandler && thinkHeaderPrinted) {
|
|
499
|
-
process.stdout.write(
|
|
518
|
+
process.stdout.write("\x1b[0m\n");
|
|
500
519
|
thinkHeaderPrinted = false;
|
|
501
520
|
}
|
|
502
521
|
}
|
|
@@ -539,10 +558,10 @@ ${(0, getSystemPromptTemplate_1.getSystemPromptTemplate)(this.targetDir)}
|
|
|
539
558
|
// 收尾:stdout 模式下关闭样式和换行
|
|
540
559
|
if (!hasExternalHandler) {
|
|
541
560
|
if (thinkHeaderPrinted) {
|
|
542
|
-
process.stdout.write(
|
|
561
|
+
process.stdout.write("\x1b[0m\n");
|
|
543
562
|
}
|
|
544
563
|
if (aiHeaderPrinted)
|
|
545
|
-
process.stdout.write(
|
|
564
|
+
process.stdout.write("\n");
|
|
546
565
|
}
|
|
547
566
|
const aiMsg = result.generations[0].message;
|
|
548
567
|
const meta = aiMsg.response_metadata || {};
|
|
@@ -591,57 +610,49 @@ ${(0, getSystemPromptTemplate_1.getSystemPromptTemplate)(this.targetDir)}
|
|
|
591
610
|
const totalTokens = state.tokenUsage?.total || 0;
|
|
592
611
|
const totalMs = state.totalDuration || 0;
|
|
593
612
|
if (totalTokens > 0 || totalMs > 0) {
|
|
594
|
-
console.log(
|
|
613
|
+
console.log("\n" + "═".repeat(50));
|
|
595
614
|
console.log(`🏁 \x1b[32;1m[审计任务全量结算]\x1b[0m`);
|
|
596
615
|
console.log(` - 累计消耗总额: \x1b[33m${totalTokens}\x1b[0m Tokens`);
|
|
597
616
|
console.log(` - 累计执行耗时: \x1b[36m${(totalMs / 1000).toFixed(2)}\x1b[0m s`);
|
|
598
|
-
console.log(
|
|
617
|
+
console.log("═".repeat(50) + "\n");
|
|
599
618
|
}
|
|
600
619
|
}
|
|
601
620
|
async createGraph() {
|
|
602
621
|
const workflow = new langgraph_1.StateGraph(AgentState)
|
|
603
|
-
.addNode(
|
|
604
|
-
.addNode(
|
|
605
|
-
.addNode(
|
|
622
|
+
.addNode("agent", (state) => this.callModel(state))
|
|
623
|
+
.addNode("tools", this.toolNode)
|
|
624
|
+
.addNode("interrupt", (state) => {
|
|
606
625
|
return {
|
|
607
|
-
messages: [
|
|
608
|
-
|
|
609
|
-
|
|
610
|
-
|
|
611
|
-
|
|
612
|
-
**任务还没完成吗?** 没关系,只要您回复“继续”,我马上就回来接着干!或者您有什么新的想法要告诉我?`
|
|
613
|
-
})]
|
|
626
|
+
messages: [
|
|
627
|
+
new messages_1.AIMessage({
|
|
628
|
+
content: `Final Answer:\n💡 ⚠️ 抱歉,当前任务消耗的 Token 已达上限 ${this.options.maxTokens}。为了防止无限循环,我已停止执行。你可以尝试分步骤指令,继续发送指令,直到任务完成。`,
|
|
629
|
+
}),
|
|
630
|
+
],
|
|
614
631
|
};
|
|
615
632
|
})
|
|
616
|
-
.addEdge(langgraph_1.START,
|
|
617
|
-
.addConditionalEdges(
|
|
633
|
+
.addEdge(langgraph_1.START, "agent")
|
|
634
|
+
.addConditionalEdges("agent", (state) => {
|
|
618
635
|
const { messages } = state;
|
|
619
636
|
const lastMsg = messages[messages.length - 1];
|
|
620
|
-
const content = lastMsg.content ||
|
|
637
|
+
const content = lastMsg.content || "";
|
|
621
638
|
// 🛑 新增:全局 Token 熔断保护
|
|
622
639
|
// 如果已消耗 Token 超过了 options 中设置的 maxTokens (假设是总限额)
|
|
623
|
-
if (this.options.maxTokens &&
|
|
624
|
-
|
|
625
|
-
|
|
626
|
-
content: `⚠️ 抱歉,当前任务消耗的 Token 已达上限 ${this.options.maxTokens}。为了防止无限循环,我已停止执行。你可以尝试分步骤指令,继续发送指令,直到任务完成。`
|
|
627
|
-
}));
|
|
628
|
-
this.printFinalSummary(state);
|
|
629
|
-
return langgraph_1.END;
|
|
640
|
+
if (this.options.maxTokens &&
|
|
641
|
+
state.tokenUsage.total >= this.options.maxTokens) {
|
|
642
|
+
return "interrupt";
|
|
630
643
|
}
|
|
631
644
|
// 1. 如果 AI 想要调用工具,去 tools 节点
|
|
632
645
|
if (lastMsg.tool_calls && lastMsg.tool_calls.length > 0) {
|
|
633
|
-
return
|
|
646
|
+
return "tools";
|
|
634
647
|
}
|
|
635
|
-
const isFinalAnswer = content.includes(
|
|
648
|
+
const isFinalAnswer = content.includes("Final Answer");
|
|
636
649
|
if (isFinalAnswer) {
|
|
637
650
|
this.printFinalSummary(state);
|
|
638
651
|
return langgraph_1.END;
|
|
639
652
|
}
|
|
640
|
-
if (messages.length > 50) {
|
|
641
|
-
return 'interrupt';
|
|
642
|
-
}
|
|
643
653
|
return langgraph_1.END;
|
|
644
|
-
})
|
|
654
|
+
})
|
|
655
|
+
.addEdge("tools", "agent");
|
|
645
656
|
return workflow.compile({ checkpointer: this.checkpointer });
|
|
646
657
|
}
|
|
647
658
|
}
|
package/lib/types/type.d.ts
CHANGED
|
@@ -1,13 +1,13 @@
|
|
|
1
|
-
import { z } from
|
|
2
|
-
import { Client } from
|
|
3
|
-
import { AgentGraphModel } from
|
|
1
|
+
import { z } from "zod";
|
|
2
|
+
import { Client } from "@modelcontextprotocol/sdk/client/index";
|
|
3
|
+
import { AgentGraphModel } from "../model/AgentGraphModel";
|
|
4
4
|
export interface ApiConfig {
|
|
5
5
|
baseURL: string;
|
|
6
6
|
apiKey: string;
|
|
7
7
|
model: string;
|
|
8
8
|
}
|
|
9
9
|
export interface ToolInfo {
|
|
10
|
-
type:
|
|
10
|
+
type: "function";
|
|
11
11
|
function: {
|
|
12
12
|
name: string;
|
|
13
13
|
description?: string;
|
|
@@ -42,6 +42,7 @@ export interface GraphAgentOptions<T extends AgentGraphModel = any> extends Agen
|
|
|
42
42
|
recursionLimit?: number;
|
|
43
43
|
/** 是否启用流式输出,默认 false */
|
|
44
44
|
stream?: boolean;
|
|
45
|
+
filterTools?: (tool: ToolInfo) => boolean;
|
|
45
46
|
}
|
|
46
47
|
export interface CreateAgentOptions {
|
|
47
48
|
apiKey: string;
|