pycityagent 2.0.0a52__cp312-cp312-macosx_11_0_arm64.whl → 2.0.0a53__cp312-cp312-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 -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
@@ -21,20 +21,18 @@ from pycityagent.workflow import Block
|
|
21
21
|
from pycityagent.economy import EconomyClient
|
22
22
|
from .utils import *
|
23
23
|
import numbers
|
24
|
-
import asyncio
|
25
24
|
|
26
|
-
|
27
|
-
|
28
|
-
|
29
25
|
class WorkBlock(Block):
|
26
|
+
"""WorkPlace Block"""
|
30
27
|
def __init__(self, llm: LLM, memory: Memory, simulator: Simulator):
|
31
|
-
super().__init__("WorkBlock", llm, memory, simulator)
|
28
|
+
super().__init__("WorkBlock", llm=llm, memory=memory, simulator=simulator)
|
32
29
|
self.description = "Do work"
|
33
30
|
self.guidance_prompt = FormatPrompt(template=TIME_ESTIMATE_PROMPT)
|
34
31
|
async def forward(self, step, context):
|
35
32
|
self.guidance_prompt.format(
|
36
33
|
plan=context['plan'],
|
37
|
-
intention=step['intention']
|
34
|
+
intention=step['intention'],
|
35
|
+
emotion_types=await self.memory.status.get("emotion_types"),
|
38
36
|
)
|
39
37
|
result = await self.llm.atext_request(self.guidance_prompt.to_dialog())
|
40
38
|
result = clean_json_response(result)
|
@@ -42,108 +40,94 @@ class WorkBlock(Block):
|
|
42
40
|
result = json.loads(result)
|
43
41
|
time = result['time']
|
44
42
|
start_time = await self.simulator.get_time(format_time=True)
|
45
|
-
await self.memory.update("working_experience", [f"Start from {start_time}, worked {time} minutes on {step['intention']}"], mode="merge")
|
46
|
-
work_hour_finish = await self.memory.get("work_hour_finish")
|
43
|
+
await self.memory.status.update("working_experience", [f"Start from {start_time}, worked {time} minutes on {step['intention']}"], mode="merge")
|
44
|
+
work_hour_finish = await self.memory.status.get("work_hour_finish")
|
47
45
|
work_hour_finish += float(time/60)
|
48
|
-
await self.memory.
|
46
|
+
node_id = await self.memory.stream.add_economy(description=f"I worked {time} minutes on {step['intention']}")
|
47
|
+
await self.memory.status.update("work_hour_finish", work_hour_finish)
|
49
48
|
return {
|
50
49
|
'success': True,
|
51
|
-
'evaluation': f'
|
52
|
-
'consumed_time': time
|
50
|
+
'evaluation': f'work: {step["intention"]}',
|
51
|
+
'consumed_time': time,
|
52
|
+
'node_id': node_id
|
53
53
|
}
|
54
54
|
except Exception as e:
|
55
55
|
logger.warning(f"解析时间评估响应时发生错误: {str(e)}, 原始结果: {result}")
|
56
56
|
time = random.randint(1, 5)*60
|
57
57
|
start_time = await self.simulator.get_time(format_time=True)
|
58
|
-
await self.memory.update("working_experience", [f"Start from {start_time}, worked {time} minutes on {step['intention']}"], mode="merge")
|
59
|
-
work_hour_finish = await self.memory.get("work_hour_finish")
|
58
|
+
await self.memory.status.update("working_experience", [f"Start from {start_time}, worked {time} minutes on {step['intention']}"], mode="merge")
|
59
|
+
work_hour_finish = await self.memory.status.get("work_hour_finish")
|
60
|
+
node_id = await self.memory.stream.add_economy(description=f"I worked {time} minutes on {step['intention']}")
|
60
61
|
work_hour_finish += float(time/60)
|
61
|
-
await self.memory.update("work_hour_finish", work_hour_finish)
|
62
|
+
await self.memory.status.update("work_hour_finish", work_hour_finish)
|
62
63
|
return {
|
63
64
|
'success': True,
|
64
|
-
'evaluation': f'
|
65
|
-
'consumed_time': time
|
65
|
+
'evaluation': f'work: {step["intention"]}',
|
66
|
+
'consumed_time': time,
|
67
|
+
'node_id': node_id
|
66
68
|
}
|
67
69
|
|
68
70
|
class ConsumptionBlock(Block):
|
69
71
|
"""
|
70
|
-
determine the consumption
|
72
|
+
determine the consumption amount, and items
|
71
73
|
"""
|
72
74
|
def __init__(self, llm: LLM, memory: Memory, simulator: Simulator, economy_client: EconomyClient):
|
73
|
-
super().__init__("ConsumptionBlock", llm, memory, simulator)
|
75
|
+
super().__init__("ConsumptionBlock", llm=llm, memory=memory, simulator=simulator)
|
74
76
|
self.economy_client = economy_client
|
75
77
|
self.forward_times = 0
|
76
|
-
self.description = "Used to determine the consumption
|
78
|
+
self.description = "Used to determine the consumption amount, and items"
|
77
79
|
|
78
80
|
async def forward(self, step, context):
|
79
81
|
self.forward_times += 1
|
80
|
-
agent_id = await self.memory.get("id") # agent_id
|
81
|
-
firm_id = await self.memory.get('firm_id')
|
82
|
+
agent_id = await self.memory.status.get("id") # agent_id
|
83
|
+
firm_id = await self.memory.status.get('firm_id')
|
82
84
|
intention = step["intention"]
|
83
|
-
|
84
|
-
|
85
|
-
nowPlace = (nowPlace['x'], nowPlace['y'])
|
86
|
-
all_poi_types = self.simulator.get_poi_categories(nowPlace, 2000)
|
87
|
-
if len(all_poi_types) == 0:
|
88
|
-
all_poi_types.extend(['supermarket'])
|
89
|
-
prompt = 'Your intention is to {intention}. Which type of POI do you want to go to? Select one and only one type from the following types: {all_poi_types}. Any other output is NOT acceptable.'
|
90
|
-
prompt = FormatPrompt(prompt)
|
91
|
-
prompt.format(intention=intention, all_poi_types=all_poi_types)
|
92
|
-
poi_type = await self.llm.atext_request(prompt.to_dialog(), timeout=300)
|
93
|
-
poi_type = poi_type.strip('[').strip(']')
|
94
|
-
if poi_type not in all_poi_types:
|
95
|
-
poi_type = random.choice(all_poi_types)
|
96
|
-
around_pois = self.simulator.get_around_poi(nowPlace, 2000, poi_type)
|
97
|
-
if len(around_pois) == 0:
|
98
|
-
around_pois.extend(['沃尔玛', '家乐福', '华润万家', '物美'])
|
99
|
-
prompt = """
|
100
|
-
You're a {gender} and your education level is {education}.
|
101
|
-
Your intention is to {intention} and you plan to consume for ${consumption}.
|
102
|
-
The follwing are the places you can go to consume:
|
103
|
-
{around_pois}
|
104
|
-
Where should you go to make a purchase, when should you go, and what items will you spend on?
|
105
|
-
Respond with this format: [place, time, items to buy]. Any other output is NOT acceptable.
|
106
|
-
"""
|
107
|
-
prompt = FormatPrompt(prompt)
|
108
|
-
data = {key: await self.memory.get(key) for key in ['gender', 'education']}
|
109
|
-
data['around_pois'] = around_pois
|
110
|
-
data['intention'] = intention
|
111
|
-
month_consumption = await self.memory.get('to_consumption_currency')
|
112
|
-
consumption_currency = await self.memory.get('consumption_currency')
|
85
|
+
month_consumption = await self.memory.status.get('to_consumption_currency')
|
86
|
+
consumption_currency = await self.memory.status.get('consumption_currency')
|
113
87
|
if consumption_currency >= month_consumption:
|
114
|
-
|
115
|
-
|
88
|
+
node_id = await self.memory.stream.add_economy(description=f"I have passed the monthly consumption limit, so I will not consume.")
|
89
|
+
return {
|
90
|
+
'success': False,
|
91
|
+
'evaluation': f"I have passed the monthly consumption limit, so I will not consume.",
|
92
|
+
'consumed_time': 0,
|
93
|
+
'node_id': node_id
|
94
|
+
}
|
95
|
+
consumption = min(month_consumption/1, month_consumption-consumption_currency)
|
116
96
|
price = await self.economy_client.get(firm_id, 'price') # agent_id
|
117
97
|
wealth = await self.economy_client.get(agent_id, 'currency')
|
118
|
-
this_demand = int(
|
98
|
+
this_demand = int(consumption//price)
|
119
99
|
_, post_consumption_wealth = await self.economy_client.calculate_consumption(firm_id, [agent_id], [this_demand])
|
120
100
|
post_consumption_wealth = post_consumption_wealth[0]
|
121
|
-
await self.memory.update('consumption_currency', consumption_currency+wealth-post_consumption_wealth)
|
122
|
-
goods_demand = await self.memory.get('goods_demand')
|
123
|
-
goods_consumption = await self.memory.get('goods_consumption')
|
124
|
-
await self.memory.update('goods_demand', goods_demand+this_demand)
|
125
|
-
await self.memory.update('goods_consumption', goods_consumption+int((wealth-post_consumption_wealth)//price))
|
126
|
-
|
127
|
-
|
128
|
-
|
101
|
+
await self.memory.status.update('consumption_currency', consumption_currency+wealth-post_consumption_wealth)
|
102
|
+
goods_demand = await self.memory.status.get('goods_demand')
|
103
|
+
goods_consumption = await self.memory.status.get('goods_consumption')
|
104
|
+
await self.memory.status.update('goods_demand', goods_demand+this_demand)
|
105
|
+
await self.memory.status.update('goods_consumption', goods_consumption+int((wealth-post_consumption_wealth)//price))
|
106
|
+
node_id = await self.memory.stream.add_economy(description=f"I bought some goods, and spent {this_demand} on {intention}")
|
107
|
+
evaluation = {
|
108
|
+
'success': True,
|
109
|
+
'evaluation': f"I bought some goods, and spent {this_demand} on {intention}",
|
110
|
+
'consumed_time': 20,
|
111
|
+
'node_id': node_id
|
112
|
+
}
|
129
113
|
return evaluation
|
130
114
|
|
131
115
|
class EconomyNoneBlock(Block):
|
132
116
|
"""
|
133
|
-
Do
|
117
|
+
Do anything else
|
134
118
|
NoneBlock
|
135
119
|
"""
|
136
120
|
def __init__(self, llm: LLM, memory: Memory):
|
137
|
-
super().__init__("NoneBlock", llm, memory)
|
138
|
-
self.description = "Do
|
139
|
-
self.forward_times = 0
|
121
|
+
super().__init__("NoneBlock", llm=llm, memory=memory)
|
122
|
+
self.description = "Do anything else"
|
140
123
|
|
141
124
|
async def forward(self, step, context):
|
142
|
-
self.
|
125
|
+
node_id = await self.memory.stream.add_economy(description=f"I {step['intention']}")
|
143
126
|
return {
|
144
127
|
'success': True,
|
145
|
-
'evaluation': f'
|
146
|
-
'consumed_time': 0
|
128
|
+
'evaluation': f'Finished{step["intention"]}',
|
129
|
+
'consumed_time': 0,
|
130
|
+
'node_id': node_id
|
147
131
|
}
|
148
132
|
|
149
133
|
class EconomyBlock(Block):
|
@@ -152,37 +136,26 @@ class EconomyBlock(Block):
|
|
152
136
|
none_block: EconomyNoneBlock
|
153
137
|
|
154
138
|
def __init__(self, llm: LLM, memory: Memory, simulator: Simulator, economy_client: EconomyClient):
|
155
|
-
super().__init__("EconomyBlock", llm, memory, simulator)
|
139
|
+
super().__init__("EconomyBlock", llm=llm, memory=memory, simulator=simulator)
|
156
140
|
self.economy_client = economy_client
|
157
|
-
# 初始化所有块
|
158
141
|
self.work_block = WorkBlock(llm, memory, simulator)
|
159
142
|
self.consumption_block = ConsumptionBlock(llm, memory, simulator, economy_client)
|
160
143
|
self.none_block = EconomyNoneBlock(llm, memory)
|
161
144
|
self.trigger_time = 0
|
162
145
|
self.token_consumption = 0
|
163
|
-
# 初始化调度器
|
164
146
|
self.dispatcher = BlockDispatcher(llm)
|
165
|
-
# 注册所有块
|
166
147
|
self.dispatcher.register_blocks([self.work_block, self.consumption_block, self.none_block])
|
167
148
|
|
168
149
|
async def forward(self, step, context):
|
169
150
|
self.trigger_time += 1
|
170
|
-
consumption_start = self.llm.prompt_tokens_used + self.llm.completion_tokens_used
|
171
|
-
|
172
|
-
# Select the appropriate sub-block using dispatcher
|
173
|
-
# selected_block = await self.dispatcher.dispatch(step)
|
174
151
|
selected_block = self.consumption_block
|
175
|
-
|
176
|
-
# Execute the selected sub-block and get the result
|
177
|
-
result = await selected_block.forward(step, context) # type: ignore
|
178
|
-
|
152
|
+
result = await selected_block.forward(step, context) # type: ignore
|
179
153
|
return result
|
180
154
|
|
181
|
-
# @trigger_class()
|
182
155
|
class MonthPlanBlock(Block):
|
183
156
|
"""Monthly Planning"""
|
184
157
|
def __init__(self, llm: LLM, memory: Memory, simulator: Simulator, economy_client: EconomyClient):
|
185
|
-
super().__init__("MonthPlanBlock", llm, memory, simulator)
|
158
|
+
super().__init__("MonthPlanBlock", llm=llm, memory=memory, simulator=simulator)
|
186
159
|
self.economy_client = economy_client
|
187
160
|
self.llm_error = 0
|
188
161
|
self.last_time_trigger = None
|
@@ -198,26 +171,16 @@ class MonthPlanBlock(Block):
|
|
198
171
|
|
199
172
|
async def forward(self):
|
200
173
|
if await self.month_trigger():
|
201
|
-
|
202
|
-
|
203
|
-
|
204
|
-
|
205
|
-
|
206
|
-
|
207
|
-
|
208
|
-
|
209
|
-
|
210
|
-
|
211
|
-
agent_id = await self.memory.get("id")
|
212
|
-
firm_id = await self.memory.get('firm_id')
|
213
|
-
bank_id = await self.memory.get('bank_id')
|
214
|
-
name = await self.memory.get('name')
|
215
|
-
age = await self.memory.get('age')
|
216
|
-
city = await self.memory.get('city')
|
217
|
-
job = await self.memory.get('occupation')
|
218
|
-
skill = await self.memory.get('work_skill')
|
219
|
-
consumption = await self.memory.get('consumption_currency')
|
220
|
-
tax_paid = await self.memory.get('tax_paid')
|
174
|
+
agent_id = await self.memory.status.get("id")
|
175
|
+
firm_id = await self.memory.status.get('firm_id')
|
176
|
+
bank_id = await self.memory.status.get('bank_id')
|
177
|
+
name = await self.memory.status.get('name')
|
178
|
+
age = await self.memory.status.get('age')
|
179
|
+
city = await self.memory.status.get('city')
|
180
|
+
job = await self.memory.status.get('occupation')
|
181
|
+
skill = await self.memory.status.get('work_skill')
|
182
|
+
consumption = await self.memory.status.get('consumption_currency')
|
183
|
+
tax_paid = await self.memory.status.get('tax_paid')
|
221
184
|
price = await self.economy_client.get(firm_id, 'price')
|
222
185
|
wealth = await self.economy_client.get(agent_id, 'currency')
|
223
186
|
interest_rate = await self.economy_client.get(bank_id, 'interest_rate')
|
@@ -228,7 +191,7 @@ class MonthPlanBlock(Block):
|
|
228
191
|
job_prompt = f'''
|
229
192
|
In the previous month, you worked as a(an) {job}. If you continue working this month, your expected hourly income will be ${skill:.2f}.
|
230
193
|
'''
|
231
|
-
consumption_propensity = await self.memory.get('consumption_propensity')
|
194
|
+
consumption_propensity = await self.memory.status.get('consumption_propensity')
|
232
195
|
if (consumption <= 0) and (consumption_propensity > 0):
|
233
196
|
consumption_prompt = f'''
|
234
197
|
Besides, you had no consumption due to shortage of goods.
|
@@ -238,7 +201,7 @@ class MonthPlanBlock(Block):
|
|
238
201
|
Besides, your consumption was ${consumption:.2f}.
|
239
202
|
'''
|
240
203
|
tax_prompt = f'''Your tax deduction amounted to ${tax_paid:.2f}, and the government uses the tax revenue to provide social services to all citizens.'''
|
241
|
-
if UBI:
|
204
|
+
if UBI and self.forward_times >= 96:
|
242
205
|
tax_prompt = f'{tax_prompt} Specifically, the government directly provides ${UBI} per capita in each month.'
|
243
206
|
price_prompt = f'''Meanwhile, in the consumption market, the average price of essential goods is now at ${price:.2f}.'''
|
244
207
|
job_prompt = prettify_document(job_prompt)
|
@@ -254,41 +217,42 @@ class MonthPlanBlock(Block):
|
|
254
217
|
Any other output words are NOT allowed.
|
255
218
|
'''
|
256
219
|
obs_prompt = prettify_document(obs_prompt)
|
257
|
-
await self.memory.update('dialog_queue', [{'role': 'user', 'content': obs_prompt}], mode='merge')
|
258
|
-
dialog_queue = await self.memory.get('dialog_queue')
|
220
|
+
await self.memory.status.update('dialog_queue', [{'role': 'user', 'content': obs_prompt}], mode='merge')
|
221
|
+
dialog_queue = await self.memory.status.get('dialog_queue')
|
259
222
|
content = await self.llm.atext_request(list(dialog_queue), timeout=300)
|
260
|
-
await self.memory.update('dialog_queue', [{'role': 'assistant', 'content': content}], mode='merge')
|
223
|
+
await self.memory.status.update('dialog_queue', [{'role': 'assistant', 'content': content}], mode='merge')
|
261
224
|
try:
|
262
225
|
propensity_dict = extract_dict_from_string(content)[0]
|
263
226
|
work_propensity, consumption_propensity = propensity_dict['work'], propensity_dict['consumption']
|
264
227
|
if isinstance(work_propensity, numbers.Number) and isinstance(consumption_propensity, numbers.Number):
|
265
|
-
await self.memory.update('work_propensity', work_propensity)
|
266
|
-
await self.memory.update('consumption_propensity', consumption_propensity)
|
228
|
+
await self.memory.status.update('work_propensity', work_propensity)
|
229
|
+
await self.memory.status.update('consumption_propensity', consumption_propensity)
|
267
230
|
else:
|
268
231
|
self.llm_error += 1
|
269
232
|
except:
|
270
233
|
self.llm_error += 1
|
271
234
|
|
272
|
-
skill = await self.memory.get('work_skill')
|
273
|
-
work_propensity = await self.memory.get('work_propensity')
|
274
|
-
consumption_propensity = await self.memory.get('consumption_propensity')
|
235
|
+
skill = await self.memory.status.get('work_skill')
|
236
|
+
work_propensity = await self.memory.status.get('work_propensity')
|
237
|
+
consumption_propensity = await self.memory.status.get('consumption_propensity')
|
275
238
|
work_hours = work_propensity * num_labor_hours
|
276
|
-
await self.memory.update('income_currency', work_hours * skill)
|
239
|
+
await self.memory.status.update('income_currency', work_hours * skill)
|
277
240
|
wealth = await self.economy_client.get(agent_id, 'currency')
|
278
241
|
await self.economy_client.update(agent_id, 'currency', wealth + work_hours * skill)
|
279
242
|
await self.economy_client.add_delta_value(firm_id, 'inventory', int(work_hours*productivity_per_labor))
|
280
243
|
|
281
|
-
|
282
|
-
|
283
|
-
|
284
|
-
|
244
|
+
if UBI and self.forward_times >= 96:
|
245
|
+
income_currency = await self.memory.status.get('income_currency')
|
246
|
+
await self.memory.status.update('income_currency', income_currency + UBI)
|
247
|
+
wealth = await self.economy_client.get(agent_id, 'currency')
|
248
|
+
await self.economy_client.update(agent_id, 'currency', wealth + UBI)
|
285
249
|
|
286
250
|
wealth = await self.economy_client.get(agent_id, 'currency')
|
287
|
-
await self.memory.update('to_consumption_currency', consumption_propensity*wealth)
|
251
|
+
await self.memory.status.update('to_consumption_currency', consumption_propensity*wealth)
|
288
252
|
|
289
|
-
await self.memory.update('consumption_currency', 0)
|
290
|
-
await self.memory.update('goods_demand', 0)
|
291
|
-
await self.memory.update('goods_consumption', 0)
|
253
|
+
await self.memory.status.update('consumption_currency', 0)
|
254
|
+
await self.memory.status.update('goods_demand', 0)
|
255
|
+
await self.memory.status.update('goods_consumption', 0)
|
292
256
|
|
293
257
|
if self.forward_times % 3 == 0:
|
294
258
|
obs_prompt = f'''
|
@@ -335,11 +299,11 @@ class MonthPlanBlock(Block):
|
|
335
299
|
else:
|
336
300
|
content[k] = category2score[content[k].lower()]
|
337
301
|
depression = sum(list(content.values()))
|
338
|
-
await self.memory.update('depression', depression)
|
302
|
+
await self.memory.status.update('depression', depression)
|
339
303
|
except:
|
340
304
|
self.llm_error += 1
|
341
305
|
|
342
|
-
if UBI and self.forward_times % 12:
|
306
|
+
if UBI and self.forward_times >= 96 and self.forward_times % 12:
|
343
307
|
obs_prompt = f'''
|
344
308
|
{problem_prompt} {job_prompt} {consumption_prompt} {tax_prompt} {price_prompt}
|
345
309
|
Your current savings account balance is ${wealth:.2f}. Interest rates, as set by your bank, stand at {interest_rate*100:.2f}%.
|
@@ -347,10 +311,7 @@ class MonthPlanBlock(Block):
|
|
347
311
|
'''
|
348
312
|
obs_prompt = prettify_document(obs_prompt)
|
349
313
|
content = await self.llm.atext_request([{'role': 'user', 'content': obs_prompt}], timeout=300)
|
350
|
-
await self.memory.update('ubi_opinion', [content], mode='merge')
|
351
|
-
ubi_opinion = await self.memory.get('ubi_opinion')
|
352
|
-
with open(f'/data1/linian/SocietySim/socialcity-agent-zoo/ubi_opinions/{agent_id}.pkl', 'wb') as f:
|
353
|
-
pkl.dump(ubi_opinion, f)
|
314
|
+
await self.memory.status.update('ubi_opinion', [content], mode='merge')
|
354
315
|
|
355
316
|
self.forward_times += 1
|
356
|
-
await self.memory.update('forward', self.forward_times)
|
317
|
+
await self.memory.status.update('forward', self.forward_times)
|
@@ -1,8 +1,8 @@
|
|
1
1
|
from typing import List
|
2
2
|
from .dispatcher import BlockDispatcher
|
3
3
|
from pycityagent.environment.simulator import Simulator
|
4
|
-
from pycityagent.llm
|
5
|
-
from pycityagent.memory
|
4
|
+
from pycityagent.llm import LLM
|
5
|
+
from pycityagent.memory import Memory
|
6
6
|
from pycityagent.workflow.block import Block
|
7
7
|
import random
|
8
8
|
import logging
|
@@ -31,25 +31,32 @@ User requirement: {intention}
|
|
31
31
|
Your output must be a single selection from ['home', 'workplace', 'other'] without any additional text or explanation.
|
32
32
|
"""
|
33
33
|
|
34
|
+
RADIUS_PROMPT = """As an intelligent decision system, please determine the maximum travel radius (in meters) based on the current emotional state.
|
35
|
+
|
36
|
+
Your current emotion: {emotion_types}
|
37
|
+
Your current thought: {thought}
|
38
|
+
|
39
|
+
Please analyze how these emotions would affect travel willingness and return only a single integer number between 1000-100000 representing the maximum travel radius in meters. A more positive emotional state generally leads to greater willingness to travel further.
|
40
|
+
|
41
|
+
Return only the integer number without any additional text or explanation."""
|
42
|
+
|
34
43
|
class PlaceSelectionBlock(Block):
|
35
44
|
"""
|
36
45
|
选择目的地
|
37
46
|
PlaceSelectionBlock
|
38
47
|
"""
|
39
|
-
configurable_fields: List[str] = ["
|
48
|
+
configurable_fields: List[str] = ["search_limit"]
|
40
49
|
default_values = {
|
41
|
-
"search_radius": 100000,
|
42
50
|
"search_limit": 10
|
43
51
|
}
|
44
52
|
|
45
53
|
def __init__(self, llm: LLM, memory: Memory, simulator: Simulator):
|
46
|
-
super().__init__("PlaceSelectionBlock", llm, memory, simulator)
|
54
|
+
super().__init__("PlaceSelectionBlock", llm=llm, memory=memory, simulator=simulator)
|
47
55
|
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"
|
48
56
|
self.typeSelectionPrompt = FormatPrompt(PLACE_TYPE_SELECTION_PROMPT)
|
49
57
|
self.secondTypeSelectionPrompt = FormatPrompt(PLACE_SECOND_TYPE_SELECTION_PROMPT)
|
50
|
-
|
58
|
+
self.radiusPrompt = FormatPrompt(RADIUS_PROMPT)
|
51
59
|
# configurable fields
|
52
|
-
self.search_radius = 100000
|
53
60
|
self.search_limit = 10
|
54
61
|
|
55
62
|
async def forward(self, step, context):
|
@@ -71,13 +78,18 @@ class PlaceSelectionBlock(Block):
|
|
71
78
|
poi_category = sub_category
|
72
79
|
)
|
73
80
|
levelTwoType = await self.llm.atext_request(self.secondTypeSelectionPrompt.to_dialog()) # type: ignore
|
74
|
-
center = await self.memory.get('position')
|
81
|
+
center = await self.memory.status.get('position')
|
75
82
|
center = (center['xy_position']['x'], center['xy_position']['y'])
|
83
|
+
self.radiusPrompt.format(
|
84
|
+
emotion_types=await self.memory.status.get("emotion_types"),
|
85
|
+
thought=await self.memory.status.get("thought")
|
86
|
+
)
|
87
|
+
radius = int(await self.llm.atext_request(self.radiusPrompt.to_dialog())) # type: ignore
|
76
88
|
try:
|
77
89
|
pois = self.simulator.map.query_pois(
|
78
90
|
center = center,
|
79
91
|
category_prefix = levelTwoType,
|
80
|
-
radius=
|
92
|
+
radius=radius,
|
81
93
|
limit=self.search_limit
|
82
94
|
)
|
83
95
|
except Exception as e:
|
@@ -86,7 +98,7 @@ class PlaceSelectionBlock(Block):
|
|
86
98
|
pois = self.simulator.map.query_pois(
|
87
99
|
center = center,
|
88
100
|
category_prefix = levelTwoType,
|
89
|
-
radius=
|
101
|
+
radius=radius,
|
90
102
|
limit=self.search_limit
|
91
103
|
)
|
92
104
|
if len(pois) > 0:
|
@@ -94,11 +106,12 @@ class PlaceSelectionBlock(Block):
|
|
94
106
|
nextPlace = (poi['name'], poi['aoi_id'])
|
95
107
|
# 将地点信息保存到context中
|
96
108
|
context['next_place'] = nextPlace
|
97
|
-
|
109
|
+
node_id = await self.memory.stream.add_mobility(description=f"For {step['intention']}, I selected the destination: {nextPlace}")
|
98
110
|
return {
|
99
111
|
'success': True,
|
100
112
|
'evaluation': f'Successfully selected the destination: {nextPlace}',
|
101
|
-
'consumed_time': 5
|
113
|
+
'consumed_time': 5,
|
114
|
+
'node_id': node_id
|
102
115
|
}
|
103
116
|
else:
|
104
117
|
simmap = self.simulator.map
|
@@ -106,11 +119,12 @@ class PlaceSelectionBlock(Block):
|
|
106
119
|
nextPlace = (poi['name'], poi['aoi_id'])
|
107
120
|
# 将地点信息保存到context中
|
108
121
|
context['next_place'] = nextPlace
|
109
|
-
|
122
|
+
node_id = await self.memory.stream.add_mobility(description=f"For {step['intention']}, I selected the destination: {nextPlace}")
|
110
123
|
return {
|
111
124
|
'success': True,
|
112
125
|
'evaluation': f'Successfully selected the destination: {nextPlace}',
|
113
|
-
'consumed_time': 5
|
126
|
+
'consumed_time': 5,
|
127
|
+
'node_id': node_id
|
114
128
|
}
|
115
129
|
|
116
130
|
class MoveBlock(Block):
|
@@ -119,30 +133,35 @@ class MoveBlock(Block):
|
|
119
133
|
MoveBlock
|
120
134
|
"""
|
121
135
|
def __init__(self, llm: LLM, memory: Memory, simulator: Simulator):
|
122
|
-
super().__init__("MoveBlock", llm, memory, simulator)
|
136
|
+
super().__init__("MoveBlock", llm=llm, memory=memory, simulator=simulator)
|
123
137
|
self.description = "Used to execute specific mobility operations, such as returning home, going to work, or visiting a specific location"
|
124
138
|
self.placeAnalysisPrompt = FormatPrompt(PLACE_ANALYSIS_PROMPT)
|
125
139
|
|
126
140
|
async def forward(self, step, context):
|
127
141
|
# 这里应该添加移动的具体逻辑
|
128
|
-
agent_id = await self.memory.get("id")
|
142
|
+
agent_id = await self.memory.status.get("id")
|
129
143
|
self.placeAnalysisPrompt.format(
|
130
144
|
plan = context['plan'],
|
131
145
|
intention = step["intention"]
|
132
146
|
)
|
147
|
+
number_poi_visited = await self.memory.status.get("number_poi_visited")
|
148
|
+
number_poi_visited += 1
|
149
|
+
await self.memory.status.update("number_poi_visited", number_poi_visited)
|
133
150
|
response = await self.llm.atext_request(self.placeAnalysisPrompt.to_dialog()) # type: ignore
|
134
151
|
if response == 'home':
|
135
152
|
# 返回到家
|
136
|
-
home = await self.memory.get('home')
|
153
|
+
home = await self.memory.status.get('home')
|
137
154
|
home = home['aoi_position']['aoi_id']
|
138
|
-
nowPlace = await self.memory.get('position')
|
155
|
+
nowPlace = await self.memory.status.get('position')
|
156
|
+
node_id = await self.memory.stream.add_mobility(description=f"I returned home")
|
139
157
|
if 'aoi_position' in nowPlace and nowPlace['aoi_position']['aoi_id'] == home:
|
140
158
|
return {
|
141
159
|
'success': True,
|
142
160
|
'evaluation': f'Successfully returned home (already at home)',
|
143
161
|
'from_place': home,
|
144
162
|
'to_place': home,
|
145
|
-
'consumed_time': 0
|
163
|
+
'consumed_time': 0,
|
164
|
+
'node_id': node_id
|
146
165
|
}
|
147
166
|
await self.simulator.set_aoi_schedules(
|
148
167
|
person_id=agent_id,
|
@@ -153,20 +172,23 @@ class MoveBlock(Block):
|
|
153
172
|
'evaluation': f'Successfully returned home',
|
154
173
|
'from_place': nowPlace['aoi_position']['aoi_id'],
|
155
174
|
'to_place': home,
|
156
|
-
'consumed_time': 45
|
175
|
+
'consumed_time': 45,
|
176
|
+
'node_id': node_id
|
157
177
|
}
|
158
178
|
elif response == 'workplace':
|
159
179
|
# 返回到工作地点
|
160
|
-
work = await self.memory.get('work')
|
180
|
+
work = await self.memory.status.get('work')
|
161
181
|
work = work['aoi_position']['aoi_id']
|
162
|
-
nowPlace = await self.memory.get('position')
|
182
|
+
nowPlace = await self.memory.status.get('position')
|
183
|
+
node_id = await self.memory.stream.add_mobility(description=f"I went to my workplace")
|
163
184
|
if 'aoi_position' in nowPlace and nowPlace['aoi_position']['aoi_id'] == work:
|
164
185
|
return {
|
165
186
|
'success': True,
|
166
187
|
'evaluation': f'Successfully reached the workplace (already at the workplace)',
|
167
188
|
'from_place': work,
|
168
189
|
'to_place': work,
|
169
|
-
'consumed_time': 0
|
190
|
+
'consumed_time': 0,
|
191
|
+
'node_id': node_id
|
170
192
|
}
|
171
193
|
await self.simulator.set_aoi_schedules(
|
172
194
|
person_id=agent_id,
|
@@ -177,12 +199,14 @@ class MoveBlock(Block):
|
|
177
199
|
'evaluation': f'Successfully reached the workplace',
|
178
200
|
'from_place': nowPlace['aoi_position']['aoi_id'],
|
179
201
|
'to_place': work,
|
180
|
-
'consumed_time': 45
|
202
|
+
'consumed_time': 45,
|
203
|
+
'node_id': node_id
|
181
204
|
}
|
182
205
|
else:
|
183
206
|
# 移动到其他地点
|
184
207
|
next_place = context.get('next_place', None)
|
185
|
-
nowPlace = await self.memory.get('position')
|
208
|
+
nowPlace = await self.memory.status.get('position')
|
209
|
+
node_id = await self.memory.stream.add_mobility(description=f"I went to {next_place}")
|
186
210
|
if next_place != None:
|
187
211
|
await self.simulator.set_aoi_schedules(
|
188
212
|
person_id=agent_id,
|
@@ -205,7 +229,8 @@ class MoveBlock(Block):
|
|
205
229
|
'evaluation': f'Successfully reached the destination: {next_place}',
|
206
230
|
'from_place': nowPlace['aoi_position']['aoi_id'],
|
207
231
|
'to_place': next_place[1],
|
208
|
-
'consumed_time': 45
|
232
|
+
'consumed_time': 45,
|
233
|
+
'node_id': node_id
|
209
234
|
}
|
210
235
|
|
211
236
|
class MobilityNoneBlock(Block):
|
@@ -214,14 +239,16 @@ class MobilityNoneBlock(Block):
|
|
214
239
|
MobilityNoneBlock
|
215
240
|
"""
|
216
241
|
def __init__(self, llm: LLM, memory: Memory):
|
217
|
-
super().__init__("MobilityNoneBlock", llm, memory)
|
242
|
+
super().__init__("MobilityNoneBlock", llm=llm, memory=memory)
|
218
243
|
self.description = "Used to handle other cases"
|
219
244
|
|
220
245
|
async def forward(self, step, context):
|
246
|
+
node_id = await self.memory.stream.add_mobility(description=f"I finished {step['intention']}")
|
221
247
|
return {
|
222
248
|
'success': True,
|
223
249
|
'evaluation': f'Finished executing {step["intention"]}',
|
224
|
-
'consumed_time': 0
|
250
|
+
'consumed_time': 0,
|
251
|
+
'node_id': node_id
|
225
252
|
}
|
226
253
|
|
227
254
|
class MobilityBlock(Block):
|
@@ -230,7 +257,7 @@ class MobilityBlock(Block):
|
|
230
257
|
mobility_none_block: MobilityNoneBlock
|
231
258
|
|
232
259
|
def __init__(self, llm: LLM, memory: Memory, simulator: Simulator):
|
233
|
-
super().__init__("MobilityBlock", llm, memory, simulator)
|
260
|
+
super().__init__("MobilityBlock", llm=llm, memory=memory, simulator=simulator)
|
234
261
|
# 初始化所有块
|
235
262
|
self.place_selection_block = PlaceSelectionBlock(llm, memory, simulator)
|
236
263
|
self.move_block = MoveBlock(llm, memory, simulator)
|