pycityagent 2.0.0a43__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 (81) hide show
  1. pycityagent/__init__.py +23 -0
  2. pycityagent/agent.py +833 -0
  3. pycityagent/cli/wrapper.py +44 -0
  4. pycityagent/economy/__init__.py +5 -0
  5. pycityagent/economy/econ_client.py +355 -0
  6. pycityagent/environment/__init__.py +7 -0
  7. pycityagent/environment/interact/__init__.py +0 -0
  8. pycityagent/environment/interact/interact.py +198 -0
  9. pycityagent/environment/message/__init__.py +0 -0
  10. pycityagent/environment/sence/__init__.py +0 -0
  11. pycityagent/environment/sence/static.py +416 -0
  12. pycityagent/environment/sidecar/__init__.py +8 -0
  13. pycityagent/environment/sidecar/sidecarv2.py +109 -0
  14. pycityagent/environment/sim/__init__.py +29 -0
  15. pycityagent/environment/sim/aoi_service.py +39 -0
  16. pycityagent/environment/sim/client.py +126 -0
  17. pycityagent/environment/sim/clock_service.py +44 -0
  18. pycityagent/environment/sim/economy_services.py +192 -0
  19. pycityagent/environment/sim/lane_service.py +111 -0
  20. pycityagent/environment/sim/light_service.py +122 -0
  21. pycityagent/environment/sim/person_service.py +295 -0
  22. pycityagent/environment/sim/road_service.py +39 -0
  23. pycityagent/environment/sim/sim_env.py +145 -0
  24. pycityagent/environment/sim/social_service.py +59 -0
  25. pycityagent/environment/simulator.py +331 -0
  26. pycityagent/environment/utils/__init__.py +14 -0
  27. pycityagent/environment/utils/base64.py +16 -0
  28. pycityagent/environment/utils/const.py +244 -0
  29. pycityagent/environment/utils/geojson.py +24 -0
  30. pycityagent/environment/utils/grpc.py +57 -0
  31. pycityagent/environment/utils/map_utils.py +157 -0
  32. pycityagent/environment/utils/port.py +11 -0
  33. pycityagent/environment/utils/protobuf.py +41 -0
  34. pycityagent/llm/__init__.py +11 -0
  35. pycityagent/llm/embeddings.py +231 -0
  36. pycityagent/llm/llm.py +377 -0
  37. pycityagent/llm/llmconfig.py +13 -0
  38. pycityagent/llm/utils.py +6 -0
  39. pycityagent/memory/__init__.py +13 -0
  40. pycityagent/memory/const.py +43 -0
  41. pycityagent/memory/faiss_query.py +302 -0
  42. pycityagent/memory/memory.py +448 -0
  43. pycityagent/memory/memory_base.py +170 -0
  44. pycityagent/memory/profile.py +165 -0
  45. pycityagent/memory/self_define.py +165 -0
  46. pycityagent/memory/state.py +173 -0
  47. pycityagent/memory/utils.py +28 -0
  48. pycityagent/message/__init__.py +3 -0
  49. pycityagent/message/messager.py +88 -0
  50. pycityagent/metrics/__init__.py +6 -0
  51. pycityagent/metrics/mlflow_client.py +147 -0
  52. pycityagent/metrics/utils/const.py +0 -0
  53. pycityagent/pycityagent-sim +0 -0
  54. pycityagent/pycityagent-ui +0 -0
  55. pycityagent/simulation/__init__.py +8 -0
  56. pycityagent/simulation/agentgroup.py +580 -0
  57. pycityagent/simulation/simulation.py +634 -0
  58. pycityagent/simulation/storage/pg.py +184 -0
  59. pycityagent/survey/__init__.py +4 -0
  60. pycityagent/survey/manager.py +54 -0
  61. pycityagent/survey/models.py +120 -0
  62. pycityagent/utils/__init__.py +11 -0
  63. pycityagent/utils/avro_schema.py +109 -0
  64. pycityagent/utils/decorators.py +99 -0
  65. pycityagent/utils/parsers/__init__.py +13 -0
  66. pycityagent/utils/parsers/code_block_parser.py +37 -0
  67. pycityagent/utils/parsers/json_parser.py +86 -0
  68. pycityagent/utils/parsers/parser_base.py +60 -0
  69. pycityagent/utils/pg_query.py +92 -0
  70. pycityagent/utils/survey_util.py +53 -0
  71. pycityagent/workflow/__init__.py +26 -0
  72. pycityagent/workflow/block.py +211 -0
  73. pycityagent/workflow/prompt.py +79 -0
  74. pycityagent/workflow/tool.py +240 -0
  75. pycityagent/workflow/trigger.py +163 -0
  76. pycityagent-2.0.0a43.dist-info/LICENSE +21 -0
  77. pycityagent-2.0.0a43.dist-info/METADATA +235 -0
  78. pycityagent-2.0.0a43.dist-info/RECORD +81 -0
  79. pycityagent-2.0.0a43.dist-info/WHEEL +5 -0
  80. pycityagent-2.0.0a43.dist-info/entry_points.txt +3 -0
  81. pycityagent-2.0.0a43.dist-info/top_level.txt +3 -0
@@ -0,0 +1,240 @@
1
+ import time
2
+ from collections import defaultdict
3
+ from collections.abc import Callable, Sequence
4
+ from typing import Any, Optional, Union
5
+
6
+ from mlflow.entities import Metric
7
+
8
+ from ..agent import Agent
9
+ from ..environment import (LEVEL_ONE_PRE, POI_TYPE_DICT, AoiService,
10
+ PersonService)
11
+ from ..workflow import Block
12
+
13
+
14
+ class Tool:
15
+ """Abstract tool class for callable tools. Can be bound to an `Agent` or `Block` instance.
16
+
17
+ This class serves as a base for creating various tools that can perform different operations.
18
+ It is intended to be subclassed by specific tool implementations.
19
+ """
20
+
21
+ def __get__(self, instance, owner):
22
+ if instance is None:
23
+ return self
24
+ subclass = type(self)
25
+ if not hasattr(instance, "_tools"):
26
+ instance._tools = {}
27
+ if subclass not in instance._tools:
28
+ tool_instance = subclass()
29
+ tool_instance._instance = instance # type: ignore
30
+ instance._tools[subclass] = tool_instance
31
+ return instance._tools[subclass]
32
+
33
+ def __call__(self, *args: Any, **kwds: Any) -> Any:
34
+ """Invoke the tool's functionality.
35
+
36
+ This method must be implemented by subclasses to provide specific behavior.
37
+ """
38
+ raise NotImplementedError
39
+
40
+ @property
41
+ def agent(self) -> Agent:
42
+ instance = self._instance # type:ignore
43
+ if not isinstance(instance, Agent):
44
+ raise RuntimeError(
45
+ f"Tool bind to object `{type(instance).__name__}`, not an `Agent` object!"
46
+ )
47
+ return instance
48
+
49
+ @property
50
+ def block(self) -> Block:
51
+ instance = self._instance # type:ignore
52
+ if not isinstance(instance, Block):
53
+ raise RuntimeError(
54
+ f"Tool bind to object `{type(instance).__name__}`, not an `Block` object!"
55
+ )
56
+ return instance
57
+
58
+
59
+ class GetMap(Tool):
60
+ """Retrieve the map from the simulator. Can be bound only to an `Agent` instance."""
61
+
62
+ def __init__(self) -> None:
63
+ self.variables = []
64
+
65
+ async def __call__(self) -> Union[Any, Callable]:
66
+ agent = self.agent
67
+ if agent.simulator is None:
68
+ raise ValueError("Simulator is not set.")
69
+ return agent.simulator.map
70
+
71
+
72
+ class SencePOI(Tool):
73
+ """Retrieve the Point of Interest (POI) of the current scene.
74
+
75
+ This tool computes the POI based on the current `position` stored in memory and returns
76
+ points of interest (POIs) within a specified radius. Can be bound only to an `Agent` instance.
77
+
78
+ Attributes:
79
+ radius (int): The radius within which to search for POIs.
80
+ category_prefix (str): The prefix for the categories of POIs to consider.
81
+ variables (list[str]): A list of variables relevant to the tool's operation.
82
+
83
+ Args:
84
+ radius (int, optional): The circular search radius. Defaults to 100.
85
+ category_prefix (str, optional): The category prefix to filter POIs. Defaults to LEVEL_ONE_PRE.
86
+
87
+ Methods:
88
+ __call__(radius: Optional[int] = None, category_prefix: Optional[str] = None) -> Union[Any, Callable]:
89
+ Executes the AOI retrieval operation, returning POIs based on the current state of memory and simulator.
90
+ """
91
+
92
+ def __init__(self, radius: int = 100, category_prefix=LEVEL_ONE_PRE) -> None:
93
+ self.radius = radius
94
+ self.category_prefix = category_prefix
95
+ self.variables = ["position"]
96
+
97
+ async def __call__(
98
+ self, radius: Optional[int] = None, category_prefix: Optional[str] = None
99
+ ) -> Union[Any, Callable]:
100
+ """Retrieve the POIs within the specified radius and category prefix.
101
+
102
+ If both `radius` and `category_prefix` are None, the method will use the current position
103
+ from memory to query POIs using the simulator. Otherwise, it will return a new instance
104
+ of SenceAoi with the specified parameters.
105
+
106
+ Args:
107
+ radius (Optional[int]): A specific radius for the AOI query. If not provided, defaults to the instance's radius.
108
+ category_prefix (Optional[str]): A specific category prefix to filter POIs. If not provided, defaults to the instance's category_prefix.
109
+
110
+ Raises:
111
+ ValueError: If memory or simulator is not set.
112
+
113
+ Returns:
114
+ Union[Any, Callable]: The query results or a callable for a new SenceAoi instance.
115
+ """
116
+ agent = self.agent
117
+ if agent.memory is None or agent.simulator is None:
118
+ raise ValueError("Memory or Simulator is not set.")
119
+ if radius is None and category_prefix is None:
120
+ position = await agent.memory.get("position")
121
+ resp = []
122
+ for prefix in self.category_prefix:
123
+ resp += agent.simulator.map.query_pois(
124
+ center=(position["xy_position"]["x"], position["xy_position"]["y"]),
125
+ radius=self.radius,
126
+ category_prefix=prefix,
127
+ )
128
+ # * Map six-digit codes to specific types
129
+ for poi in resp:
130
+ cate_str = poi[0]["category"]
131
+ poi[0]["category"] = POI_TYPE_DICT[cate_str]
132
+ else:
133
+ radius_ = radius if radius else self.radius
134
+ return SencePOI(radius_, category_prefix)
135
+
136
+
137
+ class UpdateWithSimulator(Tool):
138
+ def __init__(self) -> None:
139
+ pass
140
+
141
+ async def _update_motion_with_sim(
142
+ self,
143
+ ):
144
+ agent = self.agent
145
+ if agent._simulator is None:
146
+ return
147
+ if not agent._has_bound_to_simulator:
148
+ await agent._bind_to_simulator() # type: ignore
149
+ simulator = agent.simulator
150
+ memory = agent.memory
151
+ person_id = await memory.get("id")
152
+ resp = await simulator.get_person(person_id)
153
+ resp_dict = resp["person"]
154
+ for k, v in resp_dict.get("motion", {}).items():
155
+ try:
156
+ await memory.get(k)
157
+ await memory.update(
158
+ k, v, mode="replace", protect_llm_read_only_fields=False
159
+ )
160
+ except KeyError as e:
161
+ continue
162
+
163
+ async def __call__(
164
+ self,
165
+ ):
166
+ agent = self.agent
167
+ await self._update_motion_with_sim()
168
+
169
+
170
+ class ResetAgentPosition(Tool):
171
+ def __init__(self) -> None:
172
+ pass
173
+
174
+ async def __call__(
175
+ self,
176
+ aoi_id: Optional[int] = None,
177
+ poi_id: Optional[int] = None,
178
+ lane_id: Optional[int] = None,
179
+ s: Optional[float] = None,
180
+ ):
181
+ agent = self.agent
182
+ memory = agent.memory
183
+ await agent.simulator.reset_person_position(
184
+ person_id=await memory.get("id"),
185
+ aoi_id=aoi_id,
186
+ poi_id=poi_id,
187
+ lane_id=lane_id,
188
+ s=s,
189
+ )
190
+
191
+
192
+ class ExportMlflowMetrics(Tool):
193
+ def __init__(self, log_batch_size: int = 100) -> None:
194
+ self._log_batch_size = log_batch_size
195
+ # TODO: support other log types
196
+ self.metric_log_cache: dict[str, list[Metric]] = defaultdict(list)
197
+
198
+ async def __call__(
199
+ self,
200
+ metric: Union[Sequence[Union[Metric, dict]], Union[Metric, dict]],
201
+ clear_cache: bool = False,
202
+ ):
203
+ agent = self.agent
204
+ batch_size = self._log_batch_size
205
+ if not isinstance(metric, Sequence):
206
+ metric = [metric]
207
+ for _metric in metric:
208
+ if isinstance(_metric, Metric):
209
+ item = _metric
210
+ metric_key = item.key
211
+ else:
212
+ item = Metric(
213
+ key=_metric["key"],
214
+ value=_metric["value"],
215
+ timestamp=_metric.get("timestamp", int(1000 * time.time())),
216
+ step=_metric["step"],
217
+ )
218
+ metric_key = _metric["key"]
219
+ self.metric_log_cache[metric_key].append(item)
220
+ for metric_key, _cache in self.metric_log_cache.items():
221
+ if len(_cache) > batch_size:
222
+ client = agent.mlflow_client
223
+ await client.log_batch(
224
+ metrics=_cache[:batch_size],
225
+ )
226
+ _cache = _cache[batch_size:]
227
+ if clear_cache:
228
+ await self._clear_cache()
229
+
230
+ async def _clear_cache(
231
+ self,
232
+ ):
233
+ agent = self.agent
234
+ client = agent.mlflow_client
235
+ for metric_key, _cache in self.metric_log_cache.items():
236
+ if len(_cache) > 0:
237
+ await client.log_batch(
238
+ metrics=_cache,
239
+ )
240
+ _cache = []
@@ -0,0 +1,163 @@
1
+ import asyncio
2
+ from typing import Optional
3
+ import socket
4
+ from ..memory import Memory
5
+ from ..environment import Simulator
6
+
7
+ KEY_TRIGGER_COMPONENTS = [Memory, Simulator]
8
+
9
+
10
+ class EventTrigger:
11
+ """Base class for event triggers that wait for specific conditions to be met."""
12
+
13
+ # 定义该trigger需要的组件类型
14
+ required_components: list[type] = []
15
+
16
+ def __init__(self, block=None):
17
+ self.block = block
18
+ if block is not None:
19
+ self.initialize()
20
+
21
+ def initialize(self) -> None:
22
+ """Initialize the trigger with necessary dependencies."""
23
+ if not self.block:
24
+ raise RuntimeError("Block not set for trigger")
25
+
26
+ # 检查所需组件是否都存在
27
+ missing_components = []
28
+ for component_type in self.required_components:
29
+ component_name = component_type.__name__.lower()
30
+ if not hasattr(self.block, component_name):
31
+ missing_components.append(component_type.__name__)
32
+
33
+ if missing_components:
34
+ raise RuntimeError(
35
+ f"Block is missing required components for {self.__class__.__name__}: "
36
+ f"{', '.join(missing_components)}"
37
+ )
38
+
39
+ async def wait_for_trigger(self) -> None:
40
+ """Wait for the event trigger to be activated.
41
+
42
+ Raises:
43
+ NotImplementedError: Subclasses must implement this method.
44
+ """
45
+ raise NotImplementedError
46
+
47
+
48
+ class MemoryChangeTrigger(EventTrigger):
49
+ """Event trigger that activates when a specific key in memory changes."""
50
+
51
+ required_components = [Memory]
52
+
53
+ def __init__(self, key: str) -> None:
54
+ """Initialize the memory change trigger.
55
+
56
+ Args:
57
+ key (str): The key in memory to monitor for changes.
58
+ """
59
+ self.key = key
60
+ self.trigger_event = asyncio.Event()
61
+ self._initialized = False
62
+ super().__init__()
63
+
64
+ def initialize(self) -> None:
65
+ """Initialize the trigger with memory from block."""
66
+ super().initialize() # 首先检查必需组件
67
+ self.memory = self.block.memory
68
+ asyncio.create_task(self.memory.add_watcher(self.key, self.trigger_event.set))
69
+ self._initialized = True
70
+
71
+ async def wait_for_trigger(self) -> None:
72
+ """Wait for the memory change trigger to be activated."""
73
+ if not self._initialized:
74
+ raise RuntimeError("Trigger not properly initialized")
75
+ await self.trigger_event.wait()
76
+ self.trigger_event.clear()
77
+
78
+
79
+ class TimeTrigger(EventTrigger):
80
+ """Event trigger that activates periodically based on time intervals."""
81
+
82
+ required_components = [Simulator]
83
+
84
+ def __init__(
85
+ self,
86
+ days: Optional[int] = None,
87
+ hours: Optional[int] = None,
88
+ minutes: Optional[int] = None,
89
+ ) -> None:
90
+ """Initialize the time trigger with interval settings.
91
+
92
+ Args:
93
+ days (Optional[int]): Execute every N days
94
+ hours (Optional[int]): Execute every N hours
95
+ minutes (Optional[int]): Execute every N minutes
96
+
97
+ Raises:
98
+ ValueError: If all interval parameters are None or negative
99
+ """
100
+ if all(param is None for param in (days, hours, minutes)):
101
+ raise ValueError("At least one time interval must be specified")
102
+
103
+ # 验证参数有效性
104
+ for param_name, param_value in [
105
+ ("days", days),
106
+ ("hours", hours),
107
+ ("minutes", minutes),
108
+ ]:
109
+ if param_value is not None and param_value < 0:
110
+ raise ValueError(f"{param_name} cannot be negative")
111
+
112
+ # 将所有时间间隔转换为秒
113
+ self.interval = 0
114
+ if days is not None:
115
+ self.interval += days * 24 * 60 * 60
116
+ if hours is not None:
117
+ self.interval += hours * 60 * 60
118
+ if minutes is not None:
119
+ self.interval += minutes * 60
120
+
121
+ self.trigger_event = asyncio.Event()
122
+ self._initialized = False
123
+ self._monitoring_task = None
124
+ self._last_trigger_time = None
125
+ super().__init__()
126
+
127
+ def initialize(self) -> None:
128
+ """Initialize the trigger with necessary dependencies."""
129
+ super().initialize() # 首先检查必需组件
130
+ self.memory = self.block.memory
131
+ self.simulator = self.block.simulator
132
+ # 启动时间监控任务
133
+ self._monitoring_task = asyncio.create_task(self._monitor_time())
134
+ self._initialized = True
135
+
136
+ async def _monitor_time(self):
137
+ """持续监控时间并在达到间隔时触发事件"""
138
+ # 第一次调用时直接触发
139
+ self.trigger_event.set()
140
+
141
+ while True:
142
+ try:
143
+ current_time = await self.simulator.get_time()
144
+
145
+ # 如果是第一次或者已经过了指定的时间间隔
146
+ if (
147
+ self._last_trigger_time is None
148
+ or current_time - self._last_trigger_time >= self.interval
149
+ ):
150
+ self._last_trigger_time = current_time
151
+ self.trigger_event.set()
152
+
153
+ await asyncio.sleep(5) # 避免过于频繁的检查
154
+ except Exception as e:
155
+ print(f"Error in time monitoring: {e}")
156
+ await asyncio.sleep(10) # 发生错误时等待较长时间
157
+
158
+ async def wait_for_trigger(self) -> None:
159
+ """Wait for the time trigger to be activated."""
160
+ if not self._initialized:
161
+ raise RuntimeError("Trigger not properly initialized")
162
+ await self.trigger_event.wait()
163
+ self.trigger_event.clear()
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2024 FIB LAB, Tsinghua University
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
@@ -0,0 +1,235 @@
1
+ Metadata-Version: 2.1
2
+ Name: pycityagent
3
+ Version: 2.0.0a43
4
+ Summary: LLM-based城市环境agent构建库
5
+ Author-email: Yuwei Yan <pinkgranite86@gmail.com>, Junbo Yan <yanjb20thu@gmali.com>, Jun Zhang <zhangjun990222@gmali.com>
6
+ License: MIT License
7
+
8
+ Copyright (c) 2024 FIB LAB, Tsinghua University
9
+
10
+ Permission is hereby granted, free of charge, to any person obtaining a copy
11
+ of this software and associated documentation files (the "Software"), to deal
12
+ in the Software without restriction, including without limitation the rights
13
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
14
+ copies of the Software, and to permit persons to whom the Software is
15
+ furnished to do so, subject to the following conditions:
16
+
17
+ The above copyright notice and this permission notice shall be included in all
18
+ copies or substantial portions of the Software.
19
+
20
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
21
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
22
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
23
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
24
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
25
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
26
+ SOFTWARE.
27
+ Project-URL: Homepage, https://github.com/tsinghua-fib-lab/pycityagent
28
+ Project-URL: Repository, https://github.com/tsinghua-fib-lab/pycityagent.git
29
+ Project-URL: Issues, https://github.com/tsinghua-fib-lab/pycityagent/issues
30
+ Classifier: Programming Language :: Python :: 3
31
+ Classifier: Operating System :: POSIX :: Linux
32
+ Classifier: Operating System :: MacOS :: MacOS X
33
+ Requires-Python: >=3.9
34
+ Description-Content-Type: text/markdown
35
+ License-File: LICENSE
36
+ Requires-Dist: aiohttp>=3.10.10
37
+ Requires-Dist: citystreetview>=1.2.4
38
+ Requires-Dist: dashscope>=1.14.1
39
+ Requires-Dist: geojson>=3.1.0
40
+ Requires-Dist: grpcio<2.0.0,>=1.67.1
41
+ Requires-Dist: matplotlib>=3.8.3
42
+ Requires-Dist: networkx>=3.2.1
43
+ Requires-Dist: numpy<2.0.0,>=1.20.0
44
+ Requires-Dist: openai>=1.58.1
45
+ Requires-Dist: Pillow<12.0.0,>=11.0.0
46
+ Requires-Dist: protobuf<5.0.0,<=4.24.0
47
+ Requires-Dist: pycitydata>=1.0.3
48
+ Requires-Dist: pycityproto>=2.1.5
49
+ Requires-Dist: requests>=2.32.3
50
+ Requires-Dist: Shapely>=2.0.6
51
+ Requires-Dist: PyYAML>=6.0.2
52
+ Requires-Dist: zhipuai>=2.1.5.20230904
53
+ Requires-Dist: gradio>=5.7.1
54
+ Requires-Dist: mosstool>=1.3.0
55
+ Requires-Dist: ray>=2.40.0
56
+ Requires-Dist: aiomqtt>=2.3.0
57
+ Requires-Dist: fastavro>=1.10.0
58
+ Requires-Dist: pandavro>=1.8.0
59
+ Requires-Dist: langchain-core>=0.3.28
60
+ Requires-Dist: mlflow>=2.19.0
61
+ Requires-Dist: psycopg[binary]>=3.2.3
62
+ Requires-Dist: transformers>=4.47.1
63
+ Requires-Dist: torch>=2.5.1
64
+ Requires-Dist: faiss-cpu>=1.9.0.post1
65
+ Requires-Dist: langchain-community>=0.3.13
66
+
67
+
68
+
69
+ <div style="text-align: center; background-color: white; padding: 20px; border-radius: 30px;">
70
+ <img src="./static/cityagent_logo.png" alt="CityAgent Logo" width="200" style="display: block; margin: 0 auto;">
71
+ <h1 style="color: black; margin: 0; font-size: 3em;">CityAgent: LLM Agents in City</h1>
72
+ </div>
73
+
74
+
75
+ # 🚀 CityAgent
76
+ ![License](https://img.shields.io/badge/license-MIT-green) &ensp;
77
+ [![Online Documentation](https://img.shields.io/badge/docs-online-blue)](https://docs.fiblab.net/pycityagent) &ensp;
78
+
79
+
80
+ CityAgent is an advanced framework specifically designed for building intelligent agents in urban simulation environments. With CityAgent, you can easily create and manage agents, enabling complex urban scenarios to be modeled and simulated efficiently.
81
+
82
+ ## 🌟 Features
83
+ - **Modular Design**: Plug-and-play components for agent behavior.
84
+ - **Urban Environment Simulation**: Built to simulate diverse urban scenarios.
85
+ - **LLM Integration**: Connects to language models for enriched agent behavior.
86
+ - **Flexible Configuration**: YAML-based configuration for easy customization.
87
+
88
+ ## 📑 Table of Contents
89
+
90
+ 1. [News](#news)
91
+ 2. [Framework](#framework)
92
+ 3. [Setup](#setup)
93
+ 4. [QuickStart](#quickstart)
94
+ 5. [Contributing](#contributing)
95
+ 6. [License](#license)
96
+
97
+ <a id="news"></a>
98
+ ## 📰 News
99
+
100
+ - 📢 **11.10** - Initial update is now live!
101
+ - 📢 **2.x version is not compatible* with 1.x version**
102
+
103
+ Stay tuned for upcoming updates!
104
+
105
+ <a id="framework"></a>
106
+ ## 🛠️ Framework
107
+
108
+ CityAgent is built with a multi-layered architecture that allows users to create and manage intelligent agents for urban environments in a scalable and flexible manner. The framework is divided into several key layers, each responsible for different functionalities as depicted in the diagram below:
109
+
110
+ <img src="./static/framework.png" alt="CityAgent Framework Overview" width="600" style="display: block; margin: 20px auto;">
111
+
112
+ ### Architectural Layers
113
+ - **Model Layer**: Handles agent configuration, task definitions, logging setup, and result aggregation.
114
+ - **Task Configuration**: Defines agent behaviors and objectives.
115
+ - **Unified Execution**: Centralized entry point for agent processes.
116
+
117
+ - **Agent Layer**: Implements multi-head workflows to manage agent actions.
118
+ - **Memory**: Stores agent-related information, such as location and motion.
119
+ - **Static Profiles**: Maintains unchanging agent attributes.
120
+ - **Custom Data Pool**: Functions as a working memory.
121
+ - **Multi-Head Workflow**: Supports both normal and event-driven modes.
122
+ - **Reason Block**: Utilizes LLMs to determine decisions based on context and tools.
123
+ - **Route Block**: Chooses the best path based on defined criteria using LLMs or rules.
124
+ - **Action Block**: Executes actions as per defined contexts and tools.
125
+
126
+ - **Message Layer**: Facilitates agent communication through peer-to-peer (P2P), peer-to-group (P2G), and group chats.
127
+
128
+ - **Environment Layer**: Manages interaction with the urban environment.
129
+ - **Environment Sensing**: Reads data from the environment.
130
+ - **Interaction Handling**: Writes or modifies environmental states.
131
+ - **Message Management**: Handles incoming and outgoing agent messages.
132
+
133
+ - **LLM Layer**: Provides configuration and integration for using LLMs in the agent's workflow.
134
+ - **Prompting & Execution**: Supports model invocation and monitoring.
135
+ - **Model Support**: Compatible with various LLMs, such as OpenAI, Qwen, Deepseek, etc.
136
+
137
+ - **Tool Layer**: Provides additional utilities to the agents.
138
+ - **String Processing**: Handles parsing and formatting.
139
+ - **Result Analysis**: Parses responses in formats like JSON or dictionaries.
140
+ - **Data Storage & Retrieval**: Includes ranking and search tools.
141
+
142
+ <a id="setup"></a>
143
+ ## ⚙️ Setup
144
+
145
+ You can set up CityAgent in two different ways:
146
+
147
+ ### 1. From Scratch
148
+
149
+ Follow these steps to set up CityAgent from scratch by cloning the repository. The project is built using Python and managed with Poetry.
150
+
151
+ 1. **Clone the Repository**
152
+ ```bash
153
+ git clone [This Repository]
154
+ ```
155
+ 2. **Navigate to the Project Directory**
156
+ ```bash
157
+ cd pycityagent
158
+ ```
159
+ 3. **Install Poetry** (if not installed)
160
+ ```bash
161
+ curl -sSL https://install.python-poetry.org | python3 -
162
+ ```
163
+ 4. **Install Dependencies**
164
+ ```bash
165
+ poetry install
166
+ ```
167
+ 5. **Activate the Virtual Environment**
168
+ ```bash
169
+ poetry shell
170
+ ```
171
+
172
+ ### 2. Install via pip
173
+
174
+ This method is not yet available. Stay tuned for future updates!
175
+
176
+ <a id="quickstart"></a>
177
+ ## 🚀 QuickStart
178
+
179
+ Get started with CityAgent in just a few minutes!
180
+
181
+ ### 1. Config Configuration
182
+ CityAgent uses a configuration file written in `.yaml` format to manage settings for various components. Below is a sample configuration file (`config_template.yaml`) that showcases the structure:
183
+
184
+ ```yaml
185
+ llm_request:
186
+ text_request:
187
+ request_type: openai
188
+ api_key: <YOUR_API_KEY>
189
+ model: gpt-4o
190
+ img_understand_request:
191
+ request_type: none
192
+ api_key: none
193
+ model: none
194
+ img_generate_request:
195
+ request_type: none
196
+ api_key: none
197
+ model: none
198
+
199
+ simulator_request:
200
+ simulator:
201
+ server: https://api-opencity-2x.fiblab.net:58081
202
+ map_request:
203
+ mongo_uri: <MONGO_URI>
204
+ mongo_db: llmsim
205
+ mongo_coll: map_beijing5ring_withpoi_0424
206
+ cache_dir: ./cache
207
+ route_request:
208
+ server: http://api-opencity-2x.fiblab.net:58082
209
+ streetview_request:
210
+ engine: baidumap / googlemap
211
+ mapAK: baidumap api-key (if you use baidumap engine)
212
+ proxy: googlemap proxy (if you use googlemap engine)
213
+ ```
214
+
215
+ ### 2. Example Usage
216
+ To get started quickly, please refer to the `examples` folder in the repository. It contains sample scripts and configurations to help you understand how to create and use agents in an urban simulation environment.
217
+
218
+ <a id="contributing"></a>
219
+ ## 🤝 Contributing
220
+ We welcome contributions from the community!.
221
+
222
+ <a id="license"></a>
223
+ ## 📄 License
224
+
225
+ CityAgent is licensed under the MIT License. See the [LICENSE](LICENSE) file for more details.
226
+
227
+ ---
228
+
229
+ Feel free to reach out if you have any questions, suggestions, or want to collaborate!
230
+
231
+ ---
232
+
233
+ > **Follow us**: Stay updated with the latest news and features by watching the repository.
234
+
235
+ ---