
Ethan Collins
Pattern Recognition Specialist

capsolver-agent文档化为capsolver-core的工具适配层,具有LangChain工具用于代理框架和Playwright会话的浏览器方法。一个LangGraph Cloudflare Turnstile集成允许代理在授权的浏览器工作流中从验证步骤中恢复,然后继续原始任务。图不应要求语言模型点击或通过小部件推理。相反,模型或浏览器控制器检测到工作流被阻塞,图评估策略,并由确定性适配器调用记录的CapSolver功能。
CapSolver在其代理工具指南中记录了这种分工:模型处理导航和决策,capsolver-agent暴露工具模式和执行器,capsolver-core执行检测、求解和浏览器填充。
这种架构使LangGraph具有有用的作用。它可以使得恢复可见,强制重试预算,将敏感操作路由到人工,确保浏览器在图继续之前验证成功。
使用隔离的Python环境。CapSolver当前的官方指南从GitHub安装核心和代理包:
python -m venv .venv
source .venv/bin/activate
pip install git+https://github.com/capsolver-ai/capsolver-core.git
pip install "capsolver-agent[langchain] @ git+https://github.com/capsolver-ai/capsolver-agent.git"
pip install langchain-openai langgraph playwright
playwright install chromium
将CAPSOLVER_API_KEY和任何模型凭证放在批准的密钥存储中。不要将真实值写入图状态、检查点、提示、追踪事件或源文件中。
您还需要:
在图状态中仅保留非秘密的操作数据:
from typing import Literal, TypedDict
class AgentState(TypedDict, total=False):
request_id: str
purpose: str
page_id: str
current_url: str
step: str
challenge_detected: bool
challenge_attempts: int
challenge_status: Literal[
"not-needed", "pending", "resolved", "review", "denied"
]
error_code: str | None
final_assertion_passed: bool
不要添加CapSolver凭证、解决方案令牌、cookies或原始页面内容。将浏览器对象存储在由page_id键控的应用程序拥有的注册表中;图检查点应仅包含不透明的标识符。
授权应在任何挑战工具之前运行:
from urllib.parse import urlparse
ALLOWED_HOSTS = {"staging.example.com", "research.example.com"}
ALLOWED_PURPOSES = {"qa-validation", "public-data-research"}
def authorize_challenge(state: AgentState) -> AgentState:
host = urlparse(state["current_url"]).hostname
attempts = state.get("challenge_attempts", 0)
if host not in ALLOWED_HOSTS:
return {**state, "challenge_status": "denied", "error_code": "domain"}
if state.get("purpose") not in ALLOWED_PURPOSES:
return {**state, "challenge_status": "denied", "error_code": "purpose"}
if attempts >= 2:
return {**state, "challenge_status": "review", "error_code": "retry-limit"}
return {**state, "challenge_status": "pending"}
此节点是语法验证的,并且独立于模型。在生产中,从版本化配置中加载策略并拒绝未知字段。
class BrowserRegistry:
def __init__(self):
self._pages = {}
def register(self, page_id: str, page) -> None:
self._pages[page_id] = page
def get(self, page_id: str):
if page_id not in self._pages:
raise KeyError("browser page is not registered")
return self._pages[page_id]
async def remove(self, page_id: str) -> None:
page = self._pages.pop(page_id, None)
if page is not None:
await page.close()
注册表防止序列化Playwright Page,并为宿主提供一个地方来强制清理。
领取您的CapSolver优惠码
立即提升您的自动化预算!
在充值CapSolver账户时使用优惠码 CAP26,每次充值可获得额外 5% 的奖励 —— 没有限制。
现在在您的 CapSolver仪表板 中领取
CapSolver的Core SDK文档记录了detect(page)和solve_on_page(page)用于浏览器模式。下面的适配器使用这些方法并仅返回图决策:
import os
from capsolver_core import create_capsolver
async def solve_turnstile_node(
state: AgentState,
registry: BrowserRegistry,
) -> AgentState:
page = registry.get(state["page_id"])
attempts = state.get("challenge_attempts", 0) + 1
async with create_capsolver(
api_key=os.environ["CAPSOLVER_API_KEY"],
default_timeout=120,
polling_interval=5,
) as cap:
detected = await cap.detect(page)
if not detected:
return {
**state,
"challenge_attempts": attempts,
"challenge_status": "not-needed",
"error_code": None,
}
results = await cap.solve_on_page(page)
failures = [item for item in results if item.error or not item.filled]
if failures:
return {
**state,
"challenge_attempts": attempts,
"challenge_status": "review" if attempts >= 2 else "pending",
"error_code": "fill-back-failed",
}
return {
**state,
"challenge_attempts": attempts,
"challenge_status": "resolved",
"error_code": None,
}
代码已通过语法检查,但未运行凭证。实时测试需要经过批准的页面和秘密。图永远不会收到solution.token。
填充的令牌是中间结果。验证应用程序的预期状态:
async def verify_page_node(
state: AgentState,
registry: BrowserRegistry,
) -> AgentState:
page = registry.get(state["page_id"])
try:
await page.get_by_test_id("authorized-content").wait_for(timeout=15_000)
return {
**state,
"final_assertion_passed": True,
"step": "continue",
"error_code": None,
}
except Exception:
return {
**state,
"final_assertion_passed": False,
"challenge_status": "review",
"error_code": "page-assertion-failed",
}
使用由您的应用程序拥有的断言。避免在日志中暴露个人或敏感页面内容的选择器。
from langgraph.graph import END, StateGraph
def route_after_authorization(state: AgentState) -> str:
if state["challenge_status"] == "pending":
return "solve"
if state["challenge_status"] in {"denied", "review"}:
return "human_review"
return "verify"
def route_after_solve(state: AgentState) -> str:
if state["challenge_status"] == "resolved":
return "verify"
if state["challenge_status"] == "pending":
return "authorize"
return "human_review"
def build_graph(authorize, solve, verify, human_review):
graph = StateGraph(AgentState)
graph.add_node("authorize", authorize)
graph.add_node("solve", solve)
graph.add_node("verify", verify)
graph.add_node("human_review", human_review)
graph.set_entry_point("authorize")
graph.add_conditional_edges(
"authorize",
route_after_authorization,
{"solve": "solve", "verify": "verify", "human_review": "human_review"},
)
graph.add_conditional_edges(
"solve",
route_after_solve,
{"authorize": "authorize", "verify": "verify", "human_review": "human_review"},
)
graph.add_edge("verify", END)
graph.add_edge("human_review", END)
return graph.compile()
注入的函数可以封闭浏览器注册表。依赖注入使策略和故障路由可测试,而无需实时服务。
审核员应收到:
审核员不应收到CapSolver凭证或解决方案令牌。提交、购买、账户更改或消息发送等状态更改操作即使在验证成功后也需要自己的授权。
单元测试可以将解决节点替换为确定性存根:
async def solved_stub(state: AgentState) -> AgentState:
return {
**state,
"challenge_attempts": state.get("challenge_attempts", 0) + 1,
"challenge_status": "resolved",
"error_code": None,
}
async def failed_stub(state: AgentState) -> AgentState:
return {
**state,
"challenge_attempts": state.get("challenge_attempts", 0) + 1,
"challenge_status": "review",
"error_code": "fixture-failure",
}
测试批准和拒绝的域名、不支持的用途、重试耗尽、缺少浏览器页面、解决的挑战但页面断言失败,以及终端状态后的清理。
当应用程序知道Turnstile页面URL和公开站点密钥时,令牌模式可能更简单。CapSolver文档记录了AntiTurnstileTaskProxyLess任务,需要websiteURL和websiteKey,以及可选的metadata.action和metadata.cdata。
不要让模型发明这些字段。从批准的页面或应用程序配置中确定性地提取它们。
追踪:
不要追踪包含凭证、浏览器cookies、原始令牌或未脱敏表单数据的提示。为截图和DOM证据定义保留和访问规则。
确认页面已完成加载,浏览器使用了预期的会话,并且SDK版本支持挑战类型。仅当页面断言仍能通过时,才将空检测结果视为not-needed。
记录错误类别,将参数与当前CapSolver文档进行比较,并在重试预算后停止。不要自动增加重试次数。
保持相同的浏览器页面,审查回调或小部件行为,并验证页面导航未替换上下文。
存储并强制challenge_attempts。在配置的限制后路由到人工审核。
保持CAPTCHA恢复与下游操作分离。图应显示操作本身的错误,而不是重新解决挑战。
当LangGraph Cloudflare Turnstile集成像有限恢复工作流一样行为时,最可靠:检测、授权、解决、验证、继续或停止。图提供路由和可观测性;确定性代码提供策略;CapSolver提供记录的识别层。
仅在合法、授权的自动化中使用CapSolver。在固定实现前,查阅当前的代理工具文档、Core SDK指南和相关的CapSolver博客教程。
Q: LangGraph是否自行解决Cloudflare Turnstile?
No. LangGraph控制工作流状态和路由;CapSolver适配器调用识别服务和浏览器方法。
Q: 是否应将解决方案令牌返回给模型?
No. 在受控浏览器适配器中应用它,并仅返回状态、错误类别和验证结果。
Q: 与Playwright一起使用哪个CapSolver方法?
当前Core SDK文档记录了detect(page)、get_captcha_info(page)和solve_on_page(page)用于浏览器模式。
Q: 图应允许多少次重试?
使用基于工作流的小的显式预算,并在路由到审核而不是允许无限制循环时进行路由。
Q: 代理可以对任何URL调用恢复节点吗?
No. 在节点调用CapSolver之前,强制执行确定性的域名和用途白名单。
Q: 什么证明挑战已成功处理?
页面级应用断言证明工作流恢复;仅提供者状态或填充的令牌不足以。
创建一个使用 CapSolver、安全的工具模式、重试预算以及针对 reCAPTCHA 和 Cloudflare Turnstile 的验证的 LangChain CAPTCHA 求解代理工具。

OpenAI Agents 验证码求解器内容应展示工具调用如何进入和退出模型循环。CapSolver 应作为经过记录的代理功能进行连接:浏览器或模型检测到验证挑战,经批准的工具处理它,并且代理仅在原始用户授权任务仍然有效时才恢复。官方的 CapSolver AI 文档描述了三个实用层:CapSolver for AI Agents 用于架构,Core SDK 浏览器模式用于 Playwright
