RFC: Mitsein agent + generic document workflow template (generate & edit with diff) #228

Closed
opened 2026-05-13 03:10:45 +00:00 by jiayi · 2 comments
Member

设计文档:文档生成/编辑 Workflow 体系

日期: 2026-05-13

概述

本设计分三层,实现通过 mitsein 生成或编辑 Word 文档的完整 workflow 体系:

位置 职责
执行器 @uncaged/workflow-agent-mitsein monorepo packages/workflow-agent-mitsein/ 通用 mitsein CLI 执行器(writer 角色)
执行器 @uncaged/workflow-agent-docx-diff monorepo packages/workflow-agent-docx-diff/ docx-diff CLI 执行器(differ 角色)
模板 @uncaged/workflow-template-document monorepo packages/workflow-template-document/ 纯结构:角色定义、meta 类型、调度表
Bundle mitsein-doc(示例名) 外部 workspace(如 xingyue-workflows/ 组合 agent + template,可注册执行

Template 只定义结构,不含任何执行逻辑。执行器与 template 解耦:任何能生成/编辑文档的 agent 均可绑定 writer 角色,任何能产出 diff 报告的 agent 均可绑定 differ 角色。


一、workflow-agent-mitsein

通用 mitsein 执行器,与 workflow-agent-cursorworkflow-agent-hermes 平级。接收 role definition + thread context,通过 mitsein CLI 执行任务,返回结果。不含业务判断逻辑,不做 docx-diff。

执行流程

--prompt 传入的是 MitseinStartInput.prompt(从 ctx.start.content JSON 解析)。RoleDefinition.systemPrompt 在 mitsein agent 中不使用。

1. 若 ctx.start.content 中含 inputDocx 路径(edit 模式):
   mitsein uploads create <inputDocx> --json
   → 获取 ref_id

2. mitsein delegate submit --prompt <MitseinStartInput.prompt> [--ref <ref_id>] --output mitsein --json
   → 获取 run_id

3. mitsein delegate events <run_id> --json --filter completed,failed --timeout 600
   → 阻塞直到收到终态事件(completed / failed)或超时
   → 不解析 event payload,只用作完成信号

4. mitsein delegate get <run_id> --json
   → 验证 status = completed(否则抛错)
   → 检查 JSON 响应结构,定位 artifact file_id 字段

5. mitsein delegate artifact <run_id> <file_id> -o <output_dir>/<filename>
   → 下载 artifact 到本地

6. 返回 JSON 字符串(WriterMeta),由 workflow extract 解析为结构化 meta

说明:

  • 步骤 3 使用 SSE 事件流作为完成信号,无轮询、无 sleep
  • 步骤 4 使用 delegate get 取完整快照提取 file_id,规避 SSE payload schema 不确定性
  • --timeout 600 为最长等待上限(10 分钟),超时视为失败
  • inputDocx 须为调用方环境下的有效绝对路径,路径有效性由调用方保证
  • 当前只取第一个匹配的 docx artifact;若 mitsein 产出多文件,其余忽略

输出目录: ~/.uncaged/workflow/outputs/<thread_id>/

包结构

packages/workflow-agent-mitsein/
  src/
    agent.ts      # createMitseinAgent():实现 AgentFn 接口
    runner.ts     # mitsein CLI 调用封装(uploads / submit / events / get / artifact)
    types.ts      # MitseinAgentConfig、CliRunner、RunnerConfig
    index.ts      # 公开 re-exports
  package.json    # @uncaged/workflow-agent-mitsein
  tsconfig.json

依赖

用途
@uncaged/workflow-protocol Resultokerr
@uncaged/workflow-util createLogger、storage path 工具函数

运行时依赖 mitsein CLI 在 PATH 中可用。


二、workflow-agent-docx-diff

differ 角色的专用执行器,与 workflow-agent-mitsein 平级。读取 ctx.steps 中的 WriterMeta,调用 docx-diff CLI 生成差异报告,返回 DifferMeta

执行流程

1. 从 ctx.steps 找到 writer 步骤,读取 WriterMeta
2. 验证 mode === "edit"(否则抛错,differ 不应在 generate 模式下运行)
3. docx-diff <writer.inputDocx> <writer.outputDocx> --output docx --out-file <outputDir>/diff.docx
   → exit 0 = 无变更,exit 1 = 有变更(均视为正常)
   → exit 2+ = 错误,抛出异常
4. 返回 JSON 字符串(DifferMeta),由 workflow extract 解析

包结构

packages/workflow-agent-docx-diff/
  src/
    agent.ts      # createDocxDiffAgent():实现 AgentFn 接口
    index.ts      # 公开 re-exports
  __tests__/
    agent.test.ts
  package.json    # @uncaged/workflow-agent-docx-diff
  tsconfig.json

依赖

用途
@uncaged/workflow-protocol AgentFnAgentContext
@uncaged/workflow-template-document WriterMetaDifferMeta 类型

运行时依赖 docx-diff CLI 在 PATH 中可用(仅编辑模式)。


三、workflow-template-document

通用文档生成/编辑流程模板,不绑定任何具体 agent。定义两个角色和调度表。

前提假设: 本设计假设 workflow 在本机执行,inputDocx 绝对路径在运行环境中有效。跨机器或远端执行不在当前范围内。

Thread 启动输入

// src/types.ts
type DocumentStartInput = {
  prompt: string;           // 用户指令
  inputDocx: string | null; // null = 生成模式;本机绝对路径 = 编辑模式
};

角色与 Meta

// writer:由外部 agent 执行,负责生成或修改文档
type WriterMeta = {
  mode: "generate" | "edit";
  outputDocx: string;       // 生成/修改后的文档绝对路径
  inputDocx: string | null; // 原始文档路径(编辑模式);生成模式为 null
};

// differ:仅编辑模式执行,运行 docx-diff 生成差异报告
type DifferMeta = {
  sourceDocx: string;   // 原始文档
  modifiedDocx: string; // 修改后文档
  diffDocx: string;     // 差异报告绝对路径
};

调度表

START → writer ──(mode = "edit")──→ differ → END
               ↘(mode = "generate")→ END

differ 角色由 @uncaged/workflow-agent-docx-diff 提供的 createDocxDiffAgent() 执行,直接调用本地 docx-diff CLI,不经过 LLM:

docx-diff <sourceDocx> <modifiedDocx> --output docx --out-file <diffDocx>

bundle 中通过 AgentBinding.overrides 绑定:overrides: { differ: createDocxDiffAgent() }

包结构

packages/workflow-template-document/
  src/
    types.ts          # DocumentStartInput
    roles/
      writer.ts       # WriterMeta + writerRole(RoleDefinition,抽象,无 agent 绑定)
      differ.ts       # DifferMeta + differRole
    roles.ts          # DocumentMeta + documentRoles
    moderator.ts      # 调度表
    descriptor.ts     # workflow descriptor
    index.ts          # 公开 re-exports
  package.json        # @uncaged/workflow-template-document
  tsconfig.json

依赖

用途
@uncaged/workflow-runtime RoleDefinitionModeratorTableSTARTEND
@uncaged/workflow-protocol Resultokerr
@uncaged/workflow-util createLogger

运行时编辑模式依赖 docx-diff CLI 在 PATH 中可用。


四、Bundle(外部 workspace)

在外部 workspace(如 xingyue-workflows/)中组合上述两个包,生成可注册执行的 .esm.js bundle。

// src/mitsein-doc.ts(示意)
import { createDocxDiffAgent } from "@uncaged/workflow-agent-docx-diff";
import { createMitseinAgent } from "@uncaged/workflow-agent-mitsein";
import { documentDescriptor, documentRoles, documentTable } from "@uncaged/workflow-template-document";
import { createWorkflow } from "@uncaged/workflow-runtime";
import { getDefaultWorkflowStorageRoot } from "@uncaged/workflow-util";
import { join } from "node:path";

const outputDir = join(getDefaultWorkflowStorageRoot(), "outputs");
const mitseinAgent = createMitseinAgent({ outputDir });
const docxDiffAgent = createDocxDiffAgent();

export const descriptor = documentDescriptor;
export const run = createWorkflow(documentRoles, documentTable, {
  agent: mitseinAgent,
  overrides: { differ: docxDiffAgent },
});

Build 命令:bun run build:develop → 产出单文件 .esm.js

注册与执行:

uncaged-workflow workflow add mitsein-doc ./dist/mitsein-doc.esm.js
uncaged-workflow run mitsein-doc

不在范围内

  • mitsein 调用失败的重试逻辑(CLI 不在 PATH 或 run 失败均视为不可恢复错误,直接 throw)
  • 多 artifact 处理(只取第一个匹配的 docx,其余忽略)
  • 多轮迭代 / review 循环(单次生成任务不需要)
  • differ 角色的 HTML/terminal 格式输出(仅 docx)
# 设计文档:文档生成/编辑 Workflow 体系 **日期:** 2026-05-13 ## 概述 本设计分三层,实现通过 mitsein 生成或编辑 Word 文档的完整 workflow 体系: | 层 | 包 | 位置 | 职责 | |----|---|------|------| | 执行器 | `@uncaged/workflow-agent-mitsein` | monorepo `packages/workflow-agent-mitsein/` | 通用 mitsein CLI 执行器(writer 角色) | | 执行器 | `@uncaged/workflow-agent-docx-diff` | monorepo `packages/workflow-agent-docx-diff/` | docx-diff CLI 执行器(differ 角色) | | 模板 | `@uncaged/workflow-template-document` | monorepo `packages/workflow-template-document/` | 纯结构:角色定义、meta 类型、调度表 | | Bundle | `mitsein-doc`(示例名) | 外部 workspace(如 `xingyue-workflows/`)| 组合 agent + template,可注册执行 | Template 只定义结构,不含任何执行逻辑。执行器与 template 解耦:任何能生成/编辑文档的 agent 均可绑定 `writer` 角色,任何能产出 diff 报告的 agent 均可绑定 `differ` 角色。 --- ## 一、`workflow-agent-mitsein` 通用 mitsein 执行器,与 `workflow-agent-cursor`、`workflow-agent-hermes` 平级。接收 role definition + thread context,通过 mitsein CLI 执行任务,返回结果。不含业务判断逻辑,不做 docx-diff。 ### 执行流程 `--prompt` 传入的是 `MitseinStartInput.prompt`(从 `ctx.start.content` JSON 解析)。`RoleDefinition.systemPrompt` 在 mitsein agent 中不使用。 ``` 1. 若 ctx.start.content 中含 inputDocx 路径(edit 模式): mitsein uploads create <inputDocx> --json → 获取 ref_id 2. mitsein delegate submit --prompt <MitseinStartInput.prompt> [--ref <ref_id>] --output mitsein --json → 获取 run_id 3. mitsein delegate events <run_id> --json --filter completed,failed --timeout 600 → 阻塞直到收到终态事件(completed / failed)或超时 → 不解析 event payload,只用作完成信号 4. mitsein delegate get <run_id> --json → 验证 status = completed(否则抛错) → 检查 JSON 响应结构,定位 artifact file_id 字段 5. mitsein delegate artifact <run_id> <file_id> -o <output_dir>/<filename> → 下载 artifact 到本地 6. 返回 JSON 字符串(WriterMeta),由 workflow extract 解析为结构化 meta ``` **说明:** - 步骤 3 使用 SSE 事件流作为完成信号,无轮询、无 sleep - 步骤 4 使用 `delegate get` 取完整快照提取 file_id,规避 SSE payload schema 不确定性 - `--timeout 600` 为最长等待上限(10 分钟),超时视为失败 - `inputDocx` 须为调用方环境下的有效绝对路径,路径有效性由调用方保证 - 当前只取第一个匹配的 docx artifact;若 mitsein 产出多文件,其余忽略 **输出目录:** `~/.uncaged/workflow/outputs/<thread_id>/` ### 包结构 ``` packages/workflow-agent-mitsein/ src/ agent.ts # createMitseinAgent():实现 AgentFn 接口 runner.ts # mitsein CLI 调用封装(uploads / submit / events / get / artifact) types.ts # MitseinAgentConfig、CliRunner、RunnerConfig index.ts # 公开 re-exports package.json # @uncaged/workflow-agent-mitsein tsconfig.json ``` ### 依赖 | 包 | 用途 | |----|------| | `@uncaged/workflow-protocol` | `Result`、`ok`、`err` | | `@uncaged/workflow-util` | `createLogger`、storage path 工具函数 | 运行时依赖 `mitsein` CLI 在 PATH 中可用。 --- ## 二、`workflow-agent-docx-diff` `differ` 角色的专用执行器,与 `workflow-agent-mitsein` 平级。读取 `ctx.steps` 中的 `WriterMeta`,调用 `docx-diff` CLI 生成差异报告,返回 `DifferMeta`。 ### 执行流程 ``` 1. 从 ctx.steps 找到 writer 步骤,读取 WriterMeta 2. 验证 mode === "edit"(否则抛错,differ 不应在 generate 模式下运行) 3. docx-diff <writer.inputDocx> <writer.outputDocx> --output docx --out-file <outputDir>/diff.docx → exit 0 = 无变更,exit 1 = 有变更(均视为正常) → exit 2+ = 错误,抛出异常 4. 返回 JSON 字符串(DifferMeta),由 workflow extract 解析 ``` ### 包结构 ``` packages/workflow-agent-docx-diff/ src/ agent.ts # createDocxDiffAgent():实现 AgentFn 接口 index.ts # 公开 re-exports __tests__/ agent.test.ts package.json # @uncaged/workflow-agent-docx-diff tsconfig.json ``` ### 依赖 | 包 | 用途 | |----|------| | `@uncaged/workflow-protocol` | `AgentFn`、`AgentContext` | | `@uncaged/workflow-template-document` | `WriterMeta`、`DifferMeta` 类型 | 运行时依赖 `docx-diff` CLI 在 PATH 中可用(仅编辑模式)。 --- ## 三、`workflow-template-document` 通用文档生成/编辑流程模板,不绑定任何具体 agent。定义两个角色和调度表。 **前提假设:** 本设计假设 workflow 在本机执行,`inputDocx` 绝对路径在运行环境中有效。跨机器或远端执行不在当前范围内。 ### Thread 启动输入 ```typescript // src/types.ts type DocumentStartInput = { prompt: string; // 用户指令 inputDocx: string | null; // null = 生成模式;本机绝对路径 = 编辑模式 }; ``` ### 角色与 Meta ```typescript // writer:由外部 agent 执行,负责生成或修改文档 type WriterMeta = { mode: "generate" | "edit"; outputDocx: string; // 生成/修改后的文档绝对路径 inputDocx: string | null; // 原始文档路径(编辑模式);生成模式为 null }; // differ:仅编辑模式执行,运行 docx-diff 生成差异报告 type DifferMeta = { sourceDocx: string; // 原始文档 modifiedDocx: string; // 修改后文档 diffDocx: string; // 差异报告绝对路径 }; ``` ### 调度表 ``` START → writer ──(mode = "edit")──→ differ → END ↘(mode = "generate")→ END ``` `differ` 角色由 `@uncaged/workflow-agent-docx-diff` 提供的 `createDocxDiffAgent()` 执行,直接调用本地 `docx-diff` CLI,不经过 LLM: ```bash docx-diff <sourceDocx> <modifiedDocx> --output docx --out-file <diffDocx> ``` bundle 中通过 `AgentBinding.overrides` 绑定:`overrides: { differ: createDocxDiffAgent() }`。 ### 包结构 ``` packages/workflow-template-document/ src/ types.ts # DocumentStartInput roles/ writer.ts # WriterMeta + writerRole(RoleDefinition,抽象,无 agent 绑定) differ.ts # DifferMeta + differRole roles.ts # DocumentMeta + documentRoles moderator.ts # 调度表 descriptor.ts # workflow descriptor index.ts # 公开 re-exports package.json # @uncaged/workflow-template-document tsconfig.json ``` ### 依赖 | 包 | 用途 | |----|------| | `@uncaged/workflow-runtime` | `RoleDefinition`、`ModeratorTable`、`START`、`END` | | `@uncaged/workflow-protocol` | `Result`、`ok`、`err` | | `@uncaged/workflow-util` | `createLogger` | 运行时编辑模式依赖 `docx-diff` CLI 在 PATH 中可用。 --- ## 四、Bundle(外部 workspace) 在外部 workspace(如 `xingyue-workflows/`)中组合上述两个包,生成可注册执行的 `.esm.js` bundle。 ```typescript // src/mitsein-doc.ts(示意) import { createDocxDiffAgent } from "@uncaged/workflow-agent-docx-diff"; import { createMitseinAgent } from "@uncaged/workflow-agent-mitsein"; import { documentDescriptor, documentRoles, documentTable } from "@uncaged/workflow-template-document"; import { createWorkflow } from "@uncaged/workflow-runtime"; import { getDefaultWorkflowStorageRoot } from "@uncaged/workflow-util"; import { join } from "node:path"; const outputDir = join(getDefaultWorkflowStorageRoot(), "outputs"); const mitseinAgent = createMitseinAgent({ outputDir }); const docxDiffAgent = createDocxDiffAgent(); export const descriptor = documentDescriptor; export const run = createWorkflow(documentRoles, documentTable, { agent: mitseinAgent, overrides: { differ: docxDiffAgent }, }); ``` Build 命令:`bun run build:develop` → 产出单文件 `.esm.js` 注册与执行: ```bash uncaged-workflow workflow add mitsein-doc ./dist/mitsein-doc.esm.js uncaged-workflow run mitsein-doc ``` --- ## 不在范围内 - mitsein 调用失败的重试逻辑(CLI 不在 PATH 或 run 失败均视为不可恢复错误,直接 throw) - 多 artifact 处理(只取第一个匹配的 docx,其余忽略) - 多轮迭代 / review 循环(单次生成任务不需要) - `differ` 角色的 HTML/terminal 格式输出(仅 docx)
Owner

星月的 Review 🌙

整体印象:设计很干净 👍

三层分离(执行器 / 模板 / Bundle)思路清晰,和现有 workflow-agent-cursorworkflow-agent-hermes 平级,架构一致性很好。

亮点

  • Template 与 Agent 解耦的设计很漂亮——换个 agent 不用改 template
  • 调度表简洁:START → writer →(edit)→ differ → END,生成模式直接跳过 differ
  • 包结构、依赖列表都写得很清楚,开发时不用猜

几个问题 🤔

1. 轮询策略没定义

runner.ts 第 3 步「轮询 delegate get 直到 completed」,但没说间隔、超时、最大重试。Mitsein 一次文档生成可能几十秒到几分钟,建议明确写一个退避策略(比如 2s → 5s → 10s,总超时 5min)。

2. differ 的执行器是谁?

说了「由 shell 执行器驱动,不经过 LLM」,Bundle 示例里写 differ: shellAgent,注释说「由 workflow-executeworkflow-util-agent 提供的 shell 执行器」。这个 shellAgent 目前存在吗?如果不存在,是不是要先实现?建议在 RFC 里明确是复用现有还是新建。

3. 错误路径不够细

  • mitsein uploads create 失败怎么办?(文件不存在 / 网络超时)
  • delegate submit 成功但 delegate get 最终状态是 failed
  • docx-diff CLI 返回非零?

建议至少列一下每个步骤的 error → Result.err 映射,不然实现的人要猜。

4. 建议分 Phase + 开 Testing Issue

按我们的惯例,RFC 落地建议拆成:

  • Phase 1: workflow-agent-mitsein 包 scaffold + runner 封装
  • Phase 2: workflow-template-document 包 + moderator
  • Phase 3: Bundle 集成 + 端到端验证

每个 Phase 开一个 Testing Issue 带具体验证步骤,方便 review。

5. inputDocx 用绝对路径的风险

DocumentStartInput.inputDocx 写的是「绝对路径」,跨机器执行时路径会失效。考虑用相对于 workflow workspace 的路径,或者统一走 uploads?


总结:设计本身没问题,补上轮询策略和错误处理就可以开干了 💪

## 星月的 Review 🌙 整体印象:**设计很干净** 👍 三层分离(执行器 / 模板 / Bundle)思路清晰,和现有 `workflow-agent-cursor`、`workflow-agent-hermes` 平级,架构一致性很好。 ### 亮点 - **Template 与 Agent 解耦**的设计很漂亮——换个 agent 不用改 template - 调度表简洁:`START → writer →(edit)→ differ → END`,生成模式直接跳过 differ - 包结构、依赖列表都写得很清楚,开发时不用猜 ### 几个问题 🤔 **1. 轮询策略没定义** `runner.ts` 第 3 步「轮询 `delegate get` 直到 completed」,但没说间隔、超时、最大重试。Mitsein 一次文档生成可能几十秒到几分钟,建议明确写一个退避策略(比如 2s → 5s → 10s,总超时 5min)。 **2. differ 的执行器是谁?** 说了「由 shell 执行器驱动,不经过 LLM」,Bundle 示例里写 `differ: shellAgent`,注释说「由 `workflow-execute` 或 `workflow-util-agent` 提供的 shell 执行器」。这个 `shellAgent` 目前存在吗?如果不存在,是不是要先实现?建议在 RFC 里明确是复用现有还是新建。 **3. 错误路径不够细** - `mitsein uploads create` 失败怎么办?(文件不存在 / 网络超时) - `delegate submit` 成功但 `delegate get` 最终状态是 `failed`? - `docx-diff` CLI 返回非零? 建议至少列一下每个步骤的 error → Result.err 映射,不然实现的人要猜。 **4. 建议分 Phase + 开 Testing Issue** 按我们的惯例,RFC 落地建议拆成: - Phase 1: `workflow-agent-mitsein` 包 scaffold + runner 封装 - Phase 2: `workflow-template-document` 包 + moderator - Phase 3: Bundle 集成 + 端到端验证 每个 Phase 开一个 Testing Issue 带具体验证步骤,方便 review。 **5. `inputDocx` 用绝对路径的风险** `DocumentStartInput.inputDocx` 写的是「绝对路径」,跨机器执行时路径会失效。考虑用相对于 workflow workspace 的路径,或者统一走 uploads? --- 总结:设计本身没问题,补上轮询策略和错误处理就可以开干了 💪
Owner

Re-review 🌙

伽艺哥哥更新得很到位,我之前提的 5 个问题逐个过一下:

已解决

1. 轮询策略 → 改成了 delegate events --filter completed,failed --timeout 600 SSE 事件流阻塞等待,比轮询优雅多了!超时 10 分钟也合理。👍

2. differ 执行器 → 拆成了独立的 @uncaged/workflow-agent-docx-diff 包,有自己的 createDocxDiffAgent(),不再含糊地引用 shellAgent。Bundle 里通过 overrides: { differ: docxDiffAgent } 绑定,清晰。👍

3. 错误路径 → 明确了「CLI 不在 PATH 或 run 失败均视为不可恢复错误,直接 throw」,docx-diff 也说明了 exit code 语义(0=无变更,1=有变更,2+=错误)。够用了。👍

5. 绝对路径 → 加了前提假设「本设计假设 workflow 在本机执行,inputDocx 绝对路径在运行环境中有效。跨机器或远端执行不在当前范围内」。scope 界定清楚,OK。👍

📋 还没提到

4. 分 Phase + Testing Issue — RFC 本身不需要写这个,但建议开工前先拆好。


结论:LGTM 🚀 可以开干了!

## Re-review 🌙 伽艺哥哥更新得很到位,我之前提的 5 个问题逐个过一下: ### ✅ 已解决 **1. 轮询策略** → 改成了 `delegate events --filter completed,failed --timeout 600` SSE 事件流阻塞等待,比轮询优雅多了!超时 10 分钟也合理。👍 **2. differ 执行器** → 拆成了独立的 `@uncaged/workflow-agent-docx-diff` 包,有自己的 `createDocxDiffAgent()`,不再含糊地引用 `shellAgent`。Bundle 里通过 `overrides: { differ: docxDiffAgent }` 绑定,清晰。👍 **3. 错误路径** → 明确了「CLI 不在 PATH 或 run 失败均视为不可恢复错误,直接 throw」,docx-diff 也说明了 exit code 语义(0=无变更,1=有变更,2+=错误)。够用了。👍 **5. 绝对路径** → 加了前提假设「本设计假设 workflow 在本机执行,inputDocx 绝对路径在运行环境中有效。跨机器或远端执行不在当前范围内」。scope 界定清楚,OK。👍 ### 📋 还没提到 **4. 分 Phase + Testing Issue** — RFC 本身不需要写这个,但建议开工前先拆好。 --- **结论:LGTM 🚀** 可以开干了!
Sign in to join this conversation.
No Label
2 Participants
Notifications
Due Date
No due date set.
Dependencies

No dependencies set.

Reference: uncaged/workflow#228