基于Discord与Modal构建云端AI编程沙箱:低成本弹性架构实践
1. 项目概述一个将Discord聊天室变成AI编程工坊的“妄想工厂”如果你和我一样既想随时随地用上Claude Code这样的AI编程助手又不想在本地电脑上常驻一个吃资源的进程或者希望团队能共享一个稳定、隔离的AI编程环境那么这个名为“SMILE-factory”的项目绝对值得你花十分钟了解一下。它的核心我称之为“妄想工厂”Delulu是一个精巧的Discord机器人。你只需要在Discord的任意频道里它它就能在云端瞬间为你拉起一个专属的、临时的Claude Code编程沙箱执行完你的任务后自动销毁而所有的工作区文件和会话状态都会被妥善保存。下次你在同一个对话线程里继续提问它还能接着上次的上下文继续工作。这听起来有点像科幻场景但实现它的技术栈却相当务实和清晰一个轻量级的Discord Bot作为交互前端Modal平台提供强大的、按需启动的容器化计算能力Anthropic的Claude Code作为核心的AI编程引擎。整个架构的精妙之处在于它将重度的计算负载运行Claude Code完全剥离到了云端沙箱而本地或服务器上只需要运行一个内存占用极低约50MB的调度机器人。这意味着你可以用一个每月仅需几美元的VPS如DigitalOcean最基础的Droplet来托管这个机器人就能获得近乎无限的、可随时调用的AI编程算力。项目原名“SMILE-factory”下其实包含两个子项目其中活跃的“Delulu”是我们讨论的重点。另一个“LoRA-Instruct”是关于大语言模型微调的早期工作目前已被归档它展示了项目作者在AI领域的另一面探索但今天我们聚焦于更具实用性的Discord机器人。2. 核心架构与设计哲学为什么这样设计在深入代码之前理解整个系统的设计思路至关重要。这能帮助你在部署、调试乃至二次开发时清楚地知道每个环节在做什么以及为什么这么做。2.1 核心工作流程拆解整个系统的交互可以概括为以下几步我把它比作一个高效的云端餐厅你点餐用户触发你在Discord的某个频道里发送一条消息并提及这个机器人比如CodingBot 帮我写一个Python爬虫。餐厅开单创建会话机器人看到你的消息后会立即在同一个频道下创建一个新的公开讨论串Thread这个讨论串的名字通常就是你的指令摘要。同时它在自己的内存里为这个讨论串ID注册一个新的“会话”。后厨开工调度沙箱机器人自己不处理你的请求。它更像一个前台服务员拿起对讲机呼叫后厨Modal平台“来一份‘Python爬虫’送到3号桌对应讨论串ID的工作区”。它通过Modal的Python客户端远程调用一个预先部署好的函数。名厨掌勺沙箱执行Modal平台收到指令后瞬间启动一个全新的、干净的容器沙箱。这个容器里已经预装了Node.js和Claude Code。沙箱启动后会将两个关键目录挂载到Modal的持久化存储卷Volume上一个是Claude Code的“家目录”/vol/claude-home/.claude里面存放着OAuth认证信息和会话历史另一个是专属的“工作台”/vol/workspaces/讨论串ID。接着沙箱在对应的工作台目录下执行claude -p --continue命令将你的问题提交给Claude API。上菜与收摊返回与销毁Claude Code处理完成后将结果输出到标准输出stdout。沙箱捕获这个输出将其返回给前台机器人然后自己“功成身退”——容器被销毁。Modal的计费精确到秒任务结束即停止计费。加菜持续对话当你在这个讨论串里回复想继续追问或修改代码时你不需要再次机器人。机器人会监测到这是同一个讨论串内的回复自动使用claude --continue命令基于之前保存在Volume里的会话历史和工作区文件让Claude Code接着上次的上下文继续回答。关键设计洞察这种“前台轻量调度 后台重型沙箱”的架构是项目成本与弹性得以平衡的核心。机器人进程极其轻量可以运行在非常廉价的VPS上。所有昂贵的、动态的计算和AI调用都发生在按需创建、按秒计费的Modal沙箱中。这比常驻一个拥有强大GPU的服务器要经济得多。2.2 关键组件与数据流为了更直观我们可以抛开复杂的流程图用一张简单的表格来理解各个组件的职责和数据流向组件所在位置核心职责关键数据/状态持久性Discord 用户/频道Discord平台提供交互界面。用户在此机器人并接收回复。用户消息、机器人回复、讨论串ThreadDiscord服务器持久保存Delulu Bot (前台)你的VPSDroplet1. 监听Discord消息事件。2. 管理讨论串ID与沙箱会话的映射。3. 调用Modal远程函数。4. 将结果贴回Discord。DISCORD_BOT_TOKEN, Modal客户端凭证内存中的会话映射表可丢失会话映射表是内存缓存可丢失。Bot配置需持久化。Modal 沙箱函数 (后厨)Modal云平台1. 按需启动临时容器。2. 挂载持久化存储卷。3. 在指定工作区执行Claude Code命令。4. 返回输出并销毁自身。容器镜像预装软件、函数定义函数定义持久容器实例 ephemeral临时。Modal 持久化卷 (冰箱/仓库)Modal云平台持久化存储两类数据1.Claude Home (/vol/claude-home)Claude Code的配置、OAuth令牌、会话历史。2.工作区 (/vol/workspaces/): 每个讨论串对应的项目文件。.claude/目录、各个thread_id子目录持久化。这是实现--continue和跨会话状态保持的关键。Modal 密钥 (保险柜钥匙)Modal云平台安全地存储初始的Claude OAuth凭证JSON格式。仅在沙箱首次启动时用于初始化Volume中的.claude/.credentials.json。CLAUDE_CREDENTIALS_JSON作为Modal Secret持久化存储。Anthropic Claude APIAnthropic云服务提供Claude Code的AI推理能力。OAuth令牌、API调用记录云端服务。数据流总结初始化Claude OAuth凭证通过Modal Secret注入在第一次运行沙箱时被写入Volume的claude-home。此后Volume中的副本成为凭证来源Secret仅作为备份。请求流用户消息 - Discord - Bot - Bot调用Modal函数 - Modal启动沙箱 - 沙箱读取Volume中的凭证和工作区 - 沙箱调用Claude API - 结果返回给沙箱 - 沙箱返回给Bot - Bot回复到Discord讨论串。状态持久化所有与会话相关的状态认证、聊天历史、代码文件都保存在Modal Volume中与短暂存在的Bot进程和沙箱容器解耦。因此即使Bot重启或沙箱新建只要讨论串ID不变就能找到对应的工作区和历史实现无缝继续。2.3 架构设计的精妙之处与考量这个设计解决了几个关键问题安全性隔离你的Claude OAuth凭证从未到达你托管Bot的VPS。它们只存在于Modal的Secret和Volume中。VPS被攻破最多损失Discord Bot Token而无法窃取你的Claude账户。成本优化Bot作为轻量级调度器可以24/7运行在低配VPS上。昂贵的AI计算和临时的软件环境沙箱仅在需要时按秒计费用完即焚。状态持久化通过将HOME环境变量指向Volume上的claude-home巧妙地将Claude Code的所有状态包括刷新令牌持久化。这使得跨多次、离散的沙箱调用维持一个“连续”的Claude会话成为可能。简化部署Bot容器只需要Python和Modal客户端库无需安装Node.js或Claude Code CLI大大降低了宿主机的环境复杂度。3. 从零开始部署你的“妄想工厂”理解了原理我们开始动手搭建。整个过程分为三个主要部分配置Discord机器人、设置Modal云函数、最后部署Bot主机。我会详细说明每一步的意图和可能遇到的坑。3.1 第一步创建并配置Discord机器人这是机器人与Discord平台通信的凭证。创建应用访问 Discord开发者门户 点击右上角“New Application”给它起个名字比如Delulu Factory。创建机器人在应用设置页面的左侧边栏找到“Bot”选项点击进入。然后点击“Add Bot”。在弹出的确认框中选择“Yes, do it!”。获取并保存Token在Bot页面找到“TOKEN”部分点击“Reset Token”并确认。请立即复制这个Token它只会显示一次。将其安全保存这就是后面要用到的DISCORD_BOT_TOKEN。开启消息内容意图在Bot页面下方找到“Privileged Gateway Intents”区域。必须勾选“MESSAGE CONTENT INTENT”。这是因为机器人需要读取频道中它的消息内容。勾选后点击“Save Changes”。生成邀请链接转到左侧边栏的“OAuth2” - “URL Generator”。在“Scopes”下勾选bot。在“Bot Permissions”下勾选以下权限根据你的需求可以调整View Channels(查看频道)Send Messages(发送消息)Create Public Threads(创建公开讨论串)Send Messages in Threads(在讨论串中发送消息)Read Message History(读取消息历史)页面底部会生成一个URL复制它。邀请机器人在浏览器中打开复制的URL选择你想要添加机器人的服务器然后点击“Authorize”。完成人机验证后机器人就会出现在你的服务器成员列表中。实操心得妥善保管你的Bot Token。如果泄露应立即在开发者门户重置。另外MESSAGE CONTENT INTENT权限是2022年Discord API更新后要求的如果不开启机器人将无法读取消息内容也就无法响应提及。3.2 第二步设置Modal与Claude Code沙箱这是项目的“计算引擎”部分所有AI代码执行都发生在这里。3.2.1 环境准备与Modal认证首先确保你的本地环境有Python 3.14和uv一个快速的Python包管理器和安装器。然后进入沙箱项目目录cd apps/delulu_sandbox_modal uv sync # 安装依赖 uv run modal setup # 这会打开浏览器完成Modal账户认证3.2.2 创建Claude OAuth密钥这是最关键也最容易出错的一步。我们需要为机器人创建一个独立的Claude Code登录会话避免干扰你个人电脑上的claudeCLI。# 使用一个临时目录作为HOME隔离登录环境 HOME/tmp/claude-bot-home claude login # 执行后会打开浏览器引导你完成ClaudePro或Max订阅的OAuth登录流程。 # 登录成功后凭证会保存在 /tmp/claude-bot-home/.claude/.credentials.json # 将凭证作为Modal Secret创建命名为 claude-oauth uv run modal secret create claude-oauth \ CLAUDE_CREDENTIALS_JSON$(cat /tmp/claude-bot-home/.claude/.credentials.json) # 清理临时目录 rm -rf /tmp/claude-bot-home为什么这么做沙箱函数启动时会读取这个Secret并将其内容写入Volume的/vol/claude-home/.claude/.credentials.json。之后Claude Code就会使用这个文件进行认证。Secret只在首次或凭证失效需要重置时使用Volume中的文件才是运行时真正的凭证来源。3.2.3 可选但推荐配置GitHub PAT密钥项目支持一个/commit命令允许Claude Code将工作区中的代码变更提交并推送回GitHub仓库。这需要GitHub的Personal Access Token (PAT)。创建占位符Secret为了能成功部署Modal的Secret.from_name调用要求Secret必须存在。我们可以先创建一个占位符uv run modal secret create github-pat GITHUB_TOKENplaceholder生成真正的PAT后续启用功能时访问 GitHub - Settings - Developer settings - Personal access tokens - Tokens (classic) 或 Fine-grained tokens。建议使用Fine-grained token权限更精细。创建一个新Token至少需要对你想要推送的仓库有Contents: Read and write权限。复制生成的Token以ghp_开头。更新Secretuv run modal secret create github-pat GITHUB_TOKENghp_你的真实token --force可选你还可以同时设置提交者的作者信息让提交记录更美观uv run modal secret create github-pat \ GITHUB_TOKENghp_你的真实token \ GIT_AUTHOR_NAME你的名字 \ GIT_AUTHOR_EMAIL你的邮箱 \ --force注意--force参数会覆盖同名的现有Secret。3.2.4 部署沙箱应用到Modal一切就绪后从项目根目录部署make deploy-modal # 或者进入子目录部署 make -C apps/delulu_sandbox_modal modal-deploy这个过程会将apps/delulu_sandbox_modal/src/delulu_sandbox_modal/app.py中定义的Modal应用包括容器镜像、Volume、Secret引用和函数部署到你的Modal账户下。部署成功后你会得到一个类似hanl-org/delulu-sandbox-modal的应用名后续Bot会通过这个名字来远程调用函数。3.3 第三步部署Discord Bot主机Bot主机可以是你的本地电脑用于开发测试也可以是一台云服务器用于7x24小时服务。3.3.1 配置Bot环境首先复制环境变量模板并填入你的Discord Bot Tokencd apps/delulu_discord cp .env.example .env # 编辑 .env 文件将 DISCORD_BOT_TOKEN 的值替换为你之前保存的Token3.3.2 选择运行方式方式A本地运行测试用cd apps/delulu_discord uv run delulu-discord如果一切正常你会在终端看到Bot登录成功的日志。现在可以去Discord测试它了。方式B使用Docker运行推荐用于生产项目提供了Makefile来简化Docker操作# 在 apps/delulu_discord 目录下 make deploy # 这会构建Docker镜像并以容器方式运行 make logs # 查看容器日志make deploy命令默认会从/root/disco.env读取环境变量从/root/.modal.toml读取Modal凭证。如果你的文件路径不同可以覆盖环境变量make deploy ENV_FILE/你的/路径/.env MODAL_TOML/你的/路径/.modal.toml3.3.3 部署到DigitalOcean Droplet生产环境对于永久在线的服务一个廉价的VPS是完美选择。以下是详细步骤初始化Droplet在DigitalOcean创建一个最基础的Ubuntu 24.04 Droplet$4-6/月512MB内存足够。通过SSH登录。安装基础软件ssh root你的Droplet-IP apt update apt install -y docker.io git systemctl enable --now docker克隆代码git clone https://github.com/你的用户名/SMILE-factory.git cd SMILE-factory配置Bot环境文件# 创建Makefile期望的配置文件路径 cat /root/disco.env EOF DISCORD_BOT_TOKEN你的Discord_Bot_Token_在这里 EOF chmod 600 /root/disco.env # 保护Token文件上传Modal凭证从你的本地电脑将Modal认证文件复制到Droplet上。Bot容器需要这个文件来调用Modal函数。# 在你的本地终端执行 scp ~/.modal.toml root你的Droplet-IP:/root/.modal.toml构建并运行Bot容器# 在Droplet的SMILE-factory目录下 make deploy-bot # 这是顶层Makefile的快捷命令等同于 make -C apps/delulu_discord deploy验证运行make -C apps/delulu_discord logs如果看到Bot成功连接Discord的日志说明部署成功。容器设置了--restartunless-stopped即使服务器重启也会自动运行。4. 测试、使用与问题排查部署完成后是时候检验成果了。4.1 基础功能测试将Bot邀请到一个你有权限的Discord频道例如#general。发送一条消息并Bot例如你的Bot名字 用Python写一个简单的HTTP服务器。等待约10-30秒取决于Modal冷启动时间。Bot应该会在你的消息下方创建一个新的公开讨论串Thread。在该讨论串中回复Claude Code生成的结果代码和解释。在同一个讨论串内直接回复新的指令例如现在给它添加一个记录访问日志的功能。Bot会自动使用--continue参数基于之前的上下文继续生成代码。4.2 常见问题与排查指南如果Bot没有反应可以按照以下步骤排查现象可能原因排查步骤与解决方案Bot对提及毫无反应1.Message Content Intent未开启。2. Bot没有该频道的查看/发送消息权限。3. Bot进程未运行或崩溃。1. 回到Discord开发者门户确认Bot的MESSAGE CONTENT INTENT已勾选并保存。2. 检查Bot在服务器和频道中的角色权限。3. 运行docker ps查看容器状态docker logs 容器名查看日志。Bot创建了讨论串但无回复或日志显示Modal认证错误1. Modal凭证文件.modal.toml未正确挂载到容器中。2. Modal账户未认证或Token过期。1. 检查Docker运行命令或Makefile确保-v /root/.modal.toml:/root/.modal.toml:ro挂载正确。2. 在Droplet上尝试modal app list看是否需要重新登录 (modal setup)。Bot回复“Claude认证失败”或类似错误1.claude-oauthModal Secret不存在或内容错误。2. Volume中的凭证文件 (/vol/claude-home/.claude/.credentials.json) 已过期或损坏。1. 运行modal secret list确认claude-oauth存在。2.重置凭证这是一个关键操作。你需要通过Modal CLI或Web控制台删除Volume中对应的claude-home目录或至少删除.credentials.json文件然后重新执行3.2.2步骤创建Secret。下次沙箱启动时会重新注入新凭证。讨论串内回复没有延续上下文而是开始了新会话沙箱内HOME环境变量未正确指向Volume上的claude-home导致Claude Code无法找到之前的会话历史。检查apps/delulu_sandbox_modal/src/delulu_sandbox_modal/app.py中run_claude_code函数的定义确保environment字典中包含HOME: /vol/claude-home。查看Modal Dashboard中该函数的运行日志确认环境变量已设置。/commit命令失败1.github-patSecret不存在或Token是占位符。2. Token权限不足缺少repo的write权限。3. 工作区不是一个git仓库或远程地址未设置。1. 按照3.2.3步骤创建或更新一个有效的GitHub PAT Secret。2. 确保PAT对目标仓库有Contents: write权限。3. 确保在调用/commit前Claude Code已经在工作区内初始化了git并关联了远程仓库。避坑技巧最棘手的通常是Claude OAuth凭证问题。Modal Volume上的凭证文件可能会因为Claude的刷新令牌机制或未知原因失效。定期检查Bot日志如果发现认证错误最干脆的解决办法就是通过Modal CLI (modal volume ls,modal volume rm) 或Web UI删除旧的claude-home数据卷然后重新创建Secret并触发一次新的Bot调用让系统重新初始化。5. 进阶配置与维护5.1 配置GitHub提交功能如前所述配置好github-patSecret后你可以在Claude Code的对话中使用/commit命令。Bot会调用Modal函数将当前工作区的变更提交并推送到关联的GitHub仓库。这非常适合将AI生成的代码直接归档或集成到你的工作流中。安全建议为Bot创建专用的GitHub机器账户或者使用Fine-grained token并严格限制其仓库访问范围避免使用拥有过广权限的PAT。5.2 利用CI/CD实现自动化部署GitHub Actions项目内置了GitHub Actions工作流可以实现代码推送后的自动测试和部署非常专业。核心流程CI持续集成当有Pull Request或推送到main分支时自动运行代码风格检查Ruff和测试Pytest。CD持续部署当代码合并到main分支后自动执行沙箱部署在GitHub Runner上使用专用的CI Token将最新的Modal沙箱函数部署到云端。Bot部署通过SSH连接到你的Droplet拉取最新代码重新构建并重启Bot容器。一次性设置步骤在Droplet上生成SSH部署密钥在Droplet上执行ssh-keygen -t ed25519 -f /root/.ssh/disco_deploy -N -C github-actions-disco cat /root/.ssh/disco_deploy.pub /root/.ssh/authorized_keys在GitHub仓库添加SecretsDROPLET_HOST: 你的Droplet IP地址。DROPLET_SSH_KEY:私钥/root/.ssh/disco_deploy的全部内容。MODAL_TOKEN_ID和MODAL_TOKEN_SECRET: 在Modal设置页面为CI专门创建的Token。手动触发测试在GitHub仓库的Actions页面找到delulu工作流点击“Run workflow”进行手动测试。回滚策略如果新部署的版本有问题最安全的方式是使用git revert回退提交并推送到main分支CI/CD流水线会自动将生产环境回滚到上一个稳定版本。5.3 本地开发与代码规范项目使用uv管理依赖Ruff进行代码检查和格式化并配置了pre-commit钩子。同步依赖进入任一子项目目录 (apps/delulu_discord或apps/delulu_sandbox_modal)运行uv sync。代码检查与格式化make check # 在项目根目录运行检查两个子项目 make -C apps/delulu_discord lint # 仅对discord bot项目进行lint并尝试自动修复 make -C apps/delulu_discord fmt # 格式化代码启用pre-commit在本地仓库根目录运行pre-commit install。此后每次git commit都会自动对暂存的文件进行格式化检查。6. 项目结构深度解析了解代码结构有助于你进行定制化开发或故障排查。SMILE-factory/ ├── apps/ # 核心应用目录 │ ├── delulu_discord/ # Discord Bot (轻量调度器) │ │ ├── src/delulu_discord/ │ │ │ ├── main.py # 入口点处理Discord客户端事件实现提及门控 │ │ │ ├── handlers.py # 消息路由逻辑创建线程、下载附件、调用调度器、回复结果 │ │ │ ├── session_manager.py # 管理内存中的线程ID到会话的映射带TTL清理 │ │ │ ├── dispatcher.py # SandboxDispatcher类封装对Modal远程函数的调用 │ │ │ └── settings.py # 使用Pydantic从环境变量加载配置 │ │ ├── Dockerfile # 构建Bot容器镜像仅包含Python和必要库 │ │ └── Makefile # 本地开发、构建、部署的命令集 │ │ │ └── delulu_sandbox_modal/ # Modal沙箱函数 (重型执行器) │ └── src/delulu_sandbox_modal/ │ └── app.py # 核心文件定义Modal App、容器镜像、Volume、Secret和运行Claude Code的函数 │ ├── ARCHITECTURE.md # 详细的设计文档推荐阅读 └── Makefile # 顶层命令分发器方便执行跨子项目的操作定制化开发建议修改Bot行为例如想改变讨论串的命名规则或添加新的Discord斜杠命令主要修改apps/delulu_discord/src/delulu_discord/handlers.py。修改沙箱环境例如想在沙箱中预装更多系统包如ffmpeg,graphviz需要修改apps/delulu_sandbox_modal/src/delulu_sandbox_modal/app.py中的image定义部分使用.pip_install()或.apt_install()方法。调整资源规格Modal函数默认的CPU/内存配置可能不适合你的任务。你可以在app.py的app.function装饰器中调整cpu,memory,gpu等参数。7. 总结与个人体会搭建并运行这个“妄想工厂”一段时间后我最大的感受是它完美诠释了“云原生”思维在个人项目中的应用。它将状态、计算和调度清晰地分离用最适合的服务处理最适合的任务Discord负责交互Modal负责弹性计算和持久化存储Claude负责AI能力而一个微不足道的VPS则负责串联一切。这种架构带来的好处是实实在在的成本极低我的Bot运行在一个$6/月的Droplet上而Modal的计费在我间歇性使用的场景下每月费用几乎可以忽略不计。维护简单Bot本身无状态出了问题直接重启容器就好。最复杂的Claude认证和AI执行环境完全托管在Modal上。体验无缝在Discord里随时一下就能开始编程对话上下文还能保留这种流畅感是打开本地IDE、启动终端所不能比的。当然它也有其适用边界。它不适合需要极低延迟毫秒级响应的场景因为Modal沙箱有冷启动时间。它也更适合代码生成、脚本编写、问题解答这类“任务型”交互而不是持续的、流式的对话。如果你是一个喜欢在Discord社区里交流技术的开发者或者想为你的小团队提供一个共享的、隔离的AI编程环境这个项目提供了一个非常优雅且高性价比的解决方案。从零开始部署一遍不仅能获得一个实用的工具更能深入理解现代云服务如何组合起来解决复杂问题。