GitHub 推荐:32 个月 8.4K stars:Boundary 用自研语言 + 自研 VM 把 LLM 集成做出”跨 5 语言 SDK”的不可能三角
一句话总结
BAML 是一个为 LLM/Agent 工作流自研的 DSL —— 用 .baml 文件同时表达 prompt、schema、client、retry policy,编译器生成 Python / TypeScript / Go / Ruby / Rust 五种宿主语言的类型化客户端,背后用自研栈虚拟机(BexVM)做跨语言语义一致的运行时,是 Instruct、Outlines、TypeChat 这些「单宿主库」都做不到的”上游路线”。
值得关注的理由
- 结构化 LLM 输出赛道唯一的”跨语言”选手:Instructor/Outlines/TypeChat/Guidance 全部是单宿主库,BAML 是唯一把 schema+prompt+runtime 三件事用一套 DSL 表达并生成 5 个语言 SDK 的项目 —— 这是结构化输出从「Python 圈内部工具」变成「跨语言基础设施」的关键拐点。
- 「自研语言 + 自研 VM」的工程赌注罕见落地:23+ crate 的
baml_language/重写了 lexer/parser/HIR/TIR/MIR/VM,含 BexVM 半空间 GC + TLAB + superinstruction 优化,编译器架构(Salsa 查询 + bidirectional type checking + mu-binder equirecursive subtyping)有教科书深度 —— 是少数把 PL 工程扎到 LLM 基础设施里的项目。 - 「schema 嵌 prompt + 解析层宽容」路线让开源小模型也能用:不走 logits masking(被 provider 锁死),用 jsonish 多策略解析 + Flag 评分制(
StringToBool/ImpliedKey/ArrayItemParseError),让 Qwen2-VL、Llama 3 等本地模型也能跑出合规结构化输出。
项目展示

README 视觉资产极简(仅 logo + 代码 demo),无 GIF / 无视频;演示靠 docs 站 playground 与博客。Phase 1 已确认 README 无展示性动图。
项目画像
| 维度 | 数据 |
|---|---|
| GitHub | https://github.com/boundaryml/baml |
| Star / Fork | 8,453 / 439 |
| Watchers / Open Issues / Open PRs | 30 / 187 / 54 |
| 协议 | Apache License 2.0 |
| 创建于 / 最近活动 | 2023-10-06 / 2026-06-30(每日仍在 commit + 多次 nightly release) |
| 默认分支 | canary(非常规 main,canary 发布模式) |
| 仓库年龄 | 32.8 个月 |
| 代码行数 | 1,505,814 行 / 5,092 文件(tokei) |
| 主语言分布 | Rust 66.3% · TypeScript 12.2% · BAML 8.2% · Python 4.0% · JavaScript 2.7% · MDX 2.3% · Go 1.7% · 其它 < 1% |
| 开发阶段 | 密集开发 + 持续加码(最近 30 天 189 commit,与 2024-05 单月爆量期持平) |
| 贡献模式 | 核心少数 + 社区驱动:105 人贡献,TOP 5 ≈ 77%,TOP 10 ≈ 91% |
| 热度定位 | 大众热门 + 高速增长(pattern = 高速增长) |
| 质量评级 | 代码[优秀] 文档[优秀] 测试[优秀] |
| 公司 / 所在地 | Boundary · Seattle, WA(无 YC、无 cloud、无 SaaS) |
作者视角:为什么存在这个项目
创始人/作者背景
创始团队 6 人,前背景都是大厂底层 / 编译器 / 工具链:
- Vaibhav Gupta(hellovai)— Co-founder & CEO,前 Google Pixel 4 / HoloLens 工程师;仓库第一大贡献者(666 commits / 15.1%)。
- Aaron Villalpando(aaronvg)— Co-founder & CTO,前 Amazon EC2 / Prime Video / Twitch;596 commits。
- Sam Lijin(sxlijin)— 前 Google Identity / Cloud Firestore,前 Trunk 开发者工具,Vanderbilt CS;519 commits。
- Antonio Sarosi — 独立 Rust 项目背景(数据库 / reverse proxy / 内存分配器),YouTube 教学频道 18 万订阅。
- Paulo Rossi — 前 YC 创始人,ARR 曾过 $2M,早期 BAML 用户转为员工(dogfooding 的活证据)。
- Kai Orita — 写过
ere(编译期正则)和百万下载的 VS Code 扩展。
领域背景总结:大厂底层 + 编译器 / 工具链经验密集(Google 设备 / Amazon 分布式 / Rust 独立项目 / Rust 教学),无 web-app / 前端出身 —— 典型 systems + PL 工程文化。
问题判断
LLM 集成在产品工程里是「黑盒字符串拼接」:开发者用 f-string 拼 prompt、用 regex 抽 JSON、用 Pydantic 在 Python 边界救火 —— 没有一个把”提示工程”当成”工程”来对待的中间层。当 prompt 里有控制流(if / for / match)、需要 retry / fallback / 跨 provider 路由、需要 schema 校验、需要 streaming 解析时,工具链的断裂让 LLM 集成永远处在「能跑但脆」的状态。
时机判断:作者们认为 “AI agents are a new paradigm that requires a new programming language, just like in the past: Hardware → Assembly, Operating Systems → Java, Web → JavaScript, Agentic Coding → ???” —— 现在是新范式的语言空窗期,自研语言 + 自研 VM 的工程赌注值得下。
解法哲学
README 把设计哲学明示为 7 条,每条都对应到具体代码:
| 哲学 | 代码落地点 |
|---|---|
| Look like TypeScript | BAML 语法故意用 TS 风格(class Foo { field int }、Jinja {{ x }}),但底层类型系统是 Rust 风格(invariant generics、structural typing、narrowing) |
| Make undesired state unrepresentable | 用 mu-binder 表达递归类型(type JSON = string | int | JSON[]),用 equirecursive subtyping 让 alias 透明 |
| No viral edits | LLM 函数被 desugar 成 func$render_prompt / func$build_request / func$parse 三个 companion(COMPANIONS: [fn(&FunctionDef) -> Option<FunctionDef>; 3]),下游 HIR/TIR/MIR/Emit 全程零特殊处理 |
| One obvious way | IR 层(IntermediateRepr)是唯一的 schema 表示,5 个生成器都从 IR 取,不允许”每个语言有自己的方言” |
| Tools for agents, not IDEs | snapshot test 输出”designed to be readable by both humans and LLMs”;baml_test! 宏循环明确写给 coding agent |
| Make nondeterminism observable | jsonish 解析给每个 fixable error 打 Flag(Flag::ArrayItemParseError / Flag::StringToBool / Flag::ImpliedKey)存档 |
战略意图
当前 = 0 商业化:README 仅招 Rust 工程师,无 cloud / SaaS / pricing 页。但产品形态已经埋下 SaaS 化伏笔:
- Tracing / Observability ——
engine/baml-runtime/src/tracingv2/已做了 v1 + v2 两套,每次 LLM call 都有 prompt、request、response、latency、token 用量 —— 正是 Helicone / LangSmith / TensorZero 卖的东西。 - Prompt registry / versioning —— BAML 已有 lockfile (
engine/baml-lib/baml-core/src/lockfile.rs),是 BAML Cloud 的天然入口。 - Eval / optimize ——
baml-cli optimize、engine/baml-runtime/src/optimize/已存在。 - OpenAPI 协议层 —— Issue #892 暗示团队想把 BAML 当”agent 之间交换 schema 的协议”,与 AsyncAPI / OpenAPI 当年的”想当协议”姿态一致。
核心价值提炼
创新之处
按新颖度 × 实用性排序:
- Companion Function 自动展开(AST-level desugaring):每一个 LLM function 在 AST 阶段被 desugar 成 3 个 companion,实现是
COMPANIONS: [fn(&X) -> Option<X>; N];下游全程零特殊处理。新颖度 4 / 实用性 5 / 可迁移性 4。 - 宽容 JSON 解析 + Flag 评分制(jsonish + WithScore):3 套 parser(fixing / markdown / multi-json)并行跑,每个 fixable error 打 Flag,最后用加权和(数字字段比 string 字段重 10×)取最低分路径 —— 这是 BAML 兼容任何 LLM(含开源小模型)的工程根基。新颖度 4 / 实用性 5 / 可迁移性 4。
- Salsa 查询作为 IR 层(“After AST, everything is a query”):传统编译器每层拷贝 + 增量难做对;compiler2 反过来 —— HIR/TIR 是 query 不是 transformation,只有 AST/MIR/bytecode 是 transformation。加注释编辑只重跑 lexer + HIR,type 推断不会失效。新颖度 3(rust-analyzer 用过)/ 实用性 5 / 可迁移性 5。
- Stream 类型在 PPIR 层注入回 HIR(self-feeding IR):
@stream.done/@stream.not_null/@stream.with_state这些流式属性需要在生成*$stream合成类型时,把合成出的 AST 节点重新喂回 HIR —— Salsa query 架构下”两个层之间循环”的优雅解法。新颖度 5 / 实用性 4 / 可迁移性 3。 - Snapshot 测试面向 LLM 优化(“designed to be readable by LLMs”):每个 pipeline 阶段(lexer / parser / HIR / TIR / MIR / diagnostics / codegen)都有独立 snapshot 格式;
baml_test!宏 +cargo insta accept是明确给 coding agent 用的循环。新颖度 4 / 实用性 5 / 可迁移性 5。 - baml-bench dogfooding 闭环(agent 跑 BAML 找 BAML bug):用 Convex claimable queue + Next.js dashboard + claude-proxy + notion-fixer + cursor fix dispatch,把”agent 跑基准 → 写 trophy → 去重 → 派发 Cursor 修 → 重跑”做成全自动流水线。新颖度 5 / 实用性 4 / 可迁移性 4。
- Co-inductive equirecursive subtyping + mu-binder:
type JSON = string \| int \| JSON[]在 type-check 时不爆栈;用 mu-binder 表达递归,subtype 检查用 assumption set 防止无限递归。ACI unification 是 NP-hard,BAML 显式承认并 fail closed —— 工程上”我宁可让你多写一个implements也不愿意编译期允许一个运行时 panic”。新颖度 3 / 实用性 4 / 可迁移性 3。 - BexVM 半空间 GC + TLAB 无锁分配:
runtime_spaces: [ChunkedVec<Object>; 2]互为 GC 切换;tlab.alloc_ptr单调 bump,满了fetch_add抢下一块。配合 CPython 风格 superinstruction(LoadVar2(a, b)一次读两个 u32,省一次 PC++)。新颖度 3 / 实用性 4 / 可迁移性 3。
可复用的模式与技巧
- “Before AST produce the AST, after AST answer questions about the AST”:编译器层数选择的不对称原则 —— 前 3 层是 transformation(Parser/CST→AST/低层 IR),后面全是 query。适用场景:所有做 IDE-grade 编译器的项目。
- Companion function 数组(
COMPANIONS: [fn(&X) -> Option<X>; N]):把”同一 source 节点展开成多个 IR 节点”做成纯函数数组,加新 desugar 只需要加一个函数。适用场景:DSL 设计。 build.rs同时跑.baml→ AST → 生成 Rust trait impl:编译期让”自研语言的 stdlib”在宿主语言里有对应实现,免去手写镜像。适用场景:自研语言 + Rust runtime。- Snapshot format per pipeline phase + 显式 “designed for LLM” 注释:把工具链的”中间产物”做成”人类和 LLM 都能读”。适用场景:所有”用 AI 写工具、工具改 AI”的循环。
- “声明式 schema 嵌 prompt + 解析层宽容恢复”代替 logits masking:避开 provider 锁定,让开源小模型也能用。适用场景:LLM structured output 的工程妥协。
- TypeBuilder fluently typed 客户端 +
IsLiteral/CheckNever类型保护:把”运行时数据”用”编译期类型”约束住 —— TS 的string extends T ? false : true风格判别联合。适用场景:任何”动态 schema”工具。 /^[ \t]*```([a-zA-Z0-9 ]+)(?:\n|$)/m锚定首列才识别代码块的 markdown parser:避免误吃字符串里的 ```json。适用场景:所有 markdown → 结构化解析的代码。read_u32_unchecked+ 手动解释字节序的 superinstruction:性能优化的最朴素做法 ——LoadVar2(a, b)一次读两个 u32,省一次 PC++。适用场景:解释器性能调优。
关键设计决策
-
决策:做自研语言(DSL + 编译器 + VM),而不是 Python 库 / TS 库
- 问题:跨 5+ 个宿主语言做”统一的 schema + 统一的 prompt 渲染 + 统一的 retry/fallback + 统一的 stream 解析”,单宿主库做不到。
- 方案:把 schema、prompt、client、retry policy 全部用
.baml单一 DSL 表达;baml-cli generate吐出 5 种宿主语言的 typed client;运行时用 BexVM 字节码统一执行(BexVM 是 BAML → 字节码 → Rust 解释器,不依赖任何宿主 VM)。 - Trade-off:自研语言意味着语言入门成本 + 编译器维护成本 + 用户调试成本三高;换取跨语言语义一致性 + 长期可演进性。
- 可迁移性:低 —— 只有当你有”必须跨 N 个语言且不能分裂”的强需求时才划算。这是 BAML 最大的护城河决策。
-
决策:HIR / TIR / MIR / Emit 四层中间表示 + PPIR 旁路
- 问题:单纯 AST → 字节码的两层结构会让类型信息丢失、要不停回头查表。
- 方案(ARCHITECTURE.md 关键引述):“before the AST, produce the AST. After the AST, answer questions about the AST.” —— 只有 3 个”生产型”层(Parser → AST,AST → MIR,MIR → bytecode),中间全是 Salsa 查询。
- Trade-off:层数越多越难上手;编译器新人必须先吃透 5 层职责边界(“Cardinal Rule: Upstream Over Downstream”)。
- 可迁移性:中 —— 任何做 IDE-grade 编译器的项目(语言服务器、增量编译、refactor 工具)都受益。
-
决策:编译期 schema 校验 + 运行时类型校验(双层)
- 问题:纯编译期类型检查在 LLM 输出场景不够 —— LLM 可能输出不合法 JSON。纯运行时校验又拿不到 IDE 提示。
- 方案:编译期用 TIR 校验所有 prompt 模板里的
{{ ctx.output_format }}调用、类型引用、union 分支;运行时用jsonish解析器对 LLM 原始输出做”宽容解析 + flag 评分”。 - Trade-off:运行时解析器 ~1.5k 行 Rust,是 BAML 复杂度的主要来源之一;换取”LLM 输出永远能 recover 出 BamlValue”的强保证。
- 可迁移性:高 —— 任何”需要从非结构化文本中恢复结构”的场景都能用。
-
决策:约束解码走”prompt 嵌入 schema + 运行时宽容解析”路线,不强求 logits masking
- 问题:logits masking / grammar-constrained decoding 需要 provider 支持(OpenAI 的
response_format: json_schema、Anthropic 的 tool use、Google 的 response schema 等各家不同),强求 mask 会被 provider 锁死。 - 方案:BAML 不强制走 provider 的 grammar API,而是把 schema 当作提示的一部分用
{{ ctx.output_format }}嵌进 prompt(output_format: "Answer ONLY with a JSON object that has these fields..."),再让运行时jsonish做”宽容解析”。 - Trade-off:少一个”用 OpenAI 的 json_schema 时 100% 合法”的金标准保证;换取”任何 LLM(开源 / 小模型)都能用”的最大兼容。
- 可迁移性:高 —— 这套”schema 嵌 prompt + 解析层兜底”思想任何 structured-output 项目都能用。
- 问题:logits masking / grammar-constrained decoding 需要 provider 支持(OpenAI 的
-
决策:把 stdlib 写在 BAML 自己里(
baml_builtins2/baml_std/baml/)- 问题:传统”编译器内置 stdlib”会让 stdlib 类型变化需要改编译器。
- 方案:核心类型(
Client/PrimitiveClient/Array<T>/Map<K,V>/baml.llm.PromptAst/baml.http.Request)都是.baml文件;编译器通过include_str!嵌入 +build.rs在 Rust 编译时也跑一遍 codegen 生成 Rust 侧 trait 实现。 - 关键:编译器路径和运行时路径共享同一份
.baml文件但独立 codegen —— 这是 BAML “hosted language” 的基础。 - Trade-off:改一个 stdlib 类型需要同时检查编译器侧和运行时侧。
- 可迁移性:高 —— 任何”自研语言 + 自带 stdlib”项目都该这么干。
竞品格局与定位
竞品对比矩阵
| 维度 | BAML | Instructor (13.3k★) | Outlines (14.3k★) | TypeChat (8.7k★) | TensorZero (11.7k★) |
|---|---|---|---|---|---|
| 形态 | DSL + 编译器 + 5 SDK + 自研 VM | Python 库 | Python 框架 | TS 库 | Rust gateway + LLMOps 平台 |
| 跨语言 | ✅ 5 语言(Py/TS/Go/Ruby/Rust) | ❌ Python only | ❌ Python only | ❌ TS only | ❌ 服务端,多语言客户端 |
| 跨 provider | ✅ 7+ provider(OpenAI/Anthropic/Vertex/Bedrock/Ollama/…) | 主要 OpenAI + tools | ✅ 通过 logits processor | 主要 OpenAI | ✅ gateway 路由 |
| 开源模型兼容 | ✅(prompt-embed + jsonish 宽容) | 弱(依赖 OpenAI format) | 中(logits masking 要求兼容) | 弱 | 中 |
| 类型系统 | 自研(mu-binder + invariant generics + narrowing) | Pydantic | Pydantic | TS 类型系统 | 无(gateway 视角) |
| Streaming 类型化 | ✅ @stream.done / @stream.with_state | ❌ | ❌ | ❌ | ✅(gateway 视角) |
| IDE/LSP | ✅ VS Code / JetBrains / Zed | 仅 Python LSP | 无 | 仅 TS | 无 |
| 动态 schema | ✅ TypeBuilder | ❌ | ❌ | ❌ | ❌ |
| 商业化 | 0(仅招人) | 已成立公司 | 已成立公司 | 微软实验性 | 已成立公司 + 客户 |
| 学习曲线 | 高(自研语言) | 低(Python 库) | 低 | 低(TS 库) | 中(LLMOps 平台) |
差异化护城河
- 跨语言语义一致性 —— 这是 5 个语言 SDK 真正的卖点,竞品都是单宿主。
- 自研 VM(BexVM) —— 让 BAML 能”独立跑”(不依赖任何宿主 VM),同时让 tracing / profiling / optimization 成为 first class。
- Streaming 类型化 ——
@stream.done/@stream.not_null/@stream.with_state是 BAML 在 streaming 场景的杀手锏。 - 动态 schema(TypeBuilder) —— 同类产品没做或做得肤浅。
- dogfooding 闭环(baml-bench) —— 这是组织能力,不是技术。
竞争风险
- OpenAI / Anthropic 自家 structured output 越来越强 ——
json_schemamode / tool use / structured outputs 把”结构化输出”逐渐变成 commodity,BAML 在”OpenAI + Anthropic”上的差异化会被持续压缩。 - Python 生态 —— Instructor + Pydantic + LangChain 在 Python 圈根深蒂固,BAML 改写用户习惯成本高。
- 学习曲线 —— 自研语言注定比 Pydantic 类库难上手;BAML 的目标用户群比 Instructor / TypeChat 窄。
- 0 商业化 —— 30+ 核心贡献者、5 个 SDK、weekly release 节奏,烧钱速度需要 cloud 化来撑。
生态定位
BAML 不是 “Pydantic for LLM” —— 它是”自研 agent 编程语言 + 跨语言 SDK + 跨 provider 运行时”。真正的同位竞品是 LangChain / LlamaIndex(“agent 框架”),但 BAML 选择做语言而不是做框架 —— 这是更上游、更长期、风险也更高的赌注。
短中期定位:“用一套 BAML schema 替代你们跨 5 个语言各自拼 prompt 的胶水代码”。
套利机会分析
- 信息差:HN 总声量偏弱(仅 1 条 Show HN by aaronvg),增长主要靠 GitHub Trending + dev.to / 个人博客扩散 —— 在国内技术圈几乎未被充分讨论,公众号端是优质信息差。
- 技术借鉴:可立即迁移的模式 —— ① jsonish 宽容解析 + Flag 评分(任何 LLM 输出抽取场景可用)② schema 嵌 prompt + 解析层兜底(任何想兼容开源小模型的项目可用)③ snapshot per pipeline phase + “designed for LLM”(任何 AI agent 写代码的工具链该学)④ Companion function 数组 desugaring(DSL 设计的通用模式)。
- 生态位:填补”跨语言栈团队没有统一 prompt 表达层”的空白。对国内多语言栈公司(字节 / 阿里 / 美团 / 拼多多都有 Python + Go + TS 的混合栈)特别有价值 —— 这类公司目前要写 5 套 prompt 胶水代码。
- 趋势判断:① 2026-06 单月 189 star 创 2025 以来新高(agent 浪潮红利)② default branch = canary、daily nightly release,节奏不降反升 ③ 团队已宣布招 Rust 工程师 → 处在产品化加速 + 商业化前夜。
风险与不足
- 0 商业化 + 持续高投入:30+ 核心贡献者 + 5 个 SDK + weekly release 节奏,烧钱速度需要 cloud 化来撑;如果 18 个月内没有 cloud/enterprise 收入,团队稳定性有风险。
- 双代架构迁移成本:
engine/(5,574 变更)和baml_language/(23,415 变更)并存,HIR/PPIR 循环是新代独门但旧代还在维护,新人上手成本高。 - TypeScript 注释里 “ask vaibhav” 之类的 TODO 痕迹:说明作者在快速迭代中,部分模块文档化滞后。
- 部分新代 crate(如 compiler2_ppir)snapshot 还在 evolve,稳定性未达 v1 水平。
- schema 编译时版本 vs 运行时版本漂移(Issue #2241):客户端生成器与运行时版本对齐是 0.2xx 阶段的明显痛点,可能在升级到 1.0 前是 friction。
- 依赖 Rust 工具链:用户调试 / IDE 体验比 Python 库重一档 —— TypeChat 用 TS 自带 IDE 提示是 BAML 短期追不上的体验优势。
行动建议
- 如果你要用它:
- 选 BAML 的场景:跨语言栈团队(Py + TS + Go / Ruby)需要统一 prompt + schema;想用本地开源模型跑结构化输出;想要 streaming 类型化(
@stream.done不会重复发”已确认字段”)。 - 不选 BAML 的场景:只用 Python + OpenAI —— Instructor + Pydantic 五分钟上手,BAML 学习曲线不值;只跑简单 f-string prompt —— BAML 的 DSL 抽象是过度工程。
- 选 BAML 的场景:跨语言栈团队(Py + TS + Go / Ruby)需要统一 prompt + schema;想用本地开源模型跑结构化输出;想要 streaming 类型化(
- 如果你要学它:
- 必读:
baml_language/architecture/ARCHITECTURE.md、TYPE_SYSTEM.md、baml_language/crates/bex_vm/README.md—— 三个 1000+ 行设计 doc 解释了为什么这样做而不是那样做。 - 重点关注:
baml_language/crates/baml_compiler2_hir/的 Salsa 查询实现、engine/baml-lib/jsonish/的宽容解析 + Flag 评分、engine/generators/的多语言代码生成 dispatch。 - 可复用技巧:jsonish 解析 + snapshot per pipeline phase + “designed for LLM” 这三个模式任何 AI 工具都能学。
- 必读:
- 如果你要 fork 它:
- 改进方向 1:加 litellm 风格的统一 provider adapter,把 7 个 provider 的差异收敛到 trait 后面(当前是各 provider 一个文件)。
- 改进方向 2:TypeBuilder 加 IDE-time 提示 —— 当前 TS 端的
IsLiteral<T>是手写,缺泛化的”动态 schema 编译期类型保护”生成器。 - 改进方向 3:tracingv2 上做 OpenTelemetry 兼容 export,让用户能直接对接 Langfuse / Helicone / 自家 observability 后端。
知识入口
| 资源 | 链接 |
|---|---|
| DeepWiki | 未收录 |
| Zread.ai | 未收录 |
| 公开 RFC 流程(BEPs) | https://beps.boundaryml.com |
| 官方文档站 | https://docs.boundaryml.com |
| 公司站 / 团队介绍 | https://www.boundaryml.com/who-are-we |
| 官方 Playground | https://www.boundaryml.com/explore |
| 关联论文 | 无(BAML 是工程产品,非学术研究) |
| 在线 Demo | https://docs.boundaryml.com(内嵌 playground + editor preview) |