diff --git a/internal/ai/runtime/executor/context_builders.go b/internal/ai/runtime/executor/context_builders.go index e250106..8ef1fcf 100644 --- a/internal/ai/runtime/executor/context_builders.go +++ b/internal/ai/runtime/executor/context_builders.go @@ -44,7 +44,7 @@ func appendRetrievedContext(ctx context.Context, req RunInput, summary *RunResul retrieveOptions.QueryPreview = preview(req.UserMessage.Content, 120) retrieveResult, retrieveErr := retriever.RetrieveContextByOptions(ctx, retrieveOptions, strings.TrimSpace(req.UserMessage.Content)) if retrieveErr != nil || retrieveResult == nil { - return knowledgeGuardDecision{} + return buildKnowledgeUnavailableDecision(req.AIAgent, retriever.KnowledgeBaseIDs()) } if summary != nil { summary.RetrieverCount = len(retrieveResult.Hits) diff --git a/internal/ai/runtime/executor/knowledge_guard.go b/internal/ai/runtime/executor/knowledge_guard.go index aecf3a4..34eca15 100644 --- a/internal/ai/runtime/executor/knowledge_guard.go +++ b/internal/ai/runtime/executor/knowledge_guard.go @@ -15,12 +15,19 @@ type knowledgeGuardDecision struct { Instructions []*schema.Message } +func buildKnowledgeUnavailableDecision(aiAgent models.AIAgent, knowledgeBaseIDs []int64) knowledgeGuardDecision { + if len(knowledgeBaseIDs) == 0 { + return knowledgeGuardDecision{} + } + return knowledgeGuardDecision{FallbackReply: resolveKnowledgeFallbackReply(aiAgent)} +} + func buildKnowledgeGuardDecision(aiAgent models.AIAgent, retrieveResult *retrievers.KnowledgeRetrieveResult) knowledgeGuardDecision { if retrieveResult == nil || len(retrieveResult.KnowledgeBaseIDs) == 0 { return knowledgeGuardDecision{} } fallbackReply := resolveKnowledgeFallbackReply(aiAgent) - if len(retrieveResult.Hits) == 0 { + if len(retrieveResult.Hits) == 0 || strings.TrimSpace(retrieveResult.ContextText) == "" { return knowledgeGuardDecision{FallbackReply: fallbackReply} } instruction := buildKnowledgeRuntimeInstruction(retrieveResult.AnswerMode, fallbackReply) @@ -50,7 +57,7 @@ func buildKnowledgeRuntimeInstruction(answerMode enums.KnowledgeAnswerMode, fall fallbackReply = "当前知识库暂无明确信息。" } if answerMode == enums.KnowledgeAnswerModeAssist { - return "知识库回答约束:优先依据后续提供的知识片段回答,可以做轻度归纳,但不要编造片段中未提供的事实。若知识片段不足以直接支持答案,必须明确回复:" + fallbackReply + return "知识库回答约束:优先依据后续提供的知识片段回答,可以做轻度归纳,但不要编造片段中未提供的事实。回答中的具体事实、步骤、承诺必须能被知识片段直接支持;若知识片段不足以直接支持答案,必须明确回复:" + fallbackReply } - return "知识库回答约束:本轮只能依据后续提供的知识片段回答,不得使用模型常识补充未提供的事实。若知识片段不足以支持回答,必须明确回复:" + fallbackReply + return "知识库回答约束:本轮只能依据后续提供的知识片段回答,不得使用模型常识补充未提供的事实,不得输出知识片段外的具体事实、步骤、承诺或建议。若知识片段不足以支持回答,必须明确回复:" + fallbackReply } diff --git a/internal/ai/runtime/executor/knowledge_guard_test.go b/internal/ai/runtime/executor/knowledge_guard_test.go index 1059833..6446aca 100644 --- a/internal/ai/runtime/executor/knowledge_guard_test.go +++ b/internal/ai/runtime/executor/knowledge_guard_test.go @@ -44,7 +44,8 @@ func TestBuildKnowledgeGuardDecisionInjectsStrictInstructionOnHit(t *testing.T) Hits: []rag.RetrieveResult{ {KnowledgeBaseID: 1, Score: 0.88}, }, - AnswerMode: enums.KnowledgeAnswerModeStrict, + ContextText: "知识库上下文", + AnswerMode: enums.KnowledgeAnswerModeStrict, }) if decision.FallbackReply != "" { @@ -62,6 +63,43 @@ func TestBuildKnowledgeGuardDecisionInjectsStrictInstructionOnHit(t *testing.T) } } +func TestBuildKnowledgeGuardDecisionFallsBackWhenHitHasNoContext(t *testing.T) { + agent := newKnowledgeGuardAgentFixture() + agent.FallbackMessage = "我暂时没有找到足够准确的信息。" + decision := buildKnowledgeGuardDecision(agent, &retrievers.KnowledgeRetrieveResult{ + KnowledgeBaseIDs: []int64{1}, + Hits: []rag.RetrieveResult{ + {KnowledgeBaseID: 1, Score: 0.88}, + }, + AnswerMode: enums.KnowledgeAnswerModeStrict, + }) + + if decision.FallbackReply != "我暂时没有找到足够准确的信息。" { + t.Fatalf("expected fallback on empty context, got %q", decision.FallbackReply) + } + if len(decision.Instructions) != 0 { + t.Fatalf("expected no instructions on empty context, got %d", len(decision.Instructions)) + } +} + +func TestBuildKnowledgeUnavailableDecisionFallsBackWhenAgentHasKnowledge(t *testing.T) { + agent := newKnowledgeGuardAgentFixture() + agent.FallbackMessage = "知识库暂时不可用。" + decision := buildKnowledgeUnavailableDecision(agent, []int64{1}) + + if decision.FallbackReply != "知识库暂时不可用。" { + t.Fatalf("expected fallback when knowledge unavailable, got %q", decision.FallbackReply) + } +} + +func TestBuildKnowledgeUnavailableDecisionSkipsWhenAgentHasNoKnowledge(t *testing.T) { + decision := buildKnowledgeUnavailableDecision(newKnowledgeGuardAgentFixture(), nil) + + if decision.FallbackReply != "" { + t.Fatalf("expected no fallback without knowledge bases, got %q", decision.FallbackReply) + } +} + func newKnowledgeGuardAgentFixture() models.AIAgent { return models.AIAgent{ FallbackMode: enums.AIAgentFallbackModeNoAnswer,