feat: @uncaged/workflow-role-llm — role factory with zod@4 schema #9

Closed
opened 2026-05-06 06:45:49 +00:00 by xiaoju · 0 comments
Owner

背景

从 nerve 的 workflow-utils 迁移 role 工厂,用 zod@4 统一类型定义和 JSON Schema 生成。

包命名规范

@uncaged/workflow-role-xxx         # Role 实现
@uncaged/workflow-agent-xxx        # Agent Adapter(AgentFn 实现)
@uncaged/workflow-template-xxx     # Workflow 模板(可构建为 bundle)

@uncaged/workflow-role-llm 职责

从 nerve 迁移的核心能力:

  • createRole — role 工厂,zod schema 定义 meta 类型
  • llmExtract / llmExtractWithRetry — 从 LLM 输出提取结构化 meta
  • createLlmAdapter — LLM provider 抽象
  • decorateRole / withDryRun / onFail — role decorators

zod@4 的统一 schema

import { z } from "zod/v4";
import { createRole } from "@uncaged/workflow-role-llm";

const plannerMeta = z.object({
  plan: z.string(),
  files: z.array(z.string()),
});

// 一个 zod schema 同时产出:
// 1. TypeScript type: z.infer<typeof plannerMeta> → { plan: string; files: string[] }
// 2. JSON Schema: z.toJSONSchema(plannerMeta) → { type: "object", properties: { ... } }
// 3. LLM extract validation: schema.parse(extractedData)

createRole API

import { z } from "zod/v4";

const planner = createRole({
  name: "planner",
  schema: z.object({ plan: z.string(), files: z.array(z.string()) }),
  systemPrompt: "You are a planner...",
  agent: cursorAgent,  // AgentFn
});

// planner 是 Role<{ plan: string; files: string[] }>
// 自动从 agent 输出 extract meta,用 zod 验证

自动生成 descriptor

import { buildDescriptorFromRoles } from "@uncaged/workflow-role-llm";

const descriptor = buildDescriptorFromRoles({
  description: "Solve issues",
  roles: { planner, coder },
});
// 自动从每个 role 的 zod schema 生成 JSON Schema
// → { description: "...", roles: { planner: { description: "...", schema: { ... } } } }

Monorepo 结构

packages/
  workflow/              # @uncaged/workflow(已有)
  cli-workflow/          # @uncaged/cli-workflow(已有)
  workflow-role-llm/     # @uncaged/workflow-role-llm(本 issue)

从 nerve 迁移的文件

nerve 源文件 目标 说明
workflow-utils/src/create-role.ts src/create-role.ts 改用 zod@4
workflow-utils/src/create-llm-adapter.ts src/create-llm-adapter.ts 保持
workflow-utils/src/role-types.ts src/types.ts 保持
workflow-utils/src/role-decorators.ts src/decorators.ts 保持
workflow-utils/src/role-llm.ts src/role-llm.ts 保持
workflow-utils/src/shared/llm-extract.ts src/llm-extract.ts 改用 zod@4
workflow-utils/src/shared/extract-fn.ts src/extract-fn.ts zod → zod@4
workflow-utils/src/shared/llm-chat.ts src/llm-chat.ts 保持

不迁移的

  • role-cursor.ts / role-hermes.ts → 属于 @uncaged/workflow-agent-xxx
  • shared/context.ts → nerve 特有,不迁移
  • nerveCommandEnv → nerve 特有

依赖

{
  "dependencies": {
    "@uncaged/workflow": "workspace:*",
    "zod": "^4.0.0"
  }
}

验证标准

  • createRole 用 zod@4 schema 定义 meta
  • z.toJSONSchema() 生成 JSON Schema
  • buildDescriptorFromRoles 自动生成 descriptor
  • LLM extract 用 zod parse 验证
  • role decorators 工作正常
  • 不依赖 nerve-core
  • bun test 通过
  • bunx biome check . 通过
## 背景 从 nerve 的 `workflow-utils` 迁移 role 工厂,用 zod@4 统一类型定义和 JSON Schema 生成。 ## 包命名规范 ``` @uncaged/workflow-role-xxx # Role 实现 @uncaged/workflow-agent-xxx # Agent Adapter(AgentFn 实现) @uncaged/workflow-template-xxx # Workflow 模板(可构建为 bundle) ``` ## @uncaged/workflow-role-llm 职责 从 nerve 迁移的核心能力: - `createRole` — role 工厂,zod schema 定义 meta 类型 - `llmExtract` / `llmExtractWithRetry` — 从 LLM 输出提取结构化 meta - `createLlmAdapter` — LLM provider 抽象 - `decorateRole` / `withDryRun` / `onFail` — role decorators ### zod@4 的统一 schema ```typescript import { z } from "zod/v4"; import { createRole } from "@uncaged/workflow-role-llm"; const plannerMeta = z.object({ plan: z.string(), files: z.array(z.string()), }); // 一个 zod schema 同时产出: // 1. TypeScript type: z.infer<typeof plannerMeta> → { plan: string; files: string[] } // 2. JSON Schema: z.toJSONSchema(plannerMeta) → { type: "object", properties: { ... } } // 3. LLM extract validation: schema.parse(extractedData) ``` ### createRole API ```typescript import { z } from "zod/v4"; const planner = createRole({ name: "planner", schema: z.object({ plan: z.string(), files: z.array(z.string()) }), systemPrompt: "You are a planner...", agent: cursorAgent, // AgentFn }); // planner 是 Role<{ plan: string; files: string[] }> // 自动从 agent 输出 extract meta,用 zod 验证 ``` ### 自动生成 descriptor ```typescript import { buildDescriptorFromRoles } from "@uncaged/workflow-role-llm"; const descriptor = buildDescriptorFromRoles({ description: "Solve issues", roles: { planner, coder }, }); // 自动从每个 role 的 zod schema 生成 JSON Schema // → { description: "...", roles: { planner: { description: "...", schema: { ... } } } } ``` ## Monorepo 结构 ``` packages/ workflow/ # @uncaged/workflow(已有) cli-workflow/ # @uncaged/cli-workflow(已有) workflow-role-llm/ # @uncaged/workflow-role-llm(本 issue) ``` ## 从 nerve 迁移的文件 | nerve 源文件 | 目标 | 说明 | |-------------|------|------| | `workflow-utils/src/create-role.ts` | `src/create-role.ts` | 改用 zod@4 | | `workflow-utils/src/create-llm-adapter.ts` | `src/create-llm-adapter.ts` | 保持 | | `workflow-utils/src/role-types.ts` | `src/types.ts` | 保持 | | `workflow-utils/src/role-decorators.ts` | `src/decorators.ts` | 保持 | | `workflow-utils/src/role-llm.ts` | `src/role-llm.ts` | 保持 | | `workflow-utils/src/shared/llm-extract.ts` | `src/llm-extract.ts` | 改用 zod@4 | | `workflow-utils/src/shared/extract-fn.ts` | `src/extract-fn.ts` | zod → zod@4 | | `workflow-utils/src/shared/llm-chat.ts` | `src/llm-chat.ts` | 保持 | ### 不迁移的 - `role-cursor.ts` / `role-hermes.ts` → 属于 `@uncaged/workflow-agent-xxx` - `shared/context.ts` → nerve 特有,不迁移 - `nerveCommandEnv` → nerve 特有 ## 依赖 ```json { "dependencies": { "@uncaged/workflow": "workspace:*", "zod": "^4.0.0" } } ``` ## 验证标准 - [ ] `createRole` 用 zod@4 schema 定义 meta - [ ] `z.toJSONSchema()` 生成 JSON Schema - [ ] `buildDescriptorFromRoles` 自动生成 descriptor - [ ] LLM extract 用 zod parse 验证 - [ ] role decorators 工作正常 - [ ] 不依赖 nerve-core - [ ] `bun test` 通过 - [ ] `bunx biome check .` 通过
Sign in to join this conversation.
No Label
1 Participants
Notifications
Due Date
No due date set.
Dependencies

No dependencies set.

Reference: uncaged/workflow#9