- Alice CARIOU - Alhuuin - alice.cariou@epita.fr
- Eliana JUNKER - ElianaJunker - eliana.junker@epita.fr
- Juliette JACQUOT - NovaAmethyst - juliette.jacquot@epita.fr
- Sami CARRET - Flooweur - sami.carret@epita.fr
In this repository, we decided to implement generative agents inspired by the paper Generative Agents: Interactive Simulacra of Human Behavior.
Each agents must have a personality of its own, a memory of past events, opinions about the other agents, to be able to create plausible and interesting conversations, while being true to their character.
In this repository, you can find :
- /archives : contains previous versions of the code from when development and testing were still conducted separately.
- /chatting : contains all of our functions and classes, to be able to call them from the
interface.pyfile - final_code.ipynb : our final notebook, clean and gathering all the features.
- generative_agents.pdf : the paper this work is based on.
- interface.py : interface facilitating conversation generation
- requirements.txt : nedded python libraries
To use the project, you need to have several things installed:
- pymongo and requests installed (you can use
pip install -r requirements.txt) - a MongoDB database
- a local LLM
If you don't already have MongoDB on your computer, simply install a docker image, that's all you need:
docker run -d --name mongo_db -p 27017:27017 mongo:latest
If you change the ports, make sure you also change them in the Connection to the database part of the project (in the variable client).
Use this command to create the collection:
db.createCollection("agent_memories", { validator: { $jsonSchema: { bsonType: "object", required: ["agent_id", "timestamp", "importance", "content"], properties: { agent_id: { bsonType: "string" }, agents_involved: { bsonType: "array", items: { bsonType: "string" } }, timestamp: { bsonType: "date" }, importance: { bsonType: "int", minimum: 1, maximum: 10 }, content: { bsonType: "string" }, location: { bsonType: "string" }, emotion: { bsonType: "string" } } } } })
We use LM Studio for this project. You can install it and download the llama-3.2-3b-instruct model. Then go to the developer icon, load the downloaded model and launch the server.
It is possible to download and use other models. If this is the case, modify the model variable in the interaction with LLM section.
Once everything is installed, simply launch the jupyter-notebook named final_code.ipynb!
If you have a preference for an interface instead of a notebook, you cna simply run python interface.py!
The features we implemented in this project are :
As previously stated, each agent have a personality of its own. To do this, we created an Agent class, which contains :
- name
- gender
- opinion of the other agent
- description of the overall personality
The personality is initially chosen by the user. It can contains things the agent likes, dislikes, or just general informations about their hobbies and personality. All of the conversations the agents have will be heavily based on what was chosen for their personalities.
A conversation can be generated by creating a prompt with all the necessary data, organizing it in a clear manner, and giving it to our chosen LLM.
The necessary field to generate one are :
- the agents speaking, as a list
- the suject of discussion
- the memory of past events, if it exists, and if the user wants to use it
- the location of the exchange, if the user wants to specify one
In the /archives directory, our previous methods can be found, including the separated generation of each agent intervention, as opposed to the generation of the full discussion at once. This change of method enabled us to avoid a significant amount of consistency issues in the exchanges.
The memory agents have of past events is stored using MongoDB, as previously explained in the Setup part. The memory will be personal to each agent. Since they are different and have different personalities, they will remember and feel differently. It will store :
- the content of the conversation
- which agents were concerned
- the location : whether the user chose one or not, the location will be stored for context
- the emotions the concerned agent was feeling about this conversation
- the importance this discussion had for the agent
In our implementation, we mostly use the content of the conversation and which agents it included. It could be improved by future enhancements, but it was enough for this project.
After each conversation, the concerned agents will reflect on what has happened, to estimate how important it was to them, and how they felt about it. Thus, a simple conversation about the weather will not hold the same importance as a huge argument leaving the agents very emotional. The emotion and the importance will then be stored alongside the conversation.
These informations will be extracted will a prompt containing the necessary memories and the concerned agents.
If the user wishes to specify an emotion for an agent, there is no field for it, but it can be done either in the personality of the agent if it is a general emotion, or in the suject of discussion if it is specific.
This reflection on conversations is important for the next part :
This field will evolve as the agents interact with each other, as they have the ability to extract what opinion they had of the agent(s) they were talking to, from the exchange they just had. The importance they decided the conversation holds for them will have a big impact, since it will affect how much this new opinion will be important against the previous opinion they had on the others.
Both the extraction and the generation of opinions are made using our LLM.
If an agent does not have an opinion on another, it simply means it does not know them yet. It will be generated during their first interaction. But the user must be carely about the subject of this first interaction, since the agents have no information on each other, and thus will not have any patience for each other, and will less easily tolerate criticism.
The location of a conversation can either be specified by a user, or generated automatically if none has been specified, depending on the provided subject. If it is provided, it will simply be included in the generation of the conversation itself.
Its extraction can be done using our LLM, and is store for each conversation.
One of our goals were to be able to create discussions using numerous agents. It was initially a challenge, since in the earlier version, each agent participation were generated separately, one step at a time. So it was very difficult to keep a plausible conversation, with each agent speaking coherently. But, with our next approach, generating the whole conversation at once, changing the number of agents became more accessible, since the agents could simply be passed to our function as a list.
There is no real limit to the number of agent in a conversation, BUT, there must be at least two of them for it to work correctly. If we try to generate a monologue, no error will appear, but other random agents we did not create will participate to the conversation, and will be remembered.
The personality and opinion that have been set for agent are crucial. The LLM used is extremely sensitive to any negative element, like an agent disliking something or having a short temper. In which case, the conversation can escalate rather rapidly.
Thus, if any changes to these fields are made, we would advise to avoid anything remotely mean or violent, or even including sensitive activities such as gambling, as the generation of the conversation would fail, with the error I cannot create content that promotes violence or discrimination. Is there anything else I can help you with?. This is good, since it does not allow any disturbing or toxic response from the agents.
Additionally, even after having restlessely tested out features, we cannot garanty that the LLM won't make mistakes, as it is not a perfectly reliable source. More precisely, when extracting the emotion and importance of a conversation, we expect a specific format, in json. Even though we specified this, it infrequently returns another format instead, which then makes our extraction crash.