TencentDB Agent Memory 注入的标签:README 与代码产出的不是同一个

2026-08-16

如果你打算在 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:3version0.1.0(同仓主模块 MemoryCore2.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.json0.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 过滤)」。同文件 :157description 字段原文是 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注入点产出标签
SkillInjectorskill-injectorsystem.before_tools<available_skills>
SkillToolsInjectorskill-tools-injectorsystem.before_tools<skill_tools>
KnowledgeToolsInjectorknowledge-tools-injectorsystem.before_tools<knowledge_tools>
TdaiProfileMemoryInjectortdai-profile-memory-injectorsystem.suffix<tdai_profile_memory>
TdaiToolsInjectortdai-memory-tools-injectorsystem.suffix<tdai_memory_tools>
AssetReflectionInjectorasset-reflection-injectorsystem.suffix<asset_reflection>

这张表该怎么读:它不是「你启动之后一定会看到这六个块」的清单,而是「代码里存在这六个注册分支」。每个分支各有自己的前置条件,后面一节会说。

顺带说一句注入点。src/injection/pipeline.ts:165-175 把注入点的执行顺序写死成 9 个:system.prefixsystem.before_toolssystem.after_toolssystem.suffixtools.prependtools.appenduser.first_turnuser.beforeuser.after。所以上表里 system.before_tools 的三个块会排在 system.suffix 的三个块前面——这是注册表决定的顺序,不是运行时观察。

差异二:GET /v3/skill/searchthis.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-158ALLOWED_SUBPATHS 共 16 项,search 也在其中;其中 6 项属于 WRITE_SUBPATHScreateupdatepatchdeletefiles/writefiles/remove:161-169),allowLlmWrite=false 时命中写操作返回 403、错误码 40302:501-505),而 skillRuntime.allowLlmWriteconfig.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:554recallL1: true 那一行还在,注释写的是「是否启用 L1 召回」。

所以「配置里有这个字段」和「这个能力现在会跑」是两件事,要回代码核。

你自己怎么复核这类差异

四个动作,都能在只读仓库的前提下完成:

  1. grep 标签名。要找某个块到底是不是代码输出的,直接在 MemoryProxy/src/ 下搜标签字面量,注意区分命中在注释行还是在字符串里。
  2. 打开 src/injection/injectors/ 逐个看文件头。每个 injector 的文件头注释与 description 字段都写了它产出什么、依赖哪个 core 接口。
  3. 看注册段 src/injection/index.ts:210-347。注入器不是有类就会跑,得看它被注册的前置条件。
  4. src/injection/pipeline.ts。管线流程注释原文是 raw body → Adapter.parse() → AgentContext → execute hooks → Adapter.serialize() → modified body:1-5),执行顺序在 :165-175

什么情况说明你遇到的不是标签名这回事

如果你压根没看到任何注入块,那先别在标签名上纠结:按代码里的注册条件,下面这些开关只要有一处没满足,对应的 injector 就不会被注册。以下都是配置层面的取值差异,不是我们对运行结果的判断:

  • 总开关的两层取值不一致injection.enabled 在代码默认值里是 falsesrc/config.ts:77),而 config.example.yaml:435 给的是 true。配置文件的读取优先级写在 src/config.ts:260-261:CLI 覆盖项 > YAML 配置文件 > 代码默认值。
  • skill 分支injectors 列表里含 "skill",就会一次注册 SkillInjectorSkillToolsInjector 两个(src/injection/index.ts:262-275)。
  • knowledge 分支:需要同时满足 injectors"knowledge"knowledge.enabledknowledge.serviceToken 非空三个条件(src/injection/index.ts:453-457)。注意 knowledge.serviceToken 的代码默认值是空字符串(src/config.ts:128),而示例配置写的是 "local"
  • tdai-memory 分支:需要 injectors"tdai-memory"tdai.enabledtdai.memory.enabledtdai.memory.inject 四者全真(src/injection/index.ts:288);TdaiProfileMemoryInjector 还额外要求 tdai.memory.injectL2L3:307-309)。这几项的代码默认值全是 falsesrc/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 且解析出真实结构键时按锚点精确落位,否则回落到粗粒度的 pointsystem.before_tools / system.after_tools 锚点解析不到时会统一改为追加到系统提示词末尾(:416-433),注释说明这是为了不把资产块甩到用户 persona 前面。也就是说,块可能在,但不在你以为的位置。
  • 单个 hook 抛错不致命src/injection/pipeline.ts:229-250 捕获异常后 console.error 并继续下一个,缺一个块不会让整条管线停。

最后一条限定必须说清楚:MemoryProxy/config.yaml.gitignore 排除,我们在快照里找不到它。因此本文提到的每一个「默认值」都只来自 src/config.tsDEFAULT_CONFIGconfig.example.yaml 的示例值,任何一套真实部署里实际生效的值,我们一个都不知道

差异说到这里就停。README 写 <cloud_skills>,injector 文件头写 <available_skills>,配置注释写 GET,客户端实现写 POST——位置都已给出,你可以自己去这几行核一遍。至于哪一处更接近作者当下的意图、为什么没有同步,我们没有依据,不做推断。

延伸阅读


本文依据 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?报名体系课或加入会员,照着学、照着用。