Version: 0.9.76.dev.260505
后端: 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 的目录收口口径
This commit is contained in:
16
backend/services/memory/model/audit.go
Normal file
16
backend/services/memory/model/audit.go
Normal file
@@ -0,0 +1,16 @@
|
||||
package model
|
||||
|
||||
import "time"
|
||||
|
||||
// AuditLogDTO 是审计日志领域对象。
|
||||
type AuditLogDTO struct {
|
||||
ID int64
|
||||
MemoryID int64
|
||||
UserID int
|
||||
Operation string
|
||||
OperatorType string
|
||||
Reason string
|
||||
BeforeJSON string
|
||||
AfterJSON string
|
||||
CreatedAt *time.Time
|
||||
}
|
||||
134
backend/services/memory/model/config.go
Normal file
134
backend/services/memory/model/config.go
Normal file
@@ -0,0 +1,134 @@
|
||||
package model
|
||||
|
||||
import (
|
||||
"strings"
|
||||
"time"
|
||||
)
|
||||
|
||||
const (
|
||||
// MemoryReadModeLegacy 表示读取侧沿用“RAG 优先,失败再走 legacy”旧链路。
|
||||
MemoryReadModeLegacy = "legacy"
|
||||
// MemoryReadModeHybrid 表示读取侧走“结构化强约束 + 语义候选”混合链路。
|
||||
MemoryReadModeHybrid = "hybrid"
|
||||
|
||||
// MemoryInjectRenderModeFlat 表示沿用扁平列表渲染。
|
||||
MemoryInjectRenderModeFlat = "flat"
|
||||
// MemoryInjectRenderModeTypedV2 表示按记忆类型分段渲染。
|
||||
MemoryInjectRenderModeTypedV2 = "typed_v2"
|
||||
|
||||
// DefaultReadConstraintLimit 是 constraint 默认预算上限。
|
||||
DefaultReadConstraintLimit = 5
|
||||
// DefaultReadPreferenceLimit 是 preference 默认预算上限。
|
||||
DefaultReadPreferenceLimit = 5
|
||||
// DefaultReadFactLimit 是 fact 默认预算上限。
|
||||
DefaultReadFactLimit = 5
|
||||
)
|
||||
|
||||
// Config 是记忆模块配置对象(Day1 首版)。
|
||||
//
|
||||
// 职责边界:
|
||||
// 1. 只承载模块运行参数,不承载业务状态;
|
||||
// 2. 允许启动期统一注入,避免业务层直接依赖配置中心。
|
||||
type Config struct {
|
||||
Enabled bool
|
||||
RAGEnabled bool
|
||||
|
||||
ReadMode string
|
||||
ReadConstraintLimit int
|
||||
ReadPreferenceLimit int
|
||||
ReadFactLimit int
|
||||
InjectRenderMode string
|
||||
|
||||
ExtractPrompt string
|
||||
DecisionPrompt string
|
||||
|
||||
Threshold float64
|
||||
EnableReranker bool
|
||||
|
||||
LLMTemperature float64
|
||||
LLMTopP float64
|
||||
|
||||
JobMaxRetry int
|
||||
WorkerPollEvery time.Duration
|
||||
WorkerClaimBatch int
|
||||
|
||||
// 决策层配置。
|
||||
// 说明:
|
||||
// 1. DecisionEnabled 控制是否启用"召回→比对→汇总"决策流程;
|
||||
// 2. 默认关闭,旧路径完全保留,回滚无风险;
|
||||
// 3. DecisionFallbackMode 仅在决策流程整体报错时生效,不影响单条 LLM 比对失败(单条失败视为 unrelated)。
|
||||
DecisionEnabled bool
|
||||
DecisionCandidateTopK int // Milvus 语义召回候选数上限
|
||||
DecisionCandidateMinScore float64 // Milvus 语义召回最低相似度
|
||||
DecisionFallbackMode string // "legacy_add"(退回旧路径直接新增)/ "drop"(丢弃)
|
||||
WriteMode string // "legacy"(旧路径)/ "decision"(决策流程),仅 DecisionEnabled=true 时生效
|
||||
|
||||
// 写入置信度阈值。
|
||||
// 说明:
|
||||
// 1. 抽取结果 confidence 低于此值直接丢弃,不做入库;
|
||||
// 2. 默认 0.5,与"守门员"prompt 的 confidence>=0.5 输出规则配合;
|
||||
// 3. fallback 路径 confidence 设为 0.45,低于默认阈值,LLM 不可用时不写入。
|
||||
WriteMinConfidence float64
|
||||
|
||||
// 记忆模块 LLM 调用是否开启 thinking,由 config.yaml 的 agent.thinking.memory 注入。
|
||||
LLMThinking bool
|
||||
}
|
||||
|
||||
// NormalizeReadMode 统一读取模式字符串。
|
||||
func NormalizeReadMode(mode string) string {
|
||||
switch strings.ToLower(strings.TrimSpace(mode)) {
|
||||
case MemoryReadModeHybrid:
|
||||
return MemoryReadModeHybrid
|
||||
default:
|
||||
return MemoryReadModeLegacy
|
||||
}
|
||||
}
|
||||
|
||||
// NormalizeInjectRenderMode 统一注入渲染模式字符串。
|
||||
func NormalizeInjectRenderMode(mode string) string {
|
||||
switch strings.ToLower(strings.TrimSpace(mode)) {
|
||||
case MemoryInjectRenderModeTypedV2:
|
||||
return MemoryInjectRenderModeTypedV2
|
||||
default:
|
||||
return MemoryInjectRenderModeFlat
|
||||
}
|
||||
}
|
||||
|
||||
// EffectiveReadConstraintLimit 返回 constraint 生效预算。
|
||||
func (c Config) EffectiveReadConstraintLimit() int {
|
||||
return normalizePositiveLimit(c.ReadConstraintLimit, DefaultReadConstraintLimit)
|
||||
}
|
||||
|
||||
// EffectiveReadPreferenceLimit 返回 preference 生效预算。
|
||||
func (c Config) EffectiveReadPreferenceLimit() int {
|
||||
return normalizePositiveLimit(c.ReadPreferenceLimit, DefaultReadPreferenceLimit)
|
||||
}
|
||||
|
||||
// EffectiveReadFactLimit 返回 fact 生效预算。
|
||||
func (c Config) EffectiveReadFactLimit() int {
|
||||
return normalizePositiveLimit(c.ReadFactLimit, DefaultReadFactLimit)
|
||||
}
|
||||
|
||||
// EffectiveReadMode 返回生效读取模式。
|
||||
func (c Config) EffectiveReadMode() string {
|
||||
return NormalizeReadMode(c.ReadMode)
|
||||
}
|
||||
|
||||
// EffectiveInjectRenderMode 返回生效渲染模式。
|
||||
func (c Config) EffectiveInjectRenderMode() string {
|
||||
return NormalizeInjectRenderMode(c.InjectRenderMode)
|
||||
}
|
||||
|
||||
// TotalReadBudget 返回三类记忆的总预算上限。
|
||||
func (c Config) TotalReadBudget() int {
|
||||
return c.EffectiveReadConstraintLimit() +
|
||||
c.EffectiveReadPreferenceLimit() +
|
||||
c.EffectiveReadFactLimit()
|
||||
}
|
||||
|
||||
func normalizePositiveLimit(value int, defaultValue int) int {
|
||||
if value <= 0 {
|
||||
return defaultValue
|
||||
}
|
||||
return value
|
||||
}
|
||||
71
backend/services/memory/model/decision.go
Normal file
71
backend/services/memory/model/decision.go
Normal file
@@ -0,0 +1,71 @@
|
||||
package model
|
||||
|
||||
// RelationType 常量描述一条新 fact 与一条旧记忆之间的关系。
|
||||
//
|
||||
// 四种关系:
|
||||
// 1. duplicate — 完全重复,新 fact 没有新信息,旧记忆无需变动;
|
||||
// 2. update — 新 fact 是对旧记忆的修正、补充或更精确表述,需要合并更新;
|
||||
// 3. conflict — 新 fact 与旧记忆矛盾,旧记忆已过时,应删旧增新;
|
||||
// 4. unrelated — 两者说的是不同的事情,互不影响。
|
||||
const (
|
||||
RelationDuplicate = "duplicate"
|
||||
RelationUpdate = "update"
|
||||
RelationConflict = "conflict"
|
||||
RelationUnrelated = "unrelated"
|
||||
)
|
||||
|
||||
// CandidateSnapshot 是喂给 LLM 的旧记忆候选快照。
|
||||
//
|
||||
// 职责边界:
|
||||
// 1. 只承载 LLM 做关系判断所需的最小信息;
|
||||
// 2. MemoryID 是真实 memory_id,LLM 不可见,仅供汇总决策时使用;
|
||||
// 3. Score 是向量召回的相似度分数,用于多条 update 时选最优候选。
|
||||
type CandidateSnapshot struct {
|
||||
MemoryID int64
|
||||
Title string
|
||||
Content string
|
||||
MemoryType string
|
||||
Score float64 // Milvus 相似度分数(0 表示来自 Hash 查询)
|
||||
}
|
||||
|
||||
// ComparisonResult 是单次"新 fact vs 一条旧记忆"的 LLM 输出。
|
||||
//
|
||||
// 职责边界:
|
||||
// 1. 只描述 LLM 对一对比较的结果,不包含最终决策动作;
|
||||
// 2. UpdatedContent/UpdatedTitle 仅在 relation=update 时有意义;
|
||||
// 3. Reason 写审计日志用,便于后续复盘 LLM 判断依据。
|
||||
type ComparisonResult struct {
|
||||
MemoryID int64 // 被比较的旧记忆 ID
|
||||
Relation string // duplicate / update / conflict / unrelated
|
||||
UpdatedContent string // 仅 relation=update 时有意义:合并后的新内容
|
||||
UpdatedTitle string // 仅 relation=update 时有意义:合并后的新标题
|
||||
Reason string // 判断理由(写审计日志用)
|
||||
}
|
||||
|
||||
// FinalDecision 是汇总后的最终动作。
|
||||
//
|
||||
// 说明:
|
||||
// 1. 由确定性代码产出,不是 LLM 产出;
|
||||
// 2. Action 取值复用 status.go 中已定义的 DecisionActionAdd/Update/Delete/None 常量;
|
||||
// 3. TargetID 在 UPDATE/DELETE 时指向旧记忆 ID,ADD/NONE 时为 0。
|
||||
type FinalDecision struct {
|
||||
Action string // ADD / UPDATE / DELETE / NONE
|
||||
TargetID int64 // UPDATE/DELETE 时指向旧记忆 ID
|
||||
Title string // UPDATE 时的新标题
|
||||
Content string // UPDATE 时的新内容
|
||||
Reason string // 汇总理由
|
||||
}
|
||||
|
||||
// UpdateContentFields 是 UPDATE 动作需要更新的字段集合。
|
||||
//
|
||||
// 说明:
|
||||
// 1. 只包含 UPDATE 动作实际需要修改的字段,避免全量覆盖;
|
||||
// 2. NormalizedContent/ContentHash 由调用方重新计算,保证一致性。
|
||||
type UpdateContentFields struct {
|
||||
Title string
|
||||
Content string
|
||||
NormalizedContent string
|
||||
ContentHash string
|
||||
Confidence float64
|
||||
Importance float64
|
||||
}
|
||||
105
backend/services/memory/model/item.go
Normal file
105
backend/services/memory/model/item.go
Normal file
@@ -0,0 +1,105 @@
|
||||
package model
|
||||
|
||||
import "time"
|
||||
|
||||
// ItemDTO 是记忆条目对外读写 DTO。
|
||||
//
|
||||
// 职责边界:
|
||||
// 1. 面向 memory 模块内部服务层使用;
|
||||
// 2. 不直接绑定 GORM 标签,避免传输结构与存储结构强耦合。
|
||||
type ItemDTO struct {
|
||||
ID int64
|
||||
UserID int
|
||||
ConversationID string
|
||||
AssistantID string
|
||||
RunID string
|
||||
MemoryType string
|
||||
Title string
|
||||
Content string
|
||||
ContentHash string
|
||||
Confidence float64
|
||||
Importance float64
|
||||
SensitivityLevel int
|
||||
IsExplicit bool
|
||||
Status string
|
||||
TTLAt *time.Time
|
||||
CreatedAt *time.Time
|
||||
UpdatedAt *time.Time
|
||||
}
|
||||
|
||||
// ItemQuery 描述 memory_items 的通用查询条件。
|
||||
//
|
||||
// 职责边界:
|
||||
// 1. 只表达 memory 仓储层需要的过滤条件;
|
||||
// 2. 不直接承载注入策略、重排策略等上层业务语义;
|
||||
// 3. IncludeGlobal 用于“会话级 + 全局级”混合读取场景。
|
||||
type ItemQuery struct {
|
||||
UserID int
|
||||
ConversationID string
|
||||
AssistantID string
|
||||
RunID string
|
||||
Statuses []string
|
||||
MemoryTypes []string
|
||||
IncludeGlobal bool
|
||||
OnlyUnexpired bool
|
||||
Limit int
|
||||
Now time.Time
|
||||
}
|
||||
|
||||
// RetrieveRequest 描述“供提示词注入前读取”所需的最小参数。
|
||||
type RetrieveRequest struct {
|
||||
Query string
|
||||
UserID int
|
||||
ConversationID string
|
||||
AssistantID string
|
||||
RunID string
|
||||
MemoryTypes []string
|
||||
Limit int
|
||||
Now time.Time
|
||||
}
|
||||
|
||||
// ListItemsRequest 描述记忆管理页列表查询参数。
|
||||
type ListItemsRequest struct {
|
||||
UserID int
|
||||
ConversationID string
|
||||
Statuses []string
|
||||
MemoryTypes []string
|
||||
Limit int
|
||||
}
|
||||
|
||||
// CreateItemFields 是 repo 层落库时真正需要的字段集合。
|
||||
type CreateItemFields struct {
|
||||
UserID int
|
||||
ConversationID string
|
||||
AssistantID string
|
||||
RunID string
|
||||
MemoryType string
|
||||
Title string
|
||||
Content string
|
||||
NormalizedContent string
|
||||
ContentHash string
|
||||
Confidence float64
|
||||
Importance float64
|
||||
SensitivityLevel int
|
||||
IsExplicit bool
|
||||
Status string
|
||||
TTLAt *time.Time
|
||||
VectorStatus string
|
||||
SourceMessageID *int64
|
||||
SourceEventID *string
|
||||
LastAccessAt *time.Time
|
||||
}
|
||||
|
||||
// UpdateItemFields 是“用户管理侧修改记忆”时 repo 层允许更新的字段集合。
|
||||
type UpdateItemFields struct {
|
||||
MemoryType string
|
||||
Title string
|
||||
Content string
|
||||
NormalizedContent string
|
||||
ContentHash string
|
||||
Confidence float64
|
||||
Importance float64
|
||||
SensitivityLevel int
|
||||
IsExplicit bool
|
||||
TTLAt *time.Time
|
||||
}
|
||||
45
backend/services/memory/model/job.go
Normal file
45
backend/services/memory/model/job.go
Normal file
@@ -0,0 +1,45 @@
|
||||
package model
|
||||
|
||||
import "time"
|
||||
|
||||
// ExtractJobPayload 是 memory_jobs.payload_json 的领域视图。
|
||||
//
|
||||
// 职责边界:
|
||||
// 1. 只描述抽取任务执行所需字段;
|
||||
// 2. 与数据库模型解耦,避免后续表结构调整污染 worker 逻辑。
|
||||
type ExtractJobPayload struct {
|
||||
UserID int `json:"user_id"`
|
||||
ConversationID string `json:"conversation_id"`
|
||||
AssistantID string `json:"assistant_id,omitempty"`
|
||||
RunID string `json:"run_id,omitempty"`
|
||||
SourceMessageID int64 `json:"source_message_id,omitempty"`
|
||||
SourceRole string `json:"source_role"`
|
||||
SourceText string `json:"source_text"`
|
||||
OccurredAt time.Time `json:"occurred_at"`
|
||||
TraceID string `json:"trace_id,omitempty"`
|
||||
IdempotencyKey string `json:"idempotency_key"`
|
||||
}
|
||||
|
||||
// FactCandidate 表示抽取阶段得到的候选事实。
|
||||
type FactCandidate struct {
|
||||
MemoryType string
|
||||
Title string
|
||||
Content string
|
||||
Confidence float64
|
||||
Importance float64
|
||||
SensitivityLevel int
|
||||
IsExplicit bool
|
||||
}
|
||||
|
||||
// NormalizedFact 表示通过标准化后的可入库事实。
|
||||
type NormalizedFact struct {
|
||||
MemoryType string
|
||||
Title string
|
||||
Content string
|
||||
NormalizedContent string
|
||||
ContentHash string
|
||||
Confidence float64
|
||||
Importance float64
|
||||
SensitivityLevel int
|
||||
IsExplicit bool
|
||||
}
|
||||
20
backend/services/memory/model/settings.go
Normal file
20
backend/services/memory/model/settings.go
Normal file
@@ -0,0 +1,20 @@
|
||||
package model
|
||||
|
||||
import "time"
|
||||
|
||||
// UserSettingDTO 是用户记忆开关领域对象。
|
||||
type UserSettingDTO struct {
|
||||
UserID int
|
||||
MemoryEnabled bool
|
||||
ImplicitMemoryEnabled bool
|
||||
SensitiveMemoryEnabled bool
|
||||
UpdatedAt *time.Time
|
||||
}
|
||||
|
||||
// UpdateUserSettingRequest 描述记忆开关写入请求。
|
||||
type UpdateUserSettingRequest struct {
|
||||
UserID int
|
||||
MemoryEnabled bool
|
||||
ImplicitMemoryEnabled bool
|
||||
SensitiveMemoryEnabled bool
|
||||
}
|
||||
56
backend/services/memory/model/status.go
Normal file
56
backend/services/memory/model/status.go
Normal file
@@ -0,0 +1,56 @@
|
||||
package model
|
||||
|
||||
import "strings"
|
||||
|
||||
const (
|
||||
// MemoryTypePreference 表示用户偏好类记忆。
|
||||
MemoryTypePreference = "preference"
|
||||
// MemoryTypeConstraint 表示硬约束类记忆。
|
||||
MemoryTypeConstraint = "constraint"
|
||||
// MemoryTypeFact 表示一般事实类记忆。
|
||||
MemoryTypeFact = "fact"
|
||||
)
|
||||
|
||||
const (
|
||||
// DecisionActionAdd 表示新增记忆。
|
||||
DecisionActionAdd = "ADD"
|
||||
// DecisionActionUpdate 表示更新记忆。
|
||||
DecisionActionUpdate = "UPDATE"
|
||||
// DecisionActionDelete 表示删除记忆。
|
||||
DecisionActionDelete = "DELETE"
|
||||
// DecisionActionNone 表示不做写入动作。
|
||||
DecisionActionNone = "NONE"
|
||||
)
|
||||
|
||||
var validMemoryTypes = map[string]struct{}{
|
||||
MemoryTypePreference: {},
|
||||
MemoryTypeConstraint: {},
|
||||
MemoryTypeFact: {},
|
||||
}
|
||||
|
||||
var validDecisionActions = map[string]struct{}{
|
||||
DecisionActionAdd: {},
|
||||
DecisionActionUpdate: {},
|
||||
DecisionActionDelete: {},
|
||||
DecisionActionNone: {},
|
||||
}
|
||||
|
||||
// NormalizeMemoryType 统一记忆类型字符串。
|
||||
//
|
||||
// 职责边界:
|
||||
// 1. 只做字符串标准化,不做业务兜底;
|
||||
// 2. 若调用方传入非法类型,返回空字符串供上游决定丢弃或降级。
|
||||
func NormalizeMemoryType(memoryType string) string {
|
||||
normalized := strings.ToLower(strings.TrimSpace(memoryType))
|
||||
if _, ok := validMemoryTypes[normalized]; !ok {
|
||||
return ""
|
||||
}
|
||||
return normalized
|
||||
}
|
||||
|
||||
// IsValidDecisionAction 校验决策动作是否合法。
|
||||
func IsValidDecisionAction(action string) bool {
|
||||
normalized := strings.ToUpper(strings.TrimSpace(action))
|
||||
_, ok := validDecisionActions[normalized]
|
||||
return ok
|
||||
}
|
||||
Reference in New Issue
Block a user