pycityagent 2.0.0a66__cp310-cp310-macosx_11_0_arm64.whl → 2.0.0a67__cp310-cp310-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 +130 -100
  9. pycityagent/cityagent/blocks/needs_block.py +101 -44
  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 -116
  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 -44
  17. pycityagent/cityagent/memory_config.py +108 -88
  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 +7 -3
  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 +122 -77
  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 +393 -90
  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 +145 -52
  60. pycityagent/simulation/simulation.py +257 -62
  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.0a66.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.0a66.dist-info/RECORD +0 -105
  84. {pycityagent-2.0.0a66.dist-info → pycityagent-2.0.0a67.dist-info}/LICENSE +0 -0
  85. {pycityagent-2.0.0a66.dist-info → pycityagent-2.0.0a67.dist-info}/WHEEL +0 -0
  86. {pycityagent-2.0.0a66.dist-info → pycityagent-2.0.0a67.dist-info}/entry_points.txt +0 -0
  87. {pycityagent-2.0.0a66.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,12 +76,47 @@ 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])
@@ -77,36 +124,24 @@ async def bind_agent_info(simulation):
77
124
  bank_uuids = await simulation.filter(types=[BankAgent])
78
125
  nbs_uuids = await simulation.filter(types=[NBSAgent])
79
126
  citizen_agent_ids = []
80
- firm_ids = []
81
- id2uuid = {}
127
+ uid2agent, agent2uid = dict(), dict()
82
128
  for info in infos:
83
129
  for k, v in info.items():
84
130
  if k in citizen_uuids:
85
131
  citizen_agent_ids.append(v)
86
- elif k in firm_uuids:
87
- firm_ids.append(v)
88
- id2uuid[v] = k
89
- elif k in government_uuids:
90
- government_id = v
91
- elif k in bank_uuids:
92
- bank_id = v
93
- elif k in nbs_uuids:
94
- nbs_id = v
95
- for citizen_uuid in citizen_uuids:
96
- random_firm_id = random.choice(firm_ids)
97
- await simulation.update(citizen_uuid, "firm_id", random_firm_id)
98
- await simulation.update(citizen_uuid, "government_id", government_id)
99
- await simulation.update(citizen_uuid, "bank_id", bank_id)
100
- await simulation.update(citizen_uuid, "nbs_id", nbs_id)
101
- for firm_uuid in firm_uuids:
102
- await simulation.update(firm_uuid, "employees", citizen_uuids)
103
- 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:]
104
142
  for government_uuid in government_uuids:
105
- await simulation.update(government_uuid, "citizens", citizen_uuids)
106
- await simulation.update(government_uuid, "citizens_agent_id", citizen_agent_ids)
143
+ await simulation.economy_update(uid2agent[government_uuid], "citizens", citizen_agent_ids)
107
144
  for bank_uuid in bank_uuids:
108
- await simulation.update(bank_uuid, "citizens", citizen_uuids)
109
- await simulation.update(bank_uuid, "citizens_agent_id", citizen_agent_ids)
110
- for nbs_uuid in nbs_uuids:
111
- await simulation.update(nbs_uuid, "firm_id", random.choice(firm_ids))
112
- 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
+
@@ -7,8 +7,8 @@ from mosstool.map._map_util.const import AOI_START_ID
7
7
 
8
8
  from .firmagent import FirmAgent
9
9
  pareto_param = 8
10
- payment_max_skill_multiplier = 950
11
- 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)
12
12
  pmsm = payment_max_skill_multiplier
13
13
  pareto_samples = np.random.pareto(pareto_param, size=(1000, 10))
14
14
  clipped_skills = np.minimum(pmsm, (pmsm - 1) * pareto_samples + 1)
@@ -27,20 +27,17 @@ def memory_config_societyagent():
27
27
  EXTRA_ATTRIBUTES = {
28
28
  "type": (str, "citizen"),
29
29
  "city": (str, "New York", True),
30
-
31
30
  # Needs Model
32
31
  "hunger_satisfaction": (float, random.random(), True), # hunger satisfaction
33
32
  "energy_satisfaction": (float, random.random(), True), # energy satisfaction
34
33
  "safety_satisfaction": (float, random.random(), True), # safety satisfaction
35
34
  "social_satisfaction": (float, random.random(), True), # social satisfaction
36
35
  "current_need": (str, "none", True),
37
-
38
36
  # Plan Behavior Model
39
37
  "current_plan": (list, [], True),
40
38
  "current_step": (dict, {"intention": "", "type": ""}, True),
41
39
  "execution_context": (dict, {}, True),
42
40
  "plan_history": (list, [], True),
43
-
44
41
  # cognition
45
42
  "emotion": (
46
43
  dict,
@@ -57,7 +54,6 @@ def memory_config_societyagent():
57
54
  "attitude": (dict, {}, True),
58
55
  "thought": (str, "Currently nothing good or bad is happening", True),
59
56
  "emotion_types": (str, "Relief", True),
60
-
61
57
  # economy
62
58
  "work_skill": (
63
59
  float,
@@ -88,7 +84,6 @@ def memory_config_societyagent():
88
84
  "working_experience": (list, [], True),
89
85
  "work_hour_month": (float, 160, True),
90
86
  "work_hour_finish": (float, 0, True),
91
-
92
87
  # social
93
88
  "friends": (list, [], True), # friends list
94
89
  "relationships": (dict, {}, True), # relationship strength with each friend
@@ -96,97 +91,122 @@ def memory_config_societyagent():
96
91
  "chat_histories": (dict, {}, True), # all chat histories
97
92
  "interactions": (dict, {}, True), # all interaction records
98
93
  "to_discuss": (dict, {}, True),
99
-
100
94
  # mobility
101
95
  "environment": (str, "The environment outside is good", True),
102
96
  "number_poi_visited": (int, 1, True),
103
97
  }
104
98
 
105
99
  PROFILE = {
106
- "name": (str,random.choice(
107
- [
108
- "Alice",
109
- "Bob",
110
- "Charlie",
111
- "David",
112
- "Eve",
113
- "Frank",
114
- "Grace",
115
- "Helen",
116
- "Ivy",
117
- "Jack",
118
- "Kelly",
119
- "Lily",
120
- "Mike",
121
- "Nancy",
122
- "Oscar",
123
- "Peter",
124
- "Queen",
125
- "Rose",
126
- "Sam",
127
- "Tom",
128
- "Ulysses",
129
- "Vicky",
130
- "Will",
131
- "Xavier",
132
- "Yvonne",
133
- "Zack",
134
- ]
135
- ), 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
+ ),
136
134
  "gender": (str, random.choice(["male", "female"]), True),
137
135
  "age": (int, random.randint(18, 65), True),
138
- "education": (str, random.choice(
139
- ["Doctor", "Master", "Bachelor", "College", "High School"]
140
- ), True),
141
- "skill": (str, random.choice(
142
- [
143
- "Good at problem-solving",
144
- "Good at communication",
145
- "Good at creativity",
146
- "Good at teamwork",
147
- "Other",
148
- ]
149
- ), True),
150
- "occupation": (str, random.choice(
151
- [
152
- "Student",
153
- "Teacher",
154
- "Doctor",
155
- "Engineer",
156
- "Manager",
157
- "Businessman",
158
- "Artist",
159
- "Athlete",
160
- "Other",
161
- ]
162
- ), 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
+ ),
163
171
  "family_consumption": (str, random.choice(["low", "medium", "high"]), True),
164
- "consumption": (str, random.choice(["sightly low", "low", "medium", "high"]), True),
165
- "personality": (str, random.choice(
166
- ["outgoint", "introvert", "ambivert", "extrovert"]
167
- ), True),
168
- "income": "0",
169
- "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),
170
180
  "residence": (str, random.choice(["city", "suburb", "rural"]), True),
171
- "race": (str, random.choice(
172
- [
173
- "Chinese",
174
- "American",
175
- "British",
176
- "French",
177
- "German",
178
- "Japanese",
179
- "Korean",
180
- "Russian",
181
- "Other",
182
- ]
183
- ), True),
184
- "religion": (str, random.choice(
185
- ["none", "Christian", "Muslim", "Buddhist", "Hindu", "Other"]
186
- ), True),
187
- "marital_status": (str, random.choice(
188
- ["not married", "married", "divorced", "widowed"]
189
- ), 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
+ ),
190
210
  }
191
211
 
192
212
  BASE = {
@@ -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
+ )
@@ -62,6 +62,7 @@ class PlanAndActionBlock(Block):
62
62
  llm=llm, memory=memory, simulator=simulator, economy_client=economy_client
63
63
  )
64
64
  self.otherBlock = OtherBlock(llm=llm, memory=memory)
65
+
65
66
  async def plan_generation(self):
66
67
  """Generate plan"""
67
68
  current_plan = await self.memory.status.get("current_plan")
@@ -151,8 +152,10 @@ class PlanAndActionBlock(Block):
151
152
  # step execution
152
153
  await self.step_execution()
153
154
 
155
+
154
156
  class MindBlock(Block):
155
157
  """Cognition workflow"""
158
+
156
159
  cognitionBlock: CognitionBlock
157
160
 
158
161
  def __init__(self, llm: LLM, memory: Memory, simulator: Simulator):
@@ -164,11 +167,12 @@ class MindBlock(Block):
164
167
  async def forward(self):
165
168
  await self.cognitionBlock.forward()
166
169
 
170
+
167
171
  class SocietyAgent(CitizenAgent):
168
172
  update_with_sim = UpdateWithSimulator()
169
173
  mindBlock: MindBlock
170
174
  planAndActionBlock: PlanAndActionBlock
171
- update_with_sim = UpdateWithSimulator()
175
+
172
176
  configurable_fields = [
173
177
  "enable_cognition",
174
178
  "enable_mobility",
@@ -237,7 +241,7 @@ class SocietyAgent(CitizenAgent):
237
241
  ifpass = await self.check_and_update_step()
238
242
  if not ifpass:
239
243
  return
240
-
244
+
241
245
  await self.planAndActionBlock.forward()
242
246
 
243
247
  if self.enable_cognition:
@@ -362,7 +366,7 @@ class SocietyAgent(CitizenAgent):
362
366
 
363
367
  if not content:
364
368
  return ""
365
-
369
+
366
370
  # 添加记忆
367
371
  description = f"You received a social message: {content}"
368
372
  await self.memory.stream.add_social(description=description)
@@ -1,7 +1,7 @@
1
1
  import os
2
- import sys
3
- import subprocess
4
2
  import signal
3
+ import subprocess
4
+ import sys
5
5
 
6
6
  _script_dir = os.path.dirname(os.path.abspath(__file__))
7
7
  _parent_dir = os.path.dirname(_script_dir)