LangChain.js 完全开发手册(八)Agent 智能代理系统开发

第8章:Agent 智能代理系统开发

前言

大家好,我是鲫小鱼。是一名不写前端代码的前端工程师,热衷于分享非前端的知识,带领切图仔逃离切图圈子,欢迎关注我,微信公众号:《鲫小鱼不正经》。欢迎点赞、收藏、关注,一键三连!!

🎯 本章学习目标

  • 掌握 Agent 核心概念:工具调用、计划分解、多轮推理、状态管理
  • 构建具备检索、计算、API 调用能力的多功能 Agent
  • 用 LangGraph 实现 Multi-Agent 协作、冲突解决与任务编排
  • 集成代码执行环境(沙箱),支持数据分析与脚本生成
  • 在 Next.js 中构建 Agent 对话界面,支持工具选择与执行过程可视化
  • 实战项目:智能客服 Agent(多轮对话 + 知识检索 + 工单创建)
  • 工程化:Agent 安全、性能优化、可观测性与错误恢复

🤖 Agent 基础概念与架构

8.1 什么是 Agent

Agent 是具备以下能力的 AI 系统:

  • 工具调用:能使用外部工具(搜索、计算、API)
  • 计划分解:将复杂任务拆解为可执行的子步骤
  • 多轮推理:基于中间结果调整策略,而非一次性生成
  • 状态管理:维护对话历史、工具调用记录、任务进度

8.2 Agent 架构模式

rust 复制代码
用户输入 → 思考(Reasoning)→ 工具选择(Tool Selection)→ 工具执行(Tool Execution)
  → 结果分析(Result Analysis)→ 下一步决策(Next Action)→ 输出或继续循环

8.3 Agent 类型

  • ReAct Agent:思考-行动-观察循环,适合复杂推理
  • Tool-using Agent:直接工具调用,适合结构化任务
  • Multi-Agent:多个 Agent 协作,适合复杂场景
  • Code Agent:代码生成与执行,适合编程任务

🛠️ 工具系统:定义、注册与调用

8.4 工具定义与类型

typescript 复制代码
// 文件:src/ch08/tools.ts
import { Tool } from "@langchain/core/tools";
import { z } from "zod";

// 基础工具接口
export interface BaseTool {
  name: string;
  description: string;
  schema: z.ZodSchema;
  func: (...args: any[]) => Promise<any>;
}

// 搜索工具
export class SearchTool extends Tool {
  name = "search";
  description = "搜索网络信息,输入搜索关键词";
  schema = z.object({ query: z.string().describe("搜索关键词") });

  async _call(input: { query: string }) {
    // 模拟搜索,实际可接入 Google/Bing API
    return `搜索结果:${input.query} 的相关信息...`;
  }
}

// 计算工具
export class CalculatorTool extends Tool {
  name = "calculator";
  description = "执行数学计算,支持加减乘除和基本函数";
  schema = z.object({ expression: z.string().describe("数学表达式,如 2+3*4") });

  async _call(input: { expression: string }) {
    try {
      // 安全计算,避免 eval
      const result = this.safeEval(input.expression);
      return `计算结果:${result}`;
    } catch (error) {
      return `计算错误:${error}`;
    }
  }

  private safeEval(expr: string): number {
    // 简化的安全计算实现
    const allowed = /^[0-9+\-*/().\s]+$/;
    if (!allowed.test(expr)) throw new Error("包含非法字符");
    return Function(`"use strict"; return (${expr})`)();
  }
}

// 时间工具
export class TimeTool extends Tool {
  name = "get_time";
  description = "获取当前时间信息";
  schema = z.object({});

  async _call() {
    const now = new Date();
    return `当前时间:${now.toLocaleString('zh-CN')}`;
  }
}

8.5 工具注册与管理

typescript 复制代码
// 文件:src/ch08/tool-manager.ts
import { SearchTool, CalculatorTool, TimeTool } from "./tools";

export class ToolManager {
  private tools = new Map<string, any>();

  constructor() {
    this.register(new SearchTool());
    this.register(new CalculatorTool());
    this.register(new TimeTool());
  }

  register(tool: any) {
    this.tools.set(tool.name, tool);
  }

  getTool(name: string) {
    return this.tools.get(name);
  }

  getAllTools() {
    return Array.from(this.tools.values());
  }

  async execute(name: string, args: any) {
    const tool = this.getTool(name);
    if (!tool) throw new Error(`工具 ${name} 不存在`);
    return await tool._call(args);
  }
}

🧠 ReAct Agent:思考-行动-观察循环

8.6 ReAct 模式实现

typescript 复制代码
// 文件:src/ch08/react-agent.ts
import { ChatOpenAI } from "@langchain/openai";
import { PromptTemplate } from "@langchain/core/prompts";
import { ToolManager } from "./tool-manager";

export type AgentState = {
  question: string;
  thought: string;
  action: string;
  actionInput: any;
  observation: string;
  answer: string;
  step: number;
};

export class ReActAgent {
  private llm: ChatOpenAI;
  private tools: ToolManager;
  private maxSteps = 5;

  constructor() {
    this.llm = new ChatOpenAI({ temperature: 0 });
    this.tools = new ToolManager();
  }

  private getPrompt() {
    return PromptTemplate.fromTemplate(`
你是一个智能助手,可以调用工具来完成任务。

可用工具:
{tools}

思考过程:
1. 分析问题,确定需要使用的工具
2. 调用工具获取信息
3. 基于结果继续思考或给出最终答案

格式:
思考:<你的推理过程>
行动:<工具名称>
行动输入:<工具参数>
观察:<工具返回结果>
... (可以重复思考-行动-观察)
思考:<最终推理>
最终答案:<给用户的答案>

问题:{question}
思考:`);
  }

  async run(question: string): Promise<AgentState[]> {
    const steps: AgentState[] = [];
    let currentThought = "";
    let step = 0;

    while (step < this.maxSteps) {
      const prompt = this.getPrompt();
      const toolsDesc = this.tools.getAllTools()
        .map(t => `${t.name}: ${t.description}`)
        .join("\n");

      const response = await prompt.pipe(this.llm).invoke({
        question,
        tools: toolsDesc,
        thought: currentThought
      });

      const content = response.content as string;

      // 解析响应(简化版,实际需要更复杂的解析)
      const thoughtMatch = content.match(/思考:(.*?)(?=\n|$)/);
      const actionMatch = content.match(/行动:(.*?)(?=\n|$)/);
      const inputMatch = content.match(/行动输入:(.*?)(?=\n|$)/);
      const answerMatch = content.match(/最终答案:(.*?)(?=\n|$)/);

      if (answerMatch) {
        steps.push({
          question,
          thought: thoughtMatch?.[1] || "",
          action: "",
          actionInput: {},
          observation: "",
          answer: answerMatch[1],
          step
        });
        break;
      }

      if (actionMatch && inputMatch) {
        const action = actionMatch[1].trim();
        const actionInput = JSON.parse(inputMatch[1]);

        // 执行工具
        const observation = await this.tools.execute(action, actionInput);

        steps.push({
          question,
          thought: thoughtMatch?.[1] || "",
          action,
          actionInput,
          observation,
          answer: "",
          step
        });

        currentThought += `\n思考:${thoughtMatch?.[1]}\n行动:${action}\n观察:${observation}\n`;
        step++;
      } else {
        break;
      }
    }

    return steps;
  }
}

8.7 改进的响应解析

typescript 复制代码
// 文件:src/ch08/parser.ts
export function parseReActResponse(content: string) {
  const lines = content.split('\n');
  const result: any = {};

  for (const line of lines) {
    if (line.startsWith('思考:')) {
      result.thought = line.slice(3).trim();
    } else if (line.startsWith('行动:')) {
      result.action = line.slice(3).trim();
    } else if (line.startsWith('行动输入:')) {
      try {
        result.actionInput = JSON.parse(line.slice(5).trim());
      } catch {
        result.actionInput = line.slice(5).trim();
      }
    } else if (line.startsWith('观察:')) {
      result.observation = line.slice(3).trim();
    } else if (line.startsWith('最终答案:')) {
      result.answer = line.slice(5).trim();
    }
  }

  return result;
}

🔧 代码执行 Agent:沙箱环境

8.8 代码执行工具

typescript 复制代码
// 文件:src/ch08/code-tools.ts
import { Tool } from "@langchain/core/tools";
import { z } from "zod";

export class CodeExecutionTool extends Tool {
  name = "execute_code";
  description = "执行 Python 代码,支持数据分析、图表生成等";
  schema = z.object({
    code: z.string().describe("要执行的 Python 代码"),
    language: z.enum(["python"]).default("python").describe("编程语言")
  });

  private sandbox = new Map<string, any>();

  async _call(input: { code: string; language: string }) {
    if (input.language !== "python") {
      return "目前只支持 Python 代码执行";
    }

    try {
      // 简化的 Python 代码执行(实际应使用安全的沙箱)
      const result = await this.executePython(input.code);
      return `执行结果:\n${result}`;
    } catch (error) {
      return `执行错误:${error}`;
    }
  }

  private async executePython(code: string): Promise<string> {
    // 安全检查
    const dangerous = ['import os', 'import sys', 'eval(', 'exec(', '__import__'];
    for (const d of dangerous) {
      if (code.includes(d)) {
        throw new Error(`禁止使用:${d}`);
      }
    }

    // 模拟执行(实际应使用安全的 Python 运行时)
    if (code.includes('print(')) {
      return "模拟输出:代码执行成功";
    } else if (code.includes('import pandas')) {
      return "pandas 已导入,可以进行数据分析";
    } else if (code.includes('import matplotlib')) {
      return "matplotlib 已导入,可以生成图表";
    }

    return "代码执行完成";
  }
}

// 数据分析工具
export class DataAnalysisTool extends Tool {
  name = "analyze_data";
  description = "分析数据,生成统计信息和可视化";
  schema = z.object({
    data: z.string().describe("CSV 格式的数据或数据描述"),
    analysis_type: z.enum(["summary", "visualization", "correlation"]).describe("分析类型")
  });

  async _call(input: { data: string; analysis_type: string }) {
    const code = this.generateAnalysisCode(input.data, input.analysis_type);
    const execTool = new CodeExecutionTool();
    return await execTool._call({ code, language: "python" });
  }

  private generateAnalysisCode(data: string, type: string): string {
    switch (type) {
      case "summary":
        return `
import pandas as pd
import numpy as np
# 假设数据已加载
print("数据概览:")
print(f"行数:{len(data)}")
print(f"列数:{len(data.columns)}")
print("\\n统计摘要:")
print(data.describe())
`;
      case "visualization":
        return `
import matplotlib.pyplot as plt
import seaborn as sns
# 生成示例图表
plt.figure(figsize=(10, 6))
plt.title("数据可视化")
plt.show()
`;
      default:
        return "print('分析完成')";
    }
  }
}

🤝 Multi-Agent 协作系统

8.9 Agent 角色定义

typescript 复制代码
// 文件:src/ch08/multi-agent.ts
import { ChatOpenAI } from "@langchain/openai";
import { PromptTemplate } from "@langchain/core/prompts";

export interface AgentRole {
  name: string;
  description: string;
  expertise: string[];
  tools: string[];
}

export const AGENT_ROLES: AgentRole[] = [
  {
    name: "研究员",
    description: "负责信息收集和事实核查",
    expertise: ["搜索", "验证", "总结"],
    tools: ["search", "get_time"]
  },
  {
    name: "分析师",
    description: "负责数据分析和洞察",
    expertise: ["计算", "统计", "可视化"],
    tools: ["calculator", "analyze_data"]
  },
  {
    name: "协调员",
    description: "负责任务分配和结果整合",
    expertise: ["规划", "协调", "总结"],
    tools: ["get_time"]
  }
];

export class MultiAgentSystem {
  private agents: Map<string, any> = new Map();
  private llm: ChatOpenAI;

  constructor() {
    this.llm = new ChatOpenAI({ temperature: 0.7 });
    this.initializeAgents();
  }

  private initializeAgents() {
    for (const role of AGENT_ROLES) {
      this.agents.set(role.name, {
        role,
        llm: new ChatOpenAI({ temperature: 0.5 }),
        prompt: this.createRolePrompt(role)
      });
    }
  }

  private createRolePrompt(role: AgentRole) {
    return PromptTemplate.fromTemplate(`
你是 ${role.name},${role.description}

你的专长:${role.expertise.join(", ")}
可用工具:${role.tools.join(", ")}

任务:{task}
当前状态:{state}
其他 Agent 的意见:{other_opinions}

请基于你的专长提供建议或执行任务:
`);
  }

  async collaborate(task: string): Promise<any> {
    const results: any = {};
    const opinions: string[] = [];

    // 第一轮:各 Agent 独立分析
    for (const [name, agent] of this.agents) {
      const response = await agent.prompt.pipe(agent.llm).invoke({
        task,
        state: "初始分析阶段",
        other_opinions: "暂无"
      });

      results[name] = response.content;
      opinions.push(`${name}: ${response.content}`);
    }

    // 第二轮:基于其他 Agent 意见调整
    for (const [name, agent] of this.agents) {
      const otherOpinions = opinions.filter(o => !o.startsWith(name));
      const response = await agent.prompt.pipe(agent.llm).invoke({
        task,
        state: "协作调整阶段",
        other_opinions: otherOpinions.join("\n")
      });

      results[`${name}_final`] = response.content;
    }

    // 最终整合
    const coordinator = this.agents.get("协调员");
    const finalResponse = await coordinator.prompt.pipe(coordinator.llm).invoke({
      task,
      state: "最终整合",
      other_opinions: Object.entries(results).map(([k, v]) => `${k}: ${v}`).join("\n")
    });

    return {
      individual_results: results,
      final_answer: finalResponse.content
    };
  }
}

🌐 Next.js 集成:Agent 对话界面

8.10 Agent API 接口

typescript 复制代码
// 文件:src/app/api/agent/route.ts
import { NextRequest } from "next/server";
import { ReActAgent } from "@/src/ch08/react-agent";
import { MultiAgentSystem } from "@/src/ch08/multi-agent";

export const runtime = "edge";

export async function POST(req: NextRequest) {
  const { message, agentType = "react" } = await req.json();

  try {
    let result;

    if (agentType === "react") {
      const agent = new ReActAgent();
      result = await agent.run(message);
    } else if (agentType === "multi") {
      const system = new MultiAgentSystem();
      result = await system.collaborate(message);
    } else {
      return Response.json({ error: "不支持的 Agent 类型" }, { status: 400 });
    }

    return Response.json({ success: true, data: result });
  } catch (error: any) {
    return Response.json({ error: error.message }, { status: 500 });
  }
}

8.11 前端对话界面

tsx 复制代码
// 文件:src/app/agent/page.tsx
"use client";
import { useState, useRef, useEffect } from "react";

interface Message {
  id: string;
  type: "user" | "agent";
  content: string;
  steps?: any[];
  timestamp: Date;
}

export default function AgentPage() {
  const [messages, setMessages] = useState<Message[]>([]);
  const [input, setInput] = useState("");
  const [agentType, setAgentType] = useState("react");
  const [loading, setLoading] = useState(false);
  const messagesEndRef = useRef<HTMLDivElement>(null);

  const scrollToBottom = () => {
    messagesEndRef.current?.scrollIntoView({ behavior: "smooth" });
  };

  useEffect(() => {
    scrollToBottom();
  }, [messages]);

  const sendMessage = async () => {
    if (!input.trim() || loading) return;

    const userMessage: Message = {
      id: Date.now().toString(),
      type: "user",
      content: input,
      timestamp: new Date()
    };

    setMessages(prev => [...prev, userMessage]);
    setInput("");
    setLoading(true);

    try {
      const response = await fetch("/api/agent", {
        method: "POST",
        headers: { "Content-Type": "application/json" },
        body: JSON.stringify({ message: input, agentType })
      });

      const result = await response.json();

      if (result.success) {
        const agentMessage: Message = {
          id: (Date.now() + 1).toString(),
          type: "agent",
          content: agentType === "react"
            ? result.data[result.data.length - 1]?.answer || "处理完成"
            : result.data.final_answer,
          steps: agentType === "react" ? result.data : undefined,
          timestamp: new Date()
        };
        setMessages(prev => [...prev, agentMessage]);
      } else {
        throw new Error(result.error);
      }
    } catch (error: any) {
      const errorMessage: Message = {
        id: (Date.now() + 1).toString(),
        type: "agent",
        content: `错误:${error.message}`,
        timestamp: new Date()
      };
      setMessages(prev => [...prev, errorMessage]);
    } finally {
      setLoading(false);
    }
  };

  const renderStep = (step: any, index: number) => (
    <div key={index} className="ml-4 mb-2 p-2 bg-gray-50 rounded">
      <div className="text-sm text-gray-600">步骤 {step.step + 1}</div>
      {step.thought && <div className="text-sm">思考:{step.thought}</div>}
      {step.action && <div className="text-sm">行动:{step.action}</div>}
      {step.observation && <div className="text-sm">观察:{step.observation}</div>}
    </div>
  );

  return (
    <div className="max-w-4xl mx-auto h-screen flex flex-col">
      <div className="p-4 border-b">
        <h1 className="text-2xl font-bold mb-4">智能 Agent 对话</h1>
        <div className="flex gap-4">
          <select
            value={agentType}
            onChange={(e) => setAgentType(e.target.value)}
            className="border rounded px-3 py-1"
          >
            <option value="react">ReAct Agent</option>
            <option value="multi">Multi-Agent</option>
          </select>
        </div>
      </div>

      <div className="flex-1 overflow-y-auto p-4 space-y-4">
        {messages.map((message) => (
          <div key={message.id} className={`flex ${message.type === "user" ? "justify-end" : "justify-start"}`}>
            <div className={`max-w-3xl p-3 rounded-lg ${
              message.type === "user"
                ? "bg-blue-500 text-white"
                : "bg-gray-100"
            }`}>
              <div className="whitespace-pre-wrap">{message.content}</div>
              {message.steps && (
                <div className="mt-2">
                  <div className="text-sm font-semibold mb-2">执行步骤:</div>
                  {message.steps.map(renderStep)}
                </div>
              )}
              <div className="text-xs opacity-70 mt-1">
                {message.timestamp.toLocaleTimeString()}
              </div>
            </div>
          </div>
        ))}
        {loading && (
          <div className="flex justify-start">
            <div className="bg-gray-100 p-3 rounded-lg">
              <div className="flex items-center gap-2">
                <div className="animate-spin rounded-full h-4 w-4 border-b-2 border-gray-900"></div>
                Agent 正在思考...
              </div>
            </div>
          </div>
        )}
        <div ref={messagesEndRef} />
      </div>

      <div className="p-4 border-t">
        <div className="flex gap-2">
          <input
            type="text"
            value={input}
            onChange={(e) => setInput(e.target.value)}
            onKeyPress={(e) => e.key === "Enter" && sendMessage()}
            placeholder="输入你的问题..."
            className="flex-1 border rounded px-3 py-2"
            disabled={loading}
          />
          <button
            onClick={sendMessage}
            disabled={loading || !input.trim()}
            className="px-4 py-2 bg-blue-500 text-white rounded disabled:opacity-50"
          >
            发送
          </button>
        </div>
      </div>
    </div>
  );
}

🚀 实战项目:智能客服 Agent

8.12 项目需求

  • 多轮对话:理解上下文,支持追问和澄清
  • 知识检索:集成 RAG 系统,提供准确答案
  • 工单创建:复杂问题自动创建工单
  • 情感分析:识别用户情绪,调整回复策略
  • 意图识别:分类用户意图(咨询、投诉、建议等)

8.13 客服 Agent 实现

typescript 复制代码
// 文件:src/ch08/customer-service-agent.ts
import { ChatOpenAI } from "@langchain/openai";
import { PromptTemplate } from "@langchain/core/prompts";
import { ToolManager } from "./tool-manager";

export interface CustomerIntent {
  type: "query" | "complaint" | "suggestion" | "escalation";
  confidence: number;
  entities: Record<string, any>;
}

export class CustomerServiceAgent {
  private llm: ChatOpenAI;
  private tools: ToolManager;
  private memory: any[] = [];

  constructor() {
    this.llm = new ChatOpenAI({ temperature: 0.3 });
    this.tools = new ToolManager();
  }

  private async analyzeIntent(message: string): Promise<CustomerIntent> {
    const prompt = PromptTemplate.fromTemplate(`
分析用户意图,输出 JSON:
{
  "type": "query|complaint|suggestion|escalation",
  "confidence": 0.0-1.0,
  "entities": {
    "product": "产品名称",
    "issue": "问题描述",
    "urgency": "紧急程度"
  }
}

用户消息:{message}
历史对话:{history}
`);

    const response = await prompt.pipe(this.llm).invoke({
      message,
      history: this.memory.slice(-3).map(m => `${m.role}: ${m.content}`).join("\n")
    });

    return JSON.parse(response.content as string);
  }

  private async generateResponse(
    message: string,
    intent: CustomerIntent,
    context: any
  ): Promise<string> {
    const prompt = PromptTemplate.fromTemplate(`
你是专业的客服代表,基于用户意图和上下文生成回复。

用户意图:{intent}
上下文信息:{context}
历史对话:{history}

要求:
1. 语气友好专业
2. 针对意图类型调整回复策略
3. 提供具体解决方案
4. 必要时引导用户提供更多信息

用户消息:{message}
回复:`);

    const response = await prompt.pipe(this.llm).invoke({
      message,
      intent: JSON.stringify(intent),
      context: JSON.stringify(context),
      history: this.memory.slice(-3).map(m => `${m.role}: ${m.content}`).join("\n")
    });

    return response.content as string;
  }

  async processMessage(message: string): Promise<{
    response: string;
    intent: CustomerIntent;
    actions: string[];
    shouldEscalate: boolean;
  }> {
    // 1. 意图分析
    const intent = await this.analyzeIntent(message);

    // 2. 根据意图执行相应工具
    const actions: string[] = [];
    let context: any = {};

    if (intent.type === "query") {
      // 知识检索
      const searchResult = await this.tools.execute("search", {
        query: message
      });
      context.searchResult = searchResult;
      actions.push("知识检索");
    }

    if (intent.confidence < 0.7) {
      // 低置信度,需要澄清
      context.needsClarification = true;
      actions.push("请求澄清");
    }

    // 3. 生成回复
    const response = await this.generateResponse(message, intent, context);

    // 4. 更新记忆
    this.memory.push({ role: "user", content: message });
    this.memory.push({ role: "assistant", content: response });

    // 5. 判断是否需要升级
    const shouldEscalate = intent.type === "escalation" ||
                          (intent.type === "complaint" && intent.confidence > 0.8);

    return {
      response,
      intent,
      actions,
      shouldEscalate
    };
  }

  async createTicket(userInfo: any, issue: string): Promise<string> {
    // 模拟工单创建
    const ticketId = `TKT-${Date.now()}`;
    console.log(`创建工单 ${ticketId}:`, { userInfo, issue });
    return ticketId;
  }
}

8.14 客服 API 接口

typescript 复制代码
// 文件:src/app/api/customer-service/route.ts
import { NextRequest } from "next/server";
import { CustomerServiceAgent } from "@/src/ch08/customer-service-agent";

export const runtime = "edge";

export async function POST(req: NextRequest) {
  const { message, sessionId } = await req.json();

  try {
    const agent = new CustomerServiceAgent();
    const result = await agent.processMessage(message);

    // 如果需要升级,创建工单
    let ticketId = null;
    if (result.shouldEscalate) {
      ticketId = await agent.createTicket(
        { sessionId },
        `用户问题:${message}\n意图:${JSON.stringify(result.intent)}`
      );
    }

    return Response.json({
      success: true,
      data: {
        ...result,
        ticketId
      }
    });
  } catch (error: any) {
    return Response.json({ error: error.message }, { status: 500 });
  }
}

⚙️ 工程化与优化

8.15 Agent 安全与防护

typescript 复制代码
// 文件:src/ch08/security.ts
export class AgentSecurity {
  private blacklist = [
    "rm -rf", "format", "delete", "drop", "exec", "eval",
    "password", "token", "key", "secret"
  ];

  private rateLimit = new Map<string, number[]>();

  validateInput(input: string): { valid: boolean; reason?: string } {
    // 敏感词检查
    for (const word of this.blacklist) {
      if (input.toLowerCase().includes(word)) {
        return { valid: false, reason: `包含敏感词:${word}` };
      }
    }

    // 长度限制
    if (input.length > 1000) {
      return { valid: false, reason: "输入过长" };
    }

    return { valid: true };
  }

  checkRateLimit(userId: string, limit = 10, window = 60000): boolean {
    const now = Date.now();
    const userRequests = this.rateLimit.get(userId) || [];

    // 清理过期请求
    const validRequests = userRequests.filter(time => now - time < window);

    if (validRequests.length >= limit) {
      return false;
    }

    validRequests.push(now);
    this.rateLimit.set(userId, validRequests);
    return true;
  }
}

8.16 性能优化

typescript 复制代码
// 文件:src/ch08/optimization.ts
export class AgentOptimizer {
  private cache = new Map<string, any>();
  private toolCache = new Map<string, any>();

  async cachedToolCall(toolName: string, args: any, ttl = 300000): Promise<any> {
    const key = `${toolName}:${JSON.stringify(args)}`;
    const cached = this.toolCache.get(key);

    if (cached && Date.now() - cached.timestamp < ttl) {
      return cached.result;
    }

    // 实际调用工具
    const result = await this.executeTool(toolName, args);

    this.toolCache.set(key, {
      result,
      timestamp: Date.now()
    });

    return result;
  }

  private async executeTool(toolName: string, args: any): Promise<any> {
    // 工具执行逻辑
    return `Tool ${toolName} executed with ${JSON.stringify(args)}`;
  }

  // 并行工具调用
  async parallelToolCalls(calls: Array<{ tool: string; args: any }>): Promise<any[]> {
    const promises = calls.map(call => this.cachedToolCall(call.tool, call.args));
    return Promise.all(promises);
  }
}

8.17 可观测性与监控

typescript 复制代码
// 文件:src/ch08/monitoring.ts
export interface AgentMetrics {
  requestId: string;
  userId: string;
  agentType: string;
  startTime: number;
  endTime: number;
  toolCalls: Array<{ tool: string; duration: number; success: boolean }>;
  totalTokens: number;
  error?: string;
}

export class AgentMonitor {
  private metrics: AgentMetrics[] = [];

  startRequest(requestId: string, userId: string, agentType: string): AgentMetrics {
    const metric: AgentMetrics = {
      requestId,
      userId,
      agentType,
      startTime: Date.now(),
      endTime: 0,
      toolCalls: [],
      totalTokens: 0
    };

    this.metrics.push(metric);
    return metric;
  }

  recordToolCall(requestId: string, tool: string, duration: number, success: boolean) {
    const metric = this.metrics.find(m => m.requestId === requestId);
    if (metric) {
      metric.toolCalls.push({ tool, duration, success });
    }
  }

  endRequest(requestId: string, totalTokens: number, error?: string) {
    const metric = this.metrics.find(m => m.requestId === requestId);
    if (metric) {
      metric.endTime = Date.now();
      metric.totalTokens = totalTokens;
      metric.error = error;
    }
  }

  getMetrics(): AgentMetrics[] {
    return this.metrics;
  }

  getAverageResponseTime(): number {
    const completed = this.metrics.filter(m => m.endTime > 0);
    if (completed.length === 0) return 0;

    const total = completed.reduce((sum, m) => sum + (m.endTime - m.startTime), 0);
    return total / completed.length;
  }
}

🧪 测试与评估

8.18 Agent 测试框架

typescript 复制代码
// 文件:src/ch08/testing.ts
export interface TestCase {
  id: string;
  input: string;
  expectedIntent?: string;
  expectedTools?: string[];
  expectedResponse?: string;
  maxSteps?: number;
}

export class AgentTester {
  constructor(private agent: any) {}

  async runTest(testCase: TestCase): Promise<{
    passed: boolean;
    actual: any;
    expected: any;
    duration: number;
  }> {
    const startTime = Date.now();

    try {
      const result = await this.agent.processMessage(testCase.input);
      const duration = Date.now() - startTime;

      const passed = this.evaluateResult(result, testCase);

      return {
        passed,
        actual: result,
        expected: testCase,
        duration
      };
    } catch (error) {
      return {
        passed: false,
        actual: { error: error.message },
        expected: testCase,
        duration: Date.now() - startTime
      };
    }
  }

  private evaluateResult(actual: any, expected: TestCase): boolean {
    if (expected.expectedIntent && actual.intent?.type !== expected.expectedIntent) {
      return false;
    }

    if (expected.expectedTools) {
      const actualTools = actual.actions || [];
      for (const tool of expected.expectedTools) {
        if (!actualTools.includes(tool)) {
          return false;
        }
      }
    }

    if (expected.expectedResponse && !actual.response.includes(expected.expectedResponse)) {
      return false;
    }

    return true;
  }

  async runTestSuite(testCases: TestCase[]): Promise<{
    total: number;
    passed: number;
    failed: number;
    results: any[];
  }> {
    const results = await Promise.all(
      testCases.map(testCase => this.runTest(testCase))
    );

    const passed = results.filter(r => r.passed).length;
    const failed = results.length - passed;

    return {
      total: results.length,
      passed,
      failed,
      results
    };
  }
}

📚 延伸链接

  • LangChain.js Agent 文档:https://js.langchain.com/docs/use_cases/autonomous_agents/
  • LangGraph 状态图:https://langchain-ai.github.io/langgraph/
  • ReAct 论文:https://arxiv.org/abs/2210.03629
  • Agent 安全最佳实践:https://github.com/langchain-ai/langchain/tree/master/libs/langchain/langchain/agents

✅ 本章小结

  • 构建了完整的 Agent 系统:工具定义、ReAct 循环、多 Agent 协作
  • 实现了代码执行环境与数据分析能力
  • 在 Next.js 中提供了完整的 Agent 对话界面
  • 实战了智能客服 Agent,具备意图识别、知识检索、工单创建能力
  • 建立了安全、性能、可观测性的工程化体系

🎯 下章预告

下一章《LangGraph 状态图与工作流编排》中,我们将:

  • 深入 LangGraph 状态图设计模式
  • 构建复杂的工作流编排系统
  • 实现条件分支、循环、并行处理
  • 集成外部 API 与数据库操作

最后感谢阅读!欢迎关注我,微信公众号:《鲫小鱼不正经》。欢迎点赞、收藏、关注,一键三连!!!

相关推荐
代码搬运媛5 小时前
Jest 测试框架详解与实现指南
前端
counterxing5 小时前
Agent 跑起来之后,难的是复用、观测和评测
node.js·agent·ai编程
uccs5 小时前
大模型底层机制与Agent开发
agent·ai编程·claude
counterxing6 小时前
我把 Codex 里的 Skills 做成了一个 MCP,还支持分享
前端·agent·ai编程
wangqiaowq6 小时前
windows下nginx的安装
linux·服务器·前端
夜雪闻竹6 小时前
vectra 向量索引文件损坏怎么办
ai编程·向量·vectra
ZzT6 小时前
Harness 到底指什么
openai·ai编程·claude
之歆7 小时前
DAY_12JavaScript DOM 完全指南(二):实战与性能篇
开发语言·前端·javascript·ecmascript
宅小年7 小时前
AI 创业最危险的地方:太容易做出来
openai·ai编程·claude
发现一只大呆瓜7 小时前
Vite凭什么这么快?3分钟带你彻底搞懂 Vite 热更新的幕后黑手
前端·面试·vite