基于LLM的Web智能体规划框架:从理论到工程实践

📅 2026/8/21 3:52:02
基于LLM的Web智能体规划框架:从理论到工程实践
1. 项目概述当LLM学会“思考”与“规划”最近在折腾基于大语言模型LLM的Web智能体Web Agent时我遇到了一个核心瓶颈LLM虽然能理解指令并生成操作步骤但它的行动往往是“走一步看一步”的。比如你让它“去电商网站帮我找一款价格低于500元的无线鼠标并加入购物车”它可能会先打开浏览器搜索“无线鼠标”然后面对海量结果就懵了或者直接点进第一个链接完全忘了价格限制。这种缺乏“前瞻性”和“结构化思考”的行为让智能体在完成复杂、多步骤的网页任务时显得笨拙且不可靠。这正是“AI Planning Framework for LLM-Based Web Agents”这个项目要解决的核心问题。它不是一个具体的工具或库而是一套设计思想和架构模式旨在为LLM驱动的Web智能体注入“规划”能力。简单来说就是教会智能体在动手之前先“动脑”想清楚目标是什么需要分几步走每一步具体做什么遇到意外情况比如页面元素没加载出来、弹窗干扰该怎么办想象一下你是一个经验丰富的项目经理接到一个复杂任务后你不会立刻埋头苦干而是会先拆解任务、评估风险、制定时间表和备选方案。AI Planning Framework 就是要让LLM扮演这个“项目经理”的角色。它通过引入规划器Planner、状态跟踪器State Tracker、动作执行器Executor等模块将一次性的、模糊的LLM指令转化为一个可监控、可调整、可回溯的自动化流程。这对于实现自动化的数据抓取、跨网站的信息整合、复杂的业务流程测试RPA等场景价值巨大。接下来我将结合自己的实践深度拆解如何从零构建这样一个规划框架涵盖核心设计、关键技术选型、实操步骤以及那些只有踩过坑才知道的细节。2. 框架核心设计从“反应式”到“规划式”的进化一个典型的、没有规划的LLM Web Agent工作流是“反应式”的用户输入指令 - LLM解析指令并生成单个动作如点击ID为‘search’的按钮- 执行器执行动作 - 将新的页面状态HTML、截图返回给LLM - LLM再生成下一个动作。这个循环非常脆弱任何页面状态的微小变化都可能导致LLM“迷失方向”。规划框架的核心就是在这个循环中插入一个“大脑”——规划模块。其典型架构包含以下核心组件我将其类比为一个特种作战小队的分工2.1 规划器任务拆解与路径生成的“指挥官”规划器是框架的决策核心。它的输入是高层级任务描述如“预订下周五北京到上海的最早航班”和当前环境状态输出是一个动作序列或一个可执行的计划树。1. 基于LLM的规划器这是目前最主流和灵活的方式。我们不再让LLM直接生成底层操作click, type而是让它生成高级别的子任务或步骤。工作原理我们将任务描述、当前页面摘要或目标网站的结构化知识、以及规划格式要求构成一个提示词Prompt发送给LLM。提示词设计示例你是一个网页任务规划专家。请将以下用户目标分解为具体的步骤序列。每个步骤应该是原子化的、可执行的动作描述。 用户目标在Example航空官网example-air.com查找并预订明天从北京到上海价格低于1000元的经济舱航班。 已知网站结构首页有“航班查询”表单需要填写出发城市、到达城市、日期搜索结果页会列出航班列表包含价格、时间等信息每个航班条目旁有“选择”按钮。 请以JSON格式输出包含字段step_id步骤序号description步骤描述expected_outcome预期结果。输出示例[ {step_id: 1, description: 导航至 example-air.com, expected_outcome: 成功加载航空公司官网首页}, {step_id: 2, description: 在查询表单中填入出发城市北京到达城市上海日期明天, expected_outcome: 表单填写完毕}, {step_id: 3, description: 点击‘搜索航班’按钮, expected_outcome: 跳转到航班列表页面}, {step_id: 4, description: 在列表中找到第一个价格低于1000元的经济舱航班, expected_outcome: 定位到目标航班条目}, {step_id: 5, description: 点击该航班旁的‘选择’按钮, expected_outcome: 进入乘客信息填写页面} ]实操心得expected_outcome字段至关重要。它是后续“状态跟踪器”判断步骤是否成功的依据。描述要具体如“页面URL包含‘search-results’”、“出现包含‘价格XXX元’的文本块”。2. 基于经典AI规划算法可选增强对于领域固定、状态空间定义明确的任务如固定的企业内部系统可以结合经典规划算法如PDDL。LLM负责将自然语言目标翻译成规划域定义然后由专用规划器求解最优动作序列。这能提供更强的逻辑保证但灵活性和开发成本较高。在实际中我更多将其作为LLM规划的校验和补全手段。2.2 状态跟踪器感知环境与验证进度的“侦察兵”状态跟踪器负责理解“我们现在在哪上一步成功了吗”。它对比规划步骤中的expected_outcome与实际环境状态。1. 状态提取DOM/HTML分析解析当前页面DOM提取关键元素、文本、属性。这是最精确的方式但依赖于元素选择器的稳定性。视觉感知OCR多模态模型对页面截图进行识别。这对于动态加载、Canvas渲染或DOM结构复杂的页面非常有效。可以结合OCR提取文字再用多模态LLM如GPT-4V理解整体布局和元素状态按钮是否可点击、加载条是否消失。URL与页面标题最基础的状态信号常用于判断页面导航是否成功。2. 状态验证 根据提取的状态判断当前步骤的expected_outcome是否达成。这可以是一个简单的规则匹配如“检查页面标题是否包含‘搜索结果’”也可以再次调用一个小型LLM进行判断“根据以下页面摘要判断‘表单是否填写完毕’”。注意状态跟踪是规划框架中最易出错也最需要精心设计的环节。网页的微小变化如A/B测试、广告弹窗都可能导致跟踪失败。必须设计冗余和容错机制例如设置超时、准备多个特征用于验证同一状态。2.3 动作执行器精准执行命令的“突击队员”执行器接收规划器输出的具体动作描述或由另一个LLM翻译成的底层指令并操作浏览器或网络接口。1. 底层操作抽象 设计一套统一的原子操作API例如navigate(url)click(selector)type(selector, text)extract_text(selector)wait_for_element(selector, timeout)2. 动作翻译层 规划器输出的是“点击‘搜索航班’按钮”这样的高级描述。需要一个“动作翻译”模块可以是一个轻量级LLM或一套规则将其映射到底层的click(selector)并确定selector如#search-btn或button:has-text(‘搜索航班’)。这个模块需要与状态跟踪器共享对页面元素的认知。2.4 记忆与反思模块积累经验的“参谋部”这是让智能体越用越聪明的关键。它记录完整的任务执行历史规划、动作、状态、结果特别是在失败或需要人工干预的时刻。成功经验库将成功完成任务的规划序列、关键状态特征存储下来。未来遇到类似任务时可以直接检索复用或微调大幅提升效率和成功率。失败反思当任务失败时触发一个“反思”循环。让LLM分析执行日志诊断失败原因是规划不合理、状态跟踪错误还是执行器问题并提出修正方案甚至重新规划。例如失败原因是“价格筛选按钮未找到”反思后可能将动作修正为“先点击‘更多筛选’展开隐藏菜单”。3. 技术栈选型与实操搭建理论讲完了我们来点实际的。如何用现有的开源工具搭建一个可运行的规划框架原型以下是我的技术选型和搭建步骤。3.1 核心组件选型1. LLM服务规划与决策大脑云端APIOpenAI GPT-4/GPT-4o/Claude 3。优点是能力强、省心适合快速原型验证。缺点是成本、延迟和网络依赖性。关键提示为规划任务设计专用、结构化的Prompt并利用response_format{ type: json_object }确保输出可解析。本地模型Llama 3.170B/405B、Qwen 2.572B、DeepSeek-V2。开源模型可控性强、无数据隐私顾虑。需要一台性能足够的GPU服务器如RTX 4090/A100。使用llama.cpp或vLLM进行高效推理。对于规划任务70B参数以上的模型效果才比较可靠。2. 浏览器自动化执行与感知手脚Playwright我的首选。比Selenium更现代API优雅自动等待机制健全对动态网页支持好且能轻松录制脚本。其page.content()、page.screenshot()和强大的选择器text、has是状态跟踪的基础。Puppeteer与Playwright类似但主要专注于Chrome。如果只针对Chromium系浏览器也是一个好选择。3. 多模态与视觉理解增强侦察兵OCRpytesseract或easyocr用于从截图中提取文本。多模态LLM用于理解截图整体语义。云端可用GPT-4V本地可选LLaVA-NeXT、Qwen-VL等。将截图和问题如“图中‘提交’按钮是否处于灰色不可点击状态”输入获取判断。4. 应用框架与编排LangChain / LangGraph提供了智能体Agent和链Chain的抽象内置了多种工具调用和记忆模块。LangGraph特别适合构建有状态、带循环规划-执行-检查的智能体工作流强烈推荐用于构建规划框架的主循环。AutoGen微软推出的多智能体框架擅长构建协作智能体。可以将规划器、执行器、校验器设计成不同的智能体角色让他们通过对话协商完成任务架构上更清晰但复杂度也更高。纯Python脚本对于理解底层原理和高度定制化需求从零开始用asyncio等库编排上述模块能获得最大的灵活性。3.2 分步搭建一个基础规划框架假设我们使用OpenAI API Playwright LangGraph这个组合快速搭建一个原型。步骤1环境准备与初始化# 创建项目并安装核心库 pip install openai playwright langgraph langchain playwright install chromium # 安装浏览器驱动步骤2定义原子操作工具Tools我们将Playwright操作封装成LangChain可调用的工具Tool。from langchain.tools import tool from playwright.async_api import async_playwright import asyncio class BrowserController: def __init__(self): self.playwright None self.browser None self.page None async def start(self): self.playwright await async_playwright().start() self.browser await self.playwright.chromium.launch(headlessFalse) # 调试时可设为False self.page await self.browser.new_page() tool async def navigate(self, url: str) - str: 导航到指定URL。 await self.page.goto(url) return f已导航至 {url}当前标题{await self.page.title()} tool async def click(self, element_description: str) - str: 根据元素描述点击页面元素。描述应尽量具体如‘搜索按钮’、‘登录链接’。 # 这里需要实现从描述到Playwright选择器的映射。简化版示例 if “搜索” in element_description: selector button[typesubmit] else: selector ftext{element_description} # 简单按文本查找 try: await self.page.click(selector) return f已点击 {element_description} except Exception as e: return f点击失败{str(e)} tool async def extract_page_info(self) - str: 提取当前页面的关键信息用于状态跟踪。 title await self.page.title() url self.page.url # 提取首屏主要文本内容避免巨大DOM content await self.page.evaluate(() { const bodyText document.body.innerText; return bodyText.length 2000 ? bodyText.substring(0, 2000) ... : bodyText; }) summary f页面标题{title}\\nURL{url}\\n内容摘要{content[:500]}... return summary步骤3构建规划器Planner使用LLM根据目标和当前状态生成步骤规划。from langchain_openai import ChatOpenAI from langchain_core.prompts import ChatPromptTemplate from langchain_core.output_parsers import JsonOutputParser from pydantic import BaseModel, Field from typing import List # 定义规划步骤的数据模型 class PlanStep(BaseModel): step_id: int Field(description步骤序号) description: str Field(description具体、可执行的步骤描述) expected_outcome: str Field(description完成此步骤后期望看到的结果) class Plan(BaseModel): steps: List[PlanStep] Field(description规划步骤列表) class Planner: def __init__(self, llm): self.llm llm self.parser JsonOutputParser(pydantic_objectPlan) self.prompt_template ChatPromptTemplate.from_messages([ (system, 你是一个专业的网页任务规划师。请将用户目标分解为清晰的步骤。{format_instructions}), (human, 用户目标{goal}\\n当前页面状态{current_state}\\n请为此任务制定一个规划。) ]) async def generate_plan(self, goal: str, current_state: str) - Plan: prompt self.prompt_template.format_messages( goalgoal, current_statecurrent_state, format_instructionsself.parser.get_format_instructions() ) response await self.llm.ainvoke(prompt) plan_dict self.parser.invoke(response) return Plan(**plan_dict)步骤4构建状态跟踪器State Tracker比较预期结果与实际状态。class StateTracker: def __init__(self, llm): self.llm llm async def check_step_success(self, expected_outcome: str, actual_state: str) - bool: 使用LLM判断预期结果是否达成。 prompt f 请扮演一个质量检查员。 任务步骤的预期结果是{expected_outcome} 当前页面的实际状态描述是{actual_state} 根据实际状态预期结果是否已经达成 请只回答‘是’或‘否’并简要说明原因一句话。 回答格式是/否原因... response await self.llm.ainvoke(prompt) answer response.content.strip() return answer.startswith(是)步骤5使用LangGraph编排主工作流这是框架的核心将规划、执行、检查串联成一个有状态的图。from langgraph.graph import StateGraph, END from typing import TypedDict, Annotated import operator # 定义工作流状态结构 class AgentState(TypedDict): goal: str # 用户目标 plan: List[PlanStep] # 当前计划 current_step_index: int # 当前执行到第几步 current_state: str # 当前页面状态描述 history: List[str] # 执行历史记录 is_complete: bool # 任务是否完成 # 初始化组件 llm ChatOpenAI(modelgpt-4o, temperature0) browser_controller BrowserController() await browser_controller.start() planner Planner(llm) tracker StateTracker(llm) # 定义各个节点函数 async def plan_node(state: AgentState): 规划节点生成初始计划。 print(f[规划器] 开始为目标‘{state[goal]}’生成计划...) plan await planner.generate_plan(state[goal], state.get(current_state, 初始状态)) state[plan] plan.steps state[current_step_index] 0 state[history].append(f生成计划共{len(plan.steps)}步。) return state async def execute_node(state: AgentState): 执行节点执行当前步骤的动作。 step_index state[current_step_index] current_step state[plan][step_index] print(f[执行器] 执行步骤{step_index1}: {current_step.description}) # 这里需要将步骤描述映射到具体的浏览器工具调用简化处理 if 导航 in current_step.description: url https://example-air.com # 应从中提取URL result await browser_controller.navigate(url) elif 点击 in current_step.description: element current_step.description.replace(点击, ).strip() result await browser_controller.click(element) else: result f执行了{current_step.description} state[history].append(f步骤{step_index1}执行结果{result}) # 执行后获取新状态 state[current_state] await browser_controller.extract_page_info() return state async def check_node(state: AgentState): 检查节点验证当前步骤是否成功。 step_index state[current_step_index] current_step state[plan][step_index] print(f[检查器] 验证步骤{step_index1}的预期结果{current_step.expected_outcome}) success await tracker.check_step_success(current_step.expected_outcome, state[current_state]) if success: print(f步骤{step_index1} 验证成功。) state[history].append(f步骤{step_index1} 验证成功。) # 移动到下一步 state[current_step_index] 1 if state[current_step_index] len(state[plan]): state[is_complete] True else: print(f步骤{step_index1} 验证失败。) state[history].append(f步骤{step_index1} 验证失败当前状态不符预期。) # 这里可以触发重试或反思逻辑简单起见我们标记失败并停止 state[is_complete] True # 实际应进入错误处理节点 return state def should_continue(state: AgentState) - str: 判断下一步该去哪继续执行还是结束 if state.get(is_complete, False): return end else: return execute # 构建图 workflow StateGraph(AgentState) workflow.add_node(plan, plan_node) workflow.add_node(execute, execute_node) workflow.add_node(check, check_node) workflow.set_entry_point(plan) workflow.add_edge(plan, execute) workflow.add_edge(execute, check) # 根据检查结果决定循环执行还是结束 workflow.add_conditional_edges( check, should_continue, { end: END, execute: execute, } ) # 编译图 app workflow.compile() # 运行智能体 async def run_agent(goal: str): initial_state AgentState(goalgoal, plan[], current_step_index0, current_state, history[], is_completeFalse) async for event in app.astream(initial_state): for node_name, node_output in event.items(): if node_name ! __end__: print(f--- 节点 [{node_name}] 完成 ---) # 示例任务 await run_agent(在Example航空官网查找明天北京到上海的经济舱航班)这个框架虽然简陋但完整实现了“规划-执行-检查”的核心循环。你可以看到LangGraph通过定义状态和条件边优雅地管理了工作流的循环逻辑。4. 关键挑战与实战避坑指南在实际构建和运行规划框架时你会遇到一系列教科书上不会提的挑战。以下是我从多次失败中总结出的核心问题和解决方案。4.1 规划器的幻觉与不稳定性问题LLM生成的规划可能逻辑混乱、步骤不可执行如“先登录”却没说输入什么、或严重偏离网站实际流程。解决方案提供网站知识RAG在Prompt中注入目标网站的结构化知识。例如提前用爬虫或手动整理网站的“操作手册”包含关键页面URL模式、核心表单字段ID、主要按钮文本等。让规划器在“知情”的情况下做决策。思维链Chain-of-Thought提示要求LLM在输出最终规划前先“逐步推理”。例如“首先我需要确定网站首页是什么...其次找到航班查询入口...”。规划验证与重规划增加一个“规划验证”节点。用另一个LLM调用或规则集对生成的规划进行可行性检查。如果发现问题则重新生成规划或调整特定步骤。少样本示例Few-Shot在Prompt中提供1-2个针对同类网站如航空、电商的成功规划示例让LLM有更清晰的模仿对象。4.2 状态跟踪的“脆弱性”问题页面加载速度、动态内容、意外弹窗如Cookie通知都会导致状态判断错误。解决方案多模态融合判断不要只依赖DOM文本。结合视觉特征截图、URL、网络请求状态如API是否返回200进行综合判断。例如即使DOM中出现了“预订成功”文字但截图显示一个旋转的加载图标则状态应为“处理中”。设置显式等待与健康检查在执行关键动作如点击提交后强制等待一段时间如2-5秒并循环检查多个预期特征直到其中一个匹配或超时。定义容错状态除了“成功”和“失败”定义“中间状态”。例如“页面加载中”、“需要人工干预遇到验证码”、“条件不满足无符合条件的航班”。针对不同状态设计不同的恢复策略。特征冗余对expected_outcome的描述要求包含多个可验证的特征点如“页面标题包含‘订单确认’且存在一个‘订单号’元素且总价大于0”。只要有一个特征匹配就提高成功置信度。4.3 动作执行的“定位难题”问题将“点击登录按钮”这样的高级描述稳定地映射到click(#login-submit)这样的底层操作非常困难。元素选择器可能随网站更新而改变。解决方案混合定位策略不要只依赖ID或CSS Path。优先使用text定位Playwright的text、role定位[rolebutton]并结合多个属性[data-testidsubmit][aria-labelLogin]。这些通常比纯样式选择器更稳定。LLM辅助元素定位将页面DOM摘要或截图提供给一个轻量级LLM如GPT-4V或本地小模型提问“在以下页面中哪个元素最可能是‘搜索航班’按钮请返回其最稳定的CSS选择器或XPath。”这比硬编码规则灵活得多。操作录制与回放对于固定流程可以先用Playwright的Code Generator录制一遍操作生成基础脚本。规划框架在此基础上进行参数化和逻辑判断而非完全从零生成所有操作。4.4 长任务中的错误累积与恢复问题一个10步的任务第8步失败了是重试当前步骤还是回滚到第几步整个任务是否要放弃解决方案实现检查点Checkpoint在关键步骤如登录成功、搜索完成后保存完整的浏览器上下文如Playwright的browser_context.storage_state()。当后续步骤失败时可以快速恢复到上一个检查点状态而不是从头开始。分层规划与子目标将大任务分解为多个子任务如“登录”、“搜索航班”、“选择航班”、“填写信息”。每个子任务内部有独立的规划-执行循环。一个子任务失败不影响已完成的子任务只需重试或调整该子任务。设计重试与回退策略为每个步骤定义最大重试次数如3次。重试失败后尝试执行一个“回退动作”如刷新页面、返回上一页然后重新评估状态和规划。5. 性能优化与进阶方向当基础框架跑通后你会开始关注效率、成本和可靠性。5.1 降低LLM调用成本与延迟规划缓存对于常见任务如“查询A到B的航班”将成功的规划序列缓存起来。下次遇到相似任务时先检索缓存直接复用或稍作修改避免每次调用LLM生成规划。使用小型/专用模型规划任务不一定需要最强的GPT-4。经过微调Fine-tuning的较小模型如7B-13B参数在特定领域规划上可以做得又快又好。状态验证等简单判断任务甚至可以用规则或更小的模型如text-embedding模型比较文本相似度来完成。异步与并行如果任务包含多个可以并行执行的独立分支如同时查询多个比价网站利用asyncio并行执行多个浏览器实例和LLM调用大幅缩短总耗时。5.2 引入学习与自适应能力构建操作知识库持续记录成功的(页面状态描述目标动作成功定位器)三元组。当再次遇到相似页面状态和动作意图时优先从知识库中检索定位器而非每次都求助LLM。实施在线学习Online Learning当智能体执行失败并经过人工纠正后将纠正后的正确操作路径包括修正后的规划、状态判断、元素定位作为新的正例反馈到知识库或用于微调模型实现“越用越聪明”。5.3 扩展应用场景跨网站工作流规划框架不限于单个网站。可以设计“宏观规划器”将任务分解为涉及不同网站的子任务如“先在A比价再去B官网购买”并由“调度器”协调多个针对特定网站的“子智能体”执行。处理非确定性环境对于验证码、登录滑块、短信验证等挑战规划框架应能识别这些“特殊状态”并触发相应的处理模块如调用人工处理接口、使用第三方打码服务或将其作为分支条件纳入规划“如果出现验证码则执行分支A否则执行分支B”。与RPA工具集成将LLM规划框架作为“大脑”指挥传统的RPA机器人如UiPath, Automation Anywhere执行桌面或企业软件内的操作结合LLM的理解力与RPA的稳定性。构建一个健壮的AI Planning Framework for LLM-Based Web Agents是一个持续迭代的过程。它没有一劳永逸的银弹核心在于理解“规划-感知-行动”这个循环并为每个环节设计足够的鲁棒性和可调试性。从最简单的原型开始针对具体的业务场景如抓取特定数据、完成固定流程的测试逐步深化你会逐渐积累起一套属于自己的、能真正解决实际问题的自动化智能体系统。