pycityagent 2.0.0a52__cp312-cp312-macosx_11_0_arm64.whl → 2.0.0a53__cp312-cp312-macosx_11_0_arm64.whl

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (36) hide show
  1. pycityagent/agent/agent.py +48 -62
  2. pycityagent/agent/agent_base.py +66 -53
  3. pycityagent/cityagent/bankagent.py +5 -7
  4. pycityagent/cityagent/blocks/__init__.py +0 -2
  5. pycityagent/cityagent/blocks/cognition_block.py +149 -172
  6. pycityagent/cityagent/blocks/economy_block.py +90 -129
  7. pycityagent/cityagent/blocks/mobility_block.py +56 -29
  8. pycityagent/cityagent/blocks/needs_block.py +163 -145
  9. pycityagent/cityagent/blocks/other_block.py +17 -9
  10. pycityagent/cityagent/blocks/plan_block.py +44 -56
  11. pycityagent/cityagent/blocks/social_block.py +70 -51
  12. pycityagent/cityagent/blocks/utils.py +2 -0
  13. pycityagent/cityagent/firmagent.py +6 -7
  14. pycityagent/cityagent/governmentagent.py +7 -9
  15. pycityagent/cityagent/memory_config.py +48 -48
  16. pycityagent/cityagent/nbsagent.py +6 -29
  17. pycityagent/cityagent/societyagent.py +204 -119
  18. pycityagent/environment/sim/client.py +10 -1
  19. pycityagent/environment/sim/clock_service.py +2 -2
  20. pycityagent/environment/sim/pause_service.py +61 -0
  21. pycityagent/environment/simulator.py +17 -12
  22. pycityagent/llm/embeddings.py +0 -24
  23. pycityagent/memory/faiss_query.py +29 -26
  24. pycityagent/memory/memory.py +720 -272
  25. pycityagent/pycityagent-sim +0 -0
  26. pycityagent/simulation/agentgroup.py +92 -99
  27. pycityagent/simulation/simulation.py +115 -40
  28. pycityagent/tools/tool.py +7 -9
  29. pycityagent/workflow/block.py +11 -4
  30. {pycityagent-2.0.0a52.dist-info → pycityagent-2.0.0a53.dist-info}/METADATA +1 -1
  31. {pycityagent-2.0.0a52.dist-info → pycityagent-2.0.0a53.dist-info}/RECORD +35 -35
  32. pycityagent/cityagent/blocks/time_block.py +0 -116
  33. {pycityagent-2.0.0a52.dist-info → pycityagent-2.0.0a53.dist-info}/LICENSE +0 -0
  34. {pycityagent-2.0.0a52.dist-info → pycityagent-2.0.0a53.dist-info}/WHEEL +0 -0
  35. {pycityagent-2.0.0a52.dist-info → pycityagent-2.0.0a53.dist-info}/entry_points.txt +0 -0
  36. {pycityagent-2.0.0a52.dist-info → pycityagent-2.0.0a53.dist-info}/top_level.txt +0 -0
@@ -1,4 +1,4 @@
1
- from pycityagent.llm.llm import LLM
1
+ from pycityagent.llm import LLM
2
2
  from pycityagent.workflow.block import Block
3
3
  from pycityagent.memory import Memory
4
4
  from pycityagent.environment.simulator import Simulator
@@ -23,47 +23,60 @@ def extract_json(output_str):
23
23
  return None
24
24
 
25
25
  class CognitionBlock(Block):
26
+ configurable_fields = ["top_k"]
27
+ default_values = {"top_k": 20}
28
+ fields_description = {
29
+ "top_k": "Number of most relevant memories to return, defaults to 20"
30
+ }
31
+
26
32
  def __init__(self, llm: LLM, memory: Memory, simulator: Simulator):
27
- super().__init__("CognitionBlock", llm, memory, simulator)
28
- self.last_trigger_time = None
29
- self.time_diff = 24*60*60 # 24小时
30
- self.trigger_time = 0
31
- self.token_consumption = 0
33
+ super().__init__("CognitionBlock", llm=llm, memory=memory, simulator=simulator)
34
+ self.top_k = 20
32
35
 
33
36
  async def set_status(self, status):
34
37
  for key in status:
35
- await self.memory.update(key, status[key])
38
+ await self.memory.status.update(key, status[key])
36
39
  return
37
40
 
38
- async def check_trigger(self):
39
- now_time = await self.simulator.get_time()
40
- if self.last_trigger_time is None or now_time - self.last_trigger_time > self.time_diff:
41
- self.last_trigger_time = now_time
42
- return True
43
- return False
44
-
45
- async def attitude_update(self, topic):
46
- description_prompt = """
47
- You are a {gender}, aged {age}, belonging to the {race} race and identifying as {religion}.
48
- Your marital status is {marital_status}, and you currently reside in a {residence} area.
49
- Your occupation is {occupation}, and your education level is {education}.
50
- You are {personality}, with a consumption level of {consumption} and a family consumption level of {family_consumption}.
51
- Your income is {income}, and you are skilled in {skill}.
52
- My current emotion intensities are (0 meaning not at all, 10 meaning very much):
53
- sadness: {sadness}, joy: {joy}, fear: {fear}, disgust: {disgust}, anger: {anger}, surprise: {surprise}.
54
- You have the following thoughts: {thought}.
55
- In the following 21 words, I have chosen {emotion_types} to represent your current status:
56
- Joy, Distress, Resentment, Pity, Hope, Fear, Satisfaction, Relief, Disappointment, Pride, Admiration, Shame, Reproach, Liking, Disliking, Gratitude, Anger, Gratification, Remorse, Love, Hate.
57
- """
58
- incident_list = await self.memory.get("incident")
59
- if incident_list:
60
- incident_prompt = "Today, these incidents happened:"
61
- for incident in incident_list:
62
- incident_prompt += incident
63
- else:
64
- incident_prompt = "No incidents happened today."
65
- attitude = await self.memory.get("attitude")
66
- if topic in attitude:
41
+ async def attitude_update(self):
42
+ """Cognition - attitude update workflow"""
43
+ attitude = await self.memory.status.get("attitude")
44
+ prompt_data = {
45
+ "gender": await self.memory.status.get("gender"),
46
+ "age": await self.memory.status.get("age"),
47
+ "race": await self.memory.status.get("race"),
48
+ "religion": await self.memory.status.get("religion"),
49
+ "marital_status": await self.memory.status.get("marital_status"),
50
+ "residence": await self.memory.status.get("residence"),
51
+ "occupation": await self.memory.status.get("occupation"),
52
+ "education": await self.memory.status.get("education"),
53
+ "personality": await self.memory.status.get("personality"),
54
+ "consumption": await self.memory.status.get("consumption"),
55
+ "family_consumption": await self.memory.status.get("family_consumption"),
56
+ "income": await self.memory.status.get("income"),
57
+ "skill": await self.memory.status.get("skill"),
58
+ "thought": await self.memory.status.get("thought"),
59
+ "emotion_types": await self.memory.status.get("emotion_types")
60
+ }
61
+ for topic in attitude:
62
+ description_prompt = """
63
+ You are a {gender}, aged {age}, belonging to the {race} race and identifying as {religion}.
64
+ Your marital status is {marital_status}, and you currently reside in a {residence} area.
65
+ Your occupation is {occupation}, and your education level is {education}.
66
+ You are {personality}, with a consumption level of {consumption} and a family consumption level of {family_consumption}.
67
+ Your income is {income}, and you are skilled in {skill}.
68
+ My current emotion intensities are (0 meaning not at all, 10 meaning very much):
69
+ sadness: {sadness}, joy: {joy}, fear: {fear}, disgust: {disgust}, anger: {anger}, surprise: {surprise}.
70
+ You have the following thoughts: {thought}.
71
+ In the following 21 words, I have chosen {emotion_types} to represent your current status:
72
+ Joy, Distress, Resentment, Pity, Hope, Fear, Satisfaction, Relief, Disappointment, Pride, Admiration, Shame, Reproach, Liking, Disliking, Gratitude, Anger, Gratification, Remorse, Love, Hate.
73
+ """
74
+ incident_str = await self.memory.stream.search_today(top_k=self.top_k)
75
+ if incident_str:
76
+ incident_prompt = "Today, these incidents happened:"
77
+ incident_prompt += incident_str
78
+ else:
79
+ incident_prompt = "No incidents happened today."
67
80
  previous_attitude = str(attitude[topic]) # Convert to string
68
81
  problem_prompt = (
69
82
  f"You need to decide your attitude towards topic: {topic}, "
@@ -71,70 +84,39 @@ class CognitionBlock(Block):
71
84
  "(0 meaning oppose, 10 meaning support). "
72
85
  "Please return a new attitude rating (0-10, smaller meaning oppose, larger meaning support) in JSON format, and explain, e.g. {{\"attitude\": 5}}"
73
86
  )
74
- else:
75
- problem_prompt = (
76
- f"You need to decide your attitude towards topic: {topic}, "
77
- "which you have not rated your attitude towards this topic yet. "
78
- "(0 meaning oppose, 10 meaning support). "
79
- "Please return a new attitude rating (0-10, smaller meaning oppose, larger meaning support) in JSON format, and explain, e.g. {{\"attitude\": 5}}"
80
- )
81
- question_prompt = description_prompt + incident_prompt + problem_prompt
82
- question_prompt = FormatPrompt(question_prompt)
83
- emotion = await self.memory.get("emotion")
84
- sadness = emotion["sadness"]
85
- joy = emotion["joy"]
86
- fear = emotion["fear"]
87
- disgust = emotion["disgust"]
88
- anger = emotion["anger"]
89
- surprise = emotion["surprise"]
90
-
91
- question_prompt.format(
92
- gender=await self.memory.get("gender"),
93
- age=await self.memory.get("age"),
94
- race=await self.memory.get("race"),
95
- religion=await self.memory.get("religion"),
96
- marital_status=await self.memory.get("marital_status"),
97
- residence=await self.memory.get("residence"),
98
- occupation=await self.memory.get("occupation"),
99
- education=await self.memory.get("education"),
100
- personality=await self.memory.get("personality"),
101
- consumption=await self.memory.get("consumption"),
102
- family_consumption=await self.memory.get("family_consumption"),
103
- income=await self.memory.get("income"),
104
- skill=await self.memory.get("skill"),
105
- sadness=sadness,
106
- joy=joy,
107
- fear=fear,
108
- disgust=disgust,
109
- anger=anger,
110
- surprise=surprise,
111
- thought=await self.memory.get("thought"),
112
- emotion_types=await self.memory.get("emotion_types")
113
- )
114
- evaluation = True
115
- for retry in range(10):
116
- try:
117
- response = await self.llm.atext_request(question_prompt.to_dialog(), timeout=300)
118
- response = json.loads(extract_json(response))
119
- evaluation = False
120
- break
121
- except:
122
- pass
123
- if evaluation:
124
- raise f"Request for attitude:{topic} update failed"
125
- logger.info(f"""Cognition updated attitude:{topic}:
126
- attitude: {response['attitude']}""")
127
- attitude[topic] = response["attitude"]
128
- await self.memory.update("attitude", attitude)
129
- return
87
+ question_prompt = description_prompt + incident_prompt + problem_prompt
88
+ question_prompt = FormatPrompt(question_prompt)
89
+ emotion = await self.memory.status.get("emotion")
90
+ sadness = emotion["sadness"]
91
+ joy = emotion["joy"]
92
+ fear = emotion["fear"]
93
+ disgust = emotion["disgust"]
94
+ anger = emotion["anger"]
95
+ surprise = emotion["surprise"]
96
+ prompt_data["sadness"] = sadness
97
+ prompt_data["joy"] = joy
98
+ prompt_data["fear"] = fear
99
+ prompt_data["disgust"] = disgust
100
+ prompt_data["anger"] = anger
101
+ prompt_data["surprise"] = surprise
102
+
103
+ question_prompt.format(**prompt_data)
104
+ evaluation = True
105
+ for retry in range(10):
106
+ try:
107
+ response = await self.llm.atext_request(question_prompt.to_dialog(), timeout=300)
108
+ response = json.loads(extract_json(response))
109
+ evaluation = False
110
+ break
111
+ except:
112
+ pass
113
+ if evaluation:
114
+ raise f"Request for attitude:{topic} update failed"
115
+ attitude[topic] = response["attitude"]
116
+ await self.memory.status.update("attitude", attitude)
130
117
 
131
- async def forward(self): #每日结算
132
- whether_trigger = await self.check_trigger()
133
- if not whether_trigger:
134
- return
135
- self.trigger_time += 1
136
- consumption_start = self.llm.prompt_tokens_used + self.llm.completion_tokens_used
137
-
118
+ async def thought_update(self):
119
+ """Cognition - thought update workflow"""
138
120
  description_prompt = """
139
121
  You are a {gender}, aged {age}, belonging to the {race} race and identifying as {religion}.
140
122
  Your marital status is {marital_status}, and you currently reside in a {residence} area.
@@ -147,21 +129,20 @@ class CognitionBlock(Block):
147
129
  In the following 21 words, I have chosen {emotion_types} to represent your current status:
148
130
  Joy, Distress, Resentment, Pity, Hope, Fear, Satisfaction, Relief, Disappointment, Pride, Admiration, Shame, Reproach, Liking, Disliking, Gratitude, Anger, Gratification, Remorse, Love, Hate.
149
131
  """
150
- incident_list = await self.memory.get("incident")
151
- if incident_list:
152
- incident_prompt = "Today, these incidents happened:"
153
- for incident in incident_list:
154
- incident_prompt += incident
132
+ incident_str = await self.memory.stream.search_today(top_k=20)
133
+ if incident_str:
134
+ incident_prompt = "Today, these incidents happened:\n" + incident_str
155
135
  else:
156
136
  incident_prompt = "No incidents happened today."
157
137
  question_prompt = """
158
- Please reconsider your emotion intensities:
159
- sadness, joy, fear, disgust, anger, surprise (0 meaning not at all, 10 meaning very much).
160
- Also summerize you current thoughts, and choose a word to describe your status: Joy, Distress, Resentment, Pity, Hope, Fear, Satisfaction, Relief, Disappointment, Pride, Admiration, Shame, Reproach, Liking, Disliking, Gratitude, Anger, Gratification, Remorse, Love, Hate.
161
- Return in JSON format, e.g. {{"sadness": 5, "joy": 5, "fear": 5, "disgust": 5, "anger": 5, "surprise": 5, "thought": "Currently nothing good or bad is happening, I think ....", "word": "Relief"}}"""
138
+ Please review what happened today and share your thoughts and feelings about it.
139
+ Consider your current emotional state and experiences, then:
140
+ 1. Summarize your thoughts and reflections on today's events
141
+ 2. Choose one word that best describes your current emotional state from: Joy, Distress, Resentment, Pity, Hope, Fear, Satisfaction, Relief, Disappointment, Pride, Admiration, Shame, Reproach, Liking, Disliking, Gratitude, Anger, Gratification, Remorse, Love, Hate.
142
+ Return in JSON format, e.g. {{"thought": "Currently nothing good or bad is happening, I think ...."}}"""
162
143
  question_prompt = description_prompt + incident_prompt + question_prompt
163
144
  question_prompt = FormatPrompt(question_prompt)
164
- emotion = await self.memory.get("emotion")
145
+ emotion = await self.memory.status.get("emotion")
165
146
  sadness = emotion["sadness"]
166
147
  joy = emotion["joy"]
167
148
  fear = emotion["fear"]
@@ -169,28 +150,28 @@ class CognitionBlock(Block):
169
150
  anger = emotion["anger"]
170
151
  surprise = emotion["surprise"]
171
152
  question_prompt.format(
172
- gender=await self.memory.get("gender"),
173
- age=await self.memory.get("age"),
174
- race=await self.memory.get("race"),
175
- religion=await self.memory.get("religion"),
176
- marital_status=await self.memory.get("marital_status"),
177
- residence=await self.memory.get("residence"),
178
- occupation=await self.memory.get("occupation"),
179
- education=await self.memory.get("education"),
180
- personality=await self.memory.get("personality"),
181
- consumption=await self.memory.get("consumption"),
182
- family_consumption=await self.memory.get("family_consumption"),
183
- income=await self.memory.get("income"),
184
- skill=await self.memory.get("skill"),
153
+ gender=await self.memory.status.get("gender"),
154
+ age=await self.memory.status.get("age"),
155
+ race=await self.memory.status.get("race"),
156
+ religion=await self.memory.status.get("religion"),
157
+ marital_status=await self.memory.status.get("marital_status"),
158
+ residence=await self.memory.status.get("residence"),
159
+ occupation=await self.memory.status.get("occupation"),
160
+ education=await self.memory.status.get("education"),
161
+ personality=await self.memory.status.get("personality"),
162
+ consumption=await self.memory.status.get("consumption"),
163
+ family_consumption=await self.memory.status.get("family_consumption"),
164
+ income=await self.memory.status.get("income"),
165
+ skill=await self.memory.status.get("skill"),
185
166
  sadness=sadness,
186
167
  joy=joy,
187
168
  fear=fear,
188
169
  disgust=disgust,
189
170
  anger=anger,
190
171
  surprise=surprise,
191
- emotion=await self.memory.get("emotion"),
192
- thought=await self.memory.get("thought"),
193
- emotion_types=await self.memory.get("emotion_types")
172
+ emotion=await self.memory.status.get("emotion"),
173
+ thought=await self.memory.status.get("thought"),
174
+ emotion_types=await self.memory.status.get("emotion_types")
194
175
  )
195
176
 
196
177
  evaluation = True
@@ -204,30 +185,32 @@ class CognitionBlock(Block):
204
185
  pass
205
186
  if evaluation:
206
187
  raise Exception("Request for cognition update failed")
207
- logger.info(f"""Cognition updated emotion intensities:
208
- sadness: {response['sadness']},
209
- joy: {response['joy']},
210
- fear: {response['fear']},
211
- disgust: {response['disgust']},
212
- anger: {response['anger']},
213
- surprise: {response['surprise']}"
214
- thought: {response['thought']}
215
- emotion_types: {response['word']}""")
216
188
 
217
- await self.memory.update("incident", [])
218
- await self.memory.update("emotion", {"sadness": int(response["sadness"]), "joy": int(response["joy"]), "fear": int(response["fear"]), "disgust": int(response["disgust"]), "anger": int(response["anger"]), "surprise": int(response["surprise"])})
219
- await self.memory.update("thought", str(response["thought"]))
220
- await self.memory.update("emotion_types", str(response["word"]))
221
-
222
- consumption_end = self.llm.prompt_tokens_used + self.llm.completion_tokens_used
223
- self.token_consumption += consumption_end - consumption_start
189
+ thought = str(response["thought"])
190
+ await self.memory.status.update("thought", thought)
191
+ await self.memory.stream.add_cognition(description=thought)
224
192
  return
225
193
 
226
- async def emotion_update(self, incident): #每日结算
194
+ async def end_of_day(self):
195
+ """Cognition - end of day workflow"""
196
+ time = await self.simulator.get_simulator_second_from_start_of_day()
197
+ if time >= 86400 - 10 * 60:
198
+ return True
199
+ return False
200
+
201
+ async def forward(self):
202
+ """Cognition workflow: Daily update"""
203
+ # cognition update: thought and attitude
204
+ if await self.end_of_day():
205
+ await self.thought_update()
206
+ await self.attitude_update()
207
+
208
+ async def emotion_update(self, incident):
209
+ """Cognition - emotion update workflow"""
227
210
  whether_trigger = await self.check_trigger()
228
211
  if not whether_trigger:
229
212
  return
230
-
213
+ print(f"Updating emotion for {incident}")
231
214
  description_prompt = """
232
215
  You are a {gender}, aged {age}, belonging to the {race} race and identifying as {religion}.
233
216
  Your marital status is {marital_status}, and you currently reside in a {residence} area.
@@ -241,14 +224,14 @@ class CognitionBlock(Block):
241
224
  Joy, Distress, Resentment, Pity, Hope, Fear, Satisfaction, Relief, Disappointment, Pride, Admiration, Shame, Reproach, Liking, Disliking, Gratitude, Anger, Gratification, Remorse, Love, Hate.
242
225
  """
243
226
 
244
- incident_prompt = incident #waiting for incident port
227
+ incident_prompt = f"{incident}" #waiting for incident port
245
228
  question_prompt = """
246
229
  Please reconsider your emotion intensities:
247
230
  sadness, joy, fear, disgust, anger, surprise (0 meaning not at all, 10 meaning very much).
248
- Return in JSON format, e.g. {{"sadness": 5, "joy": 5, "fear": 5, "disgust": 5, "anger": 5, "surprise": 5}}"""
231
+ Return in JSON format, e.g. {{"sadness": 5, "joy": 5, "fear": 5, "disgust": 5, "anger": 5, "surprise": 5, "conclusion": "I feel ..."}}"""
249
232
  question_prompt = description_prompt + incident_prompt + question_prompt
250
233
  question_prompt = FormatPrompt(question_prompt)
251
- emotion = await self.memory.get("emotion")
234
+ emotion = await self.memory.status.get("emotion")
252
235
  sadness = emotion["sadness"]
253
236
  joy = emotion["joy"]
254
237
  fear = emotion["fear"]
@@ -256,28 +239,28 @@ class CognitionBlock(Block):
256
239
  anger = emotion["anger"]
257
240
  surprise = emotion["surprise"]
258
241
  question_prompt.format(
259
- gender=await self.memory.get("gender"),
260
- age=await self.memory.get("age"),
261
- race=await self.memory.get("race"),
262
- religion=await self.memory.get("religion"),
263
- marital_status=await self.memory.get("marital_status"),
264
- residence=await self.memory.get("residence"),
265
- occupation=await self.memory.get("occupation"),
266
- education=await self.memory.get("education"),
267
- personality=await self.memory.get("personality"),
268
- consumption=await self.memory.get("consumption"),
269
- family_consumption=await self.memory.get("family_consumption"),
270
- income=await self.memory.get("income"),
271
- skill=await self.memory.get("skill"),
242
+ gender=await self.memory.status.get("gender"),
243
+ age=await self.memory.status.get("age"),
244
+ race=await self.memory.status.get("race"),
245
+ religion=await self.memory.status.get("religion"),
246
+ marital_status=await self.memory.status.get("marital_status"),
247
+ residence=await self.memory.status.get("residence"),
248
+ occupation=await self.memory.status.get("occupation"),
249
+ education=await self.memory.status.get("education"),
250
+ personality=await self.memory.status.get("personality"),
251
+ consumption=await self.memory.status.get("consumption"),
252
+ family_consumption=await self.memory.status.get("family_consumption"),
253
+ income=await self.memory.status.get("income"),
254
+ skill=await self.memory.status.get("skill"),
272
255
  sadness=sadness,
273
256
  joy=joy,
274
257
  fear=fear,
275
258
  disgust=disgust,
276
259
  anger=anger,
277
260
  surprise=surprise,
278
- emotion=await self.memory.get("emotion"),
279
- thought=await self.memory.get("thought"),
280
- emotion_types=await self.memory.get("emotion_types")
261
+ emotion=await self.memory.status.get("emotion"),
262
+ thought=await self.memory.status.get("thought"),
263
+ emotion_types=await self.memory.status.get("emotion_types")
281
264
  )
282
265
 
283
266
  evaluation = True
@@ -288,17 +271,11 @@ class CognitionBlock(Block):
288
271
  evaluation = False
289
272
  break
290
273
  except Exception as e:
291
- print(e)
274
+ logger.warning(f"Request for cognition update failed: {e}")
292
275
  pass
293
276
  if evaluation:
294
277
  raise Exception("Request for cognition update failed")
295
- logger.info(f"""Cognition updated emotion intensities:
296
- sadness: {response['sadness']},
297
- joy: {response['joy']},
298
- fear: {response['fear']},
299
- disgust: {response['disgust']},
300
- anger: {response['anger']},
301
- surprise: {response['surprise']}""")
302
278
 
303
- await self.memory.update("emotion", {"sadness": int(response["sadness"]), "joy": int(response["joy"]), "fear": int(response["fear"]), "disgust": int(response["disgust"]), "anger": int(response["anger"]), "surprise": int(response["surprise"])})
304
- return
279
+ await self.memory.status.update("emotion", {"sadness": int(response["sadness"]), "joy": int(response["joy"]), "fear": int(response["fear"]), "disgust": int(response["disgust"]), "anger": int(response["anger"]), "surprise": int(response["surprise"])})
280
+ await self.memory.status.update("emotion_types", str(response["word"]))
281
+ return response["conclusion"]