> ## Documentation Index
> Fetch the complete documentation index at: https://docs.befailproof.ai/llms.txt
> Use this file to discover all available pages before exploring further.

# 故障排查

> AgentEye 故障排查文档。

本指南将生产环境中最常见的故障症状与具体诊断方法和修复步骤一一对应，让你能够直接用现有工具解决问题，无需搭建额外的可观测性基础设施。内容涵盖服务器、采集器、仪表盘、AI 助手、Python SDK、健康与证书监控、备份、ClickHouse 分析后端以及多租户等方面。

仪表盘页面的路由以组织为范围，格式为 `/<org-slug>/…`，事件流即为组织主页（`/<org-slug>/`）。本指南中提到的页面名称（如 `/sessions`、`/queries`）均指上述组织范围内的路由。

***

## 查看日志

AgentEye 不内置日志或监控栈。服务器和仪表盘均将结构化日志写入 **stdout**，因此可以直接通过 `kubectl` 或 `docker` 读取，无需聚合器。

### Kubernetes

实时跟踪服务器和仪表盘的日志：

```bash theme={null}
kubectl logs -n agenteye -l app=server    -f --timestamps
kubectl logs -n agenteye -l app=dashboard -f --timestamps
```

常用变体：

| 目的                    | 命令                                                                |
| --------------------- | ----------------------------------------------------------------- |
| 最近 200 行（不跟踪）         | `kubectl logs -n agenteye -l app=server --tail=200 --timestamps`  |
| 上次崩溃前的日志              | `kubectl logs -n agenteye <pod-name> --previous`                  |
| 同时跟踪所有副本              | `kubectl logs -n agenteye -l app=server --max-log-requests=10 -f` |
| Postgres（StatefulSet） | `kubectl logs -n agenteye postgres-0 -f`                          |

### Docker Compose

```bash theme={null}
docker logs -f agenteye-server
docker logs -f agenteye-dashboard
```

### 跨仪表盘与服务器关联单个请求

每个仪表盘请求都会附带 `request_id`，并通过 `x-request-id` 请求头传递给服务器。服务器会在响应头和该请求的每一行日志中回显该 ID。要端到端追踪某个请求：

1. 从响应头中获取 ID，例如：
   ```bash theme={null}
   curl -i https://dashboard.example.com/api/events | grep -i x-request-id
   # x-request-id: 9a7b0d6e-5e9b-4c0a-9f8a-5f1e4b5c0f3a
   ```
2. 在两个 Pod 的日志中 grep 该 ID：
   ```bash theme={null}
   REQ=9a7b0d6e-5e9b-4c0a-9f8a-5f1e4b5c0f3a
   kubectl logs -n agenteye -l app=dashboard --tail=5000 | grep "$REQ"
   kubectl logs -n agenteye -l app=server    --tail=5000 | grep "$REQ"
   ```

你将看到仪表盘的 `proxy passthrough`、`withAuth: authorized`、`upstream response` 日志行，以及服务器的 `http request received` / `http request completed` 日志对，它们共享同一个 `request_id`。

### JSON 日志与 `jq`

在仪表盘上设置 `AE_LOG_JSON=1`（`NODE_ENV=production` 时默认开启），使其每行输出一个 JSON 对象，然后按结构过滤：

```bash theme={null}
kubectl logs -n agenteye -l app=dashboard --tail=5000 \
  | jq 'select(.level == "warn" or .level == "error")'

kubectl logs -n agenteye -l app=dashboard --tail=5000 \
  | jq 'select(.route == "POST /api/keys")'
```

Rust 服务器以 `key=value` 格式输出 tracing 日志，无需 `jq` 即可直接 grep：

```bash theme={null}
kubectl logs -n agenteye -l app=server --tail=5000 | grep 'status=5'   # 5xx
kubectl logs -n agenteye -l app=server --tail=5000 | grep 'actor_key_id='
```

### 提高日志详细程度

| 组件  | 环境变量           | 示例                                                       |
| --- | -------------- | -------------------------------------------------------- |
| 服务器 | `RUST_LOG`     | `RUST_LOG=debug` 或 `RUST_LOG=agenteye_server=debug,info` |
| 仪表盘 | `AE_LOG_LEVEL` | `AE_LOG_LEVEL=debug`                                     |

服务器的 `debug` 级别会为每次认证添加 `api key authenticated` 日志行。仪表盘的 `debug` 级别会添加 `upstream request`、`session validated` 和 `proxy passthrough` 日志行。

### 日志保留

容器 stdout 是临时性的；kubelet 会轮转日志文件（默认每个容器约 10 MiB），并在磁盘上保留少量文件。Pod 删除后日志即消失。如需更长时间的保留或跨 Pod 搜索，请将集群接入日志采集器（Loki、CloudWatch、Cloud Logging、Datadog 等），令其跟踪 `/var/log/containers/`。AgentEye 不要求也不限定使用任何特定方案。

***

## 认证问题

### `docker pull` 报 "unauthorized"

请确保已使用 `AGENTEYE_TOKEN` 向 GHCR 认证 Docker：

```bash theme={null}
echo $AGENTEYE_TOKEN | docker login ghcr.io -u x --password-stdin
```

该 token 必须具有 `agenteye-enterprise` 组织的 `read:packages` 权限。如果 token 无效，请联系 `support@exosphere.host`。

### `gh release download` 返回 404 或 401

* 确认 `AGENTEYE_TOKEN` 已在 shell 中导出：`echo $AGENTEYE_TOKEN`
* 确认使用了 `GITHUB_TOKEN=$AGENTEYE_TOKEN gh release download ...`（`gh` CLI 读取 `GITHUB_TOKEN`）
* 该 token 需要对 `agenteye-enterprise/releases` 具有 `contents:read` 权限

***

## 服务器问题

### 服务器启动失败，报 "invalid port number"

`POSTGRES_PASSWORD`（或其他凭据）包含 URL 特殊字符（`/`、`+`、`=`），导致 `DATABASE_URL` 解析失败。请使用十六进制编码重新生成密码：

```bash theme={null}
NEW_PASS=$(openssl rand -hex 24)
```

然后更新 Kubernetes Secret 和 Postgres 内部密码（或重新创建 Docker Compose 的 `.env` 文件），并重启服务器。完整步骤请参阅 [enterprise-docs/kubernetes-deployment.md](/zh/agenteye/kubernetes-deployment) 中的 "PostgreSQL credentials" 一节。

### 服务器启动后立即退出

检查容器日志：

```bash theme={null}
docker logs agenteye-server
```

常见原因：

* `DATABASE_URL` 未设置或格式错误：服务器会记录错误并退出。
* Postgres 不可达：确认 Postgres 容器或托管数据库正在运行，且主机/端口配置正确。
* 迁移失败：检查日志中是否有 SQL 错误。

### `GET /health` 返回非 200 或超时

服务器在首次启动时可能仍在执行迁移，请等待几秒后重试：

```bash theme={null}
curl http://localhost:8080/health
```

如果问题持续，请检查 `docker logs agenteye-server` 中的错误信息。

### `GET /ready` 返回 503

`/ready` 是就绪探针：当服务器无法访问 **Postgres 或 ClickHouse** 时返回 `503`。响应体会指明失败的依赖项：

```bash theme={null}
curl -s http://localhost:8080/ready
# {"status":"not_ready","checks":{"postgres":"ok","clickhouse":"down","redis":"ok"}}
```

修复响应中报告为 `down` 的依赖项：ClickHouse/Postgres Pod 是否处于 `Running` 状态？`CLICKHOUSE_URL` / `DATABASE_URL` 是否正确且可访问？在 Kubernetes 上，Pod 在 `/ready` 恢复前会显示 `NotReady`，这是预期行为，也正是健康监控告警所依赖的信号。Redis 不会导致就绪失败：它会被上报但不影响就绪判断。

### 采集器返回 401 Unauthorized

采集器的 API key 没有 `events:add` 权限，或该 key 已被禁用。请创建一个具有正确权限的新 key：

```bash theme={null}
curl -s -X POST http://your-server:8080/keys \
  -H "Authorization: Bearer $ADMIN_KEY" \
  -H "Content-Type: application/json" \
  -d '{"name":"new-collector","permissions":["events:add"]}'
```

### 认证请求突然变慢（\~200ms 而非 \~5ms）

这是 `REDIS_URL` 已设置但 Redis 不可用时的典型症状。每次缓存调用在超时 100ms 后回退到 Postgres；在认证和 OTP 路径上，一次请求会经历两次这样的回退。

在服务器日志中确认：

```
auth cache: L2 get failed error=redis call timed out
```

解决方案：

1. 执行 `redis-cli -h <your-redis> ping`，确认 Redis 在集群网络上可达。
2. 如果 Redis 曾短暂不可用现已恢复，请**重启服务器 Pod**。`redis::aio::ConnectionManager` 在底层连接断开后无法可靠重连；重启 Pod 会重新建立干净的连接。仪表盘同理。
3. 如果暂时不想运行 Redis，请在部署配置中取消设置 `REDIS_URL` 并重启。两个服务在没有缓存的情况下也能正常运行（正确性不受影响；延迟回退到引入 Redis 前的基准水平）。

### 服务器日志显示 `OTP request rate-limited`，但用户说只尝试了一次

检查 Redis 是否不可达。回退路径使用 `SELECT COUNT(*) FROM otp_codes WHERE created_at > now() - interval '15 minutes'`，会查到之前生成的 OTP 记录。如果用户在过去一小时内一直点击"重新发送"，15 分钟窗口内可能仍存在 ≥5 条记录。解决方案：等待窗口滚动，或执行 `DELETE FROM otp_codes WHERE user_id = $1 AND created_at > now() - interval '15 minutes'`（操作员控制台）。

### 修改了 `ALLOWED_EMAILS` / `SESSION_TTL_SECS` / `OTP_TTL_SECS` 并重启后没有任何变化

这些环境变量**仅在首次启动时作为种子值使用**。一旦 `settings` 表中已有对应 key 的行，该行即为真实来源；环境变量在首次启动时读取一次，此后每次重启均被忽略。

若需在首次启动后修改，请登录仪表盘并在 `/settings` 页面编辑。修改会在数秒内在所有副本上生效，无需重启。

如需强制从环境变量重新初始化（罕见，通常仅用于开发环境），请执行 `DELETE FROM settings WHERE key = '<key>'` 并重启服务器。服务器在下次启动时会读取当前环境变量的值。在生产环境中，推荐通过 `/settings` 页面编辑。

***

## 采集器问题

### 采集器已启动，但事件未出现在仪表盘中

1. 确认采集器正在运行：`systemctl status agenteye-collector`（Linux）或检查进程状态。
2. 确认 `AGENTEYE_URL` 指向 `http(s)://your-server-host:8080/events`（注意：需包含 `/events` 路径）。
3. 执行一次性强制上传，查看即时输出：
   ```bash theme={null}
   agenteye-collector flush
   ```
4. 检查 Python SDK 是否正在写入文件：`ls ${AGENTEYE_HOME:-~/.agenteye}/events/`
5. 如果 `${AGENTEYE_HOME:-~/.agenteye}/failed/` 中存在文件，说明上传失败。检查采集器日志中的错误信息，通常是 4xx（key 错误或 URL 错误）或网络问题。

### 文件在 `$AGENTEYE_HOME/events/` 中堆积，未被上传

* 采集器可能未在运行。启动它：`agenteye-collector start`；它会在启动时自动上传已有的事件文件。
* 检查采集器健康状态：`agenteye-collector health`
* 采集器可能正在运行但无法访问服务器。检查采集器主机与服务器主机之间的防火墙规则。

### `$AGENTEYE_HOME/failed/` 中存在文件

文件在所有重试尝试耗尽后（默认：5 次，指数退避）移至 `failed/`。这意味着：

* 服务器返回了 4xx 错误（key 错误、URL 错误或负载问题）
* 在整个重试窗口期间服务器不可达

修复根本原因后，手动重新排队：

```bash theme={null}
mv ${AGENTEYE_HOME:-~/.agenteye}/failed/*.jsonl ${AGENTEYE_HOME:-~/.agenteye}/events/
agenteye-collector flush
```

### 采集器每次上传都报 `network error`（TLS 握手失败）

如果对 `AGENTEYE_URL` 执行 `curl -k` 成功，但采集器二进制文件每次上传都报 `error sending request for url (...)`，说明 AgentEye 服务器提供的 TLS 证书不是由公信 CA 签发的。

**生产路径**是在 `deploy/base/certificates/domain.env` 中配置的 ACME 入站主机名（参见 [`kubernetes-deployment.md`](/zh/agenteye/kubernetes-deployment) 第 3.1 / 4.2 阶段）。一旦 `INGEST_DOMAIN` 解析到公网 Traefik LB 且 cert-manager 已颁发 Let's Encrypt 证书，采集器将使用系统信任存储验证服务器证书，**无需设置 `AGENTEYE_TLS_CA`**；如果之前针对旧的自签名部署设置过该配置，请清除它。

**症状：采集器昨天还正常，今天（约 90 天后）突然失败。** 这意味着集群的 `ingest-tls` 仍在使用旧版 `selfsigned` 颁发者。90 天证书已轮换，固定的 CA 文件已过期。永久修复方案：将集群切换到 ACME 颁发者（部署指南第 3.1 阶段）。短期解决方案：重新提取当前服务器证书并更新 `AGENTEYE_TLS_CA`：

```bash theme={null}
kubectl get secret ingest-tls-cert -n agenteye \
  -o jsonpath='{.data.tls\.crt}' | base64 -d > /etc/agenteye/server-ca.crt
```

```bash theme={null}
export AGENTEYE_TLS_CA=/etc/agenteye/server-ca.crt
agenteye-collector flush
```

`AGENTEYE_TLS_CA` 会添加一个额外的信任锚点；标准公共根证书仍然受信任。

### 部署后 `ingest-tls` 证书一直处于 `Ready: False`

```bash theme={null}
kubectl describe certificate ingest-tls -n agenteye
```

查看 `Events` 以及关联的 `Order` / `Challenge`。常见原因：

* **DNS 未解析到公网 LB。** HTTP-01 验证器无法访问 `INGEST_DOMAIN`。使用 `dig +short INGEST_DOMAIN` 验证；它应解析到与 `traefik-public` LoadBalancer 的 `EXTERNAL-IP` 相同的地址。DNS 传播完成后 cert-manager 会自动重试，无需删除 Certificate 资源。
* **端口 80 在负载均衡器/安全组处被封锁。** HTTP-01 验证要求端口 80 对 Let's Encrypt 的公共验证器可访问。如果上游 WAF 或安全组限制了 `:80`，请开放它（Traefik 配置会重定向到 HTTPS，但 Boulder 会跟随重定向并接受响应）。
* **`dnsNames` 未被替换。** 如果 `kubectl get certificate ingest-tls -n agenteye -o jsonpath='{.spec.dnsNames}'` 显示 `INGEST_DOMAIN_PLACEHOLDER`，说明你跳过了 `domain.env` 步骤；请从 `domain.env.example` 创建该文件并重新应用。
* **被 Let's Encrypt 限流。** 对同一主机名反复失败的申请会触发重复证书或验证失败限制。请至少等待一小时后再重试；检查 Order 状态以获取确切的限流信息。

### `dashboard-tls` 证书一直处于 `Ready: False` / 浏览器仍显示警告

诊断流程与上述 `ingest-tls` 相同（`kubectl describe certificate dashboard-tls -n agenteye`）；DNS、端口 80、占位符和限流原因同样适用，此外还有两个仪表盘专有原因：

* **`DASHBOARD_DOMAIN` 解析到错误的 LoadBalancer。** 它必须指向*仪表盘* Traefik LB，而非公网入站 LB。对主机名执行 `dig +short` 并与仪表盘 LB 地址对比。
* **仪表盘 Traefik 实例无法响应验证请求。** 必须使用附带的仪表盘 values 文件安装该实例，它会为 cert-manager 的 HTTP-01 解析器启用范围限定的 Ingress provider。没有它，解析器无法路由，Order 会一直处于 `pending` 状态。使用提供的 values 文件升级该实例，待处理的验证请求随后会自动完成。
* **LoadBalancer 设置了 IP 限制。** 源 IP 范围限制同样适用于端口 80，这会阻止 Let's Encrypt 的验证器访问——无论是首次颁发还是约每 75 天的续期。请重新开放 LB，或在锁定之前与支持团队协商使用 DNS-01 解析器。

在证书颁发失败期间，仪表盘会继续提供之前的证书（或全新安装时 Ingress 的默认证书）——访问体验会因浏览器警告而降级，但不会完全不可用。

### 仪表盘获得受信任证书后，CLI 仍跳过 TLS 验证

`--insecure` 标志在登录时会持久化到 `cli.json`。一旦仪表盘提供公信证书，请使用 `agenteye --base-url https://<your-dashboard-domain> --secure login` 重新登录；验证选项会被保存为开启状态，启动时的警告也会消失。

***

## 仪表盘问题

### 无法禁用或编辑 `ADMIN_EMAIL` 用户

这是设计行为。匹配 `ADMIN_EMAIL` 的用户在每次服务器启动时都会被标记为受保护：仪表盘会在该行隐藏"禁用"按钮，且 API 会以 `403 Forbidden` 拒绝针对该用户的 `DELETE /users/:id` 和 `PUT /users/:id` 请求。数据库触发器也会拒绝直接禁用受保护行的 `UPDATE` 语句。

若要轮换引导管理员，请在环境变量中修改 `ADMIN_EMAIL` 并重启服务器。新邮箱会被 upsert 为受保护状态。之前的管理员会保留受保护标志，直到在数据库中手动清除（通常无需处理，因为之前的邮箱在你明确删除之前仍是有效的管理员）。

### 仪表盘不显示事件

1. 确认仪表盘环境变量中的服务器 URL 和 API key 配置正确（`AGENTEYE_SERVER_URL`、`AGENTEYE_API_KEY`）。
2. 仪表盘 API key 需要 `events:read` 权限。
3. 确认事件已被实际采集：`curl http://your-server:8080/events -H "Authorization: Bearer $ADMIN_KEY"`

### `/errors` 为空，但 `/events` 显示红色行

较新版本的 SDK 将失败事件作为 `agent_end` / `tool_result` / `hook_completed` 类型、`outcome: "error"` 的事件发送，而非专门的 `event_type: "error"` 行。`/errors` 页面现在同时匹配两种情况：`/events` 流中任何标红的行（显式 `event_type='error'`、payload 的 `outcome`/`status` 属于失败集合、`is_error: true`，或 `error` 字段为真值）都会出现在 `/errors` 中。如果你之前在 `/events` 有红色行的同时看到"此窗口内无错误"，请同时升级仪表盘和服务器（扩展后的过滤条件为 `GET /events` 上的 `errored=true`），两个视图将保持一致。

### `/models`、`/tools` 或 `/hooks` 在宽时间范围下加载缓慢或失败

**症状：** 在大型事件表（数百万行）上，打开 `/models`、`/tools` 或 `/hooks`，或将时间范围拓宽至 `7d`、`30d` 或 `all` 时，图表转圈后显示加载错误。服务器日志中记录了 `latency_aggregate` 请求的 ClickHouse `MEMORY_LIMIT_EXCEEDED`（Code 241）或查询超时。

**原因：** 旧版本在计算这些页面的延迟和分布汇总时，会读取完整的原始事件 `payload` 并通过内存排序和关联来配对请求/响应事件。查询峰值内存因此随窗口大小增长，在繁忙的租户上，宽时间范围可能超过 ClickHouse 的单查询内存上限。

**修复：** 升级到包含此修复的版本。汇总查询现在只读取紧凑的提升列，并通过流式聚合来配对事件，因此峰值内存不再随原始 payload 增长——宽时间范围也能保持在内存上限之内，且响应时间大幅缩短。该改进完全在查询层实现：无需重新采集或回填数据，下次页面加载时即对所有现有数据生效。

### 仪表盘加载失败 / 空白页面

检查仪表盘容器日志：

```bash theme={null}
docker logs agenteye-dashboard
```

最常见的原因是 `AGENTEYE_SERVER_URL` 或 `AGENTEYE_API_KEY` 缺失，或指向了不可达的服务器。

### 仪表盘分析 / 遥测

仪表盘默认会向 PostHog 发送匿名产品使用分析数据，通过仪表盘自身的 `/ingest` 路径（反向代理到 `https://us.i.posthog.com`）路由。以第一方方式发送可防止浏览器广告拦截器阻断它们。这与仪表盘的核心功能无关：

* **仪表盘容器**（而非浏览器）负责访问 PostHog。如果其到 `https://us.i.posthog.com` 的出站访问被阻断，遥测会静默失效；仪表盘正常运行，用户不会看到任何错误。
* 不会包含任何 Agent、会话或事件数据，仅包含仪表盘 UI 使用情况。
* 若要完全禁用遥测，请在仪表盘容器上设置 `AE_ANALYTICS_DISABLED=1` 并重启。详见部署指南中的[遥测与隐私](/zh/agenteye/deployment#telemetry--privacy)。

### CLI 分析 / 遥测

`agenteye` CLI 默认向 PostHog 发送匿名使用分析数据：执行的命令、成功/退出状态和耗时。这与 CLI 的功能无关：

* **运行 CLI 的机器**直接访问 `https://us.i.posthog.com`。如果出站访问被阻断，遥测会静默失效（发送有时间限制，不会延迟命令执行），CLI 正常运行。
* 不会包含任何 Agent、会话或事件数据：命令**参数和标志值**（仪表盘 URL、token、邮箱、会话 ID、查询过滤器）绝不会被发送。
* 若要禁用，请在 CLI 的环境中设置 `AGENTEYE_ANALYTICS_DISABLED=1`（或跨工具通用的 `DO_NOT_TRACK=1`）。详见 CLI 指南中的[遥测与隐私](/zh/agenteye/cli#telemetry--privacy)。

***

## AI 助手问题

完整设置请参阅 [enterprise-docs/assistant.md](/zh/agenteye/assistant)。

### 助手气泡未出现

气泡仅在以下**所有**条件均满足时才会显示：

* 已登录用户具有 `agent:use` 权限。
* 仪表盘上已设置 `AGENTEYE_AGENT_URL`，且 `agent` 服务可达。
* `agent` 服务上已配置 LLM 端点（`ANTHROPIC_API_KEY`、通过 `ANTHROPIC_BASE_URL` 的网关，或 Bedrock/Vertex）。如果都未设置，agent 会报告"未配置"，气泡保持隐藏。

从仪表盘主机检查 agent 的健康状态：`curl http://agent:9100/health` 应返回 `{"status":"ok","llm_configured":true,...}`。

### 助手提示无法读取某些内容

工具按用户设置门控权限。如果用户缺少 `evaluations:read`（或 `events:read`、`dashboards:read`），对应工具不会被提供，助手会提示无法读取该数据。请授予相关读取权限。

### 发送消息时报 "assistant not configured"（HTTP 503）

`agent` 容器未配置 LLM 端点，或仪表盘的 `AGENTEYE_AGENT_TOKEN` 与 agent 的 token 不匹配。请同时设置并重启。

### `agent` 容器在负载下重启 / OOM

每个对话会生成一个短生命周期的子进程。确保容器以 init 进程运行（镜像使用 `tini`；在 Compose 中设置 `init: true`），并提供足够的内存限制。如有需要，可降低 `AGENTEYE_AGENT_MAX_STEPS`。

***

## CLI 问题

### `agenteye` 启动失败，报 `ModuleNotFoundError: No module named 'click'`

**0.1.6** 版本的 `agenteye` CLI 在全新安装后可能在启动时崩溃：

```
ModuleNotFoundError: No module named 'click'
```

0.1.6 依赖 `typer` 间接安装 `click`；而当前 `typer` 版本不再隐式引入 `click`，导致全新环境中缺少该包。**请升级到 0.1.7 或更新版本**，该版本直接依赖 `click`：

```bash theme={null}
pipx upgrade agenteye      # 如果通过 pipx 安装（或：pipx install --force agenteye）
uv tool upgrade agenteye   # 如果通过 uv 安装
pip install --upgrade agenteye
```

安装指南请参阅 [enterprise-docs/cli.md](/zh/agenteye/cli)。

***

## Python SDK 问题

### `$AGENTEYE_HOME/events/` 中没有文件出现

SDK 默认每 500ms 缓冲并刷新一次事件。如果进程在刷新前退出，事件可能丢失。对于短生命周期脚本，请调用 `agenteye.configure(flush_interval=0.1)` 加快刷新速度，或确保进程运行时间足够完成一次刷新周期。

如果已设置 `AGENTEYE_HOME`，请确认 SDK 写入的是 `$AGENTEYE_HOME/events/` 而非 `~/.agenteye/events/`（需要 SDK ≥ 0.0.1b5）。

### `ValueError: Reserved field names cannot be used as custom fields`

`timestamp`、`type` 和 `environment` 是保留名称，不能用作自定义字段。传递其中任何一个都会抛出：

```
ValueError: Reserved field names cannot be used as custom fields: [...]
```

请重命名相关自定义字段。注意 `session_id` 和 `agent_id` 是事件调用的显式参数，而非自定义字段；将它们作为自定义字段再次传递会抛出 `TypeError`。

***

## 健康监控问题

### Slack 未收到告警（Robusta）

Robusta 健康告警需要**手动启用**；在安装并指向 Slack 频道之前不会发送任何内容。验证 release 及其 sink：

```bash theme={null}
kubectl get pods -n robusta          # robusta-runner + robusta-forwarder 应处于 Running 状态
kubectl logs -n robusta -l app=robusta-runner --tail=50
```

常见原因：Slack `api_key` / `slack_channel` 未设置（或 token 已被吊销）；`api_key` 是 Robusta 云中继 token（`robusta integrations slack`），但附带的 `disableCloudRouting: true` 需要自托管的 Slack **bot token**（`xoxb-…`），或者将 `disableCloudRouting` 设为 `false`；sink 的 `scope` 排除了你的 Pod 所在的命名空间（附带的 values 文件将范围限定为 `agenteye`）；或者尚未发生任何故障。通过下线一个 Pod 强制触发测试告警：

```bash theme={null}
kubectl -n agenteye delete pod -l app=clickhouse   # 它会被重新创建
```

安装和配置说明请参阅 [enterprise-docs/health-monitoring.md](/zh/agenteye/health-monitoring#2-pod-failure-alerting-with-robusta-opt-in)。

### 服务器持续在 `NotReady` 状态反复横跳

就绪探针会访问 `/ready`，当 Postgres 或 ClickHouse 不可达时会失败。如果服务器在 `NotReady` 状态间反复切换，说明某个依赖项间歇性不可用；请检查 ClickHouse 和 Postgres Pod 以及服务器的 `CLICKHOUSE_URL` / `DATABASE_URL`。确认 `/ready` 报告的内容：

```bash theme={null}
kubectl -n agenteye exec deploy/server -- sh -c 'curl -s localhost:8080/ready'
```

该探针设计上具有一定容忍度（较大的失败阈值），因此持续的状态抖动表明存在真实的依赖项问题，而非探针配置过于激进。存活探针保持在 `/health` 上，因此就绪状态抖动**不会**重启 Pod。

## 证书监控问题

### CronJob 未发送 Slack 通知

`cert-renewal-check` CronJob 需要将 Slack webhook URL 存储在 Secret 中。验证它是否存在：

```bash theme={null}
kubectl get secret cert-renewal-notify-config -n agenteye
```

如果不存在，请创建：

```bash theme={null}
kubectl create secret generic cert-renewal-notify-config \
  --namespace agenteye \
  --from-literal=SLACK_WEBHOOK_URL="https://hooks.slack.com/services/YOUR/WEBHOOK/URL"
```

没有该 Secret，CronJob 仍会运行并将结果记录到 stdout。通过以下命令检查日志：

```bash theme={null}
kubectl logs -n agenteye -l job-name --tail=50
```

### 客户端证书在收到通知前已过期

CronJob 每 12 小时运行一次。如果它一直未运行，请检查其状态：

```bash theme={null}
kubectl get cronjob cert-renewal-check -n agenteye
```

触发手动检查：

```bash theme={null}
kubectl create job --from=cronjob/cert-renewal-check manual-cert-check -n agenteye
kubectl logs -n agenteye -l job-name=manual-cert-check
```

若要立即重新颁发已过期的证书：

```bash theme={null}
cd base/certificates/client-certs
./issue-client-cert.sh <cluster-name>
```

然后在运行采集器的集群中应用重新生成的 `collector-mtls-secret.yaml` 并重启：

```bash theme={null}
kubectl apply -f collector-mtls-secret.yaml -n <collector-namespace>
```

***

## 备份问题

### `agenteye-backup` 报 "No space left on device"

`agenteye-backup` CronJob 将 Postgres + ClickHouse 转储到 `backup-tmp` `emptyDir` 临时卷（默认 `30Gi`），然后**流式**将 `tar` 归档直接上传到 S3——压缩归档文件不会写回临时卷，因此临时卷只需容纳*原始转储文件*，而非转储文件加一份归档副本。Pod 被驱逐 / 报 `No space left on device` 意味着**原始转储文件**超出了临时卷大小（ClickHouse `events` 转储文件占主导，且随时间增长）。检查失败 Job 的日志：

```bash theme={null}
kubectl logs -n agenteye -l job-name=<failed agenteye-backup job>
```

修复方案：在你的 overlay 中，将 CronJob 的 `backup-tmp` `emptyDir` 的 `sizeLimit` 提高到超过原始转储总量，并确认节点的临时存储实际上能容纳这些数据（`sizeLimit` 是上限，而非预留量）。如果转储量超过单个节点的磁盘容量，请用 PVC（EBS/PD）替换 `backup-tmp` 的 `emptyDir`，或在源头压缩转储文件。

> 旧版本会将 `.tar.gz` 写入与转储文件相同的 `20Gi` 临时卷，导致 `转储文件 + 归档文件` 溢出，Pod 在上传前就被驱逐——表现上像 S3 故障，实际上是磁盘问题。流式上传消除了这种空间翻倍问题。

### `agenteye-backup` 安装 `curl` 失败

该 Job 在 `postgres:16` 镜像上运行，并在启动时安装 `curl` 以执行 ClickHouse HTTP 转储。在没有到 Debian 软件包镜像出站访问的集群上，`apt-get` 步骤会失败。请允许备份 Pod 的出站访问，或将 `curl` 预装到镜像副本/自定义备份镜像中，并在 overlay 中引用该镜像。

### `agenteye-backup` 运行但对象存储中没有文件

基础配置内置了真实的 `BACKUP_BUCKET`（`ts-prod-agenteye/backups`）和 `agenteye-backup` ServiceAccount。该 Job **流式**将归档上传到 S3（`tar cz … | aws s3 cp - s3://…`）。如果备份 Pod 没有该存储桶的写入权限，上传会报错——由于脚本在 `set -euo pipefail` 下运行，管道中任何位置的失败都会在 `upload` 步骤导致整个 Job 失败，而非静默失效（Pod 的 EXIT trap 会记录 `backup FAILED during step: upload`）。这也是修复临时卷驱逐问题后你会遇到的步骤，因此如果备份之前在归档步骤被驱逐，请验证上传现在是否成功。在失败 Job 的日志中搜索 S3 访问错误：

```bash theme={null}
kubectl logs -n agenteye -l job-name=<failed agenteye-backup job> | grep -iE 's3|upload|denied'
```

修复方案：在你的 overlay 中将 `BACKUP_BUCKET` 设置为你拥有的存储桶，并为现有的 `agenteye-backup` ServiceAccount 添加写入权限注解（IRSA / Workload Identity / Pod Identity）。详见 [enterprise-docs/kubernetes-deployment.md](/zh/agenteye/kubernetes-deployment) 中的**备份**章节。

***

## ClickHouse 支持的评估 / 会话 / 查询

### 升级后 `/queries` 页面侧边栏为空

预期存在三张表（`events`、`evaluations`、`agent_sessions`）。如果升级后 SchemaBrowser 侧边栏为空，说明服务器在启动时未能应用 ClickHouse DDL。检查服务器日志中的 `failed to apply CH DDL statement`：

```bash theme={null}
kubectl logs -n agenteye deploy/server | grep -E 'clickhouse|CH DDL'
```

最常见的原因是迁移运行期间 ClickHouse 不可达。服务器在无法访问 ClickHouse 时会拒绝启动，因此卡住的 Pod 通常处于 `CrashLoopBackOff` 状态，而非出现静默损坏的查询页面。但部分 DDL 应用（某条语句成功，后续报 5xx）会导致 schema 处于不完整状态。在确认 ClickHouse 可达后，重启服务器 Pod：

```bash theme={null}
kubectl rollout restart deploy/server -n agenteye
```

### 新评估未出现在 `/sessions` 或 `/queries` 中

升级后，新评估被写入 ClickHouse 而非 Postgres，并在 `/sessions`（需要 `evaluations:read` 权限）和 `/queries` 中显示。如果未出现：

1. 确认评估器管道已启用（服务器上已设置 `EVALUATOR_ENDPOINT`）且正在产生终止结果；检查是否有 `evaluation_finalized` 日志行。
2. 确认服务器可访问 ClickHouse：`kubectl exec -n agenteye deploy/server -- curl -fsS http://clickhouse:8123/ping`。
3. 抽查 ClickHouse 表：`kubectl exec -n agenteye clickhouse-0 -- clickhouse-client -q 'SELECT count() FROM agenteye.evaluations'`。

### 负载下查询报 "Memory limit exceeded"，或 ClickHouse 被 `OOMKilled`

**症状：** 在仪表盘/查询负载较重时，分析页面（事件流、`/sessions`、模型/延迟视图、SQL 编辑器）开始失败或超时；服务器短暂抖动至 `NotReady`；ClickHouse Pod 的重启次数持续上升。这几乎总是**内存**问题，而非 CPU 或磁盘问题。

**确认是内存问题**（而非可通过复制解决的吞吐量问题）：

1. 检查 Pod 是否因内存不足被杀：
   ```bash theme={null}
   kubectl -n agenteye describe pod clickhouse-0 | grep -iE 'Restart Count|Last State|Reason|OOMKilled'
   ```
   `Reason: OOMKilled` / `Exit Code: 137` 加上持续上升的重启次数是明确信号。

2. 查询 ClickHouse 正在拒绝的内容：
   ```bash theme={null}
   kubectl -n agenteye exec clickhouse-0 -- clickhouse-client -q \
     "SELECT name, value FROM system.errors WHERE value>0 ORDER BY value DESC"
   ```
   `MEMORY_LIMIT_EXCEEDED` 计数较大是典型特征。错误消息中显示 *"maximum: N GiB"*——该 **N 值为 Pod 内存限制的 `0.9` 倍**（即 `deploy/base/clickhouse/configmap.yaml` 中的 `max_server_memory_usage_to_ram_ratio`）。如果繁重的读取需要超过 N 的内存，请求就会被拒绝。

3. 排除*不是*问题的因素——如果 CPU、part 数量和磁盘都较低，增加副本/分片只是浪费成本：
   ```bash theme={null}
   kubectl -n agenteye top pod clickhouse-0
   kubectl -n agenteye exec clickhouse-0 -- clickhouse-client -q \
     "SELECT table, count() parts FROM system.parts WHERE active AND database='agenteye' GROUP BY table"
   ```

**原因：** ClickHouse Pod 的内存限制对于分析工作集来说太小。最繁重的读取操作会拉取原始 JSON `payload` 列，对其运行 `JSONExtract*`，并使用 `FINAL`——每项操作都可能需要数 GiB 内存。如果配置的缓存（`mark_cache_size` + `uncompressed_cache_size`）大于 Pod 内存，问题会加剧：缓存和查询内存共享同一预算，缓存会挤占查询内存。

**修复——扩展 ClickHouse 内存：**

1. 在 overlay 中通过 patch `clickhouse` StatefulSet 容器的 `resources` 来提高 ClickHouse 内存限制（与其他组件 `resources` 使用相同的 overlay 机制）。可用的服务器预算为 `0.9 × 限制`，因此 `6Gi` 限制提供约 5.4 GiB，`16Gi` 限制提供约 14 GiB。同时将 `requests.memory` 设置为实际下限，让调度器进行预留。应用此配置**会重新创建 ClickHouse Pod**（单副本约有 30–60 秒的分析停机）；请在低流量时段操作。
2. 保持 `deploy/base/clickhouse/configmap.yaml` 中缓存与内存限制的比例——在小 Pod 上，小缓存（几百 MiB）是安全的；仅在同时提高内存限制时才增大缓存。`users.xml` profile 中显式设置了单查询的 `max_memory_usage`（见下文固定节点章节），并保持在服务器级上限（`0.9 × 限制`）以下，确保没有单个查询被允许使用超过容器拥有的内存。
3. 如果节点本身是瓶颈，检查 ClickHouse 可见的主机内存：
   ```bash theme={null}
   kubectl -n agenteye exec clickhouse-0 -- clickhouse-client -q \
     "SELECT formatReadableSize(value) FROM system.asynchronous_metrics WHERE metric='OSMemoryTotal'"
   ```
   如果该值仅略高于 Pod 限制，请在进一步提高限制之前，通过 overlay 中的节点选择器/亲和性将 ClickHouse 迁移到更大的（内存优化型）节点。

**无法增加内存时：让查询在内存中运行并快速失败——不要在慢速磁盘上溢出。** 如果节点固定且 Pod 无法扩容，请限制每个查询可使用的内存上限（避免单个查询耗尽整个节点），并且在**慢速（非 SSD）数据磁盘**上，**不要**让大型聚合/排序溢出到磁盘。在慢速磁盘上溢出比服务器的客户端读取超时还慢，溢出查询会在仪表盘返回 `500` 后继续在 ClickHouse 中执行——将查询保持在内存中并快速拒绝偶尔超预算的查询（`MEMORY_LIMIT_EXCEEDED`，亚秒级）才能恢复正常加载。应用这些配置时需注意 ClickHouse 的一个陷阱：

* **这些是 *profile* 配置，ClickHouse 只从 `users_config`（`users.xml` / `users.d/*.xml`）读取 `<profiles>` 块——绝不从 `config.d` 读取。** 放置在 `config.d/agenteye.xml` 中的 `<profiles>` 块会**被静默忽略**（`max_execution_time`、`max_memory_usage` 等根本不会生效）。因此，附带的配置将它们作为 `users.xml` key 放在 `clickhouse-config` ConfigMap 中，挂载到 `/etc/clickhouse-server/users.d/agenteye.xml`。
* 内置默认值：`max_memory_usage`（单查询上限——单个查询不能消耗整个服务器预算）、`max_bytes_before_external_group_by` / `max_bytes_before_external_sort` = **`0`（禁用溢出）**，使查询保持在内存中而不在慢速磁盘上爬行，以及 `max_execution_time`（失控保护，与服务器的客户端读取超时对齐）。
* **验证配置已生效**（这也是检测 config.d 陷阱的方法）：
  ```bash theme={null}
  kubectl -n agenteye exec clickhouse-0 -- clickhouse-client -q \
    "SELECT name, value FROM system.settings
     WHERE name IN ('max_memory_usage','max_bytes_before_external_group_by','max_execution_time')"
  ```
  期望看到非零的 `max_memory_usage` 和 `max_bytes_before_external_group_by = 0`。如果 `max_memory_usage` 读取为 `0`/默认值，说明 profile 未被应用——检查配置是否在 `users.d` 挂载中，而非 `config.d`。

权衡：禁用溢出后，工作集超过 `max_memory_usage` 的查询会被**拒绝**（`MEMORY_LIMIT_EXCEEDED`），而非缓慢完成——在慢速磁盘上，这种快速拒绝更可取，因为溢出查询无论如何都会超过客户端超时而失败。如果你的数据磁盘**速度快（SSD）**，可以提高 `max_bytes_before_external_*` 阈值，允许大型查询溢出到磁盘并完成。

***

## 多租户（组织）

### 启用组织的升级过程中出现错误（新旧服务器 Pod 混合）

**症状：** 在滚动部署启用组织功能的版本时，部分请求失败：服务器日志在 `api_keys` 路径上显示 `there is no unique or exclusion constraint matching the ON CONFLICT specification`，以及/或告警/Slack/webhook 频道在滚动发布期间停止触发。

**原因：** 该升级将 `api_keys(name)` 上的旧实例级唯一索引替换为按组织划分的部分索引，并将告警频道配置（以及 `default_user_permissions`）从全局 `settings` 表迁移到每个组织的 `org_settings`。**旧版**服务器 Pod 仍然发出 `ON CONFLICT (name)`（现在没有匹配的约束）并从旧的 `settings` 行（现在为空）读取频道配置。新旧 Pod 对这两条路径无法安全共存。

**修复：** 不要对这次特定升级进行跨版本的慢速滚动。请彻底切换：将旧版服务器缩容至零（或使用短暂的维护窗口），与迁移一起启动新版本，而非同时运行新旧副本。切换完成后流量和采集立即恢复；这只影响版本过渡窗口。

### 创建组织在 `CREATE USER` / `CREATE ROW POLICY` 时失败，或一个组织能读取另一个组织的数据

**症状：** 创建组织时返回提及 `CREATE USER`、`CREATE ROW POLICY` 或"access management is disabled"的错误；更严重的情况是，一个组织的成员在 SQL 编辑器或助手中看到另一个组织的事件/评估数据。

**原因：** 按组织的隔离通过每个组织专用的 ClickHouse 用户 + 行策略来实现。这需要在 ClickHouse 上启用 SQL **访问管理**，并将 `users_without_row_policies_can_read_rows=false`。禁用访问管理时，创建组织时无法创建用户/策略；行策略默认值保持宽松时，有 SELECT 权限但无策略的用户会读取**所有**行（默认开放）。

**修复：** 使用附带的 `deploy/base/clickhouse/` 配置，它同时设置了这两项。如果你使用自定义的 ClickHouse 配置，请在服务器内部用户上启用 SQL 访问管理，并设置 `users_without_row_policies_can_read_rows=false`（参见 `deploy/base/clickhouse/configmap.yaml`），然后重启 ClickHouse 并使用 `agenteye-orgctl` CLI 重新创建组织（参见 [enterprise-docs/tenant-management.md](/zh/agenteye/tenant-management)）。

### 修改 `ORG_CH_SECRET` 后组织用户失去 ClickHouse 访问权限

**症状：** 修改 `ORG_CH_SECRET` 或在不同副本上设置不一致后，SQL 编辑器和 AI 助手立即对所有组织返回 ClickHouse 认证失败。

**原因：** 每个组织的 ClickHouse 密码是以 `ORG_CH_SECRET` 为密钥的 HMAC 派生值。轮换该密钥（或在副本上运行不同的值）会使每个组织存储的 ClickHouse 凭据失效；派生密码不再匹配已创建的用户。

**修复：** 在创建第二个组织**之前**将 `ORG_CH_SECRET` 设置为一个强值，并在所有服务器副本上保持稳定一致。服务器的启动时协调会在启动时根据当前 Secret 重新创建每个组织的 ClickHouse 用户，因此对所有副本重启服务器（Secret 保持一致）会修复孤立的用户。请将该值视为长期 Secret，不要随意轮换。作为安全保护，如果 `ORG_CH_SECRET` 保持内置的开发默认值（即未设置），启动时协调**会跳过**非默认组织并记录错误，而非将其 ClickHouse 凭据重写为公知的开发值——这样单个副本在没有 Secret 的情况下重启时不会破坏其他副本。请一致设置该 Secret 并重启，以完成这些组织的创建。

### 启用组织后 AI 助手返回 400 / 拒绝聊天

**症状：** 助手面板加载成功，但每条消息都返回错误（HTTP `400`），且 agent 日志记录了被拒绝的无组织上下文的 `/chat` 请求。

**原因：** agent 具有组织感知能力，采用失败关闭策略；它会拒绝没有携带组织上下文的 `/chat` 请求。这发生在过渡性滚动发布期间，agent 已升级但发送请求的仪表盘尚未具备组织感知能力。

**修复：** 完成滚动发布，使仪表盘发送组织上下文（正常最终状态，无需特殊标志）。在尚未升级的仪表盘与已升级的 agent 通信的过渡期间，在 `agent` 服务上设置 `AGENTEYE_AGENT_ALLOW_NO_ORG=1`，使其回退到 `default` 组织而非拒绝请求，待仪表盘升级完成后清除该设置。详见 [enterprise-docs/assistant.md](/zh/agenteye/assistant#environment-variable-reference) 中的环境变量参考。

***

## 审计

### 审计从未运行（下次运行时间持续推迟，运行历史为空）

**症状：** 审计页面显示*上次运行：从未*，或 `next run` 持续移到未来而运行历史中没有出现任何记录。

**原因：** 审计已禁用（禁用的审计没有队列条目），或服务器的审计工作进程无法认领工作。

**修复：** 确认审计已**启用**（立即运行按钮需要审计处于启用状态）。然后检查服务器日志中启动时的 `audits pipeline started` 以及 `audits:` 错误——`claim_due failed` 日志行指向 Postgres 连接问题。`AUDIT_WORKERS` 默认值为 `1`；必须 ≥ 1 才能运行任何审计。

### 审计运行成功但未发现任何问题

**症状：** 运行历史显示 `succeeded`，`findings: 0`，但 `/errors` 明确显示有失败记录。

**原因：** 扫描窗口未覆盖这些失败，或范围过滤器将其排除。

**修复：** 对照失败发生的时间检查运行记录的窗口（`window_from → window_to`）——在 `since_last` 模式下，每次运行只扫描自上次成功运行以来的数据，因此旧的失败只有*第一次*运行或 `fixed` 窗口审计才能看到。扩大 `scope`（环境 / agent ID）。运行统计显示 `policy_hits`（确定性策略触发次数）和 `improvements`（AI 调查记录的次数）——如果两者都为 0，说明该窗口/范围内确实没有发现任何内容。

### 运行显示 `analysis_unavailable`，只产生了 policy 类型的发现

**症状：** 运行统计包含 `analysis_unavailable`，且所有发现都是 `kind: policy`；没有 AI 改进建议出现。

**原因：** 智能体调查无法运行：服务器无法访问 agent 服务（**服务器**上未设置 `AGENTEYE_AGENT_URL` / `AGENTEYE_AGENT_TOKEN`——审计复用助手的连接），助手服务未配置 LLM，或调用报错/超时（`analysis_unavailable` 字符串中有详细信息）。确定
