后端:
1.阶段 6 CP4/CP5 目录收口与共享边界纯化
- 将 backend 根目录收口为 services、client、gateway、cmd、shared 五个一级目录
- 收拢 bootstrap、inits、infra/kafka、infra/outbox、conv、respond、pkg、middleware,移除根目录旧实现与空目录
- 将 utils 下沉到 services/userauth/internal/auth,将 logic 下沉到 services/schedule/core/planning
- 将迁移期 runtime 桥接实现统一收拢到 services/runtime/{conv,dao,eventsvc,model},删除 shared/legacy 与未再被 import 的旧 service 实现
- 将 gateway/shared/respond 收口为 HTTP/Gin 错误写回适配,shared/respond 仅保留共享错误语义与状态映射
- 将 HTTP IdempotencyMiddleware 与 RateLimitMiddleware 收口到 gateway/middleware
- 将 GormCachePlugin 下沉到 shared/infra/gormcache,将共享 RateLimiter 下沉到 shared/infra/ratelimit,将 agent token budget 下沉到 services/agent/shared
- 删除 InitEino 兼容壳,收缩 cmd/internal/coreinit 仅保留旧组合壳残留域初始化语义
- 更新微服务迁移计划与桌面 checklist,补齐 CP4/CP5 当前切流点、目录终态与验证结果
- 完成 go test ./...、git diff --check 与最终真实 smoke;health、register/login、task/create+get、schedule/today、task-class/list、memory/items、agent chat/meta/timeline/context-stats 全部 200,SSE 合并结果为 CP5_OK 且 [DONE] 只有 1 个
18 KiB
HANDOFF:RAG Infra 一步到位接入方案
1. 文档目的
本文用于把 backend/infra/rag 从“可运行骨架”推进到“可被业务正式接入的共享基础设施”。
本文重点回答 4 个问题:
- 当前
RAG Infra已经做到了什么,还缺什么。 - 什么样的状态,才算“合格、可接入、可灰度、可回滚”的
RAG Infra。 - 如何以“依赖注入 + 对外只暴露方法入口”的方式收口,避免业务侧直接依赖底层实现细节。
- 如何在不打断现有业务的前提下,把
memory与websearch并行迁移到统一RAG Infra。
2. 当前现状
2.1 已完成部分
当前 backend/infra/rag 已经具备共享骨架,主要包括:
- 通用接口与类型:
core/interfaces.gocore/types.gocore/errors.go
- 通用编排器:
core/pipeline.go
- 默认切块器:
chunk/text_chunker.go
- 语料适配器:
corpus/memory_corpus.gocorpus/web_corpus.go
- 默认可运行实现:
embed/mock_embedder.gorerank/noop_reranker.gostore/inmemory_store.go
- 配置骨架:
config/config.go
这说明项目已经完成了“共享 RAG Core 的第一阶段搭骨架”,不再是单纯的设计想法。
2.2 当前存在的问题
虽然骨架已经有了,但距离“可正式接入的 Infra”还差关键几步:
- 运行时没有正式装配入口。
- 当前仍主要依赖
rag.NewDefaultPipeline()。 - 启动阶段没有统一按配置组装
embedder / store / reranker / corpus runtime。
- 当前仍主要依赖
- 真实底层实现还是占位。
embed/eino_embedder.go未实现。rerank/eino_reranker.go未实现。store/milvus_store.go未实现。
- 配置虽有结构,但还未真正接入运行链路。
rag/config/config.go定义了rag.*配置。backend/cmd/start.go尚未实例化并注入RAG Runtime。
- 业务尚未真正切流。
memory读取链路还没有正式走Pipeline.Retrieve。websearch还没有通过WebCorpus + Pipeline形成正式 WebRAG 路径。
- 工程化能力不完整。
- 缺统一 timeout。
- 缺统一日志字段。
- 缺基础指标。
- 缺单元测试与集成测试。
- 还存在潜在重复实现风险。
retrieve/vector_retriever.go与core/pipeline.go都承载部分检索逻辑。- 若后续两套逻辑并存,容易出现行为漂移与维护成本上升。
2.3 当前状态结论
当前 RAG Infra 的状态,更准确地说是:
- 已经完成“共享骨架搭建”。
- 还没有完成“统一装配、真实实现、正式接入、工程化收口”。
- 目前适合继续扩展,但还不适合直接作为长期稳定的业务依赖面。
3. 目标定义:什么叫“合格的 RAG Infra”
本轮改造完成后,backend/infra/rag 应满足以下标准:
- 启动时可统一构造并注入,不再靠业务模块自行拼装底层依赖。
- 对外只暴露稳定方法入口,不暴露底层
Pipeline / Store / Embedder / Reranker的装配细节。 - 支持按配置切换实现:
inmemory / milvusmock / einonoop / eino
- 支持
memory与websearch两类语料复用同一套chunk / embed / retrieve / rerank / fallback流程。 - 支持灰度开关与回滚,不要求业务“一次性硬切流”。
- 支持基础观测:
- 延迟
- 命中数
- fallback 原因
- 错误码
- 具备最小可依赖测试集,保证公共层改动不会悄悄破坏业务。
4. 核心改造原则
4.1 原则一:依赖注入统一由 Infra 自己负责
RAG Infra 必须自己承接“底层实现装配”,业务侧不应感知:
- 当前用的是
Milvus还是InMemoryStore。 - 当前用的是
MockEmbedder还是EinoEmbedder。 - 当前是否开启
Reranker。 - 当前超时、阈值、切块参数是多少。
业务只拿到一个已经注入好的 RAG Runtime 或 RAG Service,直接调用方法。
4.2 原则二:对外只暴露方法,不暴露底层零件
业务层不应直接依赖这些细粒度对象:
core.Pipelinecore.VectorStorecore.Embeddercore.Rerankercorpus.MemoryCorpuscorpus.WebCorpus
这些对象应被视为 infra/rag 内部拼装细节。
业务层只应调用诸如以下方法:
IngestMemoryRetrieveMemoryIngestWebRetrieveWeb
这样做的好处是:
- 业务依赖面更稳定。
- 后续替换底层实现时,不会把改动扩散到多个业务模块。
- 便于统一日志、监控、降级和权限边界。
4.3 原则三:业务语义留在业务层,通用 RAG 工序下沉到 Infra
下沉到 infra/rag 的内容:
- 切块
- 向量化
- 向量存储
- 召回
- rerank
- threshold 过滤
- fallback 语义
- 统一日志与指标
留在业务层的内容:
memory的注入优先级、门控规则、显式/隐式策略websearch的 provider 搜索、query 改写、时间过滤、domain 白名单、抓取策略- 最终给模型注入哪些证据、注入多少、如何组织引用
4.4 原则四:并行迁移,不一步删旧
本轮改造虽然目标是“一步到位把 Infra 做完整”,但切流必须保持并行迁移:
- 新 Infra 建好后,先让
memory接入并保留旧逻辑兜底。 - 再让
websearch接入并保留 V1 路径兜底。 - 观察稳定后再删除旧分支。
5. 目标架构
5.1 推荐对外结构
建议在 backend/infra/rag 新增统一对外门面,例如:
runtime.gofactory.goservice.go
推荐把正式对外依赖面收敛为一个接口,例如:
type Runtime interface {
IngestMemory(ctx context.Context, input MemoryIngestRequest) (*IngestResult, error)
RetrieveMemory(ctx context.Context, input MemoryRetrieveRequest) (*RetrieveResult, error)
IngestWeb(ctx context.Context, input WebIngestRequest) (*IngestResult, error)
RetrieveWeb(ctx context.Context, input WebRetrieveRequest) (*RetrieveResult, error)
}
说明:
- 业务侧只依赖
Runtime。 Runtime内部再去调用Pipeline + CorpusAdapter + Store + Embedder + Reranker。- 这样可以保证业务不会直接 import
core包下的底层细节。
5.2 推荐内部结构
建议内部形成以下分工:
factory.go- 负责按配置创建
Embedder / Store / Reranker / Pipeline
- 负责按配置创建
runtime.go- 负责持有
Pipeline + MemoryCorpus + WebCorpus + Logger + Metrics
- 负责持有
service.go- 负责定义
Runtime接口与对外方法
- 负责定义
core/- 保持底层通用编排逻辑
corpus/- 只负责“语料 -> 标准文档”和“业务过滤 -> 标准 filter”
5.3 推荐依赖注入方式
在 backend/cmd/start.go 中,启动期统一创建 RAG Runtime,例如:
- 读取
rag.*配置 - 构造
RAGFactory - 生成
RAGRuntime - 注入给:
memory servicenewAgent web tools
业务侧只拿运行好的对象,不再自己 new 任何底层实现。
6. 对外方法面设计
6.1 Memory 对外方法
推荐对外暴露以下方法:
IngestMemory- 输入:标准化后的记忆入库请求
- 输出:文档数、chunk 数、同步结果
RetrieveMemory- 输入:用户、会话、助手、run、query、topK、threshold
- 输出:标准
RetrieveResult
注意:
memory业务层不应直接调用MemoryCorpus。memory业务层不应自己拼向量过滤条件。- 所有过滤条件由
RetrieveMemory内部统一转换。
6.2 Web 对外方法
推荐对外暴露以下方法:
IngestWeb- 输入:抓取结果
url/title/snippet/content/domain/query_id/session_id - 输出:统一入库摘要
- 输入:抓取结果
RetrieveWeb- 输入:query、query_id/session_id、domain、topK、threshold
- 输出:标准
RetrieveResult
注意:
websearch业务层不应直接持有WebCorpus。websearch业务层只负责“拿到页面内容”与“决定是否需要调用 RAG”。- 实际向量入库、检索、rerank 由
infra/rag统一处理。
6.3 对外方法设计边界
方法层负责什么:
- 参数合法性校验
- 内部 filter 组装
- 调
Pipeline.Ingest / Retrieve - 统一日志、指标、fallback
方法层不负责什么:
- 不负责
websearch provider搜索 - 不负责 HTML 抓取
- 不负责 prompt 注入
- 不负责业务排序偏好
7. 具体改造计划
7.1 第一部分:把 RAG Infra 自身做完整
目标
让 backend/infra/rag 成为“正式可注入、正式可切换、正式可依赖”的共享基础设施。
实施项
- 新增正式运行时与工厂:
backend/infra/rag/runtime.gobackend/infra/rag/factory.go- 如有需要,新增
backend/infra/rag/service.go
- 扩展配置:
rag.enabledrag.storerag.embed.providerrag.embed.modelrag.embed.timeoutMsrag.embed.dimensionrag.reranker.providerrag.reranker.timeoutMsrag.retrieve.timeoutMsrag.ingest.chunkSizerag.ingest.chunkOverlap
- 收口运行入口:
rag.NewDefaultPipeline()保留为本地 fallback- 正式业务接入走
NewRuntimeFromConfig(...)
- 消除重复检索路径:
- 明确
Pipeline是官方检索入口 retrieve/vector_retriever.go要么内聚为内部实现,要么后续删除,避免双轨
- 明确
验收
- 启动期可按配置成功构造
RAG Runtime。 - 业务侧不需要自己组装
Pipeline / Store / Embedder / Reranker。 - 对外暴露面稳定,底层实现可替换。
7.2 第二部分:补齐真实底层实现
目标
让 RAG Infra 具备真实可用的向量能力,而不是停留在 mock。
实施项
- 实现
embed/eino_embedder.go- 负责 embedding 调用
- 负责 embedding timeout
- 负责错误包装与统一日志
- 实现
rerank/eino_reranker.go- 负责 rerank 调用
- 负责 rerank timeout
- 负责失败降级到原排序
- 实现
store/milvus_store.goUpsertSearchDeleteGet
- Milvus 元数据设计建议:
- 高频过滤字段应做显式标量字段,不建议全部依赖大 JSON 过滤
- 重点字段包括:
corpususer_idassistant_idconversation_idrun_idmemory_typequery_idsession_iddomain
验收
MilvusStore在已准备好的 Docker 环境中可稳定完成写入与检索。EinoEmbedder和EinoReranker可按配置启用。- provider 波动时,主链路仍能 fallback。
7.3 第三部分:补齐工程化能力
目标
让 RAG Infra 具备“可观测、可测试、可回滚”的基础设施属性。
实施项
- timeout 接线:
- embedding timeout
- retrieve timeout
- rerank timeout
- 统一日志字段:
trace_idcorpusactionproviderlatency_mshit_countfallback_reason
- 指标补齐:
rag_ingest_countrag_retrieve_countrag_hit_countrag_fallback_raterag_latency_ms
- 测试补齐:
chunker单测corpus filter单测pipeline fallback单测MilvusStore集成测试memory/web过滤隔离测试
验收
- 出现检索问题时,可从日志定位是:
- 没命中
- 超时
- rerank 降级
- filter 过滤过严
- 公共层测试可稳定覆盖关键路径。
7.4 第四部分:接入 Memory
目标
让 memory 成为第一个正式接入 RAG Infra 的业务域。
实施项
- 写入链路接入:
- 在 memory worker 成功写入
memory_items后,调用RAGRuntime.IngestMemory - 复用
memory_items.vector_status/vector_id
- 在 memory worker 成功写入
- 读取链路接入:
- 在
memory/service/read_service.go中新增RetrieveMemory路径 - 强制过滤:
user_idassistant_idconversation_idrun_id
- 在
- 开关控制:
memory.rag.enabled=false默认关闭- 打开后先灰度使用新路径
- 降级策略:
RAG检索失败 -> 回退旧读取链路Reranker失败 -> 保留原始排序
验收
- 开关关闭时行为与当前一致。
- 开关开启时,记忆召回可稳定工作。
- 失败时不会影响主链路回复。
7.5 第五部分:接入 WebSearch
目标
让 websearch 成为第二个正式接入 RAG Infra 的业务域,并复用 WebCorpus。
实施项
- 保留 V1 路径:
web_search做 provider 搜索web_fetch做正文抓取与清洗
- 新增 V2 路径:
- 把抓取结果映射为
WebIngestItem - 调
RAGRuntime.IngestWeb - 再调
RAGRuntime.RetrieveWeb
- 把抓取结果映射为
- 强约束过滤:
query_id或session_id至少有一个- 避免跨 query/session 串召回
- 开关控制:
websearch.rag.enabled=false默认关闭
- 降级策略:
web_rag_search失败 -> 回退到web_search + web_fetch
验收
- 新旧链路并存,互不影响。
- 新链路不会跨 query/session 串数据。
- 失败可立刻回退到 V1。
7.6 第六部分:启动接线与统一管理
目标
让 RAG Runtime 成为启动期统一装配、统一管理的依赖。
实施项
- 在
backend/cmd/start.go中:- 读取
rag.*配置 - 构造
RAG Runtime - 注入给
memory与newAgent web tools
- 读取
- 统一由启动期管理依赖生命周期:
- 初始化
- 健康检查
- 关闭清理
- 业务层禁止直接 new 底层实现:
- 禁止业务自己构建
MilvusStore - 禁止业务自己构建
EinoEmbedder - 禁止业务自己拼
Pipeline
- 禁止业务自己构建
验收
- 依赖管理集中在启动层。
- 业务代码只依赖方法入口,不接触底层实现。
- 后续替换实现时,无需大面积修改业务层代码。
8. 推荐目录改造方案
建议新增或调整如下文件:
backend/infra/rag/runtime.gobackend/infra/rag/factory.gobackend/infra/rag/service.gobackend/infra/rag/README.md或在本文件持续追加backend/infra/rag/embed/eino_embedder.gobackend/infra/rag/rerank/eino_reranker.gobackend/infra/rag/store/milvus_store.gobackend/infra/rag/core/pipeline_test.gobackend/infra/rag/chunk/text_chunker_test.gobackend/infra/rag/corpus/memory_corpus_test.gobackend/infra/rag/corpus/web_corpus_test.gobackend/infra/rag/store/milvus_store_integration_test.go
配套改动文件:
backend/cmd/start.gobackend/config.example.yamlbackend/memory/service/read_service.gobackend/newAgent/tools/registry.gobackend/agent/通用能力接入文档.md
9. 配置建议
建议新增如下配置结构:
rag:
enabled: true
store: "milvus"
topK: 8
threshold: 0.55
retrieve:
timeoutMs: 1500
ingest:
chunkSize: 400
chunkOverlap: 80
embed:
provider: "eino"
model: ""
timeoutMs: 1200
dimension: 1024
reranker:
enabled: true
provider: "eino"
timeoutMs: 1200
memory:
rag:
enabled: false
websearch:
rag:
enabled: false
说明:
rag.enabled控制公共层是否启用。memory.rag.enabled与websearch.rag.enabled控制业务级切流。- 即使
rag.enabled=true,也不代表所有业务立刻默认走新链路。
10. 回滚策略
推荐回滚顺序如下:
- 先关业务级开关:
memory.rag.enabled=falsewebsearch.rag.enabled=false
- 再关重排:
rag.reranker.enabled=false
- 再切底层实现:
rag.store=inmemoryrag.embed.provider=mockrag.reranker.provider=noop
- 若仍异常,再回退到业务旧链路
这样可以做到:
- 不因单个 provider 波动打断主流程。
- 保留最小可用能力。
- 故障定位粒度更细。
11. 风险与应对
- 风险:Milvus 过滤能力与现有 metadata 结构不匹配。
- 应对:高频过滤字段单独建模,不依赖大 JSON 粗暴过滤。
- 风险:embedding/rerank provider 波动影响延迟。
- 应对:超时控制 + fallback + 业务级开关。
- 风险:业务层绕过 Infra 直接依赖底层实现。
- 应对:通过
Runtime方法面统一收口,代码评审禁止横向绕过。
- 应对:通过
- 风险:新旧检索路径长期并存导致维护成本上升。
- 应对:本轮先保留兜底,稳定后明确删除旧实现。
- 风险:跨 query/session 串召回。
- 应对:
WebRetrieve强制校验query_id/session_id至少其一存在。
- 应对:
12. 最小落地顺序
如果按“尽快落成可接入 Infra”的优先级来排,本轮建议顺序如下:
- 先做
runtime/factory/service,把依赖注入和方法面收口。 - 再实现
MilvusStore + EinoEmbedder + EinoReranker。 - 再补 timeout、日志、指标、测试。
- 然后优先接
memory。 - 最后接
websearch。
原因:
- 若先接业务、不先收口方法面,后面会把底层细节泄露到业务层。
- 若先接 websearch、不先接 memory,会导致共享 Infra 价值不够集中,面试叙事也不完整。
13. 本轮完成后的预期收益
完成本方案后,项目会获得以下收益:
memory与websearch共享一套真正可运行的 RAG 基础设施。- 业务侧不再重复实现切块、召回、重排与降级逻辑。
infra/rag成为正式公共能力,具备统一依赖注入与统一管理能力。- 后续新增新语料域时,只需新增
CorpusAdapter + 方法面,无需再复制一套 RAG 链路。 - 项目简历叙事会更完整:
- “抽象并实现共享 RAG Infra”
- “统一 Memory/WebSearch 的检索与重排能力”
- “通过依赖注入与门面方法收口底层复杂度”
14. 当前建议结论
建议把本轮目标明确为:
- 不是“再给 RAG 补几个占位实现”。
- 而是“把
backend/infra/rag一次性做成正式可接入的公共基础设施”。
关键落点是两句话:
- 依赖注入统一由
infra/rag自己负责。 - 对外只暴露方法入口,业务侧不直接接触底层实现细节。
只要这两点收住,后续 memory、websearch、甚至更多语料域都会明显更好管理。