DeepSeek Harness: 插件配置:Config 与 Schemastery

最后更新:2026-08-31

配置是插件的"调节旋钮"——API Key、超时时间、功能开关,这些因环境而异的参数不应硬编码在插件里,而应通过配置系统外置。Schemastery 是 Cordis 的声明式配置框架,一次定义,自动获得类型安全、UI 表单、校验和文档。

💡 提示:Schemastery 的核心理念是"声明即文档"——你用 Schema 定义配置结构,框架自动生成 Web UI 表单和校验逻辑。改配置不需要改代码。

📋 前置知识:已完成 15-define-tool.md,能编写基本工具

1. 你将学到


2. Schemastery 声明式配置

(1) 基本概念

Schemastery 是 Cordis 内置的 Schema 定义库,灵感来自 Zod 和 JSON Schema,但专为交互式配置设计。

TYPESCRIPT
import { Schema } from '@deepseek-ai/cordis'

export const Config = Schema.object({
  apiKey: Schema.string().required().description('API key for the service'),
  maxRetries: Schema.number().default(3).description('Maximum retry attempts'),
  debug: Schema.boolean().default(false).description('Enable debug logging')
})

(2) 与 JSON Schema 的对比

维度 Schemastery JSON Schema
语法 链式 API JSON 对象
类型推断 ✅ 自动 ❌ 需手动
UI 表单 ✅ 自动生成 ❌ 需额外工具
默认值 .default() default 字段
描述 .description() description 字段
校验 链式校验器 pattern/min/max

(3) ▶ 示例 3

TYPESCRIPT
export const Config = Schema.object({
  apiKey: Schema.string().required(),
  maxRetries: Schema.number().default(3)
})

export const name = 'my-plugin'

export function apply(ctx: Context) {
  // ctx.config 类型自动推断
  ctx.logger.info(`API key: ${ctx.config.apiKey}`)
  ctx.logger.info(`Max retries: ${ctx.config.maxRetries}`)
}

框架会将用户提供的配置值校验后注入 ctx.config


3. Schema 类型

(1) ▶ 示例 1

TYPESCRIPT
Schema.string()                           // 任意字符串
Schema.string().required()                // 必填
Schema.string().default('hello')          // 默认值
Schema.string().description('Your name')  // 描述
Schema.string().pattern(/^[a-z]+$/)       // 正则校验
Schema.string().min(1).max(100)           // 长度限制

(2) ▶ 示例 2

TYPESCRIPT
Schema.number()                    // 任意数字
Schema.number().default(0)         // 默认值
Schema.number().min(0).max(100)    // 范围限制
Schema.number().step(1)            // 步长(用于 UI 滑块)
Schema.integer()                   // 整数

(3) Schema.boolean

TYPESCRIPT
Schema.boolean()                   // 布尔值
Schema.boolean().default(false)    // 默认 false

(4) Schema.object

TYPESCRIPT
Schema.object({
  host: Schema.string().default('localhost'),
  port: Schema.number().default(5432),
  ssl: Schema.boolean().default(false)
})

(5) Schema.array

TYPESCRIPT
Schema.array(Schema.string())                          // 字符串数组
Schema.array(Schema.string()).default([])              // 默认空数组
Schema.array(Schema.object({                           // 对象数组
  name: Schema.string().required(),
  url: Schema.string().required()
}))

(6) Schema.union / Schema.const

TYPESCRIPT
// 枚举值
Schema.union(['read', 'write', 'execute'])

// 常量
Schema.const('fixed-value')

// 带描述的枚举
Schema.union([
  Schema.const('read').description('Read-only access'),
  Schema.const('write').description('Read and write access'),
  Schema.const('execute').description('Full access')
])

(7) Schema.dict

TYPESCRIPT
// 字典类型
Schema.dict(
  Schema.string(),           // 值类型
  Schema.string()            // 键类型(可选)
)

4. 链式修饰符

(1) .required()

标记为必填。用户未提供时,框架报错并阻止加载。

TYPESCRIPT
apiKey: Schema.string().required()

(2) .default(value)

设置默认值。用户未提供时使用默认值,不报错。

TYPESCRIPT
maxRetries: Schema.number().default(3)
timeout: Schema.number().default(30000)

(3) .description(text)

为配置项添加描述,显示在 Web UI 中作为表单标签的提示文本。

TYPESCRIPT
apiKey: Schema.string().required()
  .description('API key obtained from the service dashboard')

(4) .min() / .max()

数值范围或字符串长度限制:

TYPESCRIPT
port: Schema.number().min(1).max(65535).default(8080)
name: Schema.string().min(1).max(50)

(5) .pattern()

正则校验:

TYPESCRIPT
email: Schema.string().pattern(/^[^@]+@[^@]+\.[^@]+$/)
  .description('Valid email address')

(6) .step()

数值步长,影响 UI 滑块的精度:

TYPESCRIPT
timeout: Schema.number().min(1000).max(300000).step(1000).default(30000)

(7) 组合使用

TYPESCRIPT
const Config = Schema.object({
  apiKey: Schema.string()
    .required()
    .pattern(/^sk-[a-zA-Z0-9]+$/)
    .description('DeepSeek API key (starts with sk-)'),
  
  maxTokens: Schema.number()
    .min(1).max(32768)
    .default(4096)
    .description('Maximum tokens per request'),
  
  model: Schema.union(['deepseek-chat', 'deepseek-reasoner'])
    .default('deepseek-chat')
    .description('Model to use'),
  
  temperature: Schema.number()
    .min(0).max(2).step(0.1)
    .default(0.7)
    .description('Sampling temperature')
})

5. 嵌套配置

(1) 嵌套对象

TYPESCRIPT
const Config = Schema.object({
  database: Schema.object({
    host: Schema.string().default('localhost'),
    port: Schema.number().default(5432),
    name: Schema.string().required(),
    ssl: Schema.boolean().default(false)
  }).default({ host: 'localhost', port: 5432, ssl: false }),

  cache: Schema.object({
    enabled: Schema.boolean().default(true),
    ttl: Schema.number().default(3600).description('Cache TTL in seconds')
  }).default({ enabled: true, ttl: 3600 })
})

(2) 深层嵌套

TYPESCRIPT
const Config = Schema.object({
  llm: Schema.object({
    provider: Schema.union(['deepseek', 'openai']).default('deepseek'),
    deepseek: Schema.object({
      apiKey: Schema.string().required(),
      model: Schema.string().default('deepseek-chat')
    }),
    openai: Schema.object({
      apiKey: Schema.string(),
      endpoint: Schema.string().default('https://api.openai.com/v1')
    })
  })
})

(3) 可复用 Schema

TYPESCRIPT
const ConnectionSchema = Schema.object({
  host: Schema.string().default('localhost'),
  port: Schema.number().default(5432),
  timeout: Schema.number().default(5000)
})

const Config = Schema.object({
  primary: ConnectionSchema.description('Primary connection'),
  replica: ConnectionSchema.description('Replica connection')
})

6. 配置在 Web UI 设置页的展示

(1) 自动表单生成

Schemastery 定义的 Config 会自动在 Web UI 的设置页生成表单:

Schema 类型 UI 控件
Schema.string() 文本输入框
Schema.number() 数字输入框 / 滑块
Schema.boolean() 开关
Schema.union() 下拉选择
Schema.array() 列表编辑器
Schema.object() 分组面板
Schema.dict() 键值对编辑器

(2) description 的展示

每个字段的 .description() 显示为输入框下方的提示文字:

TEXT 📖 仅展示
┌─────────────────────────────────────────┐
│ API Key                                 │
│ [sk-xxxxxxxxxxxxxxx                   ] │
│ DeepSeek API key (starts with sk-)      │
├─────────────────────────────────────────┤
│ Max Tokens                              │
│ [4096                                 ] │
│ Maximum tokens per request              │
└─────────────────────────────────────────┘

(3) 校验反馈

用户输入不符合 Schema 时,UI 即时提示:

TEXT 📖 仅展示
┌─────────────────────────────────────────┐
│ API Key                                 │
│ [invalid-key                          ] │
│ ❌ Must match pattern: /^sk-[a-zA-Z0-9]+$/ │
└─────────────────────────────────────────┘

7. 配置验证与错误提示

(1) 启动时校验

插件加载时,框架自动校验配置:

TYPESCRIPT
const Config = Schema.object({
  port: Schema.number().min(1).max(65535)
})

// 用户配置 port: -1
// → Error: Invalid config for plugin my-plugin:
//   port: must be >= 1

校验失败时,插件不会被加载,错误信息输出到终端。

(2) 类型安全

TypeScript 根据 Config 定义自动推断 ctx.config 的类型:

TYPESCRIPT
const Config = Schema.object({
  apiKey: Schema.string().required(),
  maxRetries: Schema.number().default(3)
})

export function apply(ctx: Context) {
  ctx.config.apiKey     // string ✅
  ctx.config.maxRetries // number ✅
  ctx.config.unknown    // 类型错误 ❌
}

(3) 自定义校验

TYPESCRIPT
const Config = Schema.object({
  startDate: Schema.string().required(),
  endDate: Schema.string().required()
}).validate((value) => {
  if (new Date(value.startDate) > new Date(value.endDate)) {
    throw new Error('startDate must be before endDate')
  }
  return value
})

8. 动态配置更新

配置热更新

(1) 配置变更监听

TYPESCRIPT
export function apply(ctx: Context) {
  ctx.on('config/updated', (newConfig) => {
    ctx.logger.info('config updated:', newConfig)
    // 根据新配置调整行为
  })
}

(2) 热更新流程

100%
graph LR
    UI[用户修改配置] --> VALID[Schema 校验]
    VALID --> APPLY[应用新配置]
    APPLY --> EMIT[触发 config/updated]
    EMIT --> RELOAD[插件响应更新]

(3) 需要重启的配置

某些配置(如端口号、依赖注入)变更后需要重启:

TYPESCRIPT
export function apply(ctx: Context) {
  ctx.on('config/updated', (config) => {
    if (config.port !== ctx.config.port) {
      ctx.logger.warn('port change requires restart')
    }
  })
}

❓ 常见问题

Q Config 必须导出为 Config 这个名字吗?
A 是的,框架约定查找 export const Config。其他名称不会被识别。
Q 可以在运行时动态添加配置项吗?
A 不推荐。Config 应该在插件加载前定义好。如果需要动态行为,用 ctx.on('config/updated') 响应配置变更。
Q 配置项很多时怎么组织?
A 用嵌套 Schema.object 分组,每组有清晰的 description。Web UI 会将嵌套对象渲染为折叠面板。
Q 敏感信息(如 API Key)如何保护?
A Schemastery 支持 .hidden() 修饰符,UI 中用密码框显示: typescript apiKey: Schema.string().required().hidden()
Q 配置值可以是函数吗?
A 不可以。配置必须是 JSON 可序列化的值(string/number/boolean/object/array)。函数行为应该在 apply 中实现。
Q 多个插件的配置会冲突吗?
A 不会。每个插件有独立的配置命名空间 plugins.plugin-name.config,互不干扰。 ---

📖 小节


📝 作业

1. ⭐ 基础题:为 file_count 工具添加 Config,包含 defaultPath(字符串,默认当前目录)和 maxDepth(数字,默认 10)两个配置项。在 Web UI 设置页验证表单生成。

2. ⭐⭐ 进阶题:编写一个多连接配置的插件,Config 包含 connections(对象数组,每项有 host/port/username/password),用嵌套 Schema.object 定义。验证:缺少必填项时报错,默认值正确填充。

3. ⭐⭐⭐ 挑战题:创建一个带自定义校验的配置:startDateendDate 必须满足 startDate < endDate;maxRetries 必须是正整数。测试:输入非法值,确认校验错误信息准确显示在 Web UI 中。

Web-Tutorial.com

Web-Tutorial 技术团队

由多位开发者共同维护的编程教程平台。每篇教程由对应领域的开发者编写和审核,确保内容准确可靠。如发现任何问题,欢迎向我们反馈。

100%

🙏 帮我们做得更好

我们是刚上线的编程教程站,几个人的小团队,精力有限。页面虽经检查,难免还有疏漏——链接失效、排版错乱、内容有误、语言生硬……

如果您发现了,麻烦告诉我们,我们会在收到反馈后第一时间进行修复,再次感谢您的光临 🙏