设计取舍:为什么 Hermes(以及很多流行 agent)不用 LangChain / LangGraph

发布时间:2026/6/30 14:17:58
设计取舍:为什么 Hermes(以及很多流行 agent)不用 LangChain / LangGraph 说明Hermes 仓库里没有一句明确写下「我们不用 LangChain 因为……」的声明。 本文分两层行业层面的普遍原因通用分析与Hermes 代码中可实证看到的取向有源码依据标注出处。1. 前提agent 循环的内核其实很简单很多人以为编排 agent 必须依赖一个「框架」但核心循环本质上只是一个 whilewhile 未达终止条件: resp llm(messages, tools) if resp.tool_calls: 执行工具把结果 append 回 messages else: return resp.contentHermes 的run_conversationagent/conversation_loop.py本质就是这个。真正难的不是这个循环本身而是它周边的工程细节流式输出、中断、预算控制、上下文压缩、prompt 缓存、provider 故障切换、并发工具执行、错误分类与重试……而这些恰恰是通用框架抽象得最浅、最容易「挡路」的地方。这是理解「为什么很多项目不用框架」的关键。2. 行业层面流行 agent 项目常绕开框架的普遍原因适用于多数自研循环的项目Hermes、OpenHands、Aider、Codex CLI、Claude Code 等抽象与控制权错配。框架把 LLM 调用 / 消息 / 记忆 / 工具封装成对象Chain / Runnable / Graph 节点。但生产级 agent 需要对「发给模型的每一个 byte」精确控制——例如 Anthropic 的cache_control打在哪条消息、reasoning content 如何存、报错时怎样降级到 fallback 模型。框架抽象让这「最后一公里」更难做常被迫绕过框架 monkey-patch。打个比方框架像一台万能遥控器常见电器都能控但你家那台带最新功能的机器它偏偏少了一个键你只能拆开后盖直接接线。多 provider 的 API 形态差异。需要同时支持 OpenAI chat.completions、Anthropic Messages、Bedrock、Codex Responses 等不同 API 形态时框架的「统一 LLM 接口」往往滞后于各家最新特性新模型、新参数、reasoning、prompt caching追新被动。就像翻译软件总比原文晚一步各家模型刚发布的新能力框架要等下一个版本才支持而你想第一时间用上。调试与可读性。自研循环的 stack trace 直达自身代码框架常是多层抽象 回调报错栈深、行为隐式。长期维护项目更看重可读性。依赖与供应链风险。框架本身是庞大的传递依赖树版本变动频繁、API 不稳定放大供应链攻击面。版本 churn。LangChain 早期 API 变动剧烈LLMChain→ LCEL → LangGraph把核心逻辑绑在快速变动的框架上迁移成本高。3. Hermes 代码中可实证看到的取向有据可查极致依赖最小化 供应链防御。pyproject.toml有大段注释说明核心依赖全部精确钉死版本X.Y.Z不用范围起因是 2026-05 的 Mini Shai-Hulud 蠕虫攻击并明确写道smaller dependencies smaller blast radius for the next supply-chain attackprovider 专属依赖一律惰性安装tools/lazy_deps.py。一个把「依赖面积」当一等公民管理的项目自然不会引入 LangChain 这种重依赖。唯一的 LLM SDK 是openai2.24.0其余多 provider 全靠自研 Transport / Adapter 层agent/transports/、agent/*_adapter.py适配统一以 OpenAI 消息格式为中间表示。循环周边大量自研工程中断检查、agent/iteration_budget.py、agent/error_classifier.py故障切换、agent/context_compressor.py、agent/prompt_caching.py、agent/tool_executor.py并发工具执行。仅run_agent.py单文件就有约 5300 行循环相关模块合计上万行说明他们刻意投入去拥有这个循环而非外包给框架。并非「什么都自己造」。当愿意把控制权交出去时如把工具循环交给 OpenAI Codex app-serverHermes 会显式集成它反对的是「用通用框架替代自己的核心循环」而非反对一切集成。概括 Hermes 的「理由」把可控性与供应链安全当第一优先级而 agent 核心循环又足够简单自研的收益 框架带来的便利。4. 深入极致依赖最小化 供应链防御Hermes 的供应链防御不是口号而是落在pyproject.toml 四个具体模块里的多层机制。理解这套纪律就能明白「不用框架」为何是它的必然推论而非口味选择。触发这套设计的真实攻击hermes_cli/security_advisories.py与pyproject.toml注释都点名了同一起事件Mini Shai-Hulud worm2026-05—— 在 PyPI 上投毒了mistralai 2.4.6。这是一类「自我传播的供应链蠕虫」攻陷某维护者账号 → 发布带恶意代码的新版本 → 恶意代码在安装/运行时窃取更多凭证 → 用偷来的凭证投毒更多包滚雪球扩散。pyproject.toml写得很直白若当时 mistralai 用2.3.0,3这种范围声明则在该恶意版本被隔离前的几小时内每一次 install 都会自动拉到投毒版本。这就是钉死版本的直接动机。攻击类型 → 防御策略对照把 Hermes 的防御逐条拆开每条都对应一类具体攻击场景PyPI 投毒新版本蠕虫或被劫持账号发布恶意 X.Y.Z1。对应策略核心依赖全部精确钉死X.Y.Z配uv.lock锁定传递依赖新版本只能通过「人为改 pin 重新 lock code review」进入。证据pyproject.toml注释 [project.dependencies]全是。传递依赖爆炸面直接依赖虽少但间接拉进上百个包任一被投毒都中招。对应策略核心依赖最小化只有「每个 session 都用到」的包进 coreprovider/搜索/TTS/消息平台等专属依赖踢出核心改为惰性安装。证据pyproject.toml的「Scope rule」注释 tools/lazy_deps.py里的LAZY_DEPS。[all]连坐失败某 extra 的传递依赖被隔离导致整个[all]解析失败新装用户静默退化丢功能。对应策略把可选 backend 从[all]移到 lazy-install单包隔离只影响该功能不连累其它。证据tools/lazy_deps.pydocstring 的「Fragility」段 [all]注释。恶意 MCP 扩展包npx/uvx拉的第三方 MCP server 可能是投毒包。对应策略启动前查OSV 数据库命中MAL-*恶意软件公告就BLOCK——只拦确认的恶意软件、不拦普通 CVE且网络失败时放行fail-open。证据tools/osv_check.py::check_package_for_malware。配置劫持安装源恶意 config 把安装重定向到攻击者镜像、git 或本地路径。对应策略lazy-install只允许从 PyPI 按包名装不支持--index-url/githttps/file:只能装白名单 spec且仅作用于当前 venv绝不碰系统 Python。证据tools/lazy_deps.py的「Security model」段。已知 CVE 的依赖。对应策略在钉死版本上逐条标注 CVErequests/aiohttp/starlette/PyJWT/anthropic等升级有意为之。证据pyproject.toml内联的# CVE-2026-xxxxx注释。投毒包已装进用户环境防线突破后的检测兜底。对应策略每次 CLI/gateway 启动用importlib.metadata.version()比对已知被攻陷版本清单命中即告警 给修复指引用户可hermes doctor --ack id确认并持久化。证据hermes_cli/security_advisories.py的ADVISORIES。关键策略再展开钉死版本 lockfile策略 1范围声明把「何时拉到新版本」的决定权交给 PyPI 和时间钉死则收回到「一次显式人工提交」。代价是手动uv lock收益是攻击者没有自动到达用户的通道。pyproject 明确要求升级必须同时改 pin 并重新生成uv.lock「不要在没有书面理由时把范围加回来」。最小化 惰性安装策略 2、3—— blast radius 的核心原文smaller dependencies smaller blast radius for the next supply-chain attack的工程含义是核心依赖列表越短下次供应链攻击波及你的概率越低。所以anthropic、firecrawl、edge-tts、modal、mautrix、elevenlabs等几十个 provider 专属包全部移出 core改由lazy_deps.ensure(feature.name)在首次用到时现装。只用一家模型的用户永远不会把其它几十家 provider 的依赖树拉进攻击面。OSV 恶意软件拦截策略 4唯一一处「主动外呼查询」——在 agent 真正npx/uvx启动 MCP server之前先问 Google OSV API「这个包有没有MAL-*公告」。它故意只拦确认恶意软件、不拦普通 CVE避免误杀且fail-open网络失败放行不阻断正常使用。灵感来自 Block/goose 的扩展检查。为什么这套纪律天然排斥 LangChain把上面串起来LangChain/LangGraph 是重依赖自身又拖一棵庞大且高频变动的传递依赖树。对一个把「core 依赖必须短、每个包都要能 CVE 标注、可选依赖一律惰性化」当硬规矩的项目引入这种框架等于一次性破坏策略 1/2/3/6——blast radius 直接爆炸。所以「不用框架」不是孤立的口味而是这套供应链纪律的必然推论。补充依赖只是其中一层SECURITY.md的 trust model 显示供应链只是防御的一部分。Hermes 把所有「进入 agent 上下文的内容」web 抓取、邮件、gateway 消息、文件、MCP 响应、工具结果都列为不可信输入面另有tools/url_safety.py、tools/threat_patterns.py、tools/skills_guard.py、tools/skills_ast_audit.py、tools/tirith_security.py处理 prompt 注入与技能代码审计。依赖最小化解决「你装进来的代码可信吗」这些模块解决「跑起来后喂给模型的数据可信吗」。5. 不用框架的优缺点优点完全控制 prompt / 消息 / 缓存 / 重试 / 降级能第一时间用上各家模型新特性。依赖少、攻击面小、构建可复现、长期可维护。调试直观栈短行为显式。不受框架版本升级牵连。缺点要自己造很多轮子重试、压缩、记忆、工具 schema、并发、可观测性——Hermes 为此写了上万行成本真实存在。缺少生态即插即用LangChain 有海量现成 retriever / loader / 集成自研要逐个接。概念需自己沉淀图编排、状态机、checkpoint 等 LangGraph 直接提供的能力要自行设计Hermes 用 Kanban delegate 自实现了类似能力。团队上手曲线没有通用框架的共同词汇新人要读项目私有抽象。6. 什么时候反而该用框架平衡地看框架并非没价值 -快速原型 / Demo / 一次性脚本现成集成省时间。 -需要复杂、可视化的有状态编排且不想自研LangGraph 的图 / checkpoint / human-in-the-loop 是真实价值。 -团队不愿维护底层循环愿用抽象约束换取速度。经验法则探索期用框架跑得快一旦产品要长期演进、要精细控制模型行为、要控依赖与安全多数严肃项目会像 Hermes 一样收敛回「自研瘦核心循环 OpenAI SDK」。这也是 Aider、Codex CLI、Claude Code 等流行 coding agent 同样不依赖 LangChain / LangGraph 的原因。7. 一句话总结agent 的核心循环简单到不值得用重框架去封装而循环周边真正难的工程缓存 / 降级 / 压缩 / 多 provider / 供应链又恰恰是框架抽象会挡路的地方——所以 Hermes 选择自研瘦核心循环用依赖最小化换取可控性与安全性。如果觉得这篇文章对你有帮助欢迎点赞、收藏加关注。后续持续分享更多有价值的内容。你的支持是我创作的最大动力