pycityagent 2.0.0a17__py3-none-any.whl → 2.0.0a19__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.
- pycityagent/agent.py +119 -93
- pycityagent/economy/econ_client.py +2 -2
- pycityagent/environment/simulator.py +2 -2
- pycityagent/metrics/__init__.py +5 -0
- pycityagent/metrics/mlflow_client.py +109 -0
- pycityagent/metrics/utils/const.py +0 -0
- pycityagent/simulation/agentgroup.py +56 -21
- pycityagent/simulation/simulation.py +102 -43
- pycityagent/workflow/__init__.py +5 -3
- pycityagent/workflow/block.py +2 -3
- pycityagent/workflow/tool.py +51 -2
- {pycityagent-2.0.0a17.dist-info → pycityagent-2.0.0a19.dist-info}/METADATA +2 -1
- {pycityagent-2.0.0a17.dist-info → pycityagent-2.0.0a19.dist-info}/RECORD +14 -11
- {pycityagent-2.0.0a17.dist-info → pycityagent-2.0.0a19.dist-info}/WHEEL +0 -0
pycityagent/agent.py
CHANGED
@@ -1,30 +1,28 @@
|
|
1
1
|
"""智能体模板类及其定义"""
|
2
2
|
|
3
|
-
from abc import ABC, abstractmethod
|
4
3
|
import asyncio
|
5
|
-
from uuid import UUID
|
6
|
-
from copy import deepcopy
|
7
|
-
from datetime import datetime
|
8
|
-
from enum import Enum
|
9
4
|
import logging
|
10
5
|
import random
|
11
6
|
import uuid
|
12
|
-
from
|
7
|
+
from abc import ABC, abstractmethod
|
8
|
+
from copy import deepcopy
|
9
|
+
from datetime import datetime
|
10
|
+
from enum import Enum
|
11
|
+
from typing import Any, Dict, List, Optional
|
12
|
+
from uuid import UUID
|
13
13
|
|
14
14
|
import fastavro
|
15
|
-
|
16
|
-
from pycityagent.environment.sim.person_service import PersonService
|
17
15
|
from mosstool.util.format_converter import dict2pb
|
18
16
|
from pycityproto.city.person.v2 import person_pb2 as person_pb2
|
19
|
-
from pycityagent.utils import process_survey_for_llm
|
20
|
-
|
21
|
-
from pycityagent.message.messager import Messager
|
22
|
-
from pycityagent.utils import SURVEY_SCHEMA, DIALOG_SCHEMA
|
23
17
|
|
24
18
|
from .economy import EconomyClient
|
25
19
|
from .environment import Simulator
|
20
|
+
from .environment.sim.person_service import PersonService
|
26
21
|
from .llm import LLM
|
27
22
|
from .memory import Memory
|
23
|
+
from .message.messager import Messager
|
24
|
+
from .metrics import MlflowClient
|
25
|
+
from .utils import DIALOG_SCHEMA, SURVEY_SCHEMA, process_survey_for_llm
|
28
26
|
|
29
27
|
logger = logging.getLogger("pycityagent")
|
30
28
|
|
@@ -55,6 +53,7 @@ class Agent(ABC):
|
|
55
53
|
economy_client: Optional[EconomyClient] = None,
|
56
54
|
messager: Optional[Messager] = None,
|
57
55
|
simulator: Optional[Simulator] = None,
|
56
|
+
mlflow_client: Optional[MlflowClient] = None,
|
58
57
|
memory: Optional[Memory] = None,
|
59
58
|
avro_file: Optional[Dict[str, str]] = None,
|
60
59
|
) -> None:
|
@@ -68,6 +67,7 @@ class Agent(ABC):
|
|
68
67
|
economy_client (EconomyClient): The `EconomySim` client. Defaults to None.
|
69
68
|
messager (Messager, optional): The messager object. Defaults to None.
|
70
69
|
simulator (Simulator, optional): The simulator object. Defaults to None.
|
70
|
+
mlflow_client (MlflowClient, optional): The Mlflow object. Defaults to None.
|
71
71
|
memory (Memory, optional): The memory of the agent. Defaults to None.
|
72
72
|
avro_file (Dict[str, str], optional): The avro file of the agent. Defaults to None.
|
73
73
|
"""
|
@@ -78,6 +78,7 @@ class Agent(ABC):
|
|
78
78
|
self._economy_client = economy_client
|
79
79
|
self._messager = messager
|
80
80
|
self._simulator = simulator
|
81
|
+
self._mlflow_client = mlflow_client
|
81
82
|
self._memory = memory
|
82
83
|
self._exp_id = -1
|
83
84
|
self._agent_id = -1
|
@@ -112,6 +113,12 @@ class Agent(ABC):
|
|
112
113
|
"""
|
113
114
|
self._simulator = simulator
|
114
115
|
|
116
|
+
def set_mlflow_client(self, mlflow_client: MlflowClient):
|
117
|
+
"""
|
118
|
+
Set the mlflow_client of the agent.
|
119
|
+
"""
|
120
|
+
self._mlflow_client = mlflow_client
|
121
|
+
|
115
122
|
def set_economy_client(self, economy_client: EconomyClient):
|
116
123
|
"""
|
117
124
|
Set the economy_client of the agent.
|
@@ -164,6 +171,15 @@ class Agent(ABC):
|
|
164
171
|
)
|
165
172
|
return self._economy_client
|
166
173
|
|
174
|
+
@property
|
175
|
+
def mlflow_client(self):
|
176
|
+
"""The Agent's MlflowClient"""
|
177
|
+
if self._mlflow_client is None:
|
178
|
+
raise RuntimeError(
|
179
|
+
f"MlflowClient access before assignment, please `set_mlflow_client` first!"
|
180
|
+
)
|
181
|
+
return self._mlflow_client
|
182
|
+
|
167
183
|
@property
|
168
184
|
def memory(self):
|
169
185
|
"""The Agent's Memory"""
|
@@ -218,19 +234,21 @@ class Agent(ABC):
|
|
218
234
|
response = await self._llm_client.atext_request(dialog) # type:ignore
|
219
235
|
|
220
236
|
return response # type:ignore
|
221
|
-
|
237
|
+
|
222
238
|
async def _process_survey(self, survey: dict):
|
223
239
|
survey_response = await self.generate_user_survey_response(survey)
|
224
240
|
if self._avro_file is None:
|
225
241
|
return
|
226
|
-
response_to_avro = [
|
227
|
-
|
228
|
-
|
229
|
-
|
230
|
-
|
231
|
-
|
232
|
-
|
233
|
-
|
242
|
+
response_to_avro = [
|
243
|
+
{
|
244
|
+
"id": self._uuid,
|
245
|
+
"day": await self.simulator.get_simulator_day(),
|
246
|
+
"t": await self.simulator.get_simulator_second_from_start_of_day(),
|
247
|
+
"survey_id": survey["id"],
|
248
|
+
"result": survey_response,
|
249
|
+
"created_at": int(datetime.now().timestamp() * 1000),
|
250
|
+
}
|
251
|
+
]
|
234
252
|
with open(self._avro_file["survey"], "a+b") as f:
|
235
253
|
fastavro.writer(f, SURVEY_SCHEMA, response_to_avro, codec="snappy")
|
236
254
|
|
@@ -270,28 +288,32 @@ class Agent(ABC):
|
|
270
288
|
response = await self._llm_client.atext_request(dialog) # type:ignore
|
271
289
|
|
272
290
|
return response # type:ignore
|
273
|
-
|
291
|
+
|
274
292
|
async def _process_interview(self, payload: dict):
|
275
|
-
auros = [
|
276
|
-
|
277
|
-
|
278
|
-
|
279
|
-
|
280
|
-
|
281
|
-
|
282
|
-
|
283
|
-
|
293
|
+
auros = [
|
294
|
+
{
|
295
|
+
"id": self._uuid,
|
296
|
+
"day": await self.simulator.get_simulator_day(),
|
297
|
+
"t": await self.simulator.get_simulator_second_from_start_of_day(),
|
298
|
+
"type": 2,
|
299
|
+
"speaker": "user",
|
300
|
+
"content": payload["content"],
|
301
|
+
"created_at": int(datetime.now().timestamp() * 1000),
|
302
|
+
}
|
303
|
+
]
|
284
304
|
question = payload["content"]
|
285
305
|
response = await self.generate_user_chat_response(question)
|
286
|
-
auros.append(
|
287
|
-
|
288
|
-
|
289
|
-
|
290
|
-
|
291
|
-
|
292
|
-
|
293
|
-
|
294
|
-
|
306
|
+
auros.append(
|
307
|
+
{
|
308
|
+
"id": self._uuid,
|
309
|
+
"day": await self.simulator.get_simulator_day(),
|
310
|
+
"t": await self.simulator.get_simulator_second_from_start_of_day(),
|
311
|
+
"type": 2,
|
312
|
+
"speaker": "",
|
313
|
+
"content": response,
|
314
|
+
"created_at": int(datetime.now().timestamp() * 1000),
|
315
|
+
}
|
316
|
+
)
|
295
317
|
if self._avro_file is None:
|
296
318
|
return
|
297
319
|
with open(self._avro_file["dialog"], "a+b") as f:
|
@@ -303,15 +325,17 @@ class Agent(ABC):
|
|
303
325
|
return resp
|
304
326
|
|
305
327
|
async def _process_agent_chat(self, payload: dict):
|
306
|
-
auros = [
|
307
|
-
|
308
|
-
|
309
|
-
|
310
|
-
|
311
|
-
|
312
|
-
|
313
|
-
|
314
|
-
|
328
|
+
auros = [
|
329
|
+
{
|
330
|
+
"id": self._uuid,
|
331
|
+
"day": payload["day"],
|
332
|
+
"t": payload["t"],
|
333
|
+
"type": 1,
|
334
|
+
"speaker": payload["from"],
|
335
|
+
"content": payload["content"],
|
336
|
+
"created_at": int(datetime.now().timestamp() * 1000),
|
337
|
+
}
|
338
|
+
]
|
315
339
|
asyncio.create_task(self.process_agent_chat_response(payload))
|
316
340
|
if self._avro_file is None:
|
317
341
|
return
|
@@ -341,18 +365,14 @@ class Agent(ABC):
|
|
341
365
|
raise NotImplementedError
|
342
366
|
|
343
367
|
# MQTT send message
|
344
|
-
async def _send_message(
|
345
|
-
self, to_agent_uuid: str, payload: dict, sub_topic: str
|
346
|
-
):
|
368
|
+
async def _send_message(self, to_agent_uuid: str, payload: dict, sub_topic: str):
|
347
369
|
"""通过 Messager 发送消息"""
|
348
370
|
if self._messager is None:
|
349
371
|
raise RuntimeError("Messager is not set")
|
350
372
|
topic = f"exps/{self._exp_id}/agents/{to_agent_uuid}/{sub_topic}"
|
351
373
|
await self._messager.send_message(topic, payload)
|
352
374
|
|
353
|
-
async def send_message_to_agent(
|
354
|
-
self, to_agent_uuid: str, content: str
|
355
|
-
):
|
375
|
+
async def send_message_to_agent(self, to_agent_uuid: str, content: str):
|
356
376
|
"""通过 Messager 发送消息"""
|
357
377
|
if self._messager is None:
|
358
378
|
raise RuntimeError("Messager is not set")
|
@@ -364,15 +384,17 @@ class Agent(ABC):
|
|
364
384
|
"t": await self.simulator.get_simulator_second_from_start_of_day(),
|
365
385
|
}
|
366
386
|
await self._send_message(to_agent_uuid, payload, "agent-chat")
|
367
|
-
auros = [
|
368
|
-
|
369
|
-
|
370
|
-
|
371
|
-
|
372
|
-
|
373
|
-
|
374
|
-
|
375
|
-
|
387
|
+
auros = [
|
388
|
+
{
|
389
|
+
"id": self._uuid,
|
390
|
+
"day": await self.simulator.get_simulator_day(),
|
391
|
+
"t": await self.simulator.get_simulator_second_from_start_of_day(),
|
392
|
+
"type": 1,
|
393
|
+
"speaker": self._uuid,
|
394
|
+
"content": content,
|
395
|
+
"created_at": int(datetime.now().timestamp() * 1000),
|
396
|
+
}
|
397
|
+
]
|
376
398
|
if self._avro_file is None:
|
377
399
|
return
|
378
400
|
with open(self._avro_file["dialog"], "a+b") as f:
|
@@ -403,20 +425,22 @@ class CitizenAgent(Agent):
|
|
403
425
|
name: str,
|
404
426
|
llm_client: Optional[LLM] = None,
|
405
427
|
simulator: Optional[Simulator] = None,
|
428
|
+
mlflow_client: Optional[MlflowClient] = None,
|
406
429
|
memory: Optional[Memory] = None,
|
407
430
|
economy_client: Optional[EconomyClient] = None,
|
408
431
|
messager: Optional[Messager] = None,
|
409
432
|
avro_file: Optional[dict] = None,
|
410
433
|
) -> None:
|
411
434
|
super().__init__(
|
412
|
-
name,
|
413
|
-
AgentType.Citizen,
|
414
|
-
llm_client,
|
415
|
-
economy_client,
|
416
|
-
messager,
|
417
|
-
simulator,
|
418
|
-
|
419
|
-
|
435
|
+
name=name,
|
436
|
+
type=AgentType.Citizen,
|
437
|
+
llm_client=llm_client,
|
438
|
+
economy_client=economy_client,
|
439
|
+
messager=messager,
|
440
|
+
simulator=simulator,
|
441
|
+
mlflow_client=mlflow_client,
|
442
|
+
memory=memory,
|
443
|
+
avro_file=avro_file,
|
420
444
|
)
|
421
445
|
|
422
446
|
async def bind_to_simulator(self):
|
@@ -464,9 +488,7 @@ class CitizenAgent(Agent):
|
|
464
488
|
)
|
465
489
|
person_id = resp["person_id"]
|
466
490
|
await memory.update("id", person_id, protect_llm_read_only_fields=False)
|
467
|
-
logger.debug(
|
468
|
-
f"Binding to Person `{person_id}` just added to Simulator"
|
469
|
-
)
|
491
|
+
logger.debug(f"Binding to Person `{person_id}` just added to Simulator")
|
470
492
|
# 防止模拟器还没有到prepare阶段导致get_person出错
|
471
493
|
self._has_bound_to_simulator = True
|
472
494
|
self._agent_id = person_id
|
@@ -517,24 +539,26 @@ class InstitutionAgent(Agent):
|
|
517
539
|
name: str,
|
518
540
|
llm_client: Optional[LLM] = None,
|
519
541
|
simulator: Optional[Simulator] = None,
|
542
|
+
mlflow_client: Optional[MlflowClient] = None,
|
520
543
|
memory: Optional[Memory] = None,
|
521
544
|
economy_client: Optional[EconomyClient] = None,
|
522
545
|
messager: Optional[Messager] = None,
|
523
546
|
avro_file: Optional[dict] = None,
|
524
547
|
) -> None:
|
525
548
|
super().__init__(
|
526
|
-
name,
|
527
|
-
AgentType.Institution,
|
528
|
-
llm_client,
|
529
|
-
economy_client,
|
530
|
-
|
531
|
-
|
532
|
-
|
533
|
-
|
549
|
+
name=name,
|
550
|
+
type=AgentType.Institution,
|
551
|
+
llm_client=llm_client,
|
552
|
+
economy_client=economy_client,
|
553
|
+
mlflow_client=mlflow_client,
|
554
|
+
messager=messager,
|
555
|
+
simulator=simulator,
|
556
|
+
memory=memory,
|
557
|
+
avro_file=avro_file,
|
534
558
|
)
|
535
559
|
# 添加响应收集器
|
536
560
|
self._gather_responses: Dict[str, asyncio.Future] = {}
|
537
|
-
|
561
|
+
|
538
562
|
async def bind_to_simulator(self):
|
539
563
|
await self._bind_to_economy()
|
540
564
|
|
@@ -624,22 +648,24 @@ class InstitutionAgent(Agent):
|
|
624
648
|
"""处理收到的消息,识别发送者"""
|
625
649
|
content = payload["content"]
|
626
650
|
sender_id = payload["from"]
|
627
|
-
|
651
|
+
|
628
652
|
# 将响应存储到对应的Future中
|
629
653
|
response_key = str(sender_id)
|
630
654
|
if response_key in self._gather_responses:
|
631
|
-
self._gather_responses[response_key].set_result(
|
632
|
-
|
633
|
-
|
634
|
-
|
655
|
+
self._gather_responses[response_key].set_result(
|
656
|
+
{
|
657
|
+
"from": sender_id,
|
658
|
+
"content": content,
|
659
|
+
}
|
660
|
+
)
|
635
661
|
|
636
662
|
async def gather_messages(self, agent_uuids: list[str], target: str) -> List[dict]:
|
637
663
|
"""从多个智能体收集消息
|
638
|
-
|
664
|
+
|
639
665
|
Args:
|
640
666
|
agent_uuids: 目标智能体UUID列表
|
641
667
|
target: 要收集的信息类型
|
642
|
-
|
668
|
+
|
643
669
|
Returns:
|
644
670
|
List[dict]: 收集到的所有响应
|
645
671
|
"""
|
@@ -648,7 +674,7 @@ class InstitutionAgent(Agent):
|
|
648
674
|
for agent_uuid in agent_uuids:
|
649
675
|
futures[agent_uuid] = asyncio.Future()
|
650
676
|
self._gather_responses[agent_uuid] = futures[agent_uuid]
|
651
|
-
|
677
|
+
|
652
678
|
# 发送gather请求
|
653
679
|
payload = {
|
654
680
|
"from": self._uuid,
|
@@ -656,7 +682,7 @@ class InstitutionAgent(Agent):
|
|
656
682
|
}
|
657
683
|
for agent_uuid in agent_uuids:
|
658
684
|
await self._send_message(agent_uuid, payload, "gather")
|
659
|
-
|
685
|
+
|
660
686
|
try:
|
661
687
|
# 等待所有响应
|
662
688
|
responses = await asyncio.gather(*futures.values())
|
@@ -308,11 +308,11 @@ class EconomyClient:
|
|
308
308
|
# current agent ids and org ids
|
309
309
|
return (list(response.agent_ids), list(response.org_ids))
|
310
310
|
|
311
|
-
async def get_org_entity_ids(self, org_type: economyv2.OrgType)->list[int]:
|
311
|
+
async def get_org_entity_ids(self, org_type: economyv2.OrgType) -> list[int]:
|
312
312
|
request = org_service.GetOrgEntityIdsRequest(
|
313
313
|
type=org_type,
|
314
314
|
)
|
315
315
|
response: org_service.GetOrgEntityIdsResponse = (
|
316
316
|
await self._aio_stub.GetOrgEntityIds(request)
|
317
317
|
)
|
318
|
-
return list(response.org_ids)
|
318
|
+
return list(response.org_ids)
|
@@ -5,7 +5,7 @@ import logging
|
|
5
5
|
import os
|
6
6
|
from collections.abc import Sequence
|
7
7
|
from datetime import datetime, timedelta
|
8
|
-
from typing import Any, Optional,
|
8
|
+
from typing import Any, Optional, Union, cast
|
9
9
|
|
10
10
|
from mosstool.type import TripMode
|
11
11
|
from mosstool.util.format_converter import coll2pb
|
@@ -28,7 +28,7 @@ class Simulator:
|
|
28
28
|
- Simulator Class
|
29
29
|
"""
|
30
30
|
|
31
|
-
def __init__(self, config, secure: bool = False) -> None:
|
31
|
+
def __init__(self, config:dict, secure: bool = False) -> None:
|
32
32
|
self.config = config
|
33
33
|
"""
|
34
34
|
- 模拟器配置
|
@@ -0,0 +1,109 @@
|
|
1
|
+
import asyncio
|
2
|
+
import logging
|
3
|
+
import os
|
4
|
+
import uuid
|
5
|
+
from collections.abc import Sequence
|
6
|
+
from typing import Any, Optional, Union
|
7
|
+
|
8
|
+
import mlflow
|
9
|
+
from mlflow.entities import (Dataset, DatasetInput, Document, Experiment,
|
10
|
+
ExperimentTag, FileInfo, InputTag, LifecycleStage,
|
11
|
+
LiveSpan, Metric, NoOpSpan, Param, Run, RunData,
|
12
|
+
RunInfo, RunInputs, RunStatus, RunTag, SourceType,
|
13
|
+
Span, SpanEvent, SpanStatus, SpanStatusCode,
|
14
|
+
SpanType, Trace, TraceData, TraceInfo, ViewType)
|
15
|
+
|
16
|
+
from ..utils.decorators import lock_decorator
|
17
|
+
|
18
|
+
logger = logging.getLogger("mlflow")
|
19
|
+
|
20
|
+
|
21
|
+
class MlflowClient:
|
22
|
+
"""
|
23
|
+
- Mlflow client
|
24
|
+
"""
|
25
|
+
|
26
|
+
def __init__(
|
27
|
+
self,
|
28
|
+
config: dict,
|
29
|
+
mlflow_run_name: Optional[str] = None,
|
30
|
+
experiment_name: Optional[str] = None,
|
31
|
+
experiment_description: Optional[str] = None,
|
32
|
+
experiment_tags: Optional[dict[str, Any]] = None,
|
33
|
+
) -> None:
|
34
|
+
os.environ["MLFLOW_TRACKING_USERNAME"] = config.get("username", None)
|
35
|
+
os.environ["MLFLOW_TRACKING_PASSWORD"] = config.get("password", None)
|
36
|
+
self._mlflow_uri = uri = config["mlflow_uri"]
|
37
|
+
self._client = client = mlflow.MlflowClient(tracking_uri=uri)
|
38
|
+
self._run_uuid = run_uuid = str(uuid.uuid4())
|
39
|
+
self._lock = asyncio.Lock()
|
40
|
+
# run name
|
41
|
+
if mlflow_run_name is None:
|
42
|
+
mlflow_run_name = f"exp_{run_uuid}"
|
43
|
+
|
44
|
+
# exp name
|
45
|
+
if experiment_name is None:
|
46
|
+
experiment_name = f"run_{run_uuid}"
|
47
|
+
|
48
|
+
# tags
|
49
|
+
if experiment_tags is None:
|
50
|
+
experiment_tags = {}
|
51
|
+
if experiment_description is not None:
|
52
|
+
experiment_tags["mlflow.note.content"] = experiment_description
|
53
|
+
|
54
|
+
try:
|
55
|
+
self._experiment_id = experiment_id = client.create_experiment(
|
56
|
+
name=experiment_name,
|
57
|
+
tags=experiment_tags,
|
58
|
+
)
|
59
|
+
except Exception as e:
|
60
|
+
experiment = client.get_experiment_by_name(experiment_name)
|
61
|
+
if experiment is None:
|
62
|
+
raise e
|
63
|
+
self._experiment_id = experiment_id = experiment.experiment_id
|
64
|
+
|
65
|
+
self._run = run = client.create_run(
|
66
|
+
experiment_id=experiment_id, run_name=mlflow_run_name
|
67
|
+
)
|
68
|
+
self._run_id = run.info.run_id
|
69
|
+
|
70
|
+
@property
|
71
|
+
def client(
|
72
|
+
self,
|
73
|
+
) -> mlflow.MlflowClient:
|
74
|
+
return self._client
|
75
|
+
|
76
|
+
@property
|
77
|
+
def run_id(
|
78
|
+
self,
|
79
|
+
) -> str:
|
80
|
+
return self._run_id
|
81
|
+
|
82
|
+
@lock_decorator
|
83
|
+
async def log_batch(
|
84
|
+
self,
|
85
|
+
metrics: Sequence[Metric] = (),
|
86
|
+
params: Sequence[Param] = (),
|
87
|
+
tags: Sequence[RunTag] = (),
|
88
|
+
):
|
89
|
+
self.client.log_batch(
|
90
|
+
run_id=self.run_id, metrics=metrics, params=params, tags=tags
|
91
|
+
)
|
92
|
+
|
93
|
+
@lock_decorator
|
94
|
+
async def log_metric(
|
95
|
+
self,
|
96
|
+
key: str,
|
97
|
+
value: float,
|
98
|
+
step: Optional[int] = None,
|
99
|
+
timestamp: Optional[int] = None,
|
100
|
+
):
|
101
|
+
if timestamp is not None:
|
102
|
+
timestamp = int(timestamp)
|
103
|
+
self.client.log_metric(
|
104
|
+
run_id=self.run_id,
|
105
|
+
key=key,
|
106
|
+
value=value,
|
107
|
+
timestamp=timestamp,
|
108
|
+
step=step,
|
109
|
+
)
|
File without changes
|
@@ -1,34 +1,51 @@
|
|
1
1
|
import asyncio
|
2
|
-
from datetime import datetime
|
3
2
|
import json
|
4
3
|
import logging
|
5
|
-
|
4
|
+
import time
|
6
5
|
import uuid
|
6
|
+
from datetime import datetime
|
7
|
+
from pathlib import Path
|
8
|
+
from typing import Any
|
9
|
+
from uuid import UUID
|
10
|
+
|
7
11
|
import fastavro
|
8
12
|
import ray
|
9
|
-
|
10
|
-
from
|
11
|
-
from
|
12
|
-
from
|
13
|
-
from
|
14
|
-
from
|
15
|
-
from
|
16
|
-
from
|
17
|
-
from
|
13
|
+
|
14
|
+
from ..agent import Agent, CitizenAgent, InstitutionAgent
|
15
|
+
from ..economy.econ_client import EconomyClient
|
16
|
+
from ..environment.simulator import Simulator
|
17
|
+
from ..llm.llm import LLM
|
18
|
+
from ..llm.llmconfig import LLMConfig
|
19
|
+
from ..message import Messager
|
20
|
+
from ..metrics import MlflowClient
|
21
|
+
from ..utils import (DIALOG_SCHEMA, INSTITUTION_STATUS_SCHEMA, PROFILE_SCHEMA,
|
22
|
+
STATUS_SCHEMA, SURVEY_SCHEMA)
|
18
23
|
|
19
24
|
logger = logging.getLogger("pycityagent")
|
20
25
|
|
26
|
+
|
21
27
|
@ray.remote
|
22
28
|
class AgentGroup:
|
23
|
-
def __init__(
|
29
|
+
def __init__(
|
30
|
+
self,
|
31
|
+
agents: list[Agent],
|
32
|
+
config: dict,
|
33
|
+
exp_id: str | UUID,
|
34
|
+
exp_name: str,
|
35
|
+
enable_avro: bool,
|
36
|
+
avro_path: Path,
|
37
|
+
enable_pgsql: bool,
|
38
|
+
pgsql_args: tuple[str, str, str, str, str],
|
39
|
+
logging_level: int,
|
40
|
+
):
|
24
41
|
logger.setLevel(logging_level)
|
25
42
|
self._uuid = str(uuid.uuid4())
|
26
43
|
self.agents = agents
|
27
44
|
self.config = config
|
28
45
|
self.exp_id = exp_id
|
29
46
|
self.enable_avro = enable_avro
|
30
|
-
self.avro_path = avro_path / f"{self._uuid}"
|
31
47
|
if enable_avro:
|
48
|
+
self.avro_path = avro_path / f"{self._uuid}"
|
32
49
|
self.avro_path.mkdir(parents=True, exist_ok=True)
|
33
50
|
self.avro_file = {
|
34
51
|
"profile": self.avro_path / f"profile.avro",
|
@@ -36,7 +53,7 @@ class AgentGroup:
|
|
36
53
|
"status": self.avro_path / f"status.avro",
|
37
54
|
"survey": self.avro_path / f"survey.avro",
|
38
55
|
}
|
39
|
-
|
56
|
+
|
40
57
|
self.messager = Messager(
|
41
58
|
hostname=config["simulator_request"]["mqtt"]["server"],
|
42
59
|
port=config["simulator_request"]["mqtt"]["port"],
|
@@ -63,21 +80,35 @@ class AgentGroup:
|
|
63
80
|
else:
|
64
81
|
self.economy_client = None
|
65
82
|
|
83
|
+
# Mlflow
|
84
|
+
_mlflow_config = config.get("metric_request", {}).get("mlflow")
|
85
|
+
if _mlflow_config:
|
86
|
+
logger.info(f"-----Creating Mlflow client in AgentGroup {self._uuid} ...")
|
87
|
+
self.mlflow_client = MlflowClient(
|
88
|
+
config=_mlflow_config,
|
89
|
+
mlflow_run_name=f"EXP_{exp_name}_{1000*int(time.time())}",
|
90
|
+
experiment_name=exp_name,
|
91
|
+
)
|
92
|
+
else:
|
93
|
+
self.mlflow_client = None
|
94
|
+
|
66
95
|
for agent in self.agents:
|
67
|
-
agent.set_exp_id(self.exp_id)
|
96
|
+
agent.set_exp_id(self.exp_id) # type: ignore
|
68
97
|
agent.set_llm_client(self.llm)
|
69
98
|
agent.set_simulator(self.simulator)
|
70
99
|
if self.economy_client is not None:
|
71
100
|
agent.set_economy_client(self.economy_client)
|
101
|
+
if self.mlflow_client is not None:
|
102
|
+
agent.set_mlflow_client(self.mlflow_client)
|
72
103
|
agent.set_messager(self.messager)
|
73
104
|
if self.enable_avro:
|
74
|
-
agent.set_avro_file(self.avro_file)
|
105
|
+
agent.set_avro_file(self.avro_file) # type: ignore
|
75
106
|
|
76
107
|
async def init_agents(self):
|
77
108
|
logger.debug(f"-----Initializing Agents in AgentGroup {self._uuid} ...")
|
78
109
|
logger.debug(f"-----Binding Agents to Simulator in AgentGroup {self._uuid} ...")
|
79
110
|
for agent in self.agents:
|
80
|
-
await agent.bind_to_simulator()
|
111
|
+
await agent.bind_to_simulator() # type: ignore
|
81
112
|
self.id2agent = {agent._uuid: agent for agent in self.agents}
|
82
113
|
logger.debug(f"-----Binding Agents to Messager in AgentGroup {self._uuid} ...")
|
83
114
|
await self.messager.connect()
|
@@ -104,7 +135,7 @@ class AgentGroup:
|
|
104
135
|
for agent in self.agents:
|
105
136
|
profile = await agent.memory._profile.export()
|
106
137
|
profile = profile[0]
|
107
|
-
profile[
|
138
|
+
profile["id"] = agent._uuid
|
108
139
|
profiles.append(profile)
|
109
140
|
fastavro.writer(f, PROFILE_SCHEMA, profiles)
|
110
141
|
|
@@ -139,7 +170,9 @@ class AgentGroup:
|
|
139
170
|
return results
|
140
171
|
|
141
172
|
async def update(self, target_agent_uuid: str, target_key: str, content: Any):
|
142
|
-
logger.debug(
|
173
|
+
logger.debug(
|
174
|
+
f"-----Updating {target_key} for agent {target_agent_uuid} in group {self._uuid}"
|
175
|
+
)
|
143
176
|
agent = self.id2agent[target_agent_uuid]
|
144
177
|
await agent.memory.update(target_key, content)
|
145
178
|
|
@@ -147,7 +180,9 @@ class AgentGroup:
|
|
147
180
|
logger.debug(f"-----Starting message dispatch for group {self._uuid}")
|
148
181
|
while True:
|
149
182
|
if not self.messager.is_connected():
|
150
|
-
logger.warning(
|
183
|
+
logger.warning(
|
184
|
+
"Messager is not connected. Skipping message processing."
|
185
|
+
)
|
151
186
|
|
152
187
|
# Step 1: 获取消息
|
153
188
|
messages = await self.messager.fetch_messages()
|
@@ -165,7 +200,7 @@ class AgentGroup:
|
|
165
200
|
|
166
201
|
# 提取 agent_id(主题格式为 "exps/{exp_id}/agents/{agent_uuid}/{topic_type}")
|
167
202
|
_, _, _, agent_uuid, topic_type = topic.strip("/").split("/")
|
168
|
-
|
203
|
+
|
169
204
|
if agent_uuid in self.id2agent:
|
170
205
|
agent = self.id2agent[agent_uuid]
|
171
206
|
# topic_type: agent-chat, user-chat, user-survey, gather
|
@@ -4,22 +4,21 @@ import logging
|
|
4
4
|
import os
|
5
5
|
import random
|
6
6
|
import uuid
|
7
|
-
from collections.abc import Sequence
|
7
|
+
from collections.abc import Callable, Sequence
|
8
8
|
from concurrent.futures import ThreadPoolExecutor
|
9
9
|
from datetime import datetime, timezone
|
10
10
|
from pathlib import Path
|
11
|
-
from typing import Any,
|
11
|
+
from typing import Any, Optional, Union
|
12
12
|
|
13
13
|
import pycityproto.city.economy.v2.economy_pb2 as economyv2
|
14
14
|
import yaml
|
15
15
|
from mosstool.map._map_util.const import AOI_START_ID
|
16
16
|
|
17
|
-
from pycityagent.environment.simulator import Simulator
|
18
|
-
from pycityagent.memory.memory import Memory
|
19
|
-
from pycityagent.message.messager import Messager
|
20
|
-
from pycityagent.survey import Survey
|
21
|
-
|
22
17
|
from ..agent import Agent, InstitutionAgent
|
18
|
+
from ..environment.simulator import Simulator
|
19
|
+
from ..memory.memory import Memory
|
20
|
+
from ..message.messager import Messager
|
21
|
+
from ..survey import Survey
|
23
22
|
from .agentgroup import AgentGroup
|
24
23
|
|
25
24
|
logger = logging.getLogger("pycityagent")
|
@@ -50,15 +49,16 @@ class AgentSimulation:
|
|
50
49
|
self.agent_class = [agent_class]
|
51
50
|
self.logging_level = logging_level
|
52
51
|
self.config = config
|
52
|
+
self.exp_name = exp_name
|
53
53
|
self._simulator = Simulator(config["simulator_request"])
|
54
54
|
self.agent_prefix = agent_prefix
|
55
|
-
self._agents:
|
56
|
-
self._groups:
|
57
|
-
self._agent_uuid2group:
|
58
|
-
self._agent_uuids:
|
59
|
-
self._user_chat_topics:
|
60
|
-
self._user_survey_topics:
|
61
|
-
self._user_interview_topics:
|
55
|
+
self._agents: dict[uuid.UUID, Agent] = {}
|
56
|
+
self._groups: dict[str, AgentGroup] = {} # type:ignore
|
57
|
+
self._agent_uuid2group: dict[uuid.UUID, AgentGroup] = {} # type:ignore
|
58
|
+
self._agent_uuids: list[uuid.UUID] = []
|
59
|
+
self._user_chat_topics: dict[uuid.UUID, str] = {}
|
60
|
+
self._user_survey_topics: dict[uuid.UUID, str] = {}
|
61
|
+
self._user_interview_topics: dict[uuid.UUID, str] = {}
|
62
62
|
self._loop = asyncio.get_event_loop()
|
63
63
|
|
64
64
|
self._messager = Messager(
|
@@ -69,18 +69,37 @@ class AgentSimulation:
|
|
69
69
|
)
|
70
70
|
asyncio.create_task(self._messager.connect())
|
71
71
|
|
72
|
-
|
72
|
+
# storage
|
73
|
+
_storage_config: dict[str, Any] = config.get("storage", {})
|
74
|
+
# avro
|
75
|
+
_avro_config: dict[str, Any] = _storage_config.get("avro", {})
|
76
|
+
self._enable_avro = _avro_config.get("enabled", False)
|
73
77
|
if not self._enable_avro:
|
78
|
+
self._avro_path = None
|
74
79
|
logger.warning("AVRO is not enabled, NO AVRO LOCAL STORAGE")
|
75
|
-
|
76
|
-
|
77
|
-
|
78
|
-
|
79
|
-
|
80
|
-
|
81
|
-
self.
|
82
|
-
self.
|
83
|
-
|
80
|
+
else:
|
81
|
+
self._avro_path = Path(_avro_config["path"]) / f"{self.exp_id}"
|
82
|
+
self._avro_path.mkdir(parents=True, exist_ok=True)
|
83
|
+
|
84
|
+
# pg
|
85
|
+
_pgsql_config: dict[str, Any] = _storage_config.get("pgsql", {})
|
86
|
+
self._enable_pgsql = _pgsql_config.get("enabled", False)
|
87
|
+
if not self._enable_pgsql:
|
88
|
+
logger.warning("PostgreSQL is not enabled, NO POSTGRESQL DATABASE STORAGE")
|
89
|
+
self._pgsql_args = ("", "", "", "", "")
|
90
|
+
else:
|
91
|
+
self._pgsql_host = _pgsql_config["host"]
|
92
|
+
self._pgsql_port = _pgsql_config["port"]
|
93
|
+
self._pgsql_database = _pgsql_config["database"]
|
94
|
+
self._pgsql_user = _pgsql_config.get("user", None)
|
95
|
+
self._pgsql_password = _pgsql_config.get("password", None)
|
96
|
+
self._pgsql_args: tuple[str, str, str, str, str] = (
|
97
|
+
self._pgsql_host,
|
98
|
+
self._pgsql_port,
|
99
|
+
self._pgsql_database,
|
100
|
+
self._pgsql_user,
|
101
|
+
self._pgsql_password,
|
102
|
+
)
|
84
103
|
|
85
104
|
# 添加实验信息相关的属性
|
86
105
|
self._exp_info = {
|
@@ -96,14 +115,34 @@ class AgentSimulation:
|
|
96
115
|
}
|
97
116
|
|
98
117
|
# 创建异步任务保存实验信息
|
99
|
-
|
100
|
-
|
101
|
-
|
118
|
+
if self._enable_avro:
|
119
|
+
assert self._avro_path is not None
|
120
|
+
self._exp_info_file = self._avro_path / "experiment_info.yaml"
|
121
|
+
with open(self._exp_info_file, "w") as f:
|
122
|
+
yaml.dump(self._exp_info, f)
|
123
|
+
|
124
|
+
@property
|
125
|
+
def enable_avro(
|
126
|
+
self,
|
127
|
+
) -> bool:
|
128
|
+
return self._enable_avro
|
129
|
+
|
130
|
+
@property
|
131
|
+
def enable_pgsql(
|
132
|
+
self,
|
133
|
+
) -> bool:
|
134
|
+
return self._enable_pgsql
|
102
135
|
|
103
136
|
@property
|
104
|
-
def agents(self):
|
137
|
+
def agents(self) -> dict[uuid.UUID, Agent]:
|
105
138
|
return self._agents
|
106
139
|
|
140
|
+
@property
|
141
|
+
def avro_path(
|
142
|
+
self,
|
143
|
+
) -> Path:
|
144
|
+
return self._avro_path # type:ignore
|
145
|
+
|
107
146
|
@property
|
108
147
|
def groups(self):
|
109
148
|
return self._groups
|
@@ -122,13 +161,24 @@ class AgentSimulation:
|
|
122
161
|
agents: list[Agent],
|
123
162
|
config: dict,
|
124
163
|
exp_id: str,
|
164
|
+
exp_name: str,
|
125
165
|
enable_avro: bool,
|
126
166
|
avro_path: Path,
|
167
|
+
enable_pgsql: bool,
|
168
|
+
pgsql_args: tuple[str, str, str, str, str],
|
127
169
|
logging_level: int = logging.WARNING,
|
128
170
|
):
|
129
171
|
"""创建远程组"""
|
130
172
|
group = AgentGroup.remote(
|
131
|
-
agents,
|
173
|
+
agents,
|
174
|
+
config,
|
175
|
+
exp_id,
|
176
|
+
exp_name,
|
177
|
+
enable_avro,
|
178
|
+
avro_path,
|
179
|
+
enable_pgsql,
|
180
|
+
pgsql_args,
|
181
|
+
logging_level,
|
132
182
|
)
|
133
183
|
return group_name, group, agents
|
134
184
|
|
@@ -174,7 +224,6 @@ class AgentSimulation:
|
|
174
224
|
memory_config_func.append(self.default_memory_config_institution)
|
175
225
|
else:
|
176
226
|
memory_config_func.append(self.default_memory_config_citizen)
|
177
|
-
|
178
227
|
# 使用线程池并行创建 AgentGroup
|
179
228
|
group_creation_params = []
|
180
229
|
class_init_index = 0
|
@@ -226,8 +275,11 @@ class AgentSimulation:
|
|
226
275
|
agents,
|
227
276
|
self.config,
|
228
277
|
self.exp_id,
|
229
|
-
self.
|
230
|
-
self.
|
278
|
+
self.exp_name,
|
279
|
+
self.enable_avro,
|
280
|
+
self.avro_path,
|
281
|
+
self.enable_pgsql,
|
282
|
+
self._pgsql_args,
|
231
283
|
self.logging_level,
|
232
284
|
)
|
233
285
|
creation_tasks.append((group_name, group, agents))
|
@@ -393,7 +445,7 @@ class AgentSimulation:
|
|
393
445
|
return EXTRA_ATTRIBUTES, PROFILE, BASE
|
394
446
|
|
395
447
|
async def send_survey(
|
396
|
-
self, survey: Survey, agent_uuids: Optional[
|
448
|
+
self, survey: Survey, agent_uuids: Optional[list[uuid.UUID]] = None
|
397
449
|
):
|
398
450
|
"""发送问卷"""
|
399
451
|
survey_dict = survey.to_dict()
|
@@ -410,7 +462,7 @@ class AgentSimulation:
|
|
410
462
|
await self._messager.send_message(topic, payload)
|
411
463
|
|
412
464
|
async def send_interview_message(
|
413
|
-
self, content: str, agent_uuids: Union[uuid.UUID,
|
465
|
+
self, content: str, agent_uuids: Union[uuid.UUID, list[uuid.UUID]]
|
414
466
|
):
|
415
467
|
"""发送面试消息"""
|
416
468
|
payload = {
|
@@ -438,10 +490,17 @@ class AgentSimulation:
|
|
438
490
|
async def _save_exp_info(self) -> None:
|
439
491
|
"""异步保存实验信息到YAML文件"""
|
440
492
|
try:
|
441
|
-
|
442
|
-
|
493
|
+
if self.enable_avro:
|
494
|
+
with open(self._exp_info_file, "w") as f:
|
495
|
+
yaml.dump(self._exp_info, f)
|
443
496
|
except Exception as e:
|
444
|
-
logger.error(f"保存实验信息失败: {str(e)}")
|
497
|
+
logger.error(f"Avro保存实验信息失败: {str(e)}")
|
498
|
+
try:
|
499
|
+
if self.enable_pgsql:
|
500
|
+
# TODO
|
501
|
+
pass
|
502
|
+
except Exception as e:
|
503
|
+
logger.error(f"PostgreSQL保存实验信息失败: {str(e)}")
|
445
504
|
|
446
505
|
async def _update_exp_status(self, status: int, error: str = "") -> None:
|
447
506
|
"""更新实验状态并保存"""
|
@@ -488,12 +547,12 @@ class AgentSimulation:
|
|
488
547
|
monitor_task = asyncio.create_task(self._monitor_exp_status(stop_event))
|
489
548
|
|
490
549
|
try:
|
491
|
-
|
492
|
-
|
493
|
-
|
494
|
-
|
495
|
-
|
496
|
-
|
550
|
+
for _ in range(day):
|
551
|
+
tasks = []
|
552
|
+
for group in self._groups.values():
|
553
|
+
tasks.append(group.run.remote())
|
554
|
+
# 等待所有group运行完成
|
555
|
+
await asyncio.gather(*tasks)
|
497
556
|
|
498
557
|
finally:
|
499
558
|
# 设置停止事件
|
pycityagent/workflow/__init__.py
CHANGED
@@ -4,14 +4,16 @@
|
|
4
4
|
This module contains classes for creating blocks and running workflows.
|
5
5
|
"""
|
6
6
|
|
7
|
-
from .block import Block, log_and_check, log_and_check_with_memory,
|
7
|
+
from .block import (Block, log_and_check, log_and_check_with_memory,
|
8
|
+
trigger_class)
|
8
9
|
from .prompt import FormatPrompt
|
9
|
-
from .tool import GetMap, SencePOI, Tool
|
10
|
-
from .trigger import MemoryChangeTrigger, TimeTrigger
|
10
|
+
from .tool import ExportMlflowMetrics, GetMap, SencePOI, Tool
|
11
|
+
from .trigger import EventTrigger, MemoryChangeTrigger, TimeTrigger
|
11
12
|
|
12
13
|
__all__ = [
|
13
14
|
"SencePOI",
|
14
15
|
"Tool",
|
16
|
+
"ExportMlflowMetrics",
|
15
17
|
"GetMap",
|
16
18
|
"MemoryChangeTrigger",
|
17
19
|
"TimeTrigger",
|
pycityagent/workflow/block.py
CHANGED
@@ -3,12 +3,11 @@ import functools
|
|
3
3
|
import inspect
|
4
4
|
from typing import Any, Callable, Coroutine, Optional, Union
|
5
5
|
|
6
|
-
from
|
7
|
-
from pycityagent.workflow.trigger import EventTrigger
|
8
|
-
|
6
|
+
from ..environment.simulator import Simulator
|
9
7
|
from ..llm import LLM
|
10
8
|
from ..memory import Memory
|
11
9
|
from ..utils.decorators import record_call_aio
|
10
|
+
from ..workflow.trigger import EventTrigger
|
12
11
|
|
13
12
|
TRIGGER_INTERVAL = 1
|
14
13
|
|
pycityagent/workflow/tool.py
CHANGED
@@ -1,7 +1,11 @@
|
|
1
|
+
import time
|
1
2
|
from typing import Any, Callable, Dict, List, Optional, Union
|
2
3
|
|
4
|
+
from mlflow.entities import Metric
|
5
|
+
|
3
6
|
from ..agent import Agent
|
4
|
-
from ..environment import LEVEL_ONE_PRE, POI_TYPE_DICT, AoiService,
|
7
|
+
from ..environment import (LEVEL_ONE_PRE, POI_TYPE_DICT, AoiService,
|
8
|
+
PersonService)
|
5
9
|
from ..workflow import Block
|
6
10
|
|
7
11
|
|
@@ -139,7 +143,7 @@ class UpdateWithSimulator(Tool):
|
|
139
143
|
if agent._simulator is None:
|
140
144
|
return
|
141
145
|
if not agent._has_bound_to_simulator:
|
142
|
-
await agent._bind_to_simulator()
|
146
|
+
await agent._bind_to_simulator() # type: ignore
|
143
147
|
simulator = agent.simulator
|
144
148
|
memory = agent.memory
|
145
149
|
person_id = await memory.get("id")
|
@@ -181,3 +185,48 @@ class ResetAgentPosition(Tool):
|
|
181
185
|
lane_id=lane_id,
|
182
186
|
s=s,
|
183
187
|
)
|
188
|
+
|
189
|
+
|
190
|
+
class ExportMlflowMetrics(Tool):
|
191
|
+
def __init__(self, log_batch_size: int = 100) -> None:
|
192
|
+
self._log_batch_size = log_batch_size
|
193
|
+
# TODO:support other log types
|
194
|
+
self.metric_log_cache: list[Metric] = []
|
195
|
+
|
196
|
+
async def __call__(
|
197
|
+
self,
|
198
|
+
metric: Union[Metric, dict],
|
199
|
+
clear_cache: bool = False,
|
200
|
+
):
|
201
|
+
agent = self.agent
|
202
|
+
batch_size = self._log_batch_size
|
203
|
+
if len(self.metric_log_cache) > batch_size:
|
204
|
+
client = agent.mlflow_client
|
205
|
+
await client.log_batch(
|
206
|
+
metrics=self.metric_log_cache[:batch_size],
|
207
|
+
)
|
208
|
+
self.metric_log_cache = self.metric_log_cache[batch_size:]
|
209
|
+
else:
|
210
|
+
if isinstance(metric, Metric):
|
211
|
+
self.metric_log_cache.append(metric)
|
212
|
+
else:
|
213
|
+
_metric = Metric(
|
214
|
+
key=metric["key"],
|
215
|
+
value=metric["value"],
|
216
|
+
timestamp=metric.get("timestamp", int(1000 * time.time())),
|
217
|
+
step=metric["step"],
|
218
|
+
)
|
219
|
+
self.metric_log_cache.append(_metric)
|
220
|
+
if clear_cache:
|
221
|
+
await self._clear_cache()
|
222
|
+
|
223
|
+
async def _clear_cache(
|
224
|
+
self,
|
225
|
+
):
|
226
|
+
agent = self.agent
|
227
|
+
client = agent.mlflow_client
|
228
|
+
if len(self.metric_log_cache) > 0:
|
229
|
+
await client.log_batch(
|
230
|
+
metrics=self.metric_log_cache,
|
231
|
+
)
|
232
|
+
self.metric_log_cache = []
|
@@ -1,6 +1,6 @@
|
|
1
1
|
Metadata-Version: 2.1
|
2
2
|
Name: pycityagent
|
3
|
-
Version: 2.0.
|
3
|
+
Version: 2.0.0a19
|
4
4
|
Summary: LLM-based城市环境agent构建库
|
5
5
|
License: MIT
|
6
6
|
Author: Yuwei Yan
|
@@ -26,6 +26,7 @@ Requires-Dist: gradio (>=5.7.1,<6.0.0)
|
|
26
26
|
Requires-Dist: grpcio (==1.67.1)
|
27
27
|
Requires-Dist: langchain-core (>=0.3.28,<0.4.0)
|
28
28
|
Requires-Dist: matplotlib (==3.8.3)
|
29
|
+
Requires-Dist: mlflow (>=2.19.0,<3.0.0)
|
29
30
|
Requires-Dist: mosstool (==1.0.24)
|
30
31
|
Requires-Dist: networkx (==3.2.1)
|
31
32
|
Requires-Dist: numpy (>=1.20.0,<2.0.0)
|
@@ -1,7 +1,7 @@
|
|
1
1
|
pycityagent/__init__.py,sha256=EDxt3Su3lH1IMh9suNw7GeGL7UrXeWiZTw5KWNznDzc,637
|
2
|
-
pycityagent/agent.py,sha256=
|
2
|
+
pycityagent/agent.py,sha256=gy9OlFpnEla7wK45k4m7_ySLp67mBKoG50vxUEmxbJE,24280
|
3
3
|
pycityagent/economy/__init__.py,sha256=aonY4WHnx-6EGJ4WKrx4S-2jAkYNLtqUA04jp6q8B7w,75
|
4
|
-
pycityagent/economy/econ_client.py,sha256=
|
4
|
+
pycityagent/economy/econ_client.py,sha256=EZDGxM7K83ucYZQ5qdv6HA-jhRCWbR1u5q-kLMqelKc,11192
|
5
5
|
pycityagent/environment/__init__.py,sha256=awHxlOud-btWbk0FCS4RmGJ13W84oVCkbGfcrhKqihA,240
|
6
6
|
pycityagent/environment/interact/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
7
7
|
pycityagent/environment/interact/interact.py,sha256=ifxPPzuHeqLHIZ_6zvfXMoBOnBsXNIP4bYp7OJ7pnEQ,6588
|
@@ -21,7 +21,7 @@ pycityagent/environment/sim/person_service.py,sha256=nIvOsoBoqOTDYtsiThg07-4ZBgk
|
|
21
21
|
pycityagent/environment/sim/road_service.py,sha256=phKTwTyhc_6Ht2mddEXpdENfl-lRXIVY0CHAlw1yHjI,1264
|
22
22
|
pycityagent/environment/sim/sim_env.py,sha256=HI1LcS_FotDKQ6vBnx0e49prXSABOfA20aU9KM-ZkCY,4625
|
23
23
|
pycityagent/environment/sim/social_service.py,sha256=6Iqvq6dz8H2jhLLdtaITc6Js9QnQw-Ylsd5AZgUj3-E,1993
|
24
|
-
pycityagent/environment/simulator.py,sha256=
|
24
|
+
pycityagent/environment/simulator.py,sha256=XjcxbyBIbB3Ht9z087z_oWIPAN6pP5Eq1lyf4W5atb8,12502
|
25
25
|
pycityagent/environment/utils/__init__.py,sha256=1m4Q1EfGvNpUsa1bgQzzCyWhfkpElnskNImjjFD3Znc,237
|
26
26
|
pycityagent/environment/utils/base64.py,sha256=hoREzQo3FXMN79pqQLO2jgsDEvudciomyKii7MWljAM,374
|
27
27
|
pycityagent/environment/utils/const.py,sha256=3RMNy7_bE7-23K90j9DFW_tWEzu8s7hSTgKbV-3BFl4,5327
|
@@ -45,9 +45,12 @@ pycityagent/memory/state.py,sha256=5W0c1yJ-aaPpE74B2LEcw3Ygpm77tyooHv8NylyrozE,5
|
|
45
45
|
pycityagent/memory/utils.py,sha256=wLNlNlZ-AY9VB8kbUIy0UQSYh26FOQABbhmKQkit5o8,850
|
46
46
|
pycityagent/message/__init__.py,sha256=TCjazxqb5DVwbTu1fF0sNvaH_EPXVuj2XQ0p6W-QCLU,55
|
47
47
|
pycityagent/message/messager.py,sha256=W_OVlNGcreHSBf6v-DrEnfNCXExB78ySr0w26MSncfU,2541
|
48
|
+
pycityagent/metrics/__init__.py,sha256=IrlwXs_733b3PlMV99Ogh6R8sGCEalF2qIT2mQwdPjU,75
|
49
|
+
pycityagent/metrics/mlflow_client.py,sha256=BH-YVMlYr-eTFdrCZjgMZWi8ptUEm7todOXCpYixAgU,3311
|
50
|
+
pycityagent/metrics/utils/const.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
48
51
|
pycityagent/simulation/__init__.py,sha256=jYaqaNpzM5M_e_ykISS_M-mIyYdzJXJWhgpfBpA6l5k,111
|
49
|
-
pycityagent/simulation/agentgroup.py,sha256=
|
50
|
-
pycityagent/simulation/simulation.py,sha256=
|
52
|
+
pycityagent/simulation/agentgroup.py,sha256=H2E_YQ3ir3gQmPWsupHXA_LSBVkffzkXWl4UgZ9qLOc,13327
|
53
|
+
pycityagent/simulation/simulation.py,sha256=ANf04GqaK7gLT50F_ZcP2UcpS_uGx12pLqkrK6eA2L8,21014
|
51
54
|
pycityagent/survey/__init__.py,sha256=rxwou8U9KeFSP7rMzXtmtp2fVFZxK4Trzi-psx9LPIs,153
|
52
55
|
pycityagent/survey/manager.py,sha256=S5IkwTdelsdtZETChRcfCEczzwSrry_Fly9MY4s3rbk,1681
|
53
56
|
pycityagent/survey/models.py,sha256=YE50UUt5qJ0O_lIUsSY6XFCGUTkJVNu_L1gAhaCJ2fs,3546
|
@@ -59,11 +62,11 @@ pycityagent/utils/parsers/code_block_parser.py,sha256=Cs2Z_hm9VfNCpPPll1TwteaJF-
|
|
59
62
|
pycityagent/utils/parsers/json_parser.py,sha256=FZ3XN1g8z4Dr2TFraUOoah1oQcze4fPd2m01hHoX0Mo,2917
|
60
63
|
pycityagent/utils/parsers/parser_base.py,sha256=k6DVqwAMK3jJdOP4IeLE-aFPm3V2F-St5qRBuRdx4aU,1742
|
61
64
|
pycityagent/utils/survey_util.py,sha256=Be9nptmu2JtesFNemPgORh_2GsN7rcDYGQS9Zfvc5OI,2169
|
62
|
-
pycityagent/workflow/__init__.py,sha256=
|
63
|
-
pycityagent/workflow/block.py,sha256=
|
65
|
+
pycityagent/workflow/__init__.py,sha256=QNkUV-9mACMrR8c0cSKna2gC1mMZdxXbxWzjE-Uods0,621
|
66
|
+
pycityagent/workflow/block.py,sha256=WkE2On97DCZS_9n8aIgT8wxv9Oaff4Fdf2tLqbKfMtE,6010
|
64
67
|
pycityagent/workflow/prompt.py,sha256=tY69nDO8fgYfF_dOA-iceR8pAhkYmCqoox8uRPqEuGY,2956
|
65
|
-
pycityagent/workflow/tool.py,sha256=
|
68
|
+
pycityagent/workflow/tool.py,sha256=uaB0dV35jA9v2UqWw9L8iPM-HJW5e9BlgFVgOMf9jvw,8201
|
66
69
|
pycityagent/workflow/trigger.py,sha256=t5X_i0WtL32bipZSsq_E3UUyYYudYLxQUpvxbgClp2s,5683
|
67
|
-
pycityagent-2.0.
|
68
|
-
pycityagent-2.0.
|
69
|
-
pycityagent-2.0.
|
70
|
+
pycityagent-2.0.0a19.dist-info/METADATA,sha256=x0DbwNQ4Zj0rswWGIv6MA0zSBBEiExXLIdErYA0bhnE,7800
|
71
|
+
pycityagent-2.0.0a19.dist-info/WHEEL,sha256=Nq82e9rUAnEjt98J6MlVmMCZb-t9cYE2Ir1kpBmnWfs,88
|
72
|
+
pycityagent-2.0.0a19.dist-info/RECORD,,
|
File without changes
|