第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 与数据库操作
最后感谢阅读!欢迎关注我,微信公众号:
《鲫小鱼不正经》
。欢迎点赞、收藏、关注,一键三连!!!