系列教程
41 节
Codex 教程
按顺序阅读,循序渐进掌握相关 AI 工具与实战方法。
从「ChatGPT (Codex) 教程」开始
上一章讲了三角色概念,本章落地为代码:实现一个 myCap 能力——输入一段文本,输出全部大写。它足够小,又完整覆盖 Definition、Provider、Consumer 三个包,最后在 cordis.yml 中组合加载。
Definition 只声明契约——抽象类继承 Service,用 super(ctx, 'myCap') 注册命名服务,并用声明合并补类型,不含任何实现:
import { Service, type Context } from '@deepseek-ai/cordis'
declare module '@deepseek-ai/cordis' {
interface Context {
myCap: MyCapService
}
}
export abstract class MyCapService extends Service {
constructor(ctx: Context) {
super(ctx, 'myCap') // 注册为 ctx.myCap
}
abstract execute(request: MyCapRequest): Promise<MyCapResult>
}
export interface MyCapRequest {
input: string
}
export interface MyCapResult {
output: string
}
请求与结果类型由 Definition 包拥有,Provider 与 Consumer 都从它导入。
Provider 继承抽象类填上真实行为,只依赖 Definition 包:
import type { Context } from '@deepseek-ai/cordis'
import { MyCapService, type MyCapRequest, type MyCapResult } from '@deepseek-ai/dsh-my-cap'
class MyCapLocal extends MyCapService {
async execute(request: MyCapRequest): Promise<MyCapResult> {
return { output: request.input.toUpperCase() }
}
}
export const name = 'my-cap-local'
export function apply(ctx: Context) {
ctx.plugin(MyCapLocal) // 注册成 ctx.myCap 的实际服务
}
Consumer 把能力包装成模型可调用的工具,inject 保证 tools 与 myCap 就绪:
import type { Context } from '@deepseek-ai/cordis'
import { defineTool } from '@deepseek-ai/dsh-tools'
export const name = 'tool-my-cap'
export const inject = ['tools', 'myCap']
export function apply(ctx: Context) {
ctx.tools.register(defineTool({
name: 'my_cap',
description: 'Execute my capability.',
parameters: {
input: { type: 'string', required: true },
},
output: {
schema: { type: 'string' },
render: (_args, value) => [{ type: 'text', text: value }],
},
async execute(args) {
const result = await ctx.myCap.execute({ input: args.input })
return result.output
},
}))
}
模型不认识 ctx.myCap,只认识 my_cap 工具;Consumer 是两者之间的桥梁。数据流为:模型调用工具 → Consumer 包装成 MyCapRequest → Definition 委托给当前 Provider → 结果沿原路返回并渲染成文本。换掉 Provider 时,Consumer 与模型都无感知。
# 文件:cordis.yml
# 先加载 Provider,让 ctx.myCap 有实现
- name: '@deepseek-ai/dsh-my-cap-local'
# 再加载 Consumer,让模型能用 my_cap 工具
- name: '@deepseek-ai/dsh-tool-my-cap'
Cordis 会按依赖自动排序,加载先后并不关键;换 Provider 只改第一行。
resolve(request) 处理默认值,而不是在 run() 里隐藏 ?? default——Bash seam 的 ctx.shell.resolve(request) 就是范例。写一个可替换能力 = Definition 抽象类 + Provider 子类 + Consumer 工具,再加一份 cordis.yml。自测:MyCapRequest 应定义在哪个包?Provider 与 Consumer 之间有没有依赖?为什么不建议预防性拆分?