---
title: "快速开始"
description: "部署 Ankole，配置企业身份和模型，接入聊天平台，直到用户能在 IM 中收到 Agent 的真实回复。"
url: "https://ankole.agentbull.com/zh-Hans-CN/docs/quickstart/"
lang: "zh-Hans-CN"
---

> 面向 AI Agent 的文档索引：https://ankole.agentbull.com/zh-Hans-CN/llms.txt

# 快速开始

## Ankole 的部署结构

一套 Ankole 私有化部署实例由一个控制面和一个或多个 Agent Computer Worker 组成。控制面拥有持久领域状态与监督，负责管理和调度；Worker 负责实际运行 Agent，相当于给 Agent 配的工作电脑。

一台 Worker 可以供多个 Agent 使用。金融等需要严格隔离的场景，建议每个 Agent 独享一台 Worker，就像工作电脑既可以给几个实习生共用，也可以一人一台。

> **💡 你知道吗？**
>
> 即使多个 Agent 共用一台 Worker，每个 Agent 仍在独立沙盒中运行。沙盒提供基础的进程与文件系统隔离，可以减少彼此干扰；但它属于轻量隔离，不能视为绝对安全边界。

每个实例还需要 PostgreSQL 和持久化磁盘。单机部署可以使用本地硬盘或虚拟硬盘；Kubernetes 部署则要使用 NFS 等支持 ReadWriteMany 的共享卷。

<a id="terminology"></a>

### 术语表

本页是 Ankole 用户文档和界面用语的准则。括号内保留英文名称，方便对照第三方平台、配置字段和 API；后文只使用简称。

| 统一名称 | 含义 | 后文简称 |
|---|---|---|
| **私有化部署实例** | 一家企业自行部署并管理的一套完整 Ankole 系统 | 实例 |
| **主体（Principal）** | Ankole 中可以拥有身份和权限的人、Agent 或系统服务 | 主体 |
| **身份源提供商（Identity Provider，IdP）** | 为 Console 提供单点登录，并向 Ankole 同步员工、通讯录和组织架构的外部身份源 | IdP |
| **聊天平台** | Slack、Teams、飞书 / Lark、钉钉、企业微信、Telegram、Discord、LINE、WhatsApp 或邮箱等承载会话的外部平台 | 平台 |
| **聊天渠道（Channel Provider）** | 接入 Ankole 的一套聊天应用或机器人配置，用来接收消息和发送 Agent 回复 | 聊天渠道 |
| **信号路由规则（Signal Binding）** | 决定把哪个信号源收到的消息或事件交给哪个 Agent | 路由规则 |
| **大语言模型提供商（LLM Provider）** | 保存模型服务地址、凭证和可用模型的配置 | 模型提供商 |
| **后台 Agent 任务档案（内部键：`coding`）** | 选择后台 Agent 任务使用的 AIGateway Provider 和模型；普通对话不会根据代码量切换到它 | 后台 Agent 任务 |

## 让 Agent 帮你完成安装

你也可以把下面这段话直接发给 Codex、Claude Code，或其他能操作终端的 Agent：

```text
请参考 https://ankole.agentbull.com/zh-Hans-CN/docs/quickstart/index.md，帮我部署并配置一个 Ankole 私有化部署实例。请先检查我的运行环境，帮我在 Docker Compose、Kubernetes 和源码安装中选择最合适的部署方式。随后配置身份源提供商（IdP）、大语言模型提供商（LLM Provider）、Agent、聊天渠道（Channel Provider）和信号路由规则（Signal Binding）。如果我没有指定 IdP 和聊天平台，请先询问我使用哪个身份源和聊天平台，不要自行选择。不要在对话或命令输出里泄露 secret。直到我能在选定的 IM 中收到 Agent 的真实回复，这项工作才算完成。
```

<a id="deployment"></a>

## 1. 部署 Ankole

单机部署推荐 Docker Compose，企业环境推荐 Kubernetes；源码安装适合开发和排错。

**选择部署方式**

- Docker Compose · 单机推荐
- Kubernetes · 企业级推荐
- 源码安装

### Docker Compose · 单机推荐

大多数团队从这里开始。一台装有 Docker 的 Linux、macOS 或 Windows 电脑，就能运行 PostgreSQL、控制面、一个 Agent Computer Worker 和 Caddy HTTPS。

**开始之前**

- 能运行 Linux amd64 或 arm64 容器的 Linux、macOS 或 Windows 主机
- Docker Engine 与 Compose 插件，或 Docker Desktop
- 持久化磁盘、一个域名，以及可用的 80、443 端口

#### 基础设置 · 使用 Docker Compose 安装

不要把模型 API key 写进部署文件。登录 Console 后，再在其中添加凭证。

##### 1. 下载部署文件

```bash
git clone https://github.com/AgentBull/ankole.git
cd ankole/tools/deploy/docker-compose
cp .env.example .env
chmod 600 .env
```

##### 2. 生成三个彼此独立的 secret

复制并运行这条命令，再把完整输出粘贴到 `.env`。

```bash
printf '%s\n' \
  '# PostgreSQL 数据库的密码' \
  "POSTGRES_PASSWORD=$(openssl rand -hex 32)" \
  '' \
  '# ANKOLE的主加密密钥' \
  "ANKOLE_SECRET_BASE=$(openssl rand -hex 32)" \
  '' \
  '# ANKOLE的Agent Worker和控制面的相互通讯密钥' \
  "ANKOLE_RUNTIME_FABRIC_WORKER_AUTH_KEY=$(openssl rand -hex 32)"
```

##### 3. 配置访问域名

把 ANKOLE_HOST 设为指向这台主机的域名。ACME_EMAIL 应能收到证书通知。

- `ANKOLE_HOST`: `ankole.example.com` — 用户访问、SSO 回调和 provider 回调共用的 HTTPS 域名。
- `ACME_EMAIL`: `ops@example.com` — Caddy 申请和续签证书时使用的联系邮箱。

##### 4. 启动并检查服务

Compose 会先等待 PostgreSQL 就绪，再执行 migration、写入 Worker key，最后启动控制面、Worker 和 Caddy。

```bash
docker compose pull
docker compose up -d
docker compose ps
```

##### 5. 打开首次设置页面

打开 https://<ANKOLE_HOST>/setup，输入日志中的 activation code。

```bash
docker compose logs control-plane | grep "SETUP ACTIVATION CODE"
```

#### 高级设置 · 正式环境的发布、备份和本地证书

把 Ankole 用于正式环境前，请先看完这些设置。

##### 1. 固定配套的控制面和 Worker 镜像

RuntimeFabric 验证通过后，控制面和 Worker 的 tag 会同步更新。需要固定版本时，请选用同一次源码提交生成的两个 digest。

- `ANKOLE_CONTROL_PLANE_IMAGE` — 控制面镜像，建议填写不可变的 digest。
- `ANKOLE_WORKER_IMAGE` — 与控制面镜像配套的 Worker digest。
- `ANKOLE_POSTGRESQL_IMAGE` — 可选。填写后会固定内置数据库镜像。

##### 2. 每次升级前先备份

还要为 ankole_agents_data volume 创建快照。请在另一台主机上同时测试数据库和 Agent Home 能否恢复。

```bash
docker compose exec -T postgresql \
  pg_dump -U ankole -d ankole -Fc \
  > "ankole-$(date +%Y%m%d).dump"
```

##### 3. 升级并重启服务

```bash
docker compose pull
docker compose down
docker compose up -d --force-recreate
docker compose ps
```

> docker compose down 会保留命名 volume；加上 -v 则会删除 PostgreSQL、Agent Home 和 Caddy 数据。除非确定要永久删除，而且备份已经验证可用，否则不要加 -v。

##### 4. 在本机测试 HTTPS

本机测试时，可把 ANKOLE_HOST 设为 ankole.localhost。首次启动后，复制 Caddy 根证书，并在每台客户端上将它设为受信任证书。

```bash
docker compose cp \
  caddy:/data/caddy/pki/authorities/local/root.crt \
  ./ankole-local-ca.crt
```

- [完整 Compose 运维说明](https://github.com/AgentBull/ankole/blob/main/tools/deploy/docker-compose/README.zh-Hans.md)

### Kubernetes · 企业级推荐

团队已经有 Kubernetes 集群，并且能配置调度、HTTPS Ingress 和共享存储时，选择这种方式。

**开始之前**

- Kubernetes 1.27 或更高版本，Helm 3 或更高版本
- Linux amd64 或 arm64 节点
- HTTPS Ingress，以及供 Agent Home 使用的 ReadWriteMany 存储

#### 基础设置 · 安装 Helm Chart

默认使用内置的 PostgreSQL 18 镜像，其中已经包含 pg_search 和 vector。

##### 1. 下载 Chart，并创建 namespace

```bash
git clone https://github.com/AgentBull/ankole.git
cd ankole
kubectl create namespace ankole
```

##### 2. 创建 bootstrap Secret

```bash
POSTGRES_PASSWORD="$(openssl rand -hex 24)"
ANKOLE_SECRET_BASE="$(openssl rand -hex 32)"
ANKOLE_WORKER_AUTH_KEY="$(openssl rand -hex 24)"

kubectl -n ankole create secret generic ankole-bootstrap \
  --from-literal="POSTGRES_PASSWORD=${POSTGRES_PASSWORD}" \
  --from-literal="ANKOLE_SECRET_BASE=${ANKOLE_SECRET_BASE}" \
  --from-literal="ANKOLE_RUNTIME_FABRIC_WORKER_AUTH_KEY=${ANKOLE_WORKER_AUTH_KEY}" \
  --from-literal="DATABASE_URL=ecto://ankole:${POSTGRES_PASSWORD}@ankole-postgresql:5432/ankole"

unset POSTGRES_PASSWORD ANKOLE_SECRET_BASE ANKOLE_WORKER_AUTH_KEY
```

##### 3. 创建 values-production.yaml

请替换域名、Ingress 配置和两个 StorageClass 名称。如果沿用上面的 DATABASE_URL，请保留 fullnameOverride。

```yaml
fullnameOverride: ankole

secrets:
  existingSecret: ankole-bootstrap

controlPlane:
  publicHost: ankole.example.com

worker:
  agents:
    persistence:
      storageClass: nfs-rwx
      size: 100Gi

postgresql:
  enabled: true
  persistence:
    storageClass: standard
    size: 50Gi

ingress:
  enabled: true
  className: nginx
  hosts:
    - host: ankole.example.com
      paths:
        - path: /
          pathType: Prefix
  tls:
    - secretName: ankole-tls
      hosts:
        - ankole.example.com
```

##### 4. 安装并等待就绪

```bash
helm upgrade --install ankole ./tools/deploy/helm/ankole-agent \
  --namespace ankole \
  --values values-production.yaml \
  --wait \
  --timeout 15m
```

##### 5. 检查 Pod 和 Deployment

```bash
kubectl -n ankole get pods
kubectl -n ankole rollout status deployment/ankole-control-plane --timeout=10m
kubectl -n ankole rollout status deployment/ankole-worker --timeout=10m
```

##### 6. 打开首次设置页面

打开 https://ankole.example.com/setup，输入 activation code。

```bash
kubectl -n ankole logs deployment/ankole-control-plane \
  -c control-plane | grep "SETUP ACTIVATION CODE"
```

#### 高级设置 · 外部 PostgreSQL、镜像版本和集群安全

正式上线前，请逐项确认这些条件。

##### 1. 使用外部 PostgreSQL

外部数据库必须使用 PostgreSQL 18 或更高版本，预加载 pg_search，并允许应用数据库的 owner 使用 pg_search 和 vector。

- `postgresql.enabled`: `false` — 关闭内置数据库。
- `DATABASE_URL` — 把外部数据库的 URL 存入 ankole-bootstrap。

```sql
SHOW server_version_num;
SHOW shared_preload_libraries;

SELECT name, default_version, installed_version
FROM pg_available_extensions
WHERE name IN ('pg_search', 'vector')
ORDER BY name;
```

##### 2. 确认集群允许 Worker 的安全配置

要让 bubblewrap 提供强隔离，Worker 需要 SYS_ADMIN、不受限的 seccomp profile，以及未屏蔽的 /proc。Chart 已经配置好这些选项，但集群准入策略必须允许它们。

> Worker 节点属于受信任的计算边界。如果集群策略拒绝这项配置，请改用专用节点池，或使用经过批准、隔离能力相当的 sandbox。

##### 3. 成对固定和升级控制面、Worker 镜像

控制面和 Worker 的 digest 必须来自同一次经过验证的源码提交。执行 helm upgrade 前，先备份 PostgreSQL 和 Agent Home。

> Helm rollback 不会撤销数据库 migration。如果旧版本应用依赖旧 schema，还必须恢复数据库备份。

##### 4. 查看 Chart 的完整配置说明

- [完整 Helm 部署说明](https://github.com/AgentBull/ankole/blob/main/tools/deploy/helm/ankole-agent/README.zh-Hans.md)

### 源码安装

开发、排错或在本机试用时选这个。它会启动 PostgreSQL、Phoenix 控制面、Console、前端资源和一个由 Ankole 管理的 Docker Worker。

**开始之前**

- macOS 或 Linux；Windows 使用 WSL2
- 有权限安装系统软件包的账号
- Docker Desktop 或 Docker Engine

#### 基础设置 · 启动完整开发环境

##### 1. 克隆仓库

后续命令都从仓库根目录执行。

```bash
git clone https://github.com/AgentBull/ankole.git
cd ankole
git status --short
```

##### 2. 安装项目所需的工具链

脚本会安装编译所需的软件包、Docker、Rust、Elixir、Erlang，以及项目指定版本的 Bun。

```bash
bash tools/devkit/scripts/env-setup.sh
```

##### 3. 打开一个新终端，逐项检查

macOS 用户应先启动 Docker Desktop。如果安装脚本把 Linux 用户加入了 docker group，请注销后重新登录。

```bash
bun --version
elixir --version
rustc --version
cargo clippy --version
docker compose version
docker info
```

##### 4. 安装依赖并初始化 PostgreSQL

```bash
bun install
bun run services:start
bun run services:status
bun run control-plane:setup
```

##### 5. 启动 Ankole

不要关闭这个终端。打开 http://localhost:4000，输入开发服务打印的 activation code。

```bash
bun dev
```

#### 高级设置 · 检查本地服务、停止环境和使用转发地址

需要排查本地环境、停止服务，或通过远程 workspace 访问时，再看这里。

##### 1. 在另一个终端读取 activation code

```bash
bun run kit show bootstrap-activation-code
```

##### 2. 确认各项服务都已启动

在运行 bun dev 的终端按 Ctrl+C，可停止控制面和 Worker。开发结束后，再用第二段命令停止 PostgreSQL。

```bash
bun run services:status
curl -I http://localhost:4000/
docker ps --filter name=ankole-dev-agent-computer
```

```bash
bun run services:stop
```

##### 3. 使用 Codespaces 等转发地址

把转发后的 HTTPS origin 登记到 IdP 后台。localhost 和转发地址是两个不同的回调 URL，请使用浏览器实际访问的地址测试登录。

> 请只在开发环境中从源码运行 Ankole。生产环境请选择 Docker Compose 或 Kubernetes。

<a id="identity-providers"></a>

## 2. 先设置身份源提供商

Ankole 的核心设计目标是服务企业内部的私有化部署。每家企业拥有一套实例。在同一个实例内，Agent、人类员工和系统服务都表示为主体，并由授权管理模块（AuthZ）统一管控权限。

因此，除了像 Hermes Agent、OpenClaw 那样配置聊天渠道，Ankole 还要连接企业的外部身份源，用来同步员工、通讯录和组织架构。首次设置时，先配置 IdP，使用企业账号登录 Console，再添加聊天渠道。

| 设置项 | 负责什么 | 在哪里设置 |
|---|---|---|
| **IdP** | Console 的 SSO 登录，以及员工、通讯录、组织架构和权限组的同步 | 先在 `/setup`，之后在 **Console → 身份源提供商** |
| **聊天渠道** | 接收 IM 消息并发送 Agent 回复 | 在 **Console → 信号路由** 中配置 |

身份源和聊天渠道也可以来自不同平台。例如，员工通过 Google Workspace 登录 Console，但在 Slack 中与 Agent 对话；也可以使用 Entra ID 登录，把聊天放在飞书、Slack、钉钉、Teams 或企业微信。

### 按所选 IdP 完成首次设置

先选出企业实际使用的身份源，再照着对应标签页操作。每套步骤都从第三方后台创建应用开始，一直写到首次登录和通讯录同步。

如果这次才启用对应的适配器，请在首次登录后按当前部署方式重启一次控制面。这样，通讯录长连接、Graph 订阅等插件后台任务才会完整启动。

**选择身份源提供商**

- Slack
- Microsoft Entra ID
- Google Workspace
- 飞书 / Lark
- 钉钉
- 企业微信

#### Slack

用一套 Slack app 完成 Console 登录，并同步 workspace 成员和用户组。默认配置需要 OAuth Client、Bot Token 和 App Token。

**开始之前**

- 可创建并安装 Slack app 的 workspace 管理员
- Ankole 对外可访问的 HTTPS 地址
- 用于首次登录的 workspace 成员账号

##### 基础设置 · 配置 Slack IdP

###### 1. 先从 Ankole 复制回调地址

打开 https://<ANKOLE_HOST>/setup，输入 activation code。在插件页勾选“Slack 适配器”并保存，然后选择 Slack。

“配置 ID（Provider ID）”保持 slack-main。使用页面上的“复制”按钮复制登录回调地址；后面不要手写或改动它。

###### 2. 创建 Slack app

打开 Slack API 的 Your Apps 页面，选择 Create New App → From scratch。填写应用名，并选择员工所在的 workspace。

进入 Basic Information → App Credentials，复制 Client ID 和 Client Secret。

- [打开 Slack Your Apps](https://api.slack.com/apps)

###### 3. 登记登录回调地址

进入 OAuth & Permissions → Redirect URLs，选择 Add New Redirect URL，粘贴刚才从 Ankole 复制的完整地址，然后保存。

Ankole 默认请求 openid、profile、email 三个登录 scope，不要把它们改成聊天机器人的 scope。

- [Slack 登录配置说明](https://api.slack.com/authentication/sign-in-with-slack)

###### 4. 授予通讯录权限并取得 Bot Token

仍在 OAuth & Permissions 中，找到 Bot Token Scopes，依次添加下面三项。然后选择 Install to Workspace，并批准安装。

- `users:read` — 读取成员资料
- `users:read.email` — 读取成员邮箱
- `usergroups:read` — 读取用户组及成员关系

> 安装完成后，复制 Bot User OAuth Token。它必须以 xoxb- 开头。以后每次增加 scope，都要重新安装应用。

###### 5. 取得 App Token，并启用 Socket Mode

回到 Basic Information → App-Level Tokens，选择 Generate Token and Scopes，添加下面这项 scope，生成并复制以 xapp- 开头的 token。然后进入 Socket Mode，打开 Enable Socket Mode。

- `connections:write` — 允许 App Token 建立 Socket Mode 连接

###### 6. 订阅通讯录变更事件

进入 Event Subscriptions，打开事件订阅。在 Subscribe to bot events 中逐项添加下面五个事件。

- `team_join` — 新成员加入 workspace
- `user_change` — 成员资料发生变化
- `subteam_created` — 创建用户组
- `subteam_updated` — 用户组资料发生变化
- `subteam_members_changed` — 用户组成员发生变化

- [Slack Socket Mode 配置说明](https://api.slack.com/apis/connections/socket)

###### 7. 把 Slack 凭证填回 Ankole

确认“同步通讯录”和“实时同步通讯录变更”已开启，然后选择“验证配置并登录”。在 Slack 授权页完成登录。

- Client ID：Basic Information 中的 Client ID
- Client Secret：同一页的 Client Secret
- Workspace ID：可选。填写 Slack 网页地址 /client/T… 中以 T 开头的值，可以预先指定登录的 Workspace；它不限制通讯录同步范围
- Bot User OAuth Token：刚才取得的 xoxb- token
- App-Level Token：刚才取得的 xapp- token

###### 8. 确认登录和首次同步

浏览器回到 Ankole 后，当前 Slack 用户会成为这套实例的第一位 root 管理员。打开 Console → 身份源提供商，选择 slack-main，再选择“运行全量同步”。

同步完成后，在 Console → 主体和权限组中确认 workspace 成员与 Slack 用户组已经出现。

##### 高级设置 · 只做登录、实时同步与凭证更新

只有在你明确不需要通讯录时，才关闭默认同步。

###### 1. 只使用 Slack 登录

关闭“同步通讯录”后，Bot User OAuth Token 不再必填，实时同步也会随之关闭；Client ID 和 Client Secret 仍然必填。

###### 2. 暂时不用实时同步

保留“同步通讯录”、关闭“实时同步通讯录变更”时，只需要 Bot User OAuth Token，不需要 App-Level Token。成员变化会在下一次全量同步后出现。

###### 3. 更新 scope 或 token

Slack app 的 scope 或 Bot Token 改动后，重新 Install to Workspace，再把新 token 写回身份源提供商。App Token 轮换后也要同步更新，否则 Socket Mode 无法连接。

#### Microsoft Entra ID

注册一个单租户 Entra 应用，用它登录 Console、读取用户和组，并通过 Microsoft Graph 接收目录变更。

**开始之前**

- 可注册应用并授予管理员同意的 Entra 管理员
- Ankole 对外可访问的 HTTPS 地址
- 用于首次登录的租户成员账号

##### 基础设置 · 配置 Microsoft Entra ID

###### 1. 先从 Ankole 复制回调地址

打开 https://<ANKOLE_HOST>/setup，输入 activation code。在插件页勾选“Microsoft 365 适配器”并保存，然后选择 Entra ID。

“配置 ID（Provider ID）”保持 entra-id-main。使用页面上的“复制”按钮复制登录回调地址。

###### 2. 注册单租户应用

打开 Microsoft Entra 管理中心，进入 Entra ID → App registrations → New registration。填写名称，并选择 Accounts in this organizational directory only。

在 Redirect URI 中选择 Web，粘贴 Ankole 的完整回调地址，然后选择 Register。

- [Microsoft 应用注册说明](https://learn.microsoft.com/en-us/entra/identity-platform/quickstart-register-app)

###### 3. 复制三个应用凭证

在应用的 Overview 页面复制 Application (client) ID 和 Directory (tenant) ID。

进入 Certificates & secrets → Client secrets → New client secret。创建后立即复制 Value；不要复制 Secret ID，因为离开页面后 Value 不会再次显示。

###### 4. 授予 Microsoft Graph 权限

进入 API permissions → Add a permission → Microsoft Graph。添加下面三项，然后选择 Grant admin consent for <组织名>。三项状态都应显示已授予。

- `User.Read` — Delegated permission
- `User.Read.All` — Application permission
- `Group.Read.All` — Application permission

- [Microsoft Graph 权限参考](https://learn.microsoft.com/en-us/graph/permissions-reference)

###### 5. 把 Entra 配置填回 Ankole

确认“同步通讯录”和“实时同步通讯录变更”已开启，然后选择“验证配置并登录”。用当前租户内的账号完成登录。

- 目录（租户）ID：Directory (tenant) ID
- 应用程序（客户端）ID：Application (client) ID
- 客户端密码值：刚才复制的 secret Value
- Ankole 公网地址：https://<ANKOLE_HOST>，不要填写内部容器或集群地址

###### 6. 确认登录和首次同步

登录成功后，当前用户会成为第一位 root 管理员。打开 Console → 身份源提供商，选择 entra-id-main，再选择“运行全量同步”。

同步完成后，在主体和权限组中确认目录里的用户与组已经出现。

##### 高级设置 · Graph 实时通知、来宾和组过滤

实时通知需要 Microsoft Graph 能从公网访问 Ankole。

###### 1. 确认实时通知地址可访问

开启“实时同步通讯录变更”时，“Ankole 公网地址”必须是有效的公网 HTTPS 地址。Ankole 会在其下建立 /webhooks/v1/entra-id/entra-id-main/directory 接口，并自动创建和续订 Graph 订阅。

- [Microsoft Graph 变更通知说明](https://learn.microsoft.com/en-us/graph/change-notifications-overview)

###### 2. 没有公网入口时关闭实时同步

如果 Graph 无法访问这套实例，请在保存前关闭“实时同步通讯录变更”。全量同步仍然可用，“Ankole 公网地址”也不再必填。

###### 3. 按需包含来宾或筛选组

“包含来宾用户”默认关闭。“同步组筛选条件”接受 Microsoft Graph 的 OData $filter；先让完整同步成功，再添加过滤条件，避免把权限问题误判成过滤结果。

#### Google Workspace

Google 登录和通讯录读取使用两套凭证：OAuth Client 负责登录，启用全网域授权的服务账号负责同步用户与群组。

**开始之前**

- Google Workspace 超级管理员
- 可管理 Google Cloud 项目的账号
- Ankole 对外可访问的 HTTPS 地址

##### 基础设置 · 配置 Google Workspace

###### 1. 先从 Ankole 复制回调地址

打开 https://<ANKOLE_HOST>/setup，输入 activation code。在插件页勾选“Google Workspace 适配器”并保存，然后选择 Google Workspace。

“配置 ID（Provider ID）”保持 google-workspace-main。使用页面上的“复制”按钮复制登录回调地址。

###### 2. 准备 Google Cloud 项目

打开 Google Cloud Console，选择或新建一个归企业管理的项目。在 API Library 中搜索并启用 Admin SDK API。

进入 Google Auth platform，按提示完成应用信息设置。Audience 选择 Internal，让应用只面向本 Workspace 的员工。

- [打开 Google Cloud Console](https://console.cloud.google.com/)

###### 3. 创建用于登录的 OAuth Client

进入 Google Auth platform → Clients → Create client，Application type 选择 Web application。

在 Authorized redirect URIs 中粘贴 Ankole 的完整回调地址。创建后复制 Client ID 和 Client Secret。

- [Google OAuth Client 配置说明](https://developers.google.com/workspace/guides/create-credentials)

###### 4. 创建用于同步通讯录的服务账号

进入 IAM & Admin → Service Accounts，创建服务账号。打开该账号的详情，在 Domain-wide delegation 中启用 Google Workspace 全网域授权，并记下它的数字 Client ID。

进入 Keys → Add key → Create new key，选择 JSON。下载的完整 JSON 文件稍后要粘贴到 Ankole；不要把它提交到代码仓库。

- [Google 服务账号配置说明](https://developers.google.com/identity/protocols/oauth2/service-account)

###### 5. 在 Workspace 中批准全网域授权

打开 admin.google.com，进入 Security → Access and data control → API Controls → Manage Domain Wide Delegation，选择 Add new。

Client ID 填服务账号的数字 Client ID。OAuth scopes 是一个输入框，直接复制下面这一整行并粘贴，然后授权。

**一次复制全部 OAuth scopes**

```text
https://www.googleapis.com/auth/admin.directory.user.readonly,https://www.googleapis.com/auth/admin.directory.group.readonly,https://www.googleapis.com/auth/admin.directory.group.member.readonly
```

- [Google Directory API 授权说明](https://developers.google.com/workspace/admin/directory/v1/guides/authorizing)

###### 6. 把 Google 配置填回 Ankole

确认“同步通讯录”已开启，然后选择“验证配置并登录”。使用“允许登录的 Workspace 域名”中的 Workspace 账号完成登录。

- OAuth 客户端 ID / OAuth 客户端密钥：Web application 类型的 OAuth Client 凭证
- 允许登录的 Workspace 域名：例如 example.com，不要带 @
- 服务账号 JSON 密钥：JSON 密钥文件的完整内容
- 委派管理员邮箱：服务账号要模拟的 Workspace 管理员邮箱，该账号必须能读取用户和群组

###### 7. 确认登录和首次同步

登录成功后，当前用户会成为第一位 root 管理员。打开 Console → 身份源提供商，选择 google-workspace-main，再选择“运行全量同步”。

同步完成后，在主体和权限组中确认 Workspace 用户、群组及成员关系已经出现。

##### 高级设置 · 域名边界、同步节奏与服务账号

Google Workspace 只支持全量同步，不接收实时目录事件。

###### 1. 严格填写允许登录的 Workspace 域名

只填写企业实际使用的 Workspace 域名。Ankole 还会检查邮箱已经过 Google 验证，并且账号带有 Workspace hosted-domain 标记。普通 gmail.com 账号没有这个标记，即使 Google 登录成功也会被拒绝。

###### 2. 目录变化后再运行全量同步

Google Workspace 适配器不提供实时同步。新增员工、调整群组或停用账号后，在 Console → 身份源提供商中重新运行全量同步。

###### 3. 缩小服务账号权限

首次验证完成后，可以把“委派管理员邮箱”换成只具备用户与群组读取权限的专用管理员账号。轮换 JSON 密钥时，要同时更新 Ankole 中的“服务账号 JSON 密钥”。

#### 飞书 / Lark

创建一个企业自建应用，用 App ID 和 App Secret 完成登录、同步员工与部门，并通过长连接接收通讯录变更。

**开始之前**

- 可创建并发布企业自建应用的管理员
- 可审批通讯录权限的企业管理员
- 用于首次登录的飞书或 Lark 员工账号

##### 基础设置 · 配置飞书或 Lark IdP

###### 1. 先从 Ankole 复制回调地址

打开 https://<ANKOLE_HOST>/setup，输入 activation code。在插件页勾选“飞书适配器”并保存，然后选择飞书。

“配置 ID（Provider ID）”保持 lark-main。使用页面上的“复制”按钮复制登录回调地址。

###### 2. 创建企业自建应用

飞书企业打开飞书开放平台，海外 Lark 企业打开 Lark Developer。选择创建企业自建应用或 Custom App。

进入“凭证与基础信息”或 Credentials & Basic Info，复制 App ID 和 App Secret。

- [打开飞书开放平台](https://open.feishu.cn/app)
- [打开 Lark Developer](https://open.larksuite.com/app)

###### 3. 登记登录回调地址

进入“开发配置 → 安全设置 → 重定向 URL”，添加从 Ankole 复制的完整地址。Lark 后台对应 Security Settings → Redirect URLs。

回调地址必须逐字一致。域名、协议、端口或 Provider ID 有一处不同，登录都会失败。

- [飞书网页应用登录说明](https://open.feishu.cn/document/sso/web-application-end-user-consent/guide)

###### 4. 批量开通登录和通讯录权限

进入“权限管理”，选择“批量导入 / 导出权限”，粘贴下面的 JSON 并确认导入。需要企业管理员审批的权限，要等状态变成已开通再继续。

**批量导入 JSON**

```text
{
  "scopes": {
    "tenant": [
      "contact:contact.base:readonly",
      "contact:department.base:readonly",
      "contact:department.organize:readonly",
      "contact:user.base:readonly",
      "contact:user.department:readonly",
      "contact:user.email:readonly",
      "contact:user.employee_id:readonly"
    ],
    "user": [
      "contact:user.employee_id:readonly"
    ]
  }
}
```

###### 5. 配置长连接通讯录事件

进入“事件与回调 → 事件配置”，选择“使用长连接接收事件”。添加下面七个事件；Lark 后台选择对应的 WebSocket 接收方式。

- `contact.user.created_v3`
- `contact.user.updated_v3`
- `contact.user.deleted_v3`
- `contact.department.created_v3`
- `contact.department.updated_v3`
- `contact.department.deleted_v3`
- `contact.scope.updated_v3`

###### 6. 设置可用范围并发布版本

在应用管理中把首位管理员和需要同步的部门加入可用范围。进入“版本管理与发布”，创建版本并发布；未发布的权限、回调地址和事件配置不会对员工生效。

###### 7. 把飞书或 Lark 凭证填回 Ankole

默认的通讯录同步设置通常无需修改。选择“验证配置并登录”。完成授权后，打开 Console → 身份源提供商，对 lark-main 运行一次全量同步。

最后在主体和权限组中确认员工、部门和成员关系已经出现。

- App ID / App Secret：自建应用“凭证与基础信息”中的值
- 服务区域：中国大陆选择“飞书（中国大陆）”；海外选择“Lark（海外）”

##### 高级设置 · 长连接、应用范围与聊天应用

长连接由控制面主动连出，不需要为飞书开放 webhook。

###### 1. 暂时不用实时同步

可以在“高级设置（通常无需修改）”中关闭“实时同步通讯录变更”，只保留全量同步。此时不必订阅通讯录事件，但员工和部门的变化要等下一次全量同步。

###### 2. 用应用可用范围限制登录人群

飞书和 Lark 的登录边界由应用可用范围决定。新增部门或员工后，更新可用范围并发布新版本，否则这些用户即使有企业账号也无法登录。

###### 3. 身份应用和聊天应用分开

技术上可以复用同一个自建应用，但正式使用时建议分别创建。IdP 应用只保留登录和通讯录权限，聊天应用只保留机器人权限；两者仍可使用同一个 platformSubjectNamespace 关联同一组织。

#### 钉钉

创建一个企业内部应用，用同一组 Client ID 和 Client Secret 完成登录、读取组织通讯录，并通过 Stream 接收人员与部门变更。

**开始之前**

- 可创建并发布企业内部应用的钉钉管理员
- 可审批通讯录接口权限的管理员
- 用于首次登录的本组织员工账号

##### 基础设置 · 配置钉钉 IdP

###### 1. 先从 Ankole 复制回调地址

打开 https://<ANKOLE_HOST>/setup，输入 activation code。在插件页勾选“钉钉适配器”并保存，然后选择钉钉。

“配置 ID（Provider ID）”保持 dingtalk-main。使用页面上的“复制”按钮复制登录回调地址。

###### 2. 创建企业内部应用

打开钉钉开发者后台，进入“应用开发 → 企业内部应用”，选择创建应用。进入“基础信息 → 凭证与基础信息”，复制 Client ID（原 AppKey）和 Client Secret（原 AppSecret）。

- [打开钉钉开发者后台](https://open-dev.dingtalk.com/)

###### 3. 登记登录回调地址

进入“开发配置 → 安全设置 → 重定向 URL（回调域名）”，粘贴从 Ankole 复制的完整地址并保存。

登录 scope 保持 openid corpid。corpid 用来把登录用户限定在当前钉钉组织。

###### 4. 申请登录和通讯录接口权限

进入“权限管理 → 通讯录管理”，申请下面这些接口对应的读取权限。通讯录权限范围选择“全部员工”；如果只能选部分员工，就必须覆盖准备登录和同步的所有部门。

- `Contact.User.Read` — 通讯录个人信息读权限
- `根据 unionId 获取用户 userid`
- `查询用户详情`
- `获取子部门 ID 列表`
- `查询部门详情`
- `获取部门用户基础信息`

###### 5. 用 Stream 订阅通讯录变更

进入“事件与回调”，接收方式选择 Stream 模式。添加下面十个事件；它们分别覆盖员工入职、更新、离职、激活，以及部门和管理员变更。

- `user_add_org`
- `user_modify_org`
- `user_leave_org`
- `user_active_org`
- `org_dept_create`
- `org_dept_modify`
- `org_dept_remove`
- `org_admin_add`
- `org_admin_remove`
- `org_remove`

- [钉钉员工变更 Stream 事件说明](https://open.dingtalk.com/document/orgapp/personnel-platform-employee-change-event-stream)

###### 6. 设置可用范围并发布版本

把首位管理员和需要同步的部门加入应用可用范围。进入版本管理，创建并发布一个包含回调地址、权限和事件配置的版本。未发布的应用不能完成浏览器登录。

###### 7. 把钉钉凭证填回 Ankole

确认“同步通讯录”和“实时同步通讯录变更”已开启，然后选择“验证配置并登录”。Ankole 会先校验这组凭证，再跳转到钉钉登录。

登录成功后，打开 Console → 身份源提供商，对 dingtalk-main 运行一次全量同步，再到主体和权限组检查员工与部门。

- Client ID（AppKey）：凭证与基础信息中的 Client ID
- Client Secret（AppSecret）：同一页的 Client Secret

##### 高级设置 · Stream、通讯录范围与分页

钉钉长连接由控制面主动连出，不需要公网 webhook。

###### 1. 只保留全量同步

关闭“实时同步通讯录变更”后，可以不订阅 Stream 通讯录事件。员工和部门的变化会在下一次全量同步后出现。

###### 2. 检查通讯录授权范围

同步缺人或缺部门时，先检查应用在“权限管理”中的通讯录授权范围。接口权限已通过，但数据不在授权范围内，也会得到不完整结果。

###### 3. 不要先改默认分页

分页大小默认 50，允许范围为 1–100。先用默认值完成一次全量同步；只有明确遇到限流或响应体问题时再调整。

#### 企业微信

用企业自建应用完成扫码登录，用“通讯录同步”的专用 Secret 周期性同步成员与部门。登录和通讯录接口都要求固定出口 IP。

**开始之前**

- 企业微信超级管理员账号
- 出口 IP 固定的 Ankole 部署
- 用于首次登录的本企业成员账号

##### 基础设置 · 配置企业微信 IdP

###### 1. 先从 Ankole 复制回调地址

打开 https://<ANKOLE_HOST>/setup，输入 activation code。在插件页勾选“企业微信适配器”并保存，然后选择企业微信。

“配置 ID（Provider ID）”保持 wecom-main。使用页面上的“复制”按钮复制登录回调地址。

###### 2. 记录企业 ID

打开企业微信管理后台，进入“我的企业 → 企业信息”，复制企业 ID（CorpID）。

- [打开企业微信管理后台](https://work.weixin.qq.com/wework_admin/)

###### 3. 创建自建应用，并配置可信域名和可信 IP

进入“应用管理 → 自建 → 创建应用”。创建后打开应用详情，复制 AgentId 和 Secret。

在同一页设置两项：“网页授权及 JS-SDK”可信域名填 Ankole 的域名；“企业可信 IP”填部署的固定出口 IP。

> 缺少可信 IP 时，登录和接口调用会报错 60020。可信域名不含回调地址的域名时，扫码后无法回跳。

###### 4. 开启通讯录同步，并取得专用 Secret

进入“安全与管理 → 管理工具 → 通讯录同步”，开启 API 接口同步，复制这里的专用 Secret，并配置它自己的可信 IP。

自 2022 年 6 月起，普通应用 Secret 拿不到成员姓名等字段。不配置这个专用 Secret，就无法同步通讯录，登录主体也没有姓名。

###### 5. 把企业微信配置填回 Ankole

确认“启用登录”和“同步通讯录”已开启，然后选择“验证配置并登录”，用企业微信扫码完成登录。

- 企业 ID（CorpID）：“企业信息”中的企业 ID
- 自建应用 AgentId / Secret：应用详情中的两个值
- 通讯录同步 Secret：“通讯录同步”页面的专用 Secret

###### 6. 确认登录和首次同步

登录成功后，当前成员会成为第一位 root 管理员。打开 Console → 身份源提供商，选择 wecom-main，再选择“运行全量同步”。

同步完成后，在主体和权限组中确认成员与部门已经出现。

##### 高级设置 · 同步节奏、可信 IP 与只做登录

企业微信没有实时通讯录事件，变更靠周期性全量同步收敛。

###### 1. 目录变化后运行全量同步

企业微信不向 Ankole 推送通讯录变更。新增成员或调整部门后，在 Console → 身份源提供商中重新运行全量同步，或等待下一次周期同步。

###### 2. 出口 IP 变化时更新两处可信 IP

自建应用和通讯录同步的可信 IP 各配各的。部署迁移或出口 IP 变化后，两处都要更新，否则登录和同步会同时报 60020。

###### 3. 只做登录

关闭“同步通讯录”后，通讯录同步 Secret 不再必填。此时登录主体只有企业微信账号 ID，没有姓名，也没有部门权限组。

<a id="llm-providers"></a>

## 3. 添加模型提供商，并创建 Agent

Agent 的模型档案只保存模型引用，因此要先添加模型提供商。登录 Console，打开 **模型提供商 → 新增提供商**，选择 Provider 类型，填写固定的 Provider ID、服务地址和凭证，然后保存。

控制面会加密保存 Provider 凭证。不要再把凭证写进部署环境变量或 Agent 文件。

打开 **Agents → New Agent**。填写一个以后不再更改的 UID 和容易辨认的名称，并在 mission 中写清它负责什么，以及怎样才算交付合格。

接着配置 model profile：

| Profile | 建议用途 |
|---|---|
| `primary` | 大多数任务使用的模型 |
| `light` | 简单、高频的任务 |
| `heavy` | 复杂推理任务 |

这三个 profile 缺一不可。第一次配置时，可以让它们共用同一个已经验证可用的 provider 和模型。确认聊天可以正常收发后，再按需要拆分。

### 高级设置 · 可选 Agent 档案和 Brain 维护配置

Agent 能力按需配置；Brain 只保留实例级检索模型。

#### 1. 按需配置 Agent 能力档案

- `后台 Agent 任务` — 使用 coding 档案运行持久的后台 Agent 任务。普通对话不会因为代码较多而选择这个档案。
- `vision_fallback` — 主模型无法读取图像时改用的模型。
- `web_search / web_fetch` — 负责搜索和抓取网页的 provider。
- `image_generate` — 负责生成图像的模型。

#### 2. 选择 Brain 维护 Agent 和检索模型

打开 Console → 系统配置 → Brain。选择负责维护 Brain 的活跃 Agent。Brain 的所有模型调用都以该 Agent 的身份执行，并把用量归到该 Agent。停用后，模型调用和本地网页抓取都会停止，直到重新启用或更换 Agent。再通过链接进入该 Agent 的模型配置。

- `brain.maintainer_agent_uid` — 使用所选 Agent 的 light profile 抽取知识、heavy profile 执行 Dreaming、web_fetch profile 读取 URL Source。未配置 web_fetch 时改用本地 ankole-browser。
- `brain.embedding_model` — 启用向量检索。请选择 Provider 和模型，并填写 embedding 维度。
- `brain.rerank_model` — 对融合后的搜索结果重新排序；留空会保留融合顺序。

- [Brain 指南](https://ankole.agentbull.com/zh-Hans-CN/docs/brain/index.md)
- [AppConfigure 配置键](https://ankole.agentbull.com/zh-Hans-CN/docs/app-configuration/index.md)

<a id="chat-channels"></a>

## 4. 连接聊天渠道并创建信号路由规则

先在 Console 中启用聊天平台对应的 Control Plane Plugin，再去该平台创建机器人或应用。即使 IdP 和聊天渠道来自同一平台，正式使用时也建议分别创建应用。这样，登录和通讯录权限不会与机器人权限混在一起，凭证轮换和应用发布也互不影响。

一个聊天应用通常对应一个机器人身份。多个 Agent 如果要使用不同的机器人名称、头像或权限，就要在同一平台创建多套应用。准备好应用后，紧接着在 Console 中创建路由规则，把这套应用连接到指定的 Agent。

Slack、Teams、飞书 / Lark、钉钉和企业微信是企业平台：它们的用户来自 IdP 同步的通讯录。Telegram、Discord、LINE 和 WhatsApp 是消费级 IM：它们的用户没有员工记录，管理员需要先在**身份 → 待绑定账号**中把每个新发信人映射到账号，Agent 才会为其服务；如果某个已知账号已拥有该手机号，WhatsApp 会自行完成映射。电子邮件接入的是一个专用邮箱，发信人只能通过显式的邮箱身份绑定被识别。

**选择聊天渠道**

- Slack · 默认
- Microsoft Teams
- 飞书 / Lark
- 钉钉
- 企业微信
- Telegram
- Discord
- LINE
- WhatsApp
- Email

### Slack · 默认

Slack 通过 Socket Mode 连接。Ankole 会主动建立 WebSocket，所以不必向 Slack 开放公共 webhook。

**开始之前**

- 有权限创建并安装 Slack app
- 一个测试频道或私聊

#### 基础设置 · 准备 Slack app

##### 1. 新建应用并启用 Socket Mode

新建一个 Slack app。在 Basic Information 中创建 App-Level Token，添加下面这项权限，再启用 Socket Mode。

- `connections:write` — 允许 App Token 建立 Socket Mode 连接

##### 2. 订阅聊天和频道状态事件

进入 Event Subscriptions → Subscribe to bot events，逐项添加。这个集合与 Slack 适配器当前实现的消息、reaction 和频道状态能力一致。

- `app_mention` — 接收频道中的 @ 消息
- `message.channels` — 接收公开频道消息
- `message.groups` — 接收私有频道消息
- `message.im` — 接收私聊消息
- `message.mpim` — 接收多人私聊消息
- `reaction_added` — 接收新增 reaction
- `reaction_removed` — 接收移除 reaction
- `member_joined_channel` — 同步频道成员加入
- `member_left_channel` — 同步频道成员离开
- `channel_rename` — 同步频道改名
- `group_rename` — 同步私有频道改名
- `channel_deleted` — 同步频道删除
- `channel_archive` — 同步频道归档

##### 3. 启用私聊入口

进入 App Home → Show Tabs，打开 Display Messages tab，并勾选 Allow users to send Slash commands and messages from the messages tab。只打开 Messages tab 会展示消息页，但用户仍不能向 Agent 发送私聊。

##### 4. 启用 Slack 原生交互

进入 Interactivity & Shortcuts 并启用 Interactivity。Socket Mode 会通过现有 WebSocket 交付 Block Kit 按钮点击，不需要填写公共 Request URL。

##### 5. 添加完整聊天 scope，并安装应用

在 OAuth & Permissions → Bot Token Scopes 中逐项添加。这个集合只覆盖 Ankole 实际调用的 Slack API；不要添加未使用的 assistant、工作流、文档或会议权限。

- `app_mentions:read` — 读取发给机器人的 @ 消息
- `channels:read` — 同步公开频道及成员
- `channels:history` — 读取公开频道消息并核对回信
- `groups:read` — 同步私有频道及成员
- `groups:history` — 读取私有频道消息并核对回信
- `im:read` — 同步私聊会话
- `im:history` — 读取私聊消息并核对回信
- `mpim:read` — 同步多人私聊会话及成员
- `mpim:history` — 读取多人私聊消息并核对回信
- `chat:write` — 发送、更新和撤回机器人消息
- `reactions:read` — 接收 reaction 变化
- `reactions:write` — 添加和移除 reaction
- `files:read` — 读取消息中的文件
- `files:write` — 上传 Agent 发送的文件
- `users:read` — 识别频道成员并排除机器人账号

> 修改 scope 后，必须重新安装应用到 workspace，并把新的 Bot Token 写回 Ankole。

##### 6. 准备 Console 字段

- `botToken`: `xoxb-…` — Bot User OAuth Token，必须以 xoxb- 开头。
- `appToken`: `xapp-…` — Socket Mode 使用的 App-Level Token，必须以 xapp- 开头。

#### 高级设置 · 消息策略、身份映射和 Slack IdP

Slack 负责交付事件；信号路由规则仍决定 Agent 如何处理没有 @ 它的消息。

##### 1. 选择未直接提及 Agent 时的策略

上面的事件和 scope 允许 Slack 交付完整会话，但不会替代 Ankole 的信号路由策略。第一次测试使用 addressed_only；只有明确需要旁听或主动介入时，才选择 observe_all 或 may_intervene。

##### 2. 同一个 App 也承担 Slack IdP 时补充权限

建议正式环境使用独立 IdP App。确需复用时，额外添加下面的 Bot scopes 和通讯录事件。

- `users:read.email` — 同步成员邮箱
- `usergroups:read` — 同步用户组及成员关系
- `team_join` — 接收成员加入事件
- `user_change` — 接收成员资料变化
- `subteam_created` — 接收用户组创建
- `subteam_updated` — 接收用户组更新
- `subteam_members_changed` — 接收用户组成员变化

##### 3. 设置平台身份命名空间

- `platformSubjectNamespace`: `slack-main` — 同一个 Slack workspace 只用一个命名空间。Slack 也作为 IdP 时，两处配置填写同一个值。
- `userName`: `Slack` — 机器人发送消息时显示的名称。

##### 4. 另外创建 Slack IdP 应用

正式使用时，建议为 SSO 和通讯录另建一个 Slack app。IdP 应用只授予登录和通讯录权限；聊天应用只授予机器人权限。两处可以填写相同的 platformSubjectNamespace 来关联同一个 workspace。

### Microsoft Teams

Teams 通过 Bot Framework webhook 把消息发给 Ankole，因此 Ankole 必须有能从公网访问、证书受信任的 HTTPS 地址。

**开始之前**

- 一个 Entra ID 租户
- 可从公网访问的 Ankole HTTPS 域名
- 有权限注册并安装 Teams bot

#### 基础设置 · 配置 Teams bot

##### 1. 注册应用和机器人

先在 Entra ID 中注册应用，再创建 Azure Bot。把 Application (client) ID 填入 appID，并创建一个 client secret 作为 appPassword。

##### 2. 设置 Bot Framework 消息端点

路径中的 appID 必须与信号路由规则中填写的 App ID 完全一致。

```text
https://<ANKOLE_HOST>/webhooks/v1/teams/<appID>/messages
```

##### 3. 选择 Microsoft Bot Framework 应用类型

- `botTenancy`: `single_tenant` — 大多数企业先使用单租户模式。
- `tenantID` — Entra 租户的 GUID。单租户 bot 必填。

##### 4. 准备 Console 字段

- `appID` — Azure Bot registration 中的 Microsoft App ID，必须是 GUID。
- `appPassword` — Microsoft App client secret。

#### 高级设置 · 跨 Entra 租户的 bot、Entra 身份和通讯录 webhook

机器人要服务多个 Entra 租户，或还要用 Entra ID 登录 Console 时，再看这里。

##### 1. 让 Bot Framework 应用服务多个 Entra 租户

先确认 Azure Bot registration 的 app type 允许多个 Entra 租户使用，再把 botTenancy 设为 multi_tenant。Ankole 会从 botframework.com 获取 bot token。这里的 multi_tenant 是 Microsoft 的应用注册类型，不会改变 Ankole 实例的边界。

##### 2. Teams 与 Entra 共用身份映射

- `platformSubjectNamespace`: `entra-id-main` — Teams 和 Entra IdP 对应同一个组织时，两处配置填写同一个值。
- `userName`: `Teams` — 机器人发送消息时显示的名称。

##### 3. 在 IdP 中配置 Graph 通讯录同步

全量同步和实时同步都在 Entra IdP 配置中开启。Graph notification 使用单独的 directory webhook，同样需要可从公网访问的 HTTPS 域名。

### 飞书 / Lark

Ankole 通过出站长连接接收飞书或 Lark 消息。主机能访问互联网即可，不必开放消息 webhook。

**开始之前**

- 有权限创建企业自建应用
- 测试用户已加入应用可用范围

#### 基础设置 · 配置飞书或 Lark 应用

##### 1. 创建应用并启用机器人能力

创建企业自建应用，启用机器人，并把测试用户加入应用可用范围。

##### 2. 批量添加完整聊天权限

进入“权限管理”，选择“批量导入 / 导出权限”，粘贴下面的 JSON 并确认导入。这个集合与适配器实际调用的机器人、消息、reaction、文件、卡片、群聊和成员 API 一致；聊天应用不需要通讯录写入、文档、会议或加急消息权限。

**批量导入 JSON**

```text
{
  "scopes": {
    "tenant": [
      "application:bot.basic_info:read",
      "cardkit:card:write",
      "im:chat:read",
      "im:chat.members:bot_access",
      "im:chat.members:read",
      "im:message:send_as_bot",
      "im:message:readonly",
      "im:message:update",
      "im:message:recall",
      "im:message.group_at_msg:readonly",
      "im:message.p2p_msg:readonly",
      "im:message.reactions:read",
      "im:message.reactions:write_only",
      "im:resource"
    ],
    "user": []
  }
}
```

##### 3. 启用长连接，并添加适配器事件

在“事件与回调”中选择“使用长连接接收事件”，再逐项添加下面的事件。平台检查连接状态时，请保持 Ankole 控制面运行。

- `im.message.receive_v1` — 接收发给机器人的消息
- `im.message.recalled_v1` — 接收消息撤回
- `im.message.reaction.created_v1` — 接收新增 reaction
- `im.message.reaction.deleted_v1` — 接收移除 reaction
- `im.chat.member.bot.added_v1` — 同步机器人进群
- `im.chat.member.bot.deleted_v1` — 同步机器人被移出群
- `im.chat.member.user.added_v1` — 同步用户进群
- `im.chat.member.user.deleted_v1` — 同步用户离群
- `im.chat.updated_v1` — 同步群信息变化
- `im.chat.disbanded_v1` — 同步群解散
- `card.action.trigger` — 接收卡片交互

##### 4. 发布版本，并记录 Console 所需字段

- `appID` — 企业自建应用的 App ID。
- `appSecret` — 企业自建应用的 App Secret。
- `domain`: `feishu 或 lark` — 飞书国内站选择 feishu，Larksuite.com 选择 lark。

#### 高级设置 · 读取完整群聊、SSO 和身份映射

需要让 Agent 读取群里的所有消息，或同一个应用还要用于 SSO 时，再看这里。

##### 1. 让 Agent 读取没有 @ 它的群消息

要使用 observe_all 或 may_intervene，先在权限管理中添加下面的权限。第一次测试请使用 addressed_only。

- `im:message.group_msg` — 读取群内没有 @ 机器人的消息

##### 2. 另外创建飞书 IdP 应用

正式使用时，建议为 SSO 和通讯录另建一个自建应用。IdP 应用只保留登录和通讯录权限；聊天应用只保留机器人权限。两处可以填写相同的 platformSubjectNamespace 来关联同一个组织。

##### 3. 设置身份映射字段

- `platformSubjectNamespace`: `lark-main` — 聊天连接和飞书 IdP 对应同一个组织时，两处配置填写同一个值。
- `userName`: `Lark / Feishu` — 机器人发送消息时显示的名称。

##### 4. 每个启用的 binding 使用独立应用

为每个 binding 创建独立的飞书或 Lark 应用；binding 被禁用后会释放该应用。

- 一个 Agent 可以同时启用多个飞书 / Lark binding。
- 每个启用的 binding 必须使用不同的 domain 与 appID 组合。

### 钉钉

钉钉通过 Stream 模式连接。同一组 AppKey 和 AppSecret 既用于机器人认证，也用于接收消息。

> **钉钉会限制部分功能**
>
> 在群聊中，Agent 看不到完整的聊天记录，只能收到明确 @ 它的消息。钉钉卡片是模板托管的，流式卡片回复需要先在卡片平台搭一份 AI 卡片模板；没有模板时回复保持普通 Markdown。
>
> 这些限制会明显影响 Ankole 的完整功能和使用体验。长期记忆系统也只能从 Agent 收到的消息片段中积累上下文，效果会受到影响。条件允许时，请优先选择其他聊天渠道。

**开始之前**

- 有权限创建企业内部应用和机器人
- 一个测试会话

#### 基础设置 · 准备钉钉机器人

##### 1. 创建企业内部应用与机器人

启用机器人，并把应用开放给测试用户。钉钉会发送私聊消息，以及群里明确 @ 机器人的消息。

##### 2. 启用 Stream 模式，并发布应用

Stream 连接直接使用这组应用凭证，所以不必设置公共消息 webhook。

##### 3. 准备 Console 字段

- `clientId` — 企业应用的 Client ID，也叫 AppKey；Stream clientId 填写同一个值。
- `clientSecret` — 企业应用的 Client Secret，也叫 AppSecret；Stream clientSecret 填写同一个值。
- `group_message_mode`: `addressed_only` — 钉钉群聊只支持此模式。
- `cardTemplateId` — 流式卡片回复用的 AI 卡片模板 id。首次测试可以留空；搭建步骤见下方“高级设置”。

#### 高级设置 · 搭建 AI 卡片模板、多个 Agent 与身份设置

要让回复以流式 AI 卡片呈现、为多个 Agent 配置机器人，或同时接入钉钉身份源时，再看这里。

##### 1. 创建 AI 卡片模板，并添加变量

钉钉卡片是模板托管的：版式保存在钉钉卡片平台上，Ankole 只往一组固定变量里写值。为一个钉钉组织搭一份模板即可，第一次大约需要二十分钟。前提：Agent 已能用纯文本回复，应用具备互动卡片实例写入和 AI 卡片流式更新权限。

打开钉钉开发者后台 → 卡片平台 → 新建模板，选择“AI 卡片”类别；只有这个类别带 AI 卡片容器，由它绘制输入指示、完成态和出错态。按下表逐个添加模板变量，名称必须完全一致；名称不一致时，对应区域在每次回复里都是空的。

- `state` — 文本。一行状态，例如正在运行的工具名。
- `answer` — Markdown（流式）。回复正文，每一帧全量重写。
- `thought` — Markdown。临时思考草稿，回复结束时清空。
- `plan` — 文本。执行计划与完成计数。
- `activity` — 文本。正在执行的工具调用，回复结束时清空。
- `results` — 文本。每条结构化结果一行。
- `receipts` — 文本。每条已记录的副作用一行。
- `actions` — 文本。按钮的 JSON 列表；Agent 不反问时为空。
- `meta` — 文本。触发原因、卡片序号、计数、耗时。

##### 2. 排布组件，并配置状态布局

在 AI 卡片组件中配置输入中、完成和出错布局。需要显示回复的每个布局都放置绑定 answer 的 Markdown 组件，并在输入中布局开启流式；meta 与 state 可用小号文本放顶部，plan 用文本组件，thought 和 activity 可放进折叠区，results 与 receipts 用文本组件。需要保留决策按钮时，输入中和完成布局都要放置绑定 actions 的操作区，并原样透传每个按钮的 value。

钉钉根据 AI 卡片原生生命周期切换布局：Ankole 在流式更新中发送 isFinalize 后进入完成态，发送 isError 后进入出错态。不要创建或绑定 flowStatus 或 flowStatusVar。

> 如果当前状态布局没有绑定 answer 的 Markdown 组件，该状态会显示空白卡片。每次修改状态布局后都要重新发布模板。

##### 3. 发布模板，填入 id 并验证

把模板关联到持有机器人的企业内部应用并发布，复制模板 id，填进路由规则的 cardTemplateId 并保存。改动在下一次回复生效，无需重启任何组件。

验证：发一条能产出好几句话的消息，应出现边写边显示的卡片；回复结束后输入指示停止，思考区与活动区清空。再问一个需要你做决定的问题，此时必须出现按钮，点击后本轮继续；不再对应待答问题的旧按钮会被忽略。长回复约每 2.5 KB 封口一张卡片、在新卡片上继续，卡片始终是会话中的一条新消息，表格等富结构以文本呈现，这些都是平台限制下的预期行为。

- 卡片空白：当前输入中、完成或出错布局没有绑定 answer，或模板没有重新发布。
- 某个区域始终为空：变量名与上表不一致，或组件没有绑定到它。
- 答案只在最后出现或完全不出现：answer 组件不是 markdown 流式组件。
- 回复结束后仍显示输入指示：查看控制面日志，确认钉钉接受了带 isFinalize 或 isError 的流式更新。
- 回复是普通 Markdown 消息：模板 id 为空、模板未发布到本应用，或钉钉拒绝了卡片内容，查控制面日志中的 param.contentUnsafe 或 param.cardNotExist。卡片路径永久失败时，本次回复一次性降级为普通 Markdown，回复仍会送达。
- 按钮出现但点击无反应：操作区没有原样透传按钮的 value。

##### 4. 为每个 Agent 使用独立机器人

每多接入一个 Agent，都要创建独立的机器人和凭证。

- 一个 Agent 同时只能启用一个钉钉 binding。
- 同一个 clientId 不能绑定给多个 Agent。

##### 5. 另行配置钉钉身份

钉钉也可以提供 OIDC 登录和通讯录同步，但这些功能要在 IdP 中配置。只配置聊天渠道和路由规则，不能用钉钉登录 Console。

- `platformSubjectNamespace`: `dingtalk-main` — 聊天连接和钉钉 IdP 对应同一个组织时，两处配置才填写同一个值。

### 企业微信

企业微信智能机器人通过一条出站长连接收发消息，不需要公网消息 webhook。平台强制每个机器人只允许一条长连接。

> **企业微信是限制最多的聊天渠道**
>
> 群聊里 Agent 只能收到明确 @ 机器人的消息；图片、语音、文件和视频只有单聊能收到，语音只有平台转写的文字。发出的消息无法撤回、无法编辑，也不能加表情回应。Agent 不能主动开启新会话：用户必须先在该会话给机器人发过消息，收到消息后的回复窗口为 24 小时。流式回复只在回复用户时可用，单条限时 10 分钟，长回答会拆成多条；发消息限频约每会话每分钟 30 条。交互卡片在用户点击后只有 5 秒更新窗口。通讯录变更没有实时同步。
>
> 这些限制全部来自平台本身，Ankole 无法绕过；长期记忆也只能从 Agent 收到的消息片段中积累上下文。条件允许时，请优先选择飞书 / Lark、Slack、Teams 或钉钉。

**开始之前**

- 能创建智能机器人的企业微信超级管理员
- 一个测试会话

#### 基础设置 · 准备企业微信机器人

##### 1. 用超级管理员创建智能机器人

打开企业微信管理后台，创建 API 模式的智能机器人，记录 Bot ID 和与它同页展示的 Secret。

> 机器人必须由企业超级管理员创建。否则消息里的用户 ID 是加密形态，永远无法与通讯录和登录身份对应。

##### 2. 不要让其他程序共用这个机器人

平台强制每个机器人只允许一条长连接。如果别的程序用同一个 Bot ID 连接，双方会互相顶掉；Ankole 检测到被顶掉后会停下等待，不会反复抢线。

##### 3. 准备 Console 字段

- `botId` — 智能机器人的 Bot ID。
- `secret` — 与 Bot ID 同页展示的长连接专用 Secret。
- `group_message_mode`: `addressed_only` — 企业微信群聊只支持此模式。

#### 高级设置 · 主动推送、身份映射与多个 Agent

Agent 要主动开口，用户必须先在该会话给机器人发过消息。

##### 1. 解锁主动推送

计划任务结果等主动消息只能发进用户已经激活的会话。让每位使用者先给机器人发一条消息；全新会话无法由 Agent 先发起。主动推送一次性发送完整 Markdown，不使用流式。

##### 2. 设置身份映射字段

- `platformSubjectNamespace`: `wecom-main` — 聊天连接和企业微信 IdP 对应同一家企业时，两处配置填写同一个值。
- `userName`: `企业微信 / WeCom` — 机器人发送消息时显示的名称。

##### 3. 为每个 Agent 使用独立机器人

每多接入一个 Agent，都要创建一个独立的智能机器人。

- 一个 Agent 同时只能启用一个企业微信 binding。
- 同一个 Bot ID 不能绑定给多个 Agent。

### Telegram

Telegram 使用 Bot API 长轮询。Ankole 通过出站连接调用 getUpdates，因此聊天链路不需要公网 webhook。Telegram 是消费级 IM：它的用户不是通讯录里的员工，所以要在第一条消息到来之前规划好身份映射。

**开始之前**

- 一个能与 @BotFather 对话的 Telegram 账号
- 一个测试私聊或群组

#### 基础设置 · 准备 Telegram 机器人

##### 1. 用 @BotFather 创建机器人

向 @BotFather 发送 /newbot，选择显示名称和一个以 bot 结尾的用户名，然后复制 BotFather 返回的 token。这个 token 是路由规则唯一需要的凭证。

##### 2. 允许机器人读取群消息

在 @BotFather 中打开 Bot Settings → Group Privacy，关闭隐私模式。隐私模式开启时，群组只会投递 /command@bot 形式的命令和对机器人的回复，@ 提及到不了 Agent，observe_all 或 may_intervene 也永远看不到其他消息。修改此设置后，需要把机器人移出群组再重新加入才会生效。

##### 3. 删除旧的 webhook

Ankole 轮询 Bot API，而 Telegram 在 token 设有 webhook 时拒绝轮询。如果其他程序曾用这个 token 配置过 webhook，请在启用规则前删除它。Ankole 会报告 webhook_configured，不会删除由其他系统拥有的 webhook。

```bash
curl -s "https://api.telegram.org/bot<botToken>/deleteWebhook"
```

##### 4. 准备 Console 字段

- `botToken`: `123456789:AA…` — @BotFather 返回的机器人 token。一个 token 只能属于一条已启用的路由规则。
- `group_message_mode`: `addressed_only` — 先从这里开始。私聊、@ 提及、/command@bot 命令或对机器人的回复都算指名 Agent。

#### 高级设置 · 身份映射、论坛话题与平台限制

展开以了解如何把 Telegram 用户映射到账号，以及 Agent 无法使用哪些 Telegram 功能。

##### 1. 把 Telegram 用户映射到账号

Telegram 用户没有员工记录，所以每个新发信人的自动映射账号都会失败。把「自动映射账号失败时」保持为「手动审核」：发信人会收到一条固定回复，出现在身份 → 待绑定账号列表中，由管理员把这个 Telegram 身份绑定到已有账号，例如一个本地密码账号。之后用户重新发送消息即可。只有在允许任何人与 Agent 对话的开放机器人上，才选择「自动创建独立账号」。

##### 2. 了解会话如何对应 Agent 会话

- 一个私聊、一个群组和一个超级群组各自构成一个 Agent 会话。
- 超级群组中的每个论坛话题是一个独立会话。
- 频道帖子、来自其他机器人的消息，以及匿名或访客发信人的消息会被忽略。

##### 3. 注意平台限制

- Bot API 无法下载大于 20 MB 的文件。Agent 能看到文件名和大小，但读不到内容。
- 用户删除消息时 Telegram 不发送任何事件，因此被删除的文字仍留在会话上下文中。
- 当 Telegram 可能已接受一次发送、但连接随后丢失时，Ankole 不会自动重发，而是走疑似重复流程，不会发出第二条回复。

##### 4. 为每个 Agent 使用独立机器人

一个机器人 token 只能绑定给一条已启用的路由规则。每多接入一个 Agent，都要在 @BotFather 中再创建一个机器人。

### Discord

Discord 通过出站 WebSocket 使用机器人 Gateway，因此聊天链路不需要公网 webhook。Discord 是消费级 IM：它的用户不是通讯录里的员工，所以要在第一条消息到来之前规划好身份映射。

**开始之前**

- 一个能在 Developer Portal 中创建应用的 Discord 账号
- 一个可以邀请机器人加入的服务器，或用于测试的私信

#### 基础设置 · 准备 Discord 机器人

##### 1. 创建应用和机器人

打开 Discord Developer Portal，创建 New Application，进入 Bot 页面，选择 Reset Token。立即复制 token；Discord 只显示一次。

##### 2. 启用消息内容 intent

在 Bot 页面的 Privileged Gateway Intents 下打开 Message Content Intent。没有它，Discord 投递的未提及机器人的服务器消息内容为空，Agent 只能读到私信和提及它的消息，observe_all 或 may_intervene 也看不到对话。Ankole 在连接前会读取应用标志，只有应用具备该 intent 时才会请求它。

> 不需要 Server Members Intent 和 Presence Intent。加入超过 100 个服务器的应用需要通过 Discord 验证才能保留消息内容 intent。

##### 3. 把机器人邀请进服务器

打开 OAuth2 → URL Generator，选择 bot scope，并添加下列权限。打开生成的 URL 并选择服务器。这组权限只覆盖 Ankole 调用的 Discord API。

- `View Channels` — 查看允许机器人进入的频道
- `Send Messages` — 发送 Agent 回复
- `Send Messages in Threads` — 在子区中回复
- `Read Message History` — 回复和编辑更早的消息
- `Attach Files` — 上传 Agent 发送的文件
- `Add Reactions` — 添加和移除表情回应

##### 4. 保持 Interactions Endpoint URL 为空

在 General Information 页面，不要填写 Interactions Endpoint URL。一旦设置，Discord 会把按钮点击发到该 URL 而不是 Gateway，Agent 回复中的按钮就永远到不了 Ankole。

##### 5. 准备 Console 字段

- `botToken` — Bot 页面上的机器人 token。一个 token 只能属于一条已启用的路由规则。
- `group_message_mode`: `addressed_only` — 先从这里开始。私信、@ 提及或对机器人的回复都算指名 Agent。

#### 高级设置 · 身份映射、子区与平台限制

展开以了解如何把 Discord 用户映射到账号，以及 Agent 无法使用哪些 Discord 功能。

##### 1. 把 Discord 用户映射到账号

Discord 用户没有员工记录，所以每个新发信人的自动映射账号都会失败。把「自动映射账号失败时」保持为「手动审核」：发信人会收到一条固定回复，出现在身份 → 待绑定账号列表中，由管理员把这个 Discord 身份绑定到已有账号，例如一个本地密码账号。之后用户重新发送消息即可。只有在允许任何人与 Agent 对话的开放服务器上，才选择「自动创建独立账号」。

##### 2. 了解会话如何对应 Agent 会话

- 一个私信和每个服务器频道各自构成一个 Agent 会话。
- 每个子区是一个独立会话。
- 来自其他机器人的消息、webhook 帖子、系统通知，以及没有文字和附件的消息会被忽略。
- Agent 的文字永远不会通知某个用户、角色或全体成员。Ankole 对发出的每条消息都禁用提及解析。

##### 3. 注意平台限制

- Ankole 最多下载 25 MB 的附件。更大的文件，Agent 能看到名称和大小，但读不到内容。
- 用户删除消息时 Discord 不发送 Ankole 使用的事件，因此被删除的文字仍留在会话上下文中。
- 回复按 2,000 字符拆分。一张卡片最多显示 25 个按钮。
- 当 Discord 可能已接受一次发送、但连接随后丢失时，Ankole 不会自动重发，而是走疑似重复流程，不会发出第二条回复。

##### 4. 为每个 Agent 使用独立机器人

一个机器人 token 只能绑定给一条已启用的路由规则。每多接入一个 Agent，都要再创建一个应用。

### LINE

LINE 通过 Messaging API webhook 把消息推送给 Ankole，必须有带可信证书的公网 HTTPS 地址。LINE 是消费级 IM：它的用户不是通讯录里的员工，所以要在第一条消息到来之前规划好身份映射。

> **LINE 限制了部分 Ankole 功能**
>
> Agent 的每条回复都是 push 消息，因为 LINE 的 reply token 在 webhook 之后一分钟就会过期，而一个 Agent 回合通常更长。push 消息计入 Official Account 的每月消息套餐，套餐必须覆盖预期流量。LINE 机器人不能编辑、撤回消息或加表情回应，不能发送文件，在最终答案之前也不显示实时进度。
>
> 这些限制全部来自平台本身，Ankole 无法绕过。

**开始之前**

- 一个 LINE Developers provider，以及创建 Messaging API channel 的权限
- 一个公网 HTTPS 的 Ankole 主机
- 一个用于测试的 LINE 账号

#### 基础设置 · 准备 LINE Official Account

##### 1. 创建 Messaging API channel 并收集凭证

在 LINE Developers Console 中，在你的 provider 下创建一个 Messaging API channel。从 Basic settings 复制 Channel ID 和 Channel secret，并在 Messaging API 标签页签发一个长期有效的 channel access token。

##### 2. 先创建路由规则，再验证 webhook

先在 Console 中保存并启用路由规则。对于未知的 Channel ID，Ankole 会用状态码 404 应答 webhook，因此在规则存在之前，LINE Developers Console 的 Verify 按钮会一直失败。

##### 3. 设置 webhook URL 并验证

在 Messaging API 标签页填入此 URL，打开 Use webhook，然后选择 Verify。路径中的 channelId 必须与路由规则里的 Channel ID 一致。Ankole 用 channel secret 检查每个请求的 x-line-signature，签名错误时以状态码 401 拒绝。

```text
https://<ANKOLE_HOST>/webhooks/v1/line/<channelId>/events
```

##### 4. 只让机器人应答

在 LINE Official Account Manager 中打开 Response settings。把应答方式设为 bot，并关闭自动回复和欢迎消息，这样账号不会在 Agent 之外另行回复。群聊需要在 LINE Developers Console 中启用 Allow bot to join group chats。

##### 5. 准备 Console 字段

- `channelId` — Messaging API 的 Channel ID。一个 channel 只能属于一条已启用的路由规则。
- `channelSecret` — Basic settings 中的 Channel secret。Ankole 用它验证 webhook 签名。
- `channelAccessToken` — Messaging API 标签页签发的长期有效 channel access token。
- `group_message_mode`: `addressed_only` — 先从这里开始。LINE 会投递每条群消息，因此 observe_all 和 may_intervene 无需额外权限也能工作。

#### 高级设置 · 身份映射、群聊回复与平台限制

展开以了解如何把 LINE 用户映射到账号，以及 Agent 在群里的行为。

##### 1. 把 LINE 用户映射到账号

LINE 用户没有员工记录，所以每个新发信人的自动映射账号都会失败。把「自动映射账号失败时」保持为「手动审核」：发信人会收到一条固定回复，并以 LINE 显示名称出现在身份 → 待绑定账号列表中，由管理员把这个 LINE 身份绑定到已有账号，例如一个本地密码账号。之后用户重新发送消息即可。LINE 用户 ID 属于拥有该 channel 的 LINE Developers provider，因此不同 provider 下的 channel 需要分别建立映射。

##### 2. 了解 Agent 在群里的行为

- 一个一对一聊天、一个群组和一个多人聊天各自构成一个 Agent 会话。LINE 没有子区。
- 在群里，@ 提及机器人或引用它的某条消息都算指名 Agent。群聊回复会引用提问者。
- 用户撤回的消息会从会话上下文中移除。

##### 3. 注意平台限制

- 回复按 5,000 字符拆分，一次请求最多发送五条消息。一次澄清最多显示四个按钮。
- Ankole 最多下载 25 MB 的收到文件。更大的文件，Agent 能看到名称和大小，但读不到内容。
- Agent 不能发送文件，但文字回复仍会送达。
- 每月套餐用尽时 LINE 返回状态码 429。只有更换套餐或进入下个月才能解除。

##### 4. 为每个 Agent 使用独立 channel

一个 Messaging API channel 只能属于一条已启用的路由规则。每多接入一个 Agent，都要再创建一个 channel。

### WhatsApp

WhatsApp 通过 Cloud API webhook 把消息推送给 Ankole，必须有带可信证书的公网 HTTPS 地址。WhatsApp 是消费级 IM，但如果发信人的手机号已属于某个已知的人，无需管理员操作即可完成映射。

> **WhatsApp 限制了部分 Ankole 功能**
>
> Meta 会在用户最新一条消息或按钮回复之后 24 小时关闭客服窗口。此后的回复（例如定时报告）会以明确的失败停止，不会送达用户；用户再次发信后，操作员可以在信号路由页面重试。群聊、模板消息、编辑消息、删除消息和实时进度预览均不可用。
>
> 这些限制全部来自平台本身，Ankole 无法绕过。

**开始之前**

- 一个添加了 WhatsApp 产品的 Meta App，以及一个拥有手机号的 WhatsApp Business Account
- 一个公网 HTTPS 的 Ankole 主机
- 一个用于测试的 WhatsApp 账号

#### 基础设置 · 准备 WhatsApp Business 手机号

##### 1. 接入 WhatsApp 产品并收集标识

在 Meta App Dashboard 中，为 App 添加 WhatsApp 产品，并连接拥有该手机号的 WhatsApp Business Account。从 App settings → Basic 复制 App ID 和 App secret，从 WhatsApp → API Setup 复制 Phone number ID。

##### 2. 签发永久的 System User token

在 Meta Business Suite 中创建一个 System User，为其授予该 App 的 whatsapp_business_messaging 权限，并生成一个永不过期的 token。用户 token 会过期并导致 Agent 停止工作。

##### 3. 先创建路由规则，再验证回调

选择一个 verify token，在 Console 中填入它和其他字段，然后保存并启用路由规则。Meta 会向 Ankole 验证回调 URL，因此规则必须先存在。

##### 4. 设置回调 URL 并订阅消息

打开 WhatsApp → Configuration，填入此 URL 和同一个 verify token，然后选择 Verify and save。接着为 App 订阅 messages webhook 字段；其他字段不会被使用。Ankole 用 App secret 检查每个请求的 x-hub-signature-256，签名错误时以状态码 401 拒绝。

```text
https://<ANKOLE_HOST>/webhooks/v1/whatsapp/<appId>/events
```

##### 5. 准备 Console 字段

- `appId` — Meta App ID。同一个 App 下的多个手机号可以各自服务一个 Agent，它们的规则必须使用相同的 App secret 和 verify token。
- `appSecret` — App settings → Basic 中的 App secret。Ankole 用它验证 webhook 签名。
- `verifyToken` — 由你选择的值。在 Meta 的回调配置中填入同一个值。
- `phoneNumberId` — WhatsApp → API Setup 中的 Phone number ID。一个手机号只能属于一条已启用的路由规则。
- `accessToken` — 永久的 System User access token。
- `group_message_mode`: `addressed_only` — 唯一可用的模式。WhatsApp 只投递一对一聊天，每条消息都指名 Agent。

#### 高级设置 · 身份映射、回复与平台限制

展开以了解 WhatsApp 用户如何成为账号，以及 Agent 无法使用哪些 WhatsApp 功能。

##### 1. 了解 WhatsApp 用户如何成为账号

发信人的 WhatsApp ID 是一个经 Meta 验证的手机号。如果某个已有账号（例如来自通讯录同步）拥有该手机号，发信人会立即完成映射。否则自动映射账号会失败：把「自动映射账号失败时」保持为「手动审核」，发信人会收到一条固定回复，并以 WhatsApp 资料名称和手机号出现在身份 → 待绑定账号列表中，由管理员把这个身份绑定到已有账号。只有在允许任何人与 Agent 对话的开放号码上，才选择「自动创建独立账号」。

##### 2. 了解 Agent 如何回复

- 回复会引用用户的消息。长回复按 4,096 字符拆分。
- 不超过三个选项的澄清显示为回复按钮；更多选项显示为一个列表。每个选项都以其编号开头。
- Agent 可以在 Meta 对各类型的大小限制内发送图片、视频、音频和文档。Meta 不接受的文件会以明确的错误停止，文字仍会送达。
- 用户在 WhatsApp 中删除的消息不会通知 Ankole，因此其文字仍保留在会话上下文中。

##### 3. 遵守客服窗口

Ankole 在每次发送前检查窗口。距用户最新一条消息或按钮回复超过 24 小时的回复会以 customer_service_window_closed 停止，不会向 Meta 发出任何请求。付费模板消息是打开新窗口的唯一方式，目前不在支持范围内。用户再次发信后，在信号路由页面使用「重试」发送被停止的回复。

##### 4. 让规则留在原来的手机号上

一个聊天绑定到接收它的手机号。如果把规则改到另一个手机号，对旧聊天的回复会以 binding_phone_number_mismatch 停止，而不会从用户从未联系过的号码发出。恢复原来的手机号即可解除。

### Email

Ankole 通过 IMAP 读取一个专用邮箱，并通过 SMTP 发送 Agent 的回复。每个邮件线程是一个会话。这个邮箱属于 Agent：任何人都不得用其他邮件客户端读取它。

> **电子邮件限制了部分 Ankole 功能**
>
> 被其他邮件客户端标记为已读的邮件不会到达 Agent，因此邮箱必须专用。Agent 的回复是一封纯文本邮件，没有实时进度、编辑和按钮；澄清以带编号的文字送达，用户用普通回复作答。只支持密码和应用专用密码登录，因此 Gmail 需要应用专用密码，而要求 OAuth 的 Exchange Online 邮箱无法使用。
>
> 控制平面必须能直接访问 IMAP 和 SMTP 服务器；HTTP 出站代理不覆盖它们。

**开始之前**

- 一个开通了 IMAP 和 SMTP 访问、并有密码或应用专用密码的专用邮箱
- 一个会添加带 DMARC 结果的 Authentication-Results 头的邮件服务器，或一个不接收外部邮件的内网邮件服务器

#### 基础设置 · 准备专用邮箱

##### 1. 创建邮箱及其密码

创建一个只供 Agent 使用的邮箱，并为它启用 IMAP 和 SMTP。如果服务商要求，为 Ankole 创建一个应用专用密码，而不是使用账号密码。

> 不要在任何其他邮件客户端中打开这个邮箱。被其他客户端标记为已读的邮件对 Agent 不可见。

##### 2. 收集服务器设置

Ankole 通过隐式 TLS 连接 IMAP，通常是 993 端口。SMTP 使用 STARTTLS（通常是 587 端口）或隐式 TLS（通常是 465 端口）。Ankole 会根据系统 CA 存储验证服务器证书。

##### 3. 准备 Console 字段

- `address`: `agent@example.com` — 邮箱地址。回复从这个地址发出。
- `displayName`: `Ankole Agent` — 出站邮件上的发件人名称。
- `imapHost`: `imap.example.com` — IMAP 服务器主机。
- `imapPort`: `993` — IMAP 端口。Ankole 使用隐式 TLS。
- `smtpHost`: `smtp.example.com` — SMTP 服务器主机。
- `smtpPort`: `587` — SMTP 端口。587 配 STARTTLS，465 配 TLS。
- `smtpSecurity`: `starttls` — starttls 或 tls，与 SMTP 端口匹配。
- `username` — 登录名，通常就是邮箱地址。一组 IMAP 主机和用户名只能属于一条已启用的路由规则。
- `password` — 邮箱密码或应用专用密码。Ankole 加密存储。
- `senderAuthentication`: `dmarc` — 保持 dmarc。只有不接收外部邮件的内网邮件服务器才设为 none。

##### 4. 发送测试邮件

从你自己的地址给该邮箱写一封邮件。如果你的地址尚未绑定到你的账号，你会收到一封映射提示作为回复；在身份 → 待绑定账号中绑定该地址，然后重新发送邮件。

#### 高级设置 · 发信人身份、发信人认证与线程

展开以了解邮件发信人如何成为已知账号，以及 Ankole 如何处理线程和群发邮件。

##### 1. 了解邮件发信人如何成为账号

互联网上的任何人都能给该邮箱写信，而 From 地址本身不能证明任何事情，因此 Ankole 从不按资料邮箱或本地登录邮箱匹配邮件发信人。发信人只能通过显式的邮箱身份绑定被识别：通讯录同步和提供商登录会绑定提供商报告的地址，所以已同步通讯录中的员工无需手动步骤即可接入；其他地址由管理员在身份 → 待绑定账号中绑定。「自动创建独立账号」会创建一个以该地址为标识的账号，只在开放邮箱上使用它。

##### 2. 理解发信人认证

senderAuthentication 设为 dmarc 时，Ankole 读取你的收件邮件服务器添加的 Authentication-Results 头，只有当它对 From 地址所在域报告 dmarc=pass 时才接受这封邮件。未通过的邮件会被静默忽略，不发送任何提示。只有邮件服务器位于内网且不接收外部邮件时才设为 none。

##### 3. 了解线程和群发邮件的处理方式

- 线程按 In-Reply-To 和 References 头归位，而不是按主题。Agent 的回复带有邮件客户端使用的线程头。
- 参与者只有发信人和该邮箱的线程是一对一会话。有其他收件人时就是群组会话，而只在 Cc 中列出该邮箱的邮件不算指名 Agent。
- 已知线程中的邮件会移除回复分隔线以下的引用文字。线程的第一封邮件和转发邮件保留完整正文。
- 来自该邮箱自身的邮件、自动邮件和群发邮件，以及邮件列表邮件会被静默忽略，不发送任何提示。
- 大于 25 MB 的邮件只会送达邮件头。出站附件限制为每封邮件 20 MB。

### 在 Console 中完成连接

当前版本使用直连：一条路由规则把一个聊天渠道连接到一个 Agent。它记录使用哪个适配器、哪套应用凭证、由哪个 Agent 回复，以及怎样处理群聊消息。多个机器人要分别创建多条路由规则。

打开 **Console → 信号路由 → 新增路由规则**，依次设置：

| 字段 | 如何选择 |
|---|---|
| **Target Agent** | 第 3 步创建的 Agent |
| **Adapter** | Slack、Teams、飞书 / Lark、钉钉、企业微信、Telegram、Discord、LINE、WhatsApp 或 Email |
| **规则名称** | 起一个固定的名称，例如 `slack-main` 或 `lark-main` |
| **Group message mode** | 第一次测试选择 `addressed_only` |
| **聊天渠道配置** | 填入对应平台 Tab 中准备好的凭证和字段 |

保存后，应能在列表中看到启用状态。如果表单提示凭证有误，先改正再去 IM 测试；凭证没有通过检查，adapter 就不会建立连接。

> **💡 你知道吗？**
>
> 目前，一条路由规则会把一个信号源直接交给一个 Agent，最常见的信号源就是聊天应用。未来可以按频道、会话等条件选择 Agent，也可以接收 Salesforce 等外部系统的事件，让 Agent 主动响应。因此，这个模块叫“信号路由”，而不是“聊天渠道”：它要处理的是所有能触发 Agent 的信号。

#### 高级设置 · 群聊处理方式和身份映射

第一次配置时，建议只响应明确 @ Agent 的消息。

##### 1. 选择如何处理没有 @ Agent 的群消息

- `addressed_only` — 不处理没有明确发给 Agent 的群消息。第一次测试请选这个。
- `observe_all` — 把消息记入上下文，但不唤醒 Agent。
- `may_intervene` — 由 Agent 判断要不要参与没有明确发给它的讨论。

> 聊天平台必须先把群消息发给 Ankole，这些模式才会生效。钉钉和企业微信只支持 addressed_only；Slack 和飞书还要添加额外的事件或权限，才能读取群里所有消息。

##### 2. 用命名空间区分平台身份

同一个平台下，每个企业组织使用一个 platformSubjectNamespace。IdP 和聊天渠道对应同一个组织时，两处配置才填写同一个值。

## 5. 在 IM 中与 Agent 对话

把机器人加入测试会话。第一次在群里测试时，请明确 @ 它：

> @Ankole 你能做什么？你现在服务哪个团队？

收到真实模型生成的回复后，就可以继续调整 Agent 的 mission、模型和群聊策略。

<a id="agent-not-replying"></a>

### Agent 没有回复时

按下面的顺序逐项检查：

1. 聊天平台中的应用已发布最新版本，测试用户也在可用范围内。
2. 机器人已经加入对应的频道、团队或会话。
3. 必需的消息事件与权限已经生效。
4. 路由规则已启用，并指向正确的 Agent。
5. Agent 已配置 `primary`、`light` 与 `heavy`。
6. 模型提供商凭证和模型选择器有效。
7. 至少有一个 Worker 显示为 ready。

使用 Compose 时，运行 `docker compose logs -f control-plane worker`。使用 Kubernetes 时，查看控制面和 Worker Pod 的日志。只查看相关错误，不要输出环境变量或 secret。

收到回复后，可以继续阅读 [Agent](https://ankole.agentbull.com/zh-Hans-CN/docs/agents/index.md)、[信号路由规则](https://ankole.agentbull.com/zh-Hans-CN/docs/signal-bindings/index.md)或 [后台 Agent 任务](https://ankole.agentbull.com/zh-Hans-CN/docs/background-jobs/index.md)。
