TencentDB Agent Memory 注入的标签:README 与代码产出的不是同一个
如果你打算在 MemoryProxy 的日志里、或者在被改写后的系统提示词里找那个叫 <cloud_skills> 的块,可能会白找一阵。README 里确实写着它,但在我们采集的这份快照里,src/ 下没有任何一处代码真的输出这个名字。
这篇就把这条线索捋一遍:README 写的标签名、代码里 injector 自述产出的标签名、以及同一个接口在配置注释与客户端实现里的两种方法写法。下面只陈述我们在快照里读到的差异与它们的具体位置,不去推断哪一处「才是对的」,也不拿这些差异评价这个项目。
先把坐标钉死
本文对应的是 TencentCloud/TencentDB-Agent-Memory 仓库,快照 97f9465,核对日 2026-08-16。要特别注意的是,这个仓库的默认分支是 feat/server_team,不是 main 也不是 master,所以下面出现的每一个文件路径都请按 feat/server_team 分支去对。
模块本身也有几个容易记混的点,先摆在前面:
- 目录名叫
MemoryProxy,但MemoryProxy/package.json:2里的包名是context-proxy,目录名不等于包名; - 该模块
package.json:3的version是0.1.0(同仓主模块MemoryCore是2.0.0-beta.1); - 而
GET /health返回的version字段是硬编码字符串"0.2.0"(src/server.ts:90),README 的/health示例响应里也写"version": "0.2.0"(MemoryProxy/README.md:128)。同一个模块的版本号在这三处对不上:package.json是0.1.0,另外两处是0.2.0。
也就是说,这是一个版本号还在 0.1.0、主模块尚处 beta 的项目,接口、配置项与文档口径都可能随版本变动。下面所有引用请以仓库最新内容为准。
差异一:<cloud_skills> 与 <available_skills>
两处位置如下。
README 侧:MemoryProxy/README.md:69(中文版 README_CN.md:69 同位置)写的是 <cloud_skills>,说明是「从 MemoryCore RAG 检索到的相关 Skill 摘要」。
代码侧:src/injection/injectors/skill-injector.ts:2-3 的文件头注释写的是,这个 injector 产出 <available_skills> 块,内容是「当前 agent 拥有的 skill(按 team_id + agent_id 经 /v3/skill/listing 过滤)」。同文件 :157 的 description 字段原文是 Inject agent-owned cloud skills via /v3/skill/listing before <agent_skills>.
同一份代码内部还有第三种说法:注册这个 injector 的地方,src/injection/index.ts:263 的注释写的是 RAG-driven <cloud_skills> block. Calls /v3/skill/search at prewarm time.
怎么确认这不是我们看漏了?做一个动作就够:在 MemoryProxy/src/ 下 grep cloud_skills。我们数到的命中是 5 处,分别在 src/injection/index.ts 的第 263、265、271 行,src/injection/injectors/skill-tools-injector.ts:190,以及 src/types.ts:290——全部是注释行,没有一处是会被写进输出内容的字符串。
代码里真正会产出的标签有哪几个
buildPipelineBundle 会按 config.injection.injectors 列表逐个注册 injector(src/injection/index.ts:210-347)。把这些 injector 的 id、注入点与自述产出的标签排在一起,是这样:
| injector 类 | id | 注入点 | 产出标签 |
|---|---|---|---|
SkillInjector | skill-injector | system.before_tools | <available_skills> |
SkillToolsInjector | skill-tools-injector | system.before_tools | <skill_tools> |
KnowledgeToolsInjector | knowledge-tools-injector | system.before_tools | <knowledge_tools> |
TdaiProfileMemoryInjector | tdai-profile-memory-injector | system.suffix | <tdai_profile_memory> |
TdaiToolsInjector | tdai-memory-tools-injector | system.suffix | <tdai_memory_tools> |
AssetReflectionInjector | asset-reflection-injector | system.suffix | <asset_reflection> |
这张表该怎么读:它不是「你启动之后一定会看到这六个块」的清单,而是「代码里存在这六个注册分支」。每个分支各有自己的前置条件,后面一节会说。
顺带说一句注入点。src/injection/pipeline.ts:165-175 把注入点的执行顺序写死成 9 个:system.prefix、system.before_tools、system.after_tools、system.suffix、tools.prepend、tools.append、user.first_turn、user.before、user.after。所以上表里 system.before_tools 的三个块会排在 system.suffix 的三个块前面——这是注册表决定的顺序,不是运行时观察。
差异二:GET /v3/skill/search 与 this.post(...)
第二处不一致落在方法上,位置同样具体。
配置样例侧:config.example.yaml:564 的注释写「GET /v3/skill/search 检索相关 skill」。
实现侧:src/skill/core-client.ts:258 的实现是 this.post<SearchSkillsResult>("/v3/skill/search", input, opts),同文件 :5 的文件头也把这个端点标为 POST /v3/skill/search。
CoreSkillClient 这个类值得多看两眼。它的文件头(src/skill/core-client.ts:5-12)列明了 proxy 会主动去调的 core 侧接口只有 6 个:/v3/skill/search、/v3/skill/listing、/v3/skill/conversation/add、/v3/skill/extract、/v3/skill/conversation/force-archive、/v3/skill/list。其余 /v3/skill/* 端点,注释原文说「NOT wrapped here on purpose」,并写明模型是通过 /skill-bridge 反向代理直接 curl 这些端点的。
而 skill-bridge 存在的理由写在 src/skill/skill-bridge.ts:3-7:模型会用 Bash 去 curl skill 操作,作者不希望 bearer token 落进 prompt,同时要把 session 里的 (user_id, team_id, agent_id, task_id?) 盖在出站 body 上,让模型没法伪造身份。顺带说一句 bridge 侧的白名单:src/skill/skill-bridge.ts:140-158 的 ALLOWED_SUBPATHS 共 16 项,search 也在其中;其中 6 项属于 WRITE_SUBPATHS(create、update、patch、delete、files/write、files/remove,:161-169),allowLlmWrite=false 时命中写操作返回 403、错误码 40302(:501-505),而 skillRuntime.allowLlmWrite 在 config.example.yaml:607 给的就是 false。也就是说,同一个接口名可能在文件头注释、在客户端封装、在 bridge 的 allowlist 里各出现一次。
顺着同一条线,还有一个「配置项在、注入器不在」
如果你在 config.example.yaml 里看到某个开关,请不要默认它背后一定挂着活的实现。这个模块里有一个现成的例子:
src/injection/index.ts:310-312的注释原文说,L0/L1 不再每轮自动召回注入到 user prompt(理由写的是会破坏 KV / prompt cache),并明确写着「L1 recall injector 已下线,recallL1配置保留但不再注册」;TdaiL1RecallInjector这个类仍然在src/injection/index.ts:76被 export,但在buildPipelineBundle里既没有 import 也没有 register;- 与此同时
config.example.yaml:554里recallL1: true那一行还在,注释写的是「是否启用 L1 召回」。
所以「配置里有这个字段」和「这个能力现在会跑」是两件事,要回代码核。
你自己怎么复核这类差异
四个动作,都能在只读仓库的前提下完成:
- grep 标签名。要找某个块到底是不是代码输出的,直接在
MemoryProxy/src/下搜标签字面量,注意区分命中在注释行还是在字符串里。 - 打开
src/injection/injectors/逐个看文件头。每个 injector 的文件头注释与description字段都写了它产出什么、依赖哪个 core 接口。 - 看注册段
src/injection/index.ts:210-347。注入器不是有类就会跑,得看它被注册的前置条件。 - 看
src/injection/pipeline.ts。管线流程注释原文是raw body → Adapter.parse() → AgentContext → execute hooks → Adapter.serialize() → modified body(:1-5),执行顺序在:165-175。
什么情况说明你遇到的不是标签名这回事
如果你压根没看到任何注入块,那先别在标签名上纠结:按代码里的注册条件,下面这些开关只要有一处没满足,对应的 injector 就不会被注册。以下都是配置层面的取值差异,不是我们对运行结果的判断:
- 总开关的两层取值不一致:
injection.enabled在代码默认值里是false(src/config.ts:77),而config.example.yaml:435给的是true。配置文件的读取优先级写在src/config.ts:260-261:CLI 覆盖项 > YAML 配置文件 > 代码默认值。 - skill 分支:
injectors列表里含"skill",就会一次注册SkillInjector与SkillToolsInjector两个(src/injection/index.ts:262-275)。 - knowledge 分支:需要同时满足
injectors含"knowledge"、knowledge.enabled、knowledge.serviceToken非空三个条件(src/injection/index.ts:453-457)。注意knowledge.serviceToken的代码默认值是空字符串(src/config.ts:128),而示例配置写的是"local"。 - tdai-memory 分支:需要
injectors含"tdai-memory"、tdai.enabled、tdai.memory.enabled、tdai.memory.inject四者全真(src/injection/index.ts:288);TdaiProfileMemoryInjector还额外要求tdai.memory.injectL2L3(:307-309)。这几项的代码默认值全是false(src/config.ts:104、:109-113),示例配置里全是true。 - asset-reflection 分支:只在
assetReflection.markerOptIn=true且本节点已注册至少一个资产 injector 时才注册(src/injection/index.ts:326-347),而config.example.yaml:463给的是false。
另外两点也可能让「看不到某个块」与标签名无关:
- 落位会回落。注入落位有两条路径(
src/injection/pipeline.ts:340-381):hook 声明了anchor、有匹配的AgentProfile且解析出真实结构键时按锚点精确落位,否则回落到粗粒度的point。system.before_tools/system.after_tools锚点解析不到时会统一改为追加到系统提示词末尾(:416-433),注释说明这是为了不把资产块甩到用户 persona 前面。也就是说,块可能在,但不在你以为的位置。 - 单个 hook 抛错不致命。
src/injection/pipeline.ts:229-250捕获异常后console.error并继续下一个,缺一个块不会让整条管线停。
最后一条限定必须说清楚:MemoryProxy/config.yaml 被 .gitignore 排除,我们在快照里找不到它。因此本文提到的每一个「默认值」都只来自 src/config.ts 的 DEFAULT_CONFIG 与 config.example.yaml 的示例值,任何一套真实部署里实际生效的值,我们一个都不知道。
差异说到这里就停。README 写 <cloud_skills>,injector 文件头写 <available_skills>,配置注释写 GET,客户端实现写 POST——位置都已给出,你可以自己去这几行核一遍。至于哪一处更接近作者当下的意图、为什么没有同步,我们没有依据,不做推断。
延伸阅读
- 从头读起:TencentDB Agent Memory 是什么:团队级 Agent 记忆中枢怎么读
- 本专题共 40 篇,完整分组目录见专题页
- TencentDB Agent Memory 的 MemoryProxy:一条请求经过它发生了什么
- TencentDB Agent Memory 的四级降级:源码里有一档直接 FATAL
本文依据 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,参数与接口随版本变动,请以仓库最新内容为准。
该项目会采集并存储团队的对话、文档与代码,属于敏感数据,是否使用请结合自身合规要求评估。
安全与合规相关做法请结合自身环境评估,本文不构成安全方案建议。