mirror of
https://github.com/browser-use/browser-use.git
synced 2026-09-14 19:59:47 +08:00
1c70a9dbc5
- Fix basics.mdx: change browser: Browser to browser_session: BrowserSession - Fix basics.mdx: use async functions and proper ActionResult return type - Add Warning about parameter name matching in basics.mdx - Add Warning and Common Pitfalls section in add.mdx with clear examples - Update AGENTS.md embedded docs to match The agent injects special parameters by name matching, not by type. Using incorrect parameter names (e.g., browser instead of browser_session) causes tools to fail silently, which was a common user confusion. Co-authored-by: mailmertunsal <mailmertunsal@gmail.com>
175 lines
5.6 KiB
Plaintext
175 lines
5.6 KiB
Plaintext
---
|
|
title: "Add Tools"
|
|
description: ""
|
|
icon: "plus"
|
|
mode: "wide"
|
|
---
|
|
|
|
|
|
Examples:
|
|
- deterministic clicks
|
|
- file handling
|
|
- calling APIs
|
|
- human-in-the-loop
|
|
- browser interactions
|
|
- calling LLMs
|
|
- get 2fa codes
|
|
- send emails
|
|
- Playwright integration (see [GitHub example](https://github.com/browser-use/browser-use/blob/main/examples/browser/playwright_integration.py))
|
|
- ...
|
|
|
|
Simply add `@tools.action(...)` to your function.
|
|
|
|
```python
|
|
from browser_use import Tools, Agent, ActionResult
|
|
|
|
tools = Tools()
|
|
|
|
@tools.action(description='Ask human for help with a question')
|
|
async def ask_human(question: str) -> ActionResult:
|
|
answer = input(f'{question} > ')
|
|
return ActionResult(extracted_content=f'The human responded with: {answer}')
|
|
```
|
|
|
|
```python
|
|
agent = Agent(task='...', llm=llm, tools=tools)
|
|
```
|
|
|
|
- **`description`** *(required)* - What the tool does, the LLM uses this to decide when to call it.
|
|
- **`allowed_domains`** - List of domains where tool can run (e.g. `['*.example.com']`), defaults to all domains
|
|
|
|
The Agent fills your function parameters based on their names, type hints, & defaults.
|
|
|
|
<Warning>
|
|
**Common Pitfall**: Parameter names must match exactly! Use `browser_session: BrowserSession` (not `browser: Browser`).
|
|
The agent injects special parameters by **name matching**, so using incorrect names will cause your tool to fail silently.
|
|
See [Available Objects](#available-objects) below for the correct parameter names.
|
|
</Warning>
|
|
|
|
|
|
## Available Objects
|
|
|
|
Your function has access to these objects:
|
|
|
|
- **`browser_session: BrowserSession`** - Current browser session for CDP access
|
|
- **`cdp_client`** - Direct Chrome DevTools Protocol client
|
|
- **`page_extraction_llm: BaseChatModel`** - The LLM you pass into agent. This can be used to do a custom llm call here.
|
|
- **`file_system: FileSystem`** - File system access
|
|
- **`available_file_paths: list[str]`** - Available files for upload/processing
|
|
- **`has_sensitive_data: bool`** - Whether action contains sensitive data
|
|
|
|
|
|
## Browser Interaction Examples
|
|
|
|
You can use `browser_session` to directly interact with page elements using CSS selectors:
|
|
|
|
```python
|
|
from browser_use import Tools, Agent, ActionResult, BrowserSession
|
|
|
|
tools = Tools()
|
|
|
|
@tools.action(description='Click the submit button using CSS selector')
|
|
async def click_submit_button(browser_session: BrowserSession):
|
|
# Get the current page
|
|
page = await browser_session.must_get_current_page()
|
|
|
|
# Get element(s) by CSS selector
|
|
elements = await page.get_elements_by_css_selector('button[type="submit"]')
|
|
|
|
if not elements:
|
|
return ActionResult(extracted_content='No submit button found')
|
|
|
|
# Click the first matching element
|
|
await elements[0].click()
|
|
|
|
return ActionResult(extracted_content='Submit button clicked!')
|
|
```
|
|
|
|
|
|
Available methods on `Page`:
|
|
- `get_elements_by_css_selector(selector: str)` - Returns list of matching elements
|
|
- `get_element_by_prompt(prompt: str, llm)` - Returns element or None using LLM
|
|
- `must_get_element_by_prompt(prompt: str, llm)` - Returns element or raises error
|
|
|
|
Available methods on `Element`:
|
|
- `click()` - Click the element
|
|
- `type(text: str)` - Type text into the element
|
|
- `get_text()` - Get element text content
|
|
- See `browser_use/actor/element.py` for more methods
|
|
|
|
## Pydantic Input
|
|
|
|
You can use Pydantic for the tool parameters:
|
|
|
|
```python
|
|
from pydantic import BaseModel
|
|
|
|
class Cars(BaseModel):
|
|
name: str = Field(description='The name of the car, e.g. "Toyota Camry"')
|
|
price: int = Field(description='The price of the car as int in USD, e.g. 25000')
|
|
|
|
@tools.action(description='Save cars to file')
|
|
def save_cars(cars: list[Cars]) -> str:
|
|
with open('cars.json', 'w') as f:
|
|
json.dump(cars, f)
|
|
return f'Saved {len(cars)} cars to file'
|
|
|
|
task = "find cars and save them to file"
|
|
```
|
|
## Domain Restrictions
|
|
|
|
Limit tools to specific domains:
|
|
|
|
```python
|
|
@tools.action(
|
|
description='Fill out banking forms',
|
|
allowed_domains=['https://mybank.com']
|
|
)
|
|
def fill_bank_form(account_number: str) -> str:
|
|
# Only works on mybank.com
|
|
return f'Filled form for account {account_number}'
|
|
```
|
|
|
|
## Advanced Example
|
|
|
|
For a comprehensive example of custom tools with Playwright integration, see:
|
|
**[Playwright Integration Example](https://github.com/browser-use/browser-use/blob/main/examples/browser/playwright_integration.py)**
|
|
|
|
This shows how to create custom actions that use Playwright's precise browser automation alongside Browser-Use.
|
|
|
|
## Common Pitfalls
|
|
|
|
<Warning>
|
|
The agent injects special parameters **by name**, not by type. Using incorrect parameter names is the most common cause of tools failing silently.
|
|
</Warning>
|
|
|
|
### ❌ Wrong: Using `browser: Browser`
|
|
|
|
```python
|
|
from browser_use import Tools, ActionResult, Browser
|
|
|
|
@tools.action('My action')
|
|
def my_action(browser: Browser) -> ActionResult: # WRONG!
|
|
# This will NOT receive the browser session
|
|
pass
|
|
```
|
|
|
|
### ✅ Correct: Using `browser_session: BrowserSession`
|
|
|
|
```python
|
|
from browser_use import Tools, ActionResult, BrowserSession
|
|
|
|
@tools.action('My action')
|
|
async def my_action(browser_session: BrowserSession) -> ActionResult: # CORRECT!
|
|
page = await browser_session.must_get_current_page()
|
|
# Now you have access to the browser
|
|
return ActionResult(extracted_content='Done')
|
|
```
|
|
|
|
### Key Points
|
|
|
|
1. **Use `browser_session: BrowserSession`** - not `browser: Browser`
|
|
2. **Use `async` functions** - recommended for consistency with browser operations
|
|
3. **Return `ActionResult`** - not plain strings (though strings work, `ActionResult` provides more control)
|
|
4. **Parameter names must match exactly** - see [Available Objects](#available-objects) for the full list of injectable parameters
|