feat: add fallback mode for AI agents and update knowledge base handling

- Introduced AIAgentFallbackMode with options for NoAnswer and SuggestRetry.
- Updated AI agent edit component to include fallback mode selection.
- Modified API types and payloads to accommodate fallback mode.
- Removed deprecated KnowledgeFallbackMode and related logic from knowledge base.
- Adjusted knowledge retrieval and response handling to utilize new fallback logic.
- Updated tests to reflect changes in fallback mode handling for AI agents.
This commit is contained in:
mlogclub
2026-04-18 13:36:44 +08:00
parent d9c4b669b2
commit c84870b968
25 changed files with 497 additions and 139 deletions
@@ -50,6 +50,8 @@ import {
} from "@/lib/api/admin";
import { getEnumOptions } from "@/lib/enums";
import {
AIAgentFallbackMode,
AIAgentFallbackModeLabels,
AIAgentHandoffMode,
AIAgentHandoffModeLabels,
AIModelType,
@@ -93,6 +95,7 @@ const schema = z.object({
.number()
.min(0, "回复超时秒数必须是大于等于 0 的整数"),
handoffMode: z.string().trim().min(1, "请选择转人工模式"),
fallbackMode: z.string().trim().min(1, "请选择兜底策略"),
fallbackMessage: z.string().trim(),
});
@@ -118,6 +121,13 @@ const handoffModeOptions = getEnumOptions(AIAgentHandoffModeLabels).map(
}),
);
const fallbackModeOptions = getEnumOptions(AIAgentFallbackModeLabels).map(
(option) => ({
value: String(option.value),
label: option.label,
}),
);
function buildForm(item: AIAgent | null): EditForm {
if (!item) {
return {
@@ -129,6 +139,7 @@ function buildForm(item: AIAgent | null): EditForm {
welcomeMessage: "",
replyTimeoutSeconds: 180,
handoffMode: String(AIAgentHandoffMode.WaitPool),
fallbackMode: String(AIAgentFallbackMode.NoAnswer),
fallbackMessage: "",
};
}
@@ -141,6 +152,7 @@ function buildForm(item: AIAgent | null): EditForm {
welcomeMessage: item.welcomeMessage || "",
replyTimeoutSeconds: item.replyTimeoutSeconds ?? 180,
handoffMode: String(item.handoffMode),
fallbackMode: String(item.fallbackMode),
fallbackMessage: item.fallbackMessage || "",
};
}
@@ -163,6 +175,7 @@ function buildPayload(
replyTimeoutSeconds: Number(form.replyTimeoutSeconds),
teamIds,
handoffMode: Number(form.handoffMode),
fallbackMode: Number(form.fallbackMode),
fallbackMessage: form.fallbackMessage.trim(),
knowledgeIds,
skillIds,
@@ -737,13 +750,34 @@ function EditDialogBody({
</FieldContent>
</Field>
<Field data-invalid={!!errors.fallbackMode}>
<FieldLabel></FieldLabel>
<FieldContent className="space-y-3">
<Controller
control={control}
name="fallbackMode"
render={({ field }) => (
<OptionCombobox
value={field.value}
options={fallbackModeOptions}
placeholder="请选择兜底策略"
searchPlaceholder="搜索兜底策略"
emptyText="未找到兜底策略"
onChange={field.onChange}
/>
)}
/>
<FieldError errors={[errors.fallbackMode]} />
</FieldContent>
</Field>
<Field data-invalid={!!errors.fallbackMessage}>
<FieldLabel htmlFor="ai-agent-fallback-message">
</FieldLabel>
<FieldContent>
<div className="text-xs text-muted-foreground mb-1">
使 AI Graph Tool
Graph Tool
</div>
<Textarea
id="ai-agent-fallback-message"
@@ -35,8 +35,6 @@ import {
KnowledgeChunkProviderLabels,
KnowledgeAnswerMode,
KnowledgeAnswerModeLabels,
KnowledgeFallbackMode,
KnowledgeFallbackModeLabels,
} from "@/lib/generated/enums";
import { getEnumLabel, getEnumOptions } from "@/lib/enums";
@@ -60,7 +58,6 @@ const emptyForm: EditForm = {
chunkMaxTokens: "400",
chunkOverlapTokens: "40",
answerMode: String(KnowledgeAnswerMode.Strict),
fallbackMode: String(KnowledgeFallbackMode.NoAnswer),
remark: "",
};
@@ -76,7 +73,6 @@ const knowledgeBaseFormSchema = z.object({
chunkMaxTokens: z.string().trim().min(1, "请输入最大 token 数"),
chunkOverlapTokens: z.string().trim().min(1, "请输入重叠 token 数"),
answerMode: z.string().trim().min(1, "请选择回答模式"),
fallbackMode: z.string().trim().min(1, "请选择回退模式"),
remark: z.string().trim().max(500, "备注最多500个字符"),
});
@@ -106,7 +102,6 @@ function buildForm(item: KnowledgeBase | null): EditForm {
chunkMaxTokens: String(item.chunkMaxTokens),
chunkOverlapTokens: String(item.chunkOverlapTokens),
answerMode: String(item.answerMode),
fallbackMode: String(item.fallbackMode),
remark: item.remark || "",
};
}
@@ -124,7 +119,6 @@ function buildPayload(form: EditForm): CreateKnowledgeBasePayload {
chunkMaxTokens: Number(form.chunkMaxTokens),
chunkOverlapTokens: Number(form.chunkOverlapTokens),
answerMode: Number(form.answerMode),
fallbackMode: Number(form.fallbackMode),
remark: form.remark.trim(),
};
}
@@ -477,43 +471,6 @@ function KnowledgeBaseFormDialogBody({
<FieldError errors={[errors.answerMode]} />
</FieldContent>
</Field>
<Field data-invalid={!!errors.fallbackMode}>
<FieldLabel htmlFor="kb-fallback-mode">退</FieldLabel>
<FieldContent>
<Controller
control={control}
name="fallbackMode"
render={({ field }) => (
<Select value={field.value} onValueChange={field.onChange}>
<SelectTrigger
id="kb-fallback-mode"
aria-invalid={!!errors.fallbackMode}
>
<SelectValue placeholder="选择回退模式">
{field.value
? getEnumLabel(
KnowledgeFallbackModeLabels,
Number(field.value),
)
: undefined}
</SelectValue>
</SelectTrigger>
<SelectContent>
{getEnumOptions(KnowledgeFallbackModeLabels).map((option) => (
<SelectItem
key={option.value}
value={String(option.value)}
>
{option.label}
</SelectItem>
))}
</SelectContent>
</Select>
)}
/>
<FieldError errors={[errors.fallbackMode]} />
</FieldContent>
</Field>
</div>
<Field data-invalid={!!errors.remark}>
+3 -4
View File
@@ -208,6 +208,8 @@ export type AIAgent = {
teams: { id: number; name: string }[]
handoffMode: number
handoffModeName: string
fallbackMode: number
fallbackModeName: string
fallbackMessage: string
knowledgeIds: number[]
knowledgeBaseNames: string[]
@@ -239,6 +241,7 @@ export type CreateAIAgentPayload = {
replyTimeoutSeconds: number
teamIds: number[]
handoffMode: number
fallbackMode: number
fallbackMessage: string
knowledgeIds: number[]
skillIds: number[]
@@ -1189,8 +1192,6 @@ export type KnowledgeBase = {
chunkOverlapTokens: number
answerMode: number
answerModeName: string
fallbackMode: number
fallbackModeName: string
documentCount: number
faqCount: number
remark: string
@@ -1212,7 +1213,6 @@ export type CreateKnowledgeBasePayload = {
chunkMaxTokens: number
chunkOverlapTokens: number
answerMode: number
fallbackMode: number
remark: string
}
@@ -1400,7 +1400,6 @@ export type KnowledgeSearchPayload = {
export type KnowledgeAnswerPayload = KnowledgeSearchPayload & {
answerMode?: number
fallbackMode?: number
}
export type CreateKnowledgeDocumentPayload = {
+9 -11
View File
@@ -1,5 +1,14 @@
// Code generated by cmd/enums/generator.go. DO NOT EDIT.
export enum AIAgentFallbackMode {
NoAnswer = 1,
SuggestRetry = 2,
}
export const AIAgentFallbackModeLabels: Record<AIAgentFallbackMode, string> = {
[AIAgentFallbackMode.NoAnswer]: "直接声明无答案",
[AIAgentFallbackMode.SuggestRetry]: "建议补充信息",
}
export enum AIAgentHandoffMode {
WaitPool = 1,
DefaultTeamPool = 2,
@@ -274,17 +283,6 @@ export const KnowledgeDocumentIndexStatusLabels: Record<KnowledgeDocumentIndexSt
[KnowledgeDocumentIndexStatus.Failed]: "索引失败",
}
export enum KnowledgeFallbackMode {
NoAnswer = 1,
SuggestRetry = 2,
TransferHuman = 3,
}
export const KnowledgeFallbackModeLabels: Record<KnowledgeFallbackMode, string> = {
[KnowledgeFallbackMode.NoAnswer]: "直接声明无答案",
[KnowledgeFallbackMode.SuggestRetry]: "建议重试",
[KnowledgeFallbackMode.TransferHuman]: "转人工",
}
export enum KnowledgeFeedbackType {
Like = 1,
Dislike = 2,