Agent 工程化 的核心

5 阅读4分钟

当前 Agent 工程化 的核心。我通过一个完整的代码示例,把它们串起来讲清楚。


一、整体架构图(先有个印象)

text

用户输入
   │
   ▼
┌─────────────────────────────────────────────┐
│                    Agent                      │
│  ┌─────────────────────────────────────────┐ │
│  │           历史消息 (Messages)            │ │
│  │  [{role:user, content}, {role:assistant}]│ │
│  └─────────────────────────────────────────┘ │
│                    │                          │
│  ┌─────────────────────────────────────────┐ │
│  │            工作流 (Workflow)             │ │
│  │  Plan → Execute → Observe → Loop        │ │
│  └─────────────────────────────────────────┘ │
│                    │                          │
│  ┌──────────────┬──────────────┐             │
│  │  工具调用    │   子Agent     │    Skills  │
│  │  (Tools)     │ (Sub-Agent)   │  (能力集)  │
│  └──────────────┴──────────────┘             │
└─────────────────────────────────────────────┘

二、完整代码示例(可直接运行)

用 TypeScript + Bun 实现一个能做数学计算和天气查询的简单 Agent:

typescript

// agent.ts - 一个完整的 Agent 实现

// ==================== 1. 历史消息管理 ====================
interface Message {
  role: 'user' | 'assistant' | 'tool';
  content: string;
  toolCallId?: string;
  timestamp: number;
}

class MessageHistory {
  private messages: Message[] = [];
  private maxTokens: number = 4000;

  add(message: Message) {
    this.messages.push(message);
    this.trimIfNeeded();
  }

  get() {
    return this.messages;
  }

  getForLLM() {
    // 返回 LLM 需要的格式,只保留最近的消息
    return this.messages.slice(-20).map(m => ({
      role: m.role,
      content: m.content
    }));
  }

  private trimIfNeeded() {
    // 简化版:超过 50 条就删除一半
    if (this.messages.length > 50) {
      this.messages = this.messages.slice(-25);
    }
  }
}

// ==================== 2. 工具定义与调用 ====================
interface Tool {
  name: string;
  description: string;
  parameters: Record<string, any>;
  execute: (args: any) => Promise<string>;
}

// 工具1:计算器
const calculatorTool: Tool = {
  name: 'calculator',
  description: '执行数学计算,支持 + - * / 和 sqrt',
  parameters: {
    type: 'object',
    properties: {
      expression: { type: 'string', description: '数学表达式,如 "2+3*4"' }
    },
    required: ['expression']
  },
  execute: async (args) => {
    try {
      // 安全计算(生产环境请用 math.js 等库)
      const result = eval(args.expression);
      return `计算结果: ${result}`;
    } catch (e) {
      return `计算错误: ${e.message}`;
    }
  }
};

// 工具2:模拟天气查询
const weatherTool: Tool = {
  name: 'get_weather',
  description: '查询指定城市的天气',
  parameters: {
    type: 'object',
    properties: {
      city: { type: 'string', description: '城市名称' }
    },
    required: ['city']
  },
  execute: async (args) => {
    // 模拟 API 调用
    const weathers = {
      '北京': '晴天 25°C',
      '上海': '多云 22°C',
      '深圳': '阵雨 28°C'
    };
    return weathers[args.city] || `${args.city} 天气: 晴 20°C`;
  }
};

// ==================== 3. 子 Agent(专门处理特定任务)====================
class SubAgent {
  name: string;
  description: string;
  private handler: (input: string) => Promise<string>;

  constructor(name: string, description: string, handler: (input: string) => Promise<string>) {
    this.name = name;
    this.description = description;
    this.handler = handler;
  }

  async run(input: string): Promise<string> {
    console.log(`  [子Agent:${this.name}] 处理: ${input}`);
    return this.handler(input);
  }
}

// 创建两个子 Agent
const mathSubAgent = new SubAgent(
  'math-expert',
  '专门处理复杂数学问题',
  async (input) => {
    // 模拟复杂计算
    await Bun.sleep(500); // 假装在计算
    return `【数学专家】计算结果: ${input.replace('计算', '').trim()} = 42`;
  }
);

const weatherSubAgent = new SubAgent(
  'weather-expert', 
  '专门处理天气相关问题',
  async (input) => {
    await Bun.sleep(300);
    const city = input.match(/[北京上海深圳广州]+/)?.[0] || '未知';
    return `【天气专家】${city},温度适中,建议出门带伞`;
  }
);

// ==================== 4. Skill(可复用的能力模块)====================
interface Skill {
  name: string;
  description: string;
  execute: (context: any) => Promise<any>;
}

const loggingSkill: Skill = {
  name: 'logging',
  description: '记录 Agent 的执行日志',
  execute: async (context) => {
    console.log(`[LOG] ${new Date().toISOString()} - ${context.action}`);
    return { logged: true };
  }
};

const memorySkill: Skill = {
  name: 'memory',
  description: '记住用户的重要偏好',
  execute: async (context) => {
    // 简化版:存到全局 Map
    if (context.preference) {
      userPreferences.set(context.userId, context.preference);
    }
    return { remembered: true };
  }
};

const userPreferences = new Map<string, any>();

// ==================== 5. 主 Agent(核心工作流)====================
class SimpleAgent {
  private tools: Map<string, Tool> = new Map();
  private subAgents: Map<string, SubAgent> = new Map();
  private skills: Skill[] = [];
  private messageHistory: MessageHistory;

  constructor() {
    this.messageHistory = new MessageHistory();
    this.registerDefaultTools();
  }

  // 注册工具
  registerTool(tool: Tool) {
    this.tools.set(tool.name, tool);
    console.log(`📦 注册工具: ${tool.name}`);
  }

  // 注册子 Agent
  registerSubAgent(agent: SubAgent) {
    this.subAgents.set(agent.name, agent);
    console.log(`🤖 注册子Agent: ${agent.name}`);
  }

  // 注册 Skill
  registerSkill(skill: Skill) {
    this.skills.push(skill);
    console.log(`⚡ 注册Skill: ${skill.name}`);
  }

  private registerDefaultTools() {
    this.registerTool(calculatorTool);
    this.registerTool(weatherTool);
    this.registerSubAgent(mathSubAgent);
    this.registerSubAgent(weatherSubAgent);
    this.registerSkill(loggingSkill);
    this.registerSkill(memorySkill);
  }

  // ========== 核心工作流 ==========
  async run(userInput: string): Promise<string> {
    console.log('\n' + '='.repeat(50));
    console.log(`📝 用户: ${userInput}`);
    console.log('='.repeat(50));

    // Step 1: 添加用户消息到历史
    this.messageHistory.add({
      role: 'user',
      content: userInput,
      timestamp: Date.now()
    });

    // Step 2: 意图识别(简化版,实际应该用 LLM)
    const intent = this.analyzeIntent(userInput);
    console.log(`🎯 识别意图: ${intent.type}`);

    // Step 3: 执行 Skills(前置)
    for (const skill of this.skills) {
      await skill.execute({ action: intent.type, userId: 'default' });
    }

    // Step 4: 根据意图分发处理
    let result: string;
    
    if (intent.type === 'calculation' && intent.tool) {
      // 直接调用工具
      result = await this.callTool(intent.tool, intent.args);
    } 
    else if (intent.type === 'weather') {
      // 可以调用工具或子 Agent,这里演示委托给子 Agent
      result = await this.delegateToSubAgent('weather-expert', userInput);
    }
    else if (intent.type === 'complex_math') {
      result = await this.delegateToSubAgent('math-expert', userInput);
    }
    else {
      // 普通对话
      result = await this.generateResponse(userInput);
    }

    // Step 5: 保存助手回复到历史
    this.messageHistory.add({
      role: 'assistant',
      content: result,
      timestamp: Date.now()
    });

    console.log(`🤖 助手: ${result}`);
    return result;
  }

  // 意图分析(简化版,实际应该调用 LLM)
  private analyzeIntent(input: string): {
    type: 'calculation' | 'weather' | 'complex_math' | 'chat';
    tool?: string;
    args?: any;
  } {
    // 计算器意图
    if (input.includes('+') || input.includes('-') || input.includes('*') || input.includes('/') || input.includes('计算')) {
      const match = input.match(/[\d\s+-*/()]+/);
      if (match && match[0].trim()) {
        return { type: 'calculation', tool: 'calculator', args: { expression: match[0] } };
      }
    }
    
    // 天气意图
    if (input.includes('天气')) {
      return { type: 'weather' };
    }
    
    // 复杂数学
    if (input.includes('方程') || input.includes('积分') || input.includes('导数')) {
      return { type: 'complex_math' };
    }
    
    return { type: 'chat' };
  }

  // 调用工具
  private async callTool(toolName: string, args: any): Promise<string> {
    const tool = this.tools.get(toolName);
    if (!tool) return `工具 ${toolName} 不存在`;
    
    console.log(`🔧 调用工具: ${toolName}`, args);
    return await tool.execute(args);
  }

  // 委托给子 Agent
  private async delegateToSubAgent(agentName: string, input: string): Promise<string> {
    const agent = this.subAgents.get(agentName);
    if (!agent) return `子Agent ${agentName} 不存在`;
    
    console.log(`🔄 委托给子Agent: ${agentName}`);
    return await agent.run(input);
  }

  // 生成回复(简化版,实际应该调用 LLM)
  private async generateResponse(input: string): Promise<string> {
    if (input.includes('你好') || input.includes('嗨')) {
      return '你好!我是智能助手,可以帮你计算、查天气等。试试说"计算 2+3"或"北京天气"';
    }
    return `收到: "${input}"。我是一个简单Agent,能处理计算和天气查询。`;
  }

  // 查看历史消息
  showHistory() {
    console.log('\n📜 历史消息:');
    for (const msg of this.messageHistory.get()) {
      console.log(`  [${msg.role}] ${msg.content.slice(0, 50)}`);
    }
  }
}

// ==================== 6. 运行演示 ====================
async function main() {
  console.log('🚀 启动 Simple Agent...\n');
  
  const agent = new SimpleAgent();
  
  console.log('\n' + '🌟 Agent 已就绪,开始对话...\n');
  
  // 测试各种场景
  await agent.run('你好,你是谁?');
  await agent.run('计算 15 + 27');
  await agent.run('北京天气怎么样?');
  await agent.run('帮我解方程 x^2 = 4');
  
  // 查看历史消息
  agent.showHistory();
  
  console.log('\n✅ 演示完成');
}

// 运行
main().catch(console.error);

三、用 Bun 运行

bash

# 安装 bun(如果还没装)
curl -fsSL https://bun.sh/install | bash

# 运行 Agent
bun run agent.ts

输出示例:

text

🚀 启动 Simple Agent...
📦 注册工具: calculator
📦 注册工具: get_weather
🤖 注册子Agent: math-expert
🤖 注册子Agent: weather-expert
⚡ 注册Skill: logging
⚡ 注册Skill: memory

==================================================
📝 用户: 计算 15 + 27
==================================================
🎯 识别意图: calculation
[LOG] 2026-04-04T10:30:00.000Z - calculation
🔧 调用工具: calculator { expression: "15+27" }
🤖 助手: 计算结果: 42

==================================================
📝 用户: 北京天气怎么样?
==================================================
🎯 识别意图: weather
[LOG] 2026-04-04T10:30:01.000Z - weather
🔄 委托给子Agent: weather-expert
  [子Agent:weather-expert] 处理: 北京天气怎么样?
🤖 助手: 【天气专家】北京,温度适中,建议出门带伞

四、核心概念对照表

概念在这个例子中的体现作用
历史消息MessageHistory 类保留对话上下文,支持多轮交互
工作流run() 方法中的 5 个步骤意图识别→技能执行→工具/子Agent→返回结果
工具调用calculatorToolweatherToolAgent 通过工具执行具体操作
子 AgentmathSubAgentweatherSubAgent专门化处理,可以嵌套调用
SkillloggingSkillmemorySkill可复用的横切能力,可在工作流中自动执行
Bun 一体化一个文件搞定 TypeScript 编译+运行不需要 tsc + node,直接 bun run

五、关于 Bun 的亮点

你说得对,Bun 的运行时+构建一体化确实很棒:

bash

# 传统 Node + TypeScript 需要:
npm install -g typescript ts-node
tsc agent.ts && node agent.js

# Bun 只需要:
bun run agent.ts  # 直接运行,自动编译

Bun 还内置了:

  • 包管理器(比 npm/yarn/pnpm 快很多)
  • 测试运行器
  • 打包器(bun build)
  • 原生支持 JSX、TS

运行这个例子后,你会直观感受到一个 Agent 是如何组织起来的。想深入了解哪一块?比如:

  • 如何接入真实的 LLM(OpenAI/Claude API)?
  • 如何处理更复杂的工作流(循环、重试、并行)?
  • 工具调用的 function calling 具体怎么对接?