TencentDB Agent Memory 的构建产物名:三处写法不一致
翻一个陌生仓库的时候,最容易被忽略也最容易卡住的,往往不是那些写在 README 首屏的能力表,而是「这个服务到底是用哪条命令、跑哪个文件起来的」。TencentDB Agent Memory 的 MemoryKnowledge 模块就有这么一处:同一个服务的启动入口,在仓库里被写成了两种文件名,dist/server.js 和 dist/server.mjs,而且不是随口一提,是分别落在包描述、bin 脚本、容器入口脚本这三种会被真正执行到的地方。
这篇只做一件事:把这几处原文、文件路径和行号摆出来,再给出你自己去核的动作。不推断哪一处是对的,也不推断为什么会这样。
先把坐标对齐
先交代清楚我们在看哪一份东西,不然行号对不上。
我们采集的时间锚点是 2026-08-16,对应仓库快照 97f9465。要特别注意的是,这个仓库的默认分支是 feat/server_team,不是 main 也不是 master——本文所有路径与行号都指这个分支上的状态。
MemoryKnowledge/ 这个目录对应的包名是 @tencentdb-agent-memory/knowledge-service,package.json 里的 version 是 0.1.0(MemoryKnowledge/package.json:2-3)。它自述是 monorepo 内的 Knowledge Service,也就是用户侧的 Wiki + Code-Graph 引擎,管控面在 ../MemoryPanel(MemoryKnowledge/README.md:3-4)。整个项目处于 beta 阶段,主模块版本是 2.0.0-beta.1,本模块与另外两个模块还是 0.1.0;换句话说,下面提到的这些文件名、脚本、字段随版本变动是完全可能的,以你手上仓库的最新内容为准。
四处口径,逐处摆出来
| # | 位置 | 原文写的是 |
|---|---|---|
| 1 | MemoryKnowledge/package.json:6 | "main": "./dist/server.js" |
| 2 | MemoryKnowledge/bin/server.mjs:2 | import "../dist/server.js"; |
| 3 | MemoryKnowledge/docker/entrypoint.sh:238 | exec node dist/server.mjs |
| 4 | deploy/panel-knowledge-combined/start-combined.sh:101 | node "$(test -f dist/server.js && echo dist/server.js || echo dist/server.mjs)" |
第 1、2 处是一致的,都是 .js。package.json 的 type 字段是 module,main 指向 ./dist/server.js;同时它声明了两个 bin,knowledge-server → ./bin/server.mjs,knowledge-mcp → ./bin/mcp.mjs(MemoryKnowledge/package.json:5-10)。而 bin/server.mjs 这个文件本身只有两行,第二行就是 import "../dist/server.js";。注意这里有个容易看花眼的地方:bin 脚本自己叫 .mjs,它 import 的目标却是 .js,这两个扩展名指的不是一回事,一个是仓库里手写的壳,一个是构建产物。
第 3 处是容器路径。MemoryKnowledge/docker/entrypoint.sh 全文 241 行,作用是把命令行 flag 映射成环境变量(例如 --public-url、--llm-mode、--llm-key),最后一句执行的是 exec node dist/server.mjs。而镜像的 ENTRYPOINT 正是 ["/usr/bin/tini","--","/usr/local/bin/docker-entrypoint.sh"],CMD 是 ["start"](MemoryKnowledge/Dockerfile:88-89)。也就是说,容器方式起服务时走的是这一行,不是 package.json 的 main,也不是 bin 脚本。
第 4 处是同仓另一个部署目录里的合并镜像启动脚本,它没有二选一,而是两种都试:先 test -f dist/server.js,存在就用它,否则退回 dist/server.mjs。紧挨着它的上一行还把端口换了个变量名传进去——PORT="${KNOWLEDGE_PORT}"(deploy/panel-knowledge-combined/start-combined.sh:100)。
以上四处就是全部差异。我们只陈述这四处写法不同,不判断哪一处与实际产物一致。
产物到底叫什么,看构建配置那一行
想知道 dist/ 下最终生成的文件叫什么,得看构建配置。MemoryKnowledge/tsdown.config.ts 全文 30 行,关键字段是:
entry: ["./src/server.ts", "./src/mcp/server.ts"],
outDir: "./dist",
format: "esm",
platform: "node",
clean: true,
fixedExtension: true,
dts: false,
sourcemap: false,
其中第 18 行是 fixedExtension: true,配合 format: "esm"。这两个字段合起来会产出什么扩展名,属于 tsdown 这个构建工具自己的语义,我们没有构建过这个模块,也不替它下结论,请以 tsdown 官方文档和你本地构建后 dist/ 的实际内容为准。这里只记录一个事实:仓库里同时存在指向 .js 与指向 .mjs 的启动写法,而构建配置显式开了 fixedExtension。
配置里另外两点顺带记一下:clean: true 表示构建会清目录;deps.neverBundle 把所有 node: 内建以及 package.json 里声明的依赖全部排除出 bundle(MemoryKnowledge/tsdown.config.ts:12-29)。
你自己怎么核这一处
这一段是可执行的判定动作,不需要任何推断:
- 先确认你走的是哪条启动路径。 用
pnpm/npm的 bin(knowledge-server)起,走的是第 2 处;用官方 Dockerfile 起,走的是第 3 处;用deploy/panel-knowledge-combined/那套合并镜像起,走的是第 4 处。三条路径读的是不同的文件,不要混着看。 - 构建完直接列目录。 在
MemoryKnowledge/下执行构建后,看dist/里到底是server.js还是server.mjs(顺带看dist/mcp/下那个)。这是唯一能一锤定音的动作,比读任何一处文档都直接。 - 比对上面那张表的四行原文。 打开这四个文件的对应行号,确认在你手上的版本里它们是否仍然不同——版本在变,差异可能已经不在了。
- 注意镜像里没有宿主机的 dist。
MemoryKnowledge/.dockerignore的 1-11 行把*.md、docs、__tests__、*.test.ts、dist、coverage等排除在构建上下文之外,按.dockerignore的语义,你本地已有的dist/不会被带进镜像。所以在容器里核产物名,要在容器内部核,不能拿宿主机的结果去推。
顺带一提:MCP 那个入口是同一个模型
这个模块有两个可执行入口。除了 HTTP 服务,还有一个走 stdio 的 MCP 进程:bin/mcp.mjs 同样只有两行,第二行是 import "../dist/mcp/server.js";。而 tsdown.config.ts 的 entry 数组里的第二项正是 ./src/mcp/server.ts。也就是说,两个入口共用同一份构建配置,产物扩展名这件事对它们是同一件事——你核 dist/server.* 的时候,顺手把 dist/mcp/server.* 一起核了,成本是零。
MCP 进程还有个独立的配置面:转发地址 KNOWLEDGE_API_URL 默认 http://localhost:8421,可选 KNOWLEDGE_API_TOKEN(MemoryKnowledge/src/mcp/server.ts:94-95)。这跟启动文件名没关系,只是提醒你别把两套环境变量搞混。
什么情况说明你碰到的不是这件事
排查文章最该写清楚的是边界。以下这些现象,不是由构建产物文件名引起的,别往这条线上排:
- 服务起来了但端口不对。 这个模块自己的默认端口是 8421——
MemoryKnowledge/README.md:6、src/config.ts:150、Dockerfile:78、docker-compose.yml:19都是 8421;而仓库根INSTALL.md:58的端口表写的是 8424,deploy/global-images/.env.example:50里的变量是KNOWLEDGE_PORT=8424。这是另一处口径差异,与文件名无关。 - 依赖装不上或原生模块编译失败。
MemoryKnowledge/start.sh那 80 行里有手工编译better-sqlite3的步骤,还写死了若干本机路径;这属于依赖构建,不是入口文件名。 - 接口返回 400。 每个 handler 都要求请求头带
x-tdai-service-id(MemoryKnowledge/src/api-helpers.ts:12),缺了就 400。服务本身是起着的。 - 调 LLM 报错说缺 key 或 baseUrl。
ingest-v2/llm.ts:51-53明确写了缺apiKey或baseUrl直接抛错、不再兜底读process.env。这也是运行期的事。
反过来,如果你看到的是「node 报找不到模块 / 找不到文件」,而且报的那个路径正好是 dist/server.js 或 dist/server.mjs 其中之一,那才落在本文说的这条线上——此时按上面第 2 步列一次 dist/ 就能对上。
这类差异该怎么读
最后说个方法论,不是对这个项目的评价。
在一个还在 beta、默认分支是 feat/server_team 这种功能分支的仓库里,同一件事在不同层写法不同是能遇到的:这个模块里还有端点数(规格自称 28 条,代码在 /v3 下注册了 37 条)、默认模型名(Memory-Model 与 gpt-4o-mini 等多处不同)、LLM_MAX_TOKENS 默认值(src/config.ts:165 是 32768,ingest-v2/llm.ts:45 是 8192)几处同类差异。这些我们另有篇目专门讲。
对读者来说,可迁移的只有一条:文档里写的、配置里写的、和你机器上真实存在的文件,是三件事。 遇到对不上的时候,别急着挑一个信,去做那个能一锤定音的动作——列目录、看进程实际执行的命令行、在容器内部核。这一步做完,四处写法谁对谁错就不用猜了。
延伸阅读
- 从头读起:TencentDB Agent Memory 是什么:团队级 Agent 记忆中枢怎么读
- 本专题共 40 篇,完整分组目录见专题页
- TencentDB Agent Memory 的 llm_binding 表:schema 与建表 SQL 两套
- TencentDB Agent Memory 有向量检索吗:embed 与 vector 命中 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,参数与接口随版本变动,请以仓库最新内容为准。
该项目会采集并存储团队的对话、文档与代码,属于敏感数据,是否使用请结合自身合规要求评估。
安全与合规相关做法请结合自身环境评估,本文不构成安全方案建议。