9.2 KiB
9.2 KiB
vibe-next-template
Nova Agent 示例模板工程。Nova 是一个创建 Agent 的 SaaS 产品,可以通过 Nova 创建能够操作沙箱、浏览器、文件系统的专业 Agent。本项目基于 Next.js 提供完整的 Agent 聊天前端,包含消息流、事件列表、会话管理与文件上传能力。
项目中 .nova/config.json 内置了一个示例 Agent,如需基于本模板开发自己的应用,请在 Nova 平台创建 Agent 并替换配置。
技术栈
- Next.js 16(App Router) / React 19 / TypeScript 5
- Tailwind CSS 4 / Radix UI / Lucide React Icons
- Zustand(状态管理)
- 自定义 WebSocket 客户端(实时通信)
- 自定义 HTTPClient(Fetch 封装,支持拦截器)
- Drizzle ORM + PostgreSQL
- Winston(日志,生产环境按天轮转)
- Shiki(代码高亮)/ React Markdown + Remark GFM
快速开始
- 安装依赖:
pnpm install .env环境变量文件已经初始化完成了,所以设计环境变量的需要在这里修改- (可选)修改
.nova/config.json,替换为你自己的 Agent - 启动开发服务器:
pnpm dev(端口 13000)
Agent 配置
Agent 配置存放在 .nova/config.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 客户端内置自动重连(可配置次数/间隔)、心跳检测、网络状态监听、页面可见性恢复。
常用命令
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 到原始路径