ARTICLE DETAIL

资讯详情

深耕网站建设与运营推广的一线实战洞察。

从零构建开源AI工作台WorkDSH:MCP协议与智能体循环实战

从零构建开源AI工作台WorkDSH:MCP协议与智能体循环实战 1. 为什么我要自己动手做一个 WorkDSH1.1 从一个真实痛点说起去年下半年开始我所在的团队陆续把日常开发流程往 AI 辅助的方向迁移。最开始大家用的是各种在线的 AI 编程助手写代码、改 bug、生成文档确实快了不少。但用了一段时间之后问题就暴露出来了这些工具大多只能“聊天”不能真正“干活”。你让它帮你改一个文件它给你一段代码你还得自己复制粘贴你让它帮你跑个测试它只能告诉你“你可以运行 npm test”然后就没有然后了。后来接触到 WorkBuddy 这类产品思路一下子被打开了——原来 AI 助手可以不只是聊天窗口而是能真正接管本地工作目录、执行命令、读写文件、调用外部工具的“工作台”。这个概念非常吸引我但实际用下来又有几个绕不开的坎一是闭源产品没法深度定制我想加一个自己团队内部的工具链支持根本无从下手二是数据要经过第三方服务器公司内部项目根本不敢往上放三是很多功能是“黑盒”出了问题只能等官方修自己一点办法都没有。于是我就萌生了一个念头能不能自己做一个开源版本把核心的工作台能力保留下来同时把扩展性和可控性做到极致这就是 WorkDSH 的起点。DSH 是 DeepSeek Harness 的缩写我选择以 DeepSeek 系列模型作为默认的推理后端同时通过 MCP 协议对接各种外部工具形成一个可插拔、可自托管、完全开源的工作台。1.2 WorkDSH 到底能做什么说人话就是WorkDSH 是一个跑在你本地的 AI 工作台它能理解你的自然语言指令然后自主决定调用哪些工具、读写哪些文件、执行哪些命令最终把任务完成。它不是简单的聊天机器人而是一个有“手”和“脚”的智能体运行时。具体来说它目前支持这几类核心能力文件系统操作读取、写入、编辑、搜索本地文件支持按 glob 模式批量匹配支持忽略规则比如 node_modules、.git 这些默认跳过。命令执行在受控环境下执行 shell 命令支持超时控制、输出截断、危险命令拦截。MCP 工具接入通过 Model Context Protocol 对接任意外部工具服务比如浏览器自动化、数据库查询、API 调用等。多轮任务编排把一个复杂任务拆解成多个步骤逐步执行每步都能看到中间结果支持中断和恢复。会话持久化所有对话和工具调用记录都保存在本地 SQLite随时可以回溯。适合谁来用我觉得有三类人特别合适一是想深度定制自己 AI 工作流的开发者二是对数据隐私有要求、不能把代码传到云端的团队三是想学习 AI Agent 底层实现原理的技术爱好者。如果你只是想要一个开箱即用的聊天工具那 WorkDSH 可能不是最优选择但如果你想搞清楚“AI 到底是怎么调用工具的”那这个项目值得你花时间。1.3 技术选型背后的思考在动手之前我花了大概两周时间做技术调研。核心要解决的问题有三个用什么语言写、用什么模型、怎么对接工具。语言方面我最终选了 TypeScript Node.js。原因很直接MCP 协议的官方 SDK 就是 TypeScript 写的生态最成熟而且 Node.js 的异步 IO 模型天然适合处理“模型流式输出 工具并发调用”这种场景。我也考虑过 Python但 Python 的 MCP SDK 当时还不够稳定而且打包分发比较麻烦。Go 的话性能好但开发效率会低一些对于个人项目来说不划算。模型方面默认接 DeepSeek 系列。选它有两个原因一是推理能力强尤其是代码理解和工具调用格式的遵循度很高二是 API 价格相对友好个人开发者也能承受。当然WorkDSH 的模型层是抽象过的你可以换成任何兼容 OpenAI 接口的模型包括本地部署的。工具对接方面我毫不犹豫选了 MCP。这个协议的核心价值在于标准化——以前每接一个工具都要写一套适配代码现在只要工具实现了 MCP ServerWorkDSH 就能直接调用。这就像 USB 接口统一了外设连接一样MCP 统一了 AI 和工具之间的连接方式。提示MCP 全称 Model Context Protocol是一个开放协议定义了 AI 应用如何与外部工具、数据源进行标准化通信。它分为 Server 端提供工具和 Client 端调用工具WorkDSH 扮演的是 Client 角色。2. 核心架构拆解WorkDSH 是怎么运转的2.1 整体分层设计WorkDSH 的架构我分了四层从下到上依次是基础设施层、工具层、智能体层、交互层。这样分层的好处是每一层职责清晰替换任何一层都不影响其他层。基础设施层负责最底层的脏活累活文件读写、命令执行、SQLite 持久化、日志记录、配置管理。这一层不涉及任何 AI 逻辑就是纯粹的系统能力封装。我特意把这层做得足够厚因为后面所有功能都依赖它如果这层不稳上面全是空中楼阁。工具层是 MCP 协议的实现部分包括 MCP Client 的连接管理、工具注册表、工具调用路由、结果格式化。每个外部工具通过 MCP Server 暴露自己的能力WorkDSH 在启动时扫描配置把所有可用的工具注册到一张表里模型在推理时就能看到这些工具的描述和参数 schema。智能体层是整个系统的“大脑”负责接收用户输入、构造提示词、调用模型、解析模型的工具调用请求、执行工具、把结果回传给模型、循环直到任务完成。这一层最复杂也是我调试时间最长的地方。交互层目前提供了 CLI 和 Web 两种界面。CLI 适合快速使用和脚本集成Web 界面适合可视化查看任务执行过程。两种界面共享同一套核心逻辑只是展示方式不同。2.2 智能体循环的核心机制WorkDSH 的核心是一个ReAct 风格的循环Reason推理→ Act行动→ Observe观察→ 再 Reason直到任务完成或达到最大轮次。具体流程是这样的用户输入一个任务比如“帮我把 src 目录下所有 console.log 删掉”。WorkDSH 首先把这句话和当前可用的工具列表一起发给模型。模型返回的不是直接答案而是一个结构化的工具调用请求比如{ tool: search_files, params: { pattern: **/*.ts, content: console.log } }。WorkDSH 解析这个请求调用对应的工具拿到结果后再把结果塞回对话历史再次请求模型。模型看到搜索结果后可能会发起新的工具调用比如edit_file如此循环直到模型认为任务完成返回最终答复。这个循环有几个关键设计点最大轮次限制默认 25 轮防止模型陷入死循环。超过就强制终止并返回当前进度。工具调用去重如果模型连续两次发起完全相同的工具调用系统会拦截并提示模型“这个操作已经执行过了”。错误恢复工具执行失败时错误信息会作为观察结果回传给模型让模型自己决定是重试、换方案还是放弃。流式输出模型的思考过程和工具调用结果都是流式推送到界面的用户能实时看到进展。2.3 MCP 工具接入的完整链路MCP 的接入是 WorkDSH 最有价值的部分我详细说一下链路。首先你需要在配置文件里声明 MCP Server 的连接方式。目前支持两种传输stdio本地进程通过标准输入输出通信和SSE远程服务通过 HTTP 长连接通信。stdio 适合本地工具比如文件系统、Git 操作SSE 适合远程服务比如云端 API。配置大概长这样{ mcpServers: { filesystem: { command: npx, args: [-y, modelcontextprotocol/server-filesystem, /path/to/workspace] }, playwright: { command: npx, args: [-y, playwright/mcp-server] } } }WorkDSH 启动时会读取这个配置为每个 Server 建立一个连接然后调用 MCP 协议的tools/list方法获取该 Server 提供的所有工具。每个工具都有名称、描述、参数 schema。这些信息会被汇总成一张工具表在每次请求模型时作为系统提示的一部分传进去。模型决定调用某个工具时WorkDSH 通过 MCP 的tools/call方法把参数发过去Server 执行完返回结果WorkDSH 再把结果格式化后回传给模型。整个过程对模型来说是透明的它只需要知道“有哪些工具可用”和“怎么调用”不需要关心底层是本地进程还是远程服务。注意MCP Server 的进程管理是个坑。如果 Server 崩溃了WorkDSH 需要能检测到并自动重启否则后续所有工具调用都会失败。我在实现里加了心跳检测和自动重连机制。2.4 会话持久化的设计取舍所有对话和工具调用记录都存在本地 SQLite 里。为什么选 SQLite 而不是 JSON 文件因为会话数据是结构化的有会话表、消息表、工具调用表用关系型数据库查询和关联都方便得多。而且 SQLite 零配置、单文件、跨平台非常适合本地应用。表结构设计上我用了三张核心表sessions存会话元信息ID、标题、创建时间、最后活跃时间messages存每条消息角色、内容、时间戳、所属会话tool_calls存每次工具调用工具名、参数、结果、状态、耗时、所属消息。这样设计的好处是你可以随时查询“某个会话里调用了哪些工具”“某个工具的平均耗时是多少”为后续优化提供数据支撑。持久化还带来一个额外好处任务可以中断后恢复。如果 WorkDSH 在执行一个长任务时被关闭下次启动可以从上次中断的地方继续不用从头再来。3. 从零搭建 WorkDSH 的实操过程3.1 环境准备与依赖安装先说环境要求。Node.js 版本至少 20.x因为用到了一些较新的 API。包管理器我推荐 pnpm速度快、磁盘占用小。操作系统方面macOS、Linux、Windows 都支持但 Windows 上有些 shell 命令的差异需要额外处理。安装步骤不复杂git clone https://github.com/yourname/workdsh.git cd workdsh pnpm install pnpm build构建完成后你需要创建一个配置文件。WorkDSH 默认从~/.workdsh/config.json读取配置你也可以通过环境变量WORKDSH_CONFIG指定其他路径。配置文件的核心字段包括字段说明必填model.provider模型提供商默认 deepseek是model.apiKeyAPI 密钥是model.baseUrlAPI 地址可替换为兼容接口否model.name模型名称如 deepseek-chat是workspace工作目录绝对路径是mcpServersMCP Server 配置对象否maxRounds最大推理轮次默认 25否commandTimeout命令执行超时秒数默认 60否一个最小可用的配置示例{ model: { provider: deepseek, apiKey: sk-xxxxxxxx, name: deepseek-chat }, workspace: /Users/me/projects/myapp, maxRounds: 25 }提示API 密钥建议通过环境变量注入不要直接写在配置文件里。WorkDSH 支持${ENV_VAR}语法比如apiKey: ${DEEPSEEK_API_KEY}。3.2 第一个任务让 WorkDSH 帮你整理项目配置好之后直接运行pnpm start就能进入交互界面。我拿一个真实场景来演示让 WorkDSH 帮我找出项目里所有未使用的依赖。输入指令“分析 package.json 里的依赖找出哪些在 src 目录下没有被引用过。”WorkDSH 的执行过程大致是这样的第一步它调用read_file读取 package.json解析出 dependencies 和 devDependencies 列表。第二步它调用search_files在 src 目录下搜索每个依赖名的引用情况。第三步它把搜索结果汇总判断哪些依赖在所有文件中都找不到引用。第四步它输出一份报告列出疑似未使用的依赖并给出删除建议。整个过程大概花了 40 秒调用了 3 类工具、总共 15 次工具调用。最终它准确找出了 4 个确实没用的依赖我手动验证后确认无误。这个例子说明了一个关键点WorkDSH 的价值不在于单次工具调用有多强而在于它能自主编排多个工具完成一个复合任务。你不需要告诉它“先读文件、再搜索、再汇总”它自己会规划。3.3 接入 Playwright MCP 实现浏览器自动化这是我觉得最酷的部分。通过 MCP 接入 PlaywrightWorkDSH 就能操控浏览器了。首先安装 Playwright MCP Serverpnpm add -g playwright/mcp-server然后在配置文件的mcpServers里加上{ mcpServers: { playwright: { command: npx, args: [-y, playwright/mcp-server] } } }重启 WorkDSH 后你会发现工具列表里多了十几个浏览器相关工具browser_navigate、browser_click、browser_type、browser_screenshot等等。现在你可以输入这样的指令“打开 example.com截图首页然后把页面标题提取出来。”WorkDSH 会依次调用browser_navigate打开页面调用browser_screenshot截图保存到工作目录调用browser_evaluate执行 JS 提取标题最后把结果返回给你。整个过程你只需要说一句话。我实测下来这套组合特别适合做端到端测试的辅助。比如你可以让 WorkDSH“打开登录页输入测试账号点击登录检查是否跳转到首页”它就能自动完成这一系列操作比手写 Playwright 脚本快得多。3.4 自定义 MCP Server 扩展能力如果现成的 MCP Server 不够用你完全可以自己写一个。MCP 协议本身不复杂核心就是实现两个方法tools/list返回工具列表tools/call执行工具。我用一个简单的例子说明假设你想让 WorkDSH 能查询公司内部的 API。你可以写一个 MCP Server暴露一个query_internal_api工具参数是 endpoint 和 query。Server 内部用公司的认证方式调用 API把结果返回。用 TypeScript 写的话大概是这样import { Server } from modelcontextprotocol/sdk/server/index.js; import { StdioServerTransport } from modelcontextprotocol/sdk/server/stdio.js; const server new Server({ name: internal-api, version: 1.0.0 }); server.setRequestHandler(tools/list, async () ({ tools: [{ name: query_internal_api, description: 查询公司内部 API, inputSchema: { type: object, properties: { endpoint: { type: string }, query: { type: object } }, required: [endpoint] } }] })); server.setRequestHandler(tools/call, async (request) { if (request.params.name query_internal_api) { const result await callInternalApi(request.params.arguments); return { content: [{ type: text, text: JSON.stringify(result) }] }; } }); const transport new StdioServerTransport(); await server.connect(transport);写完之后在 WorkDSH 配置里指向这个脚本就行。这种扩展方式的好处是完全解耦——你的内部 API 逻辑不需要改 WorkDSH 一行代码WorkDSH 也不需要知道你的认证细节。4. 踩坑记录与常见问题排查4.1 模型不按格式调用工具怎么办这是最常见的问题。模型有时候会“忘记”用工具调用格式而是直接在回复里写一段自然语言描述它想做什么。比如它可能说“我需要读取 package.json 文件”而不是发起一个read_file调用。我的解决思路是双重保险一是在系统提示里用非常明确的格式说明工具调用的要求并给出正例和反例二是在解析模型输出时如果检测到没有工具调用但回复里提到了文件操作意图就追加一轮提示明确要求它“请使用工具调用格式重新表达你的意图”。实测下来DeepSeek 系列模型对工具调用格式的遵循度相当高加上这两层保险后格式错误率降到了 1% 以下。4.2 工具执行超时和卡死命令执行是最容易出问题的环节。有些命令会一直挂在那里不返回比如等待输入的交互式命令或者网络请求卡住。我的处理方案是三层防护第一层是超时控制每个命令默认 60 秒超时超时后强制 kill 进程第二层是输出截断如果命令输出超过 100KB只保留前后各 50KB防止内存爆掉第三层是危险命令黑名单像rm -rf /、format、shutdown这类命令直接拦截不执行。注意危险命令黑名单只能防君子不能防小人。如果你让模型执行一个脚本脚本内部再执行危险操作黑名单是拦不住的。所以 WorkDSH 默认建议在容器或虚拟机里运行不要直接在宿主机上跑。4.3 MCP Server 连接失败的排查MCP Server 连不上是另一个高频问题。我整理了一个排查清单现象可能原因解决方法启动时报 command not found命令路径不对或未安装用绝对路径或先全局安装连接后工具列表为空Server 未正确实现 tools/list用 MCP Inspector 单独测试 Server调用工具时报 timeoutServer 处理太慢或卡死增加超时时间检查 Server 日志SSE 连接频繁断开网络不稳定或心跳缺失检查网络确认 Server 支持心跳工具调用返回权限错误工作目录权限不足检查目录读写权限我强烈推荐用官方的 MCP Inspector 工具单独测试每个 Server确认它能正常工作后再接入 WorkDSH。这样能把问题隔离在 Server 侧不用在 WorkDSH 里大海捞针。4.4 上下文窗口爆炸的处理长任务跑久了对话历史会越来越长最终超出模型的上下文窗口。这时候模型会开始“失忆”忘记前面的关键信息。我的应对策略是滑动窗口 摘要压缩。当对话历史超过阈值比如 80% 上下文窗口时把最早的一部分消息用模型压缩成一段摘要保留关键信息比如已完成的步骤、重要的文件路径、用户的特殊要求丢弃冗余的工具输出。这样既控制了长度又不丢失核心上下文。这个机制我调了好几版才稳定。早期版本压缩太激进导致模型忘记用户的核心诉求后来改成保留最近 N 轮完整对话 早期摘要效果就好多了。4.5 几个提升体验的小技巧最后分享几个我实际用下来觉得很有用的技巧。技巧一给任务加明确的验收标准。比如不要说“优化一下代码”而要说“把 src/utils 下所有函数的圈复杂度降到 10 以下”。模型有了明确的验收标准执行起来会更有方向也更容易判断何时该停止。技巧二善用工作目录的忽略规则。在 workspace 根目录放一个.workdshignore文件把 node_modules、dist、.git 这些目录排除掉。这样模型搜索文件时不会浪费时间在无关内容上速度能快好几倍。技巧三把常用任务写成模板。WorkDSH 支持从文件读取指令你可以把“每周代码审查”“依赖更新检查”这类重复任务写成 markdown 模板需要时直接workdsh run review.md省去每次重新描述。技巧四定期清理会话数据库。SQLite 文件会随着使用不断增大我一般每个月清理一次超过 30 天的旧会话。WorkDSH 提供了workdsh clean --older-than 30d命令一条命令搞定。技巧五模型选择要因任务而异。简单的文件操作和搜索用便宜快速的模型就够了复杂的代码重构和架构分析再切换到推理能力更强的模型。WorkDSH 支持在会话中动态切换模型不用重启。这套东西我从零开始搭了大概三个月中间踩了无数坑但最终跑通的那一刻还是很爽的。现在它已经成了我日常开发流程里离不开的工具每天帮我处理各种琐碎任务。如果你也想动手做一个我的建议是先从最小可用版本开始——能读文件、能执行命令、能调用一个 MCP 工具这三件事跑通剩下的就是不断迭代了。
返回列表