diff --git a/internal/ai/runtime/executor/answerability_gate.go b/internal/ai/runtime/executor/answerability_gate.go new file mode 100644 index 0000000..4b70936 --- /dev/null +++ b/internal/ai/runtime/executor/answerability_gate.go @@ -0,0 +1,78 @@ +package executor + +import ( + "context" + "time" + + "cs-agent/internal/ai/runtime/internal/impl/callbacks" + "cs-agent/internal/ai/runtime/internal/impl/factory" + "cs-agent/internal/ai/runtime/internal/impl/retrievers" + "cs-agent/internal/models" + + "github.com/cloudwego/eino/components/model" + "github.com/cloudwego/eino/schema" +) + +const ( + answerabilityNodeRetrieve = "retrieve_knowledge" + answerabilityNodeGrade = "grade_answerability" + answerabilityNodeAllow = "allow_agent" + answerabilityNodeFallback = "fallback" + + answerabilityStatusSkipped = "skipped" + answerabilityStatusAnswerable = "answerable" + answerabilityStatusUnanswerable = "unanswerable" +) + +type knowledgeContextRetriever interface { + KnowledgeBaseIDs() []int64 + RetrieveContextByOptions(ctx context.Context, opts retrievers.KnowledgeRetrieveOptions, query string) (*retrievers.KnowledgeRetrieveResult, error) +} + +type answerabilityRetrieverFactory func(aiAgent models.AIAgent) knowledgeContextRetriever + +type answerabilityChatModelFactory func(ctx context.Context, aiConfig models.AIConfig) (model.BaseChatModel, error) + +type KnowledgeAnswerabilityGate struct { + newRetriever answerabilityRetrieverFactory + newChatModel answerabilityChatModelFactory + now func() time.Time +} + +type answerabilityGateInput struct { + Request RunInput + Summary *RunResult + Collector *callbacks.RuntimeTraceCollector + Messages []*schema.Message +} + +type answerabilityGateState struct { + Input answerabilityGateInput + KnowledgeIDs []int64 + RetrieveResult *retrievers.KnowledgeRetrieveResult + Decision knowledgeGuardDecision + Grade answerabilityDecision + SkipGate bool + FallbackReply string + ErrorMessage string +} + +type answerabilityDecision struct { + Answerable bool `json:"answerable"` + Reason string `json:"reason"` + SupportingChunkIDs []string `json:"supportingChunkIds"` + MissingInfo []string `json:"missingInfo"` +} + +func NewKnowledgeAnswerabilityGate() *KnowledgeAnswerabilityGate { + chatModelFactory := factory.NewChatModelFactory() + return &KnowledgeAnswerabilityGate{ + newRetriever: func(aiAgent models.AIAgent) knowledgeContextRetriever { + return retrievers.NewKnowledgeRetriever(aiAgent) + }, + newChatModel: func(ctx context.Context, aiConfig models.AIConfig) (model.BaseChatModel, error) { + return chatModelFactory.Build(ctx, aiConfig) + }, + now: time.Now, + } +} diff --git a/internal/ai/runtime/executor/knowledge_guard.go b/internal/ai/runtime/executor/knowledge_guard.go index 34eca15..9c83de7 100644 --- a/internal/ai/runtime/executor/knowledge_guard.go +++ b/internal/ai/runtime/executor/knowledge_guard.go @@ -51,6 +51,18 @@ func resolveKnowledgeFallbackReply(aiAgent models.AIAgent) string { } } +func resolveKnowledgeHumanSupportFallback(aiAgent models.AIAgent) string { + base := strings.TrimSpace(resolveKnowledgeFallbackReply(aiAgent)) + if base == "" { + base = "当前知识库暂无明确信息。" + } + suggestion := "建议你联系人工客服进一步确认。" + if strings.Contains(base, suggestion) { + return base + } + return strings.TrimSpace(base + " " + suggestion) +} + func buildKnowledgeRuntimeInstruction(answerMode enums.KnowledgeAnswerMode, fallbackReply string) string { fallbackReply = strings.TrimSpace(fallbackReply) if fallbackReply == "" { diff --git a/internal/ai/runtime/executor/knowledge_guard_test.go b/internal/ai/runtime/executor/knowledge_guard_test.go index 6446aca..0717755 100644 --- a/internal/ai/runtime/executor/knowledge_guard_test.go +++ b/internal/ai/runtime/executor/knowledge_guard_test.go @@ -100,6 +100,29 @@ func TestBuildKnowledgeUnavailableDecisionSkipsWhenAgentHasNoKnowledge(t *testin } } +func TestResolveKnowledgeHumanSupportFallbackUsesAgentMessage(t *testing.T) { + agent := newKnowledgeGuardAgentFixture() + agent.FallbackMessage = "我暂时没有找到足够准确的信息。" + + got := resolveKnowledgeHumanSupportFallback(agent) + + want := "我暂时没有找到足够准确的信息。 建议你联系人工客服进一步确认。" + if got != want { + t.Fatalf("unexpected fallback: %q", got) + } +} + +func TestResolveKnowledgeHumanSupportFallbackUsesDefault(t *testing.T) { + agent := newKnowledgeGuardAgentFixture() + + got := resolveKnowledgeHumanSupportFallback(agent) + + want := "当前知识库暂无明确信息。 建议你联系人工客服进一步确认。" + if got != want { + t.Fatalf("unexpected fallback: %q", got) + } +} + func newKnowledgeGuardAgentFixture() models.AIAgent { return models.AIAgent{ FallbackMode: enums.AIAgentFallbackModeNoAnswer,