跳到主要内容

自主浏览器

本章讲:web agent 怎么“自己上网”——从一次搜索到逐页导航、填表、记笔记、汇总。这是整个项目工程含量最高的子系统。

3.1 它要解决的小问题

让 LLM “看网页”有两个难点:

  1. 网页是一大坨 HTML,LLM 看不了也装不下。要把它“净化”成精简文本。
  2. LLM 只能输出文本,怎么让它“点链接、填表单”?要一套约定语法把文本意图落到真实页面动作。

3.2 思路/直觉:页面转 Markdown + 文本意图语法

  • 取文: Selenium 拿到 page_source -> BeautifulSoup 去掉 script/style -> markdownify 转 Markdown -> 只留“像句子的行”。
  • 动作: LLM 用约定语法表达意图:I will navigate to <url> 跳转、[field_name](value) 填表、Note: … 记笔记、GO_BACK / REQUEST_EXIT 控制流。

动作枚举在 browser_agent.py:15-20Action)。

3.3 图示:web agent 的导航循环

怎么读:先把用户请求变成搜索词、拿到一批链接,然后“选一个进去看->记笔记->决定下一步”循环,直到 LLM 说可以退出。

user_prompt
v
search_prompt -> LLM 出搜索词
v
searxSearch.execute -> 一批结果(取前 16)
v
+-> 未完成 且 还有未访问链接?
| v
| LLM 决策(make_newsearch_prompt / make_navigation_prompt)
| |- 有 [field](value) -> fill_form 填表
| |- 含 REQUEST_EXIT -> complete=True,跳出
| |- 含 GO_BACK / 无可用链接 -> 回搜索结果
| +- 选个链接 -> browser.go_to -> 取新页文本 -> 重新生成导航 prompt
| v
+---- 抓页面截图 screenshot()
v
conclude_prompt -> LLM 把所有笔记汇总成答案

主函数 browser_agent.py:331-434BrowserAgent.process)。

3.4 真实实现:页面变精简文本

browser.py:389-417Browser.get_text):

# 示意,非源码
soup = BeautifulSoup(page_source)
for el in soup(['script','style','noscript','meta','link']):
el.decompose() # 扔掉噪声
md = markdownify.convert(soup.body) # 转 Markdown
lines = [l for l in md.splitlines() if is_sentence(l)] # 只留“像句子”的行
return "[Start of page]" + "\n".join(lines) + "[End of page]" # 截断到 32768

重点看 is_sentencebrowser.py:377-387):含数字、或“词数>=5 且有标点/足够长”的行才保留——过滤掉导航栏、按钮这种碎词。这是“喂给 LLM 的上下文”质量的关键。

3.5 真实实现:填表

LLM 写 [username](David),agent 用正则抽出(browser_agent.py:60-64extract_form),交给 Browser.fill_formbrowser.py:751-768)。

fill_form_inputsbrowser.py:681-749)按控件类型分路填入:

  • select 下拉:select_by_visible_text 不行再 select_by_value
  • textarea / 普通输入:clear + send_keys
  • file 上传:只接受绝对路径且文件存在(browser.py:729-734)。
  • checkbox/radio:按 value=="checked" 决定勾不勾。

填完后 fill_form 还会勾选所有 checkbox、找提交按钮点击、等提交结果(browser.py:756-762)。

3.6 真实实现:反检测浏览器

为了不被网站当机器人拦,create_driverbrowser.py:259)走了一套隐身:

  • undetected_chromedriver + selenium_stealthbrowser.py 顶部 import)。
  • 随机 User-Agent(get_random_user_agentbrowser.py:72-79)。
  • 人类化滚动/移动(human_scrollhuman_move)。
  • go_to 里随机 sleep + 等待绕过 “checking your browser / captcha” 屏(browser.py:347-364)。

还有个 apply_web_safetybrowser.py:814-820):每次导航后注入一段 inject_safety_script.js,拦网站的恶意/骚扰执行。

3.7 巧妙之处:“记笔记 + 反重复”

  • 记笔记是处理逾期上下文的手段。 导航 prompt 强制要求 LLM 在每页写 Note: …browser_agent.py:120-123),parse_answer 把笔记收集起来(browser_agent.py:220-235)。最后 conclude_prompt 只拿笔记汇总,而不是重读所有页面——这是“多页阅读不爆上下文”的关键。
  • 卡住检测。 若 LLM 这次回复跟上次一字不差(常因抽不出链接),换成 stuck_prompt 逼它选别的(browser_agent.py:319-329364-366)。
  • 每轮清空记忆。 导航循环里每次 self.memory.clear()browser_agent.py:358),只留 system prompt + 当前页,避免上下文爆炸。

3.8 边界/坑

  • 依赖本地 SearxNG。 searxSearch 需要 SEARXNG_BASE_URL(通常 Docker 起的),拿不到则报错“Did you run start_services.sh?”(searxSearch.py:106-107)。
  • 链接过滤偏保守。 is_link_valid 拒掉超 72 字符、图片/元数据后缀、纯数字结尾路径的 URL(browser.py:436-452),可能误伤合法长链接。
  • 截断 32768 字符。 页面文本硬截(browser.py:414),超长页面后半丢失。

3.9 代码地图

主题文件符号
web agent 主循环sources/agents/browser_agent.pyBrowserAgent.process
导航 promptsources/agents/browser_agent.pymake_navigation_prompt
笔记解析sources/agents/browser_agent.pyparse_answerconclude_prompt
表单抽取sources/agents/browser_agent.pyextract_form
页面转文本sources/browser.pyBrowser.get_textis_sentence
导航sources/browser.pyBrowser.go_to
填表sources/browser.pyfill_formfill_form_inputs
隐身驱动sources/browser.pycreate_driverget_random_user_agent
网络搜索sources/tools/searxSearch.pysearxSearch.execute