Fork me on GitHub

2025年6月

学习 Claude Code 的工具使用

在前面的学习中我们已经接触了不少 Claude Code 的工具,比如使用 List 工具列出文件列表,使用 ReadWrite 工具对文件进行读写,使用 Bash 工具调用 git addgit commit 等命令。Claude Code 内置了不少工具,但是由于 Claude Code 并没有开源,除了官方文档,我们并没有太多工具相关的资料可参考。

因此我使用 Wireshark 对 Claude Code 的请求抓包分析,看到了它内置的提示词以及 tools 参数:

claude-code-tools.png

从上图可以看到,Claude Code 内置了 16 个工具,这些工具大致可以分为下面几类:

分类分类描述示例工具
命令执行允许在 Shell 会话中执行 Bash 命令Bash
文件查找用于快速查找并匹配文件LSGlobGrep
文件读写用于读取或写入文件,允许进行文件编辑和内容替换ReadWriteEditMultiEdit
Notebook 读写用于读取和编辑 Jupyter Notebook(也就是 .ipynb 文件)NotebookReadNotebookEdit
Web 检索用于检索和分析 Web 内容的工具WebFetchWebSearch
复杂任务处理通过子智能体或任务清单来执行复杂任务TaskTodoWriteTodoReadexit_plan_mode

今天就来仔细学习一下这些工具。

Bash 执行命令

Claude Code 是一款基于终端的 AI 编程助手,它继承了你的 Shell 环境,可以调用常用的 Unix 命令。比如调用 du 查看文件夹大小:

claude-bash-du.png

调用 zip 压缩文件夹:

claude-bash-zip.png

甚至可以使用你安装的任何命令行工具,比如我们之前学过 PDFMathTranslate 这款开源的 PDF 文档翻译工具,让 Claude Code 来试试:

claude-bash-pdf2zh.png

当然,默认情况下 Claude Code 并不知道这些命令,可以通过在 CLAUDE.md 中记录常用的命令,告诉 Claude 可以使用哪些命令以及每个命令的用法:

你可以使用下面这些 Bash 命令:

* pdf2zh - 一款免费开源的 PDF 文档翻译工具,支持多种语言和翻译服务,最大的特色是翻译后保持排版不变。

基本用法,默认将英文 PDF 文件翻译成中文:`pdf2zh example.pdf`
指定源和目标语言:`pdf2zh example.pdf -li en -lo ja`
查看命令文档:`pdf2zh --help`

Bash 命令几乎可以实现任何功能,但是不同的 Bash 命令参数迥异,有些命令的参数还特别复杂,为了防止大模型生成命令时误入歧途,Claude Code 内置了一些工具来替代常见的 Unix 命令。从抓包请求里可以看到 Bash 工具的描述,其中有几个点比较有意思:

  • 含空格的路径必须用双引号包裹
  • 严禁使用 findgrep 搜索命令,改用 Grep / Glob / Task 工具
  • 避免使用 cat / head / tail / ls 等读取工具,改用 Read / LS 工具
  • 如果你仍然要运行 grep,请停止,必须优先使用预装的 rg 来替代,所有 Claude Code 用户都已预安装此工具
  • 尽量使用绝对路径,避免使用 cd,除非用户明确要求

这样做的好处是,大模型通过 function call 生成结构化的命令,比直接生成长长的命令行要可控的多。

LS 列出文件和目录

Bash 的工具描述中可以看到,Claude Code 使用内置的 LS 工具替代 ls 命令:

claude-ls.png

内置 LS 的好处是它可以递归遍历子目录,如上图所示(使用 Ctrl + R 可以展开详情)。

Glob 根据文件名查找文件

另一个内置的 Glob 工具用于替代 find 命令,可以基于模式匹配来查找文件:

claude-glob.png

它有点类似下面这个 find 命令:

$ find . -type f -name "*global*"

但比 find 命令强的地方在于它支持 Glob 模式匹配,比如 **/*.jssrc/**/*.ts 这样。

尽管在 Bash 的工具描述里特别提到 严禁使用 find 命令,但是我验证下来发现,如果内置的工具不足以完成用户的任务,Claude Code 还是会使用这些被限制的命令:

claude-bash-find.png

Grep 搜索文件内容

另外,从 Bash 的工具描述中我们还得知,Claude Code 预装了一个叫 rg 的工具用于替代 grep 命令,这个 rg 全名叫 ripgrep,是一个现代的命令行搜索工具,相对于 grep 来说搜索效率更高。可以在 Claude Code 的安装目录下找到它:

  • @anthropic-ai/claude-code/vendor/ripgrep

ripgrep 使用 Rust 编写,通过先进的搜索算法和内建的索引优化,可以递归地在当前目录中搜索正则表达式模式,它默认会遵循 gitignore 规则,并自动跳过隐藏文件和二进制文件。这些特性使得 ripgrep 成为许多开发者搜索大型代码库的常用工具之一,也是 Claude Code 能处理大型代码库的秘密武器之一。

当我们要搜索代码库中文件内容时,Claude Code 往往就会使用这个命令:

claude-bash-rg.png

这里的 Search 其实就是 Grep 工具,类似于下面的命令:

$ rg "POST|post" **/route.ts

使用 Git 命令

除了执行上面的基本命令,Claude Code 还可以处理大多数的 Git 操作,比如下面几个常见的场景:

  • 一、搜索 Git 历史回答问题,可以在指令中明确要求 Claude Code 查看 git 历史,这样效果会更好:
> v1.2.3 分支中包含哪些更改?

> 根据 git 提交历史,分析这个 API 为什么这样设计?
  • 二、编写提交消息,Claude 会自动查看你的更改和最近历史,生成符合上下文的提交消息,我们在之间的文章中已经见识过:

claude-code-commit.png

Bash 的工具描述中,有一份详细的 Git 提交规范,可以对照着上面的截图,了解下 Claude Code 是如何处理 Git 提交的:

当用户要求您创建新的 Git 提交时,请仔细按照以下步骤操作:

1. 始终并行运行以下 Bash 命令:
   - 运行 git status 查看所有未跟踪的文件
   - 运行 git diff 查看将要提交的已暂存和未暂存的更改
   - 运行 git log 查看最近的提交消息,以便可以遵循此仓库的提交消息风格

2. 分析所有已暂存的更改(包括之前已暂存和新添加的)并起草提交消息:
   - 总结更改的性质(例如:新功能、功能增强、错误修复、重构、测试、文档等)
   - 检查是否有不应提交的敏感信息
   - 起草简洁的(1-2句话)提交消息,重点关注"为什么"而不是"什么"
   - 确保消息准确反映更改及其目的

3. 始终并行运行以下 Bash 命令:
   - 将相关的未跟踪文件添加到暂存区
   - 创建提交,消息以以下内容结尾:
   .... Generated with Claude Code
   Co-Authored-By: Claude noreply@anthropic.com
   - 运行 git status 确保提交成功

4. 若因 pre-commit 钩子失败:
   - 重试一次包含自动变更
   - 仍失败则终止
   - 成功但文件被钩子修改时必须 amend 提交
  • 三、处理复杂的 Git 操作,如恢复文件、解决冲突以及比较和移植补丁等:
> 将 v1 合并到 v2 分支,并尝试解决冲突

未完待续

今天我们学习了 Claude Code 的命令执行和文件查找相关的工具:使用 Bash 运行 Unix 常见命令、用户安装的三方命令和 Git 命令等;使用 LS 列出文件和目录;使用 Glob 根据模式查找文件;使用 Grep 通过正则表达式快速搜索文件内容。文件的查找和搜索是通过 Claude Code 预装的一个叫 rg 的工具实现的,它相对于 grep 速度更快,效率更高,适用于任何规模的代码库。

Claude Code 的工具箱里还有不少其他好玩的工具,我们明天继续。


学习 Claude Code 的命令行操作

我们昨天学习了 Claude Code 的一些实用技巧,包括提问的艺术、图像的处理,文件和网址的处理、扩展思考模式等,还介绍了几个会话管理的小命令,Claude Code 还有不少实用的命令和参数,我们今天来盘点下。

使用 claude --help 查看 Claude Code 支持的所有命令和参数:

claude-help.png

非交互模式

参数 -p--print 可以让 Claude Code 直接打印响应,不进入交互模式,这对于脚本编写和自动化特别有用。比如下面的例子让 Claude Code 充当一个代码检查工具:

$ claude -p "你是一个代码检查工具,请检查主分支的更改,并报告任何与拼写错误相关的问题。报告分两行:第一行是文件名和行号,第二行描述问题。不要返回任何其他文本。"

非交互模式可以让我们很方便地将 Claude Code 无缝地集成到构建脚本或 CI/CD 工作流中。假如你在开发 Node.js 项目,可以将这个命令添加到 package.json 文件:

{
    ...
    "scripts": {
        ...
        "lint:claude": "claude -p '你是一个代码检查工具...'"
    }
}

然后运行 npm run lint:claude 来对提交的代码进行检查:

claude-p-lint.png

除了代码检查,你可以自定义提示词实现更多其他功能。为了防止非交互模式下 Claude Code 陷入死循环,可以使用 --max-turns 参数限制对话的轮数:

$ claude -p --max-turns 3 "query"

非交互模式还可以让我们使用管道将 Claude Code 集成到现有的 Shell 脚本中,与其他 Unix 工具结合使用以实现强大的工作流程:

$ cat build-error.txt | claude -p '详细解释这个构建错误并分析可能的出错原因' > output.txt

默认 Claude Code 的输出为纯文本方式,可以考虑使用 —output-format 参数进行结构化输出:

$ cat package.json | claude -p '找出这个文件中可能存在的问题' --output-format=json > output.json

这种输出结构方便我们以编程方式解析 Claude 的响应,输出内容包含状态、结果、耗时、使用的 tokens 等元数据,如下:

{
    "type": "result",
    "subtype": "success",
    "is_error": false,
    "duration_ms": 14340,
    "duration_api_ms": 17037,
    "num_turns": 3,
    "result": "这个 package.json 文件有以下问题:...",
    "session_id": "ecb8aaf4-4fbe-44c1-a27a-c88f5d85f817",
    "total_cost_usd": 0.06880885,
    "usage": {
        "input_tokens": 9,
        "cache_creation_input_tokens": 15601,
        "cache_read_input_tokens": 14445,
        "output_tokens": 289,
        "server_tool_use": {
            "web_search_requests": 0
        },
        "service_tier": "standard"
    }
}

Claude Code 支持 JSON 和流式 JSON 两种结构化输出格式,当使用流式 JSON 时,Claude Code 会实时输出一系列的 JSON 对象,而不是一个 JSON 对象。可以根据需要来选择不同的输出格式:

  • 对于只需要 Claude 响应的简单集成,使用 --output-format=text
  • 当你需要完整的对话日志时,使用 --output-format=json
  • 当你需要每个对话轮次的实时输出时,使用 --output-format=stream-json

我们除了可以控制输出格式,也可以使用 --input-format 参数指定输入格式,Claude Code 支持两种输入格式:

  • 对于简单集成,使用 --input-format=text
  • 当你需要实时流式输入时,使用 --input-format=stream-json

调试模式

参数 -d--debug 用于开启调试模式:

$ claude --debug

调试模式右下角会显示 “Debug mode” 的标记,在交互过程中会打印出 Claude Code 中 DEBUG 级别的日志:

claude-debug.png

参数 --verbose 用于开启详细模式,或者叫做冗余模式:

$ claude --verbose

详细模式下会显示出工具的完整调用结果,和正常模式下按 Ctrl + R 效果是一样地:

claude-verbose.png

这两个模式对于排查问题都很有帮助,当遇到报错时,比如调用 MCP Server 异常,可以试试这两个模式。

指定模型

参数 --model 用于指定当前会话的模型:

$ claude --model sonnet

可以提供最新模型的别名(例如 sonnetopus)或模型的全名(例如 claude-sonnet-4-20250514)。

参数 --fallback-model 用于指定回退模型:

$ claude --model opus --fallback-model sonnet -p "query"

当默认模型超负荷时,将自动切换到指定的回退模型,注意,仅在非交互模式下有效。

交互模式

在交互模式下有不少键盘快捷键可供使用,输入 ? 查看说明:

claude-shortcuts.png

  • ! - 进入 Bash 模式,可直接运行 Bash 命令
  • / - 输入快捷命令,在交互式会话中控制 Claude 的行为
  • @ - 我们知道在输入文件路径时可以按下 Tab 键自动补全,使用 @ 也可以智能提示并引用文件
  • # - 向 CLAUDE.md 文件中添加新的记忆
  • Ctrl + L - 清空当前输入框的内容
  • Esc + Esc - 当输入框有内容时,连按两次 Esc 键,清空输入框内容,和 Ctrl + L 一样;当输入框没有内容时,连按两次 Esc 键,可以回退到之前的对话记录
  • Ctrl + C - 和 Ctrl + L 一样,也是清空输入框内容,但是如果连按两次 Ctrl + C 就会退出程序
  • Ctrl + Z - 取消上一步操作,注意这个针对的仅仅是输入框中的操作,比如输入、删除、清空等
  • Shift + Tab - 自动接受文件编辑,默认情况下,编辑文件前 Claude Code 会让用户确认
  • Ctrl + R - 开启详细模式,将显示工具的完整调用结果,和上面的 --verbose 一样
  • Shift + Enter - 多行输入,也可以手动输入 \ + Enter 换行,在 macOS 中也可以使用 Option + Enter 换行
  • /vim - 启用 Vim 风格编辑,如果你是一个 Vim 党,在编辑大段文本时会很方便

斜杠命令

上面的 / 快捷键比较有意思,用于快速唤起某些命令,这些命令被称为 斜杠命令(Slash Commands)。我们在之前的学习中已经见过一些斜杠命令了,比如用 /init 创建 CLAUDE.md 记忆文件,用 /clear 清除对话历史,用 /compact 压缩对话等等。在交互式对话框中输入 / 可唤出所有可用的斜杠命令:

claude-slash-commands-0.png

常用的斜杠命令如下所示:

claude-slash-commands.png

自定义斜杠命令

关于斜杠命令的另一个有趣功能是,用户可以将经常使用的提示或指令放在特定位置的 Markdown 文件中,从而创建出属于自己独有的斜杠命令。自定义命令文件位于当前项目的 .claude/commands 目录下或者个人用户的 ~/.claude/commands 目录下,项目目录下的命令叫做 项目命令,可以存储在仓库中并与你的团队共享;用户目录下的命令叫做 用户命令,只能你自己使用,而且可以在所有项目中使用。

自定义斜杠命令的语法如下:

> /<prefix>:<command-name> [arguments]

其中,<prefix> 表示命令作用域,可以是 project 项目命令,或者 user 用户命令;<command-name> 是命令的名称,也就是 Markdown 文件的名称(不包含 .md 扩展名),一个 Markdown 文件对应一个命令;[arguments] 表示传递给命令的可选参数。

下面是一个项目命令的例子,在 .claude/commands 目录下创建一个 optimize.md 文件:

$ mkdir -p .claude/commands
$ echo "分析代码的性能并给出优化建议:" > .claude/commands/optimize.md

然后就可以通过下面的斜杠命令来调用:

> /project:optimize

下面是一个用户命令的例子,在 ~/.claude/commands 目录下创建一个 security-review.md 文件:

$ mkdir -p ~/.claude/commands
$ echo "审查代码的安全漏洞:" > ~/.claude/commands/security-review.md

然后就可以通过下面的斜杠命令来调用:

> /user:security-review

命令目录下还可以创建子目录,这些子目录被称为 命名空间,方便用户对不同类型的命令进行组织,比如对于一个全栈项目,我们可以将命令分为前端命令和后端命令,那么就可以在 .claude/commands 目录下创建 frontendbackend 两个子目录。命名空间命令的语法如下:

> /<prefix>:<namespace>:<command-name> [arguments]

另外,我们可以在 Markdown 文件中使用 $ARGUMENTS 占位符,表示传递给命令的参数,比如下面这个实际的例子:

请分析并解决这个 GitHub 问题 $ARGUMENTS,按照以下步骤进行:

1. 使用 `gh issue view` 获取问题详情
2. 理解问题中描述的问题
3. 在代码库中搜索相关文件
4. 实施必要的更改以解决问题
5. 编写并运行测试以验证修复
6. 确保代码通过 linting 和类型检查
7. 创建一个描述性的提交信息
8. 推送并创建一个 PR

请记住使用 GitHub CLI (`gh`) 进行所有与 GitHub 相关的任务。

将上面的内容保存到 .claude/commands/fix-github-issue.md 文件中,然后调用这个命令来修复 Github 上的问题:

> /project:fix-github-issue 1234

通过传入动态参数,可以创建出更加灵活的自定义命令,比如这些场景:

  • 为指定函数生成测试用例
  • 为指定组件创建文档
  • 审查指定文件中的代码
  • 将内容翻译为指定语言

所有自定义的斜杠命令也会显示在 / 快捷命令列表中:

claude-slash-commands-custom.png

小结

我们今天学习了不少和 Claude Code 命令行相关的技巧,比如使用非交互模式轻松将 Claude Code 集成到构建脚本或 CI/CD 工作流中,使用调试模式和详细模式快速定位和解决各种问题,交互模式下丰富的快捷键和斜杠命令让日常使用更高效,而自定义斜杠命令让我们可以根据项目需求创建专属的命令工具箱。

关于 Claude Code 的命令行操作还有很多,比如关于工具权限的 --allowedTools--disallowedTools 参数,关于 MCP 集成的 claude mcp 子命令,关于配置管理的 claude config 子命令等,我们将在后续的专题学习中逐一介绍。


学习 Claude Code 的基本技巧

昨天我们折腾了一下 LiteLLM 网关,成功让 Claude Code 接入了其他大模型。这下好了,直接用 OpenRouter 的免费 DeepSeek 模型,再也不用担心 API 费用把钱包掏空了,可以放心大胆地体验 Claude Code 的各种功能。

前面我们已经学习了 Claude Code 的安装和基本用法,今天咱们继续深入挖掘一些实用的小技巧,让这个工具用起来更顺手。

提问的艺术

用 Claude Code 就像跟人聊天一样,你说话越清楚,它就越能帮到你。下面是几个建议:

  • 第一,指令越具体越好,比如当你的页面出 BUG 时,不要这样问:
> 修复这个 BUG

这样问才对:

> 修复登录页面的 BUG,用户输错密码后看到的是空白页面而不是错误提示

第一种问法就像对着服务员喊"给我来个菜",第二种才是正常人的沟通方式。

  • 第二,大任务拆成小步骤,复杂的需求别一股脑全说出来,要学会拆解:
> 1. 给用户资料页面加个新的 API 接口

> 2. 添加必填字段的验证逻辑

> 3. 写几个测试用例验证功能

分步解决可以让 Claude Code 更了解用户的意图。

  • 第三,让 Claude Code 先摸清状况,别上来就让它动手改代码,先让它了解一下你的项目:
> 帮我分析一下这个数据库的表结构

> 这个数据库查询异常是怎么回事?

使用扩展思考模式

以往的大语言模型,要么擅长快速响应,要么擅长深度推理,很难兼顾两者,比如 DeepSeek 的 V3 和 R1。Claude Sonnet 3.7 号称是业界首个 混合推理(Hybrid Reasoning) 模型,同时支持 标准模式扩展思考模式

  • 标准模式:对于一些事实性问题或简单指令,能迅速给出答案,快速回复用户;
  • 扩展思考模式(Extended Thinking):模型会在回答之前进行自我反思,进行更深入的思考,并给出更全面、更准确的答案;

所有在 Claude Sonnet 3.7 之后发布的 Opus 和 Sonnet 模型都支持这两种模式。当我们面对下面这些复杂的任务时可以尝试使用扩展思考:

  • 规划复杂的架构更改
  • 调试复杂问题
  • 为新功能创建实现计划
  • 理解复杂的代码库
  • 评估不同方法之间的权衡

Claude 官方出了一份关于扩展思考的高级策略和技巧指南,推荐阅读:

我们在 Claude Code 中对话时,默认使用的是标准模式;如果要处理上面那些复杂的问题时,可以在指令中加上 “think” 或 “think harder” 等触发扩展思考模式:

> 我需要实现 OAuth2 认证,深入思考最佳方法

> 思考下,这种方法可能存在哪些潜在的安全漏洞?

> 更深入地思考我们应该处理的边缘情况

我试了下,无论是英文 “think” 还是中文 “思考” 都可以触发扩展思考模式,而且可以使用类似 “think more”、“think a lot”、“think harder” 或 “think longer” 这样的强化短语触发更深层的思考。

Claude 的思考模式可以通过 budget_tokens 参数控制令牌预算,我猜测不同的提问方式会导致这个参数的值不同,更深层的思考消耗更多的思考令牌,感兴趣的朋友可以验证一下。

我这里还是拿数独项目为例,让它给我提一些 SEO 优化的建议:

claude-extend-thinking.png

和 DeepSeek R1 一样,Claude 的思考过程对用户也是可见的。在上图中,Claude 以斜体灰色文本显示了其思考过程,你可以看到它是如何一步步分析问题、得出结论的。Claude Code 对我的代码经过一番分析,最终输出如下:

claude-extend-thinking-2.png

经过思考后的输出要更详细。

处理图像

Claude 系列模型具有视觉能力,可以理解和分析图像。当我们遇到有些问题用文本描述不清或过于繁琐时,可以给 Claude 提供图像,效果会更好,比如错误截图、UI 设计或图表等,图像比文本包含更多的上下文信息。

我们可以通过下面几种方法向 Claude Code 对话中添加图像:

  • 将图像拖放到 Claude Code 窗口中
  • 复制图像并使用 ctrl+v 粘贴到命令行中(不要使用 cmd+v
  • 提供图像路径,例如:“分析图像: /path/to/your/image.png”

可以在对话中一次性添加多个图像。

下面是使用图像的一些常用场景:

  • 让 Claude 分析图像
> 这张图片显示了什么?

> 描述截图中的用户界面元素

> 这张图中是否有任何问题元素?
  • 使用图像提供上下文
> 这是错误的截图,分析是什么原因引起的?

> 这是我们当前的数据库模式,为了实现新增的功能,我们应该如何修改它?
  • 从视觉内容获取代码建议
> 根据这个设计图生成 CSS

> 什么样的 HTML 结构可以重现这个组件?

处理文件和网址

我们还可以让 Claude Code 查看或处理仓库下的某个文件或目录,一般需要输入全路径,方便 Claude 找到正确的资源。使用 Tab 键自动补全,可以大大方便文件路径的输入:

claude-tab.png

也可以将某个网址丢给 Claude Code 让它分析或总结:

claude-fetch.png

Claude Code 内置了一个 Fetch 工具,用于抓取网页内容。

会话管理

我们每次运行 claude 命令进入交互模式时,实际上都是创建一个新会话,如果我们希望继续之前的会话,可以使用 Claude Code 的会话管理功能。

参数 -c--continue 用于继续最近一次对话:

$ claude -c

参数 -r--resume 用于恢复某个历史会话:

$ claude -r

Claude Code 会弹出一个对话选择列表,列出最近的所有会话:

claude-r.png

列表中显示了会话时间、消息条数、消息摘要等信息,你可以使用上下键导航并按 Enter 选择具体的会话,恢复后,你将看到该会话的整个对话历史。

此外,在交互模式下,还有一些 斜杠命令(Slash Commands) 也可以操作会话。比如当会话中消息过多时,可能会影响模型的效果和速度,这时我们可以执行 /clear 命令清除上下文:

> /clear

实际上 /clear 命令是创建了一个新会话,老的会话不动。

或者使用 /compact 命令压缩当前会话:

> /compact

压缩成功后,会显示如下信息:

claude-compact.png

之后的对话将从压缩后的会话开始。实际上 /compact 也是创建了一个新会话,将压缩后的会话作为新会话的第一条消息。

小结

我们今天简单学习了 Claude Code 的一些实用技巧,从提问的艺术,到处理图像,再到处理文件和网址。还学习了开启扩展思考模式的方法,遇到复杂问题时别忘了加个 "think" 或 "思考",让 AI 先动动脑子再回答。

最后介绍了几个会话管理的小命令,像这样的小命令 Claude Code 还有很多,熟练掌握可以给我们省不少事儿,下一篇我们就来盘点下 Claude Code 的其他命令。


让 Claude Code 接入其他大模型

尽管 Claude Code 非常好用,但它的费用还是太贵了,之前为了体验,在 Anthropic Console 充了 5 美刀,只用了几天,就所剩无几了:

claude-cost.png

如果能让 Claude Code 使用其他较便宜的大模型(比如 OpenAI 或 DeepSeek 等)或本地大模型(如 Ollama 或 LM Studio 等)来处理编程任务,那就太好了,我们今天就来学习下如何让 Claude Code 接入其他模型。

接入 Claude 云提供商

Claude Code 默认使用 Anthropic 的 Claude 模型,但是还有一些其他的供应商也提供 Claude 模型,比如 Amazon BedrockGoogle Vertex AI,Claude Code 也支持接入这些模型。要使用这些第三方供应商的模型,首先需要按照供应商的要求,配置好对应的 AWS 凭证 或 GCP 凭据,然后通过环境变量告诉 Claude Code 使用供应商模型。

设置以下环境变量以启用 Bedrock 模型:

export CLAUDE_CODE_USE_BEDROCK=1
export AWS_REGION=us-east-1

设置以下环境变量以启用 Vertex AI 模型:

export CLAUDE_CODE_USE_VERTEX=1
export CLOUD_ML_REGION=us-east5
export ANTHROPIC_VERTEX_PROJECT_ID=YOUR-PROJECT-ID

另外,不同供应商对模型的命名也不同,因此 Claude Code 还提供了两个环境变量来配置使用的模型。比如使用推理配置文件 ID 来配置 Bedrock 模型:

export ANTHROPIC_MODEL='us.anthropic.claude-opus-4-20250514-v1:0'
export ANTHROPIC_SMALL_FAST_MODEL='us.anthropic.claude-3-5-haiku-20241022-v1:0'

这里要配置两个模型,ANTHROPIC_MODEL 为主要模型,用于复杂的编程任务;ANTHROPIC_SMALL_FAST_MODEL 为小型快速模型,用于简单的辅助任务;Claude Code 会根据情况自动切换。

配置企业代理

有很多企业对网络有特定要求,比如需要通过特定的代理来路由所有进出企业的流量,以实现安全、合规和监控目的。Claude Code 支持标准的 HTTP/HTTPS 代理配置:

export HTTPS_PROXY=https://proxy.example.com:8080
export HTTP_PROXY=http://proxy.example.com:8080

Claude Code 不支持 SOCKS 代理。

如果代理需要基本身份验证,可以在代理 URL 中包含凭据:

export HTTPS_PROXY=http://username:password@proxy.example.com:8080

如果代理使用了自定义的 SSL 证书,还需要设置正确的证书路径:

export SSL_CERT_FILE=/path/to/certificate-bundle.crt
export NODE_EXTRA_CA_CERTS=/path/to/certificate-bundle.crt

另外要注意的是,Claude Code 在运行过程中需要访问下面这些 URL 地址:

  • api.anthropic.com - Claude API 端点
  • statsig.anthropic.com - 遥测和指标
  • sentry.io - 错误报告

确保这些 URL 在代理配置和防火墙规则中被列入白名单。

配置 LLM 网关

还有很多企业对大模型的访问是集中管控的,以实现使用跟踪或预算控制等,为满足这一类企业的需求,Claude Code 支持配置 LLM 网关。通过 LLM 网关,企业可以做到:

  • 集中身份验证 - 对大模型的 API 密钥进行集中管理
  • 使用跟踪 - 监控团队和项目的使用情况
  • 成本控制 - 实施预算和速率限制
  • 审计日志 - 跟踪所有模型交互以确保合规性
  • 模型路由 - 无需更改代码即可在提供商之间切换

通过 ANTHROPIC_BASE_URL 配置 LLM 网关:

export ANTHROPIC_BASE_URL=https://litellm-server:4000/anthropic

这里的 /anthropic 地址是 LiteLLM 网关透传的 Anthropic 接口,本质上还是调用 api.anthropic.com 接口,因此,在启动 LiteLLM 时需要配置 Anthropic 的 API KEY:

export ANTHROPIC_API_KEY=sk-ant-apikey

这个值将作为 X-Api-Key 请求头发送。

如果 LLM 网关也开启了 API KEY 认证,则通过 ANTHROPIC_AUTH_TOKEN 配置:

export ANTHROPIC_AUTH_TOKEN=sk-litellm-static-key

这个值将作为 AuthorizationProxy-Authorization 请求头发送。

安装 LiteLLM 网关

我们知道,LiteLLM 是一个小巧精悍的 Python 库,兼容 100+ 不同的大模型,所有的模型都使用标准化的输入/输出格式;我们之前学过不少项目(比如 SurfSense、Mem0 等)都是使用 LiteLLM SDK 来调用大模型服务的,其实 LiteLLM 还可以充当 LLM 网关 来使用,通过 LLM 网关代理,我们可以实现 Claude Code 与其他模型的集成。

首先安装 LiteLLM 及其代理功能:

$ pip3 install 'litellm[proxy]'

然后建一个 config.yaml 配置文件:

model_list:
- model_name: gpt-4o
  litellm_params:
    model: gpt-4o
    api_base: https://api.bianxie.ai/v1
    api_key: os.environ/OPENAI_API_KEY
- model_name: gpt-4o-mini
  litellm_params:
    model: gpt-4o-mini
    api_base: https://api.bianxie.ai/v1
    api_key: os.environ/OPENAI_API_KEY
- model_name: deepseek-v3
  litellm_params:
    model: openrouter/deepseek/deepseek-chat-v3-0324:free
    api_base: https://openrouter.ai/api/v1
    api_key: os.environ/OPENROUTER_API_KEY
- model_name: deepseek-r1
  litellm_params:
    model: openrouter/deepseek/deepseek-r1-0528:free
    api_base: https://openrouter.ai/api/v1
    api_key: os.environ/OPENROUTER_API_KEY

接着使用 litellm -c config.yaml 启动 LiteLLM 网关:

litellm-c.png

访问 http://localhost:4000/ 地址,应该显示 LiteLLM API 的 Swagger UI 页面,则说明 LiteLLM 网关启动成功。使用下面的命令验证接口能否正常调用:

$ curl -X POST http://localhost:4000/v1/messages \
  -H "Content-Type: application/json" \
  -H "Authorization: Bearer sk-1234" \
  -d '{
    "model": "gpt-4o",
    "max_tokens": 1024,
    "messages": [
      {
        "role": "user",
        "content": "Hello!"
      }
    ]
  }'

这里使用的是 Anthropic 的 v1/messages 接口,而不是 OpenAI 的 /v1/chat/completions 接口,因为 Claude Code 默认使用 Anthropic 接口。LiteLLM 可以同时兼容两种接口格式,注意有些 LLM 网关只支持 OpenAI 格式,是不行的。

最后通过将环境变量 ANTHROPIC_BASE_URL 设置成 LiteLLM 网关地址,就可以让 Claude Code 使用自定义的模型服务了:

export ANTHROPIC_BASE_URL=http://localhost:4000
export ANTHROPIC_AUTH_TOKEN=sk-1234
export ANTHROPIC_API_KEY=
export ANTHROPIC_MODEL=gpt-4o
export ANTHROPIC_SMALL_FAST_MODEL=gpt-4o-mini
export DISABLE_PROMPT_CACHING=1
claude

注意这里的 DISABLE_PROMPT_CACHING 配置,表示禁用 提示缓存,大多数的三方模型不支持该功能。

解决连接错误

通过上面的方法启动 Claude Code 后,你可能会和我一样遇到下面这样的连接问题:

> hello
  ⎿  API Error (Connection error.) · Retrying in 1 seconds… (attempt 1/10)
    ⎿  TypeError (fetch failed)
  ⎿  API Error (Connection error.) · Retrying in 1 seconds… (attempt 2/10)
    ⎿  TypeError (fetch failed)
  ⎿  API Error (Connection error.) · Retrying in 2 seconds… (attempt 3/10)
    ⎿  TypeError (fetch failed)
...

尝试用 Wireshark 抓包:

wireshark.png

可以发现 Claude Code 发出的请求源和目标都是 ::1,很显然这是 IPV6 地址,猜测可能是 LiteLLM 没有监听这个地址,于是修改 LiteLLM 的启动参数:

$ litellm -c config.yaml --host ::1 

再次运行 Claude Code 即可:

claude-code-litellm.png

小结

通过本文的学习,我们掌握了让 Claude Code 接入其他大模型的完整方案。从官方支持的 AWS Bedrock 和 Google Vertex AI,到通过 LiteLLM 网关实现与 OpenAI、DeepSeek 等第三方模型的集成,这些方法不仅可以降低使用成本,也为开发者提供了更灵活的选择空间。

需要注意的是,不同模型的能力差异较大,建议根据实际场景进行测试选择;部分 Claude 特性(如提示缓存、内置搜索)可能需要禁用或通过 MCP 实现。


实践 Claude Code 的日常使用场景

昨天我们学习了 Claude Code 的安装和基本使用,通过自然语言命令完成项目分析到文件提交等任务。但 Claude Code 的能力远不止于此,它还可以帮助我们完成更多复杂的开发任务,比如代码重构、自动化测试、项目架构分析、版本控制管理等,我们今天就来进一步探索这些使用场景和高级功能,看下 Claude Code 对这些任务的表现。

新项目快速理解

Claude Code 最突出的特点之一是其强大的代码库理解能力。它能够在几秒钟内分析整个项目的结构,理解模块间的依赖关系,识别代码的架构模式,并回答关于代码逻辑和架构的复杂问题。

假设你刚加入一个新项目,需要快速了解它的结构,可以试试下面这些命令:

> 这个项目是做什么的?

> 给我概述下这个项目

> 这个项目使用了哪些技术?

> 这个项目的主入口在哪里?

> 解释下这个项目的目录结构

或者你也可以了解项目的特定功能:

> 项目是如何处理用户认证的?

> 查找和用户认证相关的文件

> 讲解从前端到数据库的整个登录过程

在面对新项目代码时,掌握一些提问的技巧也很重要,比如先从广泛的问题开始问起,然后缩小到特定领域;还可以询问项目使用了哪些特定术语,使用了哪些编码约定;这些信息都能很好的让 Claude Code 了解这个项目。

下面还是拿数独项目作为示例,让 Claude Code 讲解下数独生成是如何实现的:

understand-code-base.png

可以看到 Claude Code 详细讲解了数独生成的算法,并准确地定位到了具体的代码位置。和 Cursor 等 AI 代码编辑器不同的是,我们不需要手动添加上下文,Claude Code 会根据需要自动查询并读取相关的文件。

添加和修改代码

基于对代码库的深度理解,Claude Code 能够进行跨文件的智能编辑,它能同时修改多个相关文件,并保持代码的一致性和最佳实践。常见的使用场景包括添加新功能和特性、问题诊断和修复、对代码进行重构或性能优化等:

> 使用 JWT 实现用户认证功能

> 解决这个编译报错问题

> 这里用户可以提交空表单,修复下这个 BUG

> 找到代码中使用的过期 API 并重构

> 使用 SQLAlchemy 重构数据库层代码

> 分析这里的代码逻辑是否存在性能问题,提出优化建议

在向 Claude Code 描述错误时,可以带上详细的错误堆栈、重现错误的任何步骤和命令、以及这个错误是偶现还是必现等信息,Claude 会尝试分析这些错误信息,找到问题源头并修复。另外,Claude Code 可以自动编写和运行测试,并从测试结果中识别和修复问题:

> 为计算器模块编写单元测试

> 找出 NotificationsService 中所有测试未覆盖的方法

> 为重构后的代码运行测试

> 修复这些失败的单元测试

现在我们来让 Claude Code 做一些实际的编程工作,尝试让它给我的数独项目加个小功能,我希望用户在进入首页时,自动生成一道数独题:

new-features.png

这里可以看到一个有趣的现象,Claude Code 首先生成了一个 Todo 列表,然后开始编写代码,结束编码后再将 Todo 列表更新为已完成。尽管这里只有一步,但是也让我们略窥到 Claude Code 的实现思路,这种方式可以让 Claude Code 解决比较复杂的多步任务。另外,Claude Code 在编辑文件时会以 diff 的形式显示出修改点,让用户确认,只有得到用户批准后才会真正执行。默认每次修改都会请求确认,如果嫌麻烦,可以为会话启用 ”全部接受” 模式。

实现效果如下:

sudoku-home.png

生成文档和注释

程序员最怕写文档和注释,而 Claude Code 最擅长干这事:

> 更新 README 文档,添加安装步骤

> 为 auth.js 文件中的所有函数加上 JSDoc 注释

比如我们接着上面的例子,让 Claude Code 将增加的功能更新到 README 文件中:

update-readme.png

Git 集成和代码审查

Claude Code 与 Git 深度集成,使得我们可以用对话的方式来执行 Git 操作,比如智能提交、创建分支、解决冲突等:

> 提交刚刚的所有修改

> 提交 README.md 文件

> 创建一个新分支 feature/quickstart

> 用刚刚的修改创建一个 PR

> 解决合并冲突

Claude Code 在提交代码时会很智能的分析提交的内容,自动生成提交消息:

git-commit.png

可以看到 Claude Code 先用 git status 检查变动的文件有哪些,然后使用 git diff 对比文件的修改点,这样它就知道了我们都改了什么内容;接着 Claude Code 执行 git log --oneline -5 命令,快速浏览最近的 5 次提交历史,这一步让它对项目最新的变化有个了解,我猜也是为了生成和历史更一致的提交消息;再然后 git addgit commit 提交代码,最后再用 git status 确认所有的变更是否都提交了。注意 -m 后面的注释非常全面,比我们自己写方便多了,另外,Claude Code 还会自动将 Claude 加到提交消息的作者里:

git-commit-message.png

另外,Claude Code 还能查看 Git 历史,查看当前变更,并对用户的修改提出审查建议:

> 我改动了哪些文件?

> 查看最近 5 次提交

> 审查我最近做的修改,提出改进建议

Claude Code 就像是一个 AI 结对编程伙伴,你只需像和同事交谈一样,描述你想要的目标,它就会帮助你自动实现。

和 Cursor 的对比

经过几天的体验,Claude Code 给我的印象还是蛮深刻的。基于终端的交互方式,对于习惯命令行操作的开发者来说是一种享受 —— 纯粹的文本对话,无需面对复杂的界面元素,让开发者能够专注于核心任务,减少视觉干扰。更重要的是,这种交互方式让我们能够用自然语言描述目标,而不必纠结于具体的实现细节。

相比之下,Cursor 走的是另一条路线。它将 AI 能力深度集成到传统的 IDE 界面中,提供熟悉的可视化代码编辑体验。实时的代码建议、智能的 Tab 补全、直观的文件管理 —— 这些特性让 Cursor 的学习曲线更加平缓,对新手开发者更加友好。你可以看到代码的每一个变化,掌控每一个编辑操作。

两者各有优势:Claude Code 更像是一个无所不能的编程助手,你可以将复杂的多步骤任务完全委托给它;而 Cursor 则更像是一个智能的代码编辑器,在保持传统开发体验的同时增强了效率。

对于追求效率和敢于尝试新事物的开发者,Claude Code 提供了一种革命性的编程体验,这可能就是未来软件开发的模样:更少的手工操作,更多的意图表达,开发者角色将从 “手写代码” 转变成 “协调和审查” AI 代理编写代码,而这种转变,值得每一个开发者去亲身体验和探索。


Claude Code 介绍:一款运行在终端里的智能编程助手

今年 2 月份,Anthropic 公司发布 Claude 3.7 Sonnet 混合推理模型,在编码和前端开发方面表现非常出色,一时之间成为所有 AI 代码编辑器的首推模型;同时,他们还推出了一款新的命令行工具 —— Claude Code ,这是一个智能编程助手,和 Cursor、 Windsurf 等传统 IDE 不同的是,它直接运行在你的终端中,能够深度理解你的代码库,并通过自然语言命令帮助开发者更高效地完成编程任务。

claude-code-home.png

不过当时还只是预览版,并没有对所有用户开放,因此反响也不大。到了 今年 5 月份,Anthropic 发布了 Claude 4,其编码能力相对于 3.7 又有了很大的提升;与此同时,Claude Code 也正式 GA ,搭配 Claude 4 出色的编码能力,它的性能得以显著提升;它在处理大型代码库、复杂编程任务中的表现超越了 Cursor、 Windsurf 等一众顶尖的 AI 代码编辑器,惊艳了整个程序员社区。

这款基于 Claude 4 模型的智能编程助手,以其独特的终端集成方式和强大的代码库理解能力,正在重新定义开发者的编程体验。

核心特点

Claude Code 是一个 代理式编程(Agentic Coding) 工具,它具备以下核心特点:

  • 终端集成:通过终端作为通用入口,Claude Code 能直接在开发者熟悉的工作环境中执行任务,无需学习新工具或更换 IDE ,降低了使用门槛;
  • 代码库理解:能够快速分析和理解整个项目的代码结构和逻辑关系,尤其适用于大型复杂代码库;
  • 自然语言交互:得益于 Claude 4 强大的指令理解和任务执行能力,用户通过简单的自然语言命令就可以执行复杂的编程任务;
  • 开发工具集成:与现有的开发环境完美配合,兼容各种命令行工具,支持文件编辑、Git 操作等,与终端、IDE、GitHub Actions 等现有工具和平台无缝集成;

安装步骤

Claude Code 的安装非常简单,首先我们需要安装 Node.js,确保你的 Node.js 版本在 18 以上,然后运行:

$ npm install -g @anthropic-ai/claude-code

接着导航到你的项目目录:

$ cd /path/to/your/project

启动 Claude Code:

$ claude

第一次运行 Claude Code 会弹出一个框让你选择文本样式:

claude-code-theme.png

这是为了更好的适配你的终端显示,选择一个看起来最舒服的样式即可。

接下来要登录你的 Claude 账号:

claude-code-login.png

Claude Code 提供了两种登录选项。第一种是使用 Claude 账号,也就是用于访问 Claude 的网页版、移动端或桌面版聊天界面的那个账号,使用 Claude.ai 进行登录,你的账号至少是 Pro 或 Max 订阅:

claude-plan.png

Pro 订阅每月 17 美刀,Max 订阅每月 100 美刀,使用这种登录方式的好处是 按月收费,费用比较固定。

第二种是使用 Anthropic Console 账号,需要通过 console.anthropic.com 登录,Anthropic Console 面向开发者,用于访问 Anthropic 的 API 服务,用户可以通过编程的方式调用 Claude 的能力,将 Claude 集成到自己的应用程序中。在这个平台上,用户可以管理 API 密钥、查看使用情况、计费信息等:

anthropic-console.png

使用这种登录方式的好处是 按调用量收费,比较灵活,用多少付多少。

如果你只是想对 Claude Code 尝尝鲜,建议使用 Anthropic Console 账号,如果你是长期使用,则建议使用 Claude Pro 订阅。

初次使用

登录成功后,看到如下界面:

claude-code-getting-started.png

此时就可以使用 Claude Code 了,直接在 REPL 交互式会话中使用自然语言向 Claude Code 发出指令即可。初次使用时,建议先让 Claude Code 分析一下你的项目:

> 总结下这个项目

我这里使用了一个数独项目进行测试,这个项目是我用大模型为家里的小朋友开发的一款小游戏,比较简单,可以随机生成数独和自动解决数独:

claude-code-summarize.png

可以看到 Claude Code 使用了 ReadList 两个工具,自动读取了 README.mdpackage.json 文件内容,并列出了当前目录下的文件列表,可以按下 ctrl+r 展开工具调用的详情。通过这些信息 Claude Code 很好的完成了总结任务。

接着运行 /init 命令:

> /init

Claude Code 会继续读取几个关键文件,并开始使用 Write 工具写入 CLAUDE.md 文件:

claude-code-init.png

Claude Code 在执行一些关键操作时会让用户确认,只有得到用许可后,命令才会真正执行:

claude-code-claude-md.png

选择 Yes 生成 CLAUDE.md 文件。

CLAUDE.md 就是一个普通的 Markdown 文件(CLAUDE 全大写,md 小写),它可以做很多事,比如定义项目的编码风格、常用的 Bash 命令、重要的项目说明等等。它是 Claude Code 的 记忆,可以让 Claude Code 持续记住你对它的指令。我们一般将 CLAUDE.md 文件放在项目根目录,并提交到代码仓库,让这些指令在你的团队成员之间共享。

然后让 Claude Code 提交生成的 CLAUDE.md 文件:

> 提交 CLAUDE.md 文件

Claude Code 会自动执行 git addgit commit 等命令,将 CLAUDE.md 文件提交到代码仓库中:

claude-code-commit.png

可以看到 Claude Code 为我们自动生成了提交消息,整个过程非常丝滑,开发人员一直聚焦在终端中,只需要用自然语言下发指令即可。

接下来

通过今天的学习,我们已经成功安装并初步体验了 Claude Code 这款革命性的终端编程助手。从项目分析到文件提交,整个过程展现了它强大的代码理解能力和自然语言交互体验。

Claude Code 的能力远不止于此,它还可以帮助我们完成更多复杂的开发任务:

  • 代码编辑与重构:在大型代码库中快速定位并修复 bug,执行复杂的代码重构操作,保持代码风格的一致性;
  • 项目架构分析:深入理解项目的整体架构,解答关于代码逻辑和设计模式的问题,为技术决策提供支持;
  • 自动化测试与质量保证:运行单元测试和集成测试,执行代码静态分析和规范检查,自动修复常见的代码质量问题;
  • 版本控制管理:智能化的 Git 操作,包括搜索提交历史、解决合并冲突、创建规范的提交信息和拉取请求;
  • 文档与资源整合:通过网络搜索获取最新的技术文档,整合外部资源,为开发过程提供实时的技术支持;

随着我们对 Claude Code 使用的深入,你会发现它不仅仅是一个编程工具,更像是一位经验丰富的编程伙伴,能够理解你的意图,主动提供解决方案,让编程工作变得更加高效和愉悦。在接下来的实践中,我们将进一步探索这些高级功能,体验真正的智能化编程体验。


使用 Patchright 绕过浏览器机器人检测

我们知道,Browser Use 默认采用 Playwright 作为浏览器操作的基础工具。Playwright 原本是由微软开源推出的一款浏览器自动化测试工具,其设计初衷是方便开发人员进行跨浏览器测试。然而,由于其功能强大且使用简便,它逐渐被广泛应用于网络爬虫领域,这也引发了一些问题。许多网站为了保护自身信息安全,采取了封禁措施,试图阻止 Playwright 的访问。

浏览器自动化在运行过程中表现出的行为往往与普通用户操作有显著差异,这些差异使得具有反爬措施和网络应用防火墙(WAF)的网站能够有效识别和对抗 Playwright 驱动的爬虫行为。这也就导致了我们在使用 Browser Use 的时候,无法在一些网站上执行操作,大大限制了 Browser Use 的功能,如何绕过这些安全措施成为了 Browser Use 用户需要解决的问题。

道高一尺,魔高一丈,有反爬,就有反反爬。在使用 Playwright 的时候,也有一些方法绕过反爬机制,我们今天学习的 Patchright 就是基于 Playwright 改造的另一款浏览器自动化工具,它通过巧妙地模拟真实用户行为,使得浏览器操作可以绕过机器人检测。

Patchright 初体验

我们先通过一个真实场景体验下 Patchright 的效果。

Fingerprint.com 是一家专注于设备识别和反欺诈解决方案的企业,他们提供的浏览器机器人检测功能,通过 Smart Signal 技术实时检测自动化工具、搜索引擎爬虫等威胁,帮助网站保护自身免受机器人攻击。

fingerprint-home.png

可以点击下面的链接,进入产品页面体验该功能:

接下来我们使用 Playwright 和 Patchright 分别打开上面的产品页面,对比下两者有何不同。使用 Playwright 打开该页面:

import asyncio
from playwright.async_api import async_playwright

async def main():
  async with async_playwright() as p:
    browser = await p.chromium.launch(headless=False)
    page = await browser.new_page()
    await page.goto("https://fingerprint.com/products/bot-detection/")
    input()
    await browser.close()

asyncio.run(main())

可以看到 Fingerprint.com 成功检测出我们是机器人,并且标识出我们是通过自动化工具访问的:

fingerprint-bot-detection-playwright.png

接下来我们换成 Patchright 打开该页面:

import asyncio
from patchright.async_api import async_playwright

async def main():
  async with async_playwright() as p:
    browser = await p.chromium.launch(headless=False)
    page = await browser.new_page()
    await page.goto("https://fingerprint.com/products/bot-detection/")
    input()
    await browser.close()

asyncio.run(main())

Patchright 的使用和 Playwright 完全兼容,我们只需将上面的 playwright.async_api 换成了 patchright.async_api 即可。可以看到 Fingerprint.com 这次没有检测出来,我们成功绕过了它的机器人检测功能:

fingerprint-bot-detection-patchright.png

在 Browser Use 中使用 Patchright

我们昨天学习了 Browser Use 中关于浏览器的很多配置参数,包括会话相关参数和浏览器配置参数,通过 BrowserSession 传入,以调试模式启动并操作浏览器。在会话相关的参数中有一个 playwright 参数,表示 Playwright 或 Patchright API 客户端句柄,一般通过 await async_playwright().start() 得到,我们可以通过这个参数让 Browser Use 连接并复用已有的 Playwright 或 Patchright 浏览器实例:

from playwright.async_api import async_playwright

async def main():
  playwright = await async_playwright().start()
  browser_session = BrowserSession(
    playwright=playwright
  )
  agent = Agent(
    task="""
      打开 https://fingerprint.com/products/bot-detection/
      检查你是否被检测为机器人
    """,
    llm=llm,
    browser_session=browser_session,
    generate_gif=True
  )
  result = await agent.run()
  print(result)

asyncio.run(main())

将上面的 playwright.async_api 换成 patchright.async_api 即可在 Browser Use 中使用 Patchright ,最终的输出结果如下:

Result: Visited the bot detection page on Fingerprint.com. 
The page indicates clearly that 'You are not a bot' under the section 'Am I a bot?'. 
Both Automation Tool and Search Engine statuses are 'Not detected'. 
Task is complete.

下面是运行的动画过程:

agent_history.gif

此外,Browser Use 在最新版本中(browser-use >= 0.2.7)还提供了一种使用 Patchright 的简便方式,只需要在 BrowserSession 中传入 stealth=True 即可:

browser_session = BrowserSession(
    stealth=True
)
agent = Agent(
    task="your task here",
    llm=llm,
    browser_session=browser_session,
)

学习机器人检测技术

到这里,有些同学肯定会好奇,Fingerprint.com 到底是如何检测浏览器机器人的?而 Patchright 又是如何绕过它的检测的?知其然,知其所以然,我们接下来就来学习学习这里面的门道。

下面这个网站是 Rebrowser 整理的一些常见的机器人检测技术,并在其 项目首页 对这些技术一一做了详细介绍,是个不错的入门资料:

正常打开这个网站是这个样子:

rebrowser-bot-detector.png

而用 Playwright 打开就是一堆报红:

rebrowser-bot-detector-playwright.jpg

这里列出了一些非常基本的测试项,这些测试在任何网站上都很容易实现。可以确定的是,所有这些测试在主流的机器人检测产品中都有使用,尽管每个产品都有自己专有的算法和检测方式,但在 90% 的情况下,当你被阻止或看到任何验证码时,都是因为这些测试不通过导致的。所以,在进行任何类型的浏览器自动化之前,务必先通过所有这些测试。

我们重点来看下这些报红的测试项:

runtimeEnableLeak

默认情况下,Puppeteer、Playwright 和其他自动化工具依赖于 Runtime.enable 这个 CDP 方法来处理执行上下文。任何网站都可以通过几行代码检测到它:

const testRuntimeEnableLeak = async () => {
  const e = new Error()
  Object.defineProperty(e, 'stack', {
    configurable: false,
    enumerable: false,
    get() {
      // 检测到 DevTools 打开,或使用了 Runtime.enable CDP 方法
      return ''
    },
  })
  console.debug(e)

  setTimeout(testRuntimeEnableLeak, 100)
}

testRuntimeEnableLeak()

这段代码非常巧妙,是一个相当高级和隐蔽的检测手段。正常情况下,console.debug(e) 只会简单输出错误对象,不会触发 stack 属性的 getter 方法,而一旦 Chrome DevTools 打开或 CDP 的 Runtime.enable 被调用时,浏览器会自动获取 Error 对象的 stack 属性来在控制台中显示更详细的错误信息,从而能被我们检测到。这种技术不仅用于浏览器机器人检测,也常用于检测是否有人在调试网页,防止别人对网站做逆向工程。

知道了检测原理,反检测也就很简单了,有几种不同的方法:

  • 在页面载入之前注入一段 JS,重写 console.debug 方法,比如 console.debug = {},或者为 console 创建一个代理,window.console = new Proxy(origConsole, handler)
  • 因为检测是每隔一段时间检测一次,可以在调用 Runtime.enable 之后立即调用 Runtime.Disable,大概率也能逃过程序的检测;

navigatorWebdriver

这是一个老掉牙的检测手段,当 Chrome 浏览器正在由自动化软件驱动时,会将 navigator.webdriver 设置为 true,在正常的浏览器中该值应该为 false,检测代码如下:

if (navigator.webdriver === true ||
    typeof navigator.webdriver === 'undefined' ||
    Object.getOwnPropertyNames(navigator).length !== 0 ||
    Object.getOwnPropertyDescriptor(navigator, 'webdriver') !== undefined) {
    // 检测到浏览器自动化
}

绕过方法很简单,启动 Chrome 时加一个 --disable-blink-features=AutomationControlled 开关即可,注意这个开关 Browser Use 默认已经加过了。

网上还有一些其他绕过方法,比如在加载页面前注入下面的脚本:

await browser_context.add_init_script("""
  Object.defineProperty(navigator, 'webdriver', {
    get: () => undefined,  // or false
  });
""")

由于 navigator.webdriver 是只读的,所以直接设成 false 是不生效的,这里通过给 navigator 定义一个 webdriver 属性并重写其 getter 方法,使得 navigator.webdriver 的值变成 undefinedfalse

这种方法也能绕过一些网站的检测,但是绕不过上面的检测代码。在上面的代码里,除了对 navigator.webdriver 的值进行判断,而且还对其类型是否为 undefined 以及是否存在自定义属性等进行判断,导致这种方法很容易被检测出来。因此最保险的做法还是 --disable-blink-features=AutomationControlled 开关。

viewport

这也是一个简单的检测手段,当运行 Puppeteer 时,它默认使用 800x600 的视口大小,运行 Playwright 时,默认使用 1280x720 的视口大小。这个固定值非常明显,很容易被检测到,因为几乎没有哪个正常用户的浏览器会有这样的视口大小。

所以有些网站通过这两个固定值来检测 Puppeteer 和 Playwright 的使用。随便改一下视口大小就能绕过:

browser = await p.chromium.launch(headless=False)
browser_context = await browser.new_context(viewport={"height":721,"width":1281})
page = await browser_context.new_page()
await page.goto("https://bot-detector.rebrowser.net/")

pwInitScripts

这是一个 Playwright 特有的属性,默认会在每个页面的 window 对象上注入,还有一个 playwright__binding 方法也是一样的,检测代码如下:

if (window.__pwInitScripts !== undefined || 
    window.__playwright__binding__ !== undefined) {
    // 检测到 Playwright 使用
}

要绕过检测,可以在页面载入之前注入一段 JS,将该属性删除:

delete window.__pwInitScripts;
delete window.__playwright__binding__;

useragent

这个测试项通过检查 navigator.userAgentData 里是否同时包含 ChromiumGoogle Chrome 两项,正常用户使用的 Chrome 浏览器应该两项都包含。但是 Puppeteer 和 Playwright 通常使用 Chromium 浏览器,数据里只有 Chromium 一项,这是一个警告信号:

const fullVersionList = await navigator.userAgentData.getHighEntropyValues(['fullVersionList']);
const brands = fullVersionList.brands.map(item => item.brand);

if (fullVersionList.length &&
    brands.includes('Chromium') && 
    !brands.includes('Google Chrome')) {
    // 检测到你可能在用 Google Chrome 测试
}

可以使用 executablePath 启动用户自定义的浏览器绕过此检查,或者通过注入脚本,修改 navigator.userAgentData 的值。

小结

我们今天学习了如何使用 Patchright 在自动化浏览器操作中绕过机器人检测,通过在 Browser Use 中集成 Patchright,用户可以轻松地在复杂的反爬环境中执行浏览器自动化任务。我们还深入探讨了一些简单而有效的浏览器机器人检测技术,例如 runtimeEnableLeaknavigatorWebdriverviewportpwInitScriptsuseragent 等,通过学习相应的检测和反检测策略,可以帮我们更好的理解 Patchright 的使用。

浏览器机器人的检测和反检测,是一个复杂的话题,涉及浏览器行为模拟、网络安全、反爬虫技术等多个领域。同时,这也是一场永无尽头的猫鼠游戏,作为网站管理员,需要持续更新技术以适应最新的爬虫技巧,而作为爬虫开发者,也需要不断学习以应对不断演进的检测机制。


详解 Browser Use 的浏览器配置

昨天我们学习了 Browser Use 启动或连接浏览器的几种方法,包括 executable_path、Playwright 对象、Browser Server、CDP 和 PID 等,这些方法有一个共同点,都是通过 BrowserSession 类来设置参数,而这个类也是 Browser Use 配置和操作浏览器的核心。

BrowserSession 的配置参数非常繁杂,不过可以将其大致分为两类,一类是 会话相关参数(Session-Specific Parameters),包括浏览器连接参数和活动的 Playwright 对象实例,BrowserSession 通过这些参数连接浏览器并跟踪其活动;另一类是 浏览器配置模版(Browser Profile),包括 Browser Use 特有的参数和 Playwright 相关的参数,这类参数的特点是静态的,可以通过 BrowserProfile 存储,方便 BrowserSession 复用。

我们今天就对照官方文档,对这些参数做个盘点。

会话相关参数

会话相关参数,包括浏览器连接参数和 Playwright 对象实例,这些参数和活动的会话相关,无法存储在 BrowserProfile(...) 模板中。

浏览器连接参数

这些参数分别对应不同的连接到现有浏览器的方式,在昨天的文章中我们已经详细学过这些内容:

参数名默认值参数说明
wss_urlNone连接到 Playwright 协议的浏览器服务器地址
cdp_urlNone通过 CDP 协议连接到开启调试模式的 Chrome 浏览器
browser_pidNone根据 PID 连接到本地运行的浏览器进程

Playwright 对象实例

我们知道,BrowserBrowserContextPage 都是 Playwright 内置的对象,一般通过 (await async_playwright().start())(await async_patchright().start()) 以调试模式启动浏览器子进程,然后通过 CDP 将命令传递给浏览器。

我们可以将这些对象传递给 BrowserSession,让 Browser Use 连接并复用已有的浏览器实例:

参数名默认值参数说明
playwrightNone可选的 Playwright 或 Patchright API 客户端句柄
browserNone可选的 Playwright Browser 对象
browser_contextNone可选的 Playwright BrowserContext 对象
pageNone也可以写成 agent_current_page,表示智能体专注的前台页面
human_current_pageNone人类专注的前台页面,不必手动设置
initializedFalse将 BrowserSession 标记为已初始化,跳过启动和连接(不推荐)

浏览器配置模版

browser_profile: BrowserProfile = BrowserProfile()

BrowserSession 可以接受一个可选的 browser_profile 参数,它是一个配置模板,可以包含一些配置默认值,用法如下:

browser_profile = BrowserProfile(
  executable_path='/Applications/Google Chrome.app/Contents/MacOS/Google Chrome',
  user_data_dir='~/.config/browseruse/profiles/default',
)
browser_session = BrowserSession(    
  browser_profile=browser_profile
)

所有 BrowserProfile 中的参数都可以直接传给 BrowserSession,下面的写法和上面的代码没有区别:

browser_session = BrowserSession(    
  executable_path='/Applications/Google Chrome.app/Contents/MacOS/Google Chrome',
  user_data_dir='~/.config/browseruse/profiles/default',
)

如果某个参数同时传给 BrowserProfileBrowserSessionBrowserSession 中的参数值会覆盖 BrowserProfile 中的默认值。

尽管 BrowserProfile 是可选的,将参数传给 BrowserProfile 或者 BrowserSession 都可以,但使用 BrowserProfile 提供了一些额外的好处:

  • 直接传给 BrowserSession**kwargs 参数,是一个普通的 dict 字典,而传给 BrowserProfile 的参数具有明确的类型提示,在 IDE 中还能显示其 pydantic 字段描述;
  • 传给 BrowserProfile 的参数在启动浏览器之前就可以快速验证;
  • BrowserProfile 提供了一些辅助方法用来自动检测屏幕大小,设置本地路径,保存或加载配置为 json 等;

除此之外,BrowserProfile 更大的一个用处是将一些可重用的静态配置独立出来,保存到数据库中,用户对其查看和编辑,供用户的不同会话复用。一个用户常用的配置文件可能只有 2 到 3 个,但他创建的浏览器会话却可能多达成千上万个,将动态的浏览器连接信息和静态的可重用配置分开,可以极大的避免数据库空间的浪费。

BrowserProfile 的参数可以分为 Browser Use 参数Playwright 参数,下面将分别介绍。

Browser Use 参数

这些参数控制 Browser Use 的特定功能,区别于 Playwright 参数。它们可以传递给 BrowserSession(...) 或存储在 BrowserProfile 模板中。

keep_alive: bool | None = None

如果该参数设置为 True ,则将在 agent.run() 结束后不关闭浏览器,这对于使用同一浏览器实例运行多个任务非常有用。如果将其保留为 None ,则根据 Browser Use 运行时是否启动浏览器来决定是否关闭:如果 Browser Use 启动了自己的浏览器,则完成任务后关闭浏览器,如果 Browser Use 连接到现有浏览器,则将保持其打开状态。

stealth: bool = False

Browser Use 默认使用 Playwright 操作浏览器,但是很容易被当成机器人检测出来,Patchright 基于 Playwright 改造,使得浏览器操作可以绕过机器人检测。设置 stealth=True 表示使用 Patchright 操作浏览器。

allowed_domains: list[str] | None = None

允许浏览器访问的域名列表,如果为 None,则允许所有域名,支持 GLOB 匹配模式:

  • ['example.com'] - 仅会完全匹配 https://example.com/* ,不允许子域名。为确保安全性,请明确列出你希望授予访问权限的所有域名。
  • ['*.example.com'] - 这将匹配 https://example.com 及其所有子域名,包括 abc.example.com , def.example.com , admin.example.com 等,你需要确保所有子域名的安全性!
disable_security: bool = False

完全禁用所有基本的浏览器安全功能,比如允许和跨站点 iFrame 进行交互,但 此选项非常不安全,仅适用于小众用例,请慎用。

deterministic_rendering: bool = False

通过禁用特定于操作系统的字体提示、抗锯齿、GPU 加速渲染,标准化 DPI,并设置特定的 JS 随机种子以尝试避免非确定性 JS,实现更确定性的渲染,以便在不同的主机操作系统和硬件上获得一致的屏幕截图。这个参数较少使用,而且更容易被视为机器人并偶尔触发一些故障行为,除非你知道需要它,否则不推荐使用。

highlight_elements: bool = True

在屏幕上用彩色边框突出显示交互元素。

viewport_expansion: int = 500

以像素为单位的视口扩展。通过此功能,可以控制在 LLM 的上下文中包含页面的多少部分:

  • -1 : 页面上的所有元素都将被包含,无论其可见性如何(最高的令牌使用量,但最完整);
  • 0 : 只有当前在视口中可见的元素会被包含;
  • 500 (默认): 视口中的元素加上每个方向额外的 500 像素将被包含在内,从而在上下文和令牌使用之间提供平衡;
include_dynamic_attributes: bool = True

在选择器中包含动态属性以更好地定位元素。

minimum_wait_page_load_time: float = 0.25

捕获页面状态之前的最短等待时间。

wait_for_network_idle_page_load_time: float = 0.5

等待网络活动停止的时间。对于较慢的网站,可以增加到 3-5 秒。注意,这只跟踪基本内容的加载,而不是视频等动态元素。

maximum_wait_page_load_time: float = 5.0

最大等待页面加载的时间。

wait_between_actions: float = 0.5

执行动作之间的等待时间。

cookies_file: str | None = None

保存 cookies 的 JSON 文件路径,此选项已弃用,请改用 storage_state 参数。

profile_directory: str = 'Default'

你在 user_data_dir 中的 Chrome 配置文件子目录名称(例如 DefaultProfile 1Work 等)。除非你在单个 user_data_dir 中设置了多个配置文件并需要使用特定的配置文件,否则无需设置此项。

window_size: dict | None = None

有头模式的浏览器窗口大小,示例: {"width": 1920, "height": 1080}

window_position: dict | None = {"width": 0, "height": 0}

窗口位置,从左上角开始。

Playwright 参数

以下所有参数都是标准的 Playwright 参数,可以传递给 BrowserSessionBrowserProfile 来控制浏览器设置,由于数量众多,这里只作简单总结,不能一一详解。可以参考 Playwright 官方文档了解每个参数的详细用法。

启动选项

headless: bool | None = None

是否开启无头模式,无头模式指的是在没有用户界面的情况下运行浏览器;默认为 None,根据显示可用性自动检测。设置 headless=False 可以提供最大的隐蔽性,也是人机协作所必需的。

如果在没有连接显示器的服务器上设置 headless=False ,浏览器将无法启动,可以使用 xvfb + vnc 为无头浏览器提供一个可以远程控制的虚拟显示。

channel: BrowserChannel = 'chromium'

浏览器频道,可选值有:

  • 'chromium' - 当 stealth=False 时为默认
  • 'chrome' - 当 stealth=True 时为默认
  • 'chrome-beta'
  • 'chrome-dev'
  • 'chrome-canary'
  • 'msedge'
  • 'msedge-beta'
  • 'msedge-dev'
  • 'msedge-canary'

对于列表中未列出的其他基于 Chromium 的浏览器也是支持的,例如 Brave,只要提供自己的 executable_path 参数,并将 channel 设置为 chromium 即可。

executable_path: str | Path | None = None

用于启动用户自己安装的浏览器。

user_data_dir: str | Path | None = '~/.config/browseruse/profiles/default'

浏览器配置文件数据的目录,设置为 None 表示使用临时配置文件,即隐身模式。注意,多个运行中的浏览器不能同时共享一个 user_data_dir,如果你需要同时运行多个浏览器,必须将其设置为 None 或为每个会话提供一个唯一的 user_data_dir

args: list[str] = []

传递给浏览器的其他命令行参数,下面这个链接列出了 Chrome 所有可用的参数:

ignore_default_args: list[str] | bool = ['--enable-automation', '--disable-extensions']

Playwright 在启动 Chrome 时过滤掉列表中指定的参数,如果将其设置为 True 则禁用 Playwright 所有默认选项,只使用 args 中的参数(不推荐)。

env: dict[str, str] = {}

启动浏览器时要设置的额外环境变量,例如,{'DISPLAY': '1'} 用于使用特定的 X11 显示。

chromium_sandbox: bool = not IN_DOCKER

是否启用 Chromium 沙箱提高安全性,在 Docker 内部运行时,应该设为 False,因为 Docker 提供了自己的沙箱,可能与 Chrome 的沙箱冲突。

devtools: bool = False

是否为每个标签页自动打开开发者工具面板,如果此选项为 True,则无头选项 headless 将设置为 False

slow_mo: float = 0

通过指定的毫秒数减慢 Playwright 操作,方便用户看清发生了什么。

accept_downloads: bool = True

是否自动接受所有下载。

downloads_path: str | Path | None = '~/.config/browseruse/downloads'

本地文件系统目录,用于保存浏览器下载的文件,该参数还有两个别名 downloads_dirsave_downloads_path,但是推荐使用标准的 downloads_path

base_url: str | None = None

当设置 base_url 之后,调用 page.goto()page.route()page.wait_for_url() 等操作时可以使用相对 URL 路径。

proxy: dict | None = None

为浏览器设置代理,示例如下:

{
  "server": "http://proxy.com:8080",
  "username": "user",
  "password": "pass"
}
permissions: list[str] = ['clipboard-read', 'clipboard-write', 'notifications']

授予浏览器权限,点击下面的链接获取可用权限的完整列表:

storage_state: str | Path | dict | None = None

使用特定的浏览器存储状态,可以通过下面的命令生成存储状态文件:

$ playwright open https://example.com/ --save-storage=./storage_state.json

存储状态文件中包含 cookies 和 localStorage 等,然后通过下面的方式使用它(注意 user_data_dir 必须设置成 None):

session = BrowserSession(
  storage_state='./storage_state.json',
  user_data_dir=None
)

超时设置

参数名默认值参数说明
timeout30000连接远程浏览器的默认超时时间(毫秒)
default_timeoutNonePlaywright 操作的默认超时时间(毫秒)
default_navigation_timeoutNone页面导航的默认超时时间(毫秒)

视口选项

参数名默认值参数说明
viewportNone使用 width 和 height 的视口大小,示例: {"width": 1280, "height": 720}
no_viewportnot headless禁用固定视口,内容将随窗口调整大小,注意,不要使用此参数
device_scale_factorNone设备缩放因子(DPI),适用于高分辨率截图(设置为 2
screenNone可供浏览器使用的屏幕大小,如果未指定,则自动检测
color_scheme'light'首选颜色方案: 'light''dark''no-preference'
contrast'no-preference'对比度偏好: 'no-preference''more''null'
reduced_motion'no-preference'减少运动偏好: 'reduce''no-preference''null'
forced_colors'none'强制颜色模式: 'active''none''null'

设备模拟

参数名默认值参数说明
offlineFalse模拟网络离线
user_agentNone为浏览器设置特定的 User-Agent
is_mobileFalse是否考虑 meta viewport 标签并启用触摸事件
has_touchFalse指定视口是否支持触摸事件
geolocationNone地理位置坐标,示例: {"latitude": 59.95, "longitude": 30.31667}
localeNone指定用户区域设置,例如 en-GBde-DE 等,区域设置将影响 navigator.language 值、Accept-Language 请求头值以及数字和日期格式规则
timezone_idNone时区标识符(例如,‘America/New_York’

除此之外,还可以通过 playwright.devices 来模拟常见的设备:

BrowserProfile(
    ...
    **playwright.devices['iPhone 13'],
)

支持接受所有标准的 Playwright 参数,参考这里:

安全选项

参数名默认值参数说明
http_credentialsNoneHTTP 身份验证的凭据
extra_http_headers{}每个请求将发送的附加 HTTP 头
ignore_https_errorsFalse在发送网络请求时是否忽略 HTTPS 错误
bypass_cspFalse是否绕过内容安全策略(Content-Security-Policy)
java_script_enabledTrue是否启用 JavaScript
service_workers'allow'是否允许页面注册使用 Service Workers: 'allow''block'
strict_selectorsFalse如果为真,传递给 Playwright 方法的选择器将在匹配多个元素时抛出错误
client_certificates[]用于请求的客户端证书

录制跟踪

参数名默认值参数说明
record_video_dirNone别名 save_recording_path,保存 .webm 视频录制的目录
record_video_sizeNone视频大小,示例:{"width": 1280, "height": 720}
record_har_pathNone别名 save_har_path,保存 .har 网络跟踪文件的路径
record_har_content'embed'如何持久化 HAR 内容:'omit''embed''attach'
record_har_mode'full'HAR 录制模式:'full''minimal'
record_har_omit_contentFalse是否从 HAR 中省略请求内容
record_har_url_filterNoneHAR 录制的 URL 过滤器
traces_dirNone别名 trace_path,保存所有跟踪文件的目录,文件自动命名为 {traces_dir}/{context_id}.zip

信号处理

参数名默认值参数说明
handle_sighupTrue是否让 Playwright 捕获 SIGHUP 信号并终止浏览器
handle_sigintFalse是否让 Playwright 捕获 SIGINT 信号并终止浏览器,也就是 Ctrl+C 发送的信号
handle_sigtermFalse是否让 Playwright 捕获 SIGTERM 信号并终止浏览器,也就是 kill 默认发送的信号

小结

我们今天主要学习了 Browser Use 中关于浏览器的配置参数,鉴于浏览器的复杂性,相关参数也是非常的多,主要可以分为会话相关参数和浏览器配置模版;还学习了 BrowserProfileBrowserSession 的区别和用途,可以将静态的浏览器配置存储在 BrowserProfile 里,供 BrowserSession 复用。更多细节可以参考 Browser Use 和 Playwright 的官方文档:


使用 Browser Use 连接你的浏览器

浏览器是我们日常生活和工作中不可或缺的重要工具,Browser Use 的本质就是模拟人类对浏览器进行各种操作以完成用户任务。在之前的示例中,默认使用的是 Playwright 内置的 Chromium 浏览器,Browser Use 还支持多种启动或连接其他浏览器的方法,我们今天来学习这方面的内容。

通过 executable_path 启动本地浏览器

在之前介绍 Agent 配置时,我们曾学过 BrowserSession 参数的概念,它是 Browser Use 配置浏览器的核心类,它有一个 executable_path 参数,可以指定用户电脑上已安装的浏览器路径。当我们设置 executable_path 参数时,Browser Use 就会启动该浏览器,而不使用 Playwright 内置的浏览器:

from browser_use import Agent, BrowserSession

browser_session = BrowserSession(    
  executable_path='/Applications/Google Chrome.app/Contents/MacOS/Google Chrome',
  # executable_path='C:\\Program Files\\Google\\Chrome\\Application\\chrome.exe',
  # executable_path='/usr/bin/google-chrome',
  user_data_dir='~/.config/browseruse/profiles/default',
)

agent = Agent(
  task="Your task here",
  llm=llm,
  browser_session=browser_session,
)

上面的代码使用的是用户已安装的 Chrome 浏览器,注意不同的操作系统下安装路径也不同。Browser Use 只支持基于 Chromium 内核的浏览器,比如 BravePatchrightRebrowserMicrosoft Edge 等,暂时不支持 Firefox 或 Safari 浏览器。

自定义 Chrome 启动参数

鉴于安全原因,从 v136 开始,Chrome 不再支持使用默认的配置文件来驱动浏览器,所以无法复用你已有的浏览器配置,如登录凭据,收藏夹,扩展程序等。Browser Use 为此创建了一个新的专用配置文件,位于 ~/.config/browseruse/profiles/default,可以在启动 Chrome 浏览器时,将配置文件设置成这个(注意使用全路径):

$ cd /Applications/Google\ Chrome.app/Contents/MacOS
$ ./Google\ Chrome --user-data-dir=/Users/aneasystone/.config/browseruse/profiles/default

在 macOS 上,还有一种更简单的方式启动应用:

# 以应用名称启动
$ open -a <应用名称> --args <参数>

# 以应用路径启动
$ open <应用路径> --args <参数>

比如:

$ open -a "Google Chrome" --args \
    --user-data-dir=/Users/aneasystone/.config/browseruse/profiles/default

或:

$ open "/Applications/Google Chrome.app" --args \
    --user-data-dir=/Users/aneasystone/.config/browseruse/profiles/default

需要注意的是,启动之前最好先退出所有的 Chrome 实例,不然参数可能无效

通过这种方式打开浏览器后,所有的用户数据会持久化到 Browser Use 的专用配置文件中,所以当我们再次用 Browser Use 启动浏览器时,就可以访问这些用户数据,包括且不限于下面这些:

  • 所有已登录的会话和 Cookies
  • 如果启用了自动填充,则可访问保存的密码
  • 浏览器历史记录和书签
  • 扩展程序及其数据

所以务必检查分配给智能体的任务,确保符合你的安全要求!对于任何敏感数据,请使用 Agent(sensitive_data=...),并使用 BrowserSession(allowed_domains=...) 限制浏览器访问。

使用现有的 Playwright 对象连接浏览器

我们之前学习过 BrowserBrowserContextPage 参数的概念,这三个都是 Playwright 内置的类:Browser 表示浏览器实例,代表整个浏览器进程,通常使用 playwright.chromium.launch() 等方法创建;BrowserContext 表示浏览器上下文,相当于一个隔离的浏览器会话,类似于浏览器的 “无痕模式”,不同 BrowserContext 之间完全隔离,互不影响;Page 表示一个页面,代表浏览器中的一个标签页,是实际进行页面操作的对象(点击、输入、导航等)。

它们之间的关系,类似下面这样的分层结构:

Browser
├── BrowserContext 1
│   ├── Page 1
│   └── Page 2
└── BrowserContext 2
    ├── Page 3
    └── Page 4

我们可以将现有的 BrowserBrowserContextPage 对象传递给 BrowserSession 类,让 Browser Use 连接并复用已有的浏览器实例:

async with async_playwright() as p:
    
  # 手动打开页面
  browser = await p.chromium.launch(headless=False)
  context = await browser.new_context()
  page = await context.new_page()
  await page.goto("https://playwright.dev")

  browser_session = BrowserSession(
    browser=browser,
    browser_context=context,
    page=page,
  )

  agent = Agent(
    task="这个页面讲的是什么?",
    llm=llm,

    # 复用浏览器会话
    browser_session=browser_session
  )
  result = await agent.run()

或者使用简写形式,直接将这三个对象传给 Agent 类:

agent = Agent(
  task="这个页面讲的是什么?",
  llm=llm,

  browser=browser,
  browser_context=browser_context,
  page=page,
)

这种连接方式可以将其他基于 Playwright 的项目非常容易地集成到 Browser Use 中,比如 PatchrightStagehand 等:

stagehand = Stagehand(
  config=config, 
  server_url=os.getenv("STAGEHAND_SERVER_URL"),
)
await stagehand.init()

agent = Agent(
  task='your task',
  page=stagehand.page
)

连接 Playwright 的 Browser Server

Playwright 的 Node.js 版本有一个 浏览器服务器(Browser Server) 的特性,我们可以通过 BrowserType.launchServer 来启动,服务器会暴露一个 WebSocket 接口供其他应用连接,实现远程操作浏览器。

使用下面的 Node.js 代码启动 Browser Server:

const { chromium } = require('playwright');  // Or 'webkit' or 'firefox'.

(async () => {
  // 启动服务器
  const browserServer = await chromium.launchServer();
  const wsEndpoint = browserServer.wsEndpoint();
  console.log(wsEndpoint);

  // 通过 ws 连接服务器
  const browser = await chromium.connect(wsEndpoint);
  browser.newPage("https://playwright.dev/");

  // 等待 600 秒
  await new Promise(resolve => setTimeout(resolve, 600 * 1000));

  // 关闭服务器
  await browserServer.close();
})();

其中 wsEndpoint 就是服务器的地址,可以通过 wss_url 参数传递给 BrowserSession 供 Browser Use 中使用:

browser_session = BrowserSession(    
  wss_url="ws://localhost:55660/4b762d7e1b8b9a66d8c3ece7a5dd3b81"
)
agent = Agent(
  task="访问 https://playwright.dev 总结页面内容",
  llm=llm,
  browser_session=browser_session
)

注意 Browser Server 的服务端和客户端版本需要一致。

通过 CDP 连接本地 Chrome 浏览器

CDP 协议,全称 Chrome DevTools Protocol,这是一个允许工具对 Chromium、Chrome 和其他基于 Blink 的浏览器进行调试、检查和性能分析的协议。目前有 很多项目 正在使用这个协议,包括 Chrome 的开发者工具

在启动 Chrome 浏览器时加上 --remote-debugging-port=9222 参数即可开启 CDP 协议:

$ open -a "Google Chrome" --args \
    --remote-debugging-port=9222 \
    --user-data-dir=/Users/aneasystone/.config/browseruse/profiles/default

注意,调试模式必须指定自定义的用户数据目录。

浏览器会监听 9222 端口,使用 curl 检查是否能连接:

$ curl http://localhost:9222/json/version

如果一切正常,将返回类似下面这样的浏览器信息:

{
  "Browser": "Chrome/137.0.7151.104",
  "Protocol-Version": "1.3",
  "User-Agent": "Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/137.0.0.0 Safari/537.36",
  "V8-Version": "13.7.152.14",
  "WebKit-Version": "537.36 (@dd98cd04c723035396b6d301e1f196b29a14a0ff)",
  "webSocketDebuggerUrl": "ws://localhost:9222/devtools/browser/6dcde03c-cc5c-41b9-a7bf-64ebab62726d"
}

可以通过 cdp_url 参数传递给 BrowserSession 供 Browser Use 中使用:

browser_session = BrowserSession(    
  cdp_url="http://localhost:9222"
)
agent = Agent(
  task="访问 https://playwright.dev 总结页面内容",
  llm=llm,
  browser_session=browser_session
)

学习 CDP 协议

上面使用 curl 访问的 /json/version 接口,就是 CDP 协议中的一个接口,像这样的接口还有很多。比如访问 /json/list 接口:

$ curl http://localhost:9222/json/list

将返回浏览器当前打开的所有页面信息:

[ {
  "description": "",
  "devtoolsFrontendUrl": "...",
  "faviconUrl": "https://chromedevtools.github.io/devtools-protocol/images/logo.png",
  "id": "7C10F98B3BDCE163464F77ED2AC7948A",
  "title": "Chrome DevTools Protocol",
  "type": "page",
  "url": "https://chromedevtools.github.io/devtools-protocol/",
  "webSocketDebuggerUrl": "ws://localhost:9222/devtools/page/7C10F98B3BDCE163464F77ED2AC7948A"
}, {
  "description": "",
  "devtoolsFrontendUrl": "...",
  "faviconUrl": "https://mintlify.s3-us-west-1.amazonaws.com/browseruse-0aece648/_generated/favicon/favicon.ico?v=3",
  "id": "9624235768323EEEB7083FBD54748808",
  "title": "Connect to your Browser - Browser Use",
  "type": "page",
  "url": "https://docs.browser-use.com/customize/real-browser",
  "webSocketDebuggerUrl": "ws://localhost:9222/devtools/page/9624235768323EEEB7083FBD54748808"
} ]

访问 /json/new?{url} 接口:

$ curl -X PUT "http://localhost:9222/json/new?https://playwright.dev"

将打开一个新页面,并返回新页面的信息:

{
  "description": "",
  "devtoolsFrontendUrl": "...",
  "id": "BB33A59C140BB527452DB607BFCE3A03",
  "title": "",
  "type": "page",
  "url": "https://playwright.dev/",
  "webSocketDebuggerUrl": "ws://localhost:9222/devtools/page/BB33A59C140BB527452DB607BFCE3A03"
}

接口里返回的 webSocketDebuggerUrl 是该页面的调试地址,我们可以通过 WebSocket 连接它,并发送 CDP 命令去操作该页面:

cdp-ws.png

这里发送的 CDP 命令是 Page.navigate,将当前页面导航到一个新地址:

{"id": 1, "method": "Page.navigate", "params": {"url": "http://www.github.com"}}

还可以访问 /json/close/{targetId} 接口:

$ curl http://localhost:9222/json/close/BB33A59C140BB527452DB607BFCE3A03

将关闭对应的页面:

Target is closing

如果你对 CDP 协议感兴趣,可以参考其官方文档:

通过 PID 连接本地 Chrome 浏览器

Browser Use 还支持通过 PID 来连接 Chrome 浏览器:

browser_session = BrowserSession(
  browser_pid=46543
)
agent = Agent(
  task="访问 https://playwright.dev 总结页面内容",
  llm=llm,
  browser_session=browser_session
)

注意 Chrome 浏览器启动时必须加上 --remote-debugging-port=9222 参数,以调试模式启动。可以看下 Browser Use 底层的实现,通过 PID 找到进程的启动参数,然后从参数中找到 --remote-debugging-port 端口号,最终还是通过 CDP 协议来连接:

browser-pid.png

在线浏览器服务

目前有很多在线浏览器服务,它们都支持 WS 协议或 CDP 协议,感兴趣的可以试下:

小结

今天学习了 Browser Use 启动或连接浏览器的方法,包括通过 executable_path 启动本地浏览器、使用现有的 Playwright 对象连接浏览器、通过 Playwright 的 Browser Server 连接远程浏览器、通过 CDP 连接本地 Chrome 浏览器,以及通过 PID 连接本地 Chrome 浏览器等方法。这些技术使得 Browser Use 可以灵活地与不同的浏览器实例交互,从而更好地完成复杂的自动化任务。在使用这些功能时,需要注意安全性,确保不要无意中泄露敏感信息。


详解 Browser Use 的 Agent 用法(四)

经过这几天的学习,总算把 Browser Use Agent 所有的配置参数都搞清楚了,我们今天先对这些参数做一个总结回顾,然后继续来研究下 agent.run() 方法,学习它的基本用法和生命周期钩子等概念。

配置参数回顾

下面这张表格概括了 Agent 所有的配置参数,以及它的默认值和功能说明,可以帮助大家快速理解和回顾之前学习的内容:

参数名默认值参数说明
task必填表示智能体需要完成的任务,通常为自然语言描述。
llm必填使用的大语言模型实例。支持 DeepSeek、GPT-4 等。
pageNonePlaywright 的 Page 对象,用于让智能体直接使用预初始化的网页。
browserNonePlaywright 的 Browser 对象,用于指定浏览器实例。
browser_contextNonePlaywright 的 BrowserContext 对象,用于设置独立的浏览器上下文。
browser_profileNoneBrowser Use 内置的配置类,包括浏览器启动参数、连接参数和默认的视图信息。
browser_sessionNoneBrowser Use 内置的会话类,表示活跃的浏览器会话(实例化后的浏览器进程)。
controllerController()自定义工具管理类,可以通过装饰器 @controller.action 注册工具。升华智能体功能。
contextNone任意用户自定义的上下文对象,例如数据库连接、文件句柄或运行时配置,仅透传给自定义工具。
override_system_messageNone重写系统提示词的内容,用于替换默认的任务规划和工具调用提示词。
extend_system_messageNone在默认提示词后追加内容,用于增强提示词,而不是完全替换。建议优先使用。
max_actions_per_step10一次最多执行多少个大模型生成的动作,控制任务中模型生成动作的数目。
message_contextNone附加的任务上下文描述,用于让模型更好地理解任务或场景。
max_input_tokens128000最大支持输入 token 数,模型的上下文窗口大小(如 DeepSeek V3 为 64K,GPT-4 为 128K)。
tool_calling_method'auto'控制工具调用方式,可选 function_callingjson_moderawtoolsauto
use_visionTrue是否开启视觉能力,表示是否将网页截图作为消息的一部分。禁用能降低 token 消耗。
planner_llmNone用于规划任务的大语言模型实例,可以是一个比主模型更小或更便宜的模型。
use_vision_for_plannerFalse是否为规划模型开启视觉能力,如果开启,会传递网页截图给模型。
planner_interval1控制每隔多少步规划一次任务,默认是每步都规划。
is_planner_reasoningFalse是否通过 HumanMessage 而不是 SystemMessage 传递规划提示词,适配不支持 SystemMessage 的推理模型。
extend_planner_system_messageNone向默认规划提示词后附加额外内容,用于定制化任务分解的逻辑。
page_extraction_llmNone用于页面内容提取的大语言模型实例,如果未配置,默认使用主模型。
initial_actionsNone初始化时执行的固定动作(如打开某个页面),无需调用大模型,能节省 token。
save_conversation_pathNone保存会话记录的目录路径,包含每一步的 Prompt 和模型响应结果,便于调试和分析。
save_conversation_path_encoding'utf-8'会话记录文件的编码格式,默认为 UTF-8。
include_attributestitle, type, name, role保留关键网页元素的部分属性,用于节省 token(默认值包括 title, type 等带有语义信息的属性)。
validate_outputFalse完成任务时开启输出校验,确保任务的最终结果准确信息。
max_failures3遇到异常时的最大重试次数,可处理浏览器异常关闭、token 超限及接口限流等问题。
retry_delay10接口限流错误时延迟的时间(单位:秒),然后重试。
generate_gifFalse运行结束后生成操作过程的 GIF 动画,包含任务、每一步目标和截屏记录。
save_playwright_script_pathNone保存 Playwright 脚本的路径,用于重放操作过程(最新版本已移除,但派生了 Workflow Use 项目)。
register_new_step_callbackNone每次调用大模型获取下一步动作之后触发的回调函数,访问当前浏览器状态和大模型输出。
register_done_callbackNone整个任务完成后触发的回调函数,可访问智能体运行的所有历史数据。
register_external_agent_status_raise_error_callbackNone自定义是否在异常时触发 InterruptedError 的回调,如果返回 True,则触发。
injected_agent_stateNone用于注入已保存的智能体状态,支持从中断的地方继续任务。
enable_memoryTrue是否开启智能体的程序性记忆功能。
memory_configNone配置程序性记忆的相关参数,例如间隔步数、嵌入模型、向量数据库等。
available_file_pathsNone指定智能体可以访问和操作的文件路径列表,会在系统提示语中通知大模型。
sensitive_dataNone定义敏感数据及其对应的占位符,可防止敏感信息暴露给大模型。
sourcegit / pip / unknown告知 Browser Use 当前使用的来源场景,默认根据运行环境自动设置。

生命周期钩子

关于 Agent 的配置参数就此告一段落,接下来我们来看看 agent.run() 方法的定义:

async def run(
  self, 
  max_steps: int = 100, 
  on_step_start: AgentHookFunc | None = None, 
  on_step_end: AgentHookFunc | None = None
) -> AgentHistoryList:

它有三个参数:

  • max_steps - 限制循环的最大步数,防止智能体陷入死循环,默认是 100 步;
  • on_step_start - 在智能体处理当前状态并决定下一步行动之前执行;
  • on_step_end - 在智能体执行完当前步骤的所有操作后执行;

后两个参数被称为 Browser Use 的 生命周期钩子(Lifecycle Hooks),允许你在特定时刻执行自定义代码。我们可以在钩子函数中读取和修改智能体状态,实现自定义逻辑,改变配置,与外部应用程序集成等。

钩子函数类型如下:

AgentHookFunc = Callable[['Agent'], Awaitable[None]]

可以看出,每个钩子函数都应该是一个可调用的 async 函数,接受 agent 实例作为唯一参数:

async def on_step_start(agent: Agent):
  print(f"======== on_step_start {agent.state.n_steps} ========")

async def on_step_end(agent: Agent):
  print(f"======== on_step_end {agent.state.n_steps} ========")

我们知道,Agent 有一个配置参数 register_new_step_callback 用于注册回调,生命周期钩子和它的机制很相似。只不过注册的回调函数参数是 BrowserStateSummaryAgentOutput,可以在函数中访问当前的浏览器状态和大模型的输出,而钩子函数的参数为 Agent,可以在函数中访问整个智能体的状态和方法,使用起来更灵活。

钩子的访问点

下面是钩子函数中的一些有用的访问点:

  • agent.task - 让你看到主要任务是什么,可以使用 agent.add_new_task(...) 新增一个新的任务;
  • agent.controller - 用于访问 Controller 对象和 Registry 对象,包含可用的操作,比如在当前页面上执行某个动作:
agent.controller.registry.execute_action(
  'click_element_by_index',
  {
    'index': 123
  },
  browser_session=agent.browser_session
)
  • agent.context - 让你访问传递给 Agent(context=...) 的任何用户提供的上下文对象;
  • agent.sensitive_data - 包含敏感数据字典,可以就地更新以添加/删除/修改其中的内容;
  • agent.settings - 包含在初始化时传递给 Agent(...) 的所有配置选项;
  • agent.llm - 直接访问主 LLM 对象 (例如 ChatOpenAI );
  • agent.state - 提供对大量内部状态的访问,包括智能体当前的推理、输出、行动等:
# 模型的推理结果
model_thoughts = agent.state.history.model_thoughts()

# 模型的原始输出
model_outputs = agent.state.history.model_outputs()

# 智能体采取的行动
model_actions = agent.state.history.model_actions()

# 从网页提取的内容
extracted_content = agent.state.history.extracted_content()

# 智能体访问的 URL
urls = agent.state.history.urls()
  • agent.browser_session - 直接访问 BrowserSessionPlaywright 对象:
# 获取当前的 Page 对象
current_page = agent.browser_session.get_current_page()

# 获取当前的 BrowserContext 对象
browser_context = agent.browser_session.browser_context

# 获取当前上下文中所有打开的标签页
pages = agent.browser_session.browser_context.pages

# 当前页面 HTML
html = agent.browser_session.get_page_html()

# 当前页面的截图
screenshot = agent.browser_session.take_screenshot()

更多内容参考官网的 Lifecycle Hooks 文档:

历史列表和结构化输出

agent.run() 方法的返回值是一个 AgentHistoryList 对象,其中包含完整的执行历史。这个历史对于调试、分析和创建可重现的脚本是非常有用。下面是该对象常用的一些方法:

history.urls()              # List of visited URLs
history.screenshots()       # List of screenshot paths
history.model_thoughts()    # Get the agent’s reasoning process
history.model_actions()     # All actions with their parameters
history.action_names()      # Names of executed actions
history.action_results()    # Get results of all actions
history.extracted_content() # Content extracted during execution
history.is_done()           # Check if the agent completed successfully
history.has_errors()        # Check if any errors occurred
history.errors()            # Any errors that occurred
history.final_result()      # Get the final extracted content

其中 history.final_result() 是最常用的,用于输出文本格式的最终结果,不过我们也可以定义一个结构化的输出格式,以便后续处理。比如我们想从豆瓣读书上搜索关于 “大模型” 相关的书籍,并以结构化的形式展示出来,我们可以先定义书籍和书籍列表类型:

from typing import List
from pydantic import BaseModel

class Book(BaseModel):
  book_title: str          # 书名
  author: str              # 作者
  brief_introduction: str  # 简介
  score: float             # 评分

class Books(BaseModel):
  books: List[Book]

然后将其绑定在 Controller 上:

from browser_use import Controller

controller = Controller(output_model=Books)

将这个自定义的 Controller 传入 Agent,就可以通过 Books.model_validate_json(...)final_result 中得到结构化结果:

agent = Agent(
  task="进入豆瓣读书,搜索关于大模型相关的书籍,获取排名前三的书籍详情",
  llm=llm,
  controller=controller
)
history = await agent.run()
result = history.final_result()
parsed: Books = Books.model_validate_json(result)
for book in parsed.books:
  print('\n--------------------------------')
  print(f'书名:  {book.book_title}')
  print(f'作者:  {book.author}')
  print(f'简介:  {book.brief_introduction}')
  print(f'评分:  {book.score}')

输出结果如下:

--------------------------------
书名:  大模型算法:强化学习、微调与对齐
作者:  余昌叶
简介:  涉及强化学习、模型微调与对齐技术的前沿算法介绍。
评分:  9.6

--------------------------------
书名:  从零构建大模型
作者:  [美]塞巴斯蒂安·拉施卡 / 覃立波 / 冯骁骋
简介:  全面介绍大模型的构建方法和技术实践。
评分:  9.3

--------------------------------
书名:  解构大语言模型:从线性回归到通用人工智能
作者:  唐亘
简介:  探讨大语言模型从基础线性回归模型到通用人工智能的演变过程。
评分:  9.6

小结

到这里,Browser Use 的 Agent 用法基本上就讲解完了,不过之前在讲解浏览器相关的配置时还留了一个小尾巴。浏览器是一个很复杂的话题,比如如何自定义浏览器配置,如何使用 CDP 连接已有的浏览器实例,如何使用 Patchright 绕过浏览器自动化检测,等等等等,我们明天就来学习这些内容,完成 Browser Use 学习拼图中的最后一块。