前端AI探索之大模型变智能体

在使用语言大模型时,AI只能通过文字告诉你怎么做,却不能帮你执行,例如生成的代码或文档,你只能手动复制到需要用的地方,粘贴格式还可能错乱。

通过使用 MCPLangChain ,我们可以给大模型赋能,让大模型拥有实操的能力,不只是纸上谈兵,这种有实操能力的大模型,我们也称它为智能体。

作为一名前端,本篇章就将使用 Nodejs 通过 MCP 实现一个 "会上网" 的智能体,再通过 LangChain 实现一个会 "写文档" 的AI应用。

概念

首先介绍一下 MCPLangChain 是什么:

MCP(Model Context Protocol,模型上下文协议)是由 Anthropic 公司于 2024 年 11 月提出的开放标准,旨在标准化大型语言模型(LLM)与外部数据源和工具之间的通信。它类似于 AI 世界的"USB-C 接口",为 AI 应用提供了一种标准化的方式,连接到不同的数据源和工具,例如文件系统、数据库、API 等。

LangChain 是一个用于构建基于大语言模型(LLM)应用的框架,旨在帮助开发者更高效地将 LLM(如 OpenAI 的 GPT、Hugging Face 的模型等)与外部工具、数据源和功能集成。它的核心思想是通过"链式调用"(Chains)将多个模块组合起来,实现复杂的任务。

区别

功能

MCP(Model Context Protocol)

  • 目标:为 LLM 提供一种标准化的方式,动态发现和调用外部工具、数据源或 API。

  • 特点

    • 标准化协议:MCP 是一个开放协议,定义了 LLM 与外部服务之间的通信规范。
    • 动态发现:LLM 可以动态发现可用的工具和数据源,无需预先配置。
    • 双向通信:支持 LLM 主动调用工具,也支持工具向 LLM 推送数据。
    • 安全与隔离:敏感数据(如 API Key)由用户管理,确保安全性。
    • 轻量级:MCP 服务器是轻量级程序,易于部署和集成。
  • 适用场景:适合需要标准化、模块化和安全性的场景,尤其是本地优先设计。

LangChain 的 Function Calling

  • 目标:通过函数调用(Function Calling)机制,让 LLM 能够与外部工具或 API 交互。

  • 特点

    • 框架集成:LangChain 是一个框架,提供了 Function Calling 的实现,开发者可以自定义函数。
    • 预定义函数:开发者需要预先定义函数,LLM 通过提示词(prompt)或工具描述调用这些函数。
    • 灵活性:支持多种工具和 API 的集成,但需要开发者手动配置。
    • 生态系统:LangChain 提供了丰富的工具链(如检索、记忆、代理等),适合构建复杂的 AI 应用。
  • 适用场景:适合需要灵活性和复杂功能的场景,尤其是构建端到端的 AI 应用。

设计理念

MCP

  • 协议优先:MCP 是一个协议,定义了 LLM 与外部服务之间的通信规范,类似于 USB-C 的标准化接口。
  • 动态性:MCP 强调动态发现和调用,LLM 无需预先知道工具的具体细节。
  • 本地优先:MCP 的设计适合本地部署,确保数据安全和隐私。

LangChain

  • 框架优先:LangChain 是一个框架,提供了 Function Calling 的实现,开发者需要在框架内定义和配置工具。
  • 预定义性:LangChain 的 Function Calling 需要开发者预先定义函数和工具,LLM 通过提示词调用这些工具。
  • 灵活性:LangChain 提供了丰富的功能和工具链,适合构建复杂的 AI 应用。

实现方式

MCP

  • 协议实现:MCP 通过 JSON-RPC 2.0 进行通信,支持标准输入输出(stdio)和基于 SSE 的 HTTP 通信。
  • 轻量级服务器:MCP 服务器是轻量级程序,负责暴露特定功能或数据源。
  • 动态发现:LLM 可以动态发现可用的工具和数据源,无需预先配置。

LangChain

  • 框架实现:LangChain 通过 Python 或 JavaScript 实现 Function Calling,开发者需要定义函数并配置工具。
  • 工具链集成:LangChain 提供了丰富的工具链(如检索、记忆、代理等),开发者可以灵活组合这些工具。
  • 预定义函数:开发者需要预先定义函数,LLM 通过提示词调用这些函数。

适用场景

MCP

  • 标准化集成:适合需要标准化、模块化和安全性的场景,尤其是本地优先设计。
  • 动态调用:适合需要动态发现和调用工具的场景,例如 IDE 插件、客户支持系统等。
  • 轻量级部署:适合需要轻量级部署的场景,例如本地文件系统、数据库等。

LangChain

  • 复杂应用:适合需要构建复杂 AI 应用的场景,例如聊天机器人、知识库系统等。
  • 灵活性需求:适合需要灵活性和自定义功能的场景,例如多工具链集成、记忆机制等。
  • 端到端开发:适合需要端到端开发的场景,例如从数据检索到生成完整响应的流程。

总结

特性 MCP LangChain Function Calling
设计目标 标准化 LLM 与外部服务的交互 提供灵活的函数调用机制
动态性 支持动态发现和调用工具 需要预先定义函数
安全性 强调数据安全和隔离 依赖开发者的配置
实现方式 基于 JSON-RPC 2.0 的协议 基于框架的函数调用实现
适用场景 标准化、模块化、本地优先设计 复杂 AI 应用、端到端开发

实践

下面就开始进入实战

MCP Puppeteer

使用该服务可以让大模型拥有启动无头浏览器的能力,能实现模拟网页操作、截图、爬虫等工作,对前端而言可应用到UI自动化测试。

本案例在Cline中演示使用效果,你也可以在支持MCP的Cursor,Winsurf等地方使用,或者提供API给自己的模型调用。

完整代码

github.com/AntonHu/AI-...

核心实现

安装工具库
sql 复制代码
pnpm add @modelcontextprotocol/sdk puppeteer
创建服务
vbscript 复制代码
// 创建 MCP Server 实例
const server = new Server(
  {
    name: "anton-puppeteer", // MCP Server 名称
    version: "0.1.0", // MCP Server 版本
  },
  {
    capabilities: {
      resources: {},
      tools: {},
    },
  }
);
定义工具
css 复制代码
import { Tool } from "@modelcontextprotocol/sdk/types.js";
​
// 定义工具列表
const TOOLS: Tool[] = [
  {
    name: "puppeteer_navigate",
    description: "导航到指定的 URL",
    inputSchema: {
      type: "object",
      properties: {
        url: { type: "string" },
      },
      required: ["url"],
    },
  },
  // ...
]
​
​
server.setRequestHandler(ListToolsRequestSchema, async () => ({
  // 列出所有可用工具
  tools: TOOLS,
}));
工具调用
typescript 复制代码
/**
 * 处理工具调用
 * @param {string} name 工具名称
 * @param {any} args 工具参数
 * @returns {Promise<CallToolResult>} 返回工具调用的结果
 */
async function handleToolCall(
  name: string,
  args: any
): Promise<CallToolResult> {
  const page = await ensureBrowser(); // 确保浏览器实例存在
​
  switch (name) {
    case "puppeteer_navigate":
      // 导航到指定的 URL
      await page.goto(args.url);
      return {
        content: [
          {
            type: "text",
            text: `Navigated to ${args.url}`,
          },
        ],
        isError: false,
      };
     // ...
  }
}
​
server.setRequestHandler(CallToolRequestSchema, async (request) =>
  // 处理工具调用
  handleToolCall(request.params.name, request.params.arguments ?? {})
);
启动服务
scss 复制代码
/**
 * 运行 MCP Server
 */
async function runServer() {
  const transport = new StdioServerTransport(); // 创建 StdioServerTransport 实例
  await server.connect(transport); // 连接到 transport
}
​
runServer().catch(console.error); // 运行 server,如果出错则打印错误信息

成品效果

与模型对话,要求其打开B站获取首屏的视频信息:

模型会自己使用puppeteer的可用工具,打开网页,分析网页的元素结构,并编写js脚本来获取我指定的信息,最终整理成 JSON:

LangChain 文件管家AI应用

让大模型具备本地读写的能力,例如通过对话让大模型访问你授权的目录,进行文件编辑。

完整代码

github.com/AntonHu/AI-...

核心实现

安装工具库
sql 复制代码
pnpm add @langchain/ollama @langchain/core
定义工具
javascript 复制代码
/**
 * 读取指定文件的内容
 * @param {string} filePath - 文件的路径
 * @returns {Promise<string>} - 文件内容
 */
class ReadFile extends Tool {
  name = "read_file";
  description = "读取指定路径的文件内容";
​
  async _call(input) {
    const { path: filePath } = JSON.parse(input);
    try {
      const absolutePath = path.resolve(filePath); // 将路径解析为绝对路径
      const content = await fs.promises.readFile(absolutePath, "utf-8");
      return content;
    } catch (error) {
      throw new Error(`读取文件失败: ${error.message}`);
    }
  }
}
​
// 创建工具实例
export default [
  new ReadFile()
];
设定模板
javascript 复制代码
import tools from "./tools.js";
​
export const functionCallTemplate = `你是一个智能助手,可以根据用户需求调用工具。以下是可用工具:
${tools.map((item) => `-${item.name}: ${item.description}`).join("\n")}
​
用户问题:{question}
请根据问题选择合适的工具并返回一个JSON数据格式的结果:
{ name: 工具名称, args: { 参数名: 参数值 } }
`;
集成工具
javascript 复制代码
import { ChatOllama } from "@langchain/ollama";
import { HumanMessage } from "@langchain/core/messages";
​
const model = new ChatOllama({
  model: "deepseek-coder-v2",
  temperature: 0.1,
  verbose: false,
});
​
// 绑定工具
const modelWithTools = model.bindTools(tools);
工具调用
javascript 复制代码
async function chatWithModel() {
  // ...
  // 调用模型
   const response = await modelWithTools.invoke([new HumanMessage(userInput)]);
   if (response && response.tool_calls && response.tool_calls.length > 0) {
      const toolCall = response.tool_calls[0];
      const toolName = toolCall.name;
      const toolInput = toolCall.args;
      const tool = tools.find((t) => t.name === toolName);
​
      if (tool) {
        const toolResult = await tool._call(JSON.stringify(toolInput));
        const response = await model.invoke([
          new HumanMessage(
            `根据用户提问 ${userInput},你使用工具 ${toolName} 获得了结果:${toolResult},请整理成易于理解的回答。`
          ),
        ]);
        console.log(response.content);
      } else {
        console.log(`未找到工具: ${toolName}`);
      }
  }
  // ...
}

成品效果

理解文件内容
获取项目结构

总结

到这你已经掌握如何用Node为大模型赋能,定制自己的AI智能体、AI应用了。有了这个能力之后,相信你的AI大有可为,有什么好的想法和整活,欢迎评论区讨论

相关推荐
冴羽7 分钟前
SvelteKit 最新中文文档教程(3)—— 数据加载
前端·javascript·svelte
百万蹄蹄向前冲23 分钟前
组建百万前端梦之队-计算机大学生竞赛发展蓝图
前端·vue.js·掘金社区
云隙阳光i37 分钟前
实现手机手势签字功能
前端·javascript·vue.js
imkaifan1 小时前
vue2升级Vue3--native、对inheritAttrs作用做以解释、声明的prop属性和未声明prop的属性
前端·vue.js·native修饰符·inheritattrs作用·声明的prop属性·未声明prop的属性
觉醒法师1 小时前
HarmonyOS NEXT - 电商App实例三( 网络请求axios)
前端·华为·typescript·axios·harmonyos·ark-ts
Danta2 小时前
HTTP协议版本演进:从HTTP/0.9到HTTP/3的高分面试回答
前端·网络协议·面试
柠檬树^-^2 小时前
app.config.globalProperties
前端·javascript·vue.js
太阳花ˉ2 小时前
react(一):特点-基本使用-JSX语法
前端·react.js
赵大仁2 小时前
深入解析 React Diff 算法:原理、优化与实践
前端·react.js·前端框架
1024小神2 小时前
vue/react/vite前端项目打包的时候加上时间最简单版本,防止后端扯皮
前端·vue.js·react.js