后端: 1.阶段 6 agent / memory 服务化收口 - 新增 cmd/agent 独立进程入口,承载 agent zrpc server、agent outbox relay / consumer 和运行时依赖初始化 - 补齐 services/agent/rpc 的 Chat stream 与 conversation meta/list/timeline、schedule-preview、context-stats、schedule-state unary RPC - 新增 gateway/client/agent 与 shared/contracts/agent,将 /api/v1/agent chat 和非 chat 门面切到 agent zrpc - 收缩 gateway 本地 AgentService 装配,双 RPC 开关开启时不再初始化本地 agent 编排、LLM、RAG 和 memory reader fallback - 将 backend/memory 物理迁入 services/memory,私有实现收入 internal,保留 module/model/observe 作为 memory 服务门面 - 调整 memory outbox、memory reader 和 agent 记忆渲染链路的 import 与服务边界,cmd/memory 独占 memory worker / consumer - 关闭 gateway 侧 agent outbox worker 所有权,agent relay / consumer 由 cmd/agent 独占,gateway 仅保留 HTTP/SSE 门面与迁移期开关回退 - 更新阶段 6 文档,记录 agent / memory 当前切流点、smoke 结果,以及 backend/client 与 gateway/shared 的目录收口口径
84 lines
3.3 KiB
Go
84 lines
3.3 KiB
Go
package service
|
||
|
||
import (
|
||
"time"
|
||
|
||
memorymodel "github.com/LoveLosita/smartflow/backend/services/memory/model"
|
||
ragservice "github.com/LoveLosita/smartflow/backend/services/rag"
|
||
)
|
||
|
||
// buildReadScopedItemQuery 构造读侧统一使用的 MySQL 查询条件。
|
||
//
|
||
// 职责边界:
|
||
// 1. 只负责把 RetrieveRequest 映射成“读侧作用域”查询参数;
|
||
// 2. 不负责真正查库,也不负责排序、裁剪或注入;
|
||
// 3. conversation_id 字段在这里刻意不参与过滤,仅保留在记忆记录元数据里供审计与溯源使用。
|
||
//
|
||
// 步骤化说明:
|
||
// 1. 读侧始终按 user_id 作为硬隔离边界,避免跨用户串记忆。
|
||
// 2. assistant_id / run_id 仍允许参与过滤,因为它们表达的是助手实例与执行轮次边界,而不是“是否跨对话召回”的问题。
|
||
// 3. conversation_id 明确置空,原因是聊天上下文窗口已经覆盖同对话信息;记忆读侧的价值主要在跨对话补充。
|
||
func buildReadScopedItemQuery(
|
||
req memorymodel.RetrieveRequest,
|
||
now time.Time,
|
||
statuses []string,
|
||
limit int,
|
||
) memorymodel.ItemQuery {
|
||
return memorymodel.ItemQuery{
|
||
UserID: req.UserID,
|
||
ConversationID: "",
|
||
AssistantID: req.AssistantID,
|
||
RunID: req.RunID,
|
||
Statuses: statuses,
|
||
MemoryTypes: normalizeRetrieveMemoryTypes(req.MemoryTypes),
|
||
IncludeGlobal: true,
|
||
OnlyUnexpired: true,
|
||
Limit: limit,
|
||
Now: now,
|
||
}
|
||
}
|
||
|
||
// buildReadScopedRAGRequest 构造读侧统一使用的 RAG 检索请求。
|
||
//
|
||
// 职责边界:
|
||
// 1. 只负责生成 memory 检索请求,不负责执行向量检索;
|
||
// 2. 不负责阈值外的重排、fallback 或去重;
|
||
// 3. conversation_id 字段同样只保留在文档 metadata 中,不再作为聊天读侧的硬过滤条件。
|
||
//
|
||
// 步骤化说明:
|
||
// 1. user_id 仍是唯一必须保留的硬过滤条件,确保召回范围限定在当前用户。
|
||
// 2. conversation_id 明确置空,避免旧对话记忆在进入相似度计算前就被 metadata filter 提前挡掉。
|
||
// 3. assistant_id / run_id 保持透传,方便后续若存在多助手场景时继续做更细粒度隔离。
|
||
func buildReadScopedRAGRequest(
|
||
req memorymodel.RetrieveRequest,
|
||
topK int,
|
||
threshold float64,
|
||
) ragservice.MemoryRetrieveRequest {
|
||
return ragservice.MemoryRetrieveRequest{
|
||
Query: req.Query,
|
||
TopK: topK,
|
||
Threshold: threshold,
|
||
Action: "search",
|
||
UserID: req.UserID,
|
||
ConversationID: "",
|
||
AssistantID: req.AssistantID,
|
||
RunID: req.RunID,
|
||
MemoryTypes: normalizeRetrieveMemoryTypes(req.MemoryTypes),
|
||
}
|
||
}
|
||
|
||
// shouldReturnSemanticRAGResult 判断当前是否可以直接采用 RAG 结果。
|
||
//
|
||
// 职责边界:
|
||
// 1. 只负责表达“RAG 是否足以短路后续 MySQL fallback”这一条业务规则;
|
||
// 2. 不负责执行任何检索,也不负责日志记录;
|
||
// 3. 返回 false 不代表错误,只代表调用方应继续尝试数据库兜底。
|
||
//
|
||
// 步骤化说明:
|
||
// 1. RAG 报错时,一定不能短路,必须继续走 MySQL fallback。
|
||
// 2. RAG 0 命中时,同样不能短路;否则会把“成功执行但没有候选”误当成最终结果。
|
||
// 3. 只有“无报错且结果非空”时,才允许直接返回 RAG 结果。
|
||
func shouldReturnSemanticRAGResult(items []memorymodel.ItemDTO, err error) bool {
|
||
return err == nil && len(items) > 0
|
||
}
|