pycityagent 2.0.0a66__cp311-cp311-macosx_11_0_arm64.whl → 2.0.0a67__cp311-cp311-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
@@ -1,6 +1,6 @@
|
|
1
1
|
import math
|
2
2
|
from typing import List
|
3
|
-
|
3
|
+
|
4
4
|
from pycityagent.environment.simulator import Simulator
|
5
5
|
from pycityagent.llm import LLM
|
6
6
|
from pycityagent.memory import Memory
|
@@ -9,8 +9,11 @@ import numpy as np
|
|
9
9
|
from operator import itemgetter
|
10
10
|
import random
|
11
11
|
import logging
|
12
|
-
logger = logging.getLogger("pycityagent")
|
13
12
|
from pycityagent.workflow.prompt import FormatPrompt
|
13
|
+
from .dispatcher import BlockDispatcher
|
14
|
+
|
15
|
+
logger = logging.getLogger("pycityagent")
|
16
|
+
|
14
17
|
|
15
18
|
PLACE_TYPE_SELECTION_PROMPT = """
|
16
19
|
As an intelligent decision system, please determine the type of place the user needs to visit based on their input requirement.
|
@@ -45,6 +48,7 @@ Please analyze how these emotions would affect travel willingness and return onl
|
|
45
48
|
|
46
49
|
Return only the integer number without any additional text or explanation."""
|
47
50
|
|
51
|
+
|
48
52
|
def gravity_model(pois):
|
49
53
|
N = len(pois)
|
50
54
|
pois_Dis = {
|
@@ -85,9 +89,7 @@ def gravity_model(pois):
|
|
85
89
|
# distance decay coefficient, use the square of distance to calculate, so that distant places are less likely to be selected
|
86
90
|
weight = density / (distance**2) # the original weight is reasonable
|
87
91
|
res.append((poi[0]["name"], poi[0]["id"], weight, distance))
|
88
|
-
distanceProb.append(
|
89
|
-
1 / (math.sqrt(distance))
|
90
|
-
)
|
92
|
+
distanceProb.append(1 / (math.sqrt(distance)))
|
91
93
|
iflt10k = False
|
92
94
|
break
|
93
95
|
|
@@ -110,32 +112,36 @@ def gravity_model(pois):
|
|
110
112
|
]
|
111
113
|
return final
|
112
114
|
|
115
|
+
|
113
116
|
class PlaceSelectionBlock(Block):
|
114
117
|
"""
|
115
118
|
Select destination
|
116
119
|
PlaceSelectionBlock
|
117
120
|
"""
|
121
|
+
|
118
122
|
configurable_fields: List[str] = ["search_limit"]
|
119
|
-
default_values = {
|
120
|
-
"search_limit": 10000
|
121
|
-
}
|
123
|
+
default_values = {"search_limit": 10000}
|
122
124
|
|
123
125
|
def __init__(self, llm: LLM, memory: Memory, simulator: Simulator):
|
124
|
-
super().__init__(
|
126
|
+
super().__init__(
|
127
|
+
"PlaceSelectionBlock", llm=llm, memory=memory, simulator=simulator
|
128
|
+
)
|
125
129
|
self.description = "Used to select and determine destinations for unknown locations (excluding home and workplace), such as choosing specific shopping malls, restaurants and other places"
|
126
130
|
self.typeSelectionPrompt = FormatPrompt(PLACE_TYPE_SELECTION_PROMPT)
|
127
|
-
self.secondTypeSelectionPrompt = FormatPrompt(
|
131
|
+
self.secondTypeSelectionPrompt = FormatPrompt(
|
132
|
+
PLACE_SECOND_TYPE_SELECTION_PROMPT
|
133
|
+
)
|
128
134
|
self.radiusPrompt = FormatPrompt(RADIUS_PROMPT)
|
129
135
|
# configurable fields
|
130
136
|
self.search_limit = 10000
|
131
137
|
|
132
138
|
async def forward(self, step, context):
|
133
139
|
self.typeSelectionPrompt.format(
|
134
|
-
plan
|
135
|
-
intention
|
136
|
-
poi_category
|
140
|
+
plan=context["plan"],
|
141
|
+
intention=step["intention"],
|
142
|
+
poi_category=list(self.simulator.poi_cate.keys()),
|
137
143
|
)
|
138
|
-
levelOneType = await self.llm.atext_request(self.typeSelectionPrompt.to_dialog())
|
144
|
+
levelOneType = await self.llm.atext_request(self.typeSelectionPrompt.to_dialog()) # type: ignore
|
139
145
|
try:
|
140
146
|
sub_category = self.simulator.poi_cate[levelOneType]
|
141
147
|
except Exception as e:
|
@@ -143,35 +149,33 @@ class PlaceSelectionBlock(Block):
|
|
143
149
|
levelOneType = random.choice(list(self.simulator.poi_cate.keys()))
|
144
150
|
sub_category = self.simulator.poi_cate[levelOneType]
|
145
151
|
self.secondTypeSelectionPrompt.format(
|
146
|
-
plan
|
147
|
-
intention = step['intention'],
|
148
|
-
poi_category = sub_category
|
152
|
+
plan=context["plan"], intention=step["intention"], poi_category=sub_category
|
149
153
|
)
|
150
|
-
levelTwoType = await self.llm.atext_request(self.secondTypeSelectionPrompt.to_dialog())
|
151
|
-
center = await self.memory.status.get(
|
152
|
-
center = (center[
|
154
|
+
levelTwoType = await self.llm.atext_request(self.secondTypeSelectionPrompt.to_dialog()) # type: ignore
|
155
|
+
center = await self.memory.status.get("position")
|
156
|
+
center = (center["xy_position"]["x"], center["xy_position"]["y"])
|
153
157
|
self.radiusPrompt.format(
|
154
158
|
emotion_types=await self.memory.status.get("emotion_types"),
|
155
159
|
thought=await self.memory.status.get("thought"),
|
156
160
|
weather=self.simulator.sence("weather"),
|
157
|
-
temperature=self.simulator.sence("temperature")
|
161
|
+
temperature=self.simulator.sence("temperature"),
|
158
162
|
)
|
159
|
-
radius = int(await self.llm.atext_request(self.radiusPrompt.to_dialog()))
|
163
|
+
radius = int(await self.llm.atext_request(self.radiusPrompt.to_dialog())) # type: ignore
|
160
164
|
try:
|
161
165
|
pois = self.simulator.map.query_pois(
|
162
|
-
center
|
163
|
-
category_prefix
|
166
|
+
center=center,
|
167
|
+
category_prefix=levelTwoType,
|
164
168
|
radius=radius,
|
165
|
-
limit=self.search_limit
|
169
|
+
limit=self.search_limit,
|
166
170
|
)
|
167
171
|
except Exception as e:
|
168
172
|
logger.warning(f"Error querying pois: {e}")
|
169
173
|
levelTwoType = random.choice(sub_category)
|
170
174
|
pois = self.simulator.map.query_pois(
|
171
|
-
center
|
172
|
-
category_prefix
|
175
|
+
center=center,
|
176
|
+
category_prefix=levelTwoType,
|
173
177
|
radius=radius,
|
174
|
-
limit=self.search_limit
|
178
|
+
limit=self.search_limit,
|
175
179
|
)
|
176
180
|
if len(pois) > 0:
|
177
181
|
pois = gravity_model(pois)
|
@@ -183,33 +187,39 @@ class PlaceSelectionBlock(Block):
|
|
183
187
|
nextPlace = pois[sample[0]]
|
184
188
|
nextPlace = (nextPlace[0], nextPlace[1])
|
185
189
|
# save the destination to context
|
186
|
-
context[
|
187
|
-
node_id = await self.memory.stream.add_mobility(
|
190
|
+
context["next_place"] = nextPlace
|
191
|
+
node_id = await self.memory.stream.add_mobility(
|
192
|
+
description=f"For {step['intention']}, I selected the destination: {nextPlace}"
|
193
|
+
)
|
188
194
|
return {
|
189
|
-
|
190
|
-
|
191
|
-
|
192
|
-
|
195
|
+
"success": True,
|
196
|
+
"evaluation": f"Successfully selected the destination: {nextPlace}",
|
197
|
+
"consumed_time": 5,
|
198
|
+
"node_id": node_id,
|
193
199
|
}
|
194
200
|
else:
|
195
201
|
simmap = self.simulator.map
|
196
202
|
poi = random.choice(list(simmap.pois.values()))
|
197
|
-
nextPlace = (poi[
|
203
|
+
nextPlace = (poi["name"], poi["aoi_id"])
|
198
204
|
# save the destination to context
|
199
|
-
context[
|
200
|
-
node_id = await self.memory.stream.add_mobility(
|
205
|
+
context["next_place"] = nextPlace
|
206
|
+
node_id = await self.memory.stream.add_mobility(
|
207
|
+
description=f"For {step['intention']}, I selected the destination: {nextPlace}"
|
208
|
+
)
|
201
209
|
return {
|
202
|
-
|
203
|
-
|
204
|
-
|
205
|
-
|
210
|
+
"success": True,
|
211
|
+
"evaluation": f"Successfully selected the destination: {nextPlace}",
|
212
|
+
"consumed_time": 5,
|
213
|
+
"node_id": node_id,
|
206
214
|
}
|
207
215
|
|
216
|
+
|
208
217
|
class MoveBlock(Block):
|
209
218
|
"""
|
210
219
|
Execute mobility operations
|
211
220
|
MoveBlock
|
212
221
|
"""
|
222
|
+
|
213
223
|
def __init__(self, llm: LLM, memory: Memory, simulator: Simulator):
|
214
224
|
super().__init__("MoveBlock", llm=llm, memory=memory, simulator=simulator)
|
215
225
|
self.description = "Used to execute specific mobility operations, such as returning home, going to work, or visiting a specific location"
|
@@ -219,68 +229,79 @@ class MoveBlock(Block):
|
|
219
229
|
# 这里应该添加移动的具体逻辑
|
220
230
|
agent_id = await self.memory.status.get("id")
|
221
231
|
self.placeAnalysisPrompt.format(
|
222
|
-
plan
|
223
|
-
intention = step["intention"]
|
232
|
+
plan=context["plan"], intention=step["intention"]
|
224
233
|
)
|
225
234
|
number_poi_visited = await self.memory.status.get("number_poi_visited")
|
226
235
|
number_poi_visited += 1
|
227
236
|
await self.memory.status.update("number_poi_visited", number_poi_visited)
|
228
|
-
response = await self.llm.atext_request(self.placeAnalysisPrompt.to_dialog())
|
229
|
-
if response ==
|
237
|
+
response = await self.llm.atext_request(self.placeAnalysisPrompt.to_dialog()) # type: ignore
|
238
|
+
if response == "home":
|
230
239
|
# 返回到家
|
231
|
-
home = await self.memory.status.get(
|
232
|
-
home = home[
|
233
|
-
nowPlace = await self.memory.status.get(
|
234
|
-
node_id = await self.memory.stream.add_mobility(
|
235
|
-
|
240
|
+
home = await self.memory.status.get("home")
|
241
|
+
home = home["aoi_position"]["aoi_id"]
|
242
|
+
nowPlace = await self.memory.status.get("position")
|
243
|
+
node_id = await self.memory.stream.add_mobility(
|
244
|
+
description=f"I returned home"
|
245
|
+
)
|
246
|
+
if (
|
247
|
+
"aoi_position" in nowPlace
|
248
|
+
and nowPlace["aoi_position"]["aoi_id"] == home
|
249
|
+
):
|
236
250
|
return {
|
237
|
-
|
238
|
-
|
239
|
-
|
240
|
-
|
241
|
-
|
251
|
+
"success": True,
|
252
|
+
"evaluation": f"Successfully returned home (already at home)",
|
253
|
+
"to_place": home,
|
254
|
+
"consumed_time": 0,
|
255
|
+
"node_id": node_id,
|
242
256
|
}
|
243
257
|
await self.simulator.set_aoi_schedules(
|
244
258
|
person_id=agent_id,
|
245
259
|
target_positions=home,
|
246
260
|
)
|
247
261
|
return {
|
248
|
-
|
249
|
-
|
250
|
-
|
251
|
-
|
252
|
-
|
262
|
+
"success": True,
|
263
|
+
"evaluation": f"Successfully returned home",
|
264
|
+
"to_place": home,
|
265
|
+
"consumed_time": 45,
|
266
|
+
"node_id": node_id,
|
253
267
|
}
|
254
|
-
elif response ==
|
268
|
+
elif response == "workplace":
|
255
269
|
# 返回到工作地点
|
256
|
-
work = await self.memory.status.get(
|
257
|
-
work = work[
|
258
|
-
nowPlace = await self.memory.status.get(
|
259
|
-
node_id = await self.memory.stream.add_mobility(
|
260
|
-
|
270
|
+
work = await self.memory.status.get("work")
|
271
|
+
work = work["aoi_position"]["aoi_id"]
|
272
|
+
nowPlace = await self.memory.status.get("position")
|
273
|
+
node_id = await self.memory.stream.add_mobility(
|
274
|
+
description=f"I went to my workplace"
|
275
|
+
)
|
276
|
+
if (
|
277
|
+
"aoi_position" in nowPlace
|
278
|
+
and nowPlace["aoi_position"]["aoi_id"] == work
|
279
|
+
):
|
261
280
|
return {
|
262
|
-
|
263
|
-
|
264
|
-
|
265
|
-
|
266
|
-
|
281
|
+
"success": True,
|
282
|
+
"evaluation": f"Successfully reached the workplace (already at the workplace)",
|
283
|
+
"to_place": work,
|
284
|
+
"consumed_time": 0,
|
285
|
+
"node_id": node_id,
|
267
286
|
}
|
268
287
|
await self.simulator.set_aoi_schedules(
|
269
288
|
person_id=agent_id,
|
270
289
|
target_positions=work,
|
271
290
|
)
|
272
291
|
return {
|
273
|
-
|
274
|
-
|
275
|
-
|
276
|
-
|
277
|
-
|
292
|
+
"success": True,
|
293
|
+
"evaluation": f"Successfully reached the workplace",
|
294
|
+
"to_place": work,
|
295
|
+
"consumed_time": 45,
|
296
|
+
"node_id": node_id,
|
278
297
|
}
|
279
298
|
else:
|
280
299
|
# 移动到其他地点
|
281
|
-
next_place = context.get(
|
282
|
-
nowPlace = await self.memory.status.get(
|
283
|
-
node_id = await self.memory.stream.add_mobility(
|
300
|
+
next_place = context.get("next_place", None)
|
301
|
+
nowPlace = await self.memory.status.get("position")
|
302
|
+
node_id = await self.memory.stream.add_mobility(
|
303
|
+
description=f"I went to {next_place}"
|
304
|
+
)
|
284
305
|
if next_place != None:
|
285
306
|
await self.simulator.set_aoi_schedules(
|
286
307
|
person_id=agent_id,
|
@@ -289,41 +310,46 @@ class MoveBlock(Block):
|
|
289
310
|
else:
|
290
311
|
while True:
|
291
312
|
r_aoi = random.choice(list(self.simulator.map.aois.values()))
|
292
|
-
if len(r_aoi[
|
293
|
-
r_poi = random.choice(r_aoi[
|
313
|
+
if len(r_aoi["poi_ids"]) > 0:
|
314
|
+
r_poi = random.choice(r_aoi["poi_ids"])
|
294
315
|
break
|
295
316
|
poi = self.simulator.map.pois[r_poi]
|
296
|
-
next_place = (poi[
|
317
|
+
next_place = (poi["name"], poi["aoi_id"])
|
297
318
|
await self.simulator.set_aoi_schedules(
|
298
319
|
person_id=agent_id,
|
299
320
|
target_positions=next_place[1],
|
300
321
|
)
|
301
322
|
return {
|
302
|
-
|
303
|
-
|
304
|
-
|
305
|
-
|
306
|
-
|
323
|
+
"success": True,
|
324
|
+
"evaluation": f"Successfully reached the destination: {next_place}",
|
325
|
+
"to_place": next_place[1],
|
326
|
+
"consumed_time": 45,
|
327
|
+
"node_id": node_id,
|
307
328
|
}
|
308
|
-
|
329
|
+
|
330
|
+
|
309
331
|
class MobilityNoneBlock(Block):
|
310
332
|
"""
|
311
333
|
空操作
|
312
334
|
MobilityNoneBlock
|
313
335
|
"""
|
336
|
+
|
314
337
|
def __init__(self, llm: LLM, memory: Memory):
|
315
338
|
super().__init__("MobilityNoneBlock", llm=llm, memory=memory)
|
316
339
|
self.description = "Used to handle other cases"
|
317
340
|
|
318
341
|
async def forward(self, step, context):
|
319
|
-
node_id = await self.memory.stream.add_mobility(
|
342
|
+
node_id = await self.memory.stream.add_mobility(
|
343
|
+
description=f"I finished {step['intention']}"
|
344
|
+
)
|
320
345
|
return {
|
321
|
-
|
322
|
-
|
323
|
-
|
324
|
-
|
346
|
+
"success": True,
|
347
|
+
"evaluation": f'Finished executing {step["intention"]}',
|
348
|
+
"consumed_time": 0,
|
349
|
+
"node_id": node_id,
|
325
350
|
}
|
326
351
|
|
352
|
+
|
327
353
|
class MobilityBlock(Block):
|
328
354
|
place_selection_block: PlaceSelectionBlock
|
329
355
|
move_block: MoveBlock
|
@@ -340,19 +366,23 @@ class MobilityBlock(Block):
|
|
340
366
|
# 初始化调度器
|
341
367
|
self.dispatcher = BlockDispatcher(llm)
|
342
368
|
# 注册所有块
|
343
|
-
self.dispatcher.register_blocks(
|
369
|
+
self.dispatcher.register_blocks(
|
370
|
+
[self.place_selection_block, self.move_block, self.mobility_none_block]
|
371
|
+
)
|
344
372
|
|
345
|
-
async def forward(self, step, context):
|
373
|
+
async def forward(self, step, context):
|
346
374
|
self.trigger_time += 1
|
347
|
-
consumption_start =
|
375
|
+
consumption_start = (
|
376
|
+
self.llm.prompt_tokens_used + self.llm.completion_tokens_used
|
377
|
+
)
|
348
378
|
|
349
379
|
# Select the appropriate sub-block using dispatcher
|
350
380
|
selected_block = await self.dispatcher.dispatch(step)
|
351
|
-
|
381
|
+
|
352
382
|
# Execute the selected sub-block and get the result
|
353
|
-
result = await selected_block.forward(step, context)
|
383
|
+
result = await selected_block.forward(step, context) # type: ignore
|
354
384
|
|
355
385
|
consumption_end = self.llm.prompt_tokens_used + self.llm.completion_tokens_used
|
356
386
|
self.token_consumption += consumption_end - consumption_start
|
357
|
-
|
358
|
-
return result
|
387
|
+
|
388
|
+
return result
|