第七章《构建你的 Agent 框架》习题答案|逐章学习笔记 #784
Unanswered
jarvanstack
asked this question in
💬 Exercises & Q&A
Replies: 0 comments
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Uh oh!
There was an error while loading. Please reload this page.
原章链接:第七章 构建你的 Agent 框架
1. 自建框架与“万物皆工具”
主流框架常见的学习成本、抽象泄漏、版本变化和黑盒调试会直接拖慢开发:简单功能要跨多层对象;错误栈落在框架内部;升级破坏提示/状态;隐式重试与消息转换使 token、延迟和失败原因难追踪。框架带来的收益只有超过这些复杂度时才值得。
把 Memory、RAG、MCP 统一为工具的优势是注册、schema、权限、观测和执行接口一致,Agent 核心保持小,模块可按需替换。例如
memory.search与web.search都可被 ReAct 调用。局限是 Memory 也参与每轮上下文构建和生命周期管理,若只当离散工具,模型可能忘记调用;流式资源、订阅和事务也不自然。应保留统一 Tool 接口,同时允许框架级 hooks/middleware 处理横切能力。框架化相比第四章的脚本提供统一 LLM、Message、Agent 生命周期、工具注册、配置和错误处理,减少重复代码并易于测试组合。我会优先考虑:最小核心、显式状态、稳定接口、结构化错误、依赖注入、异步/流式、可观测、权限默认拒绝、可重放测试,以及扩展点不破坏核心。
2. 模型供应商与本地推理
以 Anthropic 为例,可用继承适配其环境变量和默认地址;生产代码还应把不同消息/工具协议转换放在 Adapter,而不是散落在 Agent:
按教材实现,同时设置
OPENAI_API_KEY和本地 OllamaLLM_BASE_URL时会先命中特定服务商变量,选择openai,随后又可能把通用本地 URL 当 OpenAI base URL。这种冲突很隐蔽。更合理的规则是:显式构造参数 > 显式provider> 与 base URL 匹配的配置 > 专用环境变量 > 推断;发现 Key 与 URL 冲突时 fail fast,而不是静默选择。同一权重、精度和采样参数下,推理引擎不应改变模型理论精度;量化、内核、截断和采样默认值才可能造成差异。速度不能凭品牌判断,必须用自身模型、上下文长度、并发和硬件测 TTFT、ITL、吞吐、显存与正确率。参考 vLLM 文档、SGLang 文档 和 Ollama 文档。
3. Message、模板方法与单例
Pydantic 能做运行时类型/schema 验证、默认值、序列化、JSON Schema、嵌套模型和清晰错误,使消息在网络、存储和不同 Agent 间保持契约;仍需版本字段以支持升级。
公开
run固定通用流程、抽象_execute由子类实现,属于模板方法模式,并结合抽象基类。好处是日志、计时、异常、权限和 callbacks 只写一次,子类只关心策略;_execute不应绕过run直接被外部调用。单例保证进程内读取同一配置,避免重复加载和不同实例值不一致。但全局单例会污染测试、阻碍多租户和热更新。更推荐不可变配置对象 + 依赖注入;若使用单例,需线程安全初始化、显式 reset(仅测试)和密钥不出现在 repr/log。多进程下单例也不能替代集中配置服务。
4. Agent 范式扩展
框架化 ReAct 的具体改进包括:统一 LLM 接口便于换模型;Tool Registry/Executor 分离描述、校验和执行;Message 统一历史;公共 run 负责日志与异常;结构化配置替代散落常量。这样每个部件能单测、替换和复用。
Reflection 评分应输出结构化 rubric,而非让模型随意给一个数字;达到阈值且关键项无失败才停止:
Tree-of-Thought Agent 的最小设计:每层从当前候选生成
b个思路;独立 evaluator 对每个候选按可行性、约束和新颖性评分;保留 top-k;达到答案或深度/预算上限终止。每个节点保存 parent、thought、state、score 和是否已扩展。重要的是在可验证环境中评估候选,并去重/限制分支,否则成本按b^d爆炸。5. 工具系统
统一
execute(input) -> ToolResult让 Agent、链、重试、权限、日志和测试不关心具体工具。多值结果应返回结构化对象,而不是 tuple:{"ok":true,"data":{"items":[{"title":"...","snippet":"...","url":"..."}]},"meta":{"latency_ms":42}}三工具场景:论文 DOI → 元数据查询 → PDF 获取/解析 → 引用格式化。任何阶段失败都保留已完成结果并给出可恢复错误。
线程池适合相互独立、I/O 密集、底层库阻塞且会释放等待时间的工具,如并行查天气/库存。CPU 密集 Python 代码受 GIL 限制,应使用进程池/原生实现;有依赖、写同一资源、受严格限流或非线程安全的工具不能盲目并行。异步 HTTP 首选原生 async,线程池是兼容层。
6. 流式、对话与插件系统
流式输出:LLM 增加
astream()产生TokenDelta/ToolCallDelta/Usage/Done/Error;Agent 的run_stream()把内部事件转换为统一 AgentEvent;普通run()消费事件并拼接最终结果以保持兼容;工具和 Reflection 也可发进度事件。取消信号、背压和部分输出持久化必须贯穿各层。多轮对话新增
ConversationStore、Conversation、Branch、Checkpoint和HistoryPolicy。Message 增加id/parent_id/conversation_id/branch_id/created_at;分支通过 parent 链形成 DAG,回溯创建新分支而非覆盖历史;压缩摘要保留来源消息范围。持久层使用乐观锁防止并发写乱序。插件 manifest 声明名称、版本、框架兼容范围、入口点、能力和权限;接口提供
register(registry)/shutdown();注册项必须有唯一命名空间和 schema。加载前校验签名/来源,第三方插件默认最小权限,冲突或不兼容时拒绝启动。框架核心只依赖协议,不导入插件实现。All reactions