feat: AI聊天室多Agent协作讨论平台
- 实现Agent管理,支持AI辅助生成系统提示词 - 支持多个AI提供商(OpenRouter、智谱、MiniMax等) - 实现聊天室和讨论引擎 - WebSocket实时消息推送 - 前端使用React + Ant Design - 后端使用FastAPI + MongoDB Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
This commit is contained in:
245
frontend/src/pages/DiscussionHistory.tsx
Normal file
245
frontend/src/pages/DiscussionHistory.tsx
Normal file
@@ -0,0 +1,245 @@
|
||||
/**
|
||||
* 讨论历史页面
|
||||
*/
|
||||
import React, { useEffect, useState } from 'react'
|
||||
import {
|
||||
Table, Card, Tag, Typography, Space, Button,
|
||||
Modal, List, Descriptions, Progress, Empty
|
||||
} from 'antd'
|
||||
import {
|
||||
CheckCircleOutlined, CloseCircleOutlined,
|
||||
EyeOutlined, RobotOutlined
|
||||
} from '@ant-design/icons'
|
||||
import { discussionApi } from '../services/api'
|
||||
import type { DiscussionResult } from '../types'
|
||||
import dayjs from 'dayjs'
|
||||
|
||||
const { Title, Text, Paragraph } = Typography
|
||||
|
||||
const DiscussionHistory: React.FC = () => {
|
||||
const [discussions, setDiscussions] = useState<DiscussionResult[]>([])
|
||||
const [total, setTotal] = useState(0)
|
||||
const [loading, setLoading] = useState(false)
|
||||
const [detailVisible, setDetailVisible] = useState(false)
|
||||
const [selectedDiscussion, setSelectedDiscussion] = useState<DiscussionResult | null>(null)
|
||||
|
||||
const fetchDiscussions = async (page = 1, pageSize = 20) => {
|
||||
setLoading(true)
|
||||
try {
|
||||
const result = await discussionApi.list(undefined, pageSize)
|
||||
setDiscussions(result.discussions)
|
||||
setTotal(result.total)
|
||||
} catch (e) {
|
||||
console.error('Failed to fetch discussions:', e)
|
||||
} finally {
|
||||
setLoading(false)
|
||||
}
|
||||
}
|
||||
|
||||
useEffect(() => {
|
||||
fetchDiscussions()
|
||||
}, [])
|
||||
|
||||
const showDetail = (record: DiscussionResult) => {
|
||||
setSelectedDiscussion(record)
|
||||
setDetailVisible(true)
|
||||
}
|
||||
|
||||
const columns = [
|
||||
{
|
||||
title: '讨论目标',
|
||||
dataIndex: 'objective',
|
||||
key: 'objective',
|
||||
width: 300,
|
||||
ellipsis: true
|
||||
},
|
||||
{
|
||||
title: '状态',
|
||||
dataIndex: 'consensus_reached',
|
||||
key: 'consensus_reached',
|
||||
width: 120,
|
||||
render: (reached: boolean, record: DiscussionResult) => (
|
||||
<Space>
|
||||
{reached ? (
|
||||
<Tag color="success" icon={<CheckCircleOutlined />}>达成共识</Tag>
|
||||
) : (
|
||||
<Tag color="default" icon={<CloseCircleOutlined />}>未达成</Tag>
|
||||
)}
|
||||
</Space>
|
||||
)
|
||||
},
|
||||
{
|
||||
title: '置信度',
|
||||
dataIndex: 'confidence',
|
||||
key: 'confidence',
|
||||
width: 120,
|
||||
render: (confidence: number) => (
|
||||
<Progress
|
||||
percent={Math.round(confidence * 100)}
|
||||
size="small"
|
||||
strokeColor={confidence >= 0.8 ? '#52c41a' : confidence >= 0.5 ? '#faad14' : '#ff4d4f'}
|
||||
/>
|
||||
)
|
||||
},
|
||||
{
|
||||
title: '轮数',
|
||||
dataIndex: 'total_rounds',
|
||||
key: 'total_rounds',
|
||||
width: 80
|
||||
},
|
||||
{
|
||||
title: '消息数',
|
||||
dataIndex: 'total_messages',
|
||||
key: 'total_messages',
|
||||
width: 80
|
||||
},
|
||||
{
|
||||
title: '参与Agent',
|
||||
dataIndex: 'participating_agents',
|
||||
key: 'participating_agents',
|
||||
width: 150,
|
||||
render: (agents: string[]) => (
|
||||
<Text type="secondary">{agents.length} 个</Text>
|
||||
)
|
||||
},
|
||||
{
|
||||
title: '时间',
|
||||
dataIndex: 'created_at',
|
||||
key: 'created_at',
|
||||
width: 180,
|
||||
render: (time: string) => dayjs(time).format('YYYY-MM-DD HH:mm')
|
||||
},
|
||||
{
|
||||
title: '操作',
|
||||
key: 'actions',
|
||||
width: 80,
|
||||
render: (_: unknown, record: DiscussionResult) => (
|
||||
<Button
|
||||
type="link"
|
||||
icon={<EyeOutlined />}
|
||||
onClick={() => showDetail(record)}
|
||||
>
|
||||
详情
|
||||
</Button>
|
||||
)
|
||||
}
|
||||
]
|
||||
|
||||
return (
|
||||
<div style={{ marginLeft: 200 }}>
|
||||
<Title level={2}>讨论历史</Title>
|
||||
|
||||
<Card>
|
||||
<Table
|
||||
dataSource={discussions}
|
||||
columns={columns}
|
||||
rowKey="discussion_id"
|
||||
loading={loading}
|
||||
pagination={{
|
||||
total,
|
||||
pageSize: 20,
|
||||
showTotal: (t) => `共 ${t} 条记录`
|
||||
}}
|
||||
/>
|
||||
</Card>
|
||||
|
||||
{/* 详情弹窗 */}
|
||||
<Modal
|
||||
title="讨论详情"
|
||||
open={detailVisible}
|
||||
onCancel={() => setDetailVisible(false)}
|
||||
footer={null}
|
||||
width={800}
|
||||
>
|
||||
{selectedDiscussion && (
|
||||
<div>
|
||||
<Descriptions bordered column={2}>
|
||||
<Descriptions.Item label="讨论目标" span={2}>
|
||||
{selectedDiscussion.objective}
|
||||
</Descriptions.Item>
|
||||
<Descriptions.Item label="状态">
|
||||
{selectedDiscussion.consensus_reached ? (
|
||||
<Tag color="success">达成共识</Tag>
|
||||
) : (
|
||||
<Tag color="default">未达成共识</Tag>
|
||||
)}
|
||||
</Descriptions.Item>
|
||||
<Descriptions.Item label="置信度">
|
||||
<Progress
|
||||
percent={Math.round(selectedDiscussion.confidence * 100)}
|
||||
size="small"
|
||||
style={{ width: 150 }}
|
||||
/>
|
||||
</Descriptions.Item>
|
||||
<Descriptions.Item label="总轮数">
|
||||
{selectedDiscussion.total_rounds}
|
||||
</Descriptions.Item>
|
||||
<Descriptions.Item label="总消息数">
|
||||
{selectedDiscussion.total_messages}
|
||||
</Descriptions.Item>
|
||||
<Descriptions.Item label="结束原因" span={2}>
|
||||
{selectedDiscussion.end_reason || '无'}
|
||||
</Descriptions.Item>
|
||||
<Descriptions.Item label="开始时间">
|
||||
{dayjs(selectedDiscussion.created_at).format('YYYY-MM-DD HH:mm:ss')}
|
||||
</Descriptions.Item>
|
||||
<Descriptions.Item label="结束时间">
|
||||
{selectedDiscussion.completed_at
|
||||
? dayjs(selectedDiscussion.completed_at).format('YYYY-MM-DD HH:mm:ss')
|
||||
: '进行中'}
|
||||
</Descriptions.Item>
|
||||
</Descriptions>
|
||||
|
||||
<Card title="结果摘要" style={{ marginTop: 16 }}>
|
||||
<Paragraph>{selectedDiscussion.summary || '暂无摘要'}</Paragraph>
|
||||
</Card>
|
||||
|
||||
<Card title="行动项" style={{ marginTop: 16 }}>
|
||||
{selectedDiscussion.action_items.length > 0 ? (
|
||||
<List
|
||||
dataSource={selectedDiscussion.action_items}
|
||||
renderItem={(item) => (
|
||||
<List.Item>
|
||||
<CheckCircleOutlined style={{ color: '#52c41a', marginRight: 8 }} />
|
||||
{item}
|
||||
</List.Item>
|
||||
)}
|
||||
/>
|
||||
) : (
|
||||
<Empty description="暂无行动项" />
|
||||
)}
|
||||
</Card>
|
||||
|
||||
<Card title="未解决问题" style={{ marginTop: 16 }}>
|
||||
{selectedDiscussion.unresolved_issues.length > 0 ? (
|
||||
<List
|
||||
dataSource={selectedDiscussion.unresolved_issues}
|
||||
renderItem={(item) => (
|
||||
<List.Item>
|
||||
<CloseCircleOutlined style={{ color: '#faad14', marginRight: 8 }} />
|
||||
{item}
|
||||
</List.Item>
|
||||
)}
|
||||
/>
|
||||
) : (
|
||||
<Empty description="无未解决问题" />
|
||||
)}
|
||||
</Card>
|
||||
|
||||
<Card title="Agent贡献" style={{ marginTop: 16 }}>
|
||||
<Space wrap>
|
||||
{Object.entries(selectedDiscussion.agent_contributions).map(([agentId, count]) => (
|
||||
<Tag key={agentId} icon={<RobotOutlined />}>
|
||||
{agentId}: {count}条消息
|
||||
</Tag>
|
||||
))}
|
||||
</Space>
|
||||
</Card>
|
||||
</div>
|
||||
)}
|
||||
</Modal>
|
||||
</div>
|
||||
)
|
||||
}
|
||||
|
||||
export default DiscussionHistory
|
||||
Reference in New Issue
Block a user