@saber2pr/ai-agent 0.0.23 → 0.0.24
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.js +47 -19
- package/package.json +1 -1
package/lib/core/agent-graph.js
CHANGED
|
@@ -23,6 +23,7 @@ const AgentState = langgraph_1.Annotation.Root({
|
|
|
23
23
|
default: () => [],
|
|
24
24
|
}),
|
|
25
25
|
auditedFiles: (0, langgraph_1.Annotation)({
|
|
26
|
+
// 确保列表是累加且去重的
|
|
26
27
|
reducer: (x, y) => Array.from(new Set([...x, ...y])),
|
|
27
28
|
default: () => [],
|
|
28
29
|
}),
|
|
@@ -39,15 +40,15 @@ class McpGraphAgent {
|
|
|
39
40
|
constructor(options = {}) {
|
|
40
41
|
this.checkpointer = new langgraph_1.MemorySaver();
|
|
41
42
|
this.langchainTools = [];
|
|
42
|
-
// ✅
|
|
43
|
+
// ✅ 存储清理 loading 的函数
|
|
43
44
|
this.stopLoadingFunc = null;
|
|
44
45
|
this.options = options;
|
|
45
46
|
this.targetDir = options.targetDir || process.cwd();
|
|
46
47
|
process.setMaxListeners(100);
|
|
47
|
-
// ✅
|
|
48
|
+
// ✅ 全局退出处理:清理动画并恢复光标
|
|
48
49
|
const cleanup = () => {
|
|
49
50
|
this.stopLoading();
|
|
50
|
-
process.stdout.write('\u001B[?25h');
|
|
51
|
+
process.stdout.write('\u001B[?25h');
|
|
51
52
|
process.exit(0);
|
|
52
53
|
};
|
|
53
54
|
process.on("SIGINT", cleanup);
|
|
@@ -56,11 +57,11 @@ class McpGraphAgent {
|
|
|
56
57
|
this.langchainTools = [...builtinToolInfos, ...(options.tools || [])].map((t) => (0, convertToLangChainTool_1.convertToLangChainTool)(t));
|
|
57
58
|
this.toolNode = new prebuilt_1.ToolNode(this.langchainTools);
|
|
58
59
|
}
|
|
59
|
-
// ✅ 1.
|
|
60
|
+
// ✅ 1. 核心 Loading 动画效果
|
|
60
61
|
showLoading(text) {
|
|
61
62
|
const chars = ['⠋', '⠙', '⠹', '⠸', '⠼', '⠴', '⠦', '⠧', '⠇', '⠏'];
|
|
62
63
|
let i = 0;
|
|
63
|
-
//
|
|
64
|
+
// 隐藏光标
|
|
64
65
|
process.stdout.write('\u001B[?25l');
|
|
65
66
|
const timer = setInterval(() => {
|
|
66
67
|
process.stdout.write(`\r\x1b[36m${chars[i]}\x1b[0m ${text}`);
|
|
@@ -68,11 +69,10 @@ class McpGraphAgent {
|
|
|
68
69
|
}, 80);
|
|
69
70
|
return () => {
|
|
70
71
|
clearInterval(timer);
|
|
71
|
-
process.stdout.write('\r\x1b[K'); //
|
|
72
|
+
process.stdout.write('\r\x1b[K'); // 清行
|
|
72
73
|
process.stdout.write('\u001B[?25h'); // 恢复光标
|
|
73
74
|
};
|
|
74
75
|
}
|
|
75
|
-
// ✅ 2. 封装内部调用方法
|
|
76
76
|
startLoading(text) {
|
|
77
77
|
this.stopLoading();
|
|
78
78
|
this.stopLoadingFunc = this.showLoading(text);
|
|
@@ -96,6 +96,7 @@ class McpGraphAgent {
|
|
|
96
96
|
temperature: 0,
|
|
97
97
|
});
|
|
98
98
|
}
|
|
99
|
+
// 绑定工具,使模型具备调用能力
|
|
99
100
|
this.model = modelInstance.bindTools(this.langchainTools);
|
|
100
101
|
return this.model;
|
|
101
102
|
}
|
|
@@ -110,6 +111,7 @@ class McpGraphAgent {
|
|
|
110
111
|
if (!config.baseURL || !config.apiKey) {
|
|
111
112
|
const rl = readline_1.default.createInterface({ input: process.stdin, output: process.stdout });
|
|
112
113
|
const question = (q) => new Promise((res) => rl.question(q, res));
|
|
114
|
+
console.log(`💡 首次运行请配置信息:`);
|
|
113
115
|
config.baseURL = config.baseURL || await question(`? API Base URL: `);
|
|
114
116
|
config.apiKey = config.apiKey || await question(`? API Key: `);
|
|
115
117
|
config.model = config.model || await question(`? Model Name: `) || "gpt-4o";
|
|
@@ -128,6 +130,7 @@ class McpGraphAgent {
|
|
|
128
130
|
}, { configurable: { thread_id: "auto_worker" }, recursionLimit: 100 });
|
|
129
131
|
for await (const output of stream)
|
|
130
132
|
this.renderOutput(output);
|
|
133
|
+
console.log("\n✅ 审计任务已完成。");
|
|
131
134
|
}
|
|
132
135
|
async start() {
|
|
133
136
|
await this.getModel();
|
|
@@ -139,6 +142,7 @@ class McpGraphAgent {
|
|
|
139
142
|
process.stdout.write('\u001B[?25h');
|
|
140
143
|
process.exit(0);
|
|
141
144
|
});
|
|
145
|
+
console.log(`\n💬 已进入交互审计模式 (Thread: session)`);
|
|
142
146
|
const ask = () => {
|
|
143
147
|
rl.question("> ", async (input) => {
|
|
144
148
|
if (input.toLowerCase() === "exit") {
|
|
@@ -155,7 +159,8 @@ class McpGraphAgent {
|
|
|
155
159
|
}
|
|
156
160
|
renderOutput(output) {
|
|
157
161
|
var _a, _b;
|
|
158
|
-
this.stopLoading(); //
|
|
162
|
+
this.stopLoading(); // 收到输出的第一时间关掉 Loading
|
|
163
|
+
// 1. 处理 Agent 节点的输出
|
|
159
164
|
const agentNode = output.agent;
|
|
160
165
|
if (agentNode) {
|
|
161
166
|
const msg = agentNode.messages[0];
|
|
@@ -171,18 +176,29 @@ class McpGraphAgent {
|
|
|
171
176
|
});
|
|
172
177
|
}
|
|
173
178
|
}
|
|
179
|
+
// 2. 处理工具节点的简要反馈(防止大数据量锁死终端)
|
|
180
|
+
if (output.tools) {
|
|
181
|
+
console.log(`✅ [工具执行完毕]`);
|
|
182
|
+
}
|
|
174
183
|
}
|
|
175
184
|
async callModel(state) {
|
|
185
|
+
const auditedListStr = state.auditedFiles.length > 0
|
|
186
|
+
? state.auditedFiles.map(f => `\n - ${f}`).join("")
|
|
187
|
+
: "暂无";
|
|
176
188
|
const prompt = prompts_1.ChatPromptTemplate.fromMessages([
|
|
177
189
|
["system", `你是一个代码专家。工作目录:${this.targetDir}。
|
|
178
|
-
当前模式:{mode}
|
|
190
|
+
当前模式:{mode}
|
|
191
|
+
进度:{doneCount}/{targetCount}
|
|
179
192
|
已审计文件:{auditedList}
|
|
180
193
|
|
|
194
|
+
# 指令
|
|
195
|
+
1. 优先通过 directory_tree 了解结构。
|
|
196
|
+
2. 发现问题后,先用 apply_fix 修复,再用 generate_review 提交。
|
|
197
|
+
3. 严禁反复执行同一个失败的操作。
|
|
181
198
|
{extraPrompt}`],
|
|
182
199
|
new prompts_1.MessagesPlaceholder("messages"),
|
|
183
200
|
]);
|
|
184
|
-
|
|
185
|
-
this.startLoading("AI 正在分析并思考中...");
|
|
201
|
+
this.startLoading("AI 正在分析并思考中");
|
|
186
202
|
try {
|
|
187
203
|
const chain = prompt.pipe(this.model);
|
|
188
204
|
const response = await chain.invoke({
|
|
@@ -190,7 +206,7 @@ class McpGraphAgent {
|
|
|
190
206
|
mode: state.mode,
|
|
191
207
|
targetCount: state.targetCount,
|
|
192
208
|
doneCount: state.auditedFiles.length,
|
|
193
|
-
auditedList:
|
|
209
|
+
auditedList: auditedListStr,
|
|
194
210
|
extraPrompt: this.options.extraSystemPrompt || "",
|
|
195
211
|
});
|
|
196
212
|
this.stopLoading();
|
|
@@ -204,15 +220,18 @@ class McpGraphAgent {
|
|
|
204
220
|
async trackProgress(state) {
|
|
205
221
|
var _a;
|
|
206
222
|
const lastAiMsg = state.messages[state.messages.length - 1];
|
|
207
|
-
const
|
|
223
|
+
const currentAudited = [...state.auditedFiles];
|
|
208
224
|
if ((_a = lastAiMsg === null || lastAiMsg === void 0 ? void 0 : lastAiMsg.tool_calls) === null || _a === void 0 ? void 0 : _a.length) {
|
|
209
225
|
for (const tc of lastAiMsg.tool_calls) {
|
|
226
|
+
// 兼容不同的参数命名习惯
|
|
210
227
|
const file = tc.args.path || tc.args.filePath || tc.args.file;
|
|
211
|
-
if (file && typeof file === 'string')
|
|
212
|
-
|
|
228
|
+
if (file && typeof file === 'string') {
|
|
229
|
+
currentAudited.push(file);
|
|
230
|
+
}
|
|
213
231
|
}
|
|
214
232
|
}
|
|
215
|
-
|
|
233
|
+
// 注意:这里的 reducer 会自动帮我们处理去重
|
|
234
|
+
return { auditedFiles: currentAudited };
|
|
216
235
|
}
|
|
217
236
|
async createGraph() {
|
|
218
237
|
const workflow = new langgraph_1.StateGraph(AgentState)
|
|
@@ -223,14 +242,23 @@ class McpGraphAgent {
|
|
|
223
242
|
.addConditionalEdges("agent", (state) => {
|
|
224
243
|
var _a;
|
|
225
244
|
const lastMsg = state.messages[state.messages.length - 1];
|
|
245
|
+
// 1. 有工具调用,必须去 tools
|
|
226
246
|
if ((_a = lastMsg.tool_calls) === null || _a === void 0 ? void 0 : _a.length)
|
|
227
247
|
return "tools";
|
|
228
|
-
|
|
229
|
-
|
|
248
|
+
// 2. 自动模式下,如果审计文件数未达标,继续循环
|
|
249
|
+
if (state.mode === "auto") {
|
|
250
|
+
if (state.auditedFiles.length < state.targetCount)
|
|
251
|
+
return "agent";
|
|
252
|
+
}
|
|
253
|
+
// 3. 默认结束(对话模式或任务已达标)
|
|
230
254
|
return langgraph_1.END;
|
|
255
|
+
}, {
|
|
256
|
+
tools: "tools",
|
|
257
|
+
agent: "agent",
|
|
258
|
+
[langgraph_1.END]: langgraph_1.END,
|
|
231
259
|
})
|
|
232
260
|
.addEdge("tools", "progress")
|
|
233
|
-
.addEdge("progress", "agent");
|
|
261
|
+
.addEdge("progress", "agent"); // 闭环回到 agent 进行下一轮决策
|
|
234
262
|
return workflow.compile({ checkpointer: this.checkpointer });
|
|
235
263
|
}
|
|
236
264
|
}
|