news 2026/9/10 16:52:27

OpenViking ZCode 记忆插件:为 ZCode 接入长期记忆生命周期的薄适配层实战指南

作者头像

张小明

前端开发工程师

1.2k 24
文章封面图
OpenViking ZCode 记忆插件:为 ZCode 接入长期记忆生命周期的薄适配层实战指南

OpenViking ZCode 记忆插件:为 ZCode 接入长期记忆生命周期的薄适配层实战指南

【免费下载链接】OpenVikingSelf-evolving Context Database for AI Agents. Unify Agent Memory, Knowledge RAG and Skills.项目地址: https://gitcode.com/GitHub_Trending/op/OpenViking

本篇技术指南围绕 OpenViking 为 ZCode(基于 Claude Code 配置格式的 AI 编程 Agent)提供的官方长期记忆插件展开,讲解其如何通过复用memory-plugin-shared共享运行时,以四个 Hook 事件完成用户画像注入、记忆召回、viking://虚拟路径拦截与增量会话捕获。读完本文,你将掌握该插件的安装方式、事件调度与 rollout 文件机制、严格 JSON 输出契约以及回归测试方法,并能据此理解 ZCode 扩展面与 OpenViking 记忆服务之间的完整调用链。

插件定位:只做薄适配,不重复记忆逻辑

ZCode 记忆插件(examples/zcode-memory-plugin/README_CN.md)的核心理念是:复用memory-plugin-shared共享运行时,不重复任何记忆逻辑,仅新增一个 ZCode 薄适配层

也就是说,召回(recall)、批量写入(batch send)、待处理队列(pending queue)、凭据解析(credentials)与 MCP 代理等记忆能力全部来自共享库examples/memory-plugin-shared/lib/,本插件只负责两件事:

  1. 把 ZCode 的 Hook 事件翻译成 OpenViking 记忆服务能理解的动作;
  2. 处理 ZCode 特有的确认(acknowledgement)与游标(cursor)状态转换。

插件的集成清单文件 openviking.integration.json 声明了其身份与能力边界:

{ "schemaVersion": 1, "id": "openviking-memory", "version": "0.1.2", "clients": ["zcode"], "capabilities": ["hooks", "mcp"] }

功能概览:四个 Hook 事件完成全生命周期接入

插件围绕 ZCode 实际支持的 7 个 Hook 事件,选择了其中 4 个可用的子集进行接线,其功能对照如下:

Hook 事件触发时机插件行为
SessionStart会话启动注入用户画像与偏好/实体到上下文,并重放待处理队列(replay pending)
UserPromptSubmit用户提交提示词搜索 OpenViking 相关记忆并注入上下文,带去重防抖
PreToolUseRead\|Glob\|Grep工具调用前拦截viking://虚拟路径的直接访问,引导 Agent 使用 MCP 工具
Stop回合结束立即返回,在 detached worker 中捕获增量用户/助手对话并提交 OpenViking 会话

之所以只接 4 个事件,是因为 DESIGN.md 中记录的已验证事实显示:ZCode不支持PreCompactSessionEndNotificationSubagentStartSubagentStop这 5 个事件。因此插件通过Stop 时 commit来补足 compact/会话结束信号,这是整个捕获链路设计的出发点。

安装:一行命令接入 ZCode

安装使用共享安装脚本,指定目标 harness 为zcode

bash examples/memory-plugin-shared/install.sh --harness zcode

安装脚本(examples/memory-plugin-shared/install.sh)的 ZCode 分支完成以下工作:

  1. 检测 ZCode:通过~/.zcode/目录或zcode二进制是否存在来判断(脚本内HAVE_ZCODE判定逻辑),并在未被显式指定时自动探测。
  2. 合并配置:将 hooks 配置与 MCP 配置合并写入~/.zcode/cli/config.json。从源码看,安装会生成~/.zcode/hooks.json与 MCP 配置,再通过zcode_merge_config合并进用户级配置文件。
  3. 写入凭据:将 OpenViking 凭据写入~/.openviking/ovcli.conf
  4. 模板变量替换:源模板 hooks/hooks.json 中的${ZCODE_PLUGIN_ROOT}在安装时被替换为绝对路径。这是关键设计:ZCode 的 config-file hooks不做模板展开,所以安装脚本必须在写入前完成路径渲染,规避该限制。
  5. 卸载清理:脚本同时提供卸载路径,会移除~/.zcode/hooks.json~/.zcode/mcp.json以及~/.openviking/agent-integrations/zcode目录。

安装完成后,可以查看~/.zcode/cli/config.json确认 hooks 与mcp.servers已就位。插件还支持在安装脚本中与其他 harness(claude,codex,cursor,trae,opencode,pi,dsh等)一起组合安装。

架构:Vendor 共享运行时 + 单入口调度器

插件的目录结构与职责划分如下:

examples/zcode-memory-plugin/ ├── hooks/hooks.json # Hook 配置模板(${ZCODE_PLUGIN_ROOT} 占位) ├── scripts/ │ ├── zcode-hook.mjs # 事件调度器(单一入口,按事件分支) │ ├── zcode-capture.mjs # ZCode 特有确认与游标状态转换 │ ├── zcode-turns.mjs # rollout 文件解析 / stdin 回退 │ ├── session-start.mjs # 三个轻量 shim + uri-guard 独立入口 │ ├── auto-recall.mjs │ ├── auto-capture.mjs │ ├── uri-guard.mjs # PreToolUse 独立入口 │ └── shared/ # vendor 进来的共享运行时(18 个 .mjs) └── servers/mcp-proxy.mjs # OpenViking MCP 代理

关键设计一:Vendor 而非相对路径引用

与 TRAE/Cursor(通过跨目录相对路径 import 共享库)不同,ZCode 插件与 Claude Code、Codex 采用同一模式:通过sync.mjs将共享运行时 vendor 到scripts/shared/。这让插件完全自包含、可整体搬迁——因为 ZCode 的 config 驱动安装模型会把文件拷贝到~/.openviking/agent-integrations/,相对路径方案会在此场景下失效。

关键设计二:config-file hooks 而非 plugin-manifest hooks

插件把 hooks 与 MCP 配置写入~/.zcode/cli/config.json(config-file 作用域),而不是走插件市场注册。这与 Cursor/TRAE 的安装模式一致。需要特别注意的是:config-file hooks 要求hooks.enabled: true,合并脚本会自动设置该开关。

关键设计三:调度器按事件名分支

zcode-hook.mjs 是唯一的逻辑入口,它通过process.env.OPENVIKING_HOOK_EVENT(或第二个命令行参数)拿到事件名:

const eventName = process.env.OPENVIKING_HOOK_EVENT || process.argv[2] || ""; const cfg = loadAgentHookConfig("zcode"); const { log, logError } = createAgentLogger("zcode", eventName, cfg);

三个 shim(session-start.mjsauto-recall.mjsauto-capture.mjs)都只有寥寥数行:设置OPENVIKING_HOOK_EVENT环境变量后动态 import 调度器。例如:

// scripts/auto-capture.mjs process.env.OPENVIKING_HOOK_EVENT = "stop"; await import("./zcode-hook.mjs");

运行时还会做一次 session id 归一化——ZCode 可能以 camelCase 或 snake_case 传入sessionId,调度器会补齐input.session_id字段,避免同一目录下开两个窗口时因 cwd 回退导致的 session 冲突:

if (!input.session_id && input.sessionId) input.session_id = input.sessionId;

严格 JSON 输出契约:只输出 ZCode 认可的键

这是本插件最值得注意的实现约束。ZCode 将 Hook 的 stdout 解析为严格 JSON——任何不被识别的多余键都会导致整个输出被静默丢弃。因此调度器绝不输出 Claude Code 风格的{ "decision": "approve" }字段,而是使用 ZCode 规范的两类输出:

上下文注入(SessionStart / UserPromptSubmit)

process.stdout.write( JSON.stringify({ hookSpecificOutput: { hookEventName, additionalContext, }, }) + "\n", );

透传(无需输出时):不写 stdout、隐式 exit 0。

拦截拒绝(PreToolUse,见 uri-guard.mjs)

{ hookSpecificOutput: { hookEventName: "PreToolUse", permissionDecision: "deny", permissionDecisionReason: "…", } }

这个契约被 DESIGN.md 标记为"第一大静默失败模式"(#1 silent-failure mode)——一旦混入多余键,功能看似正常实则完全不生效。

SessionStart:画像注入 + 待处理队列重放

调度器对session-start事件的处理包含 2000ms 节流(防止同一会话重复触发),并依次完成:

  1. 重放待处理队列(replayAgentPending),把之前因网络失败排队未发送的消息补发出去;
  2. 构建用户画像(buildAgentProfile,从 OpenViking 读取用户偏好/实体);
  3. <openviking-context source="session-start">…</openviking-context>包裹注入上下文。

UserPromptSubmit:记忆召回 + 双重防抖

user-prompt-submit事件,调度器先清洗 prompt 文本(剥离开插件注入的<openviking-context><relevant-memories><system-reminder>块),再做召回,并使用两种去重手段防止重复注入:

  • 若 stdin 带generation_id/request_id等事件 ID,直接比较事件 ID;
  • 否则对 prompt 做stableHash并检查 500ms 内的重复提交。

召回结果同样缓存在 hook state 中(recallBlock),供 Stop 阶段回填pendingPrompt

Stop 捕获机制:rollout 文件是权威增量对话源

ZCode 的 Stop hook stdin 载荷并未被完整文档化。根据 DESIGN.md 记录的反向工程结论(#3127),Stop 载荷中至少包含session_idcwdtranscript_path(指向一个只含最后一条助手消息的临时文件)以及responseText/responsePreview。用户消息并不在 stdin 中。

因此 zcode-turns.mjs 采用双通道策略:

  1. rollout 文件优先(权威来源):ZCode 在~/.zcode/cli/rollout/model-io-<sessionId>.jsonl存放完整对话,每行一条 JSON,结构为:
    { "sessionId": "sess_…", "turnId": 123, "type": "model_io", "request": { "messages": [ { "role": "user", "content": "…" } ] }, "response": { "text": "…", "toolCalls": [], "finishReason": "…" } }

    解析器从state.lastTurnId之后读取所有未见回合(extractUnseenRolloutTurns),首个捕获周期(无 lastTurnId)则读取全部条目,避免丢失历史。稳定的 hostturnId既用于去重,也让后续 Stop 能恢复漏掉的回合。

  2. stdin 回退:仅当 rollout 文件不可读时,才从responseText/responsePreview/prompt等字段回退构造 user/assistant 回合,并结合pendingPrompt补全用户消息。

确认与游标:不丢消息的增量提交

zcode-capture.mjs 负责把解析出的回合转换为待发送载荷,并推进去重与游标状态:

export function zcodeTurnDedupKey(turn) { return turn.turnId ? `${turn.turnId}:${turn.role}` // 有 turnId 时:turnId + role 组合去重 : stableHash(turn.role, turn.content); }

核心逻辑分三步:

  1. 过滤:用shouldCaptureText判断每条回合是否值得捕获,生成{ dedupKey, turn, content }候选;
  2. 去重:剔除已在state.capturedTurnIds中的候选(该集合按确认结果滚动保留最近 1000 条);
  3. 游标推进:只有某个 turnId 下所有回合都被确认(acknowledged)后,lastTurnId才推进到该 turnId——这保证了即使部分消息发送失败,下次 Stop 也能从断点恢复,而不是跳号。

发送成功的判定基于响应中的sent + queued计数(applyZcodeCaptureResultcaptured = min(toSend.length, sent + queued)),即消息已发出或已持久化排队才算确认。捕获到新消息后,调度器还会调用commitAgentSession提交会话,并把capturedSinceCommit归零。

Detached 写入:不阻塞 ZCode

Stop 事件在 zcode-hook.mjs 的入口处先尝试maybeDetach进入 detached worker:

if (eventName === "stop" && cfg.enabled && cfg.autoCapture) { const detached = await maybeDetach(cfg, { approve: () => {} }); if (detached) return; }

这样网络写入在独立进程中执行,Hook 立即返回,ZCode 的会话流程不会被慢网络阻塞。任何未捕获异常都会走 pass-through 兜底(logError("uncaught", error)),绝不让 Hook 卡死会话。

URI Guard:拦截 viking:// 直接访问

uri-guard.mjs 独立处理PreToolUse事件,matcher 限定为Read|Glob|Grep(见 hooks/hooks.json):

"PreToolUse": [ { "matcher": "Read|Glob|Grep", "hooks": [ { "type": "command", "command": "node \"${ZCODE_PLUGIN_ROOT}/scripts/uri-guard.mjs\"", "timeout": 5 } ] } ]

它从 stdin 中兼容读取tool_name/toolName/name/tooltool_input/toolInput/input等字段(适配不同字段命名),交由共享运行时agent-uri-guard.mjsevaluateAgentUriGuard判断是否为viking://URI。命中时返回上述 deny 输出,并附带引导使用 MCP 工具的原因说明;未命中则空输出透传。这样 Agent 不会绕过 MCP 直接以文件读写方式触碰viking://虚拟路径。

Hook 配置模板速查

源模板 hooks/hooks.json 完整定义了四个事件的接线,其中timeout单位为command类型;若用process类型则对应timeoutMs毫秒):

事件入口脚本timeout(秒)
SessionStartsession-start.mjs30
UserPromptSubmitauto-recall.mjs20
PreToolUse(Read|Glob|Grep)uri-guard.mjs5
Stopauto-capture.mjs30

MCP 侧,OpenViking 通过 servers/mcp-proxy.mjs 以用户作用域注册到~/.zcode/cli/config.jsonmcp.servers,ZCode 会在会话启动时自动连接所有作用域的 MCP 服务器;工具名按plugin:<plugin>:<server>规则命名空间化。

测试:回归套件覆盖关键故障模式

插件自带聚焦回归测试,覆盖了 DESIGN.md 中对抗性评审(adversarial review)识别的全部高风险场景:

node --test scripts/*.test.mjs

四个测试文件各司其职:

  • zcode-hooks.test.mjs:事件调度与严格输出契约;
  • zcode-turns.test.mjs:rollout 文件解析、首次捕获、断点恢复;
  • zcode-capture.test.mjs:确认与游标状态转换、重复 Stop 投递去重;
  • zcode-async.test.mjs:detached 慢写入不阻塞会话。

已验证的 ZCode 扩展面(事实清单)

下表是 DESIGN.md 基于真实 ZCode 安装(内置zcode-guide插件文档 + 实际~/.zcode/cli/config.json+ 真实安装的带 hooks 插件)验证的事实,可作为二次开发或排查问题的依据:

方面已验证事实
支持的 Hook 事件SessionStartUserPromptSubmitPreToolUsePermissionRequestPostToolUsePostToolUseFailureStop(恰好 7 个)
不支持的事件PreCompactSessionEndNotificationSubagentStartSubagentStop
Manifest 探测顺序.zcode-plugin/plugin.json.claude-plugin/plugin.json.codex-plugin/plugin.json
插件 Hook 模板变量${CLAUDE_PLUGIN_ROOT}${ZCODE_PLUGIN_ROOT}${CLAUDE_PROJECT_DIR}${ZCODE_PROJECT_DIR}${CLAUDE_SESSION_ID}
config-file Hook 模板变量——config 文件中的 Hook 不做模板展开
Hook 输出 schema严格 JSON——任何多余键都会校验失败、输出被丢弃
MCP 配置位置~/.zcode/cli/config.jsonmcp.servers(用户作用域)
插件 MCP 命名空间plugin:<plugin>:<server>
MCP 自动连接会话启动时自动连接所有作用域
Hook runner 启用任一插件贡献 hook 时自动启用
超时单位command类型为秒;process类型timeoutMs为毫秒
async字段无运行时效果——hooks 始终内联执行

已知未知项与使用注意事项

DESIGN.md 明确列出的"primary unknowns"(未知项)也值得了解,它们界定了本插件的边界:

  1. Hook stdin 字段名:Stop 载荷中用户消息字段名未文档化,已通过源码反向工程确认responseText/responsePreview承载助手内容,用户内容依赖 rollout 文件;
  2. 输出 schema 兼容性hookSpecificOutput包装层是否被 ZCode 原样接受,需在真实 ZCode 会话中验证;
  3. MCP 工具名格式plugin:openviking:openviking的命名空间化工具名需与实际工具名匹配;
  4. turn 身份:rollout 条目携带单调递增的turnId,插件将其作为 OpenViking 的turn_id透传,仅在消息已发送或持久化排队后才记录去重键,且只通过完整确认的 rollout 条目推进lastTurnId

实际使用中还需注意:本插件假设已有一台可访问的 OpenViking 服务,且~/.openviking/ovcli.conf中配置了正确的服务地址与凭据(由安装脚本写入)。若 Stop 事件从未触发(例如 Agent 被强制终止),增量对话的提交会被顺延到下一次 Stop 通过 rollout 文件恢复——这正是 rollout 优先设计的意义所在。

【免费下载链接】OpenVikingSelf-evolving Context Database for AI Agents. Unify Agent Memory, Knowledge RAG and Skills.项目地址: https://gitcode.com/GitHub_Trending/op/OpenViking

创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考

版权声明: 本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若内容造成侵权/违法违规/事实不符,请联系邮箱:809451989@qq.com进行投诉反馈,一经查实,立即删除!
网站建设 2026/9/10 16:52:17

SpringBoot+Vue3构建大件物流系统的技术实践

1. 项目概述&#xff1a;大件物流快递系统的技术架构与业务场景 大件物流快递系统是区别于普通快递的特殊物流形态&#xff0c;主要服务于家电、家具、建材等超规格商品的运输配送。这类商品通常具有体积大&#xff08;单边长度超过1.2米&#xff09;、重量重&#xff08;超过3…

作者头像 李华
网站建设 2026/9/10 16:49:46

tdl下载器源码深度解析:揭秘Golang高效下载机制

tdl下载器源码深度解析&#xff1a;揭秘Golang高效下载机制 Telegram下载器tdl是一个用Golang编写的高效下载工具&#xff0c;专门用于从Telegram平台快速下载各类文件。作为GitHub加速计划的重要项目&#xff0c;tdl下载器凭借其优秀的并发处理和智能进度管理机制&#xff0c…

作者头像 李华
网站建设 2026/9/10 16:49:10

vue 在线预览 word ,Excel,pdf,图片 数据流 内网文件流 亲测有效(word 目前支持docx文件以及doc文件(doc需要后端处理))

注&#xff1a;doc转 docx后端转数据流 谷歌 114 版本以上会解析错误&#xff01; 如果是需要更好的体验&#xff1a;可以使用 kkFileView - 在线文件预览 需要后端在服务器部署一个服务 之后返回地址前端进行直接在线访问&#xff1b;&#xff08;支持内网哦&#xff09; …

作者头像 李华
网站建设 2026/9/10 16:48:25

【148+279+48+192倒计时8路抢答器2023年6月5日16:52:31】

缘由https://ask.csdn.net/questions/7957804/54226107 八路抢答器设计时&#xff0c;倒计时数字不动&#xff0c;只能显示0&#xff0c;无法自主变动。 看我的仿真运行图&#xff0c;图中各逻辑&#xff0c;不赘述。 須菩提白佛言&#xff1a;「世尊&#xff01;若一切法一切…

作者头像 李华
网站建设 2026/9/10 16:44:49

SEO优化常见误区与实战避坑指南

1. SEO优化中的常见误区与避坑指南 在互联网营销领域&#xff0c;SEO&#xff08;搜索引擎优化&#xff09;始终是获取自然流量的核心手段。从业15年来&#xff0c;我见证了无数网站因为基础SEO错误而浪费大量预算&#xff0c;也帮助不少企业通过纠正简单误区实现了流量翻倍。今…

作者头像 李华