feat: enhance AI agent functionality with tool management and logging improvements

- Added AllowedToolCodes field to Context for managing tool access.
- Introduced ResumeSource in aiReplyTraceData to track resume points.
- Enhanced logging with additional fields: PlannedSkillName, SkillRouteTrace, InterruptType, ResumeSource, and FinalStatus.
- Implemented functions to parse and resolve allowed tool codes for agents and skills.
- Refactored skill definition creation and update logic to streamline request handling.
- Updated MCP tool catalog to include source type and integrated built-in tools.
- Improved UI components to display additional tool information and enhance user experience.
This commit is contained in:
mlogclub
2026-04-10 14:43:57 +08:00
parent 7411bac57d
commit 1370e4b675
23 changed files with 642 additions and 222 deletions
+25 -8
View File
@@ -229,8 +229,8 @@ export default function DashboardAgentRunLogsPage() {
<TableHead className="w-[180px]"></TableHead>
<TableHead></TableHead>
<TableHead className="w-[120px]"></TableHead>
<TableHead className="w-[180px]">Skill / Tool</TableHead>
<TableHead className="w-[120px]"></TableHead>
<TableHead className="w-[220px]">Skill / Tool</TableHead>
<TableHead className="w-[140px]"></TableHead>
<TableHead className="w-[110px] text-right"></TableHead>
<TableHead className="w-[96px] text-right"></TableHead>
</TableRow>
@@ -265,17 +265,29 @@ export default function DashboardAgentRunLogsPage() {
</TableCell>
<TableCell className="text-sm">
{item.plannedSkillCode || item.plannedToolCode ? (
<Badge variant="outline">
{item.plannedSkillCode || item.plannedToolCode}
</Badge>
<div className="space-y-1">
<Badge variant="outline">
{item.plannedSkillCode || item.plannedToolCode}
</Badge>
{item.plannedSkillName ? (
<div className="line-clamp-1 text-xs text-muted-foreground">
{item.plannedSkillName}
</div>
) : null}
</div>
) : (
"-"
)}
</TableCell>
<TableCell>
<Badge variant={actionBadgeVariant(item.finalAction)}>
{item.finalAction || "-"}
</Badge>
<div className="space-y-1">
<Badge variant={actionBadgeVariant(item.finalAction)}>
{item.finalAction || "-"}
</Badge>
<div className="text-xs text-muted-foreground">
{item.finalStatus || "-"}
</div>
</div>
</TableCell>
<TableCell className="text-right text-sm text-muted-foreground">
{item.latencyMs} ms
@@ -342,14 +354,19 @@ export default function DashboardAgentRunLogsPage() {
lines={[
`plannedAction: ${activeLog.plannedAction || "-"}`,
`plannedSkillCode: ${activeLog.plannedSkillCode || "-"}`,
`plannedSkillName: ${activeLog.plannedSkillName || "-"}`,
`plannedToolCode: ${activeLog.plannedToolCode || "-"}`,
`planReason: ${activeLog.planReason || "-"}`,
`skillRouteTrace: ${activeLog.skillRouteTrace || "-"}`,
]}
/>
<InfoBlock
title="执行结果"
lines={[
`finalAction: ${activeLog.finalAction || "-"}`,
`finalStatus: ${activeLog.finalStatus || "-"}`,
`interruptType: ${activeLog.interruptType || "-"}`,
`resumeSource: ${activeLog.resumeSource || "-"}`,
`latencyMs: ${activeLog.latencyMs} ms`,
`createdAt: ${formatDateTime(activeLog.createdAt)}`,
]}
@@ -59,6 +59,16 @@ import {
import { getEnumOptions } from "@/lib/enums";
import { FieldDescription } from "@base-ui/react";
type DirectToolItem = CreateAIAgentPayload["directTools"][number];
type DirectToolOption = {
value: string;
label: string;
meta: DirectToolItem;
sourceType: string;
groupLabel: string;
};
type EditDialogProps = {
open: boolean;
saving: boolean;
@@ -231,18 +241,16 @@ function EditDialogBody({
const [knowledgeToAdd, setKnowledgeToAdd] = useState("");
const [teamToAdd, setTeamToAdd] = useState("");
const [skillToAdd, setSkillToAdd] = useState("");
const [directToolServerCodeToAdd, setDirectToolServerCodeToAdd] = useState("");
const [directToolGroupToAdd, setDirectToolGroupToAdd] = useState("");
const [directToolToAdd, setDirectToolToAdd] = 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<
CreateAIAgentPayload["directTools"]
>([]);
const [directToolOptions, setDirectToolOptions] = useState<
{ value: string; label: string; meta: CreateAIAgentPayload["directTools"][number] }[]
>([]);
const [directTools, setDirectTools] = useState<DirectToolItem[]>([]);
const [directToolOptions, setDirectToolOptions] = useState<DirectToolOption[]>(
[],
);
const [toolCatalog, setToolCatalog] = useState<MCPToolCatalogItem[]>([]);
useEffect(() => {
@@ -256,7 +264,7 @@ function EditDialogBody({
setKnowledgeToAdd("");
setTeamToAdd("");
setSkillToAdd("");
setDirectToolServerCodeToAdd("");
setDirectToolGroupToAdd("");
setDirectToolToAdd("");
return;
}
@@ -271,7 +279,7 @@ function EditDialogBody({
setKnowledgeToAdd("");
setTeamToAdd("");
setSkillToAdd("");
setDirectToolServerCodeToAdd("");
setDirectToolGroupToAdd("");
setDirectToolToAdd("");
} catch (error) {
toast.error(
@@ -349,6 +357,11 @@ function EditDialogBody({
catalog.map((tool) => ({
value: tool.toolCode,
label: `${tool.title || tool.toolName} · ${tool.toolCode}`,
sourceType: tool.sourceType,
groupLabel:
tool.sourceType === "builtin"
? "内置工具"
: tool.serverCode,
meta: {
toolCode: tool.toolCode,
serverCode: tool.serverCode,
@@ -446,21 +459,21 @@ function EditDialogBody({
() =>
directToolOptions.filter(
(option) =>
option.meta.serverCode === directToolServerCodeToAdd &&
option.groupLabel === directToolGroupToAdd &&
!directTools.some((tool) => tool.toolCode === option.value),
),
[directToolOptions, directToolServerCodeToAdd, directTools],
[directToolOptions, directToolGroupToAdd, directTools],
);
const directToolServerOptions = useMemo(
const directToolGroupOptions = useMemo(
() =>
Array.from(
new Map(
directToolOptions.map((option) => [
option.meta.serverCode,
option.groupLabel,
{
value: option.meta.serverCode,
label: option.meta.serverCode,
value: option.groupLabel,
label: option.groupLabel,
},
]),
).values(),
@@ -468,15 +481,16 @@ function EditDialogBody({
[directToolOptions],
);
const directToolsGroupedByServer = useMemo(() => {
const groups = new Map<
string,
CreateAIAgentPayload["directTools"]
>();
const directToolsGrouped = useMemo(() => {
const groups = new Map<string, DirectToolItem[]>();
for (const tool of directTools) {
const current = groups.get(tool.serverCode) ?? [];
const groupLabel =
tool.serverCode === "builtin" || tool.toolCode.startsWith("builtin/")
? "内置工具"
: tool.serverCode || "未分组";
const current = groups.get(groupLabel) ?? [];
current.push(tool);
groups.set(tool.serverCode, current);
groups.set(groupLabel, current);
}
return Array.from(groups.entries());
}, [directTools]);
@@ -585,7 +599,7 @@ function EditDialogBody({
}
return [...prev, option.meta];
});
setDirectToolServerCodeToAdd(option.meta.serverCode);
setDirectToolGroupToAdd(option.groupLabel);
setDirectToolToAdd("");
}
@@ -852,22 +866,22 @@ function EditDialogBody({
</div>
<div className="rounded-xl border bg-muted/10 p-4">
<div className="mb-1 text-sm font-medium">Direct MCP Tools</div>
<div className="mb-1 text-sm font-medium">Direct Tools</div>
<div className="mb-4 text-xs text-muted-foreground">
MCP Tool Catalog
MCP
</div>
<Field>
<FieldContent className="space-y-3">
<div className="flex items-center gap-2">
<div className="w-52">
<OptionCombobox
value={directToolServerCodeToAdd}
options={directToolServerOptions}
placeholder="选择 MCP Server"
searchPlaceholder="搜索 MCP Server"
emptyText="没有可用的 MCP Server"
value={directToolGroupToAdd}
options={directToolGroupOptions}
placeholder="选择工具分组"
searchPlaceholder="搜索工具分组"
emptyText="没有可用的工具分组"
onChange={(value) => {
setDirectToolServerCodeToAdd(value);
setDirectToolGroupToAdd(value);
setDirectToolToAdd("");
}}
/>
@@ -876,7 +890,7 @@ function EditDialogBody({
<OptionCombobox
value={directToolToAdd}
options={addableDirectToolOptions}
placeholder="选择该 Server 下的 Direct Tool"
placeholder="选择 Direct Tool"
searchPlaceholder="搜索 Direct Tool"
emptyText="没有可添加的 Direct Tool"
onChange={handleAddDirectTool}
@@ -886,7 +900,7 @@ function EditDialogBody({
type="button"
variant="outline"
disabled={
!directToolServerCodeToAdd || !directToolToAdd
!directToolGroupToAdd || !directToolToAdd
}
onClick={() => handleAddDirectTool(directToolToAdd)}
>
@@ -897,16 +911,16 @@ function EditDialogBody({
<div className="space-y-3">
{directTools.length === 0 ? (
<span className="text-sm text-muted-foreground">
Direct Tool Agent 访 MCP Skill
Direct Tool Agent Skill
</span>
) : (
directToolsGroupedByServer.map(([serverCode, tools]) => (
directToolsGrouped.map(([groupLabel, tools]) => (
<div
key={serverCode}
key={groupLabel}
className="rounded-md border p-3"
>
<div className="mb-2 text-xs font-medium text-muted-foreground">
{serverCode}
{groupLabel}
</div>
<div className="flex flex-wrap gap-2">
{tools.map((tool) => {
@@ -921,6 +935,11 @@ function EditDialogBody({
className="gap-1 pr-1"
>
{tool.title || catalogItem?.title || value}
<span className="text-[10px] text-muted-foreground/80">
{catalogItem?.sourceType === "builtin"
? "内置"
: tool.serverCode || "MCP"}
</span>
<Button
type="button"
variant="ghost"
+6
View File
@@ -333,6 +333,7 @@ export type MCPToolCatalogItem = {
toolCode: string
serverCode: string
toolName: string
sourceType: string
title: string
description: string
inputSchema: unknown
@@ -362,9 +363,14 @@ export type AgentRunLog = {
userMessage: string
plannedAction: string
plannedSkillCode: string
plannedSkillName: string
skillRouteTrace: string
plannedToolCode: string
planReason: string
interruptType: string
resumeSource: string
finalAction: string
finalStatus: string
replyText: string
errorMessage: string
latencyMs: number