Version: 0.9.77.dev.260505

后端:
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 个
This commit is contained in:
Losita
2026-05-05 23:25:07 +08:00
parent 2a96f4c6f9
commit 3b6fca44a6
226 changed files with 731 additions and 3497 deletions

View File

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

View File

@@ -0,0 +1,191 @@
# RAG 复用接口实施计划Memory + WebSearch 统一底座)
## 1. 目标与原则
1.`backend/infra/rag` 抽离共享 RAG Core统一 `chunk/embed/retrieve/rerank` 能力。
2. 先接入 `MemoryCorpus``WebCorpus` 两个适配器,避免后续重复造轮子。
3. 保持“并行迁移”策略:新老链路并存,先接入、再灰度、再切流、最后删除旧实现。
4. 不阻塞现有主链路;任何 RAG 子能力失败都必须可降级。
## 2. 本轮范围与非目标
### 2.1 本轮范围
1. 定义 RAG Core 接口、标准数据结构、错误码和回退语义。
2. 提供 `MemoryCorpus``WebCorpus` 适配层设计。
3. 给出分阶段落地步骤、验收标准、风险控制。
### 2.2 本轮非目标
1. 不在本轮实现完整生产级向量检索细节Milvus 连接器可先占位)。
2. 不在本轮统一改造所有调用方,只做首批接入点。
3. 不在本轮引入多 Provider 工厂(先保证单 Provider 可替换)。
## 3. 目录与模块规划
建议目录(先建骨架,逐轮填实):
```text
backend/infra/rag/
core/
types.go
interfaces.go
pipeline.go
errors.go
chunk/
text_chunker.go
embed/
eino_embedder.go
retrieve/
vector_retriever.go
rerank/
eino_reranker.go
store/
vector_store.go
milvus_store.go
corpus/
memory_corpus.go
web_corpus.go
config/
config.go
```
## 4. 核心接口设计(建议签名)
```go
type Chunker interface {
Chunk(ctx context.Context, doc SourceDocument, opt ChunkOption) ([]Chunk, error)
}
type Embedder interface {
Embed(ctx context.Context, texts []string, action string) ([][]float32, error)
}
type Retriever interface {
Retrieve(ctx context.Context, req RetrieveRequest) ([]ScoredChunk, error)
}
type Reranker interface {
Rerank(ctx context.Context, query string, candidates []ScoredChunk, topK int) ([]ScoredChunk, error)
}
type VectorStore interface {
Upsert(ctx context.Context, rows []VectorRow) error
Search(ctx context.Context, req VectorSearchRequest) ([]ScoredVectorRow, error)
Delete(ctx context.Context, ids []string) error
Get(ctx context.Context, ids []string) ([]VectorRow, error)
}
type CorpusAdapter interface {
Name() string
BuildIngestDocuments(ctx context.Context, input any) ([]SourceDocument, error)
BuildRetrieveFilter(ctx context.Context, req any) (map[string]any, error)
}
```
## 5. 统一流程约定
### 5.1 Ingest 流程
1. `CorpusAdapter.BuildIngestDocuments` 生成标准文档。
2. `Chunker.Chunk` 切块(固定 chunk_size + overlap
3. `Embedder.Embed(action=add/update)` 生成向量。
4. `VectorStore.Upsert` 写入。
5. 任一步失败按“可补偿”记录状态,不影响主业务成功返回。
### 5.2 Retrieve 流程
1. `CorpusAdapter.BuildRetrieveFilter` 构建过滤条件。
2. `Embedder.Embed(action=search)` 向量化 query。
3. `VectorStore.Search` 召回候选。
4. `threshold` 过滤。
5. 可选 `Reranker` 重排;失败则 fallback 到原排序并记录原因码。
## 6. 两类 Corpus 适配器设计
### 6.1 MemoryCorpus
1. 数据源:`memory_items`(结构化记忆事实)。
2. 强约束过滤:`user_id + assistant_id + conversation_id`
3. 元数据:`memory_type/confidence/sensitivity_level/ttl_at/source_event_id`
4. 注入优先级:`constraint/preference` 高于 `fact/todo_hint`
### 6.2 WebCorpus
1. 数据源websearch 抓取结果(`url/title/snippet/content`)。
2. 强约束过滤:`query_id/session_id`,避免跨问题污染。
3. 元数据:`domain/published_at/fetched_at/language/source_rank`
4. 检索策略:先向量召回,再结合域名可信度做轻量加权。
## 7. 与 Eino 的集成方式
1. `embed/eino_embedder.go`:封装 Eino embedding 调用。
2. `rerank/eino_reranker.go`:封装 Eino 重排调用。
3. 统一配置入口:`rag.enabled/top_k/threshold/reranker_enabled/timeout`
4. 统一日志字段:`trace_id/corpus/action/fallback_reason/latency_ms/hit_count`
## 8. 分阶段实施(建议 4 轮)
### Round 1基础骨架不切流
1.`infra/rag` 目录与接口、类型、错误码。
2. 提供 `NoopReranker``MockEmbedder` 兜底实现。
3. 验收:编译通过,主链路行为不变。
### Round 2MemoryCorpus 接入(灰度)
1. 把记忆检索从“模块内直连”改为调用 RAG Core。
2. 保留旧路径开关 `memory.rag.enabled`,默认关闭。
3. 验收:开启开关后功能等价,失败可自动降级旧链路。
### Round 3WebCorpus 接入(灰度)
1. websearch 召回改走 RAG Core。
2. 加入 `web.rag.enabled` 灰度开关。
3. 验收:检索可复用同一 pipeline质量不低于旧实现。
### Round 4统一切流与清理
1. 默认开启 RAG Core旧链路保留一段观察窗口。
2. 指标稳定后删除旧实现。
3. 验收:两条业务链路均通过统一接口,文档与监控齐全。
## 9. 配置建议
```yaml
rag:
enabled: true
topK: 8
threshold: 0.55
reranker:
enabled: true
timeoutMs: 1200
ingest:
chunkSize: 400
chunkOverlap: 80
retrieve:
timeoutMs: 1500
```
## 10. 验收标准DoD
1. 同一套 Core 能同时服务 Memory 与 WebSearch。
2. `rerank` 异常时可观测地降级,不影响主功能可用性。
3. 支持按 corpus 维度查看命中率、耗时、降级率。
4. 新老链路可开关切换,回滚路径明确。
## 11. 风险与应对
1. 风险:一次性切流影响面大。
应对:按 corpus 分轮灰度,先 Memory 后 Web。
2. 风险:向量检索延迟波动。
应对:超时控制 + fallback + 本地缓存热点 query。
3. 风险:跨域检索串数据。
应对:强制 filter 校验,不满足维度直接拒绝检索。
## 12. 下一步执行清单(紧接实现)
1. 先补 `core/interfaces.go + core/types.go + core/pipeline.go`
2. 再补 `corpus/memory_corpus.go`(首个适配器)。
3. 然后给 websearch 接 `corpus/web_corpus.go` 占位适配器。
4. 最后补 `store/milvus_store.go` 与配置接线(当前 docker compose 已准备 Milvus 依赖)。

View File

@@ -59,7 +59,7 @@ Gin Gateway 只做边缘层职责:
3. 请求编排。
4. SSE / 流式返回。
5. 前端所需的轻量组合逻辑。
6. API 层错误响应适配。迁移期可以继续复用 `backend/respond`,等全部服务边界稳定后再整体收进 gateway/shared
6. API 层错误响应适配。阶段 6 后 gateway HTTP 门面已通过 `backend/gateway/shared/respond` 统一入口复用 `backend/respond` 语义;根 `backend/respond` 迁移期继续服务 RPC/client/旧实现,避免服务层反向依赖 gateway。
网关不再承担这些职责:
@@ -73,7 +73,7 @@ Gin Gateway 只做边缘层职责:
1. `/api/v1/user/*``backend/gateway/api/userauth` 承载 HTTP 入口,核心能力通过 zrpc client 调 `cmd/userauth` zrpc。
2. `gateway/middleware` 的 JWT 鉴权和 token quota guard 只调 `userauth`,不直接读写 `users`、Redis 黑名单或额度缓存。
3. `notification``active-scheduler``agent``memory` 等跨服务 zrpc client 终态统一放在 `backend/client/<service>`;当前 `backend/gateway/client/<service>` 是迁移期旧位置,下一轮目录收口应机械迁出
3. `notification``active-scheduler``agent``memory` 等跨服务 zrpc client 终态统一放在 `backend/client/<service>`;当前已完成 `backend/gateway/client/<service>` `backend/client/<service>` 的机械迁移,`backend/gateway/client` 不再作为活跃 client 位置
4. zrpc client 不放进 `cmd``cmd` 只负责进程入口和装配,不承载跨服务 client 语义。
5. HTTP 门面统一放在 `backend/gateway/api`gateway 内部可新增 `backend/gateway/shared`,只放 HTTP/SSE/bind/multipart/respond 等门面复用能力,禁止服务层 import。
@@ -99,11 +99,11 @@ gozero 服务负责领域能力:
>
> 当前状态:`llm-service` / `rag-service` 这两个边界已经先做成 `backend/services/*` 的服务内模块,调用仍由 `backend/cmd/start.go` 在同一进程内装配,不是 gozero 独立进程。
>
> 当前状态:`user/auth` 已经完成 go-zero zrpc 独立进程拆分,是阶段 2 样板。服务端在 `backend/services/userauth`,进程入口在 `backend/cmd/userauth`gateway HTTP 门面在 `backend/gateway/api/userauth`gateway client 在 `backend/gateway/client/userauth`。
> 当前状态:`user/auth` 已经完成 go-zero zrpc 独立进程拆分,是阶段 2 样板。服务端在 `backend/services/userauth`,进程入口在 `backend/cmd/userauth`gateway HTTP 门面在 `backend/gateway/api/userauth`zrpc client 在 `backend/client/userauth`。
>
> 当前状态:`notification` 已经完成阶段 3 拆分。服务端在 `backend/services/notification`,进程入口在 `backend/cmd/notification`gateway client 在 `backend/gateway/client/notification`,服务级 outbox consumer 和 retry loop 已随服务入口迁出。
> 当前状态:`notification` 已经完成阶段 3 拆分。服务端在 `backend/services/notification`,进程入口在 `backend/cmd/notification`zrpc client 在 `backend/client/notification`,服务级 outbox consumer 和 retry loop 已随服务入口迁出。
>
> 当前状态:`active-scheduler` 已经完成阶段 4 首轮收口。服务端在 `backend/services/active_scheduler`,进程入口在 `backend/cmd/active-scheduler`gateway HTTP 门面在 `backend/gateway/api`gateway client 在 `backend/gateway/client/activescheduler`。
> 当前状态:`active-scheduler` 已经完成阶段 4 首轮收口。服务端在 `backend/services/active_scheduler`,进程入口在 `backend/cmd/active-scheduler`gateway HTTP 门面在 `backend/gateway/api`zrpc client 在 `backend/client/activescheduler`。
### 3.3 事件层
@@ -319,7 +319,7 @@ flowchart LR
1. 新增 `backend/cmd/userauth/main.go` 作为 userauth 独立进程入口。
2. 新增 `backend/services/userauth/**`,承载注册、登录、刷新 token、登出、JWT 签发/校验、黑名单、token 额度治理和 token 记账幂等。
3. 新增并归档到 `backend/gateway/api/userauth/**`,承载 `/api/v1/user/register``/api/v1/user/login``/api/v1/user/refresh-token``/api/v1/user/logout` 的 HTTP handler。
4. 新增并归档到 `backend/gateway/client/userauth/**`,承载 gateway 侧 zrpc client 和 gRPC 错误反解。
4. 新增并归档到 `backend/gateway/client/userauth/**`,承载迁移期 gateway 侧 zrpc client 和 gRPC 错误反解;阶段 6 后已机械迁到 `backend/client/userauth/**`
5. 新增 `backend/gateway/middleware/**`,把 JWT 鉴权和 token quota guard 改成调用 userauth不再直接碰 users 表或 Redis 黑名单细节。
6. 新增 `backend/shared/contracts/userauth``backend/shared/ports`,只放跨层契约和端口接口。
7. 拆分 MySQL / Redis 初始化和 AutoMigrate 边界:`cmd/all``ConnectCoreDB` / `InitCoreRedis`,只迁单体残留域;`cmd/userauth` 自己迁 `users``user_token_usage_adjustments`
@@ -335,7 +335,7 @@ flowchart LR
当前切流点:
1. 前端仍访问 `/api/v1/user/*`
2. gateway 的 user handler 只做参数绑定、调用 userauth client、复用 `respond` 写回前端。
2. gateway 的 user handler 只做参数绑定、调用 userauth client、通过 `backend/gateway/shared/respond` 写回前端。
3. gateway 鉴权和 quota guard 只依赖 `ports.UserAuthClient`,不直接依赖 userauth DAO/model。
4. `agent/chat` 的 token quota 门禁已经通过 userauth 服务判断;会话完成后的 token 记账由 agent 事件处理链路调用 userauth `AdjustTokenUsage`
@@ -349,7 +349,7 @@ flowchart LR
遗留约定:
1. `respond` 暂时继续放在 `backend/respond` 复用;等全部阶段收尾后再整体收进 gateway/shared
1. `backend/respond` 暂时继续作为服务层、RPC 层、client 层和旧实现的兼容响应语义;阶段 6 后 gateway HTTP 门面已切到 `backend/gateway/shared/respond`,该包只做薄转发,不允许服务层 import
2. `cmd/all` 单独启动不再覆盖 user/auth 完整能力,后续 smoke 必须同时确认 userauth 已启动。
3. 不要再把 user/auth 当成后续待办;阶段 3 的接手人应从 notification 开始。
@@ -386,7 +386,7 @@ flowchart LR
本轮收口状态2026-05-04
1. `cmd/notification` 已承载 notification zrpc 启动、DB 迁移、服务级 outbox consumer 和重试扫描。
2. `backend/services/notification` 已收进 DAO、model、sv、rpc、飞书 provider 和 outbox handlergateway 通过 `backend/gateway/client/notification` zrpc client 调用。
2. `backend/services/notification` 已收进 DAO、model、sv、rpc、飞书 provider 和 outbox handlergateway 通过 `backend/client/notification` zrpc client 调用。
3. 主动调度侧只写入 `notification.feishu.requested`publisher 侧只注册事件归属到 `notification`,不再启动单体 notification consumer。
4.`backend/notification`、旧 DAO/model 和旧 `service/events/notification_feishu.go` 已删除review 发现的 sending 租约恢复和 RPC timeout 边界已修复。
5. 真实 smoke 已通过:`notification_outbox_messages.id=3` 已从 `pending` 推进到 `consumed``smartflow.notification.outbox` 已出现 `outbox_id=3`,对应 `notification_records` 生成并按未启用通道进入 `skipped`
@@ -424,7 +424,7 @@ flowchart LR
1. `cmd/active-scheduler` 已承载 active-scheduler zrpc 启动、DB 迁移、服务级 outbox consumer、relay、retry loop 和 due job scanner。
2. `backend/services/active_scheduler` 已收进 DAO、sv、rpc 和主动调度核心逻辑;复杂领域流程统一下沉到 `backend/services/active_scheduler/core`,旧 `backend/active_scheduler` 活跃实现已移除。
3. gateway HTTP 门面已统一到 `backend/gateway/api`active-scheduler、notification、userauth 的 zrpc client 已统一到 `backend/gateway/client/*`
3. gateway HTTP 门面已统一到 `backend/gateway/api`active-scheduler、notification、userauth 的 zrpc client 已统一到 `backend/client/*`
4. 单体 `cmd/start.go` 不再启动 active-scheduler workflow / scanner / handlergateway 的 `/api/v1/active-schedule/*` 只做鉴权、参数绑定、超时和 zrpc 转发。
5. 迁移期仍共享主库读取 / 写入 task、schedule、agent 会话与 notification outbox 相关表active-scheduler 启动时会显式检查这些运行时依赖表,后续阶段 5/6 再逐步切成 RPC 或 read model。
6. 已完成真实 smoke`trigger -> active_scheduler_outbox_messages -> consume -> preview ready` 闭环通过,当前 trigger 没有出现单体误消费导致的 dead handler。
@@ -435,21 +435,21 @@ flowchart LR
当前进展2026-05-05
1. 首刀 `schedule` 已完成服务化:新增 `cmd/schedule``services/schedule/{dao,rpc,sv,core}``gateway/client/schedule``shared/contracts/schedule``shared/ports` schedule port。
1. 首刀 `schedule` 已完成服务化:新增 `cmd/schedule``services/schedule/{dao,rpc,sv,core}`迁移期 `gateway/client/schedule`(阶段 6 后已迁到 `backend/client/schedule``shared/contracts/schedule``shared/ports` schedule port。
2. gateway 的 `/api/v1/schedule/*` HTTP 门面已切到 schedule zrpc clientgateway 不再通过 `backend/service.ScheduleService` 直接承载 schedule HTTP 入口业务。
3. active-scheduler 的 schedule facts / feedback / confirm apply 已改为调用 schedule RPC adapter`cmd/active-scheduler` 启动依赖检查已移除 `schedule_events``schedules``task_classes``task_items`
4. 第二刀 `task` 已开始服务化:新增 `cmd/task``services/task/{dao,rpc,sv}``gateway/client/task``shared/contracts/task``shared/ports` task port。
4. 第二刀 `task` 已开始服务化:新增 `cmd/task``services/task/{dao,rpc,sv}`迁移期 `gateway/client/task`(阶段 6 后已迁到 `backend/client/task``shared/contracts/task``shared/ports` task port。
5. gateway 的 `/api/v1/task/*` HTTP 门面已切到 task zrpc clientgateway 只负责鉴权、参数绑定、短超时和响应透传,不再直接调用 `backend/service.TaskService`
6. active-scheduler 的 task facts / due job scanner 已切到 task RPC adapter`cmd/active-scheduler` 启动依赖检查已移除 `tasks`,进一步缩小 active-scheduler 对跨域主库表的直接依赖。
7. `task.urgency.promote.requested` 的 handler、relay、retry loop 已迁入 `cmd/task`;单体 outbox worker 只保留 agent / memory consumerAgent 残留查询链路只允许 publish-only 写入 `task_outbox_messages`,避免单体和 task 独立服务抢同一 task consumer group。
8. 第三刀 `task-class` 已完成 HTTP 所有权切流:新增 `cmd/task-class``services/task_class/{dao,rpc,sv}``gateway/client/taskclass``shared/contracts/taskclass``shared/ports` task-class port。
8. 第三刀 `task-class` 已完成 HTTP 所有权切流:新增 `cmd/task-class``services/task_class/{dao,rpc,sv}`迁移期 `gateway/client/taskclass`(阶段 6 后已迁到 `backend/client/taskclass``shared/contracts/taskclass``shared/ports` task-class port。
9. gateway 的 `/api/v1/task-class/*` HTTP 门面已切到 task-class zrpc clientgateway 只负责鉴权、参数绑定、短超时和响应透传,不再直接调用 `backend/service.TaskClassService`
10. task-class 本轮按主人拍板保留迁移期直写 `schedule_events` / `schedules` 权限,不走 schedule RPC bridge以保留 `insert into schedule` / `apply batch into schedule` 与 task item 状态更新的本地事务语义;`cmd/task-class` 只 AutoMigrate `task_classes` / `task_items`,启动时显式检查 schedule 依赖表是否存在。
11. 旧实现仍保留:`backend/service/schedule.go``backend/dao/schedule.go``backend/service/task.go``backend/dao/task.go``backend/service/task-class.go``backend/dao/task-class.go``backend/service/course*.go``backend/dao/course.go`、active-scheduler 旧 Gorm apply adapter 暂时保留,用于 agent 迁移期、单体残留路径和回退。
12. 当前切流点HTTP schedule 流量进入 `cmd/schedule`HTTP task 流量进入 `cmd/task`HTTP task-class 流量进入 `cmd/task-class`active-scheduler 读取 task/schedule facts 与正式写日程均走 RPCagent 内部仍存在直接 DAO 调用,后续按 agent/memory 阶段继续收。
13. 当前残留跨域 DB 依赖task-class 迁移期仍直接写 `schedule_events` / `schedules`task 服务迁移期仍 best-effort 写 `active_schedule_jobs`active-scheduler 仍直接写 agent 会话 / timeline 和 notification outbox 相关表agent 本地 task 查询、task-class upsert 和 schedule provider 仍保留 DAO 适配。
14. 已完成验证:`go test ./...` 通过避让默认端口启动完整本地服务组HTTP `18080`zrpc `19081-19086`task-class add / list / get / insert-into-schedule / delete-item / delete-class smoke 通过,并用 `docker exec` 核对 task-class 与 schedule 相关表无残留。
15. 第四刀 `course` 已完成 HTTP 所有权切流:新增 `cmd/course``services/course/{dao,rpc,sv}``gateway/client/course``shared/contracts/course``shared/ports` course port。
15. 第四刀 `course` 已完成 HTTP 所有权切流:新增 `cmd/course``services/course/{dao,rpc,sv}`迁移期 `gateway/client/course`(阶段 6 后已迁到 `backend/client/course``shared/contracts/course``shared/ports` course port。
16. gateway 的 `/api/v1/course/*` HTTP 门面已切到 course zrpc clientgateway 只负责鉴权、限流、幂等、multipart 文件读取、短超时和响应透传,不再直接调用 `backend/service.CourseService`
17. course 本轮保留迁移期直写 `schedule_events` / `schedules` 权限,不走 schedule RPC bridge以保留课程导入两个表同事务写入和冲突返回语义`cmd/course` 不 AutoMigrate schedule 表,启动时显式检查依赖表是否存在。
18. 当前切流点更新HTTP schedule / task / task-class / course 流量均进入各自独立 zrpc 服务active-scheduler 读取 task/schedule facts 与正式写日程均走 RPCagent 内部仍存在 task、task-class、schedule DAO 适配,后续按 agent/memory 阶段继续收。
@@ -509,13 +509,24 @@ flowchart LR
3. `backend/cmd/agent/main.go` 已补齐独立进程入口:负责 DB / Redis / LLM / RAG 初始化、agent outbox consumer 启停和 agent zrpc server 生命周期;旧 `backend/cmd/start.go` 的 gateway 本地链路继续保留。
4. agent 事件归属继续复用 `backend/service/events` 与服务级 outbox 路由:`chat.*` / `agent.*` 事件归 `ServiceAgent``memory.extract.requested` 只登记路由不再由 agent 进程消费,`task.urgency.promote.requested` 仍是 publish-only 写入 `task_outbox_messages`
5. `backend/services/agent/rpc` 已补齐 `Ping``Chat` server-stream 以及 conversation meta/list/timeline、schedule-preview、context-stats、schedule-state 6 个 unary JSON 透传 RPC跨进程 chat 边界传 `ChatChunk`,不传 Go channelGateway 继续对前端输出原 SSE 协议。
6. `backend/gateway/client/agent``gateway/api/agent.go` 已接入 `agent.rpc.chat.enabled``agent.rpc.api.enabled` 两个开关;本地 `config.yaml``config.example.yaml` 当前默认 `true`,真实 UTF-8 中文 SSE smoke 已通过chat 主链路走 `agent RPC Chat(stream)` 再转 SSE非 chat `/agent/*` 走 agent unary RPC。
6. `backend/client/agent``gateway/api/agent.go` 已接入 `agent.rpc.chat.enabled``agent.rpc.api.enabled` 两个开关;本地 `config.yaml``config.example.yaml` 当前默认 `true`,真实 UTF-8 中文 SSE smoke 已通过chat 主链路走 `agent RPC Chat(stream)` 再转 SSE非 chat `/agent/*` 走 agent unary RPC。
7. 历史 timeline payload key`newagent_history_kind`)暂不改名,避免破坏旧会话兼容。
8. `backend/memory/*` 已物理迁入 `backend/services/memory/*``module.go``model/``observe/` 作为公共门面保留,`cleanup/``orchestrator/``repo/``service/``utils/``vectorsync/``worker/` 收入 `internal/`,旧 `backend/memory` 目录已删除。
9. `cmd/start.go` 不再创建/注册/启动 agent outbox event busagent relay / consumer 由 `cmd/agent` 独占memory worker / 管理能力由 `cmd/memory` 承担。
10. `cmd/start.go` 已收缩 gateway 本地 `AgentService` 构建:当 `agent.rpc.chat.enabled=true``agent.rpc.api.enabled=true`gateway 不再初始化 agent 本地编排、LLM、RAG、memory reader fallback只有任一 RPC 开关关闭时才保守装配本地 fallback。
11. 最新验证:重建并重启 `api` / `agent`UTF-8 中文 SSE smoke 通过且只有单个 `[DONE]`6 个非 chat `/agent/*` HTTP smoke 中 meta/list/timeline/context-stats 返回 200schedule-preview / schedule-state 在无快照场景返回预期业务 400。
12. 下一轮目录收口按新口径推进:把 `backend/gateway/client/*` 机械迁到 `backend/client/*`gateway 和服务进程共同复用 zrpc client;保留根 `backend/shared` 承载跨服务契约,同时新增 `backend/gateway/shared` 承载 HTTP/SSE/bind/respond 等 gateway 门面复用
12. 轮目录收口 CP1 已按新口径完成:`backend/gateway/client/*` 机械迁到 `backend/client/*`gateway 和服务进程共同复用 zrpc client。
13. 本轮目录收口 CP2 已新增 `backend/gateway/shared/respond`gateway/api 与 gateway/middleware 不再直接 import 根 `backend/respond`;根 `backend/respond` 作为迁移期兼容语义保留给 RPC、client、服务层和旧实现服务代码禁止 import `backend/gateway/shared`
14. CP2 验证结果:`go test ./...``git diff --check` 通过;真实 smoke 覆盖 health、未带 token 的鉴权错误、register/login、受保护 task/memory 接口、agent SSE 和 conversation timeline确认 `gateway/shared/respond` 写回的 `401 + 40009 missing token` 保持旧协议SSE 中文合并为 `CP2中文响应正常` 且只有单个 `[DONE]`
15. 本轮目录收口 CP3 先完成低风险切流:`cmd/start.go``cmd/agent/runtime.go` 不再直接 import 根 `backend/service` 主包gateway fallback 与独立 agent runtime 的 task / schedule 注入已改用 `backend/services/task/sv``backend/services/schedule/sv` 及对应服务 DAO未使用的 course 装配 helper 也改到 `backend/services/course/sv`
16. CP3 不硬搬根 `backend/dao``backend/model`:两者当前仍被 cmd、services、gateway、旧回退面和跨域迁移期链路广泛引用直接整包搬迁风险过高后续按单一能力域继续缩小例如 task due job、schedule apply、agent 会话快照等。
17. CP3 旧实现保留:根 `backend/service/*.go``backend/dao/*.go``backend/model/*.go` 继续作为并行迁移期回退面和共享历史模型存在;当前切流点是正式启动装配不再直连根 `backend/service` 主包,服务内活跃实现优先使用 `backend/services/<service>/sv`
18. CP3 验证结果:`go test ./...``git diff --check` 通过;正式 Go 代码里对根 `github.com/LoveLosita/smartflow/backend/service` 主包的 direct import 已归零。真实 smoke 在 `D:\SmartFlow-Agent\.tmp\cp3-service-root-smoke-20260505-203454` 完成,覆盖 health、register/login、`task/create` + `task/get``schedule/today``memory/items`、agent chat SSE、conversation meta/timeline/context-stats确认 gateway 已走 `agent RPC` 而非本地 fallback中文 SSE 合并为 `CP3中文响应正常` 且只有单个 `[DONE]`
19. CP4 第一轮先收最窄的根目录公共件:`backend/bootstrap` 当前只承载统一配置加载职责,已新增 canonical 入口 `backend/shared/infra/bootstrap`,并把 `cmd/start.go``cmd/agent``cmd/memory``cmd/notification``cmd/active-scheduler``cmd/schedule``cmd/task``cmd/task-class``cmd/course``cmd/userauth` 的 import 切到新路径。
20. CP4 当前仍保留根 `backend/bootstrap` 兼容包装层,避免并行迁移期一次性删除旧目录;当前切流点是所有正式启动入口优先依赖 `backend/shared/infra/bootstrap`,后续确认无残留引用后再删除根目录兼容层。
21. CP4 第二轮继续只收“纯连接底座”这一类:已新增 `backend/shared/infra/mysql``backend/shared/infra/redis``backend/shared/infra/eino` 三个 canonical 入口,只承载 MySQL 连接、Redis 连接和 Eino `AIHub` 构造,不承载任何服务私有 AutoMigrate、回填、outbox 或 worker 启停语义。
22. CP4 当前切流点:`cmd/agent` 直接依赖 `shared/infra/mysql|redis|eino``cmd/active-scheduler``cmd/memory` 以及 `cmd/start.go` 里的本地 agent fallback LLM 构造已切到 `shared/infra/eino`;各服务 `dao/connect.go` 复用 `shared/infra/mysql|redis` 负责“开连接”,但仍在各自目录内保留本服务 own 的 AutoMigrate、依赖表检查与 outbox 建表逻辑。
23. CP4 旧实现保留:根 `backend/inits` 目前收缩为兼容入口 + core 残留域初始化;其中 `ConnectCoreDB``AutoMigrateCoreStorage``InitCoreRedis` 仍服务于旧组合壳和迁移期 core 残留域,暂不下沉到 `shared/infra`,避免把服务私有 schema 语义重新做成“大公共篮子”。
建议提交点:
@@ -756,11 +767,11 @@ SmartFlow-Agent/
> 当前目录到目标目录的映射:
>
> 1. `backend/services/userauth/*` 已经是阶段 2 终态样板;旧 `backend/api/user.go`、`backend/service/user.go`、`backend/dao/user.go`、`backend/model/user.go`、`backend/model/auth.go`、`backend/auth/jwt_handler.go`、`backend/middleware/token_handler.go`、`backend/middleware/token_quota_guard.go`、`backend/routers/routers.go` 不再作为活跃实现。
> 2. `backend/gateway/api/userauth/*` 是 user HTTP 入口,`backend/gateway/client/userauth/*` 是 userauth zrpc client,二者都属于 gateway 边缘层。
> 2. `backend/gateway/api/userauth/*` 是 user HTTP 入口,`backend/client/userauth/*` 是 userauth zrpc clientHTTP 门面属于 gateway 边缘zrpc client 属于跨进程共享 client 层。
> 3. `backend/service/*.go` 这批现有业务逻辑,后面要分别迁到各自服务根目录下的 `sv/`。
> 4. `backend/services/agent/*` 已承接原 `backend/newAgent/*` 内核,`backend/services/agent/sv/*` 已承接原 `backend/service/agentsvc/*` 编排层;后面再按风险拆到 `internal/{prompt,graph,stream,tool,session,router}`。
> 5. `backend/services/notification/*` 已经是阶段 3 终态样板;`backend/cmd/notification` 是独立进程入口,`backend/gateway/client/notification` 是 gateway 侧 zrpc client`backend/shared/contracts/notification` 只放跨层契约;旧 `backend/notification/*`、旧 DAO/model 和旧 `service/events/notification_feishu.go` 不再作为活跃实现。
> 6. `backend/services/active_scheduler/*` 已经是阶段 4 当前样板;`backend/cmd/active-scheduler` 是独立进程入口,`backend/gateway/client/activescheduler` 是 gateway 侧 zrpc client`backend/services/active_scheduler/core` 承载迁移期领域核心;旧 `backend/active_scheduler/*` 不再作为活跃实现。
> 5. `backend/services/notification/*` 已经是阶段 3 终态样板;`backend/cmd/notification` 是独立进程入口,`backend/client/notification` 是跨进程 zrpc client`backend/shared/contracts/notification` 只放跨层契约;旧 `backend/notification/*`、旧 DAO/model 和旧 `service/events/notification_feishu.go` 不再作为活跃实现。
> 6. `backend/services/active_scheduler/*` 已经是阶段 4 当前样板;`backend/cmd/active-scheduler` 是独立进程入口,`backend/client/activescheduler` 是跨进程 zrpc client`backend/services/active_scheduler/core` 承载迁移期领域核心;旧 `backend/active_scheduler/*` 不再作为活跃实现。
> 7. `backend/services/memory/*` 已成为 memory 当前 canonical 入口;`module.go`、`model/`、`observe/` 是对外可见门面,服务私有实现已收入 `internal/`,旧 `backend/memory/*` 只在 legacy 文档中作为历史路径出现。
>
> 说明 4`shared` 先保留 `events` 和少量跨服务底座型 `infra`。以后如果真的出现跨服务 DTO / 枚举 / 常量,再新增 `contracts` 一类目录,但不要把 `dao`、`model`、`sv`、`handler` 这类服务私有层塞进去。
@@ -771,7 +782,7 @@ SmartFlow-Agent/
>
> 说明 7目录树里如果暂时写成 `backend/services/llm/` 和 `backend/services/rag/`,那只是目录名写法;后文所有职责判断都以 `llm-service` / `rag-service` 这两个逻辑服务名为准。
>
> 说明 8阶段 2 已经采用 `backend/services/userauth/` 作为实际目录名,不再使用 `user-auth`。阶段 3 已经采用 `backend/services/notification/` 作为实际目录名。zrpc client 终态放在 `backend/client/<service>/`,迁移期旧 `backend/gateway/client/<service>/` 要按机械迁移逐步挪出;进程入口放在 `backend/cmd/<service>/`,不要把 rpc client 放进 `cmd`。
> 说明 8阶段 2 已经采用 `backend/services/userauth/` 作为实际目录名,不再使用 `user-auth`。阶段 3 已经采用 `backend/services/notification/` 作为实际目录名。zrpc client 终态放在 `backend/client/<service>/`,迁移期旧 `backend/gateway/client/<service>/` 已在阶段 6 后按 CP1 机械迁出;进程入口放在 `backend/cmd/<service>/`,不要把 rpc client 放进 `cmd`。
### 6.3 哪些可以不用变
@@ -814,7 +825,7 @@ SmartFlow-Agent/
| 服务 | 典型用例 | 结构收束建议 | 不允许的改法 |
| --- | --- | --- | --- |
| `user/auth` | 注册、登录、刷新、登出、JWT 签发、黑名单、token 额度门禁、token 记账幂等 | 已完成:`cmd/userauth` + `services/userauth/{sv,dao,model,internal/auth,rpc}`gateway 侧是 `gateway/api/userauth` + `gateway/client/userauth` | 不要恢复旧 Gin user/auth 实现;不要让 gateway 直连 users 表、Redis 黑名单或额度缓存;不要把 zrpc client 放进 `cmd` |
| `user/auth` | 注册、登录、刷新、登出、JWT 签发、黑名单、token 额度门禁、token 记账幂等 | 已完成:`cmd/userauth` + `services/userauth/{sv,dao,model,internal/auth,rpc}`gateway 侧是 `gateway/api/userauth`,跨进程 client 是 `backend/client/userauth` | 不要恢复旧 Gin user/auth 实现;不要让 gateway 直连 users 表、Redis 黑名单或额度缓存;不要把 zrpc client 放进 `cmd` |
| `course` | 课程导入、图片解析、课表校验、课程落表,图片解析走 `llm-service` | `handler.go` / `sv/` / `dao/` / `model/` / `internal/{parse,import,conflict,adapter}/` | 不要把课程解析代码写成网关临时脚本 |
| `task-class` | 任务类创建/更新、items 批量 upsert、嵌入时间同步 | `handler.go` / `sv/` / `dao/` / `model/` / `internal/{convert,batch,item}/` | 不要把批处理拼装沉到 handler 里,也不要让 agent 直接改库 |
| `notification` | 消费 `notification.feishu.requested`、写通知记录、幂等、重试、provider 投递 | `start.go` / `handler.go` / `sv/` / `dao/` / `model/` / `internal/{provider,runner,dedupe,channel,retry}/` | 不要把通知投递逻辑散回 worker 或 gateway |
@@ -922,8 +933,8 @@ graph TD
5. 阶段 3 `notification` 已完成实现、code review 修复和真实 smoke`llm-service``rag-service` 也已完成,不要重新当成待办。
6. 阶段 4 `active-scheduler` 已完成首轮收口;后续不要再把它当成“未拆服务”,除非是在补契约测试或继续替换跨域 DB 访问。
7. `shared` 只保留跨进程契约和少量跨服务底座不承载业务逻辑、DAO、模型或状态机。
8. `backend/client` 是 zrpc client 的终态共享位置,`backend/gateway/client` 只是迁移期旧位置;后续服务或 gateway 需要复用 client 时,优先推进机械搬迁到`client`
9. `backend/gateway/shared` 只放 gateway 门面复用,服务代码禁止 import跨服务契约仍留在根 `backend/shared`
8. `backend/client` 是 zrpc client 的终态共享位置,`backend/gateway/client` 只是迁移期旧位置且 CP1 已完成机械迁出;后续服务或 gateway 需要复用 client 时,统一使用`client`
9. `backend/gateway/shared` 只放 gateway 门面复用,服务代码禁止 import跨服务契约仍留在根 `backend/shared`当前已落地 `backend/gateway/shared/respond` 作为 HTTP 响应适配薄门面。
10. 如果后续要改目录,必须先回答“这个文件属于哪一个典型用例”,回答不清楚就先别动结构。
11. 当前文档已经可以作为切对话基线;后续代理默认按本文件推进。现阶段的迁移基线入口是 `backend/cmd/api``backend/cmd/worker``backend/cmd/all`,它们只是当前仓库的启动壳,不是终态。`backend/cmd/userauth` 是阶段 2 的独立服务入口,`backend/cmd/notification` 是阶段 3 的独立服务入口,`backend/cmd/active-scheduler` 是阶段 4 的独立服务入口,`backend/cmd/schedule``backend/cmd/task``backend/cmd/task-class``backend/cmd/course` 是阶段 5 已落地的独立服务入口。终态仍然是“一个服务一个独立 `main.go`”,只在出现新的契约风险、边界变化或业务语义变化时再重新讨论架构。
@@ -937,7 +948,7 @@ graph TD
6. 带 worker 的服务可以继续保留多入口角色,例如 `api` / `worker` / `all`,但它们仍然是同一服务的不同可执行角色,不是把多个服务硬塞进一个进程。
7. MySQL / Redis 容器的启动归 `docker compose` 或运维层Go 服务只负责在自己的进程里建立连接、做自己的 AutoMigrate 和连通性检查。
8. 阶段 5 后,旧 `cmd/start.go` / `cmd/all` 只是 gateway 和迁移期组合壳;本地完整 smoke 必须额外启动 `cmd/userauth``cmd/notification``cmd/active-scheduler``cmd/schedule``cmd/task``cmd/task-class``cmd/course`。如果同机已有另一条线占用默认端口,应复制临时配置,把 HTTP / zrpc 端口整体平移后再启动服务。
9. 阶段 6 后,`cmd/agent``cmd/memory` 也应纳入完整本地 smoke目录收口时优先把服务与 gateway 共同使用的 zrpc client 从 `gateway/client` 挪到根 `client`,再清理 gateway 门面复用到 `gateway/shared`
9. 阶段 6 后,`cmd/agent``cmd/memory` 也应纳入完整本地 smoke目录收口 CP1 已把服务与 gateway 共同使用的 zrpc client 从 `gateway/client` 挪到根 `client`CP2 已把 gateway HTTP 响应适配入口收进 `gateway/shared/respond`CP3 已先切掉 `cmd` 对根 `backend/service` 主包的直接装配依赖。下一步再按风险迁 SSE/bind/multipart 等门面复用或继续收窄根 dao/model 依赖
### 6.11 测试自动化与 smoke 权限边界
@@ -1057,7 +1068,7 @@ graph TD
5. 跑完 `go test ./...` 后必须清理工作区 `.gocache`
6. 不擅自回滚、覆盖、删除用户或其他代理的无关改动。
7. 不主动 `git commit` / `git branch`,除非用户明确要求。
8. 服务间错误传递优先使用 go-zero / gRPC 内置 `error`,调用侧保持 `res, err :=` 风格API 层对前端错误继续复用 `respond`,后续总收尾再考虑迁到 gateway/shared
8. 服务间错误传递优先使用 go-zero / gRPC 内置 `error`,调用侧保持 `res, err :=` 风格API 层对前端错误通过 `backend/gateway/shared/respond` 写回,根 `backend/respond` 继续承载迁移期跨层响应语义
9. MySQL / Redis 容器启动归 `docker compose` 或运维层Go 服务只负责自己的连接初始化、AutoMigrate 和运行时依赖。
10. gateway 只做边缘转发、鉴权和轻量组合;不要把核心业务表、服务内部 Redis key、JWT 签发、额度账本放回 gateway。
11. 新服务开发可以和后续迁移并行,但必须独立目录、端口、配置和契约,不能污染正在迁移的服务边界。
@@ -1094,7 +1105,7 @@ graph TD
1. `backend/cmd/userauth/main.go` 是 userauth 独立进程入口。
2. `backend/services/userauth` 拥有 user/auth 核心业务、DAO、模型、JWT、黑名单、额度治理、zrpc server 和 token 记账幂等表。
3. `backend/gateway/api/userauth` 是 HTTP user 入口,`backend/gateway/client/userauth` 是 zrpc client`backend/gateway/middleware` 只调 userauth 做鉴权和额度门禁。
3. `backend/gateway/api/userauth` 是 HTTP user 入口,`backend/client/userauth` 是 zrpc client`backend/gateway/middleware` 只调 userauth 做鉴权和额度门禁。
4. `backend/shared/contracts/userauth``backend/shared/ports` 只承载跨层契约,不承载服务私有业务实现。
5. `cmd/all` 不再迁 `users``cmd/userauth` 自己迁 `users``user_token_usage_adjustments`
6. 完整本地 smoke 需要同时启动 `cmd/all``cmd/userauth`
@@ -1103,7 +1114,7 @@ graph TD
1. `backend/cmd/notification/main.go` 是 notification 独立进程入口,负责 DB 迁移、zrpc server、notification outbox consumer 和 retry loop 的统一生命周期。
2. `backend/services/notification` 拥有 notification 核心业务、DAO、模型、飞书 provider、幂等、投递记录状态机、重试扫描和 outbox handler。
3. `backend/gateway/client/notification` gateway 侧 zrpc clientgateway 只保留 notification HTTP 入口、鉴权和轻量组合逻辑,不再直连 notification DAO/service。
3. `backend/client/notification`跨进程 zrpc clientgateway 只保留 notification HTTP 入口、鉴权和轻量组合逻辑,不再直连 notification DAO/service。
4. `backend/shared/contracts/notification``backend/shared/ports` 只承载跨层契约和端口接口,不承载服务私有业务实现。
5. notification 内部是 `userauth` 同款最小手搓 zrpc 框架,不使用 goctl 自动脚手架;`rpc` 只保留 `NewServer``cmd/notification` 管理 signal、outbox consumer、retry loop 和 server 生命周期。
6.`backend/notification/*`、旧 `backend/dao/notification_channel.go`、旧 `backend/model/notification_channel.go` 和旧 `backend/service/events/notification_feishu.go` 已删除;若 `backend/notification` 目录壳仍存在,它不参与编译,也不作为活跃实现。
@@ -1114,7 +1125,7 @@ graph TD
1. `backend/cmd/active-scheduler/main.go` 是 active-scheduler 独立进程入口,负责 DB 迁移、zrpc server、active-scheduler outbox consumer、relay、retry loop 和 due job scanner 的统一生命周期。
2. `backend/services/active_scheduler` 拥有 active-scheduler DAO、rpc、sv 和领域核心;核心流程当前在 `backend/services/active_scheduler/core`,旧 `backend/active_scheduler` 不再作为活跃实现存在。
3. `backend/gateway/api` 是 HTTP 门面统一目录,`backend/gateway/client/activescheduler` gateway 侧 zrpc client。
3. `backend/gateway/api` 是 HTTP 门面统一目录,`backend/client/activescheduler`跨进程 zrpc client。
4. `backend/shared/contracts/activescheduler``backend/shared/ports` 只承载跨层契约和端口接口,不承载服务私有业务实现。
5. `cmd/all` 不再启动 active-scheduler workflow / scanner / handler完整本地 smoke 需要同时启动 `cmd/all``cmd/userauth``cmd/notification``cmd/active-scheduler`
6. 阶段 4 收口时仍共享主库访问 task、schedule、agent 会话和 notification outbox 相关表;阶段 5 已先通过 schedule / task RPC 继续缩小这条共享边界。
@@ -1123,7 +1134,7 @@ graph TD
1. `backend/cmd/schedule/main.go` 是 schedule 独立进程入口,`backend/cmd/task/main.go` 是 task 独立进程入口,`backend/cmd/task-class/main.go` 是 task-class 独立进程入口,`backend/cmd/course/main.go` 是 course 独立进程入口,四者各自初始化 DB / Redis / zrpc server 和所需服务内资源。
2. `backend/services/schedule` 拥有正式日程领域核心,`backend/services/task` 拥有任务池读写、完成/撤销、紧急性平移和 task outbox handler`backend/services/task_class` 拥有任务类与任务块维护、批量排入日程等核心逻辑,`backend/services/course` 拥有课程校验、课程导入和课表图片解析逻辑。
3. `backend/gateway/api` 继续作为 HTTP 门面统一目录,`backend/gateway/client/schedule``backend/gateway/client/task``backend/gateway/client/taskclass``backend/gateway/client/course` 作为 gateway 侧 zrpc client。
3. `backend/gateway/api` 继续作为 HTTP 门面统一目录,`backend/client/schedule``backend/client/task``backend/client/taskclass``backend/client/course` 作为跨进程 zrpc client。
4. `backend/shared/contracts/schedule``backend/shared/contracts/task``backend/shared/contracts/taskclass``backend/shared/contracts/course``backend/shared/ports` 只承载跨进程契约与端口接口,不放 DAO、model 或业务状态机。
5. active-scheduler 的 schedule facts / feedback / confirm apply 已走 schedule RPCtask facts / due job scanner 已走 task RPC启动依赖检查不再要求 `schedule_events``schedules``task_classes``task_items``tasks`
6. `task.urgency.promote.requested` 的消费边界已迁入 `cmd/task`;单体 outbox worker 不再启动 task service bus只保留 Agent 残留路径的 publish-only 写入能力,避免迁移期重复 relay / consume。
@@ -1199,6 +1210,43 @@ graph TD
处理:
1. 以当前编译入口和路由装配为准:`gateway/api/userauth` + `gateway/client/userauth` + `services/userauth` 是阶段 2 当前样板。
2. 任何 user/auth 新能力先放进 `services/userauth`gateway 只做 HTTP 适配respond 响应。
1. 以当前编译入口和路由装配为准:`gateway/api/userauth` + `backend/client/userauth` + `services/userauth` 是阶段 2 当前样板。
2. 任何 user/auth 新能力先放进 `services/userauth`gateway 只做 HTTP 适配,并通过 `backend/gateway/shared/respond`响应。
3. 如果 user/auth 调用失败,先查 `cmd/userauth` 是否启动、zrpc endpoint 是否正确、服务内 MySQL/Redis 是否可连,不要把逻辑搬回 gateway。
## 7. CP4 最终收口记录2026-05-05
1. `backend` 根目录已收口到 5 个一级目录:`services``client``gateway``cmd``shared`
2.`backend/bootstrap` 已删除;统一配置加载 canonical 入口为 `backend/shared/infra/bootstrap`
3.`backend/inits` 已迁入 `backend/cmd/internal/coreinit`,仅保留旧组合壳 `cmd/start.go` 所需的 core 残留域初始化语义;纯连接/句柄构造已切到 `backend/shared/infra/mysql``backend/shared/infra/redis``backend/shared/infra/eino`,历史 `InitEino` 兼容壳已删除。
4.`backend/infra/kafka``backend/infra/outbox` 已迁入 `backend/shared/infra/{kafka,outbox}``backend/infra/rag` 历史文档已迁到 `docs/backend/legacy-infra-rag-from-backend`
5.`backend/conv``backend/respond``backend/pkg``backend/middleware` 已分别迁入:
- `backend/shared/conv`
- `backend/shared/respond`
- `backend/shared/pkg`
- `backend/shared/middleware`
6.`backend/dao``backend/model` 的活跃迁移桥已整体迁入 `backend/shared/runtime/{dao,model}`;根 `backend/service/events` 的活跃事件胶水已迁入 `backend/shared/runtime/eventsvc`。这三处是 CP4 结束时的迁移期 runtime 收容区,不是长期 canonical 归属,只是为了先把根目录收口并保持编译、启动与回退面可用。
7. `backend/shared/legacy/service/*.go` 这批未再被 import 的旧业务实现已删除,不再继续以兼容回退面保留。
8. `backend/utils/pwd_encryption.go` 已下沉到 `backend/services/userauth/internal/auth/password.go`;根 `backend/utils` 已删除。
9. `backend/logic/smart_planning.go` 已下沉到 `backend/services/schedule/core/planning/smart_planning.go`;根 `backend/logic` 已删除。
10. `backend/shared/respond` 已收缩为纯共享错误语义与状态映射,不再直接依赖 `gin``backend/gateway/shared/respond` 继续只承载 gateway HTTP 写回应与错误适配。
11. `backend/shared/middleware``backend/shared/pkg``backend/shared/runtime` 已全部清空并删除HTTP middleware 已归到 `backend/gateway/middleware``GormCachePlugin` 已拆到 `backend/shared/infra/gormcache`,共享 `RateLimiter` 已拆到 `backend/shared/infra/ratelimit`agent token budget 已下沉到 `backend/services/agent/shared/token_budget.go`
12. `backend/shared` 当前目录口径已收敛为:`contracts``events``infra``ports``respond`;迁移期 runtime 桥接实现已挪到 `backend/services/runtime/{conv,dao,eventsvc,model}`
13. 本轮验证结果:
- `go test ./...` 通过,且 `.gocache` 已清理。
- `git diff --check` 通过。
- 最终真实 smoke 通过,产物见 `.tmp/backend-artifacts/cp4-final-clean-smoke-20260505-1/smoke-summary-final.json`
- smoke 覆盖:`health``register/login``task/create + task/get``schedule/today``task-class/list``memory/items``agent/chat``conversation-meta``conversation-timeline``context-stats`;全部返回 `200`SSE 合并结果为 `CP4_OK`,且 `[DONE]` 只有 1 个。
## 8. CP5 最终收口记录2026-05-05
1. `backend/shared` 已不再承载迁移期业务实现、DAO、GORM 实体或事件胶水;根共享层当前只保留 `contracts``events``infra``ports``respond` 五类内容。
2. 迁移期 runtime 桥接实现已从根共享层下沉到 `backend/services/runtime/{conv,dao,eventsvc,model}`,不再伪装成“全后端共享契约层”。
3. `backend/gateway/shared/respond` 已接管 Gin `DealWithError` 写回逻辑;根 `backend/shared/respond` 仅保留共享错误码、错误体和状态映射。
4. `backend/gateway/middleware` 已接管 HTTP `IdempotencyMiddleware``RateLimitMiddleware`;共享层只保留基础设施 `ratelimit``gormcache`
5.`InitEino` 兼容壳、旧 `shared/legacy` 命名以及未再被 import 的 `service/*.go` 旧实现均已删除。
6. CP5 最终验证:
- `go test ./...` 通过,且 `.gocache` 已清理。
- `git diff --check` 通过。
- 最终真实 smoke 通过,产物见 `.tmp/backend-artifacts/cp5-final-smoke-20260505-1/smoke-summary-final.json`
- smoke 覆盖:`health``register/login``task/create + task/get``schedule/today``task-class/list``memory/items``agent/chat``conversation-meta``conversation-timeline``context-stats`;全部返回 `200`SSE 合并结果为 `CP5_OK`,且 `[DONE]` 只有 1 个。
7. 至此 CP5“最终清理与文档收口”中与目录形态、共享层纯化、兼容壳清理、编译、diff check 和真实 smoke 相关的核心目标均已完成。