agent / templates /index1.html
samlax12's picture
Rename templates/index.html to templates/index1.html
beec11c verified
raw
history blame contribute delete
93.5 kB
<!-- templates/index.html -->
<!DOCTYPE html>
<html lang="zh-CN">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<meta http-equiv="Content-Security-Policy" content="upgrade-insecure-requests">
<title>教育AI助手开发平台</title>
<link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/[email protected]/dist/css/bootstrap.min.css">
<link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/[email protected]/font/bootstrap-icons.css">
<script src="https://cdn.jsdelivr.net/npm/[email protected]/dist/js/bootstrap.bundle.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/jsPlumb/2.15.6/js/jsplumb.min.js"></script>
<style>
:root {
--primary-color: #4361ee;
--secondary-color: #3f37c9;
--accent-color: #4cc9f0;
--success-color: #4caf50;
--warning-color: #ff9800;
--danger-color: #f44336;
--light-color: #f8f9fa;
--dark-color: #212529;
--border-color: #dee2e6;
--border-radius: 0.375rem;
}
body {
font-family: 'Segoe UI', Tahoma, Geneva, Verdana, sans-serif;
display: flex;
margin: 0;
padding: 0;
height: 100vh;
background-color: #f5f7fa;
color: #333;
}
.sidebar {
width: 280px;
background-color: #fff;
border-right: 1px solid var(--border-color);
box-shadow: 2px 0 5px rgba(0, 0, 0, 0.05);
display: flex;
flex-direction: column;
transition: all 0.3s ease;
z-index: 1000;
}
.sidebar-header {
padding: 1.5rem;
border-bottom: 1px solid var(--border-color);
}
.sidebar-header h2 {
margin: 0;
font-size: 1.5rem;
font-weight: 600;
color: var(--primary-color);
}
.sidebar-menu {
list-style: none;
padding: 0;
margin: 0;
flex: 1;
overflow-y: auto;
}
.sidebar-menu li {
padding: 1rem 1.5rem;
cursor: pointer;
transition: background-color 0.2s ease;
display: flex;
align-items: center;
position: relative;
}
.sidebar-menu li i {
margin-right: 0.75rem;
font-size: 1.1rem;
}
.sidebar-menu li:hover {
background-color: rgba(67, 97, 238, 0.05);
color: var(--primary-color);
}
.sidebar-menu li.active {
background-color: rgba(67, 97, 238, 0.1);
color: var(--primary-color);
font-weight: 500;
}
.sidebar-menu li.active::before {
content: '';
position: absolute;
left: 0;
top: 0;
bottom: 0;
width: 4px;
background-color: var(--primary-color);
}
.main-content {
flex: 1;
padding: 2rem;
overflow-y: auto;
}
.content-header {
margin-bottom: 2rem;
}
.content-header h1 {
margin: 0;
font-size: 1.75rem;
font-weight: 600;
color: #333;
}
.content-section {
display: none;
}
.content-section.active {
display: block;
}
.card {
background-color: #fff;
border-radius: var(--border-radius);
box-shadow: 0 2px 5px rgba(0, 0, 0, 0.05);
margin-bottom: 1.5rem;
border: 1px solid var(--border-color);
overflow: hidden;
}
.card-header {
background-color: #fff;
border-bottom: 1px solid var(--border-color);
padding: 1rem 1.5rem;
display: flex;
justify-content: space-between;
align-items: center;
}
.card-header h3 {
margin: 0;
font-size: 1.2rem;
font-weight: 600;
}
.card-body {
padding: 1.5rem;
}
.form-group {
margin-bottom: 1.5rem;
}
.form-label {
font-weight: 500;
margin-bottom: 0.5rem;
display: block;
}
.form-control {
border-radius: var(--border-radius);
}
.btn {
border-radius: var(--border-radius);
font-weight: 500;
}
.btn-primary {
background-color: var(--primary-color);
border-color: var(--primary-color);
}
.btn-primary:hover {
background-color: var(--secondary-color);
border-color: var(--secondary-color);
}
.workflow-editor {
border: 1px solid var(--border-color);
border-radius: var(--border-radius);
min-height: 400px;
background-color: #f8f9fa;
position: relative;
}
.knowledge-item {
padding: 1rem;
margin-bottom: 1rem;
border: 1px solid var(--border-color);
border-radius: var(--border-radius);
background-color: #fff;
transition: all 0.2s ease;
}
.knowledge-item:hover {
box-shadow: 0 5px 15px rgba(0, 0, 0, 0.08);
}
.knowledge-item .header {
display: flex;
justify-content: space-between;
align-items: center;
margin-bottom: 0.5rem;
}
.agent-item {
padding: 1.25rem;
margin-bottom: 1.5rem;
border-radius: var(--border-radius);
background-color: #fff;
box-shadow: 0 2px 10px rgba(0, 0, 0, 0.05);
transition: all 0.2s ease;
border-left: 4px solid var(--primary-color);
}
.agent-item:hover {
box-shadow: 0 5px 15px rgba(0, 0, 0, 0.1);
transform: translateY(-2px);
}
.agent-item .header {
display: flex;
justify-content: space-between;
align-items: center;
margin-bottom: 1rem;
}
.agent-item .title {
font-size: 1.2rem;
font-weight: 600;
margin: 0;
color: #333;
}
.agent-item .description {
color: #666;
margin-bottom: 1rem;
}
.agent-item .meta {
display: flex;
font-size: 0.85rem;
color: #777;
margin-top: 1rem;
}
.agent-item .meta div {
margin-right: 1.5rem;
display: flex;
align-items: center;
}
.agent-item .meta i {
margin-right: 0.5rem;
}
.nav-tabs {
border-bottom: 1px solid var(--border-color);
margin-bottom: 1.5rem;
}
.nav-tabs .nav-link {
margin-bottom: -1px;
border: 1px solid transparent;
border-top-left-radius: 0.375rem;
border-top-right-radius: 0.375rem;
padding: 0.75rem 1.25rem;
color: #495057;
}
.nav-tabs .nav-link.active {
color: var(--primary-color);
background-color: #fff;
border-color: var(--border-color) var(--border-color) #fff;
font-weight: 500;
}
.nav-tabs .nav-link:hover {
border-color: #e9ecef #e9ecef var(--border-color);
}
.spinner-border {
width: 1.5rem;
height: 1.5rem;
}
.distribution-link {
padding: 0.75rem 1rem;
background-color: #f8f9fa;
border: 1px solid var(--border-color);
border-radius: var(--border-radius);
display: flex;
justify-content: space-between;
align-items: center;
margin-bottom: 1rem;
}
.badge {
display: inline-block;
padding: 0.35em 0.65em;
font-size: 0.75em;
font-weight: 500;
line-height: 1;
text-align: center;
white-space: nowrap;
vertical-align: baseline;
border-radius: 50rem;
margin-left: 0.5rem;
}
.badge-primary {
background-color: var(--primary-color);
color: #fff;
}
.badge-success {
background-color: var(--success-color);
color: #fff;
}
.badge-warning {
background-color: var(--warning-color);
color: #fff;
}
.plugin-option {
padding: 1rem;
margin-bottom: 1rem;
border: 1px solid var(--border-color);
border-radius: var(--border-radius);
display: flex;
align-items: center;
cursor: pointer;
transition: all 0.2s ease;
}
.plugin-option:hover {
background-color: rgba(67, 97, 238, 0.05);
border-color: var(--primary-color);
}
.plugin-option.selected {
background-color: rgba(67, 97, 238, 0.1);
border-color: var(--primary-color);
}
.plugin-option .icon {
font-size: 1.5rem;
margin-right: 1rem;
color: var(--primary-color);
}
.plugin-option .content {
flex: 1;
}
.plugin-option .title {
font-weight: 500;
margin-bottom: 0.25rem;
}
.plugin-option .description {
font-size: 0.9rem;
color: #666;
}
/* 工作流可视化相关样式 */
.workflow-canvas {
height: 400px;
background-color: #f9fbfd;
border: 1px solid var(--border-color);
border-radius: var(--border-radius);
position: relative;
}
/* 工作流节点样式 */
.workflow-node {
position: absolute;
width: 150px;
height: 50px;
background-color: white;
border-radius: 4px;
border: 1px solid #ccc;
box-shadow: 0 2px 6px rgba(0,0,0,0.1);
display: flex;
overflow: hidden;
user-select: none;
z-index: 10;
}
.node-icon {
display: flex;
align-items: center;
justify-content: center;
width: 30px;
height: 100%;
color: white;
font-weight: bold;
}
.node-content {
flex: 1;
display: flex;
align-items: center;
justify-content: center;
padding: 0 10px;
font-size: 14px;
color: #333;
}
/* 节点类型样式 */
.node-type-intent_recognition .node-icon {
background-color: #673AB7;
}
.node-type-intent_recognition {
border-color: #673AB7;
background-color: #EDE7F6;
}
.node-type-knowledge_query .node-icon {
background-color: #009688;
}
.node-type-knowledge_query {
border-color: #009688;
background-color: #E0F2F1;
}
.node-type-plugin_call .node-icon {
background-color: #FF9800;
}
.node-type-plugin_call {
border-color: #FF9800;
background-color: #FFF3E0;
}
.node-type-generate_response .node-icon {
background-color: #2196F3;
}
.node-type-generate_response {
border-color: #2196F3;
background-color: #E3F2FD;
}
/* jsPlumb 连接线标签样式 */
.connection-label {
background-color: white;
padding: 3px 6px;
border-radius: 3px;
border: 1px solid #eee;
font-size: 12px;
color: #666;
}
</style>
</head>
<body>
<div class="sidebar">
<div class="sidebar-header">
<h2>AI助教开发平台</h2>
</div>
<ul class="sidebar-menu">
<li class="active" data-section="dashboard">
<i class="bi bi-grid-1x2"></i>
<span>仪表盘</span>
</li>
<li data-section="create-agent">
<i class="bi bi-plus-circle"></i>
<span>创建Agent</span>
</li>
<li data-section="knowledge-base">
<i class="bi bi-database"></i>
<span>知识库管理</span>
</li>
<li data-section="agent-list">
<i class="bi bi-list-ul"></i>
<span>Agent列表</span>
</li>
</ul>
</div>
<div class="main-content">
<!-- 仪表盘 -->
<section id="dashboard" class="content-section active">
<div class="content-header">
<h1>仪表盘</h1>
<p class="text-muted mt-2">欢迎使用教育AI助手开发平台</p>
</div>
<div class="row">
<div class="col-md-4">
<div class="card">
<div class="card-body text-center">
<div class="display-4 mb-3">
<i class="bi bi-robot text-primary"></i>
</div>
<h3 id="agent-count">0</h3>
<p class="text-muted">AI助手</p>
</div>
</div>
</div>
<div class="col-md-4">
<div class="card">
<div class="card-body text-center">
<div class="display-4 mb-3">
<i class="bi bi-database text-success"></i>
</div>
<h3 id="knowledge-count">0</h3>
<p class="text-muted">知识库</p>
</div>
</div>
</div>
<div class="col-md-4">
<div class="card">
<div class="card-body text-center">
<div class="display-4 mb-3">
<i class="bi bi-share text-warning"></i>
</div>
<h3 id="distribution-count">0</h3>
<p class="text-muted">分发链接</p>
</div>
</div>
</div>
</div>
<div class="row mt-4">
<div class="col-md-6">
<div class="card">
<div class="card-header">
<h3>最近创建的Agent</h3>
</div>
<div class="card-body" id="recent-agents">
<div class="text-center py-4">
<div class="spinner-border text-primary" role="status">
<span class="visually-hidden">加载中...</span>
</div>
</div>
</div>
</div>
</div>
<div class="col-md-6">
<div class="card">
<div class="card-header">
<h3>快速入门</h3>
</div>
<div class="card-body">
<div class="quick-start-item mb-3 p-3 border-bottom">
<h5><i class="bi bi-1-circle me-2"></i> 创建知识库</h5>
<p>上传PDF、文本等文档,构建AI助手的知识基础。</p>
<button class="btn btn-sm btn-outline-primary" onclick="switchSection('knowledge-base')">开始创建</button>
</div>
<div class="quick-start-item mb-3 p-3 border-bottom">
<h5><i class="bi bi-2-circle me-2"></i> 创建Agent</h5>
<p>设计您的AI助手,选择插件和知识库。</p>
<button class="btn btn-sm btn-outline-primary" onclick="switchSection('create-agent')">开始创建</button>
</div>
<div class="quick-start-item p-3">
<h5><i class="bi bi-3-circle me-2"></i> 分发给学生</h5>
<p>生成访问链接,分享给您的学生。</p>
<button class="btn btn-sm btn-outline-primary" onclick="switchSection('agent-list')">查看Agent</button>
</div>
</div>
</div>
</div>
</div>
</section>
<!-- 创建Agent -->
<section id="create-agent" class="content-section">
<div class="content-header">
<h1>创建新Agent</h1>
<p class="text-muted mt-2">设计您的AI助教Agent</p>
</div>
<div class="card">
<div class="card-body">
<ul class="nav nav-tabs" id="agentCreationTabs" role="tablist">
<li class="nav-item" role="presentation">
<button class="nav-link active" id="basic-tab" data-bs-toggle="tab" data-bs-target="#basic" type="button" role="tab" aria-controls="basic" aria-selected="true">基本信息</button>
</li>
<li class="nav-item" role="presentation">
<button class="nav-link" id="plugins-tab" data-bs-toggle="tab" data-bs-target="#plugins" type="button" role="tab" aria-controls="plugins" aria-selected="false">插件选择</button>
</li>
<li class="nav-item" role="presentation">
<button class="nav-link" id="knowledge-tab" data-bs-toggle="tab" data-bs-target="#knowledge" type="button" role="tab" aria-controls="knowledge" aria-selected="false">知识库</button>
</li>
<li class="nav-item" role="presentation">
<button class="nav-link" id="workflow-tab" data-bs-toggle="tab" data-bs-target="#workflow" type="button" role="tab" aria-controls="workflow" aria-selected="false">工作流设计</button>
</li>
</ul>
<div class="tab-content" id="agentCreationTabContent">
<!-- 基本信息 -->
<div class="tab-pane fade show active" id="basic" role="tabpanel" aria-labelledby="basic-tab">
<div class="mt-4">
<div class="form-group">
<label for="agent-name" class="form-label">Agent名称</label>
<input type="text" class="form-control" id="agent-name" placeholder="例如:计算机组成原理助教">
</div>
<div class="form-group">
<label for="agent-description" class="form-label">描述</label>
<textarea class="form-control" id="agent-description" rows="3" placeholder="描述这个Agent的功能和用途..."></textarea>
</div>
<div class="form-group">
<label for="agent-subject" class="form-label">学科/课程名称</label>
<input type="text" class="form-control" id="agent-subject" placeholder="例如:计算机组成原理">
<div class="form-text">这将影响AI助教的知识领域定位</div>
</div>
<div class="form-group">
<label for="agent-instructor" class="form-label">指导教师</label>
<input type="text" class="form-control" id="agent-instructor" placeholder="例如:李志刚教授">
</div>
<div class="form-group">
<label for="agent-type" class="form-label">Agent类型</label>
<select class="form-select" id="agent-type">
<option value="educational">教育辅导</option>
<option value="programming">编程辅助</option>
<option value="math">数学辅导</option>
<option value="general">通用助手</option>
</select>
</div>
<button class="btn btn-primary" onclick="nextTab('plugins-tab')">下一步</button>
</div>
</div>
<!-- 插件选择 -->
<div class="tab-pane fade" id="plugins" role="tabpanel" aria-labelledby="plugins-tab">
<div class="mt-4">
<div class="mb-3">
<h5>选择Agent所需的插件</h5>
<p class="text-muted">插件可以扩展Agent的能力,使其更加强大</p>
</div>
<div class="plugin-option" data-plugin="code" onclick="togglePluginSelection(this)">
<div class="icon">
<i class="bi bi-code-square"></i>
</div>
<div class="content">
<div class="title">代码执行</div>
<div class="description">允许学生编写和执行Python代码,实时获取结果</div>
</div>
<div class="checkbox">
<input type="checkbox" class="form-check-input">
</div>
</div>
<div class="plugin-option" data-plugin="visualization" onclick="togglePluginSelection(this)">
<div class="icon">
<i class="bi bi-bar-chart"></i>
</div>
<div class="content">
<div class="title">3D可视化</div>
<div class="description">生成交互式3D图形,帮助学生理解数学概念</div>
</div>
<div class="checkbox">
<input type="checkbox" class="form-check-input">
</div>
</div>
<div class="plugin-option" data-plugin="mindmap" onclick="togglePluginSelection(this)">
<div class="icon">
<i class="bi bi-diagram-3"></i>
</div>
<div class="content">
<div class="title">思维导图</div>
<div class="description">创建结构化的思维导图,帮助学生梳理知识点</div>
</div>
<div class="checkbox">
<input type="checkbox" class="form-check-input">
</div>
</div>
<div class="d-flex justify-content-between mt-4">
<button class="btn btn-outline-secondary" onclick="nextTab('basic-tab')">上一步</button>
<button class="btn btn-primary" onclick="nextTab('knowledge-tab')">下一步</button>
</div>
</div>
</div>
<!-- 知识库 -->
<div class="tab-pane fade" id="knowledge" role="tabpanel" aria-labelledby="knowledge-tab">
<div class="mt-4">
<div class="mb-3">
<h5>选择Agent使用的知识库</h5>
<p class="text-muted">知识库提供Agent回答问题的专业知识</p>
</div>
<div class="mb-3">
<div class="input-group">
<input type="text" class="form-control" placeholder="搜索知识库..." id="knowledge-search">
<button class="btn btn-outline-secondary" type="button">
<i class="bi bi-search"></i>
</button>
</div>
</div>
<div id="knowledge-list" class="mb-4">
<div class="text-center py-4">
<div class="spinner-border text-primary" role="status">
<span class="visually-hidden">加载中...</span>
</div>
</div>
</div>
<div class="d-flex justify-content-between mt-4">
<button class="btn btn-outline-secondary" onclick="nextTab('plugins-tab')">上一步</button>
<button class="btn btn-primary" onclick="nextTab('workflow-tab')">下一步</button>
</div>
</div>
</div>
<!-- 工作流设计 -->
<div class="tab-pane fade" id="workflow" role="tabpanel" aria-labelledby="workflow-tab">
<div class="mt-4">
<div class="mb-3">
<h5>设计Agent工作流</h5>
<p class="text-muted">定义Agent如何处理用户的请求</p>
</div>
<div class="mb-4">
<button class="btn btn-primary" id="ai-workflow-btn">
<i class="bi bi-magic"></i> 使用AI自动设计工作流
</button>
</div>
<div class="card mb-4">
<div class="card-body p-0">
<ul class="nav nav-tabs" id="workflowViewTabs" role="tablist">
<li class="nav-item" role="presentation">
<button class="nav-link active" id="visual-tab" data-bs-toggle="tab" data-bs-target="#visual-view" type="button" role="tab">可视化视图</button>
</li>
<li class="nav-item" role="presentation">
<button class="nav-link" id="code-tab" data-bs-toggle="tab" data-bs-target="#code-view" type="button" role="tab">代码视图</button>
</li>
</ul>
<div class="tab-content" id="workflowViewContent">
<div class="tab-pane fade show active" id="visual-view" role="tabpanel">
<div id="workflow-canvas" class="workflow-canvas">
<div class="p-4 text-center text-muted">
点击"使用AI自动设计工作流"按钮,或手动设计工作流
</div>
</div>
</div>
<div class="tab-pane fade" id="code-view" role="tabpanel">
<div class="p-4">
<pre id="workflow-code" class="mb-0 p-3 bg-light border rounded">{}</pre>
</div>
</div>
</div>
</div>
</div>
<div class="d-flex justify-content-between">
<button class="btn btn-outline-secondary" onclick="nextTab('knowledge-tab')">上一步</button>
<button class="btn btn-success" id="create-agent-btn">创建Agent</button>
</div>
</div>
</div>
</div>
</div>
</div>
</section>
<!-- 知识库管理 -->
<section id="knowledge-base" class="content-section">
<div class="content-header">
<h1>知识库管理</h1>
<p class="text-muted mt-2">上传和管理Agent的知识源</p>
</div>
<div class="row">
<div class="col-md-4">
<div class="card">
<div class="card-header">
<h3>创建知识库</h3>
</div>
<div class="card-body">
<form id="knowledge-form">
<div class="form-group">
<label for="knowledge-name" class="form-label">知识库名称</label>
<input type="text" class="form-control" id="knowledge-name" placeholder="例如:计算机组成原理" required>
</div>
<div class="form-group">
<label for="knowledge-file" class="form-label">上传文件</label>
<input type="file" class="form-control" id="knowledge-file" required>
<div class="form-text">支持PDF、TXT、MD等格式</div>
</div>
<div class="progress mt-3 mb-2" style="display: none;" id="upload-progress">
<div class="progress-bar progress-bar-striped progress-bar-animated" role="progressbar" style="width: 0%"></div>
</div>
<button type="submit" class="btn btn-primary mt-3 w-100">创建知识库</button>
</form>
</div>
</div>
</div>
<div class="col-md-8">
<div class="card">
<div class="card-header">
<h3>现有知识库</h3>
</div>
<div class="card-body">
<div id="existing-knowledge-list">
<div class="text-center py-4">
<div class="spinner-border text-primary" role="status">
<span class="visually-hidden">加载中...</span>
</div>
</div>
</div>
</div>
</div>
</div>
</div>
</section>
<!-- Agent列表 -->
<section id="agent-list" class="content-section">
<div class="content-header d-flex justify-content-between align-items-center">
<div>
<h1>Agent列表</h1>
<p class="text-muted mt-2">管理和分发您创建的AI助手</p>
</div>
<div>
<button class="btn btn-primary" onclick="switchSection('create-agent')">
<i class="bi bi-plus-lg"></i> 创建新Agent
</button>
</div>
</div>
<div class="row">
<div class="col-12">
<div class="card">
<div class="card-body">
<div id="agent-list-container">
<div class="text-center py-4">
<div class="spinner-border text-primary" role="status">
<span class="visually-hidden">加载中...</span>
</div>
</div>
</div>
</div>
</div>
</div>
</div>
</section>
</div>
<!-- 模态框:Agent详情 -->
<div class="modal fade" id="agentDetailModal" tabindex="-1" aria-hidden="true">
<div class="modal-dialog modal-lg">
<div class="modal-content">
<div class="modal-header">
<h5 class="modal-title" id="agent-detail-title">Agent详情</h5>
<button type="button" class="btn-close" data-bs-dismiss="modal" aria-label="Close"></button>
</div>
<div class="modal-body" id="agent-detail-body">
<!-- Agent详情将通过JavaScript填充 -->
</div>
<div class="modal-footer">
<button type="button" class="btn btn-secondary" data-bs-dismiss="modal">关闭</button>
</div>
</div>
</div>
</div>
<!-- 模态框:创建分发链接 -->
<div class="modal fade" id="createDistributionModal" tabindex="-1" aria-hidden="true">
<div class="modal-dialog">
<div class="modal-content">
<div class="modal-header">
<h5 class="modal-title">创建分发链接</h5>
<button type="button" class="btn-close" data-bs-dismiss="modal" aria-label="Close"></button>
</div>
<div class="modal-body">
<form id="distribution-form">
<input type="hidden" id="distribution-agent-id">
<div class="form-group mb-3">
<label for="distribution-expires" class="form-label">链接有效期</label>
<select class="form-select" id="distribution-expires">
<option value="0">永不过期</option>
<option value="86400">1天</option>
<option value="604800">7天</option>
<option value="2592000">30天</option>
</select>
</div>
</form>
</div>
<div class="modal-footer">
<button type="button" class="btn btn-secondary" data-bs-dismiss="modal">取消</button>
<button type="button" class="btn btn-primary" id="create-distribution-btn">创建链接</button>
</div>
</div>
</div>
</div>
<!-- 模态框:显示分发链接 -->
<div class="modal fade" id="showDistributionModal" tabindex="-1" aria-hidden="true">
<div class="modal-dialog">
<div class="modal-content">
<div class="modal-header">
<h5 class="modal-title">分发链接</h5>
<button type="button" class="btn-close" data-bs-dismiss="modal" aria-label="Close"></button>
</div>
<div class="modal-body">
<div class="alert alert-success">
链接创建成功!请复制以下链接分享给学生
</div>
<div class="input-group mb-3">
<input type="text" class="form-control" id="distribution-link" readonly>
<button class="btn btn-outline-secondary" type="button" onclick="copyToClipboard('distribution-link')">
<i class="bi bi-clipboard"></i>
</button>
</div>
</div>
<div class="modal-footer">
<button type="button" class="btn btn-secondary" data-bs-dismiss="modal">关闭</button>
</div>
</div>
</div>
</div>
<script>
// 初始化变量
let selectedPlugins = [];
let selectedKnowledgeBases = [];
let currentWorkflow = { nodes: [], edges: [] };
let allKnowledgeBases = [];
let allAgents = [];
let jsPlumbWorkflowInstance = null; // 添加jsPlumb实例变量
// 页面加载完成后执行
document.addEventListener('DOMContentLoaded', function() {
// 初始化侧边栏菜单点击事件
document.querySelectorAll('.sidebar-menu li').forEach(item => {
item.addEventListener('click', function() {
const section = this.getAttribute('data-section');
switchSection(section);
});
});
// 初始化表单提交事件
document.getElementById('knowledge-form').addEventListener('submit', createKnowledgeBase);
// 初始化AI工作流按钮
document.getElementById('ai-workflow-btn').addEventListener('click', generateAIWorkflow);
// 初始化创建Agent按钮
document.getElementById('create-agent-btn').addEventListener('click', createAgent);
// 初始化创建分发链接按钮
document.getElementById('create-distribution-btn').addEventListener('click', createDistribution);
// 初始化工作流视图标签切换
initWorkflowTabs();
// 加载知识库列表
loadKnowledgeBases();
// 加载Agent列表
loadAgents();
});
// 初始化工作流标签切换
function initWorkflowTabs() {
// 为主工作流和详情工作流标签添加点击事件
document.querySelectorAll('#workflowViewTabs .nav-link, #detailWorkflowTabs .nav-link').forEach(tab => {
tab.addEventListener('click', function(event) {
event.preventDefault();
// 获取目标面板ID
const targetId = this.getAttribute('data-bs-target');
const targetPane = document.querySelector(targetId);
if (!targetPane) return;
// 获取所有同级标签并移除激活状态
const tabs = this.closest('.nav-tabs').querySelectorAll('.nav-link');
tabs.forEach(t => t.classList.remove('active'));
// 激活当前标签
this.classList.add('active');
// 获取所有同级面板并隐藏
const tabContent = targetPane.closest('.tab-content');
if (tabContent) {
tabContent.querySelectorAll('.tab-pane').forEach(pane => {
pane.classList.remove('show', 'active');
});
}
// 显示目标面板
targetPane.classList.add('show', 'active');
// 如果切换到可视化视图,重新渲染工作流
if (targetId === '#visual-view' || targetId === '#detail-visual-view') {
const canvasId = targetId === '#visual-view' ? 'workflow-canvas' : 'detail-workflow-canvas';
// 确定要使用的工作流数据
let workflowData = currentWorkflow;
if (targetId === '#detail-visual-view') {
const modalBody = document.getElementById('agent-detail-body');
if (modalBody && modalBody.dataset.agentWorkflow) {
try {
workflowData = JSON.parse(modalBody.dataset.agentWorkflow);
} catch (e) {
console.error('解析工作流数据出错:', e);
}
}
}
// 延迟一小段时间确保DOM已经更新
setTimeout(() => {
renderWorkflowVisualization(workflowData, canvasId);
}, 200);
}
});
});
}
// 切换内容区域
function switchSection(section) {
// 隐藏所有内容区域
document.querySelectorAll('.content-section').forEach(item => {
item.classList.remove('active');
});
// 显示选中的内容区域
document.getElementById(section).classList.add('active');
// 更新侧边栏选中状态
document.querySelectorAll('.sidebar-menu li').forEach(item => {
item.classList.remove('active');
if (item.getAttribute('data-section') === section) {
item.classList.add('active');
}
});
// 如果切换到了工作流视图,重新绘制连接
if (section === 'create-agent') {
// 获取当前激活的标签页
const activeTab = document.querySelector('#agentCreationTabs .nav-link.active');
if (activeTab && activeTab.id === 'workflow-tab') {
// 检查当前哪个视图处于激活状态
const activeView = document.querySelector('#workflowViewContent .tab-pane.active');
if (activeView && activeView.id === 'visual-view') {
// 延迟执行以确保DOM已更新
setTimeout(() => {
renderWorkflowVisualization(currentWorkflow, 'workflow-canvas');
}, 200);
}
}
}
}
// 切换到下一个选项卡
function nextTab(tabId) {
const tab = document.getElementById(tabId);
const bsTab = new bootstrap.Tab(tab);
bsTab.show();
}
// 切换插件选择状态
function togglePluginSelection(element) {
const pluginId = element.getAttribute('data-plugin');
const checkbox = element.querySelector('input[type="checkbox"]');
if (element.classList.contains('selected')) {
element.classList.remove('selected');
checkbox.checked = false;
selectedPlugins = selectedPlugins.filter(id => id !== pluginId);
} else {
element.classList.add('selected');
checkbox.checked = true;
selectedPlugins.push(pluginId);
}
}
// 切换知识库选择状态
function toggleKnowledgeSelection(element) {
const knowledgeId = element.getAttribute('data-id');
if (element.classList.contains('selected')) {
element.classList.remove('selected');
element.querySelector('input[type="checkbox"]').checked = false;
selectedKnowledgeBases = selectedKnowledgeBases.filter(id => id !== knowledgeId);
} else {
element.classList.add('selected');
element.querySelector('input[type="checkbox"]').checked = true;
selectedKnowledgeBases.push(knowledgeId);
}
}
// 加载知识库列表
async function loadKnowledgeBases() {
try {
const response = await fetch('/api/knowledge');
const result = await response.json();
if (result.success) {
allKnowledgeBases = result.data || [];
// 更新仪表盘计数
document.getElementById('knowledge-count').textContent = allKnowledgeBases.length;
// 更新Agent创建页面的知识库列表
const knowledgeListElement = document.getElementById('knowledge-list');
if (knowledgeListElement) {
if (allKnowledgeBases.length === 0) {
knowledgeListElement.innerHTML = `
<div class="alert alert-info">
<i class="bi bi-info-circle me-2"></i>
暂无知识库,请先创建知识库
</div>
<div class="text-center">
<button class="btn btn-primary" onclick="switchSection('knowledge-base')">
创建知识库
</button>
</div>
`;
} else {
knowledgeListElement.innerHTML = '';
allKnowledgeBases.forEach(knowledge => {
const item = document.createElement('div');
item.className = 'plugin-option';
item.setAttribute('data-id', knowledge.id);
item.setAttribute('onclick', 'toggleKnowledgeSelection(this)');
item.innerHTML = `
<div class="icon">
<i class="bi bi-journal-text"></i>
</div>
<div class="content">
<div class="title">${knowledge.name}</div>
<div class="description">
<small>${knowledge.fileCount}个文件</small>
</div>
</div>
<div class="checkbox">
<input type="checkbox" class="form-check-input">
</div>
`;
knowledgeListElement.appendChild(item);
});
}
}
// 更新知识库管理页面的列表
const existingKnowledgeList = document.getElementById('existing-knowledge-list');
if (existingKnowledgeList) {
if (allKnowledgeBases.length === 0) {
existingKnowledgeList.innerHTML = `
<div class="alert alert-info">
<i class="bi bi-info-circle me-2"></i>
暂无知识库,请创建第一个知识库
</div>
`;
} else {
existingKnowledgeList.innerHTML = '';
allKnowledgeBases.forEach(knowledge => {
const item = document.createElement('div');
item.className = 'knowledge-item';
let fileListHtml = '';
if (knowledge.files && knowledge.files.length > 0) {
fileListHtml = '<div class="mt-3"><small class="text-muted">包含文件:</small><ul class="list-unstyled ms-3">';
knowledge.files.forEach(file => {
fileListHtml += `<li><small><i class="bi bi-file-text me-1"></i>${file}</small></li>`;
});
fileListHtml += '</ul></div>';
}
item.innerHTML = `
<div class="header">
<h5>${knowledge.name}</h5>
<div>
<button class="btn btn-sm btn-outline-danger" onclick="deleteKnowledgeBase('${knowledge.id}')">
<i class="bi bi-trash"></i>
</button>
</div>
</div>
<div>
<span class="badge bg-primary">${knowledge.fileCount}个文件</span>
</div>
${fileListHtml}
<div class="mt-3">
<form class="upload-form" data-id="${knowledge.id}">
<div class="input-group">
<input type="file" class="form-control form-control-sm" required>
<button type="submit" class="btn btn-sm btn-outline-primary">添加文件</button>
</div>
</form>
</div>
`;
existingKnowledgeList.appendChild(item);
});
// 添加上传表单事件监听
document.querySelectorAll('.upload-form').forEach(form => {
form.addEventListener('submit', function(event) {
event.preventDefault();
addFileToKnowledgeBase(form);
});
});
}
}
} else {
console.error('加载知识库失败:', result.message);
}
} catch (error) {
console.error('加载知识库出错:', error);
}
}
// 创建知识库
async function createKnowledgeBase(event) {
event.preventDefault();
const nameInput = document.getElementById('knowledge-name');
const fileInput = document.getElementById('knowledge-file');
const progressBar = document.getElementById('upload-progress');
if (!nameInput.value || !fileInput.files[0]) {
alert('请填写知识库名称并选择文件');
return;
}
// 显示进度条
progressBar.style.display = 'block';
progressBar.querySelector('.progress-bar').style.width = '0%';
const formData = new FormData();
formData.append('name', nameInput.value);
formData.append('file', fileInput.files[0]);
try {
const response = await fetch('/api/knowledge', {
method: 'POST',
body: formData
});
const result = await response.json();
if (result.success) {
// 成功启动处理,轮询进度
pollProgress(result.task_id, progressBar);
} else {
alert('创建知识库失败: ' + result.message);
progressBar.style.display = 'none';
}
} catch (error) {
console.error('创建知识库出错:', error);
alert('创建知识库出错,请查看控制台日志');
progressBar.style.display = 'none';
}
}
// 添加文件到知识库
async function addFileToKnowledgeBase(form) {
const knowledgeId = form.getAttribute('data-id');
const fileInput = form.querySelector('input[type="file"]');
const submitBtn = form.querySelector('button[type="submit"]');
if (!fileInput.files[0]) {
alert('请选择文件');
return;
}
// 禁用提交按钮
submitBtn.disabled = true;
submitBtn.textContent = '上传中...';
const formData = new FormData();
formData.append('file', fileInput.files[0]);
try {
const response = await fetch(`/api/knowledge/${knowledgeId}/documents`, {
method: 'POST',
body: formData
});
const result = await response.json();
if (result.success) {
alert('文件上传成功,开始处理');
// 轮询处理进度
pollProgressSimple(result.task_id, submitBtn);
} else {
alert('添加文件失败: ' + result.message);
submitBtn.disabled = false;
submitBtn.textContent = '添加文件';
}
} catch (error) {
console.error('添加文件出错:', error);
alert('添加文件出错,请查看控制台日志');
submitBtn.disabled = false;
submitBtn.textContent = '添加文件';
}
}
// 删除知识库
async function deleteKnowledgeBase(knowledgeId) {
if (!confirm('确定要删除这个知识库吗?此操作不可恢复。')) {
return;
}
try {
const response = await fetch(`/api/knowledge/${knowledgeId}`, {
method: 'DELETE'
});
const result = await response.json();
if (result.success) {
alert('知识库删除成功!');
loadKnowledgeBases(); // 重新加载知识库列表
} else {
alert('删除知识库失败: ' + result.message);
}
} catch (error) {
console.error('删除知识库出错:', error);
alert('删除知识库出错,请查看控制台日志');
}
}
// 轮询进度(带进度条显示)
function pollProgress(taskId, progressElement) {
const progressBar = progressElement.querySelector('.progress-bar');
const interval = setInterval(async () => {
try {
const response = await fetch(`/api/progress/${taskId}`);
const data = await response.json();
if (data.success) {
const progressData = data.data;
// 更新进度条
progressBar.style.width = progressData.progress + '%';
// 处理完成或出错时
if (progressData.progress >= 100 || progressData.error) {
clearInterval(interval);
if (progressData.error) {
alert('处理失败: ' + progressData.status);
} else {
alert(`知识库处理成功! 已处理${progressData.docCount}个文档片段`);
}
// 隐藏进度条
progressElement.style.display = 'none';
// 重置表单
document.getElementById('knowledge-name').value = '';
document.getElementById('knowledge-file').value = '';
// 重新加载知识库列表
loadKnowledgeBases();
}
}
} catch (error) {
console.error('获取进度信息出错:', error);
}
}, 1000); // 每秒轮询一次
}
// 简化版轮询进度(不显示进度条)
function pollProgressSimple(taskId, buttonElement) {
const originalText = buttonElement.textContent;
const interval = setInterval(async () => {
try {
const response = await fetch(`/api/progress/${taskId}`);
const data = await response.json();
if (data.success) {
const progressData = data.data;
// 处理完成或出错时
if (progressData.progress >= 100 || progressData.error) {
clearInterval(interval);
if (progressData.error) {
alert('处理失败: ' + progressData.status);
} else {
alert(`处理成功! 已处理${progressData.docCount}个文档片段`);
}
// 恢复按钮
buttonElement.disabled = false;
buttonElement.textContent = originalText;
// 重新加载知识库列表
loadKnowledgeBases();
}
}
} catch (error) {
console.error('获取进度信息出错:', error);
}
}, 1000); // 每秒轮询一次
}
// 使用AI生成工作流
async function generateAIWorkflow() {
const description = document.getElementById('agent-description').value;
const subject = document.getElementById('agent-subject').value || document.getElementById('agent-name').value;
if (!description) {
alert('请先填写Agent描述,以便AI生成工作流');
nextTab('basic-tab');
return;
}
// 显示加载状态
const workflowCanvas = document.getElementById('workflow-canvas');
workflowCanvas.innerHTML = `
<div class="text-center py-5">
<div class="spinner-border text-primary" role="status">
<span class="visually-hidden">生成中...</span>
</div>
<p class="mt-3">AI正在设计工作流,请稍候...</p>
</div>
`;
try {
const response = await fetch('/api/agent/ai-assist', {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({
description: description,
subject: subject,
plugins: selectedPlugins,
knowledge_bases: selectedKnowledgeBases
})
});
const result = await response.json();
if (result.success) {
// 保存工作流
currentWorkflow = result.workflow;
// 更新代码视图
updateWorkflowCodeView(currentWorkflow);
// 处理AI推荐的知识库和插件
let recommendationHtml = '';
let recommendationsApplied = false;
// 应用推荐的知识库
if (result.recommended_knowledge_bases && result.recommended_knowledge_bases.length > 0) {
// 更新选择的知识库
selectedKnowledgeBases = result.recommended_knowledge_bases;
// 更新UI中的选择状态
document.querySelectorAll('#knowledge-list .plugin-option').forEach(item => {
const kbId = item.getAttribute('data-id');
const selected = selectedKnowledgeBases.includes(kbId);
item.classList.toggle('selected', selected);
const checkbox = item.querySelector('input[type="checkbox"]');
if (checkbox) checkbox.checked = selected;
});
recommendationsApplied = true;
recommendationHtml += `
<div class="mb-3">
<h6>AI推荐的知识库:</h6>
<ul>
${selectedKnowledgeBases.map(kb => `<li>${kb}</li>`).join('')}
</ul>
</div>
`;
}
// 应用推荐的插件
if (result.recommended_plugins && result.recommended_plugins.length > 0) {
// 更新选择的插件
selectedPlugins = result.recommended_plugins;
// 更新UI中的选择状态
document.querySelectorAll('[data-plugin]').forEach(item => {
const pluginId = item.getAttribute('data-plugin');
const selected = selectedPlugins.includes(pluginId);
item.classList.toggle('selected', selected);
const checkbox = item.querySelector('input[type="checkbox"]');
if (checkbox) checkbox.checked = selected;
});
recommendationsApplied = true;
recommendationHtml += `
<div class="mb-3">
<h6>AI推荐的插件:</h6>
<ul>
${selectedPlugins.map(plugin => {
let pluginName = plugin;
if (plugin === 'code') pluginName = '代码执行';
if (plugin === 'visualization') pluginName = '3D可视化';
if (plugin === 'mindmap') pluginName = '思维导图';
return `<li>${pluginName}</li>`;
}).join('')}
</ul>
</div>
`;
}
// 显示推荐结果
if (recommendationsApplied) {
workflowCanvas.innerHTML = `
<div class="alert alert-success mb-3">
<i class="bi bi-check-circle-fill me-2"></i>
AI已成功设计工作流并推荐了知识库和插件
</div>
${recommendationHtml}
<div id="workflow-visualization-container"></div>
`;
} else {
workflowCanvas.innerHTML = `
<div class="alert alert-info mb-3">
<i class="bi bi-info-circle-fill me-2"></i>
AI已设计工作流但未推荐额外的知识库或插件
</div>
<div id="workflow-visualization-container"></div>
`;
}
// 渲染工作流视图
setTimeout(() => {
renderWorkflowVisualization(currentWorkflow, 'workflow-canvas');
}, 200);
} else {
workflowCanvas.innerHTML = `
<div class="alert alert-danger m-3">
<i class="bi bi-exclamation-triangle-fill me-2"></i>
生成工作流失败: ${result.message}
</div>
`;
}
} catch (error) {
console.error('生成工作流出错:', error);
workflowCanvas.innerHTML = `
<div class="alert alert-danger m-3">
<i class="bi bi-exclamation-triangle-fill me-2"></i>
生成工作流时发生错误,请重试
</div>
`;
}
}
// 更新工作流代码视图
function updateWorkflowCodeView(workflow) {
const workflowCodeElement = document.getElementById('workflow-code');
if (workflowCodeElement) {
workflowCodeElement.textContent = JSON.stringify(workflow, null, 2);
}
}
// 创建Agent
async function createAgent() {
const name = document.getElementById('agent-name').value;
const description = document.getElementById('agent-description').value;
const subject = document.getElementById('agent-subject').value || name; // 使用课程名称,如果未填写则使用Agent名称
const instructor = document.getElementById('agent-instructor').value || '教师'; // 使用指导教师,如果未填写则使用默认值
const type = document.getElementById('agent-type').value;
if (!name) {
alert('请填写Agent名称');
nextTab('basic-tab');
return;
}
// 准备Agent数据
const agentData = {
name: name,
description: description,
subject: subject,
instructor: instructor,
type: type,
plugins: selectedPlugins,
knowledge_bases: selectedKnowledgeBases,
workflow: currentWorkflow
};
try {
const response = await fetch('/api/agent/create', {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify(agentData)
});
const result = await response.json();
if (result.success) {
alert(`Agent '${name}' 创建成功!`);
// 重置表单
document.getElementById('agent-name').value = '';
document.getElementById('agent-description').value = '';
document.getElementById('agent-subject').value = '';
document.getElementById('agent-instructor').value = '';
document.getElementById('agent-type').value = 'educational';
selectedPlugins = [];
selectedKnowledgeBases = [];
currentWorkflow = { nodes: [], edges: [] };
// 重置插件选择
document.querySelectorAll('.plugin-option').forEach(item => {
item.classList.remove('selected');
item.querySelector('input[type="checkbox"]').checked = false;
});
// 重置工作流视图
document.getElementById('workflow-canvas').innerHTML = `
<div class="p-4 text-center text-muted">
点击"使用AI自动设计工作流"按钮,或手动设计工作流
</div>
`;
// 更新代码视图
updateWorkflowCodeView(currentWorkflow);
// 切换到Agent列表页面
switchSection('agent-list');
// 重新加载Agent列表
loadAgents();
} else {
alert('创建Agent失败: ' + result.message);
}
} catch (error) {
console.error('创建Agent出错:', error);
alert('创建Agent出错,请查看控制台日志');
}
}
// 加载Agent列表
async function loadAgents() {
try {
const response = await fetch('/api/agent/list');
const result = await response.json();
if (result.success) {
allAgents = result.agents || [];
// 更新仪表盘计数
document.getElementById('agent-count').textContent = allAgents.length;
// 计算分发链接总数
let distributionCount = 0;
allAgents.forEach(agent => {
distributionCount += agent.distribution_count || 0;
});
document.getElementById('distribution-count').textContent = distributionCount;
// 更新最近创建的Agent
updateRecentAgents();
// 更新Agent列表页面
updateAgentListPage();
} else {
console.error('加载Agent列表失败:', result.message);
}
} catch (error) {
console.error('加载Agent列表出错:', error);
}
}
// 更新最近创建的Agent列表
function updateRecentAgents() {
const recentAgentsElement = document.getElementById('recent-agents');
if (recentAgentsElement) {
if (allAgents.length === 0) {
recentAgentsElement.innerHTML = `
<div class="alert alert-info">
<i class="bi bi-info-circle me-2"></i>
您还没有创建Agent
</div>
<div class="text-center">
<button class="btn btn-primary" onclick="switchSection('create-agent')">
创建第一个Agent
</button>
</div>
`;
} else {
// 只显示最近3个Agent
const recentAgents = allAgents.slice(0, 3);
recentAgentsElement.innerHTML = '';
recentAgents.forEach(agent => {
const item = document.createElement('div');
item.className = 'agent-item';
const createdDate = new Date(agent.created_at * 1000);
const formattedDate = createdDate.toLocaleDateString();
// 添加学科和指导教师信息
const subjectInfo = agent.subject ? `<small class="text-muted me-3"><i class="bi bi-book me-1"></i>${agent.subject}</small>` : '';
const instructorInfo = agent.instructor ? `<small class="text-muted"><i class="bi bi-person me-1"></i>${agent.instructor}</small>` : '';
item.innerHTML = `
<div class="header">
<h5 class="title">${agent.name}</h5>
<span class="badge badge-primary">${agent.type || 'educational'}</span>
</div>
<div class="description">${agent.description || '暂无描述'}</div>
<div class="mt-2">
${subjectInfo}
${instructorInfo}
</div>
<div class="meta">
<div>
<i class="bi bi-calendar3"></i>
${formattedDate}
</div>
<div>
<i class="bi bi-share"></i>
${agent.distribution_count || 0}个分发
</div>
</div>
`;
recentAgentsElement.appendChild(item);
});
}
}
}
// 更新Agent列表页面
function updateAgentListPage() {
const agentListContainer = document.getElementById('agent-list-container');
if (agentListContainer) {
if (allAgents.length === 0) {
agentListContainer.innerHTML = `
<div class="alert alert-info">
<i class="bi bi-info-circle me-2"></i>
您还没有创建Agent
</div>
<div class="text-center">
<button class="btn btn-primary" onclick="switchSection('create-agent')">
创建第一个Agent
</button>
</div>
`;
} else {
agentListContainer.innerHTML = '';
allAgents.forEach(agent => {
const item = document.createElement('div');
item.className = 'agent-item';
const createdDate = new Date(agent.created_at * 1000);
const formattedDate = createdDate.toLocaleDateString();
// 构建插件和知识库标签
let pluginsHtml = '';
if (agent.plugins && agent.plugins.length > 0) {
pluginsHtml = '<div class="mt-2"><small class="text-muted me-2">插件:</small>';
agent.plugins.forEach(plugin => {
let pluginName = '未知';
let pluginIcon = 'puzzle';
if (plugin === 'code') {
pluginName = '代码执行';
pluginIcon = 'code-square';
} else if (plugin === 'visualization') {
pluginName = '3D可视化';
pluginIcon = 'bar-chart';
} else if (plugin === 'mindmap') {
pluginName = '思维导图';
pluginIcon = 'diagram-3';
}
pluginsHtml += `
<span class="badge bg-light text-dark me-1">
<i class="bi bi-${pluginIcon} me-1"></i>
${pluginName}
</span>
`;
});
pluginsHtml += '</div>';
}
let knowledgeHtml = '';
if (agent.knowledge_bases && agent.knowledge_bases.length > 0) {
knowledgeHtml = '<div class="mt-2"><small class="text-muted me-2">知识库:</small>';
agent.knowledge_bases.forEach(kb => {
// 查找知识库名称
const knowledge = allKnowledgeBases.find(k => k.id === kb);
const knowledgeName = knowledge ? knowledge.name : kb;
knowledgeHtml += `
<span class="badge bg-light text-dark me-1">
<i class="bi bi-journal-text me-1"></i>
${knowledgeName}
</span>
`;
});
knowledgeHtml += '</div>';
}
// 添加学科和指导教师信息
const subjectInfo = agent.subject ? `<small class="text-muted me-3"><i class="bi bi-book me-1"></i>${agent.subject}</small>` : '';
const instructorInfo = agent.instructor ? `<small class="text-muted"><i class="bi bi-person me-1"></i>${agent.instructor}</small>` : '';
item.innerHTML = `
<div class="header">
<h5 class="title">${agent.name}</h5>
<div>
<button class="btn btn-sm btn-primary me-1" onclick="showCreateDistributionModal('${agent.id}')">
<i class="bi bi-share"></i> 分发
</button>
<button class="btn btn-sm btn-outline-secondary me-1" onclick="showAgentDetail('${agent.id}')">
<i class="bi bi-info-circle"></i> 详情
</button>
<button class="btn btn-sm btn-outline-danger" onclick="deleteAgent('${agent.id}')">
<i class="bi bi-trash"></i>
</button>
</div>
</div>
<div class="description">${agent.description || '暂无描述'}</div>
<div class="mt-2">
${subjectInfo}
${instructorInfo}
</div>
${pluginsHtml}
${knowledgeHtml}
<div class="meta">
<div>
<i class="bi bi-calendar3"></i>
${formattedDate}
</div>
<div>
<i class="bi bi-share"></i>
${agent.distribution_count || 0}个分发
</div>
<div>
<i class="bi bi-eye"></i>
${agent.usage_count || 0}次使用
</div>
</div>
`;
agentListContainer.appendChild(item);
});
}
}
}
// 显示Agent详情
async function showAgentDetail(agentId) {
try {
// 获取Agent详细信息
const response = await fetch(`/api/agent/${agentId}`);
const result = await response.json();
if (result.success) {
const agent = result.agent;
// 更新模态框内容
const modalTitle = document.getElementById('agent-detail-title');
const modalBody = document.getElementById('agent-detail-body');
modalTitle.textContent = agent.name;
// 格式化创建时间
const createdDate = new Date(agent.created_at * 1000);
const formattedDate = createdDate.toLocaleString();
// 构建模态框内容
modalBody.innerHTML = `
<div class="mb-3">
<h6>描述</h6>
<p>${agent.description || '暂无描述'}</p>
</div>
<div class="row mb-3">
<div class="col-md-6">
<h6>学科/课程</h6>
<p>${agent.subject || agent.name}</p>
</div>
<div class="col-md-6">
<h6>指导教师</h6>
<p>${agent.instructor || '教师'}</p>
</div>
</div>
<div class="row mb-3">
<div class="col-md-6">
<h6>创建时间</h6>
<p>${formattedDate}</p>
</div>
<div class="col-md-6">
<h6>使用次数</h6>
<p>${agent.stats ? agent.stats.usage_count || 0 : 0}次</p>
</div>
</div>
<div class="mb-3">
<h6>插件</h6>
${getPluginsHtml(agent.plugins)}
</div>
<div class="mb-3">
<h6>知识库</h6>
${getKnowledgeBasesHtml(agent.knowledge_bases)}
</div>
<div class="mb-3">
<h6>分发链接</h6>
${getDistributionsHtml(agent.distributions, agent.id)}
</div>
<div class="mb-4">
<h6>工作流配置</h6>
<ul class="nav nav-tabs mb-3" id="detailWorkflowTabs" role="tablist">
<li class="nav-item" role="presentation">
<button class="nav-link active" id="detail-visual-tab" data-bs-toggle="tab" data-bs-target="#detail-visual-view" type="button" role="tab">可视化视图</button>
</li>
<li class="nav-item" role="presentation">
<button class="nav-link" id="detail-code-tab" data-bs-toggle="tab" data-bs-target="#detail-code-view" type="button" role="tab">代码视图</button>
</li>
</ul>
<div class="tab-content" id="detailWorkflowContent">
<div class="tab-pane fade show active" id="detail-visual-view" role="tabpanel">
<div id="detail-workflow-canvas" class="workflow-canvas"></div>
</div>
<div class="tab-pane fade" id="detail-code-view" role="tabpanel">
<div class="p-3 bg-light border rounded">
<pre class="mb-0">${JSON.stringify(agent.workflow, null, 2)}</pre>
</div>
</div>
</div>
</div>
`;
// 存储工作流数据
modalBody.dataset.agentWorkflow = JSON.stringify(agent.workflow);
// 显示模态框
const modal = new bootstrap.Modal(document.getElementById('agentDetailModal'));
modal.show();
// 在模态框显示后初始化工作流标签事件
document.getElementById('agentDetailModal').addEventListener('shown.bs.modal', function() {
// 初始化详情模态框内的标签切换
document.querySelectorAll('#detailWorkflowTabs .nav-link').forEach(tab => {
tab.addEventListener('click', function(event) {
event.preventDefault();
const targetId = this.getAttribute('data-bs-target');
const targetPane = document.querySelector(targetId);
// 切换标签激活状态
document.querySelectorAll('#detailWorkflowTabs .nav-link').forEach(t =>
t.classList.remove('active'));
this.classList.add('active');
// 切换内容区域显示状态
document.querySelectorAll('#detailWorkflowContent .tab-pane').forEach(p =>
p.classList.remove('show', 'active'));
targetPane.classList.add('show', 'active');
// 如果切换到可视化视图,重新渲染工作流
if (targetId === '#detail-visual-view') {
setTimeout(() => {
const workflow = JSON.parse(modalBody.dataset.agentWorkflow);
renderWorkflowVisualization(workflow, 'detail-workflow-canvas');
}, 200);
}
});
});
// 立即渲染工作流可视化
setTimeout(() => {
const workflow = JSON.parse(modalBody.dataset.agentWorkflow);
renderWorkflowVisualization(workflow, 'detail-workflow-canvas');
}, 200);
});
} else {
alert('获取Agent详情失败: ' + result.message);
}
} catch (error) {
console.error('获取Agent详情出错:', error);
alert('获取Agent详情出错,请查看控制台日志');
}
}
// 辅助函数,用于生成插件HTML
function getPluginsHtml(plugins) {
if (!plugins || plugins.length === 0) {
return '<p>无</p>';
}
let html = '<ul>';
plugins.forEach(plugin => {
let pluginName = '未知';
if (plugin === 'code') {
pluginName = '代码执行';
} else if (plugin === 'visualization') {
pluginName = '3D可视化';
} else if (plugin === 'mindmap') {
pluginName = '思维导图';
}
html += `<li>${pluginName}</li>`;
});
html += '</ul>';
return html;
}
// 辅助函数,用于生成知识库HTML
function getKnowledgeBasesHtml(knowledgeBases) {
if (!knowledgeBases || knowledgeBases.length === 0) {
return '<p>无</p>';
}
let html = '<ul>';
knowledgeBases.forEach(kb => {
// 查找知识库名称
const knowledge = allKnowledgeBases.find(k => k.id === kb);
const knowledgeName = knowledge ? knowledge.name : kb;
html += `<li>${knowledgeName}</li>`;
});
html += '</ul>';
return html;
}
// 辅助函数,用于生成分发链接HTML
function getDistributionsHtml(distributions, agentId) {
if (!distributions || distributions.length === 0) {
return '<p>无</p>';
}
let html = '<div class="list-group">';
distributions.forEach(dist => {
const expiryText = dist.expires_at > 0
? `过期时间: ${new Date(dist.expires_at * 1000).toLocaleString()}`
: '永不过期';
const url = `/student/${agentId}?token=${dist.token}`;
html += `
<div class="distribution-link">
<div>
<div class="mb-1">${url}</div>
<small class="text-muted">${expiryText} | 使用次数: ${dist.usage_count || 0}</small>
</div>
<button class="btn btn-sm btn-outline-primary" onclick="copyToClipboard('${url}')">
<i class="bi bi-clipboard"></i> 复制
</button>
</div>
`;
});
html += '</div>';
return html;
}
// 删除Agent
async function deleteAgent(agentId) {
if (!confirm('确定要删除这个Agent吗?此操作不可恢复。')) {
return;
}
try {
const response = await fetch(`/api/agent/${agentId}`, {
method: 'DELETE'
});
const result = await response.json();
if (result.success) {
alert('Agent删除成功!');
loadAgents(); // 重新加载Agent列表
} else {
alert('删除Agent失败: ' + result.message);
}
} catch (error) {
console.error('删除Agent出错:', error);
alert('删除Agent出错,请查看控制台日志');
}
}
// 显示创建分发链接模态框
function showCreateDistributionModal(agentId) {
document.getElementById('distribution-agent-id').value = agentId;
// 显示模态框
const modal = new bootstrap.Modal(document.getElementById('createDistributionModal'));
modal.show();
}
// 创建分发链接
async function createDistribution() {
const agentId = document.getElementById('distribution-agent-id').value;
const expiresIn = parseInt(document.getElementById('distribution-expires').value);
if (!agentId) {
alert('Agent ID不能为空');
return;
}
try {
const response = await fetch(`/api/agent/${agentId}/distribute`, {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({ expires_in: expiresIn })
});
const result = await response.json();
if (result.success) {
// 隐藏创建模态框
const createModal = bootstrap.Modal.getInstance(document.getElementById('createDistributionModal'));
createModal.hide();
// 更新分发链接输入框
const fullUrl = window.location.origin + result.distribution.link;
document.getElementById('distribution-link').value = fullUrl;
// 显示分发链接模态框
const showModal = new bootstrap.Modal(document.getElementById('showDistributionModal'));
showModal.show();
// 重新加载Agent列表
loadAgents();
} else {
alert('创建分发链接失败: ' + result.message);
}
} catch (error) {
console.error('创建分发链接出错:', error);
alert('创建分发链接出错,请查看控制台日志');
}
}
// 复制到剪贴板
function copyToClipboard(textOrElementId) {
let text;
if (textOrElementId.startsWith('/') || textOrElementId.startsWith('http')) {
// 直接传入的文本
text = textOrElementId;
} else {
// 传入的是元素ID
const element = document.getElementById(textOrElementId);
text = element.value || element.textContent;
}
navigator.clipboard.writeText(text).then(() => {
alert('已复制到剪贴板!');
}).catch(err => {
console.error('复制失败:', err);
});
}
// 根据节点类型获取图标
function getNodeIcon(type) {
switch (type) {
case 'intent_recognition':
return 'AI';
case 'knowledge_query':
return 'KB';
case 'plugin_call':
return 'API';
case 'generate_response':
return 'AI';
default:
return '';
}
}
// 渲染工作流可视化
function renderWorkflowVisualization(workflow, containerId) {
const container = document.getElementById(containerId);
if (!container) return;
// 清空容器
container.innerHTML = '';
// 如果没有工作流数据,显示提示
if (!workflow || !workflow.nodes || workflow.nodes.length === 0) {
container.innerHTML = `
<div class="p-4 text-center text-muted">
暂无工作流数据
</div>
`;
return;
}
// 创建或重置jsPlumb实例
if (jsPlumbWorkflowInstance) {
jsPlumbWorkflowInstance.reset();
}
jsPlumbWorkflowInstance = jsPlumb.getInstance({
Endpoint: ["Dot", { radius: 4 }],
Connector: ["Bezier", { curviness: 50 }],
PaintStyle: { stroke: "#4361ee", strokeWidth: 2 },
HoverPaintStyle: { stroke: "#3a56e4", strokeWidth: 3 },
ConnectionOverlays: [
["Arrow", { location: 1, width: 10, length: 10, foldback: 0.7 }]
],
Container: containerId
});
// 创建节点
workflow.nodes.forEach(node => {
const nodeEl = document.createElement('div');
nodeEl.id = node.id;
nodeEl.className = `workflow-node node-type-${node.type}`;
nodeEl.innerHTML = `
<div class="node-icon">${getNodeIcon(node.type)}</div>
<div class="node-content">${node.data.name}</div>
`;
container.appendChild(nodeEl);
});
// 计算节点依赖关系
const dependencies = {};
const dependents = {};
workflow.nodes.forEach(node => {
dependencies[node.id] = [];
dependents[node.id] = [];
});
workflow.edges.forEach(edge => {
dependencies[edge.target].push(edge.source);
dependents[edge.source].push(edge.target);
});
// 计算层级
const nodeLevels = {};
const queue = [];
// 找到没有依赖的节点(入度为0的节点)
workflow.nodes.forEach(node => {
if (dependencies[node.id].length === 0) {
nodeLevels[node.id] = 0;
queue.push(node.id);
}
});
// BFS计算层级
while (queue.length > 0) {
const currentId = queue.shift();
const currentLevel = nodeLevels[currentId];
dependents[currentId].forEach(dependentId => {
const allDependenciesProcessed = dependencies[dependentId].every(
depId => nodeLevels[depId] !== undefined
);
if (allDependenciesProcessed) {
const maxDependencyLevel = Math.max(
...dependencies[dependentId].map(depId => nodeLevels[depId])
);
nodeLevels[dependentId] = maxDependencyLevel + 1;
queue.push(dependentId);
}
});
}
// 按层级对节点进行布局
const levelGroups = {};
Object.keys(nodeLevels).forEach(nodeId => {
const level = nodeLevels[nodeId];
if (!levelGroups[level]) {
levelGroups[level] = [];
}
levelGroups[level].push(nodeId);
});
const containerWidth = container.clientWidth;
const containerHeight = container.clientHeight;
const levelCount = Object.keys(levelGroups).length;
const levelHeight = containerHeight / (levelCount > 1 ? levelCount : 2);
Object.entries(levelGroups).forEach(([level, nodeIds]) => {
const levelIndex = parseInt(level);
const nodeCount = nodeIds.length;
const nodeWidth = containerWidth / (nodeCount + 1);
nodeIds.forEach((nodeId, index) => {
const node = document.getElementById(nodeId);
if (node) {
node.style.top = `${levelIndex * levelHeight + 50}px`;
node.style.left = `${(index + 1) * nodeWidth - (node.clientWidth / 2)}px`;
}
});
});
// 为每个节点创建可拖动功能
workflow.nodes.forEach(node => {
const nodeEl = document.getElementById(node.id);
if (nodeEl) {
jsPlumbWorkflowInstance.draggable(nodeEl, {
containment: container
});
}
});
// 添加连接
setTimeout(() => {
workflow.edges.forEach(edge => {
const sourceEl = document.getElementById(edge.source);
const targetEl = document.getElementById(edge.target);
if (sourceEl && targetEl) {
const connection = jsPlumbWorkflowInstance.connect({
source: sourceEl,
target: targetEl,
anchors: ["Bottom", "Top"],
connector: ["Bezier", { curviness: 50 }],
paintStyle: { stroke: "#4361ee", strokeWidth: 2 },
endpoint: "Blank",
overlays: [
["Arrow", { location: 1, width: 10, length: 10, foldback: 0.7 }]
]
});
// 添加连接标签
if (edge.condition) {
connection.addOverlay([
"Label", {
label: edge.condition,
cssClass: "connection-label",
location: 0.5
}
]);
}
}
});
// 刷新所有连接
jsPlumbWorkflowInstance.repaintEverything();
}, 100);
}
</script>
</body>
</html>