User Preference Learning 提供智能偏好学习系统,从用户交互中自动提取、管理和应用用户偏好,实现个性化的 Agent 体验。
package main
import (
"context"
"github.com/astercloud/aster/pkg/memory"
)
func main() {
// 创建偏好管理器
config := memory.DefaultPreferenceManagerConfig()
manager := memory.NewPreferenceManager(config)
// 添加偏好
pref := &memory.Preference{
UserID: "user-123",
Category: memory.CategoryUI,
Key: "theme",
Value: "dark",
Strength: 0.8, // 强度
Confidence: 0.9, // 置信度
}
err := manager.AddPreference(context.Background(), pref)
if err != nil {
panic(err)
}
}
// 创建偏好提取器
extractor := memory.NewPreferenceExtractor(manager)
// 从用户消息中提取偏好
message := agentext.Message{
Role: "user",
Content: "I prefer dark mode and Chinese language",
}
prefs, err := extractor.ExtractFromMessage(ctx, "user-123", message)
if err != nil {
panic(err)
}
fmt.Printf("提取到 %d 个偏好\n", len(prefs))
for _, pref := range prefs {
fmt.Printf("- %s: %s = %s (强度: %.2f)\n",
pref.Category, pref.Key, pref.Value, pref.Strength)
}
// 创建文件存储
storage, err := memory.NewFilePreferenceStorage("/data/preferences")
if err != nil {
panic(err)
}
// 创建持久化管理器
persistentManager := memory.NewPersistentPreferenceManager(
config,
storage,
)
// 添加偏好会自动保存
pref := &memory.Preference{
UserID: "user-123",
Category: memory.CategoryLanguage,
Key: "language",
Value: "zh",
Strength: 0.9,
Confidence: 1.0,
}
persistentManager.AddPreference(ctx, pref)
// 手动保存用户的所有偏好
persistentManager.SaveUser(ctx, "user-123")
// 从存储加载
persistentManager.LoadUser(ctx, "user-123")
pref := &memory.Preference{
UserID: "user-123",
Category: memory.CategoryUI,
Key: "theme",
Value: "dark",
Strength: 0.8,
Confidence: 0.9,
}
// 常见的 UI 偏好
// - theme: dark/light
// - font: monospace/sans-serif
// - layout: compact/spacious
// - colorScheme: default/high-contrast
pref := &memory.Preference{
UserID: "user-123",
Category: memory.CategoryLanguage,
Key: "language",
Value: "zh",
Strength: 0.9,
Confidence: 1.0,
}
// 常见的语言偏好
// - language: zh/en/ja/...
// - responseLanguage: zh/en
pref := &memory.Preference{
UserID: "user-123",
Category: memory.CategoryFormat,
Key: "codeIndent",
Value: "spaces",
Strength: 0.7,
Confidence: 0.8,
}
// 常见的格式偏好
// - codeIndent: spaces/tabs
// - dateFormat: ISO8601/US/EU
// - numberFormat: 1,000.00/1.000,00
pref := &memory.Preference{
UserID: "user-123",
Category: memory.CategoryWorkflow,
Key: "autoSave",
Value: "true",
Strength: 0.6,
Confidence: 0.7,
}
// 常见的工作流偏好
// - autoSave: true/false
// - confirmBeforeAction: true/false
// - verbosity: minimal/normal/detailed
pref := &memory.Preference{
UserID: "user-123",
Category: memory.CategoryUI,
Key: "theme",
Value: "dark",
Strength: 0.8,
Confidence: 0.9,
Metadata: map[string]string{
"source": "user_message",
"date": "2024-01-15",
},
}
err := manager.AddPreference(ctx, pref)
pref, err := manager.GetPreference(
ctx,
"user-123",
memory.CategoryUI,
"theme",
)
if err != nil {
log.Fatal(err)
}
fmt.Printf("用户主题偏好: %s (强度: %.2f)\n", pref.Value, pref.Strength)
// 列出所有偏好
allPrefs, err := manager.ListPreferences(ctx, "user-123", "")
// 列出特定类别的偏好
uiPrefs, err := manager.ListPreferences(
ctx,
"user-123",
memory.CategoryUI,
)
for _, pref := range uiPrefs {
fmt.Printf("%s = %s\n", pref.Key, pref.Value)
}
err := manager.UpdatePreference(
ctx,
"user-123",
memory.CategoryUI,
"theme",
"light", // 新值
)
err := manager.DeletePreference(
ctx,
"user-123",
memory.CategoryUI,
"theme",
)
// 获取强度最高的 10 个偏好
topPrefs, err := manager.GetTopPreferences(ctx, "user-123", 10)
for i, pref := range topPrefs {
fmt.Printf("%d. %s:%s = %s (强度: %.2f)\n",
i+1, pref.Category, pref.Key, pref.Value, pref.Strength)
}
config := memory.DefaultPreferenceManagerConfig()
config.ConflictStrategy = memory.ConflictKeepLatest
// 添加第一个偏好
pref1 := &memory.Preference{
UserID: "user-123",
Category: memory.CategoryUI,
Key: "theme",
Value: "dark",
}
manager.AddPreference(ctx, pref1)
// 添加冲突的偏好(相同 category + key)
pref2 := &memory.Preference{
UserID: "user-123",
Category: memory.CategoryUI,
Key: "theme",
Value: "light", // 不同的值
}
manager.AddPreference(ctx, pref2)
// 结果:保留 "light"(最新的值)
config.ConflictStrategy = memory.ConflictKeepStronger
// 强度 0.9 的偏好
pref1 := &memory.Preference{
UserID: "user-123",
Category: memory.CategoryUI,
Key: "theme",
Value: "dark",
Strength: 0.9,
}
manager.AddPreference(ctx, pref1)
// 强度 0.6 的冲突偏好
pref2 := &memory.Preference{
UserID: "user-123",
Category: memory.CategoryUI,
Key: "theme",
Value: "light",
Strength: 0.6,
}
manager.AddPreference(ctx, pref2)
// 结果:保留 "dark"(强度更高)
config.ConflictStrategy = memory.ConflictMerge
// 第一个偏好
pref1 := &memory.Preference{
UserID: "user-123",
Category: memory.CategoryUI,
Key: "theme",
Value: "dark",
Strength: 0.6,
}
manager.AddPreference(ctx, pref1)
// 第二个偏好
pref2 := &memory.Preference{
UserID: "user-123",
Category: memory.CategoryUI,
Key: "theme",
Value: "dark",
Strength: 0.8,
}
manager.AddPreference(ctx, pref2)
// 结果:强度合并为 (0.6 + 0.8) / 2 = 0.7
config := memory.DefaultPreferenceManagerConfig()
config.StrengthDecay = 0.01 // 每天衰减 1%
config.MinStrength = 0.1 // 最小强度阈值
manager := memory.NewPreferenceManager(config)
// 添加偏好
pref := &memory.Preference{
UserID: "user-123",
Category: memory.CategoryUI,
Key: "theme",
Value: "dark",
Strength: 0.5,
}
manager.AddPreference(ctx, pref)
// 一段时间后应用衰减
removed := manager.ApplyDecay(ctx)
fmt.Printf("清理了 %d 个弱偏好\n", removed)
// 如果偏好 10 天未更新:
// 强度 = 0.5 * (1 - 0.01 * 10) = 0.5 * 0.9 = 0.45
// 如果低于 MinStrength (0.1),会被删除
stats := manager.GetStats("user-123")
fmt.Printf("总偏好数: %d\n", stats.TotalPreferences)
fmt.Printf("平均强度: %.2f\n", stats.AverageStrength)
fmt.Printf("高强度偏好: %d\n", stats.HighStrengthCount) // >= 0.7
fmt.Printf("中等强度偏好: %d\n", stats.MediumStrengthCount) // >= 0.4
fmt.Printf("低强度偏好: %d\n", stats.LowStrengthCount) // < 0.4
// 类别分布
for category, count := range stats.CategoryDistribution {
fmt.Printf("%s: %d\n", category, count)
}
// 创建文件存储
storage, err := memory.NewFilePreferenceStorage("/data/preferences")
if err != nil {
log.Fatal(err)
}
// 创建持久化管理器
manager := memory.NewPersistentPreferenceManager(
memory.DefaultPreferenceManagerConfig(),
storage,
)
// 添加偏好
pref := &memory.Preference{
UserID: "user-123",
Category: memory.CategoryUI,
Key: "theme",
Value: "dark",
}
manager.AddPreference(ctx, pref)
// 保存到磁盘
manager.SaveUser(ctx, "user-123")
// 加载单个用户
err := manager.LoadUser(ctx, "user-123")
if err != nil {
log.Fatal(err)
}
// 加载所有用户
err = manager.LoadAll(ctx)
if err != nil {
log.Fatal(err)
}
// 启动自动保存(每 60 秒)
ctx, cancel := context.WithCancel(context.Background())
defer cancel()
go manager.AutoSave(ctx, 60)
// 程序运行期间会自动保存
// 取消 context 时会执行最后一次保存
type CustomExtractor struct {
*memory.PreferenceExtractor
}
func (e *CustomExtractor) ExtractFromMessage(
ctx context.Context,
userID string,
message agentext.Message,
) ([]*memory.Preference, error) {
prefs := []*memory.Preference{}
content := strings.ToLower(message.Content)
// 自定义提取逻辑
if strings.Contains(content, "verbose") {
prefs = append(prefs, &memory.Preference{
UserID: userID,
Category: memory.CategoryWorkflow,
Key: "verbosity",
Value: "detailed",
Strength: 0.7,
Confidence: 0.8,
})
}
// 添加到管理器
for _, pref := range prefs {
e.manager.AddPreference(ctx, pref)
}
return prefs, nil
}
// 根据偏好调整 Agent 行为
func adjustBehavior(manager *memory.PreferenceManager, userID string) {
// 获取语言偏好
langPref, err := manager.GetPreference(
ctx,
userID,
memory.CategoryLanguage,
"language",
)
if err == nil && langPref.Value == "zh" {
// 使用中文响应
setResponseLanguage("zh")
}
// 获取详细程度偏好
verbPref, err := manager.GetPreference(
ctx,
userID,
memory.CategoryWorkflow,
"verbosity",
)
if err == nil && verbPref.Value == "minimal" {
// 使用简洁的响应
setResponseStyle("minimal")
}
}
// 基于历史偏好推荐新设置
func recommendPreferences(
manager *memory.PreferenceManager,
userID string,
) []string {
recommendations := []string{}
// 获取 TOP 偏好
topPrefs, _ := manager.GetTopPreferences(ctx, userID, 10)
// 分析模式
hasDarkTheme := false
for _, pref := range topPrefs {
if pref.Key == "theme" && pref.Value == "dark" {
hasDarkTheme = true
}
}
// 推荐相关设置
if hasDarkTheme {
recommendations = append(recommendations, "建议:启用高对比度配色方案")
}
return recommendations
}
// 导出用户偏好
func exportPreferences(
manager *memory.PersistentPreferenceManager,
userID string,
) ([]byte, error) {
prefs, _ := manager.ListPreferences(ctx, userID, "")
return json.MarshalIndent(prefs, "", " ")
}
// 导入用户偏好
func importPreferences(
manager *memory.PersistentPreferenceManager,
userID string,
data []byte,
) error {
var prefs []*memory.Preference
if err := json.Unmarshal(data, &prefs); err != nil {
return err
}
for _, pref := range prefs {
pref.UserID = userID // 确保用户 ID 正确
manager.AddPreference(ctx, pref)
}
return manager.SaveUser(ctx, userID)
}
config := memory.DefaultPreferenceManagerConfig()
// ✅ 根据业务调整阈值
config.MinStrength = 0.2 // 低频偏好也保留
config.StrengthDecay = 0.005 // 慢衰减(每天 0.5%)
// ❌ 避免过于激进
// config.MinStrength = 0.8 // 太高,会丢失有用偏好
// config.StrengthDecay = 0.1 // 太快,偏好很快消失
// 定期应用衰减清理弱偏好
ticker := time.NewTicker(24 * time.Hour)
go func() {
for range ticker.C {
removed := manager.ApplyDecay(ctx)
log.Printf("清理了 %d 个弱偏好", removed)
}
}()
// ✅ UI 偏好:保留最新
uiConfig := memory.DefaultPreferenceManagerConfig()
uiConfig.ConflictStrategy = memory.ConflictKeepLatest
// ✅ 工作流偏好:合并增强
workflowConfig := memory.DefaultPreferenceManagerConfig()
workflowConfig.ConflictStrategy = memory.ConflictMerge
// ✅ 关键设置:保留强度高的
criticalConfig := memory.DefaultPreferenceManagerConfig()
criticalConfig.ConflictStrategy = memory.ConflictKeepStronger
func validatePreference(pref *memory.Preference) error {
switch pref.Category {
case memory.CategoryUI:
if pref.Key == "theme" {
validValues := []string{"dark", "light", "auto"}
if !contains(validValues, pref.Value) {
return fmt.Errorf("invalid theme value: %s", pref.Value)
}
}
case memory.CategoryLanguage:
if pref.Key == "language" {
validLangs := []string{"zh", "en", "ja", "es", "fr"}
if !contains(validLangs, pref.Value) {
return fmt.Errorf("invalid language: %s", pref.Value)
}
}
}
return nil
}
// 学习用户的交互习惯
extractor := memory.NewPreferenceExtractor(manager)
// 从每条消息中学习
messages := []agentext.Message{
{Role: "user", Content: "Use dark mode please"},
{Role: "user", Content: "I prefer Chinese responses"},
{Role: "user", Content: "Keep it brief"},
}
for _, msg := range messages {
extractor.ExtractFromMessage(ctx, "user-123", msg)
}
// 应用学到的偏好
adjustBehavior(manager, "user-123")
// 在设备 A 上学习偏好
manager.AddPreference(ctx, &memory.Preference{
UserID: "user-123",
Category: memory.CategoryUI,
Key: "theme",
Value: "dark",
})
// 保存到云端
manager.SaveUser(ctx, "user-123")
// 在设备 B 上加载
newManager := memory.NewPersistentPreferenceManager(config, cloudStorage)
newManager.LoadUser(ctx, "user-123")
// 偏好自动同步
// 记录用户对不同功能的偏好
func recordFeaturePreference(userID, feature string, liked bool) {
value := "disliked"
strength := 0.3
if liked {
value = "liked"
strength = 0.7
}
manager.AddPreference(ctx, &memory.Preference{
UserID: userID,
Category: memory.CategoryWorkflow,
Key: feature,
Value: value,
Strength: strength,
})
}
// 分析偏好趋势
stats := manager.GetStats(userID)
运行偏好学习测试:
# 运行所有偏好测试
go test ./pkg/memory/... -run TestPreference -v
# 运行存储测试
go test ./pkg/memory/... -run TestPersistent -v