OpenViking 日志导入指南:用openviking-server ingest把 Claude Code / Codex / OpenCode 等 Agent 日志重放为长期记忆
【免费下载链接】OpenVikingSelf-evolving Context Database for AI Agents. Unify Agent Memory, Knowledge RAG and Skills.项目地址: https://gitcode.com/GitHub_Trending/op/OpenViking
openviking-server ingest是 OpenViking 官方提供的一款客户端侧日志导入工具:它把本机已有的 AI 编码 / Agent harness(Claude Code、Codex、OpenCode、Hermes、OpenClaw)对话日志解析成标准消息,再经由 OpenViking 既有的会话管线(创建会话 → 批量追加消息 → 提交)"重放"进去,提交时触发记忆抽取,让历史与新增对话沉淀为长期记忆。读完本文你将掌握:如何通过ov.conf精确开启某个 harness 的导入、如何用backfill/watch/run三条命令完成存量回填与增量监听、游标与 peer_id 的设计原理,以及如何控制导入带来的 LLM 成本与隐私风险。
与"记忆插件"的定位差异:互补而非替代
OpenViking 针对各 harness 提供了实时记忆插件(参见 概览),它们在对话进行时挂载捕获;而openviking-server ingest解决的是另外两类诉求:
- 导入既有日志:把安装插件之前的历史会话一次性回填为记忆;
- 离线监听新增日志:在完全不安装插件、不改动 harness 的前提下,轮询读取其日志目录实现增量同步。
关键区别在于:本工具是 OpenViking 的客户端,运行在日志所在机器上,通过 SDK 指向本地或远端 server;它默认完全关闭,不会"装上就扫你本地文件",所有 harness 必须逐一显式开启后才会被读取。相关实现位于 openviking/ingest 目录。
默认关闭:双重开关 + 显式验证
该特性默认"双重关闭",必须显式开启:
- 总开关
ingest.enabled默认false(对应 ingest_config.py 中IngestConfig.enabled的默认值); - 每个 harness 的
enabled默认false,且未列出的 harness 不会被读取(IngestHarnessConfig.enabled默认False); - 存量回填需手动运行命令,并支持
--dry-run(只统计、不写入)与--since(限定时间窗)先行验证。
总开关与单个 harness 开关是与关系:enabled_harnesses()要求总开关为真,同时 harness 自身enabled=True且mode != "off"才生效(ingest_config.py)。
支持的 harness 一览
| harness | 状态 | 默认日志路径 | 说明 |
|---|---|---|---|
claude_code | 支持 | ~/.claude/projects/*/*.jsonl | append-only JSONL,字节偏移游标 |
codex | 支持 | ~/.codex/sessions/YYYY/MM/DD/rollout-*.jsonl | append-only JSONL |
hermes | 支持 | ~/.hermes/sessions/*.jsonl | 群聊 agent,user 取原始用户名 |
openclaw | 支持 | ~/.openclaw/agents/*/sessions/*.jsonl | 群聊 agent,user 取原始用户名 |
opencode | 实验性 | ~/.local/share/opencode/opencode.db | SQLite,按(time, id)轮询;旧版文件存储暂不支持 |
cursor | 暂缓 | ~/Library/Application Support/Cursor/User/**/state.vscdb | 无文档、随版本漂移的 KV blob,暂未实现 |
这里的 harness(agent 框架)指 Claude Code / Codex 等整套工具,区别于 OpenViking 里 "tool(工具调用)" 的概念。
实现上,每个 harness 对应一个轻量适配器(openviking/ingest/sources/ 下的claude_code.py、codex.py、hermes.py、openclaw.py、opencode.py、cursor.py),通过@register_source("name")装饰器注册进 registry.py 的SOURCE_REGISTRY;新增一个 harness 只需写一个LogSource子类,无需改动配置 schema(配置键是自由格式的)。适配器抽象出两类游标模型(sources/base.py):
JsonlLogSource:append-only JSONL(Claude Code / Codex / Hermes / OpenClaw),字节偏移游标;SqliteLogSource:关系型 SQLite(OpenCode),(time, id)游标、只读轮询。
以 claude_code.py 为例:每条记录顶层type为user/assistant的才会被解析,isSidechain/isMeta的子 agent 合成记录、纯 tool 调用轮次(无文本)都会被丢弃,只保留有实质文本的 user / assistant 轮次,并携带model、cwd、git_branch等元信息。
在 ov.conf 中开启
在ov.conf增加ingest段,列出要导入的 harness 并设置其模式:
{ "ingest": { "enabled": true, "server_url": "$OPENVIKING_URL", "api_key": "$OPENVIKING_API_KEY", "account": "default", "user": "default", "harnesses": { "claude_code": { "enabled": true, "mode": "both" }, "codex": { "enabled": true, "mode": "backfill" }, "opencode": { "enabled": false, "mode": "watch", "experimental": true }, "hermes": { "enabled": false, "mode": "both", "user_field": "sender" }, "openclaw": { "enabled": false, "mode": "both", "user_field": "sender" } } } }各字段含义与默认值(依据 ingest_config.py 的 pydantic 模型):
- 顶层:
enabled(默认false):总开关;server_url/api_key:目标 server 地址与密钥。server_url留空时回退到OPENVIKING_URL或http://localhost:1933,因此既能指向本地 server,也能指向远端;account/user(默认default):被导入会话的归属账号与用户;state_dir:游标状态库位置,默认~/.openviking/ingest;session_id_prefix(默认import):OV 会话 id 前缀,最终形如import__{harness}__{原生会话id};memory_policy:透传给重放会话的 memory_policy(空则用 server 默认值)。
- 每个 harness(
IngestHarnessConfig):enabled(默认false):是否导入该 harness;mode:off|backfill(一次性导入存量)|watch(监听新增)|both,默认backfill;paths:覆盖该 harness 的默认发现路径(可填多个文件/目录/DB 路径);poll_interval_seconds(默认5.0):watch 模式下该 harness 的轮询间隔;user_field:群聊 harness(hermes / openclaw)中存放原始用户名的日志字段名,用作 user 侧 peer_id;留空用适配器默认值;experimental:显式开启实验性适配器(如 opencode),用于声明"可能脆弱";commit:提交策略,含commit_token_threshold(默认6000,待归档 token 达到该值即提交)、commit_idle_seconds(默认5.0,watch 模式下会话空闲该时长后提交)、keep_recent_count(默认0,WM v2 滑动窗口:提交后仍在会话中保留的最近消息数,0 = 全部归档)。
配置模型extra: "forbid",即不认识的多余键会直接报错而不是被静默忽略——格式错误会在启动时暴露,不会伪装成"未配置 ingest"。
环境变量覆盖(部署期)
部署期开关也可用环境变量覆盖(优先级高于配置文件,定义见 consts.py):
OPENVIKING_INGEST_ENABLED(1/true/yes/on视为开启)OPENVIKING_INGEST_SERVER_URLOPENVIKING_INGEST_API_KEY
CLI 使用:五个子命令
openviking-server ingest命令随 OpenViking 一同安装(CLI 定义在 openviking/ingest/cli.py):
# 查看已注册 harness 及其配置 openviking-server ingest list-sources # 先干跑:统计会回填多少 session / 消息,不写入 openviking-server ingest backfill --dry-run # 只回填某个 harness、且只回填某日期之后的会话 openviking-server ingest backfill --harness claude_code --since 2026-06-01 # 正式回填(存量) openviking-server ingest backfill # 监听新增日志并增量重放(前台阻塞) openviking-server ingest watch --harness claude_code # 按每个 harness 配置的 mode 执行:先回填再监听 openviking-server ingest run # 查看各会话已导入到哪里(读取游标状态) openviking-server ingest status命令细节:
list-sources:列出已注册 harness 及当前生效配置(enabled、mode、paths,未配置的显示(not configured)),并打印ingest enabled与server_url。backfill:一次性回填存量,参数--harness/-H(默认所有已启用 harness)、--since(ISO 日期,跳过该时间之前开始的会话)、--dry-run(只统计不写入,无需 server 也无需加锁)、--reset。--dry-run会输出每个 harness 的sessions / messages统计;正式回填会输出Replayed: N sessions / N messages / N commits。watch:对mode ∈ {watch, both}的 harness 做增量轮询,前台阻塞运行,支持--harness过滤;通过SIGINT/SIGTERM优雅退出,退出时会为所有"脏"会话做最后提交(失败则needs_commit持久化、下次续传)。run:先对所有mode ∈ {backfill, both}的 harness 执行回填,再进入 watch 循环。status:展示每个会话的导入进度(harness、原生会话 id、已追加消息数、最近提交时间),可用--harness过滤。
--reset会在重放前删除并重建对应的 OV 会话(对应SessionReplayer.reset_session,见 replay.py);不加--reset时,重复运行是幂等的——游标保证不会重复追加。
运行回填/监听时,CLI 会在状态目录上获取单实例锁(SingleInstanceLock),避免两个进程同时写游标。
peer_id:为人类与模型建立画像
每条消息都会带上 peer_id(解析逻辑见 openviking/ingest/peer.py):
- assistant 消息:
{harness}/{模型名},provider 有意义时{harness}/{provider}/{模型名},例如claude_code/claude-opus-4-8、opencode/bytedance_ark/doubao-...(源码中以__拼接后经safe_peer_id校验); - user 消息:
- 单用户开发型 harness(claude_code / codex / opencode)取会话 cwd 所在仓库的 git 身份(优先
user.email,其次user.name,带每 cwd 缓存),无 git 仓库时回退为配置的ingest.user; - 群聊 harness(hermes / openclaw)取日志里的原始用户名(由
user_field指定),实现为LogSource.user_peer()的分支(sources/base.py)。
- 单用户开发型 harness(claude_code / codex / opencode)取会话 cwd 所在仓库的 git 身份(优先
任何包含非 ASCII 字符的标识(例如中文或混合文字用户名)都会将完整标识编码为无碰撞的ext-<base64>形式。ext-命名空间为编码身份保留;如果 ASCII 身份清理后会成为ext-id,系统也会对其编码,避免它冒充已有编码身份。新的读取和写入只使用规范 id。
旧版本可能把多个混合文字身份,或一个混合文字身份与真实 ASCII 身份,折叠到同一个 peer 目录中。OpenViking 不会把这些归属不明确的目录自动附加为别名;迁移既有数据前,运维人员必须先确认其真实归属。
工作原理:适配器 → 重放器 → 幂等游标
重放管线:ensure_session → 批量追加 → commit
每个 harness 的适配器把日志解析为标准消息(NormalizedMessage,见 models.py),交给重放器(SessionReplayer)执行:
reconcile() -> 每批 ≤100 条消息: set_pending -> append -> confirm -> commit_if_needed- OV 会话 id 形如
import__{harness}__{原始会话id}({prefix}__{harness}__{native_session_id}),确定且幂等; - 批量上限 100:server 端
batch_add_messages有 100 条上限(replay.py 的_BATCH = 100),读取侧DEFAULT_READ_LIMIT = 100与之对齐(sources/base.py); - 记忆抽取只在 commit 时触发:由 server 端执行,客户端只负责在合适时机提交。
崩溃自愈:reconcile 与持久化意图
每一批的意图(目标游标 + 批大小 + server 端追加前的消息数基线)会在追加前持久化。如果进程在追加中途崩溃,下一次运行reconcile()会比较 server 当前消息数与基线:若批已落地则确认(不重复追加),否则丢弃意图并从已确认游标重新读取。游标只在确认的追加后推进,needs_commit保证"已追加但未提交"的会话后续仍会被抽取(replay.py 模块文档)。
存量回填 vs 增量监听
- 存量回填:枚举所有会话,从游标读到末尾后逐会话提交一次(orchestrator.py);
since过滤按SessionRef.started_at比较。 - 监听增量:参照 OpenViking 自身的
WatchScheduler,用定时轮询(非文件系统事件)+ 持久游标驱动(poller.py);漏一拍、休眠或重启后,下一拍从游标读到末尾即可自愈。JSONL 用字节偏移游标(含半行/截断/轮转处理),SQLite 用(time, id)游标只读读取(兼容 WAL)。
JSONL 的轮转/截断处理值得一提:游标记录inode,当文件被替换(inode 变化)或游标偏移超过文件大小时,自动从头重读(sources/base.py)。SQLite 侧则以mode=ro只读连接,row_complete保证不会越过尚未写完的行(如 part 文本未 flush 的消息留给下一拍)。
游标状态持久化
游标状态持久化在~/.openviking/ingest/state.db(默认状态目录DEFAULT_INGEST_STATE_DIR = ~/.openviking/ingest),因此回填与监听都能在重启后续传,且不会重复入库。可用openviking-server ingest status查看每个会话的游标进度。
成本与隐私
- 提交会触发记忆抽取(LLM 调用)。一次性回填数月历史可能产生大量调用,建议先
--dry-run、用--since收窄时间窗、按 harness 分批开启,并善用commit_token_threshold/commit_idle_seconds控制提交频率。 - 日志中可能含敏感内容(凭据、文件内容)。请在受信任的部署中使用,并确认
server_url指向你期望的 server(必要时用环境变量OPENVIKING_INGEST_SERVER_URL/OPENVIKING_INGEST_API_KEY显式覆盖)。 - tool 调用的输入/输出默认按低价值丢弃:适配器只解析 user / assistant 的文本轮次,仅入库文本消息,不保留工具 I/O(normalize.py 只生成
textpart,空轮次直接返回None丢弃)。
参见
- 集成能力参考
- 概览 — 各 harness 的记忆插件(实时捕获方案)
- 部署指南 → CLI —
ov.conf/ 凭据配置
【免费下载链接】OpenVikingSelf-evolving Context Database for AI Agents. Unify Agent Memory, Knowledge RAG and Skills.项目地址: https://gitcode.com/GitHub_Trending/op/OpenViking
创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考