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 与数据库操作

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

相关推荐
蓝胖子的多啦A梦2 小时前
【前端】VUE+Element UI项目 页面自适应横屏、竖屏、大屏、PDA及手机等适配方案
前端·javascript·elementui·html·前端页面适配
掘金安东尼2 小时前
前端周刊431期(2025年9月8日–9月14日)
前端·javascript·github
风若飞2 小时前
npm ERR! code CERT_HAS_EXPIRED
前端·npm·node.js
北城笑笑2 小时前
NodeJS 8 ,从 0 到 1:npm 包发布与更新全流程指南( 含多场景适配与踩坑总结 )
前端·npm·node.js·github
Mike_jia2 小时前
如何找回Harbor密码
前端
码码哈哈0.02 小时前
npm : 无法加载文件 C:\Program Files\nodejs\npm.ps1,因为在此系统上禁止运行脚
前端·npm·node.js
浩男孩2 小时前
🍀简简单单结合 hooks 优雅使用弹窗🚀🚀
前端
江城开朗的豌豆2 小时前
Axios拦截器:给你的请求加上"双保险"!
前端·javascript·react.js
晓得迷路了2 小时前
栗子前端技术周刊第 98 期 - NPM 生态遭受攻击、Rspack 1.5.3、Storybook 10 beta...
前端·javascript·css