@saber2pr/ai-agent 0.0.49 → 0.0.51
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 +1 -20
- package/lib/core/agent-graph.js +17 -55
- package/lib/tools/filesystem/index.js +4 -15
- package/lib/types/type.d.ts +1 -2
- package/package.json +1 -1
|
@@ -6,9 +6,6 @@ interface TokenUsage {
|
|
|
6
6
|
}
|
|
7
7
|
declare const AgentState: import("@langchain/langgraph").AnnotationRoot<{
|
|
8
8
|
messages: import("@langchain/langgraph").BinaryOperatorAggregate<BaseMessage[], BaseMessage[]>;
|
|
9
|
-
auditedFiles: import("@langchain/langgraph").BinaryOperatorAggregate<string[], string[]>;
|
|
10
|
-
targetCount: import("@langchain/langgraph").BinaryOperatorAggregate<number, number>;
|
|
11
|
-
mode: import("@langchain/langgraph").BinaryOperatorAggregate<"chat" | "auto", "chat" | "auto">;
|
|
12
9
|
tokenUsage: import("@langchain/langgraph").BinaryOperatorAggregate<TokenUsage, TokenUsage>;
|
|
13
10
|
totalDuration: import("@langchain/langgraph").BinaryOperatorAggregate<number, number>;
|
|
14
11
|
}>;
|
|
@@ -24,7 +21,6 @@ export default class McpGraphAgent<T extends AgentGraphModel = any> {
|
|
|
24
21
|
private alwaysSystem;
|
|
25
22
|
private recursionLimit;
|
|
26
23
|
private apiConfig;
|
|
27
|
-
private maxTargetCount;
|
|
28
24
|
private maxTokens;
|
|
29
25
|
private mcpClients;
|
|
30
26
|
private streamEnabled;
|
|
@@ -66,36 +62,21 @@ export default class McpGraphAgent<T extends AgentGraphModel = any> {
|
|
|
66
62
|
totalDuration: number;
|
|
67
63
|
}>;
|
|
68
64
|
private getRecentToolCalls;
|
|
69
|
-
trackProgress(state: typeof AgentState.State): Promise<{
|
|
70
|
-
auditedFiles: string[];
|
|
71
|
-
}>;
|
|
72
65
|
private printFinalSummary;
|
|
73
66
|
createGraph(): Promise<import("@langchain/langgraph").CompiledStateGraph<import("@langchain/langgraph").StateType<{
|
|
74
67
|
messages: import("@langchain/langgraph").BinaryOperatorAggregate<BaseMessage[], BaseMessage[]>;
|
|
75
|
-
auditedFiles: import("@langchain/langgraph").BinaryOperatorAggregate<string[], string[]>;
|
|
76
|
-
targetCount: import("@langchain/langgraph").BinaryOperatorAggregate<number, number>;
|
|
77
|
-
mode: import("@langchain/langgraph").BinaryOperatorAggregate<"chat" | "auto", "chat" | "auto">;
|
|
78
68
|
tokenUsage: import("@langchain/langgraph").BinaryOperatorAggregate<TokenUsage, TokenUsage>;
|
|
79
69
|
totalDuration: import("@langchain/langgraph").BinaryOperatorAggregate<number, number>;
|
|
80
70
|
}>, import("@langchain/langgraph").UpdateType<{
|
|
81
71
|
messages: import("@langchain/langgraph").BinaryOperatorAggregate<BaseMessage[], BaseMessage[]>;
|
|
82
|
-
auditedFiles: import("@langchain/langgraph").BinaryOperatorAggregate<string[], string[]>;
|
|
83
|
-
targetCount: import("@langchain/langgraph").BinaryOperatorAggregate<number, number>;
|
|
84
|
-
mode: import("@langchain/langgraph").BinaryOperatorAggregate<"chat" | "auto", "chat" | "auto">;
|
|
85
72
|
tokenUsage: import("@langchain/langgraph").BinaryOperatorAggregate<TokenUsage, TokenUsage>;
|
|
86
73
|
totalDuration: import("@langchain/langgraph").BinaryOperatorAggregate<number, number>;
|
|
87
|
-
}>, "tools" | "__start__" | "agent"
|
|
74
|
+
}>, "tools" | "__start__" | "agent", {
|
|
88
75
|
messages: import("@langchain/langgraph").BinaryOperatorAggregate<BaseMessage[], BaseMessage[]>;
|
|
89
|
-
auditedFiles: import("@langchain/langgraph").BinaryOperatorAggregate<string[], string[]>;
|
|
90
|
-
targetCount: import("@langchain/langgraph").BinaryOperatorAggregate<number, number>;
|
|
91
|
-
mode: import("@langchain/langgraph").BinaryOperatorAggregate<"chat" | "auto", "chat" | "auto">;
|
|
92
76
|
tokenUsage: import("@langchain/langgraph").BinaryOperatorAggregate<TokenUsage, TokenUsage>;
|
|
93
77
|
totalDuration: import("@langchain/langgraph").BinaryOperatorAggregate<number, number>;
|
|
94
78
|
}, {
|
|
95
79
|
messages: import("@langchain/langgraph").BinaryOperatorAggregate<BaseMessage[], BaseMessage[]>;
|
|
96
|
-
auditedFiles: import("@langchain/langgraph").BinaryOperatorAggregate<string[], string[]>;
|
|
97
|
-
targetCount: import("@langchain/langgraph").BinaryOperatorAggregate<number, number>;
|
|
98
|
-
mode: import("@langchain/langgraph").BinaryOperatorAggregate<"chat" | "auto", "chat" | "auto">;
|
|
99
80
|
tokenUsage: import("@langchain/langgraph").BinaryOperatorAggregate<TokenUsage, TokenUsage>;
|
|
100
81
|
totalDuration: import("@langchain/langgraph").BinaryOperatorAggregate<number, number>;
|
|
101
82
|
}, import("@langchain/langgraph").StateDefinition>>;
|
package/lib/core/agent-graph.js
CHANGED
|
@@ -29,18 +29,6 @@ const AgentState = langgraph_1.Annotation.Root({
|
|
|
29
29
|
reducer: (x, y) => x.concat(y),
|
|
30
30
|
default: () => [],
|
|
31
31
|
}),
|
|
32
|
-
auditedFiles: (0, langgraph_1.Annotation)({
|
|
33
|
-
reducer: (x, y) => Array.from(new Set([...x, ...y])),
|
|
34
|
-
default: () => [],
|
|
35
|
-
}),
|
|
36
|
-
targetCount: (0, langgraph_1.Annotation)({
|
|
37
|
-
reducer: (x, y) => y !== null && y !== void 0 ? y : x,
|
|
38
|
-
default: () => 4,
|
|
39
|
-
}),
|
|
40
|
-
mode: (0, langgraph_1.Annotation)({
|
|
41
|
-
reducer: (x, y) => y !== null && y !== void 0 ? y : x,
|
|
42
|
-
default: () => 'chat',
|
|
43
|
-
}),
|
|
44
32
|
// ✅ Token 累加器
|
|
45
33
|
tokenUsage: (0, langgraph_1.Annotation)({
|
|
46
34
|
reducer: (x, y) => ({
|
|
@@ -67,8 +55,7 @@ class McpGraphAgent {
|
|
|
67
55
|
this.targetDir = options.targetDir || process.cwd();
|
|
68
56
|
this.recursionLimit = options.recursionLimit || 80;
|
|
69
57
|
this.apiConfig = options.apiConfig;
|
|
70
|
-
this.
|
|
71
|
-
this.maxTokens = options.maxTokens || 8000;
|
|
58
|
+
this.maxTokens = options.maxTokens || 100000;
|
|
72
59
|
this.streamEnabled = options.stream || false;
|
|
73
60
|
process.setMaxListeners(100);
|
|
74
61
|
// ✅ 修复 AbortSignal 监听器数量警告
|
|
@@ -283,8 +270,6 @@ class McpGraphAgent {
|
|
|
283
270
|
const app = await this.createGraph();
|
|
284
271
|
const graphStream = await app.stream({
|
|
285
272
|
messages: [new messages_1.HumanMessage(query)],
|
|
286
|
-
mode: 'auto',
|
|
287
|
-
targetCount: this.maxTargetCount,
|
|
288
273
|
}, {
|
|
289
274
|
configurable: { thread_id: 'auto_worker' },
|
|
290
275
|
recursionLimit: this.recursionLimit,
|
|
@@ -312,8 +297,6 @@ class McpGraphAgent {
|
|
|
312
297
|
const app = await this.createGraph();
|
|
313
298
|
const graphStream = await app.stream({
|
|
314
299
|
messages: [new messages_1.HumanMessage(query)],
|
|
315
|
-
mode: 'auto',
|
|
316
|
-
targetCount: this.maxTargetCount,
|
|
317
300
|
}, {
|
|
318
301
|
configurable: { thread_id: 'stream_worker' },
|
|
319
302
|
recursionLimit: this.recursionLimit,
|
|
@@ -346,7 +329,7 @@ class McpGraphAgent {
|
|
|
346
329
|
rl.close();
|
|
347
330
|
return;
|
|
348
331
|
}
|
|
349
|
-
const graphStream = await app.stream({ messages: [new messages_1.HumanMessage(input)]
|
|
332
|
+
const graphStream = await app.stream({ messages: [new messages_1.HumanMessage(input)] }, {
|
|
350
333
|
configurable: { thread_id: 'session' },
|
|
351
334
|
recursionLimit: this.recursionLimit,
|
|
352
335
|
debug: this.verbose,
|
|
@@ -416,7 +399,6 @@ class McpGraphAgent {
|
|
|
416
399
|
}
|
|
417
400
|
}
|
|
418
401
|
async callModel(state) {
|
|
419
|
-
const auditedListStr = state.auditedFiles.length > 0 ? state.auditedFiles.map(f => `\n - ${f}`).join('') : '暂无';
|
|
420
402
|
const recentToolCalls = this.getRecentToolCalls(state.messages);
|
|
421
403
|
const recentToolCallsStr = recentToolCalls.length > 0
|
|
422
404
|
? `\n\n⚠️ 最近调用的工具(避免重复调用相同工具和参数):\n${recentToolCalls
|
|
@@ -425,9 +407,6 @@ class McpGraphAgent {
|
|
|
425
407
|
: '';
|
|
426
408
|
// 1. 构建当前的系统提示词模板
|
|
427
409
|
const systemPromptTemplate = `你是一个代码专家。工作目录:${this.targetDir}。
|
|
428
|
-
当前模式:{mode}
|
|
429
|
-
进度:{doneCount}/{targetCount}
|
|
430
|
-
已审计文件:{auditedList}
|
|
431
410
|
|
|
432
411
|
# 🛠️ 工具调用规范
|
|
433
412
|
1. Arguments 必须是纯粹的 JSON 对象,严禁将其作为字符串放入引号中。
|
|
@@ -458,10 +437,6 @@ class McpGraphAgent {
|
|
|
458
437
|
try {
|
|
459
438
|
const promptParams = {
|
|
460
439
|
messages: inputMessages,
|
|
461
|
-
mode: state.mode,
|
|
462
|
-
targetCount: state.targetCount,
|
|
463
|
-
doneCount: state.auditedFiles.length,
|
|
464
|
-
auditedList: auditedListStr,
|
|
465
440
|
recentToolCalls: recentToolCallsStr,
|
|
466
441
|
extraPrompt: this.options.extraSystemPrompt || '',
|
|
467
442
|
};
|
|
@@ -611,21 +586,6 @@ class McpGraphAgent {
|
|
|
611
586
|
}
|
|
612
587
|
return toolCalls;
|
|
613
588
|
}
|
|
614
|
-
async trackProgress(state) {
|
|
615
|
-
var _a;
|
|
616
|
-
const lastAiMsg = state.messages[state.messages.length - 1];
|
|
617
|
-
const currentAudited = new Set(state.auditedFiles);
|
|
618
|
-
if ((_a = lastAiMsg === null || lastAiMsg === void 0 ? void 0 : lastAiMsg.tool_calls) === null || _a === void 0 ? void 0 : _a.length) {
|
|
619
|
-
for (const tc of lastAiMsg.tool_calls) {
|
|
620
|
-
// 兼容所有可能的路径参数字段
|
|
621
|
-
const file = tc.args.path || tc.args.filePath || tc.args.file || tc.args.file_path;
|
|
622
|
-
if (file && typeof file === 'string') {
|
|
623
|
-
currentAudited.add(file);
|
|
624
|
-
}
|
|
625
|
-
}
|
|
626
|
-
}
|
|
627
|
-
return { auditedFiles: Array.from(currentAudited) };
|
|
628
|
-
}
|
|
629
589
|
printFinalSummary(state) {
|
|
630
590
|
var _a;
|
|
631
591
|
const totalTokens = ((_a = state.tokenUsage) === null || _a === void 0 ? void 0 : _a.total) || 0;
|
|
@@ -635,7 +595,6 @@ class McpGraphAgent {
|
|
|
635
595
|
console.log(`🏁 \x1b[32;1m[审计任务全量结算]\x1b[0m`);
|
|
636
596
|
console.log(` - 累计消耗总额: \x1b[33m${totalTokens}\x1b[0m Tokens`);
|
|
637
597
|
console.log(` - 累计执行耗时: \x1b[36m${(totalMs / 1000).toFixed(2)}\x1b[0m s`);
|
|
638
|
-
console.log(` - 审计文件总数: ${state.auditedFiles.length} 个`);
|
|
639
598
|
console.log('═'.repeat(50) + '\n');
|
|
640
599
|
}
|
|
641
600
|
}
|
|
@@ -643,7 +602,6 @@ class McpGraphAgent {
|
|
|
643
602
|
const workflow = new langgraph_1.StateGraph(AgentState)
|
|
644
603
|
.addNode('agent', state => this.callModel(state))
|
|
645
604
|
.addNode('tools', this.toolNode)
|
|
646
|
-
.addNode('progress', state => this.trackProgress(state))
|
|
647
605
|
.addEdge(langgraph_1.START, 'agent')
|
|
648
606
|
.addConditionalEdges('agent', state => {
|
|
649
607
|
const { messages } = state;
|
|
@@ -653,6 +611,9 @@ class McpGraphAgent {
|
|
|
653
611
|
// 如果已消耗 Token 超过了 options 中设置的 maxTokens (假设是总限额)
|
|
654
612
|
if (this.options.maxTokens && state.tokenUsage.total >= this.options.maxTokens) {
|
|
655
613
|
console.warn('⚠️ [警告] 已达到最大 Token 限制,强制结束任务。');
|
|
614
|
+
state.messages.push(new messages_1.AIMessage({
|
|
615
|
+
content: `⚠️ 抱歉,当前任务消耗的 Token 已达上限 ${this.options.maxTokens}。为了防止无限循环,我已停止执行。你可以尝试分步骤指令,继续发送指令,直到任务完成。`
|
|
616
|
+
}));
|
|
656
617
|
this.printFinalSummary(state);
|
|
657
618
|
return langgraph_1.END;
|
|
658
619
|
}
|
|
@@ -660,22 +621,23 @@ class McpGraphAgent {
|
|
|
660
621
|
if (lastMsg.tool_calls && lastMsg.tool_calls.length > 0) {
|
|
661
622
|
return 'tools';
|
|
662
623
|
}
|
|
663
|
-
// 2. 判定结束的条件:
|
|
664
|
-
// - 模式是 auto 且审计完成
|
|
665
|
-
// - 或者 AI 明确输出了结束语
|
|
666
|
-
// - 或者 AI 输出了普通内容且没有工具调用(针对问答模式)
|
|
667
|
-
const isAutoFinished = state.mode === 'auto' && state.auditedFiles.length > state.targetCount;
|
|
668
624
|
const isFinalAnswer = content.includes('Final Answer');
|
|
669
|
-
|
|
670
|
-
if (isAutoFinished || isFinalAnswer || state.mode === 'chat') {
|
|
625
|
+
if (isFinalAnswer) {
|
|
671
626
|
this.printFinalSummary(state);
|
|
672
627
|
return langgraph_1.END;
|
|
673
628
|
}
|
|
674
|
-
|
|
629
|
+
if (messages.length > 50) {
|
|
630
|
+
state.messages.push(new messages_1.AIMessage({
|
|
631
|
+
content: `💡 **嘿,我们已经聊了很多了!**
|
|
632
|
+
|
|
633
|
+
为了保证服务质量,我通常会在 50 步操作后停下来和您确认一下。这样可以避免我产生“幻觉”或者做一些无用功。
|
|
634
|
+
|
|
635
|
+
**任务还没完成吗?** 没关系,只要您回复“继续”,我马上就回来接着干!或者您有什么新的想法要告诉我?`
|
|
636
|
+
}));
|
|
637
|
+
return langgraph_1.END;
|
|
638
|
+
}
|
|
675
639
|
return langgraph_1.END;
|
|
676
|
-
})
|
|
677
|
-
.addEdge('tools', 'progress')
|
|
678
|
-
.addEdge('progress', 'agent');
|
|
640
|
+
}).addEdge('tools', 'agent');
|
|
679
641
|
return workflow.compile({ checkpointer: this.checkpointer });
|
|
680
642
|
}
|
|
681
643
|
}
|
|
@@ -153,10 +153,7 @@ const getFilesystemTools = (targetDir) => {
|
|
|
153
153
|
});
|
|
154
154
|
const createDirectoryTool = (0, createTool_1.createTool)({
|
|
155
155
|
name: 'create_directory',
|
|
156
|
-
description: '
|
|
157
|
-
'nested directories in one operation. If the directory already exists, ' +
|
|
158
|
-
'this operation will succeed silently. Perfect for setting up directory ' +
|
|
159
|
-
'structures for projects or ensuring required paths exist. Only works within allowed directories.',
|
|
156
|
+
description: '递归创建目录。支持多级嵌套。若目录已存在则静默成功。仅限允许目录。',
|
|
160
157
|
parameters: CreateDirectoryArgsSchema,
|
|
161
158
|
handler: async (args) => {
|
|
162
159
|
const validPath = await (0, lib_1.validatePath)(targetDir, args.path);
|
|
@@ -167,10 +164,7 @@ const getFilesystemTools = (targetDir) => {
|
|
|
167
164
|
});
|
|
168
165
|
const listDirectoryWithSizesTool = (0, createTool_1.createTool)({
|
|
169
166
|
name: 'list_directory',
|
|
170
|
-
description: '
|
|
171
|
-
'Results clearly distinguish between files and directories with [FILE] and [DIR] ' +
|
|
172
|
-
'prefixes. This tool is useful for understanding directory structure and ' +
|
|
173
|
-
'finding specific files within a directory. Only works within allowed directories.',
|
|
167
|
+
description: '列出目录内容。返回带 [FILE]/[DIR] 前缀的条目、大小及汇总。支持按名称或大小排序。',
|
|
174
168
|
parameters: ListDirectoryWithSizesArgsSchema,
|
|
175
169
|
handler: async (args) => {
|
|
176
170
|
const validPath = await (0, lib_1.validatePath)(targetDir, args.path);
|
|
@@ -257,10 +251,7 @@ const getFilesystemTools = (targetDir) => {
|
|
|
257
251
|
});
|
|
258
252
|
const moveFileTool = (0, createTool_1.createTool)({
|
|
259
253
|
name: 'move_file',
|
|
260
|
-
description: '
|
|
261
|
-
'and rename them in a single operation. If the destination exists, the ' +
|
|
262
|
-
'operation will fail. Works across different directories and can be used ' +
|
|
263
|
-
'for simple renaming within the same directory. Both source and destination must be within allowed directories.',
|
|
254
|
+
description: '移动或重命名文件/目录。目标路径若已存在则失败。源与目标均须在允许目录内。',
|
|
264
255
|
parameters: MoveFileArgsSchema,
|
|
265
256
|
handler: async (args) => {
|
|
266
257
|
const validSourcePath = await (0, lib_1.validatePath)(targetDir, args.source);
|
|
@@ -272,9 +263,7 @@ const getFilesystemTools = (targetDir) => {
|
|
|
272
263
|
});
|
|
273
264
|
const searchFilesTool = (0, createTool_1.createTool)({
|
|
274
265
|
name: 'search_files',
|
|
275
|
-
description: '
|
|
276
|
-
'Returns a list of files that match the pattern. Only works within allowed directories.' +
|
|
277
|
-
'Used only for filename search',
|
|
266
|
+
description: '在指定路径下搜索匹配模式的文件名。返回匹配的相对路径列表。',
|
|
278
267
|
parameters: SearchFilesArgsSchema,
|
|
279
268
|
handler: async (args) => {
|
|
280
269
|
const combinedExcludes = [...DEFAULT_IGNORE, ...(args.excludePatterns || [])];
|
package/lib/types/type.d.ts
CHANGED
|
@@ -7,7 +7,7 @@ export interface ApiConfig {
|
|
|
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;
|
|
@@ -38,7 +38,6 @@ export interface GraphAgentOptions<T extends AgentGraphModel = any> extends Agen
|
|
|
38
38
|
apiModel?: T;
|
|
39
39
|
alwaysSystem?: boolean;
|
|
40
40
|
recursionLimit?: number;
|
|
41
|
-
maxTargetCount?: number;
|
|
42
41
|
/** 是否启用流式输出,默认 false */
|
|
43
42
|
stream?: boolean;
|
|
44
43
|
}
|