在 Next.js 15 和 Vercel 上使用 Google AI 配置 Langchain 和 Langraph
本指南展示如何在 Next.js 15 项目中配置 Langchain 和 Langraph,使用 Google 的 Gemini 模型构建 AI 智能体,并部署到 Vercel,支持 TypeScript。
心智模型:构建块架构
将此配置想象成组装现代 Web API 的专用中间件。Next.js 15 提供服务器框架,Langchain 充当您的 AI 编排层(类似于 LLM 的 Express 中间件),Langraph 添加有状态工作流功能(类似于 AI 工作流的 Redux Saga)。Google 的 Gemini 模型作为智能层,而 Vercel 平台处理无服务器部署,自动扩展您的 AI 端点,类似于 Google Cloud Functions 但具有更好的开发体验。
基础设置:项目初始化
1. 创建 Next.js 15 项目
npx create-next-app@latest my-ai-agent --typescript --tailwind --app --no-src-dir
cd my-ai-agent
创建一个新的 Next.js 15 项目,支持 TypeScript、Tailwind CSS v4、App Router,文件位于根目录。
2. 安装核心依赖
npm install langchain @langchain/core @langchain/langgraph @langchain/google-genai
npm install @langchain/community ai @ai-sdk/google
npm install daisyui@latest
安装 Langchain 核心库、用于工作流的 Langraph、Google Gemini 集成、社区工具、Vercel AI SDK 和用于 UI 组件的 DaisyUI v5。
3. TypeScript 配置
使用 --typescript
标志创建项目时,Next.js 15 会自动配置 TypeScript。默认的 tsconfig.json
已经针对 Langchain 使用进行了优化,无需更改。
4. 环境变量
# .env.local
GOOGLE_API_KEY=your-google-ai-api-key
LANGCHAIN_TRACING_V2=true # 可选:启用 LangSmith 跟踪
LANGCHAIN_API_KEY=your-langsmith-key # 可选:用于 LangSmith 调试
LANGCHAIN_CALLBACKS_BACKGROUND=false # 必需:用于 Vercel 无服务器
重要:LANGCHAIN_CALLBACKS_BACKGROUND 说明
当设置为 true
(默认值)时,Langchain 在发送响应后在后台异步运行回调。这在传统服务器上工作正常,但在 Vercel 等无服务器环境中会导致问题,因为:
- 无服务器函数会立即终止 返回响应后
- 后台回调被终止 在完成之前,导致请求挂起或超时
- Vercel 可能报告错误 如"函数执行时间过长",即使是简单请求
将其设置为 false
确保所有回调在发送响应之前同步完成,防止无服务器函数终止问题。这对于 Vercel、AWS Lambda 和类似平台是必需的。
5. 更新 globals.css 用于 Tailwind v4 和 DaisyUI v5
/* app/globals.css */
@import "tailwindcss";
@plugin "daisyui";
导入 Tailwind CSS v4 并使用新的基于 CSS 的配置将 DaisyUI v5 作为插件加载。
6. 使用 Google Gemini 的基本 Langchain API 路由
// app/api/chat/route.ts
import { ChatGoogleGenerativeAI } from '@langchain/google-genai';
import { HumanMessage, AIMessage } from '@langchain/core/messages';
import { NextResponse } from 'next/server';
export const runtime = 'nodejs';
export const maxDuration = 300; // Pro 计划的 5 分钟
export async function POST(req: Request) {
try {
const { message } = await req.json();
const model = new ChatGoogleGenerativeAI({
modelName: 'gemini-2.5-pro',
temperature: 0.7,
streaming: false,
maxOutputTokens: 2048,
});
const response = await model.invoke([
new HumanMessage(message)
]);
return NextResponse.json({
content: response.content
});
} catch (error) {
console.error('Chat API error:', error);
return NextResponse.json(
{ error: 'Failed to process chat' },
{ status: 500 }
);
}
}
创建一个 POST 端点,接受消息,通过 Gemini Pro 处理,并返回 AI 响应,使用 Node.js 运行时以获得完整的 Langchain 支持。
高级示例:带流式的 Langraph 智能体
1. 安装额外依赖
npm install @langchain/google-genai @langchain/langgraph zod
npm install @vercel/kv uuid @google/generative-ai
npm install @tanstack/react-query
添加 Google AI 嵌入、Langraph 智能体、使用 Zod 的模式验证、用于缓存的 Vercel KV 和用于数据获取的 TanStack Query 的依赖项。
2. 创建带工具的智能体
// lib/agent.ts
import { ChatGoogleGenerativeAI } from '@langchain/google-genai';
import { Calculator } from '@langchain/community/tools/calculator';
import { WebBrowser } from '@langchain/community/tools/webbrowser';
import { createReactAgent } from '@langchain/langgraph/prebuilt';
import { HumanMessage } from '@langchain/core/messages';
import { GoogleGenerativeAIEmbeddings } from '@langchain/google-genai';
export function createAgent() {
const model = new ChatGoogleGenerativeAI({
modelName: 'gemini-2.5-flash',
temperature: 0,
streaming: true,
maxOutputTokens: 8192,
});
const embeddings = new GoogleGenerativeAIEmbeddings({
modelName: "embedding-001",
});
const tools = [
new Calculator(),
new WebBrowser({ model, embeddings }),
];
return createReactAgent({
llm: model,
tools,
});
}
使用 Gemini Flash 设置 ReAct 智能体以获得快速响应,配备计算器和网络浏览器工具以增强功能。
3. 使用 Langraph 的流式 API 路由
// app/api/agent/route.ts
import { createAgent } from '@/lib/agent';
import { HumanMessage } from '@langchain/core/messages';
export const runtime = 'nodejs';
export const maxDuration = 300;
export async function POST(req: Request) {
const { message } = await req.json();
const encoder = new TextEncoder();
const stream = new TransformStream();
const writer = stream.writable.getWriter();
const agent = createAgent();
// 在后台开始流式传输
(async () => {
try {
const eventStream = await agent.stream({
messages: [new HumanMessage(message)],
});
for await (const event of eventStream) {
if (event.agent?.messages?.[0]?.content) {
await writer.write(
encoder.encode(`data: ${JSON.stringify({
content: event.agent.messages[0].content
})}\n\n`)
);
}
}
} catch (error) {
console.error('Streaming error:', error);
} finally {
await writer.close();
}
})();
return new Response(stream.readable, {
headers: {
'Content-Type': 'text/event-stream',
'Cache-Control': 'no-cache',
'Connection': 'keep-alive',
},
});
}
实现服务器发送事件(SSE)流式传输,实时发送智能体响应,提高感知性能。
4. 带流式的客户端组件
// app/providers.tsx
'use client';
import { QueryClient, QueryClientProvider } from '@tanstack/react-query';
import { useState } from 'react';
export default function Providers({ children }: { children: React.ReactNode }) {
const [queryClient] = useState(() => new QueryClient({
defaultOptions: {
queries: {
staleTime: 60 * 1000, // 1 分钟
retry: 1,
},
},
}));
return (
<QueryClientProvider client={queryClient}>
{children}
</QueryClientProvider>
);
}
使用针对 AI 应用程序优化的默认设置设置 TanStack Query 提供者。
// app/layout.tsx
import './globals.css';
import Providers from './providers';
export default function RootLayout({
children,
}: {
children: React.ReactNode;
}) {
return (
<html lang="en">
<body>
<Providers>{children}</Providers>
</body>
</html>
);
}
使用 TanStack Query 提供者包装应用程序以进行全局访问。
// components/ChatInterface.tsx
'use client';
import { useState } from 'react';
import { useMutation } from '@tanstack/react-query';
export default function ChatInterface() {
const [message, setMessage] = useState('');
const [response, setResponse] = useState('');
const streamChat = useMutation({
mutationFn: async (userMessage: string) => {
setResponse('');
const res = await fetch('/api/agent', {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({ message: userMessage }),
});
if (!res.ok) throw new Error('Failed to send message');
const reader = res.body?.getReader();
const decoder = new TextDecoder();
while (reader) {
const { done, value } = await reader.read();
if (done) break;
const chunk = decoder.decode(value);
const lines = chunk.split('\n');
for (const line of lines) {
if (line.startsWith('data: ')) {
try {
const data = JSON.parse(line.slice(6));
setResponse(prev => prev + data.content);
} catch {}
}
}
}
return response;
},
onError: (error) => {
console.error('Chat error:', error);
},
});
const handleSubmit = (e: React.FormEvent) => {
e.preventDefault();
if (message.trim()) {
streamChat.mutate(message);
}
};
return (
<div className="card w-full bg-base-100 shadow-xl">
<div className="card-body">
<h2 className="card-title">Google Gemini AI 智能体</h2>
<form onSubmit={handleSubmit}>
<div className="form-control">
<textarea
className="textarea textarea-bordered"
placeholder="问您的问题..."
value={message}
onChange={(e) => setMessage(e.target.value)}
rows={3}
disabled={streamChat.isPending}
/>
</div>
<div className="card-actions justify-end mt-4">
<button
type="submit"
className="btn btn-primary"
disabled={streamChat.isPending || !message.trim()}
>
{streamChat.isPending ? (
<>
<span className="loading loading-spinner"></span>
思考中...
</>
) : '发送'}
</button>
</div>
</form>
{streamChat.isError && (
<div className="alert alert-error mt-4">
<span>发送消息失败。请重试。</span>
</div>
)}
{response && (
<div className="alert mt-4">
<span>{response}</span>
</div>
)}
</div>
</div>
);
}
使用 TanStack Query 的 useMutation 钩子管理流式 API 调用,内置加载和错误状态的 React 组件。
5. 使用 Gemini 的提示链实现
// lib/chains/prompt-chain.ts
import { ChatGoogleGenerativeAI } from '@langchain/google-genai';
import { PromptTemplate } from '@langchain/core/prompts';
import { RunnableSequence } from '@langchain/core/runnables';
import { StringOutputParser } from '@langchain/core/output_parsers';
import { NextResponse } from 'next/server';
export function createPromptChain() {
const model = new ChatGoogleGenerativeAI({
modelName: 'gemini-2.5-flash',
temperature: 0
});
// 步骤 1:提取规格
const extractPrompt = PromptTemplate.fromTemplate(
"从以下内容提取技术规格:{input}"
);
// 步骤 2:转换为 JSON
const transformPrompt = PromptTemplate.fromTemplate(
"转换为具有 'cpu'、'memory'、'storage' 键的 JSON:{specs}"
);
// 创建链
const chain = RunnableSequence.from([
extractPrompt,
model,
new StringOutputParser(),
{ specs: (prev: string) => prev },
transformPrompt,
model,
new StringOutputParser(),
]);
return chain;
}
// 在 API 路由中使用
export async function POST(req: Request) {
const { text } = await req.json();
const chain = createPromptChain();
const result = await chain.invoke({ input: text });
return NextResponse.json({ result });
}
通过将复杂任务分解为顺序步骤来实现提示链模式 - 首先提取数据,然后将其转换为 JSON 格式。
6. 使用 Google Gemini 的上下文工程
// lib/context-engineer.ts
import { ChatGoogleGenerativeAI } from '@langchain/google-genai';
import { SystemMessage, HumanMessage } from '@langchain/core/messages';
import { Document } from '@langchain/core/documents';
interface ContextConfig {
systemPrompt: string;
retrievedDocs?: Document[];
userHistory?: string[];
environmentContext?: Record<string, any>;
}
export class ContextEngineer {
private model: ChatGoogleGenerativeAI;
constructor() {
this.model = new ChatGoogleGenerativeAI({
modelName: 'gemini-2.5-pro',
temperature: 0.3,
maxOutputTokens: 8192,
});
}
async processWithContext(
userQuery: string,
config: ContextConfig
) {
const messages = [];
// 添加系统上下文
messages.push(new SystemMessage(config.systemPrompt));
// 添加检索到的文档作为上下文
if (config.retrievedDocs?.length) {
const docContext = config.retrievedDocs
.map(doc => doc.pageContent)
.join('\n\n');
messages.push(new SystemMessage(
`相关上下文:\n${docContext}`
));
}
// 添加用户历史
if (config.userHistory?.length) {
messages.push(new SystemMessage(
`之前的对话:\n${config.userHistory.join('\n')}`
));
}
// 添加环境上下文
if (config.environmentContext) {
messages.push(new SystemMessage(
`环境:${JSON.stringify(config.environmentContext)}`
));
}
// 添加实际的用户查询
messages.push(new HumanMessage(userQuery));
return await this.model.invoke(messages);
}
}
通过在处理查询之前结合系统提示、检索到的文档、对话历史和环境数据来构建综合上下文层。
7. Vercel 部署配置
// vercel.json
{
"functions": {
"app/api/agent/route.ts": {
"maxDuration": 300
},
"app/api/chat/route.ts": {
"maxDuration": 60
}
}
}
配置 Vercel 以允许 AI 操作的更长执行时间。环境变量由 Next.js 和 Vercel 自动处理。
8. 主页集成
// app/page.tsx
import ChatInterface from '@/components/ChatInterface';
export default function Home() {
return (
<main className="min-h-screen bg-base-200">
<div className="container mx-auto p-4">
<div className="text-center mb-8">
<h1 className="text-5xl font-bold">AI 智能体平台</h1>
<p className="py-6">由 Google Gemini 和 Langraph 提供支持</p>
</div>
<div className="flex justify-center">
<div className="w-full max-w-2xl">
<ChatInterface />
</div>
</div>
</div>
</main>
);
}
主登录页面,使用响应式布局和 DaisyUI 样式将聊天界面组件居中。
9. 高级 Langraph 工作流
// lib/workflows/stateful-workflow.ts
import { StateGraph, END } from '@langchain/langgraph';
import { ChatGoogleGenerativeAI } from '@langchain/google-genai';
import { BaseMessage } from '@langchain/core/messages';
interface WorkflowState {
messages: BaseMessage[];
currentStep: string;
metadata: Record<string, any>;
}
export function createStatefulWorkflow() {
const model = new ChatGoogleGenerativeAI({
modelName: 'gemini-2.5-flash',
});
const workflow = new StateGraph<WorkflowState>({
channels: {
messages: {
value: (x: BaseMessage[], y: BaseMessage[]) => [...x, ...y],
default: () => [],
},
currentStep: {
value: (x: string, y: string) => y || x,
default: () => 'start',
},
metadata: {
value: (x: Record<string, any>, y: Record<string, any>) => ({...x, ...y}),
default: () => ({}),
},
},
});
// 定义节点
workflow.addNode('analyze', async (state) => {
const response = await model.invoke(state.messages);
return {
messages: [response],
currentStep: 'process',
};
});
workflow.addNode('process', async (state) => {
// 处理分析
return {
currentStep: 'complete',
metadata: { processed: true },
};
});
// 定义边缘
workflow.addEdge('analyze', 'process');
workflow.addEdge('process', END);
workflow.setEntryPoint('analyze');
return workflow.compile();
}
使用 Langraph 的 StateGraph 创建有状态工作流,管理消息历史、跟踪执行步骤,并通过定义的节点和边缘处理数据。
10. 将有状态工作流与前端集成
// app/api/workflow/route.ts
import { createStatefulWorkflow } from '@/lib/workflows/stateful-workflow';
import { HumanMessage } from '@langchain/core/messages';
export const runtime = 'nodejs';
export const maxDuration = 300;
export async function POST(req: Request) {
const { message, sessionId } = await req.json();
const encoder = new TextEncoder();
const stream = new TransformStream();
const writer = stream.writable.getWriter();
const workflow = createStatefulWorkflow();
(async () => {
try {
// 流式工作流事件
const events = await workflow.stream({
messages: [new HumanMessage(message)],
currentStep: 'start',
metadata: { sessionId },
});
for await (const event of events) {
// 向前端发送步骤更新
await writer.write(
encoder.encode(`data: ${JSON.stringify({
type: 'step',
step: event.currentStep || 'processing',
content: event.messages?.[event.messages.length - 1]?.content,
metadata: event.metadata
})}\n\n`)
);
}
await writer.write(
encoder.encode(`data: ${JSON.stringify({ type: 'complete' })}\n\n`)
);
} catch (error) {
await writer.write(
encoder.encode(`data: ${JSON.stringify({ type: 'error', error: String(error) })}\n\n`)
);
} finally {
await writer.close();
}
})();
return new Response(stream.readable, {
headers: {
'Content-Type': 'text/event-stream',
'Cache-Control': 'no-cache',
'Connection': 'keep-alive',
},
});
}
流式工作流执行事件的 API 路由,包括步骤转换和向前端的中间结果。
// hooks/useWorkflowStream.ts
import { useState } from 'react';
import { useMutation } from '@tanstack/react-query';
interface WorkflowStep {
name: string;
status: 'pending' | 'active' | 'complete';
output?: string;
}
interface WorkflowData {
message: string;
sessionId: string;
}
export function useWorkflowStream() {
const [steps, setSteps] = useState<WorkflowStep[]>([
{ name: 'analyze', status: 'pending' },
{ name: 'process', status: 'pending' },
{ name: 'complete', status: 'pending' }
]);
const mutation = useMutation({
mutationFn: async (data: WorkflowData) => {
// 重置步骤
setSteps(steps.map(s => ({ ...s, status: 'pending', output: undefined })));
const res = await fetch('/api/workflow', {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify(data),
});
if (!res.ok) throw new Error('Workflow failed');
const reader = res.body?.getReader();
const decoder = new TextDecoder();
while (reader) {
const { done, value } = await reader.read();
if (done) break;
const chunk = decoder.decode(value);
const lines = chunk.split('\n');
for (const line of lines) {
if (line.startsWith('data: ')) {
try {
const data = JSON.parse(line.slice(6));
if (data.type === 'step') {
setSteps(prev => prev.map(step => {
if (step.name === data.step) {
return { ...step, status: 'active', output: data.content };
} else if (prev.findIndex(s => s.name === data.step) >
prev.findIndex(s => s.name === step.name)) {
return { ...step, status: 'complete' };
}
return step;
}));
} else if (data.type === 'complete') {
setSteps(prev => prev.map(s => ({ ...s, status: 'complete' })));
} else if (data.type === 'error') {
throw new Error(data.error);
}
} catch (error) {
if (error instanceof SyntaxError) continue;
throw error;
}
}
}
}
return { success: true };
},
onError: (error) => {
console.error('Workflow error:', error);
setSteps(prev => prev.map(s => ({ ...s, status: 'pending' })));
},
});
return {
steps,
runWorkflow: mutation.mutate,
isLoading: mutation.isPending,
isError: mutation.isError,
error: mutation.error,
reset: mutation.reset,
};
}
使用 TanStack Query 管理工作流流式传输的自定义钩子,具有自动状态更新和错误处理。
// components/WorkflowInterface.tsx
'use client';
import { useState } from 'react';
import { useWorkflowStream } from '@/hooks/useWorkflowStream';
export default function WorkflowInterface() {
const [message, setMessage] = useState('');
const { steps, runWorkflow, isLoading, isError, error, reset } = useWorkflowStream();
const handleSubmit = (e: React.FormEvent) => {
e.preventDefault();
if (message.trim()) {
runWorkflow({
message,
sessionId: crypto.randomUUID()
});
}
};
return (
<div className="card w-full bg-base-100 shadow-xl">
<div className="card-body">
<h2 className="card-title">工作流执行</h2>
<form onSubmit={handleSubmit}>
<div className="form-control">
<input
type="text"
className="input input-bordered"
placeholder="输入您的请求..."
value={message}
onChange={(e) => setMessage(e.target.value)}
disabled={isLoading}
/>
</div>
<div className="flex gap-2 mt-4">
<button
type="submit"
className="btn btn-primary"
disabled={isLoading || !message.trim()}
>
{isLoading ? (
<>
<span className="loading loading-spinner"></span>
运行工作流...
</>
) : '执行工作流'}
</button>
{isError && (
<button type="button" className="btn btn-outline" onClick={() => reset()}>
重置
</button>
)}
</div>
</form>
{isError && (
<div className="alert alert-error mt-4">
<span>错误:{error?.message || '工作流失败'}</span>
</div>
)}
{/* 工作流步骤可视化 */}
<div className="mt-6">
<ul className="steps steps-vertical">
{steps.map((step, idx) => (
<li
key={idx}
className={`step ${
step.status === 'complete' ? 'step-success' :
step.status === 'active' ? 'step-primary' : ''
}`}
>
<div className="text-left ml-4">
<div className="font-semibold capitalize">{step.name}</div>
{step.output && (
<div className="text-sm opacity-70 mt-1">
{step.output.substring(0, 100)}
{step.output.length > 100 && '...'}
</div>
)}
</div>
</li>
))}
</ul>
</div>
</div>
</div>
);
}
使用自定义工作流钩子和 TanStack Query 进行清洁状态管理和错误处理的前端组件。
// app/workflow/page.tsx
import WorkflowInterface from '@/components/WorkflowInterface';
export default function WorkflowPage() {
return (
<main className="min-h-screen bg-base-200">
<div className="container mx-auto p-4">
<div className="text-center mb-8">
<h1 className="text-4xl font-bold">AI 工作流执行器</h1>
<p className="py-4">观看您的 AI 智能体逐步完成复杂任务</p>
</div>
<div className="flex justify-center">
<div className="w-full max-w-3xl">
<WorkflowInterface />
</div>
</div>
</div>
</main>
);
}
工作流界面的专用页面,为监控多步骤 AI 智能体操作提供清洁的布局。
结论
此配置为在 Next.js 15 和 Vercel 上使用 Google 的 Gemini 模型构建 AI 智能体提供了生产就绪的基础,支持 Langchain 和 Langraph。该配置利用 Gemini 的卓越性能和成本效益,同时保持完整的流式支持和高级智能体功能。关键方面包括适当的 Google AI 集成、Tailwind v4 与 DaisyUI v5 的简化配置,以及遵循智能体设计模式文档中模式的全面提示链实现。