OfficePilot 是一个 Office Add-in,作为 taskpane 嵌入 Word,通过 Claude API 驱动的对话界面和内联自动补全帮助用户撰写文档。
- Node.js 18+ 或 20+
- Office 365 订阅(Word/Excel/PowerPoint 桌面版或网页版)
- Anthropic API 密钥
-
克隆项目
git clone https://github.com/officepilot/office-pilot.git cd office-pilot -
安装依赖
npm install
-
配置环境变量 创建
.env.local文件:ANTHROPIC_API_KEY=your_api_key_here # 可选配置 # ANTHROPIC_BASE_URL=https://api.anthropic.com # ANTHROPIC_MODEL=claude-3-haiku-20240307 # DISABLE_THINKING=true # 用于 LiteLLM 代理 + Opus 模型时关闭 thinking
-
生成 HTTPS 证书 Office Add-in 要求 HTTPS 连接:
npx office-addin-dev-certs install
证书将生成在
~/.office-addin-dev-certs/目录。 -
启动开发服务器
npm run dev:https
服务器将在
https://localhost:3000启动,同时处理 HTTP 请求和 WebSocket 连接。
# 启动自定义 HTTPS 服务器(Next.js + WebSocket 共用 port 3000)
npm run dev:https
# 或使用标准 Next.js 开发服务器(仅 HTTP,不适用于 Office Add-in)
npm run dev# 构建生产版本
npm run build
# 代码检查
npm run lint- 客户端代码(
.ts,.tsx)支持热重载 - 服务端代码(
.mjs)需要手动重启服务器
确保 manifest.xml 中的 URL 指向正确的地址:
<SourceLocation DefaultValue="https://localhost:3000/taskpane"/>
<AppDomain>https://localhost:3000</AppDomain>Word 桌面版:
- 打开 Word
- 文件 → 选项 → 信任中心 → 信任中心设置 → 受信任的加载项目录
- 添加包含
manifest.xml的文件夹路径 - 插入 → 我的加载项 → 共享文件夹 → 选择 OfficePilot
Word 网页版:
- 打开 Word Online
- 插入 → 加载项 → 上传我的加载项
- 选择
manifest.xml文件
- 在 Word 中打开文档
- 点击 "打开 OfficePilot" 按钮启动任务窗格
- 在任务窗格中输入自然语言指令
- 在 Word 正文中输入文本测试自动补全功能
office-pilot/
├── src/
│ ├── app/ # Next.js App Router
│ │ └── taskpane/ # 主任务窗格页面
│ ├── lib/ # 客户端工具库
│ │ ├── office/ # Office.js 相关代码
│ │ │ ├── autocomplete.ts # 自动补全引擎
│ │ │ └── executor.ts # Office.js 代码执行器
│ │ ├── use-chat.ts # 聊天状态管理
│ │ └── use-websocket.ts # WebSocket 客户端
│ ├── server/ # 服务端代码 (.mjs)
│ │ ├── chat-session.mjs # Claude API 对话管理
│ │ ├── code-guard.mjs # 代码安全检查
│ │ ├── prompts/ # 系统提示词
│ │ └── ws-handler.mjs # WebSocket 消息处理
│ └── types/ # TypeScript 类型定义
├── public/ # 静态资源
├── manifest.xml # Office Add-in 配置文件
├── server.mjs # 自定义 HTTPS 服务器
├── package.json # 项目依赖和脚本
└── README.md # 项目文档
npm run build- 将构建后的文件(
.next/,public/,package.json等)上传到服务器 - 确保服务器支持 HTTPS
- 配置环境变量
- 将
manifest.xml中的 URL 更新为生产环境地址 - 更新图标等资源 URL
npm start症状:浏览器提示 "不安全连接" 或 Office 无法加载 Add-in 解决:
- 确认已运行
npx office-addin-dev-certs install - 检查证书是否在
~/.office-addin-dev-certs/ - 重启浏览器并清除缓存
症状:控制台显示 "Word API not available" 解决:
- 确认在 Word 中打开了文档
- 确认 Add-in 已正确加载
- 检查浏览器控制台是否有其他错误
症状:输入文本后没有补全建议 解决:
- 检查 WebSocket 连接是否正常
- 确认
ANTHROPIC_API_KEY已正确配置 - 检查控制台日志是否有错误
症状:Claude 生成的代码无法执行 解决:
- 检查
executor.ts中的错误处理 - 确认 Office.js API 在当前的 Office 版本中可用
- 检查代码是否符合安全限制
-
代码风格
- 使用 TypeScript 和严格模式
- 遵循现有的代码结构和命名约定
- 服务端代码使用
.mjs扩展名
-
提交规范
- 提交信息使用中文
- 清晰描述修改内容和原因
- 关联相关 issue 或功能
-
测试要求
- 修改自动补全逻辑后,测试各种交互场景
- 修改代码执行逻辑后,测试多种 Office.js 代码片段
- 确保修改不会破坏现有功能
-
文档更新
- 修改功能后更新 README.md 和 CLAUDE.md
- 添加新配置项时更新环境变量说明
- 记录已知问题和限制
Word 文档中集成 VSCode 风格的幻文本(Ghost Text)自动补全。用户输入时暂停即可触发 LLM 生成的文本建议,逐字符输入可自然消耗或取消建议。
- 用户在 Word 中输入文案,暂停 800ms 后自动触发补全
- 通过
onParagraphChanged事件检测文档变化,重置 debounce 计时 - 请求携带递增的
epoch计数器,恢复输入后旧 epoch 的响应自动丢弃
- 建议以灰色斜体文本插入光标位置,用 ContentControl(标签
autocomplete-suggestion,BoundingBox外观)包裹 - 光标置于 ContentControl 之前,用户输入的文字自然落在原文与建议之间
| 用户操作 | 行为 |
|---|---|
| 逐字符输入(匹配建议首字符) | 建议首字符被"消耗",建议缩短,用户输入保留为正常格式 |
| 逐字符输入(不匹配) | 建议整体丢弃,用户输入保留 |
| 全部字符被消耗 | ContentControl 自动删除 |
| 方向键 / 鼠标点击 | 建议保持不变 |
| Enter 键 | 接受建议:灰色斜体恢复为原文格式,建议内容成为正文 |
Word 文档 onParagraphChanged
│
▼
handleParagraphChanged() ← 800ms debounce,fire-and-forget
│
├─ dismissSuggestionAsync() ← 版本号取消,3 个检查点
│ ├─ 逐字符匹配消耗
│ ├─ Enter 接受
│ └─ 不匹配丢弃
│
└─ requestCompletion() ← 获取段落文本
│
▼
WebSocket ──► ws-handler.mjs ──► ChatSession.generateCompletion()
│
▼
Claude API (Haiku)
│
▼
autocomplete_result (epoch 回传)
│
▼
handleAutocompleteResult(text, epoch)
| 文件 | 职责 |
|---|---|
src/lib/office/autocomplete.ts |
核心补全引擎:状态管理、建议插入/消耗/接受/丢弃、字体恢复 |
src/app/taskpane/page.tsx |
初始化入口,传入 send 回调 |
src/lib/use-chat.ts |
接收 autocomplete_result,调用 handleAutocompleteResult |
src/server/chat-session.mjs |
调用 Claude API 生成补全文本 |
src/server/ws-handler.mjs |
WebSocket 路由,转发 epoch |
src/types/ws-messages.ts |
autocomplete / autocomplete_result 类型定义 |
- 非阻塞 fire-and-forget:
dismissSuggestionAsync不在handleParagraphChanged中await,避免阻塞后续事件 - 版本号取消:
dismissVersion计数器 + 3 个检查点,确保过期操作在context.sync()后立即中止 - Epoch 请求取消:
autocompleteEpoch随请求递增,服务端原样回传,客户端不匹配则丢弃 - 逐字符消耗:光标置于 ContentControl 之前,通过比对
textBeforeCC快照提取用户输入字符,逐字符与建议首字符匹配 - 字体恢复:插入建议前用临时标记字符捕获原文字体,接受时应用到建议文本
- 状态清理时序:
hasPendingSuggestion在 CC 实际删除(await context.sync())之后才清除,防止requestCompletion在 CC 残留时读取段落文本
Claude 生成的 Office.js 代码执行时,会修改文档内容并触发 onParagraphChanged 事件。为防止代码执行干扰自动补全,实施四层防护机制:
- 代码执行结束时记录时间戳
__lastCodeExecutionEnd - 3秒冷却期内,所有文档变化事件被忽略
- 防止代码执行后立即触发自动补全
- 代码执行期间设置
__isExecutingOfficeCode = true - 事件处理函数检查此标志并立即返回
- 确保代码执行期间不处理任何自动补全逻辑
- 使用
externalCodeExecutionCount而非布尔值 - 支持嵌套/并发代码执行场景
- 计数器归零后才恢复自动补全
- 代码执行前删除所有现有的建议 ContentControl
- 防止代码与建议内容交互导致文档错乱
- 确保代码在"干净"的文档环境中执行
代码执行开始
↓
设置全局标志 + 递增计数器
↓
删除文档中现有建议
↓
执行 Office.js 代码
↓
记录结束时间戳(开始3秒冷却期)
↓
清除全局标志 + 递减计数器
↓
3秒冷却期结束 → 自动补全恢复正常
- Word API 无键盘事件,所有用户操作通过
onParagraphChanged间接推断 compareLocationWith在 Office.js 事件回调中不可靠(总是返回 "Before"),不用于位置判断- Tab 键在 Word 正文中改变段落缩进(
leftIndent)而非插入\t,不适合用于接受操作