diff --git a/dashboard/src/components/SupervisionGraphEditor.vue b/dashboard/src/components/SupervisionGraphEditor.vue
new file mode 100644
index 0000000..e30bfa8
--- /dev/null
+++ b/dashboard/src/components/SupervisionGraphEditor.vue
@@ -0,0 +1,1159 @@
+
+
+
+
+
+
+
+ {{ errorMsg }}
+
+
+
+
+
+
+
+
+
+
+ 🔗 请点击目标 Agent 建立监督关系,或按 Esc 取消
+
+
+
+
+
+
+
+
+
+
+ 监督者
+ {{ getNodeName(selectedEdge.source) }}
+
+
+ 被监督者
+ {{ getNodeName(selectedEdge.target) }}
+
+
+
+
+
+
+ ★
+
+
{{ ['宽松', '较松', '适中', '严格', '极严'][selectedEdge.strictness - 1] }}
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ |
+
+
+
+
+
+
+ |
+ —
+
+ ✓
+
+
+ |
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/dashboard/src/views/ChatRoomView.vue b/dashboard/src/views/ChatRoomView.vue
index d100a0e..f3f52bb 100644
--- a/dashboard/src/views/ChatRoomView.vue
+++ b/dashboard/src/views/ChatRoomView.vue
@@ -5,6 +5,7 @@ import { parallelStreamManager } from '../utils/parallelStreamManager.js'
import { useParallelStreamStore } from '../utils/parallelStreamStore.js'
import MessageBubble from '../components/MessageBubble.vue'
import ParallelMessages from '../components/ParallelMessages.vue'
+import SupervisionGraphEditor from '../components/SupervisionGraphEditor.vue'
const store = useParallelStreamStore()
@@ -52,6 +53,133 @@ const creating = ref(false)
const newRoom = ref({ title: '', task: '', max_rounds: 5, agent_ids: [], execution_mode: 'sequential' })
const showAddToRoom = ref(false)
+// ============ Wizard state (Supervision Graph) ============
+const showCreateWizard = ref(false)
+const wizardStep = ref(1)
+const wizardData = ref({
+ title: '',
+ task: '',
+ max_rounds: 5,
+ execution_mode: 'sequential',
+ agent_ids: [],
+ agent_roles: {},
+ supervision_edges: []
+})
+const wizardRef = ref(null) // 用于参考 SuperGraphEditor 组件
+
+// Wizard navigation
+function openWizard() {
+ wizardStep.value = 1
+ wizardData.value = {
+ title: '',
+ task: '',
+ max_rounds: 5,
+ execution_mode: 'sequential',
+ agent_ids: [],
+ agent_roles: {},
+ supervision_edges: []
+ }
+ showCreateWizard.value = true
+}
+
+function closeWizard() {
+ showCreateWizard.value = false
+ wizardStep.value = 1
+}
+
+function nextWizardStep() {
+ if (wizardStep.value < 4) wizardStep.value++
+}
+
+function prevWizardStep() {
+ if (wizardStep.value > 1) wizardStep.value--
+}
+
+function selectWizardTemplate(templateId) {
+ // 根据任务模板填充默认的 task 描述
+ const templates = {
+ custom: '请描述需要 Agent 团队完成的任务...',
+ code_review: '请对以下代码进行全面的审查和改进:\n\n```\n\n```',
+ architecture: '请设计一个系统架构方案,涵盖:\n1. 系统架构概览\n2. 核心模块设计\n3. 数据流设计\n4. 技术选型',
+ debugging: '请协助排查以下问题:\n\n问题描述:\n\n复现步骤:\n\n错误日志:'
+ }
+ if (templates[templateId] && !wizardData.value.task) {
+ wizardData.value.task = templates[templateId]
+ }
+}
+
+function toggleWizardAgent(agentId) {
+ const idx = wizardData.value.agent_ids.indexOf(agentId)
+ if (idx >= 0) {
+ wizardData.value.agent_ids.splice(idx, 1)
+ delete wizardData.value.agent_roles[agentId]
+ } else {
+ wizardData.value.agent_ids.push(agentId)
+ wizardData.value.agent_roles[agentId] = 'producer'
+ }
+}
+
+function onSupervisionChange(config) {
+ if (config) {
+ wizardData.value.agent_roles = config.roles || {}
+ wizardData.value.supervision_edges = config.edges || []
+ }
+}
+
+async function createRoomFromWizard() {
+ const data = wizardData.value
+ if (!data.title || !data.task || data.agent_ids.length === 0) return
+
+ creating.value = true
+ try {
+ // 获取 SupervisionGraphEditor 的完整配置
+ let supervisionConfig = null
+ if (wizardRef.value && data.execution_mode === 'review_loop') {
+ supervisionConfig = wizardRef.value.getSupervisionConfig()
+ }
+
+ // 构建 agent 列表
+ const agents = data.agent_ids
+ .map(id => agentPool.value.find(a => a.id === id))
+ .filter(Boolean)
+ .map((a, idx) => {
+ const base = { agent_id: a.id }
+ if (data.execution_mode === 'review_loop') {
+ // 添加监督角色和边配置
+ const role = data.agent_roles[a.id] || 'producer'
+ const edgeConfig = supervisionConfig?.edges?.filter(e => e.source === a.id || e.target === a.id) || []
+ return {
+ ...base,
+ agent_type: role,
+ reviews_for: JSON.stringify(
+ edgeConfig.filter(e => e.source === a.id).map(e => e.target)
+ ),
+ reviewed_by: JSON.stringify(
+ edgeConfig.filter(e => e.target === a.id).map(e => e.source)
+ )
+ }
+ }
+ return base
+ })
+
+ const res = await chatRoomsAPI.create({
+ title: data.title,
+ task: data.task,
+ max_rounds: data.max_rounds,
+ execution_mode: data.execution_mode,
+ agents
+ })
+ closeWizard()
+ await loadRooms()
+ const created = res.data
+ if (created?.id) selectRoom(created.id)
+ } catch (e) {
+ console.error('Failed to create room:', e)
+ } finally {
+ creating.value = false
+ }
+}
+
// ============ Selected room state ============
const selectedId = ref(null)
const room = ref(null)
@@ -581,6 +709,33 @@ function randomColor() {
return colors[Math.floor(Math.random() * colors.length)]
}
+// ============ Wizard helpers ============
+function getAgentColor(id) {
+ const agent = agentPool.value.find(a => a.id === id)
+ return agent?.color || '#3b82f6'
+}
+
+function getAgentName(id) {
+ const agent = agentPool.value.find(a => a.id === id)
+ return agent?.name || `Agent ${id}`
+}
+
+function getSelectedAgents() {
+ return wizardData.value.agent_ids
+ .map(id => agentPool.value.find(a => a.id === id))
+ .filter(Boolean)
+}
+
+function getRoleLabelText(role) {
+ const labels = {
+ producer: '🎨 Producer - 方案提出者',
+ reviewer: '🔍 Reviewer - 审查员',
+ executor: '⚙️ Executor - 执行者',
+ observer: '👁️ Observer - 观察员'
+ }
+ return labels[role] || role
+}
+
watch(messages, () => { nextTick(scrollToBottom) }, { deep: true })
// 启动聊天室时强制滚动
@@ -639,7 +794,7 @@ onUnmounted(() => {
@@ -755,6 +911,7 @@ onUnmounted(() => {
>
+
@@ -886,6 +1043,7 @@ onUnmounted(() => {
@@ -966,6 +1124,258 @@ onUnmounted(() => {
+
+
+
+
+
+
+
+
+
+
+ 1
+ 基本信息
+
+
+
+ 2
+ 选择 Agent
+
+
+
+ 3
+ {{ wizardData.execution_mode === 'review_loop' ? '监督关系' : '确认' }}
+
+
+
+ 4
+ 确认创建
+
+
+
+
+
+
+
📋 基本设置
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ 🔍
+ 代码审查
+
+
+ 🏗️
+ 架构设计
+
+
+ 🐛
+ 调试排查
+
+
+ 📝
+ 自定义
+
+
+
+
+
+
+
🤖 选择参与讨论的 Agent
+
+
暂无可用 Agent,请先在 Agent 池中创建
+
+
+
+
+
+
{{ agent.name.charAt(0) }}
+
+ {{ agent.name }}
+ {{ agent.role || '通用' }}
+
+
{{ agent.model || 'default' }}
+
+
+
+ 已选择 {{ wizardData.agent_ids.length }} 个 Agent
+
+
请选择至少 1 个 Agent
+
+
+
+
+
+ 🔗 配置监督关系
+
+
设置每个 Agent 的角色类型,以及它们之间的监督关系。
+
+ - 🎨 Producer - 方案提出者
+ - 🔍 Reviewer - 审查员(可指定能力标签)
+ - ⚙️ Executor - 执行者
+ - 👁️ Observer - 观察员
+
+
+
+
+
+
Agent 角色分配
+
+
+
+ {{ getAgentName(agentId).charAt(0) }}
+
+
{{ getAgentName(agentId) }}
+
+
+
+
+
+
+ 监督关系图
+ 拖拽节点移动 · 双击节点连线 · 点击边配置 · 按 Delete 删除边
+
+
+
+
+ ✅ 确认 Agent 选择
+ 请先选择 Agent
+
+
+
+ {{ getAgentName(agentId).charAt(0) }}
+
+ {{ getAgentName(agentId) }}
+
+
+
+
+
+
+
+
📋 确认配置
+
+
+
基本信息
+
标题: {{ wizardData.title }}
+
执行模式: {{ wizardData.execution_mode === 'review_loop' ? '🔄 监督循环' : wizardData.execution_mode === 'parallel' ? '⚡ 并行' : '📋 串行' }}
+
最大轮次: {{ wizardData.max_rounds }}
+
任务: {{ wizardData.task }}
+
+
+
Agent 列表
+
+
+
+ {{ getAgentName(agentId).charAt(0) }}
+
+
+ {{ getAgentName(agentId) }}
+
+ {{ getRoleLabelText(wizardData.agent_roles[agentId]) }}
+
+
+
+
+
+
+
监督关系 ({{ wizardData.supervision_edges.length }} 条)
+
+
+ {{ getAgentName(edge.source) }}
+ →
+ {{ getAgentName(edge.target) }}
+ ★{{ edge.strictness }}
+ {{ edge.focus }}
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/luxx/routes/chat_rooms.py b/luxx/routes/chat_rooms.py
index 2d8c65b..f56bf6e 100644
--- a/luxx/routes/chat_rooms.py
+++ b/luxx/routes/chat_rooms.py
@@ -25,12 +25,19 @@ class AgentConfig(BaseModel):
model: str = ""
system_prompt: str = "You are a helpful AI assistant."
color: str = "#2563eb"
+ # Supervision fields
+ agent_type: str = "producer" # producer | reviewer | executor | observer
+ reviews_for: Optional[str] = None # JSON: [agent_id_1, agent_id_2]
+ reviewed_by: Optional[str] = None # JSON: [agent_id_1, agent_id_2]
+ review_strictness: int = 3
+ capability_tags: Optional[str] = None # JSON: ["security", "performance"]
class ChatRoomCreate(BaseModel):
title: str
task: str
max_rounds: int = 5
+ execution_mode: str = "sequential" # sequential | parallel | review_loop
agents: List[AgentConfig] = []
@@ -39,6 +46,7 @@ class ChatRoomUpdate(BaseModel):
task: Optional[str] = None
max_rounds: Optional[int] = None
status: Optional[str] = None
+ execution_mode: Optional[str] = None
class AgentCreate(BaseModel):
@@ -93,7 +101,8 @@ def create_room(
user_id=current_user.id,
title=data.title,
task=data.task,
- max_rounds=data.max_rounds
+ max_rounds=data.max_rounds,
+ execution_mode=data.execution_mode
)
db.add(room)
db.flush()
@@ -152,7 +161,12 @@ def create_room(
model=model,
system_prompt=system_prompt,
color=color,
- turn_order=i
+ turn_order=i,
+ agent_type=agent_cfg.agent_type,
+ reviews_for=agent_cfg.reviews_for,
+ reviewed_by=agent_cfg.reviewed_by,
+ review_strictness=agent_cfg.review_strictness,
+ capability_tags=agent_cfg.capability_tags
)
db.add(agent)