# WorkBuddy 跑在国产化 Linux 上:我把 .docx 处理的坑踩了个遍(附可复制代码)#WorkBuddy#
> 环境:WorkBuddy 客户端 + OpenHarmony / HarmonyOS(`ohos-aarch64`,musl libc)+ Electron 沙箱 > 适用:需要在信创环境、移动端或受限沙箱里落地 AI Agent 的开发者 ## 目录 1. 先看清环境:这不是普通 Linux 2. 坑 1:pip 的「假成功」 3. 坑 2:越过签名校验,问题才真正暴露 4. 换思路:长文档不要用编辑器 API 逐段读 5. 要生成 .docx,交给 WorkBuddy 的本地 editor SDK 6. 把踩坑变成「技能」,别重复交学费 7. 小结:可复制清单 --- ## 一、先看清环境:这不是普通 Linux 很多坑的根因,在动手前就已经写好了。先把环境画出来: | 项目 | 实际情况 | |---|---| | 架构 | `ohos-aarch64` | | libc | **musl**,不是 glibc | | Shell | 没有 `/bin/sh`、没有 `/bin/bash` | | 诊断工具 | 没有 `ldd` | | `/tmp` | 只读 | | Python | 3.12,自带 | 记住两个推论: 1. **凡是编译时假设 glibc 的二进制,到这里都不认。** 2. **调试手段比常规 Linux 少,报错信息就是你唯一的线索。** ## 二、坑 1:pip 的"假成功" 第一个坑最会骗人。 ```bash $ python3 -m pip install lxml Successfully installed lxml-5.4.0 ``` 装机过程一路绿灯,看不出任何异常。然后: ```python >>> import lxml.etree Permission denied ``` 明明装上了,为什么 import 不了?拆开看两件事: **第一,平台有 ELF 签名校验。** 非本机签名的动态库直接拒载,报 `Permission denied` 而不是 `ImportError`——这个报错很有误导性,容易让人往权限方向查。 **第二,ABI 根本没对上。** 看解释器的扩展后缀: ```python >>> import sysconfig; sysconfig.get_config_var('EXT_SUFFIX') '.cpython-312-aarch64-linux-ohos.so' ``` 而 pypi 上的 aarch64 轮子,后缀清一色是 `manylinux2014_aarch64`。**`--platform` 参数只影响 pip 认不认这个文件名,并不会改变二进制里的 ABI。** > 教训:`pip install` 成功 ≠ 模块可用。在非主流平台上,装完必须立刻跑一次 `import` 验证。 ## 三、坑 2:越过签名校验,问题才真正暴露 既然卡在签名上,很自然会想:那把签名这一关解决掉不就行了? 校验确实越得过去,`Permission denied` 也随之消失——**看起来解决了**。可紧接着,真正的拦路虎才露面: ```text Error relocating .../etree.cpython-312-aarch64-linux-ohos.so: PyModule_AddObject: symbol not found ``` 根因很朴素:本机**没有 `libpython3.12.so`**,Python 的符号只存在于主程序的符号表里。glibc 环境下的 CPython 扩展习惯从 `libpython` 动态解析符号,这里解析不到。 而 `ldd` 不存在,连"它到底依赖谁"都查不了。 **结论:这是平台级不兼容,不是配置问题。** 在这条路上继续投入是纯止损。果断换方案。 > 这一条花的时间最不值。判断标准其实很清晰:**当错误从"权限"变成"符号重定位",就不是配置能解决的了。** ## 四、换思路:长文档不要用编辑器 API 逐段读 任务本身是读一份超长 Word(十万字量级)并提炼要点。 **先试的正路**:WorkBuddy 自带的文档编辑器 API,逐段取内容。问题很直接——**一次调用只返回一个块**,十万字要几百上千次调用。时间和 token 双双不可接受。 **换的思路**:`.docx` 本质就是个 zip。直接拆开读 XML: ```python import zipfile, re def extract_docx(path: str) -> list[str]: """把 .docx 的正文按段落/表格抽出为文本行(保持原始顺序)。""" xml = zipfile.ZipFile(path).read('word/document.xml').decode('utf-8') body = re.search(r'(.*)', xml, re.S).group(1) blocks = re.finditer( r'.*?|].*?|', body, re.S ) out = [] for b in blocks: seg = b.group(0) text = ''.join(re.findall(r']*>(.*?)', seg, re.S)) if text.strip(): out.append(text) return out ``` 一次读完,落成**带行号的纯文本**,后续按行号定点取用——检索、摘取、核对都变成了普通的文本操作。 **收益**:几百次 API 调用 → 1 次文件读取。 > ⚠️ 一句纪律:直接拆 XML 属于**降级手段**,绕过了产品的标准通道。如果只是自己跑批处理没问题;如果是在有规范约束的场景下,**先跟需求方说明再动手**,别默默绕。 ## 五、要生成 .docx,交给 WorkBuddy 的本地 editor SDK 读的问题解决了,写的问题又来了。常规的 `python-docx`、`html-to-docx` 在这台机器上——**同样卡在 lxml**(见坑 1)。 绕开依赖链,用 WorkBuddy 内置的本地 editor SDK,三步: **① 新建文档,拿到 `file_id`** ```bash python3 edsdk.py call create_doc # → file_id=new_doc_xxxxxxxx_xxxx ``` **② 插入内容(关键:长内容走文件,别塞命令行)** ```bash python3 edsdk.py call doc_insert_markdown --json '{ "file_id": "new_doc_xxxxxxxx_xxxx", "idx": 0, "markdown": "file:///abs/path/draft.md" }' ``` `markdown` 支持 `file://`,从文件读——**省 token、免转义、免编码事故**。支持标题、列表、表格、加粗等常见语法。 **③ 保存到指定路径** ```bash python3 edsdk.py call save_file --json '{ "file_id": "new_doc_xxxxxxxx_xxxx", "file_path": "/abs/target.docx" }' ``` ### 这一步又踩了三个小坑 **坑 A:空白模板不带页面属性。** 新建出来的文档,XML 里既没有 `sectPr`(节属性)也没有 `docDefaults`(默认样式)→ 页面尺寸、页边距、字号全部走宿主默认值。 **所以想做"一页纸"这种版式,只能靠控制字数,没法靠调页面参数。** 实测经验值:**550–650 字 + 一张小表**,在常见默认字号下稳在一页内。 **坑 B:保存后不要主动关闭文件。** 用户正在预览面板里看着这个文档。`save_file` 就够了,`close_file` 会把他正在看的视图关掉。 **坑 C:`present_files` 返回的是文件地址,不是 `file_id`。** 需要 `file_id` 时去查编辑器池,**不要**默认调 `open_file`——重复 fresh open 会开出与前端订阅脱节的新实例,结果是**你在改,用户在面板里看不到任何变化**。这种 bug 排查起来极其折磨。 ## 六、把踩坑变成"技能",别重复交学费 上面这些坑,最值钱的不是解决方案,而是**别踩第二次**。 我的做法:每个坑解决后,立刻固化成一个 WorkBuddy 技能(Skill)——里面写清楚**触发场景、验证过的命令、参数、以及为什么别的路走不通**。 判断一条经验值不值得写下来,就一句话: > **如果换个人来做,他得从零再摸一遍 —— 那就值得写。** 举个反例:这次我花了不少时间在 `lxml` 上反复尝试(换轮子、换安装参数、反复重装)。这些尝试**在第一次失败后就注定无效**,但当时没有把"平台级不兼容"这个结论立刻固化,导致后面又绕了回去。 **固化之后,下一次会话开局就知道这条路封死了,直接绕行。** ## 七、小结:可复制清单 | 环节 | 结论 | |---|---| | **环境预检** | 先确认 libc(musl/glibc)、架构、Shell、`/tmp` 可写性——**90% 的坑在这里就有答案** | | **装依赖** | `pip install` 成功 ≠ 可用;**装完立刻 `import` 验证** | | **看清 ABI** | 比对 `sysconfig.get_config_var('EXT_SUFFIX')` 与轮子标签;`--platform` 不改 ABI | | **止损信号** | 报错从"权限"变成"符号重定位"→ **平台级不兼容,立刻换路** | | **读长文档** | 别用编辑器 API 逐段读;`.docx` = zip,直读 `word/document.xml` | | **写 .docx** | 绕开重依赖链,用宿主本地 SDK:`create_doc → insert_markdown(file://…) → save_file` | | **版式控制** | 模板不带页面属性时,"一页纸"靠**控字数**,不靠调参数 | | **经验固化** | 踩过的坑写成可复用文档;**换个人要重摸一遍的,就值得写** | --- **一句话总结**:用 WorkBuddy 在受限环境里干活,瓶颈往往不在模型,而在**你以为通用的那层依赖**。先画清环境,再选通道;能直读数据就别走重接口;每次踩坑都固化成技能——这三条比任何单点技巧都值钱。