Files
deerflow-code/web-code/pages/AgentTrial.tsx
T
2026-05-27 19:43:15 +08:00

533 lines
21 KiB
TypeScript
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
import React, { useState } from 'react';
import {
Bot,
PlayCircle,
ArrowLeft,
Send,
Clock,
CheckCircle,
AlertCircle,
FileText,
Sparkles,
User,
Settings,
Zap,
BookOpen,
Lightbulb,
Target,
Users,
MessageSquare,
} from 'lucide-react';
import { useTheme } from '../contexts/ThemeContext';
import { PageType } from '../components/Sidebar';
interface AgentTrialProps {
onNavigate: (page: PageType) => void;
}
// 单智能体能力卡片
const agentCapabilities = [
{
id: 'goal',
name: 'GoalAgent',
role: '目标分析专家',
description: '分析业务目标,提取核心KPI和成功标准',
icon: Target,
color: 'blue',
skills: ['目标分解', 'KPI提取', '优先级排序'],
example: '分析这次五四青年节宣传的核心目标是什么?',
},
{
id: 'audience',
name: 'AudienceAgent',
role: '受众分析专家',
description: '定义目标受众画像,分析用户特征和需求',
icon: Users,
color: 'cyan',
skills: ['用户画像', '行为分析', '需求挖掘'],
example: '谁是我们的核心受众?他们的特征是什么?',
},
{
id: 'behavior',
name: 'BehaviorAgent',
role: '行为设计专家',
description: '设计用户行为路径和转化漏斗',
icon: Sparkles,
color: 'green',
skills: ['行为设计', '转化路径', '激励策略'],
example: '我们希望用户产生什么样的行为?',
},
{
id: 'driver',
name: 'DriverAgent',
role: '驱动因素专家',
description: '分析影响用户决策的核心驱动因素',
icon: Lightbulb,
color: 'yellow',
skills: ['驱动分析', '动机挖掘', '阻力识别'],
example: '什么因素会驱动用户做出我们期望的行为?',
},
{
id: 'narrative',
name: 'NarrativeAgent',
role: '叙事策略专家',
description: '设计品牌叙事框架和内容策略',
icon: MessageSquare,
color: 'purple',
skills: ['故事设计', '内容策略', '情感共鸣'],
example: '我们应该如何讲述品牌故事?',
},
{
id: 'risk',
name: 'RiskAgent',
role: '风险评估专家',
description: '识别项目风险,制定应对策略',
icon: AlertCircle,
color: 'red',
skills: ['风险识别', '影响评估', '应对策略'],
example: '这个方案有哪些潜在风险?',
},
];
const AgentTrial: React.FC<AgentTrialProps> = ({ onNavigate }) => {
const { theme } = useTheme();
const isDark = theme === 'dark';
// 主题颜色类
const cardBg = isDark ? 'bg-slate-800/30' : 'bg-white';
const cardBorder = isDark ? 'border-blue-800/30' : 'border-red-200';
const textPrimary = isDark ? 'text-white' : 'text-gray-900';
const textSecondary = isDark ? 'text-slate-400' : 'text-gray-600';
const textMuted = isDark ? 'text-slate-500' : 'text-gray-500';
const btnSecondary = isDark ? 'bg-slate-700/50 hover:bg-slate-600/50 border-blue-800/30 text-slate-300' : 'bg-gray-100 hover:bg-gray-200 border-gray-300 text-gray-700';
const btnPrimary = isDark ? 'bg-blue-600 hover:bg-blue-500' : 'bg-red-600 hover:bg-red-500';
const inputBg = isDark ? 'bg-slate-900/50' : 'bg-gray-50';
const inputBorder = isDark ? 'border-blue-800/30' : 'border-gray-300';
const [selectedAgent, setSelectedAgent] = useState<string | null>(null);
const [inputText, setInputText] = useState('');
const [isRunning, setIsRunning] = useState(false);
const [conversation, setConversation] = useState<Array<{ role: 'user' | 'agent'; content: string; time: string }>>([]);
const currentAgent = agentCapabilities.find((a) => a.id === selectedAgent);
const handleSelectAgent = (agentId: string) => {
setSelectedAgent(agentId);
const agent = agentCapabilities.find((a) => a.id === agentId);
if (agent) {
setInputText(agent.example);
}
};
const handleSend = async () => {
if (!inputText.trim() || !currentAgent) return;
const now = new Date().toLocaleTimeString('zh-CN', { hour: '2-digit', minute: '2-digit' });
// 添加用户消息
setConversation((prev) => [...prev, { role: 'user', content: inputText, time: now }]);
const userInput = inputText;
setInputText('');
setIsRunning(true);
// 模拟智能体回复
setTimeout(() => {
const responseTime = new Date().toLocaleTimeString('zh-CN', { hour: '2-digit', minute: '2-digit' });
let response = '';
switch (currentAgent.id) {
case 'goal':
response = `【目标分析完成】
基于您的描述,我分析出以下核心目标:
1. **主目标**:提升品牌在Z世代用户中的认知度
2. **次目标**:建立品牌年轻化形象,增强情感连接
3. **量化指标**:
- 曝光量:500万+
- 互动率:5%+
- UGC数量:1000+
建议优先聚焦主目标,确保资源集中。`;
break;
case 'audience':
response = `【受众分析完成】
核心受众画像:
**人口特征**
- 年龄:18-25岁
- 身份:学生、基层青年工作者
- 地域:一二线城市为主
**行为特征**
- 社交媒体重度用户
- 短视频爱好者
- 注重真实性和态度
**核心需求**
- 社交认同与自我表达
- 归属感与群体认同
- 性价比与实用性`;
break;
case 'behavior':
response = `【行为设计完成】
目标行为路径:
浏览 → 点赞 → 评论 → 转发 → 创作
**关键转化节点**
1. 引起注意:内容需有话题性
2. 激发互动:设置讨论话题
3. 促进创作:提供UGC模板和激励
**激励机制建议**
- 虚拟勋章/积分
- 优质内容曝光
- 创作者社群归属`;
break;
case 'driver':
response = `【驱动因素分析完成】
核心驱动因素:
1. **社交认同需求**
- 年轻人渴望被关注和认可
- UGC是自我表达的重要方式
2. **群体归属感**
- 青年节与年轻人天然关联
- 参与活动获得身份认同
3. **表达欲驱动**
- 有态度的品牌内容激发创作
- 提供创作框架降低门槛
**主要阻力**
- 注意力的稀缺
- 创作成本感知`;
break;
case 'narrative':
response = `【叙事策略完成】
核心叙事框架:
**主题**:真实、有态度的青年故事
**叙事结构**
1. 讲述真实的人物故事
2. 引发情感共鸣
3. 传递品牌价值观
4. 激发用户参与
**内容类型**
- 人物专访型
- 事件记录型
- 观点表达型
**关键要素**
- 真实感 > 精致感
- 态度感 > 说教感
- 参与感 > 观看感`;
break;
case 'risk':
response = `【风险评估完成】
识别的风险项:
**高风险**
1. 内容质量不稳定
- 风险:UGC质量参差不齐
- 对策:建立内容审核机制
**中风险**
2. 预算控制难度大
- 风险:投放效果不可控
- 对策:分阶段预算分配
3. 青年群体注意力分散
- 风险:话题热度难维持
- 对策:多波次内容规划
**建议**:重点关注内容质量风险`;
break;
default:
response = '正在分析中...';
}
setConversation((prev) => [...prev, { role: 'agent', content: response, time: responseTime }]);
setIsRunning(false);
}, 2000);
};
return (
<div className="space-y-6">
{/* 顶部操作栏 */}
<div className={`${cardBg} backdrop-blur-xl ${cardBorder} rounded-xl p-4`}>
<div className="flex items-center justify-between">
<div className="flex items-center gap-4">
<button
onClick={() => onNavigate('agent-list')}
className={`flex items-center gap-2 px-3 py-2 ${btnSecondary} rounded-lg text-sm transition-colors`}
>
<ArrowLeft className="w-4 h-4" />
返回
</button>
<div className={`h-8 w-px ${isDark ? 'bg-blue-800/30' : 'bg-red-200'}`} />
<div>
<h1 className={`${textPrimary} font-bold text-lg`}>单智能体试用</h1>
<p className={`${textSecondary} text-sm`}>直接调用单个智能体进行专项分析</p>
</div>
</div>
<div className="flex items-center gap-3">
<span className={`px-3 py-1.5 ${isDark ? 'bg-green-500/20 text-green-400' : 'bg-green-100 text-green-700'} rounded-full text-sm font-medium flex items-center gap-1.5`}>
<Bot className="w-4 h-4" />
能力调用模式
</span>
</div>
</div>
</div>
<div className="grid grid-cols-12 gap-6">
{/* 左侧:智能体选择区 */}
<div className="col-span-4">
<div className={`${cardBg} backdrop-blur-xl ${cardBorder} rounded-xl p-4`}>
<h3 className={`${textPrimary} font-semibold text-sm mb-4 flex items-center gap-2`}>
<Bot className={`w-4 h-4 ${isDark ? 'text-purple-400' : 'text-purple-600'}`} />
选择智能体
</h3>
<div className="space-y-3">
{agentCapabilities.map((agent) => {
const Icon = agent.icon;
const isSelected = selectedAgent === agent.id;
return (
<button
key={agent.id}
onClick={() => handleSelectAgent(agent.id)}
className={`w-full p-4 rounded-xl border transition-all text-left ${
isSelected
? `${isDark ? 'bg-blue-500/20 border-blue-500/50' : 'bg-red-50 border-red-300'}`
: `${inputBg} ${cardBorder} hover:${isDark ? 'border-blue-500/30' : 'border-red-400'}`
}`}
>
<div className="flex items-start gap-3">
<div className={`w-10 h-10 rounded-lg ${isDark ? 'bg-blue-500/20' : 'bg-red-100'} flex items-center justify-center flex-shrink-0`}>
<Icon className={`w-5 h-5 ${isDark ? 'text-blue-400' : 'text-red-600'}`} />
</div>
<div className="flex-1 min-w-0">
<p className={`text-sm font-medium ${isSelected ? (isDark ? 'text-blue-400' : 'text-red-600') : textPrimary}`}>
{agent.name}
</p>
<p className={`${textSecondary} text-xs mt-0.5`}>{agent.role}</p>
<p className={`${textMuted} text-xs mt-1 line-clamp-2`}>{agent.description}</p>
</div>
</div>
{isSelected && (
<div className={`mt-3 pt-3 border-t ${isDark ? 'border-blue-500/30' : 'border-red-300'}`}>
<p className={`text-xs ${textSecondary} mb-2`}>核心技能</p>
<div className="flex flex-wrap gap-1">
{agent.skills.map((skill, i) => (
<span
key={i}
className={`px-2 py-0.5 ${isDark ? 'bg-blue-500/20 text-blue-400' : 'bg-red-100 text-red-600'} rounded text-xs`}
>
{skill}
</span>
))}
</div>
</div>
)}
</button>
);
})}
</div>
</div>
{/* 使用说明 */}
<div className={`mt-4 ${cardBg} backdrop-blur-xl ${cardBorder} rounded-xl p-4`}>
<h3 className={`${textPrimary} font-semibold text-sm mb-3 flex items-center gap-2`}>
<BookOpen className={`w-4 h-4 ${isDark ? 'text-blue-400' : 'text-blue-600'}`} />
使用说明
</h3>
<div className={`space-y-2 text-xs ${textSecondary}`}>
<div className="flex items-start gap-2">
<span className={`font-bold ${isDark ? 'text-blue-400' : 'text-blue-600'}`}>1.</span>
<p>选择一个智能体进入专项分析模式</p>
</div>
<div className="flex items-start gap-2">
<span className={`font-bold ${isDark ? 'text-blue-400' : 'text-blue-600'}`}>2.</span>
<p>输入您的问题或任务要求</p>
</div>
<div className="flex items-start gap-2">
<span className={`font-bold ${isDark ? 'text-blue-400' : 'text-blue-600'}`}>3.</span>
<p>智能体将基于其专业能力给出分析结果</p>
</div>
<div className="flex items-start gap-2">
<span className={`font-bold ${isDark ? 'text-blue-400' : 'text-blue-600'}`}>4.</span>
<p>结果可用于后续场景任务或直接参考</p>
</div>
</div>
</div>
</div>
{/* 右侧:对话区 */}
<div className="col-span-8">
<div className={`${cardBg} backdrop-blur-xl ${cardBorder} rounded-xl flex flex-col h-[calc(100vh-220px)]`}>
{selectedAgent ? (
<>
{/* 顶部智能体信息 */}
<div className={`p-4 border-b ${cardBorder}`}>
<div className="flex items-center justify-between">
<div className="flex items-center gap-3">
{currentAgent && (
<>
<div className={`w-10 h-10 rounded-lg ${isDark ? 'bg-blue-500/20' : 'bg-red-100'} flex items-center justify-center`}>
<currentAgent.icon className={`w-5 h-5 ${isDark ? 'text-blue-400' : 'text-red-600'}`} />
</div>
<div>
<p className={`${textPrimary} font-medium`}>{currentAgent.name}</p>
<p className={`${textSecondary} text-xs`}>{currentAgent.role}</p>
</div>
</>
)}
</div>
<div className="flex items-center gap-2">
<span className={`px-2 py-1 ${isDark ? 'bg-green-500/20 text-green-400' : 'bg-green-100 text-green-700'} rounded text-xs flex items-center gap-1`}>
<Zap className="w-3 h-3" />
独立运行中
</span>
</div>
</div>
</div>
{/* 对话区域 */}
<div className="flex-1 overflow-y-auto p-4 space-y-4">
{conversation.length === 0 ? (
<div className="flex flex-col items-center justify-center h-full text-center">
<div className={`w-16 h-16 rounded-full ${isDark ? 'bg-blue-500/20' : 'bg-red-100'} flex items-center justify-center mb-4`}>
<Bot className={`w-8 h-8 ${isDark ? 'text-blue-400' : 'text-red-600'}`} />
</div>
<p className={`${textPrimary} font-medium mb-2`}>开始与 {currentAgent?.name} 对话</p>
<p className={`${textSecondary} text-sm max-w-md`}>
{currentAgent?.description}
<br />
请在下方输入您的问题或任务要求
</p>
</div>
) : (
conversation.map((msg, i) => (
<div
key={i}
className={`flex gap-3 ${msg.role === 'user' ? 'flex-row-reverse' : ''}`}
>
<div
className={`w-8 h-8 rounded-full flex items-center justify-center flex-shrink-0 ${
msg.role === 'user'
? (isDark ? 'bg-blue-500/20' : 'bg-red-100')
: (isDark ? 'bg-blue-500/20' : 'bg-red-100')
}`}
>
{msg.role === 'user' ? (
<User className={`w-4 h-4 ${isDark ? 'text-blue-400' : 'text-red-600'}`} />
) : (
<Bot className={`w-4 h-4 ${isDark ? 'text-blue-400' : 'text-red-600'}`} />
)}
</div>
<div
className={`max-w-[80%] rounded-2xl px-4 py-3 ${
msg.role === 'user'
? (isDark ? 'bg-blue-600 text-white' : 'bg-red-600 text-white')
: `${inputBg} ${inputBorder}`
}`}
>
<div
className={`text-xs mb-1 ${
msg.role === 'user' ? (isDark ? 'text-blue-200' : 'text-red-200') : textMuted
}`}
>
{msg.role === 'user' ? '您' : currentAgent?.name} · {msg.time}
</div>
<div className="text-sm whitespace-pre-wrap">{msg.content}</div>
</div>
</div>
))
)}
{isRunning && (
<div className="flex gap-3">
<div className={`w-8 h-8 rounded-full ${isDark ? 'bg-green-500/20' : 'bg-green-100'} flex items-center justify-center`}>
<Bot className={`w-4 h-4 ${isDark ? 'text-green-400' : 'text-green-600'}`} />
</div>
<div className={`${inputBg} ${inputBorder} rounded-2xl px-4 py-3`}>
<div className={`flex items-center gap-2 ${textSecondary} text-sm`}>
<div className={`w-2 h-2 ${isDark ? 'bg-green-400' : 'bg-green-500'} rounded-full animate-pulse`} />
{currentAgent?.name} 正在分析中...
</div>
</div>
</div>
)}
</div>
{/* 输入区域 */}
<div className={`p-4 border-t ${cardBorder}`}>
<div className="flex gap-3">
<textarea
value={inputText}
onChange={(e) => setInputText(e.target.value)}
onKeyDown={(e) => {
if (e.key === 'Enter' && !e.shiftKey) {
e.preventDefault();
handleSend();
}
}}
placeholder={`输入您要咨询 ${currentAgent?.name} 的问题...`}
rows={3}
className={`flex-1 px-4 py-3 ${inputBg} ${inputBorder} rounded-xl ${textPrimary} text-sm ${textMuted} resize-none focus:outline-none focus:${isDark ? 'border-blue-500/50' : 'border-red-400'}`}
/>
<div className="flex flex-col gap-2">
<button
onClick={handleSend}
disabled={!inputText.trim() || isRunning}
className={`px-6 py-3 ${btnPrimary} ${isDark ? 'disabled:bg-slate-700 disabled:text-slate-500' : 'disabled:bg-gray-300 disabled:text-gray-500'} text-white rounded-xl text-sm font-medium transition-colors flex items-center gap-2`}
>
<Send className="w-4 h-4" />
发送
</button>
<button
onClick={() => {
setConversation([]);
setInputText('');
}}
className={`px-4 py-2 ${btnSecondary} rounded-lg text-sm transition-colors`}
>
清空
</button>
</div>
</div>
<p className={`${textMuted} text-xs mt-2`}>
按 Enter 发送,Shift+Enter 换行
</p>
</div>
</>
) : (
<div className="flex-1 flex flex-col items-center justify-center text-center p-8">
<div className={`w-20 h-20 rounded-full ${isDark ? 'bg-blue-500/20' : 'bg-red-100'} flex items-center justify-center mb-6`}>
<PlayCircle className={`w-10 h-10 ${isDark ? 'text-blue-400' : 'text-red-600'}`} />
</div>
<h3 className={`${textPrimary} font-semibold text-lg mb-2`}>选择智能体开始试用</h3>
<p className={`${textSecondary} text-sm max-w-md`}>
左侧选择一个智能体,即可进入单智能体对话模式。
<br />
该模式适合进行专项分析、快速验证或测试单个智能体能力。
</p>
</div>
)}
</div>
</div>
</div>
</div>
);
};
export default AgentTrial;