1. 项目概述一个为ChatGPT打造的代码执行与保存插件作为一名长期在开发工具和效率提升领域折腾的程序员我一直在寻找能让AI助手“动手能力”更强的方法。ChatGPT这类大语言模型在代码生成和解释上表现出色但长期以来一个核心痛点就是它无法真正“运行”代码。你让它写一段Python爬虫它写得头头是道但你想立刻验证这段代码是否能跑通、有没有语法错误、或者输出结果是否符合预期就得手动复制代码到本地IDE或在线编译器这个过程打断了流畅的交互体验。今天要深入拆解的这个项目——Code Runner Plugin/GPT正是为了解决这个痛点而生。它本质上是一个架设在ChatGPT包括插件版和后来的GPTs版与真实代码执行环境之间的桥梁。简单来说它让ChatGPT不仅是一个“代码顾问”更升级为一个“代码执行终端”。你可以直接在对话中要求ChatGPT编写并运行一段代码无论是Python、JavaScript、C还是其他70多种语言它都能通过这个插件调用后端的编译器API执行代码并将运行结果包括输出或错误信息直接返回给你。更进一步它还允许你将生成的代码片段保存为高亮语法的图片或文件方便分享和归档。这个项目非常适合所有与ChatGPT交互的开发者、编程学习者以及技术内容创作者。对于学习者它是一个即时的练习和验证工具对于开发者它能快速原型验证和调试代码片段对于内容创作者生成美观的代码截图功能则非常实用。接下来我将从设计思路、核心实现、实操部署到避坑经验为你完整还原这个插件的构建过程。2. 核心架构与设计思路拆解2.1 核心需求与方案选型这个插件的核心目标很明确安全、快速、多语言地执行用户通过ChatGPT提交的任意代码并返回结果。围绕这个目标我们需要拆解几个关键问题执行环境从哪来不可能在用户本地或插件服务器上直接开一个Docker容器跑任意代码那在安全和资源隔离上都是灾难。如何支持多种语言自己维护一个包含70多种语言编译/解释器的环境工程量和维护成本极高。如何与ChatGPT交互ChatGPT插件有特定的协议规范OpenAPI规范需要以标准化的方式提供API。基于这些问题项目作者选择了非常务实的技术方案执行引擎JDoodle Compiler API。这是一个成熟的在线代码执行API服务它封装了海量编程语言的运行环境并提供了安全的沙箱隔离。开发者只需通过HTTP请求发送代码和语言标识就能获取执行结果。这完美解决了环境搭建和多语言支持的问题将复杂度外包给了专业服务。后端框架FastAPI / Quart。项目初期使用FastAPI这是一个基于Python的现代、高性能Web框架特别适合快速构建API。后来为了适配Vercel无服务器函数的部署限制如包大小部分版本迁移到了Quart一个兼容Asyncio的Flask-like框架。两者都能轻松定义符合OpenAPI规范的端点方便ChatGPT发现和调用。交互协议ChatGPT Plugin Manifest。ChatGPT插件通过一个名为ai-plugin.json的清单文件来定义自身的能力、认证方式和API端点。后端需要暴露这个文件以及对应的OpenAPI规范描述openapi.yamlChatGPT客户端会读取这些信息来了解如何调用插件。这个架构可以概括为ChatGPT作为交互前端插件后端作为中继代理JDoodle API作为执行后端。插件后端在这里扮演了至关重要的角色它需要处理ChatGPT的请求将其转换为JDoodle API能理解的格式处理认证如嵌入API Key再将结果格式化后返回给ChatGPT。2.2 功能模块设计基于上述架构插件主要设计了三大功能模块代码执行模块 (/run): 这是核心功能。接收来自ChatGPT的代码、语言和可能的输入stdin调用JDoodle API执行并将标准输出、标准错误和执行时间等信息包装后返回。代码保存模块 (/save): 将代码内容保存到服务器的文件系统中并返回一个可访问的URL。更酷的是它集成了kod.so一个类似Carbon的代码转图片服务可以将代码生成语法高亮、风格美观的图片便于分享到社交媒体或技术文档中。代码下载模块 (/download/{filename}): 提供一个简单的端点让用户可以通过生成的URL直接下载之前保存的纯文本代码文件。此外为了管理使用量还有一个/credits端点用于查询内嵌的JDoodle API Key的剩余免费额度默认每天200次调用。设计考量为什么选择JDoodle而不是其他如Piston、Judge0等服务JDoodle提供了非常慷慨的免费套餐200次/天/Key且API设计简洁对个人开发者和小型项目足够友好。而像Piston虽然开源可自建但需要自己维护服务器和沙箱环境初期复杂度过高。这个选择体现了“快速验证想法利用现有服务降低门槛”的敏捷开发思路。3. 核心实现细节与关键技术点3.1 与ChatGPT插件的对接清单与认证要让ChatGPT识别你的服务是一个插件必须在后端提供两个关键文件/.well-known/ai-plugin.json: 这是插件的“身份证”。它定义了插件的基本信息如名称、描述、开发者信息以及最重要的——API连接方式和认证方式。{ schema_version: v1, name_for_human: Code Runner, name_for_model: codeRunner, description_for_human: Run and save code in 70 languages directly in ChatGPT., description_for_model: Plugin for running and saving code snippets. Use it when user asks to run, execute, or save code., auth: { type: user_http, authorization_type: bearer }, api: { type: openapi, url: http://localhost:8000/openapi.yaml, is_user_authenticated: false }, logo_url: http://localhost:8000/logo.png, contact_email: supportexample.com, legal_info_url: http://localhost:8000/legal }auth.type: “user_http”表示ChatGPT会在请求你的插件API时在Header中附带一个Bearer Token。这个Token是由ChatGPT生成并管理用于标识当前会话用户并非你的JDoodle API Key。你的后端需要验证这个Token虽然在这个简单插件里可能只是检查其存在性。api.url指向你的OpenAPI规范文件地址。/openapi.yaml: 这是插件的“技能说明书”。它严格遵循OpenAPI 3.0规范详细描述了后端提供了哪些API端点如/run,/save每个端点需要什么参数如JSON body里包含code,language以及返回的数据格式。openapi: 3.0.1 info: title: Code Runner Plugin API version: 1.0.0 servers: - url: http://localhost:8000 paths: /run: post: operationId: runCode summary: Execute code in a specified language. requestBody: required: true content: application/json: schema: $ref: #/components/schemas/RunCodeRequest responses: 200: description: Code execution result. content: application/json: schema: $ref: #/components/schemas/RunCodeResponse # ... 其他端点定义ChatGPT的模型会读取这个文件从而知道在什么情况下该调用哪个插件功能。例如当用户说“运行这段Python代码”ChatGPT理解了意图后就会查找插件中描述为“执行代码”的端点即/run并构造符合格式的请求发送过来。3.2 代码执行与JDoodle API的交互这是后端最核心的函数。以FastAPI版本为例其核心逻辑如下from fastapi import FastAPI, HTTPException, Header import httpx import os app FastAPI() JD00DLE_API_URL “https://api.jdoodle.com/v1/execute JD00DLE_CLIENT_ID os.environ.get(“JD00DLE_CLIENT_ID”) # 从环境变量读取 JD00DLE_CLIENT_SECRET os.environ.get(“JD00DLE_CLIENT_SECRET”) app.post(“/run”) async def run_code( request: RunCodeRequest, authorization: Optional[str] Header(None) # 接收ChatGPT传来的Token ): # 1. 验证ChatGPT的Bearer Token简单示例 if not authorization or not authorization.startswith(“Bearer “): raise HTTPException(status_code401, detail“Missing or invalid auth token”) # 2. 准备请求JDoodle的载荷 payload { “clientId”: JD00DLE_CLIENT_ID, “clientSecret”: JD00DLE_CLIENT_SECRET, “script”: request.code, “language”: request.language, # 如 “python3”, “cpp”, “nodejs” “versionIndex”: “0”, # 通常使用默认版本 “stdin”: request.stdin if request.stdin else “” } # 3. 调用JDoodle API async with httpx.AsyncClient() as client: try: resp await client.post(JD00DLE_API_URL, jsonpayload, timeout30.0) resp.raise_for_status() jdoodle_result resp.json() except httpx.RequestError as e: raise HTTPException(status_code500, detailf“JDoodle API request failed: {e}”) # 4. 处理并返回结果 output jdoodle_result.get(“output”, “”) error jdoodle_result.get(“error”, “”) status_code jdoodle_result.get(“statusCode”, 200) return { “output”: output, “error”: error, “statusCode”: status_code, “memory”: jdoodle_result.get(“memory”, None), “cpuTime”: jdoodle_result.get(“cpuTime”, None) }关键点解析API Key管理JDoodle的clientId和clientSecret必须绝对避免硬编码在代码中。最佳实践是使用环境变量如.env文件或部署平台的环境配置来存储。项目源码中提到的“API Keys are embedded in the code”仅适用于演示生产环境必须修改。超时设置代码执行时间不可控必须为HTTP客户端设置合理的超时如30秒防止长时间挂起阻塞服务器资源。错误处理必须妥善处理JDoodle API调用失败网络错误、认证失败、额度用尽等和返回错误编译错误、运行时错误的情况并将清晰的错误信息返回给ChatGPT用户。语言标识符JDoodle有自己的一套语言简称如python3,cpp17,java后端需要维护一个映射或者直接信任前端ChatGPT传来的标识符但要做好校验。3.3 代码保存与美化集成Kod.so/save端点做了两件事一是将代码以文本文件形式保存到服务器磁盘二是调用kod.so的API生成代码图片。import aiofiles from pathlib import Path import httpx SAVE_DIR Path(“./saved_codes”) SAVE_DIR.mkdir(exist_okTrue) app.post(“/save”) async def save_code(request: SaveCodeRequest): # 1. 保存为文本文件 file_path SAVE_DIR / f“{request.filename}” async with aiofiles.open(file_path, ‘w’) as f: await f.write(request.code) # 2. 调用Kod.so生成图片 kod_payload { “code”: request.code, “language”: request.language, “theme”: request.theme or “dracula” # 可指定主题 } async with httpx.AsyncClient() as client: kod_resp await client.post(“https://kod.so/api/generate”, jsonkod_payload) if kod_resp.status_code 200: image_url kod_resp.json().get(“url”) else: image_url None return { “message”: “Code saved successfully”, “file_url”: f“/download/{request.filename}”, “image_url”: image_url # 返回图片链接 }注意事项文件安全request.filename必须进行严格的净化处理防止路径遍历攻击如../../../etc/passwd。应只允许字母、数字、下划线和点号并限制扩展名。存储管理对于公开服务保存的文件需要定期清理否则会撑满磁盘。可以考虑只保留一段时间如24小时或使用对象存储服务如AWS S3、Cloudflare R2。外部依赖kod.so是一个第三方服务其可用性和费率可能变化。需要有降级方案例如图片生成失败时仅返回文件保存成功的消息。3.4 无网络访问的Python环境项目更新中提到一个重要限制Python环境没有网络访问权限。这指的是JDoodle API提供的Python沙箱环境是网络隔离的。这意味着任何需要发起网络请求的代码如import requests; requests.get(‘...’)都会失败。这对开发者的启示是在让ChatGPT运行涉及网络操作的Python代码时需要明确告知它这个限制。或者在插件后端的设计上可以提供更明确的错误提示当检测到用户代码中包含import requests,urllib,socket等网络库时提前返回一个友好的警告而不是等待JDoodle执行超时或报错。4. 从零到一的完整部署实操指南假设你希望在自己的服务器上部署一个类似的服务以下是详细的步骤。4.1 环境准备与依赖安装首先确保你的服务器或本地开发环境已安装Python 3.8。克隆项目代码git clone 项目仓库地址 cd CodeRunner-Plugin创建虚拟环境强烈推荐python -m venv venv # Linux/Mac source venv/bin/activate # Windows venv\Scripts\activate安装依赖 项目根目录下应有requirements.txt文件。pip install -r requirements.txt典型的依赖包括fastapi,uvicorn,httpx,python-dotenv等。4.2 获取并配置JDoodle API密钥访问 JDoodle Compiler API 页面。注册并登录账号。在控制台你可以找到你的Client Id和Client Secret。免费账户通常有每天200次调用的额度。切勿将密钥提交到代码仓库创建项目根目录下的.env文件JD00DLE_CLIENT_IDyour_client_id_here JD00DLE_CLIENT_SECRETyour_client_secret_here在代码中如main.py使用python-dotenv加载环境变量from dotenv import load_dotenv load_dotenv() import os client_id os.getenv(“JD00DLE_CLIENT_ID”) client_secret os.getenv(“JD00DLE_CLIENT_SECRET”)4.3 修改插件清单文件/.well-known/ai-plugin.json文件中的api.url、logo_url、legal_info_url等字段需要指向你实际部署的域名。在本地开发时可以使用localhost。{ “api”: { “type”: “openapi”, “url”: “https://your-domain.com/openapi.yaml”, // 生产环境 // “url”: “http://localhost:8000/openapi.yaml”, // 开发环境 “is_user_authenticated”: false }, “logo_url”: “https://your-domain.com/logo.png”, “legal_info_url”: “https://your-domain.com/legal” }同时确保你的服务器根目录下存在logo.png和legal或privacy页面ChatGPT会在安装插件时尝试抓取这些资源。4.4 运行与测试后端服务启动开发服务器uvicorn main:app --reload --host 0.0.0.0 --port 8000--reload参数使得代码修改后服务器会自动重启便于开发。验证API 打开浏览器访问http://localhost:8000/docs你会看到FastAPI自动生成的交互式API文档。你可以在这里直接测试/run和/save端点是否工作正常。测试/run选择POST尝试JSON body{“code”: “print(‘Hello World’)”, “language”: “python3”}。测试/save尝试{“filename”: “test.py”, “code”: “print(‘saved’)”, “language”: “python3”}。验证插件清单 访问http://localhost:8000/.well-known/ai-plugin.json确保能正确返回JSON内容。4.5 在ChatGPT中安装本地插件开发模式由于OpenAI官方插件商店审核严格个人开发者通常无法直接上架。但ChatGPT提供了本地开发模式进行测试。确保你的后端服务正在运行localhost:8000。打开ChatGPT网页版并确保你有插件测试权限通常需要开通ChatGPT Plus。在模型选择下拉框中选择 “Plugins” - “Plugin store” - “Develop your own plugin”。在弹出的对话框中输入你的本地服务地址http://localhost:8000。ChatGPT会尝试从该地址获取ai-plugin.json文件。如果成功你会看到插件的描述信息并可以安装它。安装后在新的对话中启用该插件你就可以直接对ChatGPT说“写一个快速排序的Python代码并运行它。” ChatGPT会调用你的本地插件来执行代码。4.6 部署到生产环境以Vercel为例项目原作者使用了Vercel进行部署。对于Python后端Vercel通过无服务器函数Serverless Functions来运行。项目结构调整Vercel的Python无服务器函数要求入口点位于/api目录下且文件名为index.py。你需要将主要的app对象如app FastAPI()移动到/api/index.py中。创建vercel.json在项目根目录创建此文件配置路由和构建命令。{ “builds”: [ { “src”: “api/index.py”, “use”: “vercel/python” } ], “routes”: [ { “src”: “/(.*)”, “dest”: “/api/index.py” } ] }配置环境变量在Vercel项目设置的Environment Variables页面添加JD00DLE_CLIENT_ID和JD00DLE_CLIENT_SECRET。处理静态文件/.well-known/ai-plugin.json和/openapi.yaml是静态访问的。你需要确保Vercel能正确路由这些请求到你的Python应用或者更简单的方式将它们作为静态文件处理可能需要额外的配置。部署连接你的Git仓库Vercel会自动部署。部署后将ai-plugin.json中的api.url等字段更新为你的Vercel域名如https://your-app.vercel.app。实操心得Vercel的无服务器环境对包大小有严格限制。这就是为什么项目后期引入了Quart版本main_quart.py因为Quart相比FastAPI及其依赖更轻量。如果你的依赖过多导致部署包超过50MB可能会部署失败。精简依赖或使用Monorepo拆分是解决方案。5. 常见问题、排查技巧与进阶思考5.1 安装与运行问题排查表问题现象可能原因排查步骤与解决方案访问localhost:8000/docs失败服务未启动或端口被占用1. 检查uvicorn进程是否运行。2. 使用netstat -ano | findstr :8000(Win) 或lsof -i:8000(Mac/Linux) 查看端口占用终止冲突进程。3. 尝试更换端口如--port 8001。ChatGPT无法安装本地插件提示“无法获取清单”跨域问题 (CORS) 或清单文件路径错误1. 在后端代码中启用CORS中间件FastAPI:from fastapi.middleware.cors import CORSMiddleware。2. 确保ai-plugin.json文件可通过http://localhost:8000/.well-known/ai-plugin.json直接访问。3. 检查浏览器控制台 (F12) 的网络请求看获取清单的请求是否返回404或CORS错误。运行代码时返回“Authentication failed”或“Invalid Client ID”JDoodle API密钥未设置或错误1. 确认.env文件已创建且内容正确。2. 确认代码中通过os.getenv()能正确读取到密钥可临时打印验证。3. 在Vercel等平台确认环境变量已正确配置并重新部署。运行代码长时间无响应或超时JDoodle API服务不稳定或代码有死循环1. 在后端代码中为httpx请求设置更短的超时如15秒并做好超时异常处理。2. 提醒用户通过插件运行的代码应避免无限循环或耗时极长的操作。JDoodle本身也有执行时间限制。/save功能生成的图片链接失效Kod.so服务API变更或不可用1. 直接访问Kod.so官网检查其服务状态。2. 在代码中加强错误处理当图片生成失败时优雅地返回文本保存成功的消息而不影响主流程。部署到Vercel后访问返回404路由配置错误或入口文件位置不对1. 确保vercel.json配置正确将所有流量路由到/api/index.py。2. 检查/api/index.py中是否正确定义了根路由app.get(“/”)以及/.well-known/等静态路由的处理。5.2 安全与优化进阶思考速率限制与滥用防护当前架构下所有用户共享同一个JDoodle免费额度200次/天。一旦公开极易被刷爆。必须实施速率限制。可以在插件后端层面基于ChatGPT传来的Bearer Token代表用户会话进行简单的计数限流例如每个Token每小时最多调用20次。更健壮的做法是引入数据库如Redis来记录和校验调用频率。代码安全沙箱的局限性虽然JDoodle提供了沙箱但作为插件开发者我们仍需警惕用户输入。绝对不要将用户提供的代码、文件名或任何参数直接用于拼接系统命令如os.system(f”echo {user_input}”)这会导致严重的命令注入漏洞。所有输入都应视为不可信的。成本控制JDoodle免费额度有限付费套餐有成本。如果插件用户量增长需要考虑引导用户使用自己的JDoodle API Key可在插件设置中配置。实现多API Key轮询将请求分摊到多个免费账户下。对于付费功能建立自己的订阅体系将成本转嫁或覆盖。用户体验优化语言智能检测可以让ChatGPT在请求中附带语言信息也可以在后端尝试根据代码片段自动检测语言难度较高。更丰富的执行上下文支持多个文件、定义项目结构、传递环境变量等但这会极大增加复杂度可能超出插件初衷。结果缓存对于相同的代码和输入可以缓存执行结果减少对JDoodle API的调用提升响应速度。5.3 从插件到GPTs的迁移项目提到插件已更新为GPT版本。这是OpenAI生态的演进从“插件”到“自定义GPTs”。两者的核心区别在于集成方式插件需要用户手动在插件商店安装并在对话中启用。它通过标准的OpenAPI规范与ChatGPT交互。GPTs在GPT Builder中创建可以更深度地定义指令、知识库和功能Actions。Actions本质上也是调用外部API其配置方式与插件清单类似但更集成化。如果你已经构建了一个插件后端将其迁移为GPTs的Action是非常容易的。你只需要在GPTs的配置界面以类似的方式填写你的API端点、认证信息和OpenAPI规范。好处是用户使用你的GPT时功能是内置的无需额外安装步骤。构建这样一个工具最深的体会是“做减法”的重要性。最初可能会想支持所有功能集成多个编译器、支持复杂项目、实时协作……但核心价值其实就一点在聊天环境中无缝运行代码。抓住这个核心利用JDoodle这样的现有服务快速实现验证需求再根据用户反馈迭代才是项目成功的关键。这个插件在思路上提供了一个非常清晰的范本展示了如何将强大的AI对话能力与真实世界的执行能力结合起来创造出112的体验。