mirror of
https://github.com/tcsenpai/agenticSeek.git
synced 2025-06-06 11:05:26 +00:00
feat : enhance browser agent + add tests
This commit is contained in:
parent
6fb9ce67c0
commit
42f9485a39
@ -54,7 +54,7 @@ class BrowserAgent(Agent):
|
|||||||
links_clean = []
|
links_clean = []
|
||||||
for link in links:
|
for link in links:
|
||||||
link = link.strip()
|
link = link.strip()
|
||||||
if link[-1] == '.':
|
if not (link[-1].isalpha() or link[-1].isdigit()):
|
||||||
links_clean.append(link[:-1])
|
links_clean.append(link[:-1])
|
||||||
else:
|
else:
|
||||||
links_clean.append(link)
|
links_clean.append(link)
|
||||||
@ -71,7 +71,7 @@ class BrowserAgent(Agent):
|
|||||||
Your goal is to find accurate and complete information to satisfy the user’s request.
|
Your goal is to find accurate and complete information to satisfy the user’s request.
|
||||||
User request: {user_prompt}
|
User request: {user_prompt}
|
||||||
To proceed, choose a relevant link from the search results. Announce your choice by saying: "I want to navigate to <link>"
|
To proceed, choose a relevant link from the search results. Announce your choice by saying: "I want to navigate to <link>"
|
||||||
Do not explain your choice.
|
Do not eplain your choice.
|
||||||
"""
|
"""
|
||||||
|
|
||||||
def make_navigation_prompt(self, user_prompt: str, page_text: str) -> str:
|
def make_navigation_prompt(self, user_prompt: str, page_text: str) -> str:
|
||||||
@ -120,22 +120,27 @@ class BrowserAgent(Agent):
|
|||||||
|
|
||||||
Example 1 (useful page, no need go futher):
|
Example 1 (useful page, no need go futher):
|
||||||
Note: According to karpathy site (<link>) LeCun net is ...<expand on page content>..."
|
Note: According to karpathy site (<link>) LeCun net is ...<expand on page content>..."
|
||||||
No link seem useful to provide futher information. GO_BACK
|
No link seem useful to provide futher information.
|
||||||
|
Action: GO_BACK
|
||||||
|
|
||||||
Example 2 (not useful, see useful link on page):
|
Example 2 (not useful, see useful link on page):
|
||||||
Error: reddit.com/welcome does not discuss anything related to the user’s query.
|
Error: reddit.com/welcome does not discuss anything related to the user’s query.
|
||||||
There is a link that could lead to the information, I want to navigate to http://reddit.com/r/locallama
|
There is a link that could lead to the information.
|
||||||
|
Action: navigate to http://reddit.com/r/locallama
|
||||||
|
|
||||||
Example 3 (not useful, no related links):
|
Example 3 (not useful, no related links):
|
||||||
Error: x.com does not discuss anything related to the user’s query and no navigation link are usefull.
|
Error: x.com does not discuss anything related to the user’s query and no navigation link are usefull.
|
||||||
GO_BACK
|
Action: GO_BACK
|
||||||
|
|
||||||
Example 3 (query answer found, enought notes taken):
|
Example 3 (query answer found, enought notes taken):
|
||||||
Note: I found on <link> that ...<expand on information found>...
|
Note: I found on <link> that ...<expand on information found>...
|
||||||
Given this answer the user query I should exit the web browser. REQUEST_EXIT
|
Given this answer the user query I should exit the web browser.
|
||||||
|
Action: REQUEST_EXIT
|
||||||
|
|
||||||
Example 4 (loging form visible):
|
Example 4 (loging form visible):
|
||||||
|
|
||||||
Note: I am on the login page, I will type the given username and password.
|
Note: I am on the login page, I will type the given username and password.
|
||||||
|
Action:
|
||||||
[username_field](David)
|
[username_field](David)
|
||||||
[password_field](edgerunners77)
|
[password_field](edgerunners77)
|
||||||
|
|
||||||
@ -143,7 +148,7 @@ class BrowserAgent(Agent):
|
|||||||
{user_prompt}
|
{user_prompt}
|
||||||
You previously took these notes:
|
You previously took these notes:
|
||||||
{notes}
|
{notes}
|
||||||
Do not Step-by-Step explanation. Instead write simple explanation sentence following by your notes and actions.
|
Do not Step-by-Step explanation. Write Notes or Error as a long paragraph followed by your action.
|
||||||
"""
|
"""
|
||||||
|
|
||||||
def llm_decide(self, prompt: str, show_reasoning: bool = False) -> Tuple[str, str]:
|
def llm_decide(self, prompt: str, show_reasoning: bool = False) -> Tuple[str, str]:
|
||||||
@ -190,7 +195,7 @@ class BrowserAgent(Agent):
|
|||||||
buffer = []
|
buffer = []
|
||||||
links = []
|
links = []
|
||||||
for line in lines:
|
for line in lines:
|
||||||
if "exit" in line:
|
if line == '' or 'action:' in line.lower():
|
||||||
saving = False
|
saving = False
|
||||||
if "note" in line.lower():
|
if "note" in line.lower():
|
||||||
saving = True
|
saving = True
|
||||||
@ -198,7 +203,7 @@ class BrowserAgent(Agent):
|
|||||||
buffer.append(line)
|
buffer.append(line)
|
||||||
else:
|
else:
|
||||||
links.extend(self.extract_links(line))
|
links.extend(self.extract_links(line))
|
||||||
self.notes.append('. '.join(buffer))
|
self.notes.append('. '.join(buffer).strip())
|
||||||
return links
|
return links
|
||||||
|
|
||||||
def select_link(self, links: List[str]) -> str | None:
|
def select_link(self, links: List[str]) -> str | None:
|
||||||
@ -293,6 +298,7 @@ class BrowserAgent(Agent):
|
|||||||
|
|
||||||
extracted_form = self.extract_form(answer)
|
extracted_form = self.extract_form(answer)
|
||||||
if len(extracted_form) > 0:
|
if len(extracted_form) > 0:
|
||||||
|
pretty_print(f"Filling inputs form...", color="status")
|
||||||
self.browser.fill_form_inputs(extracted_form)
|
self.browser.fill_form_inputs(extracted_form)
|
||||||
self.browser.find_and_click_submission()
|
self.browser.find_and_click_submission()
|
||||||
page_text = self.browser.get_text()
|
page_text = self.browser.get_text()
|
||||||
@ -303,22 +309,25 @@ class BrowserAgent(Agent):
|
|||||||
link = self.select_link(links)
|
link = self.select_link(links)
|
||||||
|
|
||||||
if "REQUEST_EXIT" in answer:
|
if "REQUEST_EXIT" in answer:
|
||||||
|
pretty_print(f"Agent requested exit.", color="status")
|
||||||
complete = True
|
complete = True
|
||||||
break
|
break
|
||||||
|
|
||||||
if len(unvisited) == 0:
|
if len(unvisited) == 0:
|
||||||
|
pretty_print(f"Visited all links.", color="status")
|
||||||
break
|
break
|
||||||
|
|
||||||
if "FORM_FILLED" in answer:
|
if "FORM_FILLED" in answer:
|
||||||
|
pretty_print(f"Filled form. Handling page update.", color="status")
|
||||||
page_text = self.browser.get_text()
|
page_text = self.browser.get_text()
|
||||||
self.navigable_links = self.browser.get_navigable()
|
self.navigable_links = self.browser.get_navigable()
|
||||||
prompt = self.make_navigation_prompt(user_prompt, page_text)
|
prompt = self.make_navigation_prompt(user_prompt, page_text)
|
||||||
continue
|
continue
|
||||||
|
|
||||||
if link == None or "GO_BACK" in answer:
|
if link == None or "GO_BACK" in answer:
|
||||||
|
pretty_print(f"Going back to results. Still {len(unvisited)}", color="status")
|
||||||
unvisited = self.select_unvisited(search_result)
|
unvisited = self.select_unvisited(search_result)
|
||||||
prompt = self.make_newsearch_prompt(user_prompt, unvisited)
|
prompt = self.make_newsearch_prompt(user_prompt, unvisited)
|
||||||
pretty_print(f"Going back to results. Still {len(unvisited)}", color="warning")
|
|
||||||
continue
|
continue
|
||||||
|
|
||||||
animate_thinking(f"Navigating to {link}", color="status")
|
animate_thinking(f"Navigating to {link}", color="status")
|
||||||
@ -329,8 +338,8 @@ class BrowserAgent(Agent):
|
|||||||
page_text = self.browser.get_text()
|
page_text = self.browser.get_text()
|
||||||
self.navigable_links = self.browser.get_navigable()
|
self.navigable_links = self.browser.get_navigable()
|
||||||
prompt = self.make_navigation_prompt(user_prompt, page_text)
|
prompt = self.make_navigation_prompt(user_prompt, page_text)
|
||||||
pretty_print(f"Current page: {self.current_page}", color="warning")
|
|
||||||
|
|
||||||
|
pretty_print("Exited navigation, starting to summarize finding...", color="status")
|
||||||
prompt = self.conclude_prompt(user_prompt)
|
prompt = self.conclude_prompt(user_prompt)
|
||||||
mem_last_idx = self.memory.push('user', prompt)
|
mem_last_idx = self.memory.push('user', prompt)
|
||||||
answer, reasoning = self.llm_request()
|
answer, reasoning = self.llm_request()
|
||||||
|
@ -54,7 +54,7 @@ class searxSearch(Tools):
|
|||||||
status = self.link_valid(link)
|
status = self.link_valid(link)
|
||||||
statuses.append(status)
|
statuses.append(status)
|
||||||
return statuses
|
return statuses
|
||||||
|
|
||||||
def execute(self, blocks: list, safety: bool = False) -> str:
|
def execute(self, blocks: list, safety: bool = False) -> str:
|
||||||
"""Executes a search query against a SearxNG instance using POST and extracts URLs and titles."""
|
"""Executes a search query against a SearxNG instance using POST and extracts URLs and titles."""
|
||||||
if not blocks:
|
if not blocks:
|
||||||
|
@ -2,11 +2,13 @@
|
|||||||
"""
|
"""
|
||||||
define a generic tool class, any tool can be used by the agent.
|
define a generic tool class, any tool can be used by the agent.
|
||||||
|
|
||||||
A tool can be used by deepseek like so:
|
A tool can be used by a llm like so:
|
||||||
```<tool name>
|
```<tool name>
|
||||||
<code or query to execute>
|
<code or query to execute>
|
||||||
```
|
```
|
||||||
|
|
||||||
|
we call these "blocks".
|
||||||
|
|
||||||
For example:
|
For example:
|
||||||
```python
|
```python
|
||||||
print("Hello world")
|
print("Hello world")
|
||||||
@ -40,9 +42,7 @@ class Tools():
|
|||||||
return self.current_dir
|
return self.current_dir
|
||||||
|
|
||||||
def check_config_dir_validity(self):
|
def check_config_dir_validity(self):
|
||||||
"""
|
"""Check if the config directory is valid."""
|
||||||
Check if the config directory is valid.
|
|
||||||
"""
|
|
||||||
path = self.config['MAIN']['work_dir']
|
path = self.config['MAIN']['work_dir']
|
||||||
if path == "":
|
if path == "":
|
||||||
print("WARNING: Work directory not set in config.ini")
|
print("WARNING: Work directory not set in config.ini")
|
||||||
@ -56,15 +56,11 @@ class Tools():
|
|||||||
return True
|
return True
|
||||||
|
|
||||||
def config_exists(self):
|
def config_exists(self):
|
||||||
"""
|
"""Check if the config file exists."""
|
||||||
Check if the config file exists.
|
|
||||||
"""
|
|
||||||
return os.path.exists('./config.ini')
|
return os.path.exists('./config.ini')
|
||||||
|
|
||||||
def create_work_dir(self):
|
def create_work_dir(self):
|
||||||
"""
|
"""Create the work directory if it does not exist."""
|
||||||
Create the work directory if it does not exist.
|
|
||||||
"""
|
|
||||||
default_path = os.path.dirname(os.getcwd())
|
default_path = os.path.dirname(os.getcwd())
|
||||||
if self.config_exists():
|
if self.config_exists():
|
||||||
self.config.read('./config.ini')
|
self.config.read('./config.ini')
|
||||||
|
69
tests/test_browser_agent_parsing.py
Normal file
69
tests/test_browser_agent_parsing.py
Normal file
@ -0,0 +1,69 @@
|
|||||||
|
import unittest
|
||||||
|
import os
|
||||||
|
import sys
|
||||||
|
sys.path.insert(0, os.path.abspath(os.path.join(os.path.dirname(__file__), '..'))) # Add project root to Python path
|
||||||
|
from sources.agents.browser_agent import BrowserAgent
|
||||||
|
|
||||||
|
class TestBrowserAgentParsing(unittest.TestCase):
|
||||||
|
def setUp(self):
|
||||||
|
# Initialize a basic BrowserAgent instance for testing
|
||||||
|
self.agent = BrowserAgent(
|
||||||
|
name="TestAgent",
|
||||||
|
prompt_path="../prompts/base/browser_agent.txt",
|
||||||
|
provider=None
|
||||||
|
)
|
||||||
|
|
||||||
|
def test_extract_links(self):
|
||||||
|
# Test various link formats
|
||||||
|
test_text = """
|
||||||
|
Check this out: https://example.com, and www.google.com!
|
||||||
|
Also try https://test.org/about?page=1.
|
||||||
|
"""
|
||||||
|
expected = [
|
||||||
|
"https://example.com",
|
||||||
|
"www.google.com",
|
||||||
|
"https://test.org/about?page=1"
|
||||||
|
]
|
||||||
|
result = self.agent.extract_links(test_text)
|
||||||
|
self.assertEqual(result, expected)
|
||||||
|
|
||||||
|
def test_extract_form(self):
|
||||||
|
# Test form extraction
|
||||||
|
test_text = """
|
||||||
|
Fill this: [username](john) and [password](secret123)
|
||||||
|
Not a form: [random]text
|
||||||
|
"""
|
||||||
|
expected = ["[username](john)", "[password](secret123)"]
|
||||||
|
result = self.agent.extract_form(test_text)
|
||||||
|
self.assertEqual(result, expected)
|
||||||
|
|
||||||
|
def test_clean_links(self):
|
||||||
|
# Test link cleaning
|
||||||
|
test_links = [
|
||||||
|
"https://example.com.",
|
||||||
|
"www.test.com,",
|
||||||
|
"https://clean.org!",
|
||||||
|
"https://good.com"
|
||||||
|
]
|
||||||
|
expected = [
|
||||||
|
"https://example.com",
|
||||||
|
"www.test.com",
|
||||||
|
"https://clean.org",
|
||||||
|
"https://good.com"
|
||||||
|
]
|
||||||
|
result = self.agent.clean_links(test_links)
|
||||||
|
self.assertEqual(result, expected)
|
||||||
|
|
||||||
|
def test_parse_answer(self):
|
||||||
|
# Test parsing answer with notes and links
|
||||||
|
test_text = """
|
||||||
|
Here's some info
|
||||||
|
Note: This is important. We are doing test it's very cool.
|
||||||
|
action:
|
||||||
|
i wanna navigate to https://test.com
|
||||||
|
"""
|
||||||
|
self.agent.parse_answer(test_text)
|
||||||
|
self.assertEqual(self.agent.notes[0], "Note: This is important. We are doing test it's very cool.")
|
||||||
|
|
||||||
|
if __name__ == "__main__":
|
||||||
|
unittest.main()
|
Loading…
x
Reference in New Issue
Block a user