pi
为 pi 提供跨会话长期记忆和 context takeover。召回、捕获和 takeover 基于 pi 原生扩展 API:每个 prompt 前自动召回、每个 turn 后自动捕获,并可由 OpenViking 在 pi 的 context hook 中用归档 overview 替换已 commit 的本地历史。扩展使用官方 MCP 客户端,把服务端的 MCP 工具注册成 pi 原生工具。
源码:examples/pi-coding-agent-extension
工作方式
- Session 开始时构建 OpenViking profile 块,并恢复 takeover 状态,包括
pi -c续跑所需的 branch 水位。profile 块包含你的 profile、记忆索引和<available-skills>清单(你自己的和账号共享的 OpenViking skill),每个 turn 都会附加到 pi 的系统提示词里;模型照某个 skill 做事之前,先用openviking_read读它的SKILL.md。 - 每个 prompt 向 OpenViking server 做语义召回,把命中的记忆作为隐藏上下文注入。
- 每个 turn 把用户与助手消息(含结构化工具调用)捕获进 OpenViking session;takeover 模式会在 synced token 压力超过阈值后 commit,并在模型上下文中只保留近期 live tail。
- Context takeover 默认开启。
contexthook 会先注入来自最新 archive overview 的[OpenViking Session Context],再把 recall 结果插入最新用户消息。 - 模型可调用工具就是服务端 MCP
tools/list返回的工具,注册时统一加openviking_前缀,包括:openviking_find、openviking_search、openviking_read、openviking_list、openviking_tree、openviking_grep、openviking_glob、openviking_remember、openviking_write、openviking_edit、openviking_add_resource、openviking_add_skill、openviking_list_watches、openviking_cancel_watch、openviking_forget、openviking_health。扩展自己不再维护工具清单,服务端增删工具后,下一次 pi 会话即生效,无需发布新版扩展。共享键mcpEnabled: false可关掉整个工具面,召回、捕获和 takeover 不受影响。 - Skill:扩展从自己的
skills/目录提供openviking-memory、openviking-skills和ov-experience-memory,通过 pi 的resources_discover事件加入,让模型知道什么时候该用哪个 OpenViking 工具。mcpEnabled为false时不会加入。 - 工具握手失败不会影响启动。仅 MCP 握手失败时,REST 召回、捕获和 takeover 仍可使用;整个服务不可达或认证失败时,这些功能也会失败或降级。桥接会在后续 turn 重试,所以 pi 启动之后才拉起的服务端不必重启 pi 也能接上。
- 底部状态栏显示
OV ✓ …连接与捕获状态,握手失败时还会追加tools ✗;/viking命令查看状态,包括已注册的工具数量或最近一次握手错误,/viking commit强制 commit。 - 模型用本地
read/grep/find/ls/write/edit访问viking://URI 路径时,调用会被拦截,并提示改用对应的openviking_*工具;bash命令带viking://URI 时照常执行,结果末尾附一条提示,建议改用openviking_read/openviking_search。
扩展 0.4.0 用这套镜像工具面替换了原先手写的 7 个 viking_* REST 工具,且不保留别名:--tools / --exclude-tools 白名单里写死旧名字的,必须手动改过来,否则这些工具会静默消失。部分语义也随之改变——openviking_remember 写入独立 session 并由服务端立即 commit,openviking_read 读取文件正文,支持按行分页(旧的 level="abstract" / "overview" 两档已移除,需要精简视图改用 openviking_search(mode="context", detail="overview") 或 openviking_tree(include_abstract=true)),openviking_forget 直接删除给定 URI,而不是删除某个 query 的最强匹配。Context takeover 的归档 overview 仍由扩展经 REST 注入,这条路径不变。
工具调用和结果会作为独立的 tool part 捕获,tool_output 原样上报。截断由服务端负责:超过 tool_output_externalization.threshold_chars(默认 20000)的输出会写入 session 的 tool-result 存储,part 中只保留 synopsis stub 和 tool_output_ref,原文仍可通过 /api/v1/sessions/{id}/tool-results 读回。
前置条件
- Node.js 22.19.0+ 和 npm
- pi coding agent(
npm i -g @earendil-works/pi-coding-agent) - OpenViking HTTP server——用
curl http://localhost:1933/health确认可达
安装
pi 与其他记忆插件共用同一个安装器。它会询问要安装的 harness 和 OpenViking 凭据;每一步都是幂等的。
curl -fsSL https://openviking.ai/install | bash
# AI agent 如不确定是否执行,可先阅读脚本头部,其中写明了它会做什么、如何核实。安装器会先安装 lockfile 中锁定的 npm 依赖,再将扩展放到 ~/.pi/agent/extensions/openviking,并配置 ~/.openviking/ovcli.conf。该目录是 pi 的自动发现根目录之一,复制到位后 pi 会在下次启动时自动加载,无需再执行 pi install。(对同一路径再跑 pi install 会让扩展被加载两遍——一次来自自动发现,一次来自 packages 条目——出现重复的 /viking 命令。)
也可以从仓库 checkout 手动复制:
git clone https://github.com/volcengine/OpenViking.git
cd OpenViking
node examples/memory-plugin-shared/sync.mjs
mkdir -p ~/.pi/agent/extensions/openviking
cp -R examples/pi-coding-agent-extension/. ~/.pi/agent/extensions/openviking/
npm ci --prefix ~/.pi/agent/extensions/openviking --omit=dev --ignore-scripts扩展从 shared/ 目录 import 共享模块,这个目录不在 git 里,由 sync.mjs 生成,所以复制前必须先运行它。之后 git pull 更新时,也要先重新运行 sync.mjs 再复制。
配置
凭据解析顺序是 OPENVIKING_* 环境变量、~/.openviking/ovcli.conf、~/.openviking/ov.conf——与 Claude Code、Codex、OpenCode 插件共用。尚未配置时可运行一次向导:
node ~/.pi/agent/extensions/openviking/scripts/setup.mjs行为与 peer 作用域配置写在 ~/.openviking/ovcli.conf 的 plugin 段,与其他记忆插件共用。plugin.pi 下的键只对本扩展生效,并覆盖共享键;连接和认证凭据仍来自上面的配置源:
{
"plugin": {
"recallPeerScope": "all",
"scoreThreshold": 0.35,
"recallTokenBudget": 2000,
"profileTokenBudget": 10000,
"skillCatalog": true,
"skillCatalogTokenBudget": 1200,
"resumeContextBudget": 32000,
"commitTokenThreshold": 20000,
"captureAssistantTurns": true,
"pi": {
"workspacePeer": true,
"peerSource": "git",
"takeoverEnabled": true,
"takeoverTokenThreshold": 30000,
"takeoverKeepRecentTurns": 3,
"takeoverOverviewBudget": 3000,
"takeoverOverviewPollMs": 2000,
"takeoverOverviewPollMax": 15,
"bypassSessionPatterns": []
}
}
}配置项按优先级从高到低解析:OPENVIKING_* 环境变量、工作区的 .openviking/config.json 与 config.local.json、plugin.pi、plugin,最后是内置默认值。autoCapture: false(本扩展的旧拼写是 syncTurns)让它不再回写对话,autoRecall: false 让它不再检索。
commitKeepRecentCount(OPENVIKING_COMMIT_KEEP_RECENT_COUNT)已不再生效:takeover 之外的 commit 会归档全部已捕获的消息,takeover 则发送它保留的那几轮对应的确切消息数。现有配置里的这个键可以直接删掉。
profileTokenBudget 只管 session 开始时注入的 profile 和记忆索引。<available-skills> skill 清单有独立的预算 skillCatalogTokenBudget(默认 1200,环境变量 OPENVIKING_SKILL_CATALOG_TOKEN_BUDGET):先列你自己的 skill,再列 viking://agent/skills 下共享的 skill;共享 skill 与你自己的 skill 同名时不再列出。放不下描述时只列 skill 名,名字也列不全时末尾注明 ... +N more;连一个名字都放不下时,只写一行 skill 总数。设置 skillCatalog: false(OPENVIKING_SKILL_CATALOG=0)或把预算设为 0 即可关闭 skill 清单;没有 skill,或服务端没有 GET /api/v1/skills 接口时,这一块会直接省略。
OPENVIKING_PEER_ID 优先于所有配置文件。在它之下,更具体的一层胜出:workspace 的 peer.id 和 plugin 段里写的 peerId 都优先于 ovcli.conf 的 actor_peer_id 和 ov.conf 的 pi.peerId。任何一层给出的显式 peer 又都优先于 workspace 派生值。只有启用 workspacePeer 且所有层都未给出 peer 时,才会从工作区派生。
peerSource 决定 workspace peer 的派生方式。默认的 "git" 取仓库归一化后的 origin URL(git@github.com:volcengine/OpenViking.git 得到 github.com-volcengine-openviking),其次是仓库根路径,因此同一个仓库的每个 clone、worktree 和子目录共用同一个 peer;不在仓库中则完全不发送 peer,在那里记下的内容进入用户级空间 viking://user/<you>/memories。"cwd" 恢复此前的行为——把工作目录路径中的非字母数字字符全部替换成 -;"none"(等同于 OPENVIKING_WORKSPACE_PEER=0)完全不发送 peer;也可以用 "team-{dir}" 这样的模板,或按顺序尝试的模板列表自定义。要让仓库之外的目录拥有独立记忆,请为它设置 OPENVIKING_PEER_ID(见让一个目录拥有独立记忆)。
Takeover 是 fail-open:如果 pending 写入无法 flush、commit 失败、archive overview 尚未生成,或 branch 指纹不匹配,pi 会继续保留完整本地历史,或回退到默认 compaction。验证边界推进时可设置 OPENVIKING_DEBUG_LOG=/tmp/ov-pi.log,日志按 JSON Lines 写入,每个事件一行。OV_DEBUG_LOG 是已弃用的别名,仍然可用。
验证
启动 pi。连接成功后底部状态栏出现 OV ✓ 段,/viking 会打印当前 session 映射和已注册的 openviking_* 工具数量。问 pi 一个你在早前会话里提到过的事情,确认召回生效。
故障排查
| 问题 | 排查方向 |
|---|---|
状态栏显示 OV ✗ 或提示 "server not reachable" | curl http://localhost:1933/health;检查 ~/.openviking/ovcli.conf 中的 endpoint |
| 扩展没有加载 | 确认 ~/.pi/agent/extensions/openviking/index.ts 存在(该目录会被自动发现);重跑安装器即可。不要用 pi install 注册它——那样会重复加载 |
/viking 命令出现两次(viking:1 / viking:2) | 老版本安装器用 pi install 留下了冗余的 packages 条目,导致自动发现和 packages 各加载一次;执行 pi remove ~/.pi/agent/extensions/openviking 清掉该条目(只改 settings,不删文件) |
加载时报找不到 shared/*.mjs(例如 shared/capture-utils.mjs) | 手动复制前没有运行 sync.mjs。在仓库根目录运行 node examples/memory-plugin-shared/sync.mjs 后重新复制,或者重跑安装器 |
报错提到 @mariozechner/* | pi 迁移到 @earendil-works/* 之前的旧副本——重跑安装器 |
| OpenViking 返回 401 / 403 | 检查 OPENVIKING_API_KEY;trusted-mode 部署还要检查 OPENVIKING_ACCOUNT 和 OPENVIKING_USER |
状态栏显示 tools ✗,或会话里没有 openviking_* 工具 | 用 /viking 查看完整的握手错误。root API key 访问 /mcp 会被 403 拒绝——请改用 user 或 admin key。mcpEnabled: false 是主动关闭工具面,不算故障 |
| recall 为空 | 确认 server 中已有记忆,且 prompt 长度超过 minQueryLength |
完整工具、配置与设计说明见 扩展 README。