AI 聊天
通过 Vercel AI SDK 与 AI Elements 流式返回 OpenAI 响应。
AI 聊天是基于 Vercel AI SDK、OpenAI 与 AI Elements 的可运行示例。它流式 传输 UI message,可渲染 Markdown、reasoning、source,支持附件与允许模型选择。 接口当前公开,上线前必须补齐产品访问控制。
请求流程
ChatBot → useChat + DefaultChatTransport → POST /api/chat
→ Zod 解析 → 允许的 OpenAI 模型 → streamText
→ UI message stream → AI Elements 渲染| 文件 | 作用 |
|---|---|
src/components/ai/ChatBot.tsx | 聊天状态、附件、模型/搜索控制、流式 UI、复制/重试/停止 |
src/components/ai-elements/* | Conversation、message、prompt、reasoning、source 与附件 primitive |
src/routes/api/chat.ts | 请求 schema、model message 转换、system prompt 与 stream response |
src/lib/ai/chat-config.ts | 模型白名单、默认值、search-preview 映射与 system prompt |
src/lib/ai/index.ts | 解析 runtime model 并创建 OpenAI model handle |
{-$locale}/_marketing/ai/chat.tsx | 公开示例页与 SEO |
配置
本地加入 key,生产环境保存为 Worker secret:
# .env(仅本地)
OPENAI_API_KEY=sk-your-key
pnpm wrangler secret put OPENAI_API_KEY本地 secret 变化后重启 pnpm dev。绝不要改成 VITE_* 值。
模型与搜索模式
白名单目前包含默认的 gpt-4o-mini 与 gpt-4o。客户端传入未知值时,
resolveAiChatModelId() 会回退默认模型,因此仅修改请求不能选择任意模型。
新增模型时更新 aiChatModels 的展示标签与可选 search-preview ID;暴露前检查
能力、附件支持、价格与流式行为。
“Web search”开关没有注册 AI SDK tool。它把所选模型切换到
gpt-4o-mini-search-preview 或 gpt-4o-search-preview,并通过
sendSources 返回该模型产生的 source part。不要把它描述成自定义搜索工具,也
不要假设这里已有 tool policy。
路由启用 sendReasoning: true,并按搜索模式启用 sendSources。只有 provider
真正返回对应 part 时 UI 才会显示,具体取决于模型和响应。
自定义行为
- 修改
aiChatSystemPrompt定义产品范围与拒答规则。 - 修改
PromptInput的附件类型时,同步增加服务端大小、MIME 与数量限制。 - 若需要历史记录,在 D1 持久化会话;当前聊天只存在组件 state 中。
- 通过
streamText明确增加检索/tool,并为每个 tool 定义授权、超时与输出策略。 - 产品双语时,本地化当前英文聊天标签。
生产加固
POST /api/chat 当前没有 session 校验、限流、配额、消息大小限制、审核层或用户
成本追踪。上线前应:
- 要求登录,或明确设计受限的匿名权限;
- 强制请求/body/附件限制与服务端模型白名单;
- 增加用户/IP 限流、配额、预算上限与滥用监控;
- 不返回原始 provider 错误,也不记录含敏感数据的 prompt/文件;
- 保存会话前定义保留期限与用户同意;
- 测试断连、取消与超时行为。
Cloudflare Workers 支持流式响应,但 CPU 时间、subrequest 限制、客户端断连与 provider 延迟仍需考虑。同步预处理应保持轻量、尽早开始 stream,并监控长请求。
验证
-
OPENAI_API_KEY缺失/无效时有可见服务端错误,且不泄露 key。 - 默认与备选模型可流式返回、停止和重新生成。
- 未知模型 ID 回退默认值。
- 只有 search-preview 模型提供 source 时,搜索模式才显示来源。
- 附件只能在服务端产品策略范围内使用。
- 加固后会拒绝未登录、超配额与被限流请求。
