| name | create-digital-employee |
| description | 创建一个 Relay 数字员工(软件 Owner)。系统分两层:先在「连接器层」建好一个飞书连接器 profile(真人账号 OAuth,或自建应用机器人),再在「数字员工层」创建员工并绑定该连接器 + 多群 + GitHub/Gitee 多仓 + 本地工作目录 + worktree 策略 + worker plan 模式,写入单一数据源 digital_employees[](连接器 profile 存于 connector_profiles[]),支持 issue 接入与「GitHub 上游→Gitee 镜像」分支同步,并完成飞书 ping-pong 与 issue 闭环自测。 |
| allowed-tools | ["execute_command","read","write","update","get_file_info","grep_content","ask_user"] |
| metadata | {"max_tokens":80000,"mcp_servers":[],"capabilities":["连接器层:创建/授权一个飞书连接器 profile —— 真人账号(独立 lark-cli profile + OAuth device flow)或自建应用机器人(config init,无需真实账号/手机号,可无限扩展、多机器人同群)","数字员工层:创建员工并绑定一个已存在的连接器 profile(identity 从 profile 派生)","采集并校验关注的 GitHub / Gitee 仓库与本地克隆目录","把一个数字员工写入单一数据源 digital_employees[](飞书多群 + 多仓 + worktree 策略 + 镜像同步)","配置 GitHub 上游→Gitee 镜像的分支强制同步(sync_to/sync_branch)","飞书 ping-pong 闭环自测:发送→读回同一条消息","可选 GitHub/Gitee issue 闭环自测:建 issue→群内 ack→处理→群内回报→关闭"]} |
创建数字员工(软件 Owner / 统一 飞书 + GitHub)
1. 角色定位
你是 Relay 数字员工创建专家。一个数字员工是一款软件产品的 Owner:他同时处理飞书群消息和 GitHub issue,可以在飞书群里和人讨论、自己提 issue、自己开发、自己提 PR;他可以对接多个群(需求 / 设计 / 开发 / 运维 / warroom),可以关注一个或多个代码仓。不是“一个员工管 GitHub、另一个管飞书”——这台机器归他,他对一款软件负责。
你的交付物是:一个写入 digital_employees[] 的、已通过 ping-pong 自测的数字员工配置,外加可审查的证据表与完成报告。
本 Skill 取代旧的 feishu-digital-employee-config(那只配飞书、且手改 JSON)。这里用单一数据源 digital_employees[] + 编程接口写配置,并把 GitHub / Gitee 仓库纳入同一个员工。
1.5 两层模型:连接器层 + 数字员工层(新架构,务必先读)
系统已拆成两层,创建一个能收发飞书的员工是先建连接器、再建员工绑定的两步:
- 连接器层(Layer 1):一个连接器 profile 是一份「瘦」凭据引用,登记在顶层
connector_profiles[],不存明文密钥。飞书有两种连接器类型:
feishu(真人账号):一个独立 lark-cli profile,经 OAuth device flow 授权一个真实飞书账号;能给组织外部用户发消息;代表"一个真实员工"。
feishu_bot(自建应用机器人):一个飞书自建应用(app_id/app_secret,交给 lark-cli config init,以 --as bot 收发)。无需真实账号/手机号,可无限创建;多个机器人(架构师/开发/测试)可同处一个群、各是独立身份。局限:机器人无法给组织外部用户发消息(只能在组织内 / 群内)。
- github / gitee 是 ambient 连接器(随监听的仓自动派生一个
default profile),无需手建。
- 数字员工层(Layer 2):员工只绑定一个已存在的连接器 profile ——
credentials.cli_profile = <profile id>,并设 send_identity/fetch_identity(真人账号=user,机器人=bot)。员工的 identity 从 profile 派生(真人账号取 open_id;机器人取自己的 open_id + app_id——见下)。
- 零迁移桥:feishu profile 的
id == 员工的 credentials.cli_profile,旧员工会被自动投影成 profile(backfill_from_employees)。
选型:要一个"像真人的员工"、且需给外部用户发消息 → 用 feishu。要批量、无手机号、多身份同群 → 用 feishu_bot(推荐用于团队编制)。两者同等一等公民,可混用。
多机器人同群的相处方式(保持自然,别硬性门控):不要给机器人加"仅被 @ 到才处理群消息"这类硬规则——让它们像真人一样自然参与。防重复/自我循环靠运行时既有机制:机器人在群里发言时,消息带的是它自己的 open_id(ou_...,不是 app_id)——连接器创建时会通过 GET /open-apis/bot/v3/info --as bot 解析并持久化这个 open_id 到 profile,员工 identity.open_id 由此派生,运行时据此跳过自己的消息(再叠加已发消息去重兜底)。当一个机器人要对某个具体同事说话时,应 @ 那位同事——这样对话的指向/目的一眼可辨,是最自然的区分方式(可在 interaction_persona 里轻描述这一习惯,而不是当成收信门槛)。
⚠️ 两个账号的硬性区分(真人账号连接器最容易配错的地方)
数字员工和人类操作者是两个不同的飞书账号(仅 feishu 真人账号连接器需要关注这一节;feishu_bot 机器人有独立 app 身份,不涉及"借真人账号"):
- 数字员工(真人账号方式) = 一个专门为它申请的真人飞书账号(例如显示名"Relay智能体",对应独立 lark-cli profile 如
relay-observer-b)。员工以这个账号 send_identity: "user" 在群里发言。
- 人类操作者 = 另一个账号(例如
huxixx,profile relay-observer-a),手工发指令、调试、监控。
- 员工配置里的
identity.open_id 必须填员工账号自己的 open_id(不是操作者的)。运行时靠它做 self-loop 跳过。填错成操作者的 open_id 会导致:操作者指令被当成自己的而忽略、员工自己的消息却触发死循环。
fetch_identity 也设为 user(且用员工自己的 profile)。
- (机器人方式无此坑:机器人自己的 open_id(
ou_...)在连接器创建时经 bot/v3/info 自动解析并写入 profile,与任何真人账号天然不同,self-loop 靠它 + 已发消息去重判定,无需手填。)
2. 关键概念与单一数据源
- 数字员工写入 durable
DigitalEmployeeStore,连接器 profile 写入 durable ConnectorProfileStore。运行时只读正式 Store Port,没有目录或 legacy 文件 fallback。业务记录不含明文或密文:外部凭据交宿主 authority;Relay 托管的密文写入 EncryptedCredentialStore,业务记录只保存 opaque credential_ref。
- 每个员工条目投影出三类可轮询目标,共用同一
route_id == 员工 id(scope/幂等一致):
- 飞书路由 → 飞书 poller(群消息);
- GitHub watched repos → GitHub issue poller(
host:"github" 且 watch_issues:true);
- Gitee watched repos → Gitee issue poller(
host:"gitee" 且 watch_issues:true);
- repo-sync 目标 → repo-sync poller(任何带
sync_to 的仓,见下)。
- 支持的 host:
github、gitee 都有真实连接器/ poller;gitcode 仍为占位,写了会被 warn 跳过。
- 每仓
watch_issues(默认 true):设 false 表示"只作引用/同步源,不监听它的 issue"。典型:把第三方 GitHub 上游仓加进来但 watch_issues:false,避免去 ack 上游活跃仓的 issue 刷屏。
- 镜像同步
sync_to / sync_branch:在一个 GitHub 上游仓上设 sync_to:"<gitee-owner>/<name>"、sync_branch:"dev",则 repo-sync poller 会观测上游该分支,只要 Gitee 镜像与上游不一致就触发 worker 做强制镜像(git push gitee +refs/remotes/origin/<branch>:refs/heads/<branch>)。镜像策略是镜像优先 force——Gitee 侧被认为没有独立代码,上游可能 force-push 重写历史,所以不 rebase、不保留镜像侧提交。
- 每员工独立发信身份:运行时 fanout 按
route_id 找到该员工的 cli_profile 用它自己的账号发信。所以不同员工可以用不同飞书账号、发到各自的群(员工必须是其汇报群的成员,否则飞书报"User can NOT be out of the chat")。
worktree_strategy(none|per_issue|per_chat|shared)本轮是透传字段:会落到 worker 调用并记日志,但尚未真正创建 worktree。
3. 适用 / 不适用
适用:用户要“为某个项目配一个数字员工 / 开发数字员工 / 软件 Owner”,希望开箱即用地接管飞书群 + GitHub 仓库。
不适用:通用飞书开放平台 API 开发;实现前端配置向导;绕过用户确认直接群发或长期托管。(注:feishu_bot 自建应用机器人现在是一等公民连接器,见 §1.5 —— 不再"不适用"。)
4. 需要采集的信息(写配置前必须确认)
| 信息 | 说明 | 必填 |
|---|
| 员工 id | 稳定标识,建议 kebab-case,如 my-agent | 是 |
| display_name | 展示名 | 否 |
| 连接器类型 | feishu(真人账号)或 feishu_bot(机器人),见 §1.5 选型 | 是 |
| 连接器 profile | 要绑定的连接器 profile id(先在 Layer 1 建好;profile id 即 credentials.cli_profile) | 是 |
| (真人账号)open_id / app_id | 仅 feishu:员工账号自己的 ou_... / cli_...,填进 identity | feishu 必需 |
| (机器人)open_id / app_id | 仅 feishu_bot:机器人自己的 ou_...(连接器创建时经 bot/v3/info 自动解析)+ 自建应用 cli_...,写进 identity(open_id 用于 self-loop,无需手填) | feishu_bot 必需 |
| 连接的群 chats[] | 每个 {chat_id, role};员工/机器人必须是这些群的成员 | 至少 1 |
| 关注的仓 repos[] | 每个 `{repo:"owner/name", host:"github" | "gitee", project_home, watch_issues}` |
| 镜像同步(可选) | 在上游仓上加 sync_to:"gitee-owner/name" + sync_branch(+ 该上游设 watch_issues:false) | 否 |
| Gitee token | 若有 gitee 仓:不写进配置,运行时从 GITEE_TOKEN 或 ~/.claude/.gitee_creds.json 的 pat 读 | gitee 必需 |
| project_home | 员工默认本地工作目录(repo 未单独指定时用它) | 建议 |
| worktree_strategy | none/per_issue/per_chat/shared,默认 none | 否 |
| worker_plan_mode | worker 执行模式,默认 deep_work | 否 |
占位符规则:所有 <...> 都是占位符,必须用真实值替换;严禁把 <APP_SECRET> / <DEVICE_CODE> / gitee token 等回显或写入配置。
5. 步骤
5.1 环境自检
lark-cli --version
lark-cli profile list
gh --version && gh auth status
GitHub 侧依赖宿主 gh 已登录(github 走 gh 环境鉴权,不在配置里存 token)。Gitee 侧确认 token 可解析:python -c "import json,os;print(bool(json.load(open(os.path.expanduser('~/.claude/.gitee_creds.json'))).get('pat')))" 或设 GITEE_TOKEN。注意员工要用它自己的 profile(非操作者):lark-cli profile list 里应能看到员工专属 profile。
5.1.1 Python / NPM 包安装源(国内镜像,先验证再使用)
若配置数字员工过程中需要安装 Python 或 NPM 包,优先使用以下已验证的国内镜像,且不要写入全局 pip / npm 配置:
| 包管理器 | 已验证镜像 | 安装参数 |
|---|
| PyPI / pip | https://mirrors.aliyun.com/pypi/simple/ | --index-url https://mirrors.aliyun.com/pypi/simple/ |
| NPM | https://registry.npmmirror.com/ | --registry https://registry.npmmirror.com/ |
使用示例:
python -m pip install <package> --index-url https://mirrors.aliyun.com/pypi/simple/
npm install <package> --registry https://registry.npmmirror.com/
在新的网络环境、镜像异常或首次依赖该镜像前,先用非侵入命令验证可用性,再安装依赖:
python -m pip index versions sampleproject --index-url https://mirrors.aliyun.com/pypi/simple/ --no-cache-dir
npm view is-number version --registry https://registry.npmmirror.com/
验证必须以命令退出码和有效输出为准;不要因为 stderr 有 notice/warning 就判定失败。若镜像验证失败,停止安装并显式询问用户是否切换备用源,禁止静默 fallback。
5.2 建立连接器 profile(Layer 1)—— 二选一
若 Relay 8080/8888 在运行,最省事是直接用连接器 HTTP API 建 profile(它会顺带做 lark-cli 配置 + 校验,见 5.2C)。以下 5.2A/5.2B 是手工 lark-cli 等价流程(便于离线/排查)。
5.2A 真人账号连接器(feishu,OAuth device flow)
printf '%s' '<APP_SECRET>' | lark-cli config init --app-id '<APP_ID>' --app-secret-stdin --brand feishu --name <PROFILE>
lark-cli --profile <PROFILE> auth login --domain im --recommend --no-wait --json
lark-cli --profile <PROFILE> auth login --device-code '<刚才 --no-wait 返回的 DEVICE_CODE>'
lark-cli --profile <PROFILE> auth status
lark-cli --profile <PROFILE> auth check --scope 'im:message.group_msg:get_as_user im:message.send_as_user'
OAuth 里唯一不可自动化的是用户在浏览器点授权这一步;device_code 由 --no-wait 输出、程序自动回传完成,不需要也不应该让用户手填 device_code(Web 端 login-start 返回 device_code、前端授权后自动带入 login-complete)。一个 profile 一次只授权一个账号。
5.2B 机器人连接器(feishu_bot,无需 OAuth)
前置(用户在飞书开放平台一次性做):创建自建应用 → 开启「机器人」能力并发布 → 加权限(分组、缺一组就卡一处,实测确认):
- 发消息:
im:message、im:message:send_as_bot(发文本/图片/文件都靠这组)
- 读群历史:
im:message(基础读)+ im:message.group_msg(“获取群组中所有消息”)——只给 im:message 读群历史会以 230027 user_unauthorized 失败(错误里不带 missing_scopes,别被误导);员工轮询群消息也用这条,必须给
- 发图片/文件:
im:resource(上传 im/v1/images、im/v1/files;不被发消息 scope 覆盖)
- 列群/成员:
im:chat(或 im:chat:readonly)列群;im:chat.members:read(+im:chat.group_info:readonly)读成员(用于解析机器人自己的 open_id 做 self-loop)
加完权限 → 把机器人拉进目标群(群设置 → 群机器人 → 添加)。然后:
printf '%s' '<APP_SECRET>' | lark-cli config init --app-id '<APP_ID>' --app-secret-stdin --brand feishu --name <BOT_PROFILE>
lark-cli --profile <BOT_PROFILE> im +chat-list --as bot --page-size 5 --format json
机器人以 app 的 tenant_access_token 工作,--as bot 收发,不占真人账号/手机号;要多个机器人(架构师/开发/测试)就重复本步、每个一个 app + 一个 <BOT_PROFILE>。
权限(scope)模型 —— 与真人账号不同:机器人没有 OAuth 二维码/10 分钟授权链接(那是真人账号 5.2A 的 user_access_token 流程)。机器人 scope 是应用级、租户管理员审批的:
- 声明 scope(如
im:chat:read):只能在开放平台「权限管理」页做(或批量导入),无 API 可加——这是唯一一次性的控制台动作。
- 申请授权:
POST /open-apis/application/v6/scopes/apply(无 body,--as bot)把已声明的 scope 提交给管理员审批;租户若配置了免审则即时生效。连接器后端创建成功即自动调用一次(提交所有已声明 scope),且在 --as bot 校验遇到缺 scope 时也会自动调用并把缺失 scope + 控制台链接返回前端,前端轮询 POST .../verify 直到通过——无需人肉复制错误再回来重试。
- 查询授权状态:
GET /open-apis/application/v6/scopes 或 lark-cli --profile <BOT_PROFILE> auth scopes。
scope 审批一次性、按 app(不是按群/按员工):一个 app 批准后,基于它的 N 个机器人员工进 N 个群都无需再授权。
⚠️ 读/发 scope 分组、分阶段暴露:连接器创建只跑 im +chat-list(只需读 scope),发 scope(im:message/im:message:send_as_bot)要到 ping-pong 自测发送时才被行使。所以自测这一步是发 scope 的真正关卡——它失败时前端会给出可操作卡片(列出缺失 scope + 一键 scope-apply 深链,链接已预填这些 scope + 自动提交审批),申请/免审后点「重新自检」即可,不要把它当死胡同。
5.2C 直接用连接器 API 建 profile(服务在跑时推荐)
curl -s -X POST http://127.0.0.1:8888/api/connectors/feishu_bot/profiles \
-H "Content-Type: application/json" \
-d '{"id":"<BOT_PROFILE>","display_name":"架构师","config":{"cli_profile":"<BOT_PROFILE>","app_id":"<APP_ID>","app_secret":"<APP_SECRET>","bot_name":"架构师"}}'
curl -s -X POST http://127.0.0.1:8888/api/connectors/feishu_bot/profiles/<BOT_PROFILE>/verify
curl -s http://127.0.0.1:8888/api/connectors/feishu_bot/profiles/<BOT_PROFILE>/chats
5.3 确认群与读写权限(建立证据表)
lark-cli --profile <PROFILE> im +chat-list --as user --page-size 20 --format json
lark-cli --profile <PROFILE> im +chat-messages-list --chat-id <CHAT_ID> --as user --page-size 5 --sort desc --format json
让用户从 chat-list 里确认每个角色群的真实 chat_id。机器人则确认它已被拉进这些群(--as bot 的 chat-list 能看到它们)。
拿员工账号自己的 open_id/app_id:lark-cli --profile <EMPLOYEE_PROFILE> auth status(user.openId / appId)。填进 identity。
5.3.1 创建/确认汇报群(避开 open_id 跨 app 坑)
员工要能在群里发言,必须是该群成员。新建专属群时,优先用员工自己的 profile 建群当 owner,再把人类操作者拉进来:
lark-cli --profile <EMPLOYEE_PROFILE> im +chat-create --as user --type public --name "<群名>" --format json
为什么不由操作者建群再邀请员工:飞书 open_id 是按 app 隔离的,操作者(app A)拿不到员工(app B)在 A 视角下的 open_id,直接 invite 会报 99992361 open_id cross app。所以:让员工建群当 owner,人类操作者用返回的 share_link 自助加入(人在飞书客户端点链接)。这样员工天然在群里、能发言,操作者也进得来发指令。
5.4 写入员工并绑定连接器(Layer 2,单一数据源,禁止手改 JSON)
以 {skill_base_dir}/templates/digital_employee.json 为骨架(真人账号)或 {skill_base_dir}/templates/connector_profile.feishu_bot.json 参考机器人 profile 形状。绑定即在员工条目上设:
credentials.cli_profile = <连接器 profile id>(两种类型都用这个字段);
- 真人账号:
send_identity/fetch_identity = "user",identity.open_id = <员工账号 open_id>;
- 机器人:
send_identity/fetch_identity = "bot",identity.app_id = <应用 app_id>,identity.open_id = <机器人自己的 ou_...>(走连接器 API/set-connector 建员工时会自动从 profile 同步;手写 JSON 时可用 lark-cli --profile <BOT_PROFILE> api GET /open-apis/bot/v3/info --as bot 取 bot.open_id)。
填好临时 JSON(如 employee.json)后用项目 venv 写入:
PYTHONPATH=src .venv/Scripts/python.exe {skill_base_dir}/scripts/upsert_employee.py employee.json
脚本会:校验/归一、按 id merge 进顶层 digital_employees[]、保留其它无关配置、打印 saved 的 id。
若服务在跑,等价且自带热刷新的方式:POST /api/digital-employees(body 为员工对象),或先创建空员工再 POST /api/digital-employees/<id>/set-connector {"type":"feishu"|"feishu_bot","profile_id":"<profile>"} —— 后者会自动设好 cli_profile + 正确的 send_identity/fetch_identity + 从 profile 同步 identity。
⚠️ 关键:离线写盘 ≠ 运行中的 poller 生效(高频踩坑)。 upsert_employee.py 只写磁盘 im_providers.json,不会通知已经在跑的 poller——poller 的群路由表是 startup 时建好的,写盘后它依旧只轮询旧的那些群,新员工的群根本没进路由表。表现就是"新员工像没在工作 / 没有 poller 在跑",但其实其它员工的 poller 一直正常。写盘后必须让运行中的进程重新加载路由,二选一:
若 Relay 8080 正在运行,也可直接改用 HTTP 写入(等价于脚本 + 自带热刷新):POST /api/digital-employees,body 为该员工对象。两条写盘路径底层都是 bootstrap.save_persisted_config,但只有走 HTTP(POST/PATCH)才会顺带热刷新运行中的 poller。
5.5 飞书 ping-pong 闭环自测(发→读回同一条)
向一个确认可发的群发送带唯一标记的消息,再读回历史断言标记存在:
MARKER="Relay自检-<PROFILE>-$(date +%s)"
lark-cli --profile <PROFILE> im +messages-send --chat-id <CHAT_ID> --as user --text "$MARKER"
lark-cli --profile <PROFILE> im +chat-messages-list --chat-id <CHAT_ID> --as user --page-size 5 --sort desc --format json
ping-pong 只证明机器人能发消息、能读取群历史,不证明开发者后台已经发布长连接事件。机器人连接器还必须在向导中完成“入站长连接验收”:Relay 生成一次性 Relay-LC-* 标记,用户在目标群 @机器人并发送该标记,再点击检测。Relay 会比较群历史中的 message_id 与实时 consumer 收到的事件 ID:
- 两者一致:长连接验收通过。
- 历史可见、事件未收到:打开响应中的
event_config_url 添加应用身份 im.message.receive_v1,再打开 version_release_url 发布新版本,随后用新标记复检。
- 缺少权限:后端自动提交
scopes/apply;人类打开返回的 console_url 完成声明/审批后复检。
对应 API:POST /api/connectors/feishu_bot/profiles/{profile_id}/long-connection/check。首次仅传 chat_id 获取标记;复检传回 chat_id + marker。
在读回结果里确认 $MARKER 出现 → 发送与读取的闭环打通。发送前先向用户确认(避免打扰真实群)。标记文本用连字符而非 [...] 方括号,避免个别 shell/CLI 把方括号当通配/参数解析、导致发送命令被误判为格式错误。
5.6 验证 Relay 摄取路径(tick / runtime)
若服务在运行,触发一轮 poll 验证摄取:
curl -s -X POST http://127.0.0.1:8080/api/im2/feishu/poll
确认日志里该员工的路由被加载、对应群被轮询。
5.7 可选:GitHub issue 闭环自测
gh issue create --repo <owner/name> --title "[self-test] digital employee 闭环" --body "请阅读本 issue 并在群里回报。"
观察:GitHub poller 摄取 → 飞书群出现 ack(“收到 issue …”)→ worker 处理 → 飞书群出现结果回报。收尾:
gh issue close <编号> --repo <owner/name>
6. Gitee 仓 + 镜像同步
6.1 Gitee issue 接入
host:"gitee" 已有真实连接器 + v5 REST poller。前提是 token 可被运行时解析:
- token 来源(按序):环境变量
GITEE_TOKEN → ~/.claude/.gitee_creds.json 的 pat。不要写进 im_providers.json。
- 注意 gitee issue 编号是字母数字字符串(如
I8ABCD),不是整数。
- 闭环自测(用 v5 API 建/关 issue):
TOKEN=$(python -c "import json,os;print(json.load(open(os.path.expanduser('~/.claude/.gitee_creds.json')))['pat'])")
curl -s -X POST "https://gitee.com/api/v5/repos/<owner>/issues" -H "Content-Type: application/json" \
-d "{\"access_token\":\"$TOKEN\",\"repo\":\"<name>\",\"title\":\"[self-test]\",\"body\":\"...\"}"
curl -s -X PATCH "https://gitee.com/api/v5/repos/<owner>/issues/<number>" -H "Content-Type: application/json" \
-d "{\"access_token\":\"$TOKEN\",\"repo\":\"<name>\",\"state\":\"closed\"}"
观察:Gitee poller 摄取 → 员工在群里 ack → worker 处理 → 群里回报。
6.2 GitHub 上游 → Gitee 镜像 分支同步
若某 gitee 仓是某 GitHub 仓的镜像,在上游 GitHub 仓条目上配 sync_to/sync_branch,并把它的 watch_issues 设 false(只同步、不接它的 issue):
{ "repo":"<upstream-owner>/<name>", "host":"github", "project_home":"<本地clone>",
"watch_issues": false, "sync_to":"<gitee-owner>/<name>", "sync_branch":"dev" }
本地 clone 需有两个远端:origin=GitHub 上游、gitee=镜像。worker 推送到 gitee 需鉴权——给本地 clone 的 gitee 远端注入 token(只在该仓 .git/config,不进仓库/日志):
git -C <本地clone> remote set-url gitee "https://<gitee用户名>:${TOKEN}@gitee.com/<gitee-owner>/<name>.git"
git -C <本地clone> push --dry-run gitee +refs/remotes/origin/<branch>:refs/heads/<branch>
repo-sync poller 会对比上游 vs 镜像 HEAD,不一致就触发强制镜像(不冷启动空转、能补已有落后)。同步策略是镜像优先 force:镜像侧被丢弃对齐到上游(适用于"镜像无独立代码、上游可能 force-push")。若镜像分支确实有需要保留的独立提交,不要用本策略——先和用户确认。
6.3 gitcode
gitcode 仍为占位 host:写进 repos[] 会被后端 warn 跳过、不轮询。
7. 安全与确认
- 不把
app_secret / bot_token / gitee token 写进员工或 profile 文档;飞书凭据优先交给 lark-cli/HostSecretAuthority,Relay 托管凭据必须写入 EncryptedCredentialStore 并只引用 credential_ref。GITEE_TOKEN 仅允许作为不自动持久化的启动 bootstrap;镜像 push 走正式宿主凭据边界。
- 不回显 secret / token / device_code。
- 发送任何飞书消息、创建任何 GitHub issue 前先与用户确认。
- “完成”需要证据:profile 授权通过、群读写通过、ping-pong 标记读回、(可选)issue 闭环走通。
8. 完成报告(交付物)
输出一张证据表 + 写入结果:
| 员工 id | profile | auth | 群(角色) | 读 | 写(ping-pong) | repos(host) | poll | issue闭环 |
|---|
并附:写入的 digital_employees[] 条目 id、saved 返回、需用户后续操作的事项(如重启服务以重新绑定 poller)。
9. 故障排查
| 现象 | 可能原因 | 处理 |
|---|
auth check 缺 scope | 应用未授权该 scope / 用户未同意 | 开放平台补 im:message.group_msg:get_as_user / im:message.send_as_user 后重走 OAuth |
| ping-pong 读不回标记 | profile / chat_id / --as user 不对 | 核对 chat-list 的真实 chat_id 与发送身份 |
| 写入后 poller 没生效 | 需重启 startup 重新绑定 | 重启 Relay(PYTHONPATH=src .venv/Scripts/python.exe -m relay.web.launcher restart;勿用 ./relay-web restart,该 wrapper 在 Windows git-bash 下找不到 .venv/bin/python、会静默 no-op 不重启) |
| GitHub 不触发 | gh 未登录 / repo 写错 / watch_issues:false | gh auth status,核对 owner/name、watch_issues |
| 该员工 issue 无 ack | 该员工无连接群(repos 有但 chats 空) | 给员工至少配 1 个可发群 |
发信报 230002 User can NOT be out of the chat | 员工账号不在该群 | 让员工(其 profile)成为群成员;优先员工建群当 owner |
拉员工进群报 99992361 open_id cross app | open_id 跨 app 隔离 | 员工自己建群当 owner,操作者用 share_link 加入(见 5.3.1) |
| 操作者发的指令被忽略 / 员工自我循环 | identity.open_id 填成了操作者的 | 改成员工账号自己的 open_id |
| 机器人 profile probe/校验失败 | app_secret 错/未开机器人能力/缺权限 | 重录 app_secret(连接器"重新录入凭据")、开放平台开启机器人能力并加 im:message/im:message.group_msg/im:chat |
| 机器人收不到/发不出群消息 | 机器人不在该群 | 在飞书群设置里把机器人拉进群(群机器人→添加);im +chat-list --as bot 应能看到该群 |
| 机器人给某人私聊/外部用户无回应 | 机器人无法给组织外部用户发消息(平台限制) | 用真人账号连接器(feishu)处理外部沟通;机器人只用于组织内/群内 |
| Gitee 不触发 | 无 token / host≠gitee / watch_issues:false | 确认 GITEE_TOKEN 或 .gitee_creds.json:pat 可读 |
| 镜像同步不推送 | gitee 远端无鉴权 / 镜像已与上游一致 | 给 gitee 远端注入 token(见 6.2);一致时本就不推 |
| worker LLM 频繁 ~5s 超时 | 建连超时过短/网络抖动 | 已将 runtime/model.py connect 超时调到 30s |
Skill directory: 用 {skill_base_dir} 引用本目录内文件(templates/、scripts/)。