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