> ## 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.

# Xử lý sự cố

> Tài liệu xử lý sự cố AgentEye.

Hướng dẫn này ánh xạ các triệu chứng bạn có khả năng gặp phải trong môi trường production đến một chẩn đoán cụ thể và cách khắc phục, để bạn có thể giải quyết sự cố từ các công cụ bạn đã có, mà không cần thiết lập thêm cơ sở hạ tầng quan sát. Nó bao gồm máy chủ, collector, dashboard, trợ lý AI, Python SDK, giám sát health và chứng chỉ, sao lưu, phân tích dựa trên ClickHouse và multi-tenancy.

Các trang dashboard được phân biệt theo org dưới `/<org-slug>/…`, và luồng sự kiện là trang chủ org (`/<org-slug>/`). Tên trang trong hướng dẫn này (ví dụ `/sessions`, `/queries`) đề cập đến những route được phân biệt theo org đó.

***

## Xem nhật ký

AgentEye không đi kèm với stack logging hoặc monitoring. Cả máy chủ và dashboard đều ghi structured logs vào **stdout**, vì vậy bạn có thể đọc chúng trực tiếp bằng `kubectl` hoặc `docker`; không cần aggregator.

### Kubernetes

Theo dõi nhật ký trực tiếp cho máy chủ và dashboard:

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

Các biến thể hữu ích:

| Mục đích                                 | Lệnh                                                              |
| ---------------------------------------- | ----------------------------------------------------------------- |
| 200 dòng cuối cùng (không theo dõi)      | `kubectl logs -n agenteye -l app=server --tail=200 --timestamps`  |
| Nhật ký từ lần crash trước đó            | `kubectl logs -n agenteye <pod-name> --previous`                  |
| Theo dõi tất cả các replica cùng một lúc | `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
```

### Liên kết một yêu cầu duy nhất qua dashboard và máy chủ

Mỗi yêu cầu dashboard được gắn thẻ với `request_id` và được lan truyền đến máy chủ qua header `x-request-id`. Máy chủ phản hồi nó trong header phản hồi và trong mỗi dòng nhật ký mà nó phát hành cho yêu cầu đó. Để theo dõi một yêu cầu từ đầu đến cuối:

1. Ghi lại id từ header phản hồi, ví dụ:
   ```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. Grep nhật ký của cả hai pod cho 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"
   ```

Bạn sẽ thấy các dòng `proxy passthrough`, `withAuth: authorized` và `upstream response` của dashboard cùng với cặp `http request received` / `http request completed` của máy chủ, tất cả đều chia sẻ `request_id` giống nhau.

### JSON logs và `jq`

Đặt `AE_LOG_JSON=1` trên dashboard (nó được bật mặc định khi `NODE_ENV=production`) để phát hành một đối tượng JSON trên mỗi dòng. Sau đó lọc cấu trúc:

```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")'
```

Máy chủ Rust phát hành các cặp `key=value` tracing mà grep tốt mà không cần `jq`:

```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='
```

### Tăng mức chi tiết

| Thành phần | Biến môi trường | Ví dụ                                                       |
| ---------- | --------------- | ----------------------------------------------------------- |
| Server     | `RUST_LOG`      | `RUST_LOG=debug` hoặc `RUST_LOG=agenteye_server=debug,info` |
| Dashboard  | `AE_LOG_LEVEL`  | `AE_LOG_LEVEL=debug`                                        |

`debug` trên máy chủ thêm một dòng `api key authenticated` cho mỗi auth. `debug` trên dashboard thêm các dòng `upstream request`, `session validated` và `proxy passthrough`.

### Giữ lại nhật ký

Stdout của container là tạm thời; kubelet xoay vòng các tệp nhật ký (mặc định \~10 MiB trên mỗi container) và giữ một số ít trên đĩa. Khi một pod bị xóa, nhật ký cũng biến mất. Nếu bạn cần giữ lâu hơn hoặc tìm kiếm giữa các pod, hãy trỏ cluster của bạn đến bộ thu log (Loki, CloudWatch, Cloud Logging, Datadog, v.v.) theo dõi `/var/log/containers/`. AgentEye không yêu cầu hoặc quy định bất kỳ lựa chọn cụ thể nào.

***

## Các vấn đề xác thực

### `docker pull` không thành công với "unauthorized"

Đảm bảo bạn đã xác thực Docker đối với GHCR bằng `AGENTEYE_TOKEN`:

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

Token phải có quyền `read:packages` trên org `agenteye-enterprise`. Liên hệ `support@exosphere.host` nếu token của bạn không hoạt động.

### `gh release download` trả về 404 hoặc 401

* Xác nhận `AGENTEYE_TOKEN` được xuất trong shell của bạn: `echo $AGENTEYE_TOKEN`
* Xác nhận bạn đang sử dụng `GITHUB_TOKEN=$AGENTEYE_TOKEN gh release download ...` (CLI `gh` đọc `GITHUB_TOKEN`)
* Token cần `contents:read` trên `agenteye-enterprise/releases`

***

## Các vấn đề máy chủ

### Máy chủ không thành công với "invalid port number"

`POSTGRES_PASSWORD` (hoặc thông tin xác thực khác) chứa các ký tự đặc biệt của URL (`/`, `+`, `=`) làm phá vỡ phân tích cú pháp `DATABASE_URL`. Tạo lại mật khẩu bằng mã hóa hex:

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

Sau đó cập nhật Kubernetes secret và mật khẩu bên trong Postgres (hoặc tạo lại `.env` cho Docker Compose), và khởi động lại máy chủ. Xem các bước đầy đủ trong [enterprise-docs/kubernetes-deployment.md](/vi/agenteye/kubernetes-deployment) § "PostgreSQL credentials".

### Máy chủ thoát ngay lập tức khi khởi động

Kiểm tra nhật ký container:

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

Các nguyên nhân phổ biến:

* `DATABASE_URL` không được đặt hoặc có định dạng sai: máy chủ sẽ ghi lại lỗi và thoát.
* Postgres không thể truy cập được: xác nhận container Postgres hoặc managed DB đang chạy và host/port chính xác.
* Migrations không thành công: kiểm tra nhật ký để tìm lỗi SQL.

### `GET /health` trả về non-200 hoặc timeout

Máy chủ vẫn có thể đang chạy migrations khi khởi động lần đầu. Đợi một vài giây và thử lại:

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

Nếu vấn đề vẫn tiếp tục, hãy kiểm tra `docker logs agenteye-server` để tìm lỗi.

### `GET /ready` trả về 503

`/ready` là readiness probe: nó trả về `503` khi máy chủ không thể truy cập **Postgres hoặc ClickHouse**. Phần thân đặt tên phụ thuộc không thành công:

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

Sửa phụ thuộc nào mà nó báo cáo là `down`: pod ClickHouse/Postgres có `Running` không? `CLICKHOUSE_URL` / `DATABASE_URL` có chính xác và có thể truy cập được không? Trên Kubernetes, pod đọc `NotReady` cho đến khi `/ready` khôi phục; điều đó là bình thường và chính xác là tín hiệu để giám sát health cảnh báo. Redis không bao giờ là nguyên nhân: nó được báo cáo nhưng không làm hỏng readiness.

### Collector trả về 401 Unauthorized

API key của collector không có quyền `events:add`, hoặc key đã bị vô hiệu hóa. Tạo key mới với quyền chính xác:

```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"]}'
```

### Các yêu cầu được xác thực đột ngột chậm (\~200ms thay vì \~5ms)

Đây là triệu chứng của Redis bị down trong khi `REDIS_URL` được đặt. Mỗi lệnh gọi cache timeout sau 100ms rồi rơi vào Postgres; trên các đường dẫn auth và OTP, yêu cầu tạo ra hai lần rơi như vậy.

Xác nhận trong nhật ký máy chủ:

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

Giải pháp:

1. `redis-cli -h <your-redis> ping` để xác nhận Redis có thể truy cập được trên mạng cluster.
2. Nếu Redis bị down tạm thời và hiện tại lại hoạt động, **khởi động lại server pods**. `redis::aio::ConnectionManager` không tái thiết lập đáng tin cậy sau khi kết nối cơ bản bị ngắt; khởi động lại pod tải kết nối mới một cách sạch sẽ. Điều tương tự cũng áp dụng cho dashboard.
3. Nếu bạn không muốn chạy Redis ngay bây giờ, hãy bỏ đặt `REDIS_URL` trong deployment và khởi động lại. Cả hai dịch vụ chạy mà không có cache (độ chính xác được bảo tồn; độ trễ quay trở lại baseline trước Redis).

### Máy chủ báo cáo `OTP request rate-limited` trong nhật ký nhưng người dùng nói họ chỉ thử một lần

Kiểm tra xem Redis có bị không thể truy cập được không. Đường dẫn fallback sử dụng `SELECT COUNT(*) FROM otp_codes WHERE created_at > now() - interval '15 minutes'`, nó nhìn thấy các hàng OTP được tạo trước đó. Nếu người dùng đã nhấp vào "Resend" suốt một giờ, cửa sổ 15 phút có thể vẫn chứa ≥5 mã. Giải quyết bằng cách chờ cửa sổ cuộn qua hoặc `DELETE FROM otp_codes WHERE user_id = $1 AND created_at > now() - interval '15 minutes'` (bảng điều khiển nhà điều hành).

### Tôi đã thay đổi `ALLOWED_EMAILS` / `SESSION_TTL_SECS` / `OTP_TTL_SECS` và khởi động lại; không có gì xảy ra

Các biến môi trường này là **first-boot seeds chỉ**. Khi bảng `settings` có một hàng cho khóa phù hợp, hàng đó là nguồn sự thật; biến môi trường được đọc một lần khi khởi động lần đầu rồi bị bỏ qua ở mọi lần khởi động lại tiếp theo.

Để thay đổi chúng sau khi khởi động lần đầu, hãy đăng nhập vào dashboard và chỉnh sửa chúng dưới `/settings`. Thay đổi được áp dụng trong vòng một vài giây trên tất cả các replica; không cần khởi động lại.

Nếu bạn cần buộc re-seed từ env (hiếm, thường chỉ hữu ích trong phát triển), `DELETE FROM settings WHERE key = '<key>'` và khởi động lại máy chủ. Bootstrap sẽ nhận giá trị biến môi trường hiện tại khi khởi động lần tiếp theo. Chỉnh sửa qua `/settings` là con đường được hỗ trợ trong production.

***

## Các vấn đề Collector

### Collector bắt đầu nhưng các sự kiện không xuất hiện trong dashboard

1. Xác nhận collector đang chạy: `systemctl status agenteye-collector` (Linux) hoặc kiểm tra quy trình.
2. Xác nhận `AGENTEYE_URL` trỏ đến `http(s)://your-server-host:8080/events` (lưu ý: đường dẫn `/events`).
3. Chạy một flush một lần để xem đầu ra ngay lập tức:
   ```bash theme={null}
   agenteye-collector flush
   ```
4. Kiểm tra xem Python SDK có thực sự ghi các tệp không: `ls ${AGENTEYE_HOME:-~/.agenteye}/events/`
5. Nếu các tệp tồn tại trong `${AGENTEYE_HOME:-~/.agenteye}/failed/`, các uploads đang thất bại. Kiểm tra nhật ký collector để tìm lỗi, có khả năng là 4xx (bad key hoặc URL) hoặc sự cố mạng.

### Các tệp đang tích lũy trong `$AGENTEYE_HOME/events/` và không được tải lên

* Collector có thể không chạy. Khởi động nó: `agenteye-collector start`; nó tự động flush các sự kiện đã tồn tại khi khởi động.
* Kiểm tra health của collector: `agenteye-collector health`
* Collector có thể chạy nhưng không thể tiếp cận máy chủ. Kiểm tra quy tắc tường lửa giữa các host collector và máy chủ.

### Các tệp trong `$AGENTEYE_HOME/failed/`

Các tệp chuyển đến `failed/` sau khi tất cả các lần thử lại bị cạn kiệt (mặc định: 5 lần với backoff hàm mũ). Điều này có nghĩa là:

* Máy chủ trả về lỗi 4xx (bad key, URL sai, hoặc sự cố payload)
* Máy chủ không thể truy cập được cho toàn bộ cửa sổ thử lại

Sửa vấn đề cơ bản, sau đó xếp hàng lại thủ công:

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

### Collector báo cáo `network error` trên mọi lần tải lên (TLS handshake không thành công)

Nếu `curl -k` đối với `AGENTEYE_URL` thành công nhưng nhị phân collector thất bại mọi lần tải với `error sending request for url (...)`, máy chủ AgentEye đang trình bày chứng chỉ TLS không được ký bởi CA đáng tin cậy công khai.

**Đường dẫn production** là tên máy chủ ingest ACME được cấu hình trong `deploy/base/certificates/domain.env` (xem [`kubernetes-deployment.md`](/vi/agenteye/kubernetes-deployment) Phase 3.1 / 4.2). Khi `INGEST_DOMAIN` phân giải thành public Traefik LB và cert-manager đã cấp chứng chỉ Let's Encrypt, collectors xác minh chứng chỉ máy chủ dựa trên cây tin cậy hệ thống với **không cần `AGENTEYE_TLS_CA`**; xóa nó khỏi cấu hình collector của bạn nếu nó được đặt chống lại việc triển khai tự ký cũ hơn.

**Triệu chứng: collector hoạt động hôm qua, thất bại hôm nay sau khoảng 90 ngày.** Điều này có nghĩa là triển khai vẫn còn trên issuer `selfsigned` cũ cho `ingest-tls`. Chứng chỉ 90 ngày xoay vòng và tệp CA được ghim là đã lỗi thời. Sửa vĩnh viễn bằng cách chuyển cluster sang issuer ACME (Phase 3.1 của hướng dẫn triển khai). Giải pháp ngắn hạn: tái trích xuất chứng chỉ máy chủ hiện tại và cập nhật `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` thêm một neo tin cậy bổ sung; các gốc công khai tiêu chuẩn vẫn được tin cậy.

### Chứng chỉ `ingest-tls` bị kẹt `Ready: False` sau khi triển khai

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

Nhìn vào `Events` và `Order` / `Challenge` được tham chiếu. Các nguyên nhân phổ biến:

* **DNS không phân giải thành public LB.** Trình xác nhận HTTP-01 không thể truy cập `INGEST_DOMAIN`. Xác minh với `dig +short INGEST_DOMAIN`; nó phải phân giải thành cùng một địa chỉ với `traefik-public` LoadBalancer's `EXTERNAL-IP`. cert-manager tự động thử lại khi DNS lan truyền; không cần xóa Chứng chỉ.
* **Port 80 bị chặn ở load balancer / security group.** HTTP-01 yêu cầu port 80 có thể truy cập được từ các bộ xác nhận công khai của Let's Encrypt. Nếu bạn có WAF upstream hoặc SG hạn chế `:80`, hãy mở nó (cấu hình Traefik chuyển hướng đến HTTPS, nhưng Boulder theo dõi chuyển hướng và chấp nhận phản hồi).
* **`dnsNames` không được thay thế.** Nếu `kubectl get certificate ingest-tls -n agenteye -o jsonpath='{.spec.dnsNames}'` cho thấy `INGEST_DOMAIN_PLACEHOLDER`, bạn đã bỏ qua bước `domain.env`; tạo nó từ `domain.env.example` và áp dụng lại.
* **Bị giới hạn tốc độ bởi Let's Encrypt.** Các đơn hàng thất bại lặp lại cho cùng một tên máy chủ kích hoạt giới hạn chứng chỉ trùng lặp hoặc xác thực thất bại. Đợi ít nhất một giờ trước khi thử lại; kiểm tra trạng thái Order để tìm thông báo giới hạn tốc độ chính xác.

### Chứng chỉ `dashboard-tls` bị kẹt `Ready: False` / trình duyệt vẫn hiển thị cảnh báo

Cùng dòng chẩn đoán như `ingest-tls` ở trên (`kubectl describe certificate dashboard-tls -n agenteye`); các nguyên nhân DNS, port-80, placeholder và rate-limit đều áp dụng, cộng với hai nguyên nhân cụ thể dashboard:

* **`DASHBOARD_DOMAIN` phân giải thành LoadBalancer sai.** Nó phải trỏ đến *dashboard* Traefik LB, không phải ingest công khai. `dig +short` tên máy chủ và so sánh với địa chỉ LB dashboard.
* **Dashboard Traefik instance không thể phục vụ challenge.** Nó phải được cài đặt với tệp giá trị dashboard được đặt kèm, cho phép Ingress provider phạm vi cho trình giải quyết HTTP-01 của cert-manager. Không có nó, trình giải quyết không có thể định tuyến được và Đơn hàng vẫn `pending` mãi mãi. Nâng cấp instance với các giá trị được cung cấp; challenge đang chờ xử lý sau đó hoàn thành tự động.
* **LoadBalancer bị hạn chế IP.** Phạm vi nguồn áp dụng cho port 80 cũng, điều này chặn các bộ xác nhận của Let's Encrypt — cả lần cấp đầu tiên và mỗi lần gia hạn \~75 ngày. Mở lại LB, hoặc phối hợp một trình giải quyết DNS-01 với hỗ trợ trước khi khóa nó.

Khi cấp phát đang thất bại, dashboard tiếp tục phục vụ chứng chỉ trước đó của nó (hoặc mặc định ingress trên cài đặt mới) — truy cập bị suy giảm bởi cảnh báo trình duyệt, không bao giờ bị ngắt.

### CLI vẫn bỏ qua xác minh TLS sau khi dashboard có chứng chỉ đáng tin cậy

`--insecure` được duy trì vào `cli.json` khi đăng nhập. Khi dashboard phục vụ chứng chỉ công khai đáng tin cậy, hãy đăng nhập lại bằng `agenteye --base-url https://<your-dashboard-domain> --secure login`; xác minh được lưu lại và cảnh báo khởi động biến mất.

***

## Các vấn đề Dashboard

### Không thể vô hiệu hóa hoặc chỉnh sửa người dùng `ADMIN_EMAIL`

Thiết kế. Người dùng khớp với `ADMIN_EMAIL` được đánh dấu bảo vệ ở mỗi lần khởi động máy chủ: dashboard ẩn nút Disable cho hàng đó, và API từ chối `DELETE /users/:id` và `PUT /users/:id` chống lại nó với `403 Forbidden`. Trigger cơ sở dữ liệu cũng từ chối các câu lệnh `UPDATE` trực tiếp sẽ vô hiệu hóa hàng được bảo vệ.

Để xoay bootstrap admin, hãy thay đổi `ADMIN_EMAIL` trong môi trường của bạn và khởi động lại máy chủ. Email mới được upsert như bảo vệ. Admin trước đó giữ lại cờ bảo vệ cho đến khi được xóa trong cơ sở dữ liệu (thường tốt, vì email trước đó vẫn là admin hợp lệ cho đến khi bạn rõ ràng xóa họ).

### Dashboard không hiển thị sự kiện

1. Xác nhận URL máy chủ và API key chính xác trong các biến môi trường của dashboard (`AGENTEYE_SERVER_URL`, `AGENTEYE_API_KEY`).
2. API key dashboard cần quyền `events:read`.
3. Xác nhận các sự kiện đã được ingested thực sự: `curl http://your-server:8080/events -H "Authorization: Bearer $ADMIN_KEY"`

### `/errors` trống nhưng `/events` hiển thị các hàng đỏ

Các phiên bản SDK mới hơn phát hành các lỗi như các sự kiện `agent_end` / `tool_result` / `hook_completed` với `outcome: "error"` trong payload, chứ không phải như hàng `event_type: "error"` chuyên dụng. Trang `/errors` giờ đây khớp với cả hai: bất kỳ hàng nào luồng `/events` sơn đỏ (`event_type='error'` rõ ràng, `outcome`/`status` payload trong tập thất bại, `is_error: true`, hoặc trường `error` truthy) xuất hiện trên `/errors`. Nếu trước đây bạn thấy "không có lỗi trong cửa sổ này" trong khi các hàng đỏ được hiển thị trên `/events`, hãy nâng cấp dashboard + máy chủ cùng nhau (bộ lọc mở rộng là `errored=true` trên `GET /events`) và hai chế độ xem sẽ đồng ý.

### `/models`, `/tools`, hoặc `/hooks` chậm hoặc không tải trên phạm vi thời gian rộng

**Triệu chứng:** trên bảng sự kiện lớn (hàng triệu hàng), mở `/models`, `/tools`, hoặc `/hooks` — hoặc mở rộng phạm vi thời gian thành `7d`, `30d`, hoặc `all` — các biểu đồ xoay và sau đó hiển thị lỗi tải. Máy chủ ghi nhật ký `MEMORY_LIMIT_EXCEEDED` ClickHouse (Code 241) hoặc timeout truy vấn cho yêu cầu `latency_aggregate`.

**Nguyên nhân:** các bản dựng cũ hơn tính toán các rollup latency và phân phối trang này bằng một truy vấn đọc `payload` sự kiện thô hoàn chỉnh và ghép các sự kiện request/response với sắp xếp và kết hợp trong bộ nhớ. Do đó, bộ nhớ truy vấn đỉnh phát triển theo kích thước cửa sổ, vì vậy trên tenant bận rộn, một phạm vi rộng có thể vượt quá trần bộ nhớ cho mỗi truy vấn ClickHouse.

**Sửa:** nâng cấp lên bản dựng bao gồm sửa chữa này. Rollup giờ đây chỉ đọc các cột được quảng bá nhỏ gọn và ghép các sự kiện với tổng hợp truyền phát, vì vậy bộ nhớ đỉnh không còn mở rộng theo payload thô — các cửa sổ rộng vẫn nằm trong trần bộ nhớ và trả về trong một phần thời gian. Sự cải thiện hoàn toàn là query-side: nó áp dụng cho tất cả các dữ liệu hiện có khi tải trang tiếp theo, không có re-ingest hoặc backfill.

### Dashboard không tải được / trang trắng

Kiểm tra nhật ký container dashboard:

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

Nguyên nhân phổ biến nhất là `AGENTEYE_SERVER_URL` hoặc `AGENTEYE_API_KEY` bị thiếu hoặc trỏ đến máy chủ không thể truy cập được.

### Phân tích / telemetry dashboard

Dashboard gửi phân tích sử dụng sản phẩm ẩn danh cho PostHog mặc định, được định tuyến qua đường dẫn `/ingest` của chính dashboard (một reverse proxy cho `https://us.i.posthog.com`). Gửi chúng first-party có nghĩa là ad-blocker trình duyệt không bỏ chúng. Điều này độc lập với chức năng cốt lõi của dashboard:

* **Container dashboard** (không phải trình duyệt) là những gì đạt PostHog. Nếu truy cập outbound của nó đến `https://us.i.posthog.com` bị chặn, telemetry im lặng no-ops; dashboard hoạt động bình thường và không có lỗi nào được hiển thị cho người dùng.
* Dữ liệu agent, session hoặc event không bao giờ được đưa vào, chỉ là sử dụng UI dashboard.
* Để vô hiệu hóa telemetry hoàn toàn, hãy đặt `AE_ANALYTICS_DISABLED=1` trên container dashboard và khởi động lại. Xem [Telemetry & privacy](/vi/agenteye/deployment#telemetry--privacy) trong hướng dẫn triển khai.

### Telemetry / analytics CLI

CLI `agenteye` gửi phân tích sử dụng ẩn danh cho PostHog mặc định: các lệnh nào chạy, trạng thái thành công/thoát, và khoảng thời gian. Điều này độc lập với chức năng CLI:

* **Máy chạy CLI** đạt `https://us.i.posthog.com` trực tiếp. Nếu truy cập outbound của nó bị chặn, telemetry im lặng no-ops (gửi được giới hạn thời gian, vì vậy nó không bao giờ làm chậm lệnh) và CLI hoạt động bình thường.
* Dữ liệu agent, session hoặc event không bao giờ được đưa vào: **đối số lệnh và giá trị cờ** (URL dashboard, token, email, session ids, bộ lọc truy vấn) không bao giờ được gửi.
* Để vô hiệu hóa nó, hãy đặt `AGENTEYE_ANALYTICS_DISABLED=1` (hoặc `DO_NOT_TRACK=1` cross-tool) trong môi trường CLI. Xem [Telemetry & privacy](/vi/agenteye/cli#telemetry--privacy) trong hướng dẫn CLI.

***

## Các vấn đề trợ lý AI

Xem [enterprise-docs/assistant.md](/vi/agenteye/assistant) để thiết lập đầy đủ.

### Bubble trợ lý không xuất hiện

Bubble ẩn trừ khi **tất cả** những điều này giữ:

* Người dùng đã đăng nhập có quyền `agent:use`.
* `AGENTEYE_AGENT_URL` được đặt trên dashboard và dịch vụ `agent` có thể truy cập được.
* Một điểm cuối LLM được cấu hình trên dịch vụ `agent` (`ANTHROPIC_API_KEY`, gateway qua `ANTHROPIC_BASE_URL`, hoặc Bedrock/Vertex). Không có bộ nào được đặt, agent báo cáo "not configured" và bubble ở ẩn.

Kiểm tra health của agent từ host dashboard: `curl http://agent:9100/health` sẽ trả về `{"status":"ok","llm_configured":true,...}`.

### Trợ lý nói nó không thể đọc cái gì đó

Công cụ được gated theo người dùng. Nếu người dùng thiếu `evaluations:read` (hoặc `events:read`, `dashboards:read`), các công cụ phù hợp không được cung cấp và trợ lý sẽ nói nó không thể đọc dữ liệu đó. Cấp quyền đọc liên quan.

### "assistant not configured" (HTTP 503) khi gửi

Container `agent` không có điểm cuối LLM được cấu hình, hoặc `AGENTEYE_AGENT_TOKEN` của dashboard không khớp với agent. Đặt cả hai và khởi động lại.

### Container `agent` khởi động lại / OOMs dưới tải

Mỗi cuộc trò chuyện sinh ra một quá trình con tồn tại ngắn. Đảm bảo container chạy với một quá trình init (hình ảnh sử dụng `tini`; trong Compose đặt `init: true`) và cung cấp cho nó giới hạn bộ nhớ thích hợp. Giảm `AGENTEYE_AGENT_MAX_STEPS` nếu cần.

***

## Các vấn đề CLI

### `agenteye` không khởi động được với `ModuleNotFoundError: No module named 'click'`

Cài đặt tươi `agenteye` CLI ở phiên bản **0.1.6** có thể gặp sự cố khi khởi động với:

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

0.1.6 dựa vào `click` được cài đặt gián tiếp bởi `typer`; các bản phát hành `typer` hiện tại không còn kéo theo nó, vì vậy môi trường sạch sẽ kết thúc thiếu gói. **Nâng cấp lên 0.1.7 hoặc mới hơn**, phụ thuộc vào `click` trực tiếp:

```bash theme={null}
pipx upgrade agenteye      # nếu cài đặt bằng pipx (hoặc: pipx install --force agenteye)
uv tool upgrade agenteye   # nếu cài đặt bằng uv
pip install --upgrade agenteye
```

Xem [enterprise-docs/cli.md](/vi/agenteye/cli) để hướng dẫn cài đặt.

***

## Các vấn đề Python SDK

### Không có tệp nào xuất hiện trong `$AGENTEYE_HOME/events/`

SDK lưu đệm các sự kiện và flush mỗi 500 ms theo mặc định. Nếu quá trình của bạn thoát trước flush, các sự kiện có thể bị mất. Gọi `agenteye.configure(flush_interval=0.1)` để flush nhanh hơn trong các script có thời gian sống ngắn, hoặc đảm bảo quá trình của bạn chạy đủ lâu để một chu kỳ flush.

Nếu `AGENTEYE_HOME` được đặt, hãy xác minh SDK đang ghi vào `$AGENTEYE_HOME/events/` và không phải `~/.agenteye/events/` (yêu cầu SDK ≥ 0.0.1b5).

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

Tên `timestamp`, `type` và `environment` được dành riêng và không thể được sử dụng làm trường tùy chỉnh. Chuyển bất kỳ chúng tăng:

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

Đổi tên trường tùy chỉnh vi phạm. Lưu ý rằng `session_id` và `agent_id` là tham số rõ ràng của lệnh gọi sự kiện, không phải trường tùy chỉnh; chuyển cái nào lại làm trường tùy chỉnh tăng `TypeError`.

***

## Các vấn đề giám sát health

### Không có cảnh báo nào đến Slack (Robusta)

Cảnh báo health Robusta là **opt-in**; nó không gửi gì cho đến khi được cài đặt và trỏ đến kênh Slack. Xác minh bản phát hành và sink của nó:

```bash theme={null}
kubectl get pods -n robusta          # robusta-runner + robusta-forwarder nên Running
kubectl logs -n robusta -l app=robusta-runner --tail=50
```

Nguyên nhân phổ biến: `api_key` / `slack_channel` Slack không được đặt (hoặc token bị thu hồi); `api_key` là token cloud-relay Robusta (`robusta integrations slack`) nhưng `disableCloudRouting: true` được đặt kèm cần token **bot** Slack tự lưu trữ (`xoxb-…`), hoặc đặt `disableCloudRouting: false`; sink `scope` loại trừ namespace pods của bạn chạy trong (các giá trị được đặt kèm phạm vi đến `agenteye`); hoặc chưa có lỗi nào xảy ra. Buộc một cảnh báo thử bằng cách hạ pod:

```bash theme={null}
kubectl -n agenteye delete pod -l app=clickhouse   # nó sẽ được tạo lại
```

Xem [enterprise-docs/health-monitoring.md](/vi/agenteye/health-monitoring#2-pod-failure-alerting-with-robusta-opt-in) để cài đặt và cấu hình.

### Máy chủ giữ flapping `NotReady`

Readiness probe hits `/ready`, nó không thành công khi Postgres hoặc ClickHouse không thể truy cập được. Nếu máy chủ chu kỳ trong và ngoài `NotReady`, một phụ thuộc là không thể truy cập được từng lúc; kiểm tra các pod ClickHouse và Postgres và `CLICKHOUSE_URL` / `DATABASE_URL` của máy chủ. Xác nhận `/ready` báo cáo:

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

Probe này được cố ý khoan dung (ngưỡng thất bại hào phóng), vì vậy flapping kéo dài cho thấy vấn đề phụ thuộc thực tế chứ không phải probe quá tích cực. Liveness ở lại `/health`, vì vậy flapping readiness sẽ **không** khởi động lại pod.

## Các vấn đề giám sát chứng chỉ

### CronJob không gửi thông báo Slack

`cert-renewal-check` CronJob yêu cầu URL webhook Slack được lưu trữ trong một Secret. Xác minh nó tồn tại:

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

Nếu thiếu, hãy tạo nó:

```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"
```

Không có secret, CronJob vẫn chạy và ghi kết quả vào stdout. Kiểm tra nhật ký với:

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

### Chứng chỉ khách hàng hết hạn trước khi nhận được thông báo

CronJob chạy mỗi 12 giờ. Nếu nó chưa chạy, hãy kiểm tra trạng thái của nó:

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

Kích hoạt một kiểm tra thủ công:

```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
```

Để tái cấp chứng chỉ hết hạn ngay:

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

Sau đó áp dụng `collector-mtls-secret.yaml` được tạo lại trong cluster(s) chạy collectors của bạn và khởi động lại chúng:

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

***

## Các vấn đề sao lưu

### `agenteye-backup` không thành công với "No space left on device"

CronJob `agenteye-backup` xả Postgres + ClickHouse vào scratch volume `backup-tmp` `emptyDir` (mặc định `30Gi`), sau đó **streams** lưu trữ `tar` thẳng đến S3 — lưu trữ nén không bao giờ được ghi lại vào scratch, vì vậy scratch chỉ cần giữ **raw dumps**, không dumps + sao chép lưu trữ on-disk thứ hai. Pod evicted / `No space left on device` do đó có nghĩa là **raw dumps** vượt quá kích thước scratch (dump ClickHouse `events` chiếm ưu thế và phát triển theo thời gian). Kiểm tra nhật ký của công việc không thành công:

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

Sửa: trong overlay của bạn, tăng `sizeLimit` `emptyDir` `backup-tmp` của CronJob vượt quá raw dump tổng cộng, và đảm bảo node có thể thực sự giữ nó (`sizeLimit` là một cap, không phải đặt trước). Nếu dumps vượt quá đĩa của một nút duy nhất, hãy thay thế `emptyDir` bằng PVC (EBS/PD) cho `backup-tmp`, hoặc nén dumps ở nguồn.

> Các bản phát hành cũ hơn ghi `.tar.gz` vào *cùng* `20Gi` scratch như dumps, vì vậy `dumps + archive` tràn nó và pod được evicted **trước** upload chạy — điều này trông giống như lỗi S3 nhưng thực sự là đĩa. Streaming upload loại bỏ sự tăng gấp đôi đó.

### `agenteye-backup` không thành công cài đặt `curl`

Công việc chạy trên hình ảnh `postgres:16` và cài đặt `curl` khi khởi động để dump ClickHouse HTTP. Trên một cluster không có egress đến các gương gói Debian, bước `apt-get` không thành công. Hoặc cho phép egress đó từ backup pod, hoặc bake `curl` vào hình ảnh backup được soi kính/tùy chỉnh và tham chiếu nó trong overlay của bạn.

### `agenteye-backup` chạy nhưng không có gì hạ xuống kho lưu trữ đối tượng

Base vận chuyển một `BACKUP_BUCKET` thực (`ts-prod-agenteye/backups`) và `agenteye-backup` ServiceAccount. Công việc **streams** lưu trữ đến S3 (`tar cz … | aws s3 cp - s3://…`). Nếu backup pod không có quyền ghi vào bucket, upload lỗi — và vì script chạy dưới `set -euo pipefail`, một lỗi bất cứ nơi nào trong pipe đó **thất bại** toàn bộ công việc ở bước `upload` chứ không im lặng no-op (trap EXIT của pod ghi nhật ký `backup FAILED during step: upload`). Đây cũng là bước bạn tiếp cận *sau* khi sửa lỗi khoảng trắng scratch, vì vậy nếu backups trước đây bị evicted ở bước lưu trữ, hãy xác minh upload hiện tại hạ xuống. Grep nhật ký công việc không thành công để tìm lỗi S3:

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

Sửa: trong overlay của bạn đặt `BACKUP_BUCKET` thành bucket bạn sở hữu và chú thích `agenteye-backup` ServiceAccount hiện có bằng quyền ghi (IRSA / Workload Identity / Pod Identity). Xem phần **Backups** của [enterprise-docs/kubernetes-deployment.md](/vi/agenteye/kubernetes-deployment).

***

## Đánh giá / sessions / queries dựa trên ClickHouse

### Sidebar trang `/queries` trống sau khi nâng cấp

Ba bảng (`events`, `evaluations`, `agent_sessions`) được dự kiến. Nếu sidebar SchemaBrowser trống sau khi nâng cấp, máy chủ không áp dụng DDL ClickHouse khi khởi động. Kiểm tra nhật ký máy chủ để tìm `failed to apply CH DDL statement`:

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

Nguyên nhân phổ biến nhất là ClickHouse không thể truy cập được trong khi migrations chạy. Máy chủ từ chối khởi động nếu không thể tiếp cận CH, vì vậy một pod bị kẹt thường có `CrashLoopBackOff` chứ không phải trang queries im lặng bị hỏng, nhưng DDL partial apply (một câu lệnh OK, 5 tiếp theo 5xx) để lại schema nửa nướng. Khởi động lại server pod sau khi CH được xác minh có thể truy cập:

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

### Các đánh giá mới không xuất hiện trong `/sessions` hoặc `/queries`

Sau khi nâng cấp, các đánh giá mới được ghi vào ClickHouse, không phải Postgres, và bề mặt dưới `/sessions` (gated trên `evaluations:read`) và trong `/queries`. Nếu chúng không xuất hiện:

1. Xác nhận đường dẫn evaluator được bật (`EVALUATOR_ENDPOINT` được đặt trên máy chủ) và sản xuất các kết quả terminal; kiểm tra các dòng `evaluation_finalized`.
2. Xác nhận CH có thể truy cập được từ máy chủ: `kubectl exec -n agenteye deploy/server -- curl -fsS http://clickhouse:8123/ping`.
3. Kiểm tra kỹ bảng CH: `kubectl exec -n agenteye clickhouse-0 -- clickhouse-client -q 'SELECT count() FROM agenteye.evaluations'`.

### Truy vấn thất bại dưới tải bằng "Memory limit exceeded", hoặc ClickHouse bị `OOMKilled`

**Triệu chứng:** dưới tải dashboard/truy vấn nặng, các trang phân tích (luồng sự kiện, `/sessions`, chế độ xem models/latency, trình chỉnh sửa SQL) bắt đầu thất bại hoặc timeout; máy chủ flaps `NotReady` một lúc; và pod ClickHouse hiển thị số lần khởi động lại tăng. Đây gần như luôn luôn là **bộ nhớ**, không phải CPU hoặc đĩa.

**Xác nhận đó là bộ nhớ** (không phải vấn đề throughput mà sao chép sẽ sửa):

1. Kiểm tra pod để tìm các lần bị kill out-of-memory:
   ```bash theme={null}
   kubectl -n agenteye describe pod clickhouse-0 | grep -iE 'Restart Count|Last State|Reason|OOMKilled'
   ```
   `Reason: OOMKilled` / `Exit Code: 137` với số lần khởi động lại tăng là điểm chỉ.

2. Hỏi ClickHouse cái gì nó từ chối:
   ```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"
   ```
   Số lượng `MEMORY_LIMIT_EXCEEDED` lớn là chữ ký. Thông báo đọc *"maximum: N GiB"* — **N là `0.9 × giới hạn bộ nhớ pod`** (`max_server_memory_usage_to_ram_ratio` trong `deploy/base/clickhouse/configmap.yaml`). Nếu các đọc nặng của bạn cần nhiều hơn N, chúng bị từ chối.

3. Loại trừ những điều *không phải* là vấn đề — nếu CPU, part count và đĩa đều thấp, thêm replicas/sharding sẽ lãng phí chi phí:
   ```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"
   ```

**Nguyên nhân:** giới hạn bộ nhớ pod ClickHouse quá nhỏ cho tập làm việc phân tích. Những lần đọc nặng nhất kéo cột `payload` JSON thô, chạy `JSONExtract*` trên nó và sử dụng `FINAL` — mỗi cái có thể cần vài GiB. Nếu các cache được cấu hình (`mark_cache_size` + `uncompressed_cache_size`) lớn hơn pod, chúng tổng hợp nó: các cache được tính vào cùng ngân sách và làm đầy bộ nhớ truy vấn.

**Sửa — mở rộng bộ nhớ ClickHouse:**

1. Tăng giới hạn bộ nhớ ClickHouse trong overlay của bạn bằng cách vá `resources` container của `clickhouse` StatefulSet (cơ chế overlay tương tự được sử dụng cho `resources` của các thành phần khác). Ngân sách máy chủ có thể sử dụng là `0.9 × limit`, vì vậy giới hạn `6Gi` cung cấp \~5.4 GiB, `16Gi` cung cấp \~14 GiB. Đặt `requests.memory` thành một sàn thực, vì vậy bộ lập lịch dự trữ nó. Áp dụng điều này **tạo lại pod CH** (replica duy nhất → \~30–60s downtime phân tích); làm nó trong cửa sổ lưu lượng thấp.
2. Giữ các cache trong `deploy/base/clickhouse/configmap.yaml` tỷ lệ với giới hạn — các cache nhỏ (vài trăm MiB) an toàn trên một pod nhỏ; chỉ tăng chúng cùng với tăng giới hạn bộ nhớ phù hợp. Per-query `max_memory_usage` được đặt rõ ràng trong profile `users.xml` (xem phần nút cố định bên dưới) và được giữ dưới cap level máy chủ (`0.9 × limit`) để không có truy vấn duy nhất *được phép* RAM nhiều hơn container có.
3. Nếu chính nút là trần, hãy kiểm tra bộ nhớ máy chủ ClickHouse có thể thấy:
   ```bash theme={null}
   kubectl -n agenteye exec clickhouse-0 -- clickhouse-client -q \
     "SELECT formatReadableSize(value) FROM system.asynchronous_metrics WHERE metric='OSMemoryTotal'"
   ```
   Nếu chỉ có một chút trên giới hạn pod, hãy chuyển ClickHouse đến một nút lớn hơn (tối ưu hóa bộ nhớ) — qua node selector/affinity trong overlay của bạn — trước khi tăng giới hạn thêm.

**Khi bạn không thể thêm bộ nhớ: chạy truy vấn trong RAM và thất bại nhanh — không tràn trên đĩa chậm.** Nếu nút được sửa và pod không thể lớn, cắt những gì bất kỳ truy vấn nào có thể sử dụng (vì vậy một truy vấn không thể chiếm toàn bộ nút) và, trên **đĩa dữ liệu chậm (non-SSD)**, **không** để các tổng hợp/sắp xếp lớn tràn trên đĩa. Tràn trên đĩa chậm chậm hơn timeout đọc khách hàng máy chủ, vì vậy một truy vấn tràn trả về dashboard `500` giữa chuyến bay trong khi ClickHouse tiếp tục xay — giữ truy vấn trong RAM và từ chối cái hiếm quá ngân sách một *nhanh* (`MEMORY_LIMIT_EXCEEDED`, sub-second) là cái khôi phục tải. Lưu ý một gotcha ClickHouse để áp dụng các cài đặt này:

* **Đây là cài đặt *profile*, và ClickHouse đọc `<profiles>` chỉ từ `users_config` (`users.xml` / `users.d/*.xml`) — không bao giờ từ `config.d`.** Một khối `<profiles>` được đặt trong `config.d/agenteye.xml` là **im lặng bị bỏ qua** (`max_execution_time`, `max_memory_usage`, v.v. đơn giản là không áp dụng). Do đó, cấu hình được đặt kèm gửi chúng làm khóa `users.xml` trên `clickhouse-config` ConfigMap, được gắn tại `/etc/clickhouse-server/users.d/agenteye.xml`.
* Các mặc định được gửi: `max_memory_usage` (per-query ceiling — một truy vấn không thể tiêu thụ toàn bộ ngân sách máy chủ), `max_bytes_before_external_group_by` / `max_bytes_before_external_sort` = **`0` (tràn vô hiệu)** vì vậy truy vấn ở RAM chứ không bò trên đĩa chậm, và `max_execution_time` (bảo vệ runaway, căn chỉnh với timeout đọc khách hàng máy chủ).
* **Xác minh chúng hoạt động** (đây cũng là cách bạn phát hiện gotcha 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')"
  ```
  Mong đợi `max_memory_usage` khác không và `max_bytes_before_external_group_by = 0`. Nếu `max_memory_usage` đọc `0`/default, profile không được áp dụng — kiểm tra các cài đặt hoạt động trong `users.d` mount, không `config.d`.

Trade-off: với tràn vô hiệu, một truy vấn có tập làm việc vượt quá `max_memory_usage` là **bị từ chối** (`MEMORY_LIMIT_EXCEEDED`) chứ không phải hoàn thành chậm — trên đĩa chậm trước đó từ chối nhanh là tốt hơn, vì một truy vấn tràn sẽ vượt quá timeout khách hàng và thất bại anyway. Nếu đĩa dữ liệu của bạn là \*\*nhanh (S
