feat(chat): 封装L1会话管理器打通自然语言多轮人机交互闭环
Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
@@ -0,0 +1,106 @@
|
||||
package chat
|
||||
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
"time"
|
||||
|
||||
"chat-agent-system/internal/coordinator"
|
||||
"chat-agent-system/internal/intent"
|
||||
"chat-agent-system/internal/squad"
|
||||
)
|
||||
|
||||
// Message 历史消息结构
|
||||
type Message struct {
|
||||
ID string `json:"id"`
|
||||
Role string `json:"role"` // "user" | "assistant"
|
||||
Content string `json:"content"`
|
||||
Intent intent.IntentType `json:"intent,omitempty"`
|
||||
AdvisorBrief *squad.AdvisorBriefing `json:"advisor_brief,omitempty"`
|
||||
Timestamp time.Time `json:"timestamp"`
|
||||
}
|
||||
|
||||
// ChatResponse 会话单轮响应
|
||||
type ChatResponse struct {
|
||||
ReplyText string `json:"reply_text"`
|
||||
IntentResult *intent.IntentRecognitionResult `json:"intent_result"`
|
||||
CoordinationResult *coordinator.CoordinationResult `json:"coordination_result"`
|
||||
AdvisorBrief *squad.AdvisorBriefing `json:"advisor_brief,omitempty"`
|
||||
}
|
||||
|
||||
// Session 多轮对话与协同会话管理器
|
||||
type Session struct {
|
||||
ID string `json:"id"`
|
||||
UserCtx *coordinator.UserProjectContext `json:"user_ctx"`
|
||||
Recognizer intent.Recognizer `json:"-"`
|
||||
Coordinator *coordinator.CoordinatorEngine `json:"-"`
|
||||
Messages []Message `json:"messages"`
|
||||
CreatedAt time.Time `json:"created_at"`
|
||||
}
|
||||
|
||||
// NewSession 创建新对话会话
|
||||
func NewSession(id string, userCtx *coordinator.UserProjectContext, rec intent.Recognizer, coord *coordinator.CoordinatorEngine) *Session {
|
||||
if rec == nil {
|
||||
rec = intent.NewRecognizer()
|
||||
}
|
||||
if id == "" {
|
||||
id = fmt.Sprintf("sess_%d", time.Now().Unix())
|
||||
}
|
||||
return &Session{
|
||||
ID: id,
|
||||
UserCtx: userCtx,
|
||||
Recognizer: rec,
|
||||
Coordinator: coord,
|
||||
Messages: make([]Message, 0),
|
||||
CreatedAt: time.Now(),
|
||||
}
|
||||
}
|
||||
|
||||
// SendMessage 处理一轮用户输入
|
||||
func (s *Session) SendMessage(ctx context.Context, userInput string) (*ChatResponse, error) {
|
||||
userMsg := Message{
|
||||
ID: fmt.Sprintf("msg_%d_u", time.Now().UnixNano()),
|
||||
Role: "user",
|
||||
Content: userInput,
|
||||
Timestamp: time.Now(),
|
||||
}
|
||||
s.Messages = append(s.Messages, userMsg)
|
||||
|
||||
// 1. 意图识别 (L1 对话层)
|
||||
var snap *intent.ContextSnapshot
|
||||
if s.UserCtx != nil {
|
||||
snap = s.UserCtx.ToSnapshot()
|
||||
}
|
||||
recRes, err := s.Recognizer.Recognize(ctx, userInput, snap)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("intent recognition failed: %w", err)
|
||||
}
|
||||
|
||||
// 2. 协调调度与校验 (L2 协调层)
|
||||
coordRes, err := s.Coordinator.ProcessIntent(ctx, recRes, s.UserCtx)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("coordination execution failed: %w", err)
|
||||
}
|
||||
|
||||
// 3. 更新上下文与历史
|
||||
if coordRes.UpdatedContext != nil {
|
||||
s.UserCtx = coordRes.UpdatedContext
|
||||
}
|
||||
|
||||
asstMsg := Message{
|
||||
ID: fmt.Sprintf("msg_%d_a", time.Now().UnixNano()),
|
||||
Role: "assistant",
|
||||
Content: coordRes.NaturalReply,
|
||||
Intent: coordRes.Intent,
|
||||
AdvisorBrief: coordRes.AdvisorBrief,
|
||||
Timestamp: time.Now(),
|
||||
}
|
||||
s.Messages = append(s.Messages, asstMsg)
|
||||
|
||||
return &ChatResponse{
|
||||
ReplyText: coordRes.NaturalReply,
|
||||
IntentResult: recRes,
|
||||
CoordinationResult: coordRes,
|
||||
AdvisorBrief: coordRes.AdvisorBrief,
|
||||
}, nil
|
||||
}
|
||||
@@ -0,0 +1,86 @@
|
||||
package chat_test
|
||||
|
||||
import (
|
||||
"context"
|
||||
"strings"
|
||||
"testing"
|
||||
|
||||
"chat-agent-system/internal/chat"
|
||||
"chat-agent-system/internal/coordinator"
|
||||
"chat-agent-system/internal/intent"
|
||||
"chat-agent-system/internal/squad"
|
||||
)
|
||||
|
||||
func TestChatSession_EndToEndDialog(t *testing.T) {
|
||||
rec := intent.NewRecognizer()
|
||||
engine := coordinator.NewCoordinatorEngine(nil)
|
||||
|
||||
userCtx := &coordinator.UserProjectContext{
|
||||
SnapshotID: "sess_ctx_1",
|
||||
User: coordinator.UserInfo{
|
||||
ID: "u1",
|
||||
Name: "张工",
|
||||
Role: "engineer",
|
||||
ProjectRole: "composer",
|
||||
},
|
||||
Project: coordinator.ProjectInfo{
|
||||
Name: "掌上无线超声系统",
|
||||
},
|
||||
LatestAdvisorBrief: &squad.AdvisorBriefing{
|
||||
MaturityScore: 75,
|
||||
MaturityLevel: "V0.1",
|
||||
MaturityHeadline: "初步框架已就绪",
|
||||
PreemptiveRisks: []string{"探头功耗与散热设计"},
|
||||
NextBestActions: []string{"补充客户供电参数", "推进到V0.5可行性论证"},
|
||||
CommunicationKit: "建议与客户确认供电方式",
|
||||
},
|
||||
}
|
||||
|
||||
session := chat.NewSession("sess_001", userCtx, rec, engine)
|
||||
|
||||
// Turn 1: 问候
|
||||
resp1, err := session.SendMessage(context.Background(), "你好,请问你是谁?")
|
||||
if err != nil {
|
||||
t.Fatalf("turn 1 failed: %v", err)
|
||||
}
|
||||
if resp1.IntentResult.TopIntent.Intent != intent.IntentCasualChat {
|
||||
t.Errorf("turn 1 expected CASUAL_CHAT, got %s", resp1.IntentResult.TopIntent.Intent)
|
||||
}
|
||||
|
||||
// Turn 2: 查进展与风险
|
||||
resp2, err := session.SendMessage(context.Background(), "现在这个项目进展怎么样?有啥风险没?")
|
||||
if err != nil {
|
||||
t.Fatalf("turn 2 failed: %v", err)
|
||||
}
|
||||
if resp2.IntentResult.TopIntent.Intent != intent.IntentQueryProgress {
|
||||
t.Errorf("turn 2 expected QUERY_PROGRESS, got %s", resp2.IntentResult.TopIntent.Intent)
|
||||
}
|
||||
if !strings.Contains(resp2.ReplyText, "掌上无线超声系统") {
|
||||
t.Errorf("turn 2 reply should mention project name: %s", resp2.ReplyText)
|
||||
}
|
||||
|
||||
// Turn 3: 补充信息
|
||||
resp3, err := session.SendMessage(context.Background(), "客户补充说目标市场是国内及欧洲CE")
|
||||
if err != nil {
|
||||
t.Fatalf("turn 3 failed: %v", err)
|
||||
}
|
||||
if resp3.IntentResult.TopIntent.Intent != intent.IntentSupplementInfo {
|
||||
t.Errorf("turn 3 expected SUPPLEMENT_INFO, got %s", resp3.IntentResult.TopIntent.Intent)
|
||||
}
|
||||
if session.UserCtx.BlackboardFacts["target_market"] != "国内及欧洲CE" {
|
||||
t.Errorf("target_market not updated: %v", session.UserCtx.BlackboardFacts)
|
||||
}
|
||||
|
||||
// Turn 4: 推进阶段
|
||||
resp4, err := session.SendMessage(context.Background(), "好的,进入方案论证阶段吧")
|
||||
if err != nil {
|
||||
t.Fatalf("turn 4 failed: %v", err)
|
||||
}
|
||||
if resp4.IntentResult.TopIntent.Intent != intent.IntentAdvanceStage {
|
||||
t.Errorf("turn 4 expected ADVANCE_STAGE, got %s", resp4.IntentResult.TopIntent.Intent)
|
||||
}
|
||||
|
||||
if len(session.Messages) != 8 {
|
||||
t.Errorf("expected 8 messages (4 user + 4 asst), got %d", len(session.Messages))
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user