docs/en/s24-plugin-architecture.mdXem bản gốc Bản dịch đầy đủ theo đúng cấu trúc tài liệu; code, liên kết và định danh kỹ thuật được giữ nguyên.
s24: Kiến trúc plugin
s00 > s01 > s02 > s03 > s04 > s05 > s06 > s07 > s08 > s09 > s10 > s11 > s12 > s13 > s14 > s15 > s16 > s17 > s18 > s19 > s20 > s21 > s22 > s23 > [ s24 ] > s25 > s26 > s27
Bộ nhớ tích hợp sử dụng các tệp, nhưng một số người muốn Honcho để lập mô hình người dùng, những người khác muốn mem0 để tìm kiếm ngữ nghĩa. Hệ thống plugin cho phép mọi người sử dụng những gì họ thích mà không cần thay đổi một dòng mã lõi nào.

Chương này giải quyết vấn đề gì
Hệ thống bộ nhớ tích hợp từ s07 sử dụng các tệp MEMORY.md và USER.md để lưu trữ. Nó hoạt động, nhưng có những hạn chế:
- Chỉ tra cứu đối sánh chính xác, không tìm kiếm ngữ nghĩa ("lỗi mà tôi đã đề cập tuần trước" sẽ không được tìm thấy)
- Không có mô hình người dùng (không biết "người này thích phong cách nào" hoặc "người này đã từng làm dự án gì trước đây")
- Dữ liệu được lưu trữ trong các tệp cục bộ và không thể đồng bộ hóa giữa các thiết bị
Cộng đồng có nhiều dịch vụ bộ nhớ chuyên dụng -- Honcho (lập hồ sơ người dùng + bộ nhớ đàm thoại), mem0 (tìm kiếm ngữ nghĩa + trích xuất dữ kiện), hình ba chiều (biểu đồ kiến thức cục bộ). Mỗi cái đều có API và mô hình dữ liệu riêng.
Nếu bạn mã hóa Honcho thành memory_manager.py, ai muốn chuyển sang mem0 thì phải sửa core code. Làm điều này cho mọi dịch vụ bộ nhớ sẽ nhanh chóng dẫn đến sự hỗn loạn.
Điều cần thiết là giao diện plugin: xác định "những gì nhà cung cấp bộ nhớ phải làm", để mỗi dịch vụ tự triển khai và chuyển đổi thông qua cấu hình.
Vấn đề này không phải chỉ xảy ra với bộ nhớ. Các chiến lược nén ngữ cảnh (s05) có nhu cầu tương tự -- tính năng nén tóm tắt tích hợp hoạt động nhưng một số người muốn các chiến lược thông minh hơn. Đó là lý do tại sao Hermes Agent có ba hệ thống plugin độc lập.
Đề xuất đọc
s07-memory-system.md-- Hệ thống bộ nhớ tích hợp (MEMORY.md / USER.md)s05-context-compression.md-- Tích hợp tính năng nén ngữ cảnhs16-mcp.md-- MCP là một cách khác để mở rộng khả năng, nhưng nhắm mục tiêu vào các công cụ thay vì bộ nhớ
Các khái niệm chính
Nhà cung cấp bộ nhớ là gì
Một giao diện thống nhất cho các dịch vụ bộ nhớ ngoài. Mỗi nhà cung cấp (Honcho, mem0, v.v.) triển khai giao diện này và có thể được cắm vào agent Hermes.
Ràng buộc cốt lõi: mỗi lần chỉ có một MemoryProvider bên ngoài có thể hoạt động. Bộ nhớ tích hợp (MEMORY.md) luôn trực tuyến; nhà cung cấp bên ngoài là một cải tiến tùy chọn.
Công cụ bối cảnh là gì
Một giao diện thống nhất cho các chiến lược nén ngữ cảnh. ContextCompressor tích hợp sẵn (nén tóm tắt được đề cập trong s05) là cách triển khai mặc định. Bạn có thể thay thế nó bằng một chiến lược khác -- một lần nữa, mỗi lần chỉ kích hoạt một chiến lược.
Trình quản lý bộ nhớ là gì
Điều phối viên. Nó quản lý sự cùng tồn tại của bộ nhớ tích hợp và nhà cung cấp bên ngoài -- người cung cấp công cụ, người xử lý việc tìm nạp trước, người xử lý đồng bộ hóa.
Bắt đầu từ cách tiếp cận đơn giản nhất
Hardcode Honcho trực tiếp vào mã quản lý bộ nhớ:
# memory_manager.py
def prefetch(query):
# Built-in memory
memory_text = load_memory(MEMORY_FILE)
# Honcho
if HONCHO_ENABLED:
from honcho import HonchoClient
client = HonchoClient(api_key=HONCHO_KEY)
honcho_context = client.recall(query)
return memory_text + "\n" + honcho_context
return memory_text
Hai vấn đề:
Vấn đề 1: Thay đổi mã lõi cho mọi dịch vụ mới
Đã thêm Honcho, bây giờ cần thêm mem0. Sau đó là hình ba chiều. Mỗi cái có cách khởi tạo, tìm nạp trước, đồng bộ hóa và đăng ký công cụ khác nhau -- tất cả đều được nhồi nhét vào Memory_manager.py. Đây là vấn đề tương tự như các bộ điều hợp nền tảng if-else từ s12.
Vấn đề 2: Hai dịch vụ bên ngoài hoạt động đồng thời sẽ xung đột
Cả Honcho và mem0 đều muốn tìm nạp trước ngữ cảnh trước mỗi lượt trò chuyện và cả hai đều muốn đăng ký các công cụ của riêng mình. Kích hoạt chúng đồng thời có thể gây nhiễu. Nhưng bạn không thể đơn giản cấm nó - một số tình huống thực sự yêu cầu phải chuyển đổi.
Giải pháp: Xác định giao diện MemoryProvider + một quy tắc (mỗi lần chỉ có một nhà cung cấp bên ngoài).
Mô hình tinh thần tối thiểu
Built-in memory (always on) External provider (optional, at most one)
MEMORY.md / USER.md Honcho / mem0 / holographic
| |
v v
┌──────────────────────────────────────────────┐
│ MemoryManager (coordinator) │
│ │
│ Before each conversation turn: │
│ prefetch_all(query) │
│ -> built-in: load MEMORY.md │
│ -> external: call provider.prefetch(query) │
│ -> merge context and inject into messages │
│ │
│ After each conversation turn: │
│ sync_all(user_msg, assistant_msg) │
│ -> built-in: may write to MEMORY.md │
│ -> external: call provider.sync_turn(...) │
│ │
│ Tool routing: │
│ built-in tool: memory(action="add", ...) │
│ external tool: honcho_search(query="...") │
│ -> route to the correct provider by name │
└──────────────────────────────────────────────┘
Giao diện nhà cung cấp bộ nhớ
Các phương thức mà nhà cung cấp bộ nhớ ngoài phải triển khai:
class MemoryProvider(ABC):
# --- Required ---
@property
def name(self) -> str:
"""Short identifier, e.g. 'honcho', 'mem0'."""
...
def is_available(self) -> bool:
"""Check availability (no network requests -- only check config and dependencies)."""
...
def initialize(self, session_id: str, **kwargs):
"""Initialize at session start (create connections, load state)."""
...
def get_tool_schemas(self) -> list[dict]:
"""Return the list of tools this provider exposes to the model."""
...
# --- Optional (have default empty implementations) ---
def prefetch(self, query: str) -> str:
"""Recall relevant context before each conversation turn. Must be fast (can use background thread prefetch)."""
return ""
def sync_turn(self, user_content: str, assistant_content: str):
"""Persist after each conversation turn. Should be non-blocking (queue to background)."""
pass
def handle_tool_call(self, tool_name: str, args: dict) -> str:
"""Execute the provider's own tool calls."""
return "{}"
def shutdown(self):
"""Close connections, flush queues."""
pass
4 phương pháp bắt buộc + 4 phương pháp tùy chọn. Cách tiếp cận tương tự như BaseEnvironment của s14 (2 phương thức) và BasePlatformAdapter của s13 (3 phương thức) -- xác định một giao diện và cho phép người triển khai chỉ tập trung vào logic của riêng họ.
Cách tọa độ của Trình quản lý bộ nhớ
class MemoryManager:
def __init__(self):
self._providers: list[MemoryProvider] = []
self._has_external = False
self._tool_to_provider: dict[str, MemoryProvider] = {}
def add_provider(self, provider: MemoryProvider):
"""Register a provider. At most one external provider."""
if provider.name != "builtin":
if self._has_external:
print(f" [memory] external provider already active, rejecting {provider.name}")
return
self._has_external = True
self._providers.append(provider)
# Index tool name -> provider for routing
for schema in provider.get_tool_schemas():
self._tool_to_provider[schema["name"]] = provider
def prefetch_all(self, query: str) -> str:
"""Before each conversation turn, recall context from all providers."""
parts = []
for provider in self._providers:
try:
context = provider.prefetch(query)
if context:
parts.append(f"[{provider.name}] {context}")
except Exception as exc:
pass # One provider failing doesn't affect the others
return "\n".join(parts)
def handle_tool_call(self, tool_name: str, args: dict) -> str:
"""Route to the correct provider by tool name."""
provider = self._tool_to_provider.get(tool_name)
if not provider:
return json.dumps({"error": f"Unknown memory tool: {tool_name}"})
return provider.handle_tool_call(tool_name, args)
Thiết kế chính: sự cố của một nhà cung cấp không ảnh hưởng đến các nhà cung cấp khác. các lệnh gọi tìm nạp trước, đồng bộ hóa và công cụ đều được gói gọn trong thử/ngoại trừ. Nếu dịch vụ Honcho ngừng hoạt động, bộ nhớ tích hợp vẫn tiếp tục hoạt động.
Hướng dẫn đầy đủ với Honcho
1. Configuration
config.yaml: memory.provider = "honcho"
2. Startup
-> load_memory_provider("honcho")
-> Find plugins/memory/honcho/__init__.py
-> Call register(ctx) -> create HonchoMemoryProvider instance
-> provider.is_available() -> check that SDK is installed, API key is set
-> manager.add_provider(provider)
-> manager.initialize_all(session_id="...")
3. Before each conversation turn (prefetch)
User says "Did that bug I mentioned last week get fixed?"
-> manager.prefetch_all("Did that bug I mentioned last week get fixed?")
-> Built-in: load MEMORY.md (may not have relevant information)
-> Honcho: semantic search -> "Last week user mentioned #1234 null pointer bug, not yet fixed"
-> Merged and injected into messages:
<memory-context>
[honcho] Last week user mentioned #1234 null pointer bug, not yet fixed
</memory-context>
-> Model sees the context, can respond "That bug was #1234, let me check on it"
4. Model calls a Honcho tool
-> tool_call: honcho_search(query="null pointer bug")
-> manager.handle_tool_call("honcho_search", ...) -> routes to Honcho provider
-> Honcho returns search results
5. After each conversation turn (sync)
-> manager.sync_all("Did that bug I mentioned...", "That bug was #1234, it's been fixed")
-> Built-in: check whether MEMORY.md needs updating
-> Honcho: store this conversation turn in Honcho backend (non-blocking, queued to background thread)
6. Shutdown
-> manager.shutdown_all()
-> Honcho: flush queue, close connections
Nếu bạn chuyển sang mem0 thì sao? Thay đổi một dòng trong config.yaml: memory.provider = "mem0". Không có thay đổi nào đối với mã lõi.
Cách các plugin được phát hiện và tải
plugins/memory/ <- Providers bundled with the repo
├── honcho/
│ ├── __init__.py <- register(ctx) function
│ └── plugin.yaml <- Metadata (name, description)
├── mem0/
├── holographic/
└── ...
~/.hermes/plugins/ <- User-installed plugins
└── my-memory/
├── __init__.py
└── plugin.yaml
Thứ tự tải: nhà cung cấp đi kèm được ưu tiên; trong trường hợp xung đột tên, những người đi kèm sẽ thắng.
Mỗi plugin __init__.py phải có một register(ctx) chức năng:
# plugins/memory/honcho/__init__.py
def register(ctx):
ctx.register_memory_provider(HonchoMemoryProvider())
ctx là một đối tượng thu thập mà trình tải sử dụng để thu thập các phiên bản của nhà cung cấp. Mẫu này có nghĩa là plugin không cần biết về MemoryManager -- nó chỉ tự đăng ký.
Cách nó cắm vào vòng lặp chính
Trình quản lý bộ nhớ can thiệp vào hai điểm trong vòng lặp lõi:
Each turn of the core loop:
1. User message arrives
|
v
2. manager.prefetch_all(user_message) <- before turn: recall context
| inject into messages
v
3. Send to model -> model responds
|
|-- if tool_call is a memory tool -> manager.handle_tool_call(...)
|
v
4. manager.sync_all(user_msg, assistant_msg) <- after turn: persist
|
v
5. Next turn
Vòng lặp lõi chỉ gọi ba phương thức trên trình quản lý (prefetch_all, hand_tool_call, sync_all). Nó không biết cũng như không quan tâm nhà cung cấp nào đứng sau hậu trường.
Những lỗi thường gặp của người mới bắt đầu
1. Kích hoạt đồng thời hai nhà cung cấp bên ngoài
"Tôi muốn sử dụng mô hình người dùng của Honcho và tìm kiếm ngữ nghĩa của mem0 cùng lúc" -- MemoryManager sẽ từ chối cái thứ hai.
Khắc phục: Chọn một. Nếu bạn cần nhiều chức năng, hãy chọn nhà cung cấp toàn diện nhất hoặc đợi cộng đồng phát triển một giải pháp kết hợp.
2. Yêu cầu mạng chậm trong quá trình tìm nạp trước khiến mọi lượt quay đều bị lag
tìm nạp trước chạy sau khi tin nhắn người dùng đến nhưng trước khi nó được gửi đến mô hình. Nếu API Honcho chậm 2 giây thì mỗi lượt trò chuyện sẽ phải chờ thêm 2 giây.
Khắc phục: Sử dụng tìm nạp trước chuỗi nền. Lời kêu gọi triển khai của Honcho queue_prefetch() ở cuối mỗi lượt để tìm nạp trước ngữ cảnh cho lượt tiếp theo, vì vậy lượt tiếp theo prefetch() cuộc gọi đọc trực tiếp từ bộ đệm.
3. Ngoại lệ của nhà cung cấp làm gián đoạn cuộc trò chuyện
API của Honcho không hoạt động, tìm nạp trước sẽ đưa ra một ngoại lệ.
Khắc phục: MemoryManager kết thúc mọi lệnh gọi của nhà cung cấp trong thử/ngoại trừ. Một cái bị hỏng không ảnh hưởng đến những cái khác.
4. Không hiểu "Tích hợp luôn bật"
Giả sử rằng việc định cấu hình Honcho có nghĩa là MEMORY.md không còn được sử dụng nữa. Trên thực tế, bộ nhớ trong luôn hoạt động; Honcho là một cải tiến bổ sung. Các công cụ và bối cảnh từ cả hai đều được hợp nhất.
Ranh giới giảng dạy
Chương này trình bày phần trung tâm nhất trong ba hệ thống plugin của Hermes Agent: nhà cung cấp bộ nhớ.
Ba điều được đề cập:
- Giao diện MemoryProvider -- 4 phương thức bắt buộc + 4 phương thức tùy chọn
- Cách phối hợp của MemoryManager -- tìm nạp trước, đồng bộ hóa, định tuyến công cụ, giới hạn một nhà cung cấp bên ngoài
- Cách phát hiện và tải plugin -- Quét thư mục +
register(ctx)hoa văn
Không được bảo hiểm:
- ContextEngine (plugin chiến lược nén ngữ cảnh) -> mẫu tương tự như MemoryProvider, chỉ là một giao diện khác
- Hệ thống plugin chung (hook, lệnh CLI) -> cơ chế mở rộng rộng hơn
- Triển khai nội bộ của từng nhà cung cấp bộ nhớ -> tài liệu API tương ứng của họ
- Quét bảo mật plugin -> cơ chế bảo mật
Chương này liên quan đến chương khác như thế nào
- s07 bộ nhớ tích hợp được xác định -> chương này cho phép các dịch vụ bên ngoài nâng cao nó
- s05 tính năng nén tích hợp được xác định -> Plugin ContextEngine cho phép các chiến lược bên ngoài thay thế nó (cùng một mẫu)
- s16's MCP mở rộng khả năng của công cụ -> chương này mở rộng khả năng của bộ nhớ; cả hai đều "thêm chức năng mà không thay đổi mã lõi"
- Công cụ tự đăng ký của s02 -> plugin
register(ctx)theo cùng một mẫu đăng ký
Sau chương này, bạn sẽ có thể trả lời
- Mối quan hệ giữa bộ nhớ tích hợp và các nhà cung cấp bên ngoài là gì? MEMORY.md vẫn hoạt động sau khi định cấu hình Honcho phải không?
- Tại sao tại một thời điểm chỉ có một nhà cung cấp bộ nhớ ngoài có thể hoạt động?
- Tại sao phải có MemoryProvider's
prefetchnhanh lên? Nếu API chậm thì sao? - Nếu API của Honcho bị hỏng, agent vẫn có thể trò chuyện bình thường được không?
- Cần thay đổi bao nhiêu dòng core code để chuyển từ Honcho sang mem0?
Một lớp: Xác định giao diện, tải theo cấu hình, định tuyến thông qua bộ điều phối -- chuyển đổi phần phụ trợ bộ nhớ mà không thay đổi một dòng mã lõi nào.