装饰器与定义池
装饰器与读取池从 @jangts/agent-core/support/decorators 导入,仅支持 TypeScript 标准装饰器语义。启用了 experimentalDecorators 的 NestJS 编译单元不能直接使用。普通对象、define、构造与 bind 入口保持可用。无新增运行时依赖,不使用 reflect-metadata 或自动 DI。
类装饰器代办定义实例化与 id 注入;实例方法装饰器将原方法绑定为 handler,保留原对象及 this。产物仍是原合同的定义或配置,不创建 Engine/ModelRunner,不改变预算、取消、授权、状态提交与工具回执。
类:处理单元与内部协作
从同一子路径导入 Node、Tool、Fragment、Workflow、Agent、Model 类型,描述类应提供的原字段与方法;id 由装饰器补齐,无需重复写 Omit。Agent 保留原配置的联合类型分支,由装饰器检查,不要求 class implements 联合类型。
import { AgentCoreEngine, defineWorkflow } from '@jangts/agent-core';
import { node, nodePool } from '@jangts/agent-core/support/decorators';
import type { Node } from '@jangts/agent-core/support/decorators';
type State = { text: string; answer?: string };
@node<State, string>('greet', { ns: 'chat' })
class Greeting implements Node<State, string> {
readonly description = '问候';
readonly routes = [{ complete: (state: Readonly<State>) => state.answer! }];
#prefix = '你好,';
run(state: Readonly<State>) {
return { answer: this._format(state.text) };
}
private _format(text: string) { return this.#prefix + text.trim(); }
}
const workflow = defineWorkflow<State, string>({
id: 'greeting', nodes: nodePool.getAll('chat'),
});
console.log(await new AgentCoreEngine({ workflow }).run({ text: '访客' }));id 只来自装饰器;类中重复声明 id 会拒绝。定义类必须可无参构造,在静态初始化完成后构造一次;池读取不再构造。配置字段、handler 及内部方法由类提供,不在装饰器中重复 metadata。私有字段、getter 和原型方法保留真实接收者。构造函数只初始化定义,不执行业务操作。
命名空间与读取
每种定义各有一个共享读取池:nodePool、toolPool、fragmentPool、workflowPool、agentPool、modelPool。调用方无需创建池或手动登记。所有装饰器接受可选的 { ns: string | readonly string[] },缺省为 'default'。
nodePool.get('greet', 'chat'); // 单个定义,缺失为 undefined
nodePool.getAll('chat'); // 此命名空间全部定义
nodePool.pick(['greet', 'answer'], 'chat'); // 按指定顺序选取,缺失或重复 id 抛错
nodePool.get('greet'); // 等价于 get('greet', 'default')
nodePool.getAll(); // 只读 default,不跨命名空间合并
nodePool.pick(['greet']); // 在 default 中选取同一类型、同一命名空间内 id 唯一;不同命名空间或不同类型可同名。ns 精确匹配,不支持层级、通配或隐式回退,空白名称与空数组拒绝。数组中的重复命名空间去重。ns: ['chat', 'assistant'] 将同一已构造定义登记到两个空间,不重复构造、不复制实例;任一目标重名则整次登记拒绝,不留下部分写入。
getAll 返回登记顺序的只读数组快照;未知命名空间返回空数组。pick 返回入参顺序的只读数组快照,空 id 数组返回空数组。读取方法可使用原定义类型泛型,如 nodePool.get<WorkflowNode<State, string>>('greet', 'chat');泛型描述调用方预期合同,不是运行时类型证明。
ns 仅用于辅助登记,不进入 Core options;取出的 id 仍为原 id。把不同命名空间的同名定义装入同一 Workflow/ToolRegistry 时,原入口仍拒绝重名。Workflow 节点顺序与路由由 App 明确组织,不把模块导入顺序当成业务执行顺序。池不公开创建、写入、清空或封存接口,不支持 tags 或 owner 查询。
已有实例:保留业务方法名
class GreetingService {
constructor(private readonly prefix: string) {}
@node<State, string>('reply', {
ns: 'chat-service',
description: '调用已有服务',
routes: [{ complete: (state: Readonly<State>) => state.answer! }],
})
answer(state: Readonly<State>) {
return { answer: this._format(state.text) };
}
private _format(text: string) { return this.prefix + text; }
}
const service = new GreetingService('你好,'); // 创建原宿主,自动绑定与登记
const definition = nodePool.get('reply', 'chat-service');方法装饰器用标准实例初始化钩子绑定并登记,不需要 registerFrom、手写 bind 或额外类装饰器。该钩子先于实例字段与构造函数体运行,因此只绑定、不执行 handler,不访问未初始化的业务字段。调用方必须等待宿主构造成功后再读取并运行定义;构造失败不回滚已完成的初始化登记,应视为装配失败。池持有真实宿主的绑定引用,适用于长期存活的定义/服务实例,不是请求级实例工厂。同命名空间同 id 的第二个实例拒绝登记,不覆盖第一个绑定。
metadata 中的直接回调同样绑定到宿主;嵌套 schema 与 routes 保留原合同接收者,箭头函数保留词法 this。仅支持公开实例方法,不支持静态/私有方法、普通顶层函数的 @ 语法或隐式继承合并。
getter 与按状态选路
get routes(): Node<State, string>['routes'] {
return [
{ when: state => this._hasAnswer(state), complete: state => state.answer! },
{ complete: () => '没有可交付的回答' },
];
}
private _hasAnswer(state: Readonly<State>) {
return Boolean(state.answer?.trim());
}Workflow 装配时读取并固定路由表;getter 应无副作用,不依赖调用次数,也不读取本次请求状态。每次 run 提交补丁后,when/complete 获得最新 state,并可调用原类辅助方法。getter 不会每轮重建路由;不要把请求 state 存在共享节点 this 上。
定义类型与原入口
| 装饰器 | 类的结构 | 方法适配 | 读取池 | 后续入口 |
|---|---|---|---|---|
| node | Node<State, Result, App> | run | nodePool | defineWorkflow |
| tool | Tool<Input, Output, App> | execute | toolPool | defineTool/defineToolCatalog |
| fragment | Fragment<Input> | format | fragmentPool | defineContextFragments |
| workflow | Workflow<State, Result, App> | 不提供 | workflowPool | defineWorkflow |
| agent | Agent<State, Result, App> | 不提供 | agentPool | App 创建 Engine |
| model | Model | 不提供 | modelPool | App 创建 ModelRunner |
agent/model 的 id 是辅助登记键,不覆盖 Core 分支、模型或 transport 身份。工具复用原 defineTool 校验;Workflow 复用原图校验,节点在完整图中验证;fragment 复用原 builder 的 formatter 检查,数据、信任与预算保持原合同;model 复用原配置快照验证,不消耗模型调用。辅助层不公开 metadata+普通函数转换入口,顶层函数直接填入原 options。
同一 JavaScript realm 内 ESM/CJS 副本共享池;不同进程、Worker 或独立 realm 不共享。两次独立加载并装饰同命名空间同 id 的类仍会重名拒绝,不自动合并。定义模块必须被打包图明确保留;被 tree-shaking 删除的纯副作用导入不能保证登记。共享定义不能保存请求身份、signal 或请求预算;池读取不是执行或授权。
WorkflowTool 与 Intent
WorkflowTool、WorkflowNode 分别是 tool、node 的明确命名入口。WorkflowTool 支持定义类与公开实例方法。模型 Tool 提供 inputSchema 和 resolve;执行 Tool 另外提供 kind、authorize、execute 和 parseOutput。归一化定义进入同一个 toolPool。
import { WorkflowTool, Intent, toolPool } from '@jangts/agent-core/support/decorators';
import { Support } from '@jangts/agent-core';
@Intent('用户希望记录事情或设置提醒')
@WorkflowTool('memo', { ns: 'assistant' })
class MemoTool {
description = '接收备忘草案';
inputSchema = memoSchema;
resolve(input) { return input; }
}
const candidates = Support.defineModelTools(toolPool.getAll('assistant')).withIntent(context);Intent 支持无参数、描述字符串、同步布尔判断函数或 { description?, when?, delegates? }。必须与同一目标的 WorkflowTool 配合,没有独立 ID,单用不注册;两者书写顺序均可。WorkflowTool 参数和被装饰类中不提供 asIntent,使用独立 Intent 装饰器;普通配置对象仍支持 asIntent。