pycityagent 2.0.0a65__cp39-cp39-macosx_11_0_arm64.whl → 2.0.0a67__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 (87) hide show
  1. pycityagent/agent/agent.py +157 -57
  2. pycityagent/agent/agent_base.py +316 -43
  3. pycityagent/cityagent/bankagent.py +49 -9
  4. pycityagent/cityagent/blocks/__init__.py +1 -2
  5. pycityagent/cityagent/blocks/cognition_block.py +54 -31
  6. pycityagent/cityagent/blocks/dispatcher.py +22 -17
  7. pycityagent/cityagent/blocks/economy_block.py +46 -32
  8. pycityagent/cityagent/blocks/mobility_block.py +209 -105
  9. pycityagent/cityagent/blocks/needs_block.py +101 -54
  10. pycityagent/cityagent/blocks/other_block.py +42 -33
  11. pycityagent/cityagent/blocks/plan_block.py +59 -42
  12. pycityagent/cityagent/blocks/social_block.py +167 -126
  13. pycityagent/cityagent/blocks/utils.py +13 -6
  14. pycityagent/cityagent/firmagent.py +17 -35
  15. pycityagent/cityagent/governmentagent.py +3 -3
  16. pycityagent/cityagent/initial.py +79 -49
  17. pycityagent/cityagent/memory_config.py +123 -94
  18. pycityagent/cityagent/message_intercept.py +0 -4
  19. pycityagent/cityagent/metrics.py +41 -0
  20. pycityagent/cityagent/nbsagent.py +24 -36
  21. pycityagent/cityagent/societyagent.py +9 -4
  22. pycityagent/cli/wrapper.py +2 -2
  23. pycityagent/economy/econ_client.py +407 -81
  24. pycityagent/environment/__init__.py +0 -3
  25. pycityagent/environment/sim/__init__.py +0 -3
  26. pycityagent/environment/sim/aoi_service.py +2 -2
  27. pycityagent/environment/sim/client.py +3 -31
  28. pycityagent/environment/sim/clock_service.py +2 -2
  29. pycityagent/environment/sim/lane_service.py +8 -8
  30. pycityagent/environment/sim/light_service.py +8 -8
  31. pycityagent/environment/sim/pause_service.py +9 -10
  32. pycityagent/environment/sim/person_service.py +20 -20
  33. pycityagent/environment/sim/road_service.py +2 -2
  34. pycityagent/environment/sim/sim_env.py +21 -5
  35. pycityagent/environment/sim/social_service.py +4 -4
  36. pycityagent/environment/simulator.py +249 -27
  37. pycityagent/environment/utils/__init__.py +2 -2
  38. pycityagent/environment/utils/geojson.py +2 -2
  39. pycityagent/environment/utils/grpc.py +4 -4
  40. pycityagent/environment/utils/map_utils.py +2 -2
  41. pycityagent/llm/embeddings.py +147 -28
  42. pycityagent/llm/llm.py +178 -111
  43. pycityagent/llm/llmconfig.py +5 -0
  44. pycityagent/llm/utils.py +4 -0
  45. pycityagent/memory/__init__.py +0 -4
  46. pycityagent/memory/const.py +2 -2
  47. pycityagent/memory/faiss_query.py +140 -61
  48. pycityagent/memory/memory.py +394 -91
  49. pycityagent/memory/memory_base.py +140 -34
  50. pycityagent/memory/profile.py +13 -13
  51. pycityagent/memory/self_define.py +13 -13
  52. pycityagent/memory/state.py +14 -14
  53. pycityagent/message/message_interceptor.py +253 -3
  54. pycityagent/message/messager.py +133 -6
  55. pycityagent/metrics/mlflow_client.py +47 -4
  56. pycityagent/pycityagent-sim +0 -0
  57. pycityagent/pycityagent-ui +0 -0
  58. pycityagent/simulation/__init__.py +3 -2
  59. pycityagent/simulation/agentgroup.py +150 -54
  60. pycityagent/simulation/simulation.py +276 -66
  61. pycityagent/survey/manager.py +45 -3
  62. pycityagent/survey/models.py +42 -2
  63. pycityagent/tools/__init__.py +1 -2
  64. pycityagent/tools/tool.py +93 -69
  65. pycityagent/utils/avro_schema.py +2 -2
  66. pycityagent/utils/parsers/code_block_parser.py +1 -1
  67. pycityagent/utils/parsers/json_parser.py +2 -2
  68. pycityagent/utils/parsers/parser_base.py +2 -2
  69. pycityagent/workflow/block.py +64 -13
  70. pycityagent/workflow/prompt.py +31 -23
  71. pycityagent/workflow/trigger.py +91 -24
  72. {pycityagent-2.0.0a65.dist-info → pycityagent-2.0.0a67.dist-info}/METADATA +2 -2
  73. pycityagent-2.0.0a67.dist-info/RECORD +97 -0
  74. pycityagent/environment/interact/__init__.py +0 -0
  75. pycityagent/environment/interact/interact.py +0 -198
  76. pycityagent/environment/message/__init__.py +0 -0
  77. pycityagent/environment/sence/__init__.py +0 -0
  78. pycityagent/environment/sence/static.py +0 -416
  79. pycityagent/environment/sidecar/__init__.py +0 -8
  80. pycityagent/environment/sidecar/sidecarv2.py +0 -109
  81. pycityagent/environment/sim/economy_services.py +0 -192
  82. pycityagent/metrics/utils/const.py +0 -0
  83. pycityagent-2.0.0a65.dist-info/RECORD +0 -105
  84. {pycityagent-2.0.0a65.dist-info → pycityagent-2.0.0a67.dist-info}/LICENSE +0 -0
  85. {pycityagent-2.0.0a65.dist-info → pycityagent-2.0.0a67.dist-info}/WHEEL +0 -0
  86. {pycityagent-2.0.0a65.dist-info → pycityagent-2.0.0a67.dist-info}/entry_points.txt +0 -0
  87. {pycityagent-2.0.0a65.dist-info → pycityagent-2.0.0a67.dist-info}/top_level.txt +0 -0
@@ -1,55 +1,67 @@
1
1
  import random
2
-
2
+ import numpy as np
3
3
  from pycityagent.cityagent import (BankAgent, FirmAgent, GovernmentAgent,
4
4
  NBSAgent, SocietyAgent)
5
5
 
6
+ import logging
7
+
8
+ logger = logging.getLogger("pycityagent")
6
9
 
7
10
  async def initialize_social_network(simulation):
8
11
  """
9
- 初始化智能体之间的社交网络,包括好友关系类型、好友关系和社交关系强度
12
+ Initializes the social network between agents.
13
+
14
+ - **Description**:
15
+ - Creates friendship relationships between agents
16
+ - Assigns relationship types (family, colleague, friend)
17
+ - Sets relationship strengths based on type
18
+ - Initializes chat histories and interaction records
19
+
20
+ - **Returns**:
21
+ - None
10
22
  """
11
23
  try:
12
- print("Initializing social network...")
24
+ logger.info("Initializing social network...")
13
25
 
14
- # 定义可能的关系类型
26
+ # Define possible relationship types
15
27
  relation_types = ["family", "colleague", "friend"]
16
28
 
17
- # 获取所有智能体ID
29
+ # Get all agent IDs
18
30
  agent_ids = simulation.agent_uuids
19
31
  for agent_id in agent_ids:
20
- # 为每个智能体随机选择2-5个好友
32
+ # Randomly select 2-5 friends for each agent
21
33
  num_friends = random.randint(2, 5)
22
34
  possible_friends = [aid for aid in agent_ids if aid != agent_id]
23
35
  friends = random.sample(
24
36
  possible_friends, min(num_friends, len(possible_friends))
25
37
  )
26
38
 
27
- # 初始化好友关系
39
+ # Initialize friend relationships
28
40
  await simulation.update(agent_id, "friends", friends)
29
41
 
30
- # 初始化与每个好友的关系类型和关系强度
42
+ # Initialize relationship types and strengths with each friend
31
43
  relationships = {}
32
44
  relation_type_map = {}
33
45
 
34
46
  for friend_id in friends:
35
- # 随机选择关系类型
47
+ # Randomly select relationship type
36
48
  relation_type = random.choice(relation_types)
37
- # 根据关系类型设置初始关系强度范围
49
+ # Set initial relationship strength range based on type
38
50
  if relation_type == "family":
39
- strength = random.randint(60, 90) # 家人关系强度较高
51
+ strength = random.randint(60, 90) # Higher strength for family
40
52
  elif relation_type == "colleague":
41
- strength = random.randint(40, 70) # 同事关系强度中等
53
+ strength = random.randint(40, 70) # Medium strength for colleagues
42
54
  else: # friend
43
- strength = random.randint(30, 80) # 朋友关系强度范围较广
55
+ strength = random.randint(30, 80) # Wide range for friends
44
56
 
45
57
  relationships[friend_id] = strength
46
58
  relation_type_map[friend_id] = relation_type
47
59
 
48
- # 更新关系强度和类型
60
+ # Update relationship strengths and types
49
61
  await simulation.update(agent_id, "relationships", relationships)
50
62
  await simulation.update(agent_id, "relation_types", relation_type_map)
51
63
 
52
- # 初始化空的聊天历史和互动记录
64
+ # Initialize empty chat histories and interaction records
53
65
  await simulation.update(
54
66
  agent_id, "chat_histories", {friend_id: [] for friend_id in friends}
55
67
  )
@@ -64,54 +76,72 @@ async def initialize_social_network(simulation):
64
76
  print(f"Error initializing social network: {str(e)}")
65
77
  return False
66
78
 
79
+ def zipf_distribution(N, F, s=1.0):
80
+ """
81
+ Generates employee counts for F companies following Zipf's law, with total employees N.
82
+
83
+ - **Description**:
84
+ - Uses Zipf's law to distribute N total employees across F companies
85
+ - The distribution follows a power law where employee count is proportional to 1/rank^s
86
+ - Normalizes the distribution to ensure total employees equals N
87
+
88
+ - **Parameters**:
89
+ - `N`: Total number of employees across all companies
90
+ - `F`: Number of companies to distribute employees across
91
+ - `s`: Power law exponent for Zipf's law, typically close to 1
92
+
93
+ - **Returns**:
94
+ - List of integer employee counts for each company, summing to N
95
+ """
96
+ # Calculate employee count for each rank (following Zipf's law distribution)
97
+ ranks = np.arange(1, F + 1) # Ranks from 1 to F
98
+ sizes = 1 / (ranks ** s) # Calculate employee count ratio according to Zipf's law
99
+
100
+ # Calculate normalization coefficient to make total employees equal N
101
+ total_size = np.sum(sizes)
102
+ normalized_sizes = sizes / total_size * N # Normalize to total employees N
103
+
104
+ # Return employee count for each company (integers)
105
+ return np.round(normalized_sizes).astype(int)
67
106
 
68
107
  async def bind_agent_info(simulation):
69
108
  """
70
- 绑定智能体的信息,包括公民、公司、政府、银行和NBS的ID
109
+ Binds agent information including IDs for citizens, firms, government, banks and NBS.
110
+
111
+ - **Description**:
112
+ - Gathers all agent IDs and maps them between UUID and agent ID
113
+ - Assigns employees to firms following Zipf's law distribution
114
+ - Links citizens to government and bank systems
115
+
116
+ - **Returns**:
117
+ - None
71
118
  """
72
- print("Binding agent info...")
119
+ logger.info("Binding agent info...")
73
120
  infos = await simulation.gather("id")
74
121
  citizen_uuids = await simulation.filter(types=[SocietyAgent])
75
122
  firm_uuids = await simulation.filter(types=[FirmAgent])
76
- locations = await simulation.gather("location", firm_uuids)
77
- locations_plain = {}
78
- for info in locations:
79
- for k, v in info.items():
80
- locations_plain[k] = v
81
123
  government_uuids = await simulation.filter(types=[GovernmentAgent])
82
124
  bank_uuids = await simulation.filter(types=[BankAgent])
83
125
  nbs_uuids = await simulation.filter(types=[NBSAgent])
84
126
  citizen_agent_ids = []
85
- firm_ids = []
127
+ uid2agent, agent2uid = dict(), dict()
86
128
  for info in infos:
87
129
  for k, v in info.items():
88
130
  if k in citizen_uuids:
89
131
  citizen_agent_ids.append(v)
90
- elif k in firm_uuids:
91
- firm_ids.append(v)
92
- elif k in government_uuids:
93
- government_id = v
94
- elif k in bank_uuids:
95
- bank_id = v
96
- elif k in nbs_uuids:
97
- nbs_id = v
98
- for citizen_uuid in citizen_uuids:
99
- random_firm_id = random.choice(firm_ids)
100
- location = locations_plain[random_firm_id]
101
- await simulation.update(citizen_uuid, "firm_id", random_firm_id)
102
- await simulation.update(citizen_uuid, "work", location)
103
- await simulation.update(citizen_uuid, "government_id", government_id)
104
- await simulation.update(citizen_uuid, "bank_id", bank_id)
105
- await simulation.update(citizen_uuid, "nbs_id", nbs_id)
106
- for firm_uuid in firm_uuids:
107
- await simulation.update(firm_uuid, "employees", citizen_uuids)
108
- await simulation.update(firm_uuid, "employees_agent_id", citizen_agent_ids)
132
+ uid2agent[k] = v
133
+ agent2uid[v] = k
134
+ citizen_agent_ids_cp = citizen_agent_ids.copy()
135
+ random.shuffle(citizen_agent_ids_cp)
136
+ employee_sizes = zipf_distribution(len(citizen_agent_ids_cp), len(firm_uuids))
137
+ for firm_uuid, size in zip(firm_uuids, employee_sizes):
138
+ await simulation.economy_update(uid2agent[firm_uuid], "employees", citizen_agent_ids_cp[:size])
139
+ for citizen_agent_id in citizen_agent_ids_cp[:size]:
140
+ await simulation.update(agent2uid[citizen_agent_id], "firm_id", uid2agent[firm_uuid])
141
+ citizen_agent_ids_cp = citizen_agent_ids_cp[size:]
109
142
  for government_uuid in government_uuids:
110
- await simulation.update(government_uuid, "citizens", citizen_uuids)
111
- await simulation.update(government_uuid, "citizens_agent_id", citizen_agent_ids)
143
+ await simulation.economy_update(uid2agent[government_uuid], "citizens", citizen_agent_ids)
112
144
  for bank_uuid in bank_uuids:
113
- await simulation.update(bank_uuid, "citizens", citizen_uuids)
114
- await simulation.update(bank_uuid, "citizens_agent_id", citizen_agent_ids)
115
- for nbs_uuid in nbs_uuids:
116
- await simulation.update(nbs_uuid, "firm_id", random.choice(firm_ids))
117
- print("Agent info binding completed!")
145
+ await simulation.economy_update(uid2agent[bank_uuid], "citizens", citizen_agent_ids)
146
+ logger.info("Agent info binding completed!")
147
+
@@ -5,34 +5,39 @@ import numpy as np
5
5
  import pycityproto.city.economy.v2.economy_pb2 as economyv2
6
6
  from mosstool.map._map_util.const import AOI_START_ID
7
7
 
8
+ from .firmagent import FirmAgent
8
9
  pareto_param = 8
9
- payment_max_skill_multiplier = 950
10
- payment_max_skill_multiplier = float(payment_max_skill_multiplier)
10
+ payment_max_skill_multiplier_base = 950
11
+ payment_max_skill_multiplier = float(payment_max_skill_multiplier_base)
11
12
  pmsm = payment_max_skill_multiplier
12
13
  pareto_samples = np.random.pareto(pareto_param, size=(1000, 10))
13
14
  clipped_skills = np.minimum(pmsm, (pmsm - 1) * pareto_samples + 1)
14
15
  sorted_clipped_skills = np.sort(clipped_skills, axis=1)
15
16
  agent_skills = list(sorted_clipped_skills.mean(axis=0))
16
17
 
18
+ work_locations = [AOI_START_ID + random.randint(1000, 10000) for _ in range(1000)]
19
+
20
+ async def memory_config_init(simulation):
21
+ global work_locations
22
+ number_of_firm = simulation.agent_count[FirmAgent]
23
+ work_locations = [AOI_START_ID + random.randint(1000, 10000) for _ in range(number_of_firm)]
17
24
 
18
25
  def memory_config_societyagent():
26
+ global work_locations
19
27
  EXTRA_ATTRIBUTES = {
20
28
  "type": (str, "citizen"),
21
29
  "city": (str, "New York", True),
22
-
23
30
  # Needs Model
24
- "hunger_satisfaction": (float, random.random(), True), # 饥饿满意度
25
- "energy_satisfaction": (float, random.random(), True), # 精力满意度
26
- "safety_satisfaction": (float, random.random(), True), # 安全满意度
27
- "social_satisfaction": (float, random.random(), True), # 社交满意度
31
+ "hunger_satisfaction": (float, random.random(), True), # hunger satisfaction
32
+ "energy_satisfaction": (float, random.random(), True), # energy satisfaction
33
+ "safety_satisfaction": (float, random.random(), True), # safety satisfaction
34
+ "social_satisfaction": (float, random.random(), True), # social satisfaction
28
35
  "current_need": (str, "none", True),
29
-
30
36
  # Plan Behavior Model
31
37
  "current_plan": (list, [], True),
32
38
  "current_step": (dict, {"intention": "", "type": ""}, True),
33
39
  "execution_context": (dict, {}, True),
34
40
  "plan_history": (list, [], True),
35
-
36
41
  # cognition
37
42
  "emotion": (
38
43
  dict,
@@ -49,7 +54,6 @@ def memory_config_societyagent():
49
54
  "attitude": (dict, {}, True),
50
55
  "thought": (str, "Currently nothing good or bad is happening", True),
51
56
  "emotion_types": (str, "Relief", True),
52
-
53
57
  # economy
54
58
  "work_skill": (
55
59
  float,
@@ -80,7 +84,6 @@ def memory_config_societyagent():
80
84
  "working_experience": (list, [], True),
81
85
  "work_hour_month": (float, 160, True),
82
86
  "work_hour_finish": (float, 0, True),
83
-
84
87
  # social
85
88
  "friends": (list, [], True), # friends list
86
89
  "relationships": (dict, {}, True), # relationship strength with each friend
@@ -88,97 +91,122 @@ def memory_config_societyagent():
88
91
  "chat_histories": (dict, {}, True), # all chat histories
89
92
  "interactions": (dict, {}, True), # all interaction records
90
93
  "to_discuss": (dict, {}, True),
91
-
92
94
  # mobility
93
95
  "environment": (str, "The environment outside is good", True),
94
96
  "number_poi_visited": (int, 1, True),
95
97
  }
96
98
 
97
99
  PROFILE = {
98
- "name": (str,random.choice(
99
- [
100
- "Alice",
101
- "Bob",
102
- "Charlie",
103
- "David",
104
- "Eve",
105
- "Frank",
106
- "Grace",
107
- "Helen",
108
- "Ivy",
109
- "Jack",
110
- "Kelly",
111
- "Lily",
112
- "Mike",
113
- "Nancy",
114
- "Oscar",
115
- "Peter",
116
- "Queen",
117
- "Rose",
118
- "Sam",
119
- "Tom",
120
- "Ulysses",
121
- "Vicky",
122
- "Will",
123
- "Xavier",
124
- "Yvonne",
125
- "Zack",
126
- ]
127
- ), True),
100
+ "name": (
101
+ str,
102
+ random.choice(
103
+ [
104
+ "Alice",
105
+ "Bob",
106
+ "Charlie",
107
+ "David",
108
+ "Eve",
109
+ "Frank",
110
+ "Grace",
111
+ "Helen",
112
+ "Ivy",
113
+ "Jack",
114
+ "Kelly",
115
+ "Lily",
116
+ "Mike",
117
+ "Nancy",
118
+ "Oscar",
119
+ "Peter",
120
+ "Queen",
121
+ "Rose",
122
+ "Sam",
123
+ "Tom",
124
+ "Ulysses",
125
+ "Vicky",
126
+ "Will",
127
+ "Xavier",
128
+ "Yvonne",
129
+ "Zack",
130
+ ]
131
+ ),
132
+ True,
133
+ ),
128
134
  "gender": (str, random.choice(["male", "female"]), True),
129
135
  "age": (int, random.randint(18, 65), True),
130
- "education": (str, random.choice(
131
- ["Doctor", "Master", "Bachelor", "College", "High School"]
132
- ), True),
133
- "skill": (str, random.choice(
134
- [
135
- "Good at problem-solving",
136
- "Good at communication",
137
- "Good at creativity",
138
- "Good at teamwork",
139
- "Other",
140
- ]
141
- ), True),
142
- "occupation": (str, random.choice(
143
- [
144
- "Student",
145
- "Teacher",
146
- "Doctor",
147
- "Engineer",
148
- "Manager",
149
- "Businessman",
150
- "Artist",
151
- "Athlete",
152
- "Other",
153
- ]
154
- ), True),
136
+ "education": (
137
+ str,
138
+ random.choice(["Doctor", "Master", "Bachelor", "College", "High School"]),
139
+ True,
140
+ ),
141
+ "skill": (
142
+ str,
143
+ random.choice(
144
+ [
145
+ "Good at problem-solving",
146
+ "Good at communication",
147
+ "Good at creativity",
148
+ "Good at teamwork",
149
+ "Other",
150
+ ]
151
+ ),
152
+ True,
153
+ ),
154
+ "occupation": (
155
+ str,
156
+ random.choice(
157
+ [
158
+ "Student",
159
+ "Teacher",
160
+ "Doctor",
161
+ "Engineer",
162
+ "Manager",
163
+ "Businessman",
164
+ "Artist",
165
+ "Athlete",
166
+ "Other",
167
+ ]
168
+ ),
169
+ True,
170
+ ),
155
171
  "family_consumption": (str, random.choice(["low", "medium", "high"]), True),
156
- "consumption": (str, random.choice(["sightly low", "low", "medium", "high"]), True),
157
- "personality": (str, random.choice(
158
- ["outgoint", "introvert", "ambivert", "extrovert"]
159
- ), True),
160
- "income": "0",
161
- "currency": random.randint(1000, 100000),
172
+ "consumption": (float, 0, True),
173
+ "personality": (
174
+ str,
175
+ random.choice(["outgoint", "introvert", "ambivert", "extrovert"]),
176
+ True,
177
+ ),
178
+ "income": (float, 0, True),
179
+ "currency": (float, random.randint(1000, 100000), True),
162
180
  "residence": (str, random.choice(["city", "suburb", "rural"]), True),
163
- "race": (str, random.choice(
164
- [
165
- "Chinese",
166
- "American",
167
- "British",
168
- "French",
169
- "German",
170
- "Japanese",
171
- "Korean",
172
- "Russian",
173
- "Other",
174
- ]
175
- ), True),
176
- "religion": (str, random.choice(
177
- ["none", "Christian", "Muslim", "Buddhist", "Hindu", "Other"]
178
- ), True),
179
- "marital_status": (str, random.choice(
180
- ["not married", "married", "divorced", "widowed"]
181
- ), True),
181
+ "race": (
182
+ str,
183
+ random.choice(
184
+ [
185
+ "Chinese",
186
+ "American",
187
+ "British",
188
+ "French",
189
+ "German",
190
+ "Japanese",
191
+ "Korean",
192
+ "Russian",
193
+ "Other",
194
+ ]
195
+ ),
196
+ True,
197
+ ),
198
+ "religion": (
199
+ str,
200
+ random.choice(
201
+ ["none", "Christian", "Muslim", "Buddhist", "Hindu", "Other"]
202
+ ),
203
+ True,
204
+ ),
205
+ "marital_status": (
206
+ str,
207
+ random.choice(["not married", "married", "divorced", "widowed"]),
208
+ True,
209
+ ),
182
210
  }
183
211
 
184
212
  BASE = {
@@ -186,7 +214,7 @@ def memory_config_societyagent():
186
214
  "aoi_position": {"aoi_id": AOI_START_ID + random.randint(1000, 10000)}
187
215
  },
188
216
  "work": {
189
- "aoi_position": {"aoi_id": AOI_START_ID + random.randint(1000, 10000)}
217
+ "aoi_position": {"aoi_id": random.choice(work_locations)}
190
218
  },
191
219
  }
192
220
 
@@ -194,10 +222,11 @@ def memory_config_societyagent():
194
222
 
195
223
 
196
224
  def memory_config_firm():
225
+ global work_locations
197
226
  EXTRA_ATTRIBUTES = {
198
227
  "type": (int, economyv2.ORG_TYPE_FIRM),
199
228
  "location": {
200
- "aoi_position": {"aoi_id": AOI_START_ID + random.randint(1000, 10000)}
229
+ "aoi_position": {"aoi_id": random.choice(work_locations)}
201
230
  },
202
231
  "price": (float, float(np.mean(agent_skills))),
203
232
  "inventory": (int, 0),
@@ -8,10 +8,6 @@ from pycityagent.message import MessageBlockBase, MessageBlockListenerBase
8
8
  async def check_message(
9
9
  from_uuid: str, to_uuid: str, llm_client: LLM, content: str
10
10
  ) -> bool:
11
- """
12
- 使用LLM检查消息是否合规
13
- 返回: (是否合规, from_uuid, to_uuid)
14
- """
15
11
  print(f"\n检查消息: {from_uuid} -> {to_uuid}: {content}")
16
12
  is_valid = True
17
13
  prompt = f"""
@@ -0,0 +1,41 @@
1
+ import pycityproto.city.economy.v2.economy_pb2 as economyv2
2
+ from pycityagent.cityagent import SocietyAgent
3
+
4
+ async def mobility_metric(simulation):
5
+ # 使用函数属性来存储计数
6
+ if not hasattr(mobility_metric, 'step_count'):
7
+ mobility_metric.step_count = 0
8
+
9
+ # 统计访问地点数量
10
+ citizen_agents = await simulation.filter(types = [SocietyAgent])
11
+ poi_visited_info = await simulation.gather( "number_poi_visited", citizen_agents)
12
+ poi_visited_sum = 0
13
+ for group_gather in poi_visited_info:
14
+ for agent_uuid, poi_visited in group_gather.items():
15
+ poi_visited_sum += poi_visited
16
+ average_poi_visited = float(poi_visited_sum / len(citizen_agents))
17
+ print(f"Metric: Average POIs visited: {average_poi_visited}")
18
+ await simulation.mlflow_client.log_metric(key="average_poi_visited", value=average_poi_visited, step=mobility_metric.step_count)
19
+ await simulation.mlflow_client.log_metric(key="poi_visited_sum", value=poi_visited_sum, step=mobility_metric.step_count)
20
+ mobility_metric.step_count += 1
21
+
22
+ async def economy_metric(simulation):
23
+ # 使用函数属性来存储计数
24
+ if not hasattr(economy_metric, 'step_count'):
25
+ economy_metric.step_count = 0
26
+
27
+ nbs_id = await simulation.economy_client.get_org_entity_ids(economyv2.ORG_TYPE_NBS)
28
+ nbs_id = nbs_id[0]
29
+ try:
30
+ real_gdp = await simulation.economy_client.get(nbs_id, 'real_gdp')
31
+ except:
32
+ real_gdp = []
33
+ if len(real_gdp) > 0:
34
+ real_gdp = real_gdp[0]
35
+ await simulation.mlflow_client.log_metric(key="real_gdp", value=real_gdp, step=economy_metric.step_count)
36
+ other_metrics = ['prices', 'working_hours', 'depression', 'consumption_currency', 'income_currency']
37
+ other_metrics_names = ['price', 'working_hours', 'depression', 'consumption', 'income']
38
+ for metric, metric_name in zip(other_metrics, other_metrics_names):
39
+ metric_value = (await simulation.economy_client.get(nbs_id, metric))[-1]
40
+ await simulation.mlflow_client.log_metric(key=metric_name, value=metric_value, step=economy_metric.step_count)
41
+ economy_metric.step_count += 1
@@ -9,11 +9,24 @@ from pycityagent.economy import EconomyClient
9
9
  from pycityagent.llm.llm import LLM
10
10
  from pycityagent.memory import Memory
11
11
  from pycityagent.message import Messager
12
+ import pycityproto.city.economy.v2.economy_pb2 as economyv2
12
13
 
13
14
  logger = logging.getLogger("pycityagent")
14
15
 
15
16
 
16
17
  class NBSAgent(InstitutionAgent):
18
+ configurable_fields = ["time_diff", "num_labor_hours", "productivity_per_labor"]
19
+ default_values = {
20
+ "time_diff": 30 * 24 * 60 * 60,
21
+ "num_labor_hours": 168,
22
+ "productivity_per_labor": 1,
23
+ }
24
+ fields_description = {
25
+ "time_diff": "Time difference between each forward, day * hour * minute * second",
26
+ "num_labor_hours": "Number of labor hours per week",
27
+ "productivity_per_labor": "Productivity per labor hour",
28
+ }
29
+
17
30
  def __init__(
18
31
  self,
19
32
  name: str,
@@ -57,45 +70,25 @@ class NBSAgent(InstitutionAgent):
57
70
 
58
71
  async def forward(self):
59
72
  if await self.month_trigger():
60
- citizens = await self.memory.status.get("citizens")
61
- agents_forward = []
62
- if not np.all(np.array(agents_forward) > self.forward_times):
63
- return
64
- work_propensity = await self.gather_messages(citizens, "work_propensity")
73
+ await self.economy_client.calculate_real_gdp()
74
+ citizens_uuid = await self.memory.status.get("citizens")
75
+ citizens = await self.economy_client.get(self._agent_id, "citizens")
76
+ work_propensity = await self.gather_messages(citizens_uuid, "work_propensity")
65
77
  working_hours = np.mean(work_propensity) * self.num_labor_hours
66
- firm_id = await self.memory.status.get("firm_id")
67
- price = await self.economy_client.get(firm_id, "price")
68
- prices = await self.economy_client.get(self._agent_id, "prices")
69
- initial_price = prices[0]
70
- nominal_gdp = (
71
- working_hours * len(citizens) * self.productivity_per_labor * price
72
- )
73
- real_gdp = (
74
- working_hours
75
- * len(citizens)
76
- * self.productivity_per_labor
77
- * initial_price
78
- )
79
- await self.economy_client.update(
80
- self._agent_id, "nominal_gdp", [nominal_gdp], mode="merge"
81
- )
82
- await self.economy_client.update(
83
- self._agent_id, "real_gdp", [real_gdp], mode="merge"
84
- )
85
78
  await self.economy_client.update(
86
79
  self._agent_id, "working_hours", [working_hours], mode="merge"
87
80
  )
81
+ firms_id = await self.economy_client.get_org_entity_ids(economyv2.ORG_TYPE_FIRM)
82
+ prices = await self.economy_client.get(firms_id, "price")
88
83
  await self.economy_client.update(
89
- self._agent_id, "prices", [price], mode="merge"
84
+ self._agent_id, "prices", [float(np.mean(prices))], mode="merge"
90
85
  )
91
- depression = await self.gather_messages(citizens, "depression")
86
+ depression = await self.gather_messages(citizens_uuid, "depression")
92
87
  depression = np.mean(depression)
93
88
  await self.economy_client.update(
94
89
  self._agent_id, "depression", [depression], mode="merge"
95
90
  )
96
- consumption_currency = await self.gather_messages(
97
- citizens, "consumption_currency"
98
- )
91
+ consumption_currency = await self.economy_client.get(citizens, "consumption")
99
92
  consumption_currency = np.mean(consumption_currency)
100
93
  await self.economy_client.update(
101
94
  self._agent_id,
@@ -103,13 +96,8 @@ class NBSAgent(InstitutionAgent):
103
96
  [consumption_currency],
104
97
  mode="merge",
105
98
  )
106
- income_currency = await self.gather_messages(citizens, "income_currency")
99
+ income_currency = await self.economy_client.get(citizens, "income")
107
100
  income_currency = np.mean(income_currency)
108
101
  await self.economy_client.update(
109
102
  self._agent_id, "income_currency", [income_currency], mode="merge"
110
- )
111
- self.forward_times += 1
112
- for uuid in citizens:
113
- await self.send_message_to_agent(
114
- uuid, f"nbs_forward@{self.forward_times}", "economy"
115
- )
103
+ )