草案 "智能体设计模式 - 提示链"

ailangchainlangraphnextjsprompt-engineeringagents
By sko X opus 4.19/20/202516 min read

构建生产级的顺序AI工作流,将复杂任务分解为可管理的步骤,在专业化提示之间传递输出,以在Next.js应用程序中实现复杂的推理和处理能力。

心智模型:AI的流水线

将提示链想象成特斯拉工厂中的现代流水线。每个工位(提示)执行特定的操作——一个安装轮子,另一个喷漆,第三个检查质量。每个工位的输出成为下一个工位的输入,创造出精致的最终产品。在AI术语中,不是制造汽车,而是提炼信息:首先提取数据,然后分析它,再格式化它,最后验证它。正如流水线彻底改变了制造效率,提示链通过将复杂的AI任务分解为可以独立优化的专业化、可管理的操作,革命性地改变了我们处理复杂AI任务的方式。

基础顺序链实现

1. 简单文本处理链

// lib/chains/basic-chain.ts
import { ChatGoogleGenerativeAI } from '@langchain/google-genai';
import { PromptTemplate } from '@langchain/core/prompts';
import { StringOutputParser } from '@langchain/core/output_parsers';
import { RunnableSequence } from '@langchain/core/runnables';

export function createBasicChain() {
  const model = new ChatGoogleGenerativeAI({
    modelName: 'gemini-2.5-flash',
    temperature: 0,
    maxRetries: 3,
  });

  // 步骤1:总结输入
  const summarizePrompt = PromptTemplate.fromTemplate(
    `将以下文本总结为2-3句话:

    {text}`
  );

  // 步骤2:提取关键点
  const extractPrompt = PromptTemplate.fromTemplate(
    `从这个总结中提取3-5个关键点:

    {summary}`
  );

  // 步骤3:生成行动项
  const actionPrompt = PromptTemplate.fromTemplate(
    `基于这些关键点,生成可行的建议:

    {keyPoints}`
  );

  // 使用LCEL构建链
  const chain = RunnableSequence.from([
    // 第一步:总结
    summarizePrompt,
    model,
    new StringOutputParser(),
    // 传递到下一步
    (summary) => ({ summary }),
    extractPrompt,
    model,
    new StringOutputParser(),
    // 传递到最后一步
    (keyPoints) => ({ keyPoints }),
    actionPrompt,
    model,
    new StringOutputParser(),
  ]);

  return chain;
}

创建一个三步链,将文本从原始输入逐步精炼为可行的建议。

2. 带流式传输的API路由

// app/api/basic-chain/route.ts
import { createBasicChain } from '@/lib/chains/basic-chain';
import { NextResponse } from 'next/server';

export const runtime = 'nodejs';
export const maxDuration = 60;

export async function POST(req: Request) {
  try {
    const { text } = await req.json();

    if (!text || text.length < 50) {
      return NextResponse.json(
        { error: '请提供至少50个字符的文本' },
        { status: 400 }
      );
    }

    const chain = createBasicChain();
    const result = await chain.invoke({ text });

    return NextResponse.json({
      success: true,
      result,
      timestamp: new Date().toISOString(),
    });
  } catch (error) {
    console.error('链执行错误:', error);
    return NextResponse.json(
      { error: '处理链失败' },
      { status: 500 }
    );
  }
}

处理链执行,具有适当的错误处理和用于无服务器部署的验证。

3. 使用TanStack Query的前端集成

// components/BasicChainInterface.tsx
'use client';

import { useState } from 'react';
import { useMutation } from '@tanstack/react-query';
import { debounce } from 'es-toolkit';

interface ChainResponse {
  success: boolean;
  result: string;
  timestamp: string;
}

export default function BasicChainInterface() {
  const [text, setText] = useState('');
  const [charCount, setCharCount] = useState(0);

  const processChain = useMutation<ChainResponse, Error, string>({
    mutationFn: async (inputText: string) => {
      const response = await fetch('/api/basic-chain', {
        method: 'POST',
        headers: { 'Content-Type': 'application/json' },
        body: JSON.stringify({ text: inputText }),
      });

      if (!response.ok) {
        const error = await response.json();
        throw new Error(error.error || '链处理失败');
      }

      return response.json();
    },
    retry: 2,
    retryDelay: (attemptIndex) => Math.min(1000 * 2 ** attemptIndex, 30000),
  });

  const handleTextChange = debounce((value: string) => {
    setCharCount(value.length);
  }, 300);

  const handleSubmit = (e: React.FormEvent) => {
    e.preventDefault();
    if (text.length >= 50) {
      processChain.mutate(text);
    }
  };

  return (
    <div className="card w-full bg-base-100 shadow-xl">
      <div className="card-body">
        <h2 className="card-title">文本处理链</h2>

        <form onSubmit={handleSubmit} className="space-y-4">
          <div className="form-control">
            <label className="label">
              <span className="label-text">输入文本</span>
              <span className="label-text-alt">{charCount} 个字符</span>
            </label>
            <textarea
              className="textarea textarea-bordered h-32"
              placeholder="输入要处理的文本(最少50个字符)..."
              value={text}
              onChange={(e) => {
                setText(e.target.value);
                handleTextChange(e.target.value);
              }}
              disabled={processChain.isPending}
            />
          </div>

          <button
            type="submit"
            className="btn btn-primary"
            disabled={processChain.isPending || text.length < 50}
          >
            {processChain.isPending ? (
              <>
                <span className="loading loading-spinner"></span>
                处理链中...
              </>
            ) : (
              '处理文本'
            )}
          </button>
        </form>

        {processChain.isError && (
          <div className="alert alert-error mt-4">
            <span>{processChain.error.message}</span>
          </div>
        )}

        {processChain.isSuccess && (
          <div className="card bg-base-200 mt-4">
            <div className="card-body">
              <h3 className="font-semibold">结果:</h3>
              <p className="whitespace-pre-wrap">{processChain.data.result}</p>
              <p className="text-xs text-base-content/60 mt-2">
                处理时间:{new Date(processChain.data.timestamp).toLocaleString()}
              </p>
            </div>
          </div>
        )}
      </div>
    </div>
  );
}

使用TanStack Query具有防抖字符计数和全面错误处理的React组件。

高级多步链与状态管理

1. 使用LangGraph的文档分析链

// lib/chains/document-analyzer.ts
import { StateGraph, START, END, Annotation } from '@langchain/langgraph';
import { ChatGoogleGenerativeAI } from '@langchain/google-genai';
import { BaseMessage, HumanMessage, AIMessage } from '@langchain/core/messages';
import { z } from 'zod';
import { StructuredOutputParser } from '@langchain/core/output_parsers';
import { isNil, chunk } from 'es-toolkit';

// 使用Zod定义状态模式
const DocumentMetadata = z.object({
  title: z.string(),
  category: z.string(),
  confidence: z.number().min(0).max(1),
  keywords: z.array(z.string()),
});

// 定义图状态
const GraphState = Annotation.Root({
  document: Annotation<string>(),
  chunks: Annotation<string[]>(),
  metadata: Annotation<z.infer<typeof DocumentMetadata>>(),
  summary: Annotation<string>(),
  insights: Annotation<string[]>(),
  messages: Annotation<BaseMessage[]>({
    reducer: (current, update) => current.concat(update),
    default: () => [],
  }),
});

export function createDocumentAnalyzer() {
  const model = new ChatGoogleGenerativeAI({
    modelName: 'gemini-2.5-pro',
    temperature: 0.1,
    maxRetries: 3,
  });

  const metadataParser = StructuredOutputParser.fromZodSchema(DocumentMetadata);

  const workflow = new StateGraph(GraphState)
    // 节点1:分块文档
    .addNode('chunk_document', async (state) => {
      const doc = state.document;
      // 将文档分块为500字符段,重叠50个字符
      const chunks = chunk(doc.split(' '), 100).map(words => words.join(' '));

      return {
        chunks,
        messages: [new AIMessage(`文档已分块为${chunks.length}个段`)],
      };
    })
    // 节点2:提取元数据
    .addNode('extract_metadata', async (state) => {
      const prompt = `分析这个文档并提取元数据。

文档:${state.chunks[0]}

${metadataParser.getFormatInstructions()}`;

      const response = await model.invoke([new HumanMessage(prompt)]);
      const metadata = await metadataParser.parse(response.content as string);

      return {
        metadata,
        messages: [new AIMessage(`已提取元数据:${metadata.category}`)],
      };
    })
    // 节点3:生成摘要
    .addNode('generate_summary', async (state) => {
      const combinedText = state.chunks.slice(0, 3).join('\n\n');
      const prompt = `创建此文档的综合摘要。
      类别:${state.metadata.category}

      文档摘录:
      ${combinedText}`;

      const response = await model.invoke([new HumanMessage(prompt)]);

      return {
        summary: response.content as string,
        messages: [new AIMessage('已生成摘要')],
      };
    })
    // 节点4:提取洞察
    .addNode('extract_insights', async (state) => {
      const prompt = `基于此摘要和元数据,提供3-5个关键洞察:

      类别:${state.metadata.category}
      关键词:${state.metadata.keywords.join(', ')}
      摘要:${state.summary}`;

      const response = await model.invoke([new HumanMessage(prompt)]);
      const insights = (response.content as string)
        .split('\n')
        .filter(line => line.trim().length > 0);

      return {
        insights,
        messages: [new AIMessage(`已提取${insights.length}个洞察`)],
      };
    })
    // 定义边
    .addEdge(START, 'chunk_document')
    .addEdge('chunk_document', 'extract_metadata')
    .addEdge('extract_metadata', 'generate_summary')
    .addEdge('generate_summary', 'extract_insights')
    .addEdge('extract_insights', END);

  return workflow.compile();
}

实现具有元数据提取和洞察生成的有状态文档处理管道。

2. 文档分析的流式API路由

// app/api/analyze-document/route.ts
import { createDocumentAnalyzer } from '@/lib/chains/document-analyzer';
import { HumanMessage } from '@langchain/core/messages';

export const runtime = 'nodejs';
export const maxDuration = 120;

export async function POST(req: Request) {
  const { document } = await req.json();

  if (!document || document.length < 100) {
    return new Response(
      JSON.stringify({ error: '文档必须至少100个字符' }),
      { status: 400 }
    );
  }

  const encoder = new TextEncoder();
  const stream = new TransformStream();
  const writer = stream.writable.getWriter();

  const analyzer = createDocumentAnalyzer();

  (async () => {
    try {
      const eventStream = await analyzer.stream({
        document,
        chunks: [],
        metadata: null,
        summary: '',
        insights: [],
        messages: [],
      });

      for await (const event of eventStream) {
        const update = {
          type: 'update',
          node: Object.keys(event)[0],
          data: event,
          timestamp: new Date().toISOString(),
        };

        await writer.write(
          encoder.encode(`data: ${JSON.stringify(update)}\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: error instanceof Error ? error.message : '未知错误'
        })}\n\n`)
      );
    } finally {
      await writer.close();
    }
  })();

  return new Response(stream.readable, {
    headers: {
      'Content-Type': 'text/event-stream',
      'Cache-Control': 'no-cache',
      'Connection': 'keep-alive',
    },
  });
}

使用服务器发送事件实时流式传输每个节点的执行结果。

3. 具有错误恢复的高级链

// lib/chains/resilient-chain.ts
import { ChatGoogleGenerativeAI } from '@langchain/google-genai';
import { RunnableSequence, RunnableWithFallbacks } from '@langchain/core/runnables';
import { PromptTemplate } from '@langchain/core/prompts';
import { StringOutputParser } from '@langchain/core/output_parsers';
import { retry, delay } from 'es-toolkit';
import { kv } from '@vercel/kv';

interface ChainCache {
  get: (key: string) => Promise<string | null>;
  set: (key: string, value: string, ttl?: number) => Promise<void>;
}

class VercelKVCache implements ChainCache {
  async get(key: string): Promise<string | null> {
    try {
      return await kv.get(key);
    } catch {
      return null;
    }
  }

  async set(key: string, value: string, ttl = 3600): Promise<void> {
    try {
      await kv.set(key, value, { ex: ttl });
    } catch {
      // 静默处理缓存写入失败
    }
  }
}

export function createResilientChain() {
  const cache = new VercelKVCache();

  // 带回退的主模型
  const primaryModel = new ChatGoogleGenerativeAI({
    modelName: 'gemini-2.5-pro',
    temperature: 0,
    maxRetries: 2,
  });

  const fallbackModel = new ChatGoogleGenerativeAI({
    modelName: 'gemini-2.5-flash',
    temperature: 0,
    maxRetries: 1,
  });

  const modelWithFallback = primaryModel.withFallbacks({
    fallbacks: [fallbackModel],
  });

  // 创建缓存提示执行器
  const cachedExecutor = async (prompt: string, input: any) => {
    const cacheKey = `chain:${prompt.substring(0, 20)}:${JSON.stringify(input)}`;

    // 检查缓存
    const cached = await cache.get(cacheKey);
    if (cached) return cached;

    // 使用重试逻辑执行
    const result = await retry(
      async () => {
        const template = PromptTemplate.fromTemplate(prompt);
        const chain = template.pipe(modelWithFallback).pipe(new StringOutputParser());
        return await chain.invoke(input);
      },
      {
        times: 3,
        delay: 1000,
        onError: async (error, attemptNumber) => {
          console.error(`尝试${attemptNumber}失败:`, error);
          await delay(attemptNumber * 1000); // 指数退避
        },
      }
    );

    // 缓存结果
    await cache.set(cacheKey, result);
    return result;
  };

  // 构建弹性链
  const chain = RunnableSequence.from([
    async (input: { query: string }) => {
      // 步骤1:查询理解与缓存
      const understanding = await cachedExecutor(
        '为了清晰起见,重新表述这个查询:{query}',
        input
      );
      return { understanding };
    },
    async (state: { understanding: string }) => {
      // 步骤2:研究与回退
      const research = await cachedExecutor(
        '提供关于以下内容的详细研究:{understanding}',
        state
      );
      return { ...state, research };
    },
    async (state: { understanding: string; research: string }) => {
      // 步骤3:综合与验证
      const synthesis = await cachedExecutor(
        `将此研究综合为可行的洞察:
        主题:{understanding}
        研究:{research}`,
        state
      );

      // 验证输出
      if (synthesis.length < 100) {
        throw new Error('综合内容太短,正在重试...');
      }

      return { ...state, synthesis };
    },
  ]);

  return chain;
}

使用es-toolkit工具实现具有缓存、回退和重试逻辑的生产就绪链。

4. 并行链处理

// lib/chains/parallel-processor.ts
import { ChatGoogleGenerativeAI } from '@langchain/google-genai';
import { RunnableParallel, RunnableSequence } from '@langchain/core/runnables';
import { PromptTemplate } from '@langchain/core/prompts';
import { StringOutputParser } from '@langchain/core/output_parsers';
import { chunk, uniqBy } from 'es-toolkit';

export function createParallelProcessor() {
  const model = new ChatGoogleGenerativeAI({
    modelName: 'gemini-2.5-flash',
    temperature: 0.3,
  });

  // 定义并行分析分支
  const sentimentAnalysis = PromptTemplate.fromTemplate(
    '分析此文本的情感(积极/消极/中性):{text}'
  ).pipe(model).pipe(new StringOutputParser());

  const entityExtraction = PromptTemplate.fromTemplate(
    '从以下文本中提取所有命名实体(人名、地名、组织):{text}'
  ).pipe(model).pipe(new StringOutputParser());

  const topicClassification = PromptTemplate.fromTemplate(
    '对此文本的主要主题进行分类:{text}'
  ).pipe(model).pipe(new StringOutputParser());

  const keywordExtraction = PromptTemplate.fromTemplate(
    '从此文本中提取5-10个关键词:{text}'
  ).pipe(model).pipe(new StringOutputParser());

  // 创建并行执行
  const parallelAnalysis = RunnableParallel({
    sentiment: sentimentAnalysis,
    entities: entityExtraction,
    topic: topicClassification,
    keywords: keywordExtraction,
  });

  // 综合步骤
  const synthesisPrompt = PromptTemplate.fromTemplate(`
    基于以下内容创建综合分析报告:

    情感:{sentiment}
    实体:{entities}
    主题:{topic}
    关键词:{keywords}

    格式化为带有章节的结构化报告。
  `);

  // 完整链:并行分析然后综合
  const chain = RunnableSequence.from([
    parallelAnalysis,
    synthesisPrompt,
    model,
    new StringOutputParser(),
  ]);

  return chain;
}

// 多文档批处理器
export async function processBatch(documents: string[]) {
  const processor = createParallelProcessor();
  const BATCH_SIZE = 5;

  // 分批处理以避免速率限制
  const batches = chunk(documents, BATCH_SIZE);
  const results = [];

  for (const batch of batches) {
    const batchResults = await Promise.all(
      batch.map(async (doc) => {
        try {
          const result = await processor.invoke({ text: doc });
          return { success: true, result, document: doc };
        } catch (error) {
          return {
            success: false,
            error: error instanceof Error ? error.message : '未知错误',
            document: doc
          };
        }
      })
    );
    results.push(...batchResults);

    // 批次间的速率限制延迟
    if (batches.indexOf(batch) < batches.length - 1) {
      await new Promise(resolve => setTimeout(resolve, 1000));
    }
  }

  return results;
}

并行执行多个分析任务然后综合结果,支持批处理。

5. 具有记忆的上下文感知链

// lib/chains/contextual-chain.ts
import { ChatGoogleGenerativeAI } from '@langchain/google-genai';
import { BufferMemory } from 'langchain/memory';
import { ConversationChain } from 'langchain/chains';
import { PromptTemplate } from '@langchain/core/prompts';
import { MessagesPlaceholder } from '@langchain/core/prompts';
import { RunnableSequence } from '@langchain/core/runnables';

export class ContextualChain {
  private memory: BufferMemory;
  private chain: RunnableSequence;
  private model: ChatGoogleGenerativeAI;

  constructor() {
    this.model = new ChatGoogleGenerativeAI({
      modelName: 'gemini-2.5-pro',
      temperature: 0.7,
    });

    this.memory = new BufferMemory({
      returnMessages: true,
      memoryKey: 'history',
      inputKey: 'input',
      outputKey: 'output',
    });

    this.initializeChain();
  }

  private initializeChain() {
    const contextPrompt = PromptTemplate.fromTemplate(`
      您正在逐步分析文档。
      先前上下文:{history}

      当前任务:{task}
      当前输入:{input}

      提供建立在先前分析基础上的详细回应。
    `);

    this.chain = RunnableSequence.from([
      async (input: { task: string; input: string }) => {
        const history = await this.memory.loadMemoryVariables({});
        return { ...input, history: history.history || '' };
      },
      contextPrompt,
      this.model,
      async (response) => {
        const content = response.content as string;
        // 保存到记忆
        await this.memory.saveContext(
          { input: this.lastInput },
          { output: content }
        );
        return content;
      },
    ]);
  }

  private lastInput: string = '';

  async process(task: string, input: string): Promise<string> {
    this.lastInput = `${task}: ${input}`;
    return await this.chain.invoke({ task, input });
  }

  async reset() {
    await this.memory.clear();
  }

  async getHistory(): Promise<string> {
    const history = await this.memory.loadMemoryVariables({});
    return history.history || '无可用历史记录';
  }
}

// 在API路由中的用法
export async function processWithContext(
  sessionId: string,
  task: string,
  input: string
) {
  // 按会话存储链
  const chainStore = global as any;
  chainStore.contextChains = chainStore.contextChains || new Map();

  if (!chainStore.contextChains.has(sessionId)) {
    chainStore.contextChains.set(sessionId, new ContextualChain());
  }

  const chain = chainStore.contextChains.get(sessionId);
  return await chain.process(task, input);
}

在多个链执行中维护对话上下文,用于有状态的文档分析。

6. 具有监控的生产就绪链

// lib/chains/monitored-chain.ts
import { ChatGoogleGenerativeAI } from '@langchain/google-genai';
import { RunnableSequence } from '@langchain/core/runnables';
import { PromptTemplate } from '@langchain/core/prompts';
import { StringOutputParser } from '@langchain/core/output_parsers';

interface ChainMetrics {
  executionTime: number;
  tokenCount: number;
  cost: number;
  steps: Array<{
    name: string;
    duration: number;
    tokens: number;
  }>;
}

export class MonitoredChain {
  private metrics: ChainMetrics[] = [];

  async executeWithMonitoring(input: string): Promise<{
    result: string;
    metrics: ChainMetrics;
  }> {
    const startTime = Date.now();
    const stepMetrics: ChainMetrics['steps'] = [];

    const model = new ChatGoogleGenerativeAI({
      modelName: 'gemini-2.5-flash',
      temperature: 0,
      callbacks: [
        {
          handleLLMStart: async (llm, prompts) => {
            console.log('LLM开始:', prompts[0].substring(0, 100));
          },
          handleLLMEnd: async (output) => {
            const tokens = output.llmOutput?.tokenUsage?.totalTokens || 0;
            stepMetrics[stepMetrics.length - 1].tokens = tokens;
          },
        },
      ],
    });

    // 步骤1:分类
    const stepStart = Date.now();
    stepMetrics.push({ name: 'classification', duration: 0, tokens: 0 });

    const classificationPrompt = PromptTemplate.fromTemplate(
      '将此文本分类为不同类别:{text}'
    );

    const classification = await classificationPrompt
      .pipe(model)
      .pipe(new StringOutputParser())
      .invoke({ text: input });

    stepMetrics[0].duration = Date.now() - stepStart;

    // 步骤2:增强
    const step2Start = Date.now();
    stepMetrics.push({ name: 'enhancement', duration: 0, tokens: 0 });

    const enhancementPrompt = PromptTemplate.fromTemplate(
      '用详细信息增强此分类:{classification}'
    );

    const enhancement = await enhancementPrompt
      .pipe(model)
      .pipe(new StringOutputParser())
      .invoke({ classification });

    stepMetrics[1].duration = Date.now() - step2Start;

    // 计算总体指标
    const totalTokens = stepMetrics.reduce((sum, step) => sum + step.tokens, 0);
    const metrics: ChainMetrics = {
      executionTime: Date.now() - startTime,
      tokenCount: totalTokens,
      cost: totalTokens * 0.000001, // 示例定价
      steps: stepMetrics,
    };

    this.metrics.push(metrics);

    return {
      result: enhancement,
      metrics,
    };
  }

  getAverageMetrics(): ChainMetrics | null {
    if (this.metrics.length === 0) return null;

    const avgTime = this.metrics.reduce((sum, m) => sum + m.executionTime, 0) / this.metrics.length;
    const avgTokens = this.metrics.reduce((sum, m) => sum + m.tokenCount, 0) / this.metrics.length;
    const avgCost = this.metrics.reduce((sum, m) => sum + m.cost, 0) / this.metrics.length;

    return {
      executionTime: Math.round(avgTime),
      tokenCount: Math.round(avgTokens),
      cost: avgCost,
      steps: [],
    };
  }
}

跟踪每个链执行的详细指标,包括时间、token使用量和成本。

7. 链监控的前端仪表板

// components/ChainDashboard.tsx
'use client';

import { useState, useEffect } from 'react';
import { useQuery, useMutation } from '@tanstack/react-query';
import { groupBy, mean, round } from 'es-toolkit';

interface ChainExecution {
  id: string;
  chainName: string;
  status: 'pending' | 'processing' | 'complete' | 'failed';
  startTime: string;
  endTime?: string;
  metrics?: {
    executionTime: number;
    tokenCount: number;
    cost: number;
  };
}

export default function ChainDashboard() {
  const [executions, setExecutions] = useState<ChainExecution[]>([]);

  // 获取执行历史
  const { data: history } = useQuery({
    queryKey: ['chain-history'],
    queryFn: async () => {
      const response = await fetch('/api/chain-history');
      return response.json();
    },
    refetchInterval: 5000,
  });

  // 执行新链
  const executeChain = useMutation({
    mutationFn: async (chainConfig: { name: string; input: string }) => {
      const response = await fetch('/api/execute-chain', {
        method: 'POST',
        headers: { 'Content-Type': 'application/json' },
        body: JSON.stringify(chainConfig),
      });
      return response.json();
    },
    onSuccess: (data) => {
      setExecutions(prev => [...prev, data]);
    },
  });

  // 计算统计信息
  const stats = executions.reduce(
    (acc, exec) => {
      if (exec.status === 'complete' && exec.metrics) {
        acc.totalExecutions++;
        acc.totalTokens += exec.metrics.tokenCount;
        acc.totalCost += exec.metrics.cost;
        acc.avgTime = mean([acc.avgTime, exec.metrics.executionTime]);
      } else if (exec.status === 'failed') {
        acc.failures++;
      }
      return acc;
    },
    {
      totalExecutions: 0,
      totalTokens: 0,
      totalCost: 0,
      avgTime: 0,
      failures: 0,
    }
  );

  return (
    <div className="p-6 space-y-6">
      {/* 统计网格 */}
      <div className="stats shadow w-full">
        <div className="stat">
          <div className="stat-title">总执行次数</div>
          <div className="stat-value">{stats.totalExecutions}</div>
          <div className="stat-desc">
            {stats.failures > 0 && `${stats.failures} 次失败`}
          </div>
        </div>

        <div className="stat">
          <div className="stat-title">平均执行时间</div>
          <div className="stat-value">{round(stats.avgTime / 1000, 2)}秒</div>
          <div className="stat-desc">每次链执行</div>
        </div>

        <div className="stat">
          <div className="stat-title">总Token数</div>
          <div className="stat-value">{stats.totalTokens.toLocaleString()}</div>
          <div className="stat-desc">
            总成本 ${round(stats.totalCost, 4)}
          </div>
        </div>
      </div>

      {/* 执行时间线 */}
      <div className="card bg-base-100 shadow-xl">
        <div className="card-body">
          <h2 className="card-title">最近执行</h2>

          <div className="overflow-x-auto">
            <table className="table table-zebra">
              <thead>
                <tr>
                  <th>链</th>
                  <th>状态</th>
                  <th>耗时</th>
                  <th>Token数</th>
                  <th>成本</th>
                </tr>
              </thead>
              <tbody>
                {executions.slice(-10).reverse().map((exec) => (
                  <tr key={exec.id}>
                    <td>{exec.chainName}</td>
                    <td>
                      <div className={`badge ${
                        exec.status === 'complete' ? 'badge-success' :
                        exec.status === 'failed' ? 'badge-error' :
                        exec.status === 'processing' ? 'badge-warning' :
                        'badge-ghost'
                      }`}>
                        {exec.status === 'complete' ? '完成' :
                         exec.status === 'failed' ? '失败' :
                         exec.status === 'processing' ? '处理中' :
                         '等待中'}
                      </div>
                    </td>
                    <td>
                      {exec.metrics
                        ? `${round(exec.metrics.executionTime / 1000, 2)}秒`
                        : '-'}
                    </td>
                    <td>{exec.metrics?.tokenCount || '-'}</td>
                    <td>
                      {exec.metrics
                        ? `$${round(exec.metrics.cost, 4)}`
                        : '-'}
                    </td>
                  </tr>
                ))}
              </tbody>
            </table>
          </div>
        </div>
      </div>

      {/* 执行新链 */}
      <div className="card bg-base-100 shadow-xl">
        <div className="card-body">
          <h2 className="card-title">执行链</h2>

          <div className="form-control">
            <label className="label">
              <span className="label-text">选择链类型</span>
            </label>
            <select className="select select-bordered">
              <option>基础顺序</option>
              <option>文档分析器</option>
              <option>并行处理器</option>
              <option>上下文链</option>
            </select>
          </div>

          <button
            className="btn btn-primary"
            onClick={() => executeChain.mutate({
              name: '基础顺序',
              input: '示例输入文本',
            })}
            disabled={executeChain.isPending}
          >
            {executeChain.isPending ? (
              <>
                <span className="loading loading-spinner"></span>
                执行中...
              </>
            ) : (
              '执行链'
            )}
          </button>
        </div>
      </div>
    </div>
  );
}

用于监控链执行的实时仪表板,包含统计信息和成本跟踪。

结论

提示链将复杂的AI任务转化为可管理的顺序操作,可以独立优化、监控和扩展。通过在Vercel无服务器平台上使用LangChain的LCEL和LangGraph的有状态工作流实现这些模式,您可以构建可靠处理复杂推理任务的生产就绪AI应用程序。关键是从简单的链开始,随着应用程序的扩展逐步添加缓存、回退和监控等弹性功能。