让 pi coding agent 记得住当前在做什么

识别新的工作需求,记成 topic;每轮对话前,把相关 topic 的上下文静默注入给 agent。

单文件扩展(index.ts 约 2600 行) · peer 依赖 @earendil-works/pi-coding-agent

台账里最小的记忆单位:一条 topic。每轮 before_agent_start 把相关 topic 压成不超过 10 行的卡片静默注入。

特点与取舍

它监听你的输入:发现新的工作需求,就记录为一个 topic;每轮 agent 启动前,把与当前工作相关的 topic 上下文注入进去。整个过程不打断你,也不阻塞 agent 回复。

零阻塞

input handler 立即返回,分类在后台完成,agent 回复从不等它。

热路径优先

会话已有 active topic 且相似度 ≥ 0.4 时,亚毫秒同步判定,多数轮次不调用 LLM、不消耗 token。

静默注入

before_agent_start 毫秒级取出槽位,上下文自动到位,不打断用户。

双层兜底

分类模型超时或失败,依次切换到 fallback 模型、会话当前模型。

可配置

/topics-config 选择分类模型、fallback 模型和 thinking 级别;级别列表由模型元数据动态决定。

设计取舍:新需求第一次走 LLM 分类,需要 5 到 15 秒,通常赶不上当轮注入。及时性由热路径兜底,续聊轮次不缺上下文。

交互式 TUI

两条命令在交互式 TUI 里运行,选择列表即时渲染。终端保持深色:终端本来就是深色的。

两条命令各自在独立的终端窗口里运行,选择列表即时渲染;扩展自身的注入默认静默,日志写 topic-memory.log。

「蒸馏」就是 /topics 列表里的「蒸馏: off(点击切换)」开关。默认关闭;开启后,每个 agent 回合结束时,若本轮捕获了新的自动记录,就用一次 LLM 调用把这段结果压成一句话(不超过 50 字),回写更新到对应 topic 记录,台账保持精简和实时。

蒸馏非阻塞:先存原文,结果就绪后原位替换,15 秒超时,失败保留原文。代价是每轮多一次 LLM 调用。

设计:只记结论,不记过程

topic 是这条扩展记忆的基本单位:一个被识别出来的工作需求。和其他记忆插件不同,topic-memory 只做三件事:把输入分类归纳成 topic,只记 topic 的基础信息和结论,在需要的地方把相关 topic 的简洁信息注入给 LLM。不记过程记忆,是为了避免低质量记忆和无用过程日积月累,污染 context window。

一条 topic 记录

输入被分类器判定为工作需求后(输出 {isRequirement, type, title, summary, tags}),落盘成一条 topic,存在 ~/.pi/agent/topic-memory.json。下面是一条记录的完整结构,示例值仅供示意:

topic-memory.json · 单条 topic(示意)
{
  "id": "stats-page",
  "title": "给服务加 web 统计页面",
  "type": "feature",
  "tags": ["statistics", "web"],
  "derivedFrom": "",
  "links": [],
  "status": "in_progress",
  "created": 1754290000000,
  "lastUpdated": 1754290000000,
  "project": "/home/dev/github/pi-topic-memory",
  "decisions": [
    { "at": 1754290000000, "text": "收到新进展/新需求:给服务加一个 web 页面显示统计信息" }
  ],
  "outcome": "",
  "source": {
    "firstSeen": "给服务加 web 统计页面",
    "firstSession": "sess-abc123"
  }
}

整个台账文件是 {version, config, topics[]} 三部分:config 记录分类模型、fallback 模型、thinking 级别、注入显示模式和蒸馏开关;topics 是上面的记录数组。

身份与类型

id
由 title 生成:小写、非字母数字转连字符(中文被过滤),空标题回退 t-<时间戳>;重名追加 -2、-3
title
≤25 字中文短标题,分类器生成,去掉「请/帮我/我们」等客套前缀
type
需求类型:feature / bug / upgrade / refactor / other
tags
1-3 个关键词(技术栈或领域),去重、小写化,非法输入降级为空数组

关联与归属

derivedFrom
父 topic 的 id:会话已有 active topic 时,新需求自动挂到它下面
links
手动关联的 topic id 列表,双向(写入和删除对称)
project
创建时的 cwd(项目目录),同项目匹配时加分

状态与时间

status
状态:in_progress ▶ / blocked ⏸ / done ✓ / dropped ✕
created
创建时间戳(毫秒)
lastUpdated
最后更新时间戳(毫秒),每次命中都刷新

记录内容

decisions
进展与决策记录 {at, text}[],每条 ≤200 字符;回合结束蒸馏(默认关)把本轮记录压成一句话回写更新
outcome
最终结论,≤500 字符;识别到完成时自动写入并置 done,也可手动写

首次来源

source
首次来源 {firstSeen, firstSession}:识别出该需求的输入文本与会话名

和其他记忆插件的区别

1

分类归纳 topic

每条输入先过前置过滤门,命令前缀、问候、续聊、重复消息直接跳过,不耗 LLM。剩下的按相似度匹配现有 topic,没命中再交给 LLM 分类;确认是新工作需求,就归纳成一条 topic,记录类型、标题、一句话摘要和 tags,已有相似 topic 则并入。

2

只记基础信息和 decision核心

topic 保存标题、类型、状态、所属项目、时间戳这些基础信息,以及进展决策(decisions)和最终结论(outcome)。不保存逐轮原始对话,不做过程记忆。

3

在需要的地方注入

每轮 agent 启动前(before_agent_start),把与当前工作相关的 topic 压成一张不超过 10 行的简洁卡片注入上下文,LLM 拿到的是当前在做什么的摘要,不需要再读一遍历史对话。

要避免的问题

传统记忆插件

把每轮对话都记下来,一开始很有用;日积月累,大量低质量记忆和无用过程记忆堆进 context window,越用越慢、越用越乱。根因是低质量记忆和无用过程记忆堆积。

topic-memory 的对应做法:只存结论

进得少 只有被分类确认的需求才入台账。
存得短 每条决策蒸馏成一句话,长度有上限。
注入有选择 每轮只给相关 topic 的简洁卡片。

结果:context 里始终只保留当前需要的那点信息。

架构

一条消息只走一条路径。前置过滤门免 LLM 拦下命令前缀、问候、续聊和重复消息;通过的输入按相似度分流:命中 active topic 走热路径,亚毫秒完成判定;否则走冷路径,交给 LLM 分类。判定写入注入槽位,before_agent_start 毫秒级取出并静默注入,agent 始终知道当前在做什么。

跳过 超时/失败 超时/失败 读取 active topic 新需求 → 记入台账 回合结束:蒸馏(默认关) 用户输入 新需求 / 续聊 / 问候… input 事件 fire-and-forget 前置过滤门 免 LLM · 四类输入直接跳过 /、! 命令前缀 问候 / 过短 续聊语句 重复消息去重 热路径 相似度 ≥ 0.4 · 亚毫秒判定 零 LLM · 零 token 冷路径 主分类模型 · LLM 分类 5-15s · 30s 超时 fallback 模型 会话模型 输出 JSON 判定 注入槽位 per-session 新判定到达 旧槽位作废(null) before_agent_start 非阻塞取槽位 · 毫秒级 静默注入 不打断用户 agent 上下文 始终知道在做什么 topic 台账 active topic tags decisions links ~/.pi/agent/topic-memory.json store: {version, config, topics[]}
注入的时机与目的

时机:每轮 agent 启动触发 before_agent_start,非阻塞地取注入槽位;只有槽位里已有完成的分类结果才注入。还没分类完就跳过、留到下一轮,绝不等待,注入的是上一轮或更早识别出的 topic,当轮新输入的分类通常赶不上当轮注入。

目的:把「当前在做什么」的工作 topic 上下文交给 agent,让它跨轮记住任务,用户不用每轮重复交代背景。

热路径:相似度 ≥ 0.4,亚毫秒判定,不调用 LLM 冷路径:LLM 分类 5 到 15 秒,超时或失败切 fallback 模型、再切会话模型;输出判定 JSON:{isRequirement, type, title, summary, tags} 台账读写:读取 active topic、记录新需求、回合结束蒸馏更新 同一条消息只走一条路径;动画每 7.5 秒循环演示快(热)、慢(冷)、被过滤三种节奏。

使用

安装

shell
$ pi install git:github.com/fan56/pi-topic-memory

命令

/topics 列出、切换、tag 过滤、关联、删除 topic
/topics-config 选择分类模型、fallback 模型和 thinking 级别;级别列表由模型元数据动态决定

配置保存在 ~/.pi/agent/topic-memory.json,单文件,可直接编辑。源码见 GitHub