pycityagent 2.0.0a52__cp39-cp39-macosx_11_0_arm64.whl → 2.0.0a53__cp39-cp39-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
@@ -14,7 +14,6 @@ from ..environment import Simulator
14
14
  from ..llm import LLM
15
15
  from ..memory import Memory
16
16
  from ..message.messager import Messager
17
- from ..metrics import MlflowClient
18
17
  from .agent_base import Agent, AgentType
19
18
 
20
19
  logger = logging.getLogger("pycityagent")
@@ -30,7 +29,6 @@ class CitizenAgent(Agent):
30
29
  name: str,
31
30
  llm_client: Optional[LLM] = None,
32
31
  simulator: Optional[Simulator] = None,
33
- mlflow_client: Optional[MlflowClient] = None,
34
32
  memory: Optional[Memory] = None,
35
33
  economy_client: Optional[EconomyClient] = None,
36
34
  messager: Optional[Messager] = None, # type:ignore
@@ -43,7 +41,6 @@ class CitizenAgent(Agent):
43
41
  economy_client=economy_client,
44
42
  messager=messager,
45
43
  simulator=simulator,
46
- mlflow_client=mlflow_client,
47
44
  memory=memory,
48
45
  avro_file=avro_file,
49
46
  )
@@ -62,42 +59,33 @@ class CitizenAgent(Agent):
62
59
  if self._simulator is None:
63
60
  logger.warning("Simulator is not set")
64
61
  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)
62
+ FROM_MEMORY_KEYS = {
63
+ "attribute",
64
+ "home",
65
+ "work",
66
+ "vehicle_attribute",
67
+ "bus_attribute",
68
+ "pedestrian_attribute",
69
+ "bike_attribute",
70
+ }
71
+ simulator = self.simulator
72
+ status = self.status
73
+ dict_person = deepcopy(self._person_template)
74
+ for _key in FROM_MEMORY_KEYS:
75
+ try:
76
+ _value = await status.get(_key)
77
+ if _value:
78
+ dict_person[_key] = _value
79
+ except KeyError as e:
80
+ continue
81
+ resp = await simulator.add_person(
82
+ dict2pb(dict_person, person_pb2.Person())
83
+ )
84
+ person_id = resp["person_id"]
85
+ await status.update("id", person_id, protect_llm_read_only_fields=False)
86
+ logger.debug(f"Binding to Person `{person_id}` just added to Simulator")
87
+ self._agent_id = person_id
88
+ self.status.set_agent_id(person_id)
101
89
 
102
90
  async def _bind_to_economy(self):
103
91
  if self._economy_client is None:
@@ -109,8 +97,8 @@ class CitizenAgent(Agent):
109
97
  await self._economy_client.remove_agents([self._agent_id])
110
98
  except:
111
99
  pass
112
- person_id = await self.memory.get("id")
113
- currency = await self.memory.get("currency")
100
+ person_id = await self.status.get("id")
101
+ currency = await self.status.get("currency")
114
102
  await self._economy_client.add_agents(
115
103
  {
116
104
  "id": person_id,
@@ -128,7 +116,7 @@ class CitizenAgent(Agent):
128
116
  # 从消息中解析发送者 ID 和消息内容
129
117
  target = payload["target"]
130
118
  sender_id = payload["from"]
131
- content = await self.memory.get(f"{target}")
119
+ content = await self.status.get(f"{target}")
132
120
  payload = {
133
121
  "from": self._uuid,
134
122
  "content": content,
@@ -138,7 +126,7 @@ class CitizenAgent(Agent):
138
126
 
139
127
  class InstitutionAgent(Agent):
140
128
  """
141
- InstitutionAgent: 机构智能体类及其定义
129
+ InstitutionAgent: Institution agent class and definition
142
130
  """
143
131
 
144
132
  def __init__(
@@ -146,7 +134,6 @@ class InstitutionAgent(Agent):
146
134
  name: str,
147
135
  llm_client: Optional[LLM] = None,
148
136
  simulator: Optional[Simulator] = None,
149
- mlflow_client: Optional[MlflowClient] = None,
150
137
  memory: Optional[Memory] = None,
151
138
  economy_client: Optional[EconomyClient] = None,
152
139
  messager: Optional[Messager] = None, # type:ignore
@@ -157,7 +144,6 @@ class InstitutionAgent(Agent):
157
144
  type=AgentType.Institution,
158
145
  llm_client=llm_client,
159
146
  economy_client=economy_client,
160
- mlflow_client=mlflow_client,
161
147
  messager=messager,
162
148
  simulator=simulator,
163
149
  memory=memory,
@@ -178,10 +164,10 @@ class InstitutionAgent(Agent):
178
164
  # TODO: More general id generation
179
165
  _id = random.randint(100000, 999999)
180
166
  self._agent_id = _id
181
- self.memory.set_agent_id(_id)
167
+ self.status.set_agent_id(_id)
182
168
  map_header = self.simulator.map.header
183
169
  # TODO: remove random position assignment
184
- await self.memory.update(
170
+ await self.status.update(
185
171
  "position",
186
172
  {
187
173
  "xy_position": {
@@ -201,57 +187,57 @@ class InstitutionAgent(Agent):
201
187
  },
202
188
  protect_llm_read_only_fields=False,
203
189
  )
204
- await self.memory.update("id", _id, protect_llm_read_only_fields=False)
190
+ await self.status.update("id", _id, protect_llm_read_only_fields=False)
205
191
  try:
206
192
  await self._economy_client.remove_orgs([self._agent_id])
207
193
  except:
208
194
  pass
209
195
  try:
210
- _memory = self.memory
211
- _id = await _memory.get("id")
212
- _type = await _memory.get("type")
196
+ _status = self.status
197
+ _id = await _status.get("id")
198
+ _type = await _status.get("type")
213
199
  try:
214
- nominal_gdp = await _memory.get("nominal_gdp")
200
+ nominal_gdp = await _status.get("nominal_gdp")
215
201
  except:
216
202
  nominal_gdp = []
217
203
  try:
218
- real_gdp = await _memory.get("real_gdp")
204
+ real_gdp = await _status.get("real_gdp")
219
205
  except:
220
206
  real_gdp = []
221
207
  try:
222
- unemployment = await _memory.get("unemployment")
208
+ unemployment = await _status.get("unemployment")
223
209
  except:
224
210
  unemployment = []
225
211
  try:
226
- wages = await _memory.get("wages")
212
+ wages = await _status.get("wages")
227
213
  except:
228
214
  wages = []
229
215
  try:
230
- prices = await _memory.get("prices")
216
+ prices = await _status.get("prices")
231
217
  except:
232
218
  prices = []
233
219
  try:
234
- inventory = await _memory.get("inventory")
220
+ inventory = await _status.get("inventory")
235
221
  except:
236
222
  inventory = 0
237
223
  try:
238
- price = await _memory.get("price")
224
+ price = await _status.get("price")
239
225
  except:
240
226
  price = 0
241
227
  try:
242
- currency = await _memory.get("currency")
228
+ currency = await _status.get("currency")
243
229
  except:
244
230
  currency = 0.0
245
231
  try:
246
- interest_rate = await _memory.get("interest_rate")
232
+ interest_rate = await _status.get("interest_rate")
247
233
  except:
248
234
  interest_rate = 0.0
249
235
  try:
250
- bracket_cutoffs = await _memory.get("bracket_cutoffs")
236
+ bracket_cutoffs = await _status.get("bracket_cutoffs")
251
237
  except:
252
238
  bracket_cutoffs = []
253
239
  try:
254
- bracket_rates = await _memory.get("bracket_rates")
240
+ bracket_rates = await _status.get("bracket_rates")
255
241
  except:
256
242
  bracket_rates = []
257
243
  await self._economy_client.add_orgs(
@@ -48,6 +48,7 @@ class Agent(ABC):
48
48
 
49
49
  configurable_fields: list[str] = []
50
50
  default_values: dict[str, Any] = {}
51
+ fields_description: dict[str, str] = {}
51
52
 
52
53
  def __init__(
53
54
  self,
@@ -57,7 +58,6 @@ class Agent(ABC):
57
58
  economy_client: Optional[EconomyClient] = None,
58
59
  messager: Optional[Messager] = None, # type:ignore
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.
@@ -84,7 +83,6 @@ class Agent(ABC):
84
83
  self._economy_client = economy_client
85
84
  self._messager = messager
86
85
  self._simulator = simulator
87
- self._mlflow_client = mlflow_client
88
86
  self._memory = memory
89
87
  self._exp_id = -1
90
88
  self._agent_id = -1
@@ -104,25 +102,33 @@ class Agent(ABC):
104
102
  return state
105
103
 
106
104
  @classmethod
107
- def export_class_config(cls) -> dict[str, Dict]:
108
- result = {"agent_name": cls.__name__, "config": {}, "blocks": []}
105
+ def export_class_config(cls) -> Dict[str, Dict]:
106
+ result = {
107
+ "agent_name": cls.__name__,
108
+ "config": {},
109
+ "description": {},
110
+ "blocks": []
111
+ }
109
112
  config = {
110
113
  field: cls.default_values.get(field, "default_value")
111
114
  for field in cls.configurable_fields
112
115
  }
113
116
  result["config"] = config
117
+ result["description"] = {
118
+ field: cls.fields_description.get(field, "")
119
+ for field in cls.configurable_fields
120
+ }
114
121
  # 解析类中的注解,找到Block类型的字段
115
122
  hints = get_type_hints(cls)
116
123
  for attr_name, attr_type in hints.items():
117
124
  if inspect.isclass(attr_type) and issubclass(attr_type, Block):
118
125
  block_config = attr_type.export_class_config()
119
- result["blocks"].append(
120
- {
121
- "name": attr_name,
122
- "config": block_config,
123
- "children": cls._export_subblocks(attr_type),
124
- }
125
- )
126
+ result["blocks"].append({
127
+ "name": attr_name,
128
+ "config": block_config[0],
129
+ "description": block_config[1],
130
+ "children": cls._export_subblocks(attr_type)
131
+ })
126
132
  return result
127
133
 
128
134
  @classmethod
@@ -135,7 +141,8 @@ class Agent(ABC):
135
141
  children.append(
136
142
  {
137
143
  "name": attr_name,
138
- "config": block_config,
144
+ "config": block_config[0],
145
+ "description": block_config[1],
139
146
  "children": cls._export_subblocks(attr_type),
140
147
  }
141
148
  )
@@ -216,12 +223,6 @@ class Agent(ABC):
216
223
  """
217
224
  self._simulator = simulator
218
225
 
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
226
  def set_economy_client(self, economy_client: EconomyClient):
226
227
  """
227
228
  Set the economy_client of the agent.
@@ -279,16 +280,7 @@ class Agent(ABC):
279
280
  f"EconomyClient access before assignment, please `set_economy_client` first!"
280
281
  )
281
282
  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
-
283
+
292
284
  @property
293
285
  def memory(self):
294
286
  """The Agent's Memory"""
@@ -297,6 +289,24 @@ class Agent(ABC):
297
289
  f"Memory access before assignment, please `set_memory` first!"
298
290
  )
299
291
  return self._memory
292
+
293
+ @property
294
+ def status(self):
295
+ """The Agent's Status Memory"""
296
+ if self._memory.status is None:
297
+ raise RuntimeError(
298
+ f"Status access before assignment, please `set_memory` first!"
299
+ )
300
+ return self._memory.status
301
+
302
+ @property
303
+ def stream(self):
304
+ """The Agent's Stream Memory"""
305
+ if self._memory.stream is None:
306
+ raise RuntimeError(
307
+ f"Stream access before assignment, please `set_memory` first!"
308
+ )
309
+ return self._memory.stream
300
310
 
301
311
  @property
302
312
  def simulator(self):
@@ -315,6 +325,12 @@ class Agent(ABC):
315
325
  f"Copy Writer access before assignment, please `set_pgsql_writer` first!"
316
326
  )
317
327
  return self._pgsql_writer
328
+
329
+ @property
330
+ def messager(self):
331
+ if self._messager is None:
332
+ raise RuntimeError("Messager is not set")
333
+ return self._messager
318
334
 
319
335
  async def messager_ping(self):
320
336
  if self._messager is None:
@@ -338,22 +354,15 @@ class Agent(ABC):
338
354
 
339
355
  # 添加记忆上下文
340
356
  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
- )
357
+ profile_and_states = await self.status.search(survey_prompt)
358
+ relevant_activities = await self.stream.search(survey_prompt)
359
+
360
+ dialog.append(
361
+ {
362
+ "role": "system",
363
+ "content": f"Answer based on following profile and states:\n{profile_and_states}\n Related activities:\n{relevant_activities}",
364
+ }
365
+ )
357
366
 
358
367
  # 添加问卷问题
359
368
  dialog.append({"role": "user", "content": survey_prompt})
@@ -412,6 +421,7 @@ class Agent(ABC):
412
421
  _data_tuples
413
422
  )
414
423
  )
424
+ await self.messager.send_message.remote(f"exps/{self._exp_id}/user_payback", {"count": 1})
415
425
 
416
426
  async def generate_user_chat_response(self, question: str) -> str:
417
427
  """生成回答 —— 可重写
@@ -430,14 +440,15 @@ class Agent(ABC):
430
440
 
431
441
  # 添加记忆上下文
432
442
  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
- )
443
+ profile_and_states = await self.status.search(question, top_k=10)
444
+ relevant_activities = await self.stream.search(question, top_k=10)
445
+
446
+ dialog.append(
447
+ {
448
+ "role": "system",
449
+ "content": f"Answer based on following profile and states:\n{profile_and_states}\n Related activities:\n{relevant_activities}",
450
+ }
451
+ )
441
452
 
442
453
  # 添加用户问题
443
454
  dialog.append({"role": "user", "content": question})
@@ -497,6 +508,8 @@ class Agent(ABC):
497
508
  _data
498
509
  )
499
510
  )
511
+ await self.messager.send_message.remote(f"exps/{self._exp_id}/user_payback", {"count": 1})
512
+ print(f"Sent payback message to {self._exp_id}")
500
513
 
501
514
  async def process_agent_chat_response(self, payload: dict) -> str:
502
515
  resp = f"Agent {self._uuid} received agent chat response: {payload}"
@@ -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
  ]