mirror of
https://github.com/tcsenpai/agenticSeek.git
synced 2025-06-05 02:25:27 +00:00
Merge pull request #132 from Fosowl/dev
Planner agent improvement + Frontend fix
This commit is contained in:
commit
e68dc2212c
35
README.md
35
README.md
@ -8,10 +8,25 @@ English | [中文](./README_CHS.md) | [繁體中文](./README_CHT.md) | [Franç
|
||||
# AgenticSeek: Private, Local Manus Alternative.
|
||||
|
||||
|
||||
*A **100% local alternative to Manus AI**, this voice-enabled AI assistant autonomously browses the web, writes code, and plans tasks while keeping all data on your device. Powered by advanced reasoning models like DeepSeek R1, it runs entirely on your hardware, ensuring complete privacy and zero cloud dependency.*
|
||||
*A **100% local alternative to Manus AI**, this voice-enabled AI assistant autonomously browses the web, writes code, and plans tasks while keeping all data on your device. Tailored for local reasoning models, it runs entirely on your hardware, ensuring complete privacy and zero cloud dependency.*
|
||||
|
||||
[](https://fosowl.github.io/agenticSeek.html)  [](https://discord.gg/m37d7XxZ) [](https://x.com/Martin993886460) [](https://github.com/Fosowl/agenticSeek/stargazers)
|
||||
|
||||
### Why AgenticSeek ?
|
||||
|
||||
* 🔒 Fully Local & Private - Everything runs on your machine — no cloud, no data sharing. Your files, conversations, and searches stay private.
|
||||
|
||||
* 🌐 Smart Web Browsing - AgenticSeek can browse the internet by itself — search, read, extract info, fill web form — all hands-free.
|
||||
|
||||
* 💻 Autonomous Coding Assistant - Need code? It can write, debug, and run programs in Python, C, Go, Java, and more — all without supervision.
|
||||
|
||||
* 🧠 Smart Agent Selection - You ask, it figures out the best agent for the job automatically. Like having a team of experts ready to help.
|
||||
|
||||
* 📋 Plans & Executes Complex Tasks - From trip planning to complex projects — it can split big tasks into steps and get things done using multiple AI agents.
|
||||
|
||||
* 🎙️ Voice-Enabled - Clean, fast, futuristic voice and speech to text allowing you to talk to it like it's your personal AI from a sci-fi movie
|
||||
|
||||
### **Demo**
|
||||
|
||||
> *Plan a 3 days solo trip to Budapest, find me a list of attractions and hostels, save everything in a CSV file*
|
||||
|
||||
@ -19,20 +34,6 @@ https://github.com/user-attachments/assets/4bd5faf6-459f-4f94-bd1d-238c4b331469
|
||||
|
||||
> 🛠️ **Work in Progress** – Looking for contributors!
|
||||
|
||||
### *Capabilities*
|
||||
|
||||
- **100% Local**: *No cloud, runs on your hardware. Your data stays yours.*
|
||||
|
||||
- **Autonomous Web Browsing**: *Autonomous web navigation.*
|
||||
|
||||
- **Autonomous Coding**: *Can write, debug, and run code in Python, C, Golang, Java...*
|
||||
|
||||
- **Agent routing**: *Automatically picks the right agent for the job.*
|
||||
|
||||
- **Planning**: *For complex tasks, spins up multiple agents to plan and execute.*
|
||||
|
||||
- **Memory**: *Efficient memory and sessions management.*
|
||||
|
||||
## Installation
|
||||
|
||||
Make sure you have chrome driver, docker and python3.10 (or newer) installed.
|
||||
@ -172,8 +173,6 @@ python3 cli.py
|
||||
|
||||
**Options 2:** Run with the Web interface.
|
||||
|
||||
Note: Currently we advice you run the CLI instead. Web interface is an active work in progress.
|
||||
|
||||
Start the backend.
|
||||
|
||||
```sh
|
||||
@ -182,8 +181,6 @@ python3 api.py
|
||||
|
||||
Go to `http://localhost:3000/` and you should see the web interface.
|
||||
|
||||
Please note that the Web interface doesn't stream messages at the moment.
|
||||
|
||||
---
|
||||
|
||||
## Usage
|
||||
|
2
api.py
2
api.py
@ -140,7 +140,7 @@ async def get_latest_answer():
|
||||
"answer": interaction.current_agent.last_answer,
|
||||
"agent_name": interaction.current_agent.agent_name if interaction.current_agent else "None",
|
||||
"success": interaction.current_agent.success,
|
||||
"blocks": {f'{i}': block.jsonify() for i, block in enumerate(interaction.current_agent.get_blocks_result())} if interaction.current_agent else {},
|
||||
"blocks": {f'{i}': block.jsonify() for i, block in enumerate(interaction.get_last_blocks_result())} if interaction.current_agent else {},
|
||||
"status": interaction.current_agent.get_status_message if interaction.current_agent else "No status available",
|
||||
"uid": uid
|
||||
}
|
||||
|
@ -63,6 +63,7 @@ You: Sure, here is the plan:
|
||||
|
||||
Rules:
|
||||
- Do not write code. You are a planning agent.
|
||||
- If you don't know of a concept, use a web agent.
|
||||
- Put your plan in a json with the key "plan".
|
||||
- Give clear, detailled order to each agent and how their task relate to the previous task (if any).
|
||||
- You might use a file agent before code agent to setup a project properly. specify folder name.
|
||||
@ -75,7 +76,6 @@ Rules:
|
||||
- The file agent can only conduct one action at the time. successive file agent could be needed.
|
||||
- Tell agent to execute without question.
|
||||
- Only use web agent for finding necessary informations.
|
||||
- If a task might require user email (eg: api services), do not write plan instead ask for user email.
|
||||
- Do not search for tutorial.
|
||||
- Make sure json is within ```json tag
|
||||
- One step, one agent.
|
@ -75,5 +75,4 @@ Rules:
|
||||
- The file agent can only conduct one action at the time. successive file agent could be needed.
|
||||
- Tell agent to execute without question.
|
||||
- Only use web agent for finding necessary informations.
|
||||
- If a task might require user email (eg: api services), do not write plan instead ask for user email.
|
||||
- Do not search for tutorial.
|
@ -159,6 +159,23 @@ class Agent():
|
||||
|
||||
def get_last_tool_type(self) -> str:
|
||||
return self.blocks_result[-1].tool_type if len(self.blocks_result) > 0 else None
|
||||
|
||||
def raw_answer_blocks(self, answer: str) -> str:
|
||||
"""
|
||||
Return the answer with all the blocks inserted, as text.
|
||||
"""
|
||||
if self.last_answer is None:
|
||||
return
|
||||
raw = ""
|
||||
lines = self.last_answer.split("\n")
|
||||
for line in lines:
|
||||
if "block:" in line:
|
||||
block_idx = int(line.split(":")[1])
|
||||
if block_idx < len(self.blocks_result):
|
||||
raw += self.blocks_result[block_idx].__str__()
|
||||
else:
|
||||
raw += line + "\n"
|
||||
return raw
|
||||
|
||||
def show_answer(self):
|
||||
"""
|
||||
@ -175,7 +192,6 @@ class Agent():
|
||||
self.blocks_result[block_idx].show()
|
||||
else:
|
||||
pretty_print(line, color="output")
|
||||
self.blocks_result = []
|
||||
|
||||
def remove_blocks(self, text: str) -> str:
|
||||
"""
|
||||
@ -197,6 +213,14 @@ class Agent():
|
||||
post_lines.append(f"block:{block_idx}")
|
||||
block_idx += 1
|
||||
return "\n".join(post_lines)
|
||||
|
||||
def show_block(self, block: str) -> None:
|
||||
"""
|
||||
Show the block in a pretty way.
|
||||
"""
|
||||
pretty_print('▂'*64, color="status")
|
||||
pretty_print(block, color="code")
|
||||
pretty_print('▂'*64, color="status")
|
||||
|
||||
def execute_modules(self, answer: str) -> Tuple[bool, str]:
|
||||
"""
|
||||
@ -215,6 +239,7 @@ class Agent():
|
||||
|
||||
if blocks != None:
|
||||
for block in blocks:
|
||||
self.show_block(block)
|
||||
output = tool.execute([block])
|
||||
feedback = tool.interpreter_feedback(output) # tool interpreter feedback
|
||||
success = not tool.execution_failure_check(output)
|
||||
@ -226,5 +251,4 @@ class Agent():
|
||||
self.memory.push('user', feedback)
|
||||
if save_path != None:
|
||||
tool.save_block(blocks, save_path)
|
||||
self.blocks_result = self.blocks_result
|
||||
return True, feedback
|
||||
|
@ -56,6 +56,7 @@ class CoderAgent(Agent):
|
||||
self.last_answer = answer
|
||||
await asyncio.sleep(0)
|
||||
break
|
||||
self.show_answer()
|
||||
animate_thinking("Executing code...", color="status")
|
||||
self.status_message = "Executing code..."
|
||||
exec_success, _ = self.execute_modules(answer)
|
||||
@ -67,7 +68,6 @@ class CoderAgent(Agent):
|
||||
pretty_print("Execution failure", color="failure")
|
||||
pretty_print("Correcting code...", color="status")
|
||||
self.status_message = "Correcting code..."
|
||||
self.show_answer()
|
||||
attempt += 1
|
||||
self.status_message = "Ready"
|
||||
if attempt == max_attempts:
|
||||
|
@ -69,6 +69,9 @@ class PlannerAgent(Agent):
|
||||
line_json = json.loads(block)
|
||||
if 'plan' in line_json:
|
||||
for task in line_json['plan']:
|
||||
if task['agent'].lower() not in [ag_name.lower() for ag_name in self.agents.keys()]:
|
||||
pretty_print(f"Agent {task['agent']} does not exist.", color="warning")
|
||||
return []
|
||||
agent = {
|
||||
'agent': task['agent'],
|
||||
'id': task['id'],
|
||||
@ -139,7 +142,7 @@ class PlannerAgent(Agent):
|
||||
return []
|
||||
agents_tasks = self.parse_agent_tasks(answer)
|
||||
if agents_tasks == []:
|
||||
prompt = f"Failed to parse the tasks. Please make a plan within ```json.\n"
|
||||
prompt = f"Failed to parse the tasks. Please make a plan within ```json. Do not ask for clarification.\n"
|
||||
pretty_print("Failed to make plan. Retrying...", color="warning")
|
||||
continue
|
||||
self.show_plan(agents_tasks, answer)
|
||||
@ -160,7 +163,10 @@ class PlannerAgent(Agent):
|
||||
last_agent_work = agents_work_result[id]
|
||||
tool_success_str = "success" if success else "failure"
|
||||
pretty_print(f"Agent {id} work {tool_success_str}.", color="success" if success else "failure")
|
||||
next_task = agents_tasks[int(id)][0]
|
||||
if int(id) == len(agents_tasks):
|
||||
next_task = "No task follow, this was the last step. If it failed add a task to recover."
|
||||
else:
|
||||
next_task = f"Next task is: {agents_tasks[int(id)][0]}."
|
||||
#if success:
|
||||
# return agents_tasks # we only update the plan if last task failed, for now
|
||||
update_prompt = f"""
|
||||
@ -169,10 +175,11 @@ class PlannerAgent(Agent):
|
||||
The last agent working on task: {id}, did the following work:
|
||||
{last_agent_work}
|
||||
Agent {id} work was a {tool_success_str} according to system interpreter.
|
||||
The agent {id} about to work on task: {next_task}
|
||||
{next_task}
|
||||
Is the work done for task {id} leading to sucess or failure ? Did an agent fail with a task?
|
||||
If agent work was good: answer "NO_UPDATE"
|
||||
If agent work is leading to failure: update the plan.
|
||||
If a task failed add a task to try again or recover from failure. You might have near identical task twice.
|
||||
plan should be within ```json like before.
|
||||
You need to rewrite the whole plan, but only change the tasks after task {id}.
|
||||
Keep the plan as short as the original one if possible. Do not change past tasks.
|
||||
@ -196,10 +203,15 @@ class PlannerAgent(Agent):
|
||||
self.status_message = f"Starting task {task['task']}..."
|
||||
agent_prompt = self.make_prompt(task['task'], required_infos)
|
||||
pretty_print(f"Agent {task['agent']} started working...", color="status")
|
||||
agent_answer, _ = await self.agents[task['agent'].lower()].process(agent_prompt, None)
|
||||
answer, _ = await self.agents[task['agent'].lower()].process(agent_prompt, None)
|
||||
self.last_answer = answer
|
||||
self.blocks_result = self.agents[task['agent'].lower()].blocks_result
|
||||
agent_answer = self.agents[task['agent'].lower()].raw_answer_blocks(answer)
|
||||
success = self.agents[task['agent'].lower()].get_success
|
||||
self.agents[task['agent'].lower()].show_answer()
|
||||
pretty_print(f"Agent {task['agent']} completed task.", color="status")
|
||||
# TODO ajouter feedback / agent et code executer
|
||||
agent_answer += "\nAgent succeeded with task." if success else "\nAgent failed with task (Error detected)."
|
||||
return agent_answer, success
|
||||
|
||||
def get_work_result_agent(self, task_needs, agents_work_result):
|
||||
@ -215,6 +227,7 @@ class PlannerAgent(Agent):
|
||||
Tuple[str, str]: The result of the agent process and empty reasoning string.
|
||||
"""
|
||||
agents_tasks = []
|
||||
required_infos = None
|
||||
agents_work_result = dict()
|
||||
|
||||
self.status_message = "Making a plan..."
|
||||
@ -234,14 +247,12 @@ class PlannerAgent(Agent):
|
||||
if agents_work_result is not None:
|
||||
required_infos = self.get_work_result_agent(task['need'], agents_work_result)
|
||||
try:
|
||||
self.last_answer, success = await self.start_agent_process(task, required_infos)
|
||||
answer, success = await self.start_agent_process(task, required_infos)
|
||||
except Exception as e:
|
||||
raise e
|
||||
agents_work_result[task['id']] = self.last_answer
|
||||
if i == steps - 1:
|
||||
break
|
||||
agents_work_result[task['id']] = answer
|
||||
agents_tasks = await self.update_plan(goal, agents_tasks, agents_work_result, task['id'], success)
|
||||
steps = len(agents_tasks)
|
||||
i += 1
|
||||
|
||||
return self.last_answer, ""
|
||||
return answer, ""
|
@ -176,7 +176,6 @@ class Browser:
|
||||
)
|
||||
except TimeoutException:
|
||||
self.logger.warning("Timeout while waiting for page to bypass 'checking your browser'")
|
||||
return False
|
||||
self.apply_web_safety()
|
||||
self.logger.log(f"Navigated to: {url}")
|
||||
return True
|
||||
|
@ -69,6 +69,15 @@ class Interaction:
|
||||
break
|
||||
return ai_name
|
||||
|
||||
def get_last_blocks_result(self) -> List[Dict]:
|
||||
"""Get the last blocks result."""
|
||||
if self.current_agent is None:
|
||||
return []
|
||||
blks = []
|
||||
for agent in self.agents:
|
||||
blks.extend(agent.get_blocks_result())
|
||||
return blks
|
||||
|
||||
def load_last_session(self):
|
||||
"""Recover the last session."""
|
||||
for agent in self.agents:
|
||||
|
@ -29,6 +29,7 @@ class Provider:
|
||||
"openai": self.openai_fn,
|
||||
"lm-studio": self.lm_studio_fn,
|
||||
"huggingface": self.huggingface_fn,
|
||||
"google": self.google_fn,
|
||||
"deepseek": self.deepseek_fn,
|
||||
"together": self.together_fn,
|
||||
"dsk_deepseek": self.dsk_deepseek,
|
||||
@ -36,10 +37,10 @@ class Provider:
|
||||
}
|
||||
self.logger = Logger("provider.log")
|
||||
self.api_key = None
|
||||
self.unsafe_providers = ["openai", "deepseek", "dsk_deepseek", "together"]
|
||||
self.unsafe_providers = ["openai", "deepseek", "dsk_deepseek", "together", "google"]
|
||||
if self.provider_name not in self.available_providers:
|
||||
raise ValueError(f"Unknown provider: {provider_name}")
|
||||
if self.provider_name in self.unsafe_providers:
|
||||
if self.provider_name in self.unsafe_providers and self.is_local == False:
|
||||
pretty_print("Warning: you are using an API provider. You data will be sent to the cloud.", color="warning")
|
||||
self.api_key = self.get_api_key(self.provider_name)
|
||||
elif self.provider_name != "ollama":
|
||||
@ -140,7 +141,6 @@ class Provider:
|
||||
raise e
|
||||
return thought
|
||||
|
||||
|
||||
def ollama_fn(self, history, verbose = False):
|
||||
"""
|
||||
Use local ollama server to generate text.
|
||||
@ -206,6 +206,29 @@ class Provider:
|
||||
return thought
|
||||
except Exception as e:
|
||||
raise Exception(f"OpenAI API error: {str(e)}") from e
|
||||
|
||||
def google_fn(self, history, verbose=False):
|
||||
"""
|
||||
Use google gemini to generate text.
|
||||
"""
|
||||
base_url = self.server_ip
|
||||
if self.is_local:
|
||||
raise Exception("Google Gemini is not available for local use.")
|
||||
|
||||
client = OpenAI(api_key=self.api_key, base_url="https://generativelanguage.googleapis.com/v1beta/openai/")
|
||||
try:
|
||||
response = client.chat.completions.create(
|
||||
model=self.model,
|
||||
messages=history,
|
||||
)
|
||||
if response is None:
|
||||
raise Exception("Google response is empty.")
|
||||
thought = response.choices[0].message.content
|
||||
if verbose:
|
||||
print(thought)
|
||||
return thought
|
||||
except Exception as e:
|
||||
raise Exception(f"GOOGLE API error: {str(e)}") from e
|
||||
|
||||
def together_fn(self, history, verbose=False):
|
||||
"""
|
||||
|
@ -68,9 +68,8 @@ class executorResult:
|
||||
"success": self.success,
|
||||
"tool_type": self.tool_type
|
||||
}
|
||||
|
||||
|
||||
def show(self):
|
||||
pretty_print('▂'*64, color="status")
|
||||
pretty_print(self.block, color="code" if self.success else "failure")
|
||||
pretty_print('▂'*64, color="status")
|
||||
pretty_print(self.feedback, color="success" if self.success else "failure")
|
||||
pretty_print(self.feedback, color="success" if self.success else "failure")
|
||||
pretty_print('▂'*64, color="status")
|
Loading…
x
Reference in New Issue
Block a user