memory fix
frdel committed
Jul 14, 2024 at 10:49 UTC
947aa9bbffd69e3c4ef4b41a889ff43f4e408ebc
10 files changed
+234
-17
agent.py
+33
-1
@@ -12,6 +12,38 @@ from tools.helpers.rate_limiter import RateLimiter
12
13
# rate_limit = rate_limiter.rate_limiter(30,160000) #TODO! implement properly
14
15
+<<<<<<< Updated upstream
16
+=======
17
+@dataclass
18
+class AgentConfig:
19
+ chat_model:BaseChatModel
20
+ utility_model: BaseChatModel
21
+ embeddings_model:Embeddings
22
+ memory_subdir: str = ""
23
+ auto_memory_count: int = 3
24
+ auto_memory_skip: int = 2
25
+ rate_limit_seconds: int = 60
26
+ rate_limit_requests: int = 15
27
+ rate_limit_input_tokens: int = 1000000
28
+ rate_limit_output_tokens: int = 0
29
+ msgs_keep_max: int = 25
30
+ msgs_keep_start: int = 5
31
+ msgs_keep_end: int = 10
32
+ response_timeout_seconds: int = 60
33
+ max_tool_response_length: int = 3000
34
+ code_exec_docker_enabled: bool = True
35
+ code_exec_docker_name: str = "agent-zero-exe"
36
+ code_exec_docker_image: str = "frdel/agent-zero-exe:latest"
37
+ code_exec_docker_ports: dict[str,int] = field(default_factory=lambda: {"22/tcp": 50022})
38
+ code_exec_docker_volumes: dict[str, dict[str, str]] = field(default_factory=lambda: {files.get_abs_path("work_dir"): {"bind": "/root", "mode": "rw"}})
39
+ code_exec_ssh_enabled: bool = True
40
+ code_exec_ssh_addr: str = "localhost"
41
+ code_exec_ssh_port: int = 50022
42
+ code_exec_ssh_user: str = "root"
43
+ code_exec_ssh_pass: str = "toor"
44
+ additional: Dict[str, Any] = field(default_factory=dict)
45
+
46
+>>>>>>> Stashed changes
47
48
class Agent:
49
@@ -276,7 +308,7 @@ class Agent:
308
self.memory_skip_counter = self.auto_memory_skip
309
from tools import memory_tool
310
messages = self.concat_messages(self.history)
279
- memories = memory_tool.process_query(self,messages,"load")
311
+ memories = memory_tool.search(messages)
312
input = {
313
"conversation_history" : messages,
314
"raw_memories": memories
docker/Dockerfile
new
+43
@@ -0,0 +1,43 @@
1
+# Use the latest slim version of Debian
2
+FROM --platform=$TARGETPLATFORM debian:testing-slim
3
+
4
+# Set ARG for platform-specific commands
5
+ARG TARGETPLATFORM
6
+
7
+# Update and install necessary packages
8
+RUN apt-get update && apt-get install -y \
9
+ python3 \
10
+ python3-pip \
11
+ python3-venv \
12
+ nodejs \
13
+ npm \
14
+ openssh-server \
15
+ sudo \
16
+ && rm -rf /var/lib/apt/lists/*
17
+
18
+# Set up SSH
19
+RUN mkdir /var/run/sshd && \
20
+ echo 'root:toor' | chpasswd && \
21
+ sed -i 's/#PermitRootLogin prohibit-password/PermitRootLogin yes/' /etc/ssh/sshd_config
22
+
23
+# Create and activate Python virtual environment
24
+ENV VIRTUAL_ENV=/opt/venv
25
+RUN python3 -m venv $VIRTUAL_ENV
26
+
27
+# Copy initial .bashrc with virtual environment activation to a temporary location
28
+COPY .bashrc /etc/skel/.bashrc
29
+
30
+# Copy the script to ensure .bashrc is in the root directory
31
+COPY initialize.sh /usr/local/bin/initialize.sh
32
+RUN chmod +x /usr/local/bin/initialize.sh
33
+
34
+# Ensure the virtual environment and pip setup
35
+RUN $VIRTUAL_ENV/bin/pip install --upgrade pip
36
+
37
+# Expose SSH port
38
+EXPOSE 22
39
+
40
+# Init .bashrc
41
+CMD ["/usr/local/bin/initialize.sh"]
42
+
43
+
main.py
+35
@@ -28,8 +28,43 @@ def chat():
28
# chat_llm = models.get_ollama_dolphin()
29
30
# embedding model used for memory
31
+<<<<<<< Updated upstream
32
# embedding_llm = models.get_embedding_openai()
33
embedding_llm = models.get_embedding_hf()
34
+=======
35
+ embedding_llm = models.get_embedding_openai()
36
+ # embedding_llm = models.get_embedding_hf()
37
+
38
+ # agent configuration
39
+ config = AgentConfig(
40
+ chat_model = chat_llm,
41
+ utility_model = utility_llm,
42
+ embeddings_model = embedding_llm,
43
+ # memory_subdir = "",
44
+ auto_memory_count = 0,
45
+ # auto_memory_skip = 2,
46
+ # rate_limit_seconds = 60,
47
+ # rate_limit_requests = 30,
48
+ # rate_limit_input_tokens = 0,
49
+ # rate_limit_output_tokens = 0,
50
+ # msgs_keep_max = 25,
51
+ # msgs_keep_start = 5,
52
+ # msgs_keep_end = 10,
53
+ # response_timeout_seconds = 60,
54
+ # max_tool_response_length = 3000,
55
+ code_exec_docker_enabled = True,
56
+ # code_exec_docker_name = "agent-zero-exe",
57
+ # code_exec_docker_image = "frdel/agent-zero-exe:latest",
58
+ # code_exec_docker_ports = { "22/tcp": 50022 }
59
+ # code_exec_docker_volumes = { files.get_abs_path("work_dir"): {"bind": "/root", "mode": "rw"} }
60
+ code_exec_ssh_enabled = True,
61
+ # code_exec_ssh_addr = "localhost",
62
+ # code_exec_ssh_port = 50022,
63
+ # code_exec_ssh_user = "root",
64
+ # code_exec_ssh_pass = "toor",
65
+ # additional = {},
66
+ )
67
+>>>>>>> Stashed changes
68
69
# create the first agent
70
agent0 = Agent( agent_number=0,
prompts/agent.tools.md
+38
@@ -62,9 +62,19 @@ Always verify memory by online.
62
}
63
~~~
64
65
+<<<<<<< Updated upstream
66
### memorize:
67
Save information to persistent memory.
68
Memories can help you remember important details and later reuse them.
69
+=======
70
+### memory_tool:
71
+Manage long term memories. Allowed arguments are "query", "memorize", "forget" and "delete".
72
+Memories can help you remember important details and later reuse them.
73
+When querying, provide a "query" argument to search for. You will retrieve IDs and contents of relevant memories. Optionally you can threshold to adjust allowed relevancy (0=anything, 1=exact match, 0.1 is default).
74
+When memorizing, provide enough information in "memorize" argument for future reuse.
75
+When deleting, provide memory IDs from loaded memories separated by commas in "delete" argument.
76
+When forgetting, provide query and optionally threshold like you would for querying, corresponding memories will be deleted.
77
+>>>>>>> Stashed changes
78
Provide a title, short summary and and all the necessary information to help you later solve similiar tasks including details like code executed, libraries used etc.
79
**Example usages**:
80
~~~json
@@ -76,7 +86,35 @@ Provide a title, short summary and and all the necessary information to help you
86
],
87
"tool_name": "memorize",
88
"tool_args": {
89
+<<<<<<< Updated upstream
90
"memory": "# How to...",
91
+=======
92
+ "memorize": "# How to...",
93
+ }
94
+}
95
+~~~
96
+3. delete:
97
+~~~json
98
+{
99
+ "thoughts": [
100
+ "User asked to delete specific memories...",
101
+ ],
102
+ "tool_name": "memory_tool",
103
+ "tool_args": {
104
+ "delete": "32cd37ffd1-101f-4112-80e2-33b795548116, d1306e36-6a9c-4e6a-bfc3-c8335035dcf8 ...",
105
+ }
106
+}
107
+~~~
108
+4. forget:
109
+~~~json
110
+{
111
+ "thoughts": [
112
+ "User asked to delete information from memory...",
113
+ ],
114
+ "tool_name": "memory_tool",
115
+ "tool_args": {
116
+ "forget": "User's contact information",
117
+>>>>>>> Stashed changes
118
}
119
}
120
~~~
prompts/fw.memories_deleted.md
+1
-1
@@ -1 +1 @@
1
-Memories deleted: {{memories}}
\ No newline at end of file
1
+Memories deleted: {{memory_count}}
\ No newline at end of file
prompts/fw.memory_saved.md
+1
-1
@@ -1 +1 @@
1
-Memory has been saved.
\ No newline at end of file
1
+Memory has been saved with id {{memory_id}}.
\ No newline at end of file
python/tools/memory_tool.py
new
+62
@@ -0,0 +1,62 @@
1
+import re
2
+from agent import Agent
3
+from python.helpers.vector_db import VectorDB, Document
4
+from python.helpers import files
5
+import os, json
6
+from python.helpers.tool import Tool, Response
7
+from python.helpers.print_style import PrintStyle
8
+
9
+# TODO multiple DBs at once
10
+db: VectorDB | None= None
11
+
12
+class Memory(Tool):
13
+ def execute(self,**kwargs):
14
+ result=""
15
+
16
+ if "query" in kwargs:
17
+ if "threshold" in kwargs: threshold = float(kwargs["threshold"])
18
+ else: threshold = 0.1
19
+ if "count" in kwargs: count = int(kwargs["count"])
20
+ else: count = 5
21
+ result = search(self.agent, kwargs["query"], count, threshold)
22
+ elif "memorize" in kwargs:
23
+ result = save(self.agent, kwargs["memorize"])
24
+ elif "forget" in kwargs:
25
+ result = forget(self.agent, kwargs["forget"])
26
+ elif "delete" in kwargs:
27
+ result = delete(self.agent, kwargs["delete"])
28
+
29
+ # result = process_query(self.agent, self.args["memory"],self.args["action"], result_count=self.agent.config.auto_memory_count)
30
+ return Response(message=result, break_loop=False)
31
+
32
+def search(agent:Agent, query:str, count:int=5, threshold:float=0.1):
33
+ initialize(agent)
34
+ docs = db.search_similarity_threshold(query,count,threshold) # type: ignore
35
+ if len(docs)==0: return files.read_file("./prompts/fw.memories_not_found.md", query=query)
36
+ else: return str(docs)
37
+
38
+def save(agent:Agent, text:str):
39
+ initialize(agent)
40
+ id = db.insert_document(text) # type: ignore
41
+ return files.read_file("./prompts/fw.memory_saved.md", memory_id=id)
42
+
43
+def delete(agent:Agent, ids_str:str):
44
+ initialize(agent)
45
+ ids = extract_guids(ids_str)
46
+ deleted = db.delete_documents_by_ids(ids) # type: ignore
47
+ return files.read_file("./prompts/fw.memories_deleted.md", memory_count=deleted)
48
+
49
+def forget(agent:Agent, query:str):
50
+ initialize(agent)
51
+ deleted = db.delete_documents_by_query(query) # type: ignore
52
+ return files.read_file("./prompts/fw.memories_deleted.md", memory_count=deleted)
53
+
54
+def initialize(agent:Agent):
55
+ global db
56
+ if not db:
57
+ dir = os.path.join("memory",agent.config.memory_subdir)
58
+ db = VectorDB(embeddings_model=agent.config.embeddings_model, in_memory=False, cache_dir=dir)
59
+
60
+def extract_guids(text):
61
+ pattern = r'\b[0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-5][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}\b'
62
+ return re.findall(pattern, text)
\ No newline at end of file
tools/helpers/vector_db.py
+19
-9
@@ -35,29 +35,39 @@ class VectorDB:
35
def search_max_rel(self, query, results=3):
36
return self.db.max_marginal_relevance_search(query,results)
37
38
- def delete_documents(self, query):
39
- score_limit = 1
40
- k = 2
38
+ def delete_documents_by_query(self, query:str, threshold=0.1):
39
+ k = 100
40
tot = 0
41
while True:
42
# Perform similarity search with score
44
- docs = self.db.similarity_search_with_score(query, k=k)
43
+ docs = self.search_similarity_threshold(query, results=k, threshold=threshold)
44
45
# Extract document IDs and filter based on score
47
- document_ids = [result[0].metadata["id"] for result in docs if result[1] < score_limit]
46
+ # document_ids = [result[0].metadata["id"] for result in docs if result[1] < score_limit]
47
+ document_ids = [result.metadata["id"] for result in docs]
48
+
49
50
# Delete documents with IDs over the threshold score
51
if document_ids:
51
- fnd = self.db.get(where={"id": {"$in": document_ids}})
52
- if fnd["ids"]: self.db.delete(ids=fnd["ids"])
53
- tot += len(fnd["ids"])
54
-
52
+ # fnd = self.db.get(where={"id": {"$in": document_ids}})
53
+ # if fnd["ids"]: self.db.delete(ids=fnd["ids"])
54
+ # tot += len(fnd["ids"])
55
+ self.db.delete(ids=document_ids)
56
+ tot += len(document_ids)
57
+
58
# If fewer than K document IDs, break the loop
59
if len(document_ids) < k:
60
break
61
62
return tot
63
64
+ def delete_documents_by_ids(self, ids:list[str]):
65
+ # pre = self.db.get(ids=ids)["ids"]
66
+ self.db.delete(ids=ids)
67
+ # post = self.db.get(ids=ids)["ids"]
68
+ #TODO? compare pre and post
69
+ return len(ids)
70
+
71
def insert_document(self, data):
72
id = str(uuid.uuid4())
73
self.db.add_documents(documents=[ Document(data, metadata={"id": id}) ])
tools/knowledge_tool.py
+1
-1
@@ -13,7 +13,7 @@ class Knowledge(Tool):
13
with concurrent.futures.ThreadPoolExecutor() as executor:
14
# Schedule the two functions to be run in parallel
15
future_online = executor.submit(online_knowledge_tool.process_question, question)
16
- future_memory = executor.submit(memory_tool.process_query, self.agent, question)
16
+ future_memory = executor.submit(memory_tool.search, self.agent, question)
17
18
# Wait for both functions to complete
19
online_result = future_online.result()
tools/response.py
+1
-4
@@ -10,11 +10,8 @@ from tools.helpers.print_style import PrintStyle
10
class ResponseTool(Tool):
11
12
def execute(self,**kwargs):
13
- # superior = self.agent.get_data("superior")
14
- # if superior:
15
- self.agent.set_data("timeout", 60)
13
+ self.agent.set_data("timeout", self.agent.config.response_timeout_seconds)
14
return Response(message=self.args["text"], break_loop=True)
17
- # else:
15
16
def after_execution(self, response, **kwargs):
17
pass # do add anything to the history or output
\ No newline at end of file