杠精派
派聪明
0/512

热门评论

紫色蜻蜓 2026年07月20日 15:56
引用原文:当前核心规则是:
@派聪明 maxContextWindow * 90%,之前不是有什么maxContextWindow * 80%的吗,都给我搞混了,你给我总结这些规则
点赞
回复5 取消回复
派聪明 2026年07月20日 15:56
根据你选的那段文本和整篇文章的完整描述,我来帮你梳理清楚 **PaiCLI 当前的上下文压缩规则**,只有一个统一触发线:**`maxContextWindow * 90%`**。 没有 80% 这个说法——你可能是记混了,或者看了早期的设计稿,文章里从头到尾都是 90%。 --- ## 📋 PaiCLI 当前核心规则一览 | 参数 | 规则 | 说明 | |------|------|------| | **压缩触发线(统一)** | `maxContextWindow × 90%` | 无论短期记忆压缩还是 conversationHistory 压缩,都用这个阈值 | | **短期记忆预算** | `maxContextWindow × 45%` | 短期记忆最多占窗口的 45%,达到 90% 后触发压缩 | | **长期记忆注入上限** | `min(5000, max(500, window / 200))` | 最少 500 token,最多 5000 token | | **MCP resource 索引** | `window ≥ 32k` 才开启 | 窗口太小不加载资源索引 | --- ## 🔍 具体到两种压缩场景 ### 1️⃣ Memory 系统的短期记忆压缩 - **触发时机**:每次写入用户消息/助手回复/工具结果后,立刻检查 - **判断条件**:短期记忆占用 ≥ `maxContextWindow × 45% × 90%`(即窗口的 40.5%) - **方法**:Map-Reduce 摘要压缩 ### 2️⃣ conversationHistory 压缩(发往 LLM 的真实消息列表) - **触发时机**:每轮调用 LLM **之前**主动检查 - **判断条件**:消息列表 token 数 ≥ `maxContextWindow × 90%` - **方法**:按 user 边界切分,保留最近 3 轮,更早的消息摘要后替换 --- **以具体模型算一下:** | 模型 | 窗口大小 | 压缩触发线 (90%) | 短期记忆预算 (45%) | |------|---------|-----------------|-------------------| | DeepSeek V4 | 1,000,000 | **900,000** | 450,000 | | GLM-5.1 | 200,000 | **180,000** | 90,000 | | Step / Kimi | 256,000 | **230,400** | 115,200 | > 核心结论:**当前版本所有压缩统一以 `maxContextWindow × 90%` 为触发线**,不存在 80% 的规则。文章里这句话就写在第 11 节末尾——"当前实现以 90% window 作为统一触发线"。🤝
点赞
回复 取消回复
展开剩余 4 条回复

158 条评论

呃呃呃 2026年09月07日 01:00
引用原文:三层。短期记忆、长期记忆、外部记忆。
短期记忆管理当前对话并通过Compactor 做边界感知的上下文压缩、长期记忆用 SQLite 持久化跨会话知识、外部记忆存外部知识库并支持 BM25 + 余弦相似度混合检索。我这么说可以吗,我长期记忆不想用json文件,改用sqlite@派聪明@派聪明
点赞
回复 取消回复
呃呃呃 2026年09月07日 00:55
短期记忆管理当前对话并通过Compactor 做边界感知的上下文压缩、长期记忆用 SQLite 持久化跨会话知识、外部记忆存外部知识库并支持 BM25 + 余弦相似度混合检索。我这么说可以吗,我长期记忆不想用json文件,改用sqlite@派聪明
点赞
回复1 取消回复
派聪明 2026年09月07日 00:55
可以这么说,而且用 SQLite替代 JSON 文件作为长期记忆存储是完全合理的升级,面试中反而更好讲。但有几个细节需要注意修正和补充: **1. 你的表述基本成立,但要点明设计动机** 三层架构这样讲没问题,但面试官会追问“为什么这么分”。建议加上一句:短期记忆生命周期是一次会话、长期记忆跨会话、外部记忆不常驻上下文按需检索,三层各司其职,由统一的记忆管理者在每轮请求前做检索、注入和 prompt拼装。 **2.长期记忆用 SQLite 的加分点** 相比 JSON 文件,SQLite 的优势要主动讲出来: -检索从“全量读文件 + 内存关键词匹配”变成 SQL查询,记忆量增长到几千条也不慌- 天然支持元数据列(scope、project_path、创建时间),做 project/global 双作用域过滤只需 WHERE 条件-事务保证写入原子性,避免并发写坏文件- 后续记忆量大了可以直接加 FTS5 做 BM25,或加向量列做语义检索,平滑演进成混合检索注意保留“只存稳定事实、不存临时信息”的原则,以及检索时“先做 scope 可见性过滤 → 再匹配排序取 top-k 注入 system prompt”的流程,这两点是长期记忆设计的核心,换存储只是实现细节。 **3. “BM25 + 余弦相似度混合检索”的说法要能自圆其说** 你说外部记忆是混合检索,面试官大概率会问融合方式(RRF 还是加权分数)。如果你实际是“精确搜索优先 + RAG语义兜底”的两阶段策略(Agent 多轮工具调用中先精确匹配、模糊查询才走向量检索),建议直接这么讲,并说明这是 Agent场景下的混合检索实践,比声称单次融合排序更真实可信。 **4. “边界感知压缩”要准备好被追问** 如果用了这个词,要能说清:压缩分割点必须落在 user message边界,不能切断 assistant tool_call 和 tool result 的配对,否则 API报400。触发时机是每轮 LLM调用前主动检查(如窗口90%�
点赞
回复 取消回复
呃呃呃 2026年09月07日 00:31
引用原文:外部记忆就是通过检索访问的外部知识库,不在对话历史里常驻,需要的时候按需查。
长期记忆和、外部记忆的代码向量索引和代码关系图谱存的具体内容是什么格式是什么,可以举个例子吗 @派聪明
点赞
回复1 取消回复
派聪明 2026年09月07日 00:32
根据 PaiCLI 的设计,三类记忆的存储内容和格式如下: ##1.长期记忆(本地 JSON 文件) 存的是跨会话的稳定事实,含 scope 元数据,例如: ```json{ "id": "mem_001", "scope": "project", "projectPath": "/Users/me/work/paicli", "content": "这个项目使用 Java17,构建工具用 Maven", "createdAt": "2026-09-07T10:30:00", "updatedAt": "2026-09-07T10:30:00" } ``` - `project`级:绑定具体项目路径,只在该项目会话中可见- `global`级:跨项目通用偏好,如“用户偏好中文回复” ##2. RAG 向量索引(SQLite) 每个代码块一条记录,存原文 +768 维向量,例如: ```json{ "chunkType": "method", "chunkName": "UserService.authenticate(String, String)", "filePath": "src/main/java/UserService.java", "startLine":45, "endLine":80, "content": "public boolean authenticate(String user, String pwd) { ... }", "embedding": [0.021, -0.113,0.087, ...] //768维浮点数组} ``` 三种 chunk粒度:文件级(非 Java 文件回退用,约2000 字符一段)、类级(类声明开头,提供结构入口)、方法级(完整方法源码,最有价值)。 ##3.代码关系图谱(SQLite) 存 AST 解析出的结构关系,如: ```json{ "type": "调用", "from": "UserController.login", "to": "UserService.authenticate", "filePath": "src/main/java/UserController.java", "line":32} ``` 五种关系类型:**继承、接口实现、导入、方法调用、包含**。查询时用 `/graph LoginService` 可查看完整关系链。 **核心区别**:长期记忆是“Agent记住的用户事实”,向量索引是“语义相关代码在哪”,图谱是“谁调用/依赖谁”——分别回答语义检索和结构查询两类问题。
点赞
回复 取消回复
呃呃呃 2026年09月07日 00:27
引用原文:外部记忆就是通过检索访问的外部知识库,不在对话历史里常驻,需要的时候按需查。
这3层记忆都存什么地方 @派聪明
点赞
回复1 取消回复
派聪明 2026年09月07日 00:27
根据文章,PaiCLI 的三层记忆存储位置如下: **1.短期记忆** 存在当前对话会话的内存中,就是对话消息历史(用户输入、模型回复、工具调用和结果),每一轮追加。生命周期只有一次会话,关掉终端就没了。 **2.长期记忆** 持久化到**本地 JSON 文件**里,跨会话保留。每条记忆带元数据(包括项目绝对路径,路径做了标准化处理),支持 project级和 global级两种作用域。下次开新会话时从文件检索相关条目注入上下文。 **3. 外部记忆(RAG 外部知识库)** - **向量索引**:代码块经 nomic-embed-text模型转成768 维 Embedding 向量,存到 **SQLite 数据库** - **精确搜索部分**:不落库,按关键字/正则实时扫描**项目文件树**(零预处理、零冷启动) - **代码关系图谱**:五种结构关系(继承、实现、导入、方法调用、包含)也存在 **SQLite** 里另外还有一个统一的管理者负责协调三层:每轮请求模型前,它从长期记忆检索相关条目、从外部记忆获取检索结果,再和短期记忆的对话历史一起拼装成 prompt 发给模型。
点赞
回复 取消回复
呃呃呃 2026年09月07日 00:23
引用原文:外部记忆就是通过检索访问的外部知识库,不在对话历史里常驻,需要的时候按需查。
项目源代码文件存在什么地方,为什么这个也算外部记忆,这不应该是外部文件吗@派聪明
点赞
回复1 取消回复
派聪明 2026年09月07日 00:24
这个问题问得好,其实涉及对“外部记忆”定义的理解。 **项目源代码存在哪里?** 按文章所述,PaiCLI 中项目代码的 RAG索引(代码块向量化后的 Embedding)存在本地的 **SQLite 数据库**里,原始源文件就在项目的文件树上。此外还有代码关系图谱也存 SQLite。 **为什么源代码算“外部记忆”而不是“外部文件”?** 关键区别不在物理位置,而在**访问方式和是否常驻上下文**: 1. **不在对话历史里常驻**。短期记忆(对话历史)是每一轮都完整发给模型的;而项目代码并不会每轮都塞进上下文。只有当 Agent判断需要时,才通过检索工具(精确搜索或 RAG 向量检索)**按需查询**,把相关的几百行代码片段取出来注入 prompt。 2. **“外部”指的是“模型上下文之外”**。这里的“外部”不是说文件在外部系统,而是说这些知识存在于模型对话窗口之外,需要通过检索机制桥接进来。同理,长期记忆虽然也存在本地 JSON 文件里,但因为它跨会话、按需注入,也和短期记忆性质不同。 3. **与全量塞入对比**。一个10 万行代码的项目直接全量放进上下文会撑爆窗口(即使1M窗口也装不下50 万行仓库)。外部记忆机制的本质就是:**知识放外面,用到才取一小块**。 所以可以这样理解三层的划分标准: - **短期记忆**:常驻上下文,随每轮请求发送- **长期记忆**:本地持久化,按需注入相关条目- **外部记忆**:外部知识库(代码库、文档等),通过检索工具按需访问源代码文件确实是“外部文件”,但从 Agent记忆架构的视角看,它扮演的角色是“可检索的外部知识源”,所以归入外部记忆——这是按**功能角色**分类,不是按存储介质分类。
点赞
回复 取消回复
呃呃呃 2026年09月07日 00:21
引用原文:外部记忆就是通过检索访问的外部知识库,不在对话历史里常驻,需要的时候按需查。
grep检索出来的结果也会存到外部记忆中吗@派聪明
点赞
回复1 取消回复
派聪明 2026年09月07日 00:22
不会。这是两个不同的概念,需要区分清楚: **外部记忆指的是“知识库”本身,而不是检索结果。** 在 PaiCLI 的架构里,外部记忆是指: - 项目源代码文件(供精确搜索按关键字/正则实时扫描) - RAG 的 Embedding 向量索引(存在 SQLite 中) **grep检索的结果属于短期记忆。** 当你调用 `search_code` / `grep_code` 这类工具时,检索到的代码片段会作为 **tool result**追加到当前的对话历史(短期记忆)中,成为本轮会话上下文的一部分。它的生命周期和短期记忆一样——只在当前会话内有效,关闭终端就没了。 **简单总结三层的存放内容:** |层 | 内容 |生命周期 | |---|---|---| |短期记忆 | 对话历史、工具调用及结果(含 grep 结果) |一次会话 | |长期记忆 | 用户显式/Agent主动保存的稳定事实(JSON 文件) |跨会话持久 | | 外部记忆 |代码文件本身、RAG 向量索引 |持久,按需检索 | 另外注意:grep检索结果是**临时信息**,不满足长期记忆“只存稳定事实”的原则(比如“LoginService 在 src/auth 目录下”这种动态事实不该存),所以也不会被转存到长期记忆中。
点赞
回复 取消回复
呃呃呃 2026年09月07日 00:12
引用原文:外部记忆就是通过检索访问的外部知识库,不在对话历史里常驻,需要的时候按需查。
外部记忆存在哪里,存什么内容,什么时候存,什么时候取,取的时候怎么做检索@派聪明
点赞
回复1 取消回复
派聪明 2026年09月07日 00:12
# 外部记忆详解**存在哪里**:以 SQLite 数据库为主,包含两部分——RAG 向量索引库(代码块 Embedding 向量)+代码关系图谱(结构关系),以及项目文件树本身(精确搜索直接扫描,不需要额外存储)。 **存什么内容**: -代码块:按 AST 解析切分的文件级、类级、方法级 chunk(含完整方法源码、`类名.方法签名`),每个块经 nomic-embed-text转成768 维向量存储- 结构关系:继承、接口实现、导入、方法调用、包含这五种代码关系**什么时候存(建索引)**:用户执行 `/index`命令时触发,将代码库源文件按语法结构分块、向量化后写入 SQLite。精确搜索则零预处理,无需提前建索引。 **什么时候取**:Agent 回答需要代码库知识时按需检索,比如用户说“重构登录模块”,Agent需要知道 LoginService 在哪、被谁调用。 **怎么检索**: 1. **优先精确搜索**:按关键字/正则实时扫描文件树,或按 glob匹配文件名——零冷启动、定位精确符号快2. **RAG语义兜底**:查询模糊、关键词难确定时,将查询转成向量,与库中所有代码块向量算**余弦相似度**(点积除以模长乘积),取 Top-K 注入 prompt这个优先级策略写在系统提示词里,由模型自行判断选择哪个工具。
点赞
回复 取消回复
呃呃呃 2026年09月07日 00:09
引用原文:进入关键词匹配和评分排序
取长期记忆是怎么进行检索的,又是怎么进行排序的@派聪明
点赞
回复1 取消回复
派聪明 2026年09月07日 00:09
# PaiCLI长期记忆的检索与排序机制##检索流程每轮对话开始前,统一的管理者会从长期记忆(本地 JSON 文件)中检索与当前对话相关的条目,流程如下: **第一步:可见性过滤** - **global级记忆**:对所有项目可见(如“用户偏好中文回复”) - **project级记忆**:只有元数据中的项目路径匹配当前项目的会话才可见(防止 A项目的记忆干扰 B 项目) **第二步:关键词匹配** 检索用的是**最朴素的关键词匹配,不是向量检索**。原因是长期记忆通常只有几十条,关键词匹配够用且零依赖。 **第三步:评分排序** 过滤后的条目经过评分排序,取 **top-k 条**注入到 system prompt 中。 ## 为什么不用向量检索? -记忆量小(几十条),关键词匹配零成本、零依赖- 如果记忆量增长到**几千条**,就应该升级为向量检索##辅助管理命令用户还可以主动检索和管理记忆: - `/memory list`:查看所有记忆- `/memory search`:按当前项目可见性搜索- `/memory delete`:删除单条这样用户能看到 Agent 到底记住了什么,心里有底。
点赞
回复 取消回复
Ivan 2026年09月06日 09:57
引用原文:“第二套是 conversationHistory 压缩,压的是 Agent 真正发给 LLM 的消息列表。它不是 Map-Reduce,而是在调用 LLM 前检查 token,达到阈值后,把 system 后面、最近 3 个 user 轮次之前的旧消息交给 LLM 总结成一段摘要,再重建消息列表。”
@派聪明 conversationMemory是我们对话中提到的短期记忆吗,它的作用结合代码来看并没有被注入模型对话上下文,真正注入的是conversationHistory,这两种是前者被后者替换过吗?
点赞
回复5 取消回复
派聪明 2026年09月06日 09:58
简单说:**是的,conversationMemory 就是我们说的短期记忆;但不是“被替换”,而是“分工”——一个负责完整存档,一个负责实际喂给模型。**两者是并存关系,不是替换关系。 具体对应关系: **1.短期记忆(Memory系统里的 conversationMemory)** -存的是**全量对话历史**:用户消息、助手回复、工具调用及结果,每轮追加- 它的价值是“完整的记忆档案”,Map-Reduce摘要压缩发生在这一层(触发线是短期记忆预算的90%,即窗口的45% ×90%) - 它本身**不直接发给模型**——你观察得没错**2. conversationHistory(真正发给 LLM 的消息列表)** - 这是 Agent每轮调用 LLM 前**临时组装/维护**的请求消息列表- 发送前检查 token,达到窗口90% 就触发第二套压缩:保留 system + 最近3 个 user轮次,更早的消息总结成一段摘要注入-模型真正“看到”的就是它**为什么要两套?** 因为目的不同: | |短期记忆 | conversationHistory | |---|---|---| | 角色 |完整历史存档 |实际请求载荷 | |压缩方式 | Map-Reduce摘要 | user边界切割 + LLM 总结 | |触发线 |窗口45% 的90% |窗口的90% | | 目的 |控制记忆体量、可供回溯 | 保证请求不超上下文窗口 | 短期记忆在会话内可以保留相对完整的细节(预算只有窗口的45%,靠 Map-Reduce压住),而发送给模型的 conversationHistory 可以包含 system prompt、注入的长期记忆、RAG检索结果等动态拼装内容,并且靠“摘要 + 最近3轮”的方式在协议安全的前提下(不切断 tool_call/tool_result 配对)控制长度。 所以流程是:对话写入短期记忆(做第一
点赞
回复 取消回复
展开剩余 4 条回复
粗心的超短裙 2026年09月03日 12:32
引用原文:做摘要压缩的时候有个特别容易踩的坑。被压缩掉的消息如果包含工具调用和对应的工具结果,压缩后的 assistant 消息不能保留 tool_calls 字段。因为对应的 tool 消息已经被摘要吃掉了,但 tool_calls 还留在 assistant 消息里,API 就会发现有个 tool_call_id 找不到对应的 tool 结果,直接报错。
@派聪明 怎么解决这个问题的
点赞
回复1 取消回复
派聪明 2026年09月03日 12:32
# 问题原因压缩摘要时,assistant消息里的 `tool_calls` 字段和对应的 `tool` 结果消息是**成对的协议关系**(通过 `tool_call_id`匹配)。如果压缩时把 tool 结果消息摘要掉了,但 assistant 的 `tool_calls` 字段还保留着,API 会发现某个 `tool_call_id`找不到对应的 tool消息,直接返回400报错,或导致模型理解混乱。 # 解决方案PaiCLI 的做法是**按 user message边界切割,并清理残留的 tool_calls**: 1. **压缩时机**:在每轮调用 LLM之前检查,当 conversationHistory估算 token 达到 `maxContextWindow *90%` 时触发压缩。 2. **按 user边界切割**:把 system 后面、最近3 个 user轮次之前的旧消息交给 LLM 总结成一段摘要,分割点必须落在 `user message`边界上,不会把一对 `assistant tool_call` 和 `tool result` 从中间劈开。 3. **重建消息列表**:压缩后的结构为: ``` [system, user("[已压缩的历史对话摘要]\n" + summary), assistant("好的,我已了解之前的上下文,请继续。"), 最近3 个 user轮次开始的尾部消息] ``` 4. **关键点**:被摘要替代的旧消息(含 tool_call 和 tool result)整体被吃掉,重建的摘要消息是干净的 user/assistant消息,**不带 tool_calls 字段**,因此不存在悬空的 `tool_call_id`;保留下来的尾部消息则完整保留了 tool_call 与 tool 结果的配对关系。 这样既完成了压缩,又严格维持了消息格式的协议正确性,避免 API报错。
点赞
回复 取消回复

目录