feat(memory-source): 打通 commit→distill→memories.source→recall 全链(含 metadata.source 与 wiki:<路径> 标记)
- recall.go: RecallResult.Source = 记忆 source(此前只有 struct 字段没人填) - distill/engine.go: DistillInput.Source(来源透传) - auto_distill.go: AutoDistillTrigger 增加 source 参数并透传 - routes/core.go: /commit 请求体新增 source + metadata(此前缺失 → Go 静默丢弃 → 入库恒空) - server.go: 蒸馏产物 Source = wiki:<路径>(有出处时),手动蒸馏端点也支持 source - 清理 worker 误提交的 15MB 二进制(go/cmd/zhiyid/zhiyid-new)并加 .gitignore
This commit is contained in:
Binary file not shown.
@@ -13,7 +13,7 @@ import (
|
||||
var DistillEngineRef *distill.Engine
|
||||
|
||||
// AutoDistillTrigger commit 后自动触发蒸馏流水线
|
||||
func AutoDistillTrigger(episodeID string, content string, category string, namespace string, agentID string) {
|
||||
func AutoDistillTrigger(episodeID string, content string, category string, namespace string, agentID string, source string) {
|
||||
// 1. 硬规则过滤
|
||||
if !distill.HardRulesPass(content) {
|
||||
return
|
||||
@@ -27,6 +27,7 @@ func AutoDistillTrigger(episodeID string, content string, category string, names
|
||||
Category: distill.Category(category),
|
||||
Namespace: namespace,
|
||||
AgentID: agentID,
|
||||
Source: source,
|
||||
})
|
||||
} else {
|
||||
// 降级: 关键词提取
|
||||
|
||||
@@ -79,6 +79,10 @@ func (a *API) Commit(w http.ResponseWriter, r *http.Request) {
|
||||
// resolve_conflict=true: 确认这是修正/新事实, 检出冲突时仍触发蒸馏
|
||||
// (2026-09-18 t_a54aae79 #14: 此前 note 里宣传了该参数但请求结构体没有它)
|
||||
ResolveConflict bool `json:"resolve_conflict"`
|
||||
// 2026-09-26:记忆出处(笔记路径等)。顶层 source 优先;wiki_curator/插件走 metadata.source。
|
||||
// 此前结构体没有这两个字段 → Go 静默丢弃 → 入库 source 恒为空("发送≠落库")。
|
||||
Source string `json:"source"`
|
||||
Metadata map[string]string `json:"metadata"`
|
||||
}
|
||||
if err := json.NewDecoder(r.Body).Decode(&req); err != nil {
|
||||
respondError(w, 400, "invalid body")
|
||||
@@ -95,6 +99,13 @@ func (a *API) Commit(w http.ResponseWriter, r *http.Request) {
|
||||
req.Category = "general"
|
||||
}
|
||||
|
||||
// 2026-09-26:解析来源(笔记出处)—— 顶层 source 优先,其次 metadata.source。
|
||||
// 空值维持原行为:蒸馏产物来源保持 llm_distill。
|
||||
src := req.Source
|
||||
if src == "" && req.Metadata != nil {
|
||||
src = req.Metadata["source"]
|
||||
}
|
||||
|
||||
// 1. 写入 episode(原始日志,总是写入)
|
||||
epID, err := a.LanceDB.InsertEpisode(req.AgentID, req.Namespace, req.Content, req.Category)
|
||||
if err != nil {
|
||||
@@ -120,7 +131,7 @@ func (a *API) Commit(w http.ResponseWriter, r *http.Request) {
|
||||
a.mergeMemory(existing, req.Content, req.Category, req.AgentID)
|
||||
// 合并后仍需触发蒸馏:让 t6 等冲突检测器有机会比对这条记忆,
|
||||
// 且 exact_dup 可能是 namespace 相同但实际语义冲突(如"在"vs"不在")。
|
||||
go AutoDistillTrigger(epID, req.Content, req.Category, req.Namespace, req.AgentID)
|
||||
go AutoDistillTrigger(epID, req.Content, req.Category, req.Namespace, req.AgentID, src)
|
||||
respond(w, 200, map[string]string{
|
||||
"episode_id": epID, "memory_id": existing.ID,
|
||||
"status": "merged", "reason": "exact_dup",
|
||||
@@ -134,7 +145,7 @@ func (a *API) Commit(w http.ResponseWriter, r *http.Request) {
|
||||
if eErr == nil && cosineSimilarity(vector, ev) >= 0.98 {
|
||||
a.mergeMemory(existing, req.Content, req.Category, req.AgentID)
|
||||
// 与 exact_dup 同理:合并后仍需触发蒸馏,让冲突检测器有机会比对。
|
||||
go AutoDistillTrigger(epID, req.Content, req.Category, req.Namespace, req.AgentID)
|
||||
go AutoDistillTrigger(epID, req.Content, req.Category, req.Namespace, req.AgentID, src)
|
||||
respond(w, 200, map[string]string{
|
||||
"episode_id": epID, "memory_id": existing.ID,
|
||||
"status": "merged", "reason": "near_dup",
|
||||
@@ -200,9 +211,9 @@ func (a *API) Commit(w http.ResponseWriter, r *http.Request) {
|
||||
// 2026-09-18 (t_a54aae79 #14): 客户端显式确认修正/新事实 → 仍触发蒸馏
|
||||
respData["conflicts"] = conflicts
|
||||
respData["distill_note"] = "conflict detected but resolve_conflict=true: proceeding with auto-distill"
|
||||
go AutoDistillTrigger(epID, req.Content, req.Category, req.Namespace, req.AgentID)
|
||||
go AutoDistillTrigger(epID, req.Content, req.Category, req.Namespace, req.AgentID, src)
|
||||
} else {
|
||||
go AutoDistillTrigger(epID, req.Content, req.Category, req.Namespace, req.AgentID)
|
||||
go AutoDistillTrigger(epID, req.Content, req.Category, req.Namespace, req.AgentID, src)
|
||||
}
|
||||
respond(w, 201, respData)
|
||||
}
|
||||
|
||||
@@ -207,7 +207,7 @@ func NewServer() http.Handler {
|
||||
UpdatedAt: time.Now(),
|
||||
DerivedFrom: input.EpisodeID,
|
||||
Freshness: "fresh",
|
||||
Source: "llm_distill", // 2026-09-07 方案C provenance: LLM蒸馏推断来源
|
||||
Source: srcOrLLMDistill(input.Source), // 2026-09-26:有笔记出处→"wiki:<路径>";否则沿用 llm_distill(2026-09-07 方案C provenance)
|
||||
}
|
||||
if err := ldb.InsertMemory(mem); err != nil {
|
||||
log.Printf("[distill] commit fact failed: %v", err)
|
||||
@@ -953,6 +953,8 @@ func NewServer() http.Handler {
|
||||
Category string `json:"category"`
|
||||
Namespace string `json:"namespace"`
|
||||
AgentID string `json:"agent_id"`
|
||||
// 2026-09-26:手动蒸馏也支持带出处(便于补跑 wiki 批时保留笔记路径)
|
||||
Source string `json:"source"`
|
||||
}
|
||||
if err := json.NewDecoder(r.Body).Decode(&req); err != nil {
|
||||
http.Error(w, "invalid request body", http.StatusBadRequest)
|
||||
@@ -969,7 +971,7 @@ func NewServer() http.Handler {
|
||||
req.AgentID = "hermes-a06"
|
||||
}
|
||||
epID := fmt.Sprintf("ep_manual_%d", time.Now().UnixNano())
|
||||
routes.AutoDistillTrigger(epID, req.Content, req.Category, req.Namespace, req.AgentID)
|
||||
routes.AutoDistillTrigger(epID, req.Content, req.Category, req.Namespace, req.AgentID, req.Source)
|
||||
respondJSON(w, 200, map[string]string{"status": "queued", "episode_id": epID})
|
||||
})
|
||||
// 蒸馏队列状态端点(G8-G9 配套)
|
||||
@@ -1737,3 +1739,13 @@ func runStartupChecks(backend string) {
|
||||
log.Printf("[startup] 数据目录正常: %s", canonicalDataDir)
|
||||
}
|
||||
}
|
||||
|
||||
// srcOrLLMDistill 2026-09-26:蒸馏产物来源判定。
|
||||
// 有笔记出处(commit 时带 source/metadata.source,如 wiki_curator 传的 .md 路径)→ 标 "wiki:<路径>",
|
||||
// 使 recall 回来的每条记忆都能追溯到自己来自哪篇笔记;无出处则沿用历史值 llm_distill。
|
||||
func srcOrLLMDistill(src string) string {
|
||||
if src != "" {
|
||||
return "wiki:" + src
|
||||
}
|
||||
return "llm_distill"
|
||||
}
|
||||
|
||||
@@ -41,6 +41,8 @@ type DistillInput struct {
|
||||
Category Category
|
||||
Namespace string
|
||||
AgentID string
|
||||
// 2026-09-26:来源(笔记路径等)—— 透传到蒸馏产物 MemoryRecord.Source,recall 时带回
|
||||
Source string
|
||||
}
|
||||
|
||||
// DistillResult 蒸馏输出
|
||||
|
||||
@@ -240,6 +240,8 @@ func (p *RecallPipeline) recallUncached(query, namespace string, topK int, diver
|
||||
// 2026-09-22:带上来源,供全库召回时分辨记忆归属域
|
||||
Namespace: c.Namespace,
|
||||
AgentID: c.AgentID,
|
||||
// 2026-09-26:把记忆出处直通给调用方 —— 蒸馏/审核记忆的 source 形如 "wiki:<笔记路径>"
|
||||
Source: c.Source,
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user