
1. 项目缘起当LLM驱动的Web自动化陷入“重复运行”的泥潭如果你最近尝试过用大语言模型LLM来驱动浏览器自动完成一些网页操作比如填写表单、抓取数据、或者执行一系列点击流程那你大概率会遇到一个让人头疼的问题成本失控。这不仅仅是金钱上的成本更是时间与效率上的巨大消耗。想象一下你设计了一个智能体Agent它的任务是登录一个网站查询某个商品的价格然后截图保存。理论上LLM分析你的指令生成操作步骤比如“点击登录按钮”、“在搜索框输入关键词”然后由某个执行引擎如Playwright、Selenium的驱动脚本去执行。但现实是骨感的网页状态瞬息万变一个弹窗、一个加载延迟、或者元素ID的轻微变动都可能导致当前步骤失败。这时常见的“重试”逻辑是什么把整个任务描述连同当前出错的上下文再次扔给LLM请求它给出新的操作指令或调整策略。这个过程我称之为“LLM重运行危机”。每一次重试都是一次全新的、完整的LLM推理调用。你支付的不仅仅是API费用还有等待模型思考、生成文本的时间。对于一个复杂的多步任务这种“试错-重跑”的循环可能会发生十几次甚至几十次。最终你可能花费了数十美元只完成了一个简单的任务而其中90%的推理开销都浪费在了重复分析和生成那些本质上雷同的浏览器操作指令上。更糟糕的是这种模式难以规模化任何希望将LLM智能体用于生产级Web自动化的想法都会在这堵成本高墙前止步。这正是“Agentic Compilation”这个概念试图解决的问题。它不是一个具体的工具而是一种设计范式与优化思路。其核心思想是将LLM从“实时、循环的指令生成器”转变为“一次性、可编译的蓝图规划师”。我们不再让LLM在每一步操作后都参与决策而是让它在一开始就为我们生成一个健壮的、可应对常见变化的“执行蓝图”或“策略脚本”然后由一个轻量级的、无需LLM干预的执行器去运行它。这听起来有点像传统的“录制/回放”自动化但关键区别在于这个蓝图是LLM基于对任务和网页结构的“理解”智能生成的它包含了条件逻辑、错误处理备用路径而不仅仅是僵化的坐标点击序列。简单来说我们的目标是从“LLM每步驱动”转向“LLM规划轻量引擎执行”从而将昂贵的LLM推理调用次数降到最低理想情况下一个任务只调用一次。这背后的关键技术拼图就涉及对LLM输出的结构化控制比如强制输出JSON、对网页状态的可靠感知以及一个能解释并执行“蓝图”的运行时引擎。接下来我将深入拆解如何实现这一构想并分享在构建这类系统时我踩过的坑和总结的有效策略。2. 核心症结拆解为什么简单的LLMWebDriver模式成本高昂要解决问题首先得看清问题的全貌。LLM驱动Web自动化成本高昂绝非单一原因所致而是多个环节的耦合效应。理解这些我们才能有的放矢地设计“编译”方案。2.1 指令生成的脆弱性与上下文冗余LLM生成操作指令如“click(#submitBtn)”严重依赖于它对当前网页状态的“理解”。这个理解来源于我们提供给它的上下文通常包括网页的HTML结构、当前URL、可能还有截图或可访问性树。然而网页是动态的。元素定位的脆弱性LLM可能基于id、class或XPath来定位元素。但前端框架如React, Vue动态生成的id可能每次不同class可能因样式状态改变而基于文本或位置的XPath更是脆弱不堪。一旦定位失败整个步骤就卡住了。状态同步的延迟LLM发出“点击登录”指令后需要等待页面跳转或加载新内容。如何判断“完成”通常需要再次调用LLM来分析新页面的HTML判断“登录成功”的标识如用户菜单出现。这个“等待-检查”循环本身就可能需要多次LLM调用。灾难性的上下文膨胀每次重试我们都会将出错的步骤、错误信息以及完整的页面HTML可能长达数万tokens再次发送给LLM。这意味着大量的成本花费在了反复传输和处理几乎相同的、庞大的上下文上而LLM真正需要思考的可能只是其中一小部分——那个失败的元素定位策略。2.2 缺乏中间表示与复用机制在传统软件开发中编译器会将高级语言如Python编译成字节码或机器码后者可以被高效、反复地执行。而在我们最初的LLM智能体流水线中缺少这样一个“中间表示”层。LLM的输出是自然语言或简单的指令。每一次执行都是从自然语言到具体动作的“即时翻译”。没有缓存没有优化更没有将成功路径“固化”下来的机制。即使同一个任务成功运行了100次第101次仍然需要LLM从头开始解析、规划。这完全浪费了之前运行所获得的“经验”而这些经验本可以帮助我们预判哪些地方容易出错并提前准备好备用方案。2.3 错误处理与重试逻辑的粗暴性当错误发生时最常见的重试策略是try { 执行LLM指令 } catch { 将错误信息反馈给LLM请求新指令 }。这种策略简单但效率极低。它假设LLM总能根据错误信息给出正确的修正。但错误信息可能是“元素未找到”LLM可能会尝试其他定位器也可能建议“刷新页面”甚至可能误解任务本身。这个过程可能陷入死循环。它没有区分错误类型。网络超时、元素暂时不可见正在加载和元素根本不存在页面结构已改是不同性质的问题应对策略也应不同。一股脑儿丢给LLM处理让LLM去判断错误类型这本身就是一种不必要的、高成本的负担。我们需要一个更精细的错误分类和处理机制而其中大部分逻辑完全可以用确定性的、低成本的规则来实现无需劳烦LLM。3. Agentic Compilation 的核心架构从解释执行到编译执行理解了问题我们就可以勾勒出“Agentic Compilation”的架构蓝图。其核心在于引入一个“编译器”层将LLM的“一次性规划”转化为一个可持久化、可优化、可高效执行的“工件”。3.1 阶段一LLM作为规划器输出结构化蓝图这是最关键的一步。我们不能让LLM自由发挥输出自然语言必须约束其输出格式使其生成一个结构化的、机器可读的任务蓝图。JSON是目前最理想的选择因为它兼具人类可读性和强大的程序可解析性。为什么是JSON从网络热词中频繁出现的json、json格式、json转换、json序列化可以看出JSON已是数据交换的事实标准。对于LLM而言在系统提示词中强制要求输出JSON并通过json schema进行约束能极大提高输出的结构化和稳定性。相比于自然语言JSON能清晰地表达层级、列表和键值对完美适配自动化步骤的描述。一个简单的任务蓝图JSON结构可能如下{ task_name: 查询商品价格并截图, parameters: { product_name: {{user_input}} }, steps: [ { id: step_1, action: navigate, args: { url: https://example.com }, retry_policy: {max_attempts: 2, delay: 1}, error_handling: { on_timeout: refresh_page, on_element_not_found: fail_fast } }, { id: step_2, action: click, args: { selector: button:has-text(登录), strategy: playwright_text }, preconditions: [ {check: element_visible, selector: button:has-text(登录)} ] }, { id: step_3, action: fill, args: { selector: input[namesearch], value: $.parameters.product_name } }, { id: step_4, action: screenshot, args: { selector: .product-price, path: output/price_{{timestamp}}.png } } ], rollback_steps: [ {action: clear_cookies} ] }在这个阶段LLM的职责是理解任务意图分析用户的自然语言指令如“帮我到XX网站查一下iPhone 15的价格并截图发我”。分解任务步骤将宏观任务分解为具体的、顺序的或带条件分支的浏览器操作步骤。智能选择定位策略基于对常见网页模式的了解选择更稳健的定位器组合。例如优先使用aria-label或>