feat: add support for Graph Tools in AIAgent model and related services

This commit is contained in:
mlogclub
2026-04-14 15:10:50 +08:00
parent 8d560bb6bd
commit aa7f94e08f
9 changed files with 213 additions and 19 deletions
+118 -12
View File
@@ -69,6 +69,11 @@ type DirectToolOption = {
groupLabel: string;
};
type GraphToolOption = {
value: string;
label: string;
};
type EditDialogProps = {
open: boolean;
saving: boolean;
@@ -150,6 +155,7 @@ function buildPayload(
teamIds: number[],
skillIds: number[],
directTools: CreateAIAgentPayload["directTools"],
graphTools: CreateAIAgentPayload["graphTools"],
): CreateAIAgentPayload {
return {
name: form.name.trim(),
@@ -165,6 +171,7 @@ function buildPayload(
knowledgeIds,
skillIds,
directTools,
graphTools,
remark: form.remark.trim(),
};
}
@@ -226,14 +233,19 @@ function EditDialogBody({
const [skillToAdd, setSkillToAdd] = useState("");
const [directToolGroupToAdd, setDirectToolGroupToAdd] = useState("");
const [directToolToAdd, setDirectToolToAdd] = useState("");
const [graphToolToAdd, setGraphToolToAdd] = useState("");
const [aiConfigs, setAIConfigs] = useState<AIConfig[]>([]);
const [knowledgeBases, setKnowledgeBases] = useState<KnowledgeBase[]>([]);
const [agentTeams, setAgentTeams] = useState<AdminAgentTeam[]>([]);
const [skills, setSkills] = useState<SkillDefinition[]>([]);
const [directTools, setDirectTools] = useState<DirectToolItem[]>([]);
const [graphTools, setGraphTools] = useState<string[]>([]);
const [directToolOptions, setDirectToolOptions] = useState<DirectToolOption[]>(
[],
);
const [graphToolOptions, setGraphToolOptions] = useState<GraphToolOption[]>(
[],
);
const [toolCatalog, setToolCatalog] = useState<MCPToolCatalogItem[]>([]);
useEffect(() => {
@@ -244,11 +256,13 @@ function EditDialogBody({
setSelectedTeamIds([]);
setSelectedSkillIds([]);
setDirectTools([]);
setGraphTools([]);
setKnowledgeToAdd("");
setTeamToAdd("");
setSkillToAdd("");
setDirectToolGroupToAdd("");
setDirectToolToAdd("");
setGraphToolToAdd("");
return;
}
setLoading(true);
@@ -259,11 +273,13 @@ function EditDialogBody({
setSelectedTeamIds((data.teams ?? []).map((team) => team.id));
setSelectedSkillIds(data.skillIds ?? []);
setDirectTools(data.directTools ?? []);
setGraphTools(data.graphTools ?? []);
setKnowledgeToAdd("");
setTeamToAdd("");
setSkillToAdd("");
setDirectToolGroupToAdd("");
setDirectToolToAdd("");
setGraphToolToAdd("");
} catch (error) {
toast.error(
error instanceof Error ? error.message : "加载 AI Agent 详情失败",
@@ -338,7 +354,7 @@ function EditDialogBody({
setToolCatalog(catalog);
setDirectToolOptions(
catalog
.filter((tool) => !tool.autoInjected)
.filter((tool) => !tool.autoInjected && tool.sourceType === "mcp")
.map((tool) => ({
value: tool.toolCode,
label: `${tool.title || tool.toolName} · ${tool.toolCode}`,
@@ -358,6 +374,14 @@ function EditDialogBody({
},
})),
);
setGraphToolOptions(
catalog
.filter((tool) => tool.sourceType === "graph")
.map((tool) => ({
value: tool.toolCode,
label: `${tool.title || tool.toolName} · ${tool.toolCode}`,
})),
);
} catch (error) {
toast.error(
error instanceof Error ? error.message : "加载 Direct Tools 失败",
@@ -470,10 +494,7 @@ function EditDialogBody({
const directToolsGrouped = useMemo(() => {
const groups = new Map<string, DirectToolItem[]>();
for (const tool of directTools) {
const groupLabel =
tool.serverCode === "builtin" || tool.toolCode.startsWith("builtin/")
? "内置工具"
: tool.serverCode || "未分组";
const groupLabel = tool.serverCode || "未分组";
const current = groups.get(groupLabel) ?? [];
current.push(tool);
groups.set(groupLabel, current);
@@ -481,6 +502,14 @@ function EditDialogBody({
return Array.from(groups.entries());
}, [directTools]);
const addableGraphToolOptions = useMemo(
() =>
graphToolOptions.filter(
(option) => !graphTools.includes(option.value),
),
[graphToolOptions, graphTools],
);
const addableTeamOptions = useMemo(
() =>
teamOptions.filter(
@@ -512,6 +541,7 @@ function EditDialogBody({
selectedTeamIds,
selectedSkillIds,
directTools,
graphTools,
),
);
}
@@ -593,6 +623,18 @@ function EditDialogBody({
setDirectTools((prev) => prev.filter((item) => item.toolCode !== value));
}
function handleAddGraphTool(value: string) {
if (!value || graphTools.includes(value)) {
return;
}
setGraphTools((prev) => [...prev, value]);
setGraphToolToAdd("");
}
function handleRemoveGraphTool(value: string) {
setGraphTools((prev) => prev.filter((item) => item !== value));
}
return (
<ProjectDialog
open={open}
@@ -700,9 +742,9 @@ function EditDialogBody({
<SectionCard
title="能力配置"
description="知识库用于 RAGSkills 用于业务流程,Direct Tools 用于低风险实时查询。"
description="知识库用于 RAGSkills 用于业务流程,Direct Tools 用于外部 MCP 查询,Graph Tools 用于内置业务流程。"
>
<div className="grid gap-4 xl:grid-cols-3">
<div className="grid gap-4 xl:grid-cols-4">
<div className="rounded-xl border bg-muted/10 p-4">
<div className="mb-1 text-sm font-medium"></div>
<div className="mb-4 text-xs text-muted-foreground">
@@ -854,8 +896,7 @@ function EditDialogBody({
<div className="rounded-xl border bg-muted/10 p-4">
<div className="mb-1 text-sm font-medium">Direct Tools</div>
<div className="mb-4 text-xs text-muted-foreground">
MCP
`tool_search` Runtime
MCP
</div>
<Field>
<FieldContent className="space-y-3">
@@ -923,9 +964,7 @@ function EditDialogBody({
>
{tool.title || catalogItem?.title || value}
<span className="text-[10px] text-muted-foreground/80">
{catalogItem?.sourceType === "builtin"
? "内置"
: tool.serverCode || "MCP"}
{tool.serverCode || "MCP"}
</span>
<Button
type="button"
@@ -950,6 +989,73 @@ function EditDialogBody({
</FieldContent>
</Field>
</div>
<div className="rounded-xl border bg-muted/10 p-4">
<div className="mb-1 text-sm font-medium">Graph Tools</div>
<div className="mb-4 text-xs text-muted-foreground">
Direct Tools
</div>
<Field>
<FieldContent className="space-y-3">
<div className="flex items-center gap-2">
<div className="flex-1">
<OptionCombobox
value={graphToolToAdd}
options={addableGraphToolOptions}
placeholder="选择 Graph Tool"
searchPlaceholder="搜索 Graph Tool"
emptyText="没有可添加的 Graph Tool"
onChange={handleAddGraphTool}
/>
</div>
<Button
type="button"
variant="outline"
disabled={!graphToolToAdd}
onClick={() => handleAddGraphTool(graphToolToAdd)}
>
<PlusIcon />
</Button>
</div>
<div className="flex flex-wrap gap-2">
{graphTools.length === 0 ? (
<span className="text-sm text-muted-foreground">
Graph Tool Agent /
</span>
) : (
graphTools.map((toolCode) => {
const catalogItem = toolCatalog.find(
(item) => item.toolCode === toolCode,
);
return (
<Badge
key={toolCode}
variant="secondary"
className="gap-1 pr-1"
>
{catalogItem?.title || toolCode}
<span className="text-[10px] text-muted-foreground/80">
graph
</span>
<Button
type="button"
variant="ghost"
size="icon"
className="size-5"
onClick={() => handleRemoveGraphTool(toolCode)}
aria-label={`移除 Graph Tool ${toolCode}`}
>
<Trash2Icon className="size-3" />
</Button>
</Badge>
);
})
)}
</div>
</FieldContent>
</Field>
</div>
</div>
</SectionCard>
+2
View File
@@ -220,6 +220,7 @@ export type AIAgent = {
description: string
arguments?: Record<string, string>
}[]
graphTools: string[]
sortNo: number
remark: string
createdAt: string
@@ -249,6 +250,7 @@ export type CreateAIAgentPayload = {
description: string
arguments?: Record<string, string>
}[]
graphTools: string[]
remark: string
}