533 lines
21 KiB
TypeScript
533 lines
21 KiB
TypeScript
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;
|