brainlid/langchain:在 Elixir 里拼装 LLM 调用链
Elixir implementation of a LangChain style framework that lets Elixir projects integrate with and leverage LLMs.
秒懂
- 它是什么?
- 这是 LangChain 的 Elixir 实现,不做 Python/JS 版本的平级对齐,而是按函数式语言的习惯重做了模型接入层。它真正的价值在于把十几家模型供应商塞进同一套 Chat 抽象,代价是版本迭代快、API 仍在漂移。
- 适合谁用?
- 已经在用 Elixir 且需要接入多家模型供应商的团队,可以把它当成模型适配层来评估;如果你的技术栈是 Python 或 TypeScript,或者你只需要调用单一供应商的 HTTP 接口,引入这层抽象并不划算。上手前先确认三件事:mix.exs 里锁定的版本号是否与你参考的文档一致,config/runtime.exs 中各家 key 的键名(openai_key、:anthropic_key、:xai_api_key 的写法并不统一),以及你打算用的模型是否在 README 列出的支持范围内。
- 能商用吗?
- 请先确认。这个仓库使用的许可证不在我们自动归类的范围内,商用前请阅读仓库里的 LICENSE 文件。
- 还在维护吗?
- 在维护。仓库最近一次提交在 3 天前。
- 用什么语言写的?
- 主要是 Elixir(依据 GitHub 的语言统计)。
以上回答依据项目的 GitHub 数据(最近同步于 2026年9月15日)和我们的分析,不构成法律意见。
开源项目深度解析
它解决的是 Elixir 侧的模型接入碎片化
Elixir 项目要调 LLM,最直接的做法是拿 Req 或 Finch 手写 HTTP 请求,再自己解析各家返回的 JSON。单接一家还行,一旦要同时支持 Anthropic、OpenAI、Gemini,请求体结构、流式响应格式、错误码含义各不相同,适配代码会散落在业务逻辑里。这个库的定位就是把这些差异收进一层统一的 Chat 抽象,让上层代码面对的接口尽量一致。
目标读者很明确:已经在写 Elixir 的人。README 把支持范围列得很长,Anthropic Claude(含 extended thinking 和 AWS Bedrock)、AWS Bedrock Mantle、OpenAI 的 Chat Completions 与较新的 Responses API、Cloudflare Workers AI、xAI Grok、Google Gemini、Vertex AI、DeepSeek、Ollama、Mistral、Perplexity、orq.ai,以及通过 Nx 自托管的 Bumblebee 和走 req_llm 库的多供应商适配器。这份清单的宽度本身就是这个项目的主要卖点。
README 里对 LangChain 的定义沿用了原版的措辞:Data-aware 与 Agentic。前者指把模型接到外部数据源,后者指让模型与环境交互。这两个词在文档里出现得比具体机制更多,属于框架定位层面的表述,实际能落地的部分还是组件抽象和预置链。
Chat 抽象与链式组装:文档能确认的部分
README 把价值主张拆成两块:Components 和 off-the-shelf chains。组件是对语言模型的抽象以及每个抽象的多份实现,文档强调这些组件是模块化的,用不用框架其余部分都能单独使用。预置链则是为特定高阶任务预先搭好的组件装配。
按文档的说法,预置链负责降低起步门槛,组件负责在用例变复杂时改造既有链或搭新链。这个分层是 LangChain 系列一贯的思路,落到 Elixir 上,链的组装依赖语言本身的管道与函数组合,而不是 Python 那种继承式的 Runnable 体系。
需要说清楚的是,README 并没有给出链的代码示例,也没有描述消息结构、回调钩子或工具调用的具体字段。这些只能查 hexdocs.pm/langchain 上的模块文档。如果你评估时只看 README,会得到一个偏营销的组件清单,看不到实际的调用形态,这是这个仓库文档结构上的一个短板。
另一个可确认的机制是提示缓存。README 提到 ChatGPT、Claude 和 DeepSeek 都提供基于前缀的 prompt caching,对长提示词有成本和性能上的好处,DeepSeek 的支持在支持列表里单独标注。缓存的具体触发条件、命中统计怎么读,README 没有展开。
安装与配置:真实的命令和键名
环境要求写得很直接:Elixir 1.17 或更高。依赖加在 mix.exs 里,README 给出的写法是 {:langchain, "~> 0.9.0"}。这里有个明显的错位:仓库最近的发布已经到 v0.13.1,而安装示例仍停在 0.9 系列。按 ~> 0.9.0 的语义,你装到的不会是 0.13 分支的代码。这是上手时第一个要自己判断的地方。
配置集中在 config/runtime.exs。OpenAI 相关的键是 openai_key 和 openai_org_id,Anthropic 用的是 :anthropic_key,xAI 是 :xai_api_key。三个供应商三种命名风格,没有统一前缀,写配置时容易拼错。
密钥可以延迟解析。文档给了两种写法,元组形式 config :langchain, openai_key: {MyApp.Secrets, :openai_api_key, []},或者匿名函数 config :langchain, openai_key: fn -> System.fetch_env!("OPENAI_API_KEY") end。README 明确提醒 API key 应当按密钥处理,不要提交进仓库。
部署到 fly.io 时,文档给的命令是 fly secrets set OPENAI_API_KEY=MyOpenAIApiKey,Anthropic 和 xAI 同理。底层 HTTP 客户端用的是 Req 库,这一点在配置一节里写明了。
刻意不做 Python/JS 版本的对齐
这是整个项目里最值得单独拿出来讲的设计决定。README 用一整节解释为什么不追求与 JavaScript 和 Python 版本的功能对等,理由有两条。
第一条是语言范式。JS 和 Python 都是面向对象语言,Elixir 是函数式的,README 的原话是不打算强行套用一个不适用的设计。第二条涉及历史包袱:JS 和 Python 版本起步于对话式 LLM 成为标准之前,因此花了大量精力在模型本身不支持对话历史时替它保存历史。这个 Elixir 版本明确表示不做这件事。
后果是双向的。好处是这层抽象更薄,不需要为了兼容旧模型行为而维护一套历史管理机制,模型原生支持的能力直接透传。代价是无法复用 JS/Python 生态里那些可序列化、可跨语言共享的对象,README 提到原版设计目标之一就是 prompt、LLM、chain 这些对象能在两种语言间序列化传递,Elixir 版本主动放弃了这条路径。
README 也承认这个库深受 JavaScript 版本实际工作方式的影响。所以它不是从零设计的替代品,而是在同一套思路下按函数式习惯做的重写。评估时把它当成独立实现更稳妥,不要假设 Python 教程里的概念能一一对应过来。
本地模型这条路径与它的前提
Bumblebee 支持是这个库区别于多数同类封装的地方。README 把它列在支持列表里,说明是自托管模型,走 Nx,可用的模型家族包括 Llama、Mistral、Zephyr。对于不想把请求发到外部服务、或者需要在内网跑推理的场景,这是唯一一条不依赖第三方 API 的路径。
代价藏在依赖关系里。Bumblebee 背后是 Nx,Nx 背后是 EXLA 或 Torchx 这类后端,是否需要 GPU、显存要求多少、模型权重从哪里拉取,这些都不在这个库的职责范围内。README 只写到支持 Bumblebee 为止,没有给出配置示例,也没有说明模型加载的启动开销。真要在这条路上走,工作量的大头在 Nx 那一侧,不在 langchain 这一侧。
Ollama 是另一条本地路径,但性质不同:它连接的是一个已经在运行的本地服务,走的是 API 调用,不涉及 BEAM 进程内的张量计算。两者常被混为一谈,实际运维模型完全不同。
列表里还有 ReqLLM 这个适配器,README 描述为通过 req_llm 库实现的多供应商适配,覆盖 Anthropic、OpenAI、Gemini、Groq、Ollama、AWS Bedrock 等。它和内置的各家适配器在功能上有重叠,文档没有说明两者该如何取舍。
版本节奏与维护成本
从发布记录看,v0.12.0 在 2026 年 8 月 22 日,v0.13.0 在 8 月 23 日,v0.13.1 在 8 月 26 日。三天内跨了两个次版本号。这个节奏说明项目仍处在快速演进阶段,也意味着次版本之间出现破坏性变更的概率不低。
对使用方来说,这直接转化为升级成本。0.x 阶段的库通常不承诺语义化版本的兼容性保证,把这样的依赖放进生产系统,需要接受定期跟进变更的工作量。README 里安装示例停留在 0.9 系列而实际已到 0.13,本身就是这种漂移的一个信号:文档更新落后于代码发布。
许可方面,仓库的 License 字段显示为 NOASSERTION,这不是一个具体的许可证标识。这意味着无法从仓库元数据判断适用条款,需要直接查阅仓库中的 LICENSE 文件原文,必要时走内部合规流程。这里不构成法律意见,只是指出元数据本身不提供答案。
依赖面也值得算一笔账。核心依赖是 Req,但如果走 Bumblebee 路径,会连带引入 Nx 及其计算后端,依赖树规模完全不同。选型时应该按实际用到的适配器来估算,而不是按整个支持列表。
什么时候它是个错误的引入
最清楚的一种情况:技术栈不是 Elixir。这个库的抽象建立在 Elixir 的函数组合和进程模型之上,README 也明说不追求与 JS/Python 版本对齐,跨语言复用对象这条路是关闭的。用 Python 或 TypeScript 的团队看这个项目没有意义。
第二种情况是只需要接一家供应商,且调用点很少。如果整个应用只调 OpenAI 的 Chat Completions,业务代码里几十行 Req 调用就能覆盖,引入一个 0.x 阶段、三天两版的依赖,换来的是升级负担而不是收益。抽象层只有在供应商数量或调用形态增长到一定程度时才回本。
第三种情况是对稳定性有硬性要求的系统。README 没有给出任何关于 API 稳定性承诺的表述,发布节奏也印证了这一点。把核心业务流程压在一个次版本频繁跳动的库上,需要自己承担跟进成本。
还有一类是期待与 Python 版 LangChain 行为一致的团队。这个库明确放弃了历史管理这一层,README 说得很直接:JS 和 Python 版本在模型不支持对话时替它保存历史,这里不做。如果现有代码或团队认知依赖那套行为,迁移过来会发现语义对不上。
替代方案上,最直接的是自己写适配层。用 Req 直连各家的 HTTP 接口,把请求构造和响应解析包成几个模块。差别在于:自己写意味着完全掌控错误处理和重试策略,也意味着每家供应商的流式格式、tool call 结构、缓存头都要自己跟;这个库把这部分收进 Chat 抽象,代价是抽象层的更新节奏由上游决定。选择取决于你更怕哪种维护工作。
编辑结论
已经在用 Elixir 且需要接入多家模型供应商的团队,可以把它当成模型适配层来评估;如果你的技术栈是 Python 或 TypeScript,或者你只需要调用单一供应商的 HTTP 接口,引入这层抽象并不划算。上手前先确认三件事:mix.exs 里锁定的版本号是否与你参考的文档一致,config/runtime.exs 中各家 key 的键名(openai_key、:anthropic_key、:xai_api_key 的写法并不统一),以及你打算用的模型是否在 README 列出的支持范围内。许可条款在仓库中标记为 NOASSERTION,采用前需要自行核对 LICENSE 文件原文。
社区笔记