docs/playwright.mdXem bản gốc Bản dịch đầy đủ theo đúng cấu trúc tài liệu; code, liên kết và định danh kỹ thuật được giữ nguyên.
Trình duyệt nhà viết kịch
PlaywrightBrowser cung cấp cho nhân viên một trình duyệt Chrome thực sự, có trạng thái thông qua async
Nhà viết kịch: điều hướng, nhấp chuột, gõ, cuộn,
di chuyển qua lịch sử, trích xuất văn bản trang, chạy JavaScript và ảnh chụp màn hình.
Hãy tiếp cận nó khi các công cụ web nhẹ hơn không còn hiệu quả. Công cụ tìm kiếm trên web sẽ trả lời một câu hỏi nghiên cứu mà không cần tải trang và công cụ tìm nạp web xử lý một câu hỏi đã biết URL tĩnh. Khả năng này bao gồm những gì không thể tiếp cận: các trang đằng sau cookie đăng nhập hoặc phiên, SPA được hiển thị bằng JavaScript và các quy trình nhiều bước tương tác.
Mặc dù Pydantic AI Harness có trên các bản phát hành 0.x nhưng API có thể thay đổi giữa các bản phát hành nhỏ; khi đó, cảnh báo ngừng sử dụng và hướng dẫn di chuyển ghi chú phát hành sẽ cho bạn (hoặc người đại diện của bạn) biết chính xác cách nâng cấp. Xem chính sách phiên bản.
!!! lưu ý "Khả năng của trình duyệt nào?" Trang này bao gồm việc điều khiển trình duyệt từ mô hình máy chủ: đã nhập, hành động xác định, gọi một công cụ tại một thời điểm. Để trao một mục tiêu mở tới một agent tự trị điều khiển trình duyệt cho bạn, hãy xem Sử dụng trình duyệt. Cung cấp cho một agent cái này hay cái khác: mỗi khả năng chạy trình duyệt riêng của nó, do đó phiên do một người mở sẽ không hiển thị đến cái khác.
Cài đặt
các playwright sức hút bổ sung của Playwright và Chrome là một hệ nhị phân riêng biệt
tải xuống:
uv add "pydantic-ai-harness[playwright]"
playwright install chromium
Nếu tệp nhị phân Chrome bị thiếu trong runtime, công cụ trình duyệt sẽ trả về
playwright install chromium gợi ý là kết quả của nó thay vì kết thúc quá trình chạy, vì vậy
một agent có thể chạy shell có thể cài đặt trình duyệt và tiếp tục; sự thất bại
không được ghi nhớ nên cuộc gọi tiếp theo sẽ được thực hiện. Quá trình này cũng nhận được một
BrowserUnavailableWarning, vì nhà phát triển đang xem terminal cũng không thấy
kết quả công cụ cũng như dấu vết. Đặt auto_install_chromium=True để lấy
thay vào đó, tự động nhị phân trong lần bỏ lỡ đầu tiên.
Cách sử dụng
from pydantic_ai import Agent
from pydantic_ai_harness.playwright import PlaywrightBrowser
agent = Agent('anthropic:claude-sonnet-4-6', capabilities=[PlaywrightBrowser()])
result = await agent.run('Open https://example.com and tell me the page title.')
PlaywrightBrowser là một khả năng: nó đăng ký
bộ công cụ trình duyệt, đưa hướng dẫn ngắn gọn về thời điểm sử dụng vào lời nhắc hệ thống và
quản lý vòng đời của Chrome cho quá trình chạy.
Công cụ
| Công cụ | Chữ ký | Trả về |
|---|---|---|
navigate | (url, timeout_ms=None) | URL trang, tiêu đề và văn bản hiển thị (cắt ngắn) |
snapshot | (timeout_ms=None) | cây khả năng tiếp cận với aria-ref tay cầm (cắt ngắn) |
click | (selector, timeout_ms=None) | văn bản trang sau khi nhấp chuột; selector là một bộ chọn CSS, một aria-ref= xử lý, hoặc 'x,y' tọa độ pixel |
type_text | (selector, text, sequential=False, timeout_ms=None) | văn bản trang sau khi gõ (thay thế giá trị trường, không gửi); sequential=True gửi phím bấm thực sự |
press_key | (key, selector=None, timeout_ms=None) | trang văn bản sau khi nhấn phím; key là tên khóa Nhà viết kịch (Enter, Escape, Tab, Control+a) |
select_option | (selector, values, timeout_ms=None) | trang văn bản sau khi chọn các tùy chọn trong một <select> |
hover | (selector, timeout_ms=None) | văn bản trang sau khi di chuột, hiển thị các menu chỉ di chuột |
wait_for | (selector=None, text=None, gone=False, timeout_ms=None) | văn bản trang khi thành phần/văn bản xuất hiện -- hoặc, với gone=True, khi nó biến mất -- trong trang hoặc bất kỳ khung nào; vượt qua chính xác một trong selector/text |
screenshot | (full_page=False, timeout_ms=None) | một ghi chú có URL trang, cộng với PNG dưới dạng nội dung hình ảnh |
get_text | (selector=None, timeout_ms=None) | văn bản của phần tử hoặc văn bản hiển thị của toàn trang |
scroll | (direction, x=None, y=None, timeout_ms=None) | vị trí hiện tại của trang và văn bản của nó; direction lên/xuống/trái/phải (một màn hình) hoặc trên/dưới |
go_back | (timeout_ms=None) | văn bản của trang trước |
go_forward | (timeout_ms=None) | văn bản của trang tiếp theo |
execute_js | (script, timeout_ms=None) | kết quả JavaScript (chuỗi nguyên trạng, đối tượng dưới dạng JSON, null như undefined) |
console_messages | (errors_only=False) | đầu ra của bảng điều khiển và các lỗi tập lệnh chưa được phát hiện, cũ nhất trước |
tabs | (action='list', index=None) | các tab đang mở hoặc xác nhận cùng với văn bản của tab đang hoạt động; action là list/select/close/new |
handle_next_dialog | (accept, prompt_text=None) | xác nhận về cách tiếp theo alert/confirm/prompt sẽ được trả lời |
network_requests | (url_contains=None, errors_only=False) | yêu cầu trang được tạo với trạng thái của họ, bao gồm cả những trang bị chính sách thoát ra từ chối |
Mỗi hành động trên trang chấp nhận một tùy chọn timeout_ms để ghi đè cả hai giá trị mặc định cho
một cuộc gọi đó. Giá trị ghi đè phải lớn hơn 0: 0 vô hiệu hóa thời hạn
hoàn toàn, vẫn có sẵn dưới dạng mặc định khả năng nhưng không phải là đối số
mô hình chọn.
Thời hạn giới hạn toàn bộ lệnh gọi công cụ chứ không phải từng lệnh gọi Nhà viết kịch bên trong nó. một
navigate chờ đợi một goto, một trạng thái tải, một tiêu đề, một nội dung đã đọc và có thể là một
ảnh chụp màn hình; mỗi giai đoạn sẽ nhận được phần còn lại của ngân sách chứ không phải toàn bộ
lại số nữa, vậy timeout_ms là thời gian dài nhất mà cuộc gọi có thể thực hiện.
Hai lỗi mặc định chứ không phải một, vì hai lỗi này khác nhau. Một hành động mà
nhớ (click, get_text, wait_for) thường là một bộ chọn không khớp với gì,
và action_timeout_ms (5s) biến điều đó thành lỗi nhanh, dễ đọc thay vì
chờ đợi đủ lâu để được coi là một agent bị treo cổ. Tải trang một cách hợp pháp
lâu hơn, do đó việc điều hướng, xử lý tải và khởi động hoặc gắn vào trình duyệt
sử dụng navigation_timeout_ms (60s).
snapshot trả về cây khả năng truy cập của trang, cách có cấu trúc chi phí thấp dành cho
mô hình để đọc trang và lấy aria-ref=eN tay cầm. Nhắm mục tiêu một phần tử
bởi nó aria-ref= xử lý (được chuyển đến click hoặc type_text) đáng tin cậy hơn
một bộ chọn CSS do mô hình tạo ra. Ảnh chụp nhanh bao gồm nội dung iframe (xem
Nội dung được nhúng). Tiếp cận với screenshot chỉ khi
cần kiểm tra trực quan (biểu đồ, bố cục).
type_text điền vào một trường nhưng không gửi nó; press_key('Enter') có. A
bản xứ <select> không mở dưới dạng nội dung trang, vì vậy select_option vận hành nó
thay vì click. type_text đặt giá trị trong một bước và không gửi khóa
các sự kiện nhanh hơn và đủ cho một hình thức thông thường; vượt qua sequential=True
đối với trường phản ứng với mỗi lần nhấn phím -- tiện ích tự động hoàn thành và gõ trước,
đầu vào bị che hoặc được định dạng và trình chỉnh sửa bỏ qua giá trị được đặt trực tiếp.
wait_for chờ nội dung đến; wait_for(gone=True) đợi nó đi
đi, đó là cách một công cụ quay vòng hoặc một lớp phủ được chờ đợi khi có thứ thay thế nó
không được biết trước.
Mọi công cụ đều hoạt động trên tab đang hoạt động. A target="_blank" liên kết, cửa sổ bật lên đăng nhập hoặc
bước thanh toán sẽ mở bước thứ hai, bước này vẫn mở thay vì đóng:
tabs('list') cho thấy những gì đang mở và tabs('select', index) di chuyển đến đó. A
phiên giữ tối đa tám tab: qua đó, một tab mà trang mở sẽ bị đóng và
được ghi lại, trong khi tabs('new') bị từ chối và yêu cầu model đóng một cái trước.
Một hộp thoại trang (alert, confirm, prompt) chặn trang cho đến khi nó được
đã trả lời và bị loại bỏ trừ khi handle_next_dialog(accept=True) được gọi
trước hành động mở hộp thoại -- lệnh gọi đó bao gồm một hộp thoại chứ không phải phần còn lại của
cuộc chạy.
screenshot (và tùy chọn screenshot_on_navigate đính kèm) trả lại
hình ảnh như BinaryContent
thay vì chuỗi base64, do đó, các mô hình tầm nhìn sẽ nhìn thấy hình ảnh nguyên gốc thay vì
một bức tường base64 trong ngữ cảnh văn bản. Một bản chụp có dung lượng trên 5 MB (thường là toàn trang
ảnh chụp màn hình của một trang dài) được trả về dưới dạng lỗi giới hạn thay vì hình ảnh
nội dung, bởi vì nhà cung cấp mô hình từ chối hình ảnh quá khổ và lỗi sẽ
nếu không thì hủy bỏ việc chạy; chụp khung nhìn hoặc phần cuộn và chụp
thay vào đó.
Lỗi công cụ trình duyệt -- hết thời gian chờ, bộ chọn không khớp với phần tử nào, lỗi điều hướng hoặc trình duyệt bị đóng giữa chừng -- được trả về mô hình như các chuỗi lỗi mà nó có thể xử lý (thử lại, thử bộ chọn khác, điều hướng lại), không được nâng lên để hủy bỏ việc chạy agent.
Tùy chọn
| Tùy chọn | Mặc định | Mục đích |
|---|---|---|
headless | True | Chạy Chrome mà không có cửa sổ hiển thị (phù hợp với máy chủ và CI). |
allowed_domains | None | Danh sách cho phép đi ra đối với các yêu cầu điều hướng và dữ liệu; None cho phép mọi máy chủ công cộng (xem Egress). |
policy | None | đầy đủ EgressPolicy, đối với các quy tắc thì hai cách viết tắt này không thể diễn đạt được. Loại trừ lẫn nhau với họ. |
block_private_addresses | True | Từ chối các địa chỉ riêng tư, loopback, link-local và các địa chỉ dành riêng khác, cho dù được viết dưới dạng IP hay được truy cập thông qua tên máy chủ phân giải thành một (xem Egress). |
screenshot_on_navigate | False | Đính kèm ảnh chụp màn hình cho mọi navigate kết quả. |
max_content_tokens | 4000 | Ngân sách token gần đúng cho mỗi kết quả công cụ văn bản. |
action_timeout_ms | 5000 | Thời hạn mặc định cho các hành động của phần tử (nhấp, gõ, đọc, chờ). 0 vô hiệu hóa nó. |
navigation_timeout_ms | 60000 | Thời hạn mặc định cho việc điều hướng và giải quyết tải cũng như để khởi động hoặc gắn vào trình duyệt. 0 vô hiệu hóa nó. |
chromium_sandbox | True | Chạy Chrome đã khởi chạy với hộp cát kết xuất của nó. Chỉ tắt nó khi hộp cát không thể khởi động (một vùng chứa không có đặc quyền kernel cần thiết). Đã bỏ qua với cdp_url. |
auto_install_chromium | False | Tự động tìm nạp Chrome khi thiếu tệp nhị phân. |
storage_state | None | Trạng thái lưu trữ của nhà viết kịch (cookie + localStorage) được tải khi khởi chạy; xem Trang web được xác thực. |
cdp_url | None | Đính kèm vào Chrome đã chạy ở điểm cuối CDP này thay vì khởi chạy một Chrome; xem Đính kèm vào trình duyệt đang chạy. |
Trang web được xác thực
Vượt qua storage_state để khởi động trình duyệt đã đăng nhập. Đó là một Nhà viết kịch
trạng thái lưu trữ đối tượng -- cookie cộng
localStorage -- được tải vào ngữ cảnh trình duyệt khi khởi chạy, vì vậy, điều đầu tiên
điều hướng đã được xác thực.
Chụp nó một lần, bằng mã của riêng bạn, bằng cách đăng nhập bằng trình duyệt hiển thị:
from playwright.async_api import async_playwright
async def capture_state() -> object:
async with async_playwright() as pw:
browser = await pw.chromium.launch(headless=False)
context = await browser.new_context()
page = await context.new_page()
await page.goto('https://example.com/login')
# Log in by hand in the window that opened. The capture waits for a page
# only a signed-in session reaches, so it runs after the login rather than
# racing it; the deadline is long because a person is typing.
await page.wait_for_url('https://example.com/account', timeout=300_000)
state = await context.storage_state()
await browser.close()
return state
playwright codegen https://example.com --save-storage=auth.json viết tương tự
cấu trúc vào một tệp mà bạn tải bằng json.loads(Path('auth.json').read_text()).
Dù bằng cách nào, hãy giao đối tượng cho khả năng:
from pydantic_ai import Agent
from pydantic_ai_harness.playwright import PlaywrightBrowser
state = ... # captured above
agent = Agent(
'anthropic:claude-sonnet-4-6',
capabilities=[
PlaywrightBrowser(storage_state=state, allowed_domains=['example.com']),
],
)
Tùy chọn này lấy một đối tượng thay vì một đường dẫn vì các agent thường chạy trong
môi trường chia sẻ nơi hệ thống tập tin cục bộ không có nơi nào để đặt bất cứ thứ gì
bền vững: tiểu bang tồn tại ở đâu là quyết định của bạn chứ không phải khả năng. Đối với
lý do tương tự PlaywrightBrowser.from_spec không chấp nhận storage_state --
một thông số kỹ thuật sẽ mang cookie vào bất cứ nơi nào lưu trữ nó. Đặt nó trên
thay vào đó là khả năng được xây dựng.
Theo mặc định, khả năng này chạy headless và không thúc đẩy luồng đăng nhập chính nó: bạn nắm bắt được trạng thái ngoài ban nhạc. Mẫu thông thường là đăng nhập một lần bằng trình duyệt hiển thị, sau đó sử dụng lại trạng thái cho các lần chạy headless.
Hãy coi trạng thái là tài liệu thông tin xác thực: nó có thể mạo danh tài khoản. Giữ nó ngoài tầm kiểm soát nguồn và ngoài nhật ký, hãy lưu trữ nó với các quyền hạn chế nếu bạn vẫn duy trì nó và loại bỏ nó khi phiên hết hạn. Điều này phản ánh Hướng dẫn xác thực của chính nhà viết kịch cảnh báo. Thích trạng thái phạm vi tối thiểu (chỉ đăng nhập vào trang đích khi chụp nó) về việc sử dụng lại hồ sơ trình duyệt đầy đủ.
Đính kèm vào trình duyệt đang chạy
Đặt cdp_url để kết nối với Chrome đang chạy ở tốc độ
Giao thức Công cụ dành cho nhà phát triển của Chrome
điểm cuối thay vì khởi chạy một:
from pydantic_ai import Agent
from pydantic_ai_harness.playwright import PlaywrightBrowser
agent = Agent(
'anthropic:claude-sonnet-4-6',
capabilities=[PlaywrightBrowser(cdp_url='http://localhost:9222')],
)
Không có tệp nhị phân Chrome cục bộ nào được tham gia, vì vậy gợi ý cài đặt và
auto_install_chromium không áp dụng. Đây là điều mà các nhà cung cấp trình duyệt được quản lý và
khai thác điểm chuẩn mong đợi: họ khởi động trình duyệt và đưa cho bạn điểm cuối của trình duyệt đó.
Quá trình chạy vẫn có bối cảnh trình duyệt riêng, vì vậy storage_state, miền
danh sách cho phép và khối địa chỉ riêng áp dụng chính xác như đối với một danh sách đã khởi chạy
trình duyệt và agent không kế thừa các phiên đã mở trong đó
Chrome. Chỉ tay cdp_url tại trình duyệt cá nhân hàng ngày của bạn vẫn là
lựa chọn có rủi ro cao hơn -- quy trình đó giữ mọi tài khoản bạn đã đăng nhập và
bất cứ điều gì mà agent có thể tiếp cận thông qua nó đều có thể truy cập được chỉ trong một lần tiêm nhanh chóng.
Thích một trình duyệt được khởi động cho agent, với phạm vi storage_state. nhà cung cấp
điểm cuối đôi khi nhúng token xác thực vào URL; hãy coi những điều đó là bí mật.
Vòng đời
Chrome khởi động chậm trong lệnh gọi công cụ trình duyệt đầu tiên và bị đóng khi chạy
kết thúc -- khi thành công, lỗi hoặc hủy. Chạy mà không bao giờ gọi một công cụ trình duyệt
không phải trả chi phí Nhà viết kịch (không có quy trình phụ, không có cửa sổ). Mỗi hoạt động agent đều có riêng
trang và trình duyệt, đồng thời agent.run() cuộc gọi không bao giờ chia sẻ một tab.
Vòng đời đó tồn tại trong PlaywrightBrowserSession, mà khả năng tạo ra
mỗi lần chạy. Nó được xuất cho trường hợp bạn muốn có cùng một trình duyệt được bảo vệ
không có agent xung quanh -- danh sách cho phép, khối địa chỉ riêng,
khối service-worker, theo dõi tab và xử lý hộp thoại đều đi kèm với nó:
from pydantic_ai_harness.playwright import PlaywrightBrowserSession
async with PlaywrightBrowserSession() as session:
page = await session.ensure_page() # Chromium starts here, not on entry
await page.goto('https://example.com')
PlaywrightBrowserToolset được xuất trên cùng một cơ sở: chuyển nó một phiên tới
có được mười tám công cụ mà không cần móc của khả năng. Chính sách này tồn tại trên
phiên và chỉ ở đó, vì vậy bộ phận bảo vệ phiên sẽ cài đặt và kiểm tra
các công cụ chạy không thể phân kỳ: chúng là hai lớp của một quyết định, được áp dụng tại
những khoảnh khắc khác nhau.
Nội dung được nhúng (iframe)
Bộ chọn cấp độ trang dừng ở ranh giới khung, do đó, lịch trình được nhúng, bước thanh toán hoặc tiện ích trò chuyện không thể truy cập được thông qua bộ chọn CSS và một trang phần lớn là phần nhúng có thể trông gần như trống rỗng. Khả năng thu hẹp khoảng cách đó trong ba nơi:
- Công cụ trả về văn bản trang (
navigate,click,get_textkhông có bộ chọn và phần còn lại) nối thêm văn bản của từng khung con có bất kỳ, trong cùng một ngân sách token. wait_forxem trang và mọi khung hình trẻ em cùng một lúc; trận đấu đầu tiên thắng.snapshotbao gồm nội dung khung và các lượt giới thiệu của nó mang khung mà họ đã đưa ra từ (f1e4thay vìe4). Chuyển một giới thiệu như vậy đếnclick,type_text,hoverhoặcget_textgiải quyết bên trong khung đó -- một tay cầm đạt đến nội dung nhúng.
Việc quét qua các khung con bị giới hạn, do đó, một khung nhúng không phản hồi sẽ không thể tiêu thụ được thời hạn của hành động; bất cứ khung hình nào khác được trả về đều được giữ lại.
Gỡ lỗi một lần chạy
Ba điều khiến agent trình duyệt khó theo dõi: trang không hiển thị, phần tử bị thiếu trông giống như phần tử chậm và xảy ra những lỗi thú vị giữa các cuộc gọi công cụ.
- Đặt
headless=Falseđể xem cuộc chạy trong một cửa sổ thực sự. - Mọi thao tác của trình duyệt sẽ mở ra một phạm vi OpenTelemetry có tên
browser <action>(browser click,browser navigate), mang theobrowser.action,browser.timeout_ms,browser.outcome, và kết quảurl.full. Cái gì trang đã thực hiện trong thao tác đó được đính kèm dưới dạng các sự kiện span: đầu ra của bảng điều khiển, lỗi tập lệnh chưa được phát hiện, phản hồi, yêu cầu chính sách đầu ra bị từ chối, hộp thoại trang đã mở và các tab được mở. Các nhịp đi đến công cụ theo dõi riêng của cuộc chạy, vì vậy một agent được trang bị cho Logfire báo cáo chúng với mọi thứ khác. - Các agent có thể đọc cùng một bản ghi thông qua
console_messagesvànetwork_requests, đây thường là cách nó phục hồi từ một trang hiển thị từ API chứ không phải từ HTML. Chỉ những gì xác định một yêu cầu được ghi lại, không bao giờ là một cơ quan phản ứng. Một trang bận tạo ra nhiều mục hơn mức phù hợp với token ngân sách, và ngân sách cũ nhất đã bị loại bỏ, do đó sự thất bại đã thúc đẩy cuộc gọi vẫn tồn tại;network_requests(errors_only=True)thu hẹp nó hơn nữa. Các URL được ghi lại sẽ giữ lại máy chủ, đường dẫn và tên tham số nhưng bị mấtuser:password@thông tin xác thực và các giá trị của tham số mang thông tin xác thực (token,code,signature, và phần còn lại), vì chúng tiếp cận cả mô hình và phần phụ trợ đo từ xa. - Thời gian chờ dường như bị treo thường là thời gian chờ hành động.
action_timeout_msmặc định là 5s nên lỗi xảy ra nhanh chóng; hạ thấp nó hơn nữa trong khi gỡ lỗi và đọc giá trị thời gian chờ trong chuỗi lỗi để thông báo trang chậm từ một bộ chọn sai.
Tính năng lọc mặc định của Logfire sẽ loại bỏ các giá trị phù hợp với các mẫu như session,
auth, cookie và credit card, phù hợp với nội dung trang thường xuyên hơn bạn
mong đợi -- một trang web hội nghị có mọi tiêu đề đều nói "phiên" sẽ quay trở lại
như [Scrubbed due to 'session']và một trang định giá như
[Scrubbed due to 'credit card']. Giữ kết quả công cụ có thể đọc được bằng cách xóa chúng
có chọn lọc:
import re
import logfire
# The words Logfire redacts on that are ordinary vocabulary on a public page.
page_words = {'session', 'cookie', 'credit card', 'auth'}
def keep_browser_results(match: logfire.ScrubMatch) -> str | None:
# 'tool_response' is the same attribute under instrumentation version 2
if match.path[:2] not in {('attributes', 'gen_ai.tool.call.result'), ('attributes', 'tool_response')}:
return None
matched = re.sub(r'[._\- ]+', ' ', match.pattern_match.group(0)).strip().lower()
return match.value if matched in page_words else None
logfire.configure(scrubbing=logfire.ScrubbingOptions(callback=keep_browser_results))
Trở về match.value giữ nguyên văn bản gốc; trở về None rời khỏi
biên tập tại chỗ. Lệnh gọi lại được cung cấp thuộc tính chứ không phải công cụ
đã tạo ra nó, do đó nó không thể bị giới hạn ở các kết quả của trình duyệt -- thu hẹp phạm vi
từ đã kích hoạt việc chỉnh sửa là thứ giữ cho password, api_key và jwt
đã xử lý lại bất kỳ công cụ nào trả lại chúng.
Hạn chế
- Một phiên có thể mở tối đa tám tab; một trang mở ra nhiều hơn có các tính năng bổ sung đã đóng, được ghi lại trong nhật ký sự kiện.
- Tải lên và tải xuống không được hiển thị: ngữ cảnh từ chối tải xuống và ở đó không có công cụ nào để đưa tập tin vào một trang. Cả hai đều cần một hợp đồng tạo tác giữa trang và hệ thống tập tin máy chủ, được theo dõi trong #590.
- Bộ chọn CSS không thể tiếp cận nội dung bên trong iframe; đọc và hành động ở đó
đi qua
snapshotgiới thiệu (xem Nội dung được nhúng). - Thực thi bền vững (ví dụ:
TemporalDurability) bị từ chối tại agent xây dựng: trang Chrome trực tiếp không thể tồn tại khi phát lại hoạt động hoặc hoạt động khởi động lại. - Mô hình nhắm mục tiêu các phần tử theo
aria-ref=xử lý (từsnapshot), CSS bộ chọn hoặc tọa độ pixel.
Đi ra và SSRF
Theo mặc định, trình duyệt từ chối các địa chỉ không thể định tuyến toàn cầu --
169.254.169.254 (điểm cuối siêu dữ liệu đám mây), 127.0.0.1, ::1, localhost
và *.localhost tên và phạm vi riêng tư RFC 1918 -- ngay cả khi không có
danh sách cho phép đã được đặt. Tên máy chủ được phân giải trước tiên, do đó việc trỏ tên vào một trong các
những địa chỉ đó không vượt qua được nó. Đặt block_private_addresses=False khi nào
agent phải tiếp cận một ứng dụng cục bộ hoặc trang tổng quan nội bộ.
Với allowed_domains=None (mặc định) agent có thể truy cập bất kỳ URL công khai nào.
Khi agent có thể hành động trên đầu vào không đáng tin cậy, hãy đặt allowed_domains đến một cách rõ ràng
danh sách cho phép. Mỗi mục khớp với máy chủ chính xác của nó và bất kỳ tên miền phụ nào -- example.com
đạt tới api.example.com -- được so sánh ở dạng ASCII mà chính Chrome sử dụng, vì vậy
một máy chủ được quốc tế hóa và nó xn-- chính tả nhận được phán quyết tương tự. Một mục
được viết dưới dạng ký tự đại diện (*.example.com) tăng khi xây dựng: nó sẽ khớp với không
lưu trữ, trong khi đọc giống như một danh sách cho phép được định cấu hình.
Phạm vi tiếp cận của danh sách cho phép tùy thuộc vào mục đích của yêu cầu:
| Yêu cầu | Bị giới hạn bởi allowed_domains |
|---|---|
| Điều hướng cấp cao nhất | vâng |
fetch, XHR, Nguồn sự kiện, WebSocket, sendBeacon | vâng |
| Hình ảnh, bảng định kiểu, tập lệnh, phông chữ, phương tiện | không |
| Tài liệu khung con | không |
Yêu cầu dữ liệu được bao gồm vì tập lệnh trên trang được phép có thể đọc từ hoặc đăng lên bất cứ nơi nào; nguồn phụ thụ động và tài liệu khung phụ là không, bởi vì một trang có nội dung bị hủy hiển thị là một trang bị hỏng và một nhà cung cấp danh tính và các bước thanh toán của trang web được phép tồn tại trong các khung. các khối địa chỉ riêng hoàn toàn bỏ qua sự phân chia đó: nó áp dụng cho mọi khung hình và mọi loại tài nguyên. Các kết nối WebSocket mà tuyến mạng không bao giờ nhìn thấy sẽ nhận được người bảo vệ riêng của họ, áp dụng chính sách tương tự như bảng trên mô tả.
Máy chủ chưa có địa chỉ sẽ được giải quyết trước khối địa chỉ riêng
phân loại nó, do đó một tên trỏ đến một địa chỉ nội bộ (169.254.169.254.nip.io
và các dịch vụ DNS ký tự đại diện tương tự) bị từ chối thay vì tuân theo. Việc tra cứu đó chạy
cho mọi loại, khớp với việc kiểm tra theo nghĩa đen, do đó cách viết chính tả không quyết định
phán quyết; các câu trả lời được lưu vào bộ nhớ đệm, điều này làm cho nó có thể tra cứu trên mỗi máy chủ riêng biệt thay vì
mỗi yêu cầu. resolved_kinds bộ mà các loại được tra cứu.
Tra cứu không trả lời trong vòng hai giây là từ chối, không phải cho phép:
bất cứ ai kiểm soát một tên sẽ kiểm soát việc tra cứu của nó có trả lời hay không, vì vậy một gian hàng sẽ
nếu không thì hãy đi qua khối nhà. Cái giá phải trả là nhỏ khi thất bại xảy ra
Thành thật mà nói, vì cái tên mà quá trình này không thể giải quyết được là cái tên mà trình duyệt sắp sửa
cũng thất bại. Không có điều nào trong số này là bằng chứng chống lại việc khôi phục DNS: Chrome giải quyết vấn đề
đặt tên lần thứ hai trước khi kết nối và bản ghi thay đổi giữa
đánh bại nó. Các chính sách
độc lập và từ chối chiến thắng -- một danh sách được phép
địa chỉ riêng vẫn bị từ chối cho đến khi bạn chọn không tham gia block_private_addresses.
Việc thực thi có hai lớp: bộ bảo vệ tuyến mạng hủy bỏ yêu cầu bị từ chối
trước khi nó rời đi (bao gồm các nhấp chuột, execute_jsvà lịch sử chuyển động, không chỉ
navigate) và mỗi công cụ sẽ kiểm tra lại URL kết quả và trả về
about:blank vì vậy nội dung không được phép không bao giờ đến được mô hình. Nhân viên phục vụ là
bị chặn trong ngữ cảnh trình duyệt để lưu lượng truy cập của họ không thể vượt qua người bảo vệ tuyến đường.
Quy tắc hai trường không thể diễn đạt
EgressPolicy là toàn bộ chính sách, và PlaywrightBrowser(policy=...) mất một
thay vì allowed_domains / block_private_addresses viết tắt. của nó
các trường bao gồm một danh sách từ chối (blocked_domains, chiến thắng mọi thứ và đạt tới
mọi loại yêu cầu), chỉ khớp ở đỉnh (include_subdomains=False) và cái nào
sắp xếp giới hạn danh sách cho phép (allowlist_reach).
from typing import get_args
from pydantic_ai_harness.playwright import EgressPolicy, PlaywrightBrowser, RequestKind
# Nothing leaves for a host outside the list, whatever the request is for.
locked_down = EgressPolicy(
allowed_domains=['example.com'],
allowlist_reach=frozenset(get_args(RequestKind)),
)
browser = PlaywrightBrowser(policy=locked_down)
Đối với một quyết định, các trường không mô tả, phân lớp và ghi đè refuse. Đó là
đưa ra URL, loại của riêng Nhà viết kịch resource_type, phương pháp và liệu
yêu cầu là tài liệu riêng của khung chính:
from pydantic_ai_harness.playwright import EgressPolicy, EgressRequest
class FontsFromAnywhere(EgressPolicy):
def refuse(self, request: EgressRequest) -> str | None:
if request.resource_type == 'font':
return None
return super().refuse(request)
Trở về None cho phép yêu cầu; trả về một chuỗi từ chối nó và ghi lại
chuỗi đó làm lý do mà mô hình có thể đọc qua network_requests.
Ghi đè thu hẹp những gì refuse cho phép nên ghi đè describe quá:
describe là những gì mô hình được thông báo về phạm vi tiếp cận của nó và nó chỉ đọc
lĩnh vực.
Không có chính sách nào là ranh giới an ninh chung. Nhà viết kịch riêng của Microsoft-mcp từ chối bộ lọc nguồn gốc của nó theo cách tương tự. Một trang vẫn có thể báo hiệu ra bên ngoài thông qua loại yêu cầu mà danh sách cho phép không để lại -- URL hình ảnh hoặc tập lệnh chứa bất cứ điều gì trang đặt trong đó -- và tên máy chủ được phân loại theo câu trả lời này quá trình này được thực hiện, trong khi Chrome sẽ giải quyết lại trước khi kết nối, do đó, một bản ghi những thay đổi ở giữa (khởi động lại DNS) vẫn thắng. Điều đó và dựa trên proxy chế độ thực thi là chế độ đóng nó, được theo dõi trong #415.
Đối với các trường hợp đầu vào không đáng tin cậy, hãy chạy trình duyệt trong vùng chứa hoặc VM có tường lửa đi ra hoặc đặt trước nó bằng proxy và ghép nối nó với khai thác móc phê duyệt công cụ cho các hành động tiếp theo. Hãy coi đây là sự phòng thủ có chiều sâu, không phải là một sự đảm bảo.
Tham chiếu API
::: pydantic_ai_harness.playwright.PlaywrightBrowser