
这次我们来看一个来自 Hacker News Show HN 的新项目Linkly。它的定位很直接——一门“为 LLM 设计的语言”并且后端编译走的是MLIR。如果你平时做 LLM 应用开发看到这个消息的第一反应应该是又来了一个 DSL还是说编译器级别的玩法真的能改变 LLM 工程的繁琐程度先说结论方向这个项目值得关注不是因为多了一个 Prompt 模板工具而是因为它把 LLM 工作流当成“程序”来编译而不是“文本拼接”。这背后的思路会直接影响以后 LLM 应用的性能、可维护性和部署方式。这篇文章会从编译器视角拆解 Linkly 可能的技术架构、为什么 MLIR 适合做这件事、它能解决什么问题以及上手前需要了解的限制。1. 核心能力速览在深挖原理之前先把 Linkly 的技术特征列出来。这里要特别说明项目目前还在早期公开阶段部分参数需要以实际发布版本和你的测试环境为准以下表格区分了“从标题可确认的信息”和“需要实测确认的信息”。能力项说明项目类型面向 LLM 工作流的编程语言 编译器技术底座MLIR即 Multi-Level Intermediate Representation设计目标用编译器思路解决 LLM 工作流的编排、优化和执行问题与 LangChain 等框架的差异语言层静态表达 编译期优化而不是运行时解释显存需求取决于最终生成的推理后端编译产物可运行在不同硬件上支持平台理论上跟随 MLIR/LLVM 后端生态需按实际构建环境测试启动方式未公开需以项目 README / 发布说明为准API 能力未公开需以项目文档为准批量任务编译器生成原生代码后批量能力取决于运行时调度设计适合场景追求低延迟、高可控的 LLM Agent / 工作流 / 工具链开发从这张表能看出Linkly 不是一个“装上去就能用的工具”而是一个需要你理解编译器基本概念后才能发挥价值的项目。如果你只是想在 ComfyUI 里连一个 LLM 节点或者写一段 Python 调 OpenAI API那 Linkly 暂时不是你的最优选择。但如果你关心的是“几千次工具调用怎么调度才不卡”“复杂 Agent 状态怎么静态检查”“多模型后端怎么统一编译”Linkly 提供了一条值得跟进的技术路线。2. 这个项目在解决什么问题最近两年 LLM 应用开发逐渐形成了一套固定套路Prompt 模板 上下文拼装 工具调用 循环判断。这套套路看似简单真正推到生产环境就暴露出一堆问题第一Prompt 本质上是字符串。你写 Prompt 的时候变量替换只在运行时发生编译期没有任何检查。一个变量名拼错往往要等请求发出去、模型返回奇怪结果之后才能发现。第二Agent 控制流是隐式的。一个 Agent 需要判断“什么时候调用工具”“工具返回后怎么续接对话”“多轮循环什么时候退出”。这些逻辑散落在代码里靠 if/else 硬写改一处可能崩三处。第三运行时解释有额外开销。LangChain 这类框架在运行时逐条解释节点、动态构造消息。灵活是灵活但每次调用都要做大量重复的分发、校验、序列化。面对高频低延迟场景效率并不理想。Linkly 的思路则是把这些事情推到编译期。如果用一句话概括它把“我要构建一个什么样的 LLM 工作流”写成源语言然后通过编译器把工作流转换成可执行代码。模板变量、类型检查、控制流、工具签名全部在编译阶段静态校验运行时拿到的是一份优化后的执行计划而不是一边跑一边解释。这个方向之所以激进是因为它把之前“动态灵活”的 LLM 应用开发重新拉回了传统编程语言的静态安全区。好处是稳定可预测代价是上手门槛更高、迭代更重。3. 编译器视角Linkly 可能的技术架构虽然 Linkly 的完整源码和设计文档还没有全部公开但根据“通过 MLIR 编译”这个信息可以合理推演出它的编译器分层架构。以下推导基于编译器通用设计不一定与项目最终实现完全一致但用来理解该项目的工作原理足够。3.1 前端语法、AST、类型系统任何语言的第一步都是源码解析。Linkly 的源码大概会长得像一门“带 LLM 原语”的脚本语言而不是 JSON/XML。它需要表达以下元素模型调用声明调用哪个模型、温度、最大 token 数、是否流式。消息结构system / user / assistant 消息之间的类型关系。工具声明函数签名、参数 Schema、返回值类型。控制流循环、条件、分支、重试、错误处理。上下文操作历史消息裁剪、文件注入、向量数据库查询结果注入。这些元素如果都做成语言关键字前端解析器的工作量不小。更合理的设计是把最基础的控制流和类型系统沿用现有编译器的成熟方案把 LLM 相关能力做成“内建函数 内建类型”。这种方式既能保持语言表达力又能复用大量编译器基础设施。解析得到 AST 后下一步是做类型检查和语义分析。这个阶段可以检查某段消息变量是否真的存在。工具函数的参数类型和模型生成的 JSON 是否匹配。循环边界是否可能无限执行。上下文窗口大小是否静态可估算。静态类型检查是 Linkly 相对 LangChain 的核心优势。运行时才能发现的错误在编译期就拦截下来这是正经编译器带来的确定性收益。3.2 中端MLIR Dialect 与多级抽象MLIR 的核心概念是 Dialect 和 Operation。Linkly 大概率会定义一套自己的Linkly Dialect把与 LLM 相关的语义表达成 MLIR 操作例如构造消息。发起一次模型推理。解析模型输出。执行工具调用。维护多轮对话状态。这些高层操作先以 Linkly Dialect 形式存在方便上层分析和优化。比如可以写一个 Pass识别“连续两次相同模型的推理调用”判断是否能合并上下文或者识别“某个模型输出一定会进入某个工具调用”提前做格式约束。然后Linkly Dialect 会逐步降级到更底层的通用 Dialect。比如控制流部分降级到scf/cfDialect数值计算部分降级到arith/math内存操作降级到memref。等到所有高层语义都被替换成通用运算MLIR 就能把代码继续降级到 LLVM IR接着由 LLVM 生成机器码。这种多级降级的意义在于LLM 工作流的逻辑表达和实际执行可以彻底解耦。写源码的时候你面向的是“模型调用”这个抽象编译之后具体是调用云端 API、本地 vLLM 服务、还是某个加速芯片上的推理引擎全由后端决定。3.3 后端LLVM 与异构执行编译器走到 LLVM IR 之后就已经具备了跨平台生成二进制的能力。这意味着 Linkly 编译出的 Agent 程序理论上可以在 CPU 上运行也可以通过 MLIR 的 GPU 相关 Dialect 将部分计算下沉到 GPU。不过要注意LLM 本身的大模型推理通常不在 Linkly 的编译范围内——重点应该是消息拼装、工具分发、上下文管理等逻辑部分。这些逻辑编译成原生码后配合一个高性能的模型推理客户端可以做到极低的开销。从架构上讲变可以形成一条完整链路Linkly 源码 → Linkly AST → Linkly DialectMLIR 操作 → 优化 Pass → scf / arith / memref 等通用 Dialect → LLVM IR → 目标机器码也就是说Linkly 能编译出来的不仅是“一段调用 LLM 的脚本”而是一个可按需部署的原生可执行程序。它不依赖 Python 解释器也不依赖运行时框架的解释执行天然适合容器化部署和高频调用场景。4. 为什么选择 MLIR而不是自建 IR有人可能会问做一个新语言为什么非要 MLIR我自己搞一套中间表示不也行能做但没必要。MLIR 的价值主要体现在四个维度。4.1 多级抽象天然适合“语言下沉”MLIR 设计哲学就是“每一层抽象都可以用 Dialect 表达层与层之间通过转换连接”。Linkly 这样的语言最舒服的表达方式就是先停留在高层 Dialect等优化完成后逐级降低。你不需要像某些传统编译器那样一步到位从 AST 跳到机器码从而丢失中间层优化机会。如果自建 IR你必须同时维护IR 数据结构、打印与解析、Pass 管理器、Dialect 转换、与 LLVM 的衔接。这一整套工程量非常大MLIR 直接替你解决。4.2 现成的 Pass 基础设施MLIR 自带一套完整的 Pass 管理机制。你可以很轻松地写一个函数级别的 Pass或者 Module 级别的 Pass对 LLM 工作流做分析或改写。例如Dead Code Elimination如果某个 Prompt 变量的值从未被真正使用编译期直接删掉。循环优化Agent 在多轮循环里重复加载的上下文合并为一次加载。内联展开高频小工具函数直接展开到调用点减少函数调用开销。对 LLM 工作流来说这些优化不仅能降低运行延迟还能减少不必要的模型调用次数。模型少跑一次省下的不仅是时间还有真金白银的 API 成本。4.3 生态与硬件适配MLIR 的生态覆盖了 CPU、GPU、TPU、专用 AI 芯片等后端。只要 Linkly 把高层语义完整降级到通用 Dialect理论上就能接入这些硬件生态。对云原生部署场景而言这意味着同一个 Linkly 源码可以编译出面向不同环境的运行产物不需要为每个硬件平台单独写一套 Agent 逻辑。另外MLIR 文档和社区虽然学习曲线陡峭但已经是编译器领域的“事实标准”之一。项目选择 MLIR更容易吸引编译器工程师参与贡献长期维护性会优于一个封闭的自研 IR。4.4 与 LLVM 的传承关系MLIR 由 LLVM 项目孵化底层的 LLVM IR 生成和优化体系非常成熟。代码生成阶段可以直接复用 LLVM 的优化和代码生成器生成高质量机器码。对新兴语言来说这是一条风险最低的“编译器工业化路径”。5. Linkly 语言能表达什么这一节无法确认 Linkly 的真实语法所以只做一个“合理的功能推演”。如果你对编译器设计和 LLM 工作流有经验可以按照下面的方式思考 Linkly 可能会提供哪些能力。5.1 工作流即程序传统 LLM 工作流是脚本式、模板式的。Linkly 则想把它变成结构化程序。一个典型的多轮 Agent 循环用类 Python 伪代码表达大概是# 概念性伪代码不代表 Linkly 真实语法 def web_search_agent(query: str) - str: context [] for round in range(3): messages [ {role: system, content: 你是搜索助手只使用工具返回的事实回答。}, {role: user, content: query}, *context ] result llm.chat( modelqwen2.5:7b, messagesmessages, tools[search_web, extract_content], temperature0.2 ) if result.finish_reason tool_calls: context.append(result.message) for tool_call in result.tool_calls: output dispatch_tool(tool_call) context.append({role: tool, tool_call_id: tool_call.id, content: output}) continue else: return result.content这看起来有点像普通代码但关键点是tools 是类型安全的。search_web和extract_content的入参、出参类型在编译期可查。如果模型返回的 JSON 与工具签名不匹配编译器生成的解析代码可以直接报类型错误而不是运行时拿到一个空字段继续跑。5.2 类型安全的消息与上下文LLM 应用最容易出问题的地方是上下文消息格式混乱。Linkly 可以把消息定义成强类型结构# 概念示意 message UserMessage { content: string attachments: listFile } message ToolResult { call_id: string content: object status: int }编译器可以在编译期检查轮次拼接时ToolResult 是否一定出现在对应的 ToolCall 之后UserMessage 的 attachments 是否超出了模型支持的类型多轮循环中累积 token 是否超出了模型上下文窗口。这些检查放在运行时做费时费力放在编译期做则是零额外损耗。5.3 工具调用的静态检查前面提到工具调用很容易因为动态 JSON 解析而出错。Linkly 如果实现得好可以把工具的函数签名编译进模型请求的 JSON Schema 中同时对返回结果做静态反序列化。这样模型输出的合法性在编译期就被约束了一部分运行时解析逻辑也会更快。更进阶的优化是当某个工具的返回值类型已知编译器可以自动把“工具返回 → 消息拼接 → 再次调用模型”这一串操作融合成一段原生代码省去中间序列化开销。6. 一个概念性的编译流程推演为了更直观地展示 Linkly 的“编译”到底在做什么我用一个高度简化的伪代码推演整条链路。这只是概念示意不是项目真实语法。6.1 源码输入假设我们要做一个简单工具把一段 HTML 正文提取成 Markdown。用 Linkly 编写时大概会声明一个函数# 概念性示意非真实语法 def html_to_markdown(html: string) - string: cleaned tool.call(html_cleaner, {html: html}) prompt build_prompt( template请将以下 HTML 正文转换为 Markdown\n\n{{content}}, contentcleaned ) result llm.complete( modelqwen2.5:7b, messages[{role: user, content: prompt}], max_tokens1024 ) return result.text6.2 中间表示编译过程会把这个函数转换为 MLIR 层面的操作序列概念上类似// 概念性示意非真实 MLIR 输出 func.func html_to_markdown(%html: !linkly.string) - !linkly.string { %cleaned linkly.tool_call html_cleaner (%html) %prompt linkly.build_prompt(请将以下 HTML 正文转换为 Markdown\n\n{{content}}, %cleaned) %result linkly.llm_complete { model qwen2.5:7b, max_tokens 1024 } (%prompt) %text linkly.message_text(%result) return %text }这些linkly.*操作就属于 Linkly Dialect。6.3 优化与降级编译器接下来会运行一系列 Pass。例如它可能发现html_cleaner的返回结果没有传给最终答案只是拼进了 Prompt于是将html_cleaner和build_prompt合并成一次字符串构建可能发现max_tokens 1024是固定值可以在生成请求时直接硬编码省去配置分发。再降一级linkly.llm_complete操作会变成调用某个具体后端的函数体比如 HTTP 调用一个本地推理服务// 概念性示意 %client llvm.call create_http_client(http://127.0.0.1:8000/v1) %response llvm.call http_post_json(%client, %payload) %text llvm.call parse_response_text(%response)6.4 最终产物最终生成的产物不再包含任何 LLM 编排框架的解释器而是一个原生可执行文件或一个可嵌入的库。运行时做的事情几乎只有拼装 HTTP 请求。发送到推理服务。解析返回内容。执行工具函数。没有多余的分发层没有动态反射没有 Python 解释器开销。这就是“编译”带来的直接收益。7. 对 LLM 开发方式的影响Linkly 最值得关注的地方不只是“又出现了一个新语言”而是它背后的开发范式转变。7.1 从“运行时解释”到“编译期优化”LangChain、LlamaIndex 这类框架属于解释执行式。它们把节点定义成对象运行时动态决定执行路径。优点是快速原型缺点是性能边界难以突破。Linkly 的做法是把工作流描述提前编译成机器码。它把“开发时灵活”和“运行时性能”做了切割。开发时你用一门语言描述工作流表达自由编译后你拿到一份极度精简的执行计划性能逼近手写原生代码。7.2 从“文本拼接”到“静态内建”传统 Prompt 维护是个老大难问题。变量替换错误、格式错乱、上下文拼接重叠经常到了线上才暴露。Linkly 一旦把 Prompt 当成语言内建结构来处理这些问题就能在编译器前端被拦截。假设你在源码里写了{{contnet}}而不是{{content}}编译器完全可以根据变量表给出错误提示。这是字符串拼接永远做不到的。7.3 从“脚本”到“可部署产物”传统 LLM 应用部署需要准备 Python 环境、安装依赖、配置框架版本。Linkly 编译产物如果是一个原生可执行文件部署逻辑将大大简化上传二进制配好外部推理服务地址启动完事。这种部署形态对边缘计算、内网隔离环境、嵌入式设备尤其有意义。哪里需要 LLM Agent就往哪里丢一个编译好的二进制连 Python 都不用装。8. 工程化部署与性能观察Linkly 真到了生产环境需要重点观察哪些指标给你一套通用参考框架。8.1 启动性能解释型 LLM 框架启动要加载解释器、初始化运行时、解析配置。一个原生编译产物启动则是毫秒级。如果你的 Agent 服务经常要扩容、冷启动、按请求起进程启动速度的差异会非常明显。8.2 请求延迟编译器可以静态做很多优化减少不必要的上下文拼接、提前生成请求体、复用 HTTP 连接、避免重复的消息序列化。端到端延迟里模型推理时间依然是大头但在推理之外的部分编译产物可以把开销压到几乎可以忽略。8.3 资源占用编译产物不加载 Python 运行时也不拖一个框架解释器内存占用通常更低。如果后端还能把轻量计算直接集成进二进制省去进程间通信资源消耗会更可控。8.4 批量任务Linkly 的批量能力取决于编译产物如何集成调度器。如果你需要跑一万个文档的提取任务可以把它编译成一个小型 worker配合消息队列并行部署。每个 worker 只做一件事拉取任务、调用模型、返回结果。这种模式的优势是资源利用率和失败隔离都优于一个大进程内部的多线程批量。9. 局限性与现实权衡说完了优点也要看清楚这个项目的现实挑战。第一编译器上手门槛高。MLIR 本身就是著名的高门槛技术栈。如果你没写过编译器要理解 Linkly 的内部实现会非常吃力。但普通使用者可以只接触语言本身把 MLIR 当作黑盒。第二生态需要时间。一个新语言的最大问题是库和社区。LLM 工具调用、内存检索、向量数据库等生态接入都需要逐步积累。对比 LangChain 的庞大生态Linkly 短期内不可能追平。第三动态场景受限。编译器偏爱的是结构清晰、类型明确的工作流。如果你的 Agent 逻辑极端依赖动态字符串、插件热加载、运行时自定义指令编译期静态检查反而可能是约束。Linkly 适合“确定性高”的流程不适合“自由度极高”的探索式场景。第四开源成熟度未知。项目的开源许可、文档完善度、示例代码数量、维护频率都还没到可以下结论的阶段。现在可以关注但不要直接把它压到核心生产链路。第五模型推理仍是瓶颈。不管 Linkly 多高效一次大模型推理的耗时依然是几十到几百毫秒。编译优化消除的只是编排开销不能改变模型本身的性能天花板。所以 Linkly 适合的是高度标准化的 LLM 服务优化目标是把“外围开销”降到接近零而不是让模型跑得更快。10. 常见问题与排查思路问题现象可能原因排查方式解决方案编译报错找不到模型配置源码中引用的模型未在配置中定义检查模型配置文件和源码引用在配置中补充模型定义或修改源码引用提示词变量未解析模板变量与上下文类型不匹配查看编译错误信息中的变量表修改变量名或类型声明工具调用失败工具的入参类型与模型输出不匹配检查工具签名和编译生成的反序列化逻辑调整 Schema 字段约束或修改工具签名编译产物运行起来调用不到推理服务推理服务地址配置错误或未启动先 curl 验证推理服务健康状态修改服务地址或先启动本地推理服务批量任务大量失败某个输入格式异常导致编译产物崩溃查看单条任务日志和退出码增加输入校验做好单条失败隔离与重试处理长文档时上下文溢出未对输入做分块或剪裁观察模型返回的错误信息和 token 占用在源码中加入分块逻辑或调整上下文裁剪策略这套排查思路也适用于大多数 LLM 编译类项目。核心原则永远是先确认模型服务本身是否正常再查编译产物最后查源码逻辑。11. 最佳实践与使用建议如果你打算尝试 Linkly 或者同类项目下面这几条建议值得留在手边。11.1 先跑最小示例不要一上来就写复杂 Agent。先跑通一个最简单的“用户输入 Prompt → 模型返回文本”的流程确认编译链路、推理服务连接、产物执行都正常。小范围验证通过后再逐步加入工具调用、多轮循环、批量任务。11.2 用编译期检查兜底充分利用语言提供的静态检查。把工具函数的参数类型、消息结构、上下文分段都显式声明不要依赖隐式类型。编译器能帮你拦截的错误越多线上事故越少。11.3 分场景选型如果只是快速验证想法LangChain 和 Python 动态脚本依然是最快的。如果需要高频调用和低延迟才需要像 Linkly 这样走编译路线。两者不是替代关系是不同阶段的不同工具。11.4 日志与可观测性不管用什么语言编译LLM 应用的日志都极其重要。建议为每次模型调用记录输入消息、输出结果、token 消耗、延迟。编译产物可能很高效但没有日志排错会非常痛苦。11.5 关注模型服务层Linkly 优化的是编排层模型层仍然是效率大头。建议在编译产物后面接一个稳定高效的推理服务并做好并发控制、请求队列和降级策略这样才能让编译优化的优势真正发挥出来。12. 总结与下一步Linkly 这个项目最值得关注的地方不是某个具体功能而是“用编译器思维重构 LLM 工作流”这个方向本身。它把 Prompt 拼装、Agent 控制流、工具调用从“运行时解释”推向“编译期静态优化”这是 LLM 工程走向成熟必然会经历的一步。如果你想验证这个项目的价值重点看三件事Linkly 的源码语法设计是否真的让 Agent 工作流表达得更清晰。MLIR 降级链路是否真的在编译期做掉了运行时框架的开销。编译产物的部署形态是否真的带来部署和运维上的简化。最容易踩的坑是把它当成 LangChain 的替代品直接迁移。实际上它更适合为“高度标准化的高频工作流”做专业化编译。先用小项目验证思路再考虑是否进入核心链路是比较稳妥的路径。后面值得跟进的方向包括Linkly 能否产出稳定的 Docker 镜像、是否支持常见向量数据库和工具生态、多模型切换时的编译策略如何设计。如果项目持续迭代这套“面向 LLM 的编译器”思路很可能会影响更多 LLM 基础设施的设计方向。对于关注 LLM 工程化的开发者来说Linkly 值得放进收藏夹长期观察。