TencentDB Agent Memory 的四级降级:源码里有一档直接 FATAL

2026-08-16

MemoryProxy 这个模块的存储层时,有一处口径是对不上的:文档侧把四种后端串成一条会自动降级的链,源码侧对其中一档明确写了「不给降级链任何机会」。这篇只做一件事——把差异出现的位置一处一处标出来,再告诉你怎么自己回仓库核。哪一处才是项目想要的行为,我们不推断,也不评价。

先说清楚本文的边界。截至 2026-08-16,TencentCloud/TencentDB-Agent-Memory默认分支是 feat/server_team,不是 main 也不是 master,下文所有路径都在这个分支上。MemoryProxy 目录里 package.json 的包名是 context-proxy、版本 0.1.0(同仓主模块 MemoryCore2.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 | memoryconfig.example.yaml:178-184)。Redis 由独立的 redis: 段控制,不在 storage.backend 的枚举里。代码内建默认值里 storage.backendsqlitesrc/config.ts:49),storage.enabled 在示例配置里默认 falseconfig.example.yaml:176)。

源码侧:cos 这一档写的是硬失败

降级逻辑集中在 src/storage/factory.tsgetProxyStorage

src/storage/factory.ts:123-149config.backend === "cos" 的独立分支。装配失败时它打的是一条 !!! FATAL !!! 日志,然后 throw。这一段上方的注释原文写着:「一旦配了 cos,就不给降级链任何机会……绝不能悄悄退到 process-local(sqlite/fs/memory)」。

同一个函数里,非 cos 的路径确实保留了链式尝试:src/storage/factory.ts:152-181sqlite → 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 backsrc/storage/factory.ts:93-96 的注释写 cost-guard 不可用(开源用户无 submodule 等情形)时「静默跳过」。这两处用的是 fall back / 跳过的措辞,与 :123-149 分支里的 FATAL + throw 是不同口径。

补一条与之直接相关的仓库事实:packages/cost-guard 这个目录在快照里不存在,而 tsconfig.json:11vitest.config.ts:11 都配了指向 ./packages/cost-guard/src/index.ts 的 alias,src/storage/factory.ts:102 会动态 import("@context-proxy/cost-guard") 去取 openKernelStsCosBackendsrc/storage/factory.ts:98-117 里这次 import 失败只打一条 warn。cos 后端的真实装配代码不在这个快照里,它到底做什么,我们看不到,也不做任何猜测

你自己怎么核:四个位置,按顺序看

不用装,不用跑,git clone 之后读文件就能对完。注意分支要切到 feat/server_team

  1. MemoryProxy/README.md:253config.example.yaml:149-150——先确认文档侧「自动降级」这句话的原文。
  2. config.example.yaml:208-213——同一份文件里 COS 段落末尾那句「进程不会降级启动」。
  3. src/storage/factory.ts:123-149——backend === "cos" 分支里的 !!! FATAL !!!throw;紧接着读 :152-181,确认链式尝试从哪一档开始。
  4. src/index.ts:74src/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 /healthsrc/server.ts:84-104)返回体里有 storage 一节,字段是 enabled / requested / effective / degraded / lastError?requestedeffective 不一致时,你不用猜生效的是哪一档,读这两个字段即可。还有一条文档里没写的行为:当 storage.enabled 为真、requested === "cos"effective !== requested 时,status"degraded"HTTP 状态码返 503src/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.dbsrc/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.enabledfalse 示例配置里它默认就是 falseconfig.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.tsDEFAULT_CONFIG 与示例配置里的示例值。COS 段落里还写着只支持 kernel-sts 模式、「正式环境禁止静态 AK/SK」(config.example.yaml:208-213)——密钥与凭据怎么托管属于你自己环境的事,本文不给方案。

延伸阅读


本文依据 TencentDB Agent Memory 官方仓库(github.com/TencentCloud/TencentDB-Agent-Memoryfeat/server_team 分支上的 README、INSTALL、CHANGELOG、ROADMAP 与四个模块的源码整理, 核对日 2026-08-16,对应仓库快照 97f9465。该仓库的默认分支即为 feat/server_team。 本文内容为仓库源码与文档口径,我们没有部署、也没有运行过该项目的任何一个模块, 因此不涉及运行效果、检索质量与性能的任何描述。 该项目主模块处于 beta 阶段、其余模块版本号仍为 0.1.0,参数与接口随版本变动,请以仓库最新内容为准。 该项目会采集并存储团队的对话、文档与代码,属于敏感数据,是否使用请结合自身合规要求评估。 安全与合规相关做法请结合自身环境评估,本文不构成安全方案建议。

想系统学会用 AI?报名体系课或加入会员,照着学、照着用。