面向LLM的编程语言:Linkly如何通过MLIR重构AI应用开发

📅 2026/8/27 10:16:33
面向LLM的编程语言:Linkly如何通过MLIR重构AI应用开发
去年 Andrej Karpathy 抛出“LLM OS”“Software 2.0”等一系列概念时很多人还在争论“提示工程到底算不算编程”。到了今年“LLM Wiki”“LLM Agent”“LLM 框架”这些词铺天盖地而来大家终于意识到的一件事是我们缺的并不是更多 Agent 框架而是更精确、更可控地描述 LLM 行为的方式。“Show HN: Linkly – a language designed for LLMs, compiled through MLIR”这个项目恰好切在这个点上。它要做的不是给 LLM 套一层 API 封装也不是再建一个编排工具而是从语言层面重新思考当 LLM 成为计算单元时我们该如何为它设计一门编程语言这篇文章会把 Linkly 的定位、MLIR 在其中的作用、它与现有 Agent/编排框架的边界、以及实际项目里如何评估这类方案逐个拆开讲清楚。1. 这篇文章真正要解决的问题先说一个反直觉的观察LLM 应用开发最大的成本不是推理算力而是“交互复杂性”。一个典型的多智能体应用代码里会有大量这样的逻辑某个 LLM 调用返回了 JSON但字段名和预期不一致两个 Agent 之间需要传递结构化的任务描述但又不想引入过多序列化约定同一个 Prompt 在不同版本模型上表现不稳定但宿主语言里的分支逻辑已经绑定死了这些 Prompt不同类型模型返回格式不同有的支持 Function Call有的只支持纯文本调用方需要写一堆兼容代码。你会发现这些问题本质上不是“模型能力不够”而是我们在用一种不适合描述分布式、非确定性、多模态交互的语言来写业务逻辑。Java 和 Python 都很优秀但它们的设计前提是“函数是确定性的、类型是静态可推导的、指令是顺序执行的”。LLM 调用完全不满足这些前提。Linkly 这类“面向 LLM 设计的编程语言”想解决的核心问题就是把 LLM 交互从“字符串拼接 API 调用 异常处理”这种手工作坊模式升级为一种有类型、有结构、可被编译器检查的语言抽象。它并不试图取代 Python 或 Java而是提供一种更贴近 LLM 心智模型的中间表达层。如果你现在正在做 Agent 系统、复杂 Prompt 编排、模型路由、结构化输出解析这类工作这篇文章值得读完因为你会看到一种全新的工具箱选择。2. 基础知识LLM 语言与通用编程语言有什么本质区别要理解 Linkly必须先打破一个认知误区LLM 的“语言”和我们编程用的“语言”在语义层面根本不是一回事。传统编程语言C、Java、Python的本质是“精确描述状态变化”变量、函数、类型、控制流每一步都有确定语义。编译器或解释器可以严格推导程序行为。LLM 的“语言”则完全不同。自然语言本质上是模糊的、上下文依赖的、隐含大量常识的符号系统。同一个词在不同 Prompt 里含义完全不同同一个 Prompt 在不同模型上的输出也会有差异。我们写 Prompt 时实际上并不是在编程而是在做一种“高语境沟通”。于是矛盾出现了我们用 Python 这类低语境语言去驱动一个高语境模型。这中间存在一个巨大的语义阻抗失配。过去几年的解决方案是用 Pydantic 做输出校验、用 LangChain 的 Output Parser 做格式约束、用各种 Agent 框架把 Prompt 标准化。但这些方案都是在“语言之外”做约束始终要处理“模型输出不符合宿主语言类型系统”的问题。Linkly 的思路是反转的先设计一门以“LLM 交互”为一等公民的语言让编译器理解“这是一个模型调用”然后围绕这个核心做类型检查、流分析、代码生成。这个概念理解到位了后面的架构分析就会顺畅很多。这就像 SQL 出现之前人们用文件系统手工管理数据SQL 出现之后查询描述变成了语言本身的一个原生能力。面向 LLM 的语言就是试图把“与模型对话”这件事变成语言原语。3. 为什么不是又一个 Agent 框架而是编译器现在 LLM 生态里最不缺的就是框架。从 Flowise 到 LangGraph从 AutoGen 到各种自研编排平台每个框架都在解决“如何连接多个 LLM 调用”。但这里有一个被忽略的问题框架解决的是运行时的问题而语言解决的是表达力的问题。看一个很常见的场景你希望 Agent A 的输出结构化字段作为 Agent B 的输入同时保证类型一致。如果用 Python 写你只能靠运行时校验# 常规 Python 方案 result_a call_llm(prompt_a) parsed json.loads(result_a) # 如果字段缺失这里才会报错 required parsed[important_field] result_b call_llm(prompt_b.format(important_fieldrequired))在这个代码里prompt_b期望的字段和parsed实际含有的字段没有任何编译期关联。一旦 Prompt 改动、模型输出异常或者字段名变化错误只会延迟到运行时爆发。如果用一门为 LLM 设计、带有类型系统的语言来表达// 伪代码表达语言层面的意图 let result_a run AgentA(); // 编译器知道 AgentA 返回结构化的 TaskOutcome let outcome expect_field(result_a, important_field); let result_b run AgentB(outcome);关键差异在于第二种写法里“重要字段必须存在”这个约束在编译阶段就可以被检查。“AgentB 的输入来自 AgentA 的输出”这个数据流关系也能被静态分析。这就把一部分运行时防御转移到了编译期。这也是 Linkly 通过 MLIR 编译的意义——它不是一门解释型 DSL也不是配置系统而是一套完整的编译流水线。MLIR 的价值是可以在多个抽象层级表示程序结构并复用 LLVM 生态里成熟的分析、优化和代码生成能力。编译器架构的选择决定了它未来可以挂载方言Dialect、做自定义 Pass、对接不同后端这些能力是普通解释型框架不具备的。一句话总结框架解决的是“怎么跑起来”语言加编译器解决的是“怎么写才不出错”。Linkly 选择后者也就决定了它更接近基础设施而非业务流水线。4. MLIR 到底是什么Linkly 为什么选它MLIRMulti-Level Intermediate Representation是 LLVM 项目下的一个子项目目标非常宏大让“构建编译器”这件事模块化、可复用。MLIR 的核心思想是“方言”。每个方言代表一种特定的抽象层级比如tensor方言表示张量运算func方言表示函数和调用scf方言表示结构化的控制流自定义方言可以表达领域专用逻辑。编译器把源语言逐层 lower 到更低级、更接近机器的方言最终进入 LLVM IR变成机器码或可执行模块。Linkly 选择 MLIR 而不是直接手写一个编译器背后有几个非常实际的理由第一MLIR 提供了成熟的基础设施。如果你要从零设计一门语言最痛苦的不是前端 Parser而是中后端的控制流分析、SSA 形式、寄存器分配、指令选择。MLIR 把这些全部打包好了你只需要专注于设计自己的高层方言也就是描述 LLM 交互的那一层抽象。第二MLIR 天然支持多级抽象。语言前端可以先生成高层方言表达“调用模型”“解析响应”“校验字段”这些语义然后逐层 lower 到通用表示。这样调试时能看懂高层次的代码结构优化时能利用低层次的分析。第三LLVM/MLIR 社区有极强的工具支撑。从 Pass 管理器到验证工具再到各种静态分析框架都是现成的。对一个小型语言项目来说站在 MLIR 上是“成功概率最大化”的路线。一个小型的方言示例// 示意性伪代码演示 MLIR 方言结构 llvm.func call_llm(%prompt: !llvm.ptr) - !llvm.ptr { // 伪代码表示 LLM 调用方言 llm.call %prompt : () - !llm.response }注意这个代码块是“示意性的”Linkly 具体怎么写方言以项目源码为准。但从架构思路上你可以理解 MLIR 为它提供了一个“可以像拼积木一样设计编译器”的工作台。5. Linkly 的设计理念与核心能力拆解从项目名称和行业背景推断Linkly 的设计理念可以拆成几个关键词来理解。5.1 “链接”的含义Linkly 这个名字的核心是“Link”也就是链接。它暗示了这门语言要做的不是“计算”而是“连接”。连接的对象包括用户输入与模型输入之间模型输出与业务数据模型之间多个 LLM 调用之间的数据流LLM 与传统代码工具调用、数据库查询、外部 API之间的桥接。这种“以连接为中心”的设计明显区别于“以函数为中心”的通用语言。在 Linkly 的心智模型里程序是一个有向图节点是模型调用、工具、数据源边是数据依赖。编译器的职责之一就是检查这些边的类型是否正确。5.2 LLM 是一等公民在一门面向 LLM 的语言中模型调用不应该被表达为“外部副作用”而应该是一种核心原语。传统语言里调用 LLM 是一个“IO 操作”类型系统通常不关注它的输入输出结构。在 Linkly 这类语言里一次模型调用类似一次“函数调用”但它有更加丰富的语义Prompt 是参数输出是有结构的至少在语言层面声明了结构不同的模型 ProviderOpenAI、Anthropic、本地推理引擎是后端实现细节上下文管理、Token 限制、重试策略可以是语言运行时行为。这个抽象如果能建立起来业务代码的复杂度会大幅下降。开发者只需要描述“我要什么”而不是“我如何去调”。5.3 结构化交互的类型系统LLM 最不稳定的地方是输出格式。Linkly 如果能通过类型系统来约束模型输出就会有巨大的工程价值。例如你可以声明一个结构体type TaskResult { title: string status: enum { pending, done, failed } summary: string subtasks: [TaskResult] }然后让语言运行时负责把模型输出解析到这个结构体中。如果模型返回的 JSON 缺少字段或类型不匹配编译器/运行时可以明确报错而不是等到业务代码里出现 KeyError 才暴露。这就把 Prompt Engineering 的一部分工作变成了类型设计你不再需要写“请以 JSON 格式返回以下字段”这种弱约束 Prompt而是从语言层面声明了输出契约。5.4 可观测性与确定性另一个很有价值的点是一门面向 LLM 的语言有天然的“可观测性钩子”。因为所有交互都经过语言运行时你可以统一记录每次调用的 Prompt模型响应时间Token 消耗重试次数结构化输出校验结果。这些信息在传统框架里要靠大量埋点才能拿到。如果语言层面上有统一抽象那观测就是默认行为。这也符合当前 LLM 应用开发对可观测性越来越重视的趋势。6. 与传统 LLM 应用开发方式的完整对比为了更清楚地认识 Linkly 的价值和局限可以把它和当前主流方案做一个系统对比。维度传统 Python LangChain/自研传统框架 Pydantic 等校验Linkly类 LLM 原生语言交互描述方式字符串模板拼接模板 结构体定义原生语言原语类型安全运行时才能发现错误部分运行时校验编译期可检查部分错误跨模型兼容需要自行处理 API 差异框架层封装语言运行时统一抽象数据流分析手动追踪变量手动追踪编译器可静态分析可观测性需要大量埋点需要额外集成语言级默认能力学习成本低中高需要学新语言/新范式生态成熟度非常成熟成熟早期阶段适配现有代码天然适配高需要桥接层从这个表格可以看得很清楚Linkly 这类方案的真正优势不在“开箱即用”而在“长期工程化”。如果只是写一个简单的聊天机器人用 Python 和框架就够了。但如果要维护一个复杂的多 Agent 系统、需要频繁调整 Prompt/模型/输出契约、需要团队多人协作那么一门有类型、有结构、有编译器的领域语言就有不可忽视的价值。同时也要看到这不是一个替代性方案而是一个补充性方案。绝大部分 LLM 应用里最终的业务逻辑仍然需要传统语言来实现——数据库访问、用户认证、前端接口、监控告警等。Linkly 更像是“LLM 交互层的专用描述语言”它产出的结果可以被 Java/Python 等其他语言消费而不是要求整个系统全部用 Linkly 重写。7. 实际落地中的操作思路与示例由于 Linkly 目前是一个非常早期的开源项目并没有形成稳定的语言规范文档或完整 SDK。这里我不想编造具体的 API 名称或语法规则而是给出一套“如果要在你的项目里引入这类 LLM 原生语言抽象应该怎么设计”的通用思路。这套思路同样适用于你参考 Linkly 的架构在现有系统里做改良。7.1 第一步定义模型交互的协议无论是否真用 Linkly把“调用 LLM”抽象成结构化协议都值得做。# 这是传统语言里的实践但吸收了 Linkly“类型化交互”的思想 from dataclasses import dataclass from typing import Literal dataclass class TaskResult: title: str status: Literal[pending, done, failed] summary: str subtasks: list[TaskResult] field(default_factorylist)这个结构体定义了模型输出的契约。用它来校验模型返回就把“无约束的模型输出”变成了“有约束的数据结构”。7.2 第二步用统一运行时包装调用# 传统语言里的 LLM 调用统一封装 class LLMRuntime: def call(self, prompt: str, schema: type) - Any: raw self._provider_call(prompt) return schema.model_validate_json(raw) # 运行时校验 runtime LLMRuntime() result runtime.call(分析任务进度, TaskResult)这一步很接近 Linkly 想做的抽象但用的是现有语言。它的价值是把“模型调用”变成了一个具有类型签名的操作。7.3 第三步搭建静态检查层如果你决定完全拥抱 Linkly或类似 DSL那么核心工作会是定义 DSL描述流程节点写编译前端把它解析成 AST定义所有节点之间的数据流契约编译器在 AST 上做一致性检查输出可执行的计划Plan供运行时执行。用 Python 伪代码示意一下流程部署# 伪代码DSL 编译后的执行计划 plan [ (extract, {source: email}), (summarize, {model: local-llm, input: extract.result}), (route, {condition: summary.urgency 0.7}), ] executor.execute(plan)Linkly 在这个方向上企图做得更彻底如果 DSL 是通过 MLIR 编译器前端的那么这个 plan 本身就是一种中间表示它可以被优化、验证甚至生成对应的执行代码。这比 Python 里解释执行 plan 要严谨得多。8. 项目评估中的认知误区与常见问题看任何一个新兴技术项目都容易走极端。要么过度期待要么过早否定。以下是围绕 Linkly 这类项目的几个常见误区和问题排查思路。误区一认为它就是另一个 LangChainLangChain 是 Python 生态里的一个代码库它解决的是“LLM 应用的常见套路代码封装”。Linkly 是语言设计 编译器实现解决的是“LLM 交互的表达与验证”。它们层级不同。LangChain 改变的是你写代码时调用的函数Linkly 改变的是你描述程序结构的方式。未来如果二者结合也很有可能Linkly 编译后生成 Python 代码再调用 LangChain 执行。误区二认为“面向 LLM 的语言”意味着写出来全是自然语言这是一种合理的担忧因为很多人以为“用自然语言编程”就等于“写 Prompt”。实际上Linkly 这类语言设计的思路恰恰相反它们试图用更精确的语法来描述自然语言交互的行为契约而不是让整个程序都是自然语言。就像 SQL 用了很多英文单词但 SQL 是精确的声明式语言不是自然语言。误区三编译器检查能解决所有 LLM 输出的不确定性这是最危险的误区。静态类型检查可以保证“如果返回值则结构符合声明”但不能保证“模型一定返回合理语义”。即使类型完全正确模型也可能给出逻辑错误、幻觉内容、偏见输出。编译器检查解决的是“格式/契約”问题不是“语义正确性”问题。误区四一上来就把生产系统切过去这是所有新基础设施都会遇到的问题。对于 Linkly 这类早期项目最合理的切入方式是先在非核心流程试用构建一个小规模的 Pilot验证可行性再逐步扩展。常见问题与排查列表问题现象可能原因排查方式解决方案编译失败类型不匹配LLM 输出契约定义与业务数据模型不一致查看编译器错误信息中的类型位置统一模型输出的 schema 定义生成的代码执行速度慢中间表示层次过多优化 Pass 未生效分析 MLIR 各层级的 IR调整优化 Pass 的组合接入不同模型时行为不一致Provider 差异未在语言运行时层统一检查运行时各 Provider 的实现增加 Provider 适配层统一响应格式调试困难缺乏高层与低层 IR 的对应关系利用 MLIR 的行号映射功能优化 Debug Info 生成部署体积过大MLIR/LLVM 依赖完整打包检查二进制体积构成裁剪未使用的 Pass 和后端这些问题是任何编译器类项目的常态。理解它们能帮助你看清 Linkly 目前处于什么阶段以及应该对它抱什么样的预期。9. 面向开发者的工程落地建议无论 Linkly 本身最后能否成功这门语言背后的思想都值得纳入你的工程工具集。这里给出几条当下就能用的建议。9.1 在设计 LLM 交互层时引入“契约优先”意识不要再用“一个字典传到底”的方式做 LLM 应用。为每一个模型调用定义输入输出的 Schema用 Pydantic/Zod/类型系统约束它。这相当于在没有 Linkly 的情况下手工模拟它的类型检查层。// TypeScript 里定义 LLM 交互契约 interface InvoiceExtractResult { invoiceNumber: string; totalAmount: number; currency: CNY | USD | EUR; date: string; }9.2 学会把流程当作数据而不是当作代码传统编程里流程是代码中的控制流。但在 LLM 应用里流程应该是一个可被修改、可被观测、可被审计的数据结构。无论是 JSON、YAML 还是专门的 DSL把流程配置化会让你后续做 Prompt 调优、模型切换、A/B 测试时轻松得多。9.3 理解“模型调用”是昂贵的副作用在编写编译器或语言时通常会把“函数调用”和“外部副作用”分开。在 LLM 应用里一个模型调用既是高延迟操作也是高成本操作更是不可完全确定的操作。工程上要把它当作“数据库事务”来对待需要日志、需要重试、需要补偿机制、需要有清晰的生命周期管理。这正好是 Linkly 这类语言想通过运行时抽象来提供的。即使你不用它也不影响你借鉴它的工程思想。9.4 关注 MLIR 生态但不要急于全部投入MLIR 本身很强大但它的学习曲线非常陡峭。如果你想深入理解编译器思路可以从 LLVM 官方的 Kaleidoscope 教程开始再学 MLIR 自带的 Toy 教程。如果只是想了解 Linkly 怎么工作阅读它的源码和方言定义就足够了不需要从零实现一个编译器。9.5 保持对“领域语言”方向的敏感度未来 3 到 5 年LLM 应用开发的工程化程度会越来越高。类似 Linkly 这样的“面向 LLM 的领域语言”、类似 MCPModel Context Protocol这样的标准化协议、类似 Agent 编排框架的成熟会出现明显的分工领域语言负责表达意图协议负责不同组件互操作框架负责运行时调度传统语言负责承载整体业务系统。你不需要立刻成为编译器工程师但理解这一层分工会帮你做出更合理的技术选型判断。10. 总结与后续学习方向Linkly 这个项目最有价值的地方不在于它现在有多少 Star、是否支持所有模型接口而在于它把“面向 LLM 的编程语言”从 idea 变成了可编译的 reality。通过 MLIR 构建编译流水线意味着它有机会成为 LLM 应用基础设施层的一份子而不仅仅是一个 Hello World 玩具。回到工程实践层面从今天起有三件事是值得立刻开始的第一在现有 LLM 应用项目里引入“交互契约”概念。无论用 Pydantic、Zod 还是手写校验函数先把模型输入输出结构化定义好。第二学习 MLIR 的 Toy 教程。不一定要开发语言但要理解 MLIR 方言和 how it lowers这会极大帮助你理解编译器类项目的设计思路。第三持续关注 Linkly 的后续版本。看它的语法设计如何演进、吸引了哪些贡献者、产生了哪些实际案例。早参与一个语言项目的讨论和晚加入的成本是完全不同的。如果把这个话题继续深入值得看的方向包括LLM 应用中类型系统的建模方法、MLIR 方言设计模式、Prompt 契约的版本管理、以及领域专用语言在 Agent 系统中的定位。这些都是 Linkly 直接相关的工程问题也是 LLM 应用开发走向成熟必须跨过的门槛。用一句技术圈的老话结尾所有复杂系统最终都会走向“语言化”。数据库查询带来了 SQL分布式计算带来了 MapReduce 抽象而 LLM 应用大概也迟早会迎来属于它的那一门语言。