从0到1开发一个Agent(智能体)框架
推荐语
从零构建智能体框架HelloAgents,带你深入理解Agent工作原理,获得完全控制权。
核心内容:
1. 市面主流框架的局限性分析
2. 自建框架带来的能力跃迁价值
3. HelloAgents框架的设计思路与优势

杨芳贤
53AI创始人/腾讯云(TVP)最具价值专家
Datawhale干货
作者:陈思州,Datawhale成员
Datawhale干货
作者:陈思州,Datawhale成员
为了便于大家更系统的入门和学习AI智能体,最近,我们将为大家分享系列内容:Agent合集" data-itemshowtype="" linktype="text" data-linktype="2">Agent合集
在前面的文章中,我们讲解了智能体的基础知识,并体验了主流框架带来的开发便利。从本文开始,我们将进入一个更具挑战也更有价值的阶段:从零开始,逐步构建一个智能体框架 —— HelloAgents。
在智能体技术快速发展的今天,市面上已经存在众多成熟的Agent框架。那么,为什么我们还要从零开始构建一个新的框架呢?
1)市面框架的快速迭代与局限性
智能体领域是一个快速发展的领域,随时会有新的概念产生,对于智能体的设计每个框架都有自己的定位和理解,不过智能体的核心知识点是一致的。
2)从使用者到构建者的能力跃迁
构建自己的Agent框架,实际上是一个从"使用者"向"构建者"转变的过程。这种转变带来的价值是长远的。
3)定制化需求与深度掌握的必要性
在实际应用中,不同场景对智能体的需求差异巨大,往往都需要在通用框架基础上做二次开发。
构建一个新的Agent框架,关键不在于功能的多少,而在于设计理念是否能真正解决现有框架的痛点。HelloAgents框架的设计围绕着一个核心问题展开:如何让学习者既能快速上手,又能深入理解Agent的工作原理?
当你初次接触任何成熟的框架时,可能会被其丰富的功能所吸引,但很快就会发现一个问题:要完成一个简单的任务,往往需要理解Chain、Agent、Tool、Memory、Retriever等十几个不同的概念。每个概念都有自己的抽象层,学习曲线变得异常陡峭。这种复杂性虽然带来了强大的功能,但也成为了初学者的障碍。HelloAgents框架试图在功能完整性和学习友好性之间找到平衡点,形成了四个核心的设计理念。
1)轻量级与教学友好的平衡
一个优秀的学习框架应该具备完整的可读性。HelloAgents将核心代码按照章节区分开,这是基于一个简单的原则:任何有一定编程基础的开发者都应该能够在合理的时间内完全理解框架的工作原理。在依赖管理方面,框架采用了极简主义的策略。除了OpenAI的官方SDK和几个必要的基础库外,不引入任何重型依赖。如果遇到问题时,我们可以直接定位到框架本身的代码,而不需要在复杂的依赖关系中寻找答案。
2)基于标准API的务实选择
OpenAI的API已经成为了行业标准,几乎所有主流的LLM提供商都在努力兼容这套接口。HelloAgents选择在这个标准之上构建,而不是重新发明一套抽象接口。这个决定主要是出于几点动机。首先是兼容性的保证,当你掌握了HelloAgents的使用方法后,迁移到其他框架或将其集成到现有项目中时,底层的API调用逻辑是完全一致的。其次是学习成本的降低。你不需要学习新的概念模型,因为所有的操作都基于你已经熟悉的标准接口。
3)渐进式学习路径的精心设计
HelloAgents提供了一条清晰的学习路径。我们将会把每一章的学习代码,保存为一个可以pip下载的历史版本,因此无需担心代码的使用成本,因为每一个核心的功能都将会是你自己编写的。这种设计让你能够按照自己的需求和节奏前进。每一步的升级都是自然而然的,不会产生概念上的跳跃或理解上的断层。值得一提的是,我们这一章的内容,也是基于前六章的内容来完善的。同样,这一章也是为后续高级知识学习部分打下框架基础。
4)统一的“工具”抽象:万物皆为工具
为了彻底贯彻轻量级与教学友好的理念,HelloAgents在架构上做出了一个关键的简化:除了核心的Agent类,一切皆为Tools。在许多其他框架中需要独立学习的Memory(记忆)、RAG(检索增强生成)、RL(强化学习)、MCP(协议)等模块,在HelloAgents中都被统一抽象为一种“工具”。这种设计的初衷是消除不必要的抽象层,让学习者可以回归到最直观的“智能体调用工具”这一核心逻辑上,从而真正实现快速上手和深入理解的统一。
以我们的HelloAgents为例,让我们先看看核心学习内容:
在开始编写具体代码之前,我们需要先建立一个清晰的架构蓝图。HelloAgents的架构设计遵循了"分层解耦、职责单一、接口统一"的核心原则,这样既保持了代码的组织性,也便于按照章节扩展内容。
模块一:核心框架层
1、HelloAgentsLLM 扩展
本节重点给大家介绍我们的升级方向,代码不会展开。代码实践查看:https://datawhalechina.github.io/hello-agents,我们将把这个基础客户端,改造为一个更具适应性的模型调用中枢。本次升级主要围绕以下三个目标展开:
我们之前定义的HelloAgentsLLM类,已经能够通过api_key和base_url这两个核心参数,连接任何兼容 OpenAI 接口的服务。这在理论上保证了通用性,但在实际应用中,不同的服务商在环境变量命名、默认 API 地址和推荐模型等方面都存在差异。如果每次切换服务商都需要用户手动查询并修改代码,会极大影响开发效率。为了解决这一问题,我们引入provider。其改进思路是:让HelloAgentsLLM在内部处理不同服务商的配置细节,从而为用户提供一个统一、简洁的调用体验。
为了在本地实现高性能、生产级的模型推理服务,社区涌现出了 VLLM 和 Ollama 等优秀工具。它们通过连续批处理、PagedAttention 等技术,显著提升了模型的吞吐量和运行效率,并将模型封装为兼容 OpenAI 标准的 API 服务。这意味着,我们可以将它们无缝地集成到HelloAgentsLLM中。
VLLM
VLLM 是一个为 LLM 推理设计的高性能 Python 库。它通过 PagedAttention 等先进技术,可以实现比标准 Transformers 实现高出数倍的吞吐量。下面是在本地部署一个 VLLM 服务的完整步骤:
首先,需要根据你的硬件环境(特别是 CUDA 版本)安装 VLLM。推荐遵循其官方文档进行安装,以避免版本不匹配问题。
安装完成后,使用以下命令即可启动一个兼容 OpenAI 的 API 服务。VLLM 会自动从 Hugging Face Hub 下载指定的模型权重(如果本地不存在)。服务启动后,便会在http://localhost:8000/v1地址上提供与 OpenAI 兼容的 API。
Ollama
Ollama 进一步简化了本地模型的管理和部署,它将模型下载、配置和服务启动等步骤封装到了一条命令中,非常适合快速上手。访问 Ollama 官方网站下载并安装适用于你操作系统的客户端。
安装后,打开终端,执行以下命令即可下载并运行一个模型(以 Llama 3 为例)。Ollama 会自动处理模型的下载、服务封装和硬件加速配置。
当你在终端看到模型的交互提示时,即表示服务已经成功在后台启动。Ollama 默认会在http://localhost:11434/v1地址上暴露 OpenAI 兼容的 API 接口。
接入HelloAgentsLLM
由于 VLLM 和 Ollama 都遵循了行业标准 API,将它们接入HelloAgentsLLM的过程非常简单。我们只需在实例化客户端时,将它们视为一个新的provider即可。
通过这种统一的设计,我们的智能体核心代码无需任何修改,就可以在云端 API 和本地模型之间自由切换。这为后续应用的开发、部署、成本控制以及保护数据隐私提供了极大的灵活性。
为了尽可能减少用户的配置负担并遵循“约定优于配置”的原则,HelloAgentsLLM内部设计了两个核心辅助方法:_auto_detect_provider和_resolve_credentials。它们协同工作,_auto_detect_provider负责根据环境信息推断服务商,而_resolve_credentials则根据推断结果完成具体的参数配置。
_auto_detect_provider方法负责根据环境信息,按照下述优先级顺序,尝试自动推断服务商:
最高优先级:检查特定服务商的环境变量这是最直接、最可靠的判断依据。框架会依次检查
MODELSCOPE_API_KEY,OPENAI_API_KEY,ZHIPU_API_KEY等环境变量是否存在。一旦发现任何一个,就会立即确定对应的服务商。次高优先级:根据
base_url进行判断如果用户没有设置特定服务商的密钥,但设置了通用的LLM_BASE_URL,框架会转而解析这个 URL。
域名匹配:通过检查 URL 中是否包含
"api-inference.modelscope.cn","api.openai.com"等特征字符串来识别云服务商。端口匹配:通过检查 URL 中是否包含
:11434(Ollama),:8000(VLLM) 等本地服务的标准端口来识别本地部署方案。
辅助判断:分析 API 密钥的格式在某些情况下,如果上述两种方式都无法确定,框架会尝试分析通用环境变量LLM_API_KEY的格式。例如,某些服务商的 API 密钥有固定的前缀或独特的编码格式。不过,由于这种方式可能存在模糊性(例如多个服务商的密钥格式相似),因此它的优先级较低,仅作为辅助手段。
一旦provider被确定(无论是用户指定还是自动检测),_resolve_credentials方法便会接手处理服务商的差异化配置。它会根据provider的值,去主动查找对应的环境变量,并为其设置默认的base_url。
通过三种方法,现在的HelloAgentsLLM具有以下显著优势:
表 1 HelloAgentLLM不同版本特性对比

如上表所示,这种演进体现了框架设计的重要原则:从简单开始,逐步完善。我们在保持接口简洁的同时,增强了功能的完整性。
在上节中,我们构建了HelloAgentsLLM这一核心组件,解决了与大语言模型通信的关键问题。不过它还需要一系列配套的接口和组件来处理数据流、管理配置、应对异常,并为上层应用的构建提供一个清晰、统一的结构。本节将讲述以下三个核心文件:
在智能体与大语言模型的交互中,对话历史是至关重要的上下文。为了规范地管理这些信息,我们设计了一个简易Message类。在后续上下文工程章节中,会对其进行扩展。
该类的设计有几个关键点。首先,我们通过typing.Literal将role字段的取值严格限制为"user","assistant","system","tool"四种,这直接对应 OpenAI API 的规范,保证了类型安全。除了content和role这两个核心字段外,我们还增加了timestamp和metadata,为日志记录和未来功能扩展预留了空间。最后,to_dict()方法是其核心功能之一,负责将内部使用的Message对象转换为与 OpenAI API 兼容的字典格式,体现了“对内丰富,对外兼容”的设计原则。
Config类的职责是将代码中硬编码配置参数集中起来,并支持从环境变量中读取。
首先,我们将配置项按逻辑划分为LLM配置、系统配置等,使结构一目了然。其次,每个配置项都设有合理的默认值,保证了框架在零配置下也能工作。最核心的是from_env()类方法,它允许用户通过设置环境变量来覆盖默认配置,无需修改代码,这在部署到不同环境时尤其有用。
Agent类是整个框架的顶层抽象。它定义了一个智能体应该具备的通用行为和属性,但并不关心具体的实现方式。我们通过 Python 的abc(Abstract Base Classes) 模块来实现它,这强制所有具体的智能体实现(如后续章节的SimpleAgent,ReActAgent等)都必须遵循同一个“接口”。
该类的设计体现了面向对象中的抽象原则。首先,它通过继承ABC被定义为一个不能直接实例化的抽象类。其构造函数__init__清晰地定义了 Agent 的核心依赖:名称、LLM 实例、系统提示词和配置。最重要的部分是使用@abstractmethod装饰的run方法,它强制所有子类必须实现此方法,从而保证了所有智能体都有统一的执行入口。此外,基类还提供了通用的历史记录管理方法,这些方法与Message类协同工作,体现了组件间的联系。
至此,我们已经完成了HelloAgents框架核心基础组件的设计与实现。
本节内容将在三种经典Agent范式(ReAct、Plan-and-Solve、Reflection)基础上进行框架化重构,并新增SimpleAgent作为基础对话范式。我们将把这些独立的Agent实现,改造为基于统一架构的框架组件。本次重构主要围绕以下三个核心目标展开:
SimpleAgent是最基础的Agent实现,它展示了如何在框架基础上构建一个完整的对话智能体。我们将通过继承框架基类来重写SimpleAgent。首先,在你的项目目录中创建一个my_simple_agent.py文件:
接下来,我们需要重写Agent基类的抽象方法run。SimpleAgent支持可选的工具调用功能,也方便后续章节的扩展:
现在我们实现工具调用的核心逻辑:
我们还可以为自定义Agent添加流式响应功能和便利方法:
创建一个测试文件test_simple_agent.py:
在本节中,我们通过继承Agent基类,成功构建了一个功能完备且遵循框架规范的基础对话智能体MySimpleAgent。它不仅支持基础对话,还具备可选的工具调用能力、流式响应和便利的工具管理方法。
框架化的 ReActAgent 在保持核心逻辑不变的同时,提升了代码的组织性和可维护性,主要是通过提示词优化和与框架工具系统的集成。
1)提示词模板的改进
保持了原有的格式要求,强调"每次只能执行一个步骤",避免混乱,并明确了两种Action的使用场景。
(2)重写ReActAgent的完整实现
创建my_react_agent.py文件来重写ReActAgent:
其初始化参数的含义如下:
框架化的ReActAgent将执行流程分解为清晰的步骤:
通过以上重构,我们将 ReAct 范式成功地集成到了框架中。核心改进在于利用了统一的ToolRegistry接口,并通过一个可配置、格式更严谨的提示词模板,提升了智能体执行思考-行动循环的稳定性。对于ReAct的测试案例,由于需要调用工具,所以统一放在文末提供测试代码。
由于这几类Agent已经实现过核心逻辑,所以这里只给出对应的Prompt。与之前专门针对代码生成的提示词不同,框架化的版本采用了通用化设计,使其适用于文本生成、分析、创作等多种场景,并通过custom_prompts参数支持用户深度定制。
你可以尝试根据第四章的代码,以及上文ReAct的实现,构建出自己的MyReflectionAgent。下面提供一个测试代码供验证想法。
与第四章自由文本的计划输出不同,框架化版本强制要求Planner以Python列表的格式输出计划,并提供了完整的异常处理机制,确保了后续步骤能够稳定执行。框架化的Plan-and-Solve提示词:
这一节仍然给出一个综合测试文件test_plan_solve_agent.py,可以自行设计实现。
在最后可以补充一款新的提示词,可以尝试实现custom_prompt载入自定义提示词。
如表2所示,通过这种框架化的重构,我们不仅保持了第四章中各种Agent范式的核心功能,还大幅提升了代码的组织性、可维护性和扩展性。所有Agent现在都共享统一的基础架构,同时保持了各自的特色和优势。
表 2 Agent不同章节实现对比

FunctionCallAgent是hello-agents在0.2.8之后引入的Agent,它基于OpenAI原生函数调用机制的Agent,展示了如何使用OpenAI的函数调用机制来构建Agent。 它支持以下功能: _build_tool_schemas:通过工具的description构建OpenAI的function calling schema _extract_message_content:从OpenAI的响应中提取文本 _parse_function_call_arguments:解析模型返回的JSON字符串参数 _convert_parameter_types:转换参数类型
这些功能可以使其具备原生的OpenAI Functioncall的能力,对比使用prompt约束的方式,具备更强的鲁棒性。
本节内容将在前面构建的Agent基础架构上,深入探讨工具系统的设计与实现。我们将从基础设施建设开始,逐步深入到自定义开发设计。本节的学习目标围绕以下三个核心方面展开:
统一的工具抽象与管理:建立标准化的Tool基类和ToolRegistry注册机制,为工具的开发、注册、发现和执行提供统一的基础设施。
实战驱动的工具开发:以数学计算工具为案例,展示如何设计和实现自定义工具,让读者掌握工具开发的完整流程。
高级整合与优化策略:通过多源搜索工具的设计,展示如何整合多个外部服务,实现智能后端选择、结果合并和容错处理,体现工具系统在复杂场景下的设计思维。
在构建可扩展的工具系统时,我们需要首先建立一套标准化的基础设施。这套基础设施包括Tool基类、ToolRegistry注册表,以及工具管理机制。
1)Tool基类的抽象设计
Tool基类是整个工具系统的核心抽象,它定义了所有工具必须遵循的接口规范:
这个设计体现了面向对象设计的核心思想:通过统一的run方法接口,所有工具都能以一致的方式执行,接受字典参数并返回字符串结果,确保了框架的一致性。同时,工具具备了自描述能力,通过get_parameters方法能够清晰地告诉调用者自己需要什么参数,这种内省机制为自动化文档生成和参数验证提供了基础。而name和description等元数据的设计,则让工具系统具备了良好的可发现性和可理解性。
2)ToolParameter参数定义系统
为了支持复杂的参数验证和文档生成,我们设计了ToolParameter类:
这种设计让工具能够精确描述自己的参数需求,支持类型检查、默认值设置和文档自动生成。
3)ToolRegistry注册表的实现
ToolRegistry是工具系统的管理中枢,它提供了工具的注册、发现、执行等核心功能,在这一节我们主要用到以下功能:
ToolRegistry支持两种注册方式:
4)工具发现与管理机制
注册表提供了丰富的工具管理功能:
这个方法生成的描述字符串可以直接用于构建Agent的提示词,让Agent了解可用的工具。
这个方法生成的schema可以直接用于原生的OpenAI SDK的工具调用。
有了基础设施后,我们来看看如何开发一个完整的自定义工具。数学计算工具是一个很好的例子,因为它简单直观,最直接的方式是使用ToolRegistry的函数注册功能。
让我们创建一个自定义的数学计算工具。首先,在你的项目目录中创建my_calculator_tool.py:
工具不仅支持基本的四则运算,还涵盖了常用的数学函数和常数,满足了大多数计算场景的需求。你也可以自己扩展这个文件,制作一个更加完备的计算函数。我们提供一个测试文件test_my_calculator.py帮助你验证功能实现:
通过这个简化的数学计算工具案例,我们学会了如何快速开发自定义工具:编写一个简单的计算函数,通过ToolRegistry注册,然后与SimpleAgent集成使用。为了更直观的观察,这里提供了图1,可以清晰理解代码的运行逻辑。

图 1 基于Helloagents的SimpleAgent运行工作流
在实际应用中,我们经常需要整合多个外部服务来提供更强大的功能。搜索工具就是一个典型的例子,它整合多个搜索引擎,能提供更加完备的真实信息。在第一章我们使用过Tavily的搜索API,在第四章我们使用过SerpApi的搜索API。因此这次我们使用这两个API来实现多源搜索功能。如果没安装对应的python依赖可以运行下面这条脚本:
1)搜索工具的统一接口设计
HelloAgents框架内置的SearchTool展示了如何设计一个高级的多源搜索工具:
这个设计的核心思想是根据可用的API密钥和依赖库,自动选择最佳的搜索后端。
2)TAVILY与SERPAPI搜索源的整合策略
框架实现了智能的后端选择逻辑:
这种设计体现了高可用系统的核心理念:通过降级机制,系统能够从最优的搜索源逐步降级到可用的备选方案。当所有搜索源都不可用时,明确提示用户配置正确的API密钥。
3)搜索结果的统一格式化
不同搜索引擎返回的结果格式不同,框架通过统一的格式化方法来处理:
基于框架的设计思想,我们可以创建自己的高级搜索工具。这次我们使用类的方式来展示不同的实现方法,创建my_advanced_search.py:
接下来可以测试我们自己编写的工具,创建test_advanced_search.py:
通过这个高级搜索工具的设计实践,我们学会了如何使用类的方式来构建复杂的工具系统。相比函数方式,类方式更适合需要维护状态(如API客户端、配置信息)的工具。
在掌握了基础的工具开发和多源整合后,我们来探讨工具系统的高级特性。这些特性能够让工具系统在复杂的生产环境中稳定运行,并为Agent提供更强大的能力。
1)工具链式调用机制
在实际应用中,Agent经常需要组合使用多个工具来完成复杂任务。我们可以设计一个工具链管理器来支持这种场景,这里借鉴了提到的图的概念:
2)异步工具执行支持
对于耗时的工具操作,我们可以提供异步执行支持:
基于以上的设计和实现经验,我们可以总结出工具系统开发的核心理念:在设计层面,每个工具都应该遵循单一职责原则,专注于特定功能的同时保持接口的统一性,并将完善的异常处理和安全优先的输入验证作为基本要求。在性能优化方面,利用异步执行提高并发处理能力,同时合理管理外部连接和系统资源。
回顾本文,我们完成了一项富有挑战的任务:一步步构建了一个基础的智能体框架——HelloAgents。这个过程始终遵循着“分层解耦、职责单一、接口统一”的核心原则。
在框架的具体实现中,我们再次实现了四种经典的Agent范式。从SimpleAgent的基础对话模式,到ReActAgent的推理与行动结合;从ReflectionAgent的自我反思与迭代优化,到PlanAndSolveAgent的分解规划与逐步执行。而工具系统作为Agent能力延伸的核心,其构建过程则是一次完整的工程实践。

大模型技术原理大模型技术架构大模型技术路线