TencentDB Agent Memory 的四级降级:源码里有一档直接 FATAL
翻 MemoryProxy 这个模块的存储层时,有一处口径是对不上的:文档侧把四种后端串成一条会自动降级的链,源码侧对其中一档明确写了「不给降级链任何机会」。这篇只做一件事——把差异出现的位置一处一处标出来,再告诉你怎么自己回仓库核。哪一处才是项目想要的行为,我们不推断,也不评价。
先说清楚本文的边界。截至 2026-08-16,TencentCloud/TencentDB-Agent-Memory 的默认分支是 feat/server_team,不是 main 也不是 master,下文所有路径都在这个分支上。MemoryProxy 目录里 package.json 的包名是 context-proxy、版本 0.1.0(同仓主模块 MemoryCore 是 2.0.0-beta.1),项目整体仍在快速变动,参数与接口随版本改,请以仓库最新内容为准。我们没有部署过、没有启动过这个模块,也没有向它发过一个请求,下面所有「默认值」都指配置文件或代码里写死的值,不是运行表现。
文档侧:四档串成一条链
MemoryProxy/README.md:253 写的是:Degradation chain: cos → sqlite → fs → memory. If any backend fails to init, it degrades automatically。
config.example.yaml:149-150 是中文侧的同义表述:「【降级链】cos → sqlite → fs → memory;任一后端 init 失败自动降级」。
两处都把 cos 摆在链首,都用了「任一后端」这个量词。
顺带把后端枚举本身也说清楚,因为这里还有第二层差异:MemoryProxy/README.md:33 说的是五种状态存储写法(Redis、COS、SQLite、FS、Memory),而 storage.backend 的可选值只有四个——cos | sqlite | fs | memory(config.example.yaml:178-184)。Redis 由独立的 redis: 段控制,不在 storage.backend 的枚举里。代码内建默认值里 storage.backend 是 sqlite(src/config.ts:49),storage.enabled 在示例配置里默认 false(config.example.yaml:176)。
源码侧:cos 这一档写的是硬失败
降级逻辑集中在 src/storage/factory.ts 的 getProxyStorage。
src/storage/factory.ts:123-149 是 config.backend === "cos" 的独立分支。装配失败时它打的是一条 !!! FATAL !!! 日志,然后 throw。这一段上方的注释原文写着:「一旦配了 cos,就不给降级链任何机会……绝不能悄悄退到 process-local(sqlite/fs/memory)」。
同一个函数里,非 cos 的路径确实保留了链式尝试:src/storage/factory.ts:152-181 按 sqlite → fs → memory 依次试,实际生效的后端与请求的后端不一致时打 !!! DEGRADED !!!,落到进程本地后端时再补一条 !!! MULTI-NODE HAZARD !!!;:183-190 是最后兜底的 MemoryStorage。
所以「链」这个结构在源码里是存在的,只是它的入口不含 cos 这一档:README 与示例配置写的是四档一条链,src/storage/factory.ts:123-149 把首档单独拎出来做了 throw。以我们实读的快照为准,两者不一致。
同一份示例配置内部,前后也是两种写法
这一点值得单独标出来,因为它就在同一个文件里。
config.example.yaml:149-150 写「任一后端 init 失败自动降级」;而 config.example.yaml:208-213 讲 COS 的 kernel-sts 模式时,最后一行写的是「Shark 不可用时 COS 后端装配失败,进程不会降级启动」。同一份 config.example.yaml,相隔约六十行,前一处与后一处的说法不同。
第三种措辞出现在日志文案里。src/index.ts:74 那条日志的 note 字段写的是 cost-guard submodule missing or shark unreachable — cos falling back;src/storage/factory.ts:93-96 的注释写 cost-guard 不可用(开源用户无 submodule 等情形)时「静默跳过」。这两处用的是 fall back / 跳过的措辞,与 :123-149 分支里的 FATAL + throw 是不同口径。
补一条与之直接相关的仓库事实:packages/cost-guard 这个目录在快照里不存在,而 tsconfig.json:11、vitest.config.ts:11 都配了指向 ./packages/cost-guard/src/index.ts 的 alias,src/storage/factory.ts:102 会动态 import("@context-proxy/cost-guard") 去取 openKernelStsCosBackend,src/storage/factory.ts:98-117 里这次 import 失败只打一条 warn。cos 后端的真实装配代码不在这个快照里,它到底做什么,我们看不到,也不做任何猜测。
你自己怎么核:四个位置,按顺序看
不用装,不用跑,git clone 之后读文件就能对完。注意分支要切到 feat/server_team。
MemoryProxy/README.md:253与config.example.yaml:149-150——先确认文档侧「自动降级」这句话的原文。config.example.yaml:208-213——同一份文件里 COS 段落末尾那句「进程不会降级启动」。src/storage/factory.ts:123-149——backend === "cos"分支里的!!! FATAL !!!与throw;紧接着读:152-181,确认链式尝试从哪一档开始。src/index.ts:74与src/storage/factory.ts:93-96——两处 fall back / 跳过措辞。
四处读完,你手上就有一张位置清单,而不是一个印象。之后无论仓库怎么更新,你都能拿这张清单再对一遍。
处置与验证:只说文档和源码给了什么
处置层面,源码语义给的是「这一档不降级」而不是「换一档继续跑」:src/storage/factory.ts:123-149 在 cos 装配失败时直接 throw,注释写明绝不退到 process-local 的 sqlite/fs/memory。至于失败可能来自哪儿,仓库里能核到的线索是 src/index.ts:74 那条日志 note 里并列的两项——cost-guard submodule 缺失、shark 不可达。这是源码与日志文案的口径,不是我们的排障建议。
验证层面,仓库给了一个现成的观测点。 GET /health(src/server.ts:84-104)返回体里有 storage 一节,字段是 enabled / requested / effective / degraded / lastError?。requested 与 effective 不一致时,你不用猜生效的是哪一档,读这两个字段即可。还有一条文档里没写的行为:当 storage.enabled 为真、requested === "cos" 而 effective !== requested 时,status 为 "degraded" 且 HTTP 状态码返 503(src/server.ts:86-87、:103),注释说明目的是让 k8s 的 LB 摘掉这个 pod(src/server.ts:80-83)。README 的端点表只写这个接口是 runtime health check (includes storage.effective)(MemoryProxy/README.md:198),示例响应是 degraded: false 的 200 情形(MemoryProxy/README.md:123-130),没提 503。
用 /health 时还有个小坑值得先知道:它返回的 version 是硬编码字符串 "0.2.0"(src/server.ts:90),而 MemoryProxy/package.json:3 里是 0.1.0,README 的示例响应也写 "version": "0.2.0"(MemoryProxy/README.md:128)。别拿这个字段当版本依据——这同样只是位置陈述,我们不推断哪个是对的。
什么情况说明你遇到的不是这件事
这一步不能省,否则很容易把别的问题都往这条差异上套。
- 你的
storage.backend不是cos。 那走的是src/storage/factory.ts:152-181的链式路径,sqlite → fs → memory的降级在源码里是真实存在的,只是会打!!! DEGRADED !!!与!!! MULTI-NODE HAZARD !!!两条 error 级日志。这条链上还有两处写死的值顺带记一下:SQLite 的库路径解析顺序是process.env.PROXY_DB_PATH优先,取不到才落到~/.tdai-memory-proxy/proxy.db(src/db/index.ts:4、:32-34,同一段逻辑在src/storage/factory.ts:283-287又出现了一次);sqlite sweeper 的周期是硬编码的setInterval(run, 5 * 60 * 1000),并调用了unref()(src/storage/factory.ts:308-310)。这两处都是源码里的常量,不是对运行表现的承诺。 storage.enabled是false。 示例配置里它默认就是false(config.example.yaml:176),整段存储后端逻辑压根没进场,谈不上降级不降级。- 你在找 Redis 相关行为。 Redis 是独立的
redis:段(config.example.yaml:115-123,示例里enabled: true,而代码内建默认是false,见src/config.ts:34),不受storage.backend枚举管。 - 你手上的字段压根不被读。 示例配置有一张用
[必]/[可]/[死]标注的字段速查表(config.example.yaml:155-173),其中cos.rootPrefix被标为[死],理由写的是 shark 侧硬编码"proxy_cache",proxy 侧配什么都不生效(config.example.yaml:172、:222-223)。对着一个[死]字段调半天,跟降级链没有关系。
最后一句提醒:这个模块没有 config.yaml——它被 .gitignore 排除了,仓库里只有 config.example.yaml。也就是说,任何一套线上实际生效的配置值,我们一个都不知道,本文所有默认值只来自 src/config.ts 的 DEFAULT_CONFIG 与示例配置里的示例值。COS 段落里还写着只支持 kernel-sts 模式、「正式环境禁止静态 AK/SK」(config.example.yaml:208-213)——密钥与凭据怎么托管属于你自己环境的事,本文不给方案。
延伸阅读
- 从头读起:TencentDB Agent Memory 是什么:团队级 Agent 记忆中枢怎么读
- 本专题共 40 篇,完整分组目录见专题页
- TencentDB Agent Memory 注入的标签:README 与代码产出的不是同一个
- TencentDB Agent Memory 的 MemoryProxy 版本:0.1.0 还是 0.2.0
本文依据 TencentDB Agent Memory 官方仓库(github.com/TencentCloud/TencentDB-Agent-Memory)
feat/server_team 分支上的 README、INSTALL、CHANGELOG、ROADMAP 与四个模块的源码整理,
核对日 2026-08-16,对应仓库快照 97f9465。该仓库的默认分支即为 feat/server_team。
本文内容为仓库源码与文档口径,我们没有部署、也没有运行过该项目的任何一个模块,
因此不涉及运行效果、检索质量与性能的任何描述。
该项目主模块处于 beta 阶段、其余模块版本号仍为 0.1.0,参数与接口随版本变动,请以仓库最新内容为准。
该项目会采集并存储团队的对话、文档与代码,属于敏感数据,是否使用请结合自身合规要求评估。
安全与合规相关做法请结合自身环境评估,本文不构成安全方案建议。