Simplify Agent settings and start conversations directly

This commit is contained in:
鲨鱼辣椒 committed 2026-10-12 19:38:57 +08:00
1 parent f7b1003c4c
commit 0a43ae901f
9 files changed
+402 -102

No files matched your search

+5 -27
View File
@@ -1,17 +1,15 @@
import { useEffect, useState } from 'react';
import { BookOpen, Brain, Library, Plug, RefreshCw, SlidersHorizontal, Users, Wrench } from 'lucide-react';
import { BookOpen, Brain, Library, Plug, RefreshCw, Users, Wrench } from 'lucide-react';
import { cloudAgentsApi } from '@/lib/cloud-agents-api';
import { Button } from '@/components/ui/button';
import { Input } from '@/components/ui/input';
import { CloudKnowledgePanel } from './CloudKnowledge';
import { CloudResources } from './CloudResources';
import { CloudBudgetEditor } from './CloudCosts';
import { AgentConfigHeading } from './AgentConfigHeading';
import type { CloudAgentConfiguration, CloudCatalog } from '../../../shared/cloud-agents';
const resourceSections = [
{ key: 'tools', label: '工具箱', title: '工具箱(Tool)', description: '选它做事时能用的工具。', icon: Wrench, tone: 'blue' },
{ key: 'skills', label: '做事手册', title: '做事手册(Skill)', description: '给它一本手册,照着方法做。', icon: BookOpen, tone: 'lilac' },
{ key: 'tools', label: '工具箱', title: '工具箱(Tool)', icon: Wrench, tone: 'blue' },
{ key: 'skills', label: '做事手册', title: '做事手册(Skill)', icon: BookOpen, tone: 'lilac' },
{ key: 'mcps', label: 'MCP 服务', title: 'MCP 服务', icon: Plug, tone: 'neutral' },
{ key: 'subagents', label: '子智能体', title: '子智能体', icon: Users, tone: 'neutral' },
{ key: 'knowledges', label: '知识库', title: '知识库', icon: Library, tone: 'mint' },
@@ -57,7 +55,7 @@ export function ConfigurationFields({ slug, value, disabled, onChange, section =
return <fieldset key={key} disabled={disabled} className="agent-config-card agent-resource-card" data-tone={tone}>
<legend className="sr-only">{title}</legend>
<div className="agent-config-section-top">
<AgentConfigHeading title={title} icon={icon} note={'已选 ' + value[key].length} description={'description' in item ? item.description : undefined} />
<AgentConfigHeading title={title} icon={icon} note={'已选 ' + value[key].length} />
<Button type="button" variant="outline" size="sm" onClick={() => { setChoosing(choosing === key ? null : key); setSearch(''); }}>{choosing === key ? '完成选择' : '选择' + label}</Button>
</div>
{choosing === key && <Input className="mb-3" aria-label={'搜索' + label} placeholder={'搜索' + label} value={search} onChange={e => setSearch(e.target.value)} />}
@@ -78,26 +76,6 @@ export function ConfigurationFields({ slug, value, disabled, onChange, section =
onCreated={kb => { change('knowledges', [...new Set([...value.knowledges, kb.kb_id])]); setRefresh(v => v + 1); }} /></div>}
</fieldset>;
})}
<div className="agent-config-card agent-config-resource-manager" data-tone="neutral">
<p className="mb-4 text-xs leading-5 text-muted-foreground">未选择的资源不会开放给此智能体。通过微信或 API 使用时也遵循本配置。</p>
<CloudResources onChanged={() => setRefresh(v => v + 1)} />
</div>
<div className="agent-config-budget"><CloudBudgetEditor slug={slug} /></div>
<section className="agent-config-card" data-tone="peach">
<AgentConfigHeading title="运行限制" icon={SlidersHorizontal} />
<p className="mb-4 text-xs leading-6 text-muted-foreground">以下设置随草稿保存;费用上限独立保存,对后续调用生效。执行步数会统计模型调用、工具、图节点和中间件步骤。</p>
<label className="block space-y-2 text-sm"><span>工具审批</span>
<select disabled={disabled} value={value.tool_approval_mode} onChange={e => change('tool_approval_mode', e.target.value === 'always_trust' ? 'always_trust' : 'default')}>
<option value="default">按工具要求确认</option><option value="always_trust">信任已选工具,自动执行</option>
</select></label>
<div className="agent-config-limit-grid">
{([{ key: 'max_execution_steps', label: '最多执行步数', min: 1, max: 300 },
{ key: 'max_output_tokens', label: '单次输出词元上限', min: 1, max: 32768 },
{ key: 'max_run_seconds', label: '单次运行时限(秒)', min: 10, max: 3600 }] as const).map(item =>
<label key={item.key} className="space-y-2 text-xs"><span>{item.label}</span>
<Input type="number" min={item.min} max={item.max} value={value[item.key]} disabled={disabled}
onChange={e => change(item.key, Number(e.target.value))} /></label>)}
</div>
</section>
<p className="text-xs leading-5 text-muted-foreground">未选择的资源不会开放给此智能体。通过微信或 API 使用时也遵循本配置。</p>
</div>;
}
+22 -19
View File
@@ -10,7 +10,6 @@ import { EMPTY_CLOUD_CONFIGURATION, type CloudAgentDraft, type CloudRecent } fro
import { ConfigurationFields } from './ConfigurationFields';
import { CloudChat } from './CloudChat';
import { CloudChannelConversations } from './CloudChannelConversations';
import { CloudAccessPanel } from './CloudAccess';
import { CloudPendingContext, useCloudPendingState } from './CloudPending';
import { AgentRequirements } from './AgentRequirements';
import { CloudAvatar, CloudAvatarEditor } from './CloudAvatar';
@@ -18,7 +17,7 @@ import { useCloudPublication } from './useCloudPublication';
import { AgentConfigHeading } from './AgentConfigHeading';
import './agent-config.css';
type Tab = 'configuration' | 'chat' | 'access';
type Tab = 'configuration' | 'chat';
const hasDraftChanges = (draft: CloudAgentDraft, saved: CloudAgentDraft) => draft.name !== saved.name || (draft.icon ?? null) !== (saved.icon ?? null) || draft.purpose !== saved.purpose || draft.system_prompt !== saved.system_prompt
|| JSON.stringify(draft.configuration) !== JSON.stringify(saved.configuration);
const errorMessage = (error: unknown) => error instanceof Error ? error.message : '暂时无法完成操作';
@@ -103,6 +102,20 @@ export function DraftEditor({ initial, recent, onBack, onSaved, startingPrompt,
} catch { /* Keep the failed save and local input available for retry. */ }
} finally { saving.current = false; if (alive.current) setBusy(false); }
};
const startUsing = () => {
if (busy || saving.current) return;
if (remote) { navigate('configuration'); setError('云端已有新修改,请先处理保存冲突。'); return; }
if (!draft.configuration.model) {
navigate('configuration');
setEditorTab('advanced');
if (configurationScroll.current) configurationScroll.current.scrollTop = 0;
setError('请先在高级设置中选择模型。'); return;
}
if (dirty) { navigate('configuration'); setError('请先保存草稿,再开始使用。'); return; }
if (!publication.access) { setError('暂时无法确认智能体状态,请刷新后重试。'); return; }
setError('');
void publication.publish();
};
useEffect(() => {
const shortcut = (event: KeyboardEvent) => {
if ((event.ctrlKey || event.metaKey) && event.key.toLowerCase() === 's') { event.preventDefault(); void save(); }
@@ -139,14 +152,10 @@ export function DraftEditor({ initial, recent, onBack, onSaved, startingPrompt,
{publication.busy && <Loader2 className="mr-2 h-4 w-4 animate-spin" />}
{publication.busy ? '正在应用…' : publication.pending ? '重试本次应用' : !dirty && publication.appliedRevision === saved.draft_revision ? '已应用' : '应用最新修改'}
</Button>}
{!saved.published_version && <Button className="min-h-10" variant="outline" disabled={busy} onClick={() => {
if (!draft.configuration.model) {
setEditorTab('advanced');
if (configurationScroll.current) configurationScroll.current.scrollTop = 0;
setError('请先在高级设置中选择模型。'); return;
}
setError(''); navigate('access');
}}>开始使用</Button>}
{!saved.published_version && <Button className="min-h-10" variant="outline" disabled={busy || remote !== null || !publication.access} onClick={startUsing}>
{publication.busy && <Loader2 className="mr-2 h-4 w-4 animate-spin" />}
{publication.busy ? '正在开始…' : publication.pending ? '重试开始使用' : '开始使用'}
</Button>}
</header>
{tab !== 'configuration' && <nav aria-label="智能体操作" className="agent-workspace-nav"><Button variant="ghost" onClick={() => {
if (formalPending.hasPending) { setError('对话中还有未完成的内容,请先发送或处理后再调整智能体。'); return; }
@@ -156,7 +165,7 @@ export function DraftEditor({ initial, recent, onBack, onSaved, startingPrompt,
<p className="font-medium">{dirty ? '修改尚未保存和应用' : '这次修改还没有应用到正在使用的助手'}</p>
<p className="mt-1 text-xs leading-5">保存只更新草稿。请点击顶部“应用最新修改”,再到微信等渠道使用。已开始的任务保持原配置。</p>
</div>}
{publication.error && tab !== 'access' && <p role="alert" className="shrink-0 px-6 py-2 text-sm text-destructive">{publication.error} <Button variant="ghost" disabled={busy} onClick={() => void publication.refresh()}>刷新应用状态</Button></p>}
{publication.error && <p role="alert" className="shrink-0 px-6 py-2 text-sm text-destructive">{publication.error} <Button variant="ghost" disabled={busy} onClick={() => void publication.refresh()}>刷新应用状态</Button></p>}
{error && <p role="alert" className="shrink-0 px-6 py-2 text-sm text-destructive">{error}</p>}
{remote && <div className="max-h-52 shrink-0 space-y-2 overflow-auto border-b border-amber-200 bg-amber-50 px-6 py-3 text-sm">
<p className="font-medium">云端已有修订 {remote.draft_revision},本地输入仍保留</p>
@@ -175,7 +184,7 @@ export function DraftEditor({ initial, recent, onBack, onSaved, startingPrompt,
<TabsTrigger value="advanced" className="flex-1 gap-2 data-[state=active]:text-primary"><SlidersHorizontal aria-hidden="true" className="h-4 w-4" />高级设置</TabsTrigger>
</TabsList>
</div>
{/* Keep the shared form mounted so switching tabs preserves unfinished resource and budget edits. */}
{/* Keep the shared form mounted so switching tabs preserves unfinished resource edits. */}
<TabsContent value={editorTab} className="m-0 flex min-h-0 flex-1 flex-col">
<div ref={configurationScroll} className="agent-config-scroll agent-editor-scroll"><fieldset disabled={busy} className="agent-configuration-form">
<div hidden={editorTab !== 'instructions'} className="agent-config-stack">
@@ -202,13 +211,7 @@ export function DraftEditor({ initial, recent, onBack, onSaved, startingPrompt,
<CloudChat key="published" slug={saved.slug} activeView={tab === 'chat' && conversationSource === 'desktop'} /></div>
<div hidden={conversationSource !== 'wechat'} className="min-h-0 flex-1 overflow-auto">{wechatVisited && <CloudChannelConversations slug={saved.slug} activeView={tab === 'chat' && conversationSource === 'wechat'} />}</div>
</CloudPendingContext.Provider>
: <div className="m-auto space-y-4 text-center text-sm"><p>保存配置后,开始使用这个智能体。</p><Button onClick={() => navigate('access')}>开始使用</Button></div>)}
</div>
<div hidden={tab !== 'access'} className="agent-management-view">
{dirty && <div className="mb-4 space-y-3 text-sm"><p>先保存配置,再开始使用。</p><Button disabled={!canSave} onClick={() => void save()}>保存修改</Button></div>}
<fieldset disabled={dirty || busy || remote !== null}>
{visited.has('access') && <CloudAccessPanel activeView={tab === 'access'} slug={saved.slug} revision={saved.draft_revision} publication={publication} onChanged={result => { setSaved(result); setDraft(current => !hasDraftChanges(current, saved) ? result : { ...current, draft_revision: result.draft_revision, published_version: result.published_version }); onSaved(result); }} />}
</fieldset>
: <div className="m-auto space-y-4 text-center text-sm"><p>保存配置后,开始使用这个智能体。</p><Button disabled={busy || remote !== null || !publication.access} onClick={startUsing}>{publication.pending ? '重试开始使用' : '开始使用'}</Button></div>)}
</div>
<Dialog open={editingAvatar} onOpenChange={open => { if (!uploading) setEditingAvatar(open); }}>
<DialogContent><DialogTitle>修改头像</DialogTitle><DialogDescription>选择这个智能体的头像。</DialogDescription>
+3 -9
View File
@@ -4,12 +4,11 @@
.agent-editor-tabs [role="tab"][data-state="active"] { color: #b73d16; box-shadow: 0 2px 8px #2732460a; }
.agent-editor-scroll { padding: 28px 32px 40px; background: #fffdf9; color: #273246; }
.agent-config-stack { display: flex; flex-direction: column; gap: 22px; min-width: 0; }
.agent-config-card, .agent-config-budget > section { min-width: 0; margin: 0; padding: 24px; border: 1px solid #dce5f3; border-radius: 24px; background: #f1f6ff; }
.agent-config-card { min-width: 0; margin: 0; padding: 24px; border: 1px solid #dce5f3; border-radius: 24px; background: #f1f6ff; }
.agent-config-card[data-tone="lilac"] { border-color: #e5dcf5; background: #f6f2ff; }
.agent-config-card[data-tone="peach"] { border-color: #f0e2ce; background: #fff5e9; }
.agent-config-card[data-tone="mint"] { border-color: #dce9e1; background: #f0f7f3; }
.agent-config-card[data-tone="neutral"] { border-color: #e6e3de; background: #fff; }
.agent-config-budget > section { border-color: #e6e3de; background: #fff; }
.agent-config-heading { display: flex; min-width: 0; align-items: center; gap: 13px; margin-bottom: 20px; }
.agent-config-icon { display: grid; width: 44px; height: 44px; flex-shrink: 0; place-items: center; border: 2px solid #fff; border-radius: 15px; background: #e0eafb; color: #4a6e9c; }
.agent-config-icon svg { width: 23px; height: 23px; stroke-width: 1.8; }
@@ -21,7 +20,7 @@
.agent-config-heading h2 { font-size: 18px; font-weight: 650; line-height: 1.5; }
.agent-config-note { padding: 3px 9px; border-radius: 20px; background: #fff9; font-size: 12px; line-height: 1.5; color: #62677b; white-space: nowrap; }
.agent-config-description { margin-top: 5px; font-size: 14px; line-height: 1.8; color: #59657a; }
.agent-config-card input:not([type="checkbox"]), .agent-config-card textarea, .agent-config-card select, .agent-config-budget input {
.agent-config-card input:not([type="checkbox"]), .agent-config-card textarea, .agent-config-card select {
width: 100%; min-width: 0; min-height: 48px; padding: 12px 15px; border: 1px solid #cdd5e3; border-radius: 13px; background: #fff; color: #273246; font-size: 15px; box-shadow: 0 2px 2px #27324603;
}
.agent-config-card input::placeholder, .agent-config-card textarea::placeholder { color: #6c7588; }
@@ -42,16 +41,11 @@
.agent-resource-preload { display: flex; min-height: 44px; align-items: center; gap: 9px; margin-top: 12px; cursor: pointer; font-size: 13px; }
.agent-config-resource-manager { margin-top: 18px; }
.agent-config-resource-manager > details { border-radius: 14px; background: #ffffffa8; }
.agent-config-stack > .agent-config-resource-manager { margin-top: 0; }
.agent-config-limit-grid { display: grid; grid-template-columns: repeat(auto-fit, minmax(min(100%, 200px), 1fr)); gap: 18px; margin-top: 20px; }
.agent-config-budget h2 { font-size: 18px; font-weight: 650; }
.agent-config-budget form > label { min-width: 0; flex: 1 1 240px; }
.agent-config-budget form > button { border-radius: 12px; }
@media (max-width: 640px) {
.agent-editor-tabs { padding: 10px 12px; }
.agent-editor-tabs [role="tab"] { gap: 6px; padding-inline: 8px; font-size: 14px; }
.agent-editor-scroll { padding: 18px 12px 28px; }
.agent-config-card, .agent-config-budget > section { padding: 18px; border-radius: 20px; }
.agent-config-card { padding: 18px; border-radius: 20px; }
.agent-config-stack { gap: 16px; }
.agent-config-heading { gap: 10px; }
.agent-config-heading h2 { font-size: 17px; }