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

Sign up to get free protection for your applications and to get access to all the features.
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
@@ -21,20 +21,18 @@ from pycityagent.workflow import Block
21
21
  from pycityagent.economy import EconomyClient
22
22
  from .utils import *
23
23
  import numbers
24
- import asyncio
25
24
 
26
-
27
-
28
-
29
25
  class WorkBlock(Block):
26
+ """WorkPlace Block"""
30
27
  def __init__(self, llm: LLM, memory: Memory, simulator: Simulator):
31
- super().__init__("WorkBlock", llm, memory, simulator)
28
+ super().__init__("WorkBlock", llm=llm, memory=memory, simulator=simulator)
32
29
  self.description = "Do work"
33
30
  self.guidance_prompt = FormatPrompt(template=TIME_ESTIMATE_PROMPT)
34
31
  async def forward(self, step, context):
35
32
  self.guidance_prompt.format(
36
33
  plan=context['plan'],
37
- intention=step['intention']
34
+ intention=step['intention'],
35
+ emotion_types=await self.memory.status.get("emotion_types"),
38
36
  )
39
37
  result = await self.llm.atext_request(self.guidance_prompt.to_dialog())
40
38
  result = clean_json_response(result)
@@ -42,108 +40,94 @@ class WorkBlock(Block):
42
40
  result = json.loads(result)
43
41
  time = result['time']
44
42
  start_time = await self.simulator.get_time(format_time=True)
45
- await self.memory.update("working_experience", [f"Start from {start_time}, worked {time} minutes on {step['intention']}"], mode="merge")
46
- work_hour_finish = await self.memory.get("work_hour_finish")
43
+ await self.memory.status.update("working_experience", [f"Start from {start_time}, worked {time} minutes on {step['intention']}"], mode="merge")
44
+ work_hour_finish = await self.memory.status.get("work_hour_finish")
47
45
  work_hour_finish += float(time/60)
48
- await self.memory.update("work_hour_finish", work_hour_finish)
46
+ node_id = await self.memory.stream.add_economy(description=f"I worked {time} minutes on {step['intention']}")
47
+ await self.memory.status.update("work_hour_finish", work_hour_finish)
49
48
  return {
50
49
  'success': True,
51
- 'evaluation': f'工作:{step["intention"]}',
52
- 'consumed_time': time
50
+ 'evaluation': f'work: {step["intention"]}',
51
+ 'consumed_time': time,
52
+ 'node_id': node_id
53
53
  }
54
54
  except Exception as e:
55
55
  logger.warning(f"解析时间评估响应时发生错误: {str(e)}, 原始结果: {result}")
56
56
  time = random.randint(1, 5)*60
57
57
  start_time = await self.simulator.get_time(format_time=True)
58
- await self.memory.update("working_experience", [f"Start from {start_time}, worked {time} minutes on {step['intention']}"], mode="merge")
59
- work_hour_finish = await self.memory.get("work_hour_finish")
58
+ await self.memory.status.update("working_experience", [f"Start from {start_time}, worked {time} minutes on {step['intention']}"], mode="merge")
59
+ work_hour_finish = await self.memory.status.get("work_hour_finish")
60
+ node_id = await self.memory.stream.add_economy(description=f"I worked {time} minutes on {step['intention']}")
60
61
  work_hour_finish += float(time/60)
61
- await self.memory.update("work_hour_finish", work_hour_finish)
62
+ await self.memory.status.update("work_hour_finish", work_hour_finish)
62
63
  return {
63
64
  'success': True,
64
- 'evaluation': f'工作:{step["intention"]}',
65
- 'consumed_time': time
65
+ 'evaluation': f'work: {step["intention"]}',
66
+ 'consumed_time': time,
67
+ 'node_id': node_id
66
68
  }
67
69
 
68
70
  class ConsumptionBlock(Block):
69
71
  """
70
- determine the consumption place, time, amount, and items
72
+ determine the consumption amount, and items
71
73
  """
72
74
  def __init__(self, llm: LLM, memory: Memory, simulator: Simulator, economy_client: EconomyClient):
73
- super().__init__("ConsumptionBlock", llm, memory, simulator)
75
+ super().__init__("ConsumptionBlock", llm=llm, memory=memory, simulator=simulator)
74
76
  self.economy_client = economy_client
75
77
  self.forward_times = 0
76
- self.description = "Used to determine the consumption place, time, amount, and items"
78
+ self.description = "Used to determine the consumption amount, and items"
77
79
 
78
80
  async def forward(self, step, context):
79
81
  self.forward_times += 1
80
- agent_id = await self.memory.get("id") # agent_id
81
- firm_id = await self.memory.get('firm_id')
82
+ agent_id = await self.memory.status.get("id") # agent_id
83
+ firm_id = await self.memory.status.get('firm_id')
82
84
  intention = step["intention"]
83
- nowPlace = await self.memory.get('position')
84
- nowPlace = nowPlace['xy_position']
85
- nowPlace = (nowPlace['x'], nowPlace['y'])
86
- all_poi_types = self.simulator.get_poi_categories(nowPlace, 2000)
87
- if len(all_poi_types) == 0:
88
- all_poi_types.extend(['supermarket'])
89
- prompt = 'Your intention is to {intention}. Which type of POI do you want to go to? Select one and only one type from the following types: {all_poi_types}. Any other output is NOT acceptable.'
90
- prompt = FormatPrompt(prompt)
91
- prompt.format(intention=intention, all_poi_types=all_poi_types)
92
- poi_type = await self.llm.atext_request(prompt.to_dialog(), timeout=300)
93
- poi_type = poi_type.strip('[').strip(']')
94
- if poi_type not in all_poi_types:
95
- poi_type = random.choice(all_poi_types)
96
- around_pois = self.simulator.get_around_poi(nowPlace, 2000, poi_type)
97
- if len(around_pois) == 0:
98
- around_pois.extend(['沃尔玛', '家乐福', '华润万家', '物美'])
99
- prompt = """
100
- You're a {gender} and your education level is {education}.
101
- Your intention is to {intention} and you plan to consume for ${consumption}.
102
- The follwing are the places you can go to consume:
103
- {around_pois}
104
- Where should you go to make a purchase, when should you go, and what items will you spend on?
105
- Respond with this format: [place, time, items to buy]. Any other output is NOT acceptable.
106
- """
107
- prompt = FormatPrompt(prompt)
108
- data = {key: await self.memory.get(key) for key in ['gender', 'education']}
109
- data['around_pois'] = around_pois
110
- data['intention'] = intention
111
- month_consumption = await self.memory.get('to_consumption_currency')
112
- consumption_currency = await self.memory.get('consumption_currency')
85
+ month_consumption = await self.memory.status.get('to_consumption_currency')
86
+ consumption_currency = await self.memory.status.get('consumption_currency')
113
87
  if consumption_currency >= month_consumption:
114
- return {'Success': True, 'Evaluation': f"Consumption Done", 'consumed_time': 0}
115
- data['consumption'] = min(month_consumption/1, month_consumption-consumption_currency)
88
+ node_id = await self.memory.stream.add_economy(description=f"I have passed the monthly consumption limit, so I will not consume.")
89
+ return {
90
+ 'success': False,
91
+ 'evaluation': f"I have passed the monthly consumption limit, so I will not consume.",
92
+ 'consumed_time': 0,
93
+ 'node_id': node_id
94
+ }
95
+ consumption = min(month_consumption/1, month_consumption-consumption_currency)
116
96
  price = await self.economy_client.get(firm_id, 'price') # agent_id
117
97
  wealth = await self.economy_client.get(agent_id, 'currency')
118
- this_demand = int(data['consumption']//price)
98
+ this_demand = int(consumption//price)
119
99
  _, post_consumption_wealth = await self.economy_client.calculate_consumption(firm_id, [agent_id], [this_demand])
120
100
  post_consumption_wealth = post_consumption_wealth[0]
121
- await self.memory.update('consumption_currency', consumption_currency+wealth-post_consumption_wealth)
122
- goods_demand = await self.memory.get('goods_demand')
123
- goods_consumption = await self.memory.get('goods_consumption')
124
- await self.memory.update('goods_demand', goods_demand+this_demand)
125
- await self.memory.update('goods_consumption', goods_consumption+int((wealth-post_consumption_wealth)//price))
126
- prompt.format(**data)
127
- response = await self.llm.atext_request(prompt.to_dialog(), timeout=300)
128
- evaluation = {'success': True, 'evaluation': f"Consumption: {response}", 'consumed_time': 100}
101
+ await self.memory.status.update('consumption_currency', consumption_currency+wealth-post_consumption_wealth)
102
+ goods_demand = await self.memory.status.get('goods_demand')
103
+ goods_consumption = await self.memory.status.get('goods_consumption')
104
+ await self.memory.status.update('goods_demand', goods_demand+this_demand)
105
+ await self.memory.status.update('goods_consumption', goods_consumption+int((wealth-post_consumption_wealth)//price))
106
+ node_id = await self.memory.stream.add_economy(description=f"I bought some goods, and spent {this_demand} on {intention}")
107
+ evaluation = {
108
+ 'success': True,
109
+ 'evaluation': f"I bought some goods, and spent {this_demand} on {intention}",
110
+ 'consumed_time': 20,
111
+ 'node_id': node_id
112
+ }
129
113
  return evaluation
130
114
 
131
115
  class EconomyNoneBlock(Block):
132
116
  """
133
- Do nothing
117
+ Do anything else
134
118
  NoneBlock
135
119
  """
136
120
  def __init__(self, llm: LLM, memory: Memory):
137
- super().__init__("NoneBlock", llm, memory)
138
- self.description = "Do nothing"
139
- self.forward_times = 0
121
+ super().__init__("NoneBlock", llm=llm, memory=memory)
122
+ self.description = "Do anything else"
140
123
 
141
124
  async def forward(self, step, context):
142
- self.forward_times += 1
125
+ node_id = await self.memory.stream.add_economy(description=f"I {step['intention']}")
143
126
  return {
144
127
  'success': True,
145
- 'evaluation': f'完成执行{step["intention"]}',
146
- 'consumed_time': 0
128
+ 'evaluation': f'Finished{step["intention"]}',
129
+ 'consumed_time': 0,
130
+ 'node_id': node_id
147
131
  }
148
132
 
149
133
  class EconomyBlock(Block):
@@ -152,37 +136,26 @@ class EconomyBlock(Block):
152
136
  none_block: EconomyNoneBlock
153
137
 
154
138
  def __init__(self, llm: LLM, memory: Memory, simulator: Simulator, economy_client: EconomyClient):
155
- super().__init__("EconomyBlock", llm, memory, simulator)
139
+ super().__init__("EconomyBlock", llm=llm, memory=memory, simulator=simulator)
156
140
  self.economy_client = economy_client
157
- # 初始化所有块
158
141
  self.work_block = WorkBlock(llm, memory, simulator)
159
142
  self.consumption_block = ConsumptionBlock(llm, memory, simulator, economy_client)
160
143
  self.none_block = EconomyNoneBlock(llm, memory)
161
144
  self.trigger_time = 0
162
145
  self.token_consumption = 0
163
- # 初始化调度器
164
146
  self.dispatcher = BlockDispatcher(llm)
165
- # 注册所有块
166
147
  self.dispatcher.register_blocks([self.work_block, self.consumption_block, self.none_block])
167
148
 
168
149
  async def forward(self, step, context):
169
150
  self.trigger_time += 1
170
- consumption_start = self.llm.prompt_tokens_used + self.llm.completion_tokens_used
171
-
172
- # Select the appropriate sub-block using dispatcher
173
- # selected_block = await self.dispatcher.dispatch(step)
174
151
  selected_block = self.consumption_block
175
-
176
- # Execute the selected sub-block and get the result
177
- result = await selected_block.forward(step, context) # type: ignore
178
-
152
+ result = await selected_block.forward(step, context) # type: ignore
179
153
  return result
180
154
 
181
- # @trigger_class()
182
155
  class MonthPlanBlock(Block):
183
156
  """Monthly Planning"""
184
157
  def __init__(self, llm: LLM, memory: Memory, simulator: Simulator, economy_client: EconomyClient):
185
- super().__init__("MonthPlanBlock", llm, memory, simulator)
158
+ super().__init__("MonthPlanBlock", llm=llm, memory=memory, simulator=simulator)
186
159
  self.economy_client = economy_client
187
160
  self.llm_error = 0
188
161
  self.last_time_trigger = None
@@ -198,26 +171,16 @@ class MonthPlanBlock(Block):
198
171
 
199
172
  async def forward(self):
200
173
  if await self.month_trigger():
201
- # while True:
202
- # if self.forward_times == 0:
203
- # break
204
- # firm_forward = await self.memory.get('firm_forward')
205
- # bank_forward = await self.memory.get('bank_forward')
206
- # nbs_forward = await self.memory.get('nbs_forward')
207
- # government_forward = await self.memory.get('government_forward')
208
- # if self.forward_times <= firm_forward and self.forward_times <= bank_forward and self.forward_times <= nbs_forward and self.forward_times <= government_forward:
209
- # break
210
- # await asyncio.sleep(1)
211
- agent_id = await self.memory.get("id")
212
- firm_id = await self.memory.get('firm_id')
213
- bank_id = await self.memory.get('bank_id')
214
- name = await self.memory.get('name')
215
- age = await self.memory.get('age')
216
- city = await self.memory.get('city')
217
- job = await self.memory.get('occupation')
218
- skill = await self.memory.get('work_skill')
219
- consumption = await self.memory.get('consumption_currency')
220
- tax_paid = await self.memory.get('tax_paid')
174
+ agent_id = await self.memory.status.get("id")
175
+ firm_id = await self.memory.status.get('firm_id')
176
+ bank_id = await self.memory.status.get('bank_id')
177
+ name = await self.memory.status.get('name')
178
+ age = await self.memory.status.get('age')
179
+ city = await self.memory.status.get('city')
180
+ job = await self.memory.status.get('occupation')
181
+ skill = await self.memory.status.get('work_skill')
182
+ consumption = await self.memory.status.get('consumption_currency')
183
+ tax_paid = await self.memory.status.get('tax_paid')
221
184
  price = await self.economy_client.get(firm_id, 'price')
222
185
  wealth = await self.economy_client.get(agent_id, 'currency')
223
186
  interest_rate = await self.economy_client.get(bank_id, 'interest_rate')
@@ -228,7 +191,7 @@ class MonthPlanBlock(Block):
228
191
  job_prompt = f'''
229
192
  In the previous month, you worked as a(an) {job}. If you continue working this month, your expected hourly income will be ${skill:.2f}.
230
193
  '''
231
- consumption_propensity = await self.memory.get('consumption_propensity')
194
+ consumption_propensity = await self.memory.status.get('consumption_propensity')
232
195
  if (consumption <= 0) and (consumption_propensity > 0):
233
196
  consumption_prompt = f'''
234
197
  Besides, you had no consumption due to shortage of goods.
@@ -238,7 +201,7 @@ class MonthPlanBlock(Block):
238
201
  Besides, your consumption was ${consumption:.2f}.
239
202
  '''
240
203
  tax_prompt = f'''Your tax deduction amounted to ${tax_paid:.2f}, and the government uses the tax revenue to provide social services to all citizens.'''
241
- if UBI:
204
+ if UBI and self.forward_times >= 96:
242
205
  tax_prompt = f'{tax_prompt} Specifically, the government directly provides ${UBI} per capita in each month.'
243
206
  price_prompt = f'''Meanwhile, in the consumption market, the average price of essential goods is now at ${price:.2f}.'''
244
207
  job_prompt = prettify_document(job_prompt)
@@ -254,41 +217,42 @@ class MonthPlanBlock(Block):
254
217
  Any other output words are NOT allowed.
255
218
  '''
256
219
  obs_prompt = prettify_document(obs_prompt)
257
- await self.memory.update('dialog_queue', [{'role': 'user', 'content': obs_prompt}], mode='merge')
258
- dialog_queue = await self.memory.get('dialog_queue')
220
+ await self.memory.status.update('dialog_queue', [{'role': 'user', 'content': obs_prompt}], mode='merge')
221
+ dialog_queue = await self.memory.status.get('dialog_queue')
259
222
  content = await self.llm.atext_request(list(dialog_queue), timeout=300)
260
- await self.memory.update('dialog_queue', [{'role': 'assistant', 'content': content}], mode='merge')
223
+ await self.memory.status.update('dialog_queue', [{'role': 'assistant', 'content': content}], mode='merge')
261
224
  try:
262
225
  propensity_dict = extract_dict_from_string(content)[0]
263
226
  work_propensity, consumption_propensity = propensity_dict['work'], propensity_dict['consumption']
264
227
  if isinstance(work_propensity, numbers.Number) and isinstance(consumption_propensity, numbers.Number):
265
- await self.memory.update('work_propensity', work_propensity)
266
- await self.memory.update('consumption_propensity', consumption_propensity)
228
+ await self.memory.status.update('work_propensity', work_propensity)
229
+ await self.memory.status.update('consumption_propensity', consumption_propensity)
267
230
  else:
268
231
  self.llm_error += 1
269
232
  except:
270
233
  self.llm_error += 1
271
234
 
272
- skill = await self.memory.get('work_skill')
273
- work_propensity = await self.memory.get('work_propensity')
274
- consumption_propensity = await self.memory.get('consumption_propensity')
235
+ skill = await self.memory.status.get('work_skill')
236
+ work_propensity = await self.memory.status.get('work_propensity')
237
+ consumption_propensity = await self.memory.status.get('consumption_propensity')
275
238
  work_hours = work_propensity * num_labor_hours
276
- await self.memory.update('income_currency', work_hours * skill)
239
+ await self.memory.status.update('income_currency', work_hours * skill)
277
240
  wealth = await self.economy_client.get(agent_id, 'currency')
278
241
  await self.economy_client.update(agent_id, 'currency', wealth + work_hours * skill)
279
242
  await self.economy_client.add_delta_value(firm_id, 'inventory', int(work_hours*productivity_per_labor))
280
243
 
281
- income_currency = await self.memory.get('income_currency')
282
- await self.memory.update('income_currency', income_currency + UBI)
283
- wealth = await self.economy_client.get(agent_id, 'currency')
284
- await self.economy_client.update(agent_id, 'currency', wealth + UBI)
244
+ if UBI and self.forward_times >= 96:
245
+ income_currency = await self.memory.status.get('income_currency')
246
+ await self.memory.status.update('income_currency', income_currency + UBI)
247
+ wealth = await self.economy_client.get(agent_id, 'currency')
248
+ await self.economy_client.update(agent_id, 'currency', wealth + UBI)
285
249
 
286
250
  wealth = await self.economy_client.get(agent_id, 'currency')
287
- await self.memory.update('to_consumption_currency', consumption_propensity*wealth)
251
+ await self.memory.status.update('to_consumption_currency', consumption_propensity*wealth)
288
252
 
289
- await self.memory.update('consumption_currency', 0)
290
- await self.memory.update('goods_demand', 0)
291
- await self.memory.update('goods_consumption', 0)
253
+ await self.memory.status.update('consumption_currency', 0)
254
+ await self.memory.status.update('goods_demand', 0)
255
+ await self.memory.status.update('goods_consumption', 0)
292
256
 
293
257
  if self.forward_times % 3 == 0:
294
258
  obs_prompt = f'''
@@ -335,11 +299,11 @@ class MonthPlanBlock(Block):
335
299
  else:
336
300
  content[k] = category2score[content[k].lower()]
337
301
  depression = sum(list(content.values()))
338
- await self.memory.update('depression', depression)
302
+ await self.memory.status.update('depression', depression)
339
303
  except:
340
304
  self.llm_error += 1
341
305
 
342
- if UBI and self.forward_times % 12:
306
+ if UBI and self.forward_times >= 96 and self.forward_times % 12:
343
307
  obs_prompt = f'''
344
308
  {problem_prompt} {job_prompt} {consumption_prompt} {tax_prompt} {price_prompt}
345
309
  Your current savings account balance is ${wealth:.2f}. Interest rates, as set by your bank, stand at {interest_rate*100:.2f}%.
@@ -347,10 +311,7 @@ class MonthPlanBlock(Block):
347
311
  '''
348
312
  obs_prompt = prettify_document(obs_prompt)
349
313
  content = await self.llm.atext_request([{'role': 'user', 'content': obs_prompt}], timeout=300)
350
- await self.memory.update('ubi_opinion', [content], mode='merge')
351
- ubi_opinion = await self.memory.get('ubi_opinion')
352
- with open(f'/data1/linian/SocietySim/socialcity-agent-zoo/ubi_opinions/{agent_id}.pkl', 'wb') as f:
353
- pkl.dump(ubi_opinion, f)
314
+ await self.memory.status.update('ubi_opinion', [content], mode='merge')
354
315
 
355
316
  self.forward_times += 1
356
- await self.memory.update('forward', self.forward_times)
317
+ await self.memory.status.update('forward', self.forward_times)
@@ -1,8 +1,8 @@
1
1
  from typing import List
2
2
  from .dispatcher import BlockDispatcher
3
3
  from pycityagent.environment.simulator import Simulator
4
- from pycityagent.llm.llm import LLM
5
- from pycityagent.memory.memory import Memory
4
+ from pycityagent.llm import LLM
5
+ from pycityagent.memory import Memory
6
6
  from pycityagent.workflow.block import Block
7
7
  import random
8
8
  import logging
@@ -31,25 +31,32 @@ User requirement: {intention}
31
31
  Your output must be a single selection from ['home', 'workplace', 'other'] without any additional text or explanation.
32
32
  """
33
33
 
34
+ RADIUS_PROMPT = """As an intelligent decision system, please determine the maximum travel radius (in meters) based on the current emotional state.
35
+
36
+ Your current emotion: {emotion_types}
37
+ Your current thought: {thought}
38
+
39
+ Please analyze how these emotions would affect travel willingness and return only a single integer number between 1000-100000 representing the maximum travel radius in meters. A more positive emotional state generally leads to greater willingness to travel further.
40
+
41
+ Return only the integer number without any additional text or explanation."""
42
+
34
43
  class PlaceSelectionBlock(Block):
35
44
  """
36
45
  选择目的地
37
46
  PlaceSelectionBlock
38
47
  """
39
- configurable_fields: List[str] = ["search_radius", "search_limit"]
48
+ configurable_fields: List[str] = ["search_limit"]
40
49
  default_values = {
41
- "search_radius": 100000,
42
50
  "search_limit": 10
43
51
  }
44
52
 
45
53
  def __init__(self, llm: LLM, memory: Memory, simulator: Simulator):
46
- super().__init__("PlaceSelectionBlock", llm, memory, simulator)
54
+ super().__init__("PlaceSelectionBlock", llm=llm, memory=memory, simulator=simulator)
47
55
  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"
48
56
  self.typeSelectionPrompt = FormatPrompt(PLACE_TYPE_SELECTION_PROMPT)
49
57
  self.secondTypeSelectionPrompt = FormatPrompt(PLACE_SECOND_TYPE_SELECTION_PROMPT)
50
-
58
+ self.radiusPrompt = FormatPrompt(RADIUS_PROMPT)
51
59
  # configurable fields
52
- self.search_radius = 100000
53
60
  self.search_limit = 10
54
61
 
55
62
  async def forward(self, step, context):
@@ -71,13 +78,18 @@ class PlaceSelectionBlock(Block):
71
78
  poi_category = sub_category
72
79
  )
73
80
  levelTwoType = await self.llm.atext_request(self.secondTypeSelectionPrompt.to_dialog()) # type: ignore
74
- center = await self.memory.get('position')
81
+ center = await self.memory.status.get('position')
75
82
  center = (center['xy_position']['x'], center['xy_position']['y'])
83
+ self.radiusPrompt.format(
84
+ emotion_types=await self.memory.status.get("emotion_types"),
85
+ thought=await self.memory.status.get("thought")
86
+ )
87
+ radius = int(await self.llm.atext_request(self.radiusPrompt.to_dialog())) # type: ignore
76
88
  try:
77
89
  pois = self.simulator.map.query_pois(
78
90
  center = center,
79
91
  category_prefix = levelTwoType,
80
- radius=self.search_radius,
92
+ radius=radius,
81
93
  limit=self.search_limit
82
94
  )
83
95
  except Exception as e:
@@ -86,7 +98,7 @@ class PlaceSelectionBlock(Block):
86
98
  pois = self.simulator.map.query_pois(
87
99
  center = center,
88
100
  category_prefix = levelTwoType,
89
- radius=self.search_radius,
101
+ radius=radius,
90
102
  limit=self.search_limit
91
103
  )
92
104
  if len(pois) > 0:
@@ -94,11 +106,12 @@ class PlaceSelectionBlock(Block):
94
106
  nextPlace = (poi['name'], poi['aoi_id'])
95
107
  # 将地点信息保存到context中
96
108
  context['next_place'] = nextPlace
97
- # 这里应该添加选择地点的具体逻辑
109
+ node_id = await self.memory.stream.add_mobility(description=f"For {step['intention']}, I selected the destination: {nextPlace}")
98
110
  return {
99
111
  'success': True,
100
112
  'evaluation': f'Successfully selected the destination: {nextPlace}',
101
- 'consumed_time': 5
113
+ 'consumed_time': 5,
114
+ 'node_id': node_id
102
115
  }
103
116
  else:
104
117
  simmap = self.simulator.map
@@ -106,11 +119,12 @@ class PlaceSelectionBlock(Block):
106
119
  nextPlace = (poi['name'], poi['aoi_id'])
107
120
  # 将地点信息保存到context中
108
121
  context['next_place'] = nextPlace
109
- # 这里应该添加选择地点的具体逻辑
122
+ node_id = await self.memory.stream.add_mobility(description=f"For {step['intention']}, I selected the destination: {nextPlace}")
110
123
  return {
111
124
  'success': True,
112
125
  'evaluation': f'Successfully selected the destination: {nextPlace}',
113
- 'consumed_time': 5
126
+ 'consumed_time': 5,
127
+ 'node_id': node_id
114
128
  }
115
129
 
116
130
  class MoveBlock(Block):
@@ -119,30 +133,35 @@ class MoveBlock(Block):
119
133
  MoveBlock
120
134
  """
121
135
  def __init__(self, llm: LLM, memory: Memory, simulator: Simulator):
122
- super().__init__("MoveBlock", llm, memory, simulator)
136
+ super().__init__("MoveBlock", llm=llm, memory=memory, simulator=simulator)
123
137
  self.description = "Used to execute specific mobility operations, such as returning home, going to work, or visiting a specific location"
124
138
  self.placeAnalysisPrompt = FormatPrompt(PLACE_ANALYSIS_PROMPT)
125
139
 
126
140
  async def forward(self, step, context):
127
141
  # 这里应该添加移动的具体逻辑
128
- agent_id = await self.memory.get("id")
142
+ agent_id = await self.memory.status.get("id")
129
143
  self.placeAnalysisPrompt.format(
130
144
  plan = context['plan'],
131
145
  intention = step["intention"]
132
146
  )
147
+ number_poi_visited = await self.memory.status.get("number_poi_visited")
148
+ number_poi_visited += 1
149
+ await self.memory.status.update("number_poi_visited", number_poi_visited)
133
150
  response = await self.llm.atext_request(self.placeAnalysisPrompt.to_dialog()) # type: ignore
134
151
  if response == 'home':
135
152
  # 返回到家
136
- home = await self.memory.get('home')
153
+ home = await self.memory.status.get('home')
137
154
  home = home['aoi_position']['aoi_id']
138
- nowPlace = await self.memory.get('position')
155
+ nowPlace = await self.memory.status.get('position')
156
+ node_id = await self.memory.stream.add_mobility(description=f"I returned home")
139
157
  if 'aoi_position' in nowPlace and nowPlace['aoi_position']['aoi_id'] == home:
140
158
  return {
141
159
  'success': True,
142
160
  'evaluation': f'Successfully returned home (already at home)',
143
161
  'from_place': home,
144
162
  'to_place': home,
145
- 'consumed_time': 0
163
+ 'consumed_time': 0,
164
+ 'node_id': node_id
146
165
  }
147
166
  await self.simulator.set_aoi_schedules(
148
167
  person_id=agent_id,
@@ -153,20 +172,23 @@ class MoveBlock(Block):
153
172
  'evaluation': f'Successfully returned home',
154
173
  'from_place': nowPlace['aoi_position']['aoi_id'],
155
174
  'to_place': home,
156
- 'consumed_time': 45
175
+ 'consumed_time': 45,
176
+ 'node_id': node_id
157
177
  }
158
178
  elif response == 'workplace':
159
179
  # 返回到工作地点
160
- work = await self.memory.get('work')
180
+ work = await self.memory.status.get('work')
161
181
  work = work['aoi_position']['aoi_id']
162
- nowPlace = await self.memory.get('position')
182
+ nowPlace = await self.memory.status.get('position')
183
+ node_id = await self.memory.stream.add_mobility(description=f"I went to my workplace")
163
184
  if 'aoi_position' in nowPlace and nowPlace['aoi_position']['aoi_id'] == work:
164
185
  return {
165
186
  'success': True,
166
187
  'evaluation': f'Successfully reached the workplace (already at the workplace)',
167
188
  'from_place': work,
168
189
  'to_place': work,
169
- 'consumed_time': 0
190
+ 'consumed_time': 0,
191
+ 'node_id': node_id
170
192
  }
171
193
  await self.simulator.set_aoi_schedules(
172
194
  person_id=agent_id,
@@ -177,12 +199,14 @@ class MoveBlock(Block):
177
199
  'evaluation': f'Successfully reached the workplace',
178
200
  'from_place': nowPlace['aoi_position']['aoi_id'],
179
201
  'to_place': work,
180
- 'consumed_time': 45
202
+ 'consumed_time': 45,
203
+ 'node_id': node_id
181
204
  }
182
205
  else:
183
206
  # 移动到其他地点
184
207
  next_place = context.get('next_place', None)
185
- nowPlace = await self.memory.get('position')
208
+ nowPlace = await self.memory.status.get('position')
209
+ node_id = await self.memory.stream.add_mobility(description=f"I went to {next_place}")
186
210
  if next_place != None:
187
211
  await self.simulator.set_aoi_schedules(
188
212
  person_id=agent_id,
@@ -205,7 +229,8 @@ class MoveBlock(Block):
205
229
  'evaluation': f'Successfully reached the destination: {next_place}',
206
230
  'from_place': nowPlace['aoi_position']['aoi_id'],
207
231
  'to_place': next_place[1],
208
- 'consumed_time': 45
232
+ 'consumed_time': 45,
233
+ 'node_id': node_id
209
234
  }
210
235
 
211
236
  class MobilityNoneBlock(Block):
@@ -214,14 +239,16 @@ class MobilityNoneBlock(Block):
214
239
  MobilityNoneBlock
215
240
  """
216
241
  def __init__(self, llm: LLM, memory: Memory):
217
- super().__init__("MobilityNoneBlock", llm, memory)
242
+ super().__init__("MobilityNoneBlock", llm=llm, memory=memory)
218
243
  self.description = "Used to handle other cases"
219
244
 
220
245
  async def forward(self, step, context):
246
+ node_id = await self.memory.stream.add_mobility(description=f"I finished {step['intention']}")
221
247
  return {
222
248
  'success': True,
223
249
  'evaluation': f'Finished executing {step["intention"]}',
224
- 'consumed_time': 0
250
+ 'consumed_time': 0,
251
+ 'node_id': node_id
225
252
  }
226
253
 
227
254
  class MobilityBlock(Block):
@@ -230,7 +257,7 @@ class MobilityBlock(Block):
230
257
  mobility_none_block: MobilityNoneBlock
231
258
 
232
259
  def __init__(self, llm: LLM, memory: Memory, simulator: Simulator):
233
- super().__init__("MobilityBlock", llm, memory, simulator)
260
+ super().__init__("MobilityBlock", llm=llm, memory=memory, simulator=simulator)
234
261
  # 初始化所有块
235
262
  self.place_selection_block = PlaceSelectionBlock(llm, memory, simulator)
236
263
  self.move_block = MoveBlock(llm, memory, simulator)