DeepSeek Harness:Cordis 插件架构如何重新定义 AI Agent 的"身体"
2026年8月13日,DeepSeek 在 GitHub 上开源了 DeepSeek Harness(DSH)——一个"一切皆插件"的智能体运行框架。发布仅 1.5 小时,Star 突破 2.4 万,刷新了 GitHub 史上最快破两万星纪录。这个速度甚至超过了 xAI 的 Grok-1 和 DeepSeek 自己的 R1。本文将深入拆解 DSH 的 Cordis 插件架构,探讨它为什么被认为是"今年最有野心的 Agent 开源项目",以及它对 AI Coding 战场格局的深远影响。
一、从"嘴强王者"到"手脚并用":Agent 为什么需要 Harness 层
1.1 模型的困境:会说话,但不会做事
过去两年,大模型的能力边界被不断刷新——上下文窗口从 4K 扩展到 1M,推理能力从 Chain-of-Thought 进化到 Reinforcement Learning 驱动的 R1/V3,代码能力在 HumanEval 和 SWE-bench 上不断逼近甚至超越人类。
但有一个问题始终悬而未决:模型再强,也只是"嘴强"。
当你对 GPT-4o 或 Claude 3.5 说"帮我重构这个项目,加上单元测试,然后部署到生产环境",它能做什么?它能给出一段看起来很专业的文字回复,但:
- 它无法真正打开你的 IDE,找到对应文件
- 它无法运行
git diff查看实际代码改动 - 它无法执行
npm test或cargo build验证修改 - 它无法在报错时继续修正,直到跑通为止
这不是模型的 bug,而是 LLM 本质上是一个"文本生成器"。它擅长生成文字,但不擅长与真实世界交互。
1.2 API Client 时代:连接但不可控
早期的 Agent 尝试用 API Client 解决"连接"问题——让模型通过工具调用(Tool Use / Function Calling)来操作外部世界。
用户: "帮我把 user表中age>30 的记录查出来"
模型 → Function Call → {"name": "query_database", "args": {"sql": "SELECT * FROM user WHERE age > 30"}}
执行结果返回 → 模型总结输出
这套机制有效,但有几个致命缺陷:
- 工具定义碎片化:每个 Agent 项目自己定义工具接口,没有统一标准
- 上下文管理割裂:模型调用工具后的状态和原始上下文如何融合?没有标准答案
- 循环控制黑盒:当工具调用失败时,模型如何决定重试、降级还是放弃?没有通用框架
- 多 Agent 协作缺失:多个 Agent 如何共享状态、分工协作?每个项目各玩各的
1.3 Harness 层:模型与执行环境之间的"操作系统"
YC 创始人 Paul Graham 曾在 2024 年底的备忘录中提出一个观点:AI 应用的价值不在于"模型本身",而在于 "模型 + 执行层" 的组合。
DeepSeek Harness 正是这个观点的工程化实现。官方文档对 DSH 的定位非常清晰:
DSH is not a new foundation model, nor an API client. It's the Harness — the framework that connects models to filesystems, terminals, web browsers, code tools, and other agents, while organizing context, tool invocations, and task execution.
翻译成人话:DSH 不是模型,也不是 API 封装,它是模型做事时需要的"手和脚"。
用操作系统来类比:
- 模型(LLM) = CPU,处理逻辑和决策
- Harness 层(DSH) = 操作系统,管理资源调度和 I/O
- 工具插件 = 驱动程序,连接具体硬件
这个分层让不同层可以独立演进:换一个更强的模型,不需要重写整个 Agent 系统;换一个工具,不需要修改模型侧的提示词。
二、Cordis 插件系统:深入剖析"一切皆插件"架构
2.1 为什么选择插件架构?
传统的 Agent 框架通常是"大一统"设计:模型、工具、循环、上下文管理全都写死在一个代码库里。这种设计的好处是开箱即用,但坏处是:
- 替换成本高:想换一个模型?得改核心代码
- 定制化困难:想加一个特殊工具?得深入理解框架内部
- 版本迭代慢:每次框架升级都可能破坏现有定制
DeepSeek 选择插件架构,本质上是在回答一个问题:Agent 框架的核心竞争力是什么?
DeepSeek 的答案是:框架本身的调度逻辑(Agent Loop + Cordis)+ 开放的插件生态。模型可以换,工具可以换,但 Agent 的"思考-执行-反思"循环是通用能力,应该沉淀为基础设施。
2.2 Cordis 元框架的设计哲学
Cordis(拉丁语"心脏")是 DSH 的核心插件运行时。它的设计哲学可以总结为三句话:
只管插件的加载、卸载和依赖关系,不管插件做什么。
换句话说,Cordis 扮演的是"元框架"角色——它定义了插件的接口契约(Plugin Contract),但不介入插件的内部实现。
// Cordis Plugin Contract 的核心接口(TypeScript 伪代码)
interface CordisPlugin {
readonly id: string; // 全局唯一标识
readonly name: string; // 可读名称
readonly version: string; // 语义化版本
readonly dependencies: string[]; // 依赖的其他插件 ID
// 生命周期钩子
onLoad(context: CordisContext): Promise<void>;
onUnload(): Promise<void>;
// 核心能力:提供服务(Service)和监听事件(Event)
provides(): CordisService[];
subscribes(): CordisEvent[];
}
// CordisContext:插件之间的共享上下文
interface CordisContext {
// 服务注册表:根据类型查找已注册的插件服务
services: Map<string, unknown>;
// 事件总线:发布/订阅模式
events: EventBus;
// 存储:持久化数据
storage: PluginStorage;
}
这个接口设计的精妙之处在于:它只约束"谁来提供服务"和"谁对什么事件感兴趣",而不限制"服务如何实现"。
2.3 DSH 的插件生态全景图
DSH 将 Agent 的所有能力都插件化,官方文档揭示了以下插件类型:
| 插件类型 | 说明 | 可替换性 |
|---|---|---|
| Model Adapter | 模型适配器,负责 LLM API 调用、响应解析、流式输出 | ✅ 完整可替换 |
| Tool | 工具插件,文件系统、终端、浏览器、数据库等 | ✅ 完整可替换 |
| Skill | 技能插件,封装好的多步工作流(如代码审查、PR 创建) | ✅ 完整可替换 |
| Session | 会话管理,维护多轮对话上下文和记忆 | ✅ 完整可替换 |
| Sandbox | 沙箱环境,执行危险操作时的隔离容器 | ✅ 完整可替换 |
| Storage | 持久化存储,会话历史、缓存、K-V 存储 | ✅ 完整可替换 |
| Agent Loop | Agent 主循环,控制思考-执行-反思的迭代逻辑 | ✅ 完整可替换 |
| Scheduler | 调度器,决定任务优先级和并发策略 | ✅ 完整可替换 |
| UI | 前端界面,支持 Web UI、TUI(终端 UI)、Headless | ✅ 完整可替换 |
重点说几个最关键的插件:
2.4 Model Adapter:模型无关的核心设计
Model Adapter 是 DSH 中最重要的插件之一,它将"调用哪个 LLM"的决策从业务逻辑中解耦出来。
// Model Adapter 插件接口(核心部分)
interface ModelAdapter extends CordisPlugin {
// 聊天补全
chat(options: ChatOptions): Promise<ChatResponse>;
// 流式补全(用于 Web UI 的打字机效果)
streamChat(options: ChatOptions): AsyncIterable<ChatChunk>;
// 工具调用支持
supportsTools(): boolean;
bindTools(tools: ToolDefinition[]): void;
}
// ChatOptions 的标准结构(与具体模型无关)
interface ChatOptions {
messages: Message[]; // 标准 OpenAI 格式
tools?: ToolDefinition[]; // 可用工具列表
toolChoice?: 'auto' | 'none'; // 强制/禁止工具调用
temperature?: number;
maxTokens?: number;
// ... 其他标准参数
}
这意味着 DSH 可以通过更换 Model Adapter 来支持任何 LLM API——DeepSeek V3、GPT-4o、Claude 3.5、甚至未来的 GPT-5,只需实现同一个接口即可。
官方目前内置了 DeepSeek 官方适配器,社区正在开发 OpenAI 兼容适配器(因为 OpenAI 的 API 格式事实上已成为行业标准)。
2.5 Tool 插件:连接模型与真实世界
Tool 是 DSH 与外部世界交互的桥梁。每个工具都是一个独立的插件,可以自由注册到 Cordis 服务注册表中。
// Tool 插件的标准化接口
interface ToolPlugin extends CordisPlugin {
// 工具定义(暴露给模型)
definition(): ToolDefinition;
// 执行逻辑
execute(params: Record<string, unknown>): Promise<ToolResult>;
}
// ToolDefinition 的标准格式(与 OpenAI Function Calling 兼容)
interface ToolDefinition {
type: 'function';
function: {
name: string; // 工具名,如 "filesystem_read"
description: string; // 描述,模型据此决定何时调用
parameters: { // JSON Schema 格式的参数定义
type: 'object';
properties: Record<string, ParameterSchema>;
required: string[];
};
};
}
// 实际工具插件示例:文件系统读取
class FilesystemReadPlugin implements ToolPlugin {
readonly id = 'tool:filesystem:read';
readonly name = 'Filesystem Read';
definition() {
return {
type: 'function',
function: {
name: 'filesystem_read',
description: 'Read the contents of a file from the local filesystem. ' +
'Use this when you need to inspect code, config files, or any text content. ' +
'Supports syntax highlighting hints in path (e.g., "main.go").',
parameters: {
type: 'object',
properties: {
path: {
type: 'string',
description: 'Absolute or relative path to the file to read'
},
lines: {
type: 'number',
description: 'Maximum number of lines to read (default: all)'
},
offset: {
type: 'number',
description: 'Line offset to start reading from (0-indexed)'
}
},
required: ['path']
}
}
};
}
async execute(params: { path: string; lines?: number; offset?: number }) {
const content = await fs.promises.readFile(params.path, 'utf-8');
const lines = content.split('\n');
const start = params.offset ?? 0;
const end = params.lines ? start + params.lines : undefined;
return {
success: true,
content: lines.slice(start, end).join('\n'),
metadata: { totalLines: lines.length, readLines: end ? end - start : lines.length - start }
};
}
}
这种设计让工具开发者可以独立于 Agent 框架开发工具,只需实现 ToolPlugin 接口,通过 Cordis 注册,即可被任何使用 Cordis 的 Agent 系统发现和使用。
2.6 Agent Loop:可插拔的"思考-执行"循环
这是 DSH 最具野心的设计:连 Agent 的主循环(Agent Loop)都是可替换的插件。
传统 Agent 的主循环通常是这样的(伪代码):
# 传统 Agent Loop(硬编码)
while True:
user_input = get_user_input()
messages.append({"role": "user", "content": user_input})
response = llm.chat(messages, tools=all_tools)
messages.append(response)
if response.tool_calls:
for tool_call in response.tool_calls:
result = execute_tool(tool_call)
messages.append({"role": "tool", "content": result})
if should_stop(response):
break
这个循环写死在框架里,想改都没法改。
DSH 的 Agent Loop 插件接口允许开发者替换整个循环策略:
// Agent Loop 插件接口
interface AgentLoopPlugin extends CordisPlugin {
// 主循环实现:可以是简单的 while 循环,也可以是复杂的树搜索
async run(session: AgentSession): Promise<void>;
// 循环控制策略
shouldContinue(session: AgentSession): boolean;
selectNextAction(session: AgentSession): AgentAction;
}
// 示例:ReAct(Reason + Act)风格的循环
class ReActAgentLoop implements AgentLoopPlugin {
async run(session: AgentSession) {
while (this.shouldContinue(session)) {
// Step 1: 模型推理(Reason)
const reasoning = await session.model.chat([
...session.messages,
{ role: 'user', content: 'Think about what to do next.' }
], { tools: session.availableTools });
session.messages.push(reasoning);
// Step 2: 执行动作(Act)
const action = this.selectNextAction(reasoning);
if (action.type === 'tool_call') {
const result = await session.executeTool(action.toolName, action.params);
session.messages.push({
role: 'tool',
content: JSON.stringify(result)
});
}
// Step 3: 检查是否完成
if (action.type === 'done') {
break;
}
}
}
}
// 示例:Tree of Thoughts(思维树)风格的循环
class ToTAgentLoop implements AgentLoopPlugin {
async run(session: AgentSession) {
const root = new ThoughtNode(session.initialTask);
// 广度优先搜索最优路径
const queue = [root];
while (queue.length > 0 && !this.hasFoundSolution(queue)) {
const node = queue.shift();
const children = await this.expand(node, session);
queue.push(...children);
}
return this.extractBestPath(queue);
}
}
这意味着:同一个 DSH 系统,可以同时支持"快速简单任务"的 ReAct 循环和"复杂推理任务"的 ToT 循环,只需在配置中切换 Agent Loop 插件。
三、代码实战:用 DSH 打造一个"代码审查 Agent"
3.1 环境准备
DSH 可以通过 npm 直接安装(需要 Node.js 18+):
# 安装 DSH CLI
npm install -g @deepseek-ai/dsh
# 验证安装
dsh --version
# → dsh/0.1.0
# 启动 Web UI(默认端口 3000)
npx @deepseek-ai/dsh web
也可以通过配置文件初始化项目:
# 初始化 DSH 项目
mkdir my-agent && cd my-agent
dsh init
# 查看生成的项目结构
ls -la
# .dsh/
# config.toml # 主配置文件
# plugins/ # 自定义插件目录
# sessions/ # 会话历史
3.2 配置 DeepSeek 模型和工具
编辑 .dsh/config.toml:
# .dsh/config.toml
# 全局配置
[global]
name = "Code Review Agent"
version = "0.1.0"
# 模型配置:使用 DeepSeek V3
[model]
adapter = "deepseek" # 使用内置的 DeepSeek 适配器
model = "deepseek-chat-v3" # DeepSeek V3 模型
api_key = "${DEEPSEEK_API_KEY}" # 从环境变量读取 API Key
base_url = "https://api.deepseek.com"
max_tokens = 4096
temperature = 0.3 # 代码审查不需要太强的创造性
# 工具配置:启用文件系统、Git 和终端工具
[tools]
enabled = [
"tool:filesystem:read",
"tool:filesystem:write",
"tool:git:status",
"tool:git:diff",
"tool:terminal:exec",
"tool:search:code" # 代码语义搜索(基于 AST)
]
# Agent Loop 配置
[agent_loop]
adapter = "react" # 使用 ReAct 风格的循环
max_iterations = 20 # 最多 20 步,避免无限循环
stop_on_error = false # 出错不停止,继续尝试其他方法
# 存储配置
[storage]
type = "file" # 将会话保存到 .dsh/sessions/
retention_days = 30 # 保留 30 天
3.3 编写自定义代码审查工具插件
DSH 的真正威力在于可以轻松编写自定义工具。以下是一个代码审查工具插件的实现:
// plugins/code-review.ts
// 依赖:@deepseek-ai/dsh-sdk
import {
ToolPlugin,
CordisContext,
ToolDefinition,
ToolResult
} from '@deepseek-ai/dsh-sdk';
// AST 分析依赖(用于静态代码分析)
import { parse } from '@babel/parser';
import traverse from '@babel/traverse';
import * as t from '@babel/types';
export interface ReviewFinding {
severity: 'error' | 'warning' | 'info';
line: number;
column: number;
rule: string;
message: string;
suggestion?: string;
}
export class CodeReviewPlugin implements ToolPlugin {
readonly id = 'tool:code-review';
readonly name = 'Code Review';
readonly version = '1.0.0';
readonly dependencies: string[] = ['tool:filesystem:read'];
private context!: CordisContext;
async onLoad(context: CordisContext) {
this.context = context;
// 注册工具到 Cordis 服务注册表
context.services.set('tool:code-review', this);
// 订阅文件系统变化事件(当文件被修改时自动触发审查)
context.events.subscribe('filesystem:file-changed', async (event) => {
if (event.path.endsWith('.ts') || event.path.endsWith('.tsx')) {
await this.autoReview(event.path);
}
});
}
async onUnload() {
this.context.events.unsubscribe('filesystem:file-changed');
}
definition(): ToolDefinition {
return {
type: 'function',
function: {
name: 'code_review',
description: 'Perform static code review on a TypeScript/JavaScript file. ' +
'Detects common bugs, anti-patterns, security issues, and code quality problems. ' +
'Returns a structured list of findings with severity levels and fix suggestions.',
parameters: {
type: 'object',
properties: {
path: {
type: 'string',
description: 'Path to the file to review (relative to project root or absolute)'
},
rules: {
type: 'array',
items: { type: 'string' },
description: 'Specific rule categories to check: "security", "bug", "performance", "style", "best-practice". Defaults to all.',
default: ['security', 'bug', 'performance', 'best-practice']
}
},
required: ['path']
}
}
};
}
async execute(params: { path: string; rules?: string[] }): Promise<ToolResult> {
const rules = params.rules ?? ['security', 'bug', 'performance', 'best-practice'];
try {
// Step 1: 读取文件内容
const fs = this.context.services.get('tool:filesystem:read') as any;
const content = await fs.read({ path: params.path });
if (!content.success) {
return { success: false, error: `Cannot read file: ${params.path}` };
}
// Step 2: 解析 AST
let ast: t.File;
try {
ast = parse(content.content, {
sourceType: 'module',
plugins: ['typescript', 'jsx', 'decorators-legacy']
});
} catch (parseError) {
return { success: false, error: `Parse error: ${parseError.message}` };
}
// Step 3: 执行各项规则检查
const findings: ReviewFinding[] = [];
if (rules.includes('security')) {
findings.push(...this.checkSecurity(ast, content.content));
}
if (rules.includes('bug')) {
findings.push(...this.checkCommonBugs(ast));
}
if (rules.includes('performance')) {
findings.push(...this.checkPerformance(ast));
}
if (rules.includes('best-practice')) {
findings.push(...this.checkBestPractice(ast));
}
// Step 4: 生成报告
return {
success: true,
findings,
summary: {
total: findings.length,
errors: findings.filter(f => f.severity === 'error').length,
warnings: findings.filter(f => f.severity === 'warning').length,
info: findings.filter(f => f.severity === 'info').length
}
};
} catch (error) {
return { success: false, error: `Review failed: ${error.message}` };
}
}
// 安全检查规则
private checkSecurity(ast: t.File, source: string): ReviewFinding[] {
const findings: ReviewFinding[] = [];
traverse(ast, {
// 检测 eval() 使用
CallExpression(path) {
if (t.isIdentifier(path.node.callee, { name: 'eval' })) {
findings.push({
severity: 'error',
line: path.node.loc?.start.line ?? 0,
column: path.node.loc?.start.column ?? 0,
rule: 'no-eval',
message: 'Dangerous use of eval(). Code injection risk.',
suggestion: 'Use JSON.parse() for data or Function() constructor with sanitized input.'
});
}
// 检测 innerHTML 直接赋值(XSS)
if (
t.isMemberExpression(path.node.callee) &&
t.isIdentifier(path.node.callee.property, { name: 'innerHTML' })
) {
const parent = path.parent;
if (t.isAssignmentExpression(parent) && parent.left === path.node) {
findings.push({
severity: 'error',
line: path.node.loc?.start.line ?? 0,
column: path.node.loc?.start.column ?? 0,
rule: 'no-innerhtml-assignment',
message: 'Direct innerHTML assignment creates XSS vulnerability.',
suggestion: 'Use textContent for plain text, or sanitize with DOMPurify before innerHTML.'
});
}
}
// 检测 SQL 拼接
if (
t.isIdentifier(path.node.callee, { name: 'query' }) ||
path.node.callee.toString().includes('executeQuery')
) {
// 检查是否有模板字符串拼接
path.node.arguments.forEach(arg => {
if (t.isTemplateLiteral(arg) && arg.expressions.length > 0) {
findings.push({
severity: 'error',
line: arg.loc?.start.line ?? 0,
column: arg.loc?.start.column ?? 0,
rule: 'sql-injection',
message: 'Potential SQL injection: string interpolation in query.',
suggestion: 'Use parameterized queries instead of string concatenation.'
});
}
});
}
}
});
return findings;
}
// 常见 Bug 检查规则
private checkCommonBugs(ast: t.File): ReviewFinding[] {
const findings: ReviewFinding[] = [];
traverse(ast, {
// 检测 == 而非 ===
BinaryExpression(path) {
if (path.node.operator === '==' || path.node.operator === '!=') {
findings.push({
severity: 'warning',
line: path.node.loc?.start.line ?? 0,
column: path.node.loc?.start.column ?? 0,
rule: 'eqeqeq',
message: `Use ${path.node.operator}=== instead of ${path.node.operator}==.`,
suggestion: `Replace ${path.node.operator} with ${path.node.operator}===`
});
}
},
// 检测未使用的变量
VariableDeclarator(path) {
const binding = path.scope.getBinding(path.node.id.name);
if (binding && binding.references === 0 && !binding.path.node.id) {
// 排除导出的变量
if (!t.isExportNamedDeclaration(binding.path.parentPath?.parent)) {
findings.push({
severity: 'info',
line: path.node.loc?.start.line ?? 0,
column: path.node.loc?.start.column ?? 0,
rule: 'no-unused-vars',
message: `Variable '${path.node.id}' is declared but never used.`,
suggestion: 'Remove it or prefix with _ if intentionally reserved.'
});
}
}
},
// 检测 await 在非 async 函数中使用
AwaitExpression(path) {
let current = path.parent;
while (current) {
if (t.isFunction(current)) {
if (!t.isAsyncFunction(current)) {
findings.push({
severity: 'error',
line: path.node.loc?.start.line ?? 0,
column: path.node.loc?.start.column ?? 0,
rule: 'await-in-non-async',
message: 'await expression used in non-async function.',
suggestion: 'Make the enclosing function async.'
});
}
break;
}
current = current.parent;
}
}
});
return findings;
}
// 性能检查规则
private checkPerformance(ast: t.File): ReviewFinding[] {
const findings: ReviewFinding[] = [];
traverse(ast, {
// 检测 useEffect 缺少依赖数组(React)
CallExpression(path) {
if (
t.isIdentifier(path.node.callee, { name: 'useEffect' }) &&
path.node.arguments.length === 1
) {
findings.push({
severity: 'warning',
line: path.node.loc?.start.line ?? 0,
column: path.node.loc?.start.column ?? 0,
rule: 'react-hooks-exhaustive-deps',
message: 'useEffect has no dependency array, runs on every render.',
suggestion: 'Add a dependency array as the second argument.'
});
}
},
// 检测在渲染中创建新函数/对象
ArrowFunctionExpression(path) {
if (this.isInRender) {
const parent = path.parent;
if (
t.isProperty(parent) ||
(t.isJSXAttribute(parent) && parent.value === path.node)
) {
findings.push({
severity: 'info',
line: path.node.loc?.start.line ?? 0,
column: path.node.loc?.start.column ?? 0,
rule: 'render-create-objects',
message: 'Creating new functions/objects in render causes unnecessary re-renders.',
suggestion: 'Define outside component or use useCallback/useMemo.'
});
}
}
}
});
return findings;
}
// 最佳实践检查规则
private checkBestPractice(ast: t.File): ReviewFinding[] {
const findings: ReviewFinding[] = [];
traverse(ast, {
// 检测 console.log 而非结构化日志
CallExpression(path) {
if (
t.isMemberExpression(path.node.callee) &&
t.isIdentifier(path.node.callee.object, { name: 'console' }) &&
t.isIdentifier(path.node.callee.property, { name: 'log' })
) {
findings.push({
severity: 'info',
line: path.node.loc?.start.line ?? 0,
column: path.node.loc?.start.column ?? 0,
rule: 'no-console-log',
message: 'Avoid console.log in production code.',
suggestion: 'Use a structured logger like pino or winston with proper log levels.'
});
}
},
// 检测 any 类型
TSTypeAnnotation(path) {
if (
t.isTSTypeReference(path.node.typeAnnotation) &&
t.isIdentifier(path.node.typeAnnotation.typeName, { name: 'any' })
) {
findings.push({
severity: 'warning',
line: path.node.loc?.start.line ?? 0,
column: path.node.loc?.start.column ?? 0,
rule: 'no-explicit-any',
message: 'Avoid using `any` type. Use `unknown` or specific types instead.',
suggestion: 'Define proper interfaces or use `unknown` with type guards.'
});
}
}
});
return findings;
}
// 自动审查:当文件变更时触发
private async autoReview(filePath: string) {
const result = await this.execute({ path: filePath });
if (result.success && result.summary.total > 0) {
this.context.events.emit('review:completed', {
path: filePath,
findings: result.findings,
summary: result.summary
});
}
}
}
// 导出插件(DSH 插件加载器会调用)
export default new CodeReviewPlugin();
3.4 运行代码审查 Agent
将插件注册到配置中:
# .dsh/config.toml 新增
[plugins]
custom = [
"~/my-agent/plugins/code-review.ts"
]
启动 Agent 并执行审查任务:
# 启动 DSH CLI
dsh chat
# 在 Agent 对话框中输入:
# "请帮我审查 src/components/UserProfile.tsx 文件,重点检查安全问题和性能问题。"
# Agent 内部执行流程:
# 1. 模型接收任务,理解意图
# 2. 调用 filesystem_read 工具读取文件
# 3. 调用 code_review 工具执行 AST 分析
# 4. 汇总结果,输出结构化报告
预期输出示例:
✅ 代码审查完成:src/components/UserProfile.tsx
📊 审查摘要
总计: 5 个问题
🔴 错误: 1 个
🟡 警告: 2 个
🔵 建议: 2 个
🔴 安全性问题
[L42] eval() 使用 — 危险级别: 高
发现: eval(userInput)
建议: 使用 JSON.parse() 或 Function() 配合严格输入校验
🔴 SQL 注入风险
[L128] 字符串拼接查询
发现: db.query(`SELECT * FROM users WHERE id = ${userId}`)
建议: 使用参数化查询 db.query('SELECT * FROM users WHERE id = ?', [userId])
🟡 性能问题
[L89] useEffect 缺少依赖数组
发现: useEffect(() => { fetchData(); })
建议: 添加依赖数组 useEffect(() => { fetchData(); }, [])
🟡 类型安全
[L15] 使用了 any 类型
发现: const data: any = response
建议: 定义具体接口 interface UserData { id: number; name: string; }
🔵 最佳实践
[L67] 使用了 console.log
发现: console.log('User loaded:', user)
建议: 使用结构化日志 logger.info({ userId: user.id }, 'User loaded')
四、Cordis 事件总线:让插件之间无缝协作
4.1 为什么需要事件总线?
当 Agent 执行复杂任务时,往往需要多个插件协同工作。例如:
- 用户说"帮我创建一个 React 组件"
- Model 理解任务,生成代码
- Tool (Filesystem) 写入文件
- Tool (Linter/Formatter) 自动格式化
- Tool (Test Runner) 生成单元测试
- UI 实时显示执行进度
这些步骤之间的协作需要一个松耦合的通信机制——事件总线。
4.2 Cordis 事件系统详解
// Cordis 事件系统的核心接口
interface EventBus {
// 发布事件
emit(event: string, payload?: unknown): void;
// 订阅事件(返回取消订阅函数)
subscribe(event: string, handler: EventHandler): () => void;
// 订阅一次性事件
once(event: string, handler: EventHandler): void;
}
// 事件命名约定:<source>:<action> 或 <entity>:<action>
const WELL_KNOWN_EVENTS = {
// 文件系统事件
FILE_CHANGED: 'filesystem:file-changed',
FILE_CREATED: 'filesystem:file-created',
FILE_DELETED: 'filesystem:file-deleted',
// Agent 事件
AGENT_STARTED: 'agent:started',
AGENT_THINKING: 'agent:thinking',
AGENT_TOOL_CALL: 'agent:tool-call',
AGENT_TOOL_RESULT: 'agent:tool-result',
AGENT_COMPLETED: 'agent:completed',
AGENT_ERROR: 'agent:error',
// 代码审查事件
REVIEW_STARTED: 'review:started',
REVIEW_PROGRESS: 'review:progress',
REVIEW_COMPLETED: 'review:completed',
// 会话事件
SESSION_CREATED: 'session:created',
SESSION_RESTORED: 'session:restored',
};
4.3 事件驱动的多 Agent 协作
DSH 支持多 Agent 协作,核心机制就是事件总线:
// 多 Agent 协作示例:Code Review + PR Creation
// Review Agent 订阅审查完成事件
context.events.subscribe('review:completed', async (event) => {
const { path, findings, summary } = event.payload;
// 根据审查结果决定是否继续
if (summary.errors > 0) {
// 有错误,不允许合并,通知相关 Agent
context.events.emit('pr:status-update', {
path,
status: 'blocked',
reason: `${summary.errors} errors must be fixed before merge`,
findings
});
// 通知代码修改 Agent 修复问题
await context.services.get('agent:fixer').fix(findings);
} else {
// 通过审查,创建 PR
const prAgent = context.services.get('agent:pr-creator');
await prAgent.createPullRequest({
title: `Fix code review issues in ${path}`,
findings,
approved: true
});
}
});
// PR Creation Agent 订阅状态更新事件
context.events.subscribe('pr:status-update', async (event) => {
const { status, reason } = event.payload;
if (status === 'blocked') {
// 更新 GitHub PR 状态
const github = context.services.get('tool:github');
await github.updatePRStatus({
state: 'blocked',
description: reason
});
}
});
这种事件驱动的架构让每个 Agent 只需关注自己负责的任务,通过事件总线与其他 Agent 通信,实现真正的松耦合协作。
五、与 Claude Code、Cline 的横评:DSH 的差异化优势
5.1 竞品分析
目前市场上的 AI Coding Agent 框架可分为三类:
| 框架 | 类型 | 核心特点 |
|---|---|---|
| Claude Code / Cline | 单体 Agent CLI | 集成度高,但定制化困难 |
| GitHub Copilot Workspace | 云端 Agent | 与 GitHub 深度集成,但黑盒 |
| DeepSeek Harness | 插件化框架 | 开放架构,可自由组合 |
| Cursor Agent | IDE 集成 Agent | 与编辑器紧耦合 |
5.2 DSH 的差异化优势
1. 插件生态的开放性
Claude Code 和 Cline 都是"单体"设计——所有功能内置,想替换某个组件几乎不可能。DSH 的插件架构让任何人都可以开发新工具、新模型适配器、新循环策略。
# 发布一个工具插件到 npm
npm publish @my-org/dsh-tool-database
# 其他用户只需一行配置即可使用
# [tools]
# enabled = ["@my-org/dsh-tool-database"]
2. 多 Agent 原生支持
Claude Code 本质上是"单 Agent"——一个模型处理所有任务。DSH 从架构层面支持多 Agent 协作,不同 Agent 可以负责不同领域(代码审查、测试生成、安全扫描),通过事件总线协调。
3. 多种运行模式
# Web UI 模式:浏览器中的可视化界面
dsh web --port 3000
# TUI 模式:终端中的交互式界面
dsh tui
# Headless 模式:无界面,适合 CI/CD 集成
dsh run --task "Fix all TypeScript errors in src/"
# 作为库使用:嵌入到其他应用中
import { createAgent } from '@deepseek-ai/dsh';
const agent = createAgent({ /* config */ });
await agent.run('帮我重构 auth 模块');
4. 生产级可观测性
// DSH 内置的追踪和监控能力
context.events.subscribe('agent:tool-call', (event) => {
const { tool, params, duration } = event.payload;
// 发送到监控系统
metrics.increment('agent.tool.calls', { tool });
metrics.histogram('agent.tool.duration', duration, { tool });
// 记录链路追踪
tracer.recordSpan({
name: `tool:${tool}`,
duration,
attributes: { params: JSON.stringify(params) }
});
});
5.3 当前的局限性
实事求是地说,DSH 仍处于 v0.1 开发者预览阶段,有以下局限性:
- 文档不完善:官方文档仍在快速迭代,部分 API 可能有破坏性变更
- 插件生态早期:目前社区插件还很少,需要时间积累
- 生产验证不足:作为刚发布 1.5 天的项目,还没有大规模生产环境验证
- 调试工具缺失:缺乏类似 LangSmith 的完整 tracing 和调试能力
六、Cordis 架构的深层哲学:为什么"一切皆插件"是对的
6.1 从微服务到 Plugin-as-a-Service
Cordis 的设计哲学与微服务架构有异曲同工之妙。微服务将大型应用拆分为独立部署、独立演进的服务单元;Cordis 将 Agent 框架拆分为独立加载、独立替换的插件单元。
两者的核心收益相同:
- 独立演进:插件可以独立版本迭代,不影响其他组件
- 技术多样性:不同插件可以使用不同技术栈(TS、Python、Rust)
- 故障隔离:一个插件崩溃不会导致整个系统崩溃
- 按需组合:用户只需加载自己需要的插件,最小化资源占用
6.2 为什么 Agent Loop 也应该是插件?
这是 DSH 最反直觉的设计决策。传统观点认为 Agent Loop 是框架核心,不应该让用户随意替换。
但 DeepSeek 的逻辑是:不同任务需要不同的思考策略。
- 简单任务("查一下今天天气"):一步到位,不需要循环
- 中等任务("帮我写一个排序函数"):ReAct 循环即可
- 复杂任务("重构整个 auth 模块"):需要 Tree-of-Thoughts 或 Plan-and-Execute
- 探索性任务("研究一下这个开源项目的架构"):需要 Reflexion 自我反思
如果 Agent Loop 写死,框架就只能在"最通用的策略"和"最特殊的策略"之间做妥协。插件化意味着用户可以为每类任务选择最合适的循环策略,而不是用一把钥匙开所有锁。
6.3 对 AI Agent 发展的启示
DSH 的架构揭示了一个趋势:AI Agent 的竞争将从"模型能力"转向"框架工程"。
当模型能力趋于同质化(GPT-4o、Claude 3.5、DeepSeek V3 在很多场景下差距不大),框架层面的差异——工具生态、循环策略、协作机制——将成为新的竞争维度。
DeepSeek 选择在这个时间点开源 DSH,战略意图很明显:抢占 Agent 框架的标准话语权。谁定义了插件接口标准,谁就成为了 Agent 时代的"Android"——第三方可以在这个标准上开发各种应用。
七、生产环境集成指南
7.1 CI/CD 集成
将 DSH 集成到 GitHub Actions:
# .github/workflows/code-review.yml
name: AI Code Review
on:
pull_request:
paths:
- '**.ts'
- '**.tsx'
- '**.js'
- '**.jsx'
jobs:
review:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
with:
fetch-depth: 0
- name: Setup Node.js
uses: actions/setup-node@v4
with:
node-version: '20'
- name: Install DSH
run: npm install -g @deepseek-ai/dsh
- name: Run AI Code Review
env:
DEEPSEEK_API_KEY: ${{ secrets.DEEPSEEK_API_KEY }}
run: |
dsh run \
--config .dsh/config.toml \
--task "Review all changed TypeScript files in this PR. Focus on security and bug fixes."
- name: Post Review Comment
uses: actions/github-script@v7
with:
script: |
// 读取审查结果并发布到 PR 评论
const result = require('./review-result.json');
github.rest.issues.createComment({
issue_number: context.payload.pull_request.number,
owner: context.repo.owner,
repo: context.repo.repo,
body: `## 🤖 AI Code Review\n\n${result.summary}\n\n${result.details}`
});
7.2 私有化部署
对于数据安全要求高的企业,DSH 支持私有化部署:
# 使用 Docker 运行 DSH
docker run -d \
--name dsh \
-p 3000:3000 \
-v $(pwd)/.dsh:/app/.dsh \
-e DEEPSEEK_API_KEY=$DEEPSEEK_API_KEY \
-e DSG_LICENSE_SERVER=$LICENSE_SERVER \
deepseek-ai/dsh:latest
# 或使用 docker-compose 完整部署
# docker-compose.yml
version: '3.8'
services:
dsh:
image: deepseek-ai/dsh:latest
ports:
- "3000:3000"
volumes:
- ./projects:/projects
- ./.dsh:/app/.dsh
environment:
- DEEPSEEK_API_KEY=${DEEPSEEK_API_KEY}
- LOG_LEVEL=info
restart: unless-stopped
# 可选:接入本地模型(Ollama)
ollama:
image: ollama/ollama:latest
ports:
- "11434:11434"
volumes:
- ollama:/root/.ollama
restart: unless-stopped
7.3 性能监控与告警
// 生产级监控配置
import { createMetricsExporter } from '@deepseek-ai/dsh-monitoring';
// Prometheus 格式指标导出
const exporter = createMetricsExporter({
format: 'prometheus',
port: 9090,
path: '/metrics'
});
// 核心指标
exporter.register({
// Agent 运行指标
'dsh.agent.iterations': 'counter', // 总迭代次数
'dsh.agent.duration_seconds': 'histogram', // Agent 运行耗时
'dsh.agent.tasks.total': 'counter', // 总任务数
'dsh.agent.tasks.success': 'counter', // 成功任务数
'dsh.agent.tasks.failed': 'counter', // 失败任务数
// 工具调用指标
'dsh.tool.calls': 'counter', // 工具调用总数
'dsh.tool.duration_seconds': 'histogram',// 工具执行耗时
'dsh.tool.errors': 'counter', // 工具错误数
// 资源使用
'dsh.memory.bytes': 'gauge', // 内存使用
'dsh.context.tokens': 'gauge', // 上下文 token 数
});
// 设置告警规则
exporter.addAlert({
name: 'high_error_rate',
condition: 'rate(dsh.agent.tasks.failed[5m]) / rate(dsh.agent.tasks.total[5m]) > 0.1',
severity: 'critical',
annotation: 'Agent 错误率超过 10%,需要立即检查'
});
八、总结与展望
8.1 DSH 的核心价值
DeepSeek Harness 的出现,解决了 AI Agent 领域长期存在的几个痛点:
| 问题 | 传统方案 | DSH 方案 |
|---|---|---|
| 模型绑定 | 换模型要改代码 | 换 Model Adapter 插件即可 |
| 工具碎片化 | 每个项目自己定义 | 统一的 Tool Plugin 标准 |
| 循环黑盒 | 框架写死,无法定制 | Agent Loop 插件化 |
| 多 Agent 协作 | 需要自己实现 | Cordis 事件总线原生支持 |
| 可观测性 | 依赖外部方案 | 内置 tracing 和 metrics |
8.2 未来展望
根据 DeepSeek 官方路线图和行业趋势,DSH 未来可能在以下方向演进:
- Plugin Marketplace:类似 VS Code Marketplace 的插件市场,降低插件发现和安装成本
- 分布式 Agent:支持跨机器的多 Agent 部署,利用多机算力
- 持久化 Agent:支持 Agent 状态的持久化和跨会话恢复
- Formal Verification:对关键插件(尤其是 Agent Loop)进行形式化验证,确保行为确定性
- 多模态扩展:原生的图像、视频、音频工具插件支持
8.3 给开发者的建议
现在要不要上车?
- 如果你是框架开发者:强烈建议研究 DSH 的 Cordis 架构,它代表了一种被验证有效的 Agent 框架设计思路
- 如果你是应用开发者:可以先用 DSH 的 Web UI 体验一下,感受插件化带来的灵活性,再决定是否深入
- 如果你在选型:Claude Code 适合追求开箱即用的团队;DSH 适合有定制化需求、愿意投入工程资源的团队
最后一句掏心窝的话:AI Agent 的竞争刚刚开始,今天的框架格局不代表明天的市场格局。DeepSeek Harness 最大的价值不是它现在有多强,而是它证明了 "开放插件生态"这条路是走得通的。当插件市场繁荣起来的那天,DSH 才真正展示出它的野心。
本文相关代码示例基于 DeepSeek Harness v0.1 开发者预览版,API 可能在后续版本中发生变化。建议关注官方 GitHub 仓库获取最新文档:https://github.com/deepseek-ai/deepseek-harness
Tags: DeepSeek Harness | Cordis | AI Agent | 插件架构 | Vibe Coding | AI Coding | 大模型应用框架 | TypeScript