第七章笔记:从零构建智能体框架 HelloAgents(详细版)
目标:把"会用框架"升级为"能造框架"。本章以版本迭代方式构建 HelloAgents,并将智能体知识点用工程化方式串讲落地。最终框架将支撑后续章节(RAG/Memory/上下文工程/协议等)高级案例。
7.1 框架整体架构设计
7.1.1 为什么需要自建 Agent 框架
(1)市面框架的快速迭代与局限性(四大痛点)
-
过度抽象导致复杂性
- 为追求通用性引入多层抽象与配置
- 初学者要理解大量概念(Chain/Agent/Tool/Memory/Retriever...)才能做简单任务
-
快速迭代造成不稳定
- 商业化框架 API 经常变更,升级后旧代码不可用
- 维护成本高、CI/部署频繁故障
-
黑盒化实现逻辑
- 关键机制封装过深,难以理解 Agent 内部工作过程
- 遇到问题只能依赖文档/社区;社区不活跃时反馈周期长
-
依赖关系复杂
- 依赖包多、体积大、与现有项目容易发生依赖冲突
(2)从使用者到构建者:能力跃迁的价值
- 深度理解 Agent 原理:思考过程、工具调用、设计模式优劣
- 完全控制权:可以精确调优,不受第三方框架理念束缚
- 系统设计能力:模块化/接口抽象/异常处理/工程规范
(3)定制化需求与深度掌握的必要性
- 垂直领域优化:金融/医疗/教育需要特定提示词、工具、安全策略
- 性能与资源可控:响应时间、内存、并发能力要求严格
- 教学透明性:需要可观测/可解释的构建过程,便于学习复现
7.1.2 HelloAgents 的设计理念(四个核心)
(1)轻量级 + 教学友好
- 核心代码按章节拆分,保证"可读、可理解、可复现"
- 极简依赖:尽量只用 OpenAI 官方 SDK + 必要基础库
- 出问题可直接定位框架源码,而不是依赖地狱排查
(2)基于标准 API 的务实选择
-
以 OpenAI 兼容接口为"事实标准"
-
好处:
- 迁移/集成成本低(很多厂商兼容这套接口)
- 学习成本低(不用再学一套抽象)
(3)渐进式学习路径
- 每章在前一章基础上迭代,提供可 pip 安装的历史版本
- 推荐路径:先体验 → 再实现
- 本章为后续高级模块铺底(RAG/Memory/协议等)
(4)统一的"工具"抽象:万物皆为工具
-
除核心 Agent 外,Memory/RAG/RL/MCP...全部统一为 Tool
-
目的:
- 减少不必要抽象
- 让学习者抓住核心:Agent 调用 Tool 完成任务
7.1.3 本章学习目标与目录结构(框架蓝图)
hello-agents/
├── hello_agents/
│ ├── core/
│ │ ├── agent.py
│ │ ├── llm.py
│ │ ├── message.py
│ │ ├── config.py
│ │ └── exceptions.py
│ ├── agents/
│ │ ├── simple_agent.py
│ │ ├── react_agent.py
│ │ ├── reflection_agent.py
│ │ └── plan_solve_agent.py
│ ├── tools/
│ │ ├── base.py
│ │ ├── registry.py
│ │ ├── chain.py
│ │ ├── async_executor.py
│ │ └── builtin/
│ │ ├── calculator.py
│ │ └── search.py
-
分层原则:分层解耦 / 职责单一 / 接口统一
-
快速体验安装:
pip install "hello-agents==0.1.1"(python>=3.10)
7.2 HelloAgentsLLM 扩展(模型调用中枢)
7.2 目标(三件事)
- 多提供商支持:OpenAI / ModelScope / 智谱等
- 本地模型集成:VLLM / Ollama(生产级推理方案)
- 自动检测机制:根据环境推断 provider,降低配置负担
7.2.1 支持多提供商:通过继承扩展 Provider
核心思路
-
不直接改库源码
-
用继承重写
__init__:provider == modelscope→ 执行你的自定义逻辑- 否则
super().__init__(...)保留父类能力
关键点
- 解析 provider 特有环境变量(如
MODELSCOPE_API_KEY) - 设置默认 base_url / 默认模型
- 构建 OpenAI SDK Client(因为 provider 兼容 OpenAI API)
7.2.2 本地模型调用:VLLM vs Ollama
VLLM
-
特点:高吞吐推理(PagedAttention、连续批处理)
-
启动 OpenAI 兼容服务(示例)
python -m vllm.entrypoints.openai.api_server --model ... --port 8000
-
HelloAgents 接入:当成 provider + base_url + dummy api_key
Ollama
- 特点:极简模型管理与运行
- 默认 OpenAI 兼容地址:
http://localhost:11434/v1 - 接入方式同理
统一收益
- Agent 代码不用改,切换云端/本地只改配置即可
- 有利于:成本控制/隐私保护/离线运行
7.2.3 自动检测机制(_auto_detect_provider + _resolve_credentials)
_auto_detect_provider:推断 provider 的优先级
-
最高优先级:特定环境变量
- 例如:
MODELSCOPE_API_KEY/OPENAI_API_KEY/ZHIPU_API_KEY...
- 例如:
-
次高优先级:base_url 判断
-
域名特征识别云服务商(
api-inference.modelscope.cn等) -
端口识别本地推理:
:11434→ ollama:8000→ vllm
-
-
辅助:API key 格式识别
- 例如前缀
ms-等
- 例如前缀
-
都不满足 → 返回
"auto"(走通用配置)
_resolve_credentials:根据 provider 解析凭证
-
选定 provider 后:
- 优先从 provider 专属 env 读 key
- 给默认 base_url
- 兜底读
LLM_API_KEY、LLM_BASE_URL
7.3 框架接口实现(核心三件套)
7.3.1 Message:统一消息系统
-
目标:规范对话上下文的传递结构,兼容 OpenAI 格式
-
关键设计:
role限制为Literal["user","assistant","system","tool"]timestamp+metadata为可观测与扩展预留to_dict()输出 OpenAI API 所需结构
-
设计原则:对内丰富,对外兼容
7.3.2 Config:集中化配置管理
-
提供默认配置,保证"零配置可运行"
-
支持从环境变量覆盖:
debug/log_level/temperature/max_tokens...
-
目的:
- 统一参数入口
- 多环境部署更容易
7.3.3 Agent 抽象基类
-
用
ABC + @abstractmethod强制子类实现run -
内置历史记录管理:
add_message / get_history / clear_history
-
统一入口:
- 所有 Agent 都可
agent.run(...)运行
- 所有 Agent 都可
-
可打印身份:
__str__输出 agent 名称与 provider
7.4 Agent 范式的框架化实现(4种经典范式 + 新增)
7.4 重构目标(三点)
- 提示词工程系统化:从任务特化 → 通用化 + 强约束格式
- 接口标准化:统一初始化参数、run 签名、历史管理
- 高度可配置:自定义 prompt、策略、参数
7.4.1 SimpleAgent(基础对话 + 可选工具调用)
MySimpleAgent 的核心能力
- 基础对话
- 可选工具调用(prompt 约束版)
- 支持多轮工具迭代
- 支持 stream_run(流式输出)
- 动态工具管理(add/remove/list)
工具调用协议(Prompt 约束)
-
约定格式:
[TOOL_CALL:{tool_name}:{parameters}]
-
Agent 解析工具调用:
_parse_tool_calls:正则匹配_execute_tool_call:ToolRegistry 执行- 将结果作为"工具执行结果"插回 messages,再让模型生成最终回复
-
安全点(隐含要求):
- 控制迭代次数
max_tool_iterations - 工具执行 try/except 防崩溃
- 控制迭代次数
7.4.2 ReActAgent(推理-行动循环)
Prompt 模板改进点
-
强制每次输出包含:
Thought:Action: tool[input]或Finish[answer]
-
约束"每次只能执行一个步骤",避免混乱
-
工具描述
{tools}与历史{history}注入 prompt
框架化实现的结构化流程
- 构建 prompt(工具描述 + 历史 + question)
- 调用 llm.invoke
- 解析 Thought/Action
- 若 Finish → 结束
- 若 tool 调用 → 执行工具,将 Observation 加入历史
- 限制 max_steps 防无限循环
7.4.3 ReflectionAgent(执行-反思-优化)
-
提供默认通用 prompts:
- initial / reflect / refine
-
支持 custom_prompts 适配特定领域(如代码生成)
-
典型流程:
- 初次回答
- 反思找问题
- 根据反馈 refine
- 循环直到达到次数/满意度
7.4.4 PlanAndSolveAgent(规划-执行)
- Planner 输出 强制 Python list 格式,便于解析与稳定执行
- Executor 每次只解决"当前步骤",输出该步骤结果
- 加强异常处理,避免解析失败导致崩溃
- 支持自定义 prompts(例如数学专用)
7.4.5 FunctionCallAgent(原生函数调用)
-
0.2.8 后引入
-
基于 OpenAI 原生 tools/function calling schema
-
相比 prompt 约束式工具调用:
- 鲁棒性更强(结构化参数、模型更不易"编造格式")
-
核心能力(摘要):
- 构建 tool schema
- 从响应提取文本
- 解析 JSON 参数并做类型转换
- 调用底层
client.chat.completions.create(...)
7.5 工具系统(Tooling)
7.5 学习目标(三点)
- 统一工具抽象与管理
- 实战:自定义工具开发
- 高级整合与优化(多源搜索、工具链、异步并行、容错)
7.5.1 工具基类与注册机制
Tool 基类抽象(统一接口)
-
必须实现:
run(parameters: Dict[str,Any]) -> strget_parameters() -> List[ToolParameter]
-
Tool 具备自描述能力:
- name / description / parameter schema
ToolParameter 参数系统
-
字段:name/type/description/required/default
-
支撑:
- 参数验证
- 自动文档
- schema 转换
ToolRegistry(工具管理中枢)
-
两种注册方式:
register_tool(tool_obj):复杂工具register_function(name, desc, func):快速集成简单函数
-
工具发现:
get_tools_description()生成 prompt 工具清单
-
生成 OpenAI function calling schema:
to_openai_schema()(给 FunctionCallAgent 用)
7.5.2 自定义工具开发:My Calculator
-
用
ast.parse做安全表达式求值(避免 eval 风险) -
支持四则运算 + sqrt + pi
-
用
ToolRegistry.register_function快速注册:- tool_name:
"my_calculator"
- tool_name:
-
测试:
- 直接调用 registry.execute_tool
- 与 SimpleAgent 结合:先工具算,再让 LLM 把结果组织成自然语言
7.5.3 多源搜索工具(Tavily + SerpApi)
内置 SearchTool 的设计亮点
-
backend =
"hybrid":智能选择后端 -
自动检测可用性(API key + 依赖库)
-
降级容错策略:
- 优先 Tavily(AI 优化搜索)
- Tavily 失败 → fallback SerpApi
- 都不可用 → 提示配置 API key
统一结果格式化
- 把不同引擎输出整理为统一结构(标题/摘要/来源)
- 便于 Agent 接入与后续处理
自定义 MyAdvancedSearchTool(类方式)
- 类方式适合维护状态(client/配置/可用后端列表)
- 注册方式:把
search_tool.search当函数注册到 ToolRegistry
7.5.4 高级特性
(1)工具链 ToolChain(串联多工具)
-
用"步骤列表"定义链:
- tool_name
- input_template(支持变量替换)
- output_key(存入上下文供后续步骤引用)
-
ToolChainManager 管理多个 chain(注册/执行/列表)
(2)异步工具执行 AsyncToolExecutor(线程池并行)
-
适用:I/O 密集型工具(网络请求、文件读取、数据库查询)
-
设计:
execute_tool_async:run_in_executorexecute_tools_parallel:gather 并行执行多个工具任务
-
注意:
- CPU 密集型任务线程池效果有限(GIL、资源争用)
- 外部服务限流要处理(重试/退避/并发限制)
7.6 本章小结(你应该带走什么)
-
你完成了 HelloAgents 框架从 0 到 1 的核心骨架:
- LLM 调用中枢(多 provider + 本地推理 + 自动检测)
- 核心接口(Message/Config/Agent)
- 四种经典 Agent 范式框架化(Simple/ReAct/Reflection/PlanAndSolve)
- 工具系统(Tool/Registry/schema/多源搜索/链式/异步)
-
本章不是终点,而是后续章节的"地基":
- 第八章:Memory + RAG
- 第九章:上下文工程(消息系统扩展)
- 第十章:协议/工具扩展(MCP 等)
习题区:要点提示与答题框架(按题目逐条拆解)
你后续写答案时可以直接用这些小标题填充内容。
A. 7.1.1 四个局限性:结合某框架的实践经验说明如何影响效率
可从以下角度写:
- 学习成本:理解概念、调试成本
- 版本升级:API 变化导致重构
- 黑盒:无法定制/定位 bug
- 依赖冲突:与现有项目不兼容、部署困难
- 结合案例:例如 LangChain、AutoGen、LlamaIndex...(任选一个你实际用过的)
B. "万物皆为工具"优势与局限
优势(写 3-5 点)
- 统一抽象,学习路径更短
- 模块可插拔(RAG/Memory/MCP 都是 tool)
- 更容易做 registry / schema / 自动文档
- 更利于观测与测试(工具可单测)
- 工具链/并行执行更自然
局限(写 2-4 点)
- 过度工具化可能丢失"长期状态"语义(Memory 其实不是一次性调用)
- Tool 返回 string 过于弱类型,复杂结构需额外规范
- Tool 调度策略/权限/安全可能需要额外体系
- "工具即万物"可能导致概念混淆(例如 Planner/Executor 是否也算工具?)
举例:
- 把 Memory 当 Tool:检索/写入是 tool,但"长期一致性/压缩策略"需要框架层支持
C. 第四章从零实现 vs 本章框架化:具体改进点
可以写成对比表(建议)
- 统一接口(run、history、Message)
- 配置集中化(Config/from_env)
- 工具系统可复用(ToolRegistry、schema)
- prompt 标准化与可配置(custom_prompt)
- 异常体系、最大步数/迭代限制、更稳定
- 可扩展目录结构(core/agents/tools)
如果你设计框架优先原则:
- 单一职责、低耦合、高内聚
- 约定优于配置
- 可观测性(日志、trace)
- 安全优先(参数验证、沙盒)
- 可测试性(组件可单测)
D. 7.2 实践题:扩展一个新 provider + 自动检测
写作要点:
-
通过继承 HelloAgentsLLM
-
增加
_auto_detect_provider检查新 env(如ANTHROPIC_API_KEY) -
在
_resolve_credentials增加 provider 分支 -
如果该 provider 不兼容 OpenAI API:
- 你需要额外适配 client 层(或使用兼容网关)
E. 自动检测优先级分析题
题目:同时设置 OPENAI_API_KEY 与 LLM_BASE_URL="http://localhost:11434/v1" 会选谁?
-
按文中优先级:先检查特定服务商 env → 会选 openai
-
讨论是否合理:
- 合理:显式 provider key 优先,避免误把生产 key 环境当本地
- 不合理场景:用户明确想走本地,但忘删 OPENAI_API_KEY
- 改进:允许用户显式
provider="ollama"覆盖;或增加一个强制开关
F. 搜索了解 SGLang,并对比 VLLM/SGLang/Ollama
建议写法(表格维度):
- 易用性:安装、启动、模型管理
- 资源占用:显存/内存、KV cache 管理
- 推理速度:吞吐/延迟、并发能力
- 推理精度:一般取决于模型本身,但推理策略/量化可能影响
- 生态与兼容:OpenAI API 兼容性、工具链支持
注意:这题如果你要"最新特性",需要联网检索;你也可以把对比框架写好,之后填入调研结论。
G. 7.3:Pydantic、设计模式、单例
Message 用 Pydantic 的优势
- 类型验证、默认值、序列化方便
- 数据结构稳定,减少脏数据
- 便于扩展 metadata、timestamp
- 错误更早暴露(开发期)
run + _execute:是什么模式?
-
常见叫法:模板方法模式(Template Method)
-
好处:
- 对外统一入口 run
- 内部把可变步骤留给子类 _execute
- 保持流程一致性,子类只改差异点
单例模式与配置
-
单例:全局唯一实例,保证一致配置源
-
配置需要单例的原因:
- 避免多个 Config 实例不一致导致行为不确定
- 方便全局访问与热更新策略
-
不用单例的问题:
- 不同模块读到不同配置副本
- 难排查"为什么同样参数行为不同"
H. 7.4:实践题三连
ReAct 框架化 3 个改进点(示例)
- 工具通过 ToolRegistry 接入(可插拔)
- prompt 强约束格式 + history 注入更稳定
- max_steps 限制 + 统一 history 管理更安全
- 可配置 custom_prompt(扩展不同任务域)
Reflection 加质量评分机制(思路)
- reflect 后增加一个
score_prompt - LLM 输出分数(如 1-10)
- 若
score >= threshold→ 提前终止 - 否则继续 refine
- 注意:要防止模型乱报分,可加格式约束与解析失败兜底
Tree-of-Thought Agent(设计要点)
- 每步生成 K 条候选思路(branching)
- 用评分器(LLM 或规则)选择最优路径(best-first / beam search)
- 迭代直到终止条件(深度/分数/完成标记)
- 与 ToolRegistry 结合:每条路径可调用工具
I. 7.5:工具接口、多返回值、工具链与并行
为什么强制统一接口?
- Agent 调度工具时无需关心工具内部差异
- 工具可替换、可组合、可测试
- 方便 schema 化与 function calling
搜索工具返回多个值怎么办?
建议三种方案:
- 返回 JSON 字符串(结构化文本)
- 返回统一 Result 对象(但框架当前约定 string,需要扩展)
- Message.metadata 存结构化数据(对话层存引用)
工具链场景(至少 3 工具)
例:研究报告生成
- search → summarize → cite_formatter
输出流程图可用 Mermaid(见下一节建议)
并行执行何时提升性能?
- 多个 I/O 工具可并行(网络请求、多个搜索源)
- 工具之间无依赖(不需要上一步结果)
- 注意外部 API 限流/配额/线程数
J. 框架扩展:流式输出、多轮对话管理、插件系统
1)流式输出(Agent 实时返回)
设计方案要点:
- LLM 层提供
stream_invoke - Agent 增加
stream_run - Message 记录最终完整输出(流式过程中累计)
- Tool calling 的流式更复杂:需要边生成边检测工具 call(可延后)
2)多轮对话管理:分支与回溯
需要新增:
- ConversationManager:管理多个 session
- Branch:保存分叉点(history snapshot)
- Rewind:回退到某个 message id
与 Message 集成: - Message 增加
id/parent_id/branch_id(或放 metadata)
3)插件系统(第三方扩展)
目标:不改 core 即可新增 Agent/Tool
关键接口:
- Plugin 基类(name/version/register(registry))
- Entry points / 动态加载(Python packaging)
- PluginRegistry:发现、加载、隔离、冲突处理
建议画 Mermaid 架构图(你写作时可用)
附:建议的"框架化复习清单"(自测)
- 我能解释 HelloAgents 的分层结构与每层职责
- 我能实现一个新的 provider(含自动检测)
- 我能实现一个 Tool,并注册到 ToolRegistry
- 我能实现一个新 Agent(继承 Agent 基类)
- 我能把 ToolChain 串联 3 个工具完成任务
- 我能解释 ReAct/Reflection/PlanSolve 的运行流程与差异