普通视图

发现新文章,点击刷新页面。
今天 — 2026年2月2日首页

1个月双线作战:AI公文助手从0到1开发,与国产化适配的"踩坑"全记录

作者 徐小夕
2026年2月2日 10:18

全职创业2年零1个月,接下来和大家分享一下我们最近做了AI协同产品 JitWord 的研发历程。

为什么要在1个月内攻坚两个硬骨头?

说实话,启动这次迭代前,团队内部是有分歧的。

2026-01-30 21.09.31.gif

一方面,AI公文助手 是很多政企客户反复提的需求——他们想要 Word 那种严谨的公文排版,又想要AI的生成能力,还要能在线协同;另一方面,国产化适配是信创大背景下的必选项,涉及国产操作系统、国产浏览器、甚至国产芯片的兼容性问题。

这两个需求,任何一个单独做都要扒层皮。但市场不等人,我们决定在1个月内"双线作战"。

这篇文章记录了我们如何从0搭建AI公文助手模块,以及在国产化适配过程中遇到的那些让人头秃的坑。希望能给同样面临信创改造或富文本技术选型的同学一些参考。


JitWord 是什么?(如果你第一次听说)

2026-01-29 11.02.48.gif

简单给新朋友介绍一下。JitWord 是我们团队开发的协同AI文档引擎,定位是"让Web文档拥有桌面级体验",打造“云端Office”办公体验。

核心能力包括:

  • 多人实时协同:基于CRDT算法,支持Word级别的冲突解决
  • AI辅助创作:内置AI续写、润色、总结,支持自定义Prompt
  • 数学公式渲染:自研公式引擎,支持LaTeX到Word的无损转换(之前文章有详细讲过)
  • 一键导出Word:不只是PDF,是真正的.docx格式,导出后还能在Office里二次编辑
项目 描述
产品名称 JitWord 协同AI文档
技术栈 Vue3 + NestJS + CRDT + WebSocket
核心功能 实时协同、AI写作、公文处理、Word导出
适用场景 企业文档中台、科研协作、政务办公
版本状态 V2.1(AI公文助手 + 国产化适配版)

最近我们也开源了一版sdk,大家可以轻松本地使用和集成:

github地址:github.com/MrXujiang/j…


第一部分:AI公文助手从0到1

1.1 需求拆解:公文场景的残酷现实

做传统富文本编辑器的朋友可能不知道,公文排版是中文排版的地狱模式

  • 红头文件:要严格遵循 GB/T 9704-2012 国家标准,版头、发文字号、签发人都有固定位置
  • 多层嵌套结构:一、(一)、1.(1)、①,这五种层级格式不能乱
  • 表格与附件:公文里的表格必须能跨页重复表头,附件说明有特定格式
  • 严格的页面设置:A4纸张、上白边37mm±1mm、下白边35mm±1mm...

我们调研了市面上几乎所有的Web Office方案,发现要么是简单的表单模板(灵活性不够),要么是把PDF转图片(无法二次编辑)。所以决定自己实现一套结构化公文引擎

image.png

1.2 技术架构:如何把AI塞进公文流程?

我们采用了 模板引擎 + AI生成 + 人工调整 的三段式架构:

┌─────────────┐     ┌─────────────┐     ┌─────────────┐
│ 公文模板库  │────▶│  AI解析层   │────▶│ 编辑渲染层  │
│ (.docx解析) │     │ (LLM+规则)  │     │ (结构化编辑)│
└─────────────┘     └─────────────┘     └─────────────┘
       │                                        │
       │    ┌─────────────┐                     │
       │◄───│  导出引擎   │◄────────────────────┘
       │    │(Word/PDF)   │
       │    └─────────────┘

关键技术决策:

  • 模板解析:目前了复用 mammoth.js,但是它的样式映射太粗粒度,后面规划重写。
  • AI提示词工程:公文写作不是 Creative Writing,而是 Constraint Writing。我们让AI先分析模板结构,再填充内容,最后做格式合规性检查(比如检查发文字号是否符合"国发〔2024〕1号"这种格式)。
  • 编辑器选型:基础还是ProseMirror,但重写了NodeSpec来支持"公文块"(OfficialBlock)概念。每个公文块是一个不可随意拆分的逻辑单元,比如"主送机关"是一个块,"正文"是一个块。

1.3 核心代码:公文模板的JSON Schema设计

这是我们定义的公文结构规范(节选):

// types/document.ts
export interface OfficialDocument {
  version: 'GB/T-9704-2012';
  header: {
    issuingBody: string;     // 发文机关
    documentNumber: string;  // 发文字号
    urgencyLevel: '特急' | '加急' | '平急';
  };
  body: OfficialBlock[];  // 正文,由多个公文块组成
  attachments?: Attachment[];
}

export interface OfficialBlock {
  type: 'redHeader' | 'recipient' | 'text' | 'level1' | 'level2' | 'table';
  content: string | TableContent;
  style: {
    fontFamily: '仿宋_GB2312' | '黑体' | '楷体_GB2312';  // 信创字体
    fontSize: number;       // 三号=16pt,小三=15pt...
    lineHeight: number;     // 28-30磅固定值
  };
}

遇到的坑: 仿宋_GB2312 这个字体在Mac和Linux上表现差异巨大。Windows上看着好好的文档,在国产系统(基于Linux)上打开后行高会乱掉。解决方案是用CSS的line-height: fixed + 字体fallback栈,并且在导出Word时重新计算行高。

1.4 AI生成流程的优化

image.png

最初我们直接把"写一篇关于XX的通知"扔给GPT-5,结果生成的内容总是太口语化,而且格式经常出错。

优化后的流程是:

  1. 模板匹配:先根据用户选择的公文类型(通知、通报、请示、报告等),加载对应的Prompt模板
  2. 结构化生成:要求AI输出JSON格式,而不是Markdown
  3. 规则校验层:用正则表达式校验公文要素是否齐全(比如通知必须有"特此通知"结尾,请示必须有"妥否,请批复")
// ai/officialWriter.ts
async function generateOfficialDoc(params: GenerationParams) {
  const template = loadTemplate(params.type);
  
  const structuredPrompt = `
  你是一个严谨的公文写作助手。请根据以下信息,生成符合GB/T 9704-2012的公文内容。
  必须输出为JSON格式,字段定义如下:${JSON_SCHEMA}
  
  用户输入:${params.topic}
  要求:${params.requirements}
  `;
  
  const raw = await llm.generate(structuredPrompt);
  const doc = JSON.parse(raw);
  
  // 规则校验
  if (!validateOfficialFormat(doc)) {
    throw new Error('生成内容不符合公文规范,请重试');
  }
  
  return doc;
}

效果: 生成一份标准通知的时间从人工30分钟缩短到AI 10秒 + 人工审核2分钟,效率提升90%


第二部分:国产化适配的"踩坑"全记录

image.png

如果说AI公文助手是"从0到1的创造",那国产化适配就是"从能用到好用的磨砺"。

我们的目标是让 JitWord 能在统信UOS麒麟OS等国产操作系统,以及360安全浏览器奇安信可信浏览器等国产Chromium内核浏览器上稳定运行。

2.1 踩坑一:WebSocket连接的诡异断开

现象: 在麒麟V10系统上,协同编辑总是过几分钟就断开,提示"网络异常",但用户明明能正常刷网页。

排查过程:

  1. 首先排查Nginx配置,以为是proxy_read_timeout太短,改成3600秒,无效。
  2. 检查浏览器Network面板,发现国产浏览器的某些安全策略会主动断开静默的WebSocket连接
  3. 最后发现是奇安信可信浏览器内置了"长连接保护"策略,超过5分钟没有数据交互就会自动断开。

解决方案:

// 心跳机制加强版
export class ReliableWebSocket {
  private ws: WebSocket;
  private heartbeatInterval: NodeJS.Timer;
  
  // 国产浏览器的心跳间隔要更短
  private heartbeatDelay = isDomesticBrowser() ? 10000 : 30000; 
  
  connect() {
    this.ws = new WebSocket(url);
    
    this.heartbeatInterval = setInterval(() => {
      // 发送空操作或ping帧,保持连接活性
      this.send({ type: 'heartbeat', timestamp: Date.now() });
    }, this.heartbeatDelay);
  }
}

2.2 踩坑二:富文本编辑器的输入法冲突

协同.png

这是让我最想骂街的坑。

现象: 在统信UOS + 搜狗输入法(国产版)下,输入中文时,编辑器光标会乱跳,甚至吞字。

根因分析: 国产操作系统的输入法架构和Windows差异很大。我们用的ProseMirror在处理beforeinput事件时,和一些国产输入法的Composition事件冲突。具体表现为:输入法开始合成(compositionstart)时,ProseMirror尝试更新选区,导致输入法丢失了上下文。

解决方案: 不得不patch了ProseMirror的view模块,在合成输入期间暂停所有远程协同更新

// patches/prosemirror-view.ts
let isComposing = false;

editorView.dom.addEventListener('compositionstart', () => {
  isComposing = true;
  // 暂停接收远程操作,避免光标跳动
  collaboration.pauseSync();
});

editorView.dom.addEventListener('compositionend', (e) => {
  isComposing = false;
  const finalData = e.data;
  
  // 延迟恢复同步,等待输入法插入完成
  setTimeout(() => {
    collaboration.resumeSync();
  }, 100);
});

2.3 踩坑三:字体渲染与导出

现象: 同样的"仿宋",在Windows上叫"仿宋",在国产系统上可能叫"FangSong"、"Fangsong"、或者"Source Han Serif CN"。公文要求必须用仿宋_GB2312,但这个字体在某些国产系统上没有预装。

解决方案三部曲:

  1. 前端降级方案:CSS设置font-family: 'FangSong_GB2312', 'Source Han Serif CN', 'Noto Serif CJK SC', serif;
  2. 后端字体嵌入:导出Word时,如果检测目标系统缺少字体,用Java操作POI把字体文件嵌入到生成的docx中
  3. Web字体预加载:在编辑器初始化时,异步加载WOFF2格式的仿宋字体文件,确保所见即所得
// 导出Word时的字体嵌入逻辑(Java实现)
public void embedFonts(XWPFDocument doc, String[] requiredFonts) {
    for (String fontName : requiredFonts) {
        if (!systemHasFont(fontName)) {
            InputStream fontStream = getClass().getResourceAsStream("/fonts/" + fontName + ".ttf");
            doc.embedFont(fontName, fontStream);
        }
    }
}

性能优化:让国产硬件也能流畅运行

说实话,很多国产终端的硬件配置(特别是信创笔记本)不如主流Windows本。我们在1个月内做了以下针对性优化:

虚拟滚动 + 分层渲染

公文通常很长(几十页很正常),我们在ProseMirror基础上实现了虚拟滚动,只渲染可视区域内的DOM节点。同时把静态内容(已经定稿的段落)标记为contenteditable: false,减少MutationObserver的开销。

AI生成的防抖处理

02.gif

当AI生成大段文本时,不能直接一次性插入编辑器(会导致卡顿)。我们改成了逐句插入 + requestAnimationFrame

async function insertAIGeneratedContent(content: string) {
  const sentences = content.split(/([。!?])/);  // 按句分割
  
  for (let i = 0; i < sentences.length; i += 2) {
    const sentence = sentences[i] + (sentences[i+1] || '');
    
    await new Promise(resolve => {
      requestAnimationFrame(() => {
        editor.insertText(sentence);
        resolve(null);
      });
    });
    
    // 每5句暂停一下,让UI线程喘息
    if (i % 5 === 0) await sleep(10);
  }
}

最终效果与场景展示

2026-01-15 10.52.12.gif

公文助手实际应用场景

jitword-gw.png

场景1:政府机关的请示报告

  • 输入:"关于申请信息化建设经费的请示"
  • AI生成:自动匹配"请示"模板,生成红头、发文字号、正文、结尾语
  • 人工调整:只需填写具体金额和项目明细
  • 导出:直接生成符合省级办公厅格式要求的Word文件

场景2:国企的发文通知

  • 协同:办公室主任起草,分管领导在线批注修改,法务审核合规性
  • 留痕:所有修改记录保存,满足公文归档的审计要求
  • 套红:一键生成带红色抬头的正式公文版式

国产化适配验证环境

我们在以下环境完成了完整测试:

  • 操作系统:统信UOS 1060、麒麟V10 SP1、中科方德
  • CPU架构:x86_64、ARM64(鲲鹏920、飞腾2000)
  • 浏览器:360安全浏览器v13、奇安信可信浏览器、火狐中国浏览器

技术总结与反思

这1个月的"双线作战",最大的收获不是功能本身,而是对信创环境下的Web开发有了更深理解:

  1. 不要相信浏览器的UserAgent:国产浏览器都伪装成Chrome,但行为可能完全不同。必须做特性检测(feature detection)而非浏览器嗅探。

  2. 富文本编辑器要"防御性编程":输入法、选区、滚动这些在标准浏览器上稳定的功能,在特殊环境下可能有各种奇奇怪怪的表现。代码要更保守,try-catch要更密集。

  3. AI生成必须后接规则校验:大模型有幻觉,公文又是极其严谨的体裁。AI负责"快",规则引擎负责"准",两者结合才能实用。

  4. 字体和排版是信创隐形大坑:中西文混排、行高计算、字体回退,这些细节决定了产品看起来是"业余demo"还是"正式产品"。


如何集成和体验?

JitWord 目前主要面向企业级用户开发者集成

  • 在线演示:如果你想看看AI公文助手的实际效果,可以访问我们的演示环境(文中不放链接了,掘金私信我或评论获取)
  • 私有化部署:支持国产服务器私有化部署,适配信创环境
  • SDK集成:提供JavaScript SDK,可以Embed到你的业务系统中

如果你也是正在做信创改造的技术负责人,或者需要公文处理能力的产品经理,欢迎评论区交流踩坑经验。国产化这条路,大家互相搀扶才能走得快一点。

我们也开源了一版sdk,大家可以轻松本地使用和集成:

github地址:github.com/MrXujiang/j…


未来规划

这1个月的攻坚只是开始,接下来的 roadmap 包括:

  • 智能校对:接入NLP模型,自动检查公文中的政治术语准确性、数字逻辑一致性(比如"2024年"不能写成"2024年度"在某些语境下)
  • 手写签批:对接国产手写板和签章系统,实现移动端批公文
  • 更多公文类型:从现在的通知、请示、报告,扩展到会议纪要、函、议案等15种法定公文

技术栈彩蛋 🎯

如果你在关注相关技术方向,这是我们用的核心栈,也是目前市面上比较热门的技术方向:

  • Vue3 + Vite + TypeScript(前端)
  • NestJS + TypeORM(后端,支持国产数据库适配)🚀
  • ProseMirror(编辑器内核,深度定制)
  • Yjs(CRDT协同算法)🧩
  • Docker + K8s(部署)

觉得有用的话,点个赞或者收藏吧。信创适配这条路很长,希望这篇文章能帮你少走些弯路。有任何技术问题,评论区留言,我看到都会回复。

❌
❌