WPS 文字操作一站式方案(COM 直连)
结论先行
经过 23(批注)/24(修订)/25(MCP 实时编辑)/34(COM 直连)几轮折腾,结论很清楚:
做标书时,AI 操作 WPS 文字,只用一条通道:win32com 直连你正在打开的 WPS。
word-mcp-live(SSE 服务)不是不能用,而是「连接层」本身不稳定:SSE 长连接会掉、并发会堵、大文档会超时卡死、WPS 下get_comments报错。它的「实时编辑」底层也是 Word COM——等于在 COM 外面又套了一层会断的管道。- 纯 COM 直连(
win32com)是 AI 直接握着 WPS 的 COM 把手,没有中间服务器,稳定。几次实测也验证了这一点。
所以:MCP/SSE 从主流程退役,只作为「离线批量生成整篇新文档」的可选后端(而且那用 python-docx 直接做也一样,不绕 MCP)。实时改你正在编的标书模板,一律走 COM。
你项目里
com_insert.py / com_read_para.py / com_fill_blanks.py / get_table.py已经全是 COM 直连写法,方向本来就对。混乱只来自「表格读写 + 批注」还挂在 MCP 上(见第三节)。
一、连接方式(最关键,先把坑钉死)
✓ 正确:连到正在运行的 WPS(实时编辑用这个)
import win32com.client
app = win32com.client.GetActiveObject('KWPS.Application') # WPS
# 或 'Word.Application'(MS Word)
doc = app.ActiveDocument
- 你项目里四个 COM 脚本全是这个写法,实测可用。
- 多软件兜底:循环试
['KWPS.Application', 'Word.Application'],哪个能连上用哪个(见各脚本里的_get_word_app())。 - ProgID 大小写不敏感,
kwps/KWPS都行。
✗ 三个常见坑
- 别用
pythoncom.GetActiveObject(...)(低层那个) —— 返回的是裸PyIUnknown壳,没有.Documents,一调就挂。要用win32com.client.GetActiveObject(带client的,它会帮你包装好)。34 号笔记说的「GetActiveObject 不能用」指的就是低层那个,不是现在脚本里这个。两者名字只差一个client。 - 别用
win32com.client.Dispatch(...)去开已打开的文档 —— Dispatch 会新起一个 WPS 实例,文档被你手头的 WPS 锁着,它打不开。Dispatch 只在「WPS 没开这个文件、纯离线处理」时才用。 win32com.client.GetObject(Class='Word.Application')也能连运行中的实例,和GetActiveObject等价(34 号推荐它)。两种都行,脚本统一用GetActiveObject即可。
⚠ 兜底:后台进程 COM 激活失败(无效的类字符串)
WorkBuddy 的 Bash 若在后台 / 非交互桌面上下文跑,可能报 (-2147221005, '无效的类字符串')。这是 Windows COM 拒给「非交互式」子进程激活 LocalServer 的问题,不是代码错。
解法:写个 .bat,用 explorer.exe 启动它(explorer 继承真正的交互桌面上下文),Python 把结果写进 txt,再读 txt。详见 34 号笔记「QClaw/OpenClaw 无法直接调用 COM」一节。当前 WorkBuddy 一般能直连,失败才走这座桥。
二、操作全景表(每个动作对应哪个脚本)
| 你要做什么 | 走 COM 的哪个脚本 / 函数 | 命令示例 |
|---|---|---|
| 读某段文字 / 看空格段结构 | com_read_para.py → read_para / analyze_spaces |
python com_read_para.py 6 |
| 在段落指定位置插字 | com_insert.py → insert_text_at_pos |
python com_insert.py insert 12 10 "山西金辉" |
| 在某个词后面插字 | com_insert.py → insert_text_after_anchor |
python com_insert.py after "供应商:" "山西金辉" |
| 模板填空(保留空格、插内容) | com_fill_blanks.py → fill_paragraph |
先 probe 6,再 fill(读 JSON) |
| 光标定位当前表格序号 | get_table.py → get_current_table |
光标点进表,返回序号 |
| 插入文档块(营业执照等) | get_table.py → insert_block |
insert_block('营业执照.docx', after='...') |
| 读 / 写表格单元格 | ⚠ 目前还在 mcp_client.py(MCP) |
— |
| 加批注 | ⚠ 笔记里有 COM 写法,但没封装成脚本 | — |
表里打 ⚠ 的两项,就是你现在「还不得不碰 MCP」或「没脚本」的地方。这正是混乱根源,见第三节。
三、为什么还乱 + 怎么收口
混乱来自两点:
(1)表格读写和批注还挂在 MCP 上。 mcp_client.py 用 SSE 连 word_mcp_server 做 get_table_info / set_cell,而定位表格用 COM 的 get_current_table,等于「一半 COM 一半 MCP」。表格填几十个格时 SSE 一抖就前功尽弃。其实表格单元格读写用纯 COM 一句话就能做,根本不需要服务器:
# 读
val = doc.Tables(ti).Cell(row, col).Range.Text.rstrip('\r\x07')
# 写
doc.Tables(ti).Cell(row, col).Range.Text = "张三"
应该新增一个 com_table.py:读表结构 + 批量 set_cell(从后往前、插入不动原格式),把 mcp_client.py 彻底替掉。
(2)批注没脚本。 23 号笔记用的是解压 docx 改 XML 的老路(适合离线),但实时给打开的文档加批注,COM 一句话更稳:
doc.Comments.Add(range, "批注内容") # range 用 Find 定位或段落 Range
封装进 com_comment.py 即可。
收口后的工具集(全 COM,零 SSE):
.workbuddy/scripts/
├── com_conn.py # 统一连接(_get_word_app,循环 KWPS/Word + 报错提示)
├── com_read_para.py # 读段 / 空格段分析
├── com_insert.py # 插字 / 锚点插字
├── com_fill_blanks.py # 模板填空
├── com_table.py # 定位表 + 读表 + 批量写格 ← 新建
├── com_comment.py # 加批注 ← 新建
├── get_table.py # get_current_table + insert_block(可并入 com_table)
└── scripts_guide.md # 改成纯 COM 版
mcp_client.py 和 word_mcp_server.exe 从主流程删除(保留说明:仅当你哪天要离线批量生成整篇文档且不想引 python-docx 时用)。scripts 目录里那 30 多个 _*.py 实验/探查文件也应清掉(移到 _archive/ 或直接删)。
四、通用铁律(每条都来自踩坑)
- 插入,不替换。 用
InsertAfter / InsertBefore在指定位置塞字,不动原内容;别用range.Text = xxx整体覆盖(会把格式和空格带崩)。填空保留 6 空格、在第 3 空格后插内容(com_fill_blanks已内置)。 - 多格 / 多填空区,从后往前处理。 每插一处,后面的字符位置全变;从后往前才不影响已处理的(
com_fill_blanks已做)。 - 每次改完重新读位置。 调了插入 / 删空格后,段落的
Start/End会变,必须重新doc.Paragraphs(n).Range拿新位置,不能用旧的继续算。 - 段落尾部标记要清。
Range.Text尾巴一定带\r(普通段)/\n(软回车)/\x07(表格单元格)。统一rstrip("\r\n\x0d\x0a\x07"),别只 rstrip\r。 - 控制台中文乱码: 跑脚本前设
PYTHONIOENCODING=utf-8(Git Bash 管道默认 GBK,会炸)。 - 大文档先 sleep 再操作。 几十 MB 的说明文档 Open 后等 2–3 秒再动。
- 批量操作前先 Ctrl+S。 COM 直接改文档,没有「撤销服务器」这层保护;改前存盘,错了能撤。
- Find 用短词。
财建〔2019〕找不到就搜财建〔,全角半角括号差异大。
五、要不要保留 MCP?
一句话:实时编辑场景,不需要。 它给你的 120 个工具里,批注 / 修订 / 查找替换 / 表格,COM 都能做且更稳。唯一 MCP 还有点价值的是「跨平台离线生成整篇文档」(python-docx 模式,不依赖打开 WPS)——但这种用 python-docx 直接写也一样,不绕 MCP。所以主流程纯 COM,MCP 退役。
六、修订(Track Changes)怎么做
24 号笔记折腾的「解压 docx 改 XML 插 <w:ins>」是离线 fragile 路线。实时场景下,COM 一把开关就够:
doc.TrackRevisions = True # 开跟踪更改
# 之后任何 InsertBefore/InsertAfter 都会变成红色修订标记
doc.Revisions.AcceptAll() # 一次性接受(相当于填完模板后落定)
doc.Revisions.RejectAll() # 或全拒
比 XML 手改稳得多,且人工在 WPS 里 Ctrl+Z 也能撤。