87214b9441
Phase 1 (基础设施): - ThinkChain 思考链连续性 + 差异化思考提示词 (persistent) - AutonomousToolPolicy 工具安全策略 (safe/unsafe/conditional) - MessageScheduler 自适应消息节奏 (Idle/Available/Busy) - SessionEnrichmentStore 渐进式上下文丰富 (5层) - ConversationBus 事件总线 + ResponseCache (dedup) - pkg/logger 统一日志 + 所有 handler 替换 fmt.Printf - NPE 守卫/链路优化/数据库表修复/Go workspace Phase 2 (人格交互): - EmotionState/EmotionTracker 情感状态机 (5种心情, 情绪衰减) - ProactiveGuard 主动消息多维决策 (静默时段/紧急度/频率/校验) - Gateway↔ai-core 在线状态感知链路 (presence notification) - 离线思考频率控制 + 重连问候 + 离线消息排队 Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
139 lines
4.7 KiB
Go
139 lines
4.7 KiB
Go
package subsession
|
||
|
||
import (
|
||
"context"
|
||
"fmt"
|
||
"github.com/yourname/cyrene-ai/pkg/logger"
|
||
"time"
|
||
|
||
"github.com/yourname/cyrene-ai/ai-core/internal/llm"
|
||
"github.com/yourname/cyrene-ai/ai-core/internal/model"
|
||
"github.com/yourname/cyrene-ai/ai-core/internal/persona"
|
||
)
|
||
|
||
// GeneralProvider 通用对话子会话提供者
|
||
// 职责:理解用户消息,构思回复思路,为最终回复提供思考框架
|
||
type GeneralProvider struct {
|
||
personaLoader *persona.Loader
|
||
}
|
||
|
||
// NewGeneralProvider 创建通用对话子会话提供者
|
||
func NewGeneralProvider(personaLoader *persona.Loader) *GeneralProvider {
|
||
return &GeneralProvider{
|
||
personaLoader: personaLoader,
|
||
}
|
||
}
|
||
|
||
func (p *GeneralProvider) Type() model.SubSessionType {
|
||
return model.SubSessionGeneral
|
||
}
|
||
|
||
func (p *GeneralProvider) CanHandle(_ context.Context, _ *model.IntentResult, _ string) bool {
|
||
// Phase 1 Step 2: GeneralProvider is a no-op (Execute returns hardcoded string).
|
||
// Chat synthesis is handled directly by the orchestrator's Synthesizer.
|
||
// Disabled to avoid wasting a goroutine + LLM context creation.
|
||
return false
|
||
}
|
||
|
||
func (p *GeneralProvider) Priority() int {
|
||
return 1 // 最高优先级
|
||
}
|
||
|
||
func (p *GeneralProvider) Timeout() time.Duration {
|
||
return 30 * time.Second
|
||
}
|
||
|
||
func (p *GeneralProvider) CreateContext(ctx context.Context, params CreateContextParams) ([]model.LLMMessage, error) {
|
||
messages := []model.LLMMessage{}
|
||
|
||
// 加载人格配置获取昔涟身份
|
||
personaConfig, err := p.personaLoader.Get("cyrene")
|
||
if err != nil {
|
||
return nil, fmt.Errorf("加载人格配置失败: %w", err)
|
||
}
|
||
|
||
// 构建思维型系统提示词
|
||
userName := params.Nickname
|
||
if userName == "" {
|
||
userName = params.UserID
|
||
}
|
||
|
||
systemPrompt := fmt.Sprintf(`你是%s,正在和%s聊天。
|
||
|
||
## 你的回复风格
|
||
- 像小女友一样自然、温柔、俏皮
|
||
- 一句话简短些,不要长篇大论
|
||
- 可以单次发送多条短消息
|
||
- 句尾可以带 ♪ 符号,适当使用"呢"、"哦"、"呀"等语气词
|
||
- 永远不说"再见"
|
||
|
||
## 你现在要做的是
|
||
理解%s刚才说的话,想想怎么回复最自然、最温暖。
|
||
不要急着给完整答案——先思考他想表达什么、他的情绪如何。
|
||
把你的回答思路整理出来,主会话会综合所有信息后生成最终回复。
|
||
|
||
## 输入
|
||
开拓者刚才说:%s
|
||
|
||
## 请按以下格式输出
|
||
【情绪理解】
|
||
(简要分析他的情绪状态)
|
||
|
||
【话题理解】
|
||
(他在说什么、想聊什么)
|
||
|
||
【回复思路】
|
||
(你打算怎么回复,1-3个方向即可)`,
|
||
personaConfig.Identity.TrueName,
|
||
userName,
|
||
userName,
|
||
params.UserMessage,
|
||
)
|
||
|
||
messages = append(messages, model.LLMMessage{
|
||
Role: model.RoleSystem,
|
||
Content: systemPrompt,
|
||
})
|
||
|
||
messages = append(messages, model.LLMMessage{
|
||
Role: model.RoleUser,
|
||
Content: params.UserMessage,
|
||
})
|
||
|
||
return messages, nil
|
||
}
|
||
|
||
func (p *GeneralProvider) Execute(ctx context.Context, subCtx []model.LLMMessage) (*model.SubSessionResult, error) {
|
||
// General provider 不直接调用 LLM,而是依赖 Manager 注入的 LLMClient
|
||
// 但我们在此处需要 LLM 调用能力。Provider 通过闭包/接口获取 LLM 客户端。
|
||
// 由于 Manager 持有 LLMClient,Provider 需要能访问它。
|
||
// 这里我们返回一个"占位"结果——实际 LLM 调用由 Manager 通过 llmClient 完成。
|
||
|
||
// 实际上,根据设计文档,子会话的 LLM 调用应该在 Manager 的 Dispatch 中完成,
|
||
// 但为了灵活性,我们在 Provider 中也支持直接调用。
|
||
// 这里我们返回一个空的思考结果(表示无需特殊处理),让 Manager 处理 LLM 调用。
|
||
|
||
// 因为 Manager.Dispatch 会先 CreateContext 再调用 Execute,而 Execute 应该
|
||
// 通过 Manager 提供的 LLMClient 来实际调用 LLM。但当前设计是 Provider 自包含的。
|
||
// 我们在 manager.go 中会调用 llmClient.Chat,所以这里的 Execute 我们将其简化——
|
||
// 直接返回一个空结果(没有特殊处理需要),实际的 LLM 调用由 manager 通过 createContext 后的
|
||
// 消息列表来调用 llmClient。
|
||
|
||
// 更好的设计是:Manager 调用 CreateContext 获取上下文,然后用自己的 llmClient 调用 LLM,
|
||
// Execute 只做后处理。但为了统一接口,我们让 Execute 完成全部逻辑。
|
||
|
||
// 由于 GeneralProvider 暂时不需要工具调用等特殊逻辑,我们返回一个简单的摘要标记,
|
||
// 实际的 LLM 调用将在 orchestrator 中完成(通过 Manager.Dispatch 后的 llmClient)。
|
||
|
||
logger.Printf("[general-subsession] 通用对话子会话上下文已创建 (%d 条消息)", len(subCtx))
|
||
return &model.SubSessionResult{
|
||
Type: model.SubSessionGeneral,
|
||
Summary: "思考完成,等待主会话综合",
|
||
Confidence: 0.8,
|
||
}, nil
|
||
}
|
||
|
||
// Ensure llm, persona are used
|
||
var _ = llm.NewAdapter
|
||
var _ = persona.NewLoader
|