@saber2pr/ai-agent 0.0.65 → 0.0.67

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.
@@ -185,7 +185,10 @@ class McpGraphAgent {
185
185
  if (this.options.filterTools) {
186
186
  allToolInfos = allToolInfos.filter(this.options.filterTools);
187
187
  }
188
- this.langchainTools = allToolInfos.map((t) => (0, convertToLangChainTool_1.convertToLangChainTool)(t, { allTools: allToolInfos }));
188
+ this.langchainTools = allToolInfos.map((t) => (0, convertToLangChainTool_1.convertToLangChainTool)(t, {
189
+ allTools: allToolInfos,
190
+ agentOptions: this.options,
191
+ }));
189
192
  this.toolNode = new prebuilt_1.ToolNode(this.langchainTools);
190
193
  return {
191
194
  builtinToolInfos,
@@ -1,4 +1,4 @@
1
- import { AgentOptions, ToolInfo } from '../types/type';
1
+ import { AgentOptions, ToolInfo } from "../types/type";
2
2
  export interface BuiltinToolsContext {
3
3
  options?: AgentOptions;
4
4
  }
@@ -2,13 +2,15 @@
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
3
  exports.createDefaultBuiltinTools = createDefaultBuiltinTools;
4
4
  const filesystem_1 = require("./filesystem");
5
- const get_all_tools_schema_1 = require("./loader/get_all_tools_schema");
6
5
  const ts_lsp_1 = require("./ts-lsp");
6
+ const get_all_tools_schema_1 = require("./loader/get_all_tools_schema");
7
+ const batch_run_tools_1 = require("./loader/batch_run_tools");
7
8
  function createDefaultBuiltinTools(context) {
8
9
  const { options } = context;
9
10
  return [
10
11
  ...(0, ts_lsp_1.getTsLspTools)(options?.targetDir || process.cwd()),
11
12
  ...(0, filesystem_1.getFilesystemTools)(options?.targetDir || process.cwd()),
12
13
  get_all_tools_schema_1.getAllToolsSchema,
14
+ batch_run_tools_1.batchRunTools,
13
15
  ];
14
16
  }
@@ -0,0 +1,2 @@
1
+ import { ToolInfo } from '../../types/type';
2
+ export declare const batchRunTools: ToolInfo;
@@ -0,0 +1,56 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.batchRunTools = void 0;
4
+ const zod_1 = require("zod");
5
+ const createTool_1 = require("../../utils/createTool");
6
+ const kit_1 = require("../../utils/kit");
7
+ exports.batchRunTools = (0, createTool_1.createTool)({
8
+ name: 'batch_run_tools',
9
+ // 使用更具指令性的英文描述
10
+ description: `Execute multiple tools in parallel. Use this for independent tasks such as reading multiple files, applying unrelated edits to different files, or fetching diagnostics simultaneously. CRITICAL: DO NOT apply multiple edits to the same file within a single batch to avoid write conflicts.`,
11
+ parameters: zod_1.z.object({
12
+ actions: zod_1.z.array(zod_1.z.object({
13
+ // 字段名和描述对齐 AI 习惯
14
+ tool_name: zod_1.z.string().describe('The name of the tool to execute.'),
15
+ args: zod_1.z.any().describe('The JSON object containing arguments for the specific tool.')
16
+ })).describe('A list of tool-calling actions to be executed concurrently.')
17
+ }),
18
+ handler: async ({ actions }, context) => {
19
+ // 1. 获取所有可用工具的映射
20
+ const toolMap = (0, kit_1.getArray)(context?.allTools).reduce((acc, tool) => ({ ...acc, [tool.function.name]: tool }), {});
21
+ if (Object.keys(toolMap).length === 0) {
22
+ return 'Error: Failed to retrieve tool execution context.';
23
+ }
24
+ // 2. 并行执行所有 Action
25
+ const results = await Promise.all(actions.map(async (action) => {
26
+ const { tool_name, args } = action;
27
+ const tool = toolMap[tool_name];
28
+ if (!tool) {
29
+ return {
30
+ tool_name,
31
+ status: 'error',
32
+ output: `Tool not found: ${tool_name}`
33
+ };
34
+ }
35
+ try {
36
+ // 特别注意:这里调用的是 _handler,请确保参数 args 的结构与子工具 Zod 定义一致
37
+ const output = await tool._handler(args, context);
38
+ return {
39
+ tool_name,
40
+ status: 'success',
41
+ output
42
+ };
43
+ }
44
+ catch (error) {
45
+ return {
46
+ tool_name,
47
+ status: 'error',
48
+ output: error?.message || String(error)
49
+ };
50
+ }
51
+ }));
52
+ // 3. 格式化输出结果
53
+ // 虽然参数改成了英文,但返回给 AI 的结果标题可以保留清晰的结构
54
+ return results.map(res => `### Tool: ${res.tool_name} (${res.status})\n${res.output}\n---`).join('\n');
55
+ }
56
+ });
@@ -9,25 +9,36 @@ const zod_to_json_schema_1 = __importDefault(require("zod-to-json-schema"));
9
9
  const createTool_1 = require("../../utils/createTool");
10
10
  const generateToolMarkdown_1 = require("../../utils/generateToolMarkdown");
11
11
  const kit_1 = require("../../utils/kit");
12
+ const getSystemPromptTemplate_1 = require("../../utils/getSystemPromptTemplate");
12
13
  exports.getAllToolsSchema = (0, createTool_1.createTool)({
13
- name: 'get_all_tools_schema',
14
+ name: "get_all_tools_schema",
14
15
  description: 'Use this tool when you encounter a "tool not found" error or are unsure about the parameter schema. It retrieves the full definitions and JSON schemas for all available tools in the current environment.',
15
16
  parameters: zod_1.z.object({
16
- toolName: zod_1.z.string().optional().describe('Optional: Specify a tool name to get its detailed schema. If omitted, returns all available tools.')
17
+ toolName: zod_1.z
18
+ .string()
19
+ .optional()
20
+ .describe("Optional: Specify a tool name to get its detailed schema. If omitted, returns all available tools."),
17
21
  }),
18
22
  // 增加第二个参数 context
19
23
  handler: async ({ toolName }, context) => {
20
24
  // 这里的 context.allTools 是在运行时从 Agent 实例传入的
21
25
  const availableTools = (0, kit_1.getArray)(context?.allTools);
22
26
  const targetTools = toolName
23
- ? availableTools.filter(t => t.function.name === toolName)
27
+ ? availableTools.filter((t) => t.function.name === toolName)
24
28
  : availableTools;
25
- return (0, generateToolMarkdown_1.generateToolMarkdown)(targetTools.map(item => ({
29
+ let remainPrompt = "";
30
+ if (context?.agentOptions) {
31
+ // 如果AI忘记了工具用法,说明出现了记忆模糊,这里把系统提示词再补充上,加强记忆
32
+ const systemPrompt = (0, getSystemPromptTemplate_1.getSystemPromptTemplate)(context.agentOptions.targetDir);
33
+ remainPrompt = `${systemPrompt || ""}\n${context?.agentOptions?.extraSystemPrompt || ""}`;
34
+ }
35
+ const toolsMarkdown = (0, generateToolMarkdown_1.generateToolMarkdown)(targetTools.map((item) => ({
26
36
  ...item,
27
37
  function: {
28
38
  ...item.function,
29
- parameters: (0, zod_to_json_schema_1.default)(item.function.parameters)
30
- }
39
+ parameters: (0, zod_to_json_schema_1.default)(item.function.parameters),
40
+ },
31
41
  })));
32
- }
42
+ return `${remainPrompt}\n${toolsMarkdown}`;
43
+ },
33
44
  });
@@ -15,6 +15,7 @@ export interface ToolInfo {
15
15
  };
16
16
  _handler?: (args: any, context: {
17
17
  allTools: ToolInfo[];
18
+ agentOptions?: GraphAgentOptions;
18
19
  }) => Promise<any>;
19
20
  _client?: Client;
20
21
  _originalName?: string;
@@ -1,7 +1,8 @@
1
1
  import { DynamicStructuredTool } from '@langchain/core/tools';
2
- import { ToolInfo } from '../types/type';
2
+ import { GraphAgentOptions, ToolInfo } from '../types/type';
3
3
  export declare function convertToLangChainTool(info: ToolInfo, context: {
4
4
  allTools: ToolInfo[];
5
+ agentOptions: GraphAgentOptions;
5
6
  }): DynamicStructuredTool<import("zod").ZodObject<any, import("zod").UnknownKeysParam, import("zod").ZodTypeAny, {
6
7
  [x: string]: any;
7
8
  }, {
@@ -1,5 +1,5 @@
1
- import { z } from 'zod';
2
- import { ToolInfo } from '../types/type';
1
+ import { z } from "zod";
2
+ import { GraphAgentOptions, ToolInfo } from "../types/type";
3
3
  export interface CreateToolOptions {
4
4
  name: string;
5
5
  description: string;
@@ -9,6 +9,7 @@ export interface CreateToolOptions {
9
9
  parameters: z.ZodObject<any>;
10
10
  handler: (args: any, context: {
11
11
  allTools: ToolInfo[];
12
+ agentOptions?: GraphAgentOptions;
12
13
  }) => Promise<string>;
13
14
  }
14
15
  export declare function createTool(options: CreateToolOptions): ToolInfo;
@@ -3,7 +3,7 @@ Object.defineProperty(exports, "__esModule", { value: true });
3
3
  exports.createTool = createTool;
4
4
  function createTool(options) {
5
5
  return {
6
- type: 'function',
6
+ type: "function",
7
7
  function: {
8
8
  name: options.name,
9
9
  description: options.description,
@@ -12,7 +12,7 @@ function createTool(options) {
12
12
  _handler: async (input, context) => {
13
13
  // 兼容处理:如果 input 是字符串,尝试解析为 JSON 对象
14
14
  let args = input;
15
- if (typeof input === 'string') {
15
+ if (typeof input === "string") {
16
16
  try {
17
17
  args = JSON.parse(input);
18
18
  }
@@ -6,18 +6,42 @@ Object.defineProperty(exports, "__esModule", { value: true });
6
6
  exports.formatSchema = formatSchema;
7
7
  const zod_to_json_schema_1 = __importDefault(require("zod-to-json-schema"));
8
8
  function formatSchema(schema) {
9
- const res = (0, zod_to_json_schema_1.default)(schema);
10
- if (typeof res.properties === 'object') {
11
- const requiredKeys = res.required || [];
9
+ const jsonSchema = (0, zod_to_json_schema_1.default)(schema);
10
+ // 递归处理函数
11
+ const processProperties = (properties, required = [], level = 0) => {
12
+ if (!properties || typeof properties !== 'object')
13
+ return '';
14
+ const indent = ' '.repeat(level);
12
15
  const lines = [];
13
- for (const key in res.properties) {
14
- lines.push(` - ${key}: ${res.properties[key].type}${requiredKeys.includes(key) ? ' (required)' : ''}`);
16
+ for (const key in properties) {
17
+ const prop = properties[key];
18
+ const isRequired = required.includes(key);
19
+ // 1. 基础描述
20
+ let line = `${indent}- ${key}: ${prop.type || 'any'}${isRequired ? ' (required)' : ''}`;
21
+ if (prop.description)
22
+ line += ` - ${prop.description}`;
23
+ lines.push(line);
24
+ // 2. 递归处理数组 (Items)
25
+ if (prop.type === 'array' && prop.items) {
26
+ lines.push(`${indent} Items:`);
27
+ if (prop.items.type === 'object' && prop.items.properties) {
28
+ lines.push(processProperties(prop.items.properties, prop.items.required, level + 2));
29
+ }
30
+ else {
31
+ lines.push(`${indent} - Type: ${prop.items.type || 'any'}`);
32
+ }
33
+ }
34
+ // 3. 递归处理嵌套对象 (Object)
35
+ if (prop.type === 'object' && prop.properties) {
36
+ lines.push(processProperties(prop.properties, prop.required, level + 1));
37
+ }
15
38
  }
16
- if (lines.length > 0) {
17
- return lines.join('\n');
18
- }
19
- return 'No parameters';
39
+ return lines.join('\n');
40
+ };
41
+ if (jsonSchema.properties) {
42
+ const result = processProperties(jsonSchema.properties, jsonSchema.required, 2); // 保持你原来的缩进感
43
+ return result || 'No parameters';
20
44
  }
21
45
  const keys = Object.keys(schema.shape);
22
- return keys.join(', ');
46
+ return keys.length > 0 ? keys.join(', ') : 'No parameters';
23
47
  }
@@ -1,5 +1 @@
1
- /**
2
- * 将工具定义从 Zod Schema 转换为极简 Markdown 格式
3
- * 目的:显著节省 System Prompt 的 Token,同时保持 LLM 理解力
4
- */
5
1
  export declare function generateToolMarkdown(tools: any[]): string;
@@ -7,20 +7,41 @@ const kit_1 = require("./kit");
7
7
  * 将工具定义从 Zod Schema 转换为极简 Markdown 格式
8
8
  * 目的:显著节省 System Prompt 的 Token,同时保持 LLM 理解力
9
9
  */
10
+ /**
11
+ * 递归解析 JSON Schema 并生成简化的 Markdown
12
+ */
13
+ function parseSchemaRecursive(schema, indent = '') {
14
+ if (!schema)
15
+ return '';
16
+ let res = '';
17
+ // 1. 处理对象类型 (Object)
18
+ if (schema.type === 'object' && schema.properties) {
19
+ Object.entries(schema.properties).forEach(([key, val]) => {
20
+ const isReq = schema.required?.includes(key);
21
+ const type = val.type || (val.anyOf ? 'any' : 'unknown');
22
+ const desc = val.description ? `: ${val.description}` : '';
23
+ res += `${indent}- \`${key}\` (${type}${isReq ? ', required' : ''})${desc}\n`;
24
+ // 递归处理嵌套对象或数组
25
+ res += parseSchemaRecursive(val, indent + ' ');
26
+ });
27
+ }
28
+ // 2. 处理数组类型 (Array)
29
+ else if (schema.type === 'array' && schema.items) {
30
+ // 标注数组项的结构
31
+ res += `${indent} *Items:* \n${parseSchemaRecursive(schema.items, indent + ' ')}`;
32
+ }
33
+ return res;
34
+ }
10
35
  function generateToolMarkdown(tools) {
11
36
  let markdown = "## Tool Definitions\n\n";
12
37
  (0, kit_1.getArray)(tools).forEach((tool) => {
38
+ // 假设 cleanToolDefinition 返回的是符合 JSON Schema 规范的对象
13
39
  const { name, description, parameters } = (0, cleanToolDefinition_1.cleanToolDefinition)(tool);
14
40
  markdown += `- **${name}**: ${description}\n`;
15
- // 提取 Zod 参数
16
- const shape = parameters.properties || {};
17
- const requiredFields = parameters.required || [];
18
- Object.entries(shape).forEach(([paramName, schema]) => {
19
- const isRequired = requiredFields.includes(paramName);
20
- const type = schema.type.replace('Zod', '').toLowerCase();
21
- const desc = schema.description || schema.title || '';
22
- markdown += ` - \`${paramName}\` (${type}${isRequired ? ', required' : ''})${desc ? `: ${desc}` : ''}\n`;
23
- });
41
+ // 从根部开始递归解析参数
42
+ if (parameters && parameters.type === 'object') {
43
+ markdown += parseSchemaRecursive(parameters, ' ');
44
+ }
24
45
  markdown += "\n";
25
46
  });
26
47
  return markdown;
@@ -4,15 +4,24 @@ exports.getSystemPromptTemplate = void 0;
4
4
  const getSystemPromptTemplate = (targetDir) => {
5
5
  return `You are an expert software engineer. Working directory: ${targetDir}.
6
6
 
7
- # 🧠 Mandatory Thinking Process
7
+ # Mandatory Thinking Process
8
8
  Before providing any output or calling a tool, you **MUST** conduct a deep logical analysis. Wrap your thought process within <think> tags.
9
9
 
10
- # 🛠️ Tool Call Specifications
10
+ # Tool Call Specifications
11
11
  1. **Pure JSON Arguments**: Arguments must be a valid JSON object. NEVER wrap the entire JSON object in a string or quotes.
12
12
  2. **No Double Escaping**: Do not double-escape characters within the JSON.
13
13
  3. **No Idle Operations**: If the task is complete or no tool is needed, DO NOT output any "Action" structure. Never use "None", "null", or empty strings as a tool name.
14
+ 4. **Important Note**: For optimal efficiency, when performing multiple operations, use batch_run_tools to invoke all relevant tools in parallel, rather than sequentially. Prioritize parallel tool invocation whenever possible. For example, when reading three files, run three tool invocations in parallel to read all three files into the context simultaneously. When running multiple read-only commands (such as read_file_range, grep_search, or read_text_file), always run all commands in parallel. Use parallel tool invocations whenever possible, rather than running too many tools sequentially.
14
15
 
15
- # 🎯 Core Instructions
16
+ # Researching Unfamiliar Symbols (Must Follow)
17
+ 1. **No Guesswork**: When encountering any API, function, class, variable, constant, or type that is not defined within the current context, you are strictly prohibited from writing code based on intuition or assumptions.
18
+ 2. **Proactive Traceability**: You must initiate the "Search-Read-Understand" cycle immediately:
19
+ 2.1 **Search**: Use grep_search to locate relevant definitions and find the Top 3 typical usage examples within the project.
20
+ 2.2 **Read**: Use batch_run_tools to concurrently read both the definition files and the identified usage examples.
21
+ 2.3 **Analyze**: Conduct a deep analysis of the parameter structures, implementation logic, invocation patterns, and error-handling strategies.
22
+ 3. **Code Consistency**: When implementing code, you must strictly replicate the established best practices and patterns found within the project.
23
+
24
+ # Core Instructions
16
25
  1. **Termination Criterion**: Once you have read the requested files, answered the questions, or completed the code implementation, you must provide the final response immediately.
17
26
  2. **Response Format**: Upon task completion, start your summary with "Final Answer:". No further tool calls should be made after this point.
18
27
  `.trim();
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@saber2pr/ai-agent",
3
- "version": "0.0.65",
3
+ "version": "0.0.67",
4
4
  "description": "AI Assistant CLI.",
5
5
  "author": "saber2pr",
6
6
  "license": "ISC",