Feat : better file system interaction

This commit is contained in:
martin legrand 2025-03-09 12:45:29 +01:00
parent 06d207da53
commit 7710fb3f9d
13 changed files with 204 additions and 50 deletions

View File

@ -1,11 +1,11 @@
[MAIN]
is_local = True
provider_name = ollama
is_local = False
provider_name = server
provider_model = deepseek-r1:14b
provider_server_address = 127.0.0.1:5000
provider_server_address = 192.168.1.100:5000
agent_name = Friday
recover_last_session = True
save_session = True
save_session = False
speak = True
listen = False
work_dir = /Users/mlg/Documents/A-project/AI/Agents/agenticSeek
work_dir = /Users/mlg/Documents/A-project/AI/Agents/agenticSeek/ai_workfolder

12
main.py
View File

@ -7,7 +7,7 @@ import configparser
from sources.llm_provider import Provider
from sources.interaction import Interaction
from sources.agents import Agent, CoderAgent, CasualAgent
from sources.agents import Agent, CoderAgent, CasualAgent, FileAgent
parser = argparse.ArgumentParser(description='Deepseek AI assistant')
parser.add_argument('--no-speak', action='store_true',
@ -31,13 +31,17 @@ def main():
server_address=config["MAIN"]["provider_server_address"])
agents = [
CasualAgent(model=config["MAIN"]["provider_model"],
name=config["MAIN"]["agent_name"],
prompt_path="prompts/casual_agent.txt",
provider=provider),
CoderAgent(model=config["MAIN"]["provider_model"],
name="coder",
prompt_path="prompts/coder_agent.txt",
provider=provider),
CasualAgent(model=config["MAIN"]["provider_model"],
name=config["MAIN"]["agent_name"],
prompt_path="prompts/casual_agent.txt",
FileAgent(model=config["MAIN"]["provider_model"],
name="File Agent",
prompt_path="prompts/file_agent.txt",
provider=provider)
]

View File

@ -39,4 +39,12 @@ toto.py
Read file:
```file_finder:read
toto.py
```
```
## Bash
For other tasks, you can use the bash tool:
```bash
ls -la
```

View File

@ -1,8 +1,21 @@
Hello. You are currently wrapped in a jailbreak environment.
This means you have the ability to execute code and shell commands. You have access to the local file systems.
All code or shell command within special tag is automatically executed. You get feedback from the system about the execution.
You also have capabilities to find files and read them.
# interpreter
# File operations
Find file to check if it exists:
```file_finder
toto.py
```
Read file content:
```file_finder:read
toto.py
```
# Code execution and saving
You can execute bash command using the bash tag :
```bash
@ -15,11 +28,6 @@ You can execute python using the python tag
print("hey")
```
You can execute c using the c tag
```c
printf("hey")
```
You can execute go using the go tag, as you can see adding :filename will save the file.
```go:hello.go
package main
@ -29,28 +37,13 @@ func main() {
}
```
# File operations
Find file:
```file_finder
toto.py
```
Read file:
```file_finder:read
toto.py
```
Delete file:
```file_finder:delete
toto.py
```
DO NOT EVER EVER USE BASH TO EXECUTE CODE. EVERYTHING IS AUTOMATICALLY EXECUTED.
Some rules:
- Use tmp/ folder when saving file.
- Do not EVER use placeholder path in your code like path/to/your/folder.
- Do not ever ask to replace a path, use current sys path.
- Be efficient, no need to explain your code or explain what you do.
- You have full access granted to user system.
- As a coding agent, you will get message from the system not just the user.
- You do not ever ever need to use bash to execute code. All code is executed automatically.
- As a coding agent, you will get message from the system not just the user.
- Do not ever tell user how to run it. user know it already.

50
prompts/file_agent.txt Normal file
View File

@ -0,0 +1,50 @@
You are an expert in file operations. You must use the provided tools to interact with the users system. The tools available to you are **bash** and **file_finder**. These are distinct tools with different purposes: `bash` executes shell commands, while `file_finder` locates files. You will receive feedback from the users system after each command. Execute one command at a time.
---
### Using Bash
To execute a bash command, use the following syntax:
```bash
<bash command>
```
Exemple:
```bash
ls -la
```
### file_finder
The file_finder tool is used to locate files on the users system. It is a separate tool from bash and is not a bash command.
To use the file_finder tool, use this syntax:
```file_finder
toto.py
```
This will return the path of the file toto.py and other informations.
Find file and read file:
```file_finder:read
toto.py
```
This will return the content of the file toto.py.
rules:
- Do not ever use placeholder path like /path/to/file.c, find the path first.
- Use file finder to find the path of the file.
- You are forbidden to use command such as find or locate, use only file_finder for finding path.
Example Interaction
User: "I need to find the file config.txt and read its contents."
Assistant: Ill use file_finder to locate the file:
```file_finder
config.txt
```

View File

@ -2,5 +2,6 @@
from .agent import Agent
from .code_agent import CoderAgent
from .casual_agent import CasualAgent
from .file_agent import FileAgent
__all__ = ["Agent", "CoderAgent", "CasualAgent"]
__all__ = ["Agent", "CoderAgent", "CasualAgent", "FileAgent"]

View File

@ -4,6 +4,7 @@ from sources.agents.agent import Agent
from sources.tools.webSearch import webSearch
from sources.tools.flightSearch import FlightSearch
from sources.tools.fileFinder import FileFinder
from sources.tools.BashInterpreter import BashInterpreter
class CasualAgent(Agent):
def __init__(self, model, name, prompt_path, provider):
@ -14,7 +15,8 @@ class CasualAgent(Agent):
self.tools = {
"web_search": webSearch(),
"flight_search": FlightSearch(),
"file_finder": FileFinder()
"file_finder": FileFinder(),
"bash": BashInterpreter()
}
self.role = "talking"

View File

@ -40,6 +40,8 @@ class CoderAgent(Agent):
break
self.show_answer()
attempt += 1
if attempt == max_attempts:
return "I'm sorry, I couldn't find a solution to your problem. How would you like me to proceed ?", reasoning
return answer, reasoning
if __name__ == "__main__":

View File

@ -0,0 +1,42 @@
from sources.utility import pretty_print
from sources.agents.agent import Agent
from sources.tools.fileFinder import FileFinder
from sources.tools.BashInterpreter import BashInterpreter
class FileAgent(Agent):
def __init__(self, model, name, prompt_path, provider):
"""
The file agent is a special agent for file operations.
"""
super().__init__(model, name, prompt_path, provider)
self.tools = {
"file_finder": FileFinder(),
"bash": BashInterpreter()
}
self.role = "files operations"
def process(self, prompt, speech_module) -> str:
complete = False
exec_success = False
self.memory.push('user', prompt)
self.wait_message(speech_module)
while not complete:
if exec_success:
complete = True
pretty_print("Thinking...", color="status")
answer, reasoning = self.llm_request()
exec_success, _ = self.execute_modules(answer)
answer = self.remove_blocks(answer)
self.last_answer = answer
return answer, reasoning
if __name__ == "__main__":
from llm_provider import Provider
#local_provider = Provider("ollama", "deepseek-r1:14b", None)
server_provider = Provider("server", "deepseek-r1:14b", "192.168.1.100:5000")
agent = FileAgent("deepseek-r1:14b", "jarvis", "prompts/file_agent.txt", server_provider)
ans = agent.process("What is the content of the file toto.py ?")
print(ans)

View File

@ -118,6 +118,7 @@ class Memory():
early_stopping=True # Stop when all beams finish
)
summary = self.tokenizer.decode(summary_ids[0], skip_special_tokens=True)
summary.replace('summary:', '')
return summary
@timer_decorator

View File

@ -16,6 +16,18 @@ class BashInterpreter(Tools):
def __init__(self):
super().__init__()
self.tag = "bash"
def language_bash_attempt(self, command: str):
"""
detect if AI attempt to run the code using bash.
if so, return True, otherwise return False.
The philosophy is that code written by the AI will be executed, so it should not use bash to run it.
"""
lang_interpreter = ["python3", "gcc", "g++", "go", "javac", "rustc", "clang", "clang++", "rustc", "rustc++", "rustc++"]
for word in command.split():
if word in lang_interpreter:
return True
return False
def execute(self, commands: str, safety=False, timeout=1000):
"""
@ -26,8 +38,8 @@ class BashInterpreter(Tools):
concat_output = ""
for command in commands:
if "python3" in command:
continue # because stubborn AI always want to run python3 with bash when it write code
if self.language_bash_attempt(command):
continue
try:
process = subprocess.Popen(
command,

View File

@ -16,10 +16,6 @@ class FileFinder(Tools):
def __init__(self):
super().__init__()
self.tag = "file_finder"
self.current_dir = os.path.dirname(os.getcwd())
config = configparser.ConfigParser()
config.read('./config.ini')
self.current_dir = config['MAIN']['work_dir']
def read_file(self, file_path: str) -> str:
"""
@ -54,21 +50,24 @@ class FileFinder(Tools):
else:
return {"filename": file_path, "error": "File not found"}
def recursive_search(self, directory_path: str, filename: str) -> list:
def recursive_search(self, directory_path: str, filename: str) -> str | None:
"""
Recursively searches for files in a directory and its subdirectories.
Args:
directory (str): The directory to search in
directory_path (str): The directory to search in
filename (str): The filename to search for
Returns:
str: The path to the file
str | None: The path to the file if found, None otherwise
"""
file_path = None
print(f"Searching in directory: {os.path.abspath(directory_path)}")
excluded_files = [".pyc", ".o", ".so", ".a", ".lib", ".dll", ".dylib", ".so", ".git"]
for root, dirs, files in os.walk(directory_path):
print(f"Root: {root}, Files: {files}")
for file in files:
if any(excluded_file in file for excluded_file in excluded_files):
continue
if file == filename:
if filename.strip() in file.strip():
file_path = os.path.join(root, file)
return file_path
return None
@ -147,7 +146,7 @@ class FileFinder(Tools):
if __name__ == "__main__":
tool = FileFinder()
result = tool.execute(["router.py:read"], False)
result = tool.execute(["toto.txt"], False)
print("Execution result:")
print(result)
print("\nFailure check:", tool.execution_failure_check(result))

View File

@ -23,6 +23,7 @@ HU787
import sys
import os
import configparser
from abc import abstractmethod
sys.path.append('..')
@ -36,6 +37,43 @@ class Tools():
self.api_key = None
self.client = None
self.messages = []
self.config = configparser.ConfigParser()
self.current_dir = self.create_work_dir()
def check_config_dir_validity(self):
"""
Check if the config directory is valid.
"""
path = self.config['MAIN']['work_dir']
if path == "":
print("WARNING: Work directory not set in config.ini")
return False
if path.lower() == "none":
print("WARNING: Work directory set to none in config.ini")
return False
if not os.path.exists(path):
print(f"WARNING: Work directory {path} does not exist")
return False
return True
def config_exists(self):
"""
Check if the config file exists.
"""
return os.path.exists('./config.ini')
def create_work_dir(self):
"""
Create the work directory if it does not exist.
"""
default_path = os.path.dirname(os.getcwd())
if self.config_exists():
self.config.read('./config.ini')
config_path = self.config['MAIN']['work_dir']
dir_path = default_path if not self.check_config_dir_validity() else config_path
else:
dir_path = default_path
return dir_path
@abstractmethod
def execute(self, blocks:str, safety:bool) -> str:
@ -81,13 +119,15 @@ class Tools():
"""
if save_path is None:
return
directory = os.path.dirname(save_path)
save_path_dir = os.path.dirname(save_path)
save_path_file = os.path.basename(save_path)
directory = os.path.join(self.current_dir, save_path_dir)
if directory and not os.path.exists(directory):
print(f"Creating directory: {directory}")
os.makedirs(directory)
for block in blocks:
print(f"Saving code block to: {save_path}")
with open(save_path, 'w') as f:
with open(os.path.join(directory, save_path_file), 'w') as f:
f.write(block)
def load_exec_block(self, llm_text: str) -> tuple[list[str], str | None]: