DSH(DeepSeek Harness)是一个面向 DeepSeek agent 运行场景的轻量 Python 插件宿主。它把一次 agent run 拆成标准事件,让插件在请求发出前、工具执行前等关键节点介入,实现策略控制、成本预算、审计和二次开发。
项目当前内置 guardrails(工具守卫与 Token 预算)和 session_prompt(Codex 风格会话提示)两个插件。核心实现仅依赖 Python 标准库,不需要安装额外运行时依赖。
- 事件驱动插件模型:
run.start、request.before、request.after、tool_call.before、tool_call.after、run.end - 插件可拦截请求或工具调用:返回
EventResult(blocked=True, reason=...)即可阻止 - 内置
guardrails插件:- 工具白名单 / 黑名单
- 单工具调用次数上限
- 单次运行工具总调用次数上限
- prompt / completion / total 三种 Token 预算
- 内置
session_prompt插件:自动生成 Codex 风格 system prompt,并跨 run 保留会话历史与 Token 用量 - 真实 DeepSeek HTTP backend,另有
FakeBackend用于测试和离线 demo - CLI 开箱即用:
dsh demo、dsh session、dsh list-plugins - 纯标准库实现,可直接测试、直接跑 demo,之后再接真实 API
项目使用 src 布局,Python 版本要求 >=3.10。
python -m pip install -e .
dsh demo$env:PYTHONPATH = "src"
python -m dsh.cli demodsh demo 会执行一次脚本化的 agent run,演示 web_search 被放行、shell_exec 被策略拦截、write_file 因总调用上限被拦截。
查看内置插件:
dsh list-plugins启动 Codex 风格交互会话(离线演示):
dsh session --offline也可以只跑一轮:
dsh session --once "你好" --offlineguardrails 是首个内置插件,解决 agent 运行中最常见的两类问题:失控的工具调用和不可控的 Token 成本。
| 配置 | 说明 |
|---|---|
allowed_tools |
白名单;为空表示不启用白名单 |
denied_tools |
黑名单;命中后直接拦截 |
max_tool_calls |
单工具调用次数上限,例如 {"web_search": 10} |
max_total_tool_calls |
单次运行工具总调用次数上限 |
max_prompt_tokens |
单次请求 prompt Token 上限 |
max_completion_tokens |
单次请求 completion Token 上限 |
max_total_tokens |
累计总 Token 上限 |
示例配置见 src/dsh/plugins/guardrails/config.example.json:
{
"allowed_tools": [],
"denied_tools": ["shell_exec", "delete_file"],
"max_tool_calls": {
"web_search": 10,
"read_file": 20
},
"max_total_tool_calls": 50,
"max_prompt_tokens": 8000,
"max_completion_tokens": 2000,
"max_total_tokens": 10000
}通过 CLI 传入配置:
dsh demo --config src/dsh/plugins/guardrails/config.example.jsonsession_prompt 会在每次 request.before 时把自定义人设、会话 ID、轮次、Token 用量和最近对话拼成 system prompt;在 run.end 时把历史写入插件状态,支持跨 run 延续会话。
| 配置 | 说明 |
|---|---|
enabled |
是否启用,默认 true |
system_prompt |
自定义人设;为空使用内置默认人设 |
session_id |
会话 ID;为空时自动生成 |
max_history_messages |
保留历史消息条数上限 |
max_tool_output_chars |
工具输出截断长度 |
include_usage |
是否在 system prompt 中包含 Token 用量 |
persist_path |
JSONL 持久化路径;为空不持久化 |
示例配置见 src/dsh/plugins/session_prompt/config.example.json。
一个插件就是一个继承 Plugin 的类:
from dsh.plugin import EventResult, Plugin
from dsh.events import Event
class MyPlugin(Plugin):
name = "my_plugin"
version = "0.1.0"
def handle_event(self, event: Event, ctx) -> EventResult | None:
if event.name == "tool_call.before":
return EventResult(blocked=True, reason="not allowed")
return None把插件放进 src/dsh/plugins/<name>/,并提供 create_plugin(config=None),PluginRegistry.discover_builtin() 会自动加载它。
常用事件:
| 事件 | 触发时机 |
|---|---|
run.start |
一次 agent run 开始 |
request.before |
请求发给模型之前,可提前拦截 |
request.after |
模型返回之后,可检查 usage 并停止运行 |
tool_call.before |
工具执行之前,可拦截 |
tool_call.after |
工具执行之后 |
run.end |
一次 agent run 结束 |
在 request.before 中,插件还可以通过 EventResult.data 改写即将发送的请求:messages 替换整个消息列表,system_prompt 会插入或更新 system 消息。
src/dsh/
events.py 事件定义与常量
plugin.py Plugin / PluginContext / EventResult
registry.py 插件注册与内置插件发现
harness.py agent 运行循环,负责事件分发
backends.py ModelBackend / DeepSeekBackend / FakeBackend
cli.py dsh demo / dsh session / dsh list-plugins
session.py Codex 风格交互会话与离线 backend
plugins/guardrails/ 内置工具守卫与 Token 预算插件
plugins/session_prompt/ 会话提示与历史管理插件
examples/
deepseek_live.py 真实 DeepSeek API 连通性测试
tests/ 单元测试
把 API key 放进环境变量,再运行 examples/deepseek_live.py。脚本不会把 key 写进仓库或日志:
$env:DEEPSEEK_API_KEY = "your-key"
$env:PYTHONPATH = "src"
python examples/deepseek_live.py也可以在代码中直接使用:
import os
from dsh.backends import DeepSeekBackend
from dsh.harness import Harness
from dsh.plugins.guardrails import GuardrailsPlugin
backend = DeepSeekBackend(api_key=os.environ["DEEPSEEK_API_KEY"])
harness = Harness(
backend,
plugins=[GuardrailsPlugin({"max_total_tokens": 10000})],
max_turns=10,
)
result = harness.run("你的 prompt", tools=tools)$env:PYTHONPATH = "src"
python -m unittest discover -s tests- 把真实 API 测试收编为正式 CLI 子命令
- 插件配置从 JSON 文件统一加载
- 更多内置插件:JSONL 审计日志、请求重试与限流
- 把工具执行器从 backend 中独立出来,交给应用层注册
MIT