2026-10-16 09:00:40 +07:00

114 lines
8.9 KiB
Markdown

# AI Platform
AI Platform provides Lead Scoring with a Python backend and internal SvelteKit Admin Web. Behavior is in `docs/specs/ai-platform-lead-scoring.md` and the client contract in `contracts/lead-scoring/`; local checks do not establish client integration or production readiness.
## Runtime layout
- Python 3.13 backend package: `src/ai_platform`, served as `ai_platform.main:app`.
- PostgreSQL 16 with a new, project-specific Compose volume named `ai_platform_pgdata`.
- Alembic schema changes are applied as a one-shot Compose service before the backend starts.
- SvelteKit is a separate web image under `apps/web`.
- Historical implementation is retained in Git history, not as a separate source tree.
The local Compose stack runs one backend process. It is for development only; default credentials and loopback port mappings are not deployment settings. Keep production secrets outside `.env` and inject them through the deployment environment or a secrets manager.
## Local setup
Copy the backend/Compose template and the separate SvelteKit server-only template only when setting up a new workspace. Both `.env` files are ignored by Git; preserve any existing `.env` and `apps/web/.env` instead of overwriting them.
```sh
cp .env.example .env
cp apps/web/.env.example apps/web/.env
make install
```
For a new database, prepare its schema once before starting the application:
```sh
make db-up
make migrate
```
Then start PostgreSQL and both native servers from one terminal:
```sh
make dev
```
Set `BACKEND_PORT` once in the root `.env` (default `8000`; use `8002` to avoid a local conflict). `make backend-dev` uses that port, and `make web-dev` passes the matching server-only `BACKEND_INTERNAL_URL` to Web, overriding any old URL in `apps/web/.env`. An exported `BACKEND_PORT` or `make dev BACKEND_PORT=8002` can override the root value. Other root secrets are not passed to Web. The web development server normally listens at `127.0.0.1:5173` (see its terminal output). `Ctrl+C` stops the native servers; PostgreSQL stays running. `make dev` does not run migrations or start Omni/AIHub. To run the servers in separate terminals instead, use `make backend-dev` and `make web-dev`.
The local web server should call the backend through server-only configuration. Do not expose `BACKEND_INTERNAL_URL`, database credentials, or provider keys as browser-visible `PUBLIC_*` variables.
Compose can instead build and start all services, including the one-shot migration step:
```sh
docker compose up --build
```
The database is reachable from the host at `127.0.0.1:55432`, backend at `127.0.0.1:8000`, and web at `127.0.0.1:3000` by default. To discard this Compose stack's database and its data, run `docker compose down -v`; unrelated project volumes are not targeted.
The local database starts with only the development database. To run tests safely, create a separate database in that same new PostgreSQL instance, then pass its URL explicitly:
```sh
docker compose exec postgres createdb -U ai_platform ai_platform_test
AI_PLATFORM_DATABASE_URL=postgresql+asyncpg://ai_platform:local-change-this-password@127.0.0.1:55432/ai_platform_test make test
```
`make test` rejects a database URL that does not contain `ai_platform_test`. CI provisions its own isolated PostgreSQL 16 test database.
## Chấm điểm AI qua CLIProxy khi phát triển local
Không cần chạy AIHub để thử adapter OpenAI-compatible. Cấu hình URL trên backend (trong môi trường hoặc `.env` local đã có):
```sh
AI_PLATFORM_ENVIRONMENT=development
AI_PLATFORM_OPENAI_BASE_URL=http://127.0.0.1:8317
# AI_PLATFORM_OPENAI_API_KEY=... # chỉ cần nếu CLIProxy yêu cầu xác thực
```
Chạy `make backend-dev` trên máy host: `127.0.0.1:8317` bên trong Docker không trỏ tới CLIProxy trên host. Trong Admin Web (Cấu hình AI), soạn một revision mới với `OpenAI-compatible`, URL `http://127.0.0.1:8317`, model `gemini-3.5-flash-lite`, hướng dẫn chấm và timeout, chạy thử rồi Kích hoạt. `AI_PLATFORM_OPENAI_API_KEY` chỉ được gửi tới `AI_PLATFORM_OPENAI_BASE_URL`; URL khác nhập trên Web không nhận key đó. Profile AIHub dùng khóa AI của client trong header `X-AI-Key` (không lưu).
Trang “Thử chấm điểm” có form sửa lead/custom fields, thêm/sửa/xóa hội thoại và tin nhắn (thời gian UTC), dựng rule với field/operator/giá trị/điểm và chọn tỷ lệ AI. Trọng số quy tắc tự bổ sung để tổng bằng 100%; AI 0% không gọi model. JSON nâng cao đồng bộ cùng kịch bản và gửi nguyên trọng số. Kết quả hiện điểm, breakdown và fallback ngay trên trang, kèm liên kết chi tiết. Ngưỡng ở đây là điều kiện rule; phân loại lead vẫn do client quyết định. Kịch bản không lưu thành chính sách và không ghi đè fixture chuẩn. Với AIHub, nhập “Khóa AI cho lần thử (không lưu)” trên trang này, hoặc đặt `LEAD_SCORING_FIXTURE_AI_KEY` trên server Web. Khóa nhập được ưu tiên, chỉ chuyển trong header `X-AI-Key` khi AI > 0%, không nằm trong JSON/kết quả và xóa khỏi ô nhập khi gửi. Khóa dùng thử ở trang Cấu hình AI không được lưu hay chuyển sang trang này. Thiếu khóa sẽ fallback bằng điểm quy tắc. Chế độ AI có thể phát sinh chi phí.
Với endpoint OpenAI-compatible chạy ở production, URL phải dùng HTTPS. Key được cấu hình trên server chỉ gửi tới URL được gắn với key đó; thay URL trên Web không tự chuyển key. Không đặt key vào Web hoặc file versioned. CLIProxy local chỉ xác minh được đường gọi thử; chưa chứng minh các điều kiện phát hành production.
## App credentials
Mỗi installation/môi trường Omni dùng một `app_id` ổn định (tên đăng ký phía AI Platform, Omni không cần biết hay gửi) và một backend app key chung cho các account. Vận hành cấp/rotate/revoke qua CLI; raw key chỉ được xuất khi cấp/rotate, chuyển an toàn sang backend Omni, không qua browser/log/Git:
```sh
uv run --no-sync ai-platform create-app omni-local
uv run --no-sync ai-platform rotate-app-key omni-local
uv run --no-sync ai-platform revoke-app-key omni-local
```
Admin Web `/platform/apps` chỉ xem trạng thái và thu hồi, không hiển thị hay cấp/rotate key. POST `/v1/lead-scoring/score` đồng bộ, dùng `Authorization: Bearer <app_api_key>`, `Idempotency-Key` (UUID) và tùy chọn `X-AI-Key`. Dữ liệu riêng của client đi trong `metadata` và được lưu nguyên. Omni chỉ dùng `AI_PLATFORM_URL` và `AI_PLATFORM_APP_API_KEY` ở backend. Không có webhook hay đăng ký account ở AI Platform.
Generated OpenAPI và các fixture tại `contracts/lead-scoring/`; chạy `make contract` để tái sinh và `uv run --no-sync python scripts/export_contract_fixtures.py --check` để kiểm tra drift fixture mà không sửa file. Provider checks chưa chứng minh tích hợp Omni/EMS.
Schema chưa phát hành hiện có một migration `0001_initial`, tạo thẳng app credentials, admin, AI profiles, runs. Database mới chạy `make migrate`. Database local còn revision cũ không tự upgrade sang baseline: dùng database mới, hoặc xác nhận phạm vi dữ liệu cần giữ/bỏ trước khi dựng lại hay stamp sau kiểm tra schema; không chạy `alembic stamp head` để che schema khác biệt. Startup vẫn kiểm tra revision và không tự migrate. Lịch sử migrations sau lần phát hành đầu tiên phải được giữ.
Fixture runner nội bộ dùng `LEAD_SCORING_FIXTURE_APP_API_KEY` server-only.
Backup và app/account offboarding cần chính sách riêng trước production.
## Quality checks
The opt-in pre-commit runner is installed by `make install` as a project-local development dependency. Run:
```sh
make check-fast
make check
```
`make check-fast` runs the pinned Ruff and pre-commit-hooks rules. `make check` additionally runs Python type/import checks and tests; tests must use a separate test database, never a developer or production database. CI uses the same fast hooks and the Makefile test/check commands with an isolated PostgreSQL 16 service.
## Operations notes
- Apply migrations as a release step; application startup must not migrate the database.
- `/health/live` is liveness; `/health/ready` checks readiness and PostgreSQL. The Compose backend health check waits for readiness.
- Avoid logging credentials, request payloads containing personal data, or provider responses with sensitive content.
- The Compose PostgreSQL volume is disposable development data. Backup, restore, retention, secrets, TLS, and alerting must be defined for the eventual deployment target.
The web container currently assumes `package.json`, `bun.lock`, a production SvelteKit build at `build/index.js`, and Bun compatibility with the selected SvelteKit adapter. Confirm those assumptions with the web implementation before treating the image as verified.