auto: sync OpenClaw config 2026-09-17 04:13

This commit is contained in:
2026-09-17 04:13:36 +08:00
parent f6383f5725
commit 376fde448b
27 changed files with 899 additions and 3 deletions
@@ -0,0 +1,52 @@
---
name: fill-official-forms
description: 填写工伤认定/申报审批等官方表格:从材料取证据、只填可核实项、标注缺失并导出 PDF 校验。
---
# 填写官方表格(申报/审批类)
## 适用
用户给出空白表格(.doc/.docx/.xls/.xlsx+ 佐证材料(认定书、病历、证明、截图),要求"帮我填好",
并允许就缺失信息向你提问。
## 步骤
1. **清点两个方向的内容**:列出表格文件与材料文件(`ls -la` / `find`),逐份判断是空白模板、部分已填、还是参考材料。
完成标准:能逐份说出它的用途(必填表 / 模板 / 佐证)。
2. **实测工具链,别信依赖清单**`which soffice pandoc tesseract``python3 -c "import docx, openpyxl"`
缺什么补什么:`sudo apt-get install -y --no-install-recommends libreoffice-calc libreoffice-writer`
`pip3 install --break-system-packages python-docx openpyxl xlrd`
已安装技能文档里的"已安装"表格可能与实际不符,一律以实测为准。
完成标准:`soffice` 可执行,且所需 Python 库都能 import。
3. **提取材料事实**:文字层 PDF 用 `pdftotext -layout`.docx 用 python-docx.xls 用 xlrd;图片/扫描件用
`tesseract`。命令、格式转换与 OCR 失败时的重试写法见 `references/legacy-office-and-ocr.md`
完成标准:每条要填的事实都能指到出处(哪个文件、哪一行/哪一区域)。
4. **先建字段映射,再动手填**:左列=表格字段(定位到单元格坐标或段落/表格序号),右列=取值 + 出处。
取不到出处的字段一律不猜。
5. **回填**Excel 用 openpyxlWord 用 python-docx(写法见 `references/legacy-office-and-ocr.md`)。
- 缺失项写「待补充」并用醒目字体(如橙色加粗)标出,不要留空——留空会被当成漏填。
- 要求本人手写/签名的字段(表格若写明"亲笔书写,不得代写、打印")**不得代填**,在交付说明里点名。
- 只填有据可查的信息:单位名称、日期、经过一律取自材料,不推断、不润色、不补全。
6. **校验再交付**:读回文件确认值落在正确字段(openpyxl / python-docx 重读);再
`soffice --headless --convert-to pdf` 导出预览,用 `pdftotext` 抽查关键字段是否出现在预期位置。
完成标准:抽查字段全部正确,或已修正后复验通过。
7. **交付结构**:原始文件保持不动;填写件放子目录(如 `已填写/`),另附 `预览PDF/``原始模板/`
再写一份说明文件,内容为 ① 已填了什么 ② 逐字段待补清单(指明补在哪个字段)③ 针对性疑问
④ 时限提醒。完成标准:用户不打开表格就知道要补什么、补到哪一格。
## 决策点与坑
- **管辖先确认**:表格的颁发地/受理地若与材料显示的主管地不一致(例如参保证明显示参保地在 A 市,
而用户下载的是 B 市的表格),先请用户确认受理地再填,不要默认手上这套表就是对的。
- **别用打印缩放"修"页数**:给工作表设 `fitToWidth/fitToHeight` 不会减少 PDF 页数,反而改动原表版式;
原始 .xls 本来就跨多页导出属正常,保持原样。
- 合并单元格只需写一个代表格:openpyxl 写合并区左上角,python-docx 里同一合并区的 cells 返回同一对象。
- 材料日期互相对不上时(如病历写"初诊"日期晚于 MRI 日期、正文又写"复诊"),把它列为疑问请用户澄清,
不要自行选一个填进去。
@@ -0,0 +1,98 @@
# 旧版 Office 文件与 OCR 参考
配合 `fill-official-forms` 第 3、5 步使用。以下命令均为实测可用的写法。
## 读取
```bash
# 文字层 PDF:优先用 -layout,保留表格式对齐
pdftotext -layout "证明.pdf" -
# .docx 正文 + 表格
python3 -c "
from docx import Document
d=Document('模板.docx')
for p in d.paragraphs:
if p.text.strip(): print(p.text)
for ti,t in enumerate(d.tables):
print('table',ti,len(t.rows),'x',len(t.columns))
for ri,r in enumerate(t.rows):
print(' r',ri,[c.text[:30].replace(chr(10),'/') for c in r.cells])
"
# 旧版 .xlsopenpyxl 读不了,用 xlrd(含合并单元格)
python3 -c "
import xlrd
wb=xlrd.open_workbook('表.xls', formatting_info=True)
for sh in wb.sheets():
print(sh.name, sh.nrows, sh.ncols, sh.merged_cells)
for r in range(sh.nrows):
print(r, ' | '.join(str(sh.cell_value(r,c)).strip() for c in range(sh.ncols)))
"
```
`pypdf``extract_text()` 对多列表格会把字段顺序打乱(实测:参保证明一行里的险种、单位、缴费额错位),
需要按列对齐时改用 `pdftotext -layout`
## 旧版 .doc / 无法直接解析的文件:先转新格式
```bash
soffice --headless --convert-to docx --outdir /tmp/conv "旧模板.doc"
soffice --headless --convert-to xlsx --outdir /tmp/conv "旧表格.xls"
```
转完再按上面的方式读;.doc 直接当二进制解 UTF-16 只能捞到零散字符串,不可靠。
## 回填写法
```python
# Excel:合并单元格只写左上角
import openpyxl
from openpyxl.styles import Alignment, Font
c = ws["C5"]; c.value = "取值"
c.alignment = Alignment(horizontal="left", vertical="center", wrap_text=True)
c.font = Font(color="FFC000", bold=True) # 待补项用橙色加粗标出
# Word:写入第一段 run,清掉多余 run,避免残留占位文字
def set_cell(cell, text):
p = cell.paragraphs[0]
if p.runs:
p.runs[0].text = text
for r in p.runs[1:]: r.text = ""
else:
p.add_run(text)
for extra in cell.paragraphs[1:]:
for r in extra.runs: r.text = ""
```
合并单元格在同一合并区里的多个 `cells` 是同一个对象,写一次即可;但跨行拆分出来的格子要按行分别写。
## 导出校验
```bash
soffice --headless --convert-to pdf --outdir 预览PDF *.xlsx *.docx
pdfinfo "预览PDF/xx.pdf" | grep Pages
pdftotext -f 1 -l 1 "预览PDF/xx.pdf" - | head -40 # 抽查关键字段落在正确位置
```
注意:给工作表设 `fitToWidth=1 / fitToHeight=0` **不会**减少 PDF 页数(实测设置前后页数不变),
不要再试图用打印缩放"整理"页数,也不要因此改动原表版式。
## OCR(图片 / 扫描件)
```bash
tesseract 图片.jpg - -l chi_sim+eng --psm 6 # 常规截图/竖版文档
```
- 截图类(考勤、列表)用 `--psm 6` 通常可直接读出。
- **地图类图片实测无输出**:直接对地图截图 OCR 得到空白。可行的重试是先放大再识别:
```bash
python3 -c "
from PIL import Image
im=Image.open('路线图.jpg'); im=im.convert('L').resize((im.width*3, im.height*3), Image.LANCZOS)
im.save('/tmp/up.png')"
tesseract /tmp/up.png - -l chi_sim+eng --psm 11
```
放大 3 倍 + `--psm 11` 后能读出部分地名,但结果零散、含大量乱码——**只用于定位线索,不能作为填报依据**。
地图/路线内容仍需向用户确认。
- 输出重定向到文件比管道到 stdout 更稳,便于反复查看。
@@ -0,0 +1,38 @@
---
name: "cross-channel-send"
description: "消息发不出去/跨频道发送:Cross-context messaging denied、飞书主动私聊不可用时,用 CLI openclaw message send 跨频道投递并核实送达。"
---
# 跨频道发送(cross-channel send
用于「这条消息发不出去」「改投钉钉/飞书」「飞书私聊推不动」这类场景:把消息实际送到**当前会话未绑定**的频道,并拿到可核实的送达证据。与定时提醒无关(提醒的路由写在 cron `delivery` 上,见 `scheduled-reminders`)。
## 事实基础(本机 2026-09-16 实测,OpenClaw 2026.8.1
- `message(action=send)` **受会话绑定频道限制**:主会话绑 feishu 时,指定 `channel="dingtalk-connector"` 直接被拒:
`Cross-context messaging denied: action=send target provider "dingtalk-connector" while bound to "feishu"`
这是策略拒绝,不是投递失败——**原样重试无效**,换 CLI。
- 同一时刻 CLI 跨频道发送成功(`--channel` 接受插件频道名):
```bash
openclaw message send --channel dingtalk-connector --target 0464031658857345 --message "..."
# → ✅ Sent via DingTalk. Message ID: card_...
```
- 飞书「机器人主动向用户发私聊」当前不可用,报 `Feishu send failed: Sending messages to users is temporarily unavailable.`**群 / chat_id 可达、open_id 私聊不可达**,可复现;此时按第 3 步改投钉钉。
- 该飞书故障**换 CLI 也一样报错**(`openclaw message send --channel feishu ...` 同样失败)→ 属飞书侧限制,不是 CLI 或工具层问题。
## 步骤
1. 先用 `message(action=send)` 发当前频道。读错误分类:
- `Cross-context messaging denied` → 走第 2 步(跨频道)。
- `Sending messages to users is temporarily unavailable` → 走第 3 步(换通道)。
2. 跨频道补发:`openclaw message send --channel <频道> --target <目标id> --message "..."`。
目标 id 用该频道已知可达的会话:钉钉 `0464031658857345`;飞书 DM chat_id 见 `scheduled-reminders` 的「投递通道」。发送后回读输出确认 `✅ Sent via ...` + Message ID。
3. 原频道本身不可达时(如飞书主动私聊),把结果改投用户实际能收到的通道(钉钉),并在正文里说明「原频道故障 + 已改投」,让用户知道换通道的原因。
4. 核对送达:只有 CLI 输出 `✅ Sent via ...` 或 `message` 工具返回成功才算发出;任何 `Feishu send failed` / `denied` 都不算,不要在回复里声称已送达。
5. 原频道恢复或用户要求切回时,改回原频道,并撤掉临时的替代通道推送,避免同一条内容双通道重复打扰。
## 注意
- 跨频道发送属「离开本机的对外发送」,只在用户任务范围内做。
- 诊断类测试消息必须标注「测试/请忽略」,且**只发一次**——用飞书群做过一次诊断后不要再往同一群发第二条。
- 频道名以插件注册名为准(`dingtalk-connector`、`feishu`、`openclaw-weixin`);上面写死的目标 id 是本机惯例值,换部署要重新确认。
@@ -28,8 +28,11 @@ description: "创建/管理定时提醒(每周/每天/睡前等周期或一次
- **飞书目标必须用 DM 会话 chat_id**`oc_8eecfa0e1cc185c1cb19b4f3950cbc52`(杨轩的飞书私聊)。
- **不要用用户 open_id**`ou_28124d6721134f0e31d6122d30088418`):2026-09-16 实测 cron 投递报 `Feishu send failed: Sending messages to users is temporarily unavailable.`,判为 not-delivered;同一时刻改投上面的 chat_id 即刻成功。错误文案含「temporarily」,条件可能变化,但先用 chat_id 就不必试错。用 `conversations_list(channel="feishu")` 可复核 DM 目标。
- **可达性分两类**:同日实测飞书**群 / chat_id 可达**,而**机器人主动向用户私聊(open_id)不可达**、可复现——该错误文案里虽然带「temporarily」,但先按不可达处理,别计划性地依赖飞书私聊推送。
- **换通道只动 delivery**`--agent main`、时间、打卡机制等其余规则一律不变。
- **通了没有要实测,不要推断**:可临时建一条一次性测试任务,用 `automations run <jobId>` 立即跑一次,看 `state.lastDelivered` / `lastDeliveryError`,验证完把测试任务删掉。
- **测试只发一次**:验证投递时按上面的 run 方式做,不要额外往群或私聊手发多条诊断消息;确需手发时正文标注「测试/请忽略」。
- **cron 的 delivery 与手动发消息是两条路**:本 skill 的投递只写在 delivery 参数上;「消息发不出去 / 要跨频道手动投递」另见 `cross-channel-send`
## 创建一条周期提醒
@@ -95,11 +98,13 @@ openclaw cron add "打卡检查" \
只改周期任务的 payload 就完事,会一直错下去;要配一条恢复任务:
1.`automations update <jobId>`(或 `openclaw cron edit <id> --command '...'`)换成当晚版本,回读 `automations get <jobId>` 确认文案已生效
2. 同一步里建一条一次性任务,在次日合适时间把 payload 改回标准版本。它跑在 isolated 会话、**没有本次对话的记忆**,所以 message 必须写全 jobId 与目标文案原文,并注明改完回读校验、成功即回 `NO_REPLY`
3. 恢复任务不需要给人看:按核心规则加 `--no-deliver`
1.`automations update <jobId>`(或 `openclaw cron edit <id> --command '...'`把周期任务换成当晚版本。
2. 回读 `automations get <jobId>` 确认文案已生效,再继续
3. 同期建一条一次性任务,在次日合适时间把 payload 改回标准版本。它跑在 isolated 会话、**没有本次对话的记忆**,所以 message 必须写全 jobId 与目标文案原文,并注明改完回读校验、成功即回 `NO_REPLY`
4. 恢复任务不需要给人看:按核心规则加 `--no-deliver`
## 参考
- 生日提醒(农历换算、每年重设、一次性任务):见 `birthday-reminder` skill
- 消息发不出去 / 跨频道手动投递:见 `cross-channel-send` skill
- 完整钉钉推送规范:`workspace/TOOLS.md`「📢 钉钉推送统一规范」