pycityagent 2.0.0a66__cp311-cp311-macosx_11_0_arm64.whl → 2.0.0a67__cp311-cp311-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 (87) hide show
  1. pycityagent/agent/agent.py +157 -57
  2. pycityagent/agent/agent_base.py +316 -43
  3. pycityagent/cityagent/bankagent.py +49 -9
  4. pycityagent/cityagent/blocks/__init__.py +1 -2
  5. pycityagent/cityagent/blocks/cognition_block.py +54 -31
  6. pycityagent/cityagent/blocks/dispatcher.py +22 -17
  7. pycityagent/cityagent/blocks/economy_block.py +46 -32
  8. pycityagent/cityagent/blocks/mobility_block.py +130 -100
  9. pycityagent/cityagent/blocks/needs_block.py +101 -44
  10. pycityagent/cityagent/blocks/other_block.py +42 -33
  11. pycityagent/cityagent/blocks/plan_block.py +59 -42
  12. pycityagent/cityagent/blocks/social_block.py +167 -116
  13. pycityagent/cityagent/blocks/utils.py +13 -6
  14. pycityagent/cityagent/firmagent.py +17 -35
  15. pycityagent/cityagent/governmentagent.py +3 -3
  16. pycityagent/cityagent/initial.py +79 -44
  17. pycityagent/cityagent/memory_config.py +108 -88
  18. pycityagent/cityagent/message_intercept.py +0 -4
  19. pycityagent/cityagent/metrics.py +41 -0
  20. pycityagent/cityagent/nbsagent.py +24 -36
  21. pycityagent/cityagent/societyagent.py +7 -3
  22. pycityagent/cli/wrapper.py +2 -2
  23. pycityagent/economy/econ_client.py +407 -81
  24. pycityagent/environment/__init__.py +0 -3
  25. pycityagent/environment/sim/__init__.py +0 -3
  26. pycityagent/environment/sim/aoi_service.py +2 -2
  27. pycityagent/environment/sim/client.py +3 -31
  28. pycityagent/environment/sim/clock_service.py +2 -2
  29. pycityagent/environment/sim/lane_service.py +8 -8
  30. pycityagent/environment/sim/light_service.py +8 -8
  31. pycityagent/environment/sim/pause_service.py +9 -10
  32. pycityagent/environment/sim/person_service.py +20 -20
  33. pycityagent/environment/sim/road_service.py +2 -2
  34. pycityagent/environment/sim/sim_env.py +21 -5
  35. pycityagent/environment/sim/social_service.py +4 -4
  36. pycityagent/environment/simulator.py +249 -27
  37. pycityagent/environment/utils/__init__.py +2 -2
  38. pycityagent/environment/utils/geojson.py +2 -2
  39. pycityagent/environment/utils/grpc.py +4 -4
  40. pycityagent/environment/utils/map_utils.py +2 -2
  41. pycityagent/llm/embeddings.py +147 -28
  42. pycityagent/llm/llm.py +122 -77
  43. pycityagent/llm/llmconfig.py +5 -0
  44. pycityagent/llm/utils.py +4 -0
  45. pycityagent/memory/__init__.py +0 -4
  46. pycityagent/memory/const.py +2 -2
  47. pycityagent/memory/faiss_query.py +140 -61
  48. pycityagent/memory/memory.py +393 -90
  49. pycityagent/memory/memory_base.py +140 -34
  50. pycityagent/memory/profile.py +13 -13
  51. pycityagent/memory/self_define.py +13 -13
  52. pycityagent/memory/state.py +14 -14
  53. pycityagent/message/message_interceptor.py +253 -3
  54. pycityagent/message/messager.py +133 -6
  55. pycityagent/metrics/mlflow_client.py +47 -4
  56. pycityagent/pycityagent-sim +0 -0
  57. pycityagent/pycityagent-ui +0 -0
  58. pycityagent/simulation/__init__.py +3 -2
  59. pycityagent/simulation/agentgroup.py +145 -52
  60. pycityagent/simulation/simulation.py +257 -62
  61. pycityagent/survey/manager.py +45 -3
  62. pycityagent/survey/models.py +42 -2
  63. pycityagent/tools/__init__.py +1 -2
  64. pycityagent/tools/tool.py +93 -69
  65. pycityagent/utils/avro_schema.py +2 -2
  66. pycityagent/utils/parsers/code_block_parser.py +1 -1
  67. pycityagent/utils/parsers/json_parser.py +2 -2
  68. pycityagent/utils/parsers/parser_base.py +2 -2
  69. pycityagent/workflow/block.py +64 -13
  70. pycityagent/workflow/prompt.py +31 -23
  71. pycityagent/workflow/trigger.py +91 -24
  72. {pycityagent-2.0.0a66.dist-info → pycityagent-2.0.0a67.dist-info}/METADATA +2 -2
  73. pycityagent-2.0.0a67.dist-info/RECORD +97 -0
  74. pycityagent/environment/interact/__init__.py +0 -0
  75. pycityagent/environment/interact/interact.py +0 -198
  76. pycityagent/environment/message/__init__.py +0 -0
  77. pycityagent/environment/sence/__init__.py +0 -0
  78. pycityagent/environment/sence/static.py +0 -416
  79. pycityagent/environment/sidecar/__init__.py +0 -8
  80. pycityagent/environment/sidecar/sidecarv2.py +0 -109
  81. pycityagent/environment/sim/economy_services.py +0 -192
  82. pycityagent/metrics/utils/const.py +0 -0
  83. pycityagent-2.0.0a66.dist-info/RECORD +0 -105
  84. {pycityagent-2.0.0a66.dist-info → pycityagent-2.0.0a67.dist-info}/LICENSE +0 -0
  85. {pycityagent-2.0.0a66.dist-info → pycityagent-2.0.0a67.dist-info}/WHEEL +0 -0
  86. {pycityagent-2.0.0a66.dist-info → pycityagent-2.0.0a67.dist-info}/entry_points.txt +0 -0
  87. {pycityagent-2.0.0a66.dist-info → pycityagent-2.0.0a67.dist-info}/top_level.txt +0 -0
@@ -1,6 +1,6 @@
1
1
  import math
2
2
  from typing import List
3
- from .dispatcher import BlockDispatcher
3
+
4
4
  from pycityagent.environment.simulator import Simulator
5
5
  from pycityagent.llm import LLM
6
6
  from pycityagent.memory import Memory
@@ -9,8 +9,11 @@ import numpy as np
9
9
  from operator import itemgetter
10
10
  import random
11
11
  import logging
12
- logger = logging.getLogger("pycityagent")
13
12
  from pycityagent.workflow.prompt import FormatPrompt
13
+ from .dispatcher import BlockDispatcher
14
+
15
+ logger = logging.getLogger("pycityagent")
16
+
14
17
 
15
18
  PLACE_TYPE_SELECTION_PROMPT = """
16
19
  As an intelligent decision system, please determine the type of place the user needs to visit based on their input requirement.
@@ -45,6 +48,7 @@ Please analyze how these emotions would affect travel willingness and return onl
45
48
 
46
49
  Return only the integer number without any additional text or explanation."""
47
50
 
51
+
48
52
  def gravity_model(pois):
49
53
  N = len(pois)
50
54
  pois_Dis = {
@@ -85,9 +89,7 @@ def gravity_model(pois):
85
89
  # distance decay coefficient, use the square of distance to calculate, so that distant places are less likely to be selected
86
90
  weight = density / (distance**2) # the original weight is reasonable
87
91
  res.append((poi[0]["name"], poi[0]["id"], weight, distance))
88
- distanceProb.append(
89
- 1 / (math.sqrt(distance))
90
- )
92
+ distanceProb.append(1 / (math.sqrt(distance)))
91
93
  iflt10k = False
92
94
  break
93
95
 
@@ -110,32 +112,36 @@ def gravity_model(pois):
110
112
  ]
111
113
  return final
112
114
 
115
+
113
116
  class PlaceSelectionBlock(Block):
114
117
  """
115
118
  Select destination
116
119
  PlaceSelectionBlock
117
120
  """
121
+
118
122
  configurable_fields: List[str] = ["search_limit"]
119
- default_values = {
120
- "search_limit": 10000
121
- }
123
+ default_values = {"search_limit": 10000}
122
124
 
123
125
  def __init__(self, llm: LLM, memory: Memory, simulator: Simulator):
124
- super().__init__("PlaceSelectionBlock", llm=llm, memory=memory, simulator=simulator)
126
+ super().__init__(
127
+ "PlaceSelectionBlock", llm=llm, memory=memory, simulator=simulator
128
+ )
125
129
  self.description = "Used to select and determine destinations for unknown locations (excluding home and workplace), such as choosing specific shopping malls, restaurants and other places"
126
130
  self.typeSelectionPrompt = FormatPrompt(PLACE_TYPE_SELECTION_PROMPT)
127
- self.secondTypeSelectionPrompt = FormatPrompt(PLACE_SECOND_TYPE_SELECTION_PROMPT)
131
+ self.secondTypeSelectionPrompt = FormatPrompt(
132
+ PLACE_SECOND_TYPE_SELECTION_PROMPT
133
+ )
128
134
  self.radiusPrompt = FormatPrompt(RADIUS_PROMPT)
129
135
  # configurable fields
130
136
  self.search_limit = 10000
131
137
 
132
138
  async def forward(self, step, context):
133
139
  self.typeSelectionPrompt.format(
134
- plan = context['plan'],
135
- intention = step['intention'],
136
- poi_category = list(self.simulator.poi_cate.keys())
140
+ plan=context["plan"],
141
+ intention=step["intention"],
142
+ poi_category=list(self.simulator.poi_cate.keys()),
137
143
  )
138
- levelOneType = await self.llm.atext_request(self.typeSelectionPrompt.to_dialog()) # type: ignore
144
+ levelOneType = await self.llm.atext_request(self.typeSelectionPrompt.to_dialog()) # type: ignore
139
145
  try:
140
146
  sub_category = self.simulator.poi_cate[levelOneType]
141
147
  except Exception as e:
@@ -143,35 +149,33 @@ class PlaceSelectionBlock(Block):
143
149
  levelOneType = random.choice(list(self.simulator.poi_cate.keys()))
144
150
  sub_category = self.simulator.poi_cate[levelOneType]
145
151
  self.secondTypeSelectionPrompt.format(
146
- plan = context['plan'],
147
- intention = step['intention'],
148
- poi_category = sub_category
152
+ plan=context["plan"], intention=step["intention"], poi_category=sub_category
149
153
  )
150
- levelTwoType = await self.llm.atext_request(self.secondTypeSelectionPrompt.to_dialog()) # type: ignore
151
- center = await self.memory.status.get('position')
152
- center = (center['xy_position']['x'], center['xy_position']['y'])
154
+ levelTwoType = await self.llm.atext_request(self.secondTypeSelectionPrompt.to_dialog()) # type: ignore
155
+ center = await self.memory.status.get("position")
156
+ center = (center["xy_position"]["x"], center["xy_position"]["y"])
153
157
  self.radiusPrompt.format(
154
158
  emotion_types=await self.memory.status.get("emotion_types"),
155
159
  thought=await self.memory.status.get("thought"),
156
160
  weather=self.simulator.sence("weather"),
157
- temperature=self.simulator.sence("temperature")
161
+ temperature=self.simulator.sence("temperature"),
158
162
  )
159
- radius = int(await self.llm.atext_request(self.radiusPrompt.to_dialog())) # type: ignore
163
+ radius = int(await self.llm.atext_request(self.radiusPrompt.to_dialog())) # type: ignore
160
164
  try:
161
165
  pois = self.simulator.map.query_pois(
162
- center = center,
163
- category_prefix = levelTwoType,
166
+ center=center,
167
+ category_prefix=levelTwoType,
164
168
  radius=radius,
165
- limit=self.search_limit
169
+ limit=self.search_limit,
166
170
  )
167
171
  except Exception as e:
168
172
  logger.warning(f"Error querying pois: {e}")
169
173
  levelTwoType = random.choice(sub_category)
170
174
  pois = self.simulator.map.query_pois(
171
- center = center,
172
- category_prefix = levelTwoType,
175
+ center=center,
176
+ category_prefix=levelTwoType,
173
177
  radius=radius,
174
- limit=self.search_limit
178
+ limit=self.search_limit,
175
179
  )
176
180
  if len(pois) > 0:
177
181
  pois = gravity_model(pois)
@@ -183,33 +187,39 @@ class PlaceSelectionBlock(Block):
183
187
  nextPlace = pois[sample[0]]
184
188
  nextPlace = (nextPlace[0], nextPlace[1])
185
189
  # save the destination to context
186
- context['next_place'] = nextPlace
187
- node_id = await self.memory.stream.add_mobility(description=f"For {step['intention']}, I selected the destination: {nextPlace}")
190
+ context["next_place"] = nextPlace
191
+ node_id = await self.memory.stream.add_mobility(
192
+ description=f"For {step['intention']}, I selected the destination: {nextPlace}"
193
+ )
188
194
  return {
189
- 'success': True,
190
- 'evaluation': f'Successfully selected the destination: {nextPlace}',
191
- 'consumed_time': 5,
192
- 'node_id': node_id
195
+ "success": True,
196
+ "evaluation": f"Successfully selected the destination: {nextPlace}",
197
+ "consumed_time": 5,
198
+ "node_id": node_id,
193
199
  }
194
200
  else:
195
201
  simmap = self.simulator.map
196
202
  poi = random.choice(list(simmap.pois.values()))
197
- nextPlace = (poi['name'], poi['aoi_id'])
203
+ nextPlace = (poi["name"], poi["aoi_id"])
198
204
  # save the destination to context
199
- context['next_place'] = nextPlace
200
- node_id = await self.memory.stream.add_mobility(description=f"For {step['intention']}, I selected the destination: {nextPlace}")
205
+ context["next_place"] = nextPlace
206
+ node_id = await self.memory.stream.add_mobility(
207
+ description=f"For {step['intention']}, I selected the destination: {nextPlace}"
208
+ )
201
209
  return {
202
- 'success': True,
203
- 'evaluation': f'Successfully selected the destination: {nextPlace}',
204
- 'consumed_time': 5,
205
- 'node_id': node_id
210
+ "success": True,
211
+ "evaluation": f"Successfully selected the destination: {nextPlace}",
212
+ "consumed_time": 5,
213
+ "node_id": node_id,
206
214
  }
207
215
 
216
+
208
217
  class MoveBlock(Block):
209
218
  """
210
219
  Execute mobility operations
211
220
  MoveBlock
212
221
  """
222
+
213
223
  def __init__(self, llm: LLM, memory: Memory, simulator: Simulator):
214
224
  super().__init__("MoveBlock", llm=llm, memory=memory, simulator=simulator)
215
225
  self.description = "Used to execute specific mobility operations, such as returning home, going to work, or visiting a specific location"
@@ -219,68 +229,79 @@ class MoveBlock(Block):
219
229
  # 这里应该添加移动的具体逻辑
220
230
  agent_id = await self.memory.status.get("id")
221
231
  self.placeAnalysisPrompt.format(
222
- plan = context['plan'],
223
- intention = step["intention"]
232
+ plan=context["plan"], intention=step["intention"]
224
233
  )
225
234
  number_poi_visited = await self.memory.status.get("number_poi_visited")
226
235
  number_poi_visited += 1
227
236
  await self.memory.status.update("number_poi_visited", number_poi_visited)
228
- response = await self.llm.atext_request(self.placeAnalysisPrompt.to_dialog()) # type: ignore
229
- if response == 'home':
237
+ response = await self.llm.atext_request(self.placeAnalysisPrompt.to_dialog()) # type: ignore
238
+ if response == "home":
230
239
  # 返回到家
231
- home = await self.memory.status.get('home')
232
- home = home['aoi_position']['aoi_id']
233
- nowPlace = await self.memory.status.get('position')
234
- node_id = await self.memory.stream.add_mobility(description=f"I returned home")
235
- if 'aoi_position' in nowPlace and nowPlace['aoi_position']['aoi_id'] == home:
240
+ home = await self.memory.status.get("home")
241
+ home = home["aoi_position"]["aoi_id"]
242
+ nowPlace = await self.memory.status.get("position")
243
+ node_id = await self.memory.stream.add_mobility(
244
+ description=f"I returned home"
245
+ )
246
+ if (
247
+ "aoi_position" in nowPlace
248
+ and nowPlace["aoi_position"]["aoi_id"] == home
249
+ ):
236
250
  return {
237
- 'success': True,
238
- 'evaluation': f'Successfully returned home (already at home)',
239
- 'to_place': home,
240
- 'consumed_time': 0,
241
- 'node_id': node_id
251
+ "success": True,
252
+ "evaluation": f"Successfully returned home (already at home)",
253
+ "to_place": home,
254
+ "consumed_time": 0,
255
+ "node_id": node_id,
242
256
  }
243
257
  await self.simulator.set_aoi_schedules(
244
258
  person_id=agent_id,
245
259
  target_positions=home,
246
260
  )
247
261
  return {
248
- 'success': True,
249
- 'evaluation': f'Successfully returned home',
250
- 'to_place': home,
251
- 'consumed_time': 45,
252
- 'node_id': node_id
262
+ "success": True,
263
+ "evaluation": f"Successfully returned home",
264
+ "to_place": home,
265
+ "consumed_time": 45,
266
+ "node_id": node_id,
253
267
  }
254
- elif response == 'workplace':
268
+ elif response == "workplace":
255
269
  # 返回到工作地点
256
- work = await self.memory.status.get('work')
257
- work = work['aoi_position']['aoi_id']
258
- nowPlace = await self.memory.status.get('position')
259
- node_id = await self.memory.stream.add_mobility(description=f"I went to my workplace")
260
- if 'aoi_position' in nowPlace and nowPlace['aoi_position']['aoi_id'] == work:
270
+ work = await self.memory.status.get("work")
271
+ work = work["aoi_position"]["aoi_id"]
272
+ nowPlace = await self.memory.status.get("position")
273
+ node_id = await self.memory.stream.add_mobility(
274
+ description=f"I went to my workplace"
275
+ )
276
+ if (
277
+ "aoi_position" in nowPlace
278
+ and nowPlace["aoi_position"]["aoi_id"] == work
279
+ ):
261
280
  return {
262
- 'success': True,
263
- 'evaluation': f'Successfully reached the workplace (already at the workplace)',
264
- 'to_place': work,
265
- 'consumed_time': 0,
266
- 'node_id': node_id
281
+ "success": True,
282
+ "evaluation": f"Successfully reached the workplace (already at the workplace)",
283
+ "to_place": work,
284
+ "consumed_time": 0,
285
+ "node_id": node_id,
267
286
  }
268
287
  await self.simulator.set_aoi_schedules(
269
288
  person_id=agent_id,
270
289
  target_positions=work,
271
290
  )
272
291
  return {
273
- 'success': True,
274
- 'evaluation': f'Successfully reached the workplace',
275
- 'to_place': work,
276
- 'consumed_time': 45,
277
- 'node_id': node_id
292
+ "success": True,
293
+ "evaluation": f"Successfully reached the workplace",
294
+ "to_place": work,
295
+ "consumed_time": 45,
296
+ "node_id": node_id,
278
297
  }
279
298
  else:
280
299
  # 移动到其他地点
281
- next_place = context.get('next_place', None)
282
- nowPlace = await self.memory.status.get('position')
283
- node_id = await self.memory.stream.add_mobility(description=f"I went to {next_place}")
300
+ next_place = context.get("next_place", None)
301
+ nowPlace = await self.memory.status.get("position")
302
+ node_id = await self.memory.stream.add_mobility(
303
+ description=f"I went to {next_place}"
304
+ )
284
305
  if next_place != None:
285
306
  await self.simulator.set_aoi_schedules(
286
307
  person_id=agent_id,
@@ -289,41 +310,46 @@ class MoveBlock(Block):
289
310
  else:
290
311
  while True:
291
312
  r_aoi = random.choice(list(self.simulator.map.aois.values()))
292
- if len(r_aoi['poi_ids']) > 0:
293
- r_poi = random.choice(r_aoi['poi_ids'])
313
+ if len(r_aoi["poi_ids"]) > 0:
314
+ r_poi = random.choice(r_aoi["poi_ids"])
294
315
  break
295
316
  poi = self.simulator.map.pois[r_poi]
296
- next_place = (poi['name'], poi['aoi_id'])
317
+ next_place = (poi["name"], poi["aoi_id"])
297
318
  await self.simulator.set_aoi_schedules(
298
319
  person_id=agent_id,
299
320
  target_positions=next_place[1],
300
321
  )
301
322
  return {
302
- 'success': True,
303
- 'evaluation': f'Successfully reached the destination: {next_place}',
304
- 'to_place': next_place[1],
305
- 'consumed_time': 45,
306
- 'node_id': node_id
323
+ "success": True,
324
+ "evaluation": f"Successfully reached the destination: {next_place}",
325
+ "to_place": next_place[1],
326
+ "consumed_time": 45,
327
+ "node_id": node_id,
307
328
  }
308
-
329
+
330
+
309
331
  class MobilityNoneBlock(Block):
310
332
  """
311
333
  空操作
312
334
  MobilityNoneBlock
313
335
  """
336
+
314
337
  def __init__(self, llm: LLM, memory: Memory):
315
338
  super().__init__("MobilityNoneBlock", llm=llm, memory=memory)
316
339
  self.description = "Used to handle other cases"
317
340
 
318
341
  async def forward(self, step, context):
319
- node_id = await self.memory.stream.add_mobility(description=f"I finished {step['intention']}")
342
+ node_id = await self.memory.stream.add_mobility(
343
+ description=f"I finished {step['intention']}"
344
+ )
320
345
  return {
321
- 'success': True,
322
- 'evaluation': f'Finished executing {step["intention"]}',
323
- 'consumed_time': 0,
324
- 'node_id': node_id
346
+ "success": True,
347
+ "evaluation": f'Finished executing {step["intention"]}',
348
+ "consumed_time": 0,
349
+ "node_id": node_id,
325
350
  }
326
351
 
352
+
327
353
  class MobilityBlock(Block):
328
354
  place_selection_block: PlaceSelectionBlock
329
355
  move_block: MoveBlock
@@ -340,19 +366,23 @@ class MobilityBlock(Block):
340
366
  # 初始化调度器
341
367
  self.dispatcher = BlockDispatcher(llm)
342
368
  # 注册所有块
343
- self.dispatcher.register_blocks([self.place_selection_block, self.move_block, self.mobility_none_block])
369
+ self.dispatcher.register_blocks(
370
+ [self.place_selection_block, self.move_block, self.mobility_none_block]
371
+ )
344
372
 
345
- async def forward(self, step, context):
373
+ async def forward(self, step, context):
346
374
  self.trigger_time += 1
347
- consumption_start = self.llm.prompt_tokens_used + self.llm.completion_tokens_used
375
+ consumption_start = (
376
+ self.llm.prompt_tokens_used + self.llm.completion_tokens_used
377
+ )
348
378
 
349
379
  # Select the appropriate sub-block using dispatcher
350
380
  selected_block = await self.dispatcher.dispatch(step)
351
-
381
+
352
382
  # Execute the selected sub-block and get the result
353
- result = await selected_block.forward(step, context) # type: ignore
383
+ result = await selected_block.forward(step, context) # type: ignore
354
384
 
355
385
  consumption_end = self.llm.prompt_tokens_used + self.llm.completion_tokens_used
356
386
  self.token_consumption += consumption_end - consumption_start
357
-
358
- return result
387
+
388
+ return result