feat: 短期多轮历史接入 Eino 图 MessagesPlaceholder (⑨)
会话历史(Redis,易失,与长期画像分开)经 MCP 工具进出 Eino 图:
recall 召回历史填 MessagesPlaceholder,写回把本轮 user/assistant 落历史。
- mcp-go: internal/history(go-redis, sundynix:history:<session>, LPUSH+LTRIM 保留近20条,
24h TTL) + 工具 history_get(返回JSON turns)/history_append; main 开 Redis(降级)
- dispatcher Eino: 模板加 MessagesPlaceholder('history'); recall 调 history_get→转 schema.Message;
Handle 累积 answer; memorize 异步 history_append(user+assistant)
- shared: contract.MetaSessionID; gateway: SubmitTask 注入 Meta[session_id](X-Session-ID 头,缺省 default)
- demo.sh: 同会话两轮提交,验证第2轮召回第1轮历史
- 验证: 4 模块 build✓ + 3 e2e PASS; live 跑通——轮1=0轮历史→落库, 轮2 history_get 命中→注入
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
@@ -14,36 +14,42 @@ import (
|
||||
// memoryFetcher 召回某用户与本次输入相关的偏好记忆(经 MCP memory_get 工具)。
|
||||
type memoryFetcher func(ctx context.Context, userID, query string) string
|
||||
|
||||
// historyFetcher 召回某会话的短期多轮历史(经 MCP history_get 工具)。
|
||||
type historyFetcher func(ctx context.Context, sessionID string) []*schema.Message
|
||||
|
||||
// buildGraph 编译这套"记忆增强"图:
|
||||
//
|
||||
// START → recall(召回画像→写State) → prompt(注入system) → model(流式) → END
|
||||
// START → recall(召回画像+历史→写State) → prompt(注入system+history) → model(流式) → END
|
||||
//
|
||||
// 返回可流式执行的 Runnable。
|
||||
func buildGraph(ctx context.Context, pool *llm.Pool, fetch memoryFetcher) (compose.Runnable[*contract.Task, *schema.Message], error) {
|
||||
func buildGraph(ctx context.Context, pool *llm.Pool, fetch memoryFetcher, fetchHist historyFetcher) (compose.Runnable[*contract.Task, *schema.Message], error) {
|
||||
g := compose.NewGraph[*contract.Task, *schema.Message](
|
||||
compose.WithGenLocalState(func(context.Context) *AgentState { return &AgentState{} }),
|
||||
)
|
||||
|
||||
// 1) recall:取 user_id → memory_get 召回画像 → 写入 State,并输出模板变量。
|
||||
// 1) recall:取 user_id/session_id → 召回画像(memory_get)+历史(history_get) → 写 State,输出模板变量。
|
||||
if err := g.AddLambdaNode("recall", compose.InvokableLambda(
|
||||
func(ctx context.Context, t *contract.Task) (map[string]any, error) {
|
||||
uid, _ := t.Meta[contract.MetaUserID].(string)
|
||||
sid, _ := t.Meta[contract.MetaSessionID].(string)
|
||||
profile := fetch(ctx, uid, string(t.Graph))
|
||||
hist := fetchHist(ctx, sid)
|
||||
_ = compose.ProcessState(ctx, func(_ context.Context, s *AgentState) error {
|
||||
s.UserID, s.Profile, s.Input = uid, profile, string(t.Graph)
|
||||
s.UserID, s.SessionID, s.Profile, s.Input = uid, sid, profile, string(t.Graph)
|
||||
return nil
|
||||
})
|
||||
if profile == "" {
|
||||
profile = "(暂无该用户的偏好记忆)"
|
||||
}
|
||||
return map[string]any{"profile": profile, "query": string(t.Graph)}, nil
|
||||
return map[string]any{"profile": profile, "query": string(t.Graph), "history": hist}, nil
|
||||
})); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
// 2) prompt:把画像注入 system message,用户输入作为 user message。
|
||||
// 2) prompt:画像注入 system,历史用占位符插入,用户输入作为 user message。
|
||||
tpl := prompt.FromMessages(schema.FString,
|
||||
schema.SystemMessage("你在与特定用户对话。关于该用户的已知信息:\n{profile}\n请据此个性化作答并保持其偏好。"),
|
||||
schema.MessagesPlaceholder("history", true),
|
||||
schema.UserMessage("{query}"),
|
||||
)
|
||||
if err := g.AddChatTemplateNode("prompt", tpl); err != nil {
|
||||
|
||||
Reference in New Issue
Block a user