多租户多 Agent Telegram / WeCom 验收链路¶
本文是本地 Compose 环境中验证“两个租户、两个 Agent、Telegram 与 WeCom AI Bot、同一模型和 endpoint”完整闭环的操作单。目标是确认每个 Channel Binding 都能把消息路由到所属租户的 Agent,并且会话、审计、回复不会串租户。
验收范围¶
验收通过需要同时满足以下条件:
- PostgreSQL、Redis 和服务容器健康;
- 两个租户均为
active,各自拥有一个activeAgent App; - 每个 Agent 都有已发布 Revision、Model Profile 和 Backend Profile;
- 两个 Model Profile 可以使用同一个 provider、model 和 endpoint;
- Telegram 或 WeCom AI Bot Binding 分别属于对应租户,并绑定正确 Bot;
- 从两个渠道发起新会话都能得到回复;
- 同一个用户在两个 Bot 上的会话不会复用另一租户的 Runner session;
- 服务重启后重新连接 Bot,两个租户仍能完成对话。
1. 准备环境¶
复制并编辑本地环境文件。真实密钥只放在本机 deploy/service.env,不要提交到 Git:
cp deploy/example.env deploy/service.env
至少确认以下配置:
TRPC_ADMIN_USERNAME=admin
TRPC_ADMIN_PASSWORD=<本地管理员密码>
TRPC_ADMIN_TOKEN=<独立管理 token>
TRPC_ADMIN_TENANTS=*
TRPC_MODEL_PROVIDER=openai
TRPC_MODEL_API_KEY=<模型 API key>
TRPC_MODEL_NAMES=gpt-5.6-terra
TRPC_MODEL_ENDPOINT_HOSTS=api.adlzw.sbs
TRPC_MODEL_ENDPOINT_HOSTS 只填写域名,不填写协议、路径或端口。WebUI 中的 endpoint 可以填写完整 URL,例如 https://api.adlzw.sbs/v1,但它的 host 必须出现在该环境变量中。
使用环境文件启动服务:
docker compose --env-file deploy/service.env \
-p trpcfresh -f deploy/docker-compose.yml \
up -d --build service
curl -fsS http://localhost:8080/readyz
期望输出为 ready。确认容器实际加载的配置,避免 Compose 回退到默认值:
docker inspect trpcfresh-service-1 \
--format '{{range .Config.Env}}{{println .}}{{end}}' \
| rg '^TRPC_MODEL_(PROVIDER|NAMES|ENDPOINT_HOSTS)='
2. 在 WebUI 配置租户和 Agent¶
- 打开
http://localhost:8080,使用管理员账号登录。 - 创建或选择租户 A,确保状态为
active。 - 注册 Agent A,填写 Agent 名称、指令、模型
gpt-5.6-terra和 endpointhttps://api.adlzw.sbs/v1。 - 完成发布并将 Agent A 设为租户 A 的默认 Agent。
- 创建或选择租户 B,重复上述步骤注册 Agent B,并将 Agent B 设为租户 B 的默认 Agent。
- 在 Agent 详情中核对 Model、Endpoint、Instructions 和发布状态。
两个租户可以使用同一个 Model Profile 内容。租户隔离由租户 ID、Agent App、Revision、Provider Registry 和运行时计划共同保证,不要求每个租户使用不同模型。
3. 连接两个 Telegram Bot¶
从 BotFather 为两个 Bot 分别取得 Bot Token。Token 只在 WebUI 的连接表单中输入,不写入文档、日志或数据库控制面记录。
对租户 A:
- 进入租户 A 的 Agent 页面。
- 选择 Telegram channel。
- 输入 Bot A Token 并连接。
- 确认连接状态为 active,并核对 Bot username/账号展示信息。
对租户 B 重复上述步骤,使用 Bot B Token。一个 Binding 只代表一个 Bot 和一个可信 Routing Target;不要把 Bot B 连接到租户 A 的 Binding。
服务重启会清除进程内保存的 Telegram Bot secret。每次重启后都必须在 WebUI 重新连接两个 Bot。
4. 前端验收企业微信 AI Bot¶
WebUI 中的 WeCom 入口对应企业微信 AI Bot 长连接(wecom_aibot),服务端会主动连接 wss://openws.work.weixin.qq.com。它不是企业微信自建应用回调入口,因此表单中必须使用 AI Bot 管理页面生成的两项凭据:
- AI Bot ID:AI Bot 的
bot_id/aibotid; - AI Bot secret:与该 AI Bot ID 配对的长连接密钥。
不要在这里填写 Corp ID、Agent ID、应用 Secret、回调 Token 或 EncodingAESKey。协议说明见企业微信 AI Bot 长连接文档和企业微信帮助中心。
在前端完成一次真实验收:
- 选择已经配置并发布 Agent 的租户,确认 Agent 状态为 active。
- 选择 WeCom channel。
- 在
AI Bot ID中填入企业微信 AI Bot 设置页的 ID,在AI Bot secret中填入同一个 Bot 的 Secret。 - 点击 Connect channel,等待页面显示
Your agent is connected。 - 打开企业微信客户端,找到该 AI Bot,发送一条唯一 marker,例如
wecom-tenant-b-session-001。 - 确认 Bot 在同一会话返回回复,并且回复内容符合当前租户所选 Agent 的指令。
前端连接成功只代表 WebSocket 已完成认证;必须再从企业微信客户端发一条消息,才能验收入站路由、Agent 执行和回复发送。两个租户分别验收时,必须为每个租户连接其对应的 AI Bot Binding。
服务端证据:
docker logs --since 5m trpcfresh-service-1 2>&1 \
| rg 'AI Bot|wecom-aibot|tenant_id|app_id|authentication|websocket|error_detail'
期望看到连接认证成功后的运行日志,以及消息对应的 tenant/app 路由;不应出现 connection_failed、authentication_rejected、authentication_timeout 或 websocket_dial。认证失败日志只会记录错误码和非敏感错误消息,不会记录 Secret。
如果页面仍显示 Connection failed,先确认容器是用 deploy/service.env 启动的,然后按日志中的阶段定位:
| 日志阶段 | 含义 | 首要检查 |
|---|---|---|
binding_create |
控制面 Binding 创建失败 | 租户/Agent 是否 active;运行是否为最新镜像 |
target_resolve |
可信租户路由解析失败 | Binding 的 tenant、app 和 revision 是否匹配 |
websocket_dial |
未建立企业微信 WebSocket | 出站网络、代理和 wss://openws.work.weixin.qq.com |
authentication_rejected |
企业微信拒绝凭据 | AI Bot ID 与 Secret 是否来自同一个 Bot |
authentication_timeout |
未收到订阅确认 | AI Bot 长连接能力、网络和服务版本 |
服务重启会清除进程内保存的 AI Bot Secret。重启后需要在 WebUI 为每个租户重新连接,然后再执行一次真实消息验收。
5. 跨租户、跨 Agent、跨会话验收¶
准备两个 Telegram 对话窗口,分别向 Bot A 和 Bot B 发送不同的 marker,例如:
tenant-a-session-001
tenant-b-session-001
验收记录至少包含:
| 检查项 | 期望结果 |
|---|---|
| Bot A 回复 | 由 Agent A 的指令和行为生成 |
| Bot B 回复 | 由 Agent B 的指令和行为生成 |
| A 的消息到 B | 不会出现在 B 的会话上下文 |
| B 的消息到 A | 不会出现在 A 的会话上下文 |
| 同一 Bot 的连续消息 | 能恢复该 Bot 对应的 session |
| 服务重启后重新连接 | 两个 Bot 均可重新对话 |
| 相同 model/endpoint | 不影响租户和 Agent 路由 |
如果两个 Agent 使用相同指令,给它们设置不同的短标识或首句,便于人工确认实际命中了哪个 Agent。
6. 服务端证据¶
发送消息后立即查看日志,确认 tenant 和 app 成对出现:
docker logs --since 5m trpcfresh-service-1 2>&1 \
| rg 'telegram|dispatch|tenant_id|app_id|error_detail'
重点核对:
- Bot A 的
tenant_id与 Agent A 的app_id匹配; - Bot B 的
tenant_id与 Agent B 的app_id匹配; - 没有
dispatch failed; - 没有
audit tenant scope violation; - 正常回复会进入 Reply Outbox 并由对应 Binding 发送。
PostgreSQL 中可检查控制面对象:
docker exec trpcfresh-postgres-1 psql -U trpc -d trpc_agent -c \
"select tenant_id, display_name, status, default_agent_app_id, default_backend_profile_id from tenant order by created_at;"
审计检查应按两个租户分别出现执行事件。不要只看全局最新一条记录,要用 tenant_id、agent_app_id 和 request ID 关联一次完整请求。
7. 重启恢复验收¶
docker compose --env-file deploy/service.env \
-p trpcfresh -f deploy/docker-compose.yml \
up -d --build service
curl -fsS http://localhost:8080/readyz
服务恢复后:
- 在 WebUI 重新连接 Bot A 和 Bot B;
- 分别发送新的 marker;
- 检查两个 Bot 都返回回复;
- 检查新请求的 tenant/app 路由没有变化。
服务重启不会删除 PostgreSQL 中的租户、Agent、Revision、Model Profile 或 Binding;只会使进程内 Telegram 连接失效。
8. 常见故障定位¶
提示“model and endpoint match the server environment”¶
WebUI 会把多个 invalid_request 统一显示成这句话。先确认:
docker inspect trpcfresh-service-1 \
--format '{{range .Config.Env}}{{println .}}{{end}}' \
| rg '^TRPC_MODEL_(PROVIDER|NAMES|ENDPOINT_HOSTS)='
然后确认模型名称完全匹配 TRPC_MODEL_NAMES,endpoint 的 host 完全匹配 TRPC_MODEL_ENDPOINT_HOSTS。如果实际容器仍显示 gpt-4o-mini 或 api.openai.com,说明启动时没有使用 --env-file deploy/service.env。
租户 2 进入正确 tenant/app 但对话失败¶
查看 dispatch 的 error_detail:
docker logs --since 5m trpcfresh-service-1 2>&1 \
| rg 'dispatch failed|error_detail|tenant_id|app_id'
历史上该链路曾因固定租户的审计 writer 报 audit tenant scope violation,导致第二租户在执行前失败。当前实现使用按事件 tenant_id 动态路由的多租户审计 writer;若再次出现该错误,应先确认运行的是最新构建镜像。
第二个 Bot 没有任何反应¶
- 检查服务是否刚重启;若是,重新连接 Bot;
- 检查 Bot token 对应的 Telegram account 是否与 Binding 的
provider_account_id一致; - 检查同一 Bot 是否仍被其他进程或 webhook 消费;
- 检查日志中是否有 polling、identity mismatch 或 binding inactive;
- 确认租户、Agent、Revision 和 Binding 都是 active/published 状态。
两个 Bot 回复内容串了¶
核对每条请求的 tenant_id、binding_id、app_id 和 session identity。Telegram 消息不能自行提供 tenant ID;可信租户必须来自 active Channel Binding。若只通过普通 /v1/chat API 测试,还要确认 API token 映射的是哪个 tenant,因为普通 API identity 与 Telegram Binding 路由是两条入口。
实现边界¶
- Agent、Model Profile、Backend Profile 和 Revision 在控制面动态创建;不需要为新增租户重启服务。
- Tenant Runtime 按租户懒加载,并以 tenant/app/revision/profile 版本和内容摘要区分计划缓存。
- Telegram Bot secret 当前保存在服务进程内,重启后需要重新连接;生产环境应接入持久化 Secret 管理方案。
- 同一 endpoint 可以服务多个租户,但模型密钥、权限、审计和会话仍按租户边界处理。
- 本文验证的是人工真实 Telegram 对话闭环;协议适配器的 deterministic 单测和独立 live E2E 仍按 Telegram 文档 执行。