pycityagent 2.0.0a87__cp312-cp312-macosx_11_0_arm64.whl → 2.0.0a89__cp312-cp312-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/cityagent/blocks/economy_block.py +8 -6
- pycityagent/cityagent/blocks/mobility_block.py +3 -3
- pycityagent/cityagent/blocks/needs_block.py +24 -20
- pycityagent/cityagent/blocks/plan_block.py +28 -23
- pycityagent/cityagent/initial.py +1 -0
- pycityagent/cityagent/metrics.py +3 -3
- pycityagent/cityagent/societyagent.py +2 -0
- pycityagent/llm/llm.py +1 -1
- {pycityagent-2.0.0a87.dist-info → pycityagent-2.0.0a89.dist-info}/METADATA +1 -1
- {pycityagent-2.0.0a87.dist-info → pycityagent-2.0.0a89.dist-info}/RECORD +14 -14
- {pycityagent-2.0.0a87.dist-info → pycityagent-2.0.0a89.dist-info}/LICENSE +0 -0
- {pycityagent-2.0.0a87.dist-info → pycityagent-2.0.0a89.dist-info}/WHEEL +0 -0
- {pycityagent-2.0.0a87.dist-info → pycityagent-2.0.0a89.dist-info}/entry_points.txt +0 -0
- {pycityagent-2.0.0a87.dist-info → pycityagent-2.0.0a89.dist-info}/top_level.txt +0 -0
@@ -167,16 +167,19 @@ class MonthPlanBlock(Block):
|
|
167
167
|
"UBI",
|
168
168
|
"num_labor_hours",
|
169
169
|
"productivity_per_labor",
|
170
|
+
"time_diff"
|
170
171
|
]
|
171
172
|
default_values = {
|
172
173
|
"UBI": 0,
|
173
174
|
"num_labor_hours": 168,
|
174
175
|
"productivity_per_labor": 1,
|
176
|
+
"time_diff": 30 * 24 * 60 * 60
|
175
177
|
}
|
176
178
|
fields_description = {
|
177
179
|
"UBI": "Universal Basic Income",
|
178
180
|
"num_labor_hours": "Number of labor hours per month",
|
179
181
|
"productivity_per_labor": "Productivity per labor hour",
|
182
|
+
"time_diff": "Time difference between two triggers"
|
180
183
|
}
|
181
184
|
|
182
185
|
def __init__(self, llm: LLM, memory: Memory, simulator: Simulator, economy_client: EconomyClient):
|
@@ -184,14 +187,13 @@ class MonthPlanBlock(Block):
|
|
184
187
|
self.economy_client = economy_client
|
185
188
|
self.llm_error = 0
|
186
189
|
self.last_time_trigger = None
|
187
|
-
self.time_diff = 30 * 24 * 60 * 60
|
188
190
|
self.forward_times = 0
|
189
191
|
|
190
192
|
# configurable fields
|
191
193
|
self.UBI = 0
|
192
194
|
self.num_labor_hours = 168
|
193
195
|
self.productivity_per_labor = 1
|
194
|
-
|
196
|
+
self.time_diff = 30 * 24 * 60 * 60
|
195
197
|
|
196
198
|
async def month_trigger(self):
|
197
199
|
now_time = await self.simulator.get_time()
|
@@ -254,11 +256,11 @@ class MonthPlanBlock(Block):
|
|
254
256
|
Any other output words are NOT allowed.
|
255
257
|
'''
|
256
258
|
obs_prompt = prettify_document(obs_prompt)
|
257
|
-
await self.memory.status.update('dialog_queue', [{'role': 'user', 'content': obs_prompt}], mode='merge')
|
258
|
-
dialog_queue = await self.memory.status.get('dialog_queue')
|
259
|
-
content = await self.llm.atext_request(list(dialog_queue), timeout=300)
|
260
|
-
await self.memory.status.update('dialog_queue', [{'role': 'assistant', 'content': content}], mode='merge')
|
261
259
|
try:
|
260
|
+
await self.memory.status.update('dialog_queue', [{'role': 'user', 'content': obs_prompt}], mode='merge')
|
261
|
+
dialog_queue = await self.memory.status.get('dialog_queue')
|
262
|
+
content = await self.llm.atext_request(list(dialog_queue), timeout=300)
|
263
|
+
await self.memory.status.update('dialog_queue', [{'role': 'assistant', 'content': content}], mode='merge')
|
262
264
|
propensity_dict = extract_dict_from_string(content)[0]
|
263
265
|
work_propensity, consumption_propensity = propensity_dict['work'], propensity_dict['consumption']
|
264
266
|
if isinstance(work_propensity, numbers.Number) and isinstance(consumption_propensity, numbers.Number):
|
@@ -46,7 +46,7 @@ Current temperature: {temperature}
|
|
46
46
|
Your current emotion: {emotion_types}
|
47
47
|
Your current thought: {thought}
|
48
48
|
|
49
|
-
Please analyze how these emotions would affect travel willingness and return only a single integer number between 3000-
|
49
|
+
Please analyze how these emotions would affect travel willingness and return only a single integer number between 3000-200000 representing the maximum travel radius in meters. A more positive emotional state generally leads to greater willingness to travel further.
|
50
50
|
|
51
51
|
Return only the integer number without any additional text or explanation."""
|
52
52
|
|
@@ -121,7 +121,7 @@ class PlaceSelectionBlock(Block):
|
|
121
121
|
"""
|
122
122
|
|
123
123
|
configurable_fields: List[str] = ["search_limit"]
|
124
|
-
default_values = {"search_limit":
|
124
|
+
default_values = {"search_limit": 1000}
|
125
125
|
|
126
126
|
def __init__(self, llm: LLM, memory: Memory, simulator: Simulator):
|
127
127
|
super().__init__(
|
@@ -134,7 +134,7 @@ class PlaceSelectionBlock(Block):
|
|
134
134
|
)
|
135
135
|
self.radiusPrompt = FormatPrompt(RADIUS_PROMPT)
|
136
136
|
# configurable fields
|
137
|
-
self.search_limit =
|
137
|
+
self.search_limit = 1000
|
138
138
|
|
139
139
|
async def forward(self, step, context):
|
140
140
|
poi_cate = self.simulator.get_poi_cate()
|
@@ -333,26 +333,30 @@ class NeedsBlock(Block):
|
|
333
333
|
social_satisfaction=await self.memory.status.get("social_satisfaction"),
|
334
334
|
)
|
335
335
|
|
336
|
-
|
337
|
-
|
338
|
-
|
339
|
-
|
340
|
-
|
341
|
-
|
342
|
-
|
343
|
-
|
344
|
-
|
345
|
-
|
346
|
-
|
347
|
-
|
348
|
-
|
349
|
-
|
350
|
-
|
351
|
-
|
352
|
-
|
353
|
-
|
354
|
-
|
355
|
-
|
336
|
+
retry = 3
|
337
|
+
while retry > 0:
|
338
|
+
response = await self.llm.atext_request(self.evaluation_prompt.to_dialog())
|
339
|
+
try:
|
340
|
+
new_satisfaction = json.loads(self.clean_json_response(response)) # type: ignore
|
341
|
+
# 更新所有需求的数值
|
342
|
+
for need_type, new_value in new_satisfaction.items():
|
343
|
+
if need_type in [
|
344
|
+
"hunger_satisfaction",
|
345
|
+
"energy_satisfaction",
|
346
|
+
"safety_satisfaction",
|
347
|
+
"social_satisfaction",
|
348
|
+
]:
|
349
|
+
await self.memory.status.update(need_type, new_value)
|
350
|
+
return
|
351
|
+
except json.JSONDecodeError:
|
352
|
+
logger.warning(
|
353
|
+
f"Evaluation response is not a valid JSON format: {response}"
|
354
|
+
)
|
355
|
+
retry -= 1
|
356
|
+
except Exception as e:
|
357
|
+
logger.warning(f"Error processing evaluation response: {str(e)}")
|
358
|
+
logger.warning(f"Original response: {response}")
|
359
|
+
retry -= 1
|
356
360
|
|
357
361
|
def clean_json_response(self, response: str) -> str:
|
358
362
|
"""清理LLM响应中的特殊字符"""
|
@@ -213,13 +213,19 @@ class PlanBlock(Block):
|
|
213
213
|
response = await self.llm.atext_request(
|
214
214
|
self.guidance_prompt.to_dialog()
|
215
215
|
) # type: ignore
|
216
|
-
|
217
|
-
|
218
|
-
|
219
|
-
|
220
|
-
|
221
|
-
|
222
|
-
|
216
|
+
retry = 3
|
217
|
+
while retry > 0:
|
218
|
+
try:
|
219
|
+
result = json.loads(self.clean_json_response(response)) # type: ignore
|
220
|
+
if "selected_option" not in result or "evaluation" not in result:
|
221
|
+
raise ValueError("Invalid guidance selection format")
|
222
|
+
if "attitude" not in result["evaluation"] or "subjective_norm" not in result["evaluation"] or "perceived_control" not in result["evaluation"] or "reasoning" not in result["evaluation"]:
|
223
|
+
raise ValueError("Evaluation must include attitude, subjective_norm, perceived_control, and reasoning")
|
224
|
+
return result
|
225
|
+
except Exception as e:
|
226
|
+
logger.warning(f"Error parsing guidance selection response: {str(e)}")
|
227
|
+
retry -= 1
|
228
|
+
return None
|
223
229
|
|
224
230
|
async def generate_detailed_plan(
|
225
231
|
self, current_need: str, selected_option: str
|
@@ -252,20 +258,22 @@ class PlanBlock(Block):
|
|
252
258
|
)
|
253
259
|
|
254
260
|
response = await self.llm.atext_request(self.detail_prompt.to_dialog())
|
255
|
-
|
256
|
-
|
257
|
-
|
258
|
-
|
259
|
-
|
260
|
-
|
261
|
-
|
261
|
+
retry = 3
|
262
|
+
while retry > 0:
|
263
|
+
try:
|
264
|
+
result = json.loads(self.clean_json_response(response)) # type: ignore
|
265
|
+
if "plan" not in result or "target" not in result["plan"] or "steps" not in result["plan"]:
|
266
|
+
raise ValueError("Invalid plan format")
|
267
|
+
for step in result["plan"]["steps"]:
|
268
|
+
if "intention" not in step or "type" not in step:
|
269
|
+
raise ValueError("Each step must have an intention and a type")
|
270
|
+
return result
|
271
|
+
except Exception as e:
|
272
|
+
logger.warning(f"Error parsing detailed plan: {str(e)}")
|
273
|
+
retry -= 1
|
274
|
+
return None
|
262
275
|
|
263
276
|
async def forward(self):
|
264
|
-
self.trigger_time += 1
|
265
|
-
consumption_start = (
|
266
|
-
self.llm.prompt_tokens_used + self.llm.completion_tokens_used
|
267
|
-
)
|
268
|
-
|
269
277
|
# Step 1: Select guidance plan
|
270
278
|
current_need = await self.memory.status.get("current_need")
|
271
279
|
guidance_result = await self.select_guidance(current_need)
|
@@ -278,7 +286,7 @@ class PlanBlock(Block):
|
|
278
286
|
)
|
279
287
|
|
280
288
|
if not detailed_plan or "plan" not in detailed_plan:
|
281
|
-
await self.memory.status.update("current_plan",
|
289
|
+
await self.memory.status.update("current_plan", None)
|
282
290
|
await self.memory.status.update(
|
283
291
|
"current_step", {"intention": "", "type": ""}
|
284
292
|
)
|
@@ -311,9 +319,6 @@ Execution Steps: \n{formated_steps}
|
|
311
319
|
)
|
312
320
|
await self.memory.status.update("execution_context", {"plan": formated_plan})
|
313
321
|
|
314
|
-
consumption_end = self.llm.prompt_tokens_used + self.llm.completion_tokens_used
|
315
|
-
self.token_consumption += consumption_end - consumption_start
|
316
|
-
|
317
322
|
def clean_json_response(self, response: str) -> str:
|
318
323
|
"""Clean special characters in LLM response"""
|
319
324
|
response = response.replace("```json", "").replace("```", "")
|
pycityagent/cityagent/initial.py
CHANGED
@@ -142,6 +142,7 @@ async def bind_agent_info(simulation):
|
|
142
142
|
for bank_uuid in bank_uuids:
|
143
143
|
await simulation.economy_update(uid2agent[bank_uuid], "citizens", citizen_agent_ids)
|
144
144
|
for nbs_uuid in nbs_uuids:
|
145
|
+
await simulation.update(nbs_uuid, "citizens", citizen_uuids)
|
145
146
|
await simulation.economy_update(uid2agent[nbs_uuid], "citizens", citizen_agent_ids)
|
146
147
|
logger.info("Agent info binding completed!")
|
147
148
|
|
pycityagent/cityagent/metrics.py
CHANGED
@@ -33,11 +33,11 @@ async def economy_metric(simulation):
|
|
33
33
|
economy_metric.nbs_uuid = nbs_uuids[0]
|
34
34
|
|
35
35
|
try:
|
36
|
-
real_gdp = await simulation.economy_client.get(nbs_id, 'real_gdp')
|
36
|
+
real_gdp = await simulation.economy_client.get(economy_metric.nbs_id, 'real_gdp')
|
37
37
|
except:
|
38
38
|
real_gdp = []
|
39
39
|
if len(real_gdp) > 0:
|
40
|
-
real_gdp = real_gdp[
|
40
|
+
real_gdp = real_gdp[-1]
|
41
41
|
forward_times_info = await simulation.gather("forward_times", [economy_metric.nbs_uuid])
|
42
42
|
step_count = 0
|
43
43
|
for group_gather in forward_times_info:
|
@@ -48,5 +48,5 @@ async def economy_metric(simulation):
|
|
48
48
|
other_metrics = ['prices', 'working_hours', 'depression', 'consumption_currency', 'income_currency']
|
49
49
|
other_metrics_names = ['price', 'working_hours', 'depression', 'consumption', 'income']
|
50
50
|
for metric, metric_name in zip(other_metrics, other_metrics_names):
|
51
|
-
metric_value = (await simulation.economy_client.get(nbs_id, metric))[-1]
|
51
|
+
metric_value = (await simulation.economy_client.get(economy_metric.nbs_id, metric))[-1]
|
52
52
|
await simulation.mlflow_client.log_metric(key=metric_name, value=metric_value, step=step_count)
|
@@ -73,6 +73,8 @@ class PlanAndActionBlock(Block):
|
|
73
73
|
async def step_execution(self):
|
74
74
|
"""Execute the current step"""
|
75
75
|
current_plan = await self.memory.status.get("current_plan")
|
76
|
+
if current_plan is None:
|
77
|
+
return
|
76
78
|
execution_context = await self.memory.status.get("execution_context")
|
77
79
|
current_step = await self.memory.status.get("current_step")
|
78
80
|
# check current_step is valid (not empty)
|
pycityagent/llm/llm.py
CHANGED
@@ -203,7 +203,7 @@ class LLM:
|
|
203
203
|
frequency_penalty: Optional[float] = None,
|
204
204
|
presence_penalty: Optional[float] = None,
|
205
205
|
timeout: int = 300,
|
206
|
-
retries=
|
206
|
+
retries=10,
|
207
207
|
tools: Optional[list[dict[str, Any]]] = None,
|
208
208
|
tool_choice: Optional[dict[str, Any]] = None,
|
209
209
|
):
|
@@ -1,6 +1,6 @@
|
|
1
1
|
Metadata-Version: 2.2
|
2
2
|
Name: pycityagent
|
3
|
-
Version: 2.0.
|
3
|
+
Version: 2.0.0a89
|
4
4
|
Summary: LLM-based city environment agent building library
|
5
5
|
Author-email: Yuwei Yan <pinkgranite86@gmail.com>, Junbo Yan <yanjb20thu@gmali.com>, Jun Zhang <zhangjun990222@gmali.com>
|
6
6
|
License: MIT License
|
@@ -9,7 +9,7 @@ pycityagent/tools/__init__.py,sha256=y7sMVMHf0AbivlczM2h-kr7mkgXK-WAx3S9BXLXkWvw
|
|
9
9
|
pycityagent/tools/tool.py,sha256=4ZJSHbNM8dfAVwZEw8T0a2_9OuPsPQpKSVL4WxZVBUc,9022
|
10
10
|
pycityagent/llm/llmconfig.py,sha256=6AqCMV4B_JqBD2mb98bLGzpUdlOCnziQKae-Hhxxp-E,469
|
11
11
|
pycityagent/llm/__init__.py,sha256=iWs6FLgrbRVIiqOf4ILS89gkVCTvS7HFC3vG-MWuyko,205
|
12
|
-
pycityagent/llm/llm.py,sha256=
|
12
|
+
pycityagent/llm/llm.py,sha256=vMw9AVftrsmbGkhRIwJ7jftFWpzTofhfQmu7TazDCaQ,20019
|
13
13
|
pycityagent/llm/embeddings.py,sha256=3610I-_scAy8HwRNpT8hVJpH9_8_pTLCPptqnzSq10o,11322
|
14
14
|
pycityagent/llm/utils.py,sha256=rSx_fp-_Gh0vZ-x2rqAUqnpS56BVTZ4ChfAMarB8S1A,195
|
15
15
|
pycityagent/memory/memory.py,sha256=5mUweo-BgQYbXmVRAk7HTUXsar6O6iYz79VbLuMAvjo,45063
|
@@ -66,32 +66,32 @@ pycityagent/environment/sim/social_service.py,sha256=Y4A56aKXsjSv18UFumGPjQoJVMc
|
|
66
66
|
pycityagent/environment/sim/light_service.py,sha256=q4pKcGrm7WU0h29I1dFIDOz2OV0BM-2s37uC6zokkoA,4290
|
67
67
|
pycityagent/environment/sim/clock_service.py,sha256=4Hly8CToghj0x_XbDgGrIZy1YYAlDH0EUGCiCDYpk_I,1375
|
68
68
|
pycityagent/environment/sim/road_service.py,sha256=Bb1sreO0Knt9tcqH_WJF-I3P3G92bRAlzDBEa--25GE,1297
|
69
|
-
pycityagent/cityagent/metrics.py,sha256=
|
69
|
+
pycityagent/cityagent/metrics.py,sha256=wVxLpk4jvVoPf2baiK-jMIi2rPPMRnFpSjOVyj1k74A,2718
|
70
70
|
pycityagent/cityagent/memory_config.py,sha256=lCySjh8jpE3Sj-_XTCPizxJN8rjfcYD54sed7wi2EDw,11958
|
71
71
|
pycityagent/cityagent/bankagent.py,sha256=I6MNG1fUbxKyWCLxCtvOjeVahong_LhHQKmJdRPhQL8,4097
|
72
72
|
pycityagent/cityagent/__init__.py,sha256=gcBQ-a50XegFtjigQ7xDXRBZrywBKqifiQFSRnEF8gM,572
|
73
73
|
pycityagent/cityagent/firmagent.py,sha256=vZr7kdjnxcCZ5qX7hmUIYuQQWd44GcRPbdi76WGSFy4,3760
|
74
74
|
pycityagent/cityagent/nbsagent.py,sha256=rrzL-Ep-gWB8kvoPA8nBokY1zoEZGpyQjP9oy7ZuVL4,4676
|
75
|
-
pycityagent/cityagent/initial.py,sha256=
|
76
|
-
pycityagent/cityagent/societyagent.py,sha256=
|
75
|
+
pycityagent/cityagent/initial.py,sha256=tVcO9ECGvsFatpxQirZbZf0ESFBxSpSxkm4p0x2kiO0,6263
|
76
|
+
pycityagent/cityagent/societyagent.py,sha256=i7V7BeCb_pMJpxwiR5AOUcNHRw0SXHXltt_DtCxE6pE,20121
|
77
77
|
pycityagent/cityagent/message_intercept.py,sha256=dyT1G-nMxKb2prhgtyFFHFz593qBrkk5DnHsHvG1OIc,4418
|
78
78
|
pycityagent/cityagent/governmentagent.py,sha256=XIyggG83FWUTZdOuoqc6ClCP3hhfkxNmtYRu9TFo0dU,3063
|
79
79
|
pycityagent/cityagent/blocks/dispatcher.py,sha256=U5BPeUrjrTyDaznYfT6YUJIW8vfKVRDF4EO0oOn6Td4,2886
|
80
|
-
pycityagent/cityagent/blocks/needs_block.py,sha256=
|
80
|
+
pycityagent/cityagent/blocks/needs_block.py,sha256=eFWDgsqjh4FYaGeb8wL5R9ndaXN3977ymexoItDUki8,16112
|
81
81
|
pycityagent/cityagent/blocks/cognition_block.py,sha256=yzjB0D_95vytpa5xiVdmTSpGp8H9HXcjWzzFN0OpP0k,15398
|
82
82
|
pycityagent/cityagent/blocks/social_block.py,sha256=eedOlwRTGI47QFELYmfe2a_aj0GuHJweSyDxA6AYXcU,15493
|
83
83
|
pycityagent/cityagent/blocks/__init__.py,sha256=h6si6WBcVVuglIskKQKA8Cxtf_VKen1sNPqOFKI311Q,420
|
84
|
-
pycityagent/cityagent/blocks/economy_block.py,sha256=
|
84
|
+
pycityagent/cityagent/blocks/economy_block.py,sha256=l47x2Iq15Rjj9WsH3p398dtpcejat4aCkC3X160xBlE,19843
|
85
85
|
pycityagent/cityagent/blocks/utils.py,sha256=K--6odjUDUu9YrwrHPaiPIHryo7m_MBmcBqDAy3cV5M,1816
|
86
86
|
pycityagent/cityagent/blocks/other_block.py,sha256=LdtL6248xvMvvRQx6NvdlJrWWZFu8Xusjxb9yEh1M0k,4365
|
87
|
-
pycityagent/cityagent/blocks/plan_block.py,sha256=
|
88
|
-
pycityagent/cityagent/blocks/mobility_block.py,sha256=
|
87
|
+
pycityagent/cityagent/blocks/plan_block.py,sha256=AFyjj-RimYNkZ8x6sU-ZMWwB1jqJxSvr1j0vEKmhG4w,11967
|
88
|
+
pycityagent/cityagent/blocks/mobility_block.py,sha256=OZoA-swFdX0Ezpzkyp8jJMNPQ533JpeeTCdLupoqRjI,15089
|
89
89
|
pycityagent/survey/models.py,sha256=g3xni4GcA1Py3vlGt6z4ltutjgQ4G0uINYAM8vKRJAw,5225
|
90
90
|
pycityagent/survey/__init__.py,sha256=rxwou8U9KeFSP7rMzXtmtp2fVFZxK4Trzi-psx9LPIs,153
|
91
91
|
pycityagent/survey/manager.py,sha256=tHkdeq4lTfAHwvgf4-udsXri0z2l6E00rEbvwl7SqRs,3439
|
92
|
-
pycityagent-2.0.
|
93
|
-
pycityagent-2.0.
|
94
|
-
pycityagent-2.0.
|
95
|
-
pycityagent-2.0.
|
96
|
-
pycityagent-2.0.
|
97
|
-
pycityagent-2.0.
|
92
|
+
pycityagent-2.0.0a89.dist-info/RECORD,,
|
93
|
+
pycityagent-2.0.0a89.dist-info/LICENSE,sha256=n2HPXiupinpyHMnIkbCf3OTYd3KMqbmldu1e7av0CAU,1084
|
94
|
+
pycityagent-2.0.0a89.dist-info/WHEEL,sha256=VujM3ypTCyUW6hcTDdK2ej0ARVMxlU1Djlh_zWnDgqk,109
|
95
|
+
pycityagent-2.0.0a89.dist-info/entry_points.txt,sha256=BZcne49AAIFv-hawxGnPbblea7X3MtAtoPyDX8L4OC4,132
|
96
|
+
pycityagent-2.0.0a89.dist-info/top_level.txt,sha256=yOmeu6cSXmiUtScu53a3s0p7BGtLMaV0aff83EHCTic,43
|
97
|
+
pycityagent-2.0.0a89.dist-info/METADATA,sha256=ePWFzp7BbUYZs_AtmsewHwFGrmPbUyMwamMc1KJQfno,9110
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|