- 人工智能
- AI Agent
- Agent 记忆
- RAG
- MCP 服务
【免费下载链接】agent-memory
Memory 是一款面向 AI 智能体的长期记忆模块,为运行在 openJiuwen 框架上的智能体提供记忆提取、存储、检索与迁移能力。
本文以 F02-packaging-unique-toplevel 这一打包治理决策记录为主体,详解 agent-memory 为什么放弃src/平铺布局、如何将内核目录改名为唯一的jiuwen_memory顶层包、如何用 setuptoolspackages.find收敛 whl 的top_level.txt,以及开发态pytest/PYTHONPATH、安装态 site-packages 与deploy/docker三套 Dockerfile/Compose 之间如何保持一致。读完本文,你可以掌握 Python 分发包(whl)顶层命名空间的设计权衡,并能在自有项目中复现「改名 + 打包 + 部署」三方同步的完整落地路径。
背景:通用顶层名在宿主环境被 shadow
agent-memory 早期(mem2.0 时期)采用src/{api,common,config,...}的平铺目录布局,并通过 setuptools 的include=api*之类规则打 whl。其直接后果是:构建产物在top_level.txt中暴露出 9 个通用顶层名(api、common、config等)。
问题出在 Python 的模块搜索机制:当这个包以 SDK 形式被嵌入宿主项目进程时,sys.path的搜索顺序会让宿主自带的同名目录优先命中。如果宿主项目恰好有common/、config/或api/目录,sys.path优先 shadow(遮蔽)了包内的同名模块,导致JiuwenMemorySDK 在进程内initialize阶段就直接抛出ModuleNotFoundError: No module named 'common.type_def'——包自己 import 自己的代码都失败了。
决策记录同时指出第二类不同步问题:内核目录改名之后,deploy/docker/*仍执行COPY src ./src、compose 挂载../../../src:/app/src,而仓库中已不存在src/目录,镜像在构建期即失败。这说明顶层改名不是一个孤立的 import 前缀问题,而是「打包产物 + 开发态 + 部署镜像」三方必须同步的工程问题。
目录布局决策:src/改名为jiuwen_memory/
核心决策第一条是:仓库内核目录src/整体改名为jiuwen_memory/,而不是采用src/jiuwen_memory/的嵌套形式。当前仓库顶层结构印证了这一点:
- jiuwen_memory/:内核包,内部包含
api/、common/、config/、construction/、control/、ingest/、retrieval/、storage/等子包; - jiuwen_memory_entry/:HTTP / CLI / MCP 等接入 surface;
- jiuwen_memory_adapter/:面向不同宿主框架的适配器。
配套的第二条决策是 import 语义统一:所有绝对引用统一为jiuwen_memory.*;开发态通过pytest.pythonpath = ["."]或PYTHONPATH=.直接以仓库根为搜索根,与安装态(包位于 site-packages 根)保持同一套 import 路径,从机制上消除「开发假绿、安装必挂」的测试分叉。仓库内示例代码可验证这一点,例如 quickstart.py 全部使用from jiuwen_memory.api import assemble形式的导入。
whl 顶层收敛:[tool.setuptools.packages.find]配置
打包侧的决策见 pyproject.toml:
[tool.setuptools.packages.find] where = ["."] include = [ "jiuwen_memory*", "jiuwen_memory_adapter*", "jiuwen_memory_entry*", ] exclude = [ "jiuwen_memory_entry.cli*", "jiuwen_memory_entry.mcp_server*", ]要点有三:
- include 白名单:
where = ["."]表示以仓库根为搜索根,只收录三个jiuwen_memory*前缀的包,因此构建出的 whl 其top_level.txt只包含jiuwen_memory、jiuwen_memory_adapter、jiuwen_memory_entry三个顶层名,不再暴露任何api/common/config等通用名; - 排除非分发布件:
jiuwen_memory_entry.cli与jiuwen_memory_entry.mcp_server两个 surface 被显式 exclude——CLI 与 MCP 表面有各自的运行脚本与可选依赖(如mcpextra),不随主 whl 分发; - 三个顶层包保持独立:决策第 3 条明确不把
jiuwen_memory_adapter/jiuwen_memory_entry并入jiuwen_memory包内,三者都是仓库根下的独立顶层包。安装后即可from jiuwen_memory_entry.xxx import ...。
bootstrap 与 flat path-hack:surface 侧的过渡方案
决策第 4 条为接入面补上了包化能力:补齐 jiuwen_memory_entry/init.py 并在 include 规则中收录jiuwen_memory_entry*。
但 surface 侧保留了「flat import」的历史形态。以 HTTP surface 的 入口文件 为例:
# 共享应用核(server / profiles / handler / config_loader)住在 jiuwen_memory_entry/core; # 加入 sys.path 后 flat-import 复用——本 surface 只做 HTTP 传输。 _CORE_DIR = os.path.join(os.path.dirname(os.path.dirname(os.path.abspath(__file__))), "core") if _CORE_DIR not in sys.path: sys.path.append(_CORE_DIR)即__main__.py自行把jiuwen_memory_entry/core追加进sys.path,随后通过import_required("profiles")等动态导入复用core下的server/config_loader/auth_middleware模块。这也是为什么启动脚本不需要为 flatimport server额外设置环境变量:run-server.sh 与 run-mcp.sh 统一导出
export PYTHONPATH="${REPO_ROOT}:${REPO_ROOT}/jiuwen_memory_entry/core${PYTHONPATH:+:${PYTHONPATH}}"即PYTHONPATH=<repo>:<repo>/jiuwen_memory_entry/core,与文档元信息中记录的测试基线完全一致。需要注意的边界是:内核侧已经不可再把jiuwen_memory/目录当 flat root 使用——内核模块一律走jiuwen_memory.*的绝对命名空间,flat-import 仅残留在 bootstrap surface 层。
这一层「接入面不得反向依赖内核内部」的约束由测试固化:test_import_isolation.py 通过 AST 扫描断言jiuwen_memory_entry/core/import_support.py中的任何from jiuwen_memory.xxx import .../import jiuwen_memory.xxx语句都属违规,从源码结构看这正是为守护「Access 只允许依赖jiuwen_memory.api」这条边界。
deploy/docker 同步:COPY、editable 安装与热更新挂载
决策第 5 条要求部署文件与 rename 全量同步。当前仓库三套部署文件已全部落地为jiuwen_memory路径,且deploy/下已无任何src残留引用(可用rg "src" deploy复核)。
以 local/Dockerfile 为例:
COPY pyproject.toml ./ COPY jiuwen_memory ./jiuwen_memory COPY jiuwen_memory_entry ./jiuwen_memory_entry ... RUN --mount=type=cache,target=/root/.cache/pip \ pip install --upgrade pip \ && pip install torch==2.12.1+cpu --extra-index-url https://download.pytorch.org/whl/cpu \ && pip install -e ".[embed,deploy]"关键机制在注释中写明:项目采用editable 安装,site-packages 中只保留指向/app/jiuwen_memory的链接。因此 compose 挂载源码目录即可实现「改代码免重建镜像」:
# deploy/docker/local/docker-compose.yml → 服务 agent-memory volumes: - ./config.yml:/config/config.yml:ro - ../../../jiuwen_memory:/app/jiuwen_memory:ro # 替代原 src:/app/src - ../../../jiuwen_memory_entry:/app/jiuwen_memory_entry:ro见 local/docker-compose.yml。online与postgres两套 Compose 采用同样的双挂载(见 online/docker-compose.yml、postgres/docker-compose.yml),三套 Dockerfile 也都是COPY jiuwen_memory ./jiuwen_memory+pip install -e .的组合,HTTP 入口统一为python jiuwen_memory_entry/http_server/__main__.py。
由于__main__.py自己 append 了jiuwen_memory_entry/core,Dockerfile 中不再为 flatimport server额外设置PYTHONPATH环境变量——这避免了镜像内环境变量与代码内 path-hack 双轨并行的漂移风险。文档注释层面,deploy/docker/README.md 中指向配置模块的描述也已从src/config更新为jiuwen_memory/config。
拒绝的方案及其原因
决策记录给出了五个被否决的替代方案,其否决理由值得在类似项目中直接参考:
| 方案 | 否决原因 |
|---|---|
只改packages.find、不改 import | 安装后 site-packages 中仍无顶层common包,包内from common ...导入必然失败 |
继续PYTHONPATH=jiuwen_memory+ flat-import | 开发态与 site-packages 安装态语义分叉,测试「假绿」,宿主 shadow 问题(#117)会复现 |
把jiuwen_memory_adapter/jiuwen_memory_entry挂到jiuwen_memory.*下 | 本轮明确要求二者保持仓库根独立顶层,便于按 surface 独立分发与排除 |
| 只靠文档约定「宿主勿用 common 目录」 | 通用名冲突在生态中无法靠约定避免,属于反模式 |
deploy 继续COPY src或软链src→jiuwen_memory | 与仓库真实布局分叉,editable 挂载与 CI 容易漏改;软链在 Windows 与部分构建上下文下不可靠 |
验证方法与可复现的测试基线
文档「验证」一节给出的四条基线均可在当前仓库直接复现:
- whl 顶层检查:构建 wheel 后检查
top_level.txt,应无api/common/config等旧通用名,且包含jiuwen_memory、jiuwen_memory_adapter、jiuwen_memory_entry三个顶层名; - 宿主 shadow 回归测试:在临时目录放置空
common/并将其置顶PYTHONPATH,验证import jiuwen_memory.api与import jiuwen_memory_entry.core仍可用——这条直接复现并回归了 #117 的故障场景; - 单元/契约测试:
pytest -q -m unit tests/unit全量通过; - 部署构建:
deploy/docker下无残留src路径引用;docker compose -f deploy/docker/online/docker-compose.yml build agent-memory能通过COPY jiuwen_memory构建阶段(完整运行栈需要 Milvus / ES 等依赖服务,但build本身不依赖它们)。
其中 pytest 侧的支撑来自 pyproject.toml 的pythonpath = ["."]与unit/integration两个 marker:开发态以仓库根为搜索根,import 语义与安装态一致。
已知遗留与破坏性变更边界
文档「已知遗留」一节如实标注了本次治理的边界,读者在集成时应注意:
- bootstrap surface 仍有 flat path-hack:
jiuwen_memory_entry/core的 sys.path 追加与动态导入尚未完全包化,属于已确认的过渡形态; - 顶层名仍偏通用:
jiuwen_memory_adapter/jiuwen_memory_entry虽已加项目前缀,但在极端宿主命名冲突下理论上仍可能被遮蔽; - 迁移历史保留:文档前文保留
src/与旧顶层 import 的描述作为迁移历史证据,但文档内所有可执行 import 示例已统一为jiuwen_memory.*前缀,后续新增示例也须遵循该约定; - 对外 breaking change:这是面向调用方的破坏性变更——既有集成方必须把 import 前缀从旧的顶层名(如
common.xxx、api.xxx)改为jiuwen_memory.*,且src/路径不存在,旧的PYTHONPATH=src式接入方式失效。
综上,agent-memory 的这套打包治理示范了一个通用工程结论:分发包的顶层命名空间必须与项目品牌绑定(unique toplevel),并让「whl 打包规则、开发态搜索路径、部署镜像 COPY/挂载」三者共享同一套目录事实,任何一侧滞后都会在构建期或宿主集成期以ModuleNotFoundError的形式暴露出来。
- 人工智能
- AI Agent
- Agent 记忆
- RAG
- MCP 服务
【免费下载链接】agent-memory
Memory 是一款面向 AI 智能体的长期记忆模块,为运行在 openJiuwen 框架上的智能体提供记忆提取、存储、检索与迁移能力。
相关推荐
mruby 顶层命名空间治理:mruby-toplevel-ext 如何将顶层 include 私有化
mruby 顶层命名空间治理:mruby toplevel ext 如何将顶层 include 私有化 mruby toplevel ext 是 mruby 官
编程语言语言运行时编译器解释器嵌入式GameShell:终极Unix shell学习游戏 - 通过游戏化方式掌握Linux命令的完整指南
GameShell:终极Unix shell学习游戏 通过游戏化方式掌握Linux命令的完整指南 GameShell是一款创新的Unix shell学习游戏,它
人工智能AI Agent多智能体Agent 编排Agent 记忆工具调用代码智能体MCP 服务AI 评测TencentDB-Agent-Memory 插件包名迁移实战:从 @tdai/memory-tdai 平滑升级到 @tencentdb-agent-memory/memory-tencentdb
TencentDB Agent Memory 插件包名迁移实战:从 @tdai/memory tdai 平滑升级到 @tencentdb agent memor
人工智能大模型AI AgentAgent 记忆后端前端MCP 服务
创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考