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.
- 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 +209 -105
- pycityagent/cityagent/blocks/needs_block.py +101 -54
- pycityagent/cityagent/blocks/other_block.py +42 -33
- pycityagent/cityagent/blocks/plan_block.py +59 -42
- pycityagent/cityagent/blocks/social_block.py +167 -126
- pycityagent/cityagent/blocks/utils.py +13 -6
- pycityagent/cityagent/firmagent.py +17 -35
- pycityagent/cityagent/governmentagent.py +3 -3
- pycityagent/cityagent/initial.py +79 -49
- pycityagent/cityagent/memory_config.py +123 -94
- pycityagent/cityagent/message_intercept.py +0 -4
- pycityagent/cityagent/metrics.py +41 -0
- pycityagent/cityagent/nbsagent.py +24 -36
- pycityagent/cityagent/societyagent.py +9 -4
- 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 +178 -111
- 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 +394 -91
- 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 +150 -54
- pycityagent/simulation/simulation.py +276 -66
- 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.0a65.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.0a65.dist-info/RECORD +0 -105
- {pycityagent-2.0.0a65.dist-info → pycityagent-2.0.0a67.dist-info}/LICENSE +0 -0
- {pycityagent-2.0.0a65.dist-info → pycityagent-2.0.0a67.dist-info}/WHEEL +0 -0
- {pycityagent-2.0.0a65.dist-info → pycityagent-2.0.0a67.dist-info}/entry_points.txt +0 -0
- {pycityagent-2.0.0a65.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,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
|
-
|
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])
|
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
|
-
|
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
|
-
|
91
|
-
|
92
|
-
|
93
|
-
|
94
|
-
|
95
|
-
|
96
|
-
|
97
|
-
|
98
|
-
|
99
|
-
|
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.
|
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.
|
114
|
-
|
115
|
-
|
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
|
-
|
10
|
-
payment_max_skill_multiplier = float(
|
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": (
|
99
|
-
|
100
|
-
|
101
|
-
|
102
|
-
|
103
|
-
|
104
|
-
|
105
|
-
|
106
|
-
|
107
|
-
|
108
|
-
|
109
|
-
|
110
|
-
|
111
|
-
|
112
|
-
|
113
|
-
|
114
|
-
|
115
|
-
|
116
|
-
|
117
|
-
|
118
|
-
|
119
|
-
|
120
|
-
|
121
|
-
|
122
|
-
|
123
|
-
|
124
|
-
|
125
|
-
|
126
|
-
|
127
|
-
|
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": (
|
131
|
-
|
132
|
-
|
133
|
-
|
134
|
-
|
135
|
-
|
136
|
-
|
137
|
-
|
138
|
-
|
139
|
-
|
140
|
-
|
141
|
-
|
142
|
-
|
143
|
-
|
144
|
-
|
145
|
-
|
146
|
-
|
147
|
-
|
148
|
-
|
149
|
-
|
150
|
-
|
151
|
-
|
152
|
-
|
153
|
-
|
154
|
-
|
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": (
|
157
|
-
"personality": (
|
158
|
-
|
159
|
-
|
160
|
-
|
161
|
-
|
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": (
|
164
|
-
|
165
|
-
|
166
|
-
|
167
|
-
|
168
|
-
|
169
|
-
|
170
|
-
|
171
|
-
|
172
|
-
|
173
|
-
|
174
|
-
|
175
|
-
|
176
|
-
|
177
|
-
|
178
|
-
|
179
|
-
|
180
|
-
|
181
|
-
|
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":
|
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":
|
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
|
-
|
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
|
+
)
|