docs(memory): document and harden hybrid gateway setup

This commit is contained in:
2026-06-15 11:19:57 +08:00
parent c3b4f95062
commit 827e3434b3
5 changed files with 74 additions and 3 deletions

View File

@ -27,3 +27,38 @@
## 说明
后端已切到 Beaver 主线不再保留旧实现、vendored 第三方 runtime 或迁移期旧命名兼容入口。所有 agent 运行都复用 `beaver.engine`,多 agent 协调通过 Beaver 自有 coordinator 和 `ExecutionGraph` 表达。
## Memory Gateway
Curated memory 始终启用:每轮仍会冻结并注入 `MEMORY.md` / `USER.md`,原有
`memory` 工具也保持可用。`hybrid` 模式会额外启用独立的 Memory Gateway 层,
每轮先调用 `/memories/search`,正常完成后调用一次 `/memories/add`,成功后再调用
一次 `/memories/flush`。两套存储不会互相同步、覆盖或去重。
完整配置示例:
```json
{
"memory": {
"mode": "hybrid",
"gateway": {
"baseUrl": "http://127.0.0.1:8010",
"userId": "gateway_test_user",
"userKey": "uk_xxx",
"appId": "default",
"projectId": "default",
"scope": ["current_chat", "resources"],
"topK": 8,
"timeoutSeconds": 10
}
}
}
```
- `memory` 整段缺失时,默认采用隐式 `hybrid`Gateway 凭证不完整会告警并只运行 curated memory。
- 显式配置 `"mode": "hybrid"` 时,`baseUrl``userId``userKey` 缺失会导致启动失败。
- 配置 `"mode": "curated"` 可关闭 Gatewaycurated memory 行为不变。
- `userKey` 是密钥,不应写入日志、状态响应或提交到版本库。
- 容器访问宿主机 Gateway 时不能使用容器内的 `127.0.0.1`。应让 Gateway 监听
`0.0.0.0`,并把 `baseUrl` 配成该 Docker 网络的宿主机网关地址。
- 修改 memory 配置后需要重启 runtime因为 Gateway 服务在 `EngineLoader` 启动时创建。

View File

@ -209,13 +209,13 @@ class EngineLoader:
"""装配当前主链需要的最小 runtime 对象。"""
workspace = self.workspace
memory_gateway_service = self._resolve_memory_gateway_service()
session_manager = self._session_manager or SessionManager(workspace)
curated_root = workspace / "memory" / "curated"
curated_memory_store = self._curated_memory_store or MemoryStore(curated_root)
memory_service = self._memory_service or MemoryService(curated_root, store=curated_memory_store)
memory_service.initialize()
memory_gateway_service = self._resolve_memory_gateway_service()
run_memory_store = self._run_memory_store or RunMemoryStore(workspace / "memory" / "runs")
skill_learning_store = self._skill_learning_store or SkillLearningStore(workspace / "memory" / "skills")

View File

@ -266,13 +266,18 @@ def _parse_memory(data: dict[str, Any]) -> MemoryConfig:
parsed_timeout = _float(
_first_config_value(gateway_raw.get("timeoutSeconds"), gateway_raw.get("timeout_seconds"))
)
scope = (
_string_list(gateway_raw.get("scope"))
if "scope" in gateway_raw
else ["current_chat", "resources"]
)
gateway = MemoryGatewayConfig(
base_url=_string(gateway_raw.get("baseUrl") or gateway_raw.get("base_url")) or "",
user_id=_string(gateway_raw.get("userId") or gateway_raw.get("user_id")) or "",
user_key=_string(gateway_raw.get("userKey") or gateway_raw.get("user_key")) or "",
app_id=_string(gateway_raw.get("appId") or gateway_raw.get("app_id")) or "default",
project_id=_string(gateway_raw.get("projectId") or gateway_raw.get("project_id")) or "default",
scope=_string_list(gateway_raw.get("scope")) or ["current_chat", "resources"],
scope=scope,
top_k=8 if parsed_top_k is None else parsed_top_k,
timeout_seconds=10.0 if parsed_timeout is None else parsed_timeout,
)

View File

@ -579,6 +579,29 @@ def test_hybrid_memory_rejects_unknown_scope(tmp_path) -> None:
load_config(config_path=config_path)
def test_hybrid_memory_rejects_empty_scope(tmp_path) -> None:
config_path = tmp_path / "config.json"
config_path.write_text(
json.dumps(
{
"memory": {
"mode": "hybrid",
"gateway": {
"baseUrl": "http://127.0.0.1:8010",
"userId": "gateway-user",
"userKey": "uk_secret",
"scope": [],
},
}
}
),
encoding="utf-8",
)
with pytest.raises(ValueError, match="scope"):
load_config(config_path=config_path)
@pytest.mark.parametrize(
("gateway_override", "expected_error"),
[

View File

@ -68,7 +68,10 @@ def test_loader_implicit_hybrid_without_credentials_warns_and_degrades(
loaded.close()
def test_loader_explicit_hybrid_without_credentials_fails_without_secret(tmp_path) -> None:
def test_loader_explicit_hybrid_without_credentials_fails_before_opening_session_store(
tmp_path,
monkeypatch,
) -> None:
config = BeaverConfig(
memory=MemoryConfig(
mode="hybrid",
@ -77,6 +80,11 @@ def test_loader_explicit_hybrid_without_credentials_fails_without_secret(tmp_pat
)
)
monkeypatch.setattr(
"beaver.engine.loader.SessionManager",
lambda workspace: pytest.fail("session store opened before memory config validation"),
)
with pytest.raises(ValueError) as exc_info:
EngineLoader(workspace=tmp_path, config=config).load()