编程 插件化 Agent 框架新范式:DeepSeek Harness Cordis 架构深度拆解

2026-08-14 16:18:17 +0800 CST views 90

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 testcargo 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"}}

执行结果返回 → 模型总结输出

这套机制有效,但有几个致命缺陷:

  1. 工具定义碎片化:每个 Agent 项目自己定义工具接口,没有统一标准
  2. 上下文管理割裂:模型调用工具后的状态和原始上下文如何融合?没有标准答案
  3. 循环控制黑盒:当工具调用失败时,模型如何决定重试、降级还是放弃?没有通用框架
  4. 多 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 LoopAgent 主循环,控制思考-执行-反思的迭代逻辑✅ 完整可替换
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 执行复杂任务时,往往需要多个插件协同工作。例如:

  1. 用户说"帮我创建一个 React 组件"
  2. Model 理解任务,生成代码
  3. Tool (Filesystem) 写入文件
  4. Tool (Linter/Formatter) 自动格式化
  5. Tool (Test Runner) 生成单元测试
  6. 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 AgentIDE 集成 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 开发者预览阶段,有以下局限性:

  1. 文档不完善:官方文档仍在快速迭代,部分 API 可能有破坏性变更
  2. 插件生态早期:目前社区插件还很少,需要时间积累
  3. 生产验证不足:作为刚发布 1.5 天的项目,还没有大规模生产环境验证
  4. 调试工具缺失:缺乏类似 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 未来可能在以下方向演进:

  1. Plugin Marketplace:类似 VS Code Marketplace 的插件市场,降低插件发现和安装成本
  2. 分布式 Agent:支持跨机器的多 Agent 部署,利用多机算力
  3. 持久化 Agent:支持 Agent 状态的持久化和跨会话恢复
  4. Formal Verification:对关键插件(尤其是 Agent Loop)进行形式化验证,确保行为确定性
  5. 多模态扩展:原生的图像、视频、音频工具插件支持

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

推荐文章

SpaceX 600亿美元收购Cursor(中篇)
2026-06-22 03:30:23 +0800 CST
MySQL 日志详解
2024-11-19 02:17:30 +0800 CST
什么是Vue实例(Vue Instance)?
2024-11-19 06:04:20 +0800 CST
一个收银台的HTML
2025-01-17 16:15:32 +0800 CST
使用Python提取图片中的GPS信息
2024-11-18 13:46:22 +0800 CST
Go 单元测试
2024-11-18 19:21:56 +0800 CST
Go语言中实现RSA加密与解密
2024-11-18 01:49:30 +0800 CST
程序员茄子在线接单