mirror of
https://github.com/deepseek-ai/deepseek-harness
synced 2026-08-15 21:04:50 +00:00
fix(docs): address documentation site review
This commit is contained in:
@@ -4,10 +4,11 @@
|
||||
|
||||
## 定义 Config 类型
|
||||
|
||||
在插件中导出一个 `Config` 类型和可选的默认值:
|
||||
在插件中导出一个 `Config` 类型和同名的 Schemastery schema;默认值直接写在 schema 中:
|
||||
|
||||
```typescript
|
||||
import type { Context } from 'cordis'
|
||||
import Schema from 'schemastery'
|
||||
|
||||
export const name = 'my-plugin'
|
||||
|
||||
@@ -17,11 +18,11 @@ export interface Config {
|
||||
verbose?: boolean
|
||||
}
|
||||
|
||||
export const Config = {
|
||||
greeting: 'Hello',
|
||||
maxRetries: 3,
|
||||
verbose: false,
|
||||
}
|
||||
export const Config: Schema<Config> = Schema.object({
|
||||
greeting: Schema.string().default('Hello'),
|
||||
maxRetries: Schema.number().default(3),
|
||||
verbose: Schema.boolean().default(false),
|
||||
})
|
||||
|
||||
export function apply(ctx: Context, config: Config) {
|
||||
console.log(config.greeting) // 用户配置或默认值
|
||||
@@ -37,7 +38,7 @@ export function apply(ctx: Context, config: Config) {
|
||||
maxRetries: 5
|
||||
```
|
||||
|
||||
未提供的字段使用导出的 `Config` 对象中的默认值。
|
||||
插件加载时,Cordis 会通过导出的 schema 校验配置,并填充未提供字段的默认值。不要导出普通对象作为 `Config`,因为它不满足 Cordis 要求的 Standard Schema 接口。
|
||||
|
||||
## Schema 校验
|
||||
|
||||
@@ -92,7 +93,7 @@ export interface Config {
|
||||
|
||||
```typescript
|
||||
export function apply(ctx: Context, config: Config) {
|
||||
if (!ctx.llm.hasAdapter(config.model)) {
|
||||
if (!ctx.llm.models().includes(config.model)) {
|
||||
throw new Error(`Model "${config.model}" is not registered by any LLM adapter`)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -28,10 +28,8 @@ import type { Context } from 'cordis'
|
||||
export const name = 'hello-plugin'
|
||||
|
||||
export function apply(ctx: Context) {
|
||||
// 监听 agent-loop 的 ready 事件
|
||||
ctx.on('ready', () => {
|
||||
console.log('[hello-plugin] 插件已加载!')
|
||||
})
|
||||
// apply 被调用时,插件的必选依赖已就绪
|
||||
console.log('[hello-plugin] 插件已加载!')
|
||||
}
|
||||
```
|
||||
|
||||
@@ -100,17 +98,14 @@ export default {
|
||||
### 类形式
|
||||
|
||||
```typescript
|
||||
import { Service } from 'cordis'
|
||||
import { Service, type Context } from 'cordis'
|
||||
|
||||
export default class MyService extends Service {
|
||||
static inject = ['tools']
|
||||
|
||||
constructor(ctx: Context) {
|
||||
super(ctx, 'myService')
|
||||
}
|
||||
|
||||
start() {
|
||||
// 服务启动逻辑
|
||||
// 构造函数内完成同步初始化
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
@@ -133,14 +133,14 @@ defineTool({
|
||||
// ...
|
||||
presentCall(args) {
|
||||
return {
|
||||
intent: 'terminal',
|
||||
title: `bash(${JSON.stringify(args.command).slice(0, 60)})`,
|
||||
card: 'terminal',
|
||||
title: args.command,
|
||||
}
|
||||
},
|
||||
presentResult(args, result) {
|
||||
return {
|
||||
intent: 'terminal',
|
||||
body: result.content.map(b => b.type === 'text' ? b.text : '').join(''),
|
||||
card: 'terminal',
|
||||
output: result.content.map(b => b.type === 'text' ? b.text : '').join(''),
|
||||
}
|
||||
},
|
||||
})
|
||||
@@ -156,9 +156,7 @@ defineTool({
|
||||
// 这样就够了:
|
||||
ctx.tools.register(defineTool({ /* ... */ }))
|
||||
|
||||
// 不需要:
|
||||
// const dispose = ctx.tools.register(...)
|
||||
// ctx.on('dispose', dispose)
|
||||
// 不需要额外保存 disposer 或注册清理逻辑
|
||||
```
|
||||
|
||||
## 完整实战示例
|
||||
|
||||
@@ -24,15 +24,15 @@ Cordis 提供多种事件触发模式,适用于不同场景:
|
||||
|
||||
### emit — 广播
|
||||
|
||||
所有监听器并行执行,不关心返回值:
|
||||
所有监听器同步执行,不关心返回值:
|
||||
|
||||
```typescript
|
||||
// 触发
|
||||
ctx.emit('agent/turn-end', { agentId, turnIndex })
|
||||
ctx.emit('my-plugin/ready', { id: 'worker-1' })
|
||||
|
||||
// 监听
|
||||
ctx.on('agent/turn-end', ({ agentId, turnIndex }) => {
|
||||
console.log(`Turn ${turnIndex} ended`)
|
||||
ctx.on('my-plugin/ready', ({ id }) => {
|
||||
console.log(`${id} is ready`)
|
||||
})
|
||||
```
|
||||
|
||||
@@ -53,7 +53,7 @@ ctx.on('some-check', (input) => {
|
||||
|
||||
### serial — 顺序执行
|
||||
|
||||
所有监听器按注册顺序依次执行(异步安全):
|
||||
监听器按注册顺序依次执行,并等待异步结果;第一个返回非空值的监听器会终止后续执行:
|
||||
|
||||
```typescript
|
||||
await ctx.serial('setup-phase', context)
|
||||
@@ -61,18 +61,16 @@ await ctx.serial('setup-phase', context)
|
||||
|
||||
### waterfall — 管道
|
||||
|
||||
每个监听器接收前一个的输出,形成数据管道。**必须调用 `next()` 传递给下游**,不调用即为否决:
|
||||
每个监听器可以包装下游返回值,形成处理链。**必须调用 `next()` 传递给下游**,不调用即为否决:
|
||||
|
||||
```typescript
|
||||
// 触发
|
||||
const finalMessages = await ctx.waterfall('llm/pre-request', messages)
|
||||
const output = await ctx.waterfall('my-plugin/transform', input, async () => input)
|
||||
|
||||
// 监听(必须调用 next)
|
||||
ctx.on('llm/pre-request', async (messages, next) => {
|
||||
// 可以修改 messages
|
||||
messages.push(extraMessage)
|
||||
// 必须调用 next() 传递给下一个监听器
|
||||
return next(messages)
|
||||
ctx.on('my-plugin/transform', async (_input, next) => {
|
||||
const downstream = await next()
|
||||
return downstream.trim()
|
||||
})
|
||||
```
|
||||
|
||||
@@ -89,6 +87,7 @@ declare module 'cordis' {
|
||||
interface Events {
|
||||
'my-plugin/ready': (payload: { id: string }) => void
|
||||
'my-plugin/check': (input: string) => boolean | undefined
|
||||
'my-plugin/transform': (input: string, next: () => Promise<string>) => Promise<string>
|
||||
}
|
||||
}
|
||||
|
||||
@@ -96,20 +95,11 @@ declare module 'cordis' {
|
||||
// 都有正确的类型推导
|
||||
```
|
||||
|
||||
## 命名约定
|
||||
## Cordis 事件与会话记录
|
||||
|
||||
Harness 事件遵循 `namespace/action` 命名:
|
||||
Harness 的 Cordis 事件遵循 `namespace/action` 命名,例如 `agent/pre-step`、`agent/request`、`agent/step-result`、`tools/result` 和 `session/event`。完整签名与触发模式见[Events 目录](../../../../cordis-catalog/events.md)。
|
||||
|
||||
```
|
||||
agent/pre-step — agent 执行一步之前
|
||||
agent/post-step — agent 执行一步之后
|
||||
tool/call — tool 被调用
|
||||
tool/result — tool 返回结果
|
||||
llm/pre-request — LLM 请求发送前
|
||||
session/event — 会话事件被记录
|
||||
compact/start — 压缩开始
|
||||
compact/end — 压缩结束
|
||||
```
|
||||
`turn/*`、`step/*`、`tool/call`、`tool/result` 和 `compact/*` 是持久化的会话事件类型,不是同名 Cordis 事件。需要观察它们时,监听 `session/event` 并检查 `event.type`。
|
||||
|
||||
## 事件也是效果
|
||||
|
||||
@@ -118,7 +108,7 @@ compact/end — 压缩结束
|
||||
```typescript
|
||||
export function apply(ctx: Context) {
|
||||
// 这个监听器在插件 dispose 时自动清理
|
||||
ctx.on('agent/turn-end', handler)
|
||||
ctx.on('tools/result', handler)
|
||||
}
|
||||
```
|
||||
|
||||
@@ -132,14 +122,10 @@ import type { Context } from 'cordis'
|
||||
export const name = 'tool-logger'
|
||||
|
||||
export function apply(ctx: Context) {
|
||||
ctx.on('tool/call', ({ name, args }) => {
|
||||
console.log(`[tool] ${name}(${JSON.stringify(args)})`)
|
||||
})
|
||||
|
||||
ctx.on('tool/result', ({ name, result }) => {
|
||||
ctx.on('tools/result', (exec, result) => {
|
||||
console.log(`[tool] ${exec.name}(${JSON.stringify(exec.arguments)})`)
|
||||
const text = result.content
|
||||
.filter(b => b.type === 'text')
|
||||
.map(b => b.text)
|
||||
.map(block => block.type === 'text' ? block.text : '')
|
||||
.join('')
|
||||
console.log(`[tool result] ${text.slice(0, 100)}`)
|
||||
})
|
||||
|
||||
@@ -105,14 +105,6 @@ fiber.dispose()
|
||||
export function apply(ctx: Context) {
|
||||
console.log('plugin loading')
|
||||
|
||||
ctx.on('ready', () => {
|
||||
console.log('context ready')
|
||||
})
|
||||
|
||||
ctx.on('dispose', () => {
|
||||
console.log('plugin disposing')
|
||||
})
|
||||
|
||||
ctx.effect(() => {
|
||||
console.log('effect registered')
|
||||
return () => console.log('effect cleaned up')
|
||||
@@ -124,12 +116,10 @@ export function apply(ctx: Context) {
|
||||
```
|
||||
plugin loading
|
||||
effect registered
|
||||
context ready
|
||||
```
|
||||
|
||||
卸载时输出(逆序):
|
||||
卸载时输出:
|
||||
```
|
||||
plugin disposing
|
||||
effect cleaned up
|
||||
```
|
||||
|
||||
|
||||
@@ -90,8 +90,11 @@ export default class MetricsService extends Service {
|
||||
// 必选:服务不存在时,插件不会加载
|
||||
export const inject = ['tools']
|
||||
|
||||
// 可选:服务不存在时,插件仍然加载,但 ctx.xxx 可能是 undefined
|
||||
export const inject = { optional: ['metrics'] }
|
||||
// 可选:不写入 inject,使用时通过 ctx.get() 查询
|
||||
export function apply(ctx: Context) {
|
||||
const metrics = ctx.get('metrics')
|
||||
metrics?.record('plugin_loaded', 1)
|
||||
}
|
||||
```
|
||||
|
||||
### 服务消失时的行为
|
||||
@@ -109,7 +112,10 @@ export const inject = { optional: ['metrics'] }
|
||||
|
||||
```yaml
|
||||
- id: group-a
|
||||
name: 'group:'
|
||||
name: '@cordisjs/plugin-group'
|
||||
group: true
|
||||
isolate:
|
||||
bash: true
|
||||
config:
|
||||
- name: '@deepseek-ai/dsh-bash-local'
|
||||
config:
|
||||
@@ -117,7 +123,10 @@ export const inject = { optional: ['metrics'] }
|
||||
- name: './src/plugin-a.ts'
|
||||
|
||||
- id: group-b
|
||||
name: 'group:'
|
||||
name: '@cordisjs/plugin-group'
|
||||
group: true
|
||||
isolate:
|
||||
bash: true
|
||||
config:
|
||||
- name: '@deepseek-ai/dsh-bash-local'
|
||||
config:
|
||||
|
||||
@@ -114,6 +114,8 @@ interface GenerateOptions {
|
||||
maxTokens?: number
|
||||
/** 温度 */
|
||||
temperature?: number
|
||||
/** 取消或卸载时中止进行中的请求 */
|
||||
signal?: AbortSignal
|
||||
}
|
||||
```
|
||||
|
||||
@@ -160,7 +162,10 @@ mock 适配器是学习 StreamChunk 协议的最佳起点——它用纯本地
|
||||
|
||||
```typescript
|
||||
async *stream(options: GenerateOptions): AsyncIterable<StreamChunk> {
|
||||
const response = await fetch(this.endpoint, { /* ... */ })
|
||||
const response = await fetch(this.endpoint, {
|
||||
// ...method、headers 和 body
|
||||
signal: options.signal,
|
||||
})
|
||||
if (!response.ok) {
|
||||
throw new Error(`API error: ${response.status}`)
|
||||
}
|
||||
|
||||
@@ -13,7 +13,7 @@ hero:
|
||||
link: /develop/basic/
|
||||
features:
|
||||
- title: 插件化架构
|
||||
details: 基于 Cordis 效果系统,所有能力通过插件注册,加载即生效、卸载即还原。
|
||||
details: 基于 Cordis 插件系统,所有能力通过插件注册,加载即生效、卸载即还原。
|
||||
- title: 配置即组合
|
||||
details: 一个 cordis.yml 决定整个 Agent 的能力组合——换模型、加工具,只需改一行配置。
|
||||
- title: 开箱即用
|
||||
|
||||
Reference in New Issue
Block a user