pycityagent 2.0.0a6__py3-none-any.whl → 2.0.0a7__py3-none-any.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 (38) hide show
  1. pycityagent/agent.py +5 -3
  2. pycityagent/environment/interact/interact.py +86 -29
  3. pycityagent/environment/sence/static.py +3 -2
  4. pycityagent/environment/sim/aoi_service.py +1 -1
  5. pycityagent/environment/sim/economy_services.py +1 -1
  6. pycityagent/environment/sim/road_service.py +1 -1
  7. pycityagent/environment/sim/social_service.py +1 -1
  8. pycityagent/environment/simulator.py +6 -4
  9. pycityagent/environment/utils/__init__.py +5 -1
  10. pycityagent/llm/__init__.py +1 -1
  11. pycityagent/llm/embedding.py +36 -35
  12. pycityagent/llm/llm.py +197 -161
  13. pycityagent/llm/llmconfig.py +7 -9
  14. pycityagent/llm/utils.py +2 -2
  15. pycityagent/memory/memory.py +1 -2
  16. pycityagent/memory/memory_base.py +1 -2
  17. pycityagent/memory/profile.py +1 -2
  18. pycityagent/memory/self_define.py +1 -2
  19. pycityagent/memory/state.py +1 -2
  20. pycityagent/message/__init__.py +1 -1
  21. pycityagent/message/messager.py +11 -4
  22. pycityagent/simulation/__init__.py +1 -1
  23. pycityagent/simulation/agentgroup.py +13 -6
  24. pycityagent/simulation/interview.py +9 -5
  25. pycityagent/simulation/simulation.py +86 -33
  26. pycityagent/simulation/survey/__init__.py +1 -6
  27. pycityagent/simulation/survey/manager.py +22 -21
  28. pycityagent/simulation/survey/models.py +8 -5
  29. pycityagent/utils/decorators.py +14 -4
  30. pycityagent/utils/parsers/__init__.py +2 -1
  31. pycityagent/workflow/block.py +4 -3
  32. pycityagent/workflow/prompt.py +16 -9
  33. pycityagent/workflow/tool.py +1 -2
  34. pycityagent/workflow/trigger.py +36 -23
  35. {pycityagent-2.0.0a6.dist-info → pycityagent-2.0.0a7.dist-info}/METADATA +1 -1
  36. pycityagent-2.0.0a7.dist-info/RECORD +70 -0
  37. pycityagent-2.0.0a6.dist-info/RECORD +0 -70
  38. {pycityagent-2.0.0a6.dist-info → pycityagent-2.0.0a7.dist-info}/WHEEL +0 -0
pycityagent/agent.py CHANGED
@@ -77,7 +77,7 @@ class Agent(ABC):
77
77
  def __getstate__(self):
78
78
  state = self.__dict__.copy()
79
79
  # 排除锁对象
80
- del state['_llm_client']
80
+ del state["_llm_client"]
81
81
  return state
82
82
 
83
83
  async def bind_to_simulator(self):
@@ -278,12 +278,14 @@ class Agent(ABC):
278
278
  def get_interview_history(self) -> List[Dict]:
279
279
  """获取采访历史记录"""
280
280
  return self._interview_history
281
-
281
+
282
282
  async def handle_message(self, payload: str):
283
283
  """处理收到的消息,识别发送者"""
284
284
  # 从消息中解析发送者 ID 和消息内容
285
285
  message, sender_id = payload.split("|from:")
286
- print(f"Agent {self._agent_id} received message: '{message}' from Agent {sender_id}")
286
+ print(
287
+ f"Agent {self._agent_id} received message: '{message}' from Agent {sender_id}"
288
+ )
287
289
 
288
290
  async def send_message(self, to_agent_id: int, message: str):
289
291
  """通过 Messager 发送消息,附带发送者的 ID"""
@@ -1,9 +1,11 @@
1
1
  """环境相关的Interaction定义"""
2
+
2
3
  from enum import Enum
3
4
  from typing import Callable, Optional, Any
4
5
  from abc import ABC, abstractmethod
5
6
  from typing import Callable, Any
6
7
 
8
+
7
9
  class ActionType(Enum):
8
10
  """
9
11
  行动类型枚举 所有行动本质上为数据推送
@@ -14,24 +16,33 @@ class ActionType(Enum):
14
16
  - Hub = 2, 用于表示与AppHub(前端)对接的行动
15
17
  - Comp = 3, 表示综合类型 (可能同时包含与Sim以及Hub的交互)
16
18
  """
19
+
17
20
  Sim = 1
18
21
  Hub = 2
19
22
  Comp = 3
20
-
23
+
24
+
21
25
  class Action:
22
26
  """
23
27
  - Action
24
28
  """
25
- def __init__(self, agent, type:ActionType, source: Optional[str] = None, before:Optional[Callable[[list], Any]] = None) -> None:
26
- '''
29
+
30
+ def __init__(
31
+ self,
32
+ agent,
33
+ type: ActionType,
34
+ source: Optional[str] = None,
35
+ before: Optional[Callable[[list], Any]] = None,
36
+ ) -> None:
37
+ """
27
38
  默认初始化
28
-
39
+
29
40
  Args:
30
41
  - agent (Agent): the related agent
31
42
  - type (ActionType)
32
43
  - source (str): 数据来源, 默认为None, 如果为None则会从接收用户传入的数据作为Forward函数参数, 否则从WM.Reason数据缓存中取对应数据作为参数
33
44
  - before (function): 数据处理方法, 用于当Reason缓存中的参数与标准格式不符时使用
34
- '''
45
+ """
35
46
  self._agent = agent
36
47
  self._type = type
37
48
  self._source = source
@@ -51,16 +62,30 @@ class Action:
51
62
 
52
63
  @abstractmethod
53
64
  async def Forward(self):
54
- '''接口函数'''
65
+ """接口函数"""
66
+
55
67
 
56
68
  class SimAction(Action):
57
69
  """SimAction: 模拟器关联Action"""
58
- def __init__(self, agent, source: Optional[str] = None, before:Optional[Callable[[list], Any]] = None) -> None:
70
+
71
+ def __init__(
72
+ self,
73
+ agent,
74
+ source: Optional[str] = None,
75
+ before: Optional[Callable[[list], Any]] = None,
76
+ ) -> None:
59
77
  super().__init__(agent, ActionType.Sim, source, before)
60
78
 
79
+
61
80
  class HubAction(Action):
62
81
  """HubAction: Apphub关联Action"""
63
- def __init__(self, agent, source: Optional[str] = None, before:Optional[Callable[[list], Any]] = None) -> None:
82
+
83
+ def __init__(
84
+ self,
85
+ agent,
86
+ source: Optional[str] = None,
87
+ before: Optional[Callable[[list], Any]] = None,
88
+ ) -> None:
64
89
  super().__init__(agent, ActionType.Hub, source, before)
65
90
 
66
91
 
@@ -69,46 +94,70 @@ class SetSchedule(SimAction):
69
94
  用于将agent的行程信息同步至模拟器 —— 仅对citizen类型agent适用
70
95
  Synchronize agent's schedule to simulator —— only avalable for citizen type of agent
71
96
  """
72
- def __init__(self, agent, source: Optional[str] = None, before:Optional[Callable[[list], Any]] = None) -> None:
97
+
98
+ def __init__(
99
+ self,
100
+ agent,
101
+ source: Optional[str] = None,
102
+ before: Optional[Callable[[list], Any]] = None,
103
+ ) -> None:
73
104
  super().__init__(agent, source, before)
74
105
 
75
- async def Forward(self, schedule = None):
106
+ async def Forward(self, schedule=None):
76
107
  """
77
108
  如果当前行程已经同步至模拟器: 跳过同步, 否则同步至模拟器
78
109
  If current schedule has been synchronized to simulator: skip, else sync
79
110
  """
80
111
  if not schedule == None:
81
112
  if not schedule.is_set:
82
- '''同步schedule至模拟器'''
113
+ """同步schedule至模拟器"""
83
114
  self._agent.Scheduler.now.is_set = True
84
115
  departure_time = schedule.time
85
116
  mode = schedule.mode
86
117
  aoi_id = schedule.target_id_aoi
87
118
  poi_id = schedule.target_id_poi
88
- end = {'aoi_position': {'aoi_id': aoi_id, 'poi_id': poi_id}}
119
+ end = {"aoi_position": {"aoi_id": aoi_id, "poi_id": poi_id}}
89
120
  activity = schedule.description
90
- trips = [{'mode': mode, 'end': end, 'departure_time': departure_time, 'activity': activity}]
91
- set_schedule = [{'trips': trips, 'loop_count': 1, 'departure_time': departure_time}]
121
+ trips = [
122
+ {
123
+ "mode": mode,
124
+ "end": end,
125
+ "departure_time": departure_time,
126
+ "activity": activity,
127
+ }
128
+ ]
129
+ set_schedule = [
130
+ {"trips": trips, "loop_count": 1, "departure_time": departure_time}
131
+ ]
92
132
 
93
133
  # * 与模拟器对接
94
- req = {'person_id': self._agent._id, 'schedules': set_schedule}
134
+ req = {"person_id": self._agent._id, "schedules": set_schedule}
95
135
  await self._agent._client.person_service.SetSchedule(req)
96
136
  elif self._source != None:
97
137
  schedule = self.get_source()
98
138
  if schedule != None and not schedule.is_set:
99
- '''同步schedule至模拟器'''
139
+ """同步schedule至模拟器"""
100
140
  self._agent.Scheduler.now.is_set = True
101
141
  departure_time = schedule.time
102
142
  mode = schedule.mode
103
143
  aoi_id = schedule.target_id_aoi
104
144
  poi_id = schedule.target_id_poi
105
- end = {'aoi_position': {'aoi_id': aoi_id, 'poi_id': poi_id}}
145
+ end = {"aoi_position": {"aoi_id": aoi_id, "poi_id": poi_id}}
106
146
  activity = schedule.description
107
- trips = [{'mode': mode, 'end': end, 'departure_time': departure_time, 'activity': activity}]
108
- set_schedule = [{'trips': trips, 'loop_count': 1, 'departure_time': departure_time}]
147
+ trips = [
148
+ {
149
+ "mode": mode,
150
+ "end": end,
151
+ "departure_time": departure_time,
152
+ "activity": activity,
153
+ }
154
+ ]
155
+ set_schedule = [
156
+ {"trips": trips, "loop_count": 1, "departure_time": departure_time}
157
+ ]
109
158
 
110
159
  # * 与模拟器对接
111
- req = {'person_id': self._agent._id, 'schedules': set_schedule}
160
+ req = {"person_id": self._agent._id, "schedules": set_schedule}
112
161
  await self._agent._client.person_service.SetSchedule(req)
113
162
 
114
163
 
@@ -117,25 +166,33 @@ class SendAgentMessage(SimAction):
117
166
  发送信息给其他agent
118
167
  Send messages to other agents
119
168
  """
120
- def __init__(self, agent, source: Optional[str] = None, before:Optional[Callable[[list], Any]] = None) -> None:
169
+
170
+ def __init__(
171
+ self,
172
+ agent,
173
+ source: Optional[str] = None,
174
+ before: Optional[Callable[[list], Any]] = None,
175
+ ) -> None:
121
176
  super().__init__(agent, source, before)
122
177
 
123
178
  async def Forward(self, messages: Optional[dict] = None):
124
179
  if not messages == None and len(messages) > 0:
125
- req = {'messages': []}
180
+ req = {"messages": []}
126
181
  for message in messages:
127
182
  from_id = self._agent._id
128
- to_id = message['id']
129
- mes = message['message']
130
- req['messages'].append({'from': from_id, 'to': to_id, 'message': mes})
183
+ to_id = message["id"]
184
+ mes = message["message"]
185
+ req["messages"].append({"from": from_id, "to": to_id, "message": mes})
131
186
  await self._agent._client.social_service.Send(req)
132
187
  elif self._source != None:
133
188
  messages = self.get_source()
134
189
  if not messages == None and len(messages) > 0:
135
- req = {'messages': []}
190
+ req = {"messages": []}
136
191
  for message in messages:
137
192
  from_id = self._agent._id
138
- to_id = message['id']
139
- mes = message['message']
140
- req['messages'].append({'from': from_id, 'to': to_id, 'message': mes})
193
+ to_id = message["id"]
194
+ mes = message["message"]
195
+ req["messages"].append(
196
+ {"from": from_id, "to": to_id, "message": mes}
197
+ )
141
198
  await self._agent._client.social_service.Send(req)
@@ -1,6 +1,7 @@
1
1
  """
2
2
  静态数据支持; Static Resources: Poi Type association; Type prefix.
3
3
  """
4
+
4
5
  POI_TYPE_DICT = {
5
6
  "100000": "\u7f8e\u98df",
6
7
  "101000": "\u7f8e\u98df:\u4e2d\u9910\u5385",
@@ -409,7 +410,7 @@ POI_TYPE_DICT = {
409
410
  "801012": "\u5ba4\u5185\u53ca\u9644\u5c5e\u8bbe\u65bd:\u901a\u884c\u8bbe\u65bd\u7c7b:\u7535\u68af",
410
411
  "801099": "\u5ba4\u5185\u53ca\u9644\u5c5e\u8bbe\u65bd:\u901a\u884c\u8bbe\u65bd\u7c7b:\u5176\u5b83\u901a\u884c\u8bbe\u65bd\u7c7b",
411
412
  "809900": "\u5ba4\u5185\u53ca\u9644\u5c5e\u8bbe\u65bd:\u5176\u5b83\u5ba4\u5185\u53ca\u9644\u5c5e\u8bbe\u65bd",
412
- "990000": "\u5176\u5b83"
413
+ "990000": "\u5176\u5b83",
413
414
  }
414
415
 
415
- LEVEL_ONE_PRE = ["1", "2", "8", "9"]
416
+ LEVEL_ONE_PRE = ["1", "2", "8", "9"]
@@ -25,7 +25,7 @@ class AoiService:
25
25
  """
26
26
  获取AOI信息
27
27
  get AOI information
28
-
28
+
29
29
  Args:
30
30
  - req (dict): https://cityproto.sim.fiblab.net/#city.map.v2.GetAoiRequest
31
31
 
@@ -29,7 +29,7 @@ class EconomyPersonService:
29
29
  """
30
30
  批量查询人的经济情况(资金、雇佣关系)
31
31
  Query person’s economic situation (funds, employment relationship) in batches
32
-
32
+
33
33
  Args:
34
34
  - req (dict): https://cityproto.sim.fiblab.net/#city.economy.v1.GetPersonRequest
35
35
 
@@ -25,7 +25,7 @@ class RoadService:
25
25
  """
26
26
  查询道路信息
27
27
  Query road information
28
-
28
+
29
29
  Args:
30
30
  - req (dict): https://cityproto.sim.fiblab.net/#city.map.v2.GetRoadRequest
31
31
 
@@ -43,7 +43,7 @@ class SocialService:
43
43
  """
44
44
  接收消息
45
45
  Receive message
46
-
46
+
47
47
  Args:
48
48
  - req (dict): https://cityproto.sim.fiblab.net/#city.social.v1.ReceiveRequest
49
49
 
@@ -52,8 +52,8 @@ class Simulator:
52
52
  with open(_map_pb_path, "wb") as f:
53
53
  f.write(_map_pb.SerializeToString())
54
54
 
55
- if 'simulator' in config:
56
- if 'server' not in config["simulator"]:
55
+ if "simulator" in config:
56
+ if "server" not in config["simulator"]:
57
57
  self._sim_env = sim_env = ControlSimEnv(
58
58
  task_name=config["simulator"].get("task", "citysim"),
59
59
  map_file=_map_pb_path,
@@ -71,9 +71,11 @@ class Simulator:
71
71
  - grpc client of simulator
72
72
  """
73
73
  else:
74
- self._client = CityClient(config['simulator']['server'], secure=False)
74
+ self._client = CityClient(config["simulator"]["server"], secure=False)
75
75
  else:
76
- logging.warning("No simulator config found, no simulator client will be used")
76
+ logging.warning(
77
+ "No simulator config found, no simulator client will be used"
78
+ )
77
79
  self.map = SimMap(
78
80
  mongo_uri=_mongo_uri,
79
81
  mongo_db=_mongo_db,
@@ -7,4 +7,8 @@ from .geojson import wrap_feature_collection
7
7
  from .base64 import encode_to_base64
8
8
  from .port import find_free_port
9
9
 
10
- __all__ = ["wrap_feature_collection","find_free_port","find_free_port",]
10
+ __all__ = [
11
+ "wrap_feature_collection",
12
+ "find_free_port",
13
+ "find_free_port",
14
+ ]
@@ -3,4 +3,4 @@
3
3
  from .llm import LLM, LLMConfig
4
4
  from .embedding import SimpleEmbedding
5
5
 
6
- __all__ = ["LLM", "LLMConfig", "SimpleEmbedding"]
6
+ __all__ = ["LLM", "LLMConfig", "SimpleEmbedding"]
@@ -5,16 +5,17 @@ from typing import List, Dict, Optional
5
5
  import hashlib
6
6
  import json
7
7
 
8
+
8
9
  class SimpleEmbedding:
9
10
  """简单的基于内存的embedding实现
10
-
11
+
11
12
  使用简单的词袋模型(Bag of Words)和TF-IDF来生成文本的向量表示。
12
13
  所有向量都保存在内存中,适用于小规模应用。
13
14
  """
14
-
15
+
15
16
  def __init__(self, vector_dim: int = 128, cache_size: int = 1000):
16
17
  """初始化
17
-
18
+
18
19
  Args:
19
20
  vector_dim: 向量维度
20
21
  cache_size: 缓存大小,超过此大小将清除最早的缓存
@@ -25,29 +26,29 @@ class SimpleEmbedding:
25
26
  self._vocab: Dict[str, int] = {} # 词汇表
26
27
  self._idf: Dict[str, float] = {} # 逆文档频率
27
28
  self._doc_count = 0 # 文档总数
28
-
29
+
29
30
  def _text_to_hash(self, text: str) -> str:
30
31
  """将文本转换为hash值"""
31
32
  return hashlib.md5(text.encode()).hexdigest()
32
-
33
+
33
34
  def _tokenize(self, text: str) -> List[str]:
34
35
  """简单的分词"""
35
36
  # 这里使用简单的空格分词,实际应用中可以使用更复杂的分词方法
36
37
  return text.lower().split()
37
-
38
+
38
39
  def _update_vocab(self, tokens: List[str]):
39
40
  """更新词汇表"""
40
41
  for token in set(tokens): # 使用set去重
41
42
  if token not in self._vocab:
42
43
  self._vocab[token] = len(self._vocab)
43
-
44
+
44
45
  def _update_idf(self, tokens: List[str]):
45
46
  """更新IDF值"""
46
47
  self._doc_count += 1
47
48
  unique_tokens = set(tokens)
48
49
  for token in unique_tokens:
49
50
  self._idf[token] = self._idf.get(token, 0) + 1
50
-
51
+
51
52
  def _calculate_tf(self, tokens: List[str]) -> Dict[str, float]:
52
53
  """计算词频(TF)"""
53
54
  tf = {}
@@ -58,31 +59,31 @@ class SimpleEmbedding:
58
59
  for token in tf:
59
60
  tf[token] /= total_tokens
60
61
  return tf
61
-
62
+
62
63
  def _calculate_tfidf(self, tokens: List[str]) -> np.ndarray:
63
64
  """计算TF-IDF向量"""
64
65
  vector = np.zeros(self.vector_dim)
65
66
  tf = self._calculate_tf(tokens)
66
-
67
+
67
68
  for token, tf_value in tf.items():
68
69
  if token in self._idf:
69
70
  idf = np.log(self._doc_count / self._idf[token])
70
71
  idx = self._vocab[token] % self.vector_dim # 使用取模运算来控制向量维度
71
72
  vector[idx] += tf_value * idf
72
-
73
+
73
74
  # L2归一化
74
75
  norm = np.linalg.norm(vector)
75
76
  if norm > 0:
76
77
  vector /= norm
77
-
78
+
78
79
  return vector
79
-
80
+
80
81
  async def embed(self, text: str) -> np.ndarray:
81
82
  """生成文本的向量表示
82
-
83
+
83
84
  Args:
84
85
  text: 输入文本
85
-
86
+
86
87
  Returns:
87
88
  np.ndarray: 文本的向量表示
88
89
  """
@@ -90,47 +91,47 @@ class SimpleEmbedding:
90
91
  text_hash = self._text_to_hash(text)
91
92
  if text_hash in self._cache:
92
93
  return self._cache[text_hash]
93
-
94
+
94
95
  # 分词
95
96
  tokens = self._tokenize(text)
96
97
  if not tokens:
97
98
  return np.zeros(self.vector_dim)
98
-
99
+
99
100
  # 更新词汇表和IDF
100
101
  self._update_vocab(tokens)
101
102
  self._update_idf(tokens)
102
-
103
+
103
104
  # 计算向量
104
105
  vector = self._calculate_tfidf(tokens)
105
-
106
+
106
107
  # 更新缓存
107
108
  if len(self._cache) >= self.cache_size:
108
109
  # 删除最早的缓存
109
110
  oldest_key = next(iter(self._cache))
110
111
  del self._cache[oldest_key]
111
112
  self._cache[text_hash] = vector
112
-
113
+
113
114
  return vector
114
-
115
+
115
116
  def save(self, file_path: str):
116
117
  """保存模型"""
117
118
  state = {
118
- 'vector_dim': self.vector_dim,
119
- 'cache_size': self.cache_size,
120
- 'vocab': self._vocab,
121
- 'idf': self._idf,
122
- 'doc_count': self._doc_count
119
+ "vector_dim": self.vector_dim,
120
+ "cache_size": self.cache_size,
121
+ "vocab": self._vocab,
122
+ "idf": self._idf,
123
+ "doc_count": self._doc_count,
123
124
  }
124
- with open(file_path, 'w') as f:
125
+ with open(file_path, "w") as f:
125
126
  json.dump(state, f)
126
-
127
+
127
128
  def load(self, file_path: str):
128
129
  """加载模型"""
129
- with open(file_path, 'r') as f:
130
+ with open(file_path, "r") as f:
130
131
  state = json.load(f)
131
- self.vector_dim = state['vector_dim']
132
- self.cache_size = state['cache_size']
133
- self._vocab = state['vocab']
134
- self._idf = state['idf']
135
- self._doc_count = state['doc_count']
136
- self._cache = {} # 清空缓存
132
+ self.vector_dim = state["vector_dim"]
133
+ self.cache_size = state["cache_size"]
134
+ self._vocab = state["vocab"]
135
+ self._idf = state["idf"]
136
+ self._doc_count = state["doc_count"]
137
+ self._cache = {} # 清空缓存