ai agent---mcp知识汇总

一.agent定义自己的工具,并绑定他,执行他。

js 复制代码
//定义大模型
const model = new ChatOpenAI({
  modelName: process.env.MODEL_NAME,
  apiKey: process.env.OPENAI_API_KEY,
  temperature: 0,
  configuration: {
    baseURL: process.env.OPENAI_BASE_URL
  }
})
//引入工具
const tools = [
  readFileTool,
  writeTool,
  executeCommandTool,
  listDirectoryTool
]
//大模型绑定工具
const modelWithTool = model.bindTools(tools);

const messages=[
    new SystemMessage(`
    你是一个项目管理助手,使用工具完成任务,。。。
    `),
    new HumanMessage('你帮我干个啥呢')
]

//绑定工具后的大模型执行message

const response = await modelWithTool.invoke(messages);

//看一下需要执行哪个工具

for(const toolCall of response.tool_calls){
      const foundTool = tools.find(t=>t.name===toolCall.name);
      if(foundTool){
       // 这是‌调用大语言模型(LLM)‌,对应Agent的「思考/决策」环节:
        const toolResult = await foundTool.invoke(toolCall.args);
        messages.push(new ToolMessage({
          content: toolResult,
          tool_call_id: toolCall.id
        }))
      }
    }
 
return messages[messages.length-1].content;

代码逻辑:

  1. 定义工具,使用import {tool } from '@langchain/core/tools';
  2. 定义大模型,import {ChatOpenAI} from '@langchain/openai';
  3. 大模型和工具相互绑定
  4. 执行invoke()执行提示
  5. 大模型通过response.tool_calls他需要的工具,工具执行invoke()获取结果。

定义工具的办法:

js 复制代码
import {tool } from '@langchain/core/tools';
import fs from 'node:fs/promises';//node之后的规范写法
import path from 'node:path';
import {spawn} from 'node:child_process';
import {z} from 'zod';

//1.读取文件
const readFileTool = tool(
  async({filePath})=>{
    const content = await fs.readFile(filePath, 'utf-8');
    return `文件内容: ${content}`
  },{
    name: 'read_file',
    description: '读取文件',
    schema: z.object({
      filePath: z.string().describe('文件路径')
    })
  
  }
)

二.利用MCP定义远程工具,然后使用他

服务端代码

js 复制代码
#!/usr/bin/env node

import { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js";
import { StdioServerTransport } from "@modelcontextprotocol/sdk/server/stdio.js";
import { z } from "zod";
import fs from "node:fs/promises";
import path from "path";
import { fileURLToPath } from "url";
import { resolve } from 'path';

const __filename = fileURLToPath(import.meta.url);
const __dirname = path.dirname(__filename);

const dataBase = {
  users: {
    '001': { id: '001', name: '张三', age: 30 },
    '002': { id: '002', name: '李四', age: 28 },
    '003': { id: '003', name: '王五', age: 35 },
    '004': { id: '004', name: '赵六', age: 32 },
    '005': { id: '005', name: '孙七', age: 29 },
    '006': { id: '006', name: '周八', age: 27 },
    '007': { id: '007', name: '吴九', age: 31 },
    '008': { id: '008', name: '郑十', age: 33 },
  }
};
// ✅ 1. 创建 MCP Server
const server = new McpServer({
  name: 'my-mcp-server',
  version: '1.0.0',
});

server.tool(
  "Query_user",
  "查询数据库中用户信息,输入用户ID,返回该用户的详细信息(id,name,age)",
  {
    userId: z.string().describe('用户 id,例如 "001"')
  },
  async ({ userId }) => {
    const user = dataBase.users[userId];
    if (!user) {
      // ✅ 严格标准格式:外层是对象,content是数组,元素必须是{type:"text", text:字符串}
      return {
        content: [
          {
            type: "text",
            text: `查询失败:用户 ${userId} 不存在`
          }
        ],
        // 可选:标记为错误结果,不影响返回格式
        isError: true
      };
    }
    return {
      content: [
        {
          type: "text",
          text: `查询成功,用户信息如下:\n${JSON.stringify(user, null, 2)}`
        }
      ]
    };
  }
);

// ✅ 2. 注册 Resource(重点)
server.resource(
  "使用指南", // resource name
  "docs://guide", // 这个是读取资源的名字,在client的时候使用
  async (uri) => {
    const filePath = resolve(__dirname, 'docs', 'guide.md');// 读取真实的本地guide.md文件(路径:当前脚本目录下的docs/guide.md)
    let content = '';
    try {
      content = await fs.readFile(filePath, 'utf-8');
    } catch (err) {
      content = err.message;
    }
    
    return {
      contents: [
        {
          uri: uri.href,
          mimeType: "text/plain",
          text: content
        }
      ]
    };
  }
);



// ✅ 4. 启动 Server
const transport = new StdioServerTransport();
await server.connect(transport);

console.error("✅ MCP Resource Server is running...");

客户端代码

js 复制代码
import 'dotenv/config'
import { MultiServerMCPClient } from'@langchain/mcp-adapters';
import { ChatOpenAI } from'@langchain/openai';
import chalk from'chalk';
import { HumanMessage, ToolMessage } from'@langchain/core/messages'

const model = new ChatOpenAI({
  modelName:"qwen-plus",
  apiKey: process.env.OPENAI_API_KEY,
  configuration: {
  baseURL: process.env.OPENAI_BASE_URL,
}
});

const mcpClient = new MultiServerMCPClient({
    "my-mcp-server": {
      command: "node",
      args: ["src/mcp-test/my-mcp-server.mjs"],
      cwd: process.cwd(), // 强制用当前终端的工作目录作为基准,避免路径错位
      // 关键新增:显式指定stdio传输协议
      transport: "stdio"
    }

});

const tools = await mcpClient.getTools();
const modelWithTools = model.bindTools(tools);

async function runAgentWithTools(query,maxIterations = 30) {
  const messages = [
    new HumanMessage(query)
  ]; 
  
  for (let i= 0; i < maxIterations; i++) {
    console.log(chalk.bgGreen(`正在等待 AI思考...`));
    const response = await modelWithTools.invoke(messages);
    messages.push(response);
    
    // 检查是否有工具调用
  if (!response.tool_calls || response.tool_calls.length === 0) {
    console.log(` AI 最终回复:${response.content}`); 
    return response.content;
  }
  
  
  console.log(chalk.bgBlue( `检测到 ${response.tool_calls.length} 个工具调用~`));
  
  // 执行工具调用
  for (const toolCall of response.tool_calls) {
    const foundTool = tools.find(t => t.name ===toolCall.name);
    
    if (foundTool) {
       const toolResult = await foundTool.invoke(toolCall.args);
      messages.push(new ToolMessage({
        content: toolResult,
        tool_call_id: toolCall.id,
      }));
    }else{
      console.log(chalk.bgRed(`找不到名为 ${toolCall.name} 的工具`));
      return `找不到名为 ${toolCall.name} 的工具`;
    }
  }
}
  return messages[messages.length - 1].content;
}   
console.log( 12121212);
//await runAgentWithTools("查一下用户 002 的信息?");
//const res = await mcpClient.listResources("my-mcp-server");
const res = await mcpClient.readResource(
  "my-mcp-server",
  "docs://guide"//服务端有很多个读取静态资源的代码,你要说清楚是哪个资源,我这里用的是guide.md文件,如果不写,client就不知道你要读取的是谁。所以在教程里面不写是不行的
);

console.log(res)

await mcpClient.close();

你自己的本地的tool,就用model.bindTools绑定就好了,现在如果是远程的tools呢?就要用到MCP了,MCP是什么就参考这个文章:cloud.tencent.com/developer/a...

他就是个协议,很像https,在互联网上的tools都可以通过MCP引进来,自己用。

1.静态资源获取

在上面代码里面

js 复制代码
server.resource(
  "使用指南", // resource name
  "docs://guide", // 这个是读取资源的名字,在client的时候使用
  async (uri) => {
    const filePath = resolve(__dirname, 'docs', 'guide.md');// 读取真实的本地guide.md文件(路径:当前脚本目录下的docs/guide.md)
    let content = '';
    try {
      content = await fs.readFile(filePath, 'utf-8');
    } catch (err) {
      content = err.message;
    }
    
    return {
      contents: [
        {
          uri: uri.href,
          mimeType: "text/plain",
          text: content
        }
      ]
    };
  }
);

是获取静态资源的,前端调用的时候,就需要指定资源的名字

如果不指定,就不知道你想要的获取的是哪个静态资源了。

2.工具使用

server定义了工具,还命名了工具名,但是在使用的时候,却没有指定工具名,那大模型具体使用哪个?

回复如下:

言外之意就是,工具是我开发的,工具的description很重要,大模型就是靠着description来决定到底要用哪个工具,开发者不需要手动定义。

相关推荐
计算机魔术师1 小时前
AI 写了一半代码,谁来背锅?Anthropic 的安全重构笔记
前端
明月_清风2 小时前
vLLM 深度实战:2026 年生产级 LLM 推理引擎完全指南
前端·后端·ai编程
风月说与山鬼2 小时前
七、uni-app页面与组件生命周期
前端·uni-app
绿岛之北2 小时前
Electron 安全第三章:URL 加载与 WebView
前端·electron
sunoo-2292 小时前
C 语言文件 IO 全攻略:从基础函数到实战踩坑(BMP 读取 + 词典查询)
linux·c语言·前端·笔记·vscode·学习
Shinner欣儿2 小时前
React18 并发渲染小记
前端
用户921080262862 小时前
AI 对话里的消息时间线分页:上滑加载更多历史记忆的实现与坑点
前端
PedroQue992 小时前
Vue-Router 2.4.0 新增可控重定向功能
前端·uni-app
阿萨德528号2 小时前
npm 包发布实战指南:从零发布、更新迭代到版本治理
前端·npm·策略模式