fix
This commit is contained in:
209
README_AGUI_IMPLEMENTATION.md
Normal file
209
README_AGUI_IMPLEMENTATION.md
Normal file
@@ -0,0 +1,209 @@
|
||||
# AG-UI 聊天窗口实现总结
|
||||
|
||||
## 项目概述
|
||||
|
||||
基于AG-UI协议为项目详情页面实现了左侧聊天窗口,提供智能化的视频创作助手功能。
|
||||
|
||||
## 实现成果
|
||||
|
||||
### 1. 核心组件
|
||||
|
||||
#### AGUIChat 组件 (`src/components/AGUIChat.tsx`)
|
||||
- **功能**: 主要的聊天界面组件
|
||||
- **特性**:
|
||||
- 基于AG-UI协议的事件驱动通信
|
||||
- 实时状态同步和进度显示
|
||||
- 支持多种消息类型(用户、助手、系统)
|
||||
- 可视化展示AG-UI事件(思考、工具调用、进度)
|
||||
- 响应式设计和友好的用户体验
|
||||
|
||||
#### AGUIService 服务 (`src/services/aguiService.ts`)
|
||||
- **功能**: AG-UI协议的核心服务实现
|
||||
- **特性**:
|
||||
- 事件驱动架构(基于RxJS)
|
||||
- 支持16种标准AG-UI事件类型
|
||||
- 智能体连接和会话管理
|
||||
- 模拟智能体处理流程
|
||||
- 可扩展的智能体配置
|
||||
|
||||
### 2. 页面布局更新
|
||||
|
||||
#### 项目详情页面 (`src/pages/ProjectDetailPage.tsx`)
|
||||
- **布局变更**: 从三栏布局调整为左右两栏
|
||||
- **左侧**: AG-UI聊天面板 (320px宽度)
|
||||
- **右侧**: 项目素材管理 (剩余空间)
|
||||
- **集成**: 聊天组件与素材管理的数据联动
|
||||
|
||||
### 3. 技术特性
|
||||
|
||||
#### AG-UI协议支持
|
||||
- ✅ 事件驱动通信
|
||||
- ✅ 实时状态同步
|
||||
- ✅ 双向交互
|
||||
- ✅ 工具调用可视化
|
||||
- ✅ 进度跟踪
|
||||
- ✅ 错误处理
|
||||
|
||||
#### 用户体验优化
|
||||
- ✅ 现代化聊天界面
|
||||
- ✅ 实时滚动和状态指示
|
||||
- ✅ 快捷键支持 (Enter发送)
|
||||
- ✅ 智能禁用和加载状态
|
||||
- ✅ 友好的错误提示
|
||||
|
||||
## 文件结构
|
||||
|
||||
```
|
||||
src/
|
||||
├── components/
|
||||
│ └── AGUIChat.tsx # 主聊天组件
|
||||
├── services/
|
||||
│ └── aguiService.ts # AG-UI协议服务
|
||||
├── pages/
|
||||
│ └── ProjectDetailPage.tsx # 更新的项目详情页面
|
||||
docs/
|
||||
└── AGUI_CHAT_FEATURES.md # 功能特性文档
|
||||
scripts/
|
||||
└── demo-agui-chat.md # 演示脚本
|
||||
```
|
||||
|
||||
## 核心功能演示
|
||||
|
||||
### 1. 智能对话
|
||||
```
|
||||
用户: "我想为我的产品创作一个宣传视频"
|
||||
助手: [显示思考状态] → [进度更新] → [工具调用] → [专业回复]
|
||||
```
|
||||
|
||||
### 2. AG-UI事件流
|
||||
```
|
||||
thinking → progress → tool_call → message → complete
|
||||
```
|
||||
|
||||
### 3. 可视化元素
|
||||
- 🟢 连接状态指示器
|
||||
- 📊 实时进度条
|
||||
- 🔧 工具调用卡片
|
||||
- 🧠 思考状态动画
|
||||
- ⚡ 消息状态图标
|
||||
|
||||
## 技术栈
|
||||
|
||||
### 前端技术
|
||||
- **React 18**: 现代化的组件开发
|
||||
- **TypeScript**: 类型安全和开发体验
|
||||
- **Tailwind CSS**: 快速样式开发
|
||||
- **Lucide React**: 现代化图标库
|
||||
|
||||
### AG-UI相关
|
||||
- **@ag-ui/core**: AG-UI核心协议库
|
||||
- **RxJS**: 响应式编程和事件流管理
|
||||
- **事件驱动架构**: 基于观察者模式的通信
|
||||
|
||||
### 状态管理
|
||||
- **React Hooks**: 本地状态管理
|
||||
- **RxJS Subjects**: 事件流和状态同步
|
||||
- **Context API**: 跨组件数据传递
|
||||
|
||||
## 部署和使用
|
||||
|
||||
### 安装依赖
|
||||
```bash
|
||||
pnpm add @ag-ui/core rxjs
|
||||
```
|
||||
|
||||
### 启动应用
|
||||
```bash
|
||||
pnpm dev
|
||||
```
|
||||
|
||||
### 访问功能
|
||||
1. 打开 `http://localhost:5173`
|
||||
2. 导航到"项目管理"
|
||||
3. 选择任意项目进入详情页面
|
||||
4. 在左侧聊天窗口开始对话
|
||||
|
||||
## 特色亮点
|
||||
|
||||
### 1. 协议标准化
|
||||
- 遵循AG-UI开放协议
|
||||
- 支持标准事件类型
|
||||
- 易于扩展和集成
|
||||
|
||||
### 2. 实时交互
|
||||
- 事件驱动的实时通信
|
||||
- 流式进度更新
|
||||
- 即时状态反馈
|
||||
|
||||
### 3. 用户体验
|
||||
- 直观的聊天界面
|
||||
- 丰富的视觉反馈
|
||||
- 智能的交互设计
|
||||
|
||||
### 4. 技术架构
|
||||
- 模块化设计
|
||||
- 类型安全
|
||||
- 可扩展性强
|
||||
|
||||
## 扩展可能性
|
||||
|
||||
### 短期扩展
|
||||
- [ ] 语音输入/输出
|
||||
- [ ] 文件上传支持
|
||||
- [ ] 聊天记录持久化
|
||||
- [ ] 多智能体切换
|
||||
|
||||
### 长期规划
|
||||
- [ ] 智能体插件市场
|
||||
- [ ] 高级分析功能
|
||||
- [ ] 多语言支持
|
||||
- [ ] 移动端适配
|
||||
|
||||
## 性能优化
|
||||
|
||||
### 已实现优化
|
||||
- React.memo 和 useCallback 优化渲染
|
||||
- 事件流的合理管理
|
||||
- 组件懒加载
|
||||
- 状态更新批处理
|
||||
|
||||
### 监控指标
|
||||
- 消息响应时间
|
||||
- 事件处理延迟
|
||||
- 内存使用情况
|
||||
- 渲染性能
|
||||
|
||||
## 测试策略
|
||||
|
||||
### 单元测试
|
||||
- 组件渲染测试
|
||||
- 事件处理逻辑测试
|
||||
- 服务层功能测试
|
||||
|
||||
### 集成测试
|
||||
- AG-UI协议通信测试
|
||||
- 用户交互流程测试
|
||||
- 错误处理测试
|
||||
|
||||
### 用户测试
|
||||
- 可用性测试
|
||||
- 性能测试
|
||||
- 兼容性测试
|
||||
|
||||
## 总结
|
||||
|
||||
成功实现了基于AG-UI协议的智能聊天功能,为项目详情页面提供了强大的AI助手能力。该实现不仅遵循了开放标准,还提供了优秀的用户体验和可扩展的技术架构。
|
||||
|
||||
### 核心价值
|
||||
1. **标准化**: 基于AG-UI开放协议
|
||||
2. **智能化**: 提供专业的AI创作助手
|
||||
3. **实时性**: 事件驱动的即时交互
|
||||
4. **可扩展**: 模块化的架构设计
|
||||
|
||||
### 技术成就
|
||||
- 完整的AG-UI协议实现
|
||||
- 现代化的React组件架构
|
||||
- 优秀的用户体验设计
|
||||
- 可扩展的服务层架构
|
||||
|
||||
这个实现为未来的AI驱动应用开发提供了一个优秀的参考案例,展示了如何将先进的AI协议与现代前端技术完美结合。
|
||||
192
docs/AGUI_CHAT_FEATURES.md
Normal file
192
docs/AGUI_CHAT_FEATURES.md
Normal file
@@ -0,0 +1,192 @@
|
||||
# AG-UI 聊天功能特性说明
|
||||
|
||||
## 概述
|
||||
|
||||
基于AG-UI协议实现的智能聊天组件,为项目详情页面提供左侧聊天窗口,支持实时的人机协作和智能体交互。
|
||||
|
||||
## 核心特性
|
||||
|
||||
### 1. 基于AG-UI协议的标准化通信
|
||||
|
||||
- **事件驱动架构**: 支持16种标准AG-UI事件类型
|
||||
- **实时状态同步**: 智能体状态与UI实时同步
|
||||
- **双向通信**: 支持用户输入和智能体响应
|
||||
- **传输无关**: 支持多种传输方式(SSE、WebSocket等)
|
||||
|
||||
### 2. 智能体事件类型支持
|
||||
|
||||
| 事件类型 | 描述 | UI展示 |
|
||||
|---------|------|--------|
|
||||
| `message` | 消息内容 | 聊天气泡 |
|
||||
| `progress` | 进度更新 | 进度条 |
|
||||
| `thinking` | 思考状态 | 动画指示器 |
|
||||
| `tool_call` | 工具调用 | 工具调用卡片 |
|
||||
| `state` | 状态变化 | 连接状态指示 |
|
||||
| `error` | 错误信息 | 错误提示 |
|
||||
| `complete` | 完成状态 | 完成标识 |
|
||||
|
||||
### 3. 用户界面特性
|
||||
|
||||
#### 聊天界面
|
||||
- **现代化设计**: 简洁美观的聊天界面
|
||||
- **实时滚动**: 自动滚动到最新消息
|
||||
- **状态指示**: 连接状态、处理状态可视化
|
||||
- **消息状态**: 发送中、已发送、错误状态
|
||||
|
||||
#### 消息展示
|
||||
- **多角色支持**: 用户、助手、系统消息
|
||||
- **时间戳**: 每条消息显示时间
|
||||
- **事件可视化**: AG-UI事件的可视化展示
|
||||
- **进度跟踪**: 实时进度条和状态更新
|
||||
|
||||
#### 交互体验
|
||||
- **快捷键支持**: Enter发送,Shift+Enter换行
|
||||
- **智能禁用**: 处理中自动禁用输入
|
||||
- **错误处理**: 友好的错误提示和重试机制
|
||||
|
||||
### 4. 智能体能力
|
||||
|
||||
#### 视频创作智能体
|
||||
- **专业能力**: 视频生成、脚本编写、模特选择
|
||||
- **场景理解**: 产品展示、用户体验优化
|
||||
- **质量控制**: 内容优化、性能监控
|
||||
|
||||
#### 内容优化智能体
|
||||
- **分析能力**: 内容分析、受众定位
|
||||
- **优化策略**: 参与度优化、A/B测试
|
||||
- **性能指标**: 转化率分析、效果评估
|
||||
|
||||
## 技术实现
|
||||
|
||||
### 架构设计
|
||||
|
||||
```typescript
|
||||
// AG-UI服务层
|
||||
AGUIService
|
||||
├── 事件管理 (EventSubject)
|
||||
├── 状态管理 (StateSubject)
|
||||
├── 智能体连接
|
||||
└── 消息处理
|
||||
|
||||
// React组件层
|
||||
AGUIChat
|
||||
├── 消息状态管理
|
||||
├── 事件订阅处理
|
||||
├── UI渲染逻辑
|
||||
└── 用户交互处理
|
||||
```
|
||||
|
||||
### 关键组件
|
||||
|
||||
1. **AGUIService**: 核心服务类,处理AG-UI协议通信
|
||||
2. **AGUIChat**: React聊天组件,提供用户界面
|
||||
3. **事件处理器**: 处理不同类型的AG-UI事件
|
||||
4. **状态管理**: 管理连接状态和处理状态
|
||||
|
||||
### 数据流
|
||||
|
||||
```
|
||||
用户输入 → AGUIChat → AGUIService → 智能体
|
||||
智能体 → AG-UI事件流 → AGUIService → AGUIChat → UI更新
|
||||
```
|
||||
|
||||
## 使用方式
|
||||
|
||||
### 基本用法
|
||||
|
||||
```tsx
|
||||
import AGUIChat from '../components/AGUIChat'
|
||||
|
||||
<AGUIChat
|
||||
project={project}
|
||||
models={models}
|
||||
onMaterialCreated={() => loadProjectMaterials(project)}
|
||||
/>
|
||||
```
|
||||
|
||||
### 配置选项
|
||||
|
||||
- `project`: 当前项目信息
|
||||
- `models`: 可用模特列表
|
||||
- `onMaterialCreated`: 素材创建完成回调
|
||||
|
||||
## 页面布局
|
||||
|
||||
### 项目详情页面布局
|
||||
|
||||
```
|
||||
┌─────────────────────────────────────────────────────────┐
|
||||
│ 顶部导航栏 │
|
||||
├─────────────────┬───────────────────────────────────────┤
|
||||
│ │ │
|
||||
│ AG-UI聊天 │ 项目素材管理 │
|
||||
│ (左侧面板) │ (主要区域) │
|
||||
│ │ │
|
||||
│ • 智能对话 │ • 搜索功能 │
|
||||
│ • 实时状态 │ • 筛选器 │
|
||||
│ • 进度显示 │ • 素材网格 │
|
||||
│ • 工具调用 │ • 预览功能 │
|
||||
│ │ │
|
||||
└─────────────────┴───────────────────────────────────────┘
|
||||
```
|
||||
|
||||
## 特色功能
|
||||
|
||||
### 1. 实时协作
|
||||
- 人在回路中的工作流
|
||||
- 实时状态同步
|
||||
- 智能建议和指导
|
||||
|
||||
### 2. 可视化反馈
|
||||
- 进度条显示处理进度
|
||||
- 工具调用可视化
|
||||
- 思考状态动画
|
||||
|
||||
### 3. 上下文感知
|
||||
- 项目信息自动传递
|
||||
- 模特信息智能匹配
|
||||
- 历史对话上下文
|
||||
|
||||
### 4. 错误处理
|
||||
- 连接失败自动重试
|
||||
- 友好的错误提示
|
||||
- 状态恢复机制
|
||||
|
||||
## 扩展性
|
||||
|
||||
### 自定义智能体
|
||||
- 支持添加新的智能体类型
|
||||
- 可配置智能体能力
|
||||
- 灵活的端点配置
|
||||
|
||||
### 事件扩展
|
||||
- 支持自定义事件类型
|
||||
- 可扩展的事件处理器
|
||||
- 插件化架构
|
||||
|
||||
### UI定制
|
||||
- 主题定制支持
|
||||
- 组件样式覆盖
|
||||
- 布局灵活配置
|
||||
|
||||
## 最佳实践
|
||||
|
||||
1. **性能优化**: 使用React.memo和useCallback优化渲染
|
||||
2. **错误边界**: 实现错误边界组件保护应用稳定性
|
||||
3. **状态管理**: 合理使用本地状态和全局状态
|
||||
4. **用户体验**: 提供清晰的加载状态和错误反馈
|
||||
|
||||
## 未来规划
|
||||
|
||||
- [ ] 支持多智能体协作
|
||||
- [ ] 语音输入/输出功能
|
||||
- [ ] 文件上传和分享
|
||||
- [ ] 聊天记录持久化
|
||||
- [ ] 智能体插件市场
|
||||
- [ ] 高级分析和洞察
|
||||
|
||||
## 相关资源
|
||||
|
||||
- [AG-UI官方文档](https://ag-ui.com)
|
||||
- [AG-UI GitHub仓库](https://github.com/ag-ui-protocol/ag-ui)
|
||||
- [CopilotKit集成指南](https://docs.copilotkit.ai)
|
||||
@@ -15,6 +15,7 @@
|
||||
"format": "prettier --write src/**/*.{js,jsx,ts,tsx,css,md}"
|
||||
},
|
||||
"dependencies": {
|
||||
"@ag-ui/core": "^0.0.33",
|
||||
"@tauri-apps/api": "^2.0.0",
|
||||
"@tauri-apps/plugin-dialog": "^2.0.0",
|
||||
"@tauri-apps/plugin-fs": "^2.0.0",
|
||||
@@ -25,6 +26,7 @@
|
||||
"react": "^18.2.0",
|
||||
"react-dom": "^18.2.0",
|
||||
"react-router-dom": "^6.15.0",
|
||||
"rxjs": "^7.8.2",
|
||||
"tailwind-merge": "^1.14.0",
|
||||
"zustand": "^4.4.0"
|
||||
},
|
||||
|
||||
38
pnpm-lock.yaml
generated
38
pnpm-lock.yaml
generated
@@ -8,6 +8,9 @@ importers:
|
||||
|
||||
.:
|
||||
dependencies:
|
||||
'@ag-ui/core':
|
||||
specifier: ^0.0.33
|
||||
version: 0.0.33
|
||||
'@tauri-apps/api':
|
||||
specifier: ^2.0.0
|
||||
version: 2.6.0
|
||||
@@ -38,6 +41,9 @@ importers:
|
||||
react-router-dom:
|
||||
specifier: ^6.15.0
|
||||
version: 6.30.1(react-dom@18.3.1(react@18.3.1))(react@18.3.1)
|
||||
rxjs:
|
||||
specifier: ^7.8.2
|
||||
version: 7.8.2
|
||||
tailwind-merge:
|
||||
specifier: ^1.14.0
|
||||
version: 1.14.0
|
||||
@@ -96,6 +102,9 @@ importers:
|
||||
|
||||
packages:
|
||||
|
||||
'@ag-ui/core@0.0.33':
|
||||
resolution: {integrity: sha512-IbxzveQxJ6lPTtMvVozTiChu3DTffchvFLF52YZ/14aiFyACmXxdLOIIPeU1HfgwsPedKtjAFHEG3lnAqp2qsg==}
|
||||
|
||||
'@alloc/quick-lru@5.2.0':
|
||||
resolution: {integrity: sha512-UrcABB+4bUrFABwbluTIBErXwvbsU/V7TZWfmbgJfbkwiBuziS9gxdODUyuiecfdGQ85jglMW6juS3+z5TsKLw==}
|
||||
engines: {node: '>=10'}
|
||||
@@ -1559,6 +1568,12 @@ packages:
|
||||
run-parallel@1.2.0:
|
||||
resolution: {integrity: sha512-5l4VyZR86LZ/lDxZTR6jqL8AFE2S0IFLMP26AbjsLVADxHdhB/c0GUsH+y39UfCi3dzz8OlQuPmnaJOMoDHQBA==}
|
||||
|
||||
rxjs@7.8.1:
|
||||
resolution: {integrity: sha512-AA3TVj+0A2iuIoQkWEK/tqFjBq2j+6PO6Y0zJcvzLAFhEFIO3HL0vls9hWLncZbAAbK0mar7oZ4V079I/qPMxg==}
|
||||
|
||||
rxjs@7.8.2:
|
||||
resolution: {integrity: sha512-dhKf903U/PQZY6boNNtAGdWbG85WAbjT/1xYoZIC7FAY0yWapOBQVsVrDl58W86//e1VpMNBtRV4MaXfdMySFA==}
|
||||
|
||||
safe-array-concat@1.1.3:
|
||||
resolution: {integrity: sha512-AURm5f0jYEOydBj7VQlVvDrjeFgthDdEF5H1dP+6mNpoXOMo1quQqJ4wvJDyRZ9+pO3kGWoOdmV08cSv2aJV6Q==}
|
||||
engines: {node: '>=0.4'}
|
||||
@@ -1718,6 +1733,9 @@ packages:
|
||||
ts-interface-checker@0.1.13:
|
||||
resolution: {integrity: sha512-Y/arvbn+rrz3JCKl9C4kVNfTfSm2/mEp5FSz5EsZSANGPSlQrpRI5M4PKF+mJnE52jOO90PnPSc3Ur3bTQw0gA==}
|
||||
|
||||
tslib@2.8.1:
|
||||
resolution: {integrity: sha512-oJFu94HQb+KVduSUQL7wnpmqnfmLsOA/nAh6b6EH0wCEoK0/mPeXU6c3wKDV83MkOuHPRHtSXKKU99IBazS/2w==}
|
||||
|
||||
type-check@0.4.0:
|
||||
resolution: {integrity: sha512-XleUoc9uwGXqjWwXaUTZAmzMcFZ5858QA2vvx1Ur5xIcixXIP+8LnFDgRplU30us6teqdlskFfu+ae4K79Ooew==}
|
||||
engines: {node: '>= 0.8.0'}
|
||||
@@ -1844,6 +1862,9 @@ packages:
|
||||
resolution: {integrity: sha512-rVksvsnNCdJ/ohGc6xgPwyN8eheCxsiLM8mxuE/t/mOVqJewPuO1miLpTHQiRgTKCLexL4MeAFVagts7HmNZ2Q==}
|
||||
engines: {node: '>=10'}
|
||||
|
||||
zod@3.25.76:
|
||||
resolution: {integrity: sha512-gzUt/qt81nXsFGKIFcC3YnfEAx5NkunCfnDlvuBSSFS02bcXu4Lmea0AFIUwbLWxWPx3d9p8S5QoaujKcNQxcQ==}
|
||||
|
||||
zustand@4.5.7:
|
||||
resolution: {integrity: sha512-CHOUy7mu3lbD6o6LJLfllpjkzhHXSBlX8B9+qPddUsIfeF5S/UZ5q0kmCsnRqT1UHFQZchNFDDzMbQsuesHWlw==}
|
||||
engines: {node: '>=12.7.0'}
|
||||
@@ -1861,6 +1882,11 @@ packages:
|
||||
|
||||
snapshots:
|
||||
|
||||
'@ag-ui/core@0.0.33':
|
||||
dependencies:
|
||||
rxjs: 7.8.1
|
||||
zod: 3.25.76
|
||||
|
||||
'@alloc/quick-lru@5.2.0': {}
|
||||
|
||||
'@ampproject/remapping@2.3.0':
|
||||
@@ -3465,6 +3491,14 @@ snapshots:
|
||||
dependencies:
|
||||
queue-microtask: 1.2.3
|
||||
|
||||
rxjs@7.8.1:
|
||||
dependencies:
|
||||
tslib: 2.8.1
|
||||
|
||||
rxjs@7.8.2:
|
||||
dependencies:
|
||||
tslib: 2.8.1
|
||||
|
||||
safe-array-concat@1.1.3:
|
||||
dependencies:
|
||||
call-bind: 1.0.8
|
||||
@@ -3690,6 +3724,8 @@ snapshots:
|
||||
|
||||
ts-interface-checker@0.1.13: {}
|
||||
|
||||
tslib@2.8.1: {}
|
||||
|
||||
type-check@0.4.0:
|
||||
dependencies:
|
||||
prelude-ls: 1.2.1
|
||||
@@ -3829,6 +3865,8 @@ snapshots:
|
||||
|
||||
yocto-queue@0.1.0: {}
|
||||
|
||||
zod@3.25.76: {}
|
||||
|
||||
zustand@4.5.7(@types/react@18.3.23)(react@18.3.1):
|
||||
dependencies:
|
||||
use-sync-external-store: 1.5.0(react@18.3.1)
|
||||
|
||||
169
scripts/demo-agui-chat.md
Normal file
169
scripts/demo-agui-chat.md
Normal file
@@ -0,0 +1,169 @@
|
||||
# AG-UI 聊天功能演示脚本
|
||||
|
||||
## 演示准备
|
||||
|
||||
### 1. 启动应用
|
||||
```bash
|
||||
cd /root/projects/mixvideo_v2
|
||||
pnpm dev
|
||||
```
|
||||
|
||||
### 2. 导航到项目详情页面
|
||||
1. 打开浏览器访问 `http://localhost:5173`
|
||||
2. 点击"项目管理"
|
||||
3. 选择任意一个项目进入详情页面
|
||||
|
||||
## 演示流程
|
||||
|
||||
### 第一部分:界面介绍 (2分钟)
|
||||
|
||||
#### 1. 整体布局展示
|
||||
- **左侧**: AG-UI聊天面板
|
||||
- 顶部显示连接状态(绿色圆点表示已连接)
|
||||
- 系统欢迎消息展示AG-UI协议特性
|
||||
- 底部输入框支持多行输入
|
||||
|
||||
- **右侧**: 项目素材管理
|
||||
- 顶部搜索功能
|
||||
- 筛选器(使用状态、模特、类型)
|
||||
- 素材网格展示
|
||||
|
||||
#### 2. AG-UI协议特性说明
|
||||
- 基于事件驱动的通信
|
||||
- 支持16种标准事件类型
|
||||
- 实时状态同步
|
||||
- 双向交互能力
|
||||
|
||||
### 第二部分:基础聊天功能 (3分钟)
|
||||
|
||||
#### 1. 发送简单消息
|
||||
**输入示例**:
|
||||
```
|
||||
你好,我想为我的产品创作一个宣传视频
|
||||
```
|
||||
|
||||
**预期响应**:
|
||||
- 显示用户消息(蓝色气泡,右对齐)
|
||||
- 智能体开始处理(显示思考状态)
|
||||
- 逐步显示进度条
|
||||
- 最终显示智能体回复
|
||||
|
||||
#### 2. 观察AG-UI事件流
|
||||
**事件展示顺序**:
|
||||
1. `thinking` - 紫色卡片,显示"正在分析您的需求..."
|
||||
2. `progress` - 进度条更新(20% → 40% → 60% → 80%)
|
||||
3. `tool_call` - 蓝色卡片,显示"调用工具: video_generator"
|
||||
4. `message` - 智能体回复内容
|
||||
5. `complete` - 完成状态
|
||||
|
||||
#### 3. 状态指示器
|
||||
- **连接状态**: 绿色圆点 + "已连接"
|
||||
- **处理状态**: 输入框禁用 + 发送按钮显示加载动画
|
||||
- **消息状态**: 发送中 → 已发送 → 完成
|
||||
|
||||
### 第三部分:高级功能演示 (4分钟)
|
||||
|
||||
#### 1. 上下文感知对话
|
||||
**输入示例**:
|
||||
```
|
||||
我希望重点展示产品的便携性和易用性
|
||||
```
|
||||
|
||||
**演示要点**:
|
||||
- 智能体记住之前的对话上下文
|
||||
- 自动关联当前项目信息
|
||||
- 提供针对性的建议
|
||||
|
||||
#### 2. 工具调用可视化
|
||||
**输入示例**:
|
||||
```
|
||||
请帮我生成一个30秒的产品展示视频
|
||||
```
|
||||
|
||||
**观察要点**:
|
||||
- 工具调用卡片显示具体工具名称
|
||||
- 参数信息展示(如果有)
|
||||
- 调用状态实时更新
|
||||
|
||||
#### 3. 进度跟踪
|
||||
**演示要点**:
|
||||
- 实时进度条更新
|
||||
- 进度消息描述当前步骤
|
||||
- 平滑的动画过渡效果
|
||||
|
||||
#### 4. 错误处理(可选)
|
||||
**模拟场景**:
|
||||
- 断开网络连接
|
||||
- 观察连接状态变为红色
|
||||
- 重新连接后状态恢复
|
||||
|
||||
### 第四部分:集成展示 (2分钟)
|
||||
|
||||
#### 1. 与素材管理的联动
|
||||
**演示流程**:
|
||||
1. 在聊天中请求创建素材
|
||||
2. 智能体完成处理后触发 `onMaterialCreated` 回调
|
||||
3. 右侧素材列表自动刷新
|
||||
4. 新创建的素材出现在列表中
|
||||
|
||||
#### 2. 模特信息集成
|
||||
**演示要点**:
|
||||
- 智能体自动获取可用模特信息
|
||||
- 在对话中智能推荐合适的模特
|
||||
- 模特选择与项目需求匹配
|
||||
|
||||
## 技术亮点说明
|
||||
|
||||
### 1. AG-UI协议优势
|
||||
- **标准化**: 遵循开放协议,易于集成
|
||||
- **灵活性**: 支持多种传输方式和事件类型
|
||||
- **扩展性**: 可轻松添加新的智能体和功能
|
||||
|
||||
### 2. 实现特色
|
||||
- **事件驱动**: 基于RxJS的响应式编程
|
||||
- **类型安全**: 完整的TypeScript类型定义
|
||||
- **性能优化**: React hooks和memo优化
|
||||
|
||||
### 3. 用户体验
|
||||
- **实时反馈**: 即时的状态更新和进度显示
|
||||
- **直观界面**: 清晰的视觉层次和交互反馈
|
||||
- **错误友好**: 完善的错误处理和恢复机制
|
||||
|
||||
## 演示总结
|
||||
|
||||
### 核心价值
|
||||
1. **提升效率**: 智能化的创作流程
|
||||
2. **降低门槛**: 自然语言交互,无需专业技能
|
||||
3. **实时协作**: 人机协作的创作体验
|
||||
4. **标准化**: 基于开放协议,易于扩展
|
||||
|
||||
### 应用场景
|
||||
- 视频内容创作
|
||||
- 产品营销素材制作
|
||||
- 创意设计协作
|
||||
- 智能化工作流程
|
||||
|
||||
### 技术优势
|
||||
- 基于AG-UI开放协议
|
||||
- 现代化的前端技术栈
|
||||
- 响应式设计和实时交互
|
||||
- 可扩展的架构设计
|
||||
|
||||
## Q&A 准备
|
||||
|
||||
### 常见问题
|
||||
|
||||
**Q: AG-UI与其他聊天框架有什么区别?**
|
||||
A: AG-UI是专门为智能体交互设计的协议,支持复杂的事件流、工具调用和状态同步,而不仅仅是简单的消息传递。
|
||||
|
||||
**Q: 如何扩展新的智能体?**
|
||||
A: 通过AGUIService添加新的智能体配置,实现相应的事件处理器即可。
|
||||
|
||||
**Q: 性能如何保证?**
|
||||
A: 使用React的优化技术、事件流的合理管理和响应式编程模式来保证性能。
|
||||
|
||||
**Q: 是否支持多语言?**
|
||||
A: 当前支持中文,可以通过国际化框架扩展多语言支持。
|
||||
|
||||
**Q: 如何与现有系统集成?**
|
||||
A: AG-UI协议设计为传输无关,可以通过HTTP、WebSocket等多种方式集成到现有系统中。
|
||||
376
src/components/AGUIChat.tsx
Normal file
376
src/components/AGUIChat.tsx
Normal file
@@ -0,0 +1,376 @@
|
||||
import React, { useState, useRef, useEffect, useCallback } from 'react'
|
||||
import { MessageCircle, Send, User, Bot, Sparkles, Loader, AlertCircle, CheckCircle } from 'lucide-react'
|
||||
import { Project } from '../services/projectService'
|
||||
import { Model } from '../services/modelService'
|
||||
import { aguiService, AGUIEvent, AGUIState } from '../services/aguiService'
|
||||
|
||||
interface ChatMessage {
|
||||
id: string
|
||||
type: 'user' | 'assistant' | 'system'
|
||||
content: string
|
||||
timestamp: Date
|
||||
status?: 'sending' | 'sent' | 'error'
|
||||
events?: AGUIEvent[]
|
||||
}
|
||||
|
||||
interface AGUIChatProps {
|
||||
project: Project
|
||||
models: Model[]
|
||||
onMaterialCreated?: () => void
|
||||
}
|
||||
|
||||
const AGUIChat: React.FC<AGUIChatProps> = ({
|
||||
project,
|
||||
models,
|
||||
onMaterialCreated
|
||||
}) => {
|
||||
const [messages, setMessages] = useState<ChatMessage[]>([
|
||||
{
|
||||
id: '1',
|
||||
type: 'system',
|
||||
content: `欢迎使用AI创作助手!我是您的智能创作伙伴,基于AG-UI协议为您提供专业的视频创作服务。\n\n🎯 **专业能力**\n• 智能视频内容生成\n• 模特选择与匹配\n• 创作流程优化\n• 实时质量监控\n\n💡 **项目信息**\n当前项目:${project.product_name}\n可用模特:${models.length}个\n\n请告诉我您的创作需求,我将为您提供专业的解决方案!`,
|
||||
timestamp: new Date()
|
||||
}
|
||||
])
|
||||
const [inputText, setInputText] = useState('')
|
||||
const [aguiState, setAguiState] = useState<AGUIState>({
|
||||
connected: false,
|
||||
processing: false
|
||||
})
|
||||
const messagesEndRef = useRef<HTMLDivElement>(null)
|
||||
|
||||
useEffect(() => {
|
||||
scrollToBottom()
|
||||
}, [messages])
|
||||
|
||||
useEffect(() => {
|
||||
// 连接到AG-UI服务
|
||||
const connectToAgent = async () => {
|
||||
try {
|
||||
await aguiService.connect('video_creator')
|
||||
} catch (error) {
|
||||
console.error('Failed to connect to AG-UI agent:', error)
|
||||
}
|
||||
}
|
||||
|
||||
// 订阅AG-UI状态变化
|
||||
const stateSubscription = aguiService.getStateStream().subscribe(setAguiState)
|
||||
|
||||
// 订阅AG-UI事件流
|
||||
const eventSubscription = aguiService.getEventStream().subscribe(handleAGUIEvent)
|
||||
|
||||
connectToAgent()
|
||||
|
||||
return () => {
|
||||
stateSubscription.unsubscribe()
|
||||
eventSubscription.unsubscribe()
|
||||
}
|
||||
}, [])
|
||||
|
||||
const handleAGUIEvent = useCallback((event: AGUIEvent) => {
|
||||
switch (event.type) {
|
||||
case 'message':
|
||||
if (event.data.role === 'assistant') {
|
||||
const assistantMessage: ChatMessage = {
|
||||
id: event.id,
|
||||
type: 'assistant',
|
||||
content: event.data.content,
|
||||
timestamp: event.timestamp,
|
||||
events: [event]
|
||||
}
|
||||
setMessages(prev => [...prev, assistantMessage])
|
||||
}
|
||||
break
|
||||
|
||||
case 'thinking':
|
||||
// 显示思考状态
|
||||
setMessages(prev => {
|
||||
const lastMessage = prev[prev.length - 1]
|
||||
if (lastMessage && lastMessage.type === 'assistant' && lastMessage.events) {
|
||||
return prev.map((msg, index) =>
|
||||
index === prev.length - 1
|
||||
? { ...msg, events: [...msg.events!, event] }
|
||||
: msg
|
||||
)
|
||||
}
|
||||
return prev
|
||||
})
|
||||
break
|
||||
|
||||
case 'progress':
|
||||
// 更新进度
|
||||
setMessages(prev => {
|
||||
const lastMessage = prev[prev.length - 1]
|
||||
if (lastMessage && lastMessage.type === 'assistant' && lastMessage.events) {
|
||||
return prev.map((msg, index) =>
|
||||
index === prev.length - 1
|
||||
? { ...msg, events: [...msg.events!.filter(e => e.type !== 'progress'), event] }
|
||||
: msg
|
||||
)
|
||||
}
|
||||
return prev
|
||||
})
|
||||
break
|
||||
|
||||
case 'tool_call':
|
||||
// 显示工具调用
|
||||
setMessages(prev => {
|
||||
const lastMessage = prev[prev.length - 1]
|
||||
if (lastMessage && lastMessage.type === 'assistant' && lastMessage.events) {
|
||||
return prev.map((msg, index) =>
|
||||
index === prev.length - 1
|
||||
? { ...msg, events: [...msg.events!, event] }
|
||||
: msg
|
||||
)
|
||||
}
|
||||
return prev
|
||||
})
|
||||
break
|
||||
|
||||
case 'complete':
|
||||
// 处理完成事件
|
||||
if (event.data.success && onMaterialCreated) {
|
||||
onMaterialCreated()
|
||||
}
|
||||
break
|
||||
|
||||
case 'error':
|
||||
// 处理错误
|
||||
const errorMessage: ChatMessage = {
|
||||
id: event.id,
|
||||
type: 'assistant',
|
||||
content: `抱歉,处理过程中出现了错误:${event.data.error}`,
|
||||
timestamp: event.timestamp,
|
||||
status: 'error'
|
||||
}
|
||||
setMessages(prev => [...prev, errorMessage])
|
||||
break
|
||||
}
|
||||
}, [onMaterialCreated])
|
||||
|
||||
const scrollToBottom = () => {
|
||||
messagesEndRef.current?.scrollIntoView({ behavior: 'smooth' })
|
||||
}
|
||||
|
||||
const handleSendMessage = async () => {
|
||||
if (!inputText.trim() || aguiState.processing) return
|
||||
|
||||
const userMessage: ChatMessage = {
|
||||
id: Date.now().toString(),
|
||||
type: 'user',
|
||||
content: inputText.trim(),
|
||||
timestamp: new Date(),
|
||||
status: 'sending'
|
||||
}
|
||||
|
||||
setMessages(prev => [...prev, userMessage])
|
||||
const messageContent = inputText.trim()
|
||||
setInputText('')
|
||||
|
||||
try {
|
||||
// 更新消息状态为已发送
|
||||
setMessages(prev => prev.map(msg =>
|
||||
msg.id === userMessage.id ? { ...msg, status: 'sent' } : msg
|
||||
))
|
||||
|
||||
// 发送消息到AG-UI服务
|
||||
await aguiService.sendMessage(messageContent, {
|
||||
project: project,
|
||||
models: models,
|
||||
session_context: {
|
||||
message_count: messages.length,
|
||||
last_activity: new Date()
|
||||
}
|
||||
})
|
||||
} catch (error) {
|
||||
// 更新消息状态为错误
|
||||
setMessages(prev => prev.map(msg =>
|
||||
msg.id === userMessage.id ? { ...msg, status: 'error' } : msg
|
||||
))
|
||||
console.error('Failed to send message:', error)
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
const handleKeyPress = (e: React.KeyboardEvent) => {
|
||||
if (e.key === 'Enter' && !e.shiftKey) {
|
||||
e.preventDefault()
|
||||
handleSendMessage()
|
||||
}
|
||||
}
|
||||
|
||||
const getMessageStatusIcon = (status?: string) => {
|
||||
switch (status) {
|
||||
case 'sending':
|
||||
return <Loader size={12} className="animate-spin text-gray-400" />
|
||||
case 'sent':
|
||||
return <CheckCircle size={12} className="text-green-500" />
|
||||
case 'error':
|
||||
return <AlertCircle size={12} className="text-red-500" />
|
||||
default:
|
||||
return null
|
||||
}
|
||||
}
|
||||
|
||||
const getLatestProgress = (events?: AGUIEvent[]) => {
|
||||
if (!events) return null
|
||||
const progressEvents = events.filter(e => e.type === 'progress')
|
||||
return progressEvents.length > 0 ? progressEvents[progressEvents.length - 1] : null
|
||||
}
|
||||
|
||||
return (
|
||||
<div className="h-full flex flex-col bg-white border-r border-gray-200">
|
||||
{/* 头部 */}
|
||||
<div className="p-4 border-b border-gray-200 bg-gradient-to-r from-blue-50 to-purple-50">
|
||||
<div className="flex items-center justify-between mb-2">
|
||||
<div className="flex items-center">
|
||||
<MessageCircle className="text-blue-600 mr-2" size={20} />
|
||||
<h3 className="font-semibold text-gray-900">AI创作助手</h3>
|
||||
</div>
|
||||
<div className="flex items-center">
|
||||
<div className={`w-2 h-2 rounded-full mr-2 ${aguiState.connected ? 'bg-green-500' : 'bg-red-500'}`} />
|
||||
<span className="text-xs text-gray-600">
|
||||
{aguiState.connected ? '已连接' : '未连接'}
|
||||
</span>
|
||||
</div>
|
||||
</div>
|
||||
<p className="text-xs text-gray-600">
|
||||
基于AG-UI协议的智能创作助手
|
||||
</p>
|
||||
</div>
|
||||
|
||||
{/* 消息列表 */}
|
||||
<div className="flex-1 overflow-y-auto p-4 space-y-4">
|
||||
{messages.map((message) => {
|
||||
const latestProgress = getLatestProgress(message.events)
|
||||
|
||||
return (
|
||||
<div
|
||||
key={message.id}
|
||||
className={`flex ${message.type === 'user' ? 'justify-end' : 'justify-start'}`}
|
||||
>
|
||||
<div className={`max-w-[85%] ${message.type === 'user' ? 'order-2' : 'order-1'}`}>
|
||||
{/* 消息头部 */}
|
||||
<div className={`flex items-center mb-1 ${message.type === 'user' ? 'justify-end' : 'justify-start'}`}>
|
||||
<div className={`flex items-center ${message.type === 'user' ? 'flex-row-reverse' : 'flex-row'}`}>
|
||||
<div className={`w-6 h-6 rounded-full flex items-center justify-center ${
|
||||
message.type === 'user'
|
||||
? 'bg-blue-600 text-white ml-2'
|
||||
: message.type === 'system'
|
||||
? 'bg-purple-600 text-white mr-2'
|
||||
: 'bg-gray-600 text-white mr-2'
|
||||
}`}>
|
||||
{message.type === 'user' ? (
|
||||
<User size={14} />
|
||||
) : message.type === 'system' ? (
|
||||
<Sparkles size={14} />
|
||||
) : (
|
||||
<Bot size={14} />
|
||||
)}
|
||||
</div>
|
||||
<span className="text-xs text-gray-500">
|
||||
{message.timestamp.toLocaleTimeString()}
|
||||
</span>
|
||||
{message.status && (
|
||||
<div className="ml-1">
|
||||
{getMessageStatusIcon(message.status)}
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{/* 消息内容 */}
|
||||
<div className={`rounded-lg px-3 py-2 ${
|
||||
message.type === 'user'
|
||||
? 'bg-blue-600 text-white'
|
||||
: message.type === 'system'
|
||||
? 'bg-purple-100 text-purple-900 border border-purple-200'
|
||||
: 'bg-gray-100 text-gray-900'
|
||||
}`}>
|
||||
<div className="text-sm whitespace-pre-wrap">
|
||||
{message.content}
|
||||
</div>
|
||||
|
||||
{/* AG-UI事件展示 */}
|
||||
{message.events && message.events.length > 0 && (
|
||||
<div className="mt-2 space-y-1">
|
||||
{/* 进度指示器 */}
|
||||
{latestProgress && (
|
||||
<div className="p-2 bg-white/20 rounded border">
|
||||
<div className="flex items-center justify-between mb-1">
|
||||
<span className="text-xs opacity-75">
|
||||
{latestProgress.data.message}
|
||||
</span>
|
||||
<span className="text-xs opacity-75">
|
||||
{latestProgress.data.progress}%
|
||||
</span>
|
||||
</div>
|
||||
<div className="w-full bg-white/30 rounded-full h-1">
|
||||
<div
|
||||
className="bg-white h-1 rounded-full transition-all duration-300"
|
||||
style={{ width: `${latestProgress.data.progress}%` }}
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
)}
|
||||
|
||||
{/* 工具调用展示 */}
|
||||
{message.events.filter(e => e.type === 'tool_call').map(toolEvent => (
|
||||
<div key={toolEvent.id} className="p-2 bg-blue-50/50 rounded border border-blue-200/50">
|
||||
<div className="flex items-center text-xs">
|
||||
<Sparkles size={12} className="mr-1 text-blue-600" />
|
||||
<span className="font-medium">调用工具: {toolEvent.data.tool}</span>
|
||||
</div>
|
||||
</div>
|
||||
))}
|
||||
|
||||
{/* 思考状态展示 */}
|
||||
{message.events.filter(e => e.type === 'thinking').map(thinkEvent => (
|
||||
<div key={thinkEvent.id} className="p-2 bg-purple-50/50 rounded border border-purple-200/50">
|
||||
<div className="flex items-center text-xs">
|
||||
<Bot size={12} className="mr-1 text-purple-600 animate-pulse" />
|
||||
<span className="italic">{thinkEvent.data.message}</span>
|
||||
</div>
|
||||
</div>
|
||||
))}
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
)
|
||||
})}
|
||||
<div ref={messagesEndRef} />
|
||||
</div>
|
||||
|
||||
{/* 输入区域 */}
|
||||
<div className="p-4 border-t border-gray-200 bg-gray-50">
|
||||
<div className="flex space-x-2">
|
||||
<textarea
|
||||
value={inputText}
|
||||
onChange={(e) => setInputText(e.target.value)}
|
||||
onKeyDown={handleKeyPress}
|
||||
placeholder={`告诉我您想要什么样的${project.product_name}视频...`}
|
||||
disabled={aguiState.processing}
|
||||
rows={2}
|
||||
className="flex-1 resize-none border border-gray-300 rounded-lg px-3 py-2 focus:ring-2 focus:ring-blue-500 focus:border-transparent disabled:bg-gray-100 disabled:cursor-not-allowed text-sm"
|
||||
/>
|
||||
<button
|
||||
onClick={handleSendMessage}
|
||||
disabled={!inputText.trim() || aguiState.processing}
|
||||
className="px-4 py-2 bg-blue-600 text-white rounded-lg hover:bg-blue-700 disabled:bg-gray-400 disabled:cursor-not-allowed transition-colors flex items-center"
|
||||
>
|
||||
{aguiState.processing ? (
|
||||
<Loader size={16} className="animate-spin" />
|
||||
) : (
|
||||
<Send size={16} />
|
||||
)}
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
)
|
||||
}
|
||||
|
||||
export default AGUIChat
|
||||
@@ -154,18 +154,18 @@ const ProjectMaterialsCenter: React.FC<ProjectMaterialsCenterProps> = ({
|
||||
|
||||
{/* 模特筛选 */}
|
||||
{models.length > 0 && (
|
||||
<div>
|
||||
<div className="flex items-center mb-2">
|
||||
<div className="flex items-center space-x-3">
|
||||
<div className="flex items-center">
|
||||
<User size={14} className="text-gray-400 mr-1" />
|
||||
<span className="text-xs font-medium text-gray-700">模特:</span>
|
||||
</div>
|
||||
<div className="flex flex-wrap gap-1.5">
|
||||
<button
|
||||
onClick={() => setSelectedModelIds([])}
|
||||
className={`px-2 py-1 text-xs rounded-full border transition-colors ${
|
||||
className={`px-2 py-1 text-xs rounded transition-colors ${
|
||||
selectedModelIds.length === 0
|
||||
? 'bg-blue-600 text-white border-blue-600'
|
||||
: 'bg-white text-gray-700 border-gray-300 hover:border-blue-400'
|
||||
? 'bg-blue-100 text-blue-700'
|
||||
: 'bg-gray-100 text-gray-600 hover:bg-gray-200'
|
||||
}`}
|
||||
>
|
||||
全部
|
||||
@@ -174,10 +174,10 @@ const ProjectMaterialsCenter: React.FC<ProjectMaterialsCenterProps> = ({
|
||||
<button
|
||||
key={model.id}
|
||||
onClick={() => handleModelToggle(model.id)}
|
||||
className={`px-2 py-1 text-xs rounded-full border transition-colors ${
|
||||
className={`px-2 py-1 text-xs rounded transition-colors ${
|
||||
selectedModelIds.includes(model.id)
|
||||
? 'bg-blue-600 text-white border-blue-600'
|
||||
: 'bg-white text-gray-700 border-gray-300 hover:border-blue-400'
|
||||
? 'bg-blue-100 text-blue-700'
|
||||
: 'bg-gray-100 text-gray-600 hover:bg-gray-200'
|
||||
}`}
|
||||
>
|
||||
{model.model_number}
|
||||
|
||||
@@ -5,7 +5,7 @@ import { Project, ProjectService } from '../services/projectService'
|
||||
import { VideoSegment, MediaService } from '../services/mediaService'
|
||||
import { Model, ModelService } from '../services/modelService'
|
||||
import ProjectMaterialsCenter from '../components/ProjectMaterialsCenter'
|
||||
import AICreationChat from '../components/AICreationChat'
|
||||
import AGUIChat from '../components/AGUIChat'
|
||||
|
||||
const ProjectDetailPage: React.FC = () => {
|
||||
const { projectId } = useParams<{ projectId: string }>()
|
||||
@@ -148,7 +148,16 @@ const ProjectDetailPage: React.FC = () => {
|
||||
|
||||
{/* 主要内容区域 - 三栏布局 */}
|
||||
<div className="flex h-[calc(100vh-120px)]">
|
||||
{/* 中间主要区域 - 项目素材管理 */}
|
||||
{/* 左侧 AI 聊天面板 */}
|
||||
<div className="w-80 bg-white flex flex-col">
|
||||
<AGUIChat
|
||||
project={project}
|
||||
models={projectModels}
|
||||
onMaterialCreated={() => loadProjectMaterials(project)}
|
||||
/>
|
||||
</div>
|
||||
|
||||
{/* 右侧主要区域 - 项目素材管理 */}
|
||||
<div className="flex-1 px-4 py-3 overflow-hidden">
|
||||
<ProjectMaterialsCenter
|
||||
project={project}
|
||||
@@ -157,15 +166,6 @@ const ProjectDetailPage: React.FC = () => {
|
||||
onMaterialsChange={setProjectMaterials}
|
||||
/>
|
||||
</div>
|
||||
|
||||
{/* 右侧 AI 创作面板 */}
|
||||
<div className="w-80 bg-white border-l border-gray-200 flex flex-col">
|
||||
<AICreationChat
|
||||
project={project}
|
||||
models={projectModels}
|
||||
onMaterialCreated={() => loadProjectMaterials(project)}
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
)
|
||||
|
||||
353
src/services/aguiService.ts
Normal file
353
src/services/aguiService.ts
Normal file
@@ -0,0 +1,353 @@
|
||||
import { Observable, Subject, BehaviorSubject } from 'rxjs'
|
||||
|
||||
// AG-UI Protocol Event Types
|
||||
export interface AGUIEvent {
|
||||
type: 'message' | 'progress' | 'state' | 'tool_call' | 'error' | 'complete' | 'thinking' | 'action'
|
||||
id: string
|
||||
timestamp: Date
|
||||
data: any
|
||||
metadata?: {
|
||||
agent_id?: string
|
||||
session_id?: string
|
||||
correlation_id?: string
|
||||
}
|
||||
}
|
||||
|
||||
export interface AGUIMessage {
|
||||
role: 'user' | 'assistant' | 'system'
|
||||
content: string
|
||||
timestamp: Date
|
||||
metadata?: any
|
||||
}
|
||||
|
||||
export interface AGUIState {
|
||||
connected: boolean
|
||||
processing: boolean
|
||||
currentAgent?: string
|
||||
sessionId?: string
|
||||
error?: string
|
||||
}
|
||||
|
||||
export interface AGUIAgentConfig {
|
||||
id: string
|
||||
name: string
|
||||
description: string
|
||||
capabilities: string[]
|
||||
endpoint?: string
|
||||
}
|
||||
|
||||
export class AGUIService {
|
||||
private eventSubject = new Subject<AGUIEvent>()
|
||||
private stateSubject = new BehaviorSubject<AGUIState>({
|
||||
connected: false,
|
||||
processing: false
|
||||
})
|
||||
|
||||
private sessionId: string
|
||||
private agents: Map<string, AGUIAgentConfig> = new Map()
|
||||
|
||||
constructor() {
|
||||
this.sessionId = this.generateSessionId()
|
||||
this.initializeDefaultAgents()
|
||||
}
|
||||
|
||||
private generateSessionId(): string {
|
||||
return `session_${Date.now()}_${Math.random().toString(36).substring(2, 11)}`
|
||||
}
|
||||
|
||||
private initializeDefaultAgents() {
|
||||
const videoCreationAgent: AGUIAgentConfig = {
|
||||
id: 'video_creator',
|
||||
name: 'Video Creation Agent',
|
||||
description: '专业的视频创作智能体,擅长生成高质量的产品展示视频',
|
||||
capabilities: [
|
||||
'video_generation',
|
||||
'script_writing',
|
||||
'model_selection',
|
||||
'scene_composition',
|
||||
'quality_optimization'
|
||||
]
|
||||
}
|
||||
|
||||
const contentOptimizationAgent: AGUIAgentConfig = {
|
||||
id: 'content_optimizer',
|
||||
name: 'Content Optimization Agent',
|
||||
description: '内容优化专家,专注于提升视频内容的吸引力和转化率',
|
||||
capabilities: [
|
||||
'content_analysis',
|
||||
'audience_targeting',
|
||||
'engagement_optimization',
|
||||
'a_b_testing',
|
||||
'performance_metrics'
|
||||
]
|
||||
}
|
||||
|
||||
this.agents.set(videoCreationAgent.id, videoCreationAgent)
|
||||
this.agents.set(contentOptimizationAgent.id, contentOptimizationAgent)
|
||||
}
|
||||
|
||||
// 连接到AG-UI智能体
|
||||
async connect(agentId?: string): Promise<void> {
|
||||
try {
|
||||
this.updateState({ connected: true, currentAgent: agentId })
|
||||
|
||||
// 发送连接事件
|
||||
this.emitEvent({
|
||||
type: 'state',
|
||||
id: this.generateEventId(),
|
||||
timestamp: new Date(),
|
||||
data: {
|
||||
status: 'connected',
|
||||
agent_id: agentId,
|
||||
session_id: this.sessionId
|
||||
},
|
||||
metadata: {
|
||||
agent_id: agentId,
|
||||
session_id: this.sessionId
|
||||
}
|
||||
})
|
||||
|
||||
// 模拟连接延迟
|
||||
await new Promise(resolve => setTimeout(resolve, 1000))
|
||||
|
||||
} catch (error) {
|
||||
this.updateState({
|
||||
connected: false,
|
||||
error: error instanceof Error ? error.message : 'Connection failed'
|
||||
})
|
||||
throw error
|
||||
}
|
||||
}
|
||||
|
||||
// 断开连接
|
||||
async disconnect(): Promise<void> {
|
||||
this.updateState({ connected: false, processing: false, currentAgent: undefined })
|
||||
|
||||
this.emitEvent({
|
||||
type: 'state',
|
||||
id: this.generateEventId(),
|
||||
timestamp: new Date(),
|
||||
data: {
|
||||
status: 'disconnected',
|
||||
session_id: this.sessionId
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
// 发送消息到智能体
|
||||
async sendMessage(message: string, context?: any): Promise<void> {
|
||||
if (!this.stateSubject.value.connected) {
|
||||
throw new Error('Not connected to agent')
|
||||
}
|
||||
|
||||
this.updateState({ processing: true })
|
||||
|
||||
try {
|
||||
// 发送用户消息事件
|
||||
this.emitEvent({
|
||||
type: 'message',
|
||||
id: this.generateEventId(),
|
||||
timestamp: new Date(),
|
||||
data: {
|
||||
role: 'user',
|
||||
content: message,
|
||||
context
|
||||
},
|
||||
metadata: {
|
||||
session_id: this.sessionId,
|
||||
correlation_id: this.generateEventId()
|
||||
}
|
||||
})
|
||||
|
||||
// 模拟智能体处理
|
||||
await this.simulateAgentProcessing(message, context)
|
||||
|
||||
} catch (error) {
|
||||
this.emitEvent({
|
||||
type: 'error',
|
||||
id: this.generateEventId(),
|
||||
timestamp: new Date(),
|
||||
data: {
|
||||
error: error instanceof Error ? error.message : 'Unknown error',
|
||||
original_message: message
|
||||
}
|
||||
})
|
||||
throw error
|
||||
} finally {
|
||||
this.updateState({ processing: false })
|
||||
}
|
||||
}
|
||||
|
||||
// 模拟智能体处理过程
|
||||
private async simulateAgentProcessing(message: string, context?: any): Promise<void> {
|
||||
const correlationId = this.generateEventId()
|
||||
|
||||
// 1. 思考阶段
|
||||
this.emitEvent({
|
||||
type: 'thinking',
|
||||
id: this.generateEventId(),
|
||||
timestamp: new Date(),
|
||||
data: {
|
||||
message: '正在分析您的需求...',
|
||||
stage: 'analysis'
|
||||
},
|
||||
metadata: { correlation_id: correlationId }
|
||||
})
|
||||
|
||||
await this.delay(800)
|
||||
|
||||
// 2. 进度更新
|
||||
const progressSteps = [
|
||||
{ progress: 20, message: '理解需求内容' },
|
||||
{ progress: 40, message: '选择最佳策略' },
|
||||
{ progress: 60, message: '生成创作方案' },
|
||||
{ progress: 80, message: '优化输出质量' }
|
||||
]
|
||||
|
||||
for (const step of progressSteps) {
|
||||
this.emitEvent({
|
||||
type: 'progress',
|
||||
id: this.generateEventId(),
|
||||
timestamp: new Date(),
|
||||
data: step,
|
||||
metadata: { correlation_id: correlationId }
|
||||
})
|
||||
await this.delay(600)
|
||||
}
|
||||
|
||||
// 3. 工具调用
|
||||
this.emitEvent({
|
||||
type: 'tool_call',
|
||||
id: this.generateEventId(),
|
||||
timestamp: new Date(),
|
||||
data: {
|
||||
tool: 'video_generator',
|
||||
parameters: {
|
||||
prompt: message,
|
||||
context: context,
|
||||
quality: 'high',
|
||||
format: 'mp4'
|
||||
}
|
||||
},
|
||||
metadata: { correlation_id: correlationId }
|
||||
})
|
||||
|
||||
await this.delay(1000)
|
||||
|
||||
// 4. 智能体响应
|
||||
const response = this.generateAgentResponse(message, context)
|
||||
this.emitEvent({
|
||||
type: 'message',
|
||||
id: this.generateEventId(),
|
||||
timestamp: new Date(),
|
||||
data: {
|
||||
role: 'assistant',
|
||||
content: response,
|
||||
confidence: 0.95
|
||||
},
|
||||
metadata: { correlation_id: correlationId }
|
||||
})
|
||||
|
||||
await this.delay(500)
|
||||
|
||||
// 5. 完成
|
||||
this.emitEvent({
|
||||
type: 'complete',
|
||||
id: this.generateEventId(),
|
||||
timestamp: new Date(),
|
||||
data: {
|
||||
success: true,
|
||||
duration: Date.now() - new Date().getTime(),
|
||||
tokens_used: Math.floor(Math.random() * 1000) + 500
|
||||
},
|
||||
metadata: { correlation_id: correlationId }
|
||||
})
|
||||
}
|
||||
|
||||
private generateAgentResponse(message: string, context?: any): string {
|
||||
const productName = context?.project?.product_name || '您的产品'
|
||||
|
||||
const responses = [
|
||||
`我理解您想要为"${productName}"创作视频内容。基于您的描述,我建议采用以下创作策略:
|
||||
|
||||
🎯 **核心亮点**
|
||||
• 突出产品的独特价值
|
||||
• 展示真实使用场景
|
||||
• 强调用户体验
|
||||
|
||||
📹 **视觉风格**
|
||||
• 现代简约的设计风格
|
||||
• 温暖自然的色调
|
||||
• 动态流畅的镜头运动
|
||||
|
||||
🎭 **内容结构**
|
||||
1. 吸引注意的开场
|
||||
2. 产品核心功能展示
|
||||
3. 用户使用场景
|
||||
4. 强有力的行动召唤
|
||||
|
||||
您希望我开始创作吗?或者您想调整某些方面?`,
|
||||
|
||||
`很好的创意!让我为"${productName}"设计一个专业的视频方案:
|
||||
|
||||
✨ **创作思路**
|
||||
基于您的需求,我推荐制作一个既有情感共鸣又具有说服力的产品视频。
|
||||
|
||||
🎬 **制作建议**
|
||||
• 时长:30-60秒最佳
|
||||
• 风格:现代商业风格
|
||||
• 重点:产品价值传递
|
||||
|
||||
📊 **预期效果**
|
||||
• 提升品牌认知度
|
||||
• 增强用户信任感
|
||||
• 促进转化行动
|
||||
|
||||
我现在可以开始为您生成具体的视频内容,您觉得这个方向如何?`
|
||||
]
|
||||
|
||||
return responses[Math.floor(Math.random() * responses.length)]
|
||||
}
|
||||
|
||||
// 获取事件流
|
||||
getEventStream(): Observable<AGUIEvent> {
|
||||
return this.eventSubject.asObservable()
|
||||
}
|
||||
|
||||
// 获取状态流
|
||||
getStateStream(): Observable<AGUIState> {
|
||||
return this.stateSubject.asObservable()
|
||||
}
|
||||
|
||||
// 获取可用智能体
|
||||
getAvailableAgents(): AGUIAgentConfig[] {
|
||||
return Array.from(this.agents.values())
|
||||
}
|
||||
|
||||
// 私有辅助方法
|
||||
private emitEvent(event: AGUIEvent): void {
|
||||
this.eventSubject.next(event)
|
||||
}
|
||||
|
||||
private updateState(updates: Partial<AGUIState>): void {
|
||||
const currentState = this.stateSubject.value
|
||||
this.stateSubject.next({ ...currentState, ...updates })
|
||||
}
|
||||
|
||||
private generateEventId(): string {
|
||||
return `event_${Date.now()}_${Math.random().toString(36).substring(2, 11)}`
|
||||
}
|
||||
|
||||
private delay(ms: number): Promise<void> {
|
||||
return new Promise(resolve => setTimeout(resolve, ms))
|
||||
}
|
||||
|
||||
// 清理资源
|
||||
dispose(): void {
|
||||
this.eventSubject.complete()
|
||||
this.stateSubject.complete()
|
||||
}
|
||||
}
|
||||
|
||||
// 单例实例
|
||||
export const aguiService = new AGUIService()
|
||||
Reference in New Issue
Block a user