pycityagent 2.0.0a51__cp39-cp39-macosx_11_0_arm64.whl → 2.0.0a53__cp39-cp39-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 -10
  29. pycityagent/workflow/block.py +11 -4
  30. {pycityagent-2.0.0a51.dist-info → pycityagent-2.0.0a53.dist-info}/METADATA +2 -2
  31. {pycityagent-2.0.0a51.dist-info → pycityagent-2.0.0a53.dist-info}/RECORD +35 -35
  32. {pycityagent-2.0.0a51.dist-info → pycityagent-2.0.0a53.dist-info}/WHEEL +1 -1
  33. pycityagent/cityagent/blocks/time_block.py +0 -116
  34. {pycityagent-2.0.0a51.dist-info → pycityagent-2.0.0a53.dist-info}/LICENSE +0 -0
  35. {pycityagent-2.0.0a51.dist-info → pycityagent-2.0.0a53.dist-info}/entry_points.txt +0 -0
  36. {pycityagent-2.0.0a51.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
  ]