内容
iumJS AI 工具示例
一个生产就绪的启动器,将 [CesiumJS](https://cesium.com/platform/cesiumjs/) 3D 地球浏览器与 LLM 驱动的聊天界面配对。LLM 通过结构化的工具调用(例如 _“飞往巴黎”_)驱动地球,而 **LLM API 密钥从未到达浏览器** — 所有推理都在 Node.js API 服务器后面运行。
---
## 前提条件 **Node.js**20
- ** ≥ 9
- 一个免费的 [Cesium Ion://ion.cesium.com
---
##
```bash
# 1. 安装依赖
npm install
# 2环境
cp .env.example .env
# 编辑 .env 并设置 VITE_CESIUM_ION_ACCESS_TOKEN。
# 对于聊天,还需要设置 LLM 密钥(例如 OPENAI_API_KEY)— 可选;如果没有,查看器仍会运行,而 /api/chat 返回 NOT_CONFIGURED 响应。
# 3. 同时运行开发服务器(前端 + 后端)
npm run dev
```
地球浏览器在 `http://localhost:5173` 打开;聊天 API 在 `http://localhost:3001` 运行。
---
## 试用
一旦 `npm run dev` 运行并且您已打开 `http://localhost:5173`,在聊天面板中输入一个地点名称 — 例如 **飞** — 并发送。助手解析位置,地球浏览器飞往那里,助手在到达后确认。模型已知的任何城市、国家、地标或地址都有效 — 试试 **伦敦**、**珠穆朗玛峰** 或 **1600 宾夕法尼亚大道**。
如果没有配置提供商 API 密钥,聊天面板会显示一个横幅 — _“AI 未配置。向 .env 文件中添加受支持的 API 密钥。” 而地球浏览器仍然作为一个纯粹的浏览器运行。
---
## 架构
```
浏览器┌─────────────────────────┐ ┌────────────────────────────┐
│ Vite SPA │ │ Node.js API │
│ ├─ CesiumJS 浏览器 │◄────►│ ├─ /api/chat │
│ └─ 聊天面板 │ SSE │ ├─ 代理循环(streamText) │
│ └─ onToolCalls ──────┼─────►│ └─ 工具注册表 │
└────────────────────────────────────────────────────┘
▲ │
│ 浏览器工具结果 │ MCP / LLM API 密钥(仅限服务器)
└───────────────────────────────────┘
```
**分执行模型:** 浏览器工具(相机导航、实体操作)流式传输到浏览器,并在实时 `Viewer` 实例上执行。MCP 工具完全在服务器端运行,从未作为客户端工具调用流式传输。
**工作区包:** 可重用的、与模型无关的组件位于 `packages/` 中,由 `backend/` 主机应用程序使用:
- **`@cesium-ai/server`** — 一个 Express 路由器,安装 AI SDK 聊天密钥层(/api/chat)。表和已解析的语言模型,并在服务器端运行 `streamText` 代理循环,这样 LLM API 密钥从未到达浏览器。主机应用程序负责提供程序选择。
- **`@cesium-ai/tools-cesium`** — Zod 架构的 CesiumJS 浏览器工具定义(`flyTo`,...)。仅限架构,无 `execute` — 工具调用在客户端针对实时 `Viewer` 执行。
---
## 使用 Cesium 工具
两种常见的更改 — 启用或禁用工具,以及编辑工具的架构 — 每个更改都有一个单独定义明确的编辑位置。两个方面(后端、前端执行器)都遵循以下内容:
### 启用或禁用工具
应用程序的工具界面在一个数组中策划:`ENABLED_CESIUM_TOOLS` 在 [`shared/src/enabled-tools.ts`](shared/src/enabled-tools.ts)(`@cesium-ai/sample-config` 包)。它是两个层都读取的单一真实来源:
- **后端** 从中构建其注册表 — `createCesiumTools({ enabled: ENABLED_CESIUM_TOOLS })` 在 [`backend/src/index.ts`](backend/src/index.ts) — 因此模型仅被提供这些工具;
- **前端** 根据它键入其执行器处理 — `ChatPanel.tsx` 将每个传入的工具调用都限制在此集合上作为防御深度,因此禁用的(或过时/伪造的)工具调用永远不会驱动实时 `Viewer`。
要 **禁用** 工具,请从数组中删除其名称。要 **启用** 工具,请添加其名称:
```ts
// shared/src/enabled-tools.ts
export const ENABLED_CESIUM_TOOLS = [
CESIUM_TOOL_NAMES.flyTo,
// CESIUM_TOOL_NAMES.someOtherTool, // ← 添加以启用
] as const satisfies readonly CesiumToolName[];
```
两个编译时守卫确保这一点:
- 每个条目都针对 `CesiumToolName` 进行检查(`satisfies`),因此输入错误或不是真正的 Cesium 工具的名称会导致编译失败。
- 前端在 [`frontend/ChatPanel.tsx`](frontend/src/components/ChatPanel 中的 `TOOL_EXECUTORS` 映射被键Record<EnabledTool, ToolExecutor>`。启用工具 **而没有**添加客户端执行器失败 — 因此应用程序永远不会为浏览器无法工具提供模型。
编辑后,重新构建,以便两个层都:`npm run build:packages`( `npm run dev` 运行 — 它会监视)。然后用允许列表测试保护合约:
```bash
npm test -- enabled-tools
```
> **添加全新工具上述的超集:(1)在 `CESIUM` 中注册其规范名称([`packagescesium/src/tool-names.ts`](packages/toolsium/src/tool-names.ts));(2)在/tools-cesium/src/` 下添加其架构/定义模块,并将其连接到 `createCesiumTools`([`index.ts`](packages/tools-cesium/src));(3)在 `frontend/src/tools/` 下编写其客户端执行器,并将其映射到 `TOOL_EXECUTORS` 中;(4)将名称添加到 `ENABLED_CESIUM_TOOLS` 以启用它。
### 更新工具架构
工具的架构存在于 **两个层** 中,拆分后 LLM 面向的提示永远不会发送到浏览器。选择与您要更改的内容匹配的层:
| 您要更改... | 编辑 |看到它 |
| ----------------------------------------------------------------------------------------------------------------- | -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | ------------------------------------------------------------------- |
| **参数合约** — 结构规则(纬度/经度范围,哪些字段存在,可选 vs. 必需) | `flyToInputShape` 在 [`packages/tools-ces/schemas.ts`](packages/tools-cesium/src/schemas.ts) | 两个层 — 后端从中派生,前端验证它 |
| **模型面向的提示** — 人类可读的工具 `description` 和每个字段的 `.describe()` 文本,LLM 读取 | `DEFAULT_TO_DESCRIPTION` / `DEFAULT_FLY_TO_FIELD_DESCRIPTIONS` / `buildFlyToInputSchema` 在 [`packages/tools-cesium/srclyTo/flyTo.ts`](packages/toolsium/src/tools/flyTo/flyTo | 仅限后端(模型) — 永远不会捆绑到客户端 |
(`schemas.ts`)是 **参数单一真实来源**。前端直接导入它(通过 `/schemas` 子路径)以验证不受信任的参数;后端的模型面向架构从 `buildFlyToInputSchema` 中 **派生**,flyToInputShape.shape.*.describe(...)` 中分层 LLM 提示,而无需重新定义规则。因此,合约更改 — 例如,收紧纬度/经度范围 — 是对 `flyToInputShape` 的 **单个编辑**,两个层都自动获取。
编辑后,重新构建包(`npm run build:packages` 或依赖 `npm run dev` 的监视)并运行偏移守卫,它断言后端的模型面向架构和前端的验证形状在多个边界输入的电池测试中达到相同的接受/拒绝判决```bash
npm test -- flyTo.schema```
> 每主机调整,无需分叉工具:`createFlyTo` 接受 `FlyTo(`description`、`fieldDescriptions` 或完整的 `inputSchema`)。注意 `inputSchema` **模型面向** 的架构 — 使用它进行模型面向的调整,而不是更改验证的合约。要更改编辑 `flyTo`。
---
## 环境变量
| 变量 | 必需 | 描述 |
| ------------------------------ | ----------------- | ----------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| `VITE_CESIUM_ION_ACCESS_TOKEN` | 是 | Cesium Ion 令牌 — 在构建时烘焙到客户端包中。故意客户端可见;请在 Ion 控制台中限制允许的资产和 HTTP 引用者。 |
| `OPENAI_API_KEY` | 当启用聊天时 | LLM API 密钥 — 仅限服务器端,永远不会前缀为 `VITE_`。当 `AI_PROVIDER=openai` 时需要。 |
| `ANTHROPIC_API_KEY` | 当启用聊天时 | 当 `AI_PROVIDER=anthropic` 时需要。 `GOOGLE_GENERATIVE_AI_API_KEY` | 当启用聊天时 `AI_PROVIDER= 时需要。 |
| `AI_PROVIDER` | 否 | `openai`(默认)\| `anthropic` \| `google` |
| `AI | 否 重写所选提供者的默认模型。 |
| `CHAT_ENABLED` | 否 | `true`(默认)\| `false` — 当 `false` 时,`/api/chat` 返回 `NOT_CONFIGURED`,应用程序作为纯静态浏览器运行。 |
| `RATE_LIMIT_RPM` | 否 | 每 IP 每分钟 `/api/chat` 请求(默认 `20`)。 |
参见 [`.env.example`](.env.example) 以获取完整列表,包括 `AI_BASE_URL` 和遥测设置。
复制 `.env.example` 到 `.env` 并填写您的值。`.env` 文件被 git 忽略。
---
## 脚本
| 命令 | 描述 |
| ------------------------ | ------------------------------------------------------------------------------------------------- |
| `npm run dev` | 构建工作区包,然后同时启动包(监视)、后端和前端。 |
| `npm run dev:frontend` | 仅启动带有 HMR 的 Vite 开发服务器。 |
| `npm run dev:backend` | 构建包,然后在监视模式下运行后端 API 和包(无前端)。 |
| `npm run build:packages` | 类型检查和构建可重用的工作区包(`@cesium-ai/*`)。 |
| `npm run build` | 构建包,然后类型检查和构建前端包和后端。 |
| `npm run preview` | 在本地提供生产前端包。 |
| `npm run format` | 使用 Prettier 格式化整个工作区(写入就地)。 |
| `npm:check` | 验证格式而无需写入 — CI 运行的检查。 |
---
## 项目结构
```
cesiumjs-ai-tools-sample/
├── frontend/ # Vite + React SPA
│ ├── src/
│ │ ├── App.tsx # 根组件 — 安装 Viewer + 聊天面板
│ │ ├── CesiumGlobe.tsx # CesiumJS 浏览器生命周期包装器
│ │ ├── components/
│ │ │ └── ChatPanel.tsx # 主机端工具调用侦听器 — TOOL_EXECUTORS 映射
│ │ ├── tools/
│ │ │ └── camera.ts # flyToLocation — 客户端 flyTo 执行器
│ │ ├── cesium-loader.ts # 浏览器初始化(地形、默认值)
│ │ ├── config.ts # 读取 VITE_* 环境变量 │ └── main.tsx # React点
│ └── vite.config.ts # Vite 配置(复制 CesiumJS 静态资产)
├── backend/ # 薄 Node.js 主(Express)
│ ├── src/
│ │ ├── index.ts # 连接 @cesium + 工具、CORS、监听
│ │ ├── app.ts # Express CORS、速率限制器、/health、聊天路由器)
│ │ ├── tools/
│ │ └── flyto-tool.ts # 后端专用 flyTo 输入架构扩展
│ │ └── utils/
│ │ ├── env.ts # Zod 验证的、类型化的环境配置
│ │ ├── providers.ts # LLM 提供者工厂(createModel)
│ │ └── rate-limit.ts # 基于 IP 的滑动窗口限制器
│ └── tsconfig.json
├── packages/ # 可重用的工作
│ ├── server/ # @-ai/server — Express 聊天密钥层
│ │ └── src/
│ │ ├── chat-router.ts # createChatRouter — POST /api/chat (SSE)
│ │ └── agent.ts # 代理循环 — stream带有工具注册表
│ └── tools-cesium/ # @cesium-ai/tools-cesium — 浏览器工具架构
│ └── src/
│ ├── tool-names.ts # CESIUM_TOOL_NAMES — 规范工具标识符
│ ├── schemas.ts # flyToInputShape — 共享参数合约(结构)
│ ├── tools/flyTo/flyTo.ts # flyTo 工具(模型面向架构 + 提示,无执行)
│ └── index.ts # createCesiumTools 注册表(启用允许列表)
├── shared/ # @cesium-ai/sample-config — 应用程序的工具选择
│ └── src/
│ └── enabled-tools.ts # ENABLED_CESIUM_TOOLS — 在此处启用/禁用工具
├── .architecture/ # 架构决策文档
├── .prettierrc.json # Prettier 配置(代码格式化规则)
├── .editorconfig # 编辑器默认值(缩进、字符集、EOL)
├── .env.example # 环境变量模板
└── package.json # npm 工作区根目录
```
---
## Cesium Ion 令牌
Ion 令牌是加载 Cesium World Terrain 和 Cesium World Imagery 所必需的。在 [ion.cesium.com](https://ion.cesium.com) 免费获取一个。在部署到生产环境之前,在 Ion 控制台中限制令牌(允许的资产 + HTTP 引用者)。
---
## 代码风格
使用 [Prettier](https://prettier.io) 强制执行格式化。规则位于 [`.prettierrc.json`](.prettierrc.json) 中(唯一覆盖的是 `: 100`;其他都是 Prettier 默认值 — 2 个空格缩进、号、尾随逗号),`[.`](.editorconfig) 涵盖编辑器的缩进、字符集和行尾。
npm run format 在本地格式化整个工作区
npm run format:check # 验证格式化(如果有未格式化的文件,则失败)
```
在提交前运行 `npm run format CI 运行 `npm run format:check`未格式化的文件上失败。
---
## CI
GitHub Actions 在每次推送或拉取请求时运行,触及此目录下的文件([`.github/workflows/ci.yml`](.github/workflows/sample-app-ci.yml)):
- **格式检查** — `npm run format:check`(Prettier)。
- **构建** — 安装依赖项,然后构建工作区包、前端包和后端(每个作为单独的、类型检查的步骤)。
## 🤝
连接信息
猜你喜欢
暂无推荐服务器