pycityagent 2.0.0a52__cp39-cp39-macosx_11_0_arm64.whl → 2.0.0a53__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 +48 -62
- pycityagent/agent/agent_base.py +66 -53
- pycityagent/cityagent/bankagent.py +5 -7
- pycityagent/cityagent/blocks/__init__.py +0 -2
- pycityagent/cityagent/blocks/cognition_block.py +149 -172
- pycityagent/cityagent/blocks/economy_block.py +90 -129
- pycityagent/cityagent/blocks/mobility_block.py +56 -29
- pycityagent/cityagent/blocks/needs_block.py +163 -145
- pycityagent/cityagent/blocks/other_block.py +17 -9
- pycityagent/cityagent/blocks/plan_block.py +44 -56
- pycityagent/cityagent/blocks/social_block.py +70 -51
- pycityagent/cityagent/blocks/utils.py +2 -0
- pycityagent/cityagent/firmagent.py +6 -7
- pycityagent/cityagent/governmentagent.py +7 -9
- pycityagent/cityagent/memory_config.py +48 -48
- pycityagent/cityagent/nbsagent.py +6 -29
- pycityagent/cityagent/societyagent.py +204 -119
- pycityagent/environment/sim/client.py +10 -1
- pycityagent/environment/sim/clock_service.py +2 -2
- pycityagent/environment/sim/pause_service.py +61 -0
- pycityagent/environment/simulator.py +17 -12
- pycityagent/llm/embeddings.py +0 -24
- pycityagent/memory/faiss_query.py +29 -26
- pycityagent/memory/memory.py +720 -272
- pycityagent/pycityagent-sim +0 -0
- pycityagent/simulation/agentgroup.py +92 -99
- pycityagent/simulation/simulation.py +115 -40
- pycityagent/tools/tool.py +7 -9
- pycityagent/workflow/block.py +11 -4
- {pycityagent-2.0.0a52.dist-info → pycityagent-2.0.0a53.dist-info}/METADATA +1 -1
- {pycityagent-2.0.0a52.dist-info → pycityagent-2.0.0a53.dist-info}/RECORD +35 -35
- pycityagent/cityagent/blocks/time_block.py +0 -116
- {pycityagent-2.0.0a52.dist-info → pycityagent-2.0.0a53.dist-info}/LICENSE +0 -0
- {pycityagent-2.0.0a52.dist-info → pycityagent-2.0.0a53.dist-info}/WHEEL +0 -0
- {pycityagent-2.0.0a52.dist-info → pycityagent-2.0.0a53.dist-info}/entry_points.txt +0 -0
- {pycityagent-2.0.0a52.dist-info → pycityagent-2.0.0a53.dist-info}/top_level.txt +0 -0
pycityagent/agent/agent.py
CHANGED
@@ -14,7 +14,6 @@ from ..environment import Simulator
|
|
14
14
|
from ..llm import LLM
|
15
15
|
from ..memory import Memory
|
16
16
|
from ..message.messager import Messager
|
17
|
-
from ..metrics import MlflowClient
|
18
17
|
from .agent_base import Agent, AgentType
|
19
18
|
|
20
19
|
logger = logging.getLogger("pycityagent")
|
@@ -30,7 +29,6 @@ class CitizenAgent(Agent):
|
|
30
29
|
name: str,
|
31
30
|
llm_client: Optional[LLM] = None,
|
32
31
|
simulator: Optional[Simulator] = None,
|
33
|
-
mlflow_client: Optional[MlflowClient] = None,
|
34
32
|
memory: Optional[Memory] = None,
|
35
33
|
economy_client: Optional[EconomyClient] = None,
|
36
34
|
messager: Optional[Messager] = None, # type:ignore
|
@@ -43,7 +41,6 @@ class CitizenAgent(Agent):
|
|
43
41
|
economy_client=economy_client,
|
44
42
|
messager=messager,
|
45
43
|
simulator=simulator,
|
46
|
-
mlflow_client=mlflow_client,
|
47
44
|
memory=memory,
|
48
45
|
avro_file=avro_file,
|
49
46
|
)
|
@@ -62,42 +59,33 @@ class CitizenAgent(Agent):
|
|
62
59
|
if self._simulator is None:
|
63
60
|
logger.warning("Simulator is not set")
|
64
61
|
return
|
65
|
-
|
66
|
-
|
67
|
-
|
68
|
-
|
69
|
-
|
70
|
-
|
71
|
-
|
72
|
-
|
73
|
-
|
74
|
-
|
75
|
-
|
76
|
-
|
77
|
-
|
78
|
-
|
79
|
-
|
80
|
-
|
81
|
-
|
82
|
-
|
83
|
-
|
84
|
-
|
85
|
-
|
86
|
-
|
87
|
-
|
88
|
-
|
89
|
-
|
90
|
-
|
91
|
-
|
92
|
-
dict2pb(dict_person, person_pb2.Person())
|
93
|
-
)
|
94
|
-
person_id = resp["person_id"]
|
95
|
-
await memory.update("id", person_id, protect_llm_read_only_fields=False)
|
96
|
-
logger.debug(f"Binding to Person `{person_id}` just added to Simulator")
|
97
|
-
# 防止模拟器还没有到prepare阶段导致get_person出错
|
98
|
-
self._has_bound_to_simulator = True
|
99
|
-
self._agent_id = person_id
|
100
|
-
self.memory.set_agent_id(person_id)
|
62
|
+
FROM_MEMORY_KEYS = {
|
63
|
+
"attribute",
|
64
|
+
"home",
|
65
|
+
"work",
|
66
|
+
"vehicle_attribute",
|
67
|
+
"bus_attribute",
|
68
|
+
"pedestrian_attribute",
|
69
|
+
"bike_attribute",
|
70
|
+
}
|
71
|
+
simulator = self.simulator
|
72
|
+
status = self.status
|
73
|
+
dict_person = deepcopy(self._person_template)
|
74
|
+
for _key in FROM_MEMORY_KEYS:
|
75
|
+
try:
|
76
|
+
_value = await status.get(_key)
|
77
|
+
if _value:
|
78
|
+
dict_person[_key] = _value
|
79
|
+
except KeyError as e:
|
80
|
+
continue
|
81
|
+
resp = await simulator.add_person(
|
82
|
+
dict2pb(dict_person, person_pb2.Person())
|
83
|
+
)
|
84
|
+
person_id = resp["person_id"]
|
85
|
+
await status.update("id", person_id, protect_llm_read_only_fields=False)
|
86
|
+
logger.debug(f"Binding to Person `{person_id}` just added to Simulator")
|
87
|
+
self._agent_id = person_id
|
88
|
+
self.status.set_agent_id(person_id)
|
101
89
|
|
102
90
|
async def _bind_to_economy(self):
|
103
91
|
if self._economy_client is None:
|
@@ -109,8 +97,8 @@ class CitizenAgent(Agent):
|
|
109
97
|
await self._economy_client.remove_agents([self._agent_id])
|
110
98
|
except:
|
111
99
|
pass
|
112
|
-
person_id = await self.
|
113
|
-
currency = await self.
|
100
|
+
person_id = await self.status.get("id")
|
101
|
+
currency = await self.status.get("currency")
|
114
102
|
await self._economy_client.add_agents(
|
115
103
|
{
|
116
104
|
"id": person_id,
|
@@ -128,7 +116,7 @@ class CitizenAgent(Agent):
|
|
128
116
|
# 从消息中解析发送者 ID 和消息内容
|
129
117
|
target = payload["target"]
|
130
118
|
sender_id = payload["from"]
|
131
|
-
content = await self.
|
119
|
+
content = await self.status.get(f"{target}")
|
132
120
|
payload = {
|
133
121
|
"from": self._uuid,
|
134
122
|
"content": content,
|
@@ -138,7 +126,7 @@ class CitizenAgent(Agent):
|
|
138
126
|
|
139
127
|
class InstitutionAgent(Agent):
|
140
128
|
"""
|
141
|
-
InstitutionAgent:
|
129
|
+
InstitutionAgent: Institution agent class and definition
|
142
130
|
"""
|
143
131
|
|
144
132
|
def __init__(
|
@@ -146,7 +134,6 @@ class InstitutionAgent(Agent):
|
|
146
134
|
name: str,
|
147
135
|
llm_client: Optional[LLM] = None,
|
148
136
|
simulator: Optional[Simulator] = None,
|
149
|
-
mlflow_client: Optional[MlflowClient] = None,
|
150
137
|
memory: Optional[Memory] = None,
|
151
138
|
economy_client: Optional[EconomyClient] = None,
|
152
139
|
messager: Optional[Messager] = None, # type:ignore
|
@@ -157,7 +144,6 @@ class InstitutionAgent(Agent):
|
|
157
144
|
type=AgentType.Institution,
|
158
145
|
llm_client=llm_client,
|
159
146
|
economy_client=economy_client,
|
160
|
-
mlflow_client=mlflow_client,
|
161
147
|
messager=messager,
|
162
148
|
simulator=simulator,
|
163
149
|
memory=memory,
|
@@ -178,10 +164,10 @@ class InstitutionAgent(Agent):
|
|
178
164
|
# TODO: More general id generation
|
179
165
|
_id = random.randint(100000, 999999)
|
180
166
|
self._agent_id = _id
|
181
|
-
self.
|
167
|
+
self.status.set_agent_id(_id)
|
182
168
|
map_header = self.simulator.map.header
|
183
169
|
# TODO: remove random position assignment
|
184
|
-
await self.
|
170
|
+
await self.status.update(
|
185
171
|
"position",
|
186
172
|
{
|
187
173
|
"xy_position": {
|
@@ -201,57 +187,57 @@ class InstitutionAgent(Agent):
|
|
201
187
|
},
|
202
188
|
protect_llm_read_only_fields=False,
|
203
189
|
)
|
204
|
-
await self.
|
190
|
+
await self.status.update("id", _id, protect_llm_read_only_fields=False)
|
205
191
|
try:
|
206
192
|
await self._economy_client.remove_orgs([self._agent_id])
|
207
193
|
except:
|
208
194
|
pass
|
209
195
|
try:
|
210
|
-
|
211
|
-
_id = await
|
212
|
-
_type = await
|
196
|
+
_status = self.status
|
197
|
+
_id = await _status.get("id")
|
198
|
+
_type = await _status.get("type")
|
213
199
|
try:
|
214
|
-
nominal_gdp = await
|
200
|
+
nominal_gdp = await _status.get("nominal_gdp")
|
215
201
|
except:
|
216
202
|
nominal_gdp = []
|
217
203
|
try:
|
218
|
-
real_gdp = await
|
204
|
+
real_gdp = await _status.get("real_gdp")
|
219
205
|
except:
|
220
206
|
real_gdp = []
|
221
207
|
try:
|
222
|
-
unemployment = await
|
208
|
+
unemployment = await _status.get("unemployment")
|
223
209
|
except:
|
224
210
|
unemployment = []
|
225
211
|
try:
|
226
|
-
wages = await
|
212
|
+
wages = await _status.get("wages")
|
227
213
|
except:
|
228
214
|
wages = []
|
229
215
|
try:
|
230
|
-
prices = await
|
216
|
+
prices = await _status.get("prices")
|
231
217
|
except:
|
232
218
|
prices = []
|
233
219
|
try:
|
234
|
-
inventory = await
|
220
|
+
inventory = await _status.get("inventory")
|
235
221
|
except:
|
236
222
|
inventory = 0
|
237
223
|
try:
|
238
|
-
price = await
|
224
|
+
price = await _status.get("price")
|
239
225
|
except:
|
240
226
|
price = 0
|
241
227
|
try:
|
242
|
-
currency = await
|
228
|
+
currency = await _status.get("currency")
|
243
229
|
except:
|
244
230
|
currency = 0.0
|
245
231
|
try:
|
246
|
-
interest_rate = await
|
232
|
+
interest_rate = await _status.get("interest_rate")
|
247
233
|
except:
|
248
234
|
interest_rate = 0.0
|
249
235
|
try:
|
250
|
-
bracket_cutoffs = await
|
236
|
+
bracket_cutoffs = await _status.get("bracket_cutoffs")
|
251
237
|
except:
|
252
238
|
bracket_cutoffs = []
|
253
239
|
try:
|
254
|
-
bracket_rates = await
|
240
|
+
bracket_rates = await _status.get("bracket_rates")
|
255
241
|
except:
|
256
242
|
bracket_rates = []
|
257
243
|
await self._economy_client.add_orgs(
|
pycityagent/agent/agent_base.py
CHANGED
@@ -48,6 +48,7 @@ class Agent(ABC):
|
|
48
48
|
|
49
49
|
configurable_fields: list[str] = []
|
50
50
|
default_values: dict[str, Any] = {}
|
51
|
+
fields_description: dict[str, str] = {}
|
51
52
|
|
52
53
|
def __init__(
|
53
54
|
self,
|
@@ -57,7 +58,6 @@ class Agent(ABC):
|
|
57
58
|
economy_client: Optional[EconomyClient] = None,
|
58
59
|
messager: Optional[Messager] = None, # type:ignore
|
59
60
|
simulator: Optional[Simulator] = None,
|
60
|
-
mlflow_client: Optional[MlflowClient] = None,
|
61
61
|
memory: Optional[Memory] = None,
|
62
62
|
avro_file: Optional[dict[str, str]] = None,
|
63
63
|
copy_writer: Optional[ray.ObjectRef] = None,
|
@@ -72,7 +72,6 @@ class Agent(ABC):
|
|
72
72
|
economy_client (EconomyClient): The `EconomySim` client. Defaults to None.
|
73
73
|
messager (Messager, optional): The messager object. Defaults to None.
|
74
74
|
simulator (Simulator, optional): The simulator object. Defaults to None.
|
75
|
-
mlflow_client (MlflowClient, optional): The Mlflow object. Defaults to None.
|
76
75
|
memory (Memory, optional): The memory of the agent. Defaults to None.
|
77
76
|
avro_file (dict[str, str], optional): The avro file of the agent. Defaults to None.
|
78
77
|
copy_writer (ray.ObjectRef): The copy_writer of the agent. Defaults to None.
|
@@ -84,7 +83,6 @@ class Agent(ABC):
|
|
84
83
|
self._economy_client = economy_client
|
85
84
|
self._messager = messager
|
86
85
|
self._simulator = simulator
|
87
|
-
self._mlflow_client = mlflow_client
|
88
86
|
self._memory = memory
|
89
87
|
self._exp_id = -1
|
90
88
|
self._agent_id = -1
|
@@ -104,25 +102,33 @@ class Agent(ABC):
|
|
104
102
|
return state
|
105
103
|
|
106
104
|
@classmethod
|
107
|
-
def export_class_config(cls) ->
|
108
|
-
result = {
|
105
|
+
def export_class_config(cls) -> Dict[str, Dict]:
|
106
|
+
result = {
|
107
|
+
"agent_name": cls.__name__,
|
108
|
+
"config": {},
|
109
|
+
"description": {},
|
110
|
+
"blocks": []
|
111
|
+
}
|
109
112
|
config = {
|
110
113
|
field: cls.default_values.get(field, "default_value")
|
111
114
|
for field in cls.configurable_fields
|
112
115
|
}
|
113
116
|
result["config"] = config
|
117
|
+
result["description"] = {
|
118
|
+
field: cls.fields_description.get(field, "")
|
119
|
+
for field in cls.configurable_fields
|
120
|
+
}
|
114
121
|
# 解析类中的注解,找到Block类型的字段
|
115
122
|
hints = get_type_hints(cls)
|
116
123
|
for attr_name, attr_type in hints.items():
|
117
124
|
if inspect.isclass(attr_type) and issubclass(attr_type, Block):
|
118
125
|
block_config = attr_type.export_class_config()
|
119
|
-
result["blocks"].append(
|
120
|
-
|
121
|
-
|
122
|
-
|
123
|
-
|
124
|
-
|
125
|
-
)
|
126
|
+
result["blocks"].append({
|
127
|
+
"name": attr_name,
|
128
|
+
"config": block_config[0],
|
129
|
+
"description": block_config[1],
|
130
|
+
"children": cls._export_subblocks(attr_type)
|
131
|
+
})
|
126
132
|
return result
|
127
133
|
|
128
134
|
@classmethod
|
@@ -135,7 +141,8 @@ class Agent(ABC):
|
|
135
141
|
children.append(
|
136
142
|
{
|
137
143
|
"name": attr_name,
|
138
|
-
"config": block_config,
|
144
|
+
"config": block_config[0],
|
145
|
+
"description": block_config[1],
|
139
146
|
"children": cls._export_subblocks(attr_type),
|
140
147
|
}
|
141
148
|
)
|
@@ -216,12 +223,6 @@ class Agent(ABC):
|
|
216
223
|
"""
|
217
224
|
self._simulator = simulator
|
218
225
|
|
219
|
-
def set_mlflow_client(self, mlflow_client: MlflowClient):
|
220
|
-
"""
|
221
|
-
Set the mlflow_client of the agent.
|
222
|
-
"""
|
223
|
-
self._mlflow_client = mlflow_client
|
224
|
-
|
225
226
|
def set_economy_client(self, economy_client: EconomyClient):
|
226
227
|
"""
|
227
228
|
Set the economy_client of the agent.
|
@@ -279,16 +280,7 @@ class Agent(ABC):
|
|
279
280
|
f"EconomyClient access before assignment, please `set_economy_client` first!"
|
280
281
|
)
|
281
282
|
return self._economy_client
|
282
|
-
|
283
|
-
@property
|
284
|
-
def mlflow_client(self):
|
285
|
-
"""The Agent's MlflowClient"""
|
286
|
-
if self._mlflow_client is None:
|
287
|
-
raise RuntimeError(
|
288
|
-
f"MlflowClient access before assignment, please `set_mlflow_client` first!"
|
289
|
-
)
|
290
|
-
return self._mlflow_client
|
291
|
-
|
283
|
+
|
292
284
|
@property
|
293
285
|
def memory(self):
|
294
286
|
"""The Agent's Memory"""
|
@@ -297,6 +289,24 @@ class Agent(ABC):
|
|
297
289
|
f"Memory access before assignment, please `set_memory` first!"
|
298
290
|
)
|
299
291
|
return self._memory
|
292
|
+
|
293
|
+
@property
|
294
|
+
def status(self):
|
295
|
+
"""The Agent's Status Memory"""
|
296
|
+
if self._memory.status is None:
|
297
|
+
raise RuntimeError(
|
298
|
+
f"Status access before assignment, please `set_memory` first!"
|
299
|
+
)
|
300
|
+
return self._memory.status
|
301
|
+
|
302
|
+
@property
|
303
|
+
def stream(self):
|
304
|
+
"""The Agent's Stream Memory"""
|
305
|
+
if self._memory.stream is None:
|
306
|
+
raise RuntimeError(
|
307
|
+
f"Stream access before assignment, please `set_memory` first!"
|
308
|
+
)
|
309
|
+
return self._memory.stream
|
300
310
|
|
301
311
|
@property
|
302
312
|
def simulator(self):
|
@@ -315,6 +325,12 @@ class Agent(ABC):
|
|
315
325
|
f"Copy Writer access before assignment, please `set_pgsql_writer` first!"
|
316
326
|
)
|
317
327
|
return self._pgsql_writer
|
328
|
+
|
329
|
+
@property
|
330
|
+
def messager(self):
|
331
|
+
if self._messager is None:
|
332
|
+
raise RuntimeError("Messager is not set")
|
333
|
+
return self._messager
|
318
334
|
|
319
335
|
async def messager_ping(self):
|
320
336
|
if self._messager is None:
|
@@ -338,22 +354,15 @@ class Agent(ABC):
|
|
338
354
|
|
339
355
|
# 添加记忆上下文
|
340
356
|
if self._memory:
|
341
|
-
|
342
|
-
|
343
|
-
|
344
|
-
|
345
|
-
|
346
|
-
|
347
|
-
|
348
|
-
|
349
|
-
|
350
|
-
if relevant_memories:
|
351
|
-
dialog.append(
|
352
|
-
{
|
353
|
-
"role": "system",
|
354
|
-
"content": f"Answer based on these memories:\n{relevant_memories}",
|
355
|
-
}
|
356
|
-
)
|
357
|
+
profile_and_states = await self.status.search(survey_prompt)
|
358
|
+
relevant_activities = await self.stream.search(survey_prompt)
|
359
|
+
|
360
|
+
dialog.append(
|
361
|
+
{
|
362
|
+
"role": "system",
|
363
|
+
"content": f"Answer based on following profile and states:\n{profile_and_states}\n Related activities:\n{relevant_activities}",
|
364
|
+
}
|
365
|
+
)
|
357
366
|
|
358
367
|
# 添加问卷问题
|
359
368
|
dialog.append({"role": "user", "content": survey_prompt})
|
@@ -412,6 +421,7 @@ class Agent(ABC):
|
|
412
421
|
_data_tuples
|
413
422
|
)
|
414
423
|
)
|
424
|
+
await self.messager.send_message.remote(f"exps/{self._exp_id}/user_payback", {"count": 1})
|
415
425
|
|
416
426
|
async def generate_user_chat_response(self, question: str) -> str:
|
417
427
|
"""生成回答 —— 可重写
|
@@ -430,14 +440,15 @@ class Agent(ABC):
|
|
430
440
|
|
431
441
|
# 添加记忆上下文
|
432
442
|
if self._memory:
|
433
|
-
|
434
|
-
|
435
|
-
|
436
|
-
|
437
|
-
|
438
|
-
|
439
|
-
}
|
440
|
-
|
443
|
+
profile_and_states = await self.status.search(question, top_k=10)
|
444
|
+
relevant_activities = await self.stream.search(question, top_k=10)
|
445
|
+
|
446
|
+
dialog.append(
|
447
|
+
{
|
448
|
+
"role": "system",
|
449
|
+
"content": f"Answer based on following profile and states:\n{profile_and_states}\n Related activities:\n{relevant_activities}",
|
450
|
+
}
|
451
|
+
)
|
441
452
|
|
442
453
|
# 添加用户问题
|
443
454
|
dialog.append({"role": "user", "content": question})
|
@@ -497,6 +508,8 @@ class Agent(ABC):
|
|
497
508
|
_data
|
498
509
|
)
|
499
510
|
)
|
511
|
+
await self.messager.send_message.remote(f"exps/{self._exp_id}/user_payback", {"count": 1})
|
512
|
+
print(f"Sent payback message to {self._exp_id}")
|
500
513
|
|
501
514
|
async def process_agent_chat_response(self, payload: dict) -> str:
|
502
515
|
resp = f"Agent {self._uuid} received agent chat response: {payload}"
|
@@ -53,14 +53,12 @@ class BankAgent(InstitutionAgent):
|
|
53
53
|
|
54
54
|
async def forward(self):
|
55
55
|
if await self.month_trigger():
|
56
|
-
citizens = await self.memory.get("citizens")
|
57
|
-
|
58
|
-
|
59
|
-
|
60
|
-
break
|
61
|
-
await asyncio.sleep(1)
|
56
|
+
citizens = await self.memory.status.get("citizens")
|
57
|
+
agents_forward = []
|
58
|
+
if not np.all(np.array(agents_forward) > self.forward_times):
|
59
|
+
return
|
62
60
|
self.forward_times += 1
|
63
61
|
for uuid in citizens:
|
64
62
|
await self.send_message_to_agent(
|
65
|
-
uuid, f"bank_forward@{self.forward_times}"
|
63
|
+
uuid, f"bank_forward@{self.forward_times}", "economy"
|
66
64
|
)
|
@@ -5,7 +5,6 @@ from .needs_block import NeedsBlock
|
|
5
5
|
from .social_block import SocialBlock
|
6
6
|
from .economy_block import EconomyBlock
|
7
7
|
from .other_block import OtherBlock
|
8
|
-
from .time_block import TimeBlock
|
9
8
|
|
10
9
|
__all__ = [
|
11
10
|
"MobilityBlock",
|
@@ -16,5 +15,4 @@ __all__ = [
|
|
16
15
|
"EconomyBlock",
|
17
16
|
"OtherBlock",
|
18
17
|
"LongTermDecisionBlock",
|
19
|
-
"TimeBlock",
|
20
18
|
]
|