pycityagent 2.0.0a52__cp311-cp311-macosx_11_0_arm64.whl → 2.0.0a54__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 (49) hide show
  1. pycityagent/agent/agent.py +83 -62
  2. pycityagent/agent/agent_base.py +81 -54
  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 +45 -57
  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/message_intercept.py +99 -0
  17. pycityagent/cityagent/nbsagent.py +6 -29
  18. pycityagent/cityagent/societyagent.py +325 -127
  19. pycityagent/cli/wrapper.py +4 -0
  20. pycityagent/economy/econ_client.py +0 -2
  21. pycityagent/environment/__init__.py +7 -1
  22. pycityagent/environment/sim/client.py +10 -1
  23. pycityagent/environment/sim/clock_service.py +2 -2
  24. pycityagent/environment/sim/pause_service.py +61 -0
  25. pycityagent/environment/sim/sim_env.py +34 -46
  26. pycityagent/environment/simulator.py +18 -14
  27. pycityagent/llm/embeddings.py +0 -24
  28. pycityagent/llm/llm.py +18 -10
  29. pycityagent/memory/faiss_query.py +29 -26
  30. pycityagent/memory/memory.py +733 -247
  31. pycityagent/message/__init__.py +8 -1
  32. pycityagent/message/message_interceptor.py +322 -0
  33. pycityagent/message/messager.py +42 -11
  34. pycityagent/pycityagent-sim +0 -0
  35. pycityagent/simulation/agentgroup.py +137 -96
  36. pycityagent/simulation/simulation.py +184 -38
  37. pycityagent/simulation/storage/pg.py +2 -2
  38. pycityagent/tools/tool.py +7 -9
  39. pycityagent/utils/__init__.py +7 -2
  40. pycityagent/utils/pg_query.py +1 -0
  41. pycityagent/utils/survey_util.py +26 -23
  42. pycityagent/workflow/block.py +14 -7
  43. {pycityagent-2.0.0a52.dist-info → pycityagent-2.0.0a54.dist-info}/METADATA +2 -2
  44. {pycityagent-2.0.0a52.dist-info → pycityagent-2.0.0a54.dist-info}/RECORD +48 -46
  45. pycityagent/cityagent/blocks/time_block.py +0 -116
  46. {pycityagent-2.0.0a52.dist-info → pycityagent-2.0.0a54.dist-info}/LICENSE +0 -0
  47. {pycityagent-2.0.0a52.dist-info → pycityagent-2.0.0a54.dist-info}/WHEEL +0 -0
  48. {pycityagent-2.0.0a52.dist-info → pycityagent-2.0.0a54.dist-info}/entry_points.txt +0 -0
  49. {pycityagent-2.0.0a52.dist-info → pycityagent-2.0.0a54.dist-info}/top_level.txt +0 -0
@@ -13,7 +13,7 @@ from ..economy import EconomyClient
13
13
  from ..environment import Simulator
14
14
  from ..llm import LLM
15
15
  from ..memory import Memory
16
- from ..message.messager import Messager
16
+ from ..message import MessageInterceptor, Messager
17
17
  from ..metrics import MlflowClient
18
18
  from .agent_base import Agent, AgentType
19
19
 
@@ -30,10 +30,10 @@ class CitizenAgent(Agent):
30
30
  name: str,
31
31
  llm_client: Optional[LLM] = None,
32
32
  simulator: Optional[Simulator] = None,
33
- mlflow_client: Optional[MlflowClient] = None,
34
33
  memory: Optional[Memory] = None,
35
34
  economy_client: Optional[EconomyClient] = None,
36
35
  messager: Optional[Messager] = None, # type:ignore
36
+ message_interceptor: Optional[MessageInterceptor] = None, # type:ignore
37
37
  avro_file: Optional[dict] = None,
38
38
  ) -> None:
39
39
  super().__init__(
@@ -42,11 +42,12 @@ class CitizenAgent(Agent):
42
42
  llm_client=llm_client,
43
43
  economy_client=economy_client,
44
44
  messager=messager,
45
+ message_interceptor=message_interceptor,
45
46
  simulator=simulator,
46
- mlflow_client=mlflow_client,
47
47
  memory=memory,
48
48
  avro_file=avro_file,
49
49
  )
50
+ self._mlflow_client = None
50
51
 
51
52
  async def bind_to_simulator(self):
52
53
  await self._bind_to_simulator()
@@ -62,42 +63,31 @@ class CitizenAgent(Agent):
62
63
  if self._simulator is None:
63
64
  logger.warning("Simulator is not set")
64
65
  return
65
- if not self._has_bound_to_simulator:
66
- FROM_MEMORY_KEYS = {
67
- "attribute",
68
- "home",
69
- "work",
70
- "vehicle_attribute",
71
- "bus_attribute",
72
- "pedestrian_attribute",
73
- "bike_attribute",
74
- }
75
- simulator = self.simulator
76
- memory = self.memory
77
- person_id = await memory.get("id")
78
- # ATTENTION:模拟器分配的id从0开始
79
- if person_id >= 0:
80
- await simulator.get_person(person_id)
81
- logger.debug(f"Binding to Person `{person_id}` already in Simulator")
82
- else:
83
- dict_person = deepcopy(self._person_template)
84
- for _key in FROM_MEMORY_KEYS:
85
- try:
86
- _value = await memory.get(_key)
87
- if _value:
88
- dict_person[_key] = _value
89
- except KeyError as e:
90
- continue
91
- resp = await simulator.add_person(
92
- dict2pb(dict_person, person_pb2.Person())
93
- )
94
- person_id = resp["person_id"]
95
- await memory.update("id", person_id, protect_llm_read_only_fields=False)
96
- logger.debug(f"Binding to Person `{person_id}` just added to Simulator")
97
- # 防止模拟器还没有到prepare阶段导致get_person出错
98
- self._has_bound_to_simulator = True
99
- self._agent_id = person_id
100
- self.memory.set_agent_id(person_id)
66
+ FROM_MEMORY_KEYS = {
67
+ "attribute",
68
+ "home",
69
+ "work",
70
+ "vehicle_attribute",
71
+ "bus_attribute",
72
+ "pedestrian_attribute",
73
+ "bike_attribute",
74
+ }
75
+ simulator = self.simulator
76
+ status = self.status
77
+ dict_person = deepcopy(self._person_template)
78
+ for _key in FROM_MEMORY_KEYS:
79
+ try:
80
+ _value = await status.get(_key)
81
+ if _value:
82
+ dict_person[_key] = _value
83
+ except KeyError as e:
84
+ continue
85
+ resp = await simulator.add_person(dict2pb(dict_person, person_pb2.Person()))
86
+ person_id = resp["person_id"]
87
+ await status.update("id", person_id, protect_llm_read_only_fields=False)
88
+ logger.debug(f"Binding to Person `{person_id}` just added to Simulator")
89
+ self._agent_id = person_id
90
+ self.status.set_agent_id(person_id)
101
91
 
102
92
  async def _bind_to_economy(self):
103
93
  if self._economy_client is None:
@@ -109,8 +99,8 @@ class CitizenAgent(Agent):
109
99
  await self._economy_client.remove_agents([self._agent_id])
110
100
  except:
111
101
  pass
112
- person_id = await self.memory.get("id")
113
- currency = await self.memory.get("currency")
102
+ person_id = await self.status.get("id")
103
+ currency = await self.status.get("currency")
114
104
  await self._economy_client.add_agents(
115
105
  {
116
106
  "id": person_id,
@@ -128,17 +118,32 @@ class CitizenAgent(Agent):
128
118
  # 从消息中解析发送者 ID 和消息内容
129
119
  target = payload["target"]
130
120
  sender_id = payload["from"]
131
- content = await self.memory.get(f"{target}")
121
+ content = await self.status.get(f"{target}")
132
122
  payload = {
133
123
  "from": self._uuid,
134
124
  "content": content,
135
125
  }
136
126
  await self._send_message(sender_id, payload, "gather")
137
127
 
128
+ @property
129
+ def mlflow_client(self) -> MlflowClient:
130
+ """The Agent's MlflowClient"""
131
+ if self._mlflow_client is None:
132
+ raise RuntimeError(
133
+ f"MlflowClient access before assignment, please `set_mlflow_client` first!"
134
+ )
135
+ return self._mlflow_client
136
+
137
+ def set_mlflow_client(self, mlflow_client: MlflowClient):
138
+ """
139
+ Set the mlflow_client of the agent.
140
+ """
141
+ self._mlflow_client = mlflow_client
142
+
138
143
 
139
144
  class InstitutionAgent(Agent):
140
145
  """
141
- InstitutionAgent: 机构智能体类及其定义
146
+ InstitutionAgent: Institution agent class and definition
142
147
  """
143
148
 
144
149
  def __init__(
@@ -146,10 +151,10 @@ class InstitutionAgent(Agent):
146
151
  name: str,
147
152
  llm_client: Optional[LLM] = None,
148
153
  simulator: Optional[Simulator] = None,
149
- mlflow_client: Optional[MlflowClient] = None,
150
154
  memory: Optional[Memory] = None,
151
155
  economy_client: Optional[EconomyClient] = None,
152
156
  messager: Optional[Messager] = None, # type:ignore
157
+ message_interceptor: Optional[MessageInterceptor] = None, # type:ignore
153
158
  avro_file: Optional[dict] = None,
154
159
  ) -> None:
155
160
  super().__init__(
@@ -157,12 +162,13 @@ class InstitutionAgent(Agent):
157
162
  type=AgentType.Institution,
158
163
  llm_client=llm_client,
159
164
  economy_client=economy_client,
160
- mlflow_client=mlflow_client,
161
165
  messager=messager,
166
+ message_interceptor=message_interceptor,
162
167
  simulator=simulator,
163
168
  memory=memory,
164
169
  avro_file=avro_file,
165
170
  )
171
+ self._mlflow_client = None
166
172
  # 添加响应收集器
167
173
  self._gather_responses: dict[str, asyncio.Future] = {}
168
174
 
@@ -178,10 +184,10 @@ class InstitutionAgent(Agent):
178
184
  # TODO: More general id generation
179
185
  _id = random.randint(100000, 999999)
180
186
  self._agent_id = _id
181
- self.memory.set_agent_id(_id)
187
+ self.status.set_agent_id(_id)
182
188
  map_header = self.simulator.map.header
183
189
  # TODO: remove random position assignment
184
- await self.memory.update(
190
+ await self.status.update(
185
191
  "position",
186
192
  {
187
193
  "xy_position": {
@@ -201,57 +207,57 @@ class InstitutionAgent(Agent):
201
207
  },
202
208
  protect_llm_read_only_fields=False,
203
209
  )
204
- await self.memory.update("id", _id, protect_llm_read_only_fields=False)
210
+ await self.status.update("id", _id, protect_llm_read_only_fields=False)
205
211
  try:
206
212
  await self._economy_client.remove_orgs([self._agent_id])
207
213
  except:
208
214
  pass
209
215
  try:
210
- _memory = self.memory
211
- _id = await _memory.get("id")
212
- _type = await _memory.get("type")
216
+ _status = self.status
217
+ _id = await _status.get("id")
218
+ _type = await _status.get("type")
213
219
  try:
214
- nominal_gdp = await _memory.get("nominal_gdp")
220
+ nominal_gdp = await _status.get("nominal_gdp")
215
221
  except:
216
222
  nominal_gdp = []
217
223
  try:
218
- real_gdp = await _memory.get("real_gdp")
224
+ real_gdp = await _status.get("real_gdp")
219
225
  except:
220
226
  real_gdp = []
221
227
  try:
222
- unemployment = await _memory.get("unemployment")
228
+ unemployment = await _status.get("unemployment")
223
229
  except:
224
230
  unemployment = []
225
231
  try:
226
- wages = await _memory.get("wages")
232
+ wages = await _status.get("wages")
227
233
  except:
228
234
  wages = []
229
235
  try:
230
- prices = await _memory.get("prices")
236
+ prices = await _status.get("prices")
231
237
  except:
232
238
  prices = []
233
239
  try:
234
- inventory = await _memory.get("inventory")
240
+ inventory = await _status.get("inventory")
235
241
  except:
236
242
  inventory = 0
237
243
  try:
238
- price = await _memory.get("price")
244
+ price = await _status.get("price")
239
245
  except:
240
246
  price = 0
241
247
  try:
242
- currency = await _memory.get("currency")
248
+ currency = await _status.get("currency")
243
249
  except:
244
250
  currency = 0.0
245
251
  try:
246
- interest_rate = await _memory.get("interest_rate")
252
+ interest_rate = await _status.get("interest_rate")
247
253
  except:
248
254
  interest_rate = 0.0
249
255
  try:
250
- bracket_cutoffs = await _memory.get("bracket_cutoffs")
256
+ bracket_cutoffs = await _status.get("bracket_cutoffs")
251
257
  except:
252
258
  bracket_cutoffs = []
253
259
  try:
254
- bracket_rates = await _memory.get("bracket_rates")
260
+ bracket_rates = await _status.get("bracket_rates")
255
261
  except:
256
262
  bracket_rates = []
257
263
  await self._economy_client.add_orgs(
@@ -322,3 +328,18 @@ class InstitutionAgent(Agent):
322
328
  # 清理Future
323
329
  for key in futures:
324
330
  self._gather_responses.pop(key, None)
331
+
332
+ @property
333
+ def mlflow_client(self) -> MlflowClient:
334
+ """The Agent's MlflowClient"""
335
+ if self._mlflow_client is None:
336
+ raise RuntimeError(
337
+ f"MlflowClient access before assignment, please `set_mlflow_client` first!"
338
+ )
339
+ return self._mlflow_client
340
+
341
+ def set_mlflow_client(self, mlflow_client: MlflowClient):
342
+ """
343
+ Set the mlflow_client of the agent.
344
+ """
345
+ self._mlflow_client = mlflow_client
@@ -13,14 +13,13 @@ from typing import Any, Optional, Union, get_type_hints
13
13
  import fastavro
14
14
  import ray
15
15
  from pycityproto.city.person.v2 import person_pb2 as person_pb2
16
- from pyparsing import Dict
17
16
 
18
17
  from ..economy import EconomyClient
19
18
  from ..environment import Simulator
20
19
  from ..environment.sim.person_service import PersonService
21
20
  from ..llm import LLM
22
21
  from ..memory import Memory
23
- from ..message.messager import Messager
22
+ from ..message import MessageInterceptor, Messager
24
23
  from ..metrics import MlflowClient
25
24
  from ..utils import DIALOG_SCHEMA, SURVEY_SCHEMA, process_survey_for_llm
26
25
  from ..workflow import Block
@@ -48,6 +47,7 @@ class Agent(ABC):
48
47
 
49
48
  configurable_fields: list[str] = []
50
49
  default_values: dict[str, Any] = {}
50
+ fields_description: dict[str, str] = {}
51
51
 
52
52
  def __init__(
53
53
  self,
@@ -55,9 +55,9 @@ class Agent(ABC):
55
55
  type: AgentType = AgentType.Unspecified,
56
56
  llm_client: Optional[LLM] = None,
57
57
  economy_client: Optional[EconomyClient] = None,
58
- messager: Optional[Messager] = None, # type:ignore
58
+ messager: Optional[ray.ObjectRef] = None,
59
+ message_interceptor: Optional[ray.ObjectRef] = None,
59
60
  simulator: Optional[Simulator] = None,
60
- mlflow_client: Optional[MlflowClient] = None,
61
61
  memory: Optional[Memory] = None,
62
62
  avro_file: Optional[dict[str, str]] = None,
63
63
  copy_writer: Optional[ray.ObjectRef] = None,
@@ -72,7 +72,6 @@ class Agent(ABC):
72
72
  economy_client (EconomyClient): The `EconomySim` client. Defaults to None.
73
73
  messager (Messager, optional): The messager object. Defaults to None.
74
74
  simulator (Simulator, optional): The simulator object. Defaults to None.
75
- mlflow_client (MlflowClient, optional): The Mlflow object. Defaults to None.
76
75
  memory (Memory, optional): The memory of the agent. Defaults to None.
77
76
  avro_file (dict[str, str], optional): The avro file of the agent. Defaults to None.
78
77
  copy_writer (ray.ObjectRef): The copy_writer of the agent. Defaults to None.
@@ -83,8 +82,8 @@ class Agent(ABC):
83
82
  self._llm_client = llm_client
84
83
  self._economy_client = economy_client
85
84
  self._messager = messager
85
+ self._message_interceptor = message_interceptor
86
86
  self._simulator = simulator
87
- self._mlflow_client = mlflow_client
88
87
  self._memory = memory
89
88
  self._exp_id = -1
90
89
  self._agent_id = -1
@@ -104,13 +103,22 @@ class Agent(ABC):
104
103
  return state
105
104
 
106
105
  @classmethod
107
- def export_class_config(cls) -> dict[str, Dict]:
108
- result = {"agent_name": cls.__name__, "config": {}, "blocks": []}
106
+ def export_class_config(cls) -> dict[str, dict]:
107
+ result = {
108
+ "agent_name": cls.__name__,
109
+ "config": {},
110
+ "description": {},
111
+ "blocks": [],
112
+ }
109
113
  config = {
110
114
  field: cls.default_values.get(field, "default_value")
111
115
  for field in cls.configurable_fields
112
116
  }
113
117
  result["config"] = config
118
+ result["description"] = {
119
+ field: cls.fields_description.get(field, "")
120
+ for field in cls.configurable_fields
121
+ }
114
122
  # 解析类中的注解,找到Block类型的字段
115
123
  hints = get_type_hints(cls)
116
124
  for attr_name, attr_type in hints.items():
@@ -119,14 +127,15 @@ class Agent(ABC):
119
127
  result["blocks"].append(
120
128
  {
121
129
  "name": attr_name,
122
- "config": block_config,
130
+ "config": block_config[0], # type:ignore
131
+ "description": block_config[1], # type:ignore
123
132
  "children": cls._export_subblocks(attr_type),
124
133
  }
125
134
  )
126
135
  return result
127
136
 
128
137
  @classmethod
129
- def _export_subblocks(cls, block_cls: type[Block]) -> list[Dict]:
138
+ def _export_subblocks(cls, block_cls: type[Block]) -> list[dict]:
130
139
  children = []
131
140
  hints = get_type_hints(block_cls) # 获取类的注解
132
141
  for attr_name, attr_type in hints.items():
@@ -135,7 +144,8 @@ class Agent(ABC):
135
144
  children.append(
136
145
  {
137
146
  "name": attr_name,
138
- "config": block_config,
147
+ "config": block_config[0], # type:ignore
148
+ "description": block_config[1], # type:ignore
139
149
  "children": cls._export_subblocks(attr_type),
140
150
  }
141
151
  )
@@ -216,12 +226,6 @@ class Agent(ABC):
216
226
  """
217
227
  self._simulator = simulator
218
228
 
219
- def set_mlflow_client(self, mlflow_client: MlflowClient):
220
- """
221
- Set the mlflow_client of the agent.
222
- """
223
- self._mlflow_client = mlflow_client
224
-
225
229
  def set_economy_client(self, economy_client: EconomyClient):
226
230
  """
227
231
  Set the economy_client of the agent.
@@ -252,6 +256,12 @@ class Agent(ABC):
252
256
  """
253
257
  self._pgsql_writer = pgsql_writer
254
258
 
259
+ def set_message_interceptor(self, message_interceptor: ray.ObjectRef):
260
+ """
261
+ Set the PostgreSQL copy writer of the agent.
262
+ """
263
+ self._message_interceptor = message_interceptor
264
+
255
265
  @property
256
266
  def uuid(self):
257
267
  """The Agent's UUID"""
@@ -279,16 +289,7 @@ class Agent(ABC):
279
289
  f"EconomyClient access before assignment, please `set_economy_client` first!"
280
290
  )
281
291
  return self._economy_client
282
-
283
- @property
284
- def mlflow_client(self):
285
- """The Agent's MlflowClient"""
286
- if self._mlflow_client is None:
287
- raise RuntimeError(
288
- f"MlflowClient access before assignment, please `set_mlflow_client` first!"
289
- )
290
- return self._mlflow_client
291
-
292
+
292
293
  @property
293
294
  def memory(self):
294
295
  """The Agent's Memory"""
@@ -298,6 +299,24 @@ class Agent(ABC):
298
299
  )
299
300
  return self._memory
300
301
 
302
+ @property
303
+ def status(self):
304
+ """The Agent's Status Memory"""
305
+ if self.memory.status is None:
306
+ raise RuntimeError(
307
+ f"Status access before assignment, please `set_memory` first!"
308
+ )
309
+ return self.memory.status
310
+
311
+ @property
312
+ def stream(self):
313
+ """The Agent's Stream Memory"""
314
+ if self.memory.stream is None:
315
+ raise RuntimeError(
316
+ f"Stream access before assignment, please `set_memory` first!"
317
+ )
318
+ return self.memory.stream
319
+
301
320
  @property
302
321
  def simulator(self):
303
322
  """The Simulator"""
@@ -315,11 +334,17 @@ class Agent(ABC):
315
334
  f"Copy Writer access before assignment, please `set_pgsql_writer` first!"
316
335
  )
317
336
  return self._pgsql_writer
337
+
338
+ @property
339
+ def messager(self):
340
+ if self._messager is None:
341
+ raise RuntimeError("Messager is not set")
342
+ return self._messager
318
343
 
319
344
  async def messager_ping(self):
320
345
  if self._messager is None:
321
346
  raise RuntimeError("Messager is not set")
322
- return await self._messager.ping()
347
+ return await self._messager.ping.remote() # type:ignore
323
348
 
324
349
  async def generate_user_survey_response(self, survey: dict) -> str:
325
350
  """生成回答 —— 可重写
@@ -338,22 +363,15 @@ class Agent(ABC):
338
363
 
339
364
  # 添加记忆上下文
340
365
  if self._memory:
341
- relevant_memories = await self.memory.search(survey_prompt)
342
-
343
- formatted_results = []
344
- # for result in top_results:
345
- # formatted_results.append(
346
- # f"- [{result['type']}] {result['content']} "
347
- # f"(相关度: {result['similarity']:.2f})"
348
- # )
349
-
350
- if relevant_memories:
351
- dialog.append(
352
- {
353
- "role": "system",
354
- "content": f"Answer based on these memories:\n{relevant_memories}",
355
- }
356
- )
366
+ profile_and_states = await self.status.search(survey_prompt)
367
+ relevant_activities = await self.stream.search(survey_prompt)
368
+
369
+ dialog.append(
370
+ {
371
+ "role": "system",
372
+ "content": f"Answer based on following profile and states:\n{profile_and_states}\n Related activities:\n{relevant_activities}",
373
+ }
374
+ )
357
375
 
358
376
  # 添加问卷问题
359
377
  dialog.append({"role": "user", "content": survey_prompt})
@@ -412,6 +430,7 @@ class Agent(ABC):
412
430
  _data_tuples
413
431
  )
414
432
  )
433
+ await self.messager.send_message.remote(f"exps/{self._exp_id}/user_payback", {"count": 1})# type:ignore
415
434
 
416
435
  async def generate_user_chat_response(self, question: str) -> str:
417
436
  """生成回答 —— 可重写
@@ -430,14 +449,15 @@ class Agent(ABC):
430
449
 
431
450
  # 添加记忆上下文
432
451
  if self._memory:
433
- relevant_memories = await self._memory.search(question)
434
- if relevant_memories:
435
- dialog.append(
436
- {
437
- "role": "system",
438
- "content": f"Answer based on these memories:\n{relevant_memories}",
439
- }
440
- )
452
+ profile_and_states = await self.status.search(question, top_k=10)
453
+ relevant_activities = await self.stream.search(question, top_k=10)
454
+
455
+ dialog.append(
456
+ {
457
+ "role": "system",
458
+ "content": f"Answer based on following profile and states:\n{profile_and_states}\n Related activities:\n{relevant_activities}",
459
+ }
460
+ )
441
461
 
442
462
  # 添加用户问题
443
463
  dialog.append({"role": "user", "content": question})
@@ -497,6 +517,8 @@ class Agent(ABC):
497
517
  _data
498
518
  )
499
519
  )
520
+ await self.messager.send_message.remote(f"exps/{self._exp_id}/user_payback", {"count": 1})# type:ignore
521
+ print(f"Sent payback message to {self._exp_id}")
500
522
 
501
523
  async def process_agent_chat_response(self, payload: dict) -> str:
502
524
  resp = f"Agent {self._uuid} received agent chat response: {payload}"
@@ -566,7 +588,12 @@ class Agent(ABC):
566
588
  if self._messager is None:
567
589
  raise RuntimeError("Messager is not set")
568
590
  topic = f"exps/{self._exp_id}/agents/{to_agent_uuid}/{sub_topic}"
569
- await self._messager.send_message.remote(topic, payload)
591
+ await self._messager.send_message.remote( # type:ignore
592
+ topic,
593
+ payload,
594
+ self._uuid,
595
+ to_agent_uuid,
596
+ )
570
597
 
571
598
  async def send_message_to_agent(
572
599
  self, to_agent_uuid: str, content: str, type: str = "social"
@@ -630,6 +657,6 @@ class Agent(ABC):
630
657
  当_blocked为True时,不执行forward方法
631
658
  """
632
659
  if self._messager is not None:
633
- await self._messager.ping.remote()
660
+ await self._messager.ping.remote() # type:ignore
634
661
  if not self._blocked:
635
662
  await self.forward()
@@ -53,14 +53,12 @@ class BankAgent(InstitutionAgent):
53
53
 
54
54
  async def forward(self):
55
55
  if await self.month_trigger():
56
- citizens = await self.memory.get("citizens")
57
- while True:
58
- agents_forward = await self.gather_messages(citizens, "forward")
59
- if np.all(np.array(agents_forward) > self.forward_times):
60
- break
61
- await asyncio.sleep(1)
56
+ citizens = await self.memory.status.get("citizens")
57
+ agents_forward = []
58
+ if not np.all(np.array(agents_forward) > self.forward_times):
59
+ return
62
60
  self.forward_times += 1
63
61
  for uuid in citizens:
64
62
  await self.send_message_to_agent(
65
- uuid, f"bank_forward@{self.forward_times}"
63
+ uuid, f"bank_forward@{self.forward_times}", "economy"
66
64
  )
@@ -5,7 +5,6 @@ from .needs_block import NeedsBlock
5
5
  from .social_block import SocialBlock
6
6
  from .economy_block import EconomyBlock
7
7
  from .other_block import OtherBlock
8
- from .time_block import TimeBlock
9
8
 
10
9
  __all__ = [
11
10
  "MobilityBlock",
@@ -16,5 +15,4 @@ __all__ = [
16
15
  "EconomyBlock",
17
16
  "OtherBlock",
18
17
  "LongTermDecisionBlock",
19
- "TimeBlock",
20
18
  ]