feat(frontend): 重设计Common Agent Studio原型
This commit is contained in:
@@ -1,6 +1,6 @@
|
||||
<template>
|
||||
<main class="not-found">
|
||||
<h1>404</h1>
|
||||
<RouterLink to="/rag/workbench">返回RAG工作台</RouterLink>
|
||||
<RouterLink to="/studio">返回工作台</RouterLink>
|
||||
</main>
|
||||
</template>
|
||||
|
||||
69
frontend/src/pages/studio/AgentWorkspacePage.vue
Normal file
69
frontend/src/pages/studio/AgentWorkspacePage.vue
Normal file
@@ -0,0 +1,69 @@
|
||||
<script setup lang="ts">
|
||||
import { ChatDotRound, Coin, Timer } from '@element-plus/icons-vue';
|
||||
|
||||
import { chatMessages, citations, traceSteps } from '@/data/studioMock';
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<section class="studio-page agent-page">
|
||||
<header class="page-title-row">
|
||||
<div>
|
||||
<p class="studio-kicker">AgentWorkspaceView</p>
|
||||
<h1>Agent 对话调试</h1>
|
||||
</div>
|
||||
<el-button type="primary">发布 Agent</el-button>
|
||||
</header>
|
||||
|
||||
<div class="agent-layout">
|
||||
<section class="studio-panel chat-panel">
|
||||
<div class="panel-heading">
|
||||
<div>
|
||||
<h2>售前问答 Agent</h2>
|
||||
<span>POST /api/agents/1001/runs</span>
|
||||
</div>
|
||||
<el-tag>Draft</el-tag>
|
||||
</div>
|
||||
<div class="message-list">
|
||||
<article v-for="message in chatMessages" :key="message.content" :class="message.role">
|
||||
<strong>{{ message.role === 'user' ? '用户' : 'Agent' }}</strong>
|
||||
<p>{{ message.content }}</p>
|
||||
</article>
|
||||
</div>
|
||||
<div class="chat-composer">
|
||||
<span>输入调试问题,运行会写入 agent_session / agent_message 草案</span>
|
||||
<el-button type="primary"><el-icon><ChatDotRound /></el-icon> 发送</el-button>
|
||||
</div>
|
||||
</section>
|
||||
|
||||
<aside class="studio-panel citation-panel">
|
||||
<div class="panel-heading compact">
|
||||
<h2>引用切片</h2>
|
||||
<span>3 个来源</span>
|
||||
</div>
|
||||
<article v-for="citation in citations" :key="citation.title" class="citation-card">
|
||||
<strong>{{ citation.title }}</strong>
|
||||
<el-tag type="success">score {{ citation.score }}</el-tag>
|
||||
<p>{{ citation.text }}</p>
|
||||
</article>
|
||||
</aside>
|
||||
|
||||
<aside class="studio-panel run-inspector">
|
||||
<div class="panel-heading compact">
|
||||
<h2>运行追踪</h2>
|
||||
<span>modelRequestId: f4215d</span>
|
||||
</div>
|
||||
<div class="metric-mini">
|
||||
<span><el-icon><Timer /></el-icon> 1.42s</span>
|
||||
<span><el-icon><Coin /></el-icon> ¥0.018</span>
|
||||
<span>1,248 tokens</span>
|
||||
</div>
|
||||
<ol class="log-list">
|
||||
<li v-for="step in traceSteps" :key="step.node">
|
||||
<time>{{ step.duration }}</time>
|
||||
<span>{{ step.node }} · {{ step.output }}</span>
|
||||
</li>
|
||||
</ol>
|
||||
</aside>
|
||||
</div>
|
||||
</section>
|
||||
</template>
|
||||
71
frontend/src/pages/studio/IngestionPipelinePage.vue
Normal file
71
frontend/src/pages/studio/IngestionPipelinePage.vue
Normal file
@@ -0,0 +1,71 @@
|
||||
<script setup lang="ts">
|
||||
import { UploadFilled } from '@element-plus/icons-vue';
|
||||
|
||||
import { ingestionSteps } from '@/data/studioMock';
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<section class="studio-page ingestion-page">
|
||||
<header class="page-title-row">
|
||||
<div>
|
||||
<p class="studio-kicker">IngestionPipelineView</p>
|
||||
<h1>文件解析管道</h1>
|
||||
</div>
|
||||
<el-button type="primary">启动索引任务</el-button>
|
||||
</header>
|
||||
|
||||
<div class="ingestion-layout">
|
||||
<section class="studio-panel upload-panel">
|
||||
<div class="upload-dropzone">
|
||||
<el-icon><UploadFilled /></el-icon>
|
||||
<strong>拖拽文件到这里</strong>
|
||||
<span>支持 PDF / Word / Excel / Markdown / TXT,上传后自动创建 ingestion run。</span>
|
||||
<el-button type="primary">选择文件</el-button>
|
||||
</div>
|
||||
<div class="pipeline-timeline">
|
||||
<article v-for="step in ingestionSteps" :key="step.name" :class="`is-${step.status}`">
|
||||
<div class="timeline-dot" />
|
||||
<strong>{{ step.name }}</strong>
|
||||
<span>{{ step.description }}</span>
|
||||
</article>
|
||||
</div>
|
||||
</section>
|
||||
|
||||
<section class="studio-panel preview-panel">
|
||||
<div class="panel-heading">
|
||||
<h2>解析与切片预览</h2>
|
||||
<span>GET /api/knowledge/ingestion-runs/run-20260531</span>
|
||||
</div>
|
||||
<div class="preview-split">
|
||||
<article>
|
||||
<h3>解析文本</h3>
|
||||
<p>私有化部署章节应覆盖基础设施、网络、安全与运维边界。平台需说明模型服务商、知识库索引策略与日志留存周期...</p>
|
||||
</article>
|
||||
<article>
|
||||
<h3>切片 #24</h3>
|
||||
<p>chunk_size=800, overlap=120, strategy=FIXED_LENGTH。该切片将进入 rag_chunk 并在向量化后写入 rag_chunk_embedding。</p>
|
||||
</article>
|
||||
</div>
|
||||
<div class="pipeline-controls">
|
||||
<label>切片策略 <strong>固定长度</strong></label>
|
||||
<label>Chunk Size <strong>800</strong></label>
|
||||
<label>Overlap <strong>120</strong></label>
|
||||
<label>Embedding <strong>Qwen3 1024d</strong></label>
|
||||
</div>
|
||||
</section>
|
||||
|
||||
<aside class="studio-panel task-log-panel">
|
||||
<div class="panel-heading compact">
|
||||
<h2>任务日志</h2>
|
||||
<span>run-20260531</span>
|
||||
</div>
|
||||
<ol class="log-list">
|
||||
<li><time>23:08:12</time><span>上传 4 个文件并创建 rag_document</span></li>
|
||||
<li><time>23:08:24</time><span>Tika 解析完成 3 个文件</span></li>
|
||||
<li class="warn"><time>23:08:31</time><span>服务条款更新.md 编码检测失败,等待重试</span></li>
|
||||
<li><time>23:08:40</time><span>切片任务进行中 68 / 119</span></li>
|
||||
</ol>
|
||||
</aside>
|
||||
</div>
|
||||
</section>
|
||||
</template>
|
||||
94
frontend/src/pages/studio/KnowledgeWorkspacePage.vue
Normal file
94
frontend/src/pages/studio/KnowledgeWorkspacePage.vue
Normal file
@@ -0,0 +1,94 @@
|
||||
<script setup lang="ts">
|
||||
import { DataAnalysis, Document, Setting } from '@element-plus/icons-vue';
|
||||
|
||||
import { knowledgeDocuments, knowledgeStores } from '@/data/studioMock';
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<section class="studio-page workspace-page">
|
||||
<header class="page-title-row">
|
||||
<div>
|
||||
<p class="studio-kicker">KnowledgeWorkspaceView</p>
|
||||
<h1>知识资产</h1>
|
||||
</div>
|
||||
<el-button type="primary">新建知识库</el-button>
|
||||
</header>
|
||||
|
||||
<div class="three-column-layout">
|
||||
<aside class="studio-panel collection-rail">
|
||||
<div class="panel-heading compact">
|
||||
<h2>知识集合</h2>
|
||||
<span>{{ knowledgeStores.length }} 个库</span>
|
||||
</div>
|
||||
<button
|
||||
v-for="store in knowledgeStores"
|
||||
:key="store.id"
|
||||
class="collection-item"
|
||||
:class="{ active: store.id === '1001' }"
|
||||
>
|
||||
<strong>{{ store.name }}</strong>
|
||||
<span>{{ store.docs }} 文档 · 健康度 {{ store.health }}%</span>
|
||||
<em>{{ store.status }}</em>
|
||||
</button>
|
||||
</aside>
|
||||
|
||||
<main class="studio-panel knowledge-main">
|
||||
<div class="panel-heading">
|
||||
<div>
|
||||
<h2>产品制度库</h2>
|
||||
<span>绑定旧数据语义:rag_store / rag_document / rag_chunk_embedding</span>
|
||||
</div>
|
||||
<el-tag type="success">可检索</el-tag>
|
||||
</div>
|
||||
|
||||
<div class="config-grid">
|
||||
<article>
|
||||
<el-icon><Setting /></el-icon>
|
||||
<strong>Embedding 模型</strong>
|
||||
<span>Qwen3-Embedding · 1024 维</span>
|
||||
</article>
|
||||
<article>
|
||||
<el-icon><DataAnalysis /></el-icon>
|
||||
<strong>检索配置</strong>
|
||||
<span>TopK 6 · Score ≥ 0.72 · Rerank 关闭</span>
|
||||
</article>
|
||||
<article>
|
||||
<el-icon><Document /></el-icon>
|
||||
<strong>索引版本</strong>
|
||||
<span>index_version 14 · Draft 快照</span>
|
||||
</article>
|
||||
</div>
|
||||
|
||||
<div class="document-table">
|
||||
<div class="table-row table-head">
|
||||
<span>文档</span><span>解析</span><span>索引</span><span>切片</span><span>更新</span>
|
||||
</div>
|
||||
<div v-for="doc in knowledgeDocuments" :key="doc.id" class="table-row">
|
||||
<strong>{{ doc.name }}</strong>
|
||||
<span>{{ doc.parseStatus }}</span>
|
||||
<span>{{ doc.indexStatus }}</span>
|
||||
<span>{{ doc.chunks }}</span>
|
||||
<span>{{ doc.updatedAt }}</span>
|
||||
</div>
|
||||
</div>
|
||||
</main>
|
||||
|
||||
<aside class="studio-panel inspector-panel">
|
||||
<div class="panel-heading compact">
|
||||
<h2>状态 Inspector</h2>
|
||||
<span>聚合接口</span>
|
||||
</div>
|
||||
<dl class="inspector-list">
|
||||
<dt>Workspace API</dt>
|
||||
<dd>GET /api/knowledge/workspaces/1001</dd>
|
||||
<dt>文档健康度</dt>
|
||||
<dd>96% · 1 个解析失败</dd>
|
||||
<dt>待处理任务</dt>
|
||||
<dd>2 个文档等待向量化</dd>
|
||||
<dt>发布影响</dt>
|
||||
<dd>更新后需要 Workflow 重新验证引用质量</dd>
|
||||
</dl>
|
||||
</aside>
|
||||
</div>
|
||||
</section>
|
||||
</template>
|
||||
49
frontend/src/pages/studio/McpImportPage.vue
Normal file
49
frontend/src/pages/studio/McpImportPage.vue
Normal file
@@ -0,0 +1,49 @@
|
||||
<script setup lang="ts">
|
||||
import { Link, Upload } from '@element-plus/icons-vue';
|
||||
|
||||
import { mcpCapabilities } from '@/data/studioMock';
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<section class="studio-page mcp-page">
|
||||
<header class="page-title-row">
|
||||
<div>
|
||||
<p class="studio-kicker">McpImportView</p>
|
||||
<h1>MCP 导入</h1>
|
||||
</div>
|
||||
<el-button type="primary"><el-icon><Upload /></el-icon> 导入 Server</el-button>
|
||||
</header>
|
||||
|
||||
<div class="mcp-layout">
|
||||
<section class="studio-panel import-panel">
|
||||
<div class="panel-heading">
|
||||
<h2>外部能力接入</h2>
|
||||
<span>POST /api/mcp/import</span>
|
||||
</div>
|
||||
<div class="import-options">
|
||||
<button class="active"><el-icon><Link /></el-icon><strong>URL</strong><span>https://mcp.example.com/sse</span></button>
|
||||
<button><strong>npm package</strong><span>@acme/mcp-jira</span></button>
|
||||
<button><strong>JSON Manifest</strong><span>粘贴 server 能力声明</span></button>
|
||||
</div>
|
||||
<div class="manifest-box">
|
||||
<span>{ "server": "jira", "transport": "sse", "auth": "oauth2" }</span>
|
||||
</div>
|
||||
</section>
|
||||
|
||||
<section class="studio-panel capability-panel">
|
||||
<div class="panel-heading">
|
||||
<h2>能力预览</h2>
|
||||
<span>GET /api/mcp/servers/jira/capabilities</span>
|
||||
</div>
|
||||
<div class="capability-grid">
|
||||
<article v-for="item in mcpCapabilities" :key="item.name">
|
||||
<el-tag>{{ item.type }}</el-tag>
|
||||
<strong>{{ item.name }}</strong>
|
||||
<p>{{ item.description }}</p>
|
||||
<span>{{ item.status }}</span>
|
||||
</article>
|
||||
</div>
|
||||
</section>
|
||||
</div>
|
||||
</section>
|
||||
</template>
|
||||
34
frontend/src/pages/studio/ModelWorkspacePage.vue
Normal file
34
frontend/src/pages/studio/ModelWorkspacePage.vue
Normal file
@@ -0,0 +1,34 @@
|
||||
<script setup lang="ts">
|
||||
import { modelRoutes } from '@/data/studioMock';
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<section class="studio-page model-page">
|
||||
<header class="page-title-row">
|
||||
<div>
|
||||
<p class="studio-kicker">ModelRoutingView</p>
|
||||
<h1>模型与路由</h1>
|
||||
</div>
|
||||
<el-button type="primary">新增路由</el-button>
|
||||
</header>
|
||||
|
||||
<div class="studio-panel model-panel">
|
||||
<div class="panel-heading">
|
||||
<h2>任务路由规则</h2>
|
||||
<span>保留 model_provider / model_config / model_route_rule 语义</span>
|
||||
</div>
|
||||
<div class="document-table">
|
||||
<div class="table-row table-head">
|
||||
<span>任务</span><span>主模型</span><span>Fallback</span><span>最大延迟</span><span>状态</span>
|
||||
</div>
|
||||
<div v-for="route in modelRoutes" :key="route.task" class="table-row">
|
||||
<strong>{{ route.task }}</strong>
|
||||
<span>{{ route.primary }}</span>
|
||||
<span>{{ route.fallback }}</span>
|
||||
<span>{{ route.latency }}</span>
|
||||
<el-tag :type="route.status === '启用' ? 'success' : 'warning'">{{ route.status }}</el-tag>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
</template>
|
||||
49
frontend/src/pages/studio/ObservabilityPage.vue
Normal file
49
frontend/src/pages/studio/ObservabilityPage.vue
Normal file
@@ -0,0 +1,49 @@
|
||||
<script setup lang="ts">
|
||||
import { recentRuns, traceSteps } from '@/data/studioMock';
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<section class="studio-page observability-page">
|
||||
<header class="page-title-row">
|
||||
<div>
|
||||
<p class="studio-kicker">ObservabilityView</p>
|
||||
<h1>运行观测</h1>
|
||||
</div>
|
||||
<el-button>导出日志</el-button>
|
||||
</header>
|
||||
|
||||
<div class="observability-layout">
|
||||
<section class="studio-panel">
|
||||
<div class="panel-heading">
|
||||
<h2>运行记录</h2>
|
||||
<span>workflow_run / workflow_run_step / model_call_log</span>
|
||||
</div>
|
||||
<div class="run-table">
|
||||
<div class="run-row run-head">
|
||||
<span>名称</span><span>类型</span><span>状态</span><span>延迟</span><span>成本</span>
|
||||
</div>
|
||||
<div v-for="run in recentRuns" :key="run.id" class="run-row">
|
||||
<strong>{{ run.name }}</strong>
|
||||
<span>{{ run.type }}</span>
|
||||
<el-tag :type="run.status === '成功' ? 'success' : 'warning'">{{ run.status }}</el-tag>
|
||||
<span>{{ run.latency }}</span>
|
||||
<span>{{ run.cost }}</span>
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
|
||||
<aside class="studio-panel">
|
||||
<div class="panel-heading compact">
|
||||
<h2>步骤日志</h2>
|
||||
<span>run-1842</span>
|
||||
</div>
|
||||
<ol class="log-list">
|
||||
<li v-for="step in traceSteps" :key="step.node">
|
||||
<time>{{ step.duration }}</time>
|
||||
<span>{{ step.node }} · {{ step.status }} · {{ step.output }}</span>
|
||||
</li>
|
||||
</ol>
|
||||
</aside>
|
||||
</div>
|
||||
</section>
|
||||
</template>
|
||||
56
frontend/src/pages/studio/SkillWorkspacePage.vue
Normal file
56
frontend/src/pages/studio/SkillWorkspacePage.vue
Normal file
@@ -0,0 +1,56 @@
|
||||
<script setup lang="ts">
|
||||
import { skillVersions } from '@/data/studioMock';
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<section class="studio-page skill-page">
|
||||
<header class="page-title-row">
|
||||
<div>
|
||||
<p class="studio-kicker">SkillWorkspaceView</p>
|
||||
<h1>Skill 编辑与使用</h1>
|
||||
</div>
|
||||
<el-button type="primary">测试 Skill</el-button>
|
||||
</header>
|
||||
|
||||
<div class="skill-layout">
|
||||
<section class="studio-panel skill-editor">
|
||||
<div class="panel-heading">
|
||||
<h2>引用审校 Skill</h2>
|
||||
<span>PUT /api/skills/skill-citation/draft</span>
|
||||
</div>
|
||||
<div class="editor-tabs">
|
||||
<button class="active">Prompt</button>
|
||||
<button>Code</button>
|
||||
<button>Config</button>
|
||||
</div>
|
||||
<pre class="prompt-editor">你是回答审校器。请检查答案是否完整引用知识库切片,并输出:
|
||||
1. answer_quality
|
||||
2. missing_citations
|
||||
3. rewrite_suggestion</pre>
|
||||
<div class="variable-grid">
|
||||
<label>变量 <strong>answer</strong></label>
|
||||
<label>变量 <strong>citations[]</strong></label>
|
||||
<label>输出 <strong>quality_score</strong></label>
|
||||
</div>
|
||||
</section>
|
||||
|
||||
<aside class="studio-panel test-panel">
|
||||
<div class="panel-heading compact">
|
||||
<h2>测试面板</h2>
|
||||
<span>POST /api/skills/skill-citation/test</span>
|
||||
</div>
|
||||
<div class="test-result">
|
||||
<strong>quality_score: 0.86</strong>
|
||||
<p>建议补充“日志留存周期”的引用来源,并将私有化部署边界写得更明确。</p>
|
||||
</div>
|
||||
<div class="version-list">
|
||||
<article v-for="version in skillVersions" :key="version.version">
|
||||
<strong>{{ version.version }}</strong>
|
||||
<span>{{ version.status }}</span>
|
||||
<em>{{ version.note }} · {{ version.updatedAt }}</em>
|
||||
</article>
|
||||
</div>
|
||||
</aside>
|
||||
</div>
|
||||
</section>
|
||||
</template>
|
||||
95
frontend/src/pages/studio/StudioDashboardPage.vue
Normal file
95
frontend/src/pages/studio/StudioDashboardPage.vue
Normal file
@@ -0,0 +1,95 @@
|
||||
<script setup lang="ts">
|
||||
import { ArrowRight, Check, Warning } from '@element-plus/icons-vue';
|
||||
|
||||
import { lifecycleSteps, readinessChecklist, recentRuns } from '@/data/studioMock';
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<section class="studio-page dashboard-page">
|
||||
<header class="studio-hero">
|
||||
<div>
|
||||
<p class="studio-kicker">项目 / Common Agent Studio</p>
|
||||
<h1>从知识接入到 Agent 发布的一体化工作台</h1>
|
||||
<p>
|
||||
使用新的聚合 ViewModel 驱动原型:知识资产、Workflow、MCP、Skill、Agent 调试与观测都围绕一次发布旅程组织。
|
||||
</p>
|
||||
</div>
|
||||
<div class="hero-actions">
|
||||
<el-button type="primary">新建 Workflow</el-button>
|
||||
<el-button>导入 MCP</el-button>
|
||||
</div>
|
||||
</header>
|
||||
|
||||
<div class="lifecycle-strip">
|
||||
<article v-for="(step, index) in lifecycleSteps" :key="step.name" class="lifecycle-step" :class="`is-${step.status}`">
|
||||
<div class="step-index">{{ index + 1 }}</div>
|
||||
<div>
|
||||
<strong>{{ step.name }}</strong>
|
||||
<span>{{ step.description }}</span>
|
||||
</div>
|
||||
<el-icon v-if="index < lifecycleSteps.length - 1"><ArrowRight /></el-icon>
|
||||
</article>
|
||||
</div>
|
||||
|
||||
<div class="dashboard-grid">
|
||||
<section class="studio-panel readiness-panel">
|
||||
<div class="panel-heading">
|
||||
<div>
|
||||
<h2>发布就绪检查</h2>
|
||||
<span>ViewModel: StudioDashboardView</span>
|
||||
</div>
|
||||
<el-tag type="warning">Draft</el-tag>
|
||||
</div>
|
||||
<ul class="check-list">
|
||||
<li v-for="item in readinessChecklist" :key="item.label" :class="{ done: item.done }">
|
||||
<el-icon>
|
||||
<Check v-if="item.done" />
|
||||
<span v-else class="pending-dot" />
|
||||
</el-icon>
|
||||
<span>{{ item.label }}</span>
|
||||
</li>
|
||||
</ul>
|
||||
</section>
|
||||
|
||||
<section class="studio-panel metrics-panel">
|
||||
<div class="panel-heading">
|
||||
<h2>运行概览</h2>
|
||||
<span>环境: Dev</span>
|
||||
</div>
|
||||
<div class="metric-row">
|
||||
<div><strong>27</strong><span>今日运行</span></div>
|
||||
<div><strong>96.4%</strong><span>成功率</span></div>
|
||||
<div><strong>1.28s</strong><span>P50 延迟</span></div>
|
||||
<div><strong>¥4.82</strong><span>预估成本</span></div>
|
||||
</div>
|
||||
</section>
|
||||
|
||||
<section class="studio-panel recent-panel">
|
||||
<div class="panel-heading">
|
||||
<h2>最近运行</h2>
|
||||
<span>Run Trace</span>
|
||||
</div>
|
||||
<div class="run-table">
|
||||
<div class="run-row run-head">
|
||||
<span>名称</span><span>类型</span><span>状态</span><span>延迟</span><span>成本</span>
|
||||
</div>
|
||||
<div v-for="run in recentRuns" :key="run.id" class="run-row">
|
||||
<strong>{{ run.name }}</strong>
|
||||
<span>{{ run.type }}</span>
|
||||
<el-tag :type="run.status === '成功' ? 'success' : 'warning'">{{ run.status }}</el-tag>
|
||||
<span>{{ run.latency }}</span>
|
||||
<span>{{ run.cost }}</span>
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
|
||||
<section class="studio-panel warning-panel">
|
||||
<el-icon><Warning /></el-icon>
|
||||
<div>
|
||||
<h2>生产发布前仍需确认路由兜底</h2>
|
||||
<p>AGENT_PLAN 任务当前只有草稿路由,建议补齐 fallback 模型和最大延迟阈值。</p>
|
||||
</div>
|
||||
</section>
|
||||
</div>
|
||||
</section>
|
||||
</template>
|
||||
112
frontend/src/pages/studio/WorkflowBuilderPage.vue
Normal file
112
frontend/src/pages/studio/WorkflowBuilderPage.vue
Normal file
@@ -0,0 +1,112 @@
|
||||
<script setup lang="ts">
|
||||
import { Connection, Cpu, VideoPlay } from '@element-plus/icons-vue';
|
||||
|
||||
import { traceSteps, workflowEdges, workflowNodes } from '@/data/studioMock';
|
||||
|
||||
const nodeById = Object.fromEntries(workflowNodes.map((node) => [node.id, node]));
|
||||
const canvasEdges = workflowEdges.flatMap((edge) => {
|
||||
const from = nodeById[edge.from];
|
||||
const to = nodeById[edge.to];
|
||||
|
||||
if (!from || !to) {
|
||||
return [];
|
||||
}
|
||||
|
||||
return [
|
||||
{
|
||||
id: `${edge.from}-${edge.to}`,
|
||||
x1: from.x + 5,
|
||||
y1: from.y + 4,
|
||||
x2: to.x,
|
||||
y2: to.y + 4,
|
||||
},
|
||||
];
|
||||
});
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<section class="studio-page workflow-page">
|
||||
<header class="page-title-row">
|
||||
<div>
|
||||
<p class="studio-kicker">WorkflowBuilderView · Draft / Published</p>
|
||||
<h1>Workflow 图形化编排</h1>
|
||||
</div>
|
||||
<div class="toolbar-actions">
|
||||
<el-button>保存草稿</el-button>
|
||||
<el-button type="primary"><el-icon><VideoPlay /></el-icon> 运行测试</el-button>
|
||||
</div>
|
||||
</header>
|
||||
|
||||
<div class="workflow-layout">
|
||||
<aside class="studio-panel node-library">
|
||||
<div class="panel-heading compact">
|
||||
<h2>节点库</h2>
|
||||
<span>JSON Graph</span>
|
||||
</div>
|
||||
<button>Start</button>
|
||||
<button>LLM</button>
|
||||
<button>Knowledge Retrieval</button>
|
||||
<button>MCP Tool</button>
|
||||
<button>Skill</button>
|
||||
<button>Condition</button>
|
||||
<button>Answer</button>
|
||||
</aside>
|
||||
|
||||
<main class="studio-panel workflow-canvas">
|
||||
<div class="canvas-toolbar">
|
||||
<span><el-icon><Connection /></el-icon> workflow-support-rag</span>
|
||||
<span>版本快照 v7</span>
|
||||
<span>环境: Dev</span>
|
||||
</div>
|
||||
<div class="canvas-surface">
|
||||
<svg class="edge-layer" viewBox="0 0 100 100" preserveAspectRatio="none">
|
||||
<line
|
||||
v-for="edge in canvasEdges"
|
||||
:key="edge.id"
|
||||
:x1="edge.x1"
|
||||
:y1="edge.y1"
|
||||
:x2="edge.x2"
|
||||
:y2="edge.y2"
|
||||
/>
|
||||
</svg>
|
||||
<article
|
||||
v-for="node in workflowNodes"
|
||||
:key="node.id"
|
||||
class="workflow-node"
|
||||
:class="{ selected: node.id === 'llm' }"
|
||||
:style="{ left: `${node.x}%`, top: `${node.y}%` }"
|
||||
>
|
||||
<span>{{ node.type }}</span>
|
||||
<strong>{{ node.label }}</strong>
|
||||
<em>{{ node.description }}</em>
|
||||
</article>
|
||||
</div>
|
||||
<div class="run-trace-drawer">
|
||||
<strong>Run Trace</strong>
|
||||
<div v-for="step in traceSteps" :key="step.node">
|
||||
<span>{{ step.node }}</span>
|
||||
<em>{{ step.status }} · {{ step.duration }} · {{ step.output }}</em>
|
||||
</div>
|
||||
</div>
|
||||
</main>
|
||||
|
||||
<aside class="studio-panel inspector-panel">
|
||||
<div class="panel-heading compact">
|
||||
<h2>节点 Inspector</h2>
|
||||
<span>LLM</span>
|
||||
</div>
|
||||
<dl class="inspector-list">
|
||||
<dt>任务类型</dt>
|
||||
<dd>RAG_ANSWER</dd>
|
||||
<dt>输入 Schema</dt>
|
||||
<dd>question, retrieved_chunks, conversation</dd>
|
||||
<dt>输出 Schema</dt>
|
||||
<dd>answer, citations, safety_flags</dd>
|
||||
<dt>路由策略</dt>
|
||||
<dd>primary qwen-plus / fallback deepseek-v3</dd>
|
||||
</dl>
|
||||
<button class="blue-command"><el-icon><Cpu /></el-icon> 打开模型路由</button>
|
||||
</aside>
|
||||
</div>
|
||||
</section>
|
||||
</template>
|
||||
Reference in New Issue
Block a user