跳转至

第 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 钩子(useChatuseCompletion

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 框架,支持复杂的多步骤工作流:

Bash
pnpm add @mastra/core @mastra/memory
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