Files
sundynix-agentix/sundynix-dispatcher/internal/llm/pool.go
T
Blizzard cbd130ecae feat: 第一张真实 Eino 图 + 偏好记忆(让模型知道是我)
dispatcher 不再手搓 pool.Stream,改用编译好的 Eino 图驱动;接入用户常驻画像,
推理前召回并注入 system prompt,实现个性化(架构'心脏'首次真跳)。

Eino 图(dispatcher/internal/eino): START→recall→prompt→model→END + 全局 State
- recall(Lambda): 取 Meta[user_id] → 调 MCP memory_get → ProcessState 写画像
- prompt(ChatTemplate): {profile} 注入 system,{query} 作 user
- model: poolModel 适配 LLM Pool 为 model.BaseChatModel(Generate+Stream, schema.Pipe)
- 写回: 流排空后异步 memorize(流式节点走 OnEndWithStreamOutput 非 OnEndFn)

记忆存储(mcp-go owns): GORM Profile→sundynix_user_profile(复合主键, AutoMigrate,
遵守前缀约定), 新工具 memory_get/memory_upsert, 连不上降级
Gateway: SubmitTask 注入 Meta[user_id](X-User-ID 头), PUT /api/v1/memory→memory_upsert
shared: contract.MetaUserID; llm.Pool 拆出 StreamText

验证: 4 模块 build✓ + 3 e2e PASS; live 跑通——PUT 偏好落 sundynix_user_profile,
带 X-User-ID 提交→Eino recall 召回→注入→SSE 流出含画像的个性化回答, writeback 触发

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-10 14:06:18 +08:00

68 lines
2.0 KiB
Go
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
// Package llm 抽象 LLM PoolvLLM / Ollama 集群)的负载均衡与流式推理。
package llm
import (
"context"
"strings"
"time"
)
// Pool 维护后端 LLM 实例列表与路由策略。
type Pool struct{ /* backends []Backend */ }
func NewPool() *Pool { return &Pool{} }
// 占位参数:模拟真实后端的 TTFT(首 token 延迟) 与逐 token 间隔。
const (
timeToFirstToken = 700 * time.Millisecond
interTokenDelay = 60 * time.Millisecond
)
// Stream 选择一个后端进行流式推理,逐 Token 回调 onToken。
// 当前为占位实现:把对 prompt 的确定性回复按 token 流式返回,
// 真实接入 vLLM/Ollama 时替换为后端 streaming API 即可(回调签名不变)。
func (p *Pool) Stream(ctx context.Context, prompt string, onToken func([]byte)) error {
// TODO: 选路 (least-load / 模型亲和) → 调 vLLM/Ollama streaming API
return p.StreamText(ctx, buildReply(prompt), onToken)
}
// StreamText 按真实后端的 TTFT/逐 token 节奏把给定文本流式回调。
// 把"说什么"(由上层/Eino 图决定)与"怎么流"(后端节奏)解耦:
// 真实接入 vLLM/Ollama 后,由后端 streaming API 直接驱动,无需本方法。
func (p *Pool) StreamText(ctx context.Context, text string, onToken func([]byte)) error {
select {
case <-ctx.Done():
return ctx.Err()
case <-time.After(timeToFirstToken): // 模拟 TTFT
}
for _, tok := range tokenize(text) {
select {
case <-ctx.Done():
return ctx.Err()
default:
}
onToken([]byte(tok))
time.Sleep(interTokenDelay)
}
return nil
}
// buildReply 占位:真实实现应由 DSL 编排出的对话上下文驱动后端生成。
func buildReply(prompt string) string {
p := strings.TrimSpace(prompt)
if len(p) > 40 {
p = p[:40] + "…"
}
return "已编排执行该 Agent 图,输入摘要: " + p
}
// tokenize 占位分词:按 rune 切,保证多字节中文也能逐字流式。
func tokenize(s string) []string {
out := make([]string, 0, len(s))
for _, r := range s {
out = append(out, string(r))
}
return out
}