开头写给技术人的判断:DeepSeek Harness 不是拿来跑一次就结束的玩具,它是一个可以承载自定义能力的“工具管线”。但很多人在使用它的过程中会卡在同一个地方:内置命令不够用、知识文件需要手工去翻、想要接入团队内部的服务却不知道代码该放哪里。这时候真正需要解决的问题不是“再加一段提示词”,而是“给 Harness 写一个正式插件”。
本文的目标很明确:从零写一个完整的 DeepSeek Harness 插件,把代码整理成标准目录结构,装进 Harness 能扫描到的插件目录,然后作为开源项目发布到 GitHub。这里说的“正式”,指的是代码结构规范、支持独立配置、能被打包安装、可以对外发布,而不是在临时脚本里凑一个功能。
读完这篇文章,你能够独立完成插件的设计、编码、安装、验证和发布全流程。需要说明的是,DeepSeek Harness 不同版本对插件机制的约定可能有差异,本文示例采用 Python 插件系统里最通用的写法,具体 API 名称请以项目当前官方文档为准。
1. 为什么需要自己写一个 DeepSeek Harness 插件
先弄清楚一个判断:提示词和插件解决的是两种完全不同的问题。
提示词只能改变模型“怎么回答”。你在 prompt 里写“请把结果输出为 JSON”,模型会尽力照做,但如果某个数据源不在它的上下文里,它编出来的结果就是不存在的。插件则不一样,它可以介入执行过程:读文件、调服务、查数据库、把结果结构化之后塞给模型。也就是说,提示词决定模型的表达,插件决定模型的能力边界。
实际开发中,这几种需求都只有插件能解决:
- 希望 Harness 能检索本地的技能文档目录,而不是每次手工把内容粘进对话。
- 希望 Harness 能访问公司内部 API,把某个工单状态查询变成一条命令。
- 希望把固定业务流程封装好,让团队其他人不用关心内部实现。
- 希望 Harness 启动时自动加载某些上下文信息,比如项目代号、联系人、环境地址。
如果你只是调整模型参数、切换模型、改对话温度,那么配置文件就够用,不必写插件。但如果你要让 Harness 和外部系统产生真实交互,插件几乎是绕不开的路径。
最容易被忽略的一点是:插件并不神秘。它本质上就是一段被 Harness 按约定加载的 Python 代码,只是相比普通脚本多了一层“注册逻辑”。掌握了插件开发,你也顺便理解了大多数 LLM 应用框架的扩展思路,这个能力是通用的。
2. 插件机制的核心概念与工作原理
在动手写代码之前,先理解 Harness 插件体系里的几个关键抽象。无论命名怎么变,核心概念基本都是下面这几个。
2.1 Harness 是什么
“Harness”这个词在 AI 工程里通常指一层调度框架。它负责把模型、工具、上下文、配置组合到一起,形成一条可以执行的管线。DeepSeek Harness 可以简单地理解为一个可以运行任务、管理上下文、调用工具的应用框架,而插件就是插在框架里的能力模块。
2.2 插件是什么
插件不是一个独立运行的程序,而是被 Harness 进程导入的 Python 模块。它遵循框架的约定,暴露出特定入口,让 Harness 在启动或运行时执行注册、命令挂载、生命周期回调等行为。
一个插件通常包含以下几部分:
| 组成部分 | 作用 | 常见实现 |
|---|---|---|
| 插件入口类 | 对外提供加载入口 | Python 类 |
| 注册方法 | 把能力挂载到 Harness 上 | register() 方法 |
| 配置项 | 控制插件行为 | YAML 或 JSON 配置 |
| 依赖声明 | 声明第三方库 | pyproject.toml |
| 元信息 | 插件名称、版本、描述 | 类属性或元数据文件 |
2.3 插件的生命周期
多数 Python 插件框架会包含三个阶段:
- 初始化:框架读取配置,实例化插件对象。
- 注册:框架调用插件的 register 方法,把命令、钩子或工具注册到运行环境中。
- 执行:用户在 Harness 会话中触发命令,插件执行具体逻辑。
如果涉及资源释放,还会有清理阶段。插件代码要围绕这个生命周期去写,而不是在类里随手放几个函数。
2.4 常见误区:插件不等于命令行脚本
很多新手拿插件当脚本写,在入口文件里放一个 main 函数,以为 Harness 会像执行 bash 脚本一样执行它。这是错误的认知。
Harness 不会执行你的脚本文件,而是导入你的模块,调用约定的方法。所以插件的代码必须面向“被调用”来组织:类属性提供元信息,方法提供行为。这也解释了为什么插件的目录结构、入口命名比业务逻辑本身更重要——写错了,框架根本找不到你。
3. 开发环境与前置准备
建议环境如下,版本不必刻意追求最新,稳定即可:
- Python 3.9 及以上版本
- pip 和 venv 模块
- Git 客户端
- GitHub 账号
- 任意代码编辑器(VS Code、PyCharm 均可)
在开始前,先确认本机环境正常。
python3 --version pip3 --version git --version建议创建一个虚拟环境,保持插件依赖与系统环境隔离。
mkdir dsh-plugin-workspace && cd dsh-plugin-workspace python3 -m venv .venv source .venv/bin/activate注意,这里的.venv是开发环境。真正安装到 Harness 时,如果 Harness 使用独立虚拟环境,就需要在 Harness 的环境中安装插件,或者把插件目录放进 Harness 的扫描路径,二选一,不要混着来。
4. 插件工程化:目录结构与命名规范
很多写插件失败的人,不是代码逻辑出问题,而是目录结构不符合约定,导致 Harness 找不到插件类。所以先把结构搭对。
本文以一个“技能检索插件”为例,功能是让 Harness 会话中执行/skill <关键词>命令,检索本地 skills 目录下的技能模板文件。虽然这个功能不复杂,但可以完整展示一个正式插件的全部要素。
项目结构如下:
dsh-plugin-skill-search/ ├── pyproject.toml ├── README.md ├── LICENSE ├── dsh_plugin_skill_search/ │ ├── __init__.py │ ├── plugin.py │ └── config.py └── tests/ └── test_plugin.py4.1 目录命名规则
包名dsh_plugin_skill_search遵循了 Python 包命名规范:小写、下划线分隔。插件显示名称skill-search则适合作为命令前缀。类名DeepSeekHarnessPlugin清晰表达这个类的作用。
正式插件建议在包名中带上dsh_plugin_前缀,避免安装到环境中时与其他同名模块冲突。这也是一种命名约束,团队协作时会省掉不少麻烦。
4.2 各个文件的作用
| 文件 | 作用 |
|---|---|
| pyproject.toml | 声明包信息、依赖、插件入口点 |
| README.md | 使用说明和发布说明 |
| LICENSE | 开源许可证,决定别人能否合法使用 |
| dsh_plugin_skill_search/init.py | 包标识文件 |
| dsh_plugin_skill_search/plugin.py | 插件入口类,核心逻辑 |
| dsh_plugin_skill_search/config.py | 配置加载逻辑 |
| tests/test_plugin.py | 基础单元测试 |
这些文件缺一不可。发布到 GitHub 的项目如果没有 LICENSE,别人是不敢直接使用的,这是一个新手最容易忽略的坑。
5. 插件核心代码实现
现在开始写代码。先写插件入口类plugin.py:
# 文件路径:dsh_plugin_skill_search/plugin.py from pathlib import Path class DeepSeekHarnessPlugin: name = "skill-search" version = "1.0.0" description = "在 DeepSeek Harness 会话中快速检索本地技能模板" def __init__(self, config: dict): self.config = config skill_dir = config.get("skill_dir", "./skills") self.skill_dir = Path(skill_dir).resolve() def register(self, ctx): ctx.register_command("skill", self.run) def run(self, args): keyword = str(args).strip() if not keyword: return "用法:/skill <关键词>,例如 /skill knowledge" if not self.skill_dir.exists(): return f"技能目录不存在:{self.skill_dir},请检查配置。" matches = [p.name for p in self.skill_dir.glob("*.md") if keyword in p.name] if not matches: return "未找到匹配技能,请更换关键词或检查 skills 目录。" result = [f"- {name}" for name in matches[:10]] return "匹配技能:\n" + "\n".join(result)这段代码的关键点有三个:
name、version、description是插件元信息,Harness 加载插件时会读取这些属性用于展示和日志。register方法向 Harness 上下文注册了skill命令,命令入口指向run方法。run方法接收 Harness 传入的参数,返回一个字符串作为执行结果。
需要注意,ctx对象的注册方式取决于 Harness 的插件 API。这里写的ctx.register_command("skill", self.run)是通用写法,如果你的项目使用@plugin.command装饰器或其他机制,以官方文档为准。核心思想是“声明命令 + 挂载函数”。
接下来是配置加载逻辑config.py:
# 文件路径:dsh_plugin_skill_search/config.py import json from pathlib import Path def load_plugin_config(config_path: str) -> dict: config_file = Path(config_path) if not config_file.exists(): return {} with open(config_file, "r", encoding="utf-8") as f: data = json.load(f) # 只返回该插件关心的配置段,避免把无关配置塞进插件对象 return data.get("skill_search", {})配置加载逻辑并不复杂,但它体现了一个规范:插件不应该直接读取全量配置对象,而是只读取自己命名空间下的字段。这样当配置文件越来越复杂时,插件之间不会相互干扰。
为了让插件可以被pip安装并作为入口点被 Harness 识别,编写pyproject.toml:
# 文件路径:pyproject.toml [build-system] requires = ["setuptools>=61.0"] build-backend = "setuptools.build_meta" [project] name = "dsh-plugin-skill-search" version = "1.0.0" description = "A skill search plugin for DeepSeek Harness" requires-python = ">=3.9" dependencies = [] [project.entry-points."deepseek_harness.plugins"] skill-search = "dsh_plugin_skill_search.plugin:DeepSeekHarnessPlugin" [tool.setuptools.packages.find] include = ["dsh_plugin_skill_search*"]这里的 entry-points 是 Python 生态中非常常见的插件注册方式。如果 Harness 支持通过 entry-points 扫描插件,安装后就能自动被发现;如果 Harness 通过目录扫描插件,那么需要把插件装进它的扫描目录。文章后面会分别说明这两种情况。
tests/test_plugin.py提供最小测试:
# 文件路径:tests/test_plugin.py from dsh_plugin_skill_search.plugin import DeepSeekHarnessPlugin class FakeContext: def __init__(self): self.commands = {} def register_command(self, cmd, handler): self.commands[cmd] = handler def test_run_returns_usage_when_no_keyword(): plugin = DeepSeekHarnessPlugin({"skill_dir": "./skills"}) assert plugin.run("") == "用法:/skill <关键词>,例如 /skill knowledge" def test_register_command(): ctx = FakeContext() plugin = DeepSeekHarnessPlugin({}) plugin.register(ctx) assert "skill" in ctx.commands代码写到这里,插件的基本逻辑已经完整。但此时它只是“放在磁盘上的文件”,还没有真正进入 DeepSeek Harness 的运行环境。下一节就解决安装问题。
6. 本地安装与调试
插件安装有两种典型方式:直接放入 Harness 的插件目录,或者通过 pip 以可编辑模式安装。
6.1 方式一:放入插件目录
多数 Harness 框架会约定一个插件扫描目录,例如plugins/、~/.config/deepseek-harness/plugins/等。
以扫描目录为plugins/为例:
# 在 Harness 项目目录下创建插件目录 mkdir -p plugins # 将插件包复制到插件目录 cp -r dsh_plugin_skill_search plugins/ # 启动 Harness 验证插件是否被加载 dsh start启动后观察日志输出,看到类似loaded plugin: skill-search (1.0.0)的日志就说明加载成功。如果没有加载,优先检查以下两点:
- 目录层级是否正确,Harness 是否扫描到了包含
plugin.py的目录。 - 插件类名是否与 Harness 约定的入口类名一致。
6.2 方式二:pip 可编辑安装
如果要重复开发调试,推荐用可编辑模式安装到当前虚拟环境。
cd dsh-plugin-workspace pip install -e .安装后,在 Python 环境里可以验证插件是否能被 import:
python3 -c "from dsh_plugin_skill_search.plugin import DeepSeekHarnessPlugin; print(DeepSeekHarnessPlugin.name)"如果 Harness 支持 entry-points 插件发现机制,安装后重启 Harness 就能加载到插件,不需要复制文件。
6.3 配置插件
插件通常需要一份配置文件。假设 Harness 支持config.yaml:
plugins: enabled: - skill-search skill_search: skill_dir: ./skills配置的含义是:启用skill-search插件,并告诉插件技能文件放在./skills目录。配置文件路径以 Harness 实际约定为准。
6.4 运行验证
重启 Harness 后,在会话中输入:
/skill knowledge预期输出类似:
匹配技能: - knowledge-engineering.md - knowledge-base-setup.md如果输出报错,先看 Harness 日志,再看插件目录路径是否正确。调试阶段建议在plugin.py中临时打印配置项:
print("skill_dir:", self.skill_dir)确认配置真正传了进来,再继续排查业务逻辑。
7. 发布到 GitHub:从本地仓库到开源项目
插件本地跑通只是第一步。真正让插件“正式”起来,是把它发布到 GitHub,让别人可以 clone、使用、提 issue。
7.1 初始化仓库与提交代码
cd dsh-plugin-skill-search git init git add . git commit -m "feat: initial skill search plugin for DeepSeek Harness"提交之前,建议创建.gitignore忽略虚拟环境目录和缓存文件:
# 文件路径:.gitignore .venv/ __pycache__/ *.pyc dist/ build/ *.egg-info/7.2 在 GitHub 创建远程仓库
在 GitHub 网站上点击 New repository,填写仓库名,建议与插件包名一致,例如dsh-plugin-skill-search。公开仓库或私有仓库都可以,发布插件建议选择 Public。
创建完成后,在本地添加远程地址并推送:
git remote add origin git@github.com:<yourname>/dsh-plugin-skill-search.git git branch -M main git push -u origin main这里使用 SSH 协议推送,比 HTTPS 更少遇到认证问题。如果本机还没配置 SSH key,可以先运行ssh-keygen生成公钥,然后添加到 GitHub 账号设置里。
如果网络环境下直接访问 GitHub 不稳定,推送 clone 时可以考虑:
- 使用
gh repo create配合 GitHub CLI 创建仓库。 - clone 慢时尝试 SSH 协议而不是 HTTPS。
- 下载 release 资产时,可以使用国内常见的 GitHub 镜像站或文件代理服务,以加速下载。
需要提醒:不要为加速 clone 去配置任何不安全的“一键脚本”,该类工具很容易引入供应链风险。尽量使用官方 git 命令,配合可靠的镜像服务下载 release 文件。
7.3 补全 README 与 LICENSE
README 至少要说明:
- 插件是做什么的。
- 环境要求。
- 安装方式。
- 配置方式。
- 使用示例。
- 如何参与贡献。
LICENSE 建议选择 MIT、Apache-2.0 这类宽松许可证。直接在代码仓库根目录添加 LICENSE 文件即可。没有许可证的公开仓库,在法律上意味着“保留所有权利”,别人不能合法使用,这既不利于传播,也会劝退潜在贡献者。
7.4 打 tag 与创建 Release
插件版本需要与pyproject.toml中的版本号保持一致。发布一个正式版本时,在本地打 tag:
git tag v1.0.0 git push origin v1.0.0然后在 GitHub 仓库页面创建 Release,选择 tagv1.0.0,填写发布说明,附上构建好的安装包。
如果需要构建发布包,可以安装build工具:
pip install build python3 -m build执行后会生成dist/目录,里面包含.tar.gz源码包和.whl安装包。Release 页面可以把这两个文件作为附件上传,方便用户直接下载安装。
如果想上传到 PyPI 让用户通过 pip 安装,需要单独配置 PyPI 的发布流程,这一步不强制,发布到 GitHub 已经满足“正式插件”的要求。
8. 常见问题与排查思路
下面这些问题是我在开发插件过程中见过的高频问题,整理成表格方便定位。
| 问题现象 | 可能原因 | 排查方式 | 解决方案 |
|---|---|---|---|
| Harness 启动时没有加载插件 | 插件目录不在扫描范围内 | 查看 Harness 日志,确认扫描路径 | 把插件复制到约定的插件目录,或通过 pip 安装 |
报错ModuleNotFoundError | 依赖包未安装或包名冲突 | 检查当前 Python 环境 | 在 Harness 虚拟环境中安装依赖,避免把包装到系统环境 |
| 命令执行后没有任何响应 | 命令未注册成功 | 检查注册方法是否被调用 | 确认 register 方法名与框架约定一致 |
| 配置文件修改后不生效 | Harness 缓存配置 | 重启 Harness | 重启并清理缓存目录 |
| 插件代码修改后不生效 | 使用普通 pip install 安装 | 确认安装方式 | 使用pip install -e .进行可编辑安装 |
| GitHub 推送失败 | 认证失效或网络不稳定 | 查看 git 输出信息 | 使用 SSH 协议,或重新认证 GitHub CLI |
| Release 下载很慢 | 网络环境问题 | 尝试不同网络或代理 | 使用国内镜像站下载 release 资产 |
排查时有一个重要原则:先确认“框架有没有加载到你的插件”,再确认“代码逻辑对不对”。很多人一开始就钻进业务逻辑里看半天,实际问题是目录结构完全不符合约定,框架压根没扫描到插件。
9. 最佳实践与工程建议
插件写多了之后,你会意识到代码能不能跑只是底线,真正拉开差距的是工程化水平。下面几条建议值得在项目里落地。
9.1 插件 API 版本与 Harness 版本保持解耦
Harness 主框架不断迭代,插件 API 可能会变化。建议在 README 中明确声明插件支持的 Harness 版本范围,例如deepseek-harness>=0.5,<1.0。这样可以避免用户因为版本不匹配而遇到莫名其妙的错误。
9.2 配置校验前置
不要在run方法里才判断配置有没有问题。应该在__init__阶段就校验必备配置:
def __init__(self, config: dict): if "skill_dir" not in config: raise ValueError("skill_dir is required") self.skill_dir = Path(config["skill_dir"]).resolve()这样配置错误会在 Harness 启动时暴露,而不是等到用户执行命令时才报错。
9.3 合理使用日志
插件中的业务日志要区分级别。命令被调用、参数错误、搜索无结果这些场景,分别使用不同级别的日志。不要在run方法里 print,正式插件应该通过框架提供的 logger 记录日志。
9.4 错误处理要面向用户
命令执行时的异常信息应该友好。不要直接抛出 Python traceback,而是捕获异常并返回用户可以理解的中文提示,同时把详细堆栈写入日志。
9.5 安全性边界
插件访问本地文件时,要考虑路径穿越风险。配置中的目录应该限制在允许范围内,避免用户传入../../etc这类路径。如果需要调用外部 API,不要在配置中硬编码密钥,建议通过环境变量或密钥管理服务注入。
9.6 测试与 CI
正式项目建议至少包含单元测试。更进一步,可以在 GitHub 上配置 CI,当代码推送时自动运行 pytest:
# 文件路径:.github/workflows/test.yml name: test on: push: branches: [main] pull_request: jobs: test: runs-on: ubuntu-latest steps: - uses: actions/checkout@v4 - uses: actions/setup-python@v5 with: python-version: "3.11" - run: pip install -e . - run: pytest有了 CI,每次提交都能自动跑测试,插件质量会稳定很多。
9.7 发布前检查清单
pyproject.toml版本号是否与 git tag 一致。- README 是否包含安装、配置、使用说明。
- LICENSE 是否存在。
.gitignore是否忽略掉敏感文件。- 是否在本地虚拟环境完整执行过一遍安装和验证流程。
10. 总结与后续学习方向
本文围绕一个具体的插件示例,完整走通了 DeepSeek Harness 插件开发的五个关键环节:理解插件机制、搭建目录结构、编写核心代码、本地安装调试、发布到 GitHub。过程中还补充了配置管理、错误排查、工程规范和开源发布注意事项。
如果只看表面,插件开发很容易被当成“框架相关的琐碎知识”,但深入之后会发现,它本质上是一套 Python 工程化的标准流程。入口类、配置加载、依赖声明、entry-points、git tag、release 发布,这些技能在任何 Python 项目中都通用。
下一步可以继续深入研究的方向有三个:一是学习 Harness 更复杂的钩子机制,比如事件监听、上下文注入、工具链编排;二是给插件补充更完整的测试和 CI;三是考虑把插件发布到 PyPI,让用户通过pip install dsh-plugin-skill-search直接安装。
对于想立刻上手的读者,建议不要直接复制本文代码,而是先创建一个最小插件,改掉包名和命令名,跑通安装流程,再逐步加入自己的业务逻辑。插件开发最大的障碍通常不是代码,而是环境没跑通。先把最小的链路走通,后面就顺了。