pycityagent 2.0.0a52__cp311-cp311-macosx_11_0_arm64.whl → 2.0.0a54__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.
Files changed (49) hide show
  1. pycityagent/agent/agent.py +83 -62
  2. pycityagent/agent/agent_base.py +81 -54
  3. pycityagent/cityagent/bankagent.py +5 -7
  4. pycityagent/cityagent/blocks/__init__.py +0 -2
  5. pycityagent/cityagent/blocks/cognition_block.py +149 -172
  6. pycityagent/cityagent/blocks/economy_block.py +90 -129
  7. pycityagent/cityagent/blocks/mobility_block.py +56 -29
  8. pycityagent/cityagent/blocks/needs_block.py +163 -145
  9. pycityagent/cityagent/blocks/other_block.py +17 -9
  10. pycityagent/cityagent/blocks/plan_block.py +45 -57
  11. pycityagent/cityagent/blocks/social_block.py +70 -51
  12. pycityagent/cityagent/blocks/utils.py +2 -0
  13. pycityagent/cityagent/firmagent.py +6 -7
  14. pycityagent/cityagent/governmentagent.py +7 -9
  15. pycityagent/cityagent/memory_config.py +48 -48
  16. pycityagent/cityagent/message_intercept.py +99 -0
  17. pycityagent/cityagent/nbsagent.py +6 -29
  18. pycityagent/cityagent/societyagent.py +325 -127
  19. pycityagent/cli/wrapper.py +4 -0
  20. pycityagent/economy/econ_client.py +0 -2
  21. pycityagent/environment/__init__.py +7 -1
  22. pycityagent/environment/sim/client.py +10 -1
  23. pycityagent/environment/sim/clock_service.py +2 -2
  24. pycityagent/environment/sim/pause_service.py +61 -0
  25. pycityagent/environment/sim/sim_env.py +34 -46
  26. pycityagent/environment/simulator.py +18 -14
  27. pycityagent/llm/embeddings.py +0 -24
  28. pycityagent/llm/llm.py +18 -10
  29. pycityagent/memory/faiss_query.py +29 -26
  30. pycityagent/memory/memory.py +733 -247
  31. pycityagent/message/__init__.py +8 -1
  32. pycityagent/message/message_interceptor.py +322 -0
  33. pycityagent/message/messager.py +42 -11
  34. pycityagent/pycityagent-sim +0 -0
  35. pycityagent/simulation/agentgroup.py +137 -96
  36. pycityagent/simulation/simulation.py +184 -38
  37. pycityagent/simulation/storage/pg.py +2 -2
  38. pycityagent/tools/tool.py +7 -9
  39. pycityagent/utils/__init__.py +7 -2
  40. pycityagent/utils/pg_query.py +1 -0
  41. pycityagent/utils/survey_util.py +26 -23
  42. pycityagent/workflow/block.py +14 -7
  43. {pycityagent-2.0.0a52.dist-info → pycityagent-2.0.0a54.dist-info}/METADATA +2 -2
  44. {pycityagent-2.0.0a52.dist-info → pycityagent-2.0.0a54.dist-info}/RECORD +48 -46
  45. pycityagent/cityagent/blocks/time_block.py +0 -116
  46. {pycityagent-2.0.0a52.dist-info → pycityagent-2.0.0a54.dist-info}/LICENSE +0 -0
  47. {pycityagent-2.0.0a52.dist-info → pycityagent-2.0.0a54.dist-info}/WHEEL +0 -0
  48. {pycityagent-2.0.0a52.dist-info → pycityagent-2.0.0a54.dist-info}/entry_points.txt +0 -0
  49. {pycityagent-2.0.0a52.dist-info → pycityagent-2.0.0a54.dist-info}/top_level.txt +0 -0
@@ -17,24 +17,22 @@ agent_skills = list(sorted_clipped_skills.mean(axis=0))
17
17
 
18
18
  def memory_config_societyagent():
19
19
  EXTRA_ATTRIBUTES = {
20
- "city": "New York",
21
- # 需求信息
22
20
  "type": (str, "citizen"),
23
- "needs": (
24
- dict,
25
- {
26
- "hungry": random.random(), # 饥饿感
27
- "tired": random.random(), # 疲劳感
28
- "safe": random.random(), # 安全需
29
- "social": random.random(), # 社会需求
30
- },
31
- True,
32
- ),
21
+ "city": (str, "New York", True),
22
+
23
+ # Needs Model
24
+ "hunger_satisfaction": (float, random.random(), True), # 饥饿满意度
25
+ "energy_satisfaction": (float, random.random(), True), # 精力满意度
26
+ "safety_satisfaction": (float, random.random(), True), # 安全满意度
27
+ "social_satisfaction": (float, random.random(), True), # 社交满意度
33
28
  "current_need": (str, "none", True),
29
+
30
+ # Plan Behavior Model
34
31
  "current_plan": (list, [], True),
35
32
  "current_step": (dict, {"intention": "", "type": ""}, True),
36
33
  "execution_context": (dict, {}, True),
37
34
  "plan_history": (list, [], True),
35
+
38
36
  # cognition
39
37
  "emotion": (
40
38
  dict,
@@ -46,25 +44,25 @@ def memory_config_societyagent():
46
44
  "anger": 5,
47
45
  "surprise": 5,
48
46
  },
49
- True,
47
+ False,
50
48
  ),
51
49
  "attitude": (dict, {}, True),
52
50
  "thought": (str, "Currently nothing good or bad is happening", True),
53
51
  "emotion_types": (str, "Relief", True),
54
- "incident": (list, [], True),
55
- "city": (str, "Texas", True),
52
+
53
+ # economy
56
54
  "work_skill": (
57
55
  float,
58
56
  random.choice(agent_skills),
59
57
  True,
60
- ), # 工作技能, 即每小时的工资
61
- "tax_paid": (float, 0.0, True), # 纳税
62
- "consumption_currency": (float, 0.0, True), # 月消费
58
+ ), # work skill
59
+ "tax_paid": (float, 0.0, True), # tax paid
60
+ "consumption_currency": (float, 0.0, True), # consumption
63
61
  "goods_demand": (int, 0, True),
64
62
  "goods_consumption": (int, 0, True),
65
63
  "work_propensity": (float, 0.0, True),
66
64
  "consumption_propensity": (float, 0.0, True),
67
- "income_currency": (float, 0.0, True), # 月收入
65
+ "income_currency": (float, 0.0, True), # monthly income
68
66
  "to_income": (float, 0.0, True),
69
67
  "to_consumption_currency": (float, 0.0, True),
70
68
  "firm_id": (int, 0, True),
@@ -79,23 +77,25 @@ def memory_config_societyagent():
79
77
  "forward": (int, 0, True),
80
78
  "depression": (float, 0.0, True),
81
79
  "ubi_opinion": (list, [], True),
82
- # social
83
- "friends": (list, [], True), # 好友列表
84
- "relationships": (dict, {}, True), # 与每个好友的关系强度
85
- "relation_types": (dict, {}, True),
86
- "chat_histories": (dict, {}, True), # 所有聊天历史记录
87
- "interactions": (dict, {}, True), # 所有互动记录
88
- "to_discuss": (dict, {}, True),
89
- # economy
90
80
  "working_experience": (list, [], True),
91
81
  "work_hour_month": (float, 160, True),
92
82
  "work_hour_finish": (float, 0, True),
83
+
84
+ # social
85
+ "friends": (list, [], True), # friends list
86
+ "relationships": (dict, {}, True), # relationship strength with each friend
87
+ "relation_types": (dict, {}, True),
88
+ "chat_histories": (dict, {}, True), # all chat histories
89
+ "interactions": (dict, {}, True), # all interaction records
90
+ "to_discuss": (dict, {}, True),
91
+
93
92
  # mobility
94
93
  "environment": (str, "The environment outside is good", True),
94
+ "number_poi_visited": (int, 1, True),
95
95
  }
96
96
 
97
97
  PROFILE = {
98
- "name": random.choice(
98
+ "name": (str,random.choice(
99
99
  [
100
100
  "Alice",
101
101
  "Bob",
@@ -124,13 +124,13 @@ def memory_config_societyagent():
124
124
  "Yvonne",
125
125
  "Zack",
126
126
  ]
127
- ),
128
- "gender": random.choice(["male", "female"]),
129
- "age": random.randint(18, 65),
130
- "education": random.choice(
127
+ ), True),
128
+ "gender": (str, random.choice(["male", "female"]), True),
129
+ "age": (int, random.randint(18, 65), True),
130
+ "education": (str, random.choice(
131
131
  ["Doctor", "Master", "Bachelor", "College", "High School"]
132
- ),
133
- "skill": random.choice(
132
+ ), True),
133
+ "skill": (str, random.choice(
134
134
  [
135
135
  "Good at problem-solving",
136
136
  "Good at communication",
@@ -138,8 +138,8 @@ def memory_config_societyagent():
138
138
  "Good at teamwork",
139
139
  "Other",
140
140
  ]
141
- ),
142
- "occupation": random.choice(
141
+ ), True),
142
+ "occupation": (str, random.choice(
143
143
  [
144
144
  "Student",
145
145
  "Teacher",
@@ -151,16 +151,16 @@ def memory_config_societyagent():
151
151
  "Athlete",
152
152
  "Other",
153
153
  ]
154
- ),
155
- "family_consumption": random.choice(["low", "medium", "high"]),
156
- "consumption": random.choice(["sightly low", "low", "medium", "high"]),
157
- "personality": random.choice(
154
+ ), True),
155
+ "family_consumption": (str, random.choice(["low", "medium", "high"]), True),
156
+ "consumption": (str, random.choice(["sightly low", "low", "medium", "high"]), True),
157
+ "personality": (str, random.choice(
158
158
  ["outgoint", "introvert", "ambivert", "extrovert"]
159
- ),
159
+ ), True),
160
160
  "income": "0",
161
161
  "currency": random.randint(1000, 100000),
162
- "residence": random.choice(["city", "suburb", "rural"]),
163
- "race": random.choice(
162
+ "residence": (str, random.choice(["city", "suburb", "rural"]), True),
163
+ "race": (str, random.choice(
164
164
  [
165
165
  "Chinese",
166
166
  "American",
@@ -172,13 +172,13 @@ def memory_config_societyagent():
172
172
  "Russian",
173
173
  "Other",
174
174
  ]
175
- ),
176
- "religion": random.choice(
175
+ ), True),
176
+ "religion": (str, random.choice(
177
177
  ["none", "Christian", "Muslim", "Buddhist", "Hindu", "Other"]
178
- ),
179
- "marital_status": random.choice(
178
+ ), True),
179
+ "marital_status": (str, random.choice(
180
180
  ["not married", "married", "divorced", "widowed"]
181
- ),
181
+ ), True),
182
182
  }
183
183
 
184
184
  BASE = {
@@ -0,0 +1,99 @@
1
+ import asyncio
2
+
3
+ from pycityagent.llm import LLM
4
+ from pycityagent.message import MessageBlockBase, MessageBlockListenerBase
5
+
6
+
7
+ async def check_message(
8
+ from_uuid: str, to_uuid: str, llm_client: LLM, content: str
9
+ ) -> bool:
10
+ """
11
+ 使用LLM检查消息是否合规
12
+ 返回: (是否合规, from_uuid, to_uuid)
13
+ """
14
+ print(f"\n检查消息: {from_uuid} -> {to_uuid}: {content}")
15
+
16
+ prompt = f"""
17
+ 请判断以下消息是否具有情绪煽动性:
18
+ 发送者ID: {from_uuid}
19
+ 接收者ID: {to_uuid}
20
+ 消息内容: {content}
21
+
22
+ 如果消息具有情绪煽动性,请返回 False;如果消息正常,请返回 True。
23
+ """
24
+
25
+ if "test" in content.lower():
26
+ is_valid = False
27
+ else:
28
+ is_valid = True
29
+ print(f"消息检查结果: {'合规' if is_valid else '不合规'}")
30
+ return is_valid
31
+
32
+
33
+ class EdgeMessageBlock(MessageBlockBase):
34
+ async def forward( # type:ignore
35
+ self,
36
+ from_uuid: str,
37
+ to_uuid: str,
38
+ msg: str,
39
+ black_list: list[tuple[str, str]],
40
+ ):
41
+ if (from_uuid, to_uuid) in set(black_list):
42
+ # 可选同时返回入队的信息(False,err) 如果只返回bool值则默认报错信息入队
43
+ return False
44
+ else:
45
+ is_valid = await check_message(
46
+ from_uuid=from_uuid,
47
+ to_uuid=to_uuid,
48
+ llm_client=self.llm,
49
+ content=msg,
50
+ )
51
+ if not is_valid:
52
+ # 直接添加即可 在框架内部的异步锁保证不会冲突
53
+ black_list.append((from_uuid, to_uuid))
54
+ return is_valid
55
+
56
+
57
+ class PointMessageBlock(MessageBlockBase):
58
+ async def forward( # type:ignore
59
+ self,
60
+ from_uuid: str,
61
+ to_uuid: str,
62
+ msg: str,
63
+ violation_counts: dict[str, int],
64
+ black_list: list[tuple[str, str]],
65
+ ):
66
+ if (from_uuid, to_uuid) in set(black_list):
67
+ # 可选同时返回入队的信息(False,err) 如果只返回bool值则默认报错信息入队
68
+ return False
69
+ else:
70
+ # violation count在框架内自动维护 这里不用管
71
+ is_valid = await check_message(
72
+ from_uuid=from_uuid,
73
+ to_uuid=to_uuid,
74
+ llm_client=self.llm,
75
+ content=msg,
76
+ )
77
+ if not is_valid and violation_counts[from_uuid] >= 3 - 1:
78
+ # 直接添加即可 在框架内部的异步锁保证不会冲突
79
+ black_list.append((from_uuid, to_uuid))
80
+ return is_valid
81
+
82
+
83
+ class MessageBlockListener(MessageBlockListenerBase):
84
+ def __init__(
85
+ self, save_queue_values: bool = False, get_queue_period: float = 0.1
86
+ ) -> None:
87
+ super().__init__(save_queue_values, get_queue_period)
88
+
89
+ async def forward(
90
+ self,
91
+ ):
92
+ while True:
93
+ if self.has_queue:
94
+ value = await self.queue.get_async() # type: ignore
95
+ if self._save_queue_values:
96
+ self._values_from_queue.append(value)
97
+ print(f"get `{value}` from queue")
98
+ # do something with the value
99
+ await asyncio.sleep(self._get_queue_period)
@@ -9,14 +9,11 @@ from pycityagent.economy import EconomyClient
9
9
  from pycityagent.llm.llm import LLM
10
10
  from pycityagent.memory import Memory
11
11
  from pycityagent.message import Messager
12
- from pycityagent.tools import ExportMlflowMetrics
13
12
 
14
13
  logger = logging.getLogger("pycityagent")
15
14
 
16
15
 
17
16
  class NBSAgent(InstitutionAgent):
18
- export_metrics = ExportMlflowMetrics(log_batch_size=3)
19
-
20
17
  def __init__(
21
18
  self,
22
19
  name: str,
@@ -60,15 +57,13 @@ class NBSAgent(InstitutionAgent):
60
57
 
61
58
  async def forward(self):
62
59
  if await self.month_trigger():
63
- citizens = await self.memory.get("citizens")
64
- while True:
65
- agents_forward = await self.gather_messages(citizens, "forward")
66
- if np.all(np.array(agents_forward) > self.forward_times):
67
- break
68
- await asyncio.sleep(1)
60
+ citizens = await self.memory.status.get("citizens")
61
+ agents_forward = []
62
+ if not np.all(np.array(agents_forward) > self.forward_times):
63
+ return
69
64
  work_propensity = await self.gather_messages(citizens, "work_propensity")
70
65
  working_hours = np.mean(work_propensity) * self.num_labor_hours
71
- firm_id = await self.memory.get("firm_id")
66
+ firm_id = await self.memory.status.get("firm_id")
72
67
  price = await self.economy_client.get(firm_id, "price")
73
68
  prices = await self.economy_client.get(self._agent_id, "prices")
74
69
  initial_price = prices[0]
@@ -116,23 +111,5 @@ class NBSAgent(InstitutionAgent):
116
111
  self.forward_times += 1
117
112
  for uuid in citizens:
118
113
  await self.send_message_to_agent(
119
- uuid, f"nbs_forward@{self.forward_times}"
120
- )
121
-
122
- metrics = {
123
- "nominal_gdp": nominal_gdp,
124
- "working_hours": working_hours,
125
- "price": price,
126
- "depression": depression,
127
- "consumption": consumption_currency,
128
- "income": income_currency,
129
- }
130
- for k, v in metrics.items():
131
- await self.export_metrics(
132
- metric={
133
- "key": k,
134
- "value": v,
135
- "step": self.forward_times,
136
- },
137
- clear_cache=True,
114
+ uuid, f"nbs_forward@{self.forward_times}", "economy"
138
115
  )