TencentDB Agent Memory 的 OpenAPI:自称 28 个端点,代码 37 条
如果你打算照着 MemoryKnowledge 的 OpenAPI 规格生成一份客户端,有一个数字值得先自己数一遍:规格描述里写的端点数,和代码里真正注册出来的路由数,不是同一个数。
先把前提摆清楚。本文讨论的是 TencentCloud/TencentDB-Agent-Memory 仓库中的 MemoryKnowledge/ 这一个模块,截至 2026-08-16 我们采集的快照是 97f9465,分支 feat/server_team——这个仓库的默认分支就是 feat/server_team,不是 main 也不是 master,所以下文所有文件路径都要放在这条分支上看。该模块 package.json 里的 version 是 0.1.0(MemoryKnowledge/package.json:2-3),包名 @tencentdb-agent-memory/knowledge-service,而整仓 CHANGELOG 最新条目是 2.0.1-beta.1。项目仍在 beta 阶段,接口与路径随版本变动,下面写到的每一条路由都请以仓库最新内容为准。
差异本身:两处位置,两个数
位置一:MemoryKnowledge/openapi.yaml:6。 规格头部 info.description 的原文里有一句 Provides 28 endpoints。规格头还写明 openapi: 3.0.3、info.title: Knowledge Service API、info.version: "1.0.0"、servers[0].url: http://localhost:8421/v3,tags 只有 LLM-Wiki 和 Code-Graph 两个(MemoryKnowledge/openapi.yaml:1-46)。
我们用脚本数过 paths 下的路径条目,结果确实是 28 条,且全部只有 post 一个方法。规格里的注释把它们分成 LLM-Wiki (15 endpoints)(MemoryKnowledge/openapi.yaml:50)与 Code-Graph 13 条。也就是说,规格自己说 28,规格自己的 paths 也是 28,这一层是自洽的。
位置二:MemoryKnowledge/src/server.ts:56-96 挂载出来的那棵路由树。 我们按 app.post / app.get 的字面量逐文件枚举,加上一处循环注册,得到的是 37 条。
| 来源文件 | 条数 | 说明 |
|---|---|---|
src/routes/wiki.ts | 16 | 全部字面量 POST |
src/routes/code-graph.ts | 6 + 8 | 6 条字面量 POST,另有 8 条按常量数组循环注册 |
src/routes/tools.ts | 2 | /list(194)、/call(247) |
src/routes/llm-binding.ts | 3 | /set(40)、/status(95)、/list(103) |
src/routes/auto-sync.ts | 2 | GET /auto-sync/status(25)、POST /auto-sync/trigger(38) |
16 + (6 + 8) + 2 + 3 + 2 = 37。两处口径差 9 条。
这里只陈述差异,不推断哪一处「才是对的」,也不推断为什么两边没对上。以我们实读的仓库状态为准:规格里是 28,代码里是 37。
为什么按字面量数会数少:那 8 条是循环注册的
这是最容易在自己复核时翻车的一步。src/routes/code-graph.ts 里能直接搜到的 POST 只有 6 条——/create(182) /list(218) /get(232) /update-meta(244) /sync(265) /delete(285)。剩下 8 条查询类路由不是一条条写出来的,而是在 MemoryKnowledge/src/routes/code-graph.ts:322-323 用一个 for 循环,按常量 CODEGRAPH_QUERY_TOOL_NAMES 逐个注册的。
这个常量本身定义在 MemoryKnowledge/src/routes/tools.ts:385-387,共 8 个名字,不含 get_info。作为对照,同文件里的 CODE_GRAPH_TOOLS 是 9 个:get_info search explore callers callees impact node status files(MemoryKnowledge/src/routes/tools.ts:95-179)。WIKI_TOOLS 则是 7 个:get_info search list_pages read_page get_graph list_raw read_raw(同文件 49-93 行)。
所以你要是只用编辑器全局搜 app.post(,/code-graph 这一组会少数 8 条,正好把 37 数成 29。这一条常量被注释标为「单一真相源」,另有一个 toCodeGraphToolName() 把外部短名映射成内部名 codegraph_<name>(MemoryKnowledge/src/routes/tools.ts:393-395)。
差的九条:规格里查不到的部分
把两边对齐,规格里查不到、而代码里注册了的,正好是这九条:
| 方法 + 路径 | 代码位置 |
|---|---|
POST /wiki/update-meta | MemoryKnowledge/src/routes/wiki.ts:132 |
POST /code-graph/update-meta | MemoryKnowledge/src/routes/code-graph.ts:244 |
POST /tools/list | MemoryKnowledge/src/routes/tools.ts:194 |
POST /tools/call | MemoryKnowledge/src/routes/tools.ts:247 |
POST /internal/llm-binding/set | MemoryKnowledge/src/routes/llm-binding.ts:40 |
POST /internal/llm-binding/status | MemoryKnowledge/src/routes/llm-binding.ts:95 |
POST /internal/llm-binding/list | MemoryKnowledge/src/routes/llm-binding.ts:103 |
GET /auto-sync/status | MemoryKnowledge/src/routes/auto-sync.ts:25 |
POST /auto-sync/trigger | MemoryKnowledge/src/routes/auto-sync.ts:38 |
有意思的是 tools 这一组。README 自述的四类能力里就明确列了 POST /v3/tools/list 与 /v3/tools/call(MemoryKnowledge/README.md:10-16),而这两条在 openapi.yaml 的 paths 里找不到。
另外,/internal/llm-binding 这一组的源码注释写得很直白:这些是内部端点,「KS trusts the internal network like its other routes; no extra auth layer is added here」(MemoryKnowledge/src/routes/llm-binding.ts:15-17)——即这组路由没有额外鉴权层。这句是源码原文,照实记下来;至于部署时怎么处置,属于你自己环境里的事。
复核动作:三步,都能自己跑
第一步,数规格。在 MemoryKnowledge/ 目录下:
python -c "
import re
y=open('openapi.yaml',encoding='utf-8').read()
print(len(re.findall(r'^ (/[\w/-]+):', y, re.M)))
"
我们跑出来是 28。
第二步,确认那九条真的不在规格里。最省事的是直接判存在性——我们用 'update-meta' in openapi.yaml 复核,结果是 False。tools、llm-binding、auto-sync 同理各搜一遍即可。
第三步,数代码。用正则扫 src/routes/ 下的 app.post / app.get 字面量,得到 wiki 16、code-graph 6、tools 2、llm-binding 3、auto-sync 2 共 29 条;然后必须手工把 code-graph.ts:322-323 那个循环注册的 8 条加回去,才是 37。
别把这三条也算进 37
/v3 是前缀,不是全部。src/server.ts 的挂载顺序是:accessLog() 全局中间件 → onError(errorHandler) → /health(无前缀)→ 新建一个 api 实例挂 /wiki、/code-graph、/tools、/internal/llm-binding 与 auto-sync 的根路由 → 最后 app.route(config.apiPrefix, api) 统一加前缀(MemoryKnowledge/src/server.ts:56-96)。
前缀之外还有三条:GET /health(MemoryKnowledge/src/routes/health.ts:10,返回 { status: "ok", timestamp })、Swagger UI 的 /docs、以及 /openapi.json(MemoryKnowledge/src/server.ts:100-111)。数 37 的时候这三条不在其中。
顺带记一笔:/openapi.json 这个 handler 返回的 Content-Type 写的是 application/yaml(MemoryKnowledge/src/server.ts:105)——路径后缀与响应头声明的类型不一致,只陈述这一点。
还有一层要注意:/v3 本身是可配的。API_PREFIX 的默认值是 /v3(MemoryKnowledge/src/config.ts:154),README 也写「默认端口 8421,API 前缀 /v3」(MemoryKnowledge/README.md:6),两处一致。但 MCP 那侧的 HTTP 客户端拼 URL 的写法是 ${baseUrl}/v3${endpoint}——/v3 是写死的字符串(MemoryKnowledge/src/mcp/http-client.ts:36)。同一个文件里,请求头只设了 Content-Type,有 token 时加 Authorization: Bearer(37-38 行),我们在其中没有找到设置 x-tdai-service-id 的代码;而被调用的那些路由在缺该头时会返回 400(例如 MemoryKnowledge/src/routes/wiki.ts:470)。
还有一处规格与代码的错位
如果你正在用这份 spec 生成客户端,ServiceIdHeader 这一处也值得一起看。
MemoryKnowledge/openapi.yaml:716-725 定义了 components.parameters.ServiceIdHeader,且标了 required: true。但全文 ServiceIdHeader 只出现 2 次:一次是定义处,另一次是 IdFields 描述里的一句「See the ServiceIdHeader parameter」(754-755 行)。全文 parameters: 也只出现 1 次,就是 components 里那次——28 个操作没有任何一个写 parameters: - $ref: '#/components/parameters/ServiceIdHeader'。
代码那边则是每个 handler 各自读 header 并在缺失时返回 400(例如 MemoryKnowledge/src/routes/wiki.ts:61)。常量名是 SERVICE_ID_HEADER = "x-tdai-service-id"(MemoryKnowledge/src/api-helpers.ts:12),白名单正则 ^[A-Za-z0-9_-]+$、最长 200(同文件 20-21 行),注释写明这是为了防路径穿越——因为这个 id 会被拼进 data/{service_id}/{team_id}/{resource_id}/ 的目录路径里(14-19 行)。规格描述里也写了这条要求:每个端点都要在 x-tdai-service-id 请求头里带租户身份,缺失或格式错返回 400(MemoryKnowledge/openapi.yaml:11-16)。
也就是说,这个约束在规格的散文描述里有、在代码里有,但没有落到规格的结构化字段上。从生成器的角度看,读散文和读 parameters 不是一回事。同样,只陈述差异,到此为止。
同一个模块里,「有多少个能力」有三套数
再多说一层,免得你在别处看到别的数字时以为自己数错了。这个模块对外暴露能力的口径不止 HTTP 路由这一套,至少有三套彼此独立的计数,各有各的定义处:
- HTTP 路由:
/v3前缀下 37 条,前缀外/health、/docs、/openapi.json三条。定义处是src/routes/下各文件加src/server.ts:56-96的挂载。 - Tools 自发现清单:
WIKI_TOOLS7 个、CODE_GRAPH_TOOLS9 个,都定义在MemoryKnowledge/src/routes/tools.ts:49-93与95-179。它们是通过POST /v3/tools/list与/v3/tools/call这两条路由对外的,传入未知工具名会返回 403(MemoryKnowledge/src/routes/tools.ts:270-271,282-283)。 - MCP 工具清单:
MCP_TOOLS12 个——code_searchcode_explorecode_callerscode_calleescode_impactcode_nodecode_statuscode_fileswiki_searchwiki_readwiki_listwiki_graph(MemoryKnowledge/src/mcp/tools.ts:25-220)。这一套走StdioServerTransport,MCP server 自报的身份是{ name: "knowledge-mcp", version: "0.1.0" }(MemoryKnowledge/src/mcp/server.ts:34),实现上是把工具调用转成 HTTP 请求打回来(同文件 63 行),转发地址取KNOWLEDGE_API_URL,默认http://localhost:8421(94-95 行)。
三套数字互不相等,各自的定义处也不在一处:一套在 src/routes/ 加 src/server.ts 的挂载,一套在 src/routes/tools.ts 的两个常量数组,一套在 src/mcp/tools.ts。这里只记录这三套计数分别是多少、写在哪儿,不去推断它们之间该是什么关系。要紧的是你在文档、issue、对接单里写数字时,把「哪一套」说明白。
顺手记住的另外两个数字口径
同一模块里还有两处数字在不同层不一致,排查时容易撞上:
一是端口。模块内四处都是 8421——MemoryKnowledge/README.md:6、src/config.ts:150、Dockerfile:78、docker-compose.yml:19;而仓库根 INSTALL.md:58 的端口表写的是 8424,INSTALL.md:91 给的 Swagger 地址是 http://localhost:8424/docs。
二是「4 张表」。MemoryKnowledge/src/db/schema.ts:2-8 的头注释写「4 SQLite tables」,src/db/client.ts:46 的注释写「for all 4 tables + indexes」,.env.example:13 也写 4 张;而 schema 里在 131 行还定义了第五张 llm_binding,client.ts 的函数体里建的也是 5 张。
这两条和端点数是同一类东西:文档层、配置层、代码层各写各的,取数之前先确认自己读的是哪一层。 这也是本模块里最实用的一条经验——凡是要把某个数字写进代码或写进工单,先问一句它来自哪个文件的第几行。
延伸阅读
- 从头读起:TencentDB Agent Memory 是什么:团队级 Agent 记忆中枢怎么读
- 本专题共 40 篇,完整分组目录见专题页
- TencentDB Agent Memory 的检索实现:FTS5 加 bm25 再做图扩展
- TencentDB Agent Memory 三处都写 4 张表,实际建了 5 张
本文依据 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,参数与接口随版本变动,请以仓库最新内容为准。
该项目会采集并存储团队的对话、文档与代码,属于敏感数据,是否使用请结合自身合规要求评估。
安全与合规相关做法请结合自身环境评估,本文不构成安全方案建议。