pycityagent 2.0.0a52__cp310-cp310-macosx_11_0_arm64.whl → 2.0.0a53__cp310-cp310-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
@@ -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)
|