pycityagent 2.0.0a66__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.
- pycityagent/agent/agent.py +157 -57
- pycityagent/agent/agent_base.py +316 -43
- pycityagent/cityagent/bankagent.py +49 -9
- pycityagent/cityagent/blocks/__init__.py +1 -2
- pycityagent/cityagent/blocks/cognition_block.py +54 -31
- pycityagent/cityagent/blocks/dispatcher.py +22 -17
- pycityagent/cityagent/blocks/economy_block.py +46 -32
- pycityagent/cityagent/blocks/mobility_block.py +130 -100
- pycityagent/cityagent/blocks/needs_block.py +101 -44
- pycityagent/cityagent/blocks/other_block.py +42 -33
- pycityagent/cityagent/blocks/plan_block.py +59 -42
- pycityagent/cityagent/blocks/social_block.py +167 -116
- pycityagent/cityagent/blocks/utils.py +13 -6
- pycityagent/cityagent/firmagent.py +17 -35
- pycityagent/cityagent/governmentagent.py +3 -3
- pycityagent/cityagent/initial.py +79 -44
- pycityagent/cityagent/memory_config.py +108 -88
- pycityagent/cityagent/message_intercept.py +0 -4
- pycityagent/cityagent/metrics.py +41 -0
- pycityagent/cityagent/nbsagent.py +24 -36
- pycityagent/cityagent/societyagent.py +7 -3
- pycityagent/cli/wrapper.py +2 -2
- pycityagent/economy/econ_client.py +407 -81
- pycityagent/environment/__init__.py +0 -3
- pycityagent/environment/sim/__init__.py +0 -3
- pycityagent/environment/sim/aoi_service.py +2 -2
- pycityagent/environment/sim/client.py +3 -31
- pycityagent/environment/sim/clock_service.py +2 -2
- pycityagent/environment/sim/lane_service.py +8 -8
- pycityagent/environment/sim/light_service.py +8 -8
- pycityagent/environment/sim/pause_service.py +9 -10
- pycityagent/environment/sim/person_service.py +20 -20
- pycityagent/environment/sim/road_service.py +2 -2
- pycityagent/environment/sim/sim_env.py +21 -5
- pycityagent/environment/sim/social_service.py +4 -4
- pycityagent/environment/simulator.py +249 -27
- pycityagent/environment/utils/__init__.py +2 -2
- pycityagent/environment/utils/geojson.py +2 -2
- pycityagent/environment/utils/grpc.py +4 -4
- pycityagent/environment/utils/map_utils.py +2 -2
- pycityagent/llm/embeddings.py +147 -28
- pycityagent/llm/llm.py +122 -77
- pycityagent/llm/llmconfig.py +5 -0
- pycityagent/llm/utils.py +4 -0
- pycityagent/memory/__init__.py +0 -4
- pycityagent/memory/const.py +2 -2
- pycityagent/memory/faiss_query.py +140 -61
- pycityagent/memory/memory.py +393 -90
- pycityagent/memory/memory_base.py +140 -34
- pycityagent/memory/profile.py +13 -13
- pycityagent/memory/self_define.py +13 -13
- pycityagent/memory/state.py +14 -14
- pycityagent/message/message_interceptor.py +253 -3
- pycityagent/message/messager.py +133 -6
- pycityagent/metrics/mlflow_client.py +47 -4
- pycityagent/pycityagent-sim +0 -0
- pycityagent/pycityagent-ui +0 -0
- pycityagent/simulation/__init__.py +3 -2
- pycityagent/simulation/agentgroup.py +145 -52
- pycityagent/simulation/simulation.py +257 -62
- pycityagent/survey/manager.py +45 -3
- pycityagent/survey/models.py +42 -2
- pycityagent/tools/__init__.py +1 -2
- pycityagent/tools/tool.py +93 -69
- pycityagent/utils/avro_schema.py +2 -2
- pycityagent/utils/parsers/code_block_parser.py +1 -1
- pycityagent/utils/parsers/json_parser.py +2 -2
- pycityagent/utils/parsers/parser_base.py +2 -2
- pycityagent/workflow/block.py +64 -13
- pycityagent/workflow/prompt.py +31 -23
- pycityagent/workflow/trigger.py +91 -24
- {pycityagent-2.0.0a66.dist-info → pycityagent-2.0.0a67.dist-info}/METADATA +2 -2
- pycityagent-2.0.0a67.dist-info/RECORD +97 -0
- pycityagent/environment/interact/__init__.py +0 -0
- pycityagent/environment/interact/interact.py +0 -198
- pycityagent/environment/message/__init__.py +0 -0
- pycityagent/environment/sence/__init__.py +0 -0
- pycityagent/environment/sence/static.py +0 -416
- pycityagent/environment/sidecar/__init__.py +0 -8
- pycityagent/environment/sidecar/sidecarv2.py +0 -109
- pycityagent/environment/sim/economy_services.py +0 -192
- pycityagent/metrics/utils/const.py +0 -0
- pycityagent-2.0.0a66.dist-info/RECORD +0 -105
- {pycityagent-2.0.0a66.dist-info → pycityagent-2.0.0a67.dist-info}/LICENSE +0 -0
- {pycityagent-2.0.0a66.dist-info → pycityagent-2.0.0a67.dist-info}/WHEEL +0 -0
- {pycityagent-2.0.0a66.dist-info → pycityagent-2.0.0a67.dist-info}/entry_points.txt +0 -0
- {pycityagent-2.0.0a66.dist-info → pycityagent-2.0.0a67.dist-info}/top_level.txt +0 -0
pycityagent/cityagent/initial.py
CHANGED
@@ -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
|
-
|
24
|
+
logger.info("Initializing social network...")
|
13
25
|
|
14
|
-
#
|
26
|
+
# Define possible relationship types
|
15
27
|
relation_types = ["family", "colleague", "friend"]
|
16
28
|
|
17
|
-
#
|
29
|
+
# Get all agent IDs
|
18
30
|
agent_ids = simulation.agent_uuids
|
19
31
|
for agent_id in agent_ids:
|
20
|
-
#
|
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
|
-
|
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
|
-
|
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
|
-
|
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
|
-
|
87
|
-
|
88
|
-
|
89
|
-
|
90
|
-
|
91
|
-
|
92
|
-
|
93
|
-
|
94
|
-
|
95
|
-
|
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.
|
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.
|
109
|
-
|
110
|
-
|
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
|
-
|
11
|
-
payment_max_skill_multiplier = float(
|
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": (
|
107
|
-
|
108
|
-
|
109
|
-
|
110
|
-
|
111
|
-
|
112
|
-
|
113
|
-
|
114
|
-
|
115
|
-
|
116
|
-
|
117
|
-
|
118
|
-
|
119
|
-
|
120
|
-
|
121
|
-
|
122
|
-
|
123
|
-
|
124
|
-
|
125
|
-
|
126
|
-
|
127
|
-
|
128
|
-
|
129
|
-
|
130
|
-
|
131
|
-
|
132
|
-
|
133
|
-
|
134
|
-
|
135
|
-
|
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": (
|
139
|
-
|
140
|
-
|
141
|
-
|
142
|
-
|
143
|
-
|
144
|
-
|
145
|
-
|
146
|
-
|
147
|
-
|
148
|
-
|
149
|
-
|
150
|
-
|
151
|
-
|
152
|
-
|
153
|
-
|
154
|
-
|
155
|
-
|
156
|
-
|
157
|
-
|
158
|
-
|
159
|
-
|
160
|
-
|
161
|
-
|
162
|
-
|
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": (
|
165
|
-
"personality": (
|
166
|
-
|
167
|
-
|
168
|
-
|
169
|
-
|
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": (
|
172
|
-
|
173
|
-
|
174
|
-
|
175
|
-
|
176
|
-
|
177
|
-
|
178
|
-
|
179
|
-
|
180
|
-
|
181
|
-
|
182
|
-
|
183
|
-
|
184
|
-
|
185
|
-
|
186
|
-
|
187
|
-
|
188
|
-
|
189
|
-
|
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
|
-
|
61
|
-
|
62
|
-
|
63
|
-
|
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", [
|
84
|
+
self._agent_id, "prices", [float(np.mean(prices))], mode="merge"
|
90
85
|
)
|
91
|
-
depression = await self.gather_messages(
|
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.
|
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.
|
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
|
-
|
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)
|