Files
smartmate/backend/agent/graph.go
LoveLosita f9d52e0c5e Version: 0.4.1.dev.260304
feat: 💬 新增对话创建与上下文记忆机制

* 新增对话的创建与使用功能,实现会话级上下文隔离
* 实现上下文保存与传递机制,使模型具备持续对话记忆能力
* 引入滑动窗口策略控制上下文规模
* 当前窗口大小限制为 20 条消息,超过后自动丢弃最早消息以控制上下文长度

docs: 📝 更新示例配置文件

* 更新示例配置文件,新增 `agent` 相关配置信息
* 明确 Agent 模块运行所需参数,方便本地部署与环境初始化

undo: ⚠️ Agent 上下文读取性能待优化

* 当前测试中模型响应速度偏慢
* 计划后续将上下文暂存至缓存层,以减少读取与拼接开销并提升响应速度
2026-03-04 19:56:08 +08:00

87 lines
2.1 KiB
Go

package agent
import (
"context"
"encoding/json"
"io"
"strings"
"github.com/cloudwego/eino-ext/components/model/ark"
"github.com/cloudwego/eino/schema"
)
// StreamResponse 专为 Apifox/前端 识别设计的极简结构
type StreamResponse struct {
Choices []struct {
Delta struct {
Content string `json:"content"`
} `json:"delta"`
} `json:"choices"`
}
// ToStreamResponseDTO 将 Eino 的内部 Chunk 转换为 StreamResponse DTO
func ToStreamResponseDTO(chunk *schema.Message) StreamResponse {
var dto StreamResponse
dto.Choices = append(dto.Choices, struct {
Delta struct {
Content string `json:"content"`
} `json:"delta"`
}{})
dto.Choices[0].Delta.Content = chunk.Content
return dto
}
// ToOpenAIStream 负责将 Eino 的内部 Chunk 转换为 OpenAI 兼容的 data: {JSON} 字符串
func ToOpenAIStream(chunk *schema.Message) (string, error) {
dto := ToStreamResponseDTO(chunk)
jsonBytes, err := json.Marshal(dto)
if err != nil {
return "", err
}
// 严格遵循 SSE 协议格式
return string(jsonBytes), nil
}
func StreamChat(ctx context.Context, llm *ark.ChatModel, userInput string, chatHistory []*schema.Message, outChan chan<- string) (string, error) {
// 1. 组装消息
messages := make([]*schema.Message, 0)
// A. 塞入 System Message (人设)
messages = append(messages, schema.SystemMessage(SystemPrompt))
// B. 塞入历史记录 (上下文)
if len(chatHistory) > 0 {
messages = append(messages, chatHistory...)
}
// C. 塞入用户当前的消息 (当前需求)
messages = append(messages, schema.UserMessage(userInput))
// 2. 调用流式接口
reader, err := llm.Stream(ctx, messages)
if err != nil {
return "", err
}
defer reader.Close() // 记得关闭 Reader
// 3. 循环读取直到结束
var fullText strings.Builder
for {
chunk, err := reader.Recv()
if err == io.EOF {
break // 读取完成
}
if err != nil {
return "", err
}
if chunk.Content == "" {
continue
}
fullText.WriteString(chunk.Content)
// 将内容发送到通道中供前端消费
retChuck, err := ToOpenAIStream(chunk)
if err != nil {
return "", err
}
outChan <- retChuck
}
return fullText.String(), nil
}