资讯动态

oh-my-pi 如何基于 Hindsight 为终端编码 Agent 构建持久化代码库记忆:银行作用域、心智模型种子与自动 retain 管道全解析

发布时间:2026/9/13 7:34:39 来源:尧图企业网站定制
oh-my-pi 如何基于 Hindsight 为终端编码 Agent 构建持久化代码库记忆银行作用域、心智模型种子与自动 retain 管道全解析【免费下载链接】hindsightHindsight: Agent Memory That Learns项目地址: https://gitcode.com/GitHub_Trending/hindsight2/hindsightoh-my-piomp是一个 TypeScript Rust 编写的终端编码 Agent。本文以 Hindsight 官方博客对 oh-my-pi 集成的深度剖析为骨架结合本仓库Hindsight 服务端的 HTTP API 与心智模型刷新引擎源码完整还原它在 Hindsight 之上构建记忆子系统的工程实践三种银行作用域模式、三份心智模型种子的设计与刷新策略、防抖 retain 队列与自动 recall/retain 生命周期。读完本文你将掌握在自有编码 Agent 中复刻这一套会话级记忆 → 持久化记忆 → 低延迟摘要缓存完整链路的全部技术细节。TL;DRoh-my-pi 将retain/recall/reflect作为面向模型model-facing的工具暴露给 Agent底层全部由 Hindsight 提供此外还实现了模型无需感知的自动 retain / 自动 recall 生命周期。记忆默认按项目作用域隔离提供三种模式global、per-project、per-project-tagged默认模式为per-project-tagged。一个**心智模型层mental-models layer**在首次会话启动时播种三份经过 reflect 生成的摘要user-preferences、project-conventions、project-decisions并在每次重建提示词时拼接其缓存输出避免每轮都付出一次 recall 往返的代价。集成初期使用vectorize-io/hindsight-client在确定实际所需 API 端点后改用自研极简 fetch 客户端不改变底层 API 契约仅依赖retain、retainBatch、recall、reflect、bank 与文档管理、批量列表等端点。Bank-Scoping 模型什么算一个用户的一份记忆编码 Agent 要做的第一个决策是什么构成一个用户的记忆边界。oh-my-pi 通过hindsight.scoping给出三种答案global—— 单一共享 bank不做任何按项目的过滤。per-project—— 按 cwd 的 basename 为每个项目建一个独立 bank硬隔离。per-project-tagged—— 仍使用单一共享 bank但每次 retain 都会携带project:name标签recall 时按标签过滤同时仍会浮出未打标签全局的记忆。默认的per-project-tagged是三者中最精妙的选择整个用户只用一个 bank但每条记忆都打上来源项目标签recall 用tagsMatch: any过滤于是 Agent 既能看到项目作用域内的记忆也能看到用户全局 retain 的内容用户偏好、跨项目约定。它比全局银行隔离更强又比每个仓库一个银行更灵活——正好符合在多个项目间切换的编码 Agent 的取舍。作用域推导是短短一个函数来自 oh-my-pi 的bank.tsexport function computeBankScope(config: HindsightConfig, directory: string) { const base baseBankId(config); // omp unless overridden switch (config.scoping) { case global: return { bankId: base }; case per-project: return { bankId: ${base}-${projectLabel(directory)} }; case per-project-tagged: { const tag ${PROJECT_TAG_PREFIX}${projectLabel(directory)}; return { bankId: base, retainTags: [tag], recallTags: [tag], recallTagsMatch: any }; } } }projectLabel就是目录的 basename因此在~/code/superproject下工作时一切都会被标记为project:superproject。标签过滤的语义由 Hindsight 服务端完成——无需任何 schema 工作也不需要维护每个仓库的 bank 存活状态。如果想要硬隔离per-project模式直接给每个目录一个独立 bank。服务端视角tags 与 tags_match 的真实语义从 Hindsight 服务端的源码看标签过滤不是客户端的把戏而是 API 一等公民。在 recall 请求模型 中tags_match的取值语义如下服务端 list/recall 查询参数 亦有说明any默认—— OR 语义命中任意一个标签即可同时未打标签的记忆也会被包含进来all—— AND 语义要求命中全部标签但仍包含未打标签的记忆all_strict—— 安全隔离语义要求记忆必须携带模型拥有的每一个标签未打标签的记忆被排除exact—— 精确匹配tags[]配合exact可专门选中未打标签/全局作用域。这正是 oh-my-pi 选择per-project-taggedrecallTagsMatch: any的原因any模式下项目标签命中的记忆与全局未打标签记忆会同时返回天然实现项目记忆 全局记忆的混合视野而这一切都发生在服务端客户端零额外成本。Mental-Models 层把摘要变成零延迟的提示词缓存整个集成的点睛之笔是心智模型层mental-models layer。Hindsight 心智模型是一种具名、持久化、由服务端自行保鲜的摘要。omp 将其用作Agent 关于你和你的项目已知内容的低延迟缓存——在每次重建开发者指令时直接拼接进去而不是每轮都为 recall 付出一次往返。种子文件seeds.json就是全部策略{ seeds: [ { id: user-preferences, name: User Preferences, source_query: What does the user prefer in coding style, tooling, communication, and review? Capture only durable preferences expressed across sessions, not one-off requests., scopes: [global, per-project, per-project-tagged], projectTagged: false, max_tokens: 600, trigger: { mode: delta, refresh_after_consolidation: true } }, { id: project-conventions, name: Project Conventions, source_query: What are this projects conventions for code style, build, testing, release, and pull-request review? Only include conventions that are explicit in the project (settings, scripts, contributor docs, repeatedly enforced in review)., scopes: [per-project, per-project-tagged], projectTagged: true, max_tokens: 800, trigger: { mode: delta, refresh_after_consolidation: true } }, { id: project-decisions, name: Project Decisions, source_query: What durable architectural or product decisions have been made for this project, and what rationale or trade-offs were recorded? Include only decisions that are stable across sessions; exclude transient plans, unresolved ideas, and active task state., scopes: [per-project, per-project-tagged], projectTagged: true, max_tokens: 800, trigger: { mode: delta, refresh_after_consolidation: true } } ] }这份文件里有三处值得深挖的设计1.trigger.mode deltarefresh_after_consolidation true这是编码 Agent 的正确姿势心智模型不会在每次 consolidation 时整体重生成只有当 consolidator 浮出实质性改变模型的新内容时才刷新。代价低、该新鲜时新鲜。从 Hindsight 服务端的 MentalModelTrigger 模型 看mode的两种取值语义如下full默认—— 每次刷新都从零重新生成心智模型内容delta—— 对现有内容做外科手术式编辑未变化的部分逐字节保留、过期内容被移除、新内容被追加如果模型尚无内容或source_query自上次刷新后发生变化delta 模式会自动回退为 full 重生成。refresh_after_consolidation: true意味着观察合并后实时模式刷新本模型它与refresh_cronUTC 标准 5 段 cron 表达式定时刷新互斥。此外服务端还支持min_refresh_interval_seconds作为刷新频率下限触发的刷新若来得太早不会丢弃而是排队驻留直到窗口期满期间的所有触发折叠成一次刷新——一次 retain 爆发只付一次刷新成本而不是每次 retain 一次。2.source_query是提示词不是关键词过滤器Hindsight 会把source_query当作一次reflect对 bank 执行——由 LLM 从记忆中挑出相关内容而不是做精确匹配搜索。提示词本身在做筛选工作只保留跨会话表达的持久偏好、只收录项目中明确的约定、只收录跨会话稳定的决策。这解释了为什么每条种子的措辞都充满了限定语——它们在引导 reflect 的判读。从 reflect 端点 的服务端实现看reflect 的处理链路是检索经验对话与事件→ 检索相关世界事实 → 检索观察与心智模型bank 的合成视角→ LLM 基于检索结果组织上下文答案 → 返回纯文本答案与所用事实based_on含 memories、mental-models、directives 三类证据。source_query正是被注入这条 reflect 管道的查询。3. 标签纪律种子标签必须是 retain 时实际写入标签的子集oh-my-pi 的mental-models.ts中有一句注释是整个集成里最见功力的细节Hindsight 的刷新路径会用all_strict标签匹配针对模型的标签过滤源记忆。如果一个种子携带了 retain 时永远不会写入的标签它刷新出来就是空的。因此种子标签必须是retainSession/enqueueRetain在当前作用域模式下实际附加标签的子集。服务端对这条纪律给出了精确解释在 MentalModelTrigger.tags_match 的文档中写明——当模型带有标签时tags_match默认为all_strict安全隔离记忆必须携带模型的每一个标签未打标签的记忆被排除。这正是为什么一个带有其记忆并不携带的标签的模型刷新出来的内容是空的。projectTagged: true的种子会内嵌当前作用域的project:cwd标签而未打标签的种子如user-preferences在tags为空时 reflect 不施加标签过滤因此能读取 bank 中的每一条记忆。在 心智模型刷新引擎 中还有一处印证MentalModelRefreshScope明确说明模型存储的tags并非过滤记忆的东西——tags_match在存在标签时默认为all_strict且tag_groups会完全覆盖扁平标签并报告解析后的结果。delta 刷新还会以模型的last_memory_seen_at作为窗口下界watermark只读取上次刷新以来新增或编辑的记忆未读取到的写入会保持比持久化 watermark 更新留待下次刷新捕获。当缓存的心智模型块被加载时它外面会包上反反馈anti-feedback包装器让 LLM 将其视为背景知识而非可执行指令——这与 Hindsight 自家集成处理 recall 片段时使用的模式一致。Retain 管道工具触发与自动保留的双轨设计oh-my-pi 的 retain 代码把工具触发的 retain模型在回合中调用retain与自动 retain会话结束时整段会话落库分开处理。模型面向的工具使用一个防抖批量队列const RETAIN_FLUSH_BATCH_SIZE 16; const RETAIN_FLUSH_INTERVAL_MS 5_000;当模型用一条或多条内容调用retain时它们先入队。队列在攒满 16 条时立即刷新或在首个条目入队 5 秒后刷新。每次刷新是对/v1/default/banks/{bank_id}/memories的一次retainBatch(...)POST并带上async: true——这样 Agent 无需等待服务端做事实抽取。模型的工具结果是即时的count memory queued.——实际写入是 fire-and-forget刷新失败以会话警告session warning notice的形式浮出而不是抛异常。自动 retain 走另一条路径它构建会话的转录可通过hindsight.retainMode配置为full-session或last-turn作为一次大的retain调用发送。默认的full-session模式让 Hindsight 的事实抽取器来决定什么值得保留而不是让 oh-my-pi 在客户端预总结。生命周期配置的默认值见config.tsscoping: per-project-tagged, autoRecall: true, autoRetain: true, retainMode: full-session, retainContext: omp, recallBudget: mid,服务端视角async retain 到底做了什么对照 Hindsight 服务端 retain 端点 的实现async: true的语义非常明确提交后立即返回用 operations 端点监控进度。服务端会自动执行一系列操作从内容中抽取语义事实生成 embeddings对相似事实去重建立时间、语义与实体链接跟踪文档元数据提供document_id时自动 upsert。在异步路径下服务端按策略分组提交submit_async_retain返回operation_id多个策略组时返回operation_ids列表。客户端拿到的RetainResponse包含success、bank_id、items_count、async与operation_id——这正是 oh-my-pi fire-and-forget 设计能成立的服务端前提异步提交把事实抽取与落库的开销完全移出 Agent 的回合时延。值得一提的是服务端对保留请求还会做多模态归一化任何多模态 item 会被展平为规范的占位符文本图片提交到内容寻址存储下游同步或异步、operations 负载、每一次重试只携带文本原始截图字节永不进入管道。Recall 管道模型工具与自动注入的双通道Recall 以两种方式暴露面向模型的工具模型在回合中用查询字符串调用recall与自动注入步骤HindsightSessionState.beforeAgentStartPrompt在首次 LLM 调用前执行一次组合查询。前者简单——POST /v1/default/banks/{bank_id}/memories/recall携带模型的查询、配置的预算、类型与 bank 作用域标签过滤器。自动注入路径才是功夫所在。oh-my-pi 从content.ts中把最近几轮用户消息recallContextTurns组合成一条 recall 查询用配置的字符预算recallMaxQueryChars封顶然后在 recall 响应前加上这段序言Relevant memories from past conversations (prioritize recent when conflicting). Only use memories that are directly useful to continue this conversation; ignore the rest:这段序言是刻意的。Hindsight 的 recall 返回的是与查询相关的内容而不是现在恰好有用的内容。序言告诉 LLM 再做一层过滤——并且在记忆互相矛盾时优先新鲜记忆。如果你见过 recall 结果被注入 Agent 提示词会认出这个模式Hindsight 官方文档也使用它。reflect工具则拥有自己独立的POST /v1/default/banks/{bank_id}/reflect并在每个进程首次使用时对 bank 的reflect_mission/retain_mission做一次 best-effort 的PUT。与 SDK 逻辑相同只是重写在一个完全由客户端掌控的 fetch 包装器中。服务端视角recall 与 reflect 的完整请求面对照服务端源码两个端点的真实请求能力远比表面丰富recall 端点 支持types过滤world——关于人物、地点、事件与发生之事的一般知识experience——经验、对话、采取的行动与完成任务observation——从事实中综合出的已合并知识缺省时召回全部事实类型、query_timestamp时间回溯ISO 格式、include.entities/include.chunks/include.source_facts三类附带内容及其 token 上限默认分别 500 / 8192 / 4096、tagstags_matchtag_groups标签过滤、min_scores最低分阈值、temporal_window时间窗口以及prefer_observations偏好观察。服务端还会按recall_max_query_tokens校验查询长度超长直接 400。reflect 端点 支持response_schema结构化输出、apply_all_directives、fact_types、exclude_mental_models/exclude_mental_model_ids排除其他心智模型参与 reflect 循环等。老旧的context字段已废弃会与query拼接处理。两个端点都接入run_cancellable_on_disconnect客户端断连时引擎会在各阶段边界检查请求上下文并中止被遗弃的工作而不是跑完issue #2122。为什么他们重写了客户端集成始于vectorize-io/hindsight-clientSDK最终被替换为手写的 fetch 客户端。client.ts的头部注释道出了原因用亲手编写的 fetch 调用取代vectorize-io/hindsight-clientSDK这样我们只依赖实际使用的 API 端点retain、retainBatch、recall、reflect、bank 与文档管理、以及批量列表。在此集中构建所有请求为测试提供单一的可 spy 接缝。这是任何深度投入的集成都会做的事——一个真实交付的产品希望最小化依赖面并完全掌控重试、超时与测试接缝。关键细节是他们没有改变 API 契约只是直接对接同样的端点。这是对 API 设计的一份安静的信任票它足够干净值得被内部化。从本仓库的服务端看这份可内部化的判断确有依据核心端点集中、路由清晰/v1/default/banks/{bank_id}/memories、/memories/recall、/reflect、/memories/{memory_id}及其 history、/memories/list、/memories/dry-run-extract等均在 api/http.py 的集中路由表中注册客户端只需薄薄一层 HTTP 封装即可对接。这对 Hindsight 上的编码 Agent 意味着什么退一步看oh-my-pi 的集成形态与 Hindsight 生态中其他编码 AgentHermes、Claude Code、OpenClaw高度相似向模型暴露 retain / recall / reflect 三个工具首轮自动 recall、会话结束自动 retain按项目做 bank 作用域风格各异用心智模型式摘要缓存换取首轮低延迟上下文防抖 retain 队列让模型的retain调用不阻塞回合。这不是巧合。它是对代码助手有效的形态——而 Hindsight 的 API 正是围绕这个生命周期设计的。oh-my-pi 的实现是该模式最详尽的公开参考其packages/coding-agent/src/hindsight/目录下的源码可以直接借鉴。动手尝试oh-my-pi 一条命令即可安装macOS / Linux 使用官方安装脚本Windows 使用 PowerShell 安装脚本或通过 Bun 全局安装oh-my-pi/pi-coding-agent。安装后指向 Hindsight Cloudexport HINDSIGHT_API_URLhttps://api.hindsight.vectorize.io export HINDSIGHT_API_TOKENhsk_your_token或者使用应用内设置hindsight.apiUrl、hindsight.apiToken、hindsight.scoping。默认的per-project-tagged作用域会自动生效心智模型种子会在每个 bank 的首次会话时触发。你也可以注册免费的 Hindsight Cloud key或自托管 Hindsight 并将 oh-my-pi 指向http://localhost:8888——两条路使用完全相同的 API 面。若想深入理解服务端行为可对照本仓库的 HTTP 端点实现 与 心智模型刷新引擎 逐一验证上文提到的每个参数与生命周期细节。【免费下载链接】hindsightHindsight: Agent Memory That Learns项目地址: https://gitcode.com/GitHub_Trending/hindsight2/hindsight创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考

读完文章,也想定制专属网站?

尧图设计师 24 小时内与您沟通定制方案

免费获取报价