从零构建 AI Agent:完整指南
一篇完整的 AI Agent 构建教程,涵盖准备工作、核心代码实现、以及部署上线的全流程。
作者:bani
aiagenttutoriallangchainopenaideployment
从零构建 AI Agent:完整指南
本文将手把手教你如何构建一个功能完整的 AI Agent,从环境准备到最终部署上线,一步步带你掌握智能体开发的核心技术。
目录
什么是 AI Agent
AI Agent(智能体)是一种能够自主感知环境、做出决策并执行行动的 AI 系统。与传统的对话式 AI 不同,Agent 具备以下核心能力:
| 能力 | 描述 | 示例 |
|---|---|---|
| 感知 | 理解用户输入和环境状态 | 解析自然语言、读取文件 |
| 推理 | 基于上下文做出决策 | 选择合适的工具、规划任务步骤 |
| 行动 | 执行具体操作 | 调用 API、执行代码、操作文件 |
| 学习 | 从反馈中改进 | 记忆历史对话、优化策略 |
Agent vs Chatbot
传统 Chatbot: 用户输入 → LLM处理 → 文本输出
AI Agent: 用户输入 → LLM推理 → 工具调用 → 执行结果 → LLM总结 → 输出
↑ ↓
└────────── 反馈循环 ─────────┘
准备工作
1. 环境配置
系统要求
- Node.js >= 18.0 或 Python >= 3.9
- 稳定的网络连接(用于调用 LLM API)
- 至少 4GB 可用内存
创建项目目录
# 创建项目
mkdir my-ai-agent
cd my-ai-agent
# 初始化 Node.js 项目
npm init -y
# 或使用 Python
python -m venv venv
source venv/bin/activate # macOS/Linux
# venv\Scripts\activate # Windows2. 安装依赖
Node.js 版本(推荐)
npm install openai langchain @langchain/openai zod dotenv
npm install -D typescript @types/node ts-nodePython 版本
pip install openai langchain langchain-openai python-dotenv3. 获取 API Key
目前主流的 LLM 提供商:
| 提供商 | 获取地址 | 特点 |
|---|---|---|
| OpenAI | platform.openai.com | GPT-4o,能力最强 |
| Anthropic | console.anthropic.com | Claude,推理能力强 |
| DeepSeek | platform.deepseek.com | 高性价比,中文友好 |
| aistudio.google.com | Gemini,多模态强 |
配置环境变量
创建 .env 文件:
# OpenAI
OPENAI_API_KEY=sk-your-openai-key
# 或 DeepSeek (兼容 OpenAI 接口)
OPENAI_API_KEY=sk-your-deepseek-key
OPENAI_BASE_URL=https://api.deepseek.com核心架构设计
一个完整的 Agent 系统包含以下组件:
┌─────────────────────────────────────────────────────────────┐
│ AI Agent 架构 │
├─────────────────────────────────────────────────────────────┤
│ │
│ ┌──────────┐ ┌─────────────┐ ┌──────────────────┐ │
│ │ 用户输入 │───▶│ 控制器 │───▶│ LLM (大脑) │ │
│ └──────────┘ │ Controller │ │ GPT-4 / Claude │ │
│ └──────┬──────┘ └────────┬─────────┘ │
│ │ │ │
│ ▼ ▼ │
│ ┌─────────────────┐ ┌────────────────┐ │
│ │ 记忆系统 │ │ 工具集合 │ │
│ │ Memory Store │ │ Tool Registry │ │
│ └─────────────────┘ └────────────────┘ │
│ │ │
│ ┌────────────────────┴───────┐ │
│ ▼ ▼ ▼ ▼ │
│ ┌────────┐ ┌────────┐ ┌────────┐ │
│ │搜索工具 │ │计算工具 │ │文件工具 │ │
│ └────────┘ └────────┘ └────────┘ │
│ │
└─────────────────────────────────────────────────────────────┘
核心组件说明
- Controller(控制器):协调各组件,处理 Agent 的执行循环
- LLM(大语言模型):Agent 的"大脑",负责理解和决策
- Tools(工具集):Agent 可调用的能力扩展
- Memory(记忆):存储对话历史和上下文
代码实现
项目结构
my-ai-agent/
├── src/
│ ├── index.ts # 入口文件
│ ├── agent.ts # Agent 核心类
│ ├── tools/
│ │ ├── index.ts # 工具注册
│ │ ├── search.ts # 搜索工具
│ │ ├── calculator.ts # 计算工具
│ │ └── file.ts # 文件工具
│ ├── memory/
│ │ └── store.ts # 记忆存储
│ └── config.ts # 配置文件
├── .env
├── package.json
└── tsconfig.json
Step 1: 配置文件
tsconfig.json
{
"compilerOptions": {
"target": "ES2022",
"module": "NodeNext",
"moduleResolution": "NodeNext",
"strict": true,
"esModuleInterop": true,
"outDir": "dist",
"rootDir": "src"
},
"include": ["src/**/*"]
}src/config.ts
import { config } from "dotenv";
config();
export const CONFIG = {
openai: {
apiKey: process.env.OPENAI_API_KEY!,
baseURL: process.env.OPENAI_BASE_URL || "https://api.openai.com/v1",
model: process.env.OPENAI_MODEL || "gpt-4o",
},
agent: {
maxIterations: 10, // 最大执行循环次数
verbose: true, // 是否输出详细日志
},
} as const;Step 2: 定义工具
src/tools/calculator.ts
import { z } from "zod";
// 工具定义接口
export interface Tool {
name: string;
description: string;
parameters: z.ZodType<any>;
execute: (params: any) => Promise<string>;
}
// 计算器工具
export const calculatorTool: Tool = {
name: "calculator",
description: "执行数学计算。支持加减乘除、幂运算、三角函数等。",
parameters: z.object({
expression: z.string().describe("要计算的数学表达式,例如: 2 + 3 * 4"),
}),
execute: async ({ expression }) => {
try {
// 安全的数学表达式计算
const sanitized = expression.replace(/[^0-9+\-*/().%\s]/g, "");
const result = Function(`"use strict"; return (${sanitized})`)();
return `计算结果: ${expression} = ${result}`;
} catch (error) {
return `计算错误: ${(error as Error).message}`;
}
},
};src/tools/search.ts
import { z } from "zod";
import { Tool } from "./calculator";
// 模拟搜索工具(实际项目中可接入真实搜索 API)
export const searchTool: Tool = {
name: "web_search",
description: "搜索网络获取实时信息。用于回答需要最新数据的问题。",
parameters: z.object({
query: z.string().describe("搜索关键词"),
}),
execute: async ({ query }) => {
// 这里模拟搜索结果,实际可接入 Serper/Tavily 等搜索 API
console.log(`🔍 正在搜索: ${query}`);
// 模拟网络延迟
await new Promise((r) => setTimeout(r, 500));
return `搜索"${query}"的结果:
1. 相关文章: 《${query}详解》- 介绍了核心概念和应用场景
2. 官方文档: 提供了完整的使用指南
3. 社区讨论: 开发者分享的最佳实践`;
},
};src/tools/file.ts
import { z } from "zod";
import * as fs from "fs/promises";
import * as path from "path";
import { Tool } from "./calculator";
// 文件读取工具
export const readFileTool: Tool = {
name: "read_file",
description: "读取指定路径的文件内容",
parameters: z.object({
filePath: z.string().describe("文件的相对或绝对路径"),
}),
execute: async ({ filePath }) => {
try {
const content = await fs.readFile(filePath, "utf-8");
return `文件内容:\n${content}`;
} catch (error) {
return `读取文件失败: ${(error as Error).message}`;
}
},
};
// 文件写入工具
export const writeFileTool: Tool = {
name: "write_file",
description: "将内容写入指定文件",
parameters: z.object({
filePath: z.string().describe("目标文件路径"),
content: z.string().describe("要写入的内容"),
}),
execute: async ({ filePath, content }) => {
try {
await fs.mkdir(path.dirname(filePath), { recursive: true });
await fs.writeFile(filePath, content, "utf-8");
return `✅ 文件已成功写入: ${filePath}`;
} catch (error) {
return `写入文件失败: ${(error as Error).message}`;
}
},
};src/tools/index.ts
import { Tool } from "./calculator";
import { calculatorTool } from "./calculator";
import { searchTool } from "./search";
import { readFileTool, writeFileTool } from "./file";
// 工具注册表
export const tools: Tool[] = [
calculatorTool,
searchTool,
readFileTool,
writeFileTool,
];
// 按名称获取工具
export function getToolByName(name: string): Tool | undefined {
return tools.find((t) => t.name === name);
}
// 生成工具描述(用于 System Prompt)
export function generateToolDescriptions(): string {
return tools.map((t) => `- ${t.name}: ${t.description}`).join("\n");
}Step 3: 记忆系统
src/memory/store.ts
interface Message {
role: "user" | "assistant" | "system" | "tool";
content: string;
toolCallId?: string;
name?: string;
}
export class MemoryStore {
private messages: Message[] = [];
private maxMessages: number = 50;
constructor(systemPrompt?: string) {
if (systemPrompt) {
this.messages.push({ role: "system", content: systemPrompt });
}
}
/**
* 添加消息到记忆
*/
add(message: Message): void {
this.messages.push(message);
// 保持记忆在限制范围内(保留 system prompt)
if (this.messages.length > this.maxMessages) {
const systemMsg = this.messages.find((m) => m.role === "system");
this.messages = systemMsg
? [systemMsg, ...this.messages.slice(-this.maxMessages + 1)]
: this.messages.slice(-this.maxMessages);
}
}
/**
* 获取所有消息
*/
getAll(): Message[] {
return [...this.messages];
}
/**
* 获取最近 N 条消息
*/
getRecent(n: number): Message[] {
return this.messages.slice(-n);
}
/**
* 清空记忆(保留 system prompt)
*/
clear(): void {
const systemMsg = this.messages.find((m) => m.role === "system");
this.messages = systemMsg ? [systemMsg] : [];
}
/**
* 获取对话摘要(用于压缩长对话)
*/
getSummary(): string {
const userMsgs = this.messages.filter((m) => m.role === "user");
return `对话历史包含 ${userMsgs.length} 条用户消息`;
}
}Step 4: Agent 核心实现
src/agent.ts
import OpenAI from "openai";
import { CONFIG } from "./config";
import { tools, getToolByName, generateToolDescriptions } from "./tools";
import { MemoryStore } from "./memory/store";
import { Tool } from "./tools/calculator";
// Agent 系统提示词
const SYSTEM_PROMPT = `你是一个智能助手,能够帮助用户完成各种任务。
你可以使用以下工具:
${generateToolDescriptions()}
使用工具时的注意事项:
1. 仔细分析用户需求,选择合适的工具
2. 如果需要多步操作,逐步执行并汇总结果
3. 如果无法完成任务,诚实说明原因
4. 计算类问题务必使用 calculator 工具,不要自己计算
始终用中文回复用户。`;
export class Agent {
private client: OpenAI;
private memory: MemoryStore;
private maxIterations: number;
private verbose: boolean;
constructor() {
this.client = new OpenAI({
apiKey: CONFIG.openai.apiKey,
baseURL: CONFIG.openai.baseURL,
});
this.memory = new MemoryStore(SYSTEM_PROMPT);
this.maxIterations = CONFIG.agent.maxIterations;
this.verbose = CONFIG.agent.verbose;
}
/**
* 将自定义工具格式转换为 OpenAI 格式
*/
private formatToolsForOpenAI(): OpenAI.ChatCompletionTool[] {
return tools.map((tool) => ({
type: "function" as const,
function: {
name: tool.name,
description: tool.description,
parameters: this.zodToJsonSchema(tool.parameters),
},
}));
}
/**
* 简化的 Zod Schema 转 JSON Schema
*/
private zodToJsonSchema(schema: any): object {
// 简化处理,实际项目建议使用 zod-to-json-schema
return {
type: "object",
properties: {
expression: { type: "string" },
query: { type: "string" },
filePath: { type: "string" },
content: { type: "string" },
},
};
}
/**
* 执行工具调用
*/
private async executeTool(
name: string,
args: Record<string, any>
): Promise<string> {
const tool = getToolByName(name);
if (!tool) {
return `错误: 未找到工具 "${name}"`;
}
if (this.verbose) {
console.log(`🔧 调用工具: ${name}`);
console.log(` 参数: ${JSON.stringify(args)}`);
}
try {
const result = await tool.execute(args);
if (this.verbose) {
console.log(` 结果: ${result.slice(0, 100)}...`);
}
return result;
} catch (error) {
return `工具执行错误: ${(error as Error).message}`;
}
}
/**
* Agent 主循环
*/
async run(userMessage: string): Promise<string> {
// 添加用户消息到记忆
this.memory.add({ role: "user", content: userMessage });
let iterations = 0;
while (iterations < this.maxIterations) {
iterations++;
if (this.verbose) {
console.log(`\n--- 迭代 ${iterations} ---`);
}
// 调用 LLM
const response = await this.client.chat.completions.create({
model: CONFIG.openai.model,
messages: this.memory.getAll() as any,
tools: this.formatToolsForOpenAI(),
tool_choice: "auto",
});
const message = response.choices[0].message;
// 如果没有工具调用,返回最终回复
if (!message.tool_calls || message.tool_calls.length === 0) {
const content = message.content || "抱歉,我无法处理这个请求。";
this.memory.add({ role: "assistant", content });
return content;
}
// 记录 assistant 消息(包含工具调用)
this.memory.add({
role: "assistant",
content: message.content || "",
});
// 执行所有工具调用
for (const toolCall of message.tool_calls) {
const args = JSON.parse(toolCall.function.arguments);
const result = await this.executeTool(toolCall.function.name, args);
// 记录工具执行结果
this.memory.add({
role: "tool",
content: result,
toolCallId: toolCall.id,
name: toolCall.function.name,
});
}
}
return "达到最大迭代次数,任务未完成。";
}
/**
* 重置对话
*/
reset(): void {
this.memory.clear();
console.log("🔄 对话已重置");
}
}Step 5: 入口文件
src/index.ts
import * as readline from "readline";
import { Agent } from "./agent";
async function main() {
console.log("🤖 AI Agent 启动成功!");
console.log("输入你的问题,输入 'quit' 退出,输入 'reset' 重置对话\n");
const agent = new Agent();
const rl = readline.createInterface({
input: process.stdin,
output: process.stdout,
});
const prompt = () => {
rl.question("You: ", async (input) => {
const trimmed = input.trim();
if (trimmed.toLowerCase() === "quit") {
console.log("👋 再见!");
rl.close();
return;
}
if (trimmed.toLowerCase() === "reset") {
agent.reset();
prompt();
return;
}
if (!trimmed) {
prompt();
return;
}
try {
const response = await agent.run(trimmed);
console.log(`\nAgent: ${response}\n`);
} catch (error) {
console.error(`错误: ${(error as Error).message}`);
}
prompt();
});
};
prompt();
}
main();Step 6: 运行测试
# 编译并运行
npx ts-node src/index.ts
# 或添加到 package.json scripts
npm run dev测试对话示例
🤖 AI Agent 启动成功!
输入你的问题,输入 'quit' 退出,输入 'reset' 重置对话
You: 帮我计算 (15 + 27) * 3 - 18 / 2
--- 迭代 1 ---
🔧 调用工具: calculator
参数: {"expression":"(15 + 27) * 3 - 18 / 2"}
结果: 计算结果: (15 + 27) * 3 - 18 / 2 = 117...
--- 迭代 2 ---
Agent: 计算结果是 **117**
计算过程:
1. 15 + 27 = 42
2. 42 × 3 = 126
3. 18 ÷ 2 = 9
4. 126 - 9 = 117
You: 搜索一下 "LangChain 入门教程"
--- 迭代 1 ---
🔍 正在搜索: LangChain 入门教程
🔧 调用工具: web_search
参数: {"query":"LangChain 入门教程"}
结果: 搜索"LangChain 入门教程"的结果:...
Agent: 我找到了以下关于 LangChain 入门教程的资源:
1. **《LangChain 入门教程详解》** - 详细介绍了核心概念和应用场景
2. **LangChain 官方文档** - 提供了完整的使用指南
3. **社区讨论帖** - 开发者分享的最佳实践
建议你从官方文档开始学习!
部署上线
方案一:Docker 容器化部署
Dockerfile
FROM node:20-alpine
WORKDIR /app
# 复制依赖文件
COPY package*.json ./
# 安装依赖
RUN npm ci --only=production
# 复制源码
COPY . .
# 编译 TypeScript
RUN npm run build
# 设置环境变量
ENV NODE_ENV=production
# 启动应用
CMD ["node", "dist/index.js"]docker-compose.yml
version: "3.8"
services:
ai-agent:
build: .
container_name: my-ai-agent
restart: unless-stopped
environment:
- OPENAI_API_KEY=${OPENAI_API_KEY}
- OPENAI_BASE_URL=${OPENAI_BASE_URL}
volumes:
- ./data:/app/data # 持久化数据
ports:
- "3000:3000" # 如果添加了 HTTP 服务部署命令
# 构建镜像
docker build -t my-ai-agent .
# 运行容器
docker run -d \
--name ai-agent \
--env-file .env \
my-ai-agent
# 使用 docker-compose
docker-compose up -d方案二:添加 HTTP API 服务
为了让 Agent 能通过 API 调用,添加一个 HTTP 服务层。
安装额外依赖
npm install express cors
npm install -D @types/express @types/corssrc/server.ts
import express from "express";
import cors from "cors";
import { Agent } from "./agent";
const app = express();
const port = process.env.PORT || 3000;
app.use(cors());
app.use(express.json());
// Agent 实例池(生产环境建议使用 Redis 存储会话)
const agents = new Map<string, Agent>();
function getAgent(sessionId: string): Agent {
if (!agents.has(sessionId)) {
agents.set(sessionId, new Agent());
}
return agents.get(sessionId)!;
}
// 健康检查
app.get("/health", (req, res) => {
res.json({ status: "ok", timestamp: new Date().toISOString() });
});
// 对话接口
app.post("/chat", async (req, res) => {
const { message, sessionId = "default" } = req.body;
if (!message) {
return res.status(400).json({ error: "message is required" });
}
try {
const agent = getAgent(sessionId);
const response = await agent.run(message);
res.json({ response, sessionId });
} catch (error) {
res.status(500).json({ error: (error as Error).message });
}
});
// 重置会话
app.post("/reset", (req, res) => {
const { sessionId = "default" } = req.body;
const agent = getAgent(sessionId);
agent.reset();
res.json({ message: "Session reset", sessionId });
});
app.listen(port, () => {
console.log(`🚀 Agent API 服务已启动: http://localhost:${port}`);
});更新 package.json
{
"scripts": {
"dev": "ts-node src/index.ts",
"server": "ts-node src/server.ts",
"build": "tsc",
"start": "node dist/server.js"
}
}方案三:部署到云平台
Vercel(Serverless)
创建 api/chat.ts:
import { Agent } from "../src/agent";
export const config = {
maxDuration: 60, // 最大执行时间 60 秒
};
export default async function handler(req: any, res: any) {
if (req.method !== "POST") {
return res.status(405).json({ error: "Method not allowed" });
}
const { message } = req.body;
const agent = new Agent();
try {
const response = await agent.run(message);
res.json({ response });
} catch (error) {
res.status(500).json({ error: (error as Error).message });
}
}Railway/Render 部署
- 连接 GitHub 仓库
- 设置环境变量(OPENAI_API_KEY 等)
- 设置启动命令:
npm run start - 自动部署
方案四:生产环境最佳实践
添加日志和监控
// src/utils/logger.ts
import pino from "pino";
export const logger = pino({
level: process.env.LOG_LEVEL || "info",
transport: {
target: "pino-pretty",
options: { colorize: true },
},
});添加速率限制
import rateLimit from "express-rate-limit";
const limiter = rateLimit({
windowMs: 60 * 1000, // 1 分钟
max: 20, // 每分钟最多 20 个请求
message: { error: "请求过于频繁,请稍后再试" },
});
app.use("/chat", limiter);添加输入验证
import { z } from "zod";
const chatSchema = z.object({
message: z.string().min(1).max(10000),
sessionId: z.string().optional(),
});
app.post("/chat", async (req, res) => {
const result = chatSchema.safeParse(req.body);
if (!result.success) {
return res.status(400).json({ error: result.error.issues });
}
// ... 处理逻辑
});总结与展望
我们学到了什么
- Agent 架构:理解了感知-推理-行动的循环
- 工具系统:如何定义和注册可扩展的工具
- 记忆管理:实现对话历史的存储和管理
- 部署方案:从本地开发到生产级部署
进阶方向
| 方向 | 描述 | 推荐框架/工具 |
|---|---|---|
| 多 Agent 协作 | 多个 Agent 分工合作 | LangGraph, AutoGen |
| RAG 增强 | 接入知识库 | LlamaIndex, Chroma |
| 流式输出 | 实时展示思考过程 | SSE, WebSocket |
| 可观测性 | 追踪和调试 | LangSmith, Helicone |
| 安全防护 | prompt 注入防御 | Guardrails, NeMo |
💡 Tips: 构建 Agent 时,从简单开始,逐步添加功能。先让核心循环跑通,再优化细节。
如果这篇文章对你有帮助,欢迎点赞和分享!有问题可以在评论区留言讨论。