从 Cordis 模式到真实工具流水线
注册、执行、观察,一个无需密钥的完整回路。
工具服务负责注册与执行,结果事件连接独立观察者,依赖图保证每个插件在正确时机启动。
greet-tool.ts
const result = await ctx.tools.execute({
callId: CallId('demo-1'),
name: 'greet',
arguments: { name: 'Cordis' },
signal: new AbortController().signal,
})
console.log(
'tool replied:',
JSON.stringify(result.content),
)
同一条链路复用了前面所有框架机制
服务等待、effect 清理、类型声明和事件解耦在真实 Harness 接口上同时工作。
等待注册表
→inject: ['tools']注册工具
→ctx.tools.register()执行调用
→ctx.tools.execute()发出结果
tools/result为什么无需密钥
示例代码直接调用工具执行管线,相当于模型已经决定调用 greet。CallId 只为调用提供关联 id,没有模型请求。
defineTool 同时定义模型契约与结果契约
parameters 会转成展示给模型的 JSON Schema,并在 execute 前校验参数;output.schema 声明规范值,render 另行生成原生且可持久化的内容。
greet-tool.ts
import { defineTool } from '@deepseek-ai/dsh-tools'
import { CallId } from '@deepseek-ai/dsh-llm'
ctx.tools.register(defineTool({
name: 'greet',
description: 'Greet the named person.',
parameters: {
name: {
type: 'string',
required: true,
description: 'Who to greet',
},
},
output: {
schema: { type: 'string' },
render: (_args, value) => [
{ type: 'text', text: value },
],
},
async execute(args) {
return `Hello, ${args.name}!`
},
}))parameters模型可见 schema、类型推导、运行时校验
execute返回 output.schema 声明的规范值
output.render生成 Native 与持久化结果内容
registration effect插件卸载时自动注销工具
ctx.tools.execute() 走的是实际 Harness 工具管线
调用需要关联 id、工具名、已结构化参数和取消信号。返回值包含渲染后的 content,而不只是 execute 的原始字符串。
CallId('demo-1')关联调用与结果arguments: { name: 'Cordis' }由工具 schema 校验AbortController().signal传递取消能力result.content[{ "type": "text", "text": "Hello, Cordis!" }]观察者只依赖事件,不依赖工具插件
包级 type-only import 引入 dsh-tools 的声明合并,让 tools/result 及 payload 获得类型,不产生运行时导入。
tool-logger.ts
import type {} from '@deepseek-ai/dsh-tools'
export const inject = ['tools']
export function apply(ctx: Context) {
ctx.on('tools/result', (exec, result) => {
const text = result.content
.map(block => block.type === 'text' ? block.text : '')
.join('')
console.log(`[tool-logger] ${exec.name} -> ${text}`)
})
}工具执行完成
→物化结果 content
→发出 tools/resultlogger 在这里输出
→execute Promise 兑现调用方随后输出
两个插件互不认识
greet-tool 只使用工具注册表,tool-logger 只监听结果事件。tools 服务与事件系统把它们连接起来。
工具服务自己也有依赖
dsh-tools 会向系统提示词贡献工具 schema,因此它注入 systemPrompt。缺少提供方时,工具服务保持 PENDING,后面的两个插件也会等待。
cordis.yml
- name: '@deepseek-ai/dsh-system-prompt'
- name: '@deepseek-ai/dsh-tools'
- name: './tool-logger.ts'
- name: './greet-tool.ts'systemPromptdsh-system-prompt
→toolsdsh-tools
→greet-tooltool-logger
node --import tsx ../../vendor/cordis/bin.js[tool-logger] greet -> Hello, Cordis!
tool replied: [{"type":"text","text":"Hello, Cordis!"}]完整 Agent 只是继续扩展这棵插件树
在当前工具回路上加入模型适配器、Agent loop、持久化和运行入口,就得到完整 Agent。每一层仍然是可替换插件。
运行入口CLI、Web 或 SDK
Agent loop回合、步骤与工具调用
LLM 适配器可替换模型提供方
工具与持久化能力、事件与会话记录
Cordis生命周期、依赖与组合
examples/headless-agent/cordis.yml
官方示例展示了一棵可运行的完整配置树。复制它并加入 greet-tool.ts,即可从无模型的工具回路过渡到真实 Agent。