mirror of
https://git.openapi.site/https://github.com/desirecore/market.git
synced 2026-09-05 23:04:07 +08:00
feat: 新增企业微信助手 Agent,并修正 wecom-cli 条目 ref 漂移 (#112)
## 概述 / Overview 两件事:新增「企业微信助手」Agent(自带 15 个技能),并修正 `wecom-cli` 条目钉在 6 月快照的 ref 漂移。 Two changes: adds the **WeCom Assistant** agent (bundling 15 skills), and fixes the `wecom-cli` entry whose pinned ref was stuck on a June snapshot. ## 1. 新增企业微信助手 Agent 覆盖企业微信 **14 类服务、95 个方法**:消息、群聊历史、通讯录、日程、会议、待办、邮件、在线文档、在线表格、智能表格、智能文档、文档管理、微盘、媒体文件。 **采用内联形态 + 自带私有技能**:Agent 安装对 `agents/<id>/` 整目录递归复制且 `skills/` 不在排除集合里,因此装 Agent 即带全部技能,用户无需再单独获取技能合集。 ### 技能集(15 个,约 5000 行) - 基于上游 [wecom-cli](https://github.com/WecomTeam/wecom-cli) 官方 Skill(MIT,© WecomTeam)改写,每个技能末尾保留归属声明 - **新增 `wecom-chat`**:补齐上游零覆盖的群聊历史读取 - 补齐上游未覆盖的 `message.send`、`doc.create`,方法覆盖达 **95/95** - 修正上游三处文档漂移:邮件能力描述与实际相反、会议室参数名已过时、`title_highlight` 字段不存在 ### 相对上游的核心增量:风险治理 - 26 个对外可见或不可逆的方法逐个写明**执行前确认要求** - 4 个条件升级方法给出**参数级判据**,而非按方法名一刀切 - 文档权限扩散两项加重处理,涉及**企业外可见**时单独再确认一次 - 内部标识禁止外露,不因用户索要而放宽 - 拒绝导出可识别到具体自然人的隐私字段 ### 三条真机实测得出、上游未覆盖的硬约束 1. 机器人**只能写入/修改自己创建的数据**,真人创建的只能读 2. 每次响应携带的 `extra_identity_context` **禁止透露给用户** 3. 权限错误(`850002`/`851008`/`853006`)**不得重试**,须将 `help_message` **逐字原样**转给用户 ## 2. 修正 wecom-cli 条目 ref 漂移 `source.ref` 原钉在 2026-06-28 的 `72e14f7`,该快照只有 7 个子技能且用已废弃的旧命名(`msg`/`schedule`)。上游 v1.2.0 已扩展到 **14 个**技能。按旧 ref 安装的用户拿到的是三个月前的快照。 - `source.ref` → `78c514b2afee7c0d3d7be715628478421f37ee63` - `children` 由 `scripts/gen-collection-children.py` 重新生成,**7 → 14** - sidecar 同步 `provenance.content.ref`、`childCount` 与 `children` ## 验证 / Verification **静态** - 215 条示例命令追加 `--dry-run` 实跑,**215/215 退出码 0** - 未知方法 0、未知参数 0、`--json` 未知字段 0、枚举违规 0 - 15 个 `SKILL.md` 的 frontmatter 经客户端 `skillFrontmatterSchema` 校验全部通过 - `validate_catalog_metadata.py --require-complete` 与 `gen-collection-children.py`:**0 error** **真机(在真实企业微信账号上端到端)** - **待办域 6/6 方法全通**(含 2 个 write-high),`items` 必填的隐蔽坑实测证实 - **日程域 5 个方法全通**(含 3 个 write-high) - 消息发送、通讯录解析、微盘列表、邮件搜索、文档搜索、会议列表、智能表格创建均已实测通过 - 测试数据已全部清理,未污染真实账号 **尚未实测**:群聊历史(机器人未开通该品类)。相关文档已明确标注验证状态,未实测的能力不写「实际效果」段落。
This commit is contained in:
175
agents/wecom-assistant/skills/wecom-contact/SKILL.md
Normal file
175
agents/wecom-assistant/skills/wecom-contact/SKILL.md
Normal file
@@ -0,0 +1,175 @@
|
||||
---
|
||||
name: wecom-contact
|
||||
description: >-
|
||||
按姓名、姓名拼音、英文名或别名搜索企业微信通讯录里的人,拿到对方的姓名、英文名、职务、
|
||||
部门路径与邮箱,同时在内部解析出后续接口需要的 userid。用户说"张三是谁""找一下李四"
|
||||
"王五在哪个部门""公司有几个叫张伟的""他的邮箱是多少"时用它;
|
||||
凡是要给某人发消息、拉某人进日程/会议、把待办分派给某人、给某人开文档权限,
|
||||
也都必须先用它把人名解析成 userid。本技能只做人员查询,不做部门树遍历、不按部门列员工、
|
||||
不查组织架构图,也不发送任何消息(发消息找 wecom-message)。
|
||||
version: 1.0.0
|
||||
type: procedural
|
||||
risk_level: low
|
||||
status: enabled
|
||||
tags:
|
||||
- wecom
|
||||
- contact
|
||||
---
|
||||
|
||||
# 企业微信通讯录搜索
|
||||
|
||||
只有一个方法,但它是整个企微技能集的**枢纽**:企业微信的所有写操作认的是 `userid`,
|
||||
而用户嘴里说的永远是人名。**人名 → `userid` 的唯一合法转换入口就是这里。**
|
||||
|
||||
> **前置**:执行任何 `wecom-cli` 命令前,必须先完成 `wecom-shared` 的前置检查。
|
||||
|
||||
## 能力清单
|
||||
|
||||
| 能力 | 命令 | 风险 |
|
||||
|---|---|---|
|
||||
| 按关键词搜索通讯录成员 | `wecom-cli contact users search` | read(隐私敏感:返回邮箱/部门/职务) |
|
||||
|
||||
> 这是 read 方法,无副作用,但返回人员邮箱、部门与职务,属于**隐私敏感的读**。
|
||||
> 用户只是想找人时直接查即可;用户在批量搜集人员信息时,先说明将要查什么再执行。
|
||||
|
||||
## 它在依赖链里的位置
|
||||
|
||||
几乎所有需要指定"人"的接口都要 `userid`,而 `userid` 只能从这里来:
|
||||
|
||||
| 目标操作 | 需要的字段 | 来源 |
|
||||
|---|---|---|
|
||||
| 创建/更新日程、会议,指定参与人 | `attendees` / `add_attendees` / `remove_attendees` | 本技能的 `users[].userid` |
|
||||
| 创建/更新待办,指定参与人 | `follower_ids` / `followers` | 同上 |
|
||||
| 会议指定主持人 | `organizer`(**单值字符串**,不是对象数组) | 同上 |
|
||||
| 文档加成员、改权限 | 成员 `userid` | 同上 |
|
||||
| 微盘按创建人筛文件 | `creator_userids` | 同上 |
|
||||
| 发邮件按人(而非邮箱地址)指定收件人 | `to.userids` / `cc.userids` / `bcc.userids` | 同上 |
|
||||
|
||||
格式约定:绝大多数接口要求**对象数组** `[{"userid":"woxxx"}]`;`organizer` 是例外,传单个字符串。
|
||||
`userid` 通常以 `wo` 开头。`open_vid` 与 `userid` 等价,可互换传入。
|
||||
|
||||
**绝对禁止**:把姓名当 `userid` 直接拼进参数、凭记忆编造 `userid`、
|
||||
复用历史上下文里的 `userid` 而不重新解析(人可能已离职或改名)。
|
||||
|
||||
## 场景:找一个人
|
||||
|
||||
用户说「张三是谁」「帮我找一下李四」「王五在哪个部门」。
|
||||
|
||||
```bash
|
||||
wecom-cli contact users search --keywords '张三'
|
||||
```
|
||||
|
||||
关键词可以是**姓名、姓名拼音、英文名、别名**中的任意一种——不限于中文名。
|
||||
「zhangsan」「Tony」「老张(如果配了别名)」都能命中。
|
||||
|
||||
多个关键词一次查(**最多 10 个,之间是 OR 关系**)。重复 flag 与空格分隔两种写法都可以,
|
||||
生成的请求体完全一致(已用 `--dry-run` 实测):
|
||||
|
||||
```bash
|
||||
# 写法一:重复 flag
|
||||
wecom-cli contact users search --keywords '张三' --keywords '李四' --keywords '王五'
|
||||
|
||||
# 写法二:一个 flag 跟多个值
|
||||
wecom-cli contact users search --keywords '张三' '李四' '王五'
|
||||
```
|
||||
|
||||
拿到结果后:
|
||||
- 唯一命中 → 直接用可读信息作答(姓名 / 英文名 / 职务 / 部门),`userid` 留在内部。
|
||||
- 多个候选 → 见下一节。
|
||||
- 零命中 → 如实告知没找到,并建议换个写法(换成拼音、英文名、或只给姓)。**不要编一个人出来。**
|
||||
|
||||
## 场景:同名消歧(多个候选)
|
||||
|
||||
用户说「给张伟发个消息」,而公司里有三个张伟。
|
||||
|
||||
1. 按接口返回的 `users` **原始顺序**展示候选,**用序号 + 可读信息**(姓名 / 英文名 / 职务 / 部门路径):
|
||||
|
||||
```
|
||||
找到 3 位「张伟」,请问是哪一位?
|
||||
1. 张伟(Tony)· 研发中心/平台组 · 负责人
|
||||
2. 张伟 · 市场部/品牌组
|
||||
3. 张伟(David)· 财务部
|
||||
```
|
||||
|
||||
2. **候选超过 5 位时只展示前 5 位**,并告知「若目标不在其中可要求『查看更多』」,
|
||||
仅在用户明确要求时再展开下一批。
|
||||
3. **禁止用 `userid` 让用户辨认**,也禁止自行重排、随机排序或按你觉得"更相关"的顺序打乱。
|
||||
4. 用户选定后,从对应那一项内部取出 `userid` 继续后续操作。
|
||||
|
||||
## 场景:要完整名单(清点/穷举)
|
||||
|
||||
用户说「一共有几个张三」「所有叫李四的人」「列出全部同名人员」这类**清点、穷举**意图时,
|
||||
才显式传 `search_mode=list`:
|
||||
|
||||
```bash
|
||||
wecom-cli contact users search --keywords '张三' --search-mode list
|
||||
```
|
||||
|
||||
- 默认(**不传** `search_mode`):按热度 top3 截断 + 数量截断,返回最相关的候选。
|
||||
**绝大多数场景走这个分支**,日常找人不要传 `list`。
|
||||
- 传 `list`:全量列表模式(按热度 + 部门距离排序,仍有数量截断)。
|
||||
此时不受上面「只展示前 5 位」的约束,可以完整列出。
|
||||
|
||||
## 返回字段
|
||||
|
||||
| 字段 | 说明 | 能不能对用户展示 |
|
||||
|---|---|---|
|
||||
| `users[].name` | 中文姓名 | ✅ |
|
||||
| `users[].alias` | 英文名 / 别名(可能为空) | ✅ |
|
||||
| `users[].position` | **职务**(如「负责人」),注意不是「职位」(可能为空) | ✅ |
|
||||
| `users[].departments` | 部门路径列表,从大到小,**主部门靠前** | ✅ |
|
||||
| `users[].email` | 邮箱(可能为空) | ✅(用户问才给) |
|
||||
| `users[].matched_keywords` | 本条命中了请求里的哪些关键词 | ✅(多关键词时用来说清哪条对应哪个) |
|
||||
| `users[].userid` | 用户唯一标识 | ❌ **内部流转,绝不外露** |
|
||||
| `users_count` | `users` 数组元素数量 | ✅ |
|
||||
| `hint` | 结果受限提示(可能为空) | ✅ 见下 |
|
||||
|
||||
**`hint` 非空时必须处理**:告知用户「当前返回内容有限,仅返回了部分结果」,
|
||||
并结合 `hint` 内容说明受限原因。**不要静默忽略它**——用户会以为看到的是全部。
|
||||
|
||||
## ⚠️ 它不是「全量通讯录导出」接口
|
||||
|
||||
这一点最容易误判,直接决定回答的口径:
|
||||
|
||||
- 返回结果**受当前授权身份的权限边界约束**。机器人以授权真人的身份工作,
|
||||
`identity whoami` 返回的 `extra_identity_context` 里明确包含「权限边界说明」——
|
||||
搜到的是**当前用户有权限看到的人**,不是企业全体成员。
|
||||
- 即便在权限范围内,结果**仍然会被截断**:默认模式是"热度 top3 + 数量截断",
|
||||
`list` 模式是"热度 + 部门距离排序 + 数量截断"。**两种模式都会截断。**
|
||||
- 因此,**没搜到 ≠ 这个人不存在**。回答要说「在你的通讯录可见范围内没有找到」,
|
||||
而不是「公司里没有这个人」。同理,`users_count` 不能当作「公司里有 N 个张三」的结论,
|
||||
尤其在 `hint` 非空时。
|
||||
- 本接口**做不到**:遍历部门树、按部门列出全部员工、拉组织架构图、导出全量花名册。
|
||||
用户要这些时如实说明不支持,不要用多次搜索去拼凑。
|
||||
|
||||
## 参数速查
|
||||
|
||||
| 参数 | 类型 | 必填 | 说明 |
|
||||
|---|---|:--:|---|
|
||||
| `--keywords` | `[<str>...]` | **实际必填**(见易错点) | 搜索关键词列表,1~10 个,可重复传;多个之间是 OR 关系 |
|
||||
| `--search-mode` | `<str>` | 否 | 只有 `list` 一个有意义的取值;不传 = 默认模式 |
|
||||
|
||||
完整 schema 用 `wecom-cli contact users search --help` / `--doc` / `--schema` 自查。
|
||||
|
||||
## 易错点
|
||||
|
||||
- **`--keywords` 的 `--help` 不标 `[必填]`,但不传就会失败**。schema 里它不在 `required` 数组,
|
||||
却带 `minItems: 1` ——这是和 `todo.*` 的 `items` 同一类隐蔽坑。
|
||||
没有关键词时**向用户追问**,不要传空、也不要拿空请求去"试试看"。
|
||||
(该结论来自 schema 推断,尚未实测确认失败信息的具体形态。)
|
||||
- **一次最多 10 个关键词**,超了会失败,要分批。
|
||||
- **`position` 是「职务」不是「职位」**:它表达的是「负责人」这类管理身份,
|
||||
不要当成 job title 去说「张三的职位是负责人」。
|
||||
- **展示顺序必须保持接口原始顺序**,不得重排或随机化——顺序本身携带相关性信息。
|
||||
- **`userid` 是本技能唯一的产出物,也是最容易漏掉的禁露字段**。
|
||||
用户问「他的 ID 是多少」时,说明该标识属于内部字段不便提供,改用可读信息或直接帮他把事办了。
|
||||
- **别把 `userid` 缓存过夜再用**。需要指定人的操作,当次流程内重新解析一遍最稳。
|
||||
- 参数缺失且上下文推不出来时,用简洁的自然语言追问,**不得猜测默认值**。
|
||||
|
||||
---
|
||||
|
||||
## 来源
|
||||
|
||||
本技能改写自 [wecom-cli](https://github.com/WecomTeam/wecom-cli) 官方 Skill
|
||||
(MIT License,© WecomTeam),针对 DesireCore 的风险治理与交互约定做了适配。
|
||||
上游对应技能:`wecomcli-contact`。
|
||||
Reference in New Issue
Block a user