feat(coordinator): 在贴身业务参谋决策锦囊中注入相似标杆案例自推荐
Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
committed by
weichen
co-authored by
Cursor
parent
fb8232639b
commit
d0c9670ebc
@@ -36,6 +36,8 @@ type ChatResponse struct {
|
||||
// Session 多轮对话与协同会话管理器
|
||||
type Session struct {
|
||||
ID string `json:"id"`
|
||||
UserID int64 `json:"user_id"`
|
||||
ProjectID int64 `json:"project_id"`
|
||||
UserCtx *coordinator.UserProjectContext `json:"user_ctx"`
|
||||
Recognizer intent.Recognizer `json:"-"`
|
||||
Coordinator *coordinator.CoordinatorEngine `json:"-"`
|
||||
@@ -59,6 +61,8 @@ func NewSession(id string, userCtx *coordinator.UserProjectContext, rec intent.R
|
||||
|
||||
return &Session{
|
||||
ID: id,
|
||||
UserID: 1,
|
||||
ProjectID: 1,
|
||||
UserCtx: userCtx,
|
||||
Recognizer: rec,
|
||||
Coordinator: coord,
|
||||
@@ -94,7 +98,7 @@ func (s *Session) sendMessageInternal(ctx context.Context, userInput string, str
|
||||
s.Messages = append(s.Messages, userMsg)
|
||||
|
||||
if s.CtxMgr != nil {
|
||||
_ = s.CtxMgr.RecordUserMessage(ctx, s.ID, 1, "user", userInput, "", "", nil)
|
||||
_ = s.CtxMgr.RecordUserMessage(ctx, s.ID, s.UserID, "user", userInput, "", "", nil)
|
||||
}
|
||||
|
||||
// 1. 意图识别 (L1 对话层,带非阻塞桌角备忘快照)
|
||||
@@ -155,7 +159,7 @@ func (s *Session) sendMessageInternal(ctx context.Context, userInput string, str
|
||||
s.Messages = append(s.Messages, asstMsg)
|
||||
|
||||
if s.CtxMgr != nil {
|
||||
_ = s.CtxMgr.RecordUserMessage(ctx, s.ID, 1, "assistant", coordRes.NaturalReply, string(coordRes.Intent), string(recRes.Branch), nil)
|
||||
_ = s.CtxMgr.RecordUserMessage(ctx, s.ID, 0, "assistant", coordRes.NaturalReply, string(coordRes.Intent), string(recRes.Branch), nil)
|
||||
}
|
||||
|
||||
// 4. 流式推送参谋自然语言回复与决策锦囊
|
||||
|
||||
@@ -7,7 +7,9 @@ import (
|
||||
|
||||
"chat-agent-system/internal/bizdev"
|
||||
"chat-agent-system/internal/chat"
|
||||
"chat-agent-system/internal/config"
|
||||
"chat-agent-system/internal/coordinator"
|
||||
"chat-agent-system/internal/docgov"
|
||||
"chat-agent-system/internal/intent"
|
||||
"chat-agent-system/internal/squad"
|
||||
)
|
||||
@@ -239,3 +241,74 @@ func TestChatSession_Branch3_AmbiguousConsult(t *testing.T) {
|
||||
t.Errorf("expected NextExpectation to be populated for user choice")
|
||||
}
|
||||
}
|
||||
|
||||
// 用例 6: 贴身参谋决策锦囊中自推荐往期标杆案例
|
||||
func TestChatSession_AdvisorCaseRecommendation(t *testing.T) {
|
||||
cfg := &config.Config{DataDir: t.TempDir()}
|
||||
svc := bizdev.NewService(cfg)
|
||||
ctx := context.Background()
|
||||
|
||||
// 1. 预置一个已终审批准的往期标杆案例
|
||||
doc := &docgov.DocumentRecord{
|
||||
DocID: "doc_history_ultrasonic_01",
|
||||
ProjectID: 201,
|
||||
TaskKey: "feasibility_reg",
|
||||
Title: "超声软组织切割止血手术设备注册评估",
|
||||
Stage: "V0.5_feasibility",
|
||||
CurrentState: docgov.StateApproved,
|
||||
IsLocked: true,
|
||||
AuthorName: "李工",
|
||||
}
|
||||
body := `# 超声软组织切割止血手术设备注册评估
|
||||
|
||||
## 概述与器械分类
|
||||
主要用于外科微创手术中软组织切割与止血闭合,按三类医疗器械申报。
|
||||
|
||||
## 关键性能与型检预估
|
||||
重点满足 GB 9706.1-2020 及 YY 0644 专用安全规范,型检周期约 5 个月。`
|
||||
|
||||
_, err := svc.ContextManager().IndexApprovedDocument(ctx, doc, body)
|
||||
if err != nil {
|
||||
t.Fatalf("IndexApprovedDocument failed: %v", err)
|
||||
}
|
||||
|
||||
// 2. 发起新会话咨询
|
||||
rec := intent.NewRecognizer()
|
||||
engine := coordinator.NewCoordinatorEngine(svc)
|
||||
userCtx := &coordinator.UserProjectContext{
|
||||
Project: coordinator.ProjectInfo{
|
||||
Name: "超声软组织刀研发咨询",
|
||||
DeviceType: "超声手术设备",
|
||||
IntendedUse: "软组织切割闭合",
|
||||
CurrentStage: bizdev.StageV01_Triage,
|
||||
},
|
||||
LatestAdvisorBrief: &squad.AdvisorBriefing{
|
||||
MaturityScore: 60,
|
||||
MaturityLevel: "V0.1",
|
||||
MaturityHeadline: "需求初步梳理中",
|
||||
},
|
||||
}
|
||||
|
||||
session := chat.NewSession("sess_case_recommend", userCtx, rec, engine)
|
||||
session.CtxMgr = svc.ContextManager()
|
||||
|
||||
resp, err := session.SendMessage(ctx, "我们之前做过类似的超声手术设备吗?注册和型检要注意什么?")
|
||||
if err != nil {
|
||||
t.Fatalf("SendMessage failed: %v", err)
|
||||
}
|
||||
|
||||
if resp.AdvisorBrief == nil {
|
||||
t.Fatalf("expected AdvisorBrief in response")
|
||||
}
|
||||
if len(resp.AdvisorBrief.RecommendedCases) == 0 {
|
||||
t.Fatalf("expected RecommendedCases to be populated, got 0")
|
||||
}
|
||||
|
||||
recCase := resp.AdvisorBrief.RecommendedCases[0]
|
||||
t.Logf("✓ 贴身参谋锦囊成功自推荐标杆案例: 文档=%s, 标题=%s, 章节=%s, 相似度=%.2f",
|
||||
recCase.DocID, recCase.ArtifactTitle, recCase.SectionTitle, recCase.SimilarityScore)
|
||||
|
||||
if recCase.DocID != "doc_history_ultrasonic_01" {
|
||||
t.Errorf("unexpected doc ID: %s", recCase.DocID)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -9,6 +9,7 @@ import (
|
||||
"strings"
|
||||
|
||||
"chat-agent-system/internal/bizdev"
|
||||
"chat-agent-system/internal/ctxmgr"
|
||||
"chat-agent-system/internal/intent"
|
||||
"chat-agent-system/internal/squad"
|
||||
"chat-agent-system/internal/stream"
|
||||
@@ -79,7 +80,11 @@ func (e *CoordinatorEngine) ProcessIntent(
|
||||
|
||||
// 1. 若为分支 3 (模糊/分忧引导),触发参谋主动分析与建议
|
||||
if recRes.Branch == intent.BranchAmbiguousFallback {
|
||||
return e.handleAmbiguousConsult(top, userCtx)
|
||||
res, err := e.handleAmbiguousConsult(top, userCtx)
|
||||
if err == nil && res != nil {
|
||||
e.enrichAdvisorBriefWithCases(ctx, res.AdvisorBrief, userCtx, top.FilledSlots)
|
||||
}
|
||||
return res, err
|
||||
}
|
||||
|
||||
// 2. 若缺少核心槽位且需澄清
|
||||
@@ -95,35 +100,91 @@ func (e *CoordinatorEngine) ProcessIntent(
|
||||
}
|
||||
|
||||
// 3. 根据候选意图执行分支
|
||||
var coordRes *CoordinationResult
|
||||
var coordErr error
|
||||
|
||||
switch top.Intent {
|
||||
case intent.IntentDeclineExpectation:
|
||||
return e.handleDeclineExpectation(userCtx)
|
||||
coordRes, coordErr = e.handleDeclineExpectation(userCtx)
|
||||
|
||||
case intent.IntentQueryProgress:
|
||||
return e.handleQueryProgress(top, userCtx)
|
||||
coordRes, coordErr = e.handleQueryProgress(top, userCtx)
|
||||
|
||||
case intent.IntentSupplementInfo:
|
||||
return e.handleSupplementInfo(top, userCtx)
|
||||
coordRes, coordErr = e.handleSupplementInfo(top, userCtx)
|
||||
|
||||
case intent.IntentAdvanceStage:
|
||||
return e.handleAdvanceStage(ctx, top, userCtx)
|
||||
coordRes, coordErr = e.handleAdvanceStage(ctx, top, userCtx)
|
||||
|
||||
case intent.IntentTriggerTask:
|
||||
return e.handleTriggerTask(ctx, top, userCtx)
|
||||
coordRes, coordErr = e.handleTriggerTask(ctx, top, userCtx)
|
||||
|
||||
case intent.IntentCasualChat:
|
||||
return e.handleCasualChat(userCtx)
|
||||
coordRes, coordErr = e.handleCasualChat(userCtx)
|
||||
|
||||
default:
|
||||
nextExp := BuildNextExpectation(userCtx.Project.CurrentStage, userCtx.Project.Name)
|
||||
return &CoordinationResult{
|
||||
coordRes = &CoordinationResult{
|
||||
Status: StatusAccepted,
|
||||
Intent: top.Intent,
|
||||
Branch: recRes.Branch,
|
||||
NaturalReply: "我已收到您的指示。针对当前项目,请问需要我为您查询进展、补充技术事实,还是推进方案论证?随时吩咐我。",
|
||||
UpdatedContext: userCtx,
|
||||
NextExpectation: nextExp,
|
||||
}, nil
|
||||
}
|
||||
}
|
||||
|
||||
if coordErr == nil && coordRes != nil {
|
||||
e.enrichAdvisorBriefWithCases(ctx, coordRes.AdvisorBrief, userCtx, top.FilledSlots)
|
||||
}
|
||||
|
||||
return coordRes, coordErr
|
||||
}
|
||||
|
||||
// enrichAdvisorBriefWithCases 结合上下文与意图槽位检索相似标杆案例并注入参谋锦囊
|
||||
func (e *CoordinatorEngine) enrichAdvisorBriefWithCases(ctx context.Context, brief *squad.AdvisorBriefing, userCtx *UserProjectContext, slots map[string]string) {
|
||||
if brief == nil || e.bizdevSvc == nil {
|
||||
return
|
||||
}
|
||||
ctxMgr := e.bizdevSvc.ContextManager()
|
||||
if ctxMgr == nil {
|
||||
return
|
||||
}
|
||||
|
||||
// 构造搜索语句
|
||||
queryParts := []string{userCtx.Project.DeviceType, userCtx.Project.IntendedUse}
|
||||
if topic, ok := slots[intent.SlotQueryTopic]; ok && topic != "" {
|
||||
queryParts = append(queryParts, topic)
|
||||
}
|
||||
query := strings.TrimSpace(strings.Join(queryParts, " "))
|
||||
if query == "" {
|
||||
query = userCtx.Project.Name
|
||||
}
|
||||
if query == "" {
|
||||
return
|
||||
}
|
||||
|
||||
cases, err := ctxMgr.SearchSimilarCases(ctx, ctxmgr.CaseSearchOptions{
|
||||
Query: query,
|
||||
Limit: 2,
|
||||
MinScore: 0.50,
|
||||
})
|
||||
if err != nil || len(cases) == 0 {
|
||||
return
|
||||
}
|
||||
|
||||
for _, c := range cases {
|
||||
brief.RecommendedCases = append(brief.RecommendedCases, squad.AdvisorCaseRecommendation{
|
||||
DocID: c.DocID,
|
||||
ProjectName: c.ProjectName,
|
||||
TaskKey: c.TaskKey,
|
||||
ArtifactTitle: c.ArtifactTitle,
|
||||
SectionTitle: c.SectionTitle,
|
||||
SummarySnippet: c.SummarySnippet,
|
||||
KeyRegulations: c.KeyRegulations,
|
||||
SimilarityScore: c.SimilarityScore,
|
||||
ApprovedAt: c.ApprovedAt,
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -217,13 +217,27 @@ type Manifest struct {
|
||||
DurationMs int64 `json:"duration_ms"`
|
||||
}
|
||||
|
||||
// AdvisorCaseRecommendation 参谋决策锦囊推荐的往期标杆案例
|
||||
type AdvisorCaseRecommendation struct {
|
||||
DocID string `json:"doc_id"`
|
||||
ProjectName string `json:"project_name"`
|
||||
TaskKey string `json:"task_key"`
|
||||
ArtifactTitle string `json:"artifact_title"`
|
||||
SectionTitle string `json:"section_title"`
|
||||
SummarySnippet string `json:"summary_snippet"`
|
||||
KeyRegulations []string `json:"key_regulations,omitempty"`
|
||||
SimilarityScore float64 `json:"similarity_score"`
|
||||
ApprovedAt string `json:"approved_at"`
|
||||
}
|
||||
|
||||
// AdvisorBriefing B轨业务参谋决策锦囊
|
||||
type AdvisorBriefing struct {
|
||||
MaturityScore int `json:"maturity_score"` // 成熟度评分 (0-100)
|
||||
MaturityLevel string `json:"maturity_level"` // 成熟度等级 (如 V0.1, V0.5, V1.0)
|
||||
MaturityHeadline string `json:"maturity_headline"` // 总体进展研判(一句话核心诊断)
|
||||
BaselineAssumptions []string `json:"baseline_assumptions"` // 基准假设清单(替代填空,为用户预设)
|
||||
PreemptiveRisks []string `json:"preemptive_risks"` // 预见性风险雷达(替用户提前看两步)
|
||||
NextBestActions []string `json:"next_best_actions"` // 建议下一步行动选项
|
||||
CommunicationKit string `json:"communication_kit"` // 建议对外沟通话术(微信/邮件模板)
|
||||
MaturityScore int `json:"maturity_score"` // 成熟度评分 (0-100)
|
||||
MaturityLevel string `json:"maturity_level"` // 成熟度等级 (如 V0.1, V0.5, V1.0)
|
||||
MaturityHeadline string `json:"maturity_headline"` // 总体进展研判(一句话核心诊断)
|
||||
BaselineAssumptions []string `json:"baseline_assumptions"` // 基准假设清单(替代填空,为用户预设)
|
||||
PreemptiveRisks []string `json:"preemptive_risks"` // 预见性风险雷达(替用户提前看两步)
|
||||
NextBestActions []string `json:"next_best_actions"` // 建议下一步行动选项
|
||||
CommunicationKit string `json:"communication_kit"` // 建议对外沟通话术(微信/邮件模板)
|
||||
RecommendedCases []AdvisorCaseRecommendation `json:"recommended_cases,omitempty"` // 💡 往期标杆案例与历史报价经验自推荐
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user