278 lines
15 KiB
Markdown
278 lines
15 KiB
Markdown
# OfficeCLI 技术方案分析
|
|
|
|
> 调研时间:2026 年 5 月 | 作者:沙洲工作室
|
|
>
|
|
> 基于源码阅读(v1.0.72),非黑盒测评
|
|
|
|
## 基本信息
|
|
|
|
| 项目 | 信息 |
|
|
|------|------|
|
|
| 仓库 | [iOfficeAI/OfficeCLI](https://github.com/iOfficeAI/OfficeCLI) |
|
|
| 语言 | C#(.NET 10) |
|
|
| 许可证 | Apache 2.0 |
|
|
| 代码量 | ~285 个源文件,约 137,800 行 C# |
|
|
| 外部依赖 | 仅 3 个 NuGet 包 |
|
|
| 支持格式 | Word (.docx)、Excel (.xlsx)、PowerPoint (.pptx) |
|
|
|
|
---
|
|
|
|
## 一、整体架构
|
|
|
|
### 分层结构
|
|
|
|
```
|
|
┌─────────────────────────────────────────────────────────┐
|
|
│ AI Agent / 用户终端 │
|
|
├────────────┬──────────────┬─────────────────────────────┤
|
|
│ CLI 命令 │ MCP Server │ Resident 管道(Named Pipe) │
|
|
│ (System. │ (JSON-RPC │ (常驻进程,内存持有文档, │
|
|
│ CommandLine│ over stdio) │ 近零延迟响应) │
|
|
│ 路由) │ │ │
|
|
├────────────┴──────────────┴─────────────────────────────┤
|
|
│ CommandBuilder │
|
|
│ (partial class,按功能拆分十余个文件) │
|
|
│ View / Set / Add / Get / Dump / Raw / Batch / │
|
|
│ Watch / Mark / Help / Import / Refresh / Check │
|
|
├─────────────────────────────────────────────────────────┤
|
|
│ DocumentHandlerFactory │
|
|
│ (按扩展名分发到对应 Handler) │
|
|
├──────────┬───────────────┬──────────────────────────────┤
|
|
│ WordHandler │ ExcelHandler │ PowerPointHandler │
|
|
│ (~25 partial│ (~20 partial │ (~30 partial files) │
|
|
│ files) │ files) │ │
|
|
│ 持有 │ 持有 │ 持有 │
|
|
│ Wordprocessing│Spreadsheet │ Presentation │
|
|
│ Document │ Document │ Document │
|
|
├──────────┴───────────────┴──────────────────────────────┤
|
|
│ Core 模块 │
|
|
│ ┌──────────┬──────────┬──────────┬──────────────────┐ │
|
|
│ │ Formula/ │ Chart/ │ Watch/ │ PivotTableHelper │ │
|
|
│ │ 公式解析 │ SVG渲染 │ 实时预览 │ 透视表引擎 │ │
|
|
│ │ + 求值 │ + ChartEx│ + SSE │ │ │
|
|
│ ├──────────┼──────────┼──────────┼──────────────────┤ │
|
|
│ │ ThemeColor│ FontMetrics│ Units │ HtmlPreview │ │
|
|
│ │ 主题色解析│ 字体度量 │ 单位转换│ HTML 预览 │ │
|
|
│ └──────────┴──────────┴──────────┴──────────────────┘ │
|
|
├─────────────────────────────────────────────────────────┤
|
|
│ DocumentFormat.OpenXML SDK 3.4.1 │
|
|
│ (微软开源 OpenXML SDK) │
|
|
└─────────────────────────────────────────────────────────┘
|
|
```
|
|
|
|
### 模块关系
|
|
|
|
**入口层**:`Program.cs` 是 top-level statements 入口。在进入 System.CommandLine 之前,先拦截处理一批"快捷命令"——`mcp`、`install`、`skills`、`load_skill`、`config` 等,这些不走标准 CLI 路由。
|
|
|
|
**命令路由层**:`CommandBuilder` 是一个 partial class,拆分到十几个文件(`CommandBuilder.View.cs`、`CommandBuilder.Set.cs`、`CommandBuilder.Add.cs` 等),通过 `BuildRootCommand()` 注册所有子命令。注册的命令包括:`open/close`、`view`、`get`、`query`、`set`、`add`、`remove`、`move`、`swap`、`raw`、`rawSet`、`addPart`、`validate`、`batch`、`dump`、`import`、`create`、`merge`、`watch/unwatch`、`mark/unmark`、`goto`、`help` 等。
|
|
|
|
**Handler 层**:`DocumentHandlerFactory.Open()` 根据文件扩展名分发到三个 Handler。每个 Handler 持有对应的 OpenXML Document 对象,用 partial class 按功能拆分为大量文件:
|
|
|
|
| Handler | partial 文件数 | 覆盖功能 |
|
|
|---------|--------------|---------|
|
|
| WordHandler | ~25 | View/Set/Add/Query/Navigation/HtmlPreview/FormFields/StyleList/I18n/ImageHelpers/Selector |
|
|
| ExcelHandler | ~20 | View/Set/Add/Query/Import/Slicer/HtmlPreview/CheckOverflow,带行索引缓存 |
|
|
| PowerPointHandler | ~30 | View/Set/Add/Query/Fill/Effects/Animations/Background/Theme/Comments/Hyperlinks/SvgPreview/Model3D |
|
|
|
|
**Core 层**:40+ 个辅助模块,包含多个自研引擎(公式、图表、透视表等),以及主题色解析、字体度量、单位转换、HTML 预览等基础能力。
|
|
|
|
**统一接口**:三个 Handler 都实现 `IDocumentHandler` 接口,这是整个项目的核心抽象。
|
|
|
|
---
|
|
|
|
## 二、API / CLI 抽象设计
|
|
|
|
### 三层操作架构
|
|
|
|
OfficeCLI 最核心的设计是将 Office 文档操作分为三个粒度层次:
|
|
|
|
#### L1:语义视图层(只读,理解文档)
|
|
|
|
| 命令 | 视图模式 | 用途 |
|
|
|------|---------|------|
|
|
| `view --mode text` | 纯文本 | 获取文档全部文字内容 |
|
|
| `view --mode annotated` | 带编号标注 | 每个元素标上 `[P1]` `[T2]` 等编号,AI 通过编号定位 |
|
|
| `view --mode outline` | 大纲 | 文档标题层级结构 |
|
|
| `view --mode stats` | 统计 | 字数、段落数、表格数等 |
|
|
| `view --mode issues` | 问题检测 | 格式问题、兼容性问题 |
|
|
| `view --mode html` | HTML 预览 | 可视化渲染 |
|
|
|
|
所有视图模式都有 JSON 变体(如 `ViewAsStatsJson()`),支持 `--start/--end/--max-lines/--cols` 分页参数。
|
|
|
|
**`annotated` 视图是关键设计**——它为文档中的每个元素生成唯一编号,AI Agent 在后续的编辑操作中可以直接引用编号进行精确定位,不需要用模糊的自然语言描述位置。
|
|
|
|
#### L2:DOM 操作层(读写,结构化编辑)
|
|
|
|
| 命令 | 功能 | 操作粒度 |
|
|
|------|------|---------|
|
|
| `get <path>` | 获取元素,返回 `DocumentNode` | 元素级,可指定深度 |
|
|
| `query <selector>` | CSS-like 选择器查询 | 类型/属性筛选 |
|
|
| `set <path> <props>` | 修改元素属性 | 属性级(字体、颜色、对齐等) |
|
|
| `add <parent> <type>` | 添加元素 | 支持 Index/After/Before 定位 |
|
|
| `remove <path>` | 删除元素 | 元素级 |
|
|
| `move <src> <dst>` | 移动元素 | 元素级 |
|
|
| `swap <a> <b>` | 交换两个元素 | 元素级 |
|
|
|
|
`DocumentNode` 是跨文档类型的**通用 DOM 节点抽象**,包含:path(DOM 路径)、type(元素类型)、text(文本内容)、preview(预览)、style、format(属性字典)、children(子节点)。
|
|
|
|
#### L3:Raw XML 层(底层逃生舱)
|
|
|
|
| 命令 | 功能 |
|
|
|------|------|
|
|
| `raw <partPath>` | 直接读取 OpenXML part 的原始 XML |
|
|
| `rawSet <partPath> <xpath>` | XPath 定位并修改 XML 节点 |
|
|
| `addPart` | 创建新的 XML part(图表、页眉页脚等) |
|
|
| `validate` | OpenXML schema 合规性验证 |
|
|
|
|
这层的存在非常务实:Office 格式的 XML 规范极其庞大,L2 无法覆盖所有操作。当 AI Agent 遇到 L2 不支持的功能时,可以降级到 XML 层直接操作——相当于一个"逃生舱"。
|
|
|
|
### AI Agent 交互设计
|
|
|
|
**1. MCP Server**
|
|
|
|
`McpServer.cs` 实现了 JSON-RPC 2.0 over stdio 的 MCP 协议。为了兼容 .NET 的 PublishTrimmed(会裁剪掉反射),手写 `Utf8JsonWriter` 序列化,不使用 JSON 反射。支持 `tools/list` 和 `tools/call`,内置后台升级检查。
|
|
|
|
`McpInstaller` 支持一键注册到主流 AI 编辑器的配置文件:Claude Code、Cursor、VS Code Copilot、LM Studio。
|
|
|
|
**2. SKILL.md 技能系统**
|
|
|
|
内置 `SKILL.md` 文件嵌入到二进制中,AI Agent 读取后自动学会使用方式。策略分层指导 AI 先用 L1 理解文档,再用 L2 编辑,必要时降级 L3。
|
|
|
|
`skills/` 目录包含 12 个专项技能包:
|
|
|
|
| 技能 | 场景 |
|
|
|------|------|
|
|
| officecli-docx / xlsx / pptx | 三种格式的通用操作指南 |
|
|
| morph-ppt / morph-ppt-3d | PPT Morph 动画和 3D 效果 |
|
|
| officecli-academic-paper | 学术论文模板 |
|
|
| officecli-data-dashboard | 数据仪表板 |
|
|
| officecli-financial-model | 财务模型 |
|
|
| officecli-pitch-deck | 商业路演 PPT |
|
|
| officecli-word-form | Word 表单 |
|
|
|
|
通过 `officecli skills install` 安装到 Agent 环境。
|
|
|
|
**3. Schema 驱动的帮助系统**
|
|
|
|
`schemas/help/` 目录按格式(docx/pptx/xlsx)存放 JSON Schema 文件,描述每种元素的属性、别名、示例。嵌入为 EmbeddedResource,运行时通过 `SchemaHelpLoader` 读取,`SchemaHelpRenderer` 渲染。支持按动词(add/set/get)过滤——AI 可以查询"我能给 slide 设置哪些属性"。
|
|
|
|
**4. JSON 输出**
|
|
|
|
全局 `--json` 选项,`OutputFormatter.WrapEnvelopeText()` 统一 JSON 信封格式。所有命令的输出都可以结构化,方便 AI 解析。
|
|
|
|
---
|
|
|
|
## 三、Office 文档引擎
|
|
|
|
### 底层依赖:OpenXML SDK
|
|
|
|
OfficeCLI 基于微软开源的 `DocumentFormat.OpenXml 3.4.1` 构建。这个 SDK 提供了 Office Open XML(OOXML)格式的底层读写能力——本质上是操作 .docx/.xlsx/.pptx 文件内部的 XML 结构。
|
|
|
|
OpenXML SDK 本身是**低层级**的——它只提供 XML 节点的创建和操作,不提供"添加一个格式化的表格"这样的高层 API。OfficeCLI 在此基础上自研了大量高层抽象。
|
|
|
|
### 自研引擎模块
|
|
|
|
**1. 公式引擎(Core/Formula/)**
|
|
|
|
自研 Excel 公式解析和求值器:
|
|
- `FormulaParser.cs` — 将公式文本解析为 AST
|
|
- `FormulaEvaluator.cs` + `.Functions.cs` + `.Helpers.cs` — 150+ 内置函数的求值
|
|
- 支持 Numeric/String/Boolean/Error/Array 类型
|
|
- 15 位有效数字精度控制
|
|
- `ModernFunctionQualifier` 处理新版函数名
|
|
|
|
这意味着 OfficeCLI 可以在不打开 Excel 的情况下**计算公式结果**。
|
|
|
|
**2. 图表引擎(Core/Chart/)**
|
|
|
|
这是代码量最大的自研模块之一:
|
|
- `ChartSvgRenderer.cs`(2,897 行)— 完整的 SVG 图表渲染器
|
|
- `ChartSvgRenderer.CxExtract.cs` — ChartEx 扩展图表支持(直方图、漏斗图、树状图、旭日图、箱线图)
|
|
- `ChartHelper.cs` + Builder/Reader/Setter/Axis/Advanced — 图表 DOM 操作
|
|
- `ChartExBuilder.cs` — ChartEx 格式构建器
|
|
- `ChartPresets.cs` / `ChartExResources.cs` — 预设样式和嵌入的 XML 模板
|
|
|
|
可以在不安装 Office 的情况下**渲染图表为 SVG**。
|
|
|
|
**3. 透视表引擎(Core/PivotTableHelper\*)**
|
|
|
|
6 个 partial 文件覆盖完整的透视表处理链:Parse → Cache → Definition → Render → Set → Readback。自行实现了透视表的缓存计算。
|
|
|
|
**4. 主题色系统**
|
|
|
|
- `ThemeColorResolver` — 解析 Office 主题色(accent1-6 + shade/tint 变体)
|
|
- `OfficeDefaultThemeColors` — 内置默认主题
|
|
- 这是正确渲染 Office 文档颜色的关键——Office 的颜色系统不是简单的 hex,而是基于主题的相对值
|
|
|
|
**5. 其他自研模块**
|
|
|
|
| 模块 | 功能 |
|
|
|------|------|
|
|
| FontMetricsReader / LocaleFontRegistry | 字体度量和区域字体映射 |
|
|
| WordTocBuilder | 目录自动构建 |
|
|
| WordNumFmtRenderer | Word 编号格式渲染 |
|
|
| ExcelStyleManager | Excel 样式管理 |
|
|
| HtmlPreviewHelper / HtmlScreenshot | HTML 预览和浏览器截图 |
|
|
| DrawingEffectsHelper | 绘图效果(阴影、发光等) |
|
|
| SvgImageHelper | SVG 图片处理 |
|
|
| EmuConverter / Units / SpacingConverter | EMU/pt/cm/inch 单位转换 |
|
|
| OleHelper + OpenMcdf | OLE 嵌入对象处理 |
|
|
| WordStrictAttributeSanitizer | Strict OpenXML 属性清理 |
|
|
| GenericXmlQuery | 通用 XML 查询 |
|
|
| TemplateMerger | 模板合并 |
|
|
| PathAliases | DOM 路径别名系统 |
|
|
|
|
### 兼容性考量
|
|
|
|
**优势**:
|
|
- 基于 OpenXML SDK,直接操作标准 OOXML 格式,与 Microsoft Office 原生兼容
|
|
- 支持 Strict OpenXML 和 Transitional OpenXML 两种变体
|
|
- 完整的 i18n 和 RTL(从右到左文字)支持
|
|
- `validate` 命令可验证文档是否符合 OpenXML schema
|
|
|
|
**风险**:
|
|
- OpenXML SDK 只是"能读写 XML",正确性取决于上层实现——Office 格式有大量未文档化的行为和边缘情况
|
|
- 不支持旧格式(.doc/.xls/.ppt),仅支持 OOXML 格式(.docx/.xlsx/.pptx)
|
|
- 图表渲染、公式求值等自研引擎可能与 Office 的实际行为有差异
|
|
- **没有自动化测试**(13.8 万行代码零测试),格式兼容性靠人工验证
|
|
|
|
---
|
|
|
|
## 四、技术栈
|
|
|
|
| 组件 | 技术选择 | 说明 |
|
|
|------|---------|------|
|
|
| 运行时 | .NET 10.0 | 最新版,启用 nullable + implicit usings |
|
|
| CLI 框架 | System.CommandLine 3.0.0-preview | 微软官方 CLI 库(仍是预览版) |
|
|
| 文档引擎 | DocumentFormat.OpenXml 3.4.1 | 微软开源 OpenXML SDK |
|
|
| OLE 支持 | OpenMcdf 3.1.3 | 开源 OLE 复合文档库 |
|
|
| IPC | Named Pipe | 驻留模式进程间通信 |
|
|
| MCP | 手写 JSON-RPC 2.0 | 不依赖 MCP 框架库 |
|
|
| 序列化 | Utf8JsonWriter + Source Generator | 兼容 PublishTrimmed,避免反射 |
|
|
| 构建 | dotnet publish | PublishSingleFile + SelfContained + PublishTrimmed |
|
|
| 实时预览 | 内嵌 HTTP 服务器 + SSE | Watch Mode |
|
|
| CI/CD | GitHub Actions | 8 个平台目标交叉编译 |
|
|
|
|
### 单二进制分发
|
|
|
|
通过 .NET 的三个发布选项组合实现:
|
|
- `PublishSingleFile` — 打包为单文件
|
|
- `SelfContained` — 内嵌 .NET 运行时
|
|
- `PublishTrimmed` — 裁剪未使用的代码
|
|
|
|
Skills、Schemas、CSS/JS 资源全部作为 `EmbeddedResource` 嵌入程序集。`build.sh` 支持 8 个平台目标:macOS (ARM64/x64)、Linux (x64/ARM64/musl)、Windows (x64/ARM64)。macOS 构建包含 ad-hoc 代码签名。
|
|
|
|
### 驻留模式(Resident Mode)
|
|
|
|
**问题**:每次执行 CLI 命令都要启动 .NET 运行时 → 打开文件 → 解析 XML → 执行操作 → 保存文件。AI Agent 连续执行几十条命令时,这个开销不可接受。
|
|
|
|
**解法**:双管道架构的常驻进程:
|
|
- **主命令管道**:接收和执行命令
|
|
- **Ping 管道**:独立的健康检查通道,用于验证进程是否存活
|
|
- `SemaphoreSlim` 单命令锁,串行化请求
|
|
- 两种生命周期:自动启动模式(60 秒空闲退出),显式 open/close 模式(12 分钟空闲退出)
|
|
- 支持 `__set-idle-timeout__` RPC 动态调整超时
|
|
- 关键不变量:ping 响应 ⇔ handler 持有文件锁
|
|
|
|
**流程**:`open` → 启动子进程(`__resident-serve__`)→ 后续命令通过 pipe 转发 → `close` 发送关闭指令。Windows 上有特殊处理来避免 handle 继承泄漏。
|