下面这份梳理按 "环境准备 → 项目搭建 → AI 接入层 → 前端集成 → 安全与调试" 的顺序展开,以 Vue 3 + TypeScript + Vite + LangChain.js 为主线,兼顾两种实际场景:直连 LangGraph 服务器 (最简路径)和自建 BFF 代理(生产推荐)。
一、先理解整体架构:LangChain.js 在前端 AI 应用中的位置
LangChain.js 是 LangChain 的 JavaScript/TypeScript 实现,定位是前端与全栈应用 的 AI 编排层。它不要求你必须有一个独立的后端,但API Key 不能暴露在浏览器中,所以实际落地时通常有两种形态:
| 形态 | 前端职责 | 后端 / 接入层 | 适用场景 |
|---|---|---|---|
| A. 直连 LangGraph 服务器 | 用 @langchain/vue 的 useStream 连接一个已运行的 Agent 服务 |
LangGraph 服务器(localhost:2024 或部署地址) | 快速原型、内部工具、学习 LangChain 前端 SDK |
| B. 自建 BFF 代理(推荐) | fetch + ReadableStream 消费流式响应 |
Node.js / Express / 任意后端,持有 API Key,调用 LangChain.js 或任意模型 SDK | 生产环境、需要鉴权、限流、审计、成本控制 |
BFF(Backend for Frontend)的核心原则是:密钥、限流、审计留在服务端,前端只消费稳定的流式事件契约。下面两条路径都会给出可运行的步骤,你按自己的阶段选择即可。
二、环境准备
2.1 Node.js 版本
LangChain.js 当前要求 Node.js 22+(官方 Quickstart 明确注明)。建议直接使用 Node.js 22 LTS 或 24 Active LTS。
bash
node --version # 确认 >= 22
npm --version
2.2 推荐工具链
- 编辑器:VS Code + Vue 官方扩展(禁用 Vetur)
- 包管理器:npm / pnpm / yarn 均可,下文以 npm 为例
- TypeScript 运行工具 :如果需要在 Node 端直接跑
.ts文件,安装tsx,省去先编译再运行的步骤:
bash
npm install -g tsx
三、搭建 Vue 3 + TypeScript 项目(前端骨架)
3.1 用 Vite 创建项目
Vue 3 新项目目前的标准起点是 Vite,vue-ts 模板是最精简的 TypeScript 起步方式:
bash
npm create vite@latest my-ai-app -- --template vue-ts
cd my-ai-app
npm install
npm run dev
创建完成后,建议立刻跑一次生产构建,确认类型检查和构建链路没有问题:
bash
npm run build
npm run preview
3.2 保持目录简洁
新手阶段不要一上来就引入 Router、Pinia、UI 库。先把 src/ 维持在可理解的范围:
css
src/
├── App.vue
├── main.ts
├── components/
│ └── ChatBox.vue
└── composables/
└── useChat.ts # 后续放流式逻辑
3.3 组件写法:统一用 <script setup lang="ts">
vue
<script setup lang="ts">
const message = "hello vue"
</script>
<template>
<h1>{{ message }}</h1>
</template>
script setup 样板代码最少,类型推断也最顺,新组件默认用这个写法即可。
四、路径 A:用 @langchain/vue 直连 LangGraph 服务器(最快出效果)
如果你的目标是最短路径看到"AI 回复在页面上流式出现",这是首选。
4.1 安装依赖
@langchain/vue 提供了 Vue 专用的 useStream 组合式函数,负责流式传输、消息状态和中断管理:
bash
npm install @langchain/vue @langchain/core
Peer 依赖要求 Vue ^3.5 和 @langchain/core。
4.2 准备一个运行中的 Agent 服务
useStream 需要一个 LangGraph API 地址 (默认 http://localhost:2024)和一个 assistantId(对应 LangGraph 配置中的 graph 名称)。这一步的具体部署方式取决于你使用的 LangGraph 模板,但形式上只要有一个暴露 /threads、/runs/stream 等端点的服务即可。
4.3 在 Vue 组件中使用 useStream
vue
<script setup lang="ts">
import { useStream } from "@langchain/vue"
const { messages, submit, isLoading } = useStream({
assistantId: "agent",
apiUrl: "http://localhost:2024",
})
</script>
<template>
<div v-for="(msg, i) in messages" :key="msg.id ?? i">
{{ msg.content }}
</div>
<button
:disabled="isLoading"
@click="submit({ messages: [{ type: 'human', content: 'Hello!' }] })"
>
Send
</button>
</template>
useStream 返回的 messages 和 isLoading 是响应式引用,流式增量会自动更新到 UI。注意:messages 是 ComputedRef,在模板中自动解包;如果在 <script> 中访问,需要使用 .value。
4.4 这条路径的边界
@langchain/vue 定位是 LangChain / LangGraph 的前端 SDK,它适合与 LangGraph 服务器配套使用。如果你暂时不想引入 LangGraph 服务器,或者后端是自建的 Express 接口,就走路径 B。
五、路径 B:自建 BFF 代理 + LangChain.js(生产推荐)
5.1 为什么必须走后端代理
前端直连模型 API 会将 API Key 写入浏览器代码,构建产物中一旦泄露,任何人都可以直接消耗你的额度。安全实践非常明确:Never expose API keys in browser code,始终使用后端代理或边缘函数。
BFF 同时承担限流、超时、审计和模型切换的职责,前端只依赖稳定的流式事件契约,不感知上游供应商。
5.2 后端最小结构(Express + LangChain.js 或模型 SDK)
在项目根目录建一个 server/ 目录(与 Vue 前端分离部署):
bash
mkdir server && cd server
npm init -y
npm install express cors dotenv
npm install langchain @langchain/core @langchain/openai
server/index.ts(最小流式接口,NDJSON 输出):
ts
import express from "express"
import cors from "cors"
import "dotenv/config"
import { ChatOpenAI } from "@langchain/openai"
const app = express()
app.use(cors())
app.use(express.json())
const model = new ChatOpenAI({
model: "gpt-4o-mini",
apiKey: process.env.OPENAI_API_KEY,
streaming: true,
})
app.post("/api/chat", async (req, res) => {
res.setHeader("Content-Type", "application/x-ndjson; charset=utf-8")
res.setHeader("Cache-Control", "no-cache")
try {
const stream = await model.stream(req.body.input)
for await (const chunk of stream) {
res.write(JSON.stringify({ type: "delta", text: chunk.content }) + "\n")
}
res.write(JSON.stringify({ type: "done" }) + "\n")
} catch (e) {
res.write(JSON.stringify({ type: "error", message: "UPSTREAM_ERROR" }) + "\n")
} finally {
res.end()
}
})
app.listen(3001, () => console.log("BFF on http://localhost:3001"))
这份实现采用了与 BFF 文章一致的 NDJSON 逐行事件契约 (delta / done / error),前端按行解析即可,不需要额外引入 SSE 库。如果生产环境使用 SSE,只需把 Content-Type 改为 text/event-stream,并在代理层关闭缓冲。
.env 文件(只放在 server 目录,不要提交到 Git):
ini
OPENAI_API_KEY=sk-...
5.3 前端消费流式响应(Vue 3 + TS)
EventSource 只支持 GET,无法携带 POST body,因此对话场景推荐用 fetch + response.body.getReader() 逐块读取流。
在 src/composables/useChat.ts 中:
ts
import { ref } from "vue"
interface ChatEvent {
type: "delta" | "done" | "error"
text?: string
message?: string
}
export function useChat(apiUrl = "http://localhost:3001/api/chat") {
const output = ref("")
const loading = ref(false)
async function send(input: string) {
output.value = ""
loading.value = true
const res = await fetch(apiUrl, {
method: "POST",
headers: { "Content-Type": "application/json" },
body: JSON.stringify({ input }),
})
if (!res.body) {
loading.value = false
throw new Error("No response body")
}
const reader = res.body.getReader()
const decoder = new TextDecoder()
let buffer = ""
while (true) {
const { value, done } = await reader.read()
if (done) break
buffer += decoder.decode(value, { stream: true })
const lines = buffer.split("\n")
buffer = lines.pop() ?? ""
for (const line of lines) {
if (!line.trim()) continue
const event: ChatEvent = JSON.parse(line)
if (event.type === "delta") {
output.value += event.text ?? ""
} else if (event.type === "error") {
output.value = `[错误] ${event.message ?? "未知错误"}`
}
}
}
loading.value = false
}
return { output, loading, send }
}
在 ChatBox.vue 中使用:
vue
<script setup lang="ts">
import { ref } from "vue"
import { useChat } from "@/composables/useChat"
const input = ref("")
const { output, loading, send } = useChat()
function handleSend() {
if (!input.value.trim()) return
send(input.value)
input.value = ""
}
</script>
<template>
<div class="chat">
<div class="output">{{ output }}</div>
<div class="input-row">
<input v-model="input" :disabled="loading" @keyup.enter="handleSend" />
<button :disabled="loading" @click="handleSend">发送</button>
</div>
</div>
</template>
这里的关键点是 TextDecoder 的 { stream: true } 参数:二进制流可能在任意字节边界切开,必须让解码器在流模式下保留跨块状态,否则中文或 emoji 会出现乱码。
六、API Key 与安全清单(新手最容易踩的坑)
无论走路径 A 还是 B,以下几条建议直接作为上线前的检查项:
- API Key 永远不出现在 Vue 代码、
import.meta.env前端变量或构建产物中 。VITE_前缀的环境变量会被打包进浏览器代码,不能用于密钥。 - 后端
.env加入.gitignore;构建产物中可用脚本扫描是否泄露了 key 形态的字符串。 - 前端请求走同源
/api路径 (Vite dev 用server.proxy,生产用 Nginx 反向代理),避免 CORS 和硬编码域名。 - BFF 必须设置单请求超时 和输入长度上限,避免上游拖垮 Node 进程。
- 系统提示词不要从客户端接收,只能在后端拼接。
七、调试与验证步骤
路径 A(@langchain/vue) 的验证:
- 先确认 LangGraph 服务器在
http://localhost:2024可访问; - 在浏览器 Network 面板观察
useStream是否建立了流式连接; - 如果
messages不更新,检查assistantId是否与 LangGraph 配置中的 graph 名一致。
路径 B(BFF) 的验证:
-
先用
curl直接测后端流式接口是否逐行输出:bashcurl -N -X POST http://localhost:3001/api/chat \ -H "Content-Type: application/json" \ -d '{"input":"你好"}' -
再在前端 Network 面板确认 Response 是
application/x-ndjson且逐块到达; -
如果 UI 一次性出现完整回复而不是逐字出现,检查是否在某处把流"攒完再渲染"了。
八、新手学习路径建议
如果你希望在完成环境搭建后继续深入 LangChain.js,可以按以下顺序推进:
- Chat Models & 流式交互 :理解消息类型、
stream()的迭代方式; - Prompts + Structured Output:用 Zod 定义输出 schema,获得类型安全的结构化返回;
- Function Calling / Tools:让模型调用你定义的函数;
- Agents :用
createAgent组装模型 + 工具; - RAG:文档加载、切分、向量检索,再与 Agent 结合。
微软维护的 LangChain.js for Beginners 课程(9 章,含 setup)和 Dan Wahlin 的同名课程都可以作为系统跟练材料,前者侧重 Microsoft Foundry,后者覆盖 MCP、Agentic RAG 等较新主题。
两条路径的选择建议 :如果只是今天就想看到 AI 在 Vue 页面里流式打字 ,走路径 A;如果目标是能上线、能交给别人用,直接按路径 B 把 BFF 骨架搭起来,后续换模型、加鉴权、加限流都不需要改前端。