从零搭建 AI Agent:LLM + Tool + 异步并发的实战之路

0 阅读5分钟

从零搭建 AI Agent:LLM + Tool + 异步并发的实战之路

大家好,今天想和大家分享我最近在学习 AI Agent 开发过程中的一些思考和实战代码。我们从最简单的异步概念开始,一步步走到一个能调用工具的 LangChain Agent。

13fb0c8218d0de42afa80ebe8e4adc8a.jpg


一、为什么 LLM 本身不够用?

很多人以为 AI Agent 就是直接调用大模型接口,但实际上,裸的 LLM 有几个「硬伤」:

问题解决方案
无状态,记不住上次聊了什么Memory 模块(数据库、Redis 等)
不能访问网页、操作文件Tool Use 模块
不知道企业内部私有文档RAG 模块
不知道预训练之后的新信息MCP / Tool
复杂任务需要多步骤执行Skills(技能编排)

所以,Agent = LLM + Memory + Tool + RAG + MCP + Skills。就是给大模型装上「手和脚」,让它不再只是一个「嘴炮选手」。

aa0d76fb6a792a3a09ab047ac0c28381.png


二、磨刀不误砍柴工:理解异步并发

在动手写 Agent 之前,有一个绕不开的基础概念——异步并发。Agent 经常需要同时调用多个工具,如果串行执行,用户体验会非常差。

来看例子:

function getWeather() {
    return new Promise((resolve) => {
        setTimeout(() => {
            resolve({ temp: 38, conditions: 'Sunny with Clouds' })
        }, 2000)  // 模拟 2 秒的网络请求
    })
}

function getTweets() {
    return new Promise((resolve) => {
        setTimeout(() => {
            resolve(['I like cake', 'BBQ is good too!'])
        }, 500)  // 模拟 0.5 秒的网络请求
    })
}

❌ 串行写法(总耗时 ≈ 2500ms)

const weatherData = await getWeather();   // 等 2000ms
const tweetsData = await getTweets();     // 再等 500ms
// 总耗时:2000 + 500 = 2500ms

✅ 并行写法(总耗时 ≈ 2000ms)

const [weatherData, tweetsData] =
    await Promise.all([getWeather(), getTweets()]);
// 总耗时:max(2000, 500) = 2000ms,节省了 500ms

核心知识点:

  • Promise 有三种状态:pending → fulfilled 或 rejected,一旦确定就不可逆
  • Promise.all() 并行执行多个 Promise,等所有都完成才返回,结果的顺序和传入顺序一致
  • await 是 ES8 提供的语法糖,让异步代码看起来像同步代码

这个基础很重要,因为后面 Agent 调用多个 Tool 时,我们会用 Promise.all 来并发执行它们,大幅提升性能。


三、Hello LangChain:第一行代码

LangChain 是一个 LLM 开发框架,它的核心价值是统一兼容各家大模型。来看看最简版的调用:

import { ChatOpenAI } from '@langchain/openai';
import 'dotenv/config';

const model = new ChatOpenAI({
    modelName: 'deepseek-v4-flash',
    apiKey: process.env.DEEPSEEK_API_KEY,
    configuration: {
        baseURL: 'https://api.deepseek.com/v1',
    }
});

const response = await model.invoke('棍王杯台球比赛应该设什么奖励?');
console.log(response.content);

虽然底层用的是 DeepSeek 的 API,但通过 LangChain 的 ChatOpenAI 统一接口,调用方式和 OpenAI 完全一致——这就是 LangChain 的兼容层价值


四、给 LLM 装上「手」:Tool 工具调用

这是整个 Agent 开发中最核心的一步。来看看完整实现:

4.1 定义一个工具

import { tool } from '@langchain/core/tools';
import { z } from 'zod';

const readFileTool = tool(
    async ({ filePath }) => {          // ① 功能函数
        const content = await fs.readFile(filePath, 'utf-8');
        console.log(`[工具调用] read_file(${filePath}) 成功读取 ${content.length} 字节`);
        return content;
    },
    {                                   // ② 工具描述对象
        name: 'read_file',
        description: `用此工具来读取文件内容,当用户要求读取文件、
        查看代码、分析文件内容时,调用此工具。`,
        schema: z.object({              // ③ Zod 参数约束
            filePath: z.string().describe('要读取的文件路径')
        })
    }
);

一个 Tool 由两部分组成:

  • 处理函数:实际执行逻辑(读文件、调 API、写数据库……)
  • 描述对象:告诉 LLM「我是什么、我能干什么、你需要给我什么参数」

4.2 绑定工具到模型

const modelWithTools = model.bindTools(tools);

这一行代码把工具列表注册到模型中。之后当用户的问题需要用到工具时,LLM 不会直接生成答案,而是在响应中返回 tool_calls

{
  "tool_calls": [
    {
      "id": "call_abc123",
      "name": "read_file",
      "arguments": { "filePath": "tool.mjs" }
    }
  ]
}

关键设计:LLM 有「自知之明」——当它判断需要调用工具时,会停下来,把工具调用信息返回给开发者,由开发者实际执行工具函数,再把结果返回给 LLM 继续处理。这个 id 字段就是用来关联「哪个工具调用」对应「哪个执行结果」的。

4.3 多步对话流程

const messages = [
    new SystemMessage(`你是一个代码助手,可以使用工具读取文件并解释代码。...`),
    new HumanMessage('请读取 tool.mjs 文件内容并解释代码'),
];

let response = await modelWithTools.invoke(messages);
messages.push(response);  // 将 LLM 响应(含 tool_calls)加入历史

// 这里还需要:
// 1. 解析 response.tool_calls
// 2. 并发执行工具(Promise.all!)
// 3. 将工具结果以 ToolMessage 形式加入 messages
// 4. 再次调用 LLM 生成最终回答

五、Agent 的工作全流程

把上面的内容串起来,一个完整的 Agent 工作流程是这样的:

用户提出任务(Prompt)
        ↓
   LLM 规划/推理(Planning & Reasoning)
        ↓
  ┌─ 要不要加载 Memory? ──→ 加载历史对话上下文
  ├─ 要不要调用 Tool?  ──→ 并行/串行执行工具(Promise.all 加速)
  ├─ 要不要查 RAG?    ──→ 检索内部知识库 → 注入 Prompt Template
  └─ 要不要用 Skill?  ──→ 执行多步骤技能编排
        ↓
   综合所有信息,生成最终回复
        ↓
     返回给用户

六、技术栈全景图

我们选择的是一套 JavaScript/TypeScript 全栈 AI Agent 技术栈:

层级技术作用
后端框架NestJS提供企业级架构、依赖注入
Agent 开发LangChain.js单智能体开发(LLM + Tool + Memory)
多智能体LangGraph多智能体编排、状态图
工具协议MCP第三方工具标准化接入
知识检索RAG向量数据库 + 私有知识库
参数校验ZodTool 参数类型约束

七、总结

回顾整个学习路径:

  1. 异步基础Promise + Promise.all 是 Agent 并发调用工具的性能基石
  2. LangChain 统一接口:一套代码兼容 OpenAI、DeepSeek 等各家模型
  3. Tool 机制tool() 函数定义 + model.bindTools() 注册,让 LLM 从「能说」变成「能做」
  4. Agent 思维:LLM 负责思考和规划,Tool 负责执行,Memory 负责记忆,RAG 负责知识——各司其职

一个知道内部知识、能思考规划、能帮你做事情的「增强版大模型」,就是 Agent。而 LangChain 让这个过程变得工程化、可维护。

学完这些,我们即将打造自己的第一个高性能 Agent! 🚀