---
title: "Kiến trúc & các bẫy đã gặp"
url: https://phatdev.net/docs/aiusage/kien-truc
lang: vi
updated: 2026-10-23
alternate:
  en: https://phatdev.net/en/docs/aiusage/kien-truc
markdown: https://phatdev.net/docs/aiusage/kien-truc.md
---

# Kiến trúc & các bẫy đã gặp

Thuộc bộ tài liệu: [AIUsage](https://phatdev.net/docs/aiusage.md)

> Luồng dữ liệu, các thành phần, quy tắc cache và những bẫy kỹ thuật đã gặp.

Trang này dành cho người muốn hiểu hoặc sửa mã: luồng dữ liệu, các thành phần và những bẫy đã gặp.

## Luồng dữ liệu

```
claude.ai · Anthropic Console · chatgpt.com · agy · Google Cloud · ~/.claude · ~/.codex · máy từ xa (SSH)
                              |
             fetch_one.sh <nguồn>   (mỗi nguồn chạy độc lập, song song)
                              |  ghi nguyên tử, có khoá file
                       ~/.ai_usage_cache.json
                        |                     |
              AIUsage.app (SwiftUI)    AIUsageWidget (WidgetKit)
```

Ứng dụng và widget chỉ đọc cache. Nút làm mới chỉ ghi một file kích hoạt, LaunchAgent lo phần còn lại.

## Thành phần

- **`fetch_one.sh`** nhận một nguồn (`claude`, `codex`, `codexhist`, `agy`, `gemini`, `insights`) và gộp kết quả vào cache. Lỗi parser được ghi vào một file log thay vì bị nuốt.
- **`ClaudeHelper`** là ứng dụng nhỏ dùng WKWebView, giữ phiên đăng nhập và in nội dung trang cho script phân tích. Các chế độ: `login`, `fetch`, `login-aistudio`, `fetch-aistudio`, `login-console`, `fetch-console`.
- **`remote_insights.py`** chạy trên máy từ xa, đọc nhật ký phiên theo kiểu tăng dần và trả về JSON nén.
- **`Shared.swift`** được biên dịch vào cả ứng dụng và widget: parser logo, mô hình dữ liệu và logic dự báo dùng chung.

## Quy tắc dữ liệu

- Mỗi nguồn ghi vào khoá riêng của cache, kèm `fetched_at`. Cờ `_fresh` báo một lần lấy thật sự thành công; chỉ khi đó đồng hồ chung mới nhích.
- Lấy dữ liệu lỗi tạm thời thì giữ nguyên số cũ, không ghi đè bằng rỗng.
- Phiên hết hạn thì giữ số cũ và đặt `needs_login` để thẻ hiện nút đăng nhập.

## Các bẫy đã gặp

- **Cửa sổ nền của helper phải nằm trên màn hình.** Cửa sổ ngoài màn hình làm WebKit báo `visibilityState=hidden`, `requestAnimationFrame` bị đình, và các trang Next.js như Anthropic Console không render. Helper dùng cửa sổ trong suốt 99% nằm trong màn hình, không nhận chuột.
- **Cookie gắn với bundle id của helper.** Chạy tệp thực thi rời ngoài gói `.app` sẽ không thấy phiên đăng nhập.
- **Trang usage của claude.ai** có nút chứa ký tự icon ở cuối chữ, nên biểu thức chính quy không được neo `$`; văn bản của Console cũng chứa ký tự icon trên dòng riêng giữa nhãn và số.
- **Tiến trình mở rộng của widget sống dai** qua nhiều lần deploy. Sau mỗi lần deploy có sửa widget, kết thúc tiến trình `AIUsageWidgetExtension` để hệ thống chạy bản mới.
- **Build hỏng bị chép nhầm.** Không đưa kết quả `xcodebuild` qua `grep` mà bỏ qua việc kiểm `BUILD SUCCEEDED`.
- **Ký ad-hoc trong thư mục đồng bộ** bị lỗi "detritus not allowed"; ký trên bản sao ở thư mục tạm.
- **Widget không có `NSHomeDirectory()` thật** vì chạy trong sandbox; đường dẫn tới cache được dựng từ tên người dùng.

## Ảnh minh hoạ

Ứng dụng có hai biến môi trường dành cho việc chụp ảnh giới thiệu: `AIUSAGE_HOME` trỏ cache và script sang một thư mục chứa số liệu mẫu, và `AIUSAGE_APPEARANCE` (`light` hoặc `dark`) ghim giao diện. Nhờ vậy ảnh công khai không bao giờ chứa số liệu thật.
