@youkale/pi-hermes-memory

🧠 Persistent memory + 🔍 session search + 🛡️ secret scanning for Pi. Token-aware policy-only memory by default, SQLite FTS5 search, auto-consolidation, procedural skills. 368 tests. Ported from Hermes agent.

Packages

Package details

extension

Install @youkale/pi-hermes-memory from npm and Pi will load the resources declared by the package manifest.

$ pi install npm:@youkale/pi-hermes-memory
Package
@youkale/pi-hermes-memory
Version
0.7.15
Published
Aug 3, 2026
Downloads
97/mo · 97/wk
Author
youkale
License
MIT
Types
extension
Size
783.1 KB
Dependencies
2 dependencies · 1 peer
Pi manifest JSON
{
  "extensions": [
    "./src/index.ts"
  ]
}

Security note

Pi packages can execute code and influence agent behavior. Review the source before installing third-party packages.

README

Pi Hermes Memory

Pi Hermes Memory 是一个 Pi Coding Agent 扩展,为 Pi 增加跨会话持久记忆、会话搜索、后台学习、过程型技能和技能治理能力。安装后,代理可以把稳定事实写入本地存储,在后续会话按需搜索,而不是把所有历史内容塞进系统提示词。

当前包版本: 0.7.15

快速开始

pi install npm:pi-hermes-memory

从 GitHub 安装:

pi install git:github:chandra447/pi-hermes-memory

本地开发或直接运行当前 checkout:

pi -e ./src/index.ts

常用初始化命令:

/memory-index-sessions
/memory-sync-markdown
/memory-interview
/learn-memory-tool

这个扩展做什么

能力 当前行为
持久记忆 将用户画像、全局记忆、项目记忆和失败经验写入本地 Markdown 与 SQLite 搜索库;replace 的旧值在 SQLite 保留为 superseded 历史,项目与全局/组织层之间可显式 promote/demote
默认低 token 注入 默认 memoryMode: "policy-only",系统提示词只注入记忆使用策略,具体内容由工具按需搜索
项目级记忆 基于 cwd 或配置识别项目,在平面 Markdown 文件中写入真实项目归属并默认隔离
会话搜索 /memory-index-sessions 建索引后,LLM 可用 session_search 搜索历史 Pi 会话
记忆搜索 memory_search 默认使用 FTS;显式配置 embedding provider 后,在既有 scope 内执行 FTS + 进程内余弦混合检索
后台学习 按用户轮次或工具调用数触发后台 review,保存值得复用的事实和项目技能
修正捕获 用户纠正代理时可立即触发保存,避免反复犯同一类错误
自动整理 记忆接近容量时先在同 scope 内离线预聚类近重复,再由子进程按组合并、替换或压缩
技能系统 通过 Pi 原生 SKILL.md 保存过程型知识,支持全局和项目两个作用域
技能治理 记录真实 skill.use 证据,支持项目技能晋升、降级、隐藏和统计
内容扫描 记忆和技能写入前经过扫描,拦截密钥、prompt injection、角色劫持和隐形字符等风险

数据目录

agentRoot 默认是 ~/.pi/agent,也可以通过环境变量整体切换:

PI_CODING_AGENT_DIR=/custom/pi-agent pi

默认布局:

路径 用途
<agentRoot>/hermes-memory-config.json 配置文件
<agentRoot>/pi-hermes-memory/MEMORY.md 全局代理记忆
<agentRoot>/pi-hermes-memory/USER.md 用户画像和偏好
<agentRoot>/pi-hermes-memory/failures.md 失败、修正、洞察、约定、偏好和工具怪癖
<agentRoot>/pi-hermes-memory/sessions.db SQLite 会话索引、记忆 FTS、普通 BLOB 嵌入向量与 supersede 历史、技能统计(含 skill_load_dailyskill_load_events 保留历史数据,只读不再写入;按 skillGovernance.loadEventRetentionDays 机会式 TTL 清理,见下文)和治理状态
<agentRoot>/pi-hermes-memory/skills/<slug>/SKILL.md 扩展管理的全局技能
<agentRoot>/projects-memory/MEMORY.md 项目级记忆平面文件;每个受管条目通过 project=<项目名> 元数据记录真实归属
<agentRoot>/projects-memory/<project>/skills/<slug>/SKILL.md 项目级技能
<agentRoot>/sessions/ Pi 会话 JSONL,供会话索引读取

如果配置了 memoryDir,SQLite 文件实际位于 <memoryDir>/sessions.db

如果从旧版本升级,扩展会在启动时尽力迁移旧目录:

旧位置 新位置
<agentRoot>/memory <agentRoot>/pi-hermes-memory
<agentRoot>/pi-hermes-memory/skills/*.md <agentRoot>/pi-hermes-memory/skills/<slug>/SKILL.md
<agentRoot>/<project>/MEMORY.md <agentRoot>/projects-memory/MEMORY.md
<agentRoot>/projects-memory/<project>/MEMORY.md <agentRoot>/projects-memory/MEMORY.md

项目记忆合并完成后,来源文件会被改名为 MEMORY.md.migrated(内容保留),避免下次启动重复合并、复活已在平面文件中删除的条目;合并时会按来源目录补写真实 project 元数据,已有归属保持不变。/memory-sync-markdown 不会把 retired 来源导回 SQLite;它逐条读取 projects-memory/MEMORY.md 的归属字段,以真实项目名回填和对账。无归属项目条目会保留在 Markdown 中,但跳过 SQLite 并报告 warning/skipped,不会被自动改写或淘汰。

项目条目元数据格式为:

正文 <!-- created=2026-07-29, last=2026-07-29, project=my-project -->

全局 MEMORY.mdUSER.md 不写 project 字段。failures.md 在项目会话中写真实归属;非项目会话产生的无归属 failure 是全局失败,对所有项目可见。 failure 的容量统计、FIFO 淘汰和自动整理输入只包含当前项目条目与无归属全局 failure;其他项目的 failure 不会被当前会话自动整理或删除。该维护边界不受 projectMemorySharing 影响。

记忆模型

默认模式是 policy-only。启动时系统提示词会得到一段 <memory-policy>,告诉代理什么时候调用 memory_searchsession_searchmemoryskill。完整 Markdown 记忆默认不注入系统提示词,这样可以减少首轮 token 占用,也能避免旧记忆直接覆盖当前用户请求。

如需兼容旧行为,可以设置:

{
  "memoryMode": "legacy-inject"
}

legacy-inject 会把全局记忆、用户画像、当前可见的项目记忆和近期可见失败记忆用 <memory-context> 边界块注入系统提示词。默认只注入当前项目条目;无归属 failure 作为全局失败注入。即使在这个模式下,当前用户请求、仓库文件和工具输出也应优先于旧记忆。

斜杠命令

命令 用途
/memory-insights 查看当前全局、用户、失败和项目记忆概览
/memory-skills 打开技能管理界面,查看全局、项目和外部加载的技能
/memory-skill-governance 管理项目技能到全局技能的治理状态
/memory-skill-visibility 按项目隐藏、显示或重置全局技能可见性
/memory-skill-stats 查看 resources_discover 记录的技能加载和曝光统计(sinceDays 按 UTC 天粒度对齐)
/memory-review-status 查看当前进程内的后台记忆 review 计数器和最近一次 review 状态
/memory-doctor 只读检查 Markdown/SQLite 漂移、会话索引、嵌入覆盖率,并报告项目×项目及项目×全局的跨 scope 近重复候选;只报告不动作
/memory-consolidate 手动触发全局、用户和项目记忆整理
/memory-interview 通过问答预填用户画像
/memory-learn-skill 让当前代理把近期上下文或来源材料沉淀为项目优先的技能
/memory-switch-project 按真实归属列出各项目条目数和无归属计数;当前项目仍由 cwd 或配置决定
/memory-index-sessions 将历史 Pi 会话导入 SQLite 会话搜索索引
/memory-sync-markdown 将已有 Markdown 记忆回填到 SQLite 记忆搜索库
/memory-preview-context 预览当前会注入系统提示词的记忆策略或 legacy 记忆块
/learn-memory-tool 显示扩展的使用说明和排障提示

skill_load_events 表已改为只读历史留存,不再写入;仅用于回填和核对。已知边界:若回退到旧版后又升级,旧版期间新增的 raw 增量不会再次回填进入 skill_load_daily 聚合统计,但 raw 数据会被完整保留。

每次 resources_discover 写入 skill_load_daily 之后,会机会式检查是否需要清理过期行:距上次清理 ≥24 小时(或从未清理过)才会真正执行一次,skillGovernance.loadEventRetentionDays(默认 180 天)之前的 skill_load_dailyskill_load_events 行会被删除;loadEventRetentionDays: 0 完全禁用清理(连清理时间戳都不更新)。每张表单次最多删除 skillGovernance.loadEventCleanupBudget(默认 500)行,避免长事务;一次删不完的部分留到下一次到期的 24 小时窗口继续删,但当前这一轮仍会记录清理时间,防止繁忙项目在同一天内反复触发删除。清理失败会被隔离并仅打印警告,不影响 resources_discover 主流程;不会在扩展启动路径上运行。skill_usage_events 永远不受清理影响。

/memory-skill-governance 支持这些动作:

/memory-skill-governance status
/memory-skill-governance curate
/memory-skill-governance explain <skill_id>
/memory-skill-governance promote <skill_id>
/memory-skill-governance demote <skill_id>
/memory-skill-governance restore <skill_id>
/memory-skill-governance clear <skill_id>
/memory-skill-governance reload

/memory-skill-visibility 支持:

/memory-skill-visibility list [--project <project>]
/memory-skill-visibility hide <global_skill_id...> [--project <project>]
/memory-skill-visibility show <global_skill_id...> [--project <project>]
/memory-skill-visibility reset <global_skill_id...> [--project <project>]

隐藏、显示、晋升、降级或恢复技能后,运行 /memory-skill-governance reload 或开启新会话,Pi 才会刷新技能发现结果。

/memory-skill-governance status 还会附带 Curation Jobs 段,展示当前活跃作业、最近一条作业的状态/原因码与累计 skip 计数。 手动执行 /memory-skill-governance curate 与自动触发的策展共享同一项目级租约;若有正在进行或排队的作业,手动调用会提示稍后重试。僵死作业会先被清理再尝试执行,不会永久阻塞。

LLM 工具

这些工具由扩展注册给代理使用,通常不需要用户手动调用。

工具 主要用途
memory 写入、替换、删除持久记忆,并在当前项目与全局/组织层之间显式移动
memory_search 搜索 SQLite 记忆库
session_search 搜索已索引的历史会话
skill 创建、查看、使用、更新、删除技能,并管理项目级全局技能可见性
memory_skill_stats 只读查询技能加载统计、治理状态和晋升状态
skill_governance_curate 后台或子会话用于提交结构化技能治理分类

memory

支持目标:

target 含义
user 用户画像、偏好、沟通风格和长期指令
memory 全局事实、环境信息、跨项目经验
project 当前项目的架构、命令、约定和工作流
failure 失败、修正、洞察、偏好、约定或工具怪癖;项目会话写真实项目归属

支持动作:

action 含义
add 添加新条目;同 scope active 近邻会先返回提示,force: true 可明确越过
replace old_text 匹配并替换已有条目;旧 SQLite 行保留为 superseded 历史
remove old_text 匹配并删除已有条目
promote old_text 将当前项目条目移动到全局/组织层;方向隐含,不传 target
demote old_text 将全局条目移动到当前项目;方向隐含,不传 target

failure 记忆可带 category:

failure, correction, insight, preference, convention, tool-quirk

add 落盘前会全量扫描写入目标的同 scope active own 行(与 FTS 无关),再以保守的 token-set Jaccard 阈值在代码内评分,提示最多返回 3 个近邻。无空格的 Han、Hiragana、Katakana 和 Hangul 内容按 Unicode 码点 bigram 比较,包括增补平面字符;空格词 token 与中英混排同时支持。启用有效 embedding provider 后,如果新内容与候选行都有当前 provider/model 的有效向量,则余弦相似度 >= 0.90 或既有 token 判据任一命中都会拦截;任一侧缺少有效向量时仍只使用原 token 判据。项目写入只检查当前项目 own 行;foreign、无归属项目行、superseded 行和其他 target 都不参与,projectMemorySharing 不会放宽这个写入面。failure 不做近邻拦截。命中时本次不写入,并返回候选正文以及 replace / force: true / 放弃三种行动提示;provider 失败或超时会静默回落,写入不受影响,全程不调用 LLM。

force 只越过近邻提示。精确重复仍先按既有行为返回成功 NOOP,force: true 也不会创建精确副本。

operations 批量写入支持 memoryuserproject,每个 add op 也可带 force,但同一批不能混用项目记忆和全局/用户记忆。命中近邻的 add op 会带结构化提示失败,其余 op 继续按部分提交语义执行。容量触发自动整理时,单条 add 与 batch 都会在整理成功、重新加载文件后以新鲜候选视图复检;batch 保留原始 operation 坐标并重新执行顺序模拟,整理新产生的近邻不会绕过拦截。force: true 的 add 不做该近邻复检。

replace 不改变 Markdown 文件契约:MEMORY.mdUSER.mdfailures.md 只保留当前 active 条目。SQLite 会插入新的 active 行,并把旧行的 superseded_by 指向新行、superseded_at 记为当天;连续 replace 可形成 A→B→C 多级链。replace 合并到同 scope 已有相同正文时会指向该 canonical active 行,不产生 active 重复。remove 与 FIFO eviction 仍物理删除对应 active 镜像,不创建 supersede 节点。

promote / demote 是单条目的显式分层移动,不是复制,也不接受 targetoperations batch 会明确拒绝这两个 action,并提示改为单发。两者都要求活动项目:promote 从项目层定位源,匹配范围与 project replace 完全相同(默认仅 own;projectMemorySharing: true 时可显式匹配可见 foreign 条目);demote 从无归属的全局 memory 层定位源,并写入当前项目。userfailure 不参与。容器部署中全局层就是组织层,因此同一动作也覆盖项目↔组织的分层。

文件移动固定按防丢失顺序执行:先在目标 store 的写锁内写目标,再在源 store 的写锁内精确删除先前定位的源身份。目标正文不变,created 保留,last 刷新为当天;promote 后去掉 project=,demote 后写入当前项目归属。正常完成后同文 active 条目只存在于目标层。若目标已写而源删除失败,工具返回成功结果和明确 warning,并保留可能的双层副本;进程崩溃也至多留下这类可恢复双存窗口,不会因执行顺序造成两层皆无。可用 /memory-doctor/memory-sync-markdown 检查并再次处置。

目标层判定与 add 同构:精确同文优先退化为 merged,只删除源文件条目,并在 SQLite 把源 active 行 supersede 到目标 canonical 行;canonical 的 created 保持不变,Markdown last 与 SQLite last_referenced 都刷新为当天。非同文近邻会阻断且返回最多三个目标层候选,force: true 只越过该近邻提示。近邻范围是目标层 active own 集(promote 为全局集,demote 为当前项目 own 集),只使用 token 判据和双方已有的当前模型向量,移动路径从不调用 embedding provider。目标层容量、FIFO 和 auto-consolidate 行为也复用该层 add;整理成功重载后会再次检查目标近邻,不能绕过单调复检。

常规移动的 SQLite 镜像不会 delete+insert:它在源行上就地更新 project、保留行 id、既有 supersede 祖先指向以及 embedding/embedding_model,同步 created 并刷新 last_referencedmemories_au 会照常维护 external-content FTS;正文未变。Markdown 始终是权威源,镜像更新失败只附带 warning,不回滚已经安全落盘的文件移动。

自动整理

容量触发和 /memory-consolidate 使用相同的整理输入边界:全局/用户只处理各自 scope,项目只处理当前项目 own 条目,failure 只处理当前项目与无归属全局 failure;projectMemorySharing 不会扩大自动维护范围。父进程在启动整理子进程前,以纯代码对同 scope 条目做 O(n²) 近重复预聚类:

  • 双方都有当前 provider/model 的已存有效向量时,用进程内余弦,>= 0.82 进入同一候选簇。
  • 任一侧没有当前有效向量时,复用写入近邻的保守 token-set Jaccard/CJK Unicode 码点 bigram 判据。
  • 相似边组成连通分量;多条分量按“近重复簇”呈现,单例归入“未分组”。没有任何簇时,条目正文与 § 分隔保持原格式。

例如子进程可能看到:

[Near-duplicate cluster 1]
项目构建命令要求每次代码合并之前运行完整类型检查
§
项目构建命令要求每回代码合并之前运行完整类型检查

[Ungrouped entries]
发布前确认版本号

聚类只结构化输入,不强制子进程决策;提示词要求优先组内合并、跨组慎并,未分组仍按原整理规则处理。实际落盘继续调用既有 memory tool 的 replace/add/remove 路径,因此 replace 会保留 supersede 链,身份唯一、近邻复检和 batch generation/operation 坐标守卫均不变。

整理全路径不会调用 embedding provider,也不会生成或回填向量。 它只读取 SQLite 中已经存在且匹配当前模型的向量;provider 关闭、数据库缺失或条目没有有效向量时仍可用 token 判据离线整理。

memory_search

memory_search 自动绑定运行时当前项目。默认 projectMemorySharing: false 时,只返回当前项目行和 project IS NULL 的全局行;failure 因而是当前项目 + 全局失败。设置 projectMemorySharing: true 后,搜索可返回全部项目,但任何新写入仍记录真实项目归属。

参数 含义
query 搜索词或自然语言查询
target 可选,memoryuserfailure
category 可选,仅用于失败类分类
limit 默认 10,最大 20
include_superseded 默认 falsetrue 时同时返回 superseded 历史,并标出 superseded_by 目标和日期

默认搜索和统计只计算 superseded_by IS NULL 的 active 行;include_superseded 只叠加历史可见性,不改变当前项目/global/sharing 的 scope 谓词。/memory-sync-markdown 回填和对账也只比较 active 行:历史不会因 Markdown 中不存在而被删除,也不会被当成缺失事实重新导入。/memory-doctor 的一致性比较使用 active 行,并在每个 target 旁单列 superseded 数量。

embedding 默认完全关闭,此时 memory_search 直接走原 FTS 路径,结果和格式不变。显式配置并成功读取 API key 后,检索流程如下:

  1. 先应用现有 current-project/global/sharing、target、category、active/include_superseded 谓词,得到候选集;向量排序永远不会越过该边界。
  2. 查询文本生成向量;本次 scope 内没有当前模型有效向量的候选,最多批量回填 16 条。失败项保持 NULL,以后搜索可再次机会性回填,不做启动期全量任务。
  3. FTS 命中序与候选集余弦相似序用 RRF(k=60)融合;没有向量的行仍可通过 FTS 召回。

因此 projectMemorySharing: false 时,项目 A 不会因为语义相似召回项目 B 的行;failure、global 和 superseded 的可见性也仍由原谓词决定。查询向量失败、网络失败、超时或部分条目失败都会静默降级:查询回到 FTS-only,成功的记忆写入照常完成,失败向量留空。向量模型标识是 openai-compatible/<model>;行上的 embedding_model 与当前标识不全等时按 NULL 处理,并在后续搜索中机会性重嵌。

向量以 Float32Array 的普通 SQLite BLOB 保存,余弦在 JS 进程内计算。本实现不加载 sqlite-vec、ONNX runtime 或其他原生向量扩展,也没有为 embedding 增加 npm 依赖。

/memory-doctor

/memory-doctor 保持只读,并在既有目录、Markdown/SQLite 一致性、active/superseded、嵌入覆盖率和会话索引段之间新增 Cross-scope near-duplicate candidates (read-only) 段。这个分析是默认项目隔离之外的诊断视图,只读取全库 active target=memory 行:

  • 项目 A × 项目 B 命中时报告“升级候选:或属组织层通用知识”。
  • 项目 × 全局命中时报告“疑似重复”,建议人工判断去其一,或把不适合全局的知识下沉到项目。
  • userfailure、同 scope 对和 superseded 历史不参与。

判据与整理预聚类一致:双方有当前模型的已存有效向量时使用余弦 >= 0.82,否则使用既有 token/Jaccard/CJK bigram 判据;doctor 同样不会调用 provider 或回填向量。扫描前只为每行计算一次 token 集并引用一次当前有效向量,随后最多比较 250,000 个跨 scope 对;达到预算时停止并明确提示结果可能不完整。每个候选列出两个 scope、截断后的条目摘录、cosine/token 来源、相似度和建议方向,计数明确表示已扫描对中的总检出数;报告最多渲染前 50 条,超出时明确提示截断。完整扫描的空结果显示 无跨 scope 近重复;预算早停且零命中时只说明已扫描范围未发现,未扫描部分保持未知。

Cross-scope near-duplicate candidates (read-only)
Total candidates: 2 (detected in scanned pairs)
1. project:service-a x project:service-b [token=1.000]
   suggestion: 升级候选:若当前项目条目已证明组织内普适,用 memory promote 上移。
2. project:web x global [cosine=0.934]
   suggestion: 疑似重复:可用 memory promote 合并到全局,或用 demote 将仅属当前项目的全局条目下沉。

当数据量触发资源边界时,同一段会附加:

已达比较预算,结果可能不完整(708 行/250000 对上限;已比较 250000 对)
候选输出已截断:显示前 50 条,共检出 250000 条。

该段只报告不动作:doctor 不会自动改写条目。人工确认方向后,可单独调用 memorypromote / demote action 处置;项目×项目候选需先切换到要上移条目所属的当前项目。

session_search

默认 sessionSearch.variantlegacy,参数为 queryprojectrolelimit。在设置 sessionSearch.variant: "anchors" 后,工具改为接收一个 Markdown 请求,返回 JSONL 源文件范围锚点。

skill

支持动作:

create, view, use, patch, update, edit, delete, write_file, remove_file, visibility

关键规则:

规则 当前行为
create 必须传 scope global 用于可迁移流程,project 用于依赖当前仓库路径、脚本、架构或发布方式的流程
view 是只读 只查看或列出技能,不记录真实使用
use 会记录使用证据 项目技能晋升依赖主会话里的真实 skill.use
结构化写入优先 推荐传 when_to_useprocedure_stepspitfallsverification_steps
支持文件有白名单 write_fileremove_file 只能操作 references/templates/scripts/assets/ 下的支持文件
子会话限制更严 子 prompt 只能查看或改写项目技能,不能 usedeletevisibility 或创建全局技能

配置

配置文件默认位于:

~/.pi/agent/hermes-memory-config.json

完整字段示例:

{
  "memoryMode": "policy-only",
  "memoryPolicyStyle": "full",
  "memoryPolicyCustomText": "<memory-policy>Custom policy text.</memory-policy>",
  "memoryCharLimit": 5000,
  "userCharLimit": 5000,
  "projectCharLimit": 5000,
  "projectMemorySharing": false,
  "nudgeInterval": 10,
  "reviewRecentMessages": 0,
  "reviewEnabled": true,
  "reviewSkillsEnabled": true,
  "flushOnCompact": true,
  "flushOnShutdown": true,
  "flushMinTurns": 6,
  "flushRecentMessages": 0,
  "memoryDir": "pi-hermes-memory",
  "projectsMemoryDir": "projects-memory",
  "projectName": "my-project",
  "sessionSearch": {
    "variant": "legacy"
  },
  "embedding": {
    "provider": "off"
  },
  "llmModelOverride": "provider/model-name",
  "llmThinkingOverride": "medium",
  "memoryOverflowStrategy": "auto-consolidate",
  "autoConsolidate": true,
  "correctionDetection": true,
  "correctionStrongPatterns": ["^actually,\\s+(.+)$"],
  "correctionWeakPatterns": ["^please\\s+remember\\b"],
  "correctionNegativePatterns": ["^never mind\\b"],
  "correctionDirectiveWords": ["remember", "prefer"],
  "failureInjectionEnabled": true,
  "failureInjectionMaxAgeDays": 7,
  "failureInjectionMaxEntries": 5,
  "nudgeToolCalls": 15,
  "skillReviewToolCalls": 10,
  "consolidationTimeoutMs": 60000,
  "skillGovernance": {
    "scopes": {
      "globalSkillsDir": "pi-hermes-memory/skills",
      "projectSkillsDir": "project-skills-root"
    },
    "promotionEnabled": true,
    "promotionMinSameDomainUsages": 3,
    "promotionMinSessions": 2,
    "promotionMinProjects": 1,
    "promotionTaskDomains": [
      {
        "id": "repo-workflow",
        "description": "Repository build, test, and release workflows.",
        "terms": ["build", "test", "release"]
      }
    ],
    "promotionDomainTerms": [],
    "promotionRequireGlobalSafe": true,
    "promotionConflictStrategy": "block",
    "curationTimeoutMs": 60000,
    "curationMaxConsecutiveFailures": 0,
    "autoCurationCooldownMs": 86400000,
    "autoCurationFailureCooldownMs": 3600000,
    "loadEventRetentionDays": 180,
    "loadEventCleanupBudget": 500
  }
}

不需要把所有字段都写进配置文件;缺失字段会使用默认值。上面的 projectNamellmModelOverridellmThinkingOverride、correction pattern、skillGovernance.scopes.*skillGovernance.curationTimeoutMs 都是覆盖示例,不配置时分别使用 cwd 推导、子进程默认模型/thinking、内置修正规则、默认技能目录和 consolidationTimeoutMs 回落值(示例中的 60000 并非该字段的独立默认值)。

解析和路径规则:

  • agentRoot 默认是 ~/.pi/agent;设置 PI_CODING_AGENT_DIR 后,配置文件会从 <agentRoot>/hermes-memory-config.json 读取。
  • 配置文件不存在、空文件、JSON 格式错误或读取失败时,整体回退默认配置;未知字段会被忽略。
  • 字段类型或枚举值不被识别时,该字段保留默认值。数值字段按源码类型解析,部分阈值字段要求非负数。
  • memoryDir 为空或不设置时使用 <agentRoot>/pi-hermes-memory;相对路径按当前 agentRoot 解析,~ 会展开,绝对路径会保留。
  • projectsMemoryDir 只接受 agentRoot 下的安全单层目录名;绝对路径必须位于 agentRoot 下,并会规范化为目录名。
  • memoryDirprojectsMemoryDir 解析 symlink 后的物理根目录必须互不相同(尚未创建的尾段按最深现存祖先解析);冲突的显式字段会回落默认值并产生启动 warning,/memory-doctor 也会报告仍存在的同根状态。promote/demote 在运行时复用同一物理根身份检查,发现同根会在任何写入或删除前拒绝移动;权限等解析异常保守回落到词法绝对路径判定。
  • projectName 不设置时由 cwd basename 推导;空值、... 或包含路径分隔符、,<>、换行的值会被忽略。
  • embedding.provider 只接受 "off""openai-compatible";非法 provider、URL、环境变量名、空 model 或非正 timeout 会使整段 embedding 配置回落到 off。
  • openai-compatible 默认 baseUrlhttps://api.openai.com/v1、默认 modeltext-embedding-3-small、默认 timeoutMs3000apiKeyEnv 必须指名一个环境变量;未配置名称或该变量没有值时 provider 视同 off,并由 /memory-doctor 提示。
  • autoConsolidate 只用于兼容旧配置;显式设置 memoryOverflowStrategy 时以后者为准。
  • skillGovernance.scopes.globalSkillsDirskillGovernance.scopes.projectSkillsDir 的相对路径同样按 agentRoot 解析;配置项目技能根目录后,活动项目技能位于 <root>/<project>/skills

当前配置字段:

字段 默认值 说明
memoryMode "policy-only" "policy-only""legacy-inject"
memoryPolicyStyle "full" "full""compact""custom""none"
memoryPolicyCustomText 未设置 memoryPolicyStyle: "custom" 时使用;空文本会回退到 compact policy
memoryCharLimit 5000 全局 MEMORY.md 字符上限
userCharLimit 5000 USER.md 字符上限
projectCharLimit 5000 每个项目 own 条目的字符上限;foreign/无归属条目不占当前项目预算,也不会被当前项目自动淘汰
projectMemorySharing false false 时项目注入、搜索、去重和显式编辑仅限当前项目;true 放宽为跨项目可见/显式编辑,但写入仍归属当前项目,自动整理和淘汰仍仅处理 own
nudgeInterval 10 每多少用户轮次触发后台 review
reviewRecentMessages 0 后台 review 读取的最近消息数,0 表示全部
reviewEnabled true 是否启用后台记忆 review
reviewSkillsEnabled true 后台 review 是否可创建或更新项目技能
flushOnCompact true compact 前是否 flush
flushOnShutdown true 会话关闭时是否 flush
flushMinTurns 6 flush 所需最少用户轮次
flushRecentMessages 0 flush 读取的最近消息数,0 表示全部
memoryDir <agentRoot>/pi-hermes-memory 全局扩展数据目录;相对路径按 agentRoot 解析,旧 <agentRoot>/memory 会迁移到新默认目录
projectsMemoryDir "projects-memory" 项目记忆根目录名,必须是 agentRoot 下安全单层目录
projectName cwd 推导 显式项目名,用于项目记忆和项目技能;不安全值会被忽略
sessionSearch.variant "legacy" "legacy""anchors"
embedding.provider "off" "off""openai-compatible";只有后者且 key 可用时启用混合检索
embedding.baseUrl "https://api.openai.com/v1" OpenAI-compatible API 根地址;请求发送到其 /embeddings
embedding.apiKeyEnv 未设置 仅从这个名称指向的环境变量读取 API key;不接受配置文件内明文 key
embedding.model "text-embedding-3-small" embedding 模型名,也是行向量有效性标识的一部分
embedding.timeoutMs 3000 单次 embedding 请求超时毫秒数,上限 30000;超时静默降级
llmModelOverride 未设置 pi -p 调用使用的模型覆盖;会 trim,空字符串忽略
llmThinkingOverride 未设置 pi -p 调用的 thinking 覆盖,支持 offminimallowmediumhighxhigh
memoryOverflowStrategy "auto-consolidate" "auto-consolidate""reject""fifo-evict"
autoConsolidate true 兼容旧配置;没有 memoryOverflowStrategy 时会映射到新字段,显式策略优先
correctionDetection true 是否检测用户修正并触发保存
correctionStrongPatterns 内置规则 覆盖强修正正则;空数组表示禁用
correctionWeakPatterns 内置规则 覆盖弱修正正则;空数组表示禁用
correctionNegativePatterns 内置规则 覆盖否定排除正则;空数组表示禁用
correctionDirectiveWords 内置规则 覆盖弱修正后的指令词;空数组表示禁用
failureInjectionEnabled true legacy 注入模式下是否注入近期失败记忆
failureInjectionMaxAgeDays 7 注入失败记忆的最大天数
failureInjectionMaxEntries 5 注入失败记忆的最大条数
nudgeToolCalls 15 工具调用数触发后台 review 的阈值
skillReviewToolCalls 10 技能专用后台 review 的工具调用阈值,0 表示关闭独立技能 review
consolidationTimeoutMs 60000 自动整理子进程超时毫秒数
skillGovernance.scopes.globalSkillsDir <memoryDir>/skills 全局技能目录
skillGovernance.scopes.projectSkillsDir 自动推导 项目技能根目录;相对路径按 agentRoot 解析,活动项目实际路径为 <root>/<project>/skills
skillGovernance.promotionEnabled true 是否启用项目技能晋升
skillGovernance.promotionMinSameDomainUsages 3 同领域真实 skill.use 次数阈值
skillGovernance.promotionMinSessions 2 真实使用跨会话阈值
skillGovernance.promotionMinProjects 1 真实使用跨项目阈值
skillGovernance.promotionTaskDomains 未设置 可配置任务领域 { id, description?, terms }id 为小写字母开头的 slug,terms 必须非空
skillGovernance.promotionDomainTerms [] 全局可迁移领域词
skillGovernance.promotionRequireGlobalSafe true 晋升是否要求被判定为全局安全
skillGovernance.promotionConflictStrategy "block" 冲突时 "block""overwrite"
skillGovernance.curationTimeoutMs 未设置,回落 consolidationTimeoutMs 治理策展(curation)子进程超时毫秒数;不设置时使用 consolidationTimeoutMs,再兜底内置默认值
skillGovernance.curationMaxConsecutiveFailures 0(0 表示不限) 自动策展连续失败次数上限,达到上限时跳过自动触发,0 保持原行为
skillGovernance.autoCurationCooldownMs 86400000(24 小时) 一次成功的自动策展后,到下一次自动触发之间的冷却毫秒数
skillGovernance.autoCurationFailureCooldownMs 3600000(1 小时) 一次策展尝试(无论成败)后,到下一次重试之间的冷却毫秒数
skillGovernance.loadEventRetentionDays 180 skill_load_daily/skill_load_events 的保留天数;0 完全禁用机会式 TTL 清理
skillGovernance.loadEventCleanupBudget 500 每张表单次机会式清理最多删除的行数,避免长事务

技能治理简述

项目技能默认保存在项目作用域。只有在主会话中真实调用 skill 工具的 use 动作,并满足同领域次数、会话数、项目数和安全规则后,才会成为晋升候选。后台 review 可以创建或改进项目技能,也可以提交治理分类,但不会直接创建全局技能、记录使用证据或改动可见性。

skill_governance_curate 提交的策展结果只能影响任务领域词和作用域策略词(同领域证据),永远不能覆盖 skillGovernance.curationTimeoutMsautoCurationCooldownMsautoCurationFailureCooldownMs 等时序参数——这些只由静态配置决定,防止策展子进程自我解除限流。

技能统计里的 load/discovery 数字只表示 Pi 在 resources_discover 时看到了这些技能,不代表任务成功,也不代表技能质量。

安全边界

写入记忆和技能前,扩展会扫描以下风险:

风险 处理
API key、token、SSH 私钥等密钥 拒绝保存
prompt injection 和角色劫持文本 拒绝或阻断
隐形 Unicode 字符 拒绝或清理
失败类记忆过宽或过旧 通过分类、搜索和整理降低噪声

搜索结果和旧记忆只是上下文,不是指令。当前用户请求、仓库文件和工具输出始终优先。

embedding API key 只会从 embedding.apiKeyEnv 指名的环境变量读取,不会从配置内接受 key。key 值和完整 baseUrl 不会写入日志、错误、doctor 报告或工具输出。建议为不同环境使用独立的最小权限变量,例如:

{
  "embedding": {
    "provider": "openai-compatible",
    "baseUrl": "https://api.openai.com/v1",
    "apiKeyEnv": "OPENAI_API_KEY",
    "model": "text-embedding-3-small",
    "timeoutMs": 3000
  }
}

开发

npm run check
npm test

本仓库的 TypeScript 入口由 Pi 通过 jiti 直接加载,运行时不需要先编译。