examples/openclaw-plugin/README_CN.md
使用 OpenViking 作为 OpenClaw 的长期记忆后端。在 OpenClaw 中,此插件注册为 openviking 上下文引擎。
本文档不是安装教程,而是面向集成方和工程师的"当前实现设计说明"。它基于 examples/openclaw-plugin 里的现有代码,重点解释这套插件今天实际上如何工作,而不是未来可能的重构方向。
examples/openclaw-plugin 不是一个单一职责的"记忆查询插件",而是一组围绕 OpenClaw 生命周期工作的集成层。按当前代码职责看,插件同时扮演四个角色:
context-engine:实现 assemble、afterTurn、compactbefore_prompt_build、session_start、session_end、agent_end、before_reset上图对应的是当前实现里的整体边界:
OpenVikingClient,由 client 层统一补 X-OpenViking-* 头和路由日志。viking://user/*、viking://agent/*、viking://session/*。这套拆分的意义,是让 OpenClaw 继续专注推理与编排,让 OpenViking 成为长期上下文的事实源。
这套插件不是把所有请求都打到一个固定 agent ID 上,而是尽量保持 OpenClaw 会话身份和 OpenViking 路由一致。
核心规则如下:
sessionId 是 UUID 时直接复用。sessionKey 存在时优先用它生成稳定的 ovSessionId。X-OpenViking-Agent 按 session 解析,不按进程写死。plugins.entries.openviking.config.agent_prefix 非空,会形成 <agent_prefix>_<sessionAgent> 的前缀形式。main。X-OpenViking-Agent,包括启动阶段的 health check。accountId / userId 时才发送 X-OpenViking-Account / X-OpenViking-User。这样做是为了支持多 agent、多 session 并发时的记忆隔离,避免不同 OpenClaw 会话串用同一套长期上下文。
默认推荐的远程模式配置只有:
baseUrlapiKeyagent_prefix其中:
apiKey 推荐使用某个 user 的 user keyaccountId / userId 仅在部署需要显式身份 header 时作为高级选项使用,例如 root key 或 trusted server 流程isolateUserScopeByAgent / isolateAgentScopeByUser 必须与服务端 account namespace policy 保持一致agentScopeMode 已退化为兼容旧 hash 路由的 deprecated alias,仅应在旧服务端上使用对于包含 PR #1356 的 OpenViking 服务端,插件不再在本地计算 user 或 agent scope hash,而是根据配置的 namespace policy 将别名 URI 展开为 canonical URI:
viking://user/memories
isolateUserScopeByAgent=false 时展开为 viking://user/<user_id>/memoriesisolateUserScopeByAgent=true 时展开为 viking://user/<user_id>/agent/<agent_id>/memoriesviking://agent/memories
isolateAgentScopeByUser=false 时展开为 viking://agent/<agent_id>/memoriesisolateAgentScopeByUser=true 时展开为 viking://agent/<agent_id>/user/<user_id>/memories插件当前无法从 /api/v1/system/status 自动发现这两个 policy,因此需要显式配置,使其与服务端 account policy 保持一致。
当前主召回路径仍然挂在 before_prompt_build,流程是:
messages 或 prompt 中提取最后一条用户文本。sessionId/sessionKey 解析本轮的 agent 路由。viking://user/memories 和 viking://agent/memories。<relevant-memories> 形式 prepend 到 prompt。这里的重排不是单纯依赖向量分数。当前实现还会额外考虑:
level == 2 的叶子记忆Session 是这套设计的主轴。当前实现里,它覆盖了"历史组装、增量写入、异步提交、阻塞压缩回读"四个动作。
assemble() 负责什么assemble() 并不是简单地把旧聊天记录塞回来,而是按 token budget 从 OpenViking 回读当前 session context,然后重新组装成 OpenClaw 可消费的消息:
latest_archive_overview 被改写成 [Session History Summary]pre_archive_abstracts 被改写成 [Archive Index]toolCall(输入兼容 toolUse/input,输出统一规范为 toolCall/arguments)toolResulttoolCall/toolResult 配对修复,降低 transcript 结构不稳定的风险因此,OpenClaw 拿到的是"压缩后的历史摘要 + archive 索引 + 当前活跃消息",而不是无限增长的原始 transcript。
afterTurn() 负责什么afterTurn() 的职责更窄,专门处理本轮增量写入:
user / assistant 相关文本内容toolCall / toolResult 格式化进 capture 文本<relevant-memories> 和元数据噪音之后插件会读取 session 的 pending_tokens。当它超过 commitTokenThreshold 时,会触发一次 commit(wait=false):
logFindRequests,日志里能看到 task id 和后续抽取结果compact() 负责什么compact() 走的是另一条更严格的同步边界:
commit(wait=true),阻塞等待 commit 完成latest_archive_overviewov_archive_expand 读取某个 archive 的原始消息所以 afterTurn() 更像"增量写入 + 条件触发异步提交",而 compact() 才是"明确等待压缩与归档完成"的正式边界。
这套插件除了自动行为,还直接暴露了 6 个工具:
memory_recall:显式检索长期记忆memory_store:把文本写入 OpenViking session 并立即触发 commitmemory_forget:按 URI 删除,或先搜索再删除唯一高置信候选ov_archive_expand:展开某个 archive 的原始消息ov_import:导入 resource 或 skill;默认 resource,导入 skill 时使用 kind: "skill"ov_search:检索 OpenViking resources 和 skills,尤其用于导入后的确认和消费它们各自的作用不同:
memory_recall 给模型一个显式补查入口。memory_store 适合把一段明确的重要信息立刻落入记忆管线。ov_archive_expand 负责在 summary 不够细时回到 archive 级原文。ov_import 让 agent 在用户明确提出导入需求时直接完成操作,不要求用户记住 slash command。ov_search 补齐导入后的使用闭环,让用户或 agent 可以确认并消费 resources 和 skills。其中 ov_archive_expand 是 assemble() 的重要补充,因为 assemble() 默认给的是压缩后的索引和摘要,而不是完整历史正文。
Resource 和 skill 保持两个入口,因为它们落在不同 OpenViking 命名空间,并使用不同服务端 API:
/api/v1/resources,落到 viking://resources/.../api/v1/skills,落到 viking://agent/skills/...插件也提供显式 slash command,方便手动导入:
/ov-import ./README.md --to viking://resources/openviking-readme --wait
/ov-import ./skills/install-openviking-memory --kind skill --wait
/ov-search "OpenViking install" --uri viking://resources/openviking-readme
/ov-search "memory install skill" --uri viking://agent/skills
Resource 导入支持远程 URL、Git URL、本地文件、本地目录和 zip。OpenViking 内置 parser 覆盖常见文档和媒体类型,例如 Markdown、纯文本、PDF、HTML、Word、PowerPoint、Excel、EPUB、图片、音频和视频。目录导入还支持常见代码、文档和配置扩展名,例如 .py、.js、.ts、.go、.rs、.java、.cpp、.json、.yaml、.toml、.csv、.rst、.proto、.tf、.vue。
出于 HTTP 安全边界,插件不会把本地文件系统路径直接发送给 OpenViking 服务端。本地文件和目录会先通过 /api/v1/resources/temp_upload 上传;目录会先在本地使用纯 JavaScript zip 实现打包后再上传。
插件仅以远程模式运行,作为纯 HTTP 客户端:
baseUrl 和可选 apiKey 由插件配置提供OpenViking 服务需要独立部署并运行,插件才能连接到它。
仓库里还有一份更偏"未来演进方向"的设计稿:docs/design/openclaw-context-engine-refactor.md。阅读时需要区分两者的口径:
before_prompt_build,并没有完全迁到 assemble()。afterTurn() 已经负责增量写入 OpenViking session,但它仍然依赖阈值触发异步 commit。compact() 已经走 commit(wait=true),但它的职责仍以"同步提交 + 结果回读"为主,而不是承载一切上层编排。这段区分很重要,否则很容易把未来设计误读成现状。
如果你要排查这套插件,优先看这几类入口:
ov-install --current-version
openclaw config get plugins.entries.openviking.config
openclaw config get plugins.slots.contextEngine
OpenClaw 插件侧日志:
openclaw logs --follow
OpenViking 服务侧日志:
cat ~/.openviking/data/log/openviking.log
python -m openviking.console.bootstrap --host 0.0.0.0 --port 8020 --openviking-url http://127.0.0.1:1933
ov tuiov tui
| 现象 | 更可能的原因 | 优先检查 |
|---|---|---|
plugins.slots.contextEngine 不是 openviking | 插件槽位未设置或被其他插件覆盖 | openclaw config get plugins.slots.contextEngine |
| 无法连接 OpenViking 服务 | baseUrl 配置错误或服务未启动 | 检查 baseUrl 配置并手动测试连接 |
| recall 在不同 session 间不稳定 | 路由身份和预期不一致 | 打开 logFindRequests,再看 openclaw logs --follow |
| 长对话后没有持续抽取记忆 | pending_tokens 未过阈值,或服务端 Phase 2 失败 | 检查插件配置和 ~/.openviking/data/log/openviking.log |
| summary 太粗,不够回答细节问题 | 你要的是 archive 级明细,不是摘要 | 用 [Archive Index] 里的 ID 调用 ov_archive_expand |
安装、升级、卸载请查看 INSTALL-ZH.md。