agent-memory 召回路装配内收:CompositeStorage 工厂化装配 Recaller 的架构设计与循环依赖破解

发布时间:2026/10/14 21:02:49

agent-memory 召回路装配内收:CompositeStorage 工厂化装配 Recaller 的架构设计与循环依赖破解 人工智能AI AgentAgent 记忆RAGMCP 服务【免费下载链接】agent-memoryMemory 是一款面向 AI 智能体的长期记忆模块为运行在 openJiuwen 框架上的智能体提供记忆提取、存储、检索与迁移能力。项目地址https://gitcode.com/openJiuwen/agent-memory点击查看免费下载本文以 agent-memory 项目特性文档 F06-composite-recaller-assembly.md 为骨架结合jiuwen_memory/storage与jiuwen_memory/retrieval的当前源码实现、默认装配配置与测试基线完整讲解「多路 Recaller 召回路装配从 Retriever 工厂内收到 Storage/StoreManager 工厂」这一设计决策。读完你将掌握七个*_recaller选择键与三个能力开关的配置语义、具名/匿名构建下循环依赖的双预注册破解方案、PipelineRetriever不再持有召回路后的委托模型以及手工接线与构建期 fail-fast 的测试验证方式。背景统一 Storage 落地后的装配错位在 F05-unified-storage-design 统一 Storage 落地之后PipelineRetriever的生产装配仍按旧世界做事retriever 工厂自己组装 keyword/vector/graph/L0/L1 各路 Recaller再通过isinstance(storage, CompositeStorage)判断后调用bind_recallers把召回路塞回 storage。这种装配方式带来三个结构性问题责任错位recall/get/rank 三条首选路径的执行都在 Storage 内唯独召回路的组装留在 Retriever 工厂——CompositeStorage的能力开关如vector_enabled要由别人替它解释能力声明与实际装配脱节。非 Composite 实现被拖进无关概念一体化 Storage 自带检索路径根本不需要 Recaller旧装配却仍先组装一整组 recaller再发现无处可绑白白浪费构建成本并造成概念污染。构造签名被迫膨胀PipelineRetriever构造签名被迫携带recallers参数并在构造期做「无 storage 就用 unit_reader.kv 临时拼一个 CompositeStorage 再绑 recaller」的兼容逻辑把一个装配期问题泄漏进运行期对象。核心决策五条设计主线针对上述问题F06 确立了五条相互咬合的决策其最终形态在当前仓库代码中完整落地。决策 1召回路装配内收到 Storage 工厂StorageProducer.register(composite)的_build按配置组装 recaller。在 F07 拆分后该装配入口位于 composite_store_manager.py 的from_config召回路组装逻辑收敛在 composite_domain_store.py 的_assemble_recallers与for_manager能力开关vector_enabled/graph_enabled/layers_index_enabled经config.get回退 globals属于跨切面意图开关recaller 选择键keyword_recaller/vector_recaller/graph_recaller/keyword_l0_recaller/keyword_l1_recaller/vector_l0_recaller/vector_l1_recaller共七个落在storage.default.paramsF07 后进一步下移到domain_stores.default。这组键相应从defaults.py的retriever.default.params移到了 storage 侧retriever段只保留本算子私有调参over_fetch_factor、recall_max、rerank_max等。默认装配下会同步组装出 7 路 recaller。决策 2构建期同步组装 双预注册打破循环recaller builder 内部会StorageProducer.resolve(config)回取本 Storage 实例而build_named没有「构建中」检测——如果装配期直接同步组装会形成「装配 CompositeStorage → 组装 recaller → recaller 回取 Storage → 再建一个 CompositeStorage」的无限递归。破解方案是_build先把构建中的实例预注册进具名缓存再组装且区分具名/匿名两条路径。决策 3PipelineRetriever 不再持有召回路构造签名删除recallers参数与recallers属性retrieve只按storage.preferred_retrieval_pipeline()委托recall/recall_and_get/retrieve。非 Composite 实现不再被拖进 recaller 概念。storageNone回退保留为CompositeStorage(kvunit_reader.kv)无召回路仅供只走点读/短路路径的调用。决策 4bind_recallers 保留为手工/测试接线口recaller 构造需要 storage 实例如KeywordRecaller(storage)手工接线仍是「先建 storage、再建 recaller、再 bind」绑定后惰性 loader 作废。重绑守卫不变同一实例不允许绑定两套不同 recaller。决策 5模块依赖方向不破composite_storage.py只在_assemble_recallers函数体内惰性from jiuwen_memory.retrieval.recaller import RecallerProducer模块层面 storage 仍不导入 retrieval。F07 E 组把 Recaller 整体移入 storage 数据面后这条延迟导入连同它绕开的循环一起消失——这正是当前代码中Recaller契约与实现已经位于 recaller.py 的原因。配置详解七个选择键与三个能力开关召回路装配的完整默认配置位于 defaults.py。F07 参数归位后数据面的全部装配参数住在store_manager.inst.params.domain_stores.name默认形状如下globals: schema_enabled: false vector_enabled: true graph_enabled: true rerank_enabled: true embedder_dim: 64 chunk_size: 120 store_manager: default # 全局唯一 StoreManager 指名 store_manager: default: target: composite params: domain_stores: default: # 数据面 default 必建命名实例 overlay 在它之上 kv_store: default preferred_retrieval_pipeline: recall_get_rank keyword_recaller: keyword vector_recaller: vector graph_recaller: graph keyword_l0_recaller: keyword_l0 keyword_l1_recaller: keyword_l1 vector_l0_recaller: vector_l0 vector_l1_recaller: vector_l1下表给出全部装配参数的语义参数取值/默认语义kv_store端口名字符串默认default数据面真源 KV 端口名与其余消费方同一套选择键机制运行期不硬编码defaultpreferred_retrieval_pipelinerecall_get_rank/recall_and_get_rank/retrieve数据面实例级稳定的首选检索路径见 retrieval.py 的RetrievalPipeline枚举keyword_recallerkeyword关键词/全文通道L2 content包一个FulltextStorevector_recallervector向量语义通道L2 content chunk包一个VectorStoregraph_recallergraph图遍历通道关键词种子 → 1 跳邻居候选keyword_l0_recallerkeyword_l0预生成概要分表的全文倒排召回keyword_l1_recallerkeyword_l1预生成片段分表的全文倒排召回vector_l0_recallervector_l0概要整段向量分表召回vector_l1_recallervector_l1片段整段向量分表召回recaller 具名实例声明在recaller:顶层命名空间target 与注册名在 F07 迁移后逐字不变YAML 配置零迁移recaller: keyword: { target: keyword } keyword_l0: { target: keyword_l0 } keyword_l1: { target: keyword_l1 } vector: { target: vector, params: { min_similarity: 0.0 } } vector_l0: { target: vector_l0 } vector_l1: { target: vector_l1 } graph: { target: graph }三个能力开关的生效逻辑见_assemble_recallersvector_enabledfalse时不装vector_recaller/vector_l0_recaller/vector_l1_recallergraph_enabledfalse时不装graph_recallerlayers_index_enabledfalse时不装 L0/L1 四路keyword_l0/l1、vector_l0/l1。注意一个易踩的静默退化陷阱RecallerProducer.dep读的是config.params直读不回退 globals因此domain_stores的命名 entry 必须先 overlay 在defaultentry 之上再传入。若命名 entry 漏掉*_recaller键dep会落到cls.build(default, {}, ctx)匿名新建一套不共享、params 为空的 recaller——装配不报错读写悄悄用上两套实例测试 test_named_domain_stores.py 中test_named_entry_shares_recaller_instance_with_default用对象身份is断言而非通道名来锁死该退化。源码级原理循环依赖的具名/匿名双预注册Factory.build_named见 factory.py没有「构建中」检测命中缓存即返回共享实例未命中则新建并入缓存。这意味着装配期直接同步组装 recaller 时recaller builder 内部的StorageProducer.resolve(config)会触发再建一个 CompositeStorage形成递归。_assemble_recallers按构建形态拆成两条分支具名构建config.name非空CompositeStoreManager.from_config先StoreManagerProducer.put(config.name, manager)把构建中的实例预注册进具名缓存recaller 命名空间下声明的具名实例recaller.keyword等带store_manager: name字符串引用RecallerProducer.dep走build_named命中缓存StoreManagerProducer.resolve在 builder 内回取成功打破循环。匿名构建无config.name如IndexBuilderProducer调StorageProducer.resolve落到第三分支构建无缓存键dep缺省会回落cls.build(default, {}, ctx)再次触发 recaller builder 用空 params 走StorageProducer.resolve落第三分支再建一个匿名 CompositeStorage → 递归。破解方案是用合成名__anon_store_manager_{id(storage)}__id保证唯一预注册本实例改走RecallerProducer.build(target, {store_manager: synthetic_name}, ctx)直接注入 storage 引用让 builder 内resolve走第一分支cls.dep命中合成名缓存。当前实现见 composite_domain_store.py_assemble_recallers的synthetic_name分支。装配错误选择键指向未注册实现、必填插件缺失等在构建期 fail-fast 暴露不拖到首次召回。这正是测试 test_storage_factory_wiring.py 中test_recaller_assembly_error_surfaces_at_build_time锁住的行为把vector_recaller指向nonexistent_impl后StoreManagerProducer.build_named(default, context)在构建期直接抛ValidationError。PipelineRetriever 的委托模型当前 pipeline_retriever.py 的构造签名已不含recallers只收parser/fuser/discloser/unit_reader/reranker与检索调参以及 keyword-only 的domain_storeF07 后 Retriever 运行期只持数据面。_build工厂只做两件事经StoreManagerProducer.resolve取全局 manager再用manager.domain_store(resolve_name(config, domain_store))取数据面注入。retrieve的召回段按self._domain.preferred_retrieval_pipeline()三选一首选路径行为RECALL_GET_RANK先recall多路并行召回 ScoredUnit→ 再get物化真源 复核 → fuser 融合RECALL_AND_GET_RANK数据面内一次完成recall_and_getRetriever 侧复核后直接融合RETRIEVE数据面内完成 recall get rank传入 fuserRetriever 只收已融合候选多路并行与结果合并由 composite_domain_store.py 的recall负责按channels过滤已装配 recallerThreadPoolExecutor并行提交逐路捕获异常记入ChannelError全路失败才抛StorageRetrievalError部分成功语义。每个 Recaller 对应一条召回通道互不感知其他通道。手工接线bind_recallers 的测试与定制路径bind_recallers仍是公开的手工/测试接线口F06 决策 4 不变装配路径改由for_manager内部调用。测试夹具 conftest.py 的make_world就是典型手工接线形态storage CompositeStoreManager(kvkv, vectorvector, fulltextfulltext) domain_store CompositeDomainStore( managerstorage, preferred_pipelineRetrievalPipeline.RECALL_GET_RANK ) keyword KeywordRecaller(storage) vector_recaller VectorRecaller(storage) domain_store.bind_recallers([keyword, vector_recaller]) # 先建 storage → 再建 recaller → 再 bind storage.bind_domain_store(domain_store)自定义召回路集成测试如 test_pipeline_retriever.py 的test_default_config_attaches_l0_l1_recallers、test_index_contract.py也走这条路径手工构造CompositeStoreManagerCompositeDomainStorebind_recallers注入故障注入型/固定候选型 recaller 验证通道级降级、并行性与 L0/L1 装配。验证体系F06 的验证覆盖在仓库中完整对应test_storage_factory_wiring.py默认装配下retriever.storage is storage且storage.recallers同步组装出 7 路globals 关闭三个能力开关后只剩 keyword 一路选择键指向未注册实现时构建期抛ValidationError。test_index_builder.pytest_fulltext_factory_*/test_vector_factory_*/test_unified_factory_*覆盖匿名 CompositeStorage 构建经StorageProducer.resolve第三分支合成名预注册打破循环、不触发递归。test_storage_pipelines.py、test_recallers.py、test_pipeline_retriever.py含keyword_recaller覆盖键移到 storage 段的用例、test_index_contract.py 全部适配并通过conftest.py的make_world改为 bind 手工接线。test_named_domain_stores.pydomain_stores段此前全仓零覆盖F07 补上 11 例其中test_named_entry_shares_recaller_instance_with_default锁死「命名 entry 继承 default 的 recaller 实例」这一静默退化。F06 文档记录的测试基线为.venv-wsl/bin/python -m pytest tests/unit tests/integration1318 passed / 82 skipped4 failed 均为 HEAD 既有问题2 个 bge_m3 缺 torch2 个 entity_linker 测试间干扰。F07 演进后基线为pytest tests/unit/1978 passed / 8 skipped、pytest tests/integration/48 passed / 61 skipped。拒绝的方案与取舍理由F06 明确记录了被否定的备选方案理解它们有助于把握内收设计的分寸装配期同步构建仅put预注册具名路径可行但匿名构建仍会经 recaller builder 回取storage.default递归构建默认实例。最终采纳时补上合成名预注册 直接RecallerProducer.build注入 storage 引用覆盖匿名路径。首次召回时惰性物化能彻底回避循环但装配错误推迟到首次召回才暴露违背「配置错误尽早暴露」原则且增加首次召回延迟与状态管理复杂度。保留 retriever 侧装配、仅按isinstance跳过非 Composite责任依然错位开关仍由 retriever 工厂替 storage 解释治标不治本。让 recaller 不再依赖 Storage改注入裸端口可彻底消除循环但要改全部 recaller 的构造契约与测试且违背「上层握共享 Storage 入口、勿构造期握死裸端口」的本地约束RoutingStorage 惰性代理依赖这一点。召回路用命名空间扫描替代显式选择键失去按配置裁剪/替换单路的能力且会把未接线入库的具名实例也拉进来。已知遗留与后续演进F07F06 的已知遗留当前依旧成立recaller 装配的运行时错误如距离型度量向量库被VectorRecaller在首次召回时拒绝、向量库连接不通等仍推迟到首次召回才暴露装配期 fail-fast 只覆盖选择键指向未注册实现、必填插件缺失等配置类错误运行时错误依赖冒烟/健康检查覆盖。注距离型度量的拒绝实际发生在构造期——vector_recaller.py 在__init__中检查score_higher_is_better()并抛ValidationError。匿名构建用id(storage)派生合成名预注册进_instances缓存装配后该缓存条目无人再引用recaller 已持直接引用属无害死条目Factory.reset_all在每次装配前清空。用户配置迁移写在retriever.*.params的*_recaller覆盖键不再生效需迁移到storage.*.paramsF07 后进一步迁至domain_stores.nameglobals 里的能力开关不受影响。后续演进见 F07-storage-manager-domain-store-split.md本文的召回装配链路整体保留仍由 manager 工厂末尾调_assemble_recallers仅符号随拆分更名CompositeStorage→CompositeStoreManager/CompositeDomainStore、StorageProducer.resolve→StoreManagerProducer.resolve、合成名__anon_storage_{id}__→__anon_store_manager_{id}__。F07 E 组进一步把Recaller契约与实现移入storage/domain_store_impl/_assemble_recallers的调用点下沉进CompositeDomainStore.for_manager本文决策 5 的惰性导入随之删除。值得强调的是F06 拒绝方案里「让 recaller 不再依赖 Storage改注入裸端口」始终未被采纳——E 组走的是相反方向不是把 recaller 从 storage 摘出去而是把它收进来。赞分享人工智能AI AgentAgent 记忆RAGMCP 服务【免费下载链接】agent-memoryMemory 是一款面向 AI 智能体的长期记忆模块为运行在 openJiuwen 框架上的智能体提供记忆提取、存储、检索与迁移能力。项目地址https://gitcode.com/openJiuwen/agent-memory点击查看免费下载相关推荐OpenStock 使用指南免费实时行情 价格提醒如何 10 分钟搭好盯盘流程OpenStock 使用指南免费实时行情 价格提醒如何 10 分钟搭好盯盘流程 OpenStock 是一款开源股票工具免费实时行情、板块热力图与自定义金融科技前端后端AI 应用Anki-Android 模块化架构中的 :anki-common打破循环依赖的共享层设计详解Anki Android 模块化架构中的 :anki common 打破循环依赖的共享层设计详解 导读 本文以 Anki AndroidAnkiDroid移动开发教育agent-memory 检索层实现规约深度解析QueryParser / Recaller / Fuser / Discloser / Retriever 的装配、语义与取舍agent memory 检索层实现规约深度解析QueryParser / Recaller / Fuser / Discloser / Retriever人工智能AI AgentAgent 记忆RAGMCP 服务上一篇Aptos Core 二进制发布与 cargo-binstall 集成指南让用户免编译安装预编译二进制下一篇Epic Stack 导入路径方案演进用 package.json 的 imports 字段配置 前缀路径别名创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考
延伸阅读

更多相关文章

2026/10/14 21:02:49

《县中的孩子》:看见县域教育生态的真实困境

书读到一半的时候,我好几次停下来。不是看不懂,是有些场景太熟悉了。教室后墙的倒计时、县城中学门口等孩子的电动车、晚自习熄灯后还在走廊背书的影子——这些画面明明离大城市很遥远,却又让人觉得哪儿哪儿都是。《县中的孩子》不是那种告诉…

2026/10/14 21:02:49

ST7789V STM32驱动实战:三种SPI接口与初始化序列避坑指南

简介:ST7789V 驱动例程以 STM32F429 为主控,面向需要在小尺寸 TFT-LCD 上实现图形显示的嵌入式开发者。例程覆盖 SPI 三线/四线和 GPIO 模拟 SPI 等通讯方式,初始化序列包含屏幕方向、像素格式、时序参数等关键命令,并提供点绘、画…

2026/10/14 21:02:49

React高可维护表单设计:Schema驱动+状态解耦+动态渲染

简介:这是一份面向前端初学者与JavaScript进阶学习者的交互式表单开发示例资源,聚焦于通过原生JavaScript实现具备动态验证、自定义UI、平滑动画及异步提交能力的‘花式表单’,解决传统表单体验单调、反馈滞后、移动端适配不足等常见痛点。压…

2026/10/14 22:34:22

RT-Thread信号量本质:资源协调而非加锁

1. 为什么信号量不是“加锁”而是“协调”——从一个死锁现场说起刚接触 RT-Thread 时,我下意识把信号量(Semaphore)当成和互斥锁(Mutex)差不多的东西:不就是“上锁—干活—解锁”三步走?直到在…

2026/10/14 22:34:22

CoNED沿海高程数据在GEE中的淹没模拟实践与避坑指南

做沿海洪水淹没评估、海平面上升情景推演、风暴潮风险制图这类工作,第一道坎几乎都卡在高程数据上。很多新手一开始去下载普通陆地DEM,拿到沿海区域一画就露馅:陆地部分看着还行,一到滩涂、河口、潮间带,要么是空的&am…

2026/10/14 22:34:22

两台Spark实测:16GB小主机如何跑稳长上下文Agent

两台 Spark 是什么?说白了,就是我自己手头两台配置差不多的本地算力小主机,我给它们起了个代号叫 Spark,一台负责跑推理服务,一台负责干 Agent 编排和杂活。这次专门拿它们来跑本地 Agent,核心就测一件事&a…

2026/10/14 22:34:22

Certbot泛域名SSL证书自动化部署与DNS-01验证实战

1. 为什么泛域名证书不再是“高级玩家专属”——从手动折腾到一键签发的实操跃迁你有没有过这样的经历:给公司新上线的五个子站(admin.example.com、api.example.com、docs.example.com、blog.example.com、staging.example.com)逐个申请SSL证…

2026/10/14 22:34:22

生产环境Agent编排的失败降级策略:从原理到代码实践

生产环境 Agent 编排:我为什么给每个环节都加了失败降级凌晨 2 点 47 分,监控告警把我从梦里拽了出来。生产环境的某个 Agent 工作流卡在了“调用外部工具”这一步,上下游全部堵死,队列里的任务越堆越多。我爬起来看了一眼日志&am…

2026/10/14 22:29:21

C#配书代码实战:从环境配置到模块拆解与异步改造指南

简介:《C#典型模块精解》配书源代码是一套面向C#初中级学习者的配套实例工程,集中展示从基础语法到高级特性的完整落地写法。内容覆盖变量与流程控制、面向对象三大特性、ADO.NET/Entity Framework数据访问、TCP/HTTP网络通信、多线程并发、Windows Form…

2026/10/14 11:31:03

Python调用Gemini Structured Outputs实现工单路由门禁

客服工单最怕的不是模型“答错一句话”,而是它给出一段看起来合理的说明,程序却从中猜错优先级。通俗做法是:要求模型只交 JSON(JavaScript Object Notation,轻量数据格式),再让代码验证它。Gem…

2026/10/14 17:41:07

Spring Boot超市进销存系统毕设实战:从需求拆解到答辩通关

最近带的一个学生项目组里,有A同学跑来问我:选什么毕设题目最稳妥,既能让评审老师觉得工作量够,又不会在答辩时被问到语无伦次。我第一反应就是推荐基于Spring Boot的超市仓库管理系统——也就是超市进销存系统。这个题目乍一看平…

2026/10/14 2:32:55

Flutter StatefulWidget 生命周期核心解析

很多刚开始接触 Flutter 的朋友,在看完一堆“Hello World”和基础组件之后,大概率都会撞上同一堵墙:StatefulWidget 里那堆 initState、build、dispose 方法,到底什么时候被调用?为什么顺序是那样?在里面到…

2026/10/14 0:00:33

DFS算法解决飞机降落调度问题实战

1. 项目背景与核心思路飞机降落问题是一个经典的调度优化场景,也是算法竞赛和面试中的高频题目。这个题目要求我们合理安排多架飞机的降落顺序和时间,确保所有飞机都能安全着陆而不发生冲突。在实际航空管制中,这类问题直接关系到飞行安全和机…

2026/10/14 0:00:33

macOS/iOS高精度计时:mach_absolute_time原理与实战

1. 项目概述:为什么mach_absolute_time是 macOS/iOS 时间测量的“黄金标准”在 macOS 和 iOS 开发中,我见过太多人用CFAbsoluteTimeGetCurrent()或NSDate.timeIntervalSinceReferenceDate做性能打点,结果在高精度场景下被系统时钟跳变、NTP 同…

2026/10/14 0:00:33

SCORM课件演示与测试全解析:从API通信到自动化验证

简介:这是一套遵循SCORM标准的交互式课件演示与测试资源,面向E-Learning课程开发者、LMS平台测试人员及在线教学从业者,用于验证课件在不同学习管理系统中的识别、传递与跟踪能力。压缩包共28个文件,约12.07MB,以swf动…

还想了解更多?直接咨询顾问

免费诊断 + 免费方案 + 透明报价。

全国咨询热线400-8866-253
免费获取方案
☎咨询二维码 ☎ ↑