企微组织架构识别
清海接入企业微信环境的第一步:通过企微通讯录 API 认识公司每个人,为后续所有业务场景提供身份基础。 角色维度的会话旅程见 角色场景,能力定义见 Skill 清单。
背景与现状
企业微信(WeCom)是腾讯旗下的企业协同工具,在制造业、零售、金融等传统行业有较高渗透率。清海需要通过企微通讯录建立组织感知能力。与飞书/钉钉的核心差异在于:企微的消息回调支持两种模式——普通自建应用(XML 格式,FromUserName)和智能机器人(JSON 格式,from.userid);通讯录变更同样支持事件回调,但事件字段体系与钉钉有所不同。
| 维度 | 说明 |
|---|---|
| 数据源 | 企微通讯录 API(qyapi.weixin.qq.com) |
| 接入方式 | 企业自建应用(推荐)或智能机器人 |
| 用户识别 | 自建应用消息 FromUserName(XML)、智能机器人 from.userid(JSON),均为企业内 userid |
| 同步策略 | 每日全量同步一次 + 通讯录变更事件实时补偿 |
| 存储 | MongoDB unified_users 集合 + PersonIndex 内存索引 + 本地缓存 data/cache/wecom_contacts.json |
企微 API 核心鉴权
| 参数 | 说明 |
|---|---|
corpid | 企业 ID,管理后台「我的企业 → 企业信息」 |
corpsecret | 应用密钥,各应用独立(「应用管理 → 应用」中查看) |
access_token | 调用凭证,通过 corpid + corpsecret 换取,有效期 7200 秒,每应用独立获取 |
GET https://qyapi.weixin.qq.com/cgi-bin/gettoken?corpid=ID&corpsecret=SECRET
返回:{ "access_token": "xxx", "expires_in": 7200 }⚠️
access_token有效期 2 小时,建议提前 5 分钟刷新并全局缓存,避免每次请求前重复获取。
已实现 vs 待实现
| 能力 | 状态 | 说明 |
|---|---|---|
| 企微通讯录同步 | ⬜ 待实现 | 需新建 WeComSync.sync_contacts() |
| PersonIndex 内存索引 | ✅ 复用 | 已有实现,扩展 channels.wecom 即可 |
| PersonContextResolver | ✅ 复用 | 已有实现 |
| 身份自动识别 | ⬜ 待实现 | 需适配 FromUserName / from.userid → userid 匹配 |
| search_contacts 工具 | ✅ 复用 | 底层 PersonIndex 相同,无需改动 |
| 别名学习 / 平台绑定 | ✅ 复用 | save_contact_alias + link_platform_identity |
| 通讯录变更事件接收 | ⬜ 待实现 | 企微特有,需配置回调服务器 |
| access_token 自动刷新 | ⬜ 待实现 | 企微特有,飞书/钉钉已有类似逻辑可参考 |
| 权限过滤 | ⬜ 待实现 | 与飞书版本一致,按角色分层 |
当前设计方式
本场景组织架构识别在系统层自动完成,AI 通过通用工具
search_contacts查询人员信息。
企微消息进来
↓
avatar_core.identify_user()
↓
PersonContextResolver.resolve_by_channel("wecom", userid) ← 从 unified_users 读组织信息
↓
system prompt Block 1: "你正在和张三对话,他是技术部的后端工程师" ← 身份注入
↓
AI 对话(如果用户问组织问题)
↓
AI 调用 search_contacts(query="技术部", department=...) ← 组织查询工具
↓
PersonIndex.search() → 返回结果场景与能力总览
企微组织架构识别覆盖 4 大会话场景:
场景 → 能力映射表
| 场景 | 触发方式 | 需要的能力 | 企微 API |
|---|---|---|---|
| 身份自动识别 | 收到任意消息时自动触发 | 通讯录同步 + PersonIndex + userid 解析 | GET /cgi-bin/user/get?userid=USERID |
| 组织架构查询 | 用户主动询问 | PersonIndex 部门查询 | GET /cgi-bin/department/list?id=1 |
| 人员信息查询 | 用户主动询问 | PersonIndex 人员查找 + PersonContext | GET /cgi-bin/user/list?department_id=1 |
| 人员变动感知 | 企微事件回调(实时)+ 每日全量同步兜底 | 通讯录变更事件 + 定时同步 | Event=change_contact,ChangeType=create_user/update_user/delete_user |
场景一:身份自动识别
核心价值:员工在企微发消息时,清海自动知道对方是谁,带入身份认知。
两种接入模式对比
企微有两种主要接入模式,消息格式不同:
| 接入模式 | 消息格式 | 用户 ID 字段 | 推荐场景 |
|---|---|---|---|
| 自建应用(配置接收消息) | XML | FromUserName | 通用场景,权限更完整 |
| 智能机器人(AI 机器人) | JSON | from.userid | 对话交互场景,配置简单 |
两种模式的 userid 值相同,均为企业内唯一标识。
识别流程(自建应用模式)
消息 ID 字段
自建应用(XML):
<xml>
<ToUserName><![CDATA[企业微信CorpID]]></ToUserName>
<FromUserName><![CDATA[zhangsan]]></FromUserName> <!-- 即 userid -->
<CreateTime>1348831860</CreateTime>
<MsgType><![CDATA[text]]></MsgType>
<Content><![CDATA[消息内容]]></Content>
<MsgId>1234567890123456</MsgId>
<AgentID>1</AgentID>
</xml>智能机器人(JSON):
{
"msgid": "CAIQrcjMjQYY...",
"aibotid": "AIBOTID",
"chattype": "single",
"from": { "userid": "zhangsan" },
"response_url": "RESPONSEURL",
"msgtype": "text",
"text": { "content": "消息内容" }
}存储字段(unified_users.channels.wecom)
来自 GET https://qyapi.weixin.qq.com/cgi-bin/user/get?access_token=TOKEN&userid=USERID:
| 企微 API 字段 | 存储位置 | 用途 |
|---|---|---|
userid | channels.wecom.userid | 消息身份匹配主键(= FromUserName / from.userid) |
name | display_name | 显示名称 |
department | channels.wecom.department | 所属部门 ID 列表 |
position | channels.wecom.position | 职位 |
is_leader_in_dept | channels.wecom.is_leader | 是否部门主管(数组,对应每个部门) |
direct_leader | channels.wecom.direct_leader | 直属上级 userid(数组,支持多上级) |
status | channels.wecom.status | 1=已激活,2=已禁用,4=未激活,5=退出企业 |
telephone | channels.wecom.telephone | 手机号(需申请权限) |
hire_date | channels.wecom.hire_date | 入职时间 |
关键约束
FromUserName/from.userid在企业内唯一且稳定,识别准确率 100%- 企微回调消息经过 AES 加密,需先用
Token + EncodingAESKey解密 status=5(退出企业)→is_active=False,消息静默处理- 企微
response_url(智能机器人模式)每次仅可使用一次,有效期 1 小时
场景二:组织架构查询
核心价值:自然语言查询公司组织结构,不用打开企微通讯录。
典型对话
| 用户问法 | 清海应答 | 需要的能力 |
|---|---|---|
| "公司有哪些部门" | 列出所有部门(名称 + 人数) | 部门列表查询 |
| "技术部有谁" | 列出技术部全部成员(姓名 + 职位) | 部门成员查询 |
| "技术部下面有几个组" | 列出子部门结构 | 子部门ID列表 + 递归 |
| "前端组几个人" | 返回人数 + 成员列表 | 部门成员查询 |
企微 API 数据来源
| 查询类型 | 企微 API | 关键参数 |
|---|---|---|
| 全量部门列表 | GET /cgi-bin/department/list | id=1(根部门);返回所有层级,同步用 |
| 子部门 ID 列表 | GET /cgi-bin/department/getid | 性能更优,仅返回 id/parentid/order |
| 部门成员详情 | GET /cgi-bin/user/list | department_id,支持 fetch_child=1 包含子部门成员 |
| 部门返回字段 | — | id(整数)、name、name_en、department_leader(数组)、parentid、order |
| 成员返回字段 | — | userid、name、position、department(数组)、direct_leader、is_leader_in_dept |
关键约束
- 企微根部门
id = 1(与钉钉相同,飞书是"0") - 部门名支持模糊匹配:「技术」→「技术部」
- 兼职/多部门人员以
department[0]主部门为准 department/list接口性能较低,建议只在同步时调用;实时查询走 PersonIndex 内存
场景三:人员信息查询
核心价值:快速了解某人的部门、职位、上下级关系。
典型对话
| 用户问法 | 清海应答 | 需要的能力 |
|---|---|---|
| "张三是什么部门的" | 张三的部门 + 职位 | search_contacts(query="张三") |
| "张三的上级是谁" | direct_leader 字段直接返回上级 userid | query_person_relations |
| "李四管哪些人" | 李四的直属下属列表 | PersonIndex 逆向关联查询 |
| "小明是谁" | 模糊匹配结果(可能多个) | search_contacts(query="小明") |
上级关系查询流
关键约束
direct_leader字段支持多上级(数组),同步时保存完整列表is_leader_in_dept为数组,与department字段一一对应,标识该用户在各部门中是否为主管- 人名模糊匹配:PersonIndex 支持部分名、别名
场景四:人员变动感知
核心价值:实时感知入职、离职、调岗,保持组织数据准确。
变动类型与处理
| 变动类型 | 企微事件 (Event=change_contact) | ChangeType | 清海处理 |
|---|---|---|---|
| 新员工入职 | 成员变更 | create_user | 自动创建 unified_users 记录 + 更新 PersonIndex |
| 员工信息变更 | 成员变更 | update_user | 更新字段,保留别名等非企微数据 |
| 员工离职/退出 | 成员变更 | delete_user | is_active=False + 消息静默 |
| 新增部门 | 部门变更 | create_party | 触发该部门成员同步 |
| 部门信息变更 | 部门变更 | update_party | 更新部门名/层级 + 重建索引 |
| 部门删除 | 部门变更 | delete_party | 将该部门成员设为无部门 + 等待全量同步 |
| 标签成员变更 | 标签变更 | update_tag | 更新标签关联,按需更新 PersonIndex |
事件推送格式(加密 XML)
企微事件回调采用 XML 格式,且消息内容经过 AES 加密:
<xml>
<ToUserName><![CDATA[企业微信CorpID]]></ToUserName>
<Encrypt><![CDATA[加密内容,解密后得到事件 XML]]></Encrypt>
</xml>解密后的成员新增事件示例:
<xml>
<ToUserName><![CDATA[企业微信CorpID]]></ToUserName>
<FromUserName><![CDATA[sys]]></FromUserName>
<CreateTime>1403610513</CreateTime>
<MsgType><![CDATA[event]]></MsgType>
<Event><![CDATA[change_contact]]></Event>
<ChangeType>create_user</ChangeType>
<UserID><![CDATA[zhangsan]]></UserID>
<Name><![CDATA[张三]]></Name>
<Department><![CDATA[1,2]]></Department>
<Position><![CDATA[后端工程师]]></Position>
</xml>双重保障策略
回调服务配置
企微管理后台:应用 → 接收消息 → 设置API接收,填写 URL / Token / EncodingAESKey,勾选「通讯录变更」事件类型。
| 配置项 | 说明 |
|---|---|
| URL | 公网可访问的 HTTPS 回调地址 |
| Token | 用于计算签名(自定义字符串) |
| EncodingAESKey | 43 位字符,用于 AES 消息加密/解密 |
关键约束
- 企微通讯录数据为权威源(
authoritative_sync),覆盖推断和观察数据 - 同步时保留
aliases、平台绑定等非企微来源数据 - 企微回调消息必须验证签名后才能处理(防伪造)
- 事件推送的
UserID(delete_user事件)可能只有 userid,需结合缓存还原完整信息
角色权限与场景覆盖
不同角色在各场景中的可见范围不同,详见 角色场景。
角色 × 场景矩阵
| 场景 | 老板 | 部门负责人 | 员工 |
|---|---|---|---|
| 身份自动识别 | 全员信息 | 全员信息 | 仅自己(系统自动) |
| 组织架构查询 | 全公司架构 + 人员明细 | 全公司架构 + 本部门人员明细 | 全公司架构(公开信息) |
| 人员信息查询 | 全员详细信息 | 本部门个人信息 + 其他部门公开信息 | 仅自己的信息 |
| 人员变动感知 | 全公司变动 | 本部门变动 | 仅自己相关 |
⚠️ 当前状态:权限过滤尚未实现,所有用户可查所有数据。
与飞书/钉钉版本的核心差异
| 维度 | 飞书 | 钉钉 | 企业微信 |
|---|---|---|---|
| 用户主键 | open_id(ou_ 开头) | userid(senderStaffId) | userid(FromUserName / from.userid) |
| Bot 消息格式 | JSON | JSON | XML(自建应用)/ JSON(智能机器人) |
| 消息加密 | 无加密 | 无加密 | AES 加密(必须先解密) |
| Token 获取 | POST + app_id + app_secret | GET/POST + AppKey + AppSecret | GET + corpid + corpsecret,每应用独立 token |
| Token 有效期 | 最长 2 小时 | 2 小时 | 7200 秒(2 小时) |
| 部门根节点 | parent_department_id=0 | dept_id=1 | id=1 |
| 部门主管字段 | 通讯录 leader_user_id | leader_in_dept | department_leader(数组)+ is_leader_in_dept(数组) |
| 直属上级字段 | 需额外查询 | manager_userid | direct_leader(数组,支持多上级) |
| 事件变更同步 | 定时轮询(30 分钟) | 事件订阅(实时) | 事件回调(实时) |
| 通讯录事件字段 | — | user_add_org / user_leave_org | change_contact + ChangeType |
复用能力与全局约束
复用的现有能力、全局约束详见 Skill 清单 — 复用的现有能力 和 Skill 清单 — 全局约束。