'use client'; import React, { useCallback, useEffect, useState } from 'react'; import { Bot, Plus, RefreshCw, Trash2, Loader2, AlertCircle, Tags, ChevronDown } from 'lucide-react'; import { addAgent, deleteAgent, listAgents, refreshAgents } from '@/lib/api'; import { useChatStore } from '@/lib/store'; import type { UiAgentDescriptor } from '@/types'; import { Badge } from '@/components/ui/badge'; import { Button } from '@/components/ui/button'; import { Card, CardContent, CardHeader, CardTitle } from '@/components/ui/card'; import { Collapsible, CollapsibleContent, CollapsibleTrigger } from '@/components/ui/collapsible'; import { Dialog, DialogContent, DialogHeader, DialogTitle, DialogTrigger } from '@/components/ui/dialog'; import { Input } from '@/components/ui/input'; import { Label } from '@/components/ui/label'; import { Textarea } from '@/components/ui/textarea'; const EMPTY_FORM = { id: '', name: '', description: '', base_url: '', endpoint: '', card_url: '', auth_env: '', auth_mode: 'none', auth_audience: '', auth_scopes: '', tags: '', aliases: '', }; export default function AgentsPage() { const cachedAgents = useChatStore((s) => s.agentRegistry); const setCachedAgents = useChatStore((s) => s.setAgentRegistry); const [agents, setAgents] = useState(cachedAgents); const [loading, setLoading] = useState(cachedAgents.length === 0); const [refreshing, setRefreshing] = useState(false); const [error, setError] = useState(null); const [dialogOpen, setDialogOpen] = useState(false); const [submitting, setSubmitting] = useState(false); const [advancedOpen, setAdvancedOpen] = useState(false); const [form, setForm] = useState(EMPTY_FORM); const load = useCallback(async (background = false) => { if (background) { setRefreshing(true); } else { setLoading(true); } setError(null); try { const data = await listAgents(); const nextAgents = Array.isArray(data) ? data : []; setAgents(nextAgents); setCachedAgents(nextAgents); } catch (err: any) { setError(err.message || '加载智能体失败'); } finally { if (background) { setRefreshing(false); } else { setLoading(false); } } }, [setCachedAgents]); useEffect(() => { void load(cachedAgents.length > 0); }, [cachedAgents.length, load]); const handleRefresh = async () => { setError(null); setRefreshing(true); try { const data = await refreshAgents(); const nextAgents = data.agents || []; setAgents(nextAgents); setCachedAgents(nextAgents); } catch (err: any) { setError(err.message || '刷新智能体失败'); } finally { setRefreshing(false); } }; const handleDialogOpenChange = (open: boolean) => { setDialogOpen(open); if (!open) { setAdvancedOpen(false); setForm(EMPTY_FORM); } }; const handleCreate = async (e: React.FormEvent) => { e.preventDefault(); const hasAddress = [form.base_url, form.endpoint, form.card_url].some((value) => value.trim()); if (!hasAddress) { setError('请至少填写 A2A 部署地址、接口地址或卡片地址'); return; } setSubmitting(true); setError(null); try { await addAgent({ id: form.id || undefined, name: form.name || undefined, description: form.description || undefined, protocol: 'a2a', base_url: form.base_url || undefined, endpoint: form.endpoint || undefined, card_url: form.card_url || undefined, auth_env: form.auth_env || undefined, auth_mode: form.auth_mode || 'none', auth_audience: form.auth_mode === 'none' ? undefined : form.auth_audience || undefined, auth_scopes: form.auth_mode === 'none' ? [] : form.auth_scopes.split(',').map((item) => item.trim()).filter(Boolean), tags: form.tags.split(',').map((item) => item.trim()).filter(Boolean), aliases: form.aliases.split(',').map((item) => item.trim()).filter(Boolean), }); handleDialogOpenChange(false); await load(); } catch (err: any) { setError(err.message || '新增智能体失败'); } finally { setSubmitting(false); } }; const handleDelete = async (agentId: string) => { try { await deleteAgent(agentId); await load(); } catch (err: any) { setError(err.message || '删除智能体失败'); } }; if (loading) { return (
); } return (

智能体

管理工作区智能体,并查看来自插件、技能和内置能力的可委派目标。

新增工作区智能体
setForm((s) => ({ ...s, base_url: e.target.value }))} placeholder="https://agent.example.com 或 agent.example.com:19090" />

默认只需要填写部署地址。保存时会自动读取 /.well-known/agent-card/.well-known/agent-card.json/.well-known/agent.json ,并补齐 ID、名称、描述、接口地址等信息。

setForm((s) => ({ ...s, id: e.target.value }))} placeholder="留空则从 A2A card 自动生成" />
setForm((s) => ({ ...s, name: e.target.value }))} placeholder="留空则从 A2A card 自动填充" />