大多数 React Native 教程止步于 UI 层——展示如何渲染聊天气泡,然后用一句含糊的"直接从应用调用 OpenAI API"带过后端部分。
这种方式存在两个根本性问题。其一:嵌入移动端二进制文件的 API 密钥可被任何人通过反编译提取。其二:完全没有服务端控制——无速率限制、无用户上下文注入、无日志审计,也无法在不发版的情况下更换模型。
本文采用面向生产环境的方案:构建一个处理 LLM 连接的 FastAPI 后端(使用流式 Server-Sent Events),并将其接入 Expo(React Native)前端,实现逐 token 实时渲染。
这一架构同样适用于满足《数据安全法》《个人信息保护法(PIPL)》及等保2.0(尤其是涉及工控系统的 OT 环境)的私有化部署需求——推理服务可完全部署在客户自有基础设施上,数据不出境。
系统架构
flowchart TD
A["Mobile App Expo SDK 54"] --> B["FastAPI Backend"]
B --> C["LLM Provider"]
C --> D["SSE Stream"]
D --> E["Chunked Fetch RN 0.81"]
E --> F["Chat UI 逐 token 渲染"]
为什么选择 FastAPI
FastAPI 提供中间件依赖注入用于身份认证、符合等保2.0 合规要求的操作日志记录,以及与 Python 生态私有 LLM(Ollama、vLLM、LiteLLM、Qwen 系列)的无缝集成。当客户需要从公有云 LLM 切换到私有部署时,只需修改 FastAPI 侧的 base_url,移动端代码无需任何变更。
为什么用 SSE 而非 WebSocket
SSE 基于 HTTP/1.1,对反向代理友好,更易于在国内云环境(阿里云、腾讯云、华为云)上配置负载均衡和审计日志。
模型选型(2026 年 6 月行情)
| 模型 | 输入 / 百万 token | 输出 / 百万 token | 适用场景 |
|---|---|---|---|
| Claude Haiku 4.5 | $1.00 | $5.00 | 高并发聊天机器人、FAQ 机器人 |
| Claude Sonnet 4.6 | $3.00 | $15.00 | 复杂推理、销售助手 |
| DeepSeek V4 Flash | $0.14 | $0.28 | 成本敏感的国内移动端部署 |
| Qwen3.5-Plus | $0.40 | $2.40 | 中文优化场景、阿里云生态 |
对于大多数移动端聊天机器人——客服机器人、新手引导助手、FAQ 自动回复——Claude Haiku 4.5 是性价比最优选择:200K token 上下文窗口,单次对话成本约为 Sonnet 4.6 的 1/60。如需对接企业知识库(用友、金蝶等 ERP 导出的文档)进行复杂问答,可升级至 Sonnet 4.6。
若部署环境对数据出境有严格限制,DeepSeek V4 Flash 或国内版 Qwen3.5-Plus 可在阿里云/腾讯云上通过 OpenAI 兼容接口直接替换,后端代码无需修改。
第一部分:FastAPI 后端
环境搭建
mkdir chatbot-api && cd chatbot-api
python -m venv .venv && source .venv/bin/activate
pip install fastapi uvicorn anthropic python-dotenv
创建 .env 文件:
ANTHROPIC_API_KEY=your_key_here
MODEL_ID=claude-haiku-4-5
SYSTEM_PROMPT="你是 Acme 公司的智能客服助手。"
流式聊天接口
# main.py
import os
from fastapi import FastAPI, HTTPException, Header
from fastapi.responses import StreamingResponse
from pydantic import BaseModel
from typing import List
import anthropic
from dotenv import load_dotenv
load_dotenv()
app = FastAPI()
client = anthropic.Anthropic(api_key=os.getenv("ANTHROPIC_API_KEY"))
MODEL = os.getenv("MODEL_ID", "claude-haiku-4-5")
SYSTEM = os.getenv("SYSTEM_PROMPT", "你是一个有帮助的助手。")
class Message(BaseModel):
role: str
content: str
class ChatRequest(BaseModel):
messages: List[Message]
def stream_response(messages: List[Message]):
with client.messages.stream(
model=MODEL,
max_tokens=1024,
system=SYSTEM,
messages=[m.model_dump() for m in messages],
) as stream:
for text in stream.text_stream:
yield f"data: {text}\n\n"
yield "data: [DONE]\n\n"
@app.post("/chat")
async def chat(request: ChatRequest, x_api_key: str = Header(...)):
if x_api_key != os.getenv("APP_API_KEY"):
raise HTTPException(status_code=401, detail="未授权")
if not request.messages:
raise HTTPException(status_code=400, detail="消息不能为空")
return StreamingResponse(
stream_response(request.messages),
media_type="text/event-stream",
headers={"Cache-Control": "no-cache", "X-Accel-Buffering": "no"},
)
@app.get("/health")
async def health():
return {"status": "ok", "model": MODEL}
本地启动:
uvicorn main:app --reload --port 8000
第二部分:React Native 聊天 UI(Expo SDK 54)
React Native 上的 SSE 流式处理
React Native 没有原生 EventSource,但从 RN 0.79+ 开始,通过在 fetch 选项中传入 reactNative: { textStreaming: true },可以使用 response.body.getReader() 逐块读取响应。
// hooks/useChat.ts
import { useState, useCallback } from "react";
export interface Message {
id: string;
role: "user" | "assistant";
content: string;
}
const API_URL = process.env.EXPO_PUBLIC_API_URL ?? "http://localhost:8000";
const API_KEY = process.env.EXPO_PUBLIC_APP_API_KEY ?? "";
export function useChat() {
const [messages, setMessages] = useState<Message[]>([]);
const [isStreaming, setIsStreaming] = useState(false);
const sendMessage = useCallback(async (text: string) => {
const userMessage: Message = { id: Date.now().toString(), role: "user", content: text };
const updated = [...messages, userMessage];
setMessages(updated);
setIsStreaming(true);
const assistantId = (Date.now() + 1).toString();
setMessages((prev) => [...prev, { id: assistantId, role: "assistant", content: "" }]);
try {
const response = await fetch(`${API_URL}/chat`, {
method: "POST",
headers: { "Content-Type": "application/json", "x-api-key": API_KEY },
body: JSON.stringify({ messages: updated.map(({ role, content }) => ({ role, content })) }),
reactNative: { textStreaming: true },
} as RequestInit);
const reader = response.body?.getReader();
const decoder = new TextDecoder();
while (reader) {
const { done, value } = await reader.read();
if (done) break;
const chunk = decoder.decode(value, { stream: true });
for (const line of chunk.split("\n")) {
if (line.startsWith("data: ")) {
const token = line.slice(6);
if (token === "[DONE]") break;
setMessages((prev) =>
prev.map((m) => m.id === assistantId ? { ...m, content: m.content + token } : m)
);
}
}
}
} catch (err) {
console.error("流式错误:", err);
} finally {
setIsStreaming(false);
}
}, [messages]);
return { messages, sendMessage, isStreaming };
}
第三部分:移动端特有注意事项
网络中断处理 — 移动网络不稳定。用 try/catch 包裹 reader 循环,在流中断时显示"点击重试"按钮,保留已接收内容。
FlatList 重渲染优化 — 每个 token 都会触发 state 更新。用 useCallback 记忆化 renderItem,并开启 removeClippedSubviews。
API 密钥安全 — 即使使用 Header 方案,密钥仍存在于 bundle 中。对安全要求更高的应用,建议实现短期令牌机制:应用通过正常认证登录后端,后端为聊天接口签发 15 分钟有效令牌。等保2.0 三级及以上系统建议记录所有 LLM 调用日志以满足审计要求。
切换至私有 LLM(等保2.0 & 数据不出境)
若业务需要满足等保2.0 对工控系统(OT/工业控制系统)的特殊要求,或 PIPL 的数据本地化要求,可将 FastAPI 后端接入私有部署的推理服务:
# 切换至 Ollama / vLLM / Qwen 私有部署
from openai import OpenAI
client = OpenAI(
base_url="http://your-private-llm:11434/v1",
api_key="not-needed",
)
React Native 应用无需任何修改,流式协议完全兼容。
常见问题
一定需要 FastAPI 后端吗?能从 React Native 直接调用 LLM API 吗?
技术上可行,但生产环境不推荐。嵌入移动端 bundle 的 API 密钥可被反编译提取,且无法实现服务端的任何控制(限流、日志、模型切换)。
React Native 没有 EventSource,流式是怎么实现的?
RN 0.79+ 支持在 fetch 选项中传入 reactNative: { textStreaming: true },通过 response.body.getReader() 逐块读取,手动解析 SSE 的 data: 前缀。
大并发客服机器人应该选哪个模型?
从 Claude Haiku 4.5 开始——$1.00/M 输入 token,专为此类场景设计,200K 上下文窗口可容纳较长对话历史。若对话需要复杂推理或文档综合,再升级至 Sonnet 4.6。国内数据不出境场景可考虑 DeepSeek V4 Flash 或 Qwen3.5-Plus。
Android 和 iOS 上的流式代码一致吗?
是的。RN 0.81 + Expo SDK 54 在两个平台上的 chunked fetch 方案完全一致。
下一步
本文建立了基础:FastAPI 流式后端、生产级 Expo 聊天 UI,以及教程通常忽略的移动端特有处理。
R 系列接下来:
- 端侧 AI — 无需后端,直接在设备上运行量化模型
- 将聊天机器人接入业务数据 — 结合 RAG 流水线(pgvector + 私有文档),实现基于企业知识库的问答
有 React Native + AI 集成需求?联系 Simplico 团队,我们为东南亚和日本客户构建生产级移动端 AI 功能。
最新文章
- 你的员工有24个密码,你的企业就有24个攻击面 June 11, 2026
- 潜伏在工程团队中的安全隐患 June 8, 2026
- SOAR与告警疲劳:为何你的SOC正被告警淹没(以及自动化如何真正帮助) June 7, 2026
- MES与ERP:有何区别?工厂到底需要哪个? June 7, 2026
- React Native vs Flutter 2026年:如何做出正确选择 June 4, 2026
- React Native 2026年版:现在还值得用来开发应用吗? June 3, 2026
