
1. 项目缘起与整体架构设计1.1 为什么要在本地搭一套AI智能体Office套件日常办公里最割裂的一件事就是文档、表格、演示稿三件套各自为政而AI能力通常以插件或网页对话框的形式游离在它们之外。你写一份周报得先在文档工具里敲字再切到某个AI对话页面润色最后复制回来做一份数据周报得在表格里算完指标再手动把结论誊到演示稿里。这个项目要解决的就是把“AI智能体”直接嵌进Office文档流的骨架里让文档、表格、演示三种载体共享同一套智能体调度层。我给它起的定位是一个以文档对象模型为中枢、以智能体编排为大脑的轻量办公套件。它不是一个要替代成熟商业办公软件的产品而是一个可拆解、可复现、适合计算机科学与技术方向同学做课程设计或毕业设计的完整工程样本。你如果正在找“AI智能体”落地到具体场景的题目又不想只停留在调用API写个聊天框那这套东西的架构思路值得参考。核心关键词有三个AI智能体负责理解意图与调度工具Office套件负责承载文档、表格、演示三类结构化内容计算机科学与技术则决定了它的技术选型必须兼顾工程规范、可扩展性和可演示性。三者叠加最终产物是一个能“听懂人话、动手改文档”的桌面级应用。1.2 整体分层架构与选型逻辑整套系统我按四层来切从上到下依次是交互层、智能体编排层、文档能力层、存储与运行时层。这样切的好处是每一层都能单独替换比如你不想用某个大模型只换编排层里的模型适配器即可文档能力层完全不用动。交互层选的是Electron加React。理由很直接Office套件的核心是富文本编辑和表格渲染Web技术栈在这块的生态最成熟而Electron能把Web应用打包成桌面程序方便调用本地文件系统。React负责组件化文档编辑器、表格网格、演示画布各自是独立组件通过状态管理共享当前打开的文件上下文。智能体编排层是整个项目的心脏。我采用的是“规划器加执行器”的双阶段结构规划器负责把用户一句话拆成若干可执行步骤执行器负责逐步调用文档能力层提供的工具函数。这里参考了ReAct模式的思路——推理与行动交替进行每一步行动后把观察结果回灌给规划器让它决定下一步。之所以不用一次性生成完整计划再执行是因为文档操作有强依赖比如“把第二段加粗”必须先知道第二段在哪一次性规划容易在中间步骤失败后全盘崩溃。文档能力层是套件的“手和脚”。它把文档、表格、演示的操作抽象成一组原子工具比如insertParagraph、setCellValue、addSlide、applyStyle。每个工具都有明确的输入输出契约智能体只能通过这些工具改文档不能直接操作底层数据结构。这层抽象是安全边界也是可测试性的保障。存储与运行时层负责文件持久化和会话状态。文档用JSON格式存储抽象语法树表格用二维数组加元数据演示稿用幻灯片对象数组。选JSON而不是二进制格式是为了让智能体的每次修改都能被diff出来方便做撤销重做和操作审计。提示分层不是为了好看是为了让“智能体出错”这件事的影响范围可控。编排层出问题最多是任务没完成文档能力层出问题才会破坏文件内容所以工具函数的参数校验必须做在最前面。1.3 与纯云端方案的取舍市面上不少方案是把文档传到云端由云端AI处理后再传回来。我没走这条路原因有三。第一课程设计场景下本地运行能避免网络依赖演示时不怕断网。第二文档内容往往包含个人信息或未公开数据本地处理更稳妥。第三本地架构能让你完整看到数据从输入到输出的每一步流转对理解智能体工作原理更有帮助。代价是本地模型能力有限。我的处理方式是双模型适配简单任务走本地小模型复杂任务走可配置的远程接口但接口地址和密钥由用户自己填程序本身不内置任何第三方服务。这样既保证了开箱即用的体验又保留了扩展空间。2. 智能体编排层的核心细节与实操要点2.1 规划器与执行器的职责边界规划器的输入是用户自然语言指令加上当前文档的摘要信息输出是一个步骤列表每个步骤包含工具名、参数和预期结果描述。执行器拿到步骤后逐个调用工具把每次调用的返回值作为观察结果记录下来再交回规划器判断是否继续。这里有个容易踩的坑规划器如果看不到文档当前状态就会瞎规划。比如用户说“把标题改成红色”规划器得先知道标题在第几段、当前是什么样式。我的做法是在每轮规划前把文档的结构摘要注入提示词摘要只包含段落索引、类型和前若干字符不传全文既控制token消耗又保留必要上下文。执行器的关键设计是幂等性。同一个工具用相同参数调用两次结果应该一致。比如setCellValue设置单元格值重复调用不会产生副作用。但insertParagraph这类插入操作天然不幂等我的处理是给每次插入分配唯一ID重复执行时先检查ID是否存在存在就跳过。这个细节在任务重试时特别重要。2.2 工具函数的定义规范每个工具函数必须包含四要素名称、描述、参数schema、返回值schema。名称用动词加名词的驼峰格式比如insertParagraphAfter。描述要写清楚“什么时候用这个工具”而不是“这个工具做什么”因为规划器是根据场景选工具的。参数schema我用JSON Schema描述每个参数标注类型、是否必填、取值范围。比如段落索引参数类型是整数最小值0最大值是当前段落数减一。规划器生成参数时会参考这个约束减少越界错误。返回值schema统一包含三个字段success布尔值、data具体数据、error错误信息。执行器根据success决定是否继续根据error决定是否让规划器重新规划。这套规范看起来繁琐但实测下来能把工具调用的失败率降低不少因为规划器有了明确的反馈信号。2.3 上下文管理与记忆机制智能体做多轮任务时上下文会越来越长。我的策略是分层记忆短期记忆保存当前任务的步骤历史和观察结果长期记忆保存用户偏好和常用操作模式。短期记忆在任务完成后清空长期记忆持久化到本地。短期记忆的裁剪规则是保留最近若干轮完整记录更早的只保留步骤摘要。摘要由规划器自己生成比如“已完成在第二段后插入一段文字内容关于项目进度”。这样即使任务很长规划器也能快速回顾已完成的部分。长期记忆我设计成一个简单的键值存储键是操作类型值是用户历史选择。比如用户多次把标题样式设为“一级标题加粗”下次规划时就把这个偏好注入提示词。这个机制不复杂但能让智能体越用越顺手。注意上下文注入要控制长度我一般把文档摘要控制在500字以内短期记忆控制在2000字以内。超过这个量规划器的响应速度会明显下降而且容易忽略关键信息。3. 文档能力层的实现与关键参数3.1 文档编辑器的数据模型文档编辑器的核心是一个段落数组每个段落对象包含id、type、content、style四个字段。type区分普通段落、标题、列表项、代码块等。content是行内元素数组支持文本、加粗、斜体、链接等。style保存段落级样式比如对齐方式、缩进、行距。选这个模型而不是直接操作HTML是因为HTML太灵活智能体容易生成不合法的结构。段落数组的约束更强智能体只能在既定类型里选生成的内容天然合法。渲染时再把段落数组转成HTML这一步是纯函数不涉及智能体逻辑。行内元素的处理有个细节加粗和斜体可以嵌套但链接不能嵌套链接。我在工具函数里做了校验遇到非法嵌套直接返回错误让规划器重新生成。这个校验逻辑大概三十行代码但省去了大量调试时间。3.2 表格模块的公式与依赖计算表格模块支持基础公式比如求和、平均值、计数。公式以字符串形式存储比如SUM(A1:A10)。计算时先解析公式提取依赖的单元格范围然后按依赖顺序求值。这里我用的是简单的拓扑排序因为表格公式一般不会太复杂没必要上完整的电子表格计算引擎。依赖计算的关键是循环引用检测。如果A1的公式引用了B1B1的公式又引用了A1就得报错。我的做法是在求值前先构建依赖图用深度优先搜索检测环检测到环就把涉及的单元格标记为错误值并在界面上高亮提示。单元格值的类型我做了区分数字、文本、布尔、错误、空。公式计算时按类型做隐式转换比如文本加数字文本能转数字就转转不了就返回类型错误。这套规则和主流表格软件基本一致用户不会感到陌生。3.3 演示稿的幻灯片对象结构每张幻灯片是一个对象包含id、layout、elements三个字段。layout决定占位符的位置和数量比如“标题加内容”“两栏对比”“纯图片”。elements是元素数组每个元素有类型、位置、尺寸、内容。元素定位我用的是相对坐标左上角为原点宽高按百分比算。这样换不同尺寸的屏幕或导出不同比例的图片时布局不会乱。智能体添加元素时只需要指定相对位置不用关心绝对像素。演示稿和文档、表格共享同一套智能体工具接口但工具集不同。比如演示稿有addSlide、setSlideLayout、addElementToSlide文档有insertParagraph、applyStyle。规划器根据当前打开的文件类型选择对应工具集这个切换对用户透明。4. 实操过程与核心环节实现4.1 环境搭建与项目初始化先装Node.js版本建议18以上因为要用到一些较新的API。然后初始化项目我用的包管理工具是pnpm比npm快且省磁盘。初始化命令是pnpm init然后装核心依赖Electron、React、TypeScript、Vite。Vite负责开发时的热更新Electron负责桌面壳。目录结构我按功能切分src/main放Electron主进程代码src/renderer放React应用src/agent放智能体编排逻辑src/document放文档能力层src/shared放共享类型定义。这个结构清晰新人接手能快速定位代码。TypeScript配置里我开了严格模式strict设为truenoImplicitAny也设为true。刚开始写会有点痛苦但能避免大量运行时错误尤其是智能体生成的参数类型不对时编译期就能发现。4.2 智能体调度循环的代码实现调度循环的核心是一个异步函数接收用户指令返回执行结果。伪代码逻辑如下先获取当前文档摘要调用规划器生成步骤列表然后遍历步骤逐个调用执行器每次调用后把结果追加到观察历史再判断是否需要重新规划。规划器的提示词模板我调了很多版最终稳定下来的结构是系统角色说明、可用工具列表、当前文档摘要、历史观察记录、用户指令、输出格式要求。输出格式要求规划器返回JSON数组每个元素包含工具名和参数。这个格式约束很重要不约束的话规划器会返回自然语言解析起来很麻烦。执行器调用工具时要做参数校验校验失败不抛异常而是返回错误对象。规划器看到错误对象后会尝试修正参数重新调用。我设了最大重试次数默认3次超过就终止任务并提示用户。这个重试机制能解决大部分参数格式问题。4.3 文档操作的原子工具实现示例以insertParagraphAfter为例输入是参考段落ID和新段落内容输出是新段落ID。实现步骤是先根据参考段落ID找到索引然后在索引加一的位置插入新段落对象最后返回新段落ID。插入前要校验参考段落ID是否存在不存在就返回错误。再以setCellValue为例输入是工作表名、单元格地址、值输出是操作结果。实现时要先解析单元格地址比如B3解析成行2列1然后检查行列是否越界越界返回错误。设置值后要触发依赖重算把受影响的公式单元格重新求值。每个工具函数我都写了单元测试覆盖正常路径和边界情况。测试用Vitest跑起来很快。智能体逻辑的测试比较麻烦我用的是模拟规划器预设固定的步骤列表验证执行器能正确调用工具并处理返回值。4.4 界面与智能体的联动界面上有个指令输入框用户输入后按回车触发智能体调度。调度过程中界面显示进度指示每一步完成后更新状态文字比如“正在分析文档结构”“正在插入段落”“正在调整样式”。这些状态文字来自执行器的观察结果用户能看到智能体在做什么。文档修改后界面实时刷新因为文档数据模型是响应式的工具函数修改数据后React组件自动重渲染。这个联动是双向的用户手动编辑文档后文档摘要会更新下次智能体调度时能看到最新状态。撤销重做我基于操作历史实现每次工具调用前记录文档快照撤销时回滚到上一个快照。快照用结构化克隆存储避免引用共享导致的状态污染。这个机制对智能体操作尤其重要因为智能体可能连续做多个修改用户想撤销时得能一步步回退。5. 常见问题与排查技巧实录5.1 智能体不按预期调用工具最常见的问题是规划器选错工具。比如用户说“把这段文字放大”规划器可能选了setFontSize但当前文档能力层根本没这个工具只有applyStyle。排查方法是看规划器输出的工具名是否在可用工具列表里不在的话就是提示词里的工具描述不够清晰。我的解决经验是工具描述里一定要写“什么时候用”而不是“这个工具做什么”。比如applyStyle的描述写成“当用户想改变文字外观大小、颜色、粗细时使用此工具”比“设置文字样式”有效得多。另外在提示词里加一句“只能使用下列工具不得虚构工具名”能显著减少幻觉。还有一种情况是参数格式不对。比如段落索引应该是数字规划器返回了字符串“2”。执行器的参数校验会拦截这种错误返回错误信息“参数index类型应为number”。规划器看到错误后一般能自我修正。如果反复修正不了就在提示词里加参数示例比如“index: 2数字不是字符串”。5.2 文档操作后界面不更新这个问题的根源通常是数据模型改了但没触发响应式更新。排查时先看工具函数是否直接修改了对象属性如果是React检测不到变化。解决方法是改用不可变更新每次修改都创建新对象或者用状态管理库的更新方法。另一个可能原因是工具函数在异步回调里修改数据而React的批处理机制导致更新被合并。我的做法是在工具函数里用flushSync强制同步更新或者把修改操作放在微任务里。实测下来文档操作这种低频修改用flushSync完全没问题不会影响性能。如果界面部分更新部分不更新那多半是组件拆分粒度问题。比如段落组件用了React.memo但比较函数没写对导致应该更新的段落被跳过。检查memo的比较函数确保它比较了所有影响渲染的字段。5.3 公式计算结果不对先检查依赖范围是否正确。比如SUM(A1:A10)如果实际数据在A1到A9A10是空结果应该一样。但如果A10有文本SUM会忽略文本结果还是对的。问题往往出在范围写错比如写成了A1:A100把无关单元格也算进去了。再检查循环引用。如果公式引用了自己所在的单元格或者间接引用了自己就会形成环。我的实现里会检测环并标记错误但有时候环比较隐蔽比如A1引用B1B1引用C1C1又引用A1。排查方法是看错误单元格的依赖链顺着链找环。还有一种情况是类型转换不符合预期。比如A1B1A1是文本“3”B1是数字4结果是7还是“34”我的实现是尝试把文本转数字转成功就做加法转失败就返回类型错误。这个规则要在文档里写清楚避免用户困惑。5.4 智能体任务执行到一半卡住卡住的原因通常是规划器陷入了循环反复生成相同的步骤。排查方法是看观察历史如果连续几步的工具名和参数都一样就是循环了。我的处理是设置最大步骤数默认20步超过就终止并提示用户“任务过于复杂请拆分成多个指令”。另一个原因是工具调用超时。比如某个工具函数里有网络请求或大量计算执行时间过长。我给每个工具调用设了超时时间默认5秒超时就返回错误让规划器决定是否重试。对于确实耗时的操作比如全文档重算公式我会把它拆成多个小步骤每步处理一部分。还有一种情况是规划器返回了空步骤列表表示任务已完成但实际文档没变化。这通常是规划器误判以为不需要操作。排查时看规划器的推理过程如果它说“文档已符合要求”但实际不符合就是文档摘要没传对。检查摘要生成逻辑确保它反映了文档的真实状态。5.5 常见问题速查表问题现象可能原因排查方法解决措施规划器选错工具工具描述不清晰检查工具描述是否说明使用场景改写描述强调“什么时候用”参数格式错误规划器未按schema生成查看执行器返回的错误信息在提示词中加参数示例界面不更新数据模型未触发响应式检查是否直接修改对象属性改用不可变更新或强制同步公式结果不对依赖范围或循环引用检查公式范围和依赖链修正范围检测并提示环任务卡住规划器循环或工具超时查看观察历史是否重复设最大步骤数和超时时间任务提前结束文档摘要不准确对比摘要与实际文档修正摘要生成逻辑提示排查智能体问题时先把规划器的输入输出完整打印出来包括提示词、模型返回、执行器观察结果。大部分问题看一遍日志就能定位比盲目改代码高效得多。6. 扩展方向与个人实操体会这套东西做完之后我最大的体会是智能体的能力上限不取决于模型多强而取决于工具设计得多好。同样的模型工具描述清晰、参数约束严格、返回值规范任务成功率能差出一大截。我试过用同一套模型只改工具描述成功率从六成提到八成五。所以如果你也在做类似项目别急着换模型先把工具层打磨好。扩展方向上我目前想到几个。一是加协作功能多个智能体分别负责文档、表格、演示通过消息队列通信完成跨文档的复杂任务。二是加模板系统用户保存常用文档结构智能体根据模板填充内容。三是加版本对比智能体每次修改后生成diff用户可以选择性接受或拒绝。这几个方向都不需要改核心架构在现有分层上叠加即可。最后分享一个小技巧调试智能体时把温度参数调到0让模型输出尽量确定。等流程跑通了再适当调高温度增加灵活性。我一开始没注意这个温度设了0.7规划器每次生成的步骤都不一样排查问题特别费劲。调到0之后同样输入基本同样输出调试效率高了很多。