Harness Academy
Tài liệu gốc 11 22 phútTiếng Việt + English
docs/playwright.mdXem bản gốc
Ngôn ngữ bài đọc
Bản dịch tiếng Việt

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.

Nguồn

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ọnMặc địnhMục đích
headlessTrueChạy Chrome mà không có cửa sổ hiển thị (phù hợp với máy chủ và CI).
allowed_domainsNoneDanh 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).
policyNoneđầ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_addressesTrueTừ 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_navigateFalseĐính kèm ảnh chụp màn hình cho mọi navigate kết quả.
max_content_tokens4000Ngân sách token gần đúng cho mỗi kết quả công cụ văn bản.
action_timeout_ms5000Thờ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_ms60000Thờ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_sandboxTrueChạ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_chromiumFalseTự động tìm nạp Chrome khi thiếu tệp nhị phân.
storage_stateNoneTrạ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_urlNoneĐí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_text khô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_for xem trang và mọi khung hình trẻ em cùng một lúc; trận đấu đầu tiên thắng.
  • snapshot bao 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ừ (f1e4 thay vì e4). Chuyển một giới thiệu như vậy đến click, type_text, hover hoặc get_text giả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 theo browser.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_messages và 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ất user: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_ms mặ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 snapshot giớ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ầuBị giới hạn bởi allowed_domains
Điều hướng cấp cao nhấtvâng
fetch, XHR, Nguồn sự kiện, WebSocket, sendBeaconvâng
Hình ảnh, bảng định kiểu, tập lệnh, phông chữ, phương tiệnkhông
Tài liệu khung conkhô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

Mục lục nguồn