feat: 实现 LLM 文言文摘要生成,优化 Session 创建逻辑
Some checks failed
Release / build (push) Failing after 3h11m18s
Some checks failed
Release / build (push) Failing after 3h11m18s
This commit is contained in:
24
agents.md
24
agents.md
@@ -98,9 +98,10 @@
|
||||
- 实现向量生成和相似度检索
|
||||
|
||||
4. **会话管理**
|
||||
- 自动创建 Session(首次输入时)
|
||||
- 手动创建(/new 命令)
|
||||
- 消息摘要生成
|
||||
- 自动创建 Session(首次输入聊天消息时)
|
||||
- 手动创建(/new 命令,只是重置 currentSession)
|
||||
- LLM 生成摘要(文言文风格)
|
||||
- summary_timeout 配置(默认 30 秒)
|
||||
|
||||
5. **UI 优化**
|
||||
- 合并状态显示到单行
|
||||
@@ -112,7 +113,7 @@
|
||||
- 合并到 hxclaw 的会话摘要上下文
|
||||
- AI 同时看到长期记忆和会话摘要
|
||||
|
||||
5. **JSON 导出**
|
||||
7. **JSON 导出**
|
||||
- 退出时自动导出
|
||||
- 手动导出
|
||||
|
||||
@@ -147,6 +148,21 @@ tts:
|
||||
enabled: false # 全局开关(默认关闭)
|
||||
port: 9876 # mimo-tts daemon 端口
|
||||
auto: true # AI 回复后自动朗读
|
||||
|
||||
# 聊天记忆体配置
|
||||
memory:
|
||||
enabled: true # 启用开关
|
||||
auto_session: true # 自动创建 Session
|
||||
auto_export: true # 退出时自动导出
|
||||
summary_timeout: 30 # 摘要生成超时(秒)
|
||||
vector:
|
||||
api_key: "" # 硅基流动 API Key
|
||||
base_url: "https://api.siliconflow.cn/v1"
|
||||
model: "BAAI/bge-m3"
|
||||
recall:
|
||||
keywords: ["之前", "聊过", "记得"]
|
||||
auto_recall: true
|
||||
similarity_threshold: 0.7
|
||||
```
|
||||
|
||||
配置加载优先级:
|
||||
|
||||
20
changelog.md
20
changelog.md
@@ -4,6 +4,18 @@
|
||||
|
||||
### v0.3.0 (2026-04-27)
|
||||
|
||||
- **Session 创建逻辑优化**
|
||||
- Session 只在用户输入聊天消息时创建
|
||||
- /new 命令只重置 currentSession,不立即创建
|
||||
- 退出后重新进入算作新会话
|
||||
- 配置项:auto_session(默认 true)
|
||||
|
||||
- **LLM 生成摘要**
|
||||
- Chat 摘要:调用 LLM 生成极简文言文概述
|
||||
- Session 摘要:每次对话后用 LLM 精简整合
|
||||
- 配置项:summary_timeout(默认 30 秒)
|
||||
- 容错处理:超时失败回退到简单截断
|
||||
|
||||
- **双记忆系统合并**
|
||||
- 读取 picoclaw 的 MEMORY.md 作为长期记忆
|
||||
- 合并到 hxclaw 的会话摘要上下文
|
||||
@@ -105,6 +117,10 @@
|
||||
- [x] 4 个查询场景(RecallHistory, RecallTopic...)
|
||||
- [x] 三重检测机制
|
||||
- [x] MongoDB 风格导出
|
||||
- [x] Session 创建逻辑优化(输入消息时自动创建)
|
||||
- [x] /new 命令(只重置 currentSession)
|
||||
- [x] LLM 生成摘要(文言文风格)
|
||||
- [x] summary_timeout 配置
|
||||
|
||||
---
|
||||
|
||||
@@ -114,8 +130,6 @@
|
||||
|
||||
- [ ] 命令行参数支持(--theme, --tts 等)
|
||||
- [ ] 多语言支持
|
||||
- [ ] /new 命令开始新会话
|
||||
- [ ] /memory list|show 命令
|
||||
- [ ] 命令提示/补全功能(输入 / 显示内置命令列表)
|
||||
|
||||
---
|
||||
@@ -137,6 +151,8 @@
|
||||
- [x] 集成 libSQL 数据库
|
||||
- [x] 实现独立上下文系统
|
||||
- [x] UI 状态合并显示
|
||||
- [x] LLM 生成摘要(文言文风格)
|
||||
- [x] Session 自动创建逻辑
|
||||
|
||||
---
|
||||
|
||||
|
||||
@@ -68,6 +68,8 @@ type MemoryConfig struct {
|
||||
AutoSession bool `yaml:"auto_session"`
|
||||
// AutoExport 退出时自动导出
|
||||
AutoExport bool `yaml:"auto_export"`
|
||||
// SummaryTimeout 摘要生成超时时间(秒)
|
||||
SummaryTimeout int `yaml:"summary_timeout"`
|
||||
// Vector 向量服务配置
|
||||
Vector VectorConfig `yaml:"vector"`
|
||||
// Recall 检索配置
|
||||
@@ -121,10 +123,11 @@ var (
|
||||
Auto: true,
|
||||
},
|
||||
Memory: MemoryConfig{
|
||||
Enabled: true,
|
||||
DBPath: "",
|
||||
AutoSession: true,
|
||||
AutoExport: true,
|
||||
Enabled: true,
|
||||
DBPath: "",
|
||||
AutoSession: true,
|
||||
AutoExport: true,
|
||||
SummaryTimeout: 20,
|
||||
Vector: VectorConfig{
|
||||
APIKey: "",
|
||||
BaseURL: "https://api.siliconflow.cn/v1",
|
||||
@@ -222,6 +225,7 @@ memory:
|
||||
enabled: true
|
||||
auto_session: true
|
||||
auto_export: true
|
||||
summary_timeout: 30
|
||||
vector:
|
||||
api_key: ""
|
||||
base_url: "https://api.siliconflow.cn/v1"
|
||||
@@ -302,6 +306,9 @@ func mergeConfig(userCfg, projCfg *Config) *Config {
|
||||
if projCfg.Memory.AutoExport {
|
||||
result.Memory.AutoExport = projCfg.Memory.AutoExport
|
||||
}
|
||||
if projCfg.Memory.SummaryTimeout > 0 {
|
||||
result.Memory.SummaryTimeout = projCfg.Memory.SummaryTimeout
|
||||
}
|
||||
if projCfg.Memory.Vector.APIKey != "" {
|
||||
result.Memory.Vector.APIKey = projCfg.Memory.Vector.APIKey
|
||||
}
|
||||
|
||||
@@ -9,6 +9,7 @@ import (
|
||||
|
||||
"github.com/google/uuid"
|
||||
"github.com/hxclaw/hxclaw/cmd/hxclaw/internal"
|
||||
"github.com/sipeed/picoclaw/pkg/agent"
|
||||
|
||||
_ "github.com/tursodatabase/libsql-client-go/libsql"
|
||||
)
|
||||
@@ -18,8 +19,9 @@ type DB struct {
|
||||
}
|
||||
|
||||
var (
|
||||
db *DB
|
||||
cfg *DBConfig
|
||||
db *DB
|
||||
cfg *DBConfig
|
||||
summaryAgent *agent.AgentLoop
|
||||
)
|
||||
|
||||
type DBConfig struct {
|
||||
@@ -43,7 +45,9 @@ func GetDefaultDBPath() string {
|
||||
return getDefaultDBPath()
|
||||
}
|
||||
|
||||
func Init(opts ...DBSOption) error {
|
||||
func Init(agentLoop *agent.AgentLoop, opts ...DBSOption) error {
|
||||
summaryAgent = agentLoop
|
||||
|
||||
memoryCfg := internal.GetProjectConfig().Memory
|
||||
|
||||
cfg = &DBConfig{
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
package memory
|
||||
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
"os"
|
||||
"path/filepath"
|
||||
@@ -14,6 +15,56 @@ var lastContext string
|
||||
|
||||
var ErrNeedNewSession = fmt.Errorf("需要创建新会话")
|
||||
|
||||
func generateChatSummary(userInput, aiReply string) string {
|
||||
if summaryAgent == nil {
|
||||
return GenerateSummary(userInput, aiReply)
|
||||
}
|
||||
|
||||
memoryCfg := internal.GetProjectConfig().Memory
|
||||
timeout := time.Duration(memoryCfg.SummaryTimeout) * time.Second
|
||||
if timeout <= 0 {
|
||||
timeout = 30 * time.Second
|
||||
}
|
||||
|
||||
prompt := fmt.Sprintf(`用极简文言文概括对话,一句话,只包含最关键信息(如人名、地点、事件、核心结论):
|
||||
问:%s
|
||||
答:%s`, userInput, aiReply)
|
||||
|
||||
ctx, cancel := context.WithTimeout(context.Background(), timeout)
|
||||
defer cancel()
|
||||
|
||||
resp, err := summaryAgent.ProcessDirect(ctx, prompt, "summary:chat")
|
||||
if err != nil {
|
||||
return GenerateSummary(userInput, aiReply)
|
||||
}
|
||||
return strings.TrimSpace(resp)
|
||||
}
|
||||
|
||||
func generateSessionSummary(oldSummary, userInput, aiReply string) string {
|
||||
if summaryAgent == nil {
|
||||
return GenerateSummary(oldSummary, oldSummary+"\n"+aiReply)
|
||||
}
|
||||
|
||||
memoryCfg := internal.GetProjectConfig().Memory
|
||||
timeout := time.Duration(memoryCfg.SummaryTimeout) * time.Second
|
||||
if timeout <= 0 {
|
||||
timeout = 30 * time.Second
|
||||
}
|
||||
|
||||
prompt := fmt.Sprintf(`精简整合以下历史,提取关键信息,去除冗余描述,保留核心要点:
|
||||
历史:%s
|
||||
新对话:问%s 答%s`, oldSummary, userInput, aiReply)
|
||||
|
||||
ctx, cancel := context.WithTimeout(context.Background(), timeout)
|
||||
defer cancel()
|
||||
|
||||
resp, err := summaryAgent.ProcessDirect(ctx, prompt, "summary:session")
|
||||
if err != nil {
|
||||
return GenerateSummary(oldSummary, oldSummary+"\n"+aiReply)
|
||||
}
|
||||
return strings.TrimSpace(resp)
|
||||
}
|
||||
|
||||
func GetContextPrompt(userInput string) string {
|
||||
db := GetDB()
|
||||
if db == nil {
|
||||
@@ -227,8 +278,16 @@ func SaveChat(userInput, aiReply string, useSessionSummary bool) (int, error) {
|
||||
|
||||
// 获取或创建 Session
|
||||
session := currentSession
|
||||
if session == nil && memoryCfg.AutoSession {
|
||||
// 自动创建 session
|
||||
newSession, err := CreateNewSession()
|
||||
if err != nil {
|
||||
return 0, fmt.Errorf("自动创建会话失败: %v", err)
|
||||
}
|
||||
session = newSession
|
||||
}
|
||||
|
||||
if session == nil {
|
||||
// 如果没有 session,返回错误让用户创建
|
||||
return 0, ErrNeedNewSession
|
||||
}
|
||||
|
||||
@@ -243,7 +302,7 @@ func SaveChat(userInput, aiReply string, useSessionSummary bool) (int, error) {
|
||||
|
||||
// 添加 AI 回复
|
||||
chat.AddAIReply(aiReply)
|
||||
chat.Summary = GenerateSummary(userInput, aiReply)
|
||||
chat.Summary = generateChatSummary(userInput, aiReply)
|
||||
chat.UpdatedAt = time.Now().Unix()
|
||||
|
||||
// 生成并保存向量
|
||||
@@ -261,7 +320,7 @@ func SaveChat(userInput, aiReply string, useSessionSummary bool) (int, error) {
|
||||
|
||||
// 只有普通对话才更新 session summary,recall 查询保持原 summary
|
||||
if useSessionSummary {
|
||||
session.Summary = GenerateSummary("", session.Summary+"\n"+aiReply)
|
||||
session.Summary = generateSessionSummary(session.Summary, userInput, aiReply)
|
||||
}
|
||||
session.UpdatedAt = time.Now().Unix()
|
||||
|
||||
@@ -276,7 +335,11 @@ func SaveChat(userInput, aiReply string, useSessionSummary bool) (int, error) {
|
||||
|
||||
// 恢复原始 session summary(避免 recall 结果污染)
|
||||
session.Summary = originalSummary
|
||||
currentSession = session
|
||||
|
||||
// 首次自动创建后赋值 currentSession
|
||||
if currentSession == nil {
|
||||
currentSession = session
|
||||
}
|
||||
|
||||
return len(session.ChatIDs), nil
|
||||
}
|
||||
|
||||
@@ -72,7 +72,7 @@ func main() {
|
||||
dbPath = memory.GetDefaultDBPath()
|
||||
}
|
||||
fmt.Printf("初始化记忆体,db_path: %s\n", dbPath)
|
||||
if err := memory.Init(memory.WithDBPath(dbPath)); err != nil {
|
||||
if err := memory.Init(agentLoop, memory.WithDBPath(dbPath)); err != nil {
|
||||
fmt.Fprintf(os.Stderr, "警告:初始化记忆体失败: %v,将使用无记忆模式\n", err)
|
||||
} else {
|
||||
fmt.Println("记忆体初始化成功")
|
||||
@@ -435,13 +435,8 @@ func handleTTSCommandSimple(input string) {
|
||||
}
|
||||
|
||||
func handleNewSessionCommand(rl *internal.Readline, basePrompt string) {
|
||||
uuid, err := memory.CreateNewSession()
|
||||
if err != nil {
|
||||
fmt.Printf("创建新会话失败: %v\n", err)
|
||||
return
|
||||
}
|
||||
fmt.Printf("已创建新会话: %s\n", uuid)
|
||||
currentSession = nil
|
||||
fmt.Println("已重置会话,输入聊天消息后将创建新会话")
|
||||
}
|
||||
|
||||
func handleMemoryCommand(input string) {
|
||||
|
||||
@@ -26,6 +26,7 @@ memory:
|
||||
enabled: false
|
||||
auto_session: true
|
||||
auto_export: true
|
||||
summary_timeout: 30
|
||||
vector:
|
||||
base_url: "https://api.siliconflow.cn/v1"
|
||||
model: "BAAI/bge-m3"
|
||||
|
||||
Reference in New Issue
Block a user