从零构建你的 AI Agent 框架:Node.js 版 HelloAgents 实战指南
用 Node.js 实现一个完整的智能体框架,深入理解 Agent 的核心设计模式。
前言
随着大语言模型(LLM)的快速发展,AI Agent(智能体)已成为构建 AI 应用的重要范式。Agent 不仅仅是简单的对话工具,它具备自主规划、工具调用、自我反思等能力,能够完成复杂的多步骤任务。
本文将带你从零构建一个完整的 Node.js 版 Agent 框架 ------ HelloAgents,涵盖四种主流 Agent 设计模式:
- SimpleAgent:基础对话智能体
- ReActAgent:推理与行动结合
- ReflectionAgent:自我反思与迭代改进
- PlanAndSolveAgent:规划与逐步执行
框架架构设计
HelloAgents 采用分层架构设计,代码结构清晰、职责分明:
csharp
hello-agents/
├── core/ # 核心框架层
│ ├── agent.js # Agent 基类
│ ├── llm.js # LLM 统一接口
│ ├── message.js # 消息系统
│ └── config.js # 配置管理
│
├── agents/ # Agent 实现层
│ ├── simple_agent.js # SimpleAgent
│ ├── react_agent.js # ReActAgent
│ ├── reflection_agent.js # ReflectionAgent
│ └── plan_solve_agent.js # PlanAndSolveAgent
│
├── tools/ # 工具系统层
│ ├── base.js # 工具基类和注册表
│ ├── calculator.js # 计算工具
│ └── weather.js # 天气查询工具
│
└── examples/ # 示例代码
设计亮点
- 零第三方依赖 :使用原生
fetchAPI 调用 LLM,轻量高效 - 多提供商支持:兼容 OpenAI、ModelScope、智谱 AI、DeepSeek 等
- 本地模型集成:支持 Ollama、VLLM 等本地部署方案
- 统一的消息系统:标准化对话历史管理
核心组件详解
1. 消息系统(Message)
消息是 Agent 与 LLM 交互的基本单元,需要统一格式管理:
javascript
// core/message.js
const MessageRole = {
USER: 'user',
ASSISTANT: 'assistant',
SYSTEM: 'system',
TOOL: 'tool'
};
class Message {
constructor(content, role, options = {}) {
this.content = content;
this.role = role;
this.timestamp = options.timestamp || new Date();
this.metadata = options.metadata || {};
}
// 转换为 OpenAI API 格式
toDict() {
return {
role: this.role,
content: this.content
};
}
}
2. 配置管理(Config)
中心化配置管理,支持环境变量和配置文件:
javascript
// core/config.js
class Config {
constructor(options = {}) {
// LLM 配置
this.defaultModel = options.defaultModel || 'gpt-3.5-turbo';
this.temperature = options.temperature ?? 0.7;
// Agent 配置
this.maxHistoryLength = options.maxHistoryLength || 100;
this.maxReactSteps = options.maxReactSteps || 5;
this.maxReflectionIterations = options.maxReflectionIterations || 3;
}
// 从环境变量创建配置
static fromEnv() {
return new Config({
temperature: parseFloat(process.env.TEMPERATURE || '0.7'),
maxReactSteps: parseInt(process.env.MAX_REACT_STEPS || '5'),
// ... 其他配置
});
}
}
3. LLM 统一接口(HelloAgentsLLM)
这是框架的核心组件,封装 LLM 调用,支持流式响应和多提供商自动检测:
javascript
// core/llm.js
class HelloAgentsLLM {
constructor(options = {}) {
this.model = options.model || process.env.LLM_MODEL_ID;
this.apiKey = options.apiKey || process.env.LLM_API_KEY;
this.baseUrl = options.baseUrl || process.env.LLM_BASE_URL;
this.provider = options.provider || this._autoDetectProvider();
// 自动解析凭证
const { apiKey, baseUrl } = this._resolveCredentials();
this.apiKey = apiKey;
this.baseUrl = baseUrl;
}
// 自动检测 LLM 提供商
_autoDetectProvider() {
if (process.env.MODELSCOPE_API_KEY) return 'modelscope';
if (process.env.OPENAI_API_KEY) return 'openai';
if (process.env.ZHIPU_API_KEY) return 'zhipu';
// 根据 base_url 判断
const baseUrl = process.env.LLM_BASE_URL;
if (baseUrl?.includes('localhost:11434')) return 'ollama';
if (baseUrl?.includes('localhost:8000')) return 'vllm';
return 'openai';
}
// 流式调用 LLM
async think(messages, temperature = 0) {
const res = await fetch(`${this.baseUrl}/chat/completions`, {
method: 'POST',
headers: {
'Content-Type': 'application/json',
'Authorization': `Bearer ${this.apiKey}`,
},
body: JSON.stringify({
model: this.model,
messages,
temperature,
stream: true
})
});
// 处理流式响应...
}
}
支持的 LLM 提供商:
| 提供商 | 环境变量 | 默认 Base URL |
|---|---|---|
| OpenAI | OPENAI_API_KEY |
https://api.openai.com/v1 |
| ModelScope | MODELSCOPE_API_KEY |
https://api-inference.modelscope.cn/v1/ |
| 智谱 AI | ZHIPU_API_KEY |
https://open.bigmodel.cn/api/paas/v4/ |
| DeepSeek | DEEPSEEK_API_KEY |
https://api.deepseek.com/v1 |
| Ollama | - | http://localhost:11434/v1 |
| VLLM | - | http://localhost:8000/v1 |
4. Agent 基类(Agent)
定义所有智能体的抽象接口和通用行为:
javascript
// core/agent.js
class Agent {
constructor(options) {
this.name = options.name;
this.llm = options.llm;
this.systemPrompt = options.systemPrompt || '你是一个有用的AI助手。';
this.config = options.config || Config.fromEnv();
this._history = [];
}
// 抽象方法,子类必须实现
async run(inputText, kwargs = {}) {
throw new Error('子类必须实现 run 方法');
}
// 历史记录管理
addMessage(message) {
this._history.push(message);
if (this._history.length > this.config.maxHistoryLength) {
this._history = this._history.slice(-this.config.maxHistoryLength);
}
}
clearHistory() {
this._history = [];
}
getHistoryMessages() {
return this._history.map(msg => msg.toDict());
}
}
四种 Agent 范式实现
1. SimpleAgent:基础对话智能体
最基础的 Agent 实现,支持可选的工具调用能力:
javascript
// agents/simple_agent.js
class SimpleAgent extends Agent {
constructor(options) {
super(options);
this.toolRegistry = options.toolRegistry || null;
this.enableToolCalling = options.enableToolCalling && this.toolRegistry !== null;
}
async run(inputText, options = {}) {
console.log(`🤖 ${this.name} 正在处理: ${inputText}`);
// 构建消息列表
const messages = [];
messages.push({ role: 'system', content: this._getEnhancedSystemPrompt() });
// 添加历史消息
for (const msg of this._history) {
messages.push(msg.toDict());
}
// 添加当前用户消息
messages.push({ role: 'user', content: inputText });
// 简单对话逻辑
if (!this.enableToolCalling) {
const response = await this.llm.invoke(messages);
this.addMessage(new Message(inputText, 'user'));
this.addMessage(new Message(response, 'assistant'));
return response;
}
// 支持多轮工具调用
return this._runWithTools(messages, inputText, maxToolIterations);
}
// 流式响应支持
async streamRun(inputText, options = {}) {
let fullResponse = '';
for await (const chunk of this.llm.streamInvoke(messages)) {
fullResponse += chunk;
process.stdout.write(chunk);
}
// 保存历史...
}
}
使用示例:
javascript
import { HelloAgentsLLM, SimpleAgent } from './index.js';
const llm = new HelloAgentsLLM();
const agent = new SimpleAgent({
name: 'AI助手',
llm,
systemPrompt: '你是一个友好的AI助手'
});
const response = await agent.run('什么是人工智能?');
2. ReActAgent:推理与行动结合
ReAct(Reasoning + Acting)是 Agent 设计的经典模式,通过 Thought → Action → Observation 循环完成任务:
javascript
// agents/react_agent.js
const REACT_PROMPT = `你是一个具备推理和行动能力的AI助手。
## 可用工具
{tools}
## 工作流程
请严格按照以下格式进行回应:
Thought: 你的思考过程,分析问题、规划下一步
Action: 你决定采取的行动,格式如下:
- \`{tool_name}[{tool_input}]\` - 调用指定工具
- \`Finish[最终答案]\` - 给出最终答案
## 当前任务
**Question:** {question}
## 执行历史
{history}`;
class ReActAgent extends Agent {
constructor(options) {
super(options);
this.toolRegistry = options.toolRegistry;
this.maxSteps = options.maxSteps || 5;
this.currentHistory = [];
}
async run(inputText, kwargs = {}) {
this.currentHistory = [];
let currentStep = 0;
while (currentStep < this.maxSteps) {
currentStep++;
// 1. 构建提示词
const prompt = this.promptTemplate
.replace('{tools}', this.toolRegistry.getToolsDescription())
.replace('{question}', inputText)
.replace('{history}', this.currentHistory.join('\n'));
// 2. 调用 LLM
const responseText = await this.llm.invoke([{ role: 'user', content: prompt }]);
// 3. 解析输出
const { thought, action } = this._parseOutput(responseText);
// 4. 检查完成条件
if (action?.startsWith('Finish')) {
const finalAnswer = this._parseActionInput(action);
return finalAnswer;
}
// 5. 执行工具调用
if (action) {
const { toolName, toolInput } = this._parseAction(action);
const observation = this.toolRegistry.executeTool(toolName, toolInput);
this.currentHistory.push(`Action: ${action}`);
this.currentHistory.push(`Observation: ${observation}`);
}
}
return '抱歉,我无法在限定步数内完成这个任务。';
}
}
工作流程图解:
markdown
用户问题 → Thought(思考)→ Action(行动)→ Observation(观察)
↑ │
└────────────────────────────────────────────────────┘
(循环直到得到最终答案)
使用示例:
javascript
import { ReActAgent, ToolRegistry, CalculatorTool, WeatherTool } from './index.js';
const toolRegistry = new ToolRegistry();
toolRegistry.registerTool(new CalculatorTool());
toolRegistry.registerTool(new WeatherTool());
const agent = new ReActAgent({
name: '智能助手',
llm,
toolRegistry,
maxSteps: 5
});
// Agent 会先查询天气,再执行计算
const result = await agent.run('查询北京天气,然后计算 15 * 8');
3. ReflectionAgent:自我反思与迭代改进
Reflection 模式通过 Generate → Reflect → Refine 循环,让 Agent 自我审视并改进答案:
javascript
// agents/reflection_agent.js
const DEFAULT_PROMPTS = {
initial: `请根据以下要求完成任务:
任务: {task}
请提供一个完整、准确的回答。`,
reflect: `请仔细审查以下回答,找出可能的问题:
# 原始任务: {task}
# 当前回答: {content}
如果回答已经很好,请回答"SATISFIED"。
如果需要改进,请按以下格式输出:
CRITIQUE: [具体的问题]
SUGGESTION: [改进建议]`,
refine: `请根据反馈意见改进你的回答:
# 原始任务: {task}
# 上一轮回答: {lastAttempt}
# 反馈意见: {feedback}
请提供一个改进后的回答。`
};
class ReflectionAgent extends Agent {
constructor(options) {
super(options);
this.maxIterations = options.maxIterations || 3;
}
async run(inputText, kwargs = {}) {
// Phase 1: 初始生成
let currentAnswer = await this._generate(inputText);
// Phase 2-N: 反思-改进循环
for (let iteration = 1; iteration <= this.maxIterations; iteration++) {
// Step 1: Reflect(反思)
const reflection = await this._reflect(inputText, currentAnswer);
if (reflection.satisfied) {
console.log('✅ 答案已满意,无需进一步改进!');
break;
}
// Step 2: Refine(改进)
currentAnswer = await this._refine(inputText, currentAnswer, reflection);
}
return currentAnswer;
}
async _reflect(task, answer) {
const prompt = this.prompts.reflect
.replace('{task}', task)
.replace('{content}', answer);
const response = await this.llm.invoke(messages);
if (response.includes('SATISFIED')) {
return { satisfied: true };
}
// 提取批判和建议
return {
satisfied: false,
critique: this._extractCritique(response),
suggestion: this._extractSuggestion(response)
};
}
}
工作流程图解:
yaml
Phase 1: Generate(初始生成)
│
▼
Phase 2: Reflect(反思)────→ SATISFIED? ──Yes──→ 返回答案
│ │
│ No
│ │
▼ ▼
Phase 3: Refine(改进)←──────────┘
│
└──→ 继续循环(最多 maxIterations 次)
使用示例:
javascript
import { ReflectionAgent } from './index.js';
const agent = new ReflectionAgent({
name: '反思助手',
llm,
maxIterations: 2,
systemPrompt: '你是一个专业的问题解答专家'
});
const result = await agent.run('什么是机器学习中的过拟合问题?');
4. PlanAndSolveAgent:规划与逐步执行
Plan-and-Solve 模式将复杂问题分解为多个步骤,逐步执行:
javascript
// agents/plan_solve_agent.js
const DEFAULT_PLANNER_PROMPT = `你是一个顶级的AI规划专家。
将复杂问题分解成多个简单步骤,输出JSON数组格式:
问题: {question}
请输出计划(JSON数组):
["步骤1", "步骤2", "步骤3", ...]`;
const DEFAULT_EXECUTOR_PROMPT = `你是一位顶级的AI执行专家。
严格按照计划,一步步解决问题。
# 原始问题: {question}
# 完整计划: {plan}
# 历史步骤与结果: {history}
# 当前步骤: {currentStep}
请仅输出针对"当前步骤"的回答:`;
class PlanAndSolveAgent extends Agent {
async run(inputText, kwargs = {}) {
// ========== Phase 1: 规划 ==========
console.log('📋 Phase 1: Plan(规划)');
const plan = await this._plan(inputText);
// ========== Phase 2: 逐步执行 ==========
console.log('🚀 Phase 2: Execute(执行)');
const executionHistory = [];
for (let i = 0; i < plan.length; i++) {
const stepResult = await this._execute(
inputText, plan, executionHistory, plan[i]
);
executionHistory.push({ step: plan[i], result: stepResult });
}
// ========== Phase 3: 综合答案 ==========
console.log('🏆 Phase 3: Synthesize');
const finalAnswer = await this._synthesize(inputText, executionHistory);
return finalAnswer;
}
async _plan(question) {
const prompt = this.plannerPrompt.replace('{question}', question);
const response = await this.llm.invoke(messages);
// 解析 JSON 数组
const jsonMatch = response.match(/\[.*\]/s);
if (jsonMatch) {
return JSON.parse(jsonMatch[0]);
}
// 备用:文本解析
return this._parseTextPlan(response);
}
async _execute(question, plan, history, currentStep) {
const prompt = this.executorPrompt
.replace('{question}', question)
.replace('{plan}', plan.join('\n'))
.replace('{history}', history.map(h => `${h.step}: ${h.result}`).join('\n'))
.replace('{currentStep}', currentStep);
return await this.llm.invoke(messages);
}
async _synthesize(question, history) {
const messages = [
{ role: 'user', content: `根据执行结果,给出完整答案:\n${historyStr}` }
];
return await this.llm.invoke(messages);
}
}
工作流程图解:
less
用户问题
│
▼
┌─────────────────┐
│ Phase 1: Plan │ → ["步骤1", "步骤2", "步骤3"]
└─────────────────┘
│
▼
┌─────────────────┐
│ Phase 2: Execute│ → 步骤1 → 步骤2 → 步骤3
└─────────────────┘ ↓ ↓ ↓
结果1 结果2 结果3
│
▼
┌─────────────────┐
│Phase 3: Synthesize│ → 综合所有结果 → 最终答案
└─────────────────┘
使用示例:
javascript
import { PlanAndSolveAgent } from './index.js';
const agent = new PlanAndSolveAgent({
name: '规划执行助手',
llm,
systemPrompt: '你是一个专业的任务规划专家'
});
const result = await agent.run(
'水果店周一卖出15个苹果,周二卖出周一的两倍,周三比周二少5个,三天总共卖出多少?'
);
// 输出计划:["计算周二销量", "计算周三销量", "计算总销量"]
// 然后逐步执行并综合答案
工具系统设计
工具是 Agent 与外部世界交互的桥梁。HelloAgents 提供了灵活的工具系统:
工具基类
javascript
// tools/base.js
class ToolParameter {
constructor(options) {
this.name = options.name;
this.type = options.type || 'string';
this.description = options.description;
this.required = options.required !== false;
this.default = options.default;
}
}
class Tool {
constructor(options) {
this.name = options.name;
this.description = options.description;
}
// 抽象方法,子类必须实现
run(parameters) {
throw new Error('子类必须实现 run 方法');
}
getParameters() {
return [];
}
// 转换为 OpenAI function calling schema
toOpenAiSchema() {
return {
type: 'function',
function: {
name: this.name,
description: this.description,
parameters: { ... }
}
};
}
}
工具注册表
javascript
// tools/base.js
class ToolRegistry {
constructor() {
this._tools = {};
this._functions = {};
}
// 注册 Tool 对象
registerTool(tool) {
this._tools[tool.name] = tool;
}
// 直接注册函数(简便方式)
registerFunction(options) {
this._functions[options.name] = {
description: options.description,
func: options.func
};
}
// 执行工具
executeTool(name, input) {
if (this._tools[name]) {
return this._tools[name].run(input);
}
if (this._functions[name]) {
return this._functions[name].func(input);
}
return `未找到工具 '${name}'`;
}
// 获取工具描述
getToolsDescription() {
return Object.values(this._tools)
.map(t => t.getDescription())
.join('\n');
}
}
内置工具示例
计算器工具:
javascript
// tools/calculator.js
class CalculatorTool extends Tool {
constructor() {
super({
name: 'calculator',
description: '数学计算工具,支持基本运算和常用函数'
});
}
run(parameters) {
let expression = parameters;
if (typeof parameters === 'object') {
expression = parameters.expression || parameters.input;
}
try {
// 安全的表达式求值
const result = this._evaluate(expression);
return `计算结果: ${result}`;
} catch (error) {
return `计算失败: ${error.message}`;
}
}
_evaluate(expression) {
// 支持的函数:sqrt, pow, abs, sin, cos, tan, log, exp
// 使用 Function 构造器进行安全计算
const safeExpression = expression
.replace(/sqrt/g, 'Math.sqrt')
.replace(/pow/g, 'Math.pow')
// ...
const calculate = new Function('return ' + safeExpression);
return calculate();
}
getParameters() {
return [
new ToolParameter({
name: 'expression',
type: 'string',
description: '数学表达式,如: 2+3*4, sqrt(16)',
required: true
})
];
}
}
天气查询工具:
javascript
// tools/weather.js
class WeatherTool extends Tool {
constructor() {
super({
name: 'weather',
description: '查询指定城市的实时天气信息'
});
}
async run(parameters) {
let city = parameters;
if (typeof parameters === 'object') {
city = parameters.city || parameters.location;
}
// 使用 wttr.in 免费 API
const url = `https://wttr.in/${encodeURIComponent(city)}?format=j1`;
const response = await fetch(url);
const data = await response.json();
const current = data.current_condition[0];
return `${city}当前天气:${current.weatherDesc[0].value},气温${current.temp_C}度`;
}
}
自定义工具开发
javascript
import { Tool, ToolParameter } from './tools/base.js';
class MyCustomTool extends Tool {
constructor() {
super({
name: 'my_tool',
description: '自定义工具描述'
});
}
run(parameters) {
// 实现工具逻辑
return '工具执行结果';
}
getParameters() {
return [
new ToolParameter({
name: 'input',
type: 'string',
description: '输入参数',
required: true
})
];
}
}
// 使用
toolRegistry.registerTool(new MyCustomTool());
完整使用示例
环境配置
创建 .env 文件:
env
# LLM 配置
LLM_MODEL_ID=gpt-3.5-turbo
LLM_API_KEY=your_api_key
LLM_BASE_URL=https://api.openai.com/v1
# 或使用国内提供商
# MODELSCOPE_API_KEY=your_modelscope_key
# ZHIPU_API_KEY=your_zhipu_key
# DEEPSEEK_API_KEY=your_deepseek_key
综合测试示例
javascript
// examples/test_all.js
import 'dotenv/config';
import {
HelloAgentsLLM, Config,
SimpleAgent, ReActAgent, ReflectionAgent, PlanAndSolveAgent,
ToolRegistry, CalculatorTool, WeatherTool
} from '../index.js';
async function main() {
const llm = new HelloAgentsLLM();
// ===== 测试 SimpleAgent =====
const simpleAgent = new SimpleAgent({
name: 'Simple助手',
llm,
systemPrompt: '你是一个友好的AI助手'
});
const simpleResult = await simpleAgent.run('什么是智能体?');
// ===== 测试 ReActAgent =====
const reactRegistry = new ToolRegistry();
reactRegistry.registerTool(new CalculatorTool());
const reactAgent = new ReActAgent({
name: 'ReAct助手',
llm,
toolRegistry: reactRegistry,
maxSteps: 3
});
const reactResult = await reactAgent.run('计算 15 * 8 + 32');
// ===== 测试 ReflectionAgent =====
const reflectionAgent = new ReflectionAgent({
name: 'Reflection助手',
llm,
maxIterations: 2
});
const reflectionResult = await reflectionAgent.run('什么是递归?');
// ===== 测试 PlanAndSolveAgent =====
const planAgent = new PlanAndSolveAgent({
name: 'Plan助手',
llm
});
const planResult = await planAgent.run(
'小明有10个苹果,给了小红3个,又买了5个,现在有多少个?'
);
// 查看历史记录
console.log(`SimpleAgent: ${simpleAgent.getHistory().length} 条消息`);
console.log(`ReActAgent: ${reactAgent.getHistory().length} 条消息`);
}
main();
运行测试
bash
# 运行所有测试
npm test
# 运行单个测试
npm run example:simple
npm run example:react
npm run example:reflection
npm run example:plan
四种范式对比总结
| 特性 | SimpleAgent | ReActAgent | ReflectionAgent | PlanAndSolveAgent |
|---|---|---|---|---|
| 核心机制 | 单轮对话 | Thought-Action-Observation 循环 | Generate-Reflect-Refine 循环 | Plan-Execute-Synthesize |
| 适用场景 | 简单问答、基础对话 | 需要工具调用的任务 | 需要高质量答案的任务 | 复杂多步骤任务 |
| 工具支持 | 可选 | 必需 | 不需要 | 不需要 |
| 迭代次数 | 1次 | maxSteps | maxIterations | plan.length |
| 复杂度 | 低 | 中 | 中 | 高 |
选择建议:
- 简单问答 → SimpleAgent
- 需要计算、查询等外部信息 → ReActAgent
- 需要高质量、深思熟虑的回答 → ReflectionAgent
- 复杂的推理任务、数学问题 → PlanAndSolveAgent
总结
HelloAgents 框架展示了构建 AI Agent 的核心设计模式:
- 分层架构:核心层、Agent层、工具层职责分明
- 统一接口:LLM 调用标准化,支持多提供商
- 四种范式:覆盖不同场景的 Agent 设计模式
- 可扩展性:工具系统灵活,易于自定义
这个框架虽然轻量,但完整展示了 Agent 的核心概念。你可以在此基础上:
- 添加更多工具(搜索、数据库、API调用)
- 实现更复杂的 Agent 组合模式
- 集成记忆系统(向量数据库)
- 添加多 Agent 协作能力
参考资料
本文代码完整实现可在项目仓库中获取,欢迎 Star ⭐ 和交流讨论!