pycityagent 2.0.0a51__cp310-cp310-macosx_11_0_arm64.whl → 2.0.0a53__cp310-cp310-macosx_11_0_arm64.whl
Sign up to get free protection for your applications and to get access to all the features.
- 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 -10
- pycityagent/workflow/block.py +11 -4
- {pycityagent-2.0.0a51.dist-info → pycityagent-2.0.0a53.dist-info}/METADATA +2 -2
- {pycityagent-2.0.0a51.dist-info → pycityagent-2.0.0a53.dist-info}/RECORD +35 -35
- {pycityagent-2.0.0a51.dist-info → pycityagent-2.0.0a53.dist-info}/WHEEL +1 -1
- pycityagent/cityagent/blocks/time_block.py +0 -116
- {pycityagent-2.0.0a51.dist-info → pycityagent-2.0.0a53.dist-info}/LICENSE +0 -0
- {pycityagent-2.0.0a51.dist-info → pycityagent-2.0.0a53.dist-info}/entry_points.txt +0 -0
- {pycityagent-2.0.0a51.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
|
]
|