Tổng Quan Provider Resilience
Wave 2  ·  dev-v3  ·  07/04/2026

Chịu Lỗi & Tối Ưu Provider

Middleware chain, failover thông minh, caching, cooldown,
embedding, chunking, dreaming — 9 phase hoàn chỉnh.

9
Phases
hoàn chỉnh
15+
Files
mới
2-tier
Failover
logic
9
Error
categories
Mỗi request gửi tới LLM provider cần được chuẩn bị kỹ — thêm cache headers, chọn service tier, xác thực. Middleware chain cho phép cắm thêm logic mà không sửa code provider. Áp dụng sau buildRequestBody(), modify body in-place với zero-allocation fast path nếu nil.

Luồng Middleware

flowchart LR A([Request]) --> B[buildRequestBody] B --> C[ComposeMiddlewares] C --> D1[CacheMiddleware] C --> D2[ServiceTierMiddleware] C --> D3["... thêm middlewares"] D1 --> E[ApplyMiddlewares] D2 --> E D3 --> E E --> F([Gửi tới Provider]) style A fill:#262220,stroke:#d94040,color:#f5f0eb style F fill:#262220,stroke:#2ea85a,color:#f5f0eb style B fill:#1f1c18,stroke:#3d3835,color:#f5f0eb style C fill:#1f1c18,stroke:#e07030,color:#f5f0eb style E fill:#1f1c18,stroke:#e07030,color:#f5f0eb style D1 fill:#1a1714,stroke:#3d3835,color:#a39890 style D2 fill:#1a1714,stroke:#3d3835,color:#a39890 style D3 fill:#1a1714,stroke:#3d3835,color:#a39890

Core Types

🔗

RequestMiddleware

Function type composable. Nhận map[string]any, sửa in-place, trả về error.

⛓️

ComposeMiddlewares()

Chain nhiều middlewares thành một. Zero-allocation fast path khi danh sách nil.

▶️

ApplyMiddlewares()

Chạy toàn bộ chain theo thứ tự trên một request body cụ thể.

Cache Middleware

C CacheMiddleware

Inject OpenAI prompt caching headers vào request body. Tự động bật khi provider hỗ trợ — không cần cấu hình thủ công từ agent. Giúp giảm latency và cost với các nội dung system prompt lặp lại.

OpenAI Compatible Auto-apply Cost Reduction

Service Tier Middleware

A Anthropic

auto Mặc định
standard_only Không dùng priority

O OpenAI

auto Mặc định
default / flex / priority Explicit tier
💡
FastModeMiddleware() — map boolean fast_mode: true sang service_tier phù hợp từng provider. Agent không cần biết chi tiết API của từng provider.
Khi provider lỗi, hệ thống cần biết: lỗi gì? Tạm thời hay vĩnh viễn? Thử lại hay chuyển sang provider khác? Bộ phân loại 9 categories giúp quyết định đúng hành động.

9 Error Categories

Category Ví dụ Hành động
auth Invalid API key Tier 1: rotate profile
auth_permanent Account suspended Tier 2: skip model
format Invalid request body Tier 2: skip model
rate_limit 429 Too Many Requests Tier 1: rotate + wait
overloaded Server overloaded Tier 1: rotate (max 3)
billing Quota exceeded Tier 2: skip model
timeout Request timeout Tier 1: rotate
model_not_found Model not available Tier 2: skip model
unknown Unrecognized error Conservative: rotate

D DefaultClassifier

Pattern matching trên HTTP status code + error body. Hai hàm đăng ký pattern provider-specific:

  • RegisterOpenAIPatterns() — patterns cho OpenAI-compatible providers
  • RegisterAnthropicPatterns() — patterns riêng cho Anthropic API
  • Context overflow — trường hợp đặc biệt: trigger compaction, không phải failover

Two-Tier Failover

flowchart TD REQ(["Request"]) --> FAIL{"Lỗi?"} FAIL -->|"Không lỗi"| OK(["Thành công"]) FAIL -->|"Có lỗi"| CLS["Classify Error"] CLS --> T1{"Tier 1
Transient?"} CLS --> T2{"Tier 2
Permanent?"} T1 -->|"rate_limit, overloaded
timeout, auth"| ROT["Rotate API Keys
/ Profiles"] ROT --> CHK{"Còn profile?"} CHK -->|"Còn, max 5"| RETRY(["Retry cùng model"]) CHK -->|"Overloaded ≥ 3"| ESC["Escalate"] CHK -->|"Hết profile"| ESC T2 -->|"auth_permanent, billing
format, model_not_found"| NEXT["Fall back
model tiếp theo"] ESC --> NEXT NEXT --> NOMORE{"Còn model?"} NOMORE -->|"Còn"| RETRY2(["Retry model mới"]) NOMORE -->|"Hết"| ERR(["Trả lỗi cuối cùng"]) style OK fill:#0d2818,stroke:#2ea85a,color:#6ee7b7 style ERR fill:#2d0a14,stroke:#d94040,color:#fca5a5 style RETRY fill:#0a1f2e,stroke:#e07030,color:#67e8f9 style RETRY2 fill:#0a1f2e,stroke:#e07030,color:#67e8f9 style T1 fill:#1a1a0d,stroke:#d4882a,color:#fde68a style T2 fill:#2d0a14,stroke:#d94040,color:#fca5a5 style ROT fill:#1f1c18,stroke:#3d3835,color:#f5f0eb style NEXT fill:#1f1c18,stroke:#3d3835,color:#f5f0eb

G RunWithFailover[T]()

Generic function orchestrate toàn bộ failover logic. Caller chỉ cần cung cấp danh sách candidates và function thực thi:

  • ModelCandidate — gồm provider, model, profileID
  • FailoverConfig — CooldownTracker (optional), MaxProfileRotations, OverloadRotationLimit
  • Generic return type — không bị ràng buộc vào một response type cụ thể
⚠️
Context overflow KHÔNG chạy qua failover — nó trigger compaction pipeline riêng để giảm context window trước khi retry.
Hệ thống cần biết: model nào có khả năng gì? Context window bao nhiêu? Hỗ trợ vision không? Model Registry là nguồn sự thật duy nhất — thread-safe, pre-seeded, forward-compatible.

ModelSpec Struct

🆔

Identity

ID, Provider — định danh duy nhất. Thread-safe storage qua sync.Map.

📐

Capacity

ContextWindow, MaxTokens — giới hạn context. Dùng cho auto-summarization threshold.

🧠

Capabilities

Reasoning (bool), Vision (bool), TokenizerID — routing thông minh theo khả năng model.

💰

Cost

Cost struct — input/output token pricing. Phục vụ báo cáo chi phí và tối ưu lựa chọn model.

Models Pre-seeded

Anthropic Claude

claude-opus-4-6 Reasoning + Vision
claude-sonnet-4-6 Balanced
claude-haiku-4-5 Fast + Cheap

OpenAI GPT

gpt-5.4 / gpt-5.2 Latest flagship
gpt-4o Vision + Speed
o3 / o4-mini Reasoning models
🔮
ForwardCompatResolver — khi gặp model ID chưa biết, clone template gần nhất + patch thông số. Không crash khi provider ra model mới.

Embedding Providers

  • Model: text-embedding-3-small
  • Dimensions: 1536 — khớp chính xác với cột embedding vector(1536) trong pgvector
  • Batch max: 2048 texts mỗi request
  • Interface: store.EmbeddingProvider với Embed(ctx, texts) [][]float32
  • Model: voyage-3-large
  • Dimensions: 1536 — cùng schema pgvector
  • Implementation: thin wrapper bọc OpenAI provider interface
  • Chỉ 20 lines — KISS principle cho Voyage integration
Bốn phase cuối hoàn thiện vòng lặp tự cải thiện: Cooldown ngăn gọi provider đang lỗi, Chunking giúp gửi tin dài qua channels, Session Recall tìm ký ức liên quan, và Dreaming tổng hợp episodic memory thành insight dài hạn.

Cooldown Tracker

C Thiết kế in-memory

Per-provider:model tracking qua sync.Map. Khi provider lỗi, ghi nhận reason để tính duration phù hợp:

ReasonCooldown Duration
rate_limit30 giây
overloaded60 giây
billing5 phút
auth10 phút
auth_permanent1 giờ
  • Overload streak: 5 lỗi liên tiếp → nhân đôi cooldown duration
  • Probe window: sau 30 giây cho phép 1 probe attempt
  • TTL: 24h expiration, scan cleanup mỗi 5 phút
  • Max keys: 512, LRU eviction để giới hạn memory

Markdown Chunking

M ChunkMarkdown(text, maxLen)

Chia văn bản Markdown dài thành chunks phù hợp giới hạn từng channel, tôn trọng cấu trúc nội dung:

  • Không cắt giữa fenced code blocks — luôn giữ nguyên khối code
  • Ưu tiên cắt: paragraph (\n\n) > line (\n) > space
  • Force-split oversized code blocks với fence repair (thêm ``` trước/sau)
Telegram Slack Discord

Session Recall — Hybrid Search

F FTS Search

Full-text search với ts_rank trên episodic summaries. Chính xác với từ khóa cụ thể.

V Vector Search

Cosine similarity với pgvector. Tìm ngữ nghĩa liên quan dù không khớp từ khóa.

📌
Scoped per tenant: agent_id + user_id + tenant_id. Limit 10 results.
Tìm trên episodic summaries (không phải raw messages) — 80/20 approach: ít nhiễu, đủ ngữ cảnh.

Dreaming Worker

D Episodic → Long-term Memory

Worker subscribe episodic.created event qua EventBus. Khi đủ ngưỡng, dùng LLM tổng hợp các episodic summaries thành insight dài hạn, lưu vào workspace agent:

  • Debounce: tối thiểu 10 phút giữa các lần chạy per agent/user
  • Threshold: cần ≥ 5 episodic summaries chưa được promoted
  • LLM synthesis: gọi provider tóm tắt thành markdown có cấu trúc
  • Output path: _system/dreaming/{YYYYMMDD}-consolidated.md
  • Marks entries: set promoted_at timestamp để tránh xử lý lại
  • Registered: trong workers.go line 54

Luồng Dreaming

flowchart LR E(["episodic.created"]) --> EB["EventBus"] EB --> DW["DreamingWorker"] DW --> DB{"Debounce
≥ 10 phút?"} DB -->|"Chưa đủ"| SKIP(["Bỏ qua"]) DB -->|"Đủ"| CNT{"Unpromoted
≥ 5?"} CNT -->|"Không đủ"| SKIP2(["Chờ thêm"]) CNT -->|"Đủ"| FETCH["Fetch summaries"] FETCH --> LLM["LLM synthesize"] LLM --> STORE["Store markdown"] STORE --> MARK["Mark promoted_at"] MARK --> DONE(["Hoàn tất"]) style E fill:#1a1230,stroke:#c89030,color:#c4b5fd style DONE fill:#0d2818,stroke:#2ea85a,color:#6ee7b7 style SKIP fill:#262220,stroke:#3d3835,color:#6b6460 style SKIP2 fill:#262220,stroke:#3d3835,color:#6b6460 style LLM fill:#1f1c18,stroke:#d94040,color:#f5f0eb style DB fill:#1a1a0d,stroke:#d4882a,color:#fde68a style CNT fill:#1a1a0d,stroke:#d4882a,color:#fde68a
🌙
Dreaming là quá trình bất đồng bộ hoàn toàn — không block agent loop. Mỗi agent/user có clock riêng. Kết quả được agent đọc tại lần chạy kế tiếp như một context file bình thường.