Files
test1/README.md
2026-03-20 07:33:46 +00:00

214 lines
9.2 KiB
Markdown
Raw Permalink 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.
# vibe-next-template
Nova Agent 示例模板工程。[Nova](https://nova.betteryeah.com) 是一个创建 Agent 的 SaaS 产品,可以通过 Nova 创建能够操作沙箱、浏览器、文件系统的专业 Agent。本项目基于 Next.js 提供完整的 Agent 聊天前端,包含消息流、事件列表、会话管理与文件上传能力。
项目中 `.nova/config.json` 内置了一个示例 Agent如需基于本模板开发自己的应用请在 Nova 平台创建 Agent 并替换配置。
## 技术栈
- Next.js 16App Router / React 19 / TypeScript 5
- Tailwind CSS 4 / Radix UI / Lucide React Icons
- Zustand状态管理
- 自定义 WebSocket 客户端(实时通信)
- 自定义 HTTPClientFetch 封装,支持拦截器)
- Drizzle ORM + PostgreSQL
- Winston日志生产环境按天轮转
- Shiki代码高亮/ React Markdown + Remark GFM
## 快速开始
1. 安装依赖:`pnpm install`
2. `.env`环境变量文件已经初始化完成了,所以设计环境变量的需要在这里修改
3. (可选)修改 `.nova/config.json`,替换为你自己的 Agent
4. 启动开发服务器:`pnpm dev`(端口 13000
## Agent 配置
Agent 配置存放在 `.nova/config.json`
```json
{
"agents": [
{
"agent_id": "70da765f2d42490ca574d72dce4d24fe",
"agent_name": "Nova示例Agent",
"agent_description": "一个Nova的Agent示例包含Agent具备的通用功能文件操作、沙箱环境执行、浏览器操作等等。"
}
]
}
```
- `agents` 数组中的第一个 Agent 作为默认使用的 Agent
- 在 Nova 平台创建新 Agent 后,将 `agent_id``agent_name``agent_description` 替换为新 Agent 的信息
- 配置通过 `app/api/nova-config.ts``getDefaultAgentId()` 在服务端读取,启动后缓存
## 目录结构
```
.nova/
config.json # Agent 配置agent_id, agent_name, agent_description
app/
layout.tsx # 根布局Geist Sans/Mono 字体)
page.tsx # 主入口,初始化聊天连接
globals.css # 全局样式
api/ # Next.js API 路由BFF 层,代理 Nova 平台 API
oapi-client.ts # 共享的 Nova OpenAPI HTTPClient 实例
nova-config.ts # 读取 .nova/config.json提供 getDefaultAgentId()
info/route.ts # 返回客户端配置agentId, conversationId, wssUrl, token
chat/event/route.ts # 代理获取聊天事件历史
chat/stop/route.ts # 停止当前任务
conversation/route.ts # 会话列表
file/upload/route.ts # 文件上传
file/sign/route.ts # 文件签名 URL
health/route.ts # 健康检查
components/
nova-sdk/ # 核心聊天 SDK
types.ts # 核心类型定义ApiEvent, PlatformConfig, TaskArtifact 等)
websocket.ts # WebSocket 客户端(自动重连、心跳、离线队列)
store/
useNovaStore.ts # Zustand 全局状态events, artifacts, ws 状态, loading
context/
context.ts # NovaContext 定义HTTPClient + getArtifactUrl
Provider.tsx # Context Provider
useNova.ts # useContext hook
hooks/
useNovaChatLogic.ts # 主编排 hook组合所有子 hook
useNovaEvents.ts # WebSocket + 历史事件获取
useNovaService.ts # 从 platformConfig 创建 HTTPClient
useMessageSender.ts # 通过 WebSocket 发送消息
useEventProcessor.ts # ApiEvent → UI 消息转换
useBuildConversationConnect.ts # 初始化:获取 agentId、conversationId
useFileUploader.ts # 文件上传逻辑
useAttachmentHandlers.ts # 附件点击处理
useMessageScroll.ts # 消息列表自动滚动
usePanelState.ts # 产物面板开关
useSize.ts # 响应式尺寸
nova-chat/ # 聊天主组件
index.tsx # NovaChat包裹 Provider、Header、MessageList、Input、TaskPanel
ChatHeader.tsx # 聊天头部
ChatInputArea.tsx # 输入区域
message-list/ # 消息列表
index.tsx # MessageList
MessageItem.tsx # 单条消息
AttachmentItem.tsx # 文件附件
ImageAttachmentItem.tsx # 图片附件
ToolCallAction.tsx # 工具调用展示
message-input/ # 消息输入
index.tsx # 输入组件
FilePreviewList.tsx # 文件预览列表
task-panel/ # 产物预览面板
index.tsx # TaskPanel侧边栏50% 宽度)
ArtifactList.tsx # 产物列表
ArtifactPreview.tsx # 产物预览
Preview/ # 各类预览组件Markdown、代码、PPT、工具调用
ui/ # 基础 UI 组件Shadcn 风格)
button.tsx, dialog.tsx, scroll-area.tsx, image.tsx, image-preview.tsx
http/
index.ts # HTTPClient 类Fetch 封装)
http.ts # 底层 fetch 包装,支持 onRequest/onResponse/onError 钩子
type.ts # HTTP 类型定义
db/
index.ts # Drizzle ORM 连接server-only全局单例
utils/
logger.ts # Winston 日志(开发→终端,生产→按天文件轮转)
cn.ts # clsx + tailwind-merge 工具函数
```
## 数据流
```
app/page.tsx
│ useBuildConversationConnect() → GET /api/info
│ 获取 agentId来自 .nova/config.json, conversationId, platformConfig
NovaChat 组件
│ useNovaChatLogic() 编排所有子 hook
├── useNovaEvents() → WebSocket 连接 + GET /api/chat/event 加载历史
├── useMessageSender() → WebSocket 发送消息
└── useEventProcessor()→ 事件转 UI 消息
Zustand Store (useNovaStore)
│ events[], artifacts[](从 events 自动提取), loading, ws 状态
MessageList / TaskPanel 渲染
```
## API 路由
| 路由 | 方法 | 说明 |
|------|------|------|
| `/api/info` | GET | 返回客户端配置agentId, conversationId, wssUrl, token |
| `/api/chat/event` | GET | 代理 Nova `/chat/event_list`,获取事件历史 |
| `/api/chat/stop` | POST | 停止当前任务 |
| `/api/conversation` | GET | 会话列表 |
| `/api/file/upload` | POST | 上传文件 |
| `/api/file/sign` | POST | 获取文件签名 URL |
| `/api/health` | GET | 健康检查 |
所有 API 路由通过 `app/api/oapi-client.ts` 中的共享 HTTPClient 代理到 Nova 平台,请求头自动注入 `Tenant-Id``Authorization`。Agent ID 从 `.nova/config.json` 读取,不再依赖环境变量。
## WebSocket 消息格式
- 心跳:`{ message_type: 'ping' }`
- 聊天消息:`{ message_type: 'chat', conversation_id, content, ... }`
- 切换会话:`{ message_type: 'switch_conversation', conversation_id }`
WebSocket 客户端内置自动重连(可配置次数/间隔)、心跳检测、网络状态监听、页面可见性恢复。
## 常用命令
```bash
pnpm install # 安装依赖
pnpm dev # 启动开发服务器(端口 13000
pnpm build # 生产构建
pnpm start # 生产运行(端口 13000
pnpm lint # ESLint 检查并自动修复
pnpm db:generate # 生成 Drizzle 迁移
pnpm db:migrate # 执行数据库迁移
pnpm db:studio # 打开 Drizzle Studio GUI
```
## 环境变量
参考 `.env.example`,在项目根目录创建 `.env.local`
| 变量名 | 说明 |
|--------|------|
| `DATABASE_URL` | PostgreSQL 连接地址Drizzle 使用) |
| `LOG_DIR` | 生产环境日志目录(默认 `./logs`,按天切分) |
| `NOVA_BASE_URL` | Nova OpenAPI 服务地址 |
| `NOVA_TENANT_ID` | 租户 ID请求头 `Tenant-Id` |
| `NOVA_ACCESS_KEY` | 鉴权密钥(请求头 `Authorization` |
> Agent ID 已从环境变量迁移至 `.nova/config.json`,无需在 `.env.local` 中配置。
## 代码约定
- **路径别名**`@/*``./*`tsconfig paths
- **组件**PascalCase 文件名,使用 `React.memo()` 优化
- **Hooks**camelCase`use*` 前缀
- **工具函数**camelCase
- **常量枚举**UPPER_SNAKE_CASE`EventType`, `TaskStatus`
- **样式**Tailwind CSS 工具类,通过 `cn()` 函数(`utils/cn.ts`)合并类名
- **回调稳定性**:使用 ahooks 的 `useMemoizedFn()``useCallback()`
- **异步状态**`queueMicrotask()` 延迟更新,避免 React 批量更新冲突
- **Store 去重**Zustand store 按 `event_id` 去重事件
- **日志**:开发环境输出到终端,生产环境按天写入 `${LOG_DIR}/app-YYYY-MM-DD.log`
- **数据库**:所有操作通过 Drizzle ORM`import { db } from '@/db'`,仅限 server 端
- **UI 错误提示**:使用中文(如 "Chat 不可用,请检查项目 .env 配置"
- **请求头约定**`X-Locale=zh`, `X-Region=CN`
## 注意事项
- `next.config.ts``reactStrictMode: false`
- `.next/` 目录为构建产物,不要手动修改
- 数据库连接使用全局单例模式,`db/index.ts` 标记了 `server-only`
- 聊天初始化流程:先请求 `/api/info` 获取配置 → 再建立 WebSocket → 加载历史事件
- 产物提取来源:`attachments`, `attachment_files`, `files`, `generated_files`,按文件 key 去重
- 文件签名通过 `getArtifactUrl()` 懒加载POST `/file/sign`,失败时 fallback 到原始路径