让 pi coding agent 记得住当前在做什么
识别新的工作需求,记成 topic;每轮对话前,把相关 topic 的上下文静默注入给 agent。
单文件扩展(index.ts 约 2600 行) · peer 依赖
@earendil-works/pi-coding-agent
id: stats-page
title
给服务加 web 统计页面
tags
decisions 1/8
decision
收到新进展/新需求:给服务加一个 web 页面显示统计信息
台账里最小的记忆单位:一条 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 里运行,选择列表即时渲染。终端保持深色:终端本来就是深色的。
$ /topicstopics选择 topic 查看详情,Esc 退出。▸ [▶] 给服务加 web 统计页面 — feature · 3天前 [✓] 修 npm 依赖冲突 — bug · 2小时前 注入显示: silent(点击切换) 蒸馏: off(点击切换) 清除全部 topic(2)
$ /topics-configtopics-config → 分类模型选择 topic 意图分类使用的模型。Esc 退出(不保存)。▸ ✓ 跟随当前会话 (默认) anthropic/claude-sonnet-4 deepseek/deepseek-chatblanktopics-config → 思考强度分类模型: anthropic/claude-sonnet-4fallback 模型: 不配置选择思考强度。Esc 返回 fallback 模型选择。▸ ✓ 跟随当前会话 (默认) off minimal low medium high
两条命令各自在独立的终端窗口里运行,选择列表即时渲染;扩展自身的注入默认静默,日志写 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。下面是一条记录的完整结构,示例值仅供示意:
{ "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}:识别出该需求的输入文本与会话名
和其他记忆插件的区别
分类归纳 topic
每条输入先过前置过滤门,命令前缀、问候、续聊、重复消息直接跳过,不耗 LLM。剩下的按相似度匹配现有 topic,没命中再交给 LLM 分类;确认是新工作需求,就归纳成一条 topic,记录类型、标题、一句话摘要和 tags,已有相似 topic 则并入。
只记基础信息和 decision核心
topic 保存标题、类型、状态、所属项目、时间戳这些基础信息,以及进展决策(decisions)和最终结论(outcome)。不保存逐轮原始对话,不做过程记忆。
在需要的地方注入
每轮 agent 启动前(before_agent_start),把与当前工作相关的 topic 压成一张不超过 10 行的简洁卡片注入上下文,LLM 拿到的是当前在做什么的摘要,不需要再读一遍历史对话。
要避免的问题
传统记忆插件
把每轮对话都记下来,一开始很有用;日积月累,大量低质量记忆和无用过程记忆堆进 context window,越用越慢、越用越乱。根因是低质量记忆和无用过程记忆堆积。
topic-memory 的对应做法:只存结论
结果:context 里始终只保留当前需要的那点信息。
架构
一条消息只走一条路径。前置过滤门免 LLM 拦下命令前缀、问候、续聊和重复消息;通过的输入按相似度分流:命中 active topic 走热路径,亚毫秒完成判定;否则走冷路径,交给 LLM 分类。判定写入注入槽位,before_agent_start 毫秒级取出并静默注入,agent 始终知道当前在做什么。
时机:每轮 agent 启动触发 before_agent_start,非阻塞地取注入槽位;只有槽位里已有完成的分类结果才注入。还没分类完就跳过、留到下一轮,绝不等待,注入的是上一轮或更早识别出的 topic,当轮新输入的分类通常赶不上当轮注入。
目的:把「当前在做什么」的工作 topic 上下文交给 agent,让它跨轮记住任务,用户不用每轮重复交代背景。
{isRequirement, type, title, summary, tags}
台账读写:读取 active topic、记录新需求、回合结束蒸馏更新
同一条消息只走一条路径;动画每 7.5
秒循环演示快(热)、慢(冷)、被过滤三种节奏。
使用
安装
$ pi install git:github.com/fan56/pi-topic-memory
命令
/topics |
列出、切换、tag 过滤、关联、删除 topic |
/topics-config |
选择分类模型、fallback 模型和 thinking 级别;级别列表由模型元数据动态决定 |
配置保存在
~/.pi/agent/topic-memory.json,单文件,可直接编辑。源码见
GitHub。