返回顶部
热门问答 更多热门问答
技术文章 更多技术文章

从零开始的 MCP 开发

[复制链接]
链载Ai 显示全部楼层 发表于 昨天 17:54 |阅读模式 打印 上一主题 下一主题

前言:我们迎来万能插头?

在 AI 提效上,我们小组的每个人都有自己的独特方式,作为一个沉醉在业务开发+业务样式改版的终端开发,再加上我的 CSS 功底基本上样式就是靠试,每次在 UI 还原部分都是很是痛苦。这样,在团队内部同学完成了 Done 插件转React 代码并完成 OneDay Web 端落地后,我就在想,是否可以在插件端实现一样的能力,就这样 MCP 的能力自然就进入我的视野了。

先看一下效果:

这个小玩具是通过 MCP 协议进行开发的,并且集成在了 OneDay插件中。这篇文章,主要记录了自己在开发 MCP 插件的过程中的学习路径,以及是如何从零用 AI 开发一个小插件的。最后,也是趁着业务大改版的机会,将这个插件结合在我的开发流程中。

MCP 协议简介:AI 的"万能插头"

2024 年 11 月,Anthropic 推出了 Model Context Protocol (MCP),这一开放协议旨在解决 LLM 与外部工具集成的标准化问题。MCP 提供了一种统一的方式,使 AI 模型能够与各种数据源和工具进行交互,被官方形象地称为 AI 应用的"USB-C 端口"。

MCP 的本质与价值

MCP的核心价值在于提供一种标准化的方式,让 AI 模型与外部世界进行交互。在 MCP 出现之前,开发者需要为每个 AI 集成创建定制化的解决方案,这导致了严重的碎片化问题。

MCP 解决了这些问题,它提供了以下关键价值:

  • 统一集成标准:一个协议对接所有集成,降低开发难度

  • 实时数据更新:支持动态数据交互而非静态连接

  • 自动工具发现:支持动态工具发现和上下文处理

  • 隐私保护:数据和工具不需上传远端,保护数据隐私

  • 开发效率:显著减少开发时间,提高系统可靠性

核心能力

根据 MCP 协议规范,服务器可以提供三种核心对象:

支持程度

目前 MCP 这一概念的火热也让众多 IDE 和框架积极投身在这一领域,其中Claude桌面应用和Continue提供了最全面的MCP支持,包括资源、提示模板和工具集成,使其能够深度整合本地工具和数据源。众多代码编辑器和IDE(如Cursor、Zed、WindsurfEditor和Theia IDE)通过MCP增强了开发工作流程,提供如智能代码生成、AI辅助编码等功能。

在官网的示例中(https://modelcontextprotocol.io/examples),可以发现,越来越多的公司、组织开始积极拥抱 MCP,目前通过 MCP 可以进行本地文件、云端文件的修改,Git 相关仓库的阅读与更改,基于Puppeteer 进行浏览器自动化和网页抓取,甚至通过EverArt的相关服务可以进行图像生成。

一些更抽象的 MCP 服务可以在这里看一看(https://github.com/punkpeye/awesome-mcp-servers)

真的是大一统么?

文档上说的很好,MCP 是AI 届的USB-C,使用了 MCP 就意味着你的协议可以在所有的 AI 应用上使用了。

但是,强如 USB-C 现在也没有办法做到真正的大一统,不同厂商之间还是存在着不同。

所以,“MCP 可能统一,但是 MCP统一不太可能”。

现在针对不同的 AI 终端每个 MCP 支持的能力也是不尽相同的,本文说的只是在 OneDay VSC 插件上的开发体验;



前置学习一下

看完前面的 MCP 具体协议相关的文档之后,理解能力比较强的老师可能已经知道 MCP 是在干啥了,像我这种 AI 知识早就还给 CV、ML 老师了的同学来说,还是不是很清楚 MCP 具体是咋被调用的。

为了搞清楚MCP 的运作方式,我准备学习一下开源的工具以及 SDK 是如何运作的,作为一个练习时长2 坤年的终端开发,我选择的开源仓库是 Roo 和 MCP Typescript 的 SDK。

如何使用MCP TS进行开发

在 MCP 官网上,赫然写着Building MCP with LLMs(https://modelcontextprotocol.io/tutorials/building-mcp-with-llms),但是本着尊重 AI 的劳动成果的原则还是要学习一下里面具体的内容的。

这部分不太详细展开,具体的 MCP 开发还是参考官网的文档好了。

Client

负责与 MCP 服务器建立连接并发送请求,主要的方法有:

  • connect(transport):连接到服务器

  • request(request, schema, options):发送请求并等待响应

  • close():关闭连接

import{Client}from"@modelcontextprotocol/sdk/client/index.js";

McpServer

提供一个高级 API 来创建 MCP 服务器,主要的方法有:

  • tool(name, schema, handler):注册一个工具

  • resource(name, template, handler):注册一个资源

  • connect(transport):连接到传输层

import{McpServer}from"@modelcontextprotocol/sdk/server/mcp.js";constserver=newMcpServer({name:"我的MCP服务",version:"1.0.0"});

Server

一个低级类,也是本文采用的一个类,低级开发用低级类(bushi

  • setRequestHandler(schema, handler):为特定请求类型设置处理程序

  • connect(transport):连接到传输层

import{Server}from"@modelcontextprotocol/sdk/server/index.js";

传输接口(Transport)

MCP 支持多种传输方式,用于与客户端通信,主要是通过:stdio 传输(命令行应用)和SSE 传输(Web服务器)。

import{StdioServerTransport}from"@modelcontextprotocol/sdk/server/stdio.js";
consttransport =newStdioServerTransport();awaitserver.connect(transport);
import{SSEServerTransport}from"@modelcontextprotocol/sdk/server/sse.js";importexpressfrom"express";
constapp =express();
app.get("/sse",async(req, res) => {consttransport =newSSEServerTransport("/messages", res);awaitserver.connect(transport);});
app.post("/messages",async(req, res) => {awaittransport.handlePostMessage(req, res);});
app.listen(3000);

具体的开发流程如下



Roo 如何调用MCP

Roo 是谁,Cline 优化版罢了

大体上了解了 MCP SDK中的使用方式,那么问题又来了: MCP 集成在客户端上,客户端是如何判断是否需要调用 MCP 以及使用哪个 MCP 的?

打开 Roo 的源码,AI 总结启动...



可以看出来主要流程有意图识别、工具识别、工具调用这三个主要的步骤。

意图识别

Roo Code使用大型语言模型(LLM)来理解用户的自然语言输入并识别用户的意图。当用户提出一个请求时,LLM会分析请求并决定使用哪些工具来完成任务。

系统提示构建:通过 generatePrompt 函数构建完整的系统提示,包括 MCP 服务器和工具信息。

这使 LLM 能够了解可用的 MCP 服务器及其功能。

这使LLM能够了解可用的MCP服务器及其功能// src/core/prompts/system.ts// 通过 generatePrompt 函数构建完整的系统提示,包括 MCP 服务器和工具信息。// 这使 LLM 能够了解可用的 MCP 服务器及其功能asyncfunctiongeneratePrompt(  context: vscode.ExtensionContext,  cwd:string,  supportsComputerUse:boolean,  mode: Mode,  mcpHub?: McpHub, // MCP 集线器实例,负责管理所有 MCP 服务器连接  diffStrategy?: DiffStrategy,  browserViewportSize?:string, // ... 其他参数)romise<string> { // ... 前面的代码  // 异步获取两个部分:模式部分和 MCP 服务器部分 const[modesSection, mcpServersSection] =awaitPromise.all([   getModesSection(context),   // 仅当当前模式包含 mcp 组时才加载 MCP 服务器部分    modeConfig.groups.some((groupEntry) =>getGroupName(groupEntry) ==="mcp")      ?getMcpServersSection(mcpHub, effectiveDiffStrategy, enableMcpServerCreation)      romise.resolve(""),  ])
// 构建完整的系统提示,包括多个部分 constbasePrompt =`${roleDefinition}
${getSharedToolUseSection()}
${getToolDescriptionsForMode( // 这里会包含 MCP 相关工具的描述 mode, cwd, supportsComputerUse, effectiveDiffStrategy, browserViewportSize, mcpHub, customModeConfigs, experiments,)}
${getToolUseGuidelinesSection()}
${mcpServersSection} // 这部分包含所有可用的 MCP 服务器及其工具信息
// ... 其他部分 `
returnbasePrompt}

MCP 服务器信息生成:getMcpServersSection 方法收集并格式化已连接的 MCP 服务器信息:提供服务器名称、可用工具及其参数架构,让 LLM 知道如何使用它们。

// src/core/prompts/sections/mcp-servers.tsexportasyncfunctiongetMcpServersSection(  mcpHub?: McpHub,  diffStrategy?: DiffStrategy,  enableMcpServerCreation?:boolean,)romise<string> { if(!mcpHub) {   return""  }
// 构建已连接服务器的信息字符串 constconnectedServers = mcpHub.getServers().length>0 ?`${mcpHub .getServers() .filter((server) => server.status ==="connected") // 只显示已连接的服务器 .map((server) => { // 为每个服务器生成其工具列表信息 consttools = server.tools ?.map((tool) => { // 为每个工具包含输入模式(如果有) constschemaStr = tool.inputSchema ?` Input Schema: ${JSON.stringify(tool.inputSchema,null,2).split("\n").join("\n ")}` :""
return`-${tool.name}{tool.description}\n${schemaStr}` }) .join("\n\n")
// ... 生成资源模板和直接资源信息 ... // 解析服务器配置以显示命令信息 constconfig =JSON.parse(server.config)
// 返回完整的服务器描述,包括工具、资源模板和直接资源 return( `##${server.name}(\`${config.command}${config.args ?`${config.args.join(" ")}`:""}\`)`+ (tools ?`\n\n### Available Tools\n${tools}`:"") + (templates ?`\n\n### Resource Templates\n${templates}`:"") + (resources ?`\n\n### Direct Resources\n${resources}`:"") ) }) .join("\n\n")}` :"(No MCP servers currently connected)"// 如果没有连接服务器,显示此消息
// ... 返回完整部分,包括 MCP 服务器介绍和创建指南 ...}

工具描述提供:getUseMcpToolDescription 函数定义了 MCP 工具的使用方法和参数格式。包含使用示例,帮助 LLM 生成正确格式的工具调用。

// src/core/prompts/tools/use-mcp-tool.tsexportfunctiongetUseMcpToolDescription(args: ToolArgs): string | undefined {  // 如果没有 MCP 集线器,不需要此工具描述 if(!args.mcpHub) {   returnundefined  }   // 返回标准化的工具描述,包括参数说明和使用示例 return`## use_mcp_toolDescription: Request to use a tool provided by a connected MCP server. Each MCP server can provide multiple tools with different capabilities. Tools have defined input schemas that specify required and optional parameters.Parameters:- server_name: (required) The name of the MCP server providing the tool- tool_name: (required) The name of the tool to execute- arguments: (required) A JSON object containing the tool's input parameters, following the tool's input schemaUsage:<use_mcp_tool><server_name>server name here</server_name><tool_name>tool name here</tool_name><arguments>{"param1":"value1","param2":"value2"}</arguments></use_mcp_tool>
Example: Requesting to use an MCP tool
<use_mcp_tool><server_name>weather-server</server_name><tool_name>get_forecast</tool_name><arguments>{"city":"San Francisco","days": 5}</arguments></use_mcp_tool>`}

工具识别&调用

首先通过 use_mcp_tool 工具来解析 LLM 返回的工具调用并验证参数。

//src/core/Cline.tsasyncpresentAssistantMessage(){//...前面的代码case"use_mcp_tool":{constserver_name:string|undefined=block.params.server_nameconsttool_name:string|undefined=block.params.tool_nameconstmcp_arguments:string|undefined=block.params.argumentstry{//处理部分工具调用-这是处理未完成的工具调用的机制if(block.partial){constpartialMessage=JSON.stringify({type:"use_mcp_tool",serverName:removeClosingTag("server_name",server_name),toolName:removeClosingTag("tool_name",tool_name),arguments:removeClosingTag("arguments",mcp_arguments),}satisfiesClineAskUseMcpServer)awaitthis.ask("use_mcp_server",partialMessage,block.partial).catch(()=>{})break}else{//验证必要参数是否存在if(!server_name){this.consecutiveMistakeCount++pushToolResult(awaitthis.sayAndCreateMissingParamError("use_mcp_tool","server_name"),)break}if(!tool_name){this.consecutiveMistakeCount++pushToolResult(awaitthis.sayAndCreateMissingParamError("use_mcp_tool","tool_name"),)break}//解析JSON参数(如果提供)letparsedArguments:Record<string,unknown>|undefinedif(mcp_arguments){try{parsedArguments=JSON.parse(mcp_arguments)}catch(error){//处理JSON解析错误this.consecutiveMistakeCount++awaitthis.say("error",`Rootriedtouse${tool_name}withaninvalidJSONargument.Retrying...`,)pushToolResult(formatResponse.toolError(formatResponse.invalidMcpToolArgumentError(server_name,tool_name),),)break}}

然后通过McpHub.callTool方法来实现 MCP 工具的调用。

//src/core/Cline.ts-继续上面的代码awaitthis.say("mcp_server_request_started")consttoolResult=awaitthis.providerRef.deref()?.getMcpHub()?.callTool(server_name,tool_name,parsedArguments)
// src/services/mcp/McpHub.tsasynccallTool( serverName:string, toolName:string,  toolArguments?:Record<string,unknown>,)romise<McpToolCallResponse> { // 查找对应的服务器连接 constconnection =this.connections.find((conn) =>conn.server.name=== serverName) if(!connection) {   thrownewError(     `No connection found for server{serverName}. Please make sure to use MCP servers available under 'Connected MCP Servers'.`,    )  } // 检查服务器是否被禁用 if(connection.server.disabled) {   thrownewError(`Server "${serverName}" is disabled and cannot be used`)  }
// 从服务器配置中获取超时设置 lettimeout:number try{ constparsedConfig =ServerConfigSchema.parse(JSON.parse(connection.server.config)) timeout = (parsedConfig.timeout??60) *1000// 默认 60 秒 }catch(error) { console.error("Failed to parse server config for timeout:", error) // 解析失败时使用默认值 timeout =60*1000 }
// 使用 MCP SDK 的 Client 接口发送请求 returnawaitconnection.client.request( { method:"tools/call", params: { name: toolName, arguments: toolArguments, }, }, CallToolResultSchema, // 用于验证响应的模式 { timeout, // 应用从配置获取的超时值 }, )}

callTool 里面最后的调用还是落在我们创建 Server 时使用的 connection。

connection.client.request({method:"tools/call",params:{name:toolName,arguments:toolArguments,},},CallToolResultSchema,{timeout,},)

问题的答案

讲到这里我们终于可以给之前疑问画上一个句号了,具体的 MCP被调用的链路如下:

1.初始化连接:

  • McpHub 实例化各种 McpConnection

  • 每个连接包含 Client 和 StdioClientTransport/SSEClientTransport

2.工具调用:

  • McpHub.callTool 找到合适的 McpConnection

  • 使用 connection.client.request 发送请求

  • 请求通过 transport 发送到 MCP 服务器

3.服务端处理:

  • McpServer 接收请求

  • 找到对应的工具处理函数

  • 验证参数并执行处理函数

  • 返回结果

4.结果处理:

  • 结果通过 transport 返回

  • Client 解析响应并将其返回给 McpHub

  • McpHub 处理结果并返回给调用者

接下来又到了 AI 画图时间,具体的关系如下:





MCP-Pixelator 设计

场景分析

回到当前的 MCP 场景,目前图生码的链路已经打通,现在需要解决的问题就很是清晰了,如何把图生码的结果应用在本地 IDE 上。

流程如下:

1.用户通过 OneDay VSC 等支持 MCP 的 AI 客户端上传 ZIP 文件

2.MCP 服务器解析 ZIP 文件,提取 AST 数据

3.服务器调用 AST 转码 API,将 AST 转换为 React 代码

4.根据用户选择,生成新项目或将组件添加到现有项目

5.返回生成的代码给用户

这一流程可以通过以下图表直观展示:



架构设计

基于 MCP 协议,我们的系统架构如下:



系统模块设计

McpPixelator 系统包含以下核心模块:

1.MCP 服务器模块:负责与 AI 客户端通信,处理请求和响应

2.工具注册模块:注册和管理 MCP 工具

3.文件处理模块:解析 ZIP 文件,提取 AST 数据

4.API 通信模块:与 AST 转码 API 进行交互

5.错误处理模块:处理各种异常情况

这些模块之间的关系如下图所示:



通过这样的系统设计,我们构建了一个基于 MCP 协议的、能够将设计稿 AST 转换为 React 代码的服务。接下来,SHOW ME THE CODE!

核心代码实现

代码核心实现大部分基于 AI 实现,MCP 插件通过合理的 Prompt 调试+拆分任务维度,很容易就可以实现了。

基本结构与初始化

McpPixelatorServer 类是Ï整个系统的核心,负责初始化 MCP 服务器、设置工具处理器、处理请求等。以下是其基本结构和初始化逻辑:

classMcpPixelatorServer{ privateserver:Server; privatezipHandler:ZipHandler; privateapiToken:string=""; privateapiEndpoint:string="fake"; privateuserId:string="MCP_PIXELATOR"+"_"+ process.env.USER_ID; privatefrom:string= process.env.FROM||"unknown"; 
constructor() { // 初始化token this.fetchToken() .then((token) =>{ this.apiToken= token; console.log("Token已更新"); }) .catch((error) =>{ console.error("初始化token失败:", error); });
// 初始化MCP服务器 this.server=newServer( { name:"mcp-pixelator", version:"0.1.0", }, { capabilities: { tools: {}, }, }, );
this.zipHandler=newZipHandler(); this.setupToolHandlers();
// 错误处理 this.server.onerror=(error:Error) =>console.error("[MCP Error]", error); process.on("SIGINT",async() => { awaitthis.server.close(); process.exit(0); }); } // 其他方法... }

在构造函数中,我们首先初始化 API Token,然后创建 MCP 服务器实例,设置基本配置和能力。接着初始化 ZIP 文件处理器,并设置工具处理器。最后,我们配置错误处理逻辑和进程退出处理。

工具注册与处理

MCP 协议的核心是工具(Tools)的注册和处理。以下是我们注册工具的代码:

privatesetupToolHandlers() { // 注册工具列表  this.server.setRequestHandler(ListToolsRequestSchema,async() => ({  tools: [    {    name:"process_done_zip_and_generate",    description:"读取 Done Zip文件并直接生成 React 代码",    inputSchema: {     type:"object",     properties: {      options: {       type:"object",       properties: {        type: {         type:"string",         enum: ["create","add"],         description:"生成代码的类型:create - 创建新项目,add - 添加到现有项目",         default:"create",         },        projectPath: {         type:"string",         description:"当 type 为 add 时,需要提供项目路径",         },        },       required: ["type"],       },      },     required: ["options"],     },    },   ],  })); // 处理工具调用请求  this.server.setRequestHandler(CallToolRequestSchema,async(request) => {  try{    console.log("收到工具调用请求:", {     工具名称: request.params.name,     参数: request.params.arguments,    }); 
if(request.params.name ==="process_done_zip_and_generate") { // 处理逻辑... }else{ thrownewMcpError( ErrorCode.MethodNotFound, `未知工具: ${request.params.name}`, ); } }catch(error) { // 错误处理... } });

这段代码首先注册了一个名为process_done_zip_and_generate的工具,用于读取 ZIP 文件并生成 React 代码。该工具接受一个options参数,包含type(创建新项目或添加到现有项目)和可选的projectPath(当 type 为 add 时的项目路径)。

然后,我们设置了处理工具调用的逻辑,根据工具名称执行相应的操作。如果请求的是未知工具,则抛出MethodNotFound错误。 

ZIP 文件处理与 AST 提取

当接收到工具调用请求后,我们需要处理 ZIP 文件并提取 AST 数据:

// 在 CallToolRequestSchema 处理函数中 if(request.params.name==="process_done_zip_and_generate") { // 获取参数 const{ options } = request.params.argumentsas{  options:CodeGenerationOptions;  }; 
// 打开文件选择器 constzipFilePath =awaitthis.zipHandler.selectFile();
console.log(`处理ZIP文件{zipFilePath}`);
// 读取ZIP文件内容 constcontents =awaitthis.zipHandler.readZipFile(zipFilePath);
// 提取AST数据 constastData =this.extractAstData(contents);
console.log("已提取AST数据,开始生成代码");
// 直接调用API生成代码 constresult =awaitthis.generateReactCode(astData, options);
// 处理结果... }

AST 数据提取的具体实现如下:

privateextractAstData(contents:ZipContents):AstData{ // 首先尝试找到 AST 文件 constastFile = contents.files.find(  (f: { name:string;type:string}) =>   (f.name==="ast.json"||     f.name==="ast.txt"||     f.name.endsWith(".ast")) &&    (f.type==="json"|| f.type==="text"),  ); 
if(!astFile ||typeofastFile.content!=="string") { thrownewError("未找到有效的 AST 文件"); }
try{ // 尝试解析 JSON constastContent =JSON.parse(astFile.content); return{ast: astContent }; }catch(e) { // 如果解析失败,直接使用原始字符串 console.log("AST 文件解析为 JSON 失败,使用原始字符串"); return{ast: astFile.content}; } }

这段代码首先在 ZIP 内容中查找 AST 文件(名为 "ast.json"、"ast.txt" 或以 ".ast" 结尾),然后尝试将其解析为 JSON 对象。如果解析失败,则使用原始字符串。 

调用 AST 转码 API

提取 AST 数据后,我们调用外部 API 将其转换为 React 代码:

privateasyncgenerateReactCode( astData:AstData, options:CodeGenerationOptions, )romise<ApiResponse> { // 确保有可用的token if(!this.apiToken) {  try{   this.apiToken=awaitthis.fetchToken();   }catch(error) {   thrownewError("无法获取API Token: "+ error);   }  } 
console.log("开始生成 React 代码,输入参数:",JSON.stringify(astData)); try{ console.log("发送请求到 API..."); constresponse =awaitfetch(this.apiEndpoint, { method:"OST", headers: { Authorization:`Bearer${this.apiToken}`, "Content-Type":"application/json", }, body:JSON.stringify({ inputs: { from:this.from, options,// 添加 options 到请求中 }, query: astData.ast, response_mode:"blocking", conversation_id:"", user:this.userId, }), });
if(!response.ok) { thrownewError( `API 请求失败{response.status}${response.statusText}`, ); }
constdata = (awaitresponse.json())asApiServerResponse; console.log("API 响应数据:",JSON.stringify(data,null,2)); return{ answer: data?.answer||undefined, error: data?.error|| data?.message, }; }catch(error) { console.error("请求失败:", error); throwerror; } }

这段代码首先确保有可用的 API Token,然后向 AST 转码 API 发送请求,将 AST 数据、用户选项等信息包含在请求体中。如果请求成功,则解析响应数据并返回;如果失败,则抛出相应的错误。 

返回结果处理

最后,我们需要处理 API 响应并将结果返回给 AI 客户端:

// 在 CallToolRequestSchema 处理函数中 constresult =awaitthis.generateReactCode(astData, options); 
if(result.error) { return{ content: [ { type:"text", text:`处理ZIP并生成代码失败{result.error}`, }, ], isError:true, }; }
// 返回结果 constresponseData = result.answer ? (JSON.parse(result.answer)asApiResponseData) : {}; return{ content: [ { type:"text", text:`已成功处理ZIP文件并生成React${ options.type==="create"?"项目":"组件" }代码:\n\n${responseData.text ||""}`, }, ], };

这段代码检查 API 响应中是否有错误。如果有,则返回错误信息;如果没有,则解析响应数据并返回生成的 React 代码。 

服务器启动与运行

最后,我们需要启动 MCP 服务器,开始监听请求:

asyncrun() { consttransport =newStdioServerTransport(); awaitthis.server.connect(transport); console.error("MCP Pixelator server 正在运行..."); } 
// 主程序 constserver =newMcpPixelatorServer(); server.run().catch(console.error);

这段代码创建了一个标准输入/输出传输器(StdioServerTransport),并使用它连接 MCP 服务器。服务器成功连接后,输出运行消息,等待 AI 客户端的请求。

至此,我们已经完成了 McpPixelatorServer 的核心实现。 集成在插件内的整体流程如下:





实战一下

这是在本次在开发逛逛视频沉浸流的新版本场景为例,将 UI 和逻辑完全拆分后(这部分具体的前端开发理念暂且不提),直接开发 UI 相关的部分。

选中对应设计稿区域:

使用 Pixelator 插件生成 AST



在插件中输入



选择对应的 Zip 文件



等待生成(一般耗时 60s 左右)



生成完成



代码被插在了一个现有的React Demo 仓库中并找到了对应的位置



运行一下

待改进

1.现在还是比较粗暴的方式,用一个 LLM 去调用 MCP,然后用 MCP 调用相关接口(但是这个接口里面也是一个 LLM),再把返回的代码结构吐回来,再来判断是否要新增还是补全;这个有点不够优雅,后续会考虑直接融合在一个对话内。

2.现在还是全 Web 相关代码,在移动端上的适配还是有点弱,针对插件侧的产出,可以和移动端、大屏、Weex相关的内容结合,这样需要二次创作的东西会少一点。

3.现在 D2C 图生码的时间会比较长,可能会存在超时被插件强制超时处理的情况(Roo 的默认超时时间是60s可以配置,OneDay VSC 默认还是 60s 但是并不能配置),这个后面考虑和插件开发同学一道,完成。

一些槽点记录

文件选择器

在直接用 LLM 生成我的诉求的时候,初始的诉求是打开一个文件选择器,并且可以支持用户选择 Zip 文件,LLM 一直在尝试安装 electron 来实现这个文件选择器,矫正了几次都是一直在使用这个,同时还试了一下 inquiry 这个社区包,但是也是没能实现,后面发现了个神奇的脚本,解决了这个问题,也算是有所收获。

osascript-e'choosefilewithprompt"选择ZIP文件"oftype{"ZIP"}'2>/dev/null

任务之间的衔接

一个大的任务拆成多个子任务时,比如说代码生成任务拆成选择 Zip 并解析和生成 Code 两步后,负责衔接两步任务的是 LLM,因此遇到了很多次,JSON 数据要么 String 了一下 要么直接给我套上了个双引号,总之质疑了自己好几次,最后还是选择了将两个任务合二为一了...

调试

调试是非常之困难...学习了一下官方提供的 inspector,但是对于这个使用骚操作拉起来选择框的情况,是没有办法直接使用 inspector 的只能再魔改...

同时如果想要看 mcp 插件具体返回了什么,还要 console 打印/写文件里面,又是一堆 token 浪费..

回复

使用道具 举报

您需要登录后才可以回帖 登录 | 立即注册

本版积分规则

链载AI是专业的生成式人工智能教程平台。提供Stable Diffusion、Midjourney AI绘画教程,Suno AI音乐生成指南,以及Runway、Pika等AI视频制作与动画生成实战案例。从提示词编写到参数调整,手把手助您从入门到精通。
  • 官方手机版

  • 微信公众号

  • 商务合作

  • Powered by Discuz! X3.5 | Copyright © 2025-2025. | 链载Ai
  • 桂ICP备2024021734号 | 营业执照 | |广西笔趣文化传媒有限公司|| QQ