
Ethan Collins
AI Agent Workflow Engineer
已发表 Aug 17, 2026
已更新 Aug 17, 2026 · 最小阅读量

sessions: write权限(返回403)和工具的第一个参数缺少Pydantic BaseModel注解(触发ValidationError)。本指南将CapSolver与Composio集成,作为完成reCAPTCHA v2工作流的代理工具。该工具不仅返回令牌,还会运行完整的页面序列,并将页面的实际响应视为成功条件。OpenAI代理SDK决定何时调用该工具,而Playwright浏览器自动化保留用于提交和验证的页面上下文。
仅在合法、合理、负责任且获得用户授权的工作流中使用此模式。技术能力不意味着有权访问私有、受限、敏感或未经授权的数据;部署前请查阅相关AI自动化指南。
工作流:
运行脚本
-> OpenAI代理SDK决定调用哪个工具
-> Composio自定义工具: complete_recaptcha_v2
-> Playwright打开页面
-> capsolver.solve(...)返回gRecaptchaResponse
-> 将令牌应用到g-recaptcha-response
-> Playwright提交并等待页面
-> 读取页面并判断是否通过
-> 工具返回 {"accepted": ..., "message": ...}
-> 代理报告accepted的结果
各组件职责如下:
| 组件 | 职责 |
|---|---|
| OpenAI代理SDK | 理解自然语言指令,决定何时调用工具,执行工具并整理响应 |
| Composio | 将标准Python函数注册为代理可调用的工具 |
| Playwright | 打开页面,应用结果,提交表单并读取结果页面状态 |
| CapSolver SDK | 通过单次solve()调用返回CAPTCHA结果 |
pip install composio composio-openai-agents openai-agents capsolver pydantic playwright
playwright install chromium
每个依赖项都有特定作用:
| 包 | 目的 |
|---|---|
| composio | 创建会话并注册或加载自定义工具 |
| composio-openai-agents | 将Composio工具转换为OpenAI代理可调用的对象 |
| openai-agents | 提供Agent、Runner和SQLite多轮记忆 |
| capsolver | 提供官方SDK并通过solve()返回结果 |
| pydantic | 定义工具输入模式 |
| playwright | 打开页面,应用结果,提交表单并读取响应 |
# API密钥。
COMPOSIO_API_KEY = "ak_..."
OPENAI_API_KEY = "sk-..." # 您的官方OpenAI API密钥。
os.environ["OPENAI_API_KEY"] = OPENAI_API_KEY # OpenAI SDK从环境变量中读取密钥。
# 配置CapSolver和Composio。
capsolver.api_key = "CAP-..."
composio = Composio(
api_key=COMPOSIO_API_KEY,
provider=OpenAIAgentsProvider(),
)
配置说明:
OPENAI_API_KEY必须写入环境变量,因为SDK从那里读取;OpenAIAgentsProvider使session.tools()返回的工具与Agent兼容;Composio密钥需要sessions: write权限,否则会话创建返回403。
当前Composio OpenAI提供者和OpenAI代理SDK参考文档解释了此配置使用的提供者和代理边界。
领取您的CapSolver优惠码
立即提升您的自动化预算!
在充值CapSolver账户时使用优惠码CAP26,每次充值可获得5%的额外奖励——无限制。
现在在您的CapSolver仪表板中领取
停止条件: 仅当页面包含预期的成功文本时,工具才报告成功。
finally块在成功和失败路径中都关闭浏览器。
import os
from typing import List, cast
import capsolver
from agents import Agent, Runner, SQLiteSession
from composio import Composio
from composio.core.models.custom_tool import CustomTool
from composio.core.models.tool_router import ToolRouterExperimentalConfig
from composio_openai_agents import OpenAIAgentsProvider
from playwright.sync_api import sync_playwright
from pydantic import BaseModel, Field
# API密钥。
COMPOSIO_API_KEY = "ak_..."
OPENAI_API_KEY = "sk-..." # 您的官方OpenAI API密钥。
os.environ["OPENAI_API_KEY"] = OPENAI_API_KEY
# 配置CapSolver和Composio。
capsolver.api_key = "CAP-..."
composio = Composio(
api_key=COMPOSIO_API_KEY,
provider=OpenAIAgentsProvider(),
)
# 自定义工具的输入模式;Composio要求此处为Pydantic BaseModel。
class CompleteRecaptchaInput(BaseModel):
target_url: str = Field(
default="https://www.google.com/recaptcha/api2/demo",
description="包含reCAPTCHA v2演示的页面URL",
)
website_key: str = Field(
default="6Le-wvkSAAAAAPBMRTvw0Q4Muexq9bi0DJwx_mJ-",
description="当前页面的reCAPTCHA v2网站密钥",
)
# 将整个流程注册为一个Composio工具,代理可调用。
# 第一个参数的类型注解是Composio推断模式所必需的。
@composio.experimental.tool(preload=True)
def complete_recaptcha_v2(input: CompleteRecaptchaInput, _ctx):
"""使用Playwright打开页面,解决reCAPTCHA v2,提交并验证。"""
with sync_playwright() as p:
browser = p.chromium.launch(headless=False) # 设置headless=True以隐藏窗口。
page = browser.new_page()
try:
page.goto(input.target_url)
# 请求CapSolver解决reCAPTCHA v2挑战。
solution = capsolver.solve(
{
"type": "ReCaptchaV2TaskProxyLess",
"websiteURL": input.target_url,
"websiteKey": input.website_key,
}
)
token = solution.get("gRecaptchaResponse")
page.evaluate(
"""
(token) => {
const textarea = document.getElementById('g-recaptcha-response');
if (textarea) {
textarea.value = token;
}
}
""",
token,
)
page.click("#recaptcha-demo-submit")
page.wait_for_load_state("networkidle")
result_page = page.content()
# 仅当页面实际显示成功文本时才视为成功
accepted = "Verification Success" in result_page
return {
"accepted": accepted,
"message": (
"Verification Success"
if accepted
else "页面未报告Verification Success"
),
}
finally:
browser.close()
def main():
experimental: ToolRouterExperimentalConfig = {
"custom_tools": cast(List[CustomTool], [complete_recaptcha_v2]),
}
session = composio.sessions.create(
user_id="playwright-recaptcha-demo-user",
experimental=experimental,
sandbox={"enable": False}, # 在此进程中运行工具,而非沙箱。
)
agent = Agent(
name="Playwright reCAPTCHA助手",
instructions=(
"当用户要求运行演示时,使用默认值调用complete_recaptcha_v2 "
"。仅当accepted为true时报告成功。"
),
model="gpt-5.2",
tools=session.tools(),
)
# 多轮对话的内存
memory = SQLiteSession("conversation")
print("Composio + Playwright reCAPTCHA v2演示正在运行...")
user_input = (
"现在使用默认的target_url和website_key调用complete_recaptcha_v2 "
"。不要询问确认。"
)
result = Runner.run_sync(
starting_agent=agent,
input=user_input,
session=memory,
)
print(f"助手: {result.final_output}\n")
if __name__ == "__main__":
main()
相同模式可以处理标准图片文本CAPTCHA,通过注册第二个Composio工具。此示例使用BotDetect CAPTCHA演示:图片元素为#demoCaptcha_CaptchaImage,输入为#captchaCode,验证按钮为#validateCaptchaButton。

ImageToTextTask请求通过body提交Base64图像。与基于令牌的任务不同,此任务直接返回识别文本,无需单独轮询循环。
image_src = page.locator("#demoCaptcha_CaptchaImage").get_attribute("src")
if not image_src or "," not in image_src:
raise RuntimeError("未找到有效的CAPTCHA图片Data URL")
base64_image = image_src.split(",", 1)[1] # 去除"data:image/...;base64,"前缀。
class CompleteImageCaptchaInput(BaseModel):
target_url: str = Field(
default="https://captcha.com/demos/features/captcha-demo.aspx",
description="图片CAPTCHA演示页面URL",
)
module: str = Field(
default="common",
description="CapSolver ImageToTextTask识别模块",
)
@composio.experimental.tool(preload=True)
def complete_image_captcha(input: CompleteImageCaptchaInput, _ctx):
"""使用Playwright打开页面,识别图片CAPTCHA,提交并验证。"""
with sync_playwright() as p:
browser = p.chromium.launch(headless=False)
page = browser.new_page()
try:
page.goto(input.target_url)
page.wait_for_selector("#demoCaptcha_CaptchaImage", state="visible")
# 图片src已经是数据URL;去除前缀以获取Base64。
image_src = page.locator("#demoCaptcha_CaptchaImage").get_attribute("src")
if not image_src or "," not in image_src:
raise RuntimeError("未找到有效的CAPTCHA图片Data URL")
base64_image = image_src.split(",", 1)[1]
solution = capsolver.solve(
{
"type": "ImageToTextTask",
"websiteURL": input.target_url,
"module": input.module,
"body": base64_image,
}
)
captcha_text = solution.get("text")
if not isinstance(captcha_text, str) or not captcha_text:
raise RuntimeError("CapSolver未返回识别文本")
page.fill("#captchaCode", captcha_text) # 填写识别文本。
page.click("#validateCaptchaButton")
page.wait_for_load_state("networkidle")
result_page = page.content()
# 演示页面在成功时显示"Correct!",失败时显示"Incorrect!"。
accepted = "Correct!" in result_page
return {
"accepted": accepted,
"recognized_text": captcha_text,
"message": "Correct!" if accepted else "页面未报告Correct!",
}
finally:
browser.close()
流程概述:
Playwright打开CAPTCHA页面
-> 等待#demoCaptcha_CaptchaImage可见
-> 读取src(数据URL)并去除前缀以获取Base64
-> capsolver.solve(ImageToTextTask)返回文本
-> page.fill将结果写入#captchaCode
-> page.click激活#validateCaptchaButton
-> page.content检查Correct!或Incorrect!
-> finally关闭浏览器
module参数是可选的,默认为common。如果CAPTCHA仅包含数字,请使用number。特殊样式可在适当情况下使用文档记录的独立模型。

例如,以下源代码可直接用于纯数字识别:
solution = capsolver.solve({
"type": "ImageToTextTask",
"module": "number",
"images": [base64_image],
})
answers = solution["answers"]
number模型支持一次提交多个图像,images最多可包含九个Base64字符串。支持的模型名称和用例列在上述CapSolver ImageToTextTask页面中。
experimental.tool: "complete_recaptcha_v2"的第一个参数必须
标注为Pydantic BaseModel子类。得到: <class 'inspect._empty'>
Composio从第一个参数的类型注解推断输入模式,因此input: CompleteRecaptchaInput不能省略。这是一个功能注解,而不是可选类型提示。Pydantic BaseModel参考文档描述了用于模式的模型类型。
会话创建可能会返回以下错误:
403 APIKey_InsufficientPermissions
此路由需要 "sessions" 写入权限
原因是 composio.sessions.create() 需要项目密钥的写入权限来处理会话,而当前密钥只有只读权限。密钥有效,但其作用域不足,因此返回 403 而不是 401。
解决步骤:
sessions: write 的新密钥,并替换脚本顶部的 COMPOSIO_API_KEY。此集成的核心是一个打包为 Composio 工具的完整业务流程:
Composio 工具 = Playwright 页面操作 + CapSolver 结果 + 页面状态验证
仅在您拥有或授权自动化的页面和进程中运行示例。使用环境变量或密钥管理器处理凭证,在页面未达到预期业务状态时停止操作,并审查重复失败情况而非无限重试。
对于需要专注 CAPTCHA 基础设施层的授权 Composio 代理工作流,使用您控制的页面测试 CapSolver,并在每次求解后验证应用结果。
Composio 在此集成中处理什么?
Composio 将 Python 函数注册为代理可调用的自定义工具,创建会话,暴露工具模式,并从 OpenAI 代理路由执行。
为什么第一个工具参数必须是 Pydantic BaseModel?
Composio 使用该注解来推断工具的输入模式。省略它会阻止模式构建,并在浏览器工作流开始前引发验证错误。
reCAPTCHA v2 工具在 CapSolver 返回令牌后会停止吗?
不会。代码保持不变,应用令牌,提交演示表单,读取结果 HTML,并仅在页面包含预期的“Verification Success”文本时报告成功。
ImageToTextTask 是否需要单独的轮询循环?
不需要。在此工作流中,官方 SDK 会直接返回识别文本。工具随后填写输入,提交页面,并以“Correct!”作为停止条件进行检查。
此工作流能否用于任何网站?
不能。仅用于合法、合理、负责任且用户授权的自动化。尊重网站条款、适用法律、速率限制和数据最小化要求。

Ethan Collins
AI Agent Workflow Engineer
Building clearer handoffs between AI agents and tools.
关于作者