每个新员工、每个新数字人先登记自己的档案,再创建工作坊会话。目录支持任意人数,不按姓名或两位现有机器人的角色分配固定槽。登记资料与平台认证分开:本流程不创建登录账号,accountAuthenticated 始终为 false。
默认采用 SKILL.md 的“首次入库或上传前确认称呼”流程。发送者登记、显示称呼、通讯录实名是三个独立事实。没有目录权限时照常询问并保存昵称,不把通讯录查询作为业务前置条件。
employee-name 只接受当前可信消息登记的员工profileKey和其已有会话;服务端校验服务owner、工作坊参与键派生关系及Agent。读取只用data:read;保存称呼或明确跳过使用既有ingest:write,无需identity:write或运维凭据。私有运行配置 workshopDisplayNames 按bot作用域+发送者摘要存储,跨工作坊复用,同名不合并。更新只涉及称呼注册表及操作审计,不改员工/Agent档案、会话索引、报告、项目或旧归属快照。
promptState=not_asked且没有可用称呼才询问;provided直接复用,skipped继续业务不再追问。用户主动改名时可更新。自报姓名与昵称均标source=self_reported、nameVerified=false,不写入employee.name,也不冒称企微显示名或实名。看板在employee.displayName展示称呼,标“本人提供的称呼 · 未经通讯录核验”;已有可信通讯录姓名优先。历史本地占位名可保持原样,不代表保存昵称失败。
数字人优先读取当前执行实例的可信运行配置,渠道从当前消息上下文补充。先核实本环境实际提供的配置位置或接口,只提取下面的白名单,不扫描全盘凭据或输出配置原文。不能把聊天里提到的另一位机器人登记为自己,也不能声称已调用不存在的自动身份接口。
Agent 字段:name、role、runtimePlatform、channel、identityBasis;account 内仅 platform、tenantId、accountId、accountName、runtimeAgentId、wecomAppAgentId;可附 collectedAt(UTC ISO 时间)。role 描述用途,课堂执行者用 workshop,运维用 operations,其他数字人可登记其他角色;目前只有 workshop 可用课堂写命令,角色不是授权凭据。王刚001、运维001是现有实例名,不是新数字人的默认名。
员工字段:name、identityBasis、account;account 内为 platform、tenantId、accountId、accountName、employeeNumber;aibot 通道额外使用 identifierType、scopeId。可选 userId 只代表本应用已存在的 _User.objectId。通讯录成员 userId 填 account.accountId,corpId 填 tenantId,工号填 employeeNumber。aibot 的 from.userid 另按下节标注类型和作用域,不假定它等于通讯录成员 UserID。群聊会话键不能代替发送者 ID,运行时 Agent ID 与企微应用 agentId 也不能混用。
未知账号字段填 null。密码、令牌、私钥、cookie 不属于身份资料。将身份摘要保存到 /opt/data 下的私有文件(0600),不包含配置中的其他字段。
人工确认的最小摘要如下,替换成当前数字人的实际名称;取得真实字段时才补入。
{
"name":"当前数字人的实际名称",
"role":"workshop",
"identityBasis":"operator_registration",
"runtimePlatform":"hermes",
"channel":"wecom",
"account":{"platform":"wecom","tenantId":null,"accountId":null,"accountName":null,"runtimeAgentId":null,"wecomAppAgentId":null}
}
node /opt/data/skills/ksf-workshop-runtime/scripts/ksf-identity.mjs status
node /opt/data/skills/ksf-workshop-runtime/scripts/ksf-identity.mjs register --input /绝对路径/自身摘要.json
node /opt/data/skills/ksf-workshop-runtime/scripts/ksf-identity.mjs check --agent-profile <返回的完整档案键> --input /绝对路径/自身摘要.json
runtime_config / channel_context 必须有可信稳定账号 ID;不得只凭名字使用这两种来源。无稳定 ID 时用 operator_registration,生成 agent-registration:UUID;有真实稳定 ID 时生成 agent-account:摘要。check 不因新 collectedAt 改变身份键,也不改旧档案。
同名同角色但无稳定 ID 时返回 needsReview=true 和 candidates,尚未登记成功。确认是原实例后,在摘要中明确填 reuseKey;确认是另一实例则填 confirmNew:true。两者不可并用。不静默覆盖、不删除旧档案。已有明确档案键时直接 check,不通过就停。
档案存于 /opt/data/ksf-workshop-state/agents/<键后缀>.json。原双槽版或更早的 agent-self.json 可用 adopt-agent --input /旧档案绝对路径 迁入通用目录;仅复制经验证的同一身份,原文件保留。旧 --slot 和 --agent-profile workshop 不再使用。
当前实际数字人只有王刚001和运维001。2026-11-02运维核查报告指出:此接入为 aibot WebSocket,适配器已读取 body.from.userid;单聊缺 chatid 时以发送者 ID 作为会话键。此结论依据报告中的字段映射,不依据 wo/wr 前缀判断。相同前缀不能证明账号类型或跨机器人同一性。
自动登记必须通过无参数的进程内工具 ksf_register_current_employee。工具直接调用当前回合的 identity_bridge.current_identity(),核对维护人配置的 Agent 档案键与 botId 指纹,然后通过私有管道调用 Skill 的档案代码。只返回员工摘要、profile.key、agentProfileKey、invocationId 与是否新增;不把 botId/senderId 放进报告正文。
工具是否安装、是否能取得本条消息,均以当前环境的真实返回为准;历史登记成功不能代替本次身份核对。工具缺失或 ok=false 时停止,不换用人工登记绕过本次自动识别验收。wecom_identity_current 是身份诊断查询,不直接登记;把它的六字段由模型复制到文件不构成可信登记。
旧 register-channel-employee --context 及该命令的所有参数形式均返回 TRUSTED_IDENTITY_TOOL_REQUIRED。通用 register-employee 也拒绝 channel_context/aibot 账号输入,不能改走它复述通道身份。内部 ksf-identity-bridge.mjs 只由网关登记 handler 调用,不是模型可用的身份导入命令;stdin 本身不是身份认证,任意同 UID 代码执行不在本流程隔离保证内。
Agent 自身 account.accountId 必须是当前实例实际订阅 botId,channel/account.platform 均为 wecom,role=workshop。维护人需在受控配置中将真实 bot 账号对应到 Agent 名称和档案键,不能从包名、聊天自述或模板推导自身身份。
工具每次从本条消息取 senderId,群聊不能使用群 ID 当员工;单聊原始 chatId 可为 null,有效会话键校验可以使用 chatId 或 senderId,但不得反过来从会话键猜员工。没有姓名时显示待绑定标签。
自动登记 account 保存 platform=wecom、identifierType=wecom_aibot_sender、scopeId=真实botid、accountId=from.userid;tenantId、accountName、employeeNumber 未知则 null,userId=null。作用域是机器人实例,不冒称企业 corpId。稳定键只由机器人作用域和发送者构成,不包含聊天ID、消息ID、名字;同一发送者跨单聊/群聊复用,不同群成员分别登记,跨机器人先保留独立别名,不自动合并。
无姓名时本地登记生成“员工待绑定·摘要”标签并返回 needsNameBinding=true;这只表示本地档案没有姓名,不是企微实名查询的结论。无需员工回答账号问题即可建立发送者归属。上传服务可按下节取得通讯录姓名,不能凭 _User、报告 ownerName、历史 Agent 展示名或姓名相同来猜测身份。
该模式的 workshop-start 还须读到服务合同 channelIdentitySchema=ksf.wecom-aibot-sender.v1;缺少则写入前停。私人账号与作用域仅进私有快照;公开看板显示已核验姓名,未取得姓名时显示可识别的通道资料或待同步标签,不暴露原始 ID。本地自动登记成功不代表通讯录实名链路已上线,需新的真实消息验收。
本节为可选通讯录能力:只在维护人已启用通讯录配置时由上传服务查询;未配置则正常保存,返回pending,不阻塞前述自报称呼流程。不要为使用昵称主动运行employee-read或索要通讯录配置。
Skill 负责顺序与结果核对:当前消息可信登记 → 核对员工/Agent 与 session 绑定 → start 或 save → 核对员工回执 → 成果回读。不要另造“让员工先登录网页/手填企微账号”的前置步骤。续传前仍调用当前消息登记工具;本地状态须同时绑定员工键和 Agent 键,不能因为同处一个群或沿用上一次 session 就跳过发送者核对。
查询由 ksfStartWorkshopSession / ksfSaveWorkshopTask 服务端扩展执行。start 使用可信登记注入的账号,save 使用原 session 的私有归属快照。aibot 发送者须通过受控映射对应到通讯录 UserID,不能根据 wo/wr 前缀认定是通讯录账号。配置包含企微应用通讯录权限、企业范围和发送者映射,由维护人在私有运行配置中管理;Skill 不读取、不保存这些密钥,也不调用外部通讯录接口拼装姓名。
成功命令的业务结果增加以下字段;通过 ksf-secure 执行时查看 entryResult.result,直接调用运行库时查看返回对象:
{
"employee": {
"key": "employee:<服务返回的稳定摘要>",
"name": "通讯录返回姓名",
"departmentNames": ["通讯录返回部门"],
"position": null,
"source": "wecom_directory",
"nameVerified": true,
"verifiedAt": "服务返回的核验时间"
},
"employeeIdentity": {"automatic": true, "status": "verified"}
}
上述为字段示意,不可原样作为员工资料上传。实名通过条件为 status=verified、employee.nameVerified=true、employee.source=wecom_directory、姓名非空;记录实际稳定键、姓名和核验时间即可。通讯录姓名核验不代表员工本人正式确认报告,也不代表企业审批。
| 实际返回 | 处理 |
|---|---|
| 业务成功且实名通过 | 说明成果已保存、通讯录实名已取得,按返回编号回读成果 |
| 业务成功,status=pending,reason=WECOM_DIRECTORY_NOT_CONFIGURED | 说明成果已保存、发送者归属已建立,实名待维护人配置;不重跑保存 |
| 业务成功,但没有 employee/employeeIdentity 或字段不一致 | 保留成功回执,说明实名能力尚未核实;交维护人检查版本,不推断已实名、不重存 |
| 查询权限不足、映射缺失或身份冲突报错 | 报告实际错误并停止;新版服务在业务保存前查询,但身份同步审计可能已写入,不声称零写入 |
| 网络失败或结果未知 | 保留原 requestId 与输入,先查回状态;不自动重试、不换键 |
本地工具的 needsNameBinding 与上传回执属于两层结果:本地标签可仍为“员工待绑定”,看板按服务端实名档案展示。不要直接修改本地档案、覆盖旧报告正文或再开 session 来使两者字符串相同。同一员工跨工作坊由稳定身份关联,原成果 ID 和版本保持不变;不同机器人作用域只在服务端有已核验映射时才可归到同一员工。
交付代码、安装 Skill、配置权限、取得本次通讯录回执及生产看板显示是不同验收项。尚未部署或缺配置时如实说明,不能用夹具姓名、历史回执或本地测试替代真实员工验收。后端依赖及维护人配置见仓库 docs/wecom-identity-integration-20261103.md,不将私有运行配置复制进 Skill 包。
只读取当前员工姓名用 employee-read:登记本条可信发送者后传入已有sessionId和participant.profileKey,Agent键沿用 --agent-profile。入口及服务端均核对员工与Agent归属,然后由 ksfReadWecomEmployee 以 data:read 权限查询通讯录。结果位于 entryResult.result.employee,readOnly=true、databaseWrites=0、employeeIdentity.persisted=false;成功仅说明本次姓名读取成功,不表示看板已同步。此路径不调用运维用的 ksfSyncWecomEmployee,也不需要其identity:write权限。
本地员工档案键与工作坊参与键原本就是不同层级。服务端 participantKey = "participant:" + SHA256(canonical([workshopKey, employeeProfileKey])),摘要为UTF-8输入的十六进制SHA256;不能将工作坊键当作旧算法并改写员工档案。缺本地会话索引时,先只读核对服务端记录、上述派生关系和Agent,再恢复私有索引;不据本地目录缺失推断服务端无会话。
从当前消息可信发送者信息取得姓名和账号。无法取得时,只问缺少的显示名/身份依据;已确认的信息直接复用,不让员工反复填写四项。
{
"name":"实际员工显示名",
"identityBasis":"participant_registration",
"account":{"platform":"wecom","tenantId":null,"accountId":null,"accountName":null,"employeeNumber":null}
}
node /opt/data/skills/ksf-workshop-runtime/scripts/ksf-identity.mjs register-employee --input /绝对路径/员工摘要.json
node /opt/data/skills/ksf-workshop-runtime/scripts/ksf-identity.mjs lookup-employee --name 实际员工显示名
node /opt/data/skills/ksf-workshop-runtime/scripts/ksf-identity.mjs check-employee --participant-profile <员工完整档案键> --input /绝对路径/员工摘要.json
channel_context 必须有稳定发送者账号(企微账号需 tenantId+accountId)或其他可校验稳定 ID;existing_record 必须有已有账号依据。无稳定 ID 时 participant_registration 生成 participant-registration:UUID,并在后续消息复用该键。稳定账号生成 participant-account:摘要;同账号同资料返回原键,不新增。
同名员工即使摘要内容相同也不能自动合并。needsReview=true 时先确认候选是同一人:明确复用才在摘要填 reuseKey;确认不同人填 confirmNew:true 新建。身份字段冲突则停,不通过换键绕过。姓名检索只用于找候选,不能证明身份。
员工档案位于 /opt/data/ksf-workshop-state/participants/<键后缀>.json。跨容器无法自动发现对方的本地 UUID;需受控核对和迁移同一档案后复用,不能按姓名推算同一 UUID。登记只写本地档案,数据库归属快照在 workshop-start 成功时才落库。
workshop-start 输入使用 participant:{"profileKey":"员工完整档案键"};Agent 用命令行 --agent-profile <Agent完整档案键>。入口加载两份档案并自动注入名称、键、身份来源和账号快照。缺档案、姓名冲突、角色不符均在创建会话前停止。写入前还检查服务合同 identitySchema 是否为 ksf.agent-self.v1;没有则等待两函数升级。
本地状态记录真实上下文与这两个档案键的绑定,以及 workshopKey/sessionId/projectId/任务键版本,不保存凭据。换发送者或执行数字人需重新解析对应档案;多人群聊不能只用群会话键缓存一个员工。跨 Agent 延续项目需为当前执行者新建 session,再使用同一员工/小组的项目,不冒用另一位 Agent 的 session。
人工登记身份与业务 dataMode 无关。构造待查清单从 start 就用 sample;save 继承 session。账号信息只进私有归属快照,不放报告正文。CLI 和公开看板均不返回完整账号;远端落库核验由受控管理端完成。本地登记成功不能表述为企微认证或远端员工账号开通。