Chuyển tới nội dung chính

Nén bối cảnh và bộ nhớ đệm

Hermes Agent sử dụng hệ thống nén kép và bộ nhớ đệm nhắc nhở của Anthropic để quản lý việc sử dụng cửa sổ ngữ cảnh một cách hiệu quả trong các cuộc hội thoại dài.

Tệp nguồn: agent/context_compressor.py, agent/Prompt_caching.py, gateway/run.py (vệ sinh phiên), run_agent.py (tìm kiếm _compress_context)

Hệ thống nén kép

Hermes có hai lớp nén riêng biệt hoạt động độc lập:

                     ┌──────────────────────────┐
Incoming message │ Gateway Session Hygiene │ Fires at 85% of context
─────────────────► │ (pre-agent, rough est.) │ Safety net for large sessions
└─────────────┬────────────┘


┌──────────────────────────┐
│ Agent ContextCompressor │ Fires at 50% of context (default)
│ (in-loop, real tokens) │ Normal context management
└──────────────────────────┘

1. Vệ sinh phiên cổng (ngưỡng 85%)

Nằm trong gateway/run.py (tìm kiếm _maybe_compress_session). Đây là mạng lưới an toàn chạy trước khi tác nhân xử lý một tin nhắn. Nó ngăn ngừa lỗi API khi phiên tăng quá lớn giữa các lượt (ví dụ: tích lũy qua đêm trong Telegram/Discord).

  • Ngưỡng: Đã sửa ở mức 85% độ dài ngữ cảnh của mô hình
  • Nguồn mã thông báo: Ưu tiên mã thông báo được báo cáo API thực tế từ lượt trước; ngã lại đến ước tính sơ bộ dựa trên ký tự (estimate_messages_tokens_rough)
  • Kích hoạt: Chỉ khi len(history) >= 4 và tính năng nén được bật
  • Mục đích: Bắt các phiên thoát khỏi máy nén của chính tác nhân

Ngưỡng vệ sinh cửa ngõ cố tình cao hơn máy nén của đại lý. Đặt nó ở mức 50% (giống như tác nhân) gây ra tình trạng nén sớm ở mỗi lượt trong các phiên cổng dài.

2. Agent ContextCompressor (ngưỡng 50%, có thể định cấu hình)

Nằm trong agent/context_compressor.py. Đây là ** nén chính hệ thống** chạy bên trong vòng công cụ của tác nhân với quyền truy cập chính xác, Số lượng mã thông báo được API báo cáo.

Cấu hình

Tất cả cài đặt nén được đọc từ config.yaml dưới phím compression:

compression:
enabled: true # Enable/disable compression (default: true)
threshold: 0.50 # Fraction of context window (default: 0.50 = 50%)
target_ratio: 0.20 # How much of threshold to keep as tail (default: 0.20)
protect_last_n: 20 # Minimum protected tail messages (default: 20)
summary_model: null # Override model for summaries (default: uses auxiliary)

Chi tiết tham số

Tham sốMặc địnhPhạm viMô tả
ngưỡng0,500,0-1,0Quá trình nén được kích hoạt khi mã thông báo nhắc ≥ ngưỡng × context_length
tỷ lệ_mục tiêu0,200,10-0,80Kiểm soát ngân sách mã thông báo bảo vệ đuôi: threshold_tokens × target_ratio
bảo vệ_last_n20≥1Số lượng tin nhắn gần đây tối thiểu luôn được giữ nguyên
bảo vệ_đầu tiên_n3(mã hóa cứng)Lời nhắc hệ thống + trao đổi đầu tiên luôn được bảo tồn

Giá trị được tính toán (đối với mô hình bối cảnh 200K theo mặc định)

context_length       = 200,000
threshold_tokens = 200,000 × 0.50 = 100,000
tail_token_budget = 100,000 × 0.20 = 20,000
max_summary_tokens = min(200,000 × 0.05, 12,000) = 10,000

Thuật toán nén

Phương thức ContextCompressor.compress() tuân theo thuật toán 4 pha:

Giai đoạn 1: Cắt bỏ kết quả công cụ cũ (rẻ, không gọi LLM)

Kết quả công cụ cũ (>200 ký tự) bên ngoài phần đuôi được bảo vệ được thay thế bằng:

[Old tool output cleared to save context space]

Đây là một thẻ vượt qua giá rẻ giúp tiết kiệm các mã thông báo đáng kể từ công cụ dài dòng kết quả đầu ra (nội dung tập tin, đầu ra thiết bị đầu cuối, kết quả tìm kiếm).

Giai đoạn 2: Xác định ranh giới

┌─────────────────────────────────────────────────────────────┐
│ Message list │
│ │
│ [0..2] ← protect_first_n (system + first exchange) │
│ [3..N] ← middle turns → SUMMARIZED │
│ [N..end] ← tail (by token budget OR protect_last_n) │
│ │
└─────────────────────────────────────────────────────────────┘

Bảo vệ đuôi dựa trên ngân sách mã thông báo: đi lùi từ cuối, tích lũy token cho đến khi hết ngân sách. Trở lại trạng thái cố định protect_last_n tính nếu ngân sách sẽ bảo vệ ít thư hơn.

Các ranh giới được căn chỉnh để tránh chia tách các nhóm tool_call/tool_result. Phương thức _align_boundary_backward() duyệt qua các kết quả công cụ liên tiếp để tìm thông báo trợ lý phụ huynh, giữ nguyên các nhóm.

Giai đoạn 3: Tạo bản tóm tắt có cấu trúc

Các lượt giữa được tóm tắt bằng cách sử dụng LLM phụ trợ với cấu trúc bản mẫu:

## Goal
[What the user is trying to accomplish]

## Constraints & Preferences
[User preferences, coding style, constraints, important decisions]

## Progress
### Done
[Completed work — specific file paths, commands run, results]
### In Progress
[Work currently underway]
### Blocked
[Any blockers or issues encountered]

## Key Decisions
[Important technical decisions and why]

## Relevant Files
[Files read, modified, or created — with brief note on each]

## Next Steps
[What needs to happen next]

## Critical Context
[Specific values, error messages, configuration details]

Tóm tắt quy mô ngân sách theo số lượng nội dung được nén:

  • Công thức: content_tokens × 0,20 (hằng số _SUMMARY_RATIO)
  • Tối thiểu: 2.000 token
  • Tối đa: mã thông báo min(context_length × 0,05, 12.000)

Giai đoạn 4: Tập hợp tin nhắn nén

Danh sách tin nhắn nén là:

  1. Thông báo chính (có ghi chú được thêm vào lời nhắc hệ thống trong lần nén đầu tiên)
  2. Thông báo tóm tắt (vai trò được chọn để tránh vi phạm liên tiếp cùng vai trò)
  3. Tin nhắn đuôi (chưa sửa đổi)

Các cặp tool_call/tool_result mồ côi được dọn sạch bởi _sanitize_tool_pairs():

  • Công cụ kết quả tham chiếu cuộc gọi đã xóa → đã xóa
  • Cuộc gọi công cụ có kết quả bị xóa → kết quả sơ khai được chèn

Nén lại lặp đi lặp lại

Trong các lần nén tiếp theo, bản tóm tắt trước đó được chuyển tới LLM với hướng dẫn cập nhật thay vì tóm tắt từ đầu. Điều này bảo tồn thông tin qua nhiều lần nén — các mục chuyển từ "Đang tiến hành" sang "Xong", tiến trình mới được thêm vào và thông tin lỗi thời bị xóa.

Trường _previous_summary trên phiên bản máy nén lưu trữ bản tóm tắt cuối cùng văn bản cho mục đích này.

Ví dụ trước/sau

Trước khi nén (45 tin nhắn, ~95K token)

[0] system:    "You are a helpful assistant..." (system prompt)
[1] user: "Help me set up a FastAPI project"
[2] assistant: <tool_call> terminal: mkdir project </tool_call>
[3] tool: "directory created"
[4] assistant: <tool_call> write_file: main.py </tool_call>
[5] tool: "file written (2.3KB)"
... 30 more turns of file editing, testing, debugging ...
[38] assistant: <tool_call> terminal: pytest </tool_call>
[39] tool: "8 passed, 2 failed\n..." (5KB output)
[40] user: "Fix the failing tests"
[41] assistant: <tool_call> read_file: tests/test_api.py </tool_call>
[42] tool: "import pytest\n..." (3KB)
[43] assistant: "I see the issue with the test fixtures..."
[44] user: "Great, also add error handling"

Sau khi nén (25 tin nhắn, ~45K token)

[0] system:    "You are a helpful assistant...
[Note: Some earlier conversation turns have been compacted...]"
[1] user: "Help me set up a FastAPI project"
[2] assistant: "[CONTEXT COMPACTION] Earlier turns were compacted...

## Goal
Set up a FastAPI project with tests and error handling

## Progress
### Done
- Created project structure: main.py, tests/, requirements.txt
- Implemented 5 API endpoints in main.py
- Wrote 10 test cases in tests/test_api.py
- 8/10 tests passing

### In Progress
- Fixing 2 failing tests (test_create_user, test_delete_user)

## Relevant Files
- main.py — FastAPI app with 5 endpoints
- tests/test_api.py — 10 test cases
- requirements.txt — fastapi, pytest, httpx

## Next Steps
- Fix failing test fixtures
- Add error handling"
[3] user: "Fix the failing tests"
[4] assistant: <tool_call> read_file: tests/test_api.py </tool_call>
[5] tool: "import pytest\n..."
[6] assistant: "I see the issue with the test fixtures..."
[7] user: "Great, also add error handling"

Bộ nhớ đệm nhanh chóng (Anthropic)

Nguồn: agent/Prompt_caching.py

Giảm chi phí mã thông báo đầu vào khoảng ~75% cho các cuộc hội thoại nhiều lượt bằng cách lưu vào bộ nhớ đệm tiền tố hội thoại. Sử dụng các điểm ngắt cache_control của Anthropic.

Chiến lược: system_and_3

Anthropic cho phép tối đa 4 điểm dừng cache_control cho mỗi yêu cầu. Hermes sử dụng chiến lược "system_and_3":

Breakpoint 1: System prompt           (stable across all turns)
Breakpoint 2: 3rd-to-last non-system message ─┐
Breakpoint 3: 2nd-to-last non-system message ├─ Rolling window
Breakpoint 4: Last non-system message ─┘

Cách thức hoạt động

apply_anthropic_cache_control() sao chép sâu các tin nhắn và đưa vào điểm đánh dấu cache_control:

# Cache marker format
marker = {"type": "ephemeral"}
# Or for 1-hour TTL:
marker = {"type": "ephemeral", "ttl": "1h"}

Điểm đánh dấu được áp dụng khác nhau dựa trên loại nội dung:

Loại nội dungĐiểm đánh dấu đi đâu
Nội dung chuỗiĐã chuyển đổi thành [{"type": "text", "text": ..., "cache_control": ...}]
Liệt kê nội dungĐã thêm vào lệnh của phần tử cuối cùng
Không có/trốngĐã thêm dưới dạng msg["cache_control"]
Thông báo công cụĐã thêm dưới dạng msg["cache_control"] (chỉ dành cho Anthropic bản địa)

Mẫu thiết kế nhận biết bộ đệm

  1. Lời nhắc hệ thống ổn định: Lời nhắc hệ thống là điểm dừng 1 và được lưu trong bộ nhớ đệm tất cả các lượt. Tránh thay đổi nó giữa cuộc trò chuyện (nén sẽ thêm ghi chú chỉ trong lần đầm đầu tiên).

  2. Vấn đề về thứ tự tin nhắn: Lượt truy cập bộ đệm yêu cầu phải khớp tiền tố. Thêm hoặc xóa tin nhắn ở giữa sẽ làm mất hiệu lực bộ đệm cho mọi thứ sau đó.

  3. Tương tác bộ nhớ đệm nén: Sau khi nén, bộ đệm không còn hiệu lực cho vùng nén nhưng bộ đệm nhắc hệ thống vẫn tồn tại. Sự lăn Cửa sổ 3 thông báo thiết lập lại bộ nhớ đệm trong vòng 1-2 lượt.

  4. Lựa chọn TTL: Mặc định là 5m (5 phút). Sử dụng 1h để chạy lâu các phiên mà người dùng nghỉ giải lao giữa các lượt.

Kích hoạt bộ đệm nhanh

Bộ nhớ đệm nhắc nhở được kích hoạt tự động khi:

  • Model là model Anthropic Claude (được phát hiện theo tên model)
  • Nhà cung cấp hỗ trợ cache_control (API gốc Anthropic hoặc OpenRouter)
# config.yaml — TTL is configurable
model:
cache_ttl: "5m" # "5m" or "1h"

CLI hiển thị trạng thái bộ đệm khi khởi động:

💾 Prompt caching: ENABLED (Claude via OpenRouter, 5m TTL)

Cảnh báo áp lực bối cảnh

Tác nhân phát ra cảnh báo áp suất ngữ cảnh ở mức 85% ngưỡng nén (không phải 85% ngữ cảnh - 85% ngưỡng mà bản thân nó là 50% ngữ cảnh):

⚠️  Context is 85% to compaction threshold (42,500/50,000 tokens)

Sau khi nén, nếu mức sử dụng giảm xuống dưới 85% ngưỡng, trạng thái cảnh báo được xóa. Nếu quá trình nén không giảm xuống dưới mức cảnh báo (mức độ nén cuộc trò chuyện quá dày đặc), cảnh báo vẫn tồn tại nhưng không nén được kích hoạt lại cho đến khi vượt quá ngưỡng một lần nữa.