Enhance workflow routing and agent configuration management

- Introduce WorkflowLLMRouter for pre-response LLM routing, allowing agents to determine the appropriate function to call based on user input.
- Implement UserTurnRoutingProcessor to manage user turns before reaching the LLM, ensuring proper routing and handling of user messages.
- Refactor WorkflowBrain to integrate new routing logic and enhance agent stage configuration, including entry modes and resource management.
- Update service factory to support dynamic LLM resource configuration based on workflow settings.
- Add tests for new routing functionality and ensure proper handling of user messages in various scenarios.
This commit is contained in:
Xin Wang
2026-07-14 09:36:28 +08:00
parent 32aef14ddb
commit 72856bf3a7
19 changed files with 2611 additions and 552 deletions

View File

@@ -0,0 +1,168 @@
"use client";
import { Settings2 } from "lucide-react";
import { useState } from "react";
import { Button } from "@/components/ui/button";
import {
Dialog,
DialogContent,
DialogDescription,
DialogFooter,
DialogHeader,
DialogTitle,
} from "@/components/ui/dialog";
import { Input } from "@/components/ui/input";
import {
Select,
SelectContent,
SelectItem,
SelectTrigger,
SelectValue,
} from "@/components/ui/select";
import type { KnowledgeRetrievalConfig } from "@/lib/api";
export const DEFAULT_KNOWLEDGE_RETRIEVAL_CONFIG: KnowledgeRetrievalConfig = {
mode: "automatic",
topN: 5,
scoreThreshold: 0,
};
export function KnowledgeRetrievalConfigDialog({
disabled,
value,
onChange,
}: {
disabled: boolean;
value: KnowledgeRetrievalConfig;
onChange: (config: KnowledgeRetrievalConfig) => void;
}) {
const [open, setOpen] = useState(false);
const [draft, setDraft] = useState(value);
const [error, setError] = useState<string | null>(null);
function openDialog() {
setDraft(value);
setError(null);
setOpen(true);
}
function saveDraft() {
if (draft.topN === 0 || draft.topN < -1 || !Number.isInteger(draft.topN)) {
setError("Top N 必须为 -1 或大于 0 的整数");
return;
}
if (draft.scoreThreshold < 0 || draft.scoreThreshold > 1) {
setError("最低相关度必须在 0 到 1 之间");
return;
}
onChange(draft);
setOpen(false);
}
return (
<>
<button
type="button"
disabled={disabled}
onClick={openDialog}
aria-label="打开知识库高级配置"
title={
disabled
? "请先选择知识库"
: `${value.mode === "automatic" ? "自动检索" : "模型主动检索"} · Top N ${value.topN === -1 ? "不限" : value.topN} · 最低相关度 ${value.scoreThreshold}`
}
className="flex h-5 w-5 items-center justify-center rounded-full text-muted-soft transition-colors hover:bg-surface-strong hover:text-foreground disabled:cursor-not-allowed disabled:opacity-40"
>
<Settings2 size={14} />
</button>
<Dialog open={open} onOpenChange={setOpen}>
<DialogContent className="sm:max-w-lg">
<DialogHeader>
<DialogTitle></DialogTitle>
<DialogDescription>
</DialogDescription>
</DialogHeader>
<div className="space-y-5 py-2">
<div className="space-y-2">
<div className="text-sm font-medium text-foreground"></div>
<Select
value={draft.mode}
onValueChange={(mode: "automatic" | "on_demand") =>
setDraft({ ...draft, mode })
}
>
<SelectTrigger className="w-full border-hairline-strong bg-background">
<SelectValue />
</SelectTrigger>
<SelectContent>
<SelectItem value="automatic"></SelectItem>
<SelectItem value="on_demand"></SelectItem>
</SelectContent>
</Select>
<p className="text-xs text-muted-foreground">
{draft.mode === "automatic"
? "每轮用户提问后自动检索,响应行为更稳定。"
: "由大模型判断是否调用知识库,依赖模型的工具调用能力。"}
</p>
</div>
<label className="block">
<span className="mb-2 block text-sm font-medium text-foreground">
</span>
<Input
type="number"
step="1"
min="-1"
value={draft.topN}
onChange={(event) =>
setDraft({ ...draft, topN: Number(event.target.value) })
}
/>
<span className="mt-1.5 block text-xs text-muted-foreground">
-1
</span>
</label>
<label className="block">
<span className="mb-2 block text-sm font-medium text-foreground">
</span>
<Input
type="number"
step="0.01"
min="0"
max="1"
value={draft.scoreThreshold}
onChange={(event) =>
setDraft({
...draft,
scoreThreshold: Number(event.target.value),
})
}
/>
<span className="mt-1.5 block text-xs text-muted-foreground">
01
</span>
</label>
{error && <p className="text-sm text-destructive">{error}</p>}
</div>
<DialogFooter>
<Button type="button" variant="outline" onClick={() => setOpen(false)}>
</Button>
<Button type="button" onClick={saveDraft}>
</Button>
</DialogFooter>
</DialogContent>
</Dialog>
</>
);
}