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:
@@ -0,0 +1,640 @@
|
||||
# HANDOFF:RAG 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`、甚至更多语料域都会明显更好管理。
|
||||
191
docs/backend/legacy-infra-rag-from-backend/RAG复用接口实施计划.md
Normal file
191
docs/backend/legacy-infra-rag-from-backend/RAG复用接口实施计划.md
Normal 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 2:MemoryCorpus 接入(灰度)
|
||||
|
||||
1. 把记忆检索从“模块内直连”改为调用 RAG Core。
|
||||
2. 保留旧路径开关 `memory.rag.enabled`,默认关闭。
|
||||
3. 验收:开启开关后功能等价,失败可自动降级旧链路。
|
||||
|
||||
### Round 3:WebCorpus 接入(灰度)
|
||||
|
||||
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 依赖)。
|
||||
@@ -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 handler;gateway 通过 `backend/gateway/client/notification` zrpc client 调用。
|
||||
2. `backend/services/notification` 已收进 DAO、model、sv、rpc、飞书 provider 和 outbox handler;gateway 通过 `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 / handler;gateway 的 `/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 client;gateway 不再通过 `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 client;gateway 只负责鉴权、参数绑定、短超时和响应透传,不再直接调用 `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 consumer,Agent 残留查询链路只允许 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 client;gateway 只负责鉴权、参数绑定、短超时和响应透传,不再直接调用 `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 与正式写日程均走 RPC;agent 内部仍存在直接 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 client;gateway 只负责鉴权、限流、幂等、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 与正式写日程均走 RPC;agent 内部仍存在 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 channel;Gateway 继续对前端输出原 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 bus;agent 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 返回 200,schedule-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 client;HTTP 门面属于 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 client;gateway 只保留 notification HTTP 入口、鉴权和轻量组合逻辑,不再直连 notification DAO/service。
|
||||
3. `backend/client/notification` 是跨进程 zrpc client;gateway 只保留 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 RPC,task 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 相关的核心目标均已完成。
|
||||
|
||||
Reference in New Issue
Block a user