From 3d09bd6d7d19a75e7c5fde77dbe372d59550fbbc Mon Sep 17 00:00:00 2001 From: weichen Date: Sat, 12 Sep 2026 16:54:19 +0000 Subject: [PATCH] =?UTF-8?q?fix(quality):=20=E9=97=AD=E7=8E=AF=20Go=20?= =?UTF-8?q?=E8=B4=A8=E9=87=8F=E9=97=A8=E7=A6=81=EF=BC=88=E9=89=B4=E6=9D=83?= =?UTF-8?q?=E5=AF=8C=E5=8C=96/V0.5/vet/health/KB=20gate/=E8=AE=B0=E5=BF=86?= =?UTF-8?q?=E8=BD=AE=E8=AF=A2=EF=BC=89?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- internal/bizdev/full_link_e2e_test.go | 13 ++++--- internal/bizdev/service.go | 17 +++++++- internal/bizdev/types_test.go | 6 +-- internal/chat/session_test.go | 43 ++++++++++++--------- internal/coordinator/action_planner.go | 13 +++++++ internal/coordinator/action_planner_test.go | 3 ++ internal/kb/integration_test.go | 7 ++++ internal/server/asset_handlers_test.go | 5 ++- internal/server/handlers.go | 6 ++- 9 files changed, 84 insertions(+), 29 deletions(-) diff --git a/internal/bizdev/full_link_e2e_test.go b/internal/bizdev/full_link_e2e_test.go index 40be50db..68e7af7d 100644 --- a/internal/bizdev/full_link_e2e_test.go +++ b/internal/bizdev/full_link_e2e_test.go @@ -121,12 +121,15 @@ func TestBizDev_FullLink_V01_V05_V10_E2E(t *testing.T) { if !resV05.Success { t.Fatalf("Stage V0.5 should succeed, got error: %s", resV05.ErrorReason) } - if len(resV05.SubArtifacts) != 3 { - t.Fatalf("Stage V0.5 should produce 3 sub-artifacts (research, feasibility_reg, prelim_design), got %d", len(resV05.SubArtifacts)) + if len(resV05.SubArtifacts) != 5 { + t.Fatalf("Stage V0.5 should produce 5 sub-artifacts, got %d", len(resV05.SubArtifacts)) } - t.Logf("✓ Stage V0.5 成功完成: 包含任务=[%s, %s, %s], 成熟度评分=%d (%s)", - resV05.SubArtifacts[0].TaskKey, resV05.SubArtifacts[1].TaskKey, resV05.SubArtifacts[2].TaskKey, - resV05.AdvisorBrief.MaturityScore, resV05.AdvisorBrief.MaturityLevel) + keys := make([]string, len(resV05.SubArtifacts)) + for i, sub := range resV05.SubArtifacts { + keys[i] = sub.TaskKey + } + t.Logf("✓ Stage V0.5 成功完成: 包含任务=%v, 成熟度评分=%d (%s)", + keys, resV05.AdvisorBrief.MaturityScore, resV05.AdvisorBrief.MaturityLevel) // 验证 feasibility_reg 受控文档已建立 docID := "doc_proj1001_feasibility_reg" diff --git a/internal/bizdev/service.go b/internal/bizdev/service.go index d54f777a..462ef648 100644 --- a/internal/bizdev/service.go +++ b/internal/bizdev/service.go @@ -159,7 +159,7 @@ func NewService(cfg *config.Config) (*Service, error) { return svc, nil } -// SchemaVersion 返回当前数据库 schema 版本(goose)。 +// SchemaVersion 返回启动时缓存的数据库 schema 版本(goose)。 func (s *Service) SchemaVersion() int64 { if s == nil { return 0 @@ -167,6 +167,21 @@ func (s *Service) SchemaVersion() int64 { return s.schemaVersion } +// LiveSchemaVersion 从数据库实时读取当前 schema 版本;失败时回退缓存值。 +func (s *Service) LiveSchemaVersion(ctx context.Context) int64 { + if s == nil { + return 0 + } + if s.pool == nil { + return s.schemaVersion + } + ver, err := db.CurrentVersion(ctx, s.pool) + if err != nil { + return s.schemaVersion + } + return ver +} + // DatabaseReady 数据库是否已连接且迁移完成。 func (s *Service) DatabaseReady() bool { return s != nil && s.pool != nil diff --git a/internal/bizdev/types_test.go b/internal/bizdev/types_test.go index 0aff88f9..850d91e1 100644 --- a/internal/bizdev/types_test.go +++ b/internal/bizdev/types_test.go @@ -145,9 +145,9 @@ func TestExecuteStageV01AndV05(t *testing.T) { if stage2Res.AdvisorBrief.MaturityLevel != "V0.5" { t.Errorf("expected V0.5 maturity level, got %s", stage2Res.AdvisorBrief.MaturityLevel) } - // Stage 2 包含 3 个任务 (research, feasibility_reg, prelim_design) - if len(stage2Res.SubArtifacts) != 3 { - t.Errorf("expected 3 sub artifacts in stage 2, got %d", len(stage2Res.SubArtifacts)) + // Stage V0.5 包含 5 个任务(与 SupportedStages 一致) + if len(stage2Res.SubArtifacts) != 5 { + t.Errorf("expected 5 sub artifacts in stage 2, got %d", len(stage2Res.SubArtifacts)) } // 3. 运行 Stage 3 (V1.0 Quoting),透传全部前序累积 diff --git a/internal/chat/session_test.go b/internal/chat/session_test.go index fc51ec19..8c52b352 100644 --- a/internal/chat/session_test.go +++ b/internal/chat/session_test.go @@ -522,28 +522,35 @@ func TestChatSession_SemanticMemoryRecallAndAsyncTurnRecord(t *testing.T) { t.Errorf("expected snippet to contain YY 0505, got: %s", session.UserCtx.RecalledMemorySnippet) } - // 4. 等待异步写入 goroutine 落盘 - time.Sleep(100 * time.Millisecond) - - // 5. 验证本轮当场生成的问答对是否已被向量记忆库索引 - results, err := ctxMgr.SearchChatMemory(ctx, ctxmgr.ChatMemorySearchOptions{ - Query: "电磁兼容样机数量", - UserID: 99, - ProjectID: 200, - TopK: 5, - MinScore: 0.1, - }) - if err != nil { - t.Fatalf("SearchChatMemory failed: %v", err) - } - + // 4. 等待异步写入 goroutine 落盘(轮询至 deadline,避免固定 sleep 竞态) + deadline := time.Now().Add(2 * time.Second) + var results []ctxmgr.RecalledMemoryItem foundCurrentTurn := false - for _, r := range results { - if r.SessionID == "sess_current_recall" { - foundCurrentTurn = true + for time.Now().Before(deadline) { + var searchErr error + results, searchErr = ctxMgr.SearchChatMemory(ctx, ctxmgr.ChatMemorySearchOptions{ + Query: "电磁兼容样机数量", + UserID: 99, + ProjectID: 200, + TopK: 5, + MinScore: 0.1, + }) + if searchErr != nil { + t.Fatalf("SearchChatMemory failed: %v", searchErr) + } + for _, r := range results { + if r.SessionID == "sess_current_recall" { + foundCurrentTurn = true + break + } + } + if foundCurrentTurn { break } + time.Sleep(20 * time.Millisecond) } + + // 5. 验证本轮当场生成的问答对是否已被向量记忆库索引 if !foundCurrentTurn { t.Errorf("expected current turn to be asynchronously indexed into chat memory") } diff --git a/internal/coordinator/action_planner.go b/internal/coordinator/action_planner.go index 59f10f46..a5bfd8df 100644 --- a/internal/coordinator/action_planner.go +++ b/internal/coordinator/action_planner.go @@ -93,6 +93,19 @@ func (e *CoordinatorEngine) executeIntentPlan( if pendingConfirm != nil && userCtx.PendingExpectation != nil { last.NextExpectation = userCtx.PendingExpectation } + + slots := map[string]string{} + if recRes.TopIntent != nil && len(recRes.TopIntent.FilledSlots) > 0 { + slots = recRes.TopIntent.FilledSlots + } + if last.AdvisorBrief == nil { + last.AdvisorBrief = userCtx.LatestAdvisorBrief + } + if last.AdvisorBrief == nil { + last.AdvisorBrief = synthesizeBaselineBrief(userCtx) + } + e.enrichAdvisorBriefWithCases(ctx, last.AdvisorBrief, userCtx, slots) + return last, nil } diff --git a/internal/coordinator/action_planner_test.go b/internal/coordinator/action_planner_test.go index e0215998..79034a28 100644 --- a/internal/coordinator/action_planner_test.go +++ b/internal/coordinator/action_planner_test.go @@ -38,6 +38,9 @@ func TestEngine_IntentPlan_QueryThenQuery(t *testing.T) { if !strings.Contains(res.NaturalReply, "步骤") { t.Errorf("expected multi-step reply, got: %s", res.NaturalReply) } + if res.AdvisorBrief == nil { + t.Fatal("expected AdvisorBrief on multi-intent plan result") + } } func TestEngine_ExpectationPlusNew_ExecutesQueryKeepsConfirm(t *testing.T) { diff --git a/internal/kb/integration_test.go b/internal/kb/integration_test.go index 8ad281de..0fc64e1c 100644 --- a/internal/kb/integration_test.go +++ b/internal/kb/integration_test.go @@ -2,6 +2,7 @@ package kb_test import ( "context" + "os" "strings" "testing" "time" @@ -11,6 +12,12 @@ import ( ) func TestKB_RealIntegration(t *testing.T) { + if testing.Short() { + t.Skip("skipping KB real integration in short mode") + } + if os.Getenv("RUN_KB_INTEGRATION") != "1" { + t.Skip("set RUN_KB_INTEGRATION=1 to run KB real integration") + } cfg, err := config.Load("") if err != nil { t.Fatalf("failed to load config: %v", err) diff --git a/internal/server/asset_handlers_test.go b/internal/server/asset_handlers_test.go index bd9355e4..69eb7bad 100644 --- a/internal/server/asset_handlers_test.go +++ b/internal/server/asset_handlers_test.go @@ -87,7 +87,10 @@ func TestUploadProjectAsset_RejectsOversizedFilename(t *testing.T) { req, _ := http.NewRequest(http.MethodPost, ts.URL+"/api/v1/projects/7/assets", &body) req.Header.Set("Content-Type", w.FormDataContentType()) withBearerAuth(req, loginTestAdmin(t, srv)) - resp, _ := http.DefaultClient.Do(req) + resp, err := http.DefaultClient.Do(req) + if err != nil { + t.Fatalf("request failed: %v", err) + } defer resp.Body.Close() if resp.StatusCode != http.StatusUnsupportedMediaType { t.Fatalf("expected 415 for long filename, got %d", resp.StatusCode) diff --git a/internal/server/handlers.go b/internal/server/handlers.go index d05355a7..1efa7edc 100644 --- a/internal/server/handlers.go +++ b/internal/server/handlers.go @@ -91,7 +91,11 @@ func (s *Server) buildHealthResponse(strict bool) (HealthResponse, int) { dbReady := s.svc != nil && s.svc.DatabaseReady() schemaVersion := int64(0) if s.svc != nil { - schemaVersion = s.svc.SchemaVersion() + if dbReady { + schemaVersion = s.svc.LiveSchemaVersion(context.Background()) + } else { + schemaVersion = s.svc.SchemaVersion() + } } schemaReady := schemaVersion > 0 || (s.cfg != nil && s.cfg.AllowInMemory) taskStoreReady := s.svc != nil && taskrun.TablesReady(context.Background(), s.svc.Pool())