第 8 章:AI 应用开发¶
学习时间:5 小时 | 难度:⭐⭐⭐⭐ 高级 | 前置知识:第 6-7 章
本章概览¶
TypeScript 是 2025 年 AI 应用开发的主流语言。Vercel AI SDK、LangChain.js 和 Mastra 都以 TypeScript 为第一公民,提供完整的类型安全。
学习目标:
- 掌握 Vercel AI SDK 4.x 的流式输出与工具调用
- 使用 LangChain.js 构建 RAG(检索增强生成)流程
- 理解 Mastra AI 工作流框架
- 构建支持多模态的 AI 聊天应用
- 掌握 AI SDK 的 React 钩子(
useChat、useCompletion)
8.1 Vercel AI SDK 4.x 快速入门¶
Bash
pnpm add ai @ai-sdk/openai @ai-sdk/anthropic @ai-sdk/google
# 或者使用 Azure OpenAI
pnpm add @ai-sdk/azure
TypeScript
// 基础:单次文本生成
import { generateText } from "ai";
import { openai } from "@ai-sdk/openai";
const { text } = await generateText({
model: openai("gpt-4o"),
prompt: "用中文解释量子纠缠,限 100 字",
});
console.log(text);
// 结构化输出(JSON Schema 约束,自动类型推断)
import { generateObject } from "ai";
import { z } from "zod";
const { object } = await generateObject({
model: openai("gpt-4o"),
schema: z.object({
recipe: z.object({
name: z.string(),
ingredients: z.array(z.object({
name: z.string(),
amount: z.string(),
})),
steps: z.array(z.string()),
cookingTime: z.number().describe("烹饪时间(分钟)"),
}),
}),
prompt: "给我一个宫保鸡丁的食谱",
});
// object 类型被完整推断:
console.log(object.recipe.name); // string
console.log(object.recipe.cookingTime); // number
console.log(object.recipe.ingredients[0].name); // string
8.2 流式输出与 React 集成¶
TypeScript
// app/api/chat/route.ts — Next.js API 路由
import { openai } from "@ai-sdk/openai";
import { streamText, convertToCoreMessages } from "ai";
export const runtime = "edge"; // 使用 Edge Runtime,延迟更低
export async function POST(req: Request) {
const { messages } = await req.json();
const result = streamText({
model: openai("gpt-4o"),
system: "你是一个专业的 TypeScript 学习助手,回答简洁且包含代码示例。",
messages: convertToCoreMessages(messages),
maxTokens: 1024,
temperature: 0.7,
// 流结束时的回调
onFinish({ text, usage, finishReason }) {
console.log("Token 使用:", usage);
console.log("结束原因:", finishReason);
// 在这里写数据库(不影响流式响应)
},
});
return result.toDataStreamResponse();
}
TypeScript
// components/ChatUI.tsx — 客户端聊天组件
"use client";
import { useChat } from "ai/react";
import { useState } from "react";
export function ChatUI() {
const {
messages, // Message[] — 对话历史
input, // string — 当前输入
handleInputChange,
handleSubmit,
isLoading, // boolean — 是否正在生成
stop, // () => void — 停止生成
reload, // () => void — 重新生成最后一条
error, // Error | undefined
} = useChat({
api: "/api/chat",
onError: (err) => console.error("Chat error:", err),
});
return (
<div className="flex flex-col h-screen max-w-2xl mx-auto p-4">
{/* 消息历史 */}
<div className="flex-1 overflow-y-auto space-y-4">
{messages.map((msg) => (
<div key={msg.id}
className={`p-3 rounded-lg ${
msg.role === "user"
? "bg-blue-100 ml-auto max-w-xs"
: "bg-gray-100 mr-auto max-w-2xl"
}`}
>
<div className="prose prose-sm">{msg.content}</div>
</div>
))}
{isLoading && (
<div className="text-gray-400 animate-pulse">AI 正在思考...</div>
)}
</div>
{/* 输入框 */}
<form onSubmit={handleSubmit} className="flex gap-2 mt-4">
<input
value={input}
onChange={handleInputChange}
placeholder="问我任何 TypeScript 问题..."
className="flex-1 border rounded-lg px-3 py-2"
disabled={isLoading}
/>
{isLoading
? <button type="button" onClick={stop} className="px-4 py-2 bg-red-500 text-white rounded-lg">停止</button>
: <button type="submit" className="px-4 py-2 bg-blue-600 text-white rounded-lg">发送</button>
}
</form>
</div>
);
}
8.3 工具调用(Function Calling)¶
AI SDK 的工具调用类型完全安全——工具的参数类型从 Zod Schema 自动推断:
TypeScript
// app/api/agent/route.ts — 带工具调用的 AI Agent
import { openai } from "@ai-sdk/openai";
import { streamText, tool } from "ai";
import { z } from "zod";
export async function POST(req: Request) {
const { messages } = await req.json();
const result = streamText({
model: openai("gpt-4o"),
system: "你是一个有用的助手,可以查询天气和搜索代码文档。",
messages,
tools: {
// 工具 1:获取天气
getWeather: tool({
description: "获取指定城市的实时天气",
parameters: z.object({
city: z.string().describe("城市名,如:北京、上海"),
unit: z.enum(["celsius", "fahrenheit"]).default("celsius"),
}),
execute: async ({ city, unit }) => {
// city: string, unit: "celsius" | "fahrenheit" — 完整类型推断
const apiUrl = `https://weather.api.com/${encodeURIComponent(city)}`;
const data = await fetch(apiUrl).then((r) => r.json());
return {
city,
temperature: unit === "celsius" ? data.temp_c : data.temp_f,
condition: data.condition,
humidity: data.humidity,
};
},
}),
// 工具 2:搜索 MDN 文档
searchDocs: tool({
description: "搜索 MDN Web 文档或 TypeScript 文档",
parameters: z.object({
query: z.string().describe("搜索关键词"),
source: z.enum(["mdn", "typescript", "nodejs"]).default("mdn"),
}),
execute: async ({ query, source }) => {
// 实际项目中调用搜索 API
return {
results: [
{ title: `${source}: ${query}`, url: `https://developer.mozilla.org/search?q=${query}`, snippet: "..." },
],
};
},
}),
// 工具 3:执行代码(沙箱环境)
runCode: tool({
description: "在安全的沙箱中运行 TypeScript/JavaScript 代码",
parameters: z.object({
code: z.string().describe("要执行的代码"),
language: z.enum(["typescript", "javascript"]).default("typescript"),
}),
execute: async ({ code, language }) => {
// 调用代码执行服务(如 e2b.dev)
return {
output: `// ${language} 执行结果\n// ... 实际输出`,
error: null,
};
},
}),
},
maxSteps: 5, // 允许最多 5 轮工具调用链
});
return result.toDataStreamResponse();
}
8.4 RAG(检索增强生成)¶
TypeScript
// src/lib/rag.ts — 使用 AI SDK + Vercel Vector Store 构建 RAG
import { openai } from "@ai-sdk/openai";
import { embed, embedMany, cosineSimilarity } from "ai";
// 步骤 1:嵌入文档(离线处理)
async function embedDocuments(documents: { id: string; content: string }[]) {
const { embeddings } = await embedMany({
model: openai.embedding("text-embedding-3-small"),
values: documents.map((d) => d.content),
});
// 将嵌入向量与文档一起存储到向量数据库
return documents.map((doc, i) => ({
id: doc.id,
content: doc.content,
embedding: embeddings[i], // number[]
}));
}
// 步骤 2:语义搜索(在线检索)
async function semanticSearch(
query: string,
vectorStore: { id: string; content: string; embedding: number[] }[],
topK = 3
): Promise<string[]> {
const { embedding: queryEmbedding } = await embed({
model: openai.embedding("text-embedding-3-small"),
value: query,
});
// 计算余弦相似度并排序
const results = vectorStore
.map((doc) => ({
content: doc.content,
score: cosineSimilarity(queryEmbedding, doc.embedding),
}))
.sort((a, b) => b.score - a.score)
.slice(0, topK);
return results.map((r) => r.content);
}
// 步骤 3:RAG 查询
export async function ragQuery(query: string) {
// 从向量数据库检索相关上下文
const contexts = await semanticSearch(query, vectorStore);
const { text } = await generateText({
model: openai("gpt-4o"),
system: `你是一个专业助手。根据以下上下文回答问题,如果上下文中没有相关信息,诚实说明。
上下文:
${contexts.map((c, i) => `[${i + 1}] ${c}`).join("\n\n")}`,
prompt: query,
});
return { answer: text, sources: contexts };
}
8.5 Mastra AI 工作流¶
Mastra 是专为 TypeScript 设计的 AI Agent 框架,支持复杂的多步骤工作流:
TypeScript
// src/agents/research-agent.ts
import { Agent, createTool } from "@mastra/core";
import { Memory } from "@mastra/memory";
import { openai } from "@ai-sdk/openai";
import { z } from "zod";
// 工具定义
const webSearchTool = createTool({
id: "web-search",
description: "搜索互联网获取最新信息",
inputSchema: z.object({
query: z.string(),
maxResults: z.number().default(5),
}),
outputSchema: z.object({
results: z.array(z.object({
title: z.string(),
url: z.string(),
snippet: z.string(),
})),
}),
execute: async ({ context }) => {
// 调用搜索 API
const results = await searchWeb(context.query, context.maxResults);
return { results };
},
});
// Agent 定义
export const researchAgent = new Agent({
name: "Research Assistant",
instructions: `你是一个专业的研究助手,能够:
1. 搜索最新信息
2. 综合多个来源
3. 提供有引用的结构化答案
请优先使用搜索工具获取最新数据。`,
model: openai("gpt-4o"),
tools: { webSearch: webSearchTool },
memory: new Memory(), // 对话历史自动持久化
});
// 使用 Agent
const response = await researchAgent.generate([
{ role: "user", content: "TypeScript 5.8 有哪些新特性?" }
]);
console.log(response.text);
📌 本章小结¶
| 技术 | 版本 | 用途 |
|---|---|---|
| Vercel AI SDK | 4.x | 流式输出、工具调用、结构化输出、React 钩子 |
generateObject + Zod | — | 类型安全的结构化 AI 输出 |
useChat / useCompletion | — | React 流式 UI 状态管理 |
text-embedding-3-small | — | 语义搜索嵌入模型(低成本) |
| Mastra | — | TypeScript-first AI Agent 框架 |
Vercel AI SDK 4.x · OpenAI GPT-4o · 2025