
跑通了 DeepSeek Harness 的第一个 Demo 之后我一度觉得很爽——模型能按我给的提示词完成任务了。但紧接着就陷入另一个尴尬它只能在我的“语言世界”里打转读不到数据库、查不了设计稿、没法调用内部系统。说白了Harness 如果只是接大模型那和直接开网页版聊天没区别。真正让它值钱的是那一整套“接入外部世界”的能力工具、MCP、Skill。这一篇就把这三件事彻底讲透毕竟“入门很简单”系列已经走到第三篇前面两篇我们把基础概念和跑通流程都聊过了这一篇直接上最常用的接入实操。1. 为什么第三篇必须聊“接入”Harness 的价值不在孤岛里1.1 先花两分钟回顾 Harness 是干什么的DeepSeek Harness 本质上是给大模型套的一层“编排框架”。大模型本身只会根据上下文生成文本你问它“上海明天天气怎么样”它能编得有模有样但它并不知道真实天气。Harness 起的作用就是给模型装上眼睛、耳朵和手——让它能发起工具调用、查询外部数据、执行具体动作然后根据返回结果继续思考。前两篇里我们讲过怎么安装、怎么跑起一个最简单的 Agent 任务。那篇文章里大家基本都会用“提示词 模型回复”这种最朴素的方式。但老实说只做这一步Harness 还停留在“玩具”阶段。你想想任何真实工作流里模型都不是孤立存在的它要读文件、要查状态、要调接口、要跟其他系统交换数据。所以从这一篇开始我们正式进入“能用”的阶段。1.2 “接入”在 Harness 里到底指哪几件事很多人一听“接入”就发怵觉得是不是要写一堆底层代码。其实在 Harness 的世界里“接入”就三条路工具Tools直接在 Harness 进程内定义的可调用函数模型通过函数名和参数来调用。适合封装一些本地的、轻量的能力。MCP Server通过 MCP 协议连接的外部服务。适合接入那些已经有人写好的、跑在独立进程里的数据源或工具比如 Figma 设计稿、本地数据库、网页搜索引擎。Skill一套结构化的“技能包”。它不只是给模型一个函数而是把提示词、工具调用策略、校验规则、示例打包在一起让模型在合适场景下自动使用。这三条路不是互斥的实际项目里经常混着用。我给一个很直白的比喻工具像你直接递给厨师一袋盐MCP 像是给他接通了超市的配货系统Skill 则是他脑子里那本“宫保鸡丁标准做法”的手册——知道什么时候放盐、什么时候颠勺、出锅前怎么判断火候。1.3 三种接入方式怎么选接入方式难度典型场景例子工具Tools低本地小功能、轻量数据查询查 SQLite、算哈希、读本地文件MCP Server中接入已有外部服务、多人协作Figma MCP、蓝湖 MCP、数据库 MCPSkill中高沉淀某个业务领域的完整经验周报生成、代码审查、数学建模新手最容易犯的错误是一上来就追求复杂的 MCP 和 Skill。我的建议是反过来先用工具跑通一个“模型调用函数 → 拿到结果 → 再回答”的闭环再慢慢升级到 MCP最后才用 Skill 把经验固化。这个顺序符合调试成本递增的规律每层都有稳定的地基。2. 从“会跑”到“会用”DeepSeek Harness 的插件与工具接入套路2.1 先弄清 Harness 的扩展点长什么样Harness 的扩展点其实不神秘核心就一个概念注册一个函数让模型知道它的存在、它的用途、它的参数结构。用过一次之后你就会发现模型能不能正确调用函数很大程度上取决于两件事一是函数名和描述是否清晰二是参数的 JSON Schema 是否准确。Harness 内部维护了一个“工具清单”每次和模型交互时这个清单会被序列化后塞进系统提示词里。模型从清单里挑一个函数、填好参数Harness 再在本地执行它。所以接入工具的第一步是“找到注册入口”。在 Python SDK 版本里通常是一个tool装饰器或者一个ToolRegistration类在桌面版里则是通过配置文件声明。2.2 装一个官方插件包试试水最省事的路径是用官方维护的插件包。以我们项目常用的harness-plugins-contrib为例pip install harness-plugins-contrib然后在配置里启用你需要的插件plugins: - name: web_search enabled: true - name: file_tools enabled: true启动 Harness 之后你在调试面板里能看到模型的消息里包含了一个巨大的工具列表。如果看到web_search相关的函数描述说明插件已经成功注册进模型上下文。这一步的核心收获是建立起“插件 一组预注册工具”的认知。插件不过是批量帮你注册函数的快捷方式自己写工具也是一样。2.3 自己封装一个工具从“查 SQLite 表结构”开始官方插件虽好但业务场景永远是私有的。我拿一个最常见、也特别容易上手的例子让模型能查询一个本地 SQLite 数据库的表结构。假设你手里有个myapp.db想让模型回答“订单表有哪些字段”模型自己是不知道的。那就写一个函数暴露给它import sqlite3 from deepseek_harness import tool tool( nameget_table_schema, description当用户询问数据库表结构、字段列表、建表语句时使用。参数是数据库路径和表名。 ) def get_table_schema(db_path: str, table_name: str) - str: conn sqlite3.connect(db_path) try: cursor conn.cursor() cursor.execute(fSELECT sql FROM sqlite_master WHERE typetable AND name?, (table_name,)) row cursor.fetchone() return row[0] if row else 表不存在 finally: conn.close()注册之后你可以在对话里问一句“帮我看看 myapp.db 里 orders 表的结构。”模型就会尝试调用get_table_schema把参数填成db_pathmyapp.db, table_nameorders拿到返回的建表 SQL再组织成自然语言回答你。这段代码的注意点有两个描述要写“什么时候用”而不是“这个函数是什么”。模型是靠描述来判断要不要调用它的。参数名要语义化。模型虽然是猜着填参数但它会尽量按名字推断所以db_path就比p1好一万倍。2.4 接入工具时最容易出错的三个坑第一个坑是注册名与函数名对不上。有些框架默认用函数名做注册名有些会要求单独指定name字段两边不一致时模型调用的函数名和实际执行函数名对不上直接报 FunctionNotFound。我自己就踩过后来养成了习惯每次注册后先在调试面板看一眼实际暴露出来的名字是什么。第二个坑是参数 Schema 写得太宽松。你要给模型明确的类型和约束。比如table_name必须写成string并且加description如果写成自由对象模型很容易填出奇怪结构。第三个坑是同步阻塞函数拖慢整个 Agent 循环。模型调用一个工具Agent 是要等它返回的。如果你在工具里做了一个 5 秒的同步网络请求整个会话会像卡死一样。我现在对任何可能耗时的操作都习惯性用异步函数或者至少在代码里加超时。关于这两点我多说一句很多人在接入自定义工具时总觉得“函数越强大越好”于是把一堆逻辑塞进一个函数里参数多达十几个。模型就算再聪明面对一个 12 个入参的工具也很容易填错或干脆不调用。更好的做法是拆成多个语义单一的小工具每个工具 2~4 个参数。3. MCP把外部数据喂给 Harness 的标准姿势3.1 MCP 到底解决的是什么问题MCP全称 Model Context Protocol是一套开放协议。你可以把它理解成 AI 世界的“USB-C 接口”——过去每个 AI 应用要对接某个外部服务都得写一套私有集成换个服务就得重写。MCP 把这条链路标准化了只要服务方实现了 MCP 协议任何支持 MCP 的主机都能直接连上去。回到 Harness 的场景我需要的不是再写一个get_figma_design_size的函数而是让 Harness 能连接上那些已经封装好的 MCP 服务连接之后服务方暴露的工具就自动变成了模型可调用的工具。这就是热词里什么大家都在搜 Figma MCP、蓝湖 MCP、DevSpace MCP 的原因——这些服务把自己日常使用的软件能力通过标准协议开放出来了。3.2 HOST、Client、Server三方角色搞清楚了就不会乱MCP 协议里最基础的概念模型是三段式MCP Host宿主程序也就是发起连接的应用程序。在咱们这Host 通常是 DeepSeek Harness 本体。MCP Server提供工具和数据的一方可以是一个独立进程、一个远程 HTTP 服务。MCP ClientHost 内部维护的连接器。Host 和每个 Server 之间都会建立一个 Client 实例负责协议通信。你不需要自己实现 ClientHarness 的 SDK 已经内置了 MCP 客户端。你要做的只是告诉 Harness去连哪个 Server、用什么协议、带什么鉴权信息。MCP 传输方式上最常见的两种是 stdio本地启动一个子进程通过标准输入输出通信和 HTTPSSE / 流式 HTTP通过网络连接到远程服务。本地开发的、需要读你本地文件的服务用 stdio部署在服务器上的团队级服务用 HTTP。3.3 社区生态里已经有什么好东西去 MCP 的官方注册表或者 GitHub 上翻一圈你会发现生态已经相当丰富了设计领域Figma MCP 可以读取设计稿的图层、尺寸、文本蓝湖 MCP 也做了类似的能力方便开发直接拿设计标注。开发领域DevSpace MCP 可以操作云原生开发环境数据库类的 MCP Server 能直接查表、执行 SQL。本地数据文件系统 MCP 可以在授权目录内读写文件还有一些把股票软件本地数据做成 MCP 的方便做量化分析的人通过对话直接取值。我特意提到这些是因为它们的接入方式是完全一致的。所以你不是在学“某个具体服务的接入方法”而是在学“一套协议”。学会了以后接任何新的 MCP Server也就是配置文件的差别。4. 从示例到生产把 MCP Server 接入 Harness 的完整实操4.1 两条接入路径声明式配置和代码注册Harness 对 MCP 的支持有两条路我用下来都觉得顺手。第一条是声明式配置。在 Harness 的配置文件里直接写一段mcp字段mcp: servers: figma: type: http url: https://your-figma-mcp.example.com/mcp headers: Authorization: Bearer ${FIGMA_MCP_TOKEN}这种做法的好处是配置清晰、不污染代码适合团队协作时把 MCP 清单统一管理。注意${FIGMA_MCP_TOKEN}这种占位符写法Harness 会从环境变量里读取真实值避免你把密钥硬编码进配置文件。第二条是代码注册。如果你在写一个独立脚本或服务更灵活的方式是直接在代码里挂载from deepseek_harness.mcp import McpServerConfig, add_mcp_server add_mcp_server( McpServerConfig( namefigma, typehttp, urlhttps://your-figma-mcp.example.com/mcp, headers{Authorization: fBearer {os.environ[FIGMA_MCP_TOKEN]}} ) )代码注册的好处是可以动态决定连哪些服务比如根据运行环境加载不同地址。4.2 实例一接入一个社区 MCP Server以 Figma MCP 为例Figma MCP 是社区里非常热门的接入对象。它的核心用途是让模型读取设计稿内容比如图层名称、画板尺寸、文本内容。对前端开发来说这意味着你可以让模型直接“看”设计稿然后生成对应代码。接入步骤大致如下准备一个 Figma Access Token在 Figma 账户设置 → Personal access tokens 里生成。找到你想读取的文件 ID文件 URL 里/file/后面那串。配置环境变量export FIGMA_MCP_TOKEN你的token。在 Harness 配置里加上前面那段figma的 MCP 配置。重启 Harness在调试面板里确认figma对应的工具已经出现在工具列表。对话测试“请帮我读取这个设计文件的画板尺寸https://www.figma.com/file/xxx”。这里最容易翻车的地方是 token 权限范围。Figma 的 personal access token 默认可能只读某些资源如果连接成功了但读取不了具体文件先去检查 token 有没有开启Files: Read权限。4.3 实例二自己写一个迷你 MCP Server社区服务覆盖不了所有场景自己写 MCP Server 其实没想象中难。以 Python 为例用官方 SDK 或 FastMCP 都很方便。我拿 FastMCP 写个几十行的示例from fastmcp import FastMCP mcp FastMCP(my-weather-server) mcp.tool() def get_temperature(city: str) - int: 返回指定城市的当前温度示例数据。 # 这里替换成真实天气 API 调用 data {北京: 23, 上海: 26, 广州: 29} return data.get(city, 20) if __name__ __main__: mcp.run()启动它python weather_server.py默认会以 stdio 方式运行。然后在 Harness 配置里这样连mcp: servers: weather: type: stdio command: python args: [weather_server.py] cwd: /path/to/your/project重启后问一句“北京现在多少度”模型就会从 weather 这个 MCP Server 里找到get_temperature工具并调用。自己写 Server 时有一个经验不要在工具函数里放太多业务实体类保持输入输出都是最朴素的 JSON 可序列化类型。因为 MCP 传输过程中所有数据都要经过序列化自定义对象很容易在这里卡住。4.4 调试 MCP 连接的通用排查清单无论接社区服务还是自己写的服务遇到问题都按这个清单过一遍能解决至少八成问题症状可能原因排查动作Harness 启动时提示连不上地址写错 / 服务没启动确认 URL 协议头、端口先手动 curl 一下stdio 方式启动后立即退出command 路径不对 / 依赖没装手动执行一遍python weather_server.py看报错连接成功但模型不调用工具描述太模糊进入调试面板看工具列表确认描述里有明确的触发词工具调用报鉴权失败token 过期 / 权限不足检查环境变量确认服务端日志工具超时远程服务响应慢 / 网络问题调高 MCP 客户端超时时间或更换传输方式关于超时我多说一句有些远程 MCP Server 第一个请求特别慢要在服务端初始化模型或加载资源Harness 默认的客户端超时可能不够。我通常把超时配置开到 30 秒以上等到第一个请求成功后后面的就快很多。5. Skill把“会做”变成“熟练做”的关键一跃5.1 Skill 和普通 Prompt、Tool、Agent 究竟差在哪很多人看到 Skill 这个词就联想到“插件”。实际上 Skill 是一种更偏“方法论”的封装。和 Prompt 的区别普通 Prompt 是一次性的对话指令写在聊天框里用完就没了。Skill 是结构化的文件包含元信息、步骤说明、示例、甚至参考脚本。它可以被检索、被复用、被版本管理。和 Tool 的区别Tool 是一个可执行函数模型直接调用返回结果。Skill 更像一份“作战手册”它告诉模型在某个场景下“要按什么步骤思考、先调用哪个工具、如何判断结果是否可用”。和 Agent 的区别Agent 是运行时动态规划的执行者Skill 是静态的经验沉淀。Agent 可以同时加载多个 Skill在具体任务里决定先拿起哪本手册、执行到哪一步再换另一本。用一个职场类比Tool 是一台打印机Agent 是员工Skill 是员工的 SOP 文档。员工Agent接到任务后查 SOPSkill然后决定去用打印机Tool。5.2 Skill 的典型目录结构在 Harness 里一个 Skill 通常是一个目录my-skill/ ├── SKILL.md ├── instructions.md ├── examples/ │ └── sample-input.md ├── references/ │ └── checklist.md └── scripts/ └── verify.py其中SKILL.md是入口文件负责声明这个技能的元信息。一段标准的 SKILL.md 长这样--- name: weekly_report description: 当用户需要生成周报、周总结、本周工作汇报时使用。 trigger: 周报, 周总结, weekly report, 本周工作 version: 1.0.0 --- # 周报生成流程 1. 询问用户本周完成了哪几项任务。 2. 对每个任务补充可量化的结果用户没提供就提示补充。 3. 使用 report_template 工具生成 Markdown 格式周报。 4. 输出前检查每条任务是否包含「动作结果」。 示例和模板见 examples/ 目录。description和trigger最关键它们决定了模型能不能在合适的时机想起这个 Skill。很多人写的 description 太泛比如“生成文档”模型根本不知道什么时候该用。要写成“当用户需要……时使用”最好带上常见的同义表达。5.3 从 0 到 1 写一个 Skill 的完整流程我建议新手用“最小可用 Skill”来入门。比如写一个“项目复盘 Skill”创建目录 skills/retro/在里面放一个SKILL.md上面按格式写好名称、描述、触发词。instructions.md里写清楚执行步骤先收集项目目标和实际结果再逐项对照偏差最后输出复盘结论。如果这个 Skill 需要调用某个脚本就把脚本放在scripts/在instructions.md中告诉模型“先运行 verify 脚本检查数据是否完整”。把 skills 目录路径配置到 Harness 的skill_dirs里。对话测试“帮我对这个项目做个复盘”。你很快会发现模型输出的结构化程度明显比“随口问它”高很多。因为 SKILL.md 里的步骤相当于给它搭了一个思维脚手架。5.4 Skill 进阶把多步骤流程固化减少 token 浪费Skill 最值钱的地方是它能把“每次都要反复强调的流程”沉淀下来。我见过很多团队把代码审查规则写成一个 Skill描述里明确“当用户提交代码并要求审查时使用”然后 instructions.md 里写“先检查测试覆盖、再检查命名规范、最后检查边界条件”。这样每个开发者问 Harness都不用重新贴一遍团队规范。这里还有一个隐蔽的收益Skill 可以把很长的指导内容放在独立文件里系统在“需要时才把对应 Skill 内容注入上下文”而不是把所有 Skill 一股脑全丢给模型。否则每次对话都背着几十个 Skill 的超大上下文token 消耗高到吓人模型的选择精度反而下降。所以设计 Skill 时要克制尽量让每个 Skill 短小、聚焦、触发条件清晰。我个人给团队定的标准是单个 SKILL.md 正文不超过 200 行如果超过就拆成多个 Skill。6. Skill 实战与排错为什么模型“看不见”刚装好的 Skill6.1 常见症状模型死活不调用 Skill接入 Skill 后最打击人的场景是你在配置文件里加了skill_dirs目录也建好了SKILL.md 写得规规矩矩但你去问“帮我写个周报”模型却只是平平无奇地回答你完全没有按照 Skill 的流程来。别急这个问题十有八九不是模型笨而是 Skill 没有被正确加载或没有被正确检索。我用一个固定链路排查基本几次就定位了。6.2 完整排查链路从日志到触发词挨个过第一步看加载日志。Harness 启动时会把加载到哪些 Skill 打印出来。如果日志里根本没有你的 Skill 目录那说明skill_dirs配置路径不对或目录结构里缺了 SKILL.md。第二步看描述和触发词。如果你把description写成“周报技能”但实际用户表达是“帮我总结一下这周的工作”模型可能匹配不上。这时候把description补充成“当用户提到周报、周总结、本周工作、weekly report 等词时使用”就能解决。第三步看名称冲突。如果你有两个 Skill一个叫report一个叫weekly_report且描述相似模型很可能混淆。我给每个 Skill 起名都要求全局唯一宁可长一点。第四步看是否启用默认开关。Harness 对 Skill 有时要求显式启用比如skills: weekly_report: enabled: true如果是团队模板别人能加载你不能优先检查这一步。第五步看格式是否解析通过。SKILL.md 开头的---元信息块是 YAML 格式解析的如果里面写错了冒号、引号或者少了结尾横线Harness 会静默跳过这个 Skill只在详细日志里留一条 warning。这个坑藏得最深。第六步用调试消息确认工具列表。如果以上都没问题我会单独开一个会话让模型“输出你当前可用的 Skill 列表”。如果里面有 weekly_report说明加载成功问题只可能在描述或触发策略如果没有回到第一步重查。6.3 一类特殊问题文件放对了但格式不对我之前帮同事排过一个案例Skill 目录、文件权限、配置路径全对但模型就是不识别。后来一条一条看日志才发现是他SKILL.md里手滑把---写成了--导致 YAML 元信息没被解析整个文件被当成了普通 Markdown 内容。模型读到的只是一堆没有“Skill 身份”的文字自然不会按技能去调用。这个经历让我养成一个习惯写 Skill 时先跑一个本地格式校验脚本解析 YAML、检查必需字段再启动 Harness 做对话测试。宁可多花一分钟校验也不要带着错误配置去猜问题。6.4 我的个人工作流最小 Skill 打底版本化管理踩过几次坑之后我现在接入任何新 Skill 都遵循一套固定流程先写一个 5 行以内、功能单一的最小 Skill验证 Harness 能加载、模型能调用。确认链路通了之后再往里面加 instructions、examples、scripts。每次修改 Skill 都走 Git 提交SKILL.md里的version字段同步递增。在 CI 里加一个检查凡是修改skills/目录的 PR必须通过 YAML 解析校验和字段完整性校验。这套流程看起来繁琐但它帮我避免了大量“看着像没问题、一上线就失效”的尴尬。Skill 本质上也是代码代码怎么管理Skill 就应该怎么管理。最后再分享一个小技巧如果你接入了很多 MCP Server 和 Skill对话时模型可能会被一堆可选项淹没导致“选择困难”。我现在会在关键任务的系统提示词里加一句“优先使用 weekly_report Skill 和 figma MCP”把选择范围缩小。这个做法不算什么高深技术但实测下来模型回答的稳定性和准确率能提升一大截。