1. 项目概述从零构建AI智能体如果你对AI智能体AI Agent充满好奇看着LangChain、AutoGPT这些框架觉得既强大又神秘心里总有个疑问“它们到底是怎么工作的”那么这个项目就是为你准备的。ai-agents-from-scratch不是一个教你如何使用框架的速成课而是一张带你深入AI智能体内部构造的“地图”。它的核心哲学很简单亲手建造方能真正理解。通过使用本地大语言模型LLM和node-llama-cpp库从最基础的LLM交互开始一步步搭建出具备工具调用、记忆和复杂推理能力的智能体最终甚至能让你窥见主流框架如LangChain的设计精髓。这个项目适合所有层次的开发者无论是刚接触AI应用的新手希望夯实基础、理解每个API调用背后的逻辑还是已经用过框架的中高级开发者渴望摆脱“黑箱”感在遇到诡异bug时能精准定位或想定制独特工作流而无从下手。它剥离了生产级框架的便利性包装迫使你直面智能体最核心的组件大脑LLM、身份系统提示词、双手工具、状态记忆和策略推理模式。当你完整走完这个学习路径再回头看那些框架你会发现自己不再是在使用一个“魔法盒子”而是在驾驭一套你已了然于胸的工具。2. 学习路径与核心概念拆解2.1 渐进式学习路径设计逻辑项目的学习路径设计极具匠心它遵循着认知和能力递进的客观规律而不是一股脑地抛出复杂概念。整个旅程被清晰地划分为两个主要阶段第一阶段打基础第二阶段建框架。第一阶段从最原子化的操作开始。intro让你成功加载并运行一个本地模型完成一次最简单的问答。这看似简单却是后续一切的基石你会接触到模型加载、上下文Context和推理管道Inference Pipeline这些底层概念。紧接着translation和think示例引入了系统提示词System Prompt这一关键设计。这里你需要理解LLM本身并无固定角色它的“人格”和“能力边界”完全由你通过系统提示词来塑造和约束。例如在翻译任务中你通过提示词将其限定为“专业翻译器”禁止其发挥创造性在数学推理任务中你又要求它“逐步思考”。这揭示了智能体设计的第一个核心行为是提示出来的而非模型固有的。batch和coding则开始关注性能和交互体验。批量处理教你利用GPU并行能力提升吞吐量而流式响应则关乎用户体验让用户能实时看到生成过程这对代码生成、长文写作等场景至关重要。至此你还在与一个“超级文本生成器”打交道。真正的转折点发生在simple-agent。这里引入了函数调用Function Calling或称工具Tools的概念。你定义一些函数如获取天气、计算器并以特定的JSON Schema格式描述给LLM。LLM在理解用户请求后可以主动选择调用合适的工具并将工具执行结果整合进回复中。这一步是质变智能体从此拥有了与外部世界交互的“手”不再局限于文本生成。后续的memory和react-agent在此基础上叠加了持久化和复杂策略。记忆让智能体有了“状态”能记住跨对话的信息而ReActReasoning Acting模式则提供了一种强大的迭代式问题解决框架思考Reason- 行动Act- 观察Observe循环直至问题解决。这构成了现代智能体框架最核心的循环逻辑。注意许多初学者会急于跳过前面基础示例直接奔向“智能体”。但这样你会错过对LLM行为塑造、上下文管理、工具调用决策逻辑等基础概念的深刻理解导致在后续构建复杂智能体时对出现的问题根源模糊不清。务必按顺序实践。2.2 智能体架构的演进图谱通过这一系列示例你能清晰地看到智能体架构的演进脉络基础生成器User - LLM - Response。单向、无状态、无工具。工具调用者User - LLM - Tools - Response。LLM具备了决策能力可以根据需要调用工具但决策可能是一次性的。有状态的助手在工具调用者的基础上加入了Memory模块。LLM在生成回复或决策时可以查询和更新记忆实现个性化服务。战略思考者ReActUser - LLM - Think - Act - Observe - (Loop)。这是一个闭环反馈系统。智能体先“思考”出一步计划或一个疑问然后“行动”可能是调用工具也可能是直接输出接着“观察”行动的结果并基于此进行下一轮“思考”。这种模式特别适合需要多步骤探索、信息检索或自我修正的复杂任务。这种从简到繁的架构演进正是所有高级智能体框架内部所封装的核心模式。理解了这个演进过程你就掌握了智能体设计的“语法”。3. 环境准备与模型选择实战3.1 本地开发环境搭建要点项目基于Node.js环境这为前后端开发者提供了极低的入门门槛。你需要确保Node.js版本在18以上这是因为一些现代的ES模块和API特性被广泛使用。内存方面8GB是勉强可用的底线运行一个7B参数的量化模型可能就会比较吃力16GB或以上是推荐配置它能让你更流畅地运行13B甚至更大参数的模型并为智能体复杂的推理过程提供充足的运算空间。安装依赖非常简单只需在项目根目录执行npm install。这里隐藏的一个关键是node-llama-cpp这个包它是本项目与本地LLM交互的桥梁。它是一个Node.js绑定底层调用的是用C编写的高效推理库llama.cpp因此你不需要安装Python或复杂的深度学习环境。这种设计使得整个项目非常“纯粹”专注于JavaScript/TypeScript生态下的智能体逻辑本身。3.2 模型下载与配置的避坑指南项目的/models目录是存放模型文件的地方这也是新手最容易卡住的一步。这里需要的模型格式是GGUF。GGUF是llama.cpp推出的一种二进制格式它统一了之前多种量化格式并包含了模型的元数据如架构、上下文长度等使得加载和使用更加方便。模型来源最常用的来源是Hugging Face。你可以搜索你感兴趣的模型并寻找带有“GGUF”标签的版本。例如Llama 3、Mistral、Qwen等热门模型都有社区转换好的GGUF版本。参数与量化模型文件名通常像qwen2.5-7b-instruct-q4_k_m.gguf。7b代表70亿参数。q4_k_m是一种量化方式表示4位量化带有混合精度。量化是为了在精度损失可接受的前提下大幅降低模型对内存和显存的占用。常见的量化等级有q4_0/q4_k_m 兼顾性能与质量最常用的选择。q8_0 8位量化质量损失极小但文件更大。q2_k 2位量化极度压缩可在低资源设备运行但质量下降明显。对于学习和开发q4_k_m通常是性价比较高的起点。将下载好的.gguf文件放入./models目录即可。首次运行的加载当你第一次运行示例代码如node intro/intro.js时node-llama-cpp会检测并加载模型。这个过程可能需要几十秒到几分钟因为它需要将模型文件加载到内存中并准备推理上下文。请耐心等待控制台会有加载进度提示。实操心得建议在models目录下建立一个README.md记录你下载的模型名称、大小、量化等级和来源链接。当项目示例增多或你开始试验不同模型时这能帮你快速管理。另外对于内存有限的机器务必选择参数量小如7B且量化程度高如q4的模型否则很容易遇到内存不足OOM的错误。4. 核心示例深度解析与实操4.1 从“翻译官”到“思考者”系统提示词的魔力translation.js和think.js这两个示例是理解如何“操控”LLM的绝佳起点。它们展示了如何通过系统提示词System Prompt来为LLM赋予特定的角色和能力边界。在translation.js中系统提示词可能是这样的const systemPrompt 你是一个专业的翻译引擎。请将用户输入的任何语言翻译成中文。你的回复应只包含翻译后的内容不要添加任何解释、评论或额外信息。;这个提示词做了几件关键事定义角色专业翻译引擎、规定任务任何语言译中、约束输出格式只含译文。当你运行这个示例时即使用户问“你好吗”模型也会严格按照指令将其识别为待翻译文本而非问候并输出对应的中文翻译。这揭示了与LLM交互的第一原则你得到的输出质量极大程度上取决于你输入的指令质量。think.js则更进一步它通常使用类似“让我们一步步思考”的提示词来激发模型的链式推理Chain-of-Thought能力。对于一道复杂的数学或逻辑题直接提问可能得到错误答案。但要求模型“逐步思考”后它会在内部生成推理步骤最终得出更准确的结论。这个示例会让你直观感受到同样的模型在不同的提示词驱动下表现出的“智能”水平天差地别。这是构建可靠智能体的基础你必须学会为不同任务设计精准的提示词。4.2 “简单智能体”揭秘工具调用的实现机制simple-agent.js是实现质变的一步。这里我们来看看一个最简单的工具调用智能体是如何构建的。首先你需要定义“工具”。工具本质上是一个JavaScript函数附带一个符合JSON Schema的描述。例如一个获取天气的工具const tools [ { name: get_weather, description: 获取指定城市的当前天气情况, parameters: { type: object, properties: { city: { type: string, description: 城市名称 } }, required: [city] }, execute: async ({ city }) { // 模拟或真实调用天气API return 城市 ${city} 的天气是晴朗25摄氏度。; } } ];关键步骤在于如何让LLM知道并使用这些工具。代码中会将这些工具的定义以特定的格式通常是OpenAI的function calling格式放入给LLM的请求消息中。LLM在分析用户输入如“北京天气怎么样”后不会直接生成回复文本而是输出一个结构化的JSON表明它想调用get_weather工具并提供了参数{“city”: “北京”}。你的程序需要解析这个JSON找到对应的工具函数并执行然后将执行结果“城市北京的天气是晴朗…”作为新的上下文信息再次发送给LLM。最后LLM会整合工具返回的结果和原始问题生成面向用户的自然语言回复“北京目前天气晴朗气温25度…”。这个过程揭示了智能体的核心工作流感知用户输入- 决策LLM选择工具- 执行调用工具- 整合LLM生成最终回复。simple-agent示例完整地实现了这个循环让你看清了所谓“函数调用”背后其实是LLM与外部代码的一次标准RPC远程过程调用协作。4.3 为智能体注入“记忆”状态持久化实践simple-agent-with-memory示例在工具调用的基础上增加了记忆层。记忆不是简单的聊天记录堆砌而是一个需要精心设计的系统。该示例通常会实现一个MemoryManager类其核心功能包括存储将对话历史用户消息、AI回复、工具调用及结果以结构化的方式保存起来。可能使用内存对象、本地文件或数据库。检索当新对话发生时不是把所有历史都塞给LLM有上下文长度限制而是根据当前问题从记忆中检索最相关的片段。这可以通过简单的最近N条对话或更复杂的向量相似度搜索来实现。摘要对于长对话可以将遥远的对话历史压缩成一段摘要既保留了关键信息又节省了上下文窗口。在代码中你会在每次与LLM交互前先调用memoryManager.getRelevantContext(userInput)来获取相关的历史记忆然后将这些记忆作为系统提示词或上下文的一部分喂给LLM。这样LLM就能在回复中引用之前提到过的信息实现连贯的、个性化的对话。注意事项记忆是一把双刃剑。不相关的旧记忆可能会干扰LLM对当前问题的判断称为“记忆干扰”。同时记忆可能包含敏感信息需要考虑隐私和安全问题。在实际应用中记忆的存储周期、清理策略和加密是需要仔细设计的。4.4 ReAct模式构建会“思考-行动”的智能体react-agent.js实现的是ReAct模式这是当前解决复杂问题最有效的智能体范式之一。它的核心在于一个循环思考ThoughtLLM分析当前情况用户问题、已有信息、可用工具思考下一步应该做什么。输出格式通常是“我需要先查找X信息所以我将使用Y工具。”行动Action根据思考的结果执行一个具体的动作。这通常是调用一个工具并传入参数。观察Observation获取行动的结果工具返回的数据或错误信息。循环将“思考”、“行动”、“观察”这三者形成的新记录加入到对话历史中再次让LLM进行“思考”判断问题是否已解决若未解决则继续下一轮。这个循环会一直持续直到LLM在“思考”步骤中认为问题已解决并输出最终答案Final Answer。在代码实现上你需要构建一个runReActLoop函数其内部是一个while循环。在每次循环中你将包含历史记录的完整上下文发送给LLM并提示其按照“Thought: ... Action: ... Observation: ...”的格式输出。你的程序需要解析这个输出如果包含Action就执行工具调用将结果作为Observation追加如果包含Final Answer就跳出循环并返回答案。这种模式强大之处在于它将问题分解为可管理的步骤并且允许智能体根据上一步的结果动态调整下一步计划具备了初步的规划和纠错能力。许多框架中的“AgentExecutor”本质上就是在执行一个强化版的ReAct循环。5. 第二阶段从原理到框架——重建LangChain核心完成第一阶段后你已经掌握了智能体的所有核心“零件”。第二阶段的目标是学习如何像LangChain、LangGraph这样的生产级框架一样将这些零件优雅地、可扩展地组装起来。这不是让你造一个替代品而是通过重建其核心概念来深度理解框架的设计哲学。5.1 万物皆可“运行”理解Runnable接口几乎所有现代AI框架都围绕一个核心抽象Runnable。你可以把它理解为一个统一的、可执行的“任务单元”。在tutorial/01-foundation/01-runnable中你会亲手实现一个最简单的Runnable接口。一个Runnable最基本的要求是有一个invoke(input)方法它接受输入并返回输出。神奇之处在于组合性一个LLM调用是Runnable一个提示词模板是Runnable一个输出解析器也是Runnable。然后你可以通过.pipe()方法将它们串联起来prompt - llm - parser。这就构成了一个链Chain。当你实现它时会恍然大悟框架提供的各种便捷的链式调用如LCEL底层就是通过这种统一的接口将不同的组件粘合在一起。这带来了巨大的灵活性你可以像搭积木一样构建复杂的工作流。5.2 消息系统对话的结构化表达在基础示例中我们可能用简单的字符串数组来管理对话历史。但在生产框架中需要更精细的结构。02-messages部分会让你实现一个简单的消息系统区分不同角色HumanMessage: 用户输入。AIMessage: AI的回复可能包含工具调用请求。ToolMessage: 工具执行结果的返回。SystemMessage: 系统指令。通过为消息打上类型标签框架能更准确地进行上下文管理、序列化/反序列化以及为不同下游组件如提示词模板提供正确的数据格式。实现这个消息系统你会理解为什么框架的聊天历史看起来比简单的字符串数组更复杂——那是为了支撑更强大、更可靠的功能。5.3 构建链与智能体执行器在02-composition和03-agency中你将把前面积累的模块组装起来。提示词模板实现一个简单的模板引擎将用户变量注入到预设的提示词字符串中。LLM链将提示词模板Runnable、LLM Runnable和输出解析器Runnable用.pipe()连接形成一个完整的LLM调用管道。工具执行器实现一个安全执行工具调用的模块包含超时控制、错误处理并将结果格式化为标准的ToolMessage。智能体执行器这是第一阶段react-agent的升级版。你将实现一个通用的AgentExecutor类它内部封装了ReAct循环但接收的是你定义的BaseAgent包含LLM和工具列表以及Memory。执行器负责管理循环逻辑、处理各种中间状态思考、行动、观察直到返回最终结果。通过亲手搭建这些你会彻底明白框架中AgentExecutor.run()那一行代码背后究竟发生了多少事情消息的组装、历史的裁剪、工具的匹配与安全调用、循环条件的判断等等。5.4 图状态机LangGraph核心思想入门最硬核但也最令人兴奋的部分是04-graphs这里你将触及LangGraph的核心概念——图状态机。复杂的智能体工作流往往不是简单的线性链而是包含条件分支、循环、并行任务的图。你会从最基本的StateGraph开始实现它由节点Nodes和边Edges组成。节点是一个Runnable代表一个处理步骤如调用LLM、执行工具。边定义了节点之间的流转条件。例如在一个审核流程中节点A内容生成之后可能根据其输出质量通过条件边流向节点B通过或节点C驳回修改。实现一个简单的图执行引擎让它能够根据当前状态和节点输出决定下一步走到哪个节点直到到达终点。你会理解到像AutoGPT这样的多智能体协作系统其底层都可以用这种图状态机来优雅地描述和执行业务流程。踩坑实录在实现图状态机时最容易出错的是状态State的设计。状态应该是一个包含所有必要信息的扁平字典。要仔细定义哪些信息是每个节点都需要读写的避免状态结构过于复杂或存在隐式依赖。良好的状态设计是图流程清晰可维护的关键。6. 常见问题、调试技巧与资源推荐6.1 实战中常见问题排查指南在学习和实践过程中你肯定会遇到各种问题。下面是一个快速排查清单问题现象可能原因排查步骤与解决方案模型加载失败或报错1. 模型文件路径错误。2. 模型文件损坏。3. 系统内存/显存不足。4.node-llama-cpp版本与模型不兼容。1. 检查./models目录下是否有正确的.gguf文件。2. 重新下载模型文件验证哈希值。3. 使用htop或任务管理器查看内存占用。尝试更小参数或更高量化的模型。4. 查阅node-llama-cpp的GitHub页面确认支持的GGUF版本。工具调用不生效LLM直接回复1. 工具描述JSON Schema不够清晰。2. 系统提示词未引导LLM使用工具。3. 给LLM的上下文未包含工具定义。4. 模型本身工具调用能力弱。1. 使用helper/prompt-debugger.js查看发送给LLM的完整提示词确认工具定义已包含且格式正确。2. 在系统提示词中明确要求“你可以使用以下工具…”。3. 检查代码逻辑确保每次请求都携带了工具信息。4. 尝试更换一个在工具调用上表现更好的指令微调模型如Qwen2.5-7B-Instruct。ReAct智能体陷入死循环1. LLM未能生成正确的Thought/Action/Final Answer格式。2. 问题过于复杂超出模型规划能力。3. 循环退出条件设置不当。1. 在提示词中强化输出格式要求并提供更清晰的示例few-shot。2. 增加最大循环次数限制避免无限循环。3. 在“观察”步骤后可以加入一些启发式判断如果多次尝试无效则主动终止。响应速度非常慢1. 模型太大硬件跟不上。2. 上下文长度设置过长。3. 未使用流式生成等待全部生成完毕才返回。1. 换用更小或量化等级更高的模型。2. 适当限制maxTokens和上下文窗口。3. 参考coding示例实现流式响应提升用户体验。记忆检索返回无关信息1. 检索策略过于简单如只取最近N条。2. 记忆未进行向量化无法做语义搜索。1. 实现基于向量相似度的检索。将记忆片段和当前问题转换为向量计算余弦相似度返回最相关的几条。2. 引入记忆摘要功能压缩旧记忆保留核心信息。6.2 调试利器PromptDebugger的使用项目提供的helper/prompt-debugger.js是一个极其宝贵的工具。智能体开发中很多问题源于“你以为你给了LLM这样的提示但实际上它收到的是那样的”。这个调试器能打印出发送给LLM的完整、格式化后的提示词包括系统提示、对话历史、工具定义等所有内容。使用心得在遇到智能体行为异常时第一反应就应该是用调试器把prompt打出来。仔细检查工具定义是否正确嵌入历史对话的格式是否符合预期当前的系统指令是否被意外覆盖百分之八十的“智能体不智能”问题通过审查原始提示词都能找到原因。6.3 延伸学习与资源推荐完成本项目后你已具备了扎实的底层知识。接下来可以深入框架带着你已理解的核心概念去官方学习LangChain.js或LangGraph.js。你会发现它们的文档和源码变得异常亲切你能看懂Runnable、Chain、AgentExecutor这些抽象背后的意图。探索更多架构研究AutoGPT的“Planning - Execution - Feedback”循环或CrewAI的多智能体协作模式。你现在有能力去剖析它们的实现思路。投入实战用你学到的知识结合LangChain这样的成熟框架去构建一个真正的应用。例如一个能联网搜索、处理文档、并基于知识库回答问题的个人研究助手。这个项目最大的价值是给了你一张AI智能体领域的“底层地图”。当地图在手无论上面的建筑各种框架如何变化翻新你都知道脚下的路该怎么走。从“知其然”到“知其所以然”这种深度理解带来的自信和掌控感是任何速成教程都无法给予的。现在从运行node intro/intro.js开始你的建造之旅吧。