feat: add rag answerability gate types

This commit is contained in:
mlogclub
2026-05-02 13:56:01 +08:00
parent 28913ad440
commit 269042b9d4
3 changed files with 113 additions and 0 deletions
@@ -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,
}
}
@@ -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 == "" {
@@ -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,