@saber2pr/ai-agent 0.0.26 → 0.0.28

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 CHANGED
@@ -1,205 +1,101 @@
1
- # 🚀 Saber2pr AI Agent
1
+ # 🤖 AI-Agent
2
2
 
3
- A high-performance AI Agent toolkit designed for automated code auditing, repository mapping, and architectural analysis. It supports both a lightweight **Standard Edition** for direct API interaction and a powerful **LangChain Edition** for complex multi-step reasoning and private LLM integration.
3
+ A professional code architect assistant built with **Model Context Protocol (MCP)** and **LangGraph**. It bridges Large Language Models (LLMs) with local file systems and remote services through standardized workflows.
4
4
 
5
- ## Core Features
5
+ ## 🌟 Core Modes
6
6
 
7
- * **Dual Mode Support**:
8
- * **Standard Mode**: Lightweight, fast, and uses direct OpenAI-compatible API calls.
9
- * **LangChain Mode**: Orchestrated via ReAct agents, supporting complex tool-chains and custom model extensions.
7
+ This project provides two core interaction modes to adapt to different LLM capabilities:
10
8
 
9
+ ### 1. 🚀 Standard Agent (`sagent`)
11
10
 
12
- * **MCP Integration**: Built on the Model Context Protocol to bridge local development environments with AI.
13
- * **Repository Intelligence**: Integrated `PromptEngine` for generating project maps and code skeletons without exhausting tokens.
14
- * **Automated Audit Workflow**: Specialized tools for locating code violations, providing line-specific fixes, and generating structured JSON reports.
15
- * **Private LLM Gateway**: Easily adapt to non-standard API protocols (e.g., Jarvis, internal enterprise gateways) by extending the `BaseChatModel`.
11
+ **Best for**: Modern models that support native `tools` parameters (e.g., GPT-4o, Claude 3.5, Qwen-Max).
16
12
 
17
- ---
18
-
19
- ## 🛠️ Installation
20
-
21
- ```bash
22
- sudo npm i -g @saber2pr/ai-agent
23
-
24
- # call openapi
25
- sagent
26
-
27
- # call third api
28
- sagent-chain
29
-
30
- # Clone the repository
31
- git clone https://github.com/saber2pr/ai-agent.git
32
- cd ai-agent
13
+ * **Native Function Calling**: Leverages the model's built-in tool-calling capabilities for precise parameter parsing and low latency.
14
+ * **Intuitive Interaction**: A classic Chat loop suitable for instant Q&A, quick code lookups, or single-file refactoring tasks.
33
15
 
34
- # Install dependencies
35
- npm install
16
+ ### 2. 🏗️ Graph Agent (`sagent-graph`)
36
17
 
37
- # Build the project
38
- npm run build
18
+ **Best for**: Long-running automated audits or scenarios where the API does not support native `tools`.
39
19
 
40
- ```
20
+ * **State Machine Architecture**: Built on **LangGraph** to implement an automated "Think-Act-Observe-Summarize" loop.
21
+ * **Audit Tracking**: Built-in progress manager that automatically records analyzed files to prevent redundant analysis in complex projects.
22
+ * **Auto-Settlement**: Automatically summarizes and prints **Total Tokens** and execution duration upon task completion or manual exit.
41
23
 
42
24
  ---
43
25
 
44
- ## 🚀 Usage Modes
26
+ ## 🛠️ Built-in Toolset
45
27
 
46
- ### 1. Standard Edition (Direct API)
28
+ The Agent comes pre-installed with a suite of tools specifically customized for **code architecture analysis**:
47
29
 
48
- Best for quick scripts and simple chat interactions. It uses a straightforward message-loop logic.
30
+ | Category | Tool Name | Description |
31
+ | ---------------- | ----------------- | ------------------------------------------------------------------------- |
32
+ | | `list_directory` | Lists files in a specified directory. |
33
+ | | `analyze_deps` | Analyzes import dependencies of a specific file. |
34
+ | | `read_skeleton` | Reads only code skeletons (class names/signatures) to save tokens. |
35
+ | | `edit_file` | Precise Diff-based editing to avoid rewriting large files. |
36
+ | | `get_method_body` | Precisely extracts the implementation of a specific method. |
37
+ | | `get_file_info` | Gets file metadata like size, permissions, and timestamps. |
38
+ | **Architecture** | `get_repo_map` | **Core**: Extracts export definitions and builds a module dependency map. |
39
+ | **General** | `search_files` | Performs a global keyword search across the project. |
40
+ | **Navigation** | `directory_tree` | Recursively gets the project structure (The entry point for analysis). |
41
+ | **Operations** | `read_text_file` | Reads file content (supports pagination via head/tail). |
49
42
 
50
- ```javascript
51
- import McpAgent from "@saber2pr/ai-agent";
43
+ ---
52
44
 
53
- const agent = new McpAgent({
54
- targetDir: "/path/to/project"
55
- });
45
+ ## 💻 CLI Guide
56
46
 
57
- await agent.chat("Analyze the directory structure.");
47
+ ### Installation & Build
58
48
 
49
+ ```bash
50
+ yarn
51
+ yarn build
59
52
  ```
60
53
 
61
- ### 2. LangChain Edition (Advanced Agent)
62
-
63
- Best for complex tasks like "Audit the whole project and fix bugs." It supports autonomous tool usage.
64
-
65
- ```javascript
66
- import { McpChainAgent } from "@saber2pr/ai-agent";
67
- import { MyPrivateLLM } from "./your-custom-llm";
54
+ ### Usage
68
55
 
69
- const agent = new McpChainAgent({
70
- apiModel: new MyPrivateLLM(), // Inject custom LLM
71
- maxIterations: 15,
72
- targetDir: "/path/to/project"
73
- });
74
-
75
- await agent.chat("Scan for hardcoded colors and submit a review report.");
56
+ ```sh
57
+ sudo npm i -g @saber2pr/ai-agent
76
58
 
77
59
  ```
78
60
 
79
- ---
80
-
81
- ## 🔧 Extending with Private LLMs
82
-
83
- ### Using AgentChainModel (Recommended)
84
-
85
- For LangChain mode, extend `AgentChainModel` which provides a simplified interface for integrating custom LLMs:
86
-
87
- ```javascript
88
- import { AgentChainModel } from "@saber2pr/ai-agent";
89
-
90
- class MyPrivateLLM extends AgentChainModel {
91
- constructor(fields) {
92
- super(fields || {});
93
- }
94
-
95
- async generateAgentChainResponse(messages) {
96
- const lastMessage = messages[messages.length - 1];
97
- const queryText = lastMessage.content;
98
-
99
- const response = await fetch("https://your-api-gateway.com/api/completions", {
100
- method: 'POST',
101
- body: JSON.stringify({ query: queryText, stream: false }),
102
- headers: {
103
- 'Content-Type': 'application/json',
104
- Authorization: `Bearer YOUR_API_KEY`,
105
- }
106
- });
107
-
108
- if (!response.ok) {
109
- const errorText = await response.text();
110
- throw new Error(`HTTP Error: ${response.status}, ${errorText}`);
111
- }
112
-
113
- const data = await response.json();
114
- let text = data.text || "";
115
-
116
- // Handle special response formats if needed
117
- if (text.includes("Action:") && text.includes("Final Answer:")) {
118
- text = text.split("Final Answer:")[0].trim();
119
- }
120
-
121
- return text;
122
- }
123
- }
61
+ * **Start Standard Chat**:
62
+ ```bash
63
+ sagent
124
64
  ```
125
65
 
126
- **Key Points:**
127
- - `AgentChainModel` abstracts away LangChain's internal message handling
128
- - You only need to implement `generateAgentChainResponse(messages)` which receives an array of messages
129
- - The method should return a plain string response
130
- - The base class handles conversion to LangChain's expected format
131
-
132
- ---
133
-
134
- ## 📦 Built-in Toolset
135
-
136
- The toolkit provides a comprehensive set of built-in tools organized into two categories: **Filesystem Tools** and **Code Analysis Tools**. All tools operate within the `targetDir` scope for security.
137
-
138
- ### Filesystem Tools
139
-
140
- | Tool | Description | Parameters |
141
- |------|-------------|------------|
142
- | `read_text_file` | Read complete file contents as text. Supports `head` and `tail` parameters for partial reading. Handles various text encodings. | `path` (required), `head?`, `tail?` |
143
- | `read_multiple_files` | Read multiple files simultaneously for efficient batch analysis. Individual failures won't stop the operation. | `paths` (array, required) |
144
- | `write_file` | Create a new file or completely overwrite an existing file. Use with caution as it overwrites without warning. | `path` (required), `content` (required) |
145
- | `edit_file` | Make line-based edits to a text file. Replaces exact line sequences with new content. Returns git-style diff. Supports `dryRun` mode for preview. | `path` (required), `edits` (array, required), `dryRun?` |
146
- | `get_directory_tree` | Get a recursive tree view of files and directories as JSON. Supports `excludePatterns` for filtering (minimatch patterns). Essential for understanding project structure. | `path` (required), `excludePatterns?` (array) |
147
- | `list_directory` | List all files and directories in a specified path. Results distinguish files and directories with `[FILE]` and `[DIR]` prefixes. | `path` (required) |
148
- | `list_directory_with_sizes` | List directory contents with file sizes. Supports sorting by name or size. Includes summary statistics. | `path` (required), `sortBy?` ("name" \| "size") |
149
- | `search_files` | Search for files matching a glob pattern. Supports exclude patterns for filtering. | `path` (required), `pattern` (required), `excludePatterns?` (array) |
150
- | `move_file` | Move or rename files and directories. Can move between directories and rename in a single operation. | `source` (required), `destination` (required) |
151
- | `create_directory` | Create a new directory or ensure it exists. Can create multiple nested directories recursively. | `path` (required) |
152
- | `get_file_info` | Get detailed metadata about a file: size, last modified time, type, etc. | `path` (required) |
153
66
 
154
- ### Code Analysis Tools
155
-
156
- | Tool | Description | Parameters |
157
- |------|-------------|------------|
158
- | `get_repo_map` | Generate a high-level map of project files and exports. Extracts export definitions to understand module relationships. Use this first to understand project structure. | None |
159
- | `read_skeleton` | Extract structural definitions (interfaces, classes, method signatures) without implementation details. Token-efficient for code analysis. | `filePath` (required) |
160
- | `analyze_deps` | Analyze dependency relationships for a specific file. Supports TypeScript path alias resolution via tsconfig. | `filePath` (required) |
161
- | `get_method_body` | Get the complete implementation code for a specific method or function within a file. | `filePath` (required), `methodName` (required) |
162
-
163
- ### Tool Usage Tips
164
-
165
- 1. **Start with `get_directory_tree`**: Always begin by understanding the project structure before reading files.
166
- 2. **Use `read_skeleton` before `read_text_file`**: Extract signatures first to save tokens, then read full content only when needed.
167
- 3. **Leverage `excludePatterns`**: Use minimatch patterns to exclude `node_modules`, `.git`, build artifacts, etc.
168
- 4. **Batch operations**: Use `read_multiple_files` when analyzing multiple files to improve efficiency.
169
- 5. **Preview changes**: Use `edit_file` with `dryRun: true` to preview changes before applying them.
67
+ * **Start Automated Audit**:
68
+ ```bash
69
+ sagent-graph
70
+ ```
170
71
 
171
72
  ---
172
73
 
173
- ## 📋 Audit Rule Configuration
174
-
175
- You can pass structured rules via the `extraSystemPrompt`:
74
+ ## ⚙️ Configuration
176
75
 
177
- ```javascript
178
- import McpAgent from "@saber2pr/ai-agent";
76
+ On the first run, the program will prompt you to configure `~/.saber2pr-agent.json`. You can define your API keys and dynamically connect **MCP Servers** here:
179
77
 
180
- const agent = new McpAgent({
181
- extraSystemPrompt: {
182
- role: "Code Auditor",
183
- rules: [
184
- { id: "THEME-001", name: "Theme Check", description: "No hardcoded hex colors." }
185
- ]
186
- }
187
- });
78
+ ```json
79
+ {
80
+ "baseURL": "https://api.example.com/v1",
81
+ "apiKey": "sk-your-key",
82
+ "model": "gpt-4o"
83
+ }
188
84
 
189
85
  ```
190
86
 
191
87
  ---
192
88
 
193
- ## ⚙️ Configuration
89
+ ## 🔄 Professional Audit Workflow
194
90
 
195
- The agent stores API keys and base URLs in `~/.saber2pr-agent.json`.
91
+ Regardless of the mode, the Agent follows a standardized logic chain:
196
92
 
197
- * `baseURL`: The API endpoint.
198
- * `apiKey`: Your authentication key.
199
- * `model`: The model name (e.g., `gpt-4o`, `claude-3-5-sonnet`).
93
+ 1. **Phase 1: Panoramic Perception (Where)**: Uses `directory_tree` to identify project layout (e.g., Monorepo vs. traditional src structure).
94
+ 2. **Phase 2: Logic Mapping (What)**: Uses `get_repo_map` to establish logical relationships—understanding "who calls whom."
95
+ 3. **Phase 3: Source Diving (How)**: Locates critical implementations and performs detailed auditing via `read_text_file` or specific extraction tools.
200
96
 
201
97
  ---
202
98
 
203
- ## 📜 License
99
+ ## 📄 License
204
100
 
205
101
  ISC
@@ -1,5 +1,5 @@
1
- import { BaseMessage } from "@langchain/core/messages";
2
- import { AgentOptions } from "../types/type";
1
+ import { BaseMessage } from '@langchain/core/messages';
2
+ import { GraphAgentOptions } from '../types/type';
3
3
  export declare const CONFIG_FILE: string;
4
4
  interface TokenUsage {
5
5
  total: number;
@@ -23,7 +23,17 @@ export default class McpGraphAgent {
23
23
  private verbose;
24
24
  private alwaysSystem;
25
25
  private recursionLimit;
26
- constructor(options?: AgentOptions);
26
+ private apiConfig;
27
+ private maxTargetCount;
28
+ private maxTokens;
29
+ private mcpClients;
30
+ constructor(options?: GraphAgentOptions);
31
+ private printLoadedTools;
32
+ private loadMcpConfigs;
33
+ private initMcpTools;
34
+ private prepareTools;
35
+ private ensureInitialized;
36
+ private closeMcpClients;
27
37
  private showLoading;
28
38
  private startLoading;
29
39
  private stopLoading;
@@ -33,7 +43,7 @@ export default class McpGraphAgent {
33
43
  start(): Promise<void>;
34
44
  private renderOutput;
35
45
  callModel(state: typeof AgentState.State): Promise<{
36
- messages: unknown[];
46
+ messages: any[];
37
47
  tokenUsage: {
38
48
  total: number;
39
49
  };
@@ -58,7 +68,7 @@ export default class McpGraphAgent {
58
68
  mode: import("@langchain/langgraph").BinaryOperatorAggregate<"chat" | "auto", "chat" | "auto">;
59
69
  tokenUsage: import("@langchain/langgraph").BinaryOperatorAggregate<TokenUsage, TokenUsage>;
60
70
  totalDuration: import("@langchain/langgraph").BinaryOperatorAggregate<number, number>;
61
- }>, "tools" | "agent" | "__start__" | "progress", {
71
+ }>, "tools" | "__start__" | "agent" | "progress", {
62
72
  messages: import("@langchain/langgraph").BinaryOperatorAggregate<BaseMessage[], BaseMessage[]>;
63
73
  auditedFiles: import("@langchain/langgraph").BinaryOperatorAggregate<string[], string[]>;
64
74
  targetCount: import("@langchain/langgraph").BinaryOperatorAggregate<number, number>;
@@ -4,18 +4,21 @@ var __importDefault = (this && this.__importDefault) || function (mod) {
4
4
  };
5
5
  Object.defineProperty(exports, "__esModule", { value: true });
6
6
  exports.CONFIG_FILE = void 0;
7
- const openai_1 = require("@langchain/openai");
7
+ const events_1 = require("events");
8
+ const fs_1 = __importDefault(require("fs"));
9
+ const os_1 = __importDefault(require("os"));
10
+ const path_1 = __importDefault(require("path"));
11
+ const readline_1 = __importDefault(require("readline"));
8
12
  const messages_1 = require("@langchain/core/messages");
13
+ const prompts_1 = require("@langchain/core/prompts");
9
14
  const langgraph_1 = require("@langchain/langgraph");
10
15
  const prebuilt_1 = require("@langchain/langgraph/prebuilt");
11
- const prompts_1 = require("@langchain/core/prompts");
12
- const readline_1 = __importDefault(require("readline"));
13
- const fs_1 = __importDefault(require("fs"));
14
- const path_1 = __importDefault(require("path"));
15
- const os_1 = __importDefault(require("os"));
16
- const events_1 = require("events");
16
+ const openai_1 = require("@langchain/openai");
17
17
  const builtin_1 = require("../tools/builtin");
18
18
  const convertToLangChainTool_1 = require("../utils/convertToLangChainTool");
19
+ const index_js_1 = require("@modelcontextprotocol/sdk/client/index.js");
20
+ const stdio_js_1 = require("@modelcontextprotocol/sdk/client/stdio.js");
21
+ const jsonSchemaToZod_1 = require("../utils/jsonSchemaToZod");
19
22
  exports.CONFIG_FILE = path_1.default.join(os_1.default.homedir(), ".saber2pr-agent.json");
20
23
  // ✅ 全局设置:修复 AbortSignal 监听器数量警告
21
24
  // LangChain 的 HTTP 客户端会创建多个 AbortSignal,需要增加默认限制
@@ -56,28 +59,139 @@ class McpGraphAgent {
56
59
  this.checkpointer = new langgraph_1.MemorySaver();
57
60
  this.langchainTools = [];
58
61
  this.stopLoadingFunc = null;
62
+ this.mcpClients = [];
59
63
  this.options = options;
60
64
  this.verbose = options.verbose || false;
61
65
  this.alwaysSystem = options.alwaysSystem || true;
62
66
  this.targetDir = options.targetDir || process.cwd();
63
- this.recursionLimit = options.recursionLimit || 200;
67
+ this.recursionLimit = options.recursionLimit || 80;
68
+ this.apiConfig = options.apiConfig;
69
+ this.maxTargetCount = options.maxTargetCount || 4;
70
+ this.maxTokens = options.maxTokens || 8000;
64
71
  process.setMaxListeners(100);
65
72
  // ✅ 修复 AbortSignal 监听器数量警告
66
73
  // LangChain 的 HTTP 客户端会创建多个 AbortSignal,需要增加默认限制
67
74
  // 设置 EventEmitter 的默认 maxListeners,这会影响所有事件发射器(包括 AbortSignal)
68
75
  events_1.EventEmitter.defaultMaxListeners = 100;
69
- const cleanup = () => {
76
+ const cleanup = async () => {
70
77
  this.stopLoading();
78
+ await this.closeMcpClients(); // 清理 MCP 连接
71
79
  process.stdout.write('\u001B[?25h');
72
80
  process.exit(0);
73
81
  };
74
82
  process.on("SIGINT", cleanup);
75
83
  process.on("SIGTERM", cleanup);
76
- // ✅ 初始化内置工具
77
- const builtinToolInfos = (0, builtin_1.createDefaultBuiltinTools)({ options });
78
- this.langchainTools = [...builtinToolInfos, ...(options.tools || [])].map((t) => (0, convertToLangChainTool_1.convertToLangChainTool)(t));
84
+ }
85
+ printLoadedTools() {
86
+ console.log("\n🛠️ [Graph] 正在加载工具节点...");
87
+ this.langchainTools.forEach((tool) => {
88
+ // 工具名称
89
+ console.log(`\n🧰 工具名: ${tool.name}`);
90
+ // 提取参数结构 (LangChain Tool 的 schema 是 Zod 对象)
91
+ const schema = tool.schema;
92
+ if (schema && schema.shape) {
93
+ // 如果是 ZodObject,打印其内部 key
94
+ const keys = Object.keys(schema.shape);
95
+ console.log(` 参数结构: [ ${keys.join(", ")} ]`);
96
+ }
97
+ else if (schema && schema._def) {
98
+ // 兼容其他 Zod 类型
99
+ console.log(` 参数类型: ${schema._def.typeName}`);
100
+ }
101
+ else {
102
+ // 降级:如果已经是 JSON 对象
103
+ console.log(` 参数结构:`, JSON.stringify(schema, null, 2));
104
+ }
105
+ });
106
+ console.log(`\n✅ Graph 节点就绪,总计加载 ${this.langchainTools.length} 个工具。\n`);
107
+ }
108
+ loadMcpConfigs() {
109
+ const combined = { mcpServers: {} };
110
+ const paths = [
111
+ path_1.default.join(os_1.default.homedir(), ".cursor", "mcp.json"),
112
+ path_1.default.join(os_1.default.homedir(), ".vscode", "mcp.json"),
113
+ ];
114
+ paths.forEach((p) => {
115
+ if (fs_1.default.existsSync(p)) {
116
+ const content = JSON.parse(fs_1.default.readFileSync(p, "utf-8"));
117
+ Object.assign(combined.mcpServers, content.mcpServers);
118
+ }
119
+ });
120
+ return combined;
121
+ }
122
+ async initMcpTools() {
123
+ const mcpConfig = this.loadMcpConfigs();
124
+ const mcpServers = mcpConfig.mcpServers || {};
125
+ const mcpToolInfos = [];
126
+ for (const [name, config] of Object.entries(mcpServers)) {
127
+ try {
128
+ const transport = new stdio_js_1.StdioClientTransport({
129
+ command: config.command,
130
+ args: config.args,
131
+ env: { ...process.env, ...(config.env || {}) },
132
+ });
133
+ const client = new index_js_1.Client({ name: "mcp-graph-client", version: "1.0.0" }, { capabilities: {} });
134
+ await client.connect(transport);
135
+ this.mcpClients.push(client);
136
+ const { tools } = await client.listTools();
137
+ tools.forEach((tool) => {
138
+ mcpToolInfos.push({
139
+ type: "function",
140
+ function: {
141
+ name: tool.name,
142
+ description: tool.description,
143
+ parameters: (0, jsonSchemaToZod_1.jsonSchemaToZod)(tool.inputSchema), // MCP 使用 JSON Schema
144
+ },
145
+ _handler: async (args) => {
146
+ const result = await client.callTool({
147
+ name: tool.name,
148
+ arguments: args,
149
+ });
150
+ return result.content;
151
+ },
152
+ });
153
+ });
154
+ console.log(`\n✅ 已连接 MCP 服务 [${name}]: 加载了 ${tools.length} 个工具`);
155
+ }
156
+ catch (error) {
157
+ console.error(`\n❌ 连接 MCP 服务 [${name}] 失败:`, error.message);
158
+ }
159
+ }
160
+ return mcpToolInfos;
161
+ }
162
+ async prepareTools() {
163
+ const builtinToolInfos = (0, builtin_1.createDefaultBuiltinTools)({ options: this.options });
164
+ const mcpToolInfos = await this.initMcpTools();
165
+ // 合并内置、手动传入和 MCP 工具
166
+ const allToolInfos = [
167
+ ...builtinToolInfos,
168
+ ...(this.options.tools || []),
169
+ ...mcpToolInfos
170
+ ];
171
+ this.langchainTools = allToolInfos.map((t) => (0, convertToLangChainTool_1.convertToLangChainTool)(t));
79
172
  this.toolNode = new prebuilt_1.ToolNode(this.langchainTools);
80
173
  }
174
+ // ✅ 修改:初始化逻辑
175
+ async ensureInitialized() {
176
+ if (this.model && this.langchainTools.length > 0)
177
+ return;
178
+ // 1. 加载所有工具(含 MCP)
179
+ await this.prepareTools();
180
+ // 2. 初始化模型
181
+ await this.getModel();
182
+ // 3. 打印工具状态
183
+ this.printLoadedTools();
184
+ }
185
+ // ✅ 新增:关闭连接
186
+ async closeMcpClients() {
187
+ for (const client of this.mcpClients) {
188
+ try {
189
+ await client.close();
190
+ }
191
+ catch (e) { }
192
+ }
193
+ this.mcpClients = [];
194
+ }
81
195
  showLoading(text) {
82
196
  const chars = ['⠋', '⠙', '⠹', '⠸', '⠼', '⠴', '⠦', '⠧', '⠇', '⠏'];
83
197
  let i = 0;
@@ -113,13 +227,15 @@ class McpGraphAgent {
113
227
  configuration: { baseURL: config.baseURL },
114
228
  modelName: config.model,
115
229
  temperature: 0,
230
+ maxTokens: this.maxTokens,
116
231
  });
117
232
  }
118
- // 绑定工具,使模型具备调用能力
119
233
  this.model = modelInstance.bindTools(this.langchainTools);
120
234
  return this.model;
121
235
  }
122
236
  async askForConfig() {
237
+ if (this.apiConfig)
238
+ return this.apiConfig;
123
239
  let config = {};
124
240
  if (fs_1.default.existsSync(exports.CONFIG_FILE)) {
125
241
  try {
@@ -139,17 +255,19 @@ class McpGraphAgent {
139
255
  return config;
140
256
  }
141
257
  async chat(query = "开始代码审计") {
258
+ await this.ensureInitialized();
142
259
  await this.getModel();
143
260
  const app = await this.createGraph();
144
261
  const stream = await app.stream({
145
262
  messages: [new messages_1.HumanMessage(query)],
146
263
  mode: "auto",
147
- targetCount: 4,
264
+ targetCount: this.maxTargetCount,
148
265
  }, { configurable: { thread_id: "auto_worker" }, recursionLimit: this.recursionLimit, debug: this.verbose, });
149
266
  for await (const output of stream)
150
267
  this.renderOutput(output);
151
268
  }
152
269
  async start() {
270
+ await this.ensureInitialized();
153
271
  await this.getModel();
154
272
  const app = await this.createGraph();
155
273
  const rl = readline_1.default.createInterface({ input: process.stdin, output: process.stdout });
@@ -165,7 +283,7 @@ class McpGraphAgent {
165
283
  rl.close();
166
284
  return;
167
285
  }
168
- const stream = await app.stream({ messages: [new messages_1.HumanMessage(input)], mode: "chat" }, { configurable: { thread_id: "session" }, recursionLimit: 50 });
286
+ const stream = await app.stream({ messages: [new messages_1.HumanMessage(input)], mode: "chat" }, { configurable: { thread_id: "session" }, recursionLimit: this.recursionLimit, debug: this.verbose, });
169
287
  for await (const output of stream)
170
288
  this.renderOutput(output);
171
289
  ask();
@@ -252,9 +370,8 @@ class McpGraphAgent {
252
370
  2. 严禁对 JSON 内容进行二次转义。
253
371
 
254
372
  # 指令
255
- 1. 一旦完成对一个文件的修改(apply_fix),请立即调用 generate_review 总结该文件的变动。
256
- 2. 避免陷入在同一个文件上的无限循环尝试。
257
- 3. 不要重复调用相同的工具和参数,如果工具已经返回结果,请基于结果继续工作而不是再次调用。{recentToolCalls}
373
+ 1. 避免陷入在同一个文件上的无限循环尝试。
374
+ 2. 不要重复调用相同的工具和参数,如果工具已经返回结果,请基于结果继续工作而不是再次调用。{recentToolCalls}
258
375
  {extraPrompt}`;
259
376
  // 2. 核心逻辑:处理消息上下文
260
377
  let inputMessages;
@@ -352,6 +469,13 @@ class McpGraphAgent {
352
469
  const messages = state.messages;
353
470
  const lastMsg = messages[messages.length - 1];
354
471
  const content = lastMsg.content || "";
472
+ // 🛑 新增:全局 Token 熔断保护
473
+ // 如果已消耗 Token 超过了 options 中设置的 maxTokens (假设是总限额)
474
+ if (this.options.maxTokens && state.tokenUsage.total >= this.options.maxTokens) {
475
+ console.warn("⚠️ [警告] 已达到最大 Token 限制,强制结束任务。");
476
+ this.printFinalSummary(state);
477
+ return langgraph_1.END;
478
+ }
355
479
  // 1. 如果 AI 想要调用工具,去 tools 节点
356
480
  if (lastMsg.tool_calls && lastMsg.tool_calls.length > 0) {
357
481
  return "tools";
@@ -360,7 +484,7 @@ class McpGraphAgent {
360
484
  // - 模式是 auto 且审计完成
361
485
  // - 或者 AI 明确输出了结束语
362
486
  // - 或者 AI 输出了普通内容且没有工具调用(针对问答模式)
363
- const isAutoFinished = state.mode === "auto" && state.auditedFiles.length >= state.targetCount;
487
+ const isAutoFinished = state.mode === "auto" && state.auditedFiles.length > state.targetCount;
364
488
  const isFinalAnswer = content.includes("Final Answer");
365
489
  // ✅ 修复核心:如果 AI 只是在聊天(没有工具调用),直接结束,不要跳回 agent
366
490
  if (isAutoFinished || isFinalAnswer || state.mode === "chat") {
@@ -19,6 +19,7 @@ export default class McpAgent {
19
19
  private ensureApiConfig;
20
20
  private loadMcpConfigs;
21
21
  init(): Promise<void>;
22
+ private getToolsForOpenAIAPI;
22
23
  private processChat;
23
24
  /**
24
25
  * 裁剪上下文消息列表
package/lib/core/agent.js CHANGED
@@ -46,6 +46,7 @@ const index_js_1 = require("@modelcontextprotocol/sdk/client/index.js");
46
46
  const stdio_js_1 = require("@modelcontextprotocol/sdk/client/stdio.js");
47
47
  const config_1 = require("../config/config");
48
48
  const builtin_1 = require("../tools/builtin");
49
+ const zod_to_json_schema_1 = require("zod-to-json-schema");
49
50
  const jsonSchemaToZod_1 = require("../utils/jsonSchemaToZod");
50
51
  class McpAgent {
51
52
  constructor(options) {
@@ -215,6 +216,25 @@ class McpAgent {
215
216
  }
216
217
  }
217
218
  }
219
+ getToolsForOpenAIAPI() {
220
+ return this.allTools.map((tool) => {
221
+ const { _handler, _client, _originalName, ...rest } = tool;
222
+ let parameters = rest.function.parameters;
223
+ // 💡 核心逻辑:判断是否为 Zod 实例并转换
224
+ // Zod 对象通常包含 _def 属性,或者你可以用 instanceof z.ZodType
225
+ if (parameters && typeof parameters === 'object' && ('_def' in parameters || parameters.safeParse)) {
226
+ // 使用 zod-to-json-schema 转换为标准 JSON Schema
227
+ parameters = (0, zod_to_json_schema_1.zodToJsonSchema)(parameters);
228
+ }
229
+ return {
230
+ ...rest,
231
+ function: {
232
+ ...rest.function,
233
+ parameters: parameters, // 确保这里是 Plain Object
234
+ },
235
+ };
236
+ });
237
+ }
218
238
  async processChat(userInput) {
219
239
  var _a;
220
240
  this.messages.push({ role: 'user', content: userInput });
@@ -237,7 +257,7 @@ class McpAgent {
237
257
  response = await this.openai.chat.completions.create({
238
258
  model: this.modelName,
239
259
  messages: this.messages,
240
- tools: this.allTools.map(({ _handler, _client, _originalName, ...rest }) => rest),
260
+ tools: this.getToolsForOpenAIAPI(),
241
261
  tool_choice: 'auto'
242
262
  });
243
263
  }
@@ -262,7 +282,9 @@ class McpAgent {
262
282
  if (args.filePath) {
263
283
  console.log(` 📂 正在查看文件: ${args.filePath}`);
264
284
  }
265
- console.log(` 🛠️ 执行: ${call.function.name}`);
285
+ // 2. 打印操作信息和参数
286
+ console.log(`\n 🛠️ 执行工具: \x1b[36m${call.function.name}\x1b[0m`);
287
+ console.log(` 📦 传入参数: \x1b[2m${JSON.stringify(args)}\x1b[0m`);
266
288
  let result;
267
289
  if (tool === null || tool === void 0 ? void 0 : tool._handler) {
268
290
  result = await tool._handler(args);
package/lib/index.d.ts CHANGED
@@ -1,7 +1,5 @@
1
1
  export * from './core/agent';
2
- export { default as McpChainAgent } from './core/agent-chain';
3
2
  export { default } from './core/agent';
4
3
  export { createTool } from './utils/createTool';
5
- export { AgentChainModel } from './model/AgentChainModel';
6
4
  export { AgentGraphModel, AgentGraphLLMResponse } from './model/AgentGraphModel';
7
5
  export { default as McpGraphAgent } from './core/agent-graph';
package/lib/index.js CHANGED
@@ -17,16 +17,12 @@ var __importDefault = (this && this.__importDefault) || function (mod) {
17
17
  return (mod && mod.__esModule) ? mod : { "default": mod };
18
18
  };
19
19
  Object.defineProperty(exports, "__esModule", { value: true });
20
- exports.McpGraphAgent = exports.AgentGraphModel = exports.AgentChainModel = exports.createTool = exports.default = exports.McpChainAgent = void 0;
20
+ exports.McpGraphAgent = exports.AgentGraphModel = exports.createTool = exports.default = void 0;
21
21
  __exportStar(require("./core/agent"), exports);
22
- var agent_chain_1 = require("./core/agent-chain");
23
- Object.defineProperty(exports, "McpChainAgent", { enumerable: true, get: function () { return __importDefault(agent_chain_1).default; } });
24
22
  var agent_1 = require("./core/agent");
25
23
  Object.defineProperty(exports, "default", { enumerable: true, get: function () { return __importDefault(agent_1).default; } });
26
24
  var createTool_1 = require("./utils/createTool");
27
25
  Object.defineProperty(exports, "createTool", { enumerable: true, get: function () { return createTool_1.createTool; } });
28
- var AgentChainModel_1 = require("./model/AgentChainModel");
29
- Object.defineProperty(exports, "AgentChainModel", { enumerable: true, get: function () { return AgentChainModel_1.AgentChainModel; } });
30
26
  var AgentGraphModel_1 = require("./model/AgentGraphModel");
31
27
  Object.defineProperty(exports, "AgentGraphModel", { enumerable: true, get: function () { return AgentGraphModel_1.AgentGraphModel; } });
32
28
  var agent_graph_1 = require("./core/agent-graph");
@@ -1,5 +1,6 @@
1
1
  import { Client } from '@modelcontextprotocol/sdk/client/index';
2
2
  import { z } from 'zod';
3
+ import { AgentGraphModel } from '../model/AgentGraphModel';
3
4
  export interface ApiConfig {
4
5
  baseURL: string;
5
6
  apiKey: string;
@@ -30,41 +31,12 @@ export interface AgentOptions {
30
31
  tools?: ToolInfo[];
31
32
  extraSystemPrompt?: any;
32
33
  maxTokens?: number;
33
- /**
34
- * only for chain agent
35
- */
36
- apiConfig?: ApiConfig;
37
- /**
38
- * only for chain agent
39
- * extends BaseChatModel
40
- */
41
- apiModel?: any;
42
- /**
43
- * only for chain agent
44
- */
45
- maxIterations?: number;
46
- /**
47
- * only for chain agent
48
- */
49
34
  verbose?: boolean;
50
- /**
51
- * only for graph agent
52
- */
53
- baseURL?: string;
54
- /**
55
- * only for graph agent
56
- */
57
- apiKey?: string;
58
- /**
59
- * only for graph agent
60
- */
61
- modelName?: string;
62
- /**
63
- * only for graph agent
64
- */
35
+ apiConfig?: ApiConfig;
36
+ }
37
+ export interface GraphAgentOptions extends AgentOptions {
38
+ apiModel?: AgentGraphModel;
65
39
  alwaysSystem?: boolean;
66
- /**
67
- * only for graph agent
68
- */
69
40
  recursionLimit?: number;
41
+ maxTargetCount?: number;
70
42
  }
@@ -4,7 +4,7 @@ export interface CreateToolOptions {
4
4
  name: string;
5
5
  description: string;
6
6
  /**
7
- * zod@3.23.8
7
+ * zod@3.25
8
8
  */
9
9
  parameters: z.ZodObject<any>;
10
10
  handler: (args: any) => Promise<string>;
@@ -4,6 +4,14 @@ exports.jsonSchemaToZod = jsonSchemaToZod;
4
4
  const zod_1 = require("zod");
5
5
  function jsonSchemaToZod(parameters) {
6
6
  var _a;
7
+ // ✅ 跨版本/跨实例的特征检测
8
+ const isZod = parameters && (typeof parameters.safeParse === 'function' || // 所有 Zod 类型都有
9
+ !!parameters._def || // Zod 内部定义属性
10
+ !!parameters.shape // 专门针对 ZodObject 的特征
11
+ );
12
+ if (isZod) {
13
+ return parameters;
14
+ }
7
15
  if (!parameters || typeof parameters !== 'object') {
8
16
  return zod_1.z.object({}).passthrough();
9
17
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@saber2pr/ai-agent",
3
- "version": "0.0.26",
3
+ "version": "0.0.28",
4
4
  "description": "AI Assistant CLI.",
5
5
  "author": "saber2pr",
6
6
  "license": "ISC",
@@ -9,7 +9,6 @@
9
9
  ],
10
10
  "bin": {
11
11
  "sagent": "./lib/cli.js",
12
- "sagent-chain": "./lib/cli-chain.js",
13
12
  "sagent-graph": "./lib/cli-graph.js"
14
13
  },
15
14
  "publishConfig": {
@@ -20,6 +19,7 @@
20
19
  "scripts": {
21
20
  "test": "node lib/index",
22
21
  "start": "tsc --watch",
22
+ "build": "tsc",
23
23
  "prepublishOnly": "tsc"
24
24
  },
25
25
  "dependencies": {
@@ -34,9 +34,11 @@
34
34
  "langchain": "0.3.15",
35
35
  "minimatch": "^10.0.1",
36
36
  "openai": "^6.16.0",
37
- "zod": "3.23.8"
37
+ "zod": "3.25",
38
+ "zod-to-json-schema": "3.23.2"
38
39
  },
39
40
  "resolutions": {
41
+ "zod": "3.25",
40
42
  "@langchain/core": "0.3.39"
41
43
  },
42
44
  "devDependencies": {
@@ -1,2 +0,0 @@
1
- #!/usr/bin/env node
2
- export {};
package/lib/cli-chain.js DELETED
@@ -1,9 +0,0 @@
1
- #!/usr/bin/env node
2
- "use strict";
3
- var __importDefault = (this && this.__importDefault) || function (mod) {
4
- return (mod && mod.__esModule) ? mod : { "default": mod };
5
- };
6
- Object.defineProperty(exports, "__esModule", { value: true });
7
- const agent_chain_1 = __importDefault(require("./core/agent-chain"));
8
- const agent = new agent_chain_1.default();
9
- agent.start();
@@ -1,23 +0,0 @@
1
- import { AgentOptions } from '../types/type';
2
- export default class McpChainAgent {
3
- private allTools;
4
- private encoder;
5
- private extraTools;
6
- private maxTokens;
7
- private executor?;
8
- private apiConfig;
9
- private maxIterations;
10
- private apiModel?;
11
- private memory?;
12
- private systemPrompt;
13
- private runningTokenCounter;
14
- private verbose;
15
- constructor(options?: AgentOptions);
16
- private initTools;
17
- private wrapHandler;
18
- init(): Promise<void>;
19
- chat(input: string): Promise<string>;
20
- private showLoading;
21
- start(): Promise<void>;
22
- private ensureApiConfig;
23
- }
@@ -1,246 +0,0 @@
1
- "use strict";
2
- var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
3
- if (k2 === undefined) k2 = k;
4
- var desc = Object.getOwnPropertyDescriptor(m, k);
5
- if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
6
- desc = { enumerable: true, get: function() { return m[k]; } };
7
- }
8
- Object.defineProperty(o, k2, desc);
9
- }) : (function(o, m, k, k2) {
10
- if (k2 === undefined) k2 = k;
11
- o[k2] = m[k];
12
- }));
13
- var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
14
- Object.defineProperty(o, "default", { enumerable: true, value: v });
15
- }) : function(o, v) {
16
- o["default"] = v;
17
- });
18
- var __importStar = (this && this.__importStar) || (function () {
19
- var ownKeys = function(o) {
20
- ownKeys = Object.getOwnPropertyNames || function (o) {
21
- var ar = [];
22
- for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
23
- return ar;
24
- };
25
- return ownKeys(o);
26
- };
27
- return function (mod) {
28
- if (mod && mod.__esModule) return mod;
29
- var result = {};
30
- if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
31
- __setModuleDefault(result, mod);
32
- return result;
33
- };
34
- })();
35
- var __importDefault = (this && this.__importDefault) || function (mod) {
36
- return (mod && mod.__esModule) ? mod : { "default": mod };
37
- };
38
- Object.defineProperty(exports, "__esModule", { value: true });
39
- const fs_1 = __importDefault(require("fs"));
40
- const js_tiktoken_1 = require("js-tiktoken");
41
- const agents_1 = require("langchain/agents");
42
- const memory_1 = require("langchain/memory");
43
- const readline = __importStar(require("readline"));
44
- const prompts_1 = require("@langchain/core/prompts");
45
- const tools_1 = require("@langchain/core/tools");
46
- const openai_1 = require("@langchain/openai");
47
- const config_1 = require("../config/config");
48
- const builtin_1 = require("../tools/builtin");
49
- class McpChainAgent {
50
- constructor(options) {
51
- this.allTools = [];
52
- this.encoder = (0, js_tiktoken_1.getEncoding)("cl100k_base");
53
- this.extraTools = [];
54
- this.runningTokenCounter = 0;
55
- this.extraTools = (options === null || options === void 0 ? void 0 : options.tools) || [];
56
- this.maxTokens = (options === null || options === void 0 ? void 0 : options.maxTokens) || 8000;
57
- this.apiConfig = options === null || options === void 0 ? void 0 : options.apiConfig;
58
- this.maxIterations = (options === null || options === void 0 ? void 0 : options.maxIterations) || 50;
59
- this.apiModel = options === null || options === void 0 ? void 0 : options.apiModel;
60
- this.verbose = (options === null || options === void 0 ? void 0 : options.verbose) || false;
61
- const baseSystemPrompt = `你是一个专业的代码架构师。
62
- 你的目标是理解并分析用户项目,请务必遵循以下工作流:
63
-
64
- ### 第一阶段:全景感知 (The "Where" Phase)
65
- 1. **必须首先调用 'get_directory_tree'**:获取项目完整文件列表,包括样式文件 (.less, .css) 和资源文件。
66
- 2. 结合目录结构,观察项目架构(如 Monorepo 结构或 src 布局)。
67
-
68
- ### 第二阶段:逻辑映射 (The "What" Phase)
69
- 1. **调用 'get_repo_map'**:针对代码文件提取导出定义,理解模块间的调用关系。
70
- 2. 如果需要查看具体的样式定义,直接使用 'read_text_file' 读取 .less 或 .css 文件。
71
-
72
- ### 核心原则:
73
- - 不要猜测文件是否存在,先看目录树。
74
- - 优先查看 Skeleton(骨架),只有需要修复逻辑时才读取完整 Text(全文)。
75
- - 始终以中文回答思考过程。`;
76
- this.systemPrompt = (options === null || options === void 0 ? void 0 : options.extraSystemPrompt)
77
- ? `${baseSystemPrompt}\n\n[额外指令]:\n${JSON.stringify(options.extraSystemPrompt)}`
78
- : baseSystemPrompt;
79
- this.initTools(options);
80
- }
81
- initTools(options) {
82
- const allTools = [...(0, builtin_1.createDefaultBuiltinTools)({ options: { ...options, ...this } }), ...this.extraTools];
83
- this.allTools = allTools.map((t) => ({
84
- ...t,
85
- _handler: this.wrapHandler(t.function.name, t._handler),
86
- }));
87
- }
88
- wrapHandler(name, handler) {
89
- return async (args) => {
90
- console.log(`\n [工具调用]: ${name}`);
91
- const result = await handler(args);
92
- const content = typeof result === "string" ? result : JSON.stringify(result);
93
- this.runningTokenCounter += this.encoder.encode(content).length;
94
- return content;
95
- };
96
- }
97
- async init() {
98
- if (this.executor)
99
- return;
100
- let model;
101
- if (this.apiModel) {
102
- model = this.apiModel;
103
- }
104
- else {
105
- const apiConfig = await this.ensureApiConfig();
106
- model = new openai_1.ChatOpenAI({
107
- configuration: { baseURL: apiConfig.baseURL, apiKey: apiConfig.apiKey },
108
- modelName: apiConfig.model,
109
- temperature: 0,
110
- });
111
- }
112
- // 1. 初始化 SummaryBufferMemory
113
- // maxTokenLimit 决定了当对话历史超过多少 Token 时触发“自动总结”
114
- this.memory = new memory_1.ConversationSummaryBufferMemory({
115
- llm: model,
116
- maxTokenLimit: this.maxTokens,
117
- memoryKey: "chat_history",
118
- returnMessages: true,
119
- // 必须添加下面这两行显式声明:
120
- inputKey: "input", // 对应 invoke 里的 input 字段
121
- outputKey: "output", // 对应 Agent 输出的字段
122
- chatHistory: new memory_1.ChatMessageHistory(), // 👈 显式指定 history 实例
123
- });
124
- const langchainTools = this.allTools.map(t => new tools_1.DynamicStructuredTool({
125
- name: t.function.name,
126
- description: t.function.description || "",
127
- schema: (t.function.parameters),
128
- func: async (args) => await t._handler(args),
129
- }));
130
- const prompt = prompts_1.PromptTemplate.fromTemplate(`{system_prompt}
131
-
132
- ### 🛠 可用工具:
133
- {tools}
134
-
135
- ### 🛠 工具名称:
136
- [{tool_names}]
137
-
138
- ### 📝 历史记录:
139
- {chat_history}
140
-
141
- ### ⚠️ 回复规范(严格遵守):
142
- 1. 首先输出 **Thought:**,用中文详细说明你的分析思路。
143
- 2. 接着输出一个 **JSON Action 代码块**。
144
-
145
- 示例格式:
146
- Thought: 正在查看目录。
147
- \`\`\`json
148
- {{
149
- "action": "directory_tree",
150
- "action_input": {{}}
151
- }}
152
- \`\`\`
153
-
154
- Begin!
155
- Question: {input}
156
- {agent_scratchpad}`); // 👈 强制以 Thought: 开头,解决断更问题
157
- const agent = await (0, agents_1.createStructuredChatAgent)({ llm: model, tools: langchainTools, prompt });
158
- this.executor = new agents_1.AgentExecutor({
159
- agent,
160
- tools: langchainTools,
161
- memory: this.memory, // 挂载内存模块
162
- verbose: this.verbose,
163
- maxIterations: this.maxIterations,
164
- handleParsingErrors: (e) => {
165
- // 简化报错,不要再给 AI 错误的 JSON 示例
166
- return `格式不正确。请确保你输出了一个正确的 Markdown JSON 代码块,例如:\n\`\`\`json\n{ "action": "...", "action_input": { ... } }\n\`\`\``;
167
- },
168
- });
169
- }
170
- async chat(input) {
171
- if (!this.executor)
172
- await this.init();
173
- this.runningTokenCounter = this.encoder.encode(input).length;
174
- const stopLoading = this.showLoading("🤖 Agent 正在思考并管理上下文...");
175
- try {
176
- // 执行请求,AgentExecutor 会自动:
177
- // 1. 从 memory 加载历史 (chat_history)
178
- // 2. 将这次对话的结果 saveContext 到 memory
179
- const response = await this.executor.invoke({
180
- input: input,
181
- system_prompt: this.systemPrompt,
182
- }, {
183
- callbacks: [{
184
- handleAgentAction: (action) => {
185
- const rawLog = action.log || "";
186
- // 兼容 Thought: Thought: [内容]
187
- let thought = rawLog.split(/```json|\{/)[0]
188
- .replace(/Thought:/gi, "") // 全局替换掉所有 Thought: 标签
189
- .trim();
190
- if (thought) {
191
- console.log(`\n💭 [思考]: ${thought}`);
192
- }
193
- }
194
- }]
195
- });
196
- return typeof response.output === 'string' ? response.output : JSON.stringify(response.output);
197
- }
198
- finally {
199
- stopLoading();
200
- }
201
- }
202
- showLoading(text) {
203
- const chars = ['⠋', '⠙', '⠹', '⠸', '⠼', '⠴', '⠦', '⠧', '⠇', '⠏'];
204
- let i = 0;
205
- const timer = setInterval(() => {
206
- process.stdout.write(`\r${chars[i]} ${text}`);
207
- i = (i + 1) % chars.length;
208
- }, 80);
209
- return () => { clearInterval(timer); process.stdout.write('\r\x1b[K'); };
210
- }
211
- async start() {
212
- await this.init();
213
- const rl = readline.createInterface({ input: process.stdin, output: process.stdout });
214
- console.log(`\n🚀 AI 助手 (Summary+Window 模式) 已启动`);
215
- const chatLoop = () => {
216
- rl.question("\n👤 你: ", async (input) => {
217
- if (!input.trim())
218
- return chatLoop();
219
- if (input.toLowerCase() === "exit")
220
- process.exit(0);
221
- try {
222
- const res = await this.chat(input);
223
- console.log(`\n🤖 Agent: ${res}`);
224
- }
225
- catch (err) {
226
- console.error("\n❌ 系统错误:", err.message);
227
- }
228
- chatLoop();
229
- });
230
- };
231
- chatLoop();
232
- }
233
- async ensureApiConfig() {
234
- if (this.apiConfig)
235
- return this.apiConfig;
236
- if (fs_1.default.existsSync(config_1.CONFIG_FILE))
237
- return JSON.parse(fs_1.default.readFileSync(config_1.CONFIG_FILE, "utf-8"));
238
- const rl = readline.createInterface({ input: process.stdin, output: process.stdout });
239
- const question = (q) => new Promise((res) => rl.question(q, res));
240
- const config = { baseURL: await question("? API Base URL: "), apiKey: await question("? API Key: "), model: await question("? Model Name: ") };
241
- fs_1.default.writeFileSync(config_1.CONFIG_FILE, JSON.stringify(config, null, 2));
242
- rl.close();
243
- return config;
244
- }
245
- }
246
- exports.default = McpChainAgent;
@@ -1,18 +0,0 @@
1
- import { BaseChatModel } from '@langchain/core/language_models/chat_models';
2
- import { AIMessage, MessageFieldWithRole } from '@langchain/core/messages';
3
- interface AgentChainModelImpl {
4
- generateResponse: (messages: MessageFieldWithRole[]) => Promise<string>;
5
- }
6
- export declare abstract class AgentChainModel extends BaseChatModel implements AgentChainModelImpl {
7
- bind(args: any): any;
8
- constructor(fields?: any);
9
- generateResponse(messages: MessageFieldWithRole[]): Promise<string>;
10
- _generate(messages: any): Promise<{
11
- generations: {
12
- text: string;
13
- message: AIMessage;
14
- }[];
15
- }>;
16
- _llmType(): string;
17
- }
18
- export {};
@@ -1,23 +0,0 @@
1
- "use strict";
2
- Object.defineProperty(exports, "__esModule", { value: true });
3
- exports.AgentChainModel = void 0;
4
- const chat_models_1 = require("@langchain/core/language_models/chat_models");
5
- const messages_1 = require("@langchain/core/messages");
6
- class AgentChainModel extends chat_models_1.BaseChatModel {
7
- bind(args) {
8
- // 逻辑上调用基类(即使基类在类型上说没有,运行时通常是有的)
9
- // 如果运行时也没有,这里就返回 this 本身
10
- // @ts-ignore
11
- return (super.bind ? super.bind(args) : this);
12
- }
13
- constructor(fields) { super(fields || {}); }
14
- async generateResponse(messages) {
15
- return '';
16
- }
17
- async _generate(messages) {
18
- let text = await this.generateResponse(messages);
19
- return { generations: [{ text, message: new messages_1.AIMessage(text) }] };
20
- }
21
- _llmType() { return "my_private_llm"; }
22
- }
23
- exports.AgentChainModel = AgentChainModel;