
1. 这个改造到底在折腾什么第一次看到“80行 TypeScript Mods 把 Claude Code 改造成 Deepseek Harness”这个说法我脑子里蹦出来的第一个念头是这又是一个拿命令行工具开刀的玩法。Claude Code 本身是一个跑在终端里的智能编码助手它通过读取项目文件、执行命令、调用模型接口来完成代码生成和修改任务。而 Deepseek Harness 这个词在圈子里通常指的是一套围绕 Deepseek 模型构建的本地化调用框架——说白了就是把模型能力封装成可编排、可拦截、可扩展的“马具”让上层工具能像骑马一样驾驭模型输出。这个项目的核心思路非常直接不去改 Claude Code 的源码也不去重新编译整个工具链而是利用它对外暴露的 Mods 机制用大约 80 行 TypeScript 写一个中间层把原本发往某个模型服务的请求重定向到 Deepseek 的接口上同时对请求和响应做一层适配。这样做的好处是你依然保留 Claude Code 的交互体验、文件操作能力和命令执行逻辑但底层跑的模型换成了 Deepseek。对于想用 Deepseek 做代码辅助、又舍不得 Claude Code 那套顺手交互的人来说这几乎是最小成本的方案。适合谁来参考如果你已经用过 Claude Code知道怎么在终端里跟它对话、怎么让它改文件同时对 TypeScript 有一定了解能看懂接口定义和异步请求那这个改造对你来说就是一顿饭的功夫。如果你完全没接触过命令行编码助手也没关系我会把每一步拆开讲包括为什么要这么改、参数怎么填、哪里容易翻车。提示本文提到的所有工具、模型、接口均为技术讨论实际使用时请确保你的调用方式符合相关服务的使用条款。2. 为什么选择 Mods 而不是 Fork 源码2.1 改源码的代价有多大很多人第一反应是直接去改 Claude Code 的源码把里面调用模型的地方替换掉。我试过类似的路子结论是不划算。原因有三。第一Claude Code 的代码库更新频率不低你改完一版下次它升级你的修改要么被覆盖要么产生冲突维护成本极高。第二它的模型调用逻辑往往和上下文管理、工具调用、流式输出等模块耦合在一起你动一处可能影响一片调试起来非常痛苦。第三改源码意味着你要重新构建整个项目依赖安装、编译环境、版本对齐每一步都可能卡住。而 Mods 机制的设计初衷就是让外部开发者能在不侵入核心逻辑的前提下对行为进行扩展或替换。它通常提供钩子函数、中间件管道或者配置注入点让你在请求发出前、响应返回后插入自己的处理逻辑。80 行代码能搞定的事情没必要去动几千行的源码。2.2 Mods 机制的核心优势用 Mods 做改造最大的好处是隔离性。你的 TypeScript 文件独立于主程序存在升级 Claude Code 时只要 Mods 的接口签名没变你的代码就不用动。即使接口有调整改动的范围也局限在你自己的文件里不会波及整个工具链。第二个优势是可测试性。你可以单独写单元测试来验证请求转换逻辑比如给定一个 Claude Code 发出的请求对象检查转换后的 Deepseek 请求是否符合预期。这种测试在源码改造模式下很难做因为你要模拟整个运行环境。第三个优势是可逆性。哪天你想换回原来的模型或者想试试别的模型只需要修改 Mods 里的配置或替换转换逻辑不用回滚代码库。这种灵活性在快速实验阶段特别重要。2.3 80 行代码的边界在哪里80 行 TypeScript 能做的事情有限它主要覆盖的是请求地址替换、请求体格式转换、响应体格式转换这三件事。它不会去改 Claude Code 的 UI不会去动文件读写逻辑也不会去重新实现工具调用协议。换句话说它只负责“把话递对地方把回话翻译回来”其他的都交给原有框架。这也意味着如果你的需求超出了这个范围比如你想让 Deepseek 支持 Claude Code 特有的某种工具调用格式而 Deepseek 原生不支持那 80 行可能就不够了你需要额外的适配层。但在大多数常规编码辅助场景下请求和响应的结构差异并不大80 行足够覆盖核心逻辑。3. 核心细节拆解请求与响应怎么转3.1 请求侧从 Claude Code 格式到 Deepseek 格式Claude Code 发出的请求通常包含几个关键字段模型名称、消息列表、温度参数、最大输出长度、是否流式返回等。Deepseek 的接口在这些字段上大同小异但命名和结构可能有差异。比如Claude Code 可能用max_tokens而 Deepseek 用max_output_tokens消息列表里角色的命名也可能不同一个用assistant另一个用ai。转换逻辑就是做字段映射。你需要写一个函数接收原始请求对象返回一个新的对象字段名和结构按照 Deepseek 的要求来。这里有个细节如果原始请求里包含 Claude Code 特有的字段而 Deepseek 不认识你要么删掉要么放到一个扩展字段里避免接口报错。function transformRequest(original: ClaudeRequest): DeepseekRequest { return { model: deepseek-chat, messages: original.messages.map(m ({ role: m.role assistant ? assistant : user, content: m.content })), max_tokens: original.max_tokens || 4096, temperature: original.temperature ?? 0.7, stream: original.stream ?? false }; }上面这段代码就是最核心的转换逻辑。注意??和||的区别??只在值为null或undefined时用默认值而||会在值为0、等假值时也替换。温度参数可能被设为 0这时候用||就会错误地变成 0.7所以用??更安全。3.2 响应侧从 Deepseek 格式回到 Claude Code 格式响应转换是反向操作。Deepseek 返回的数据结构里生成的内容可能在choices[0].message.content而 Claude Code 期望的可能是completion或者text字段。你需要把内容提取出来重新组装成 Claude Code 能识别的对象。如果涉及流式返回事情会稍微复杂一点。流式响应是一系列数据块每个块包含一小段生成内容。你需要逐个块转换确保每个块的结构都符合 Claude Code 的预期。这里容易踩的坑是Deepseek 的流式块可能包含额外的元数据字段而 Claude Code 的解析器遇到未知字段可能会报错所以转换时要过滤掉不需要的字段。function transformResponse(original: DeepseekResponse): ClaudeResponse { const content original.choices?.[0]?.message?.content ?? ; return { completion: content, stop_reason: original.choices?.[0]?.finish_reason ?? stop, model: original.model }; }这段代码处理的是非流式响应。流式响应的转换需要放在一个循环里对每个数据块调用类似的转换函数然后重新序列化发送给 Claude Code 的解析器。3.3 错误处理与超时配置请求发出去不一定能顺利回来。网络抖动、接口限流、模型过载都可能导致失败。你的 Mods 里需要包含基本的错误处理逻辑捕获请求异常判断是超时还是服务端错误然后决定是重试还是直接返回错误信息给 Claude Code。超时配置也很关键。Claude Code 默认的超时时间可能不适合 Deepseek 的响应速度尤其是当你在处理长文本生成时。你可以在 Mods 里覆盖这个超时值比如从默认的 30 秒调整到 120 秒。但要注意超时设得太长用户会以为程序卡死设得太短长任务会被频繁中断。根据我的经验60 到 90 秒是一个比较平衡的区间。注意错误处理里不要直接把原始错误堆栈抛给 Claude Code那样用户看到的是一堆看不懂的报错。最好包装成友好的提示比如“模型服务暂时不可用请稍后重试”。4. 实操过程从零到跑通的完整步骤4.1 环境准备与依赖安装开始之前你需要确认几件事。第一Claude Code 已经安装并能正常运行。第二你的机器上有 Node.js 和 npm因为 TypeScript 需要编译。第三你有一个可用的 Deepseek API 密钥并且知道接口地址。依赖方面通常需要安装typescript和ts-node前者用于编译后者用于直接运行 TypeScript 文件。如果你打算把 Mods 集成到 Claude Code 的启动流程里可能还需要一个打包工具比如esbuild把 TypeScript 编译成 JavaScript 后放到指定目录。npm install -g typescript ts-node npm install --save-dev types/node这两条命令全局安装 TypeScript 和 ts-node然后在项目里安装 Node.js 的类型定义。类型定义很重要它让你在写代码时能获得自动补全和类型检查减少低级错误。4.2 编写 Mods 文件的关键步骤创建一个名为deepseek-mod.ts的文件然后按照以下结构组织代码。第一步导入必要的类型和工具函数。第二步定义请求和响应的接口明确每个字段的类型。第三步实现转换函数。第四步注册 Mods 钩子把转换函数挂到请求发出前和响应返回后的时机上。注册钩子的方式取决于 Claude Code 的 Mods API。常见的形式是调用一个registerMod函数传入一个对象对象里包含onRequest和onResponse两个方法。onRequest在请求发出前执行你在这里做请求转换onResponse在响应返回后执行你在这里做响应转换。import { registerMod } from claude-code-mods; registerMod({ name: deepseek-harness, onRequest: async (req) { const transformed transformRequest(req); transformed.baseURL https://api.deepseek.com/v1; transformed.apiKey process.env.DEEPSEEK_API_KEY; return transformed; }, onResponse: async (res) { return transformResponse(res); } });这段代码里baseURL和apiKey是从环境变量里读取的这样你就不用把密钥硬编码在文件里。硬编码密钥是大忌一旦文件被分享出去密钥就泄露了。用环境变量既安全又灵活换密钥时不用改代码。4.3 参数计算与配置调优Deepseek 的接口有一些参数需要你根据实际情况调整。比如max_tokens它决定了模型一次最多生成多少内容。设得太小代码生成到一半就断了设得太大响应时间会变长而且可能浪费额度。对于代码辅助场景我一般设 4096 到 8192 之间。如果是在做长文件重构可以临时调到 16384但要注意接口是否支持这么高的上限。温度参数temperature控制输出的随机性。写代码时我希望模型输出稳定、可预测所以温度设低一点比如 0.2 到 0.4。如果是在做创意性的代码注释或者文档生成可以适当调高到 0.7 左右。这个参数没有绝对标准多试几次就能找到适合你任务的值。还有一个容易被忽略的参数是top_p它和温度配合使用控制候选词的采样范围。通常保持默认值 1.0 就行除非你发现输出质量不稳定可以尝试降到 0.9 或 0.95。参数推荐值适用场景注意事项max_tokens4096-8192常规代码生成超过 8192 需确认接口支持temperature0.2-0.4代码补全、重构过高会导致输出不稳定top_p0.9-1.0通用场景与温度配合调整timeout60000-90000长文本生成单位毫秒过短会频繁超时4.4 跑通验证与日志排查配置完成后启动 Claude Code随便输入一个编码任务比如“帮我写一个快速排序函数”。观察终端输出如果一切正常你应该能看到 Deepseek 生成的代码。如果报错先看错误信息里有没有“401”或“403”这通常是密钥问题如果有“404”检查接口地址是否写对如果有“429”说明请求太频繁需要降低调用频率或增加重试间隔。日志是你最好的朋友。在 Mods 里加几行console.log把转换前后的请求和响应打印出来对比一下结构是否一致。很多时候问题就出在某个字段名拼错了或者某个必填字段漏了。日志不用一直开着调试阶段打开跑通后关掉就行免得刷屏。5. 常见问题与排查技巧实录5.1 请求发出后没有响应怎么办这是最常见的问题。先检查网络连通性确认你的机器能访问 Deepseek 的接口地址。然后检查密钥是否有效可以在终端里用curl手动发一个最简单的请求测试。如果手动请求能通但 Claude Code 里不通那问题就出在 Mods 的转换逻辑上。重点检查请求体里有没有多余的字段或者字段类型不对比如把数字写成了字符串。另一个可能的原因是流式配置不匹配。Claude Code 可能默认开启流式而 Deepseek 的流式接口需要额外的头部或者参数。你可以在 Mods 里强制把stream设为false先跑通非流式再逐步调试流式。5.2 响应内容被截断或乱码内容截断通常和max_tokens有关。如果模型生成到一半停了检查这个值是不是设得太小。另外有些接口在流式模式下最后一个数据块可能不包含完整内容你需要把所有块拼接起来再返回给 Claude Code。如果拼接逻辑写错了就会丢内容。乱码问题多半是编码格式不统一。确保你的请求和响应都使用 UTF-8 编码。在 Node.js 里默认就是 UTF-8但如果你手动处理了 Buffer可能会引入编码错误。检查一下有没有对响应体做了不必要的二进制转换。5.3 工具调用功能失效Claude Code 支持工具调用比如让它执行一个 shell 命令或者读取某个文件。这个功能依赖于模型返回特定格式的工具调用请求。Deepseek 可能不支持完全相同的格式导致 Claude Code 无法识别。解决办法是在响应转换时把 Deepseek 的工具调用格式映射成 Claude Code 期望的格式。如果 Deepseek 根本不支持工具调用那这个功能就只能放弃或者你在 Mods 里模拟一个工具调用层把 Deepseek 的普通文本输出解析成工具调用请求。提示工具调用的适配是 80 行代码之外的事情如果你重度依赖这个功能可能需要额外写几百行解析逻辑。5.4 常见问题速查表现象可能原因排查方法解决思路无响应网络不通/密钥无效curl 手动测试检查网络和密钥401 错误密钥错误或过期查看密钥状态更换有效密钥404 错误接口地址错误核对 baseURL修正接口路径429 错误请求频率过高查看调用间隔增加重试延迟内容截断max_tokens 过小检查参数值调大 max_tokens乱码编码不一致检查 Buffer 处理统一使用 UTF-8工具调用失效格式不兼容对比请求结构增加格式映射层6. 实操心得与避坑经验6.1 密钥管理别偷懒我见过太多人把 API 密钥直接写在代码里然后不小心提交到了公开仓库。一旦泄露别人就能用你的额度甚至可能产生费用。正确的做法是用环境变量或者用一个.env文件并把.env加到.gitignore里。如果你在团队里共享配置用密钥管理服务别用聊天工具发密钥。6.2 先跑通再优化很多人一上来就想把流式、工具调用、错误重试全部做完美结果卡在某个细节上迟迟跑不通。我的建议是先用最简单的非流式请求跑通整个链路确认请求能发出去、响应能回来、Claude Code 能正常显示。然后再逐步加上流式、重试、工具调用适配。每一步只改一个变量这样出问题时容易定位。6.3 日志要加时间戳调试时打印日志一定要带上时间戳。否则当请求和响应交错出现时你根本分不清哪个响应对应哪个请求。时间戳加上请求 ID能让你快速追踪一次完整调用。请求 ID 可以用随机字符串生成在请求发出时打印一次在响应返回时再打印一次两边一对就能串起来。6.4 版本兼容性要留意Claude Code 和 Deepseek 的接口都可能更新。你的 Mods 代码里最好把接口版本号写成配置项而不是硬编码在 URL 里。这样当接口升级时你只需要改配置不用改代码。另外定期检查 Claude Code 的 Mods API 有没有变化如果签名变了你的代码可能编译不过提前发现比运行时崩溃要好。6.5 性能监控不能少跑通之后记录一下每次请求的耗时。如果发现响应时间越来越长可能是模型服务端负载高了也可能是你的网络环境变差了。有了监控数据你就能判断是暂时波动还是趋势性恶化。简单的做法是在日志里打印请求开始和结束的时间差复杂一点可以接入一个轻量级的监控工具把耗时数据可视化。这个改造方案的核心价值在于用最小的代码量撬动了模型切换的能力。80 行 TypeScript 听起来不多但每一行都落在关键路径上请求转换、响应转换、错误处理、配置注入。你把这几块写扎实了整个链路就稳了。后续如果想支持更多模型只需要增加转换函数不用动框架本身。我在实际使用中发现最耗时间的不是写代码而是调试接口格式的差异。所以建议你在动手之前先把两个接口的文档对照看一遍把字段映射关系列成表格写代码时直接照着填能省下不少来回试错的时间。