pycityagent 2.0.0a12__py3-none-any.whl → 2.0.0a14__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 +171 -52
- pycityagent/economy/econ_client.py +2 -0
- pycityagent/memory/const.py +1 -0
- pycityagent/simulation/agentgroup.py +78 -42
- pycityagent/simulation/simulation.py +99 -8
- pycityagent/survey/manager.py +58 -0
- pycityagent/survey/models.py +120 -0
- pycityagent/utils/__init__.py +7 -0
- pycityagent/utils/avro_schema.py +85 -0
- pycityagent/utils/survey_util.py +53 -0
- {pycityagent-2.0.0a12.dist-info → pycityagent-2.0.0a14.dist-info}/METADATA +3 -1
- {pycityagent-2.0.0a12.dist-info → pycityagent-2.0.0a14.dist-info}/RECORD +14 -15
- pycityagent/simulation/interview.py +0 -40
- pycityagent/simulation/survey/manager.py +0 -68
- pycityagent/simulation/survey/models.py +0 -52
- pycityagent/simulation/ui/__init__.py +0 -3
- pycityagent/simulation/ui/interface.py +0 -602
- /pycityagent/{simulation/survey → survey}/__init__.py +0 -0
- {pycityagent-2.0.0a12.dist-info → pycityagent-2.0.0a14.dist-info}/WHEEL +0 -0
@@ -1,18 +1,21 @@
|
|
1
1
|
import asyncio
|
2
2
|
import json
|
3
3
|
import logging
|
4
|
+
import os
|
5
|
+
from pathlib import Path
|
4
6
|
import uuid
|
5
7
|
from datetime import datetime
|
6
8
|
import random
|
7
9
|
from typing import Dict, List, Optional, Callable, Union,Any
|
10
|
+
import fastavro
|
8
11
|
from mosstool.map._map_util.const import AOI_START_ID
|
9
12
|
import pycityproto.city.economy.v2.economy_pb2 as economyv2
|
10
13
|
from pycityagent.memory.memory import Memory
|
14
|
+
from pycityagent.message.messager import Messager
|
15
|
+
from pycityagent.survey import Survey
|
16
|
+
from pycityagent.utils.avro_schema import PROFILE_SCHEMA, DIALOG_SCHEMA, STATUS_SCHEMA, SURVEY_SCHEMA
|
11
17
|
|
12
18
|
from ..agent import Agent, InstitutionAgent
|
13
|
-
from .interview import InterviewManager
|
14
|
-
from .survey import QuestionType, SurveyManager
|
15
|
-
from .ui import InterviewUI
|
16
19
|
from .agentgroup import AgentGroup
|
17
20
|
|
18
21
|
logger = logging.getLogger(__name__)
|
@@ -44,10 +47,34 @@ class AgentSimulation:
|
|
44
47
|
self._groups: Dict[str, AgentGroup] = {}
|
45
48
|
self._agent_uuid2group: Dict[uuid.UUID, AgentGroup] = {}
|
46
49
|
self._agent_uuids: List[uuid.UUID] = []
|
47
|
-
|
50
|
+
self._user_chat_topics: Dict[uuid.UUID, str] = {}
|
51
|
+
self._user_survey_topics: Dict[uuid.UUID, str] = {}
|
52
|
+
self._user_interview_topics: Dict[uuid.UUID, str] = {}
|
48
53
|
self._loop = asyncio.get_event_loop()
|
49
|
-
|
50
|
-
self.
|
54
|
+
|
55
|
+
self._messager = Messager(
|
56
|
+
hostname=config["simulator_request"]["mqtt"]["server"],
|
57
|
+
port=config["simulator_request"]["mqtt"]["port"],
|
58
|
+
username=config["simulator_request"]["mqtt"].get("username", None),
|
59
|
+
password=config["simulator_request"]["mqtt"].get("password", None),
|
60
|
+
)
|
61
|
+
asyncio.create_task(self._messager.connect())
|
62
|
+
|
63
|
+
self._enable_avro = config["storage"]["avro"]["enabled"]
|
64
|
+
self._avro_path = Path(config["storage"]["avro"]["path"])
|
65
|
+
self._avro_file = {
|
66
|
+
"profile": self._avro_path / f"{self.exp_id}_profile.avro",
|
67
|
+
"dialog": self._avro_path / f"{self.exp_id}_dialog.avro",
|
68
|
+
"status": self._avro_path / f"{self.exp_id}_status.avro",
|
69
|
+
"survey": self._avro_path / f"{self.exp_id}_survey.avro",
|
70
|
+
}
|
71
|
+
|
72
|
+
self._enable_pgsql = config["storage"]["pgsql"]["enabled"]
|
73
|
+
self._pgsql_host = config["storage"]["pgsql"]["host"]
|
74
|
+
self._pgsql_port = config["storage"]["pgsql"]["port"]
|
75
|
+
self._pgsql_database = config["storage"]["pgsql"]["database"]
|
76
|
+
self._pgsql_user = config["storage"]["pgsql"]["user"]
|
77
|
+
self._pgsql_password = config["storage"]["pgsql"]["password"]
|
51
78
|
|
52
79
|
@property
|
53
80
|
def agents(self):
|
@@ -126,6 +153,7 @@ class AgentSimulation:
|
|
126
153
|
agent = agent_class(
|
127
154
|
name=agent_name,
|
128
155
|
memory=memory,
|
156
|
+
avro_file=self._avro_file,
|
129
157
|
)
|
130
158
|
|
131
159
|
self._agents[agent._uuid] = agent
|
@@ -145,7 +173,7 @@ class AgentSimulation:
|
|
145
173
|
# 获取当前组的agents
|
146
174
|
agents = list(self._agents.values())[start_idx:end_idx]
|
147
175
|
group_name = f"AgentType_{i}_Group_{k}"
|
148
|
-
group = AgentGroup.remote(agents, self.config, self.exp_id)
|
176
|
+
group = AgentGroup.remote(agents, self.config, self.exp_id, self._avro_file)
|
149
177
|
self._groups[group_name] = group
|
150
178
|
for agent in agents:
|
151
179
|
self._agent_uuid2group[agent._uuid] = group
|
@@ -156,6 +184,42 @@ class AgentSimulation:
|
|
156
184
|
for group in self._groups.values():
|
157
185
|
init_tasks.append(group.init_agents.remote())
|
158
186
|
await asyncio.gather(*init_tasks)
|
187
|
+
for uuid, agent in self._agents.items():
|
188
|
+
self._user_chat_topics[uuid] = f"exps/{self.exp_id}/agents/{uuid}/user-chat"
|
189
|
+
self._user_survey_topics[uuid] = f"exps/{self.exp_id}/agents/{uuid}/user-survey"
|
190
|
+
|
191
|
+
# save profile
|
192
|
+
if self._enable_avro:
|
193
|
+
self._avro_path.mkdir(parents=True, exist_ok=True)
|
194
|
+
# profile
|
195
|
+
filename = self._avro_file["profile"]
|
196
|
+
with open(filename, "wb") as f:
|
197
|
+
profiles = []
|
198
|
+
for agent in self._agents.values():
|
199
|
+
profile = await agent.memory._profile.export()
|
200
|
+
profile = profile[0]
|
201
|
+
profile['id'] = str(agent._uuid)
|
202
|
+
profiles.append(profile)
|
203
|
+
fastavro.writer(f, PROFILE_SCHEMA, profiles)
|
204
|
+
|
205
|
+
# dialog
|
206
|
+
filename = self._avro_file["dialog"]
|
207
|
+
with open(filename, "wb") as f:
|
208
|
+
dialogs = []
|
209
|
+
fastavro.writer(f, DIALOG_SCHEMA, dialogs)
|
210
|
+
|
211
|
+
# status
|
212
|
+
filename = self._avro_file["status"]
|
213
|
+
with open(filename, "wb") as f:
|
214
|
+
statuses = []
|
215
|
+
fastavro.writer(f, STATUS_SCHEMA, statuses)
|
216
|
+
|
217
|
+
# survey
|
218
|
+
filename = self._avro_file["survey"]
|
219
|
+
with open(filename, "wb") as f:
|
220
|
+
surveys = []
|
221
|
+
fastavro.writer(f, SURVEY_SCHEMA, surveys)
|
222
|
+
|
159
223
|
|
160
224
|
async def gather(self, content: str):
|
161
225
|
"""收集智能体的特定信息"""
|
@@ -219,6 +283,7 @@ class AgentSimulation:
|
|
219
283
|
}
|
220
284
|
|
221
285
|
PROFILE = {
|
286
|
+
"name": "unknown",
|
222
287
|
"gender": random.choice(["male", "female"]),
|
223
288
|
"education": random.choice(
|
224
289
|
["Doctor", "Master", "Bachelor", "College", "High School"]
|
@@ -251,7 +316,7 @@ class AgentSimulation:
|
|
251
316
|
"personality": random.choice(
|
252
317
|
["outgoint", "introvert", "ambivert", "extrovert"]
|
253
318
|
),
|
254
|
-
"income": random.randint(1000, 10000),
|
319
|
+
"income": str(random.randint(1000, 10000)),
|
255
320
|
"currency": random.randint(10000, 100000),
|
256
321
|
"residence": random.choice(["city", "suburb", "rural"]),
|
257
322
|
"race": random.choice(
|
@@ -285,6 +350,32 @@ class AgentSimulation:
|
|
285
350
|
}
|
286
351
|
|
287
352
|
return EXTRA_ATTRIBUTES, PROFILE, BASE
|
353
|
+
|
354
|
+
async def send_survey(self, survey: Survey, agent_uuids: Optional[List[uuid.UUID]] = None):
|
355
|
+
"""发送问卷"""
|
356
|
+
survey = survey.to_dict()
|
357
|
+
if agent_uuids is None:
|
358
|
+
agent_uuids = self._agent_uuids
|
359
|
+
payload = {
|
360
|
+
"from": "none",
|
361
|
+
"survey_id": survey["id"],
|
362
|
+
"timestamp": int(datetime.now().timestamp() * 1000),
|
363
|
+
"data": survey,
|
364
|
+
}
|
365
|
+
for uuid in agent_uuids:
|
366
|
+
topic = self._user_survey_topics[uuid]
|
367
|
+
await self._messager.send_message(topic, payload)
|
368
|
+
|
369
|
+
async def send_interview_message(self, content: str, agent_uuids: Union[uuid.UUID, List[uuid.UUID]]):
|
370
|
+
"""发送面试消息"""
|
371
|
+
payload = {
|
372
|
+
"from": "none",
|
373
|
+
"content": content,
|
374
|
+
"timestamp": int(datetime.now().timestamp() * 1000),
|
375
|
+
}
|
376
|
+
for uuid in agent_uuids:
|
377
|
+
topic = self._user_chat_topics[uuid]
|
378
|
+
await self._messager.send_message(topic, payload)
|
288
379
|
|
289
380
|
async def step(self):
|
290
381
|
"""运行一步, 即每个智能体执行一次forward"""
|
@@ -0,0 +1,58 @@
|
|
1
|
+
from typing import List, Dict, Optional
|
2
|
+
from datetime import datetime
|
3
|
+
import uuid
|
4
|
+
import json
|
5
|
+
from .models import Survey, Question, QuestionType, Page
|
6
|
+
|
7
|
+
|
8
|
+
class SurveyManager:
|
9
|
+
def __init__(self):
|
10
|
+
self._surveys: Dict[str, Survey] = {}
|
11
|
+
|
12
|
+
def create_survey(
|
13
|
+
self, title: str, description: str, pages: List[dict]
|
14
|
+
) -> Survey:
|
15
|
+
"""创建新问卷"""
|
16
|
+
survey_id = uuid.uuid4()
|
17
|
+
|
18
|
+
# 转换页面和问题数据
|
19
|
+
survey_pages = []
|
20
|
+
for page_data in pages:
|
21
|
+
questions = []
|
22
|
+
for q in page_data["elements"]:
|
23
|
+
question = Question(
|
24
|
+
name=q["name"],
|
25
|
+
title=q["title"],
|
26
|
+
type=QuestionType(q["type"]),
|
27
|
+
required=q.get("required", True),
|
28
|
+
choices=q.get("choices", []),
|
29
|
+
columns=q.get("columns", []),
|
30
|
+
rows=q.get("rows", []),
|
31
|
+
min_rating=q.get("min_rating", 1),
|
32
|
+
max_rating=q.get("max_rating", 5),
|
33
|
+
)
|
34
|
+
questions.append(question)
|
35
|
+
|
36
|
+
page = Page(
|
37
|
+
name=page_data["name"],
|
38
|
+
elements=questions
|
39
|
+
)
|
40
|
+
survey_pages.append(page)
|
41
|
+
|
42
|
+
survey = Survey(
|
43
|
+
id=survey_id,
|
44
|
+
title=title,
|
45
|
+
description=description,
|
46
|
+
pages=survey_pages,
|
47
|
+
)
|
48
|
+
|
49
|
+
self._surveys[str(survey_id)] = survey
|
50
|
+
return survey
|
51
|
+
|
52
|
+
def get_survey(self, survey_id: str) -> Optional[Survey]:
|
53
|
+
"""获取指定问卷"""
|
54
|
+
return self._surveys.get(survey_id)
|
55
|
+
|
56
|
+
def get_all_surveys(self) -> List[Survey]:
|
57
|
+
"""获取所有问卷"""
|
58
|
+
return list(self._surveys.values())
|
@@ -0,0 +1,120 @@
|
|
1
|
+
from dataclasses import dataclass, field
|
2
|
+
from typing import List, Dict, Optional
|
3
|
+
from datetime import datetime
|
4
|
+
from enum import Enum
|
5
|
+
import uuid
|
6
|
+
import json
|
7
|
+
|
8
|
+
|
9
|
+
class QuestionType(Enum):
|
10
|
+
TEXT = "text"
|
11
|
+
RADIO = "radiogroup"
|
12
|
+
CHECKBOX = "checkbox"
|
13
|
+
BOOLEAN = "boolean"
|
14
|
+
RATING = "rating"
|
15
|
+
MATRIX = "matrix"
|
16
|
+
|
17
|
+
|
18
|
+
@dataclass
|
19
|
+
class Question:
|
20
|
+
name: str
|
21
|
+
title: str
|
22
|
+
type: QuestionType
|
23
|
+
choices: List[str] = field(default_factory=list)
|
24
|
+
columns: List[str] = field(default_factory=list)
|
25
|
+
rows: List[str] = field(default_factory=list)
|
26
|
+
min_rating: int = 1
|
27
|
+
max_rating: int = 5
|
28
|
+
|
29
|
+
def to_dict(self) -> dict:
|
30
|
+
base_dict = {
|
31
|
+
"type": self.type.value,
|
32
|
+
"name": self.name,
|
33
|
+
"title": self.title,
|
34
|
+
}
|
35
|
+
|
36
|
+
if self.type in [QuestionType.RADIO, QuestionType.CHECKBOX]:
|
37
|
+
base_dict["choices"] = self.choices
|
38
|
+
elif self.type == QuestionType.MATRIX:
|
39
|
+
base_dict["columns"] = self.columns
|
40
|
+
base_dict["rows"] = self.rows
|
41
|
+
elif self.type == QuestionType.RATING:
|
42
|
+
base_dict["min_rating"] = self.min_rating
|
43
|
+
base_dict["max_rating"] = self.max_rating
|
44
|
+
|
45
|
+
return base_dict
|
46
|
+
|
47
|
+
|
48
|
+
@dataclass
|
49
|
+
class Page:
|
50
|
+
name: str
|
51
|
+
elements: List[Question]
|
52
|
+
|
53
|
+
def to_dict(self) -> dict:
|
54
|
+
return {
|
55
|
+
"name": self.name,
|
56
|
+
"elements": [q.to_dict() for q in self.elements]
|
57
|
+
}
|
58
|
+
|
59
|
+
|
60
|
+
@dataclass
|
61
|
+
class Survey:
|
62
|
+
id: uuid.UUID
|
63
|
+
title: str
|
64
|
+
description: str
|
65
|
+
pages: List[Page]
|
66
|
+
responses: Dict[str, dict] = field(default_factory=dict)
|
67
|
+
created_at: datetime = field(default_factory=datetime.now)
|
68
|
+
|
69
|
+
def to_dict(self) -> dict:
|
70
|
+
return {
|
71
|
+
"id": str(self.id),
|
72
|
+
"title": self.title,
|
73
|
+
"description": self.description,
|
74
|
+
"pages": [p.to_dict() for p in self.pages],
|
75
|
+
"response_count": len(self.responses),
|
76
|
+
}
|
77
|
+
|
78
|
+
def to_json(self) -> str:
|
79
|
+
"""Convert the survey to a JSON string for MQTT transmission"""
|
80
|
+
survey_dict = {
|
81
|
+
"id": str(self.id),
|
82
|
+
"title": self.title,
|
83
|
+
"description": self.description,
|
84
|
+
"pages": [p.to_dict() for p in self.pages],
|
85
|
+
"responses": self.responses,
|
86
|
+
"created_at": self.created_at.isoformat()
|
87
|
+
}
|
88
|
+
return json.dumps(survey_dict)
|
89
|
+
|
90
|
+
@classmethod
|
91
|
+
def from_json(cls, json_str: str) -> 'Survey':
|
92
|
+
"""Create a Survey instance from a JSON string"""
|
93
|
+
data = json.loads(json_str)
|
94
|
+
pages = [
|
95
|
+
Page(
|
96
|
+
name=p["name"],
|
97
|
+
elements=[
|
98
|
+
Question(
|
99
|
+
name=q["name"],
|
100
|
+
title=q["title"],
|
101
|
+
type=QuestionType(q["type"]),
|
102
|
+
required=q.get("required", True),
|
103
|
+
choices=q.get("choices", []),
|
104
|
+
columns=q.get("columns", []),
|
105
|
+
rows=q.get("rows", []),
|
106
|
+
min_rating=q.get("min_rating", 1),
|
107
|
+
max_rating=q.get("max_rating", 5)
|
108
|
+
) for q in p["elements"]
|
109
|
+
]
|
110
|
+
) for p in data["pages"]
|
111
|
+
]
|
112
|
+
|
113
|
+
return cls(
|
114
|
+
id=uuid.UUID(data["id"]),
|
115
|
+
title=data["title"],
|
116
|
+
description=data["description"],
|
117
|
+
pages=pages,
|
118
|
+
responses=data.get("responses", {}),
|
119
|
+
created_at=datetime.fromisoformat(data["created_at"])
|
120
|
+
)
|
pycityagent/utils/__init__.py
CHANGED
@@ -0,0 +1,85 @@
|
|
1
|
+
PROFILE_SCHEMA = {
|
2
|
+
"doc": "Agent属性",
|
3
|
+
"name": "AgentProfile",
|
4
|
+
"namespace": "com.socialcity",
|
5
|
+
"type": "record",
|
6
|
+
"fields": [
|
7
|
+
{"name": "id", "type": "string"}, # uuid as string
|
8
|
+
{"name": "name", "type": "string"},
|
9
|
+
{"name": "gender", "type": "string"},
|
10
|
+
{"name": "age", "type": "float"},
|
11
|
+
{"name": "education", "type": "string"},
|
12
|
+
{"name": "skill", "type": "string"},
|
13
|
+
{"name": "occupation", "type": "string"},
|
14
|
+
{"name": "family_consumption", "type": "string"},
|
15
|
+
{"name": "consumption", "type": "string"},
|
16
|
+
{"name": "personality", "type": "string"},
|
17
|
+
{"name": "income", "type": "string"},
|
18
|
+
{"name": "currency", "type": "float"},
|
19
|
+
{"name": "residence", "type": "string"},
|
20
|
+
{"name": "race", "type": "string"},
|
21
|
+
{"name": "religion", "type": "string"},
|
22
|
+
{"name": "marital_status", "type": "string"},
|
23
|
+
],
|
24
|
+
}
|
25
|
+
|
26
|
+
DIALOG_SCHEMA = {
|
27
|
+
"doc": "Agent对话",
|
28
|
+
"name": "AgentDialog",
|
29
|
+
"namespace": "com.socialcity",
|
30
|
+
"type": "record",
|
31
|
+
"fields": [
|
32
|
+
{"name": "id", "type": "string"}, # uuid as string
|
33
|
+
{"name": "day", "type": "int"},
|
34
|
+
{"name": "t", "type": "float"},
|
35
|
+
{"name": "type", "type": "int"},
|
36
|
+
{"name": "speaker", "type": "string"},
|
37
|
+
{"name": "content", "type": "string"},
|
38
|
+
{
|
39
|
+
"name": "created_at",
|
40
|
+
"type": {"type": "long", "logicalType": "timestamp-millis"},
|
41
|
+
},
|
42
|
+
],
|
43
|
+
}
|
44
|
+
|
45
|
+
STATUS_SCHEMA = {
|
46
|
+
"doc": "Agent状态",
|
47
|
+
"name": "AgentStatus",
|
48
|
+
"namespace": "com.socialcity",
|
49
|
+
"type": "record",
|
50
|
+
"fields": [
|
51
|
+
{"name": "id", "type": "string"}, # uuid as string
|
52
|
+
{"name": "day", "type": "int"},
|
53
|
+
{"name": "t", "type": "float"},
|
54
|
+
{"name": "lng", "type": "double"},
|
55
|
+
{"name": "lat", "type": "double"},
|
56
|
+
{"name": "parent_id", "type": "int"},
|
57
|
+
{"name": "action", "type": "string"},
|
58
|
+
{"name": "hungry", "type": "float"},
|
59
|
+
{"name": "tired", "type": "float"},
|
60
|
+
{"name": "safe", "type": "float"},
|
61
|
+
{"name": "social", "type": "float"},
|
62
|
+
{
|
63
|
+
"name": "created_at",
|
64
|
+
"type": {"type": "long", "logicalType": "timestamp-millis"},
|
65
|
+
},
|
66
|
+
],
|
67
|
+
}
|
68
|
+
|
69
|
+
SURVEY_SCHEMA = {
|
70
|
+
"doc": "Agent问卷",
|
71
|
+
"name": "AgentSurvey",
|
72
|
+
"namespace": "com.socialcity",
|
73
|
+
"type": "record",
|
74
|
+
"fields": [
|
75
|
+
{"name": "id", "type": "string"}, # uuid as string
|
76
|
+
{"name": "day", "type": "int"},
|
77
|
+
{"name": "t", "type": "float"},
|
78
|
+
{"name": "survey_id", "type": "string"},
|
79
|
+
{"name": "result", "type": "string"},
|
80
|
+
{
|
81
|
+
"name": "created_at",
|
82
|
+
"type": {"type": "long", "logicalType": "timestamp-millis"},
|
83
|
+
},
|
84
|
+
],
|
85
|
+
}
|
@@ -0,0 +1,53 @@
|
|
1
|
+
def process_survey_for_llm(survey_dict: dict) -> str:
|
2
|
+
"""
|
3
|
+
将问卷字典转换为LLM可以逐题处理的格式,使用英文提示
|
4
|
+
"""
|
5
|
+
prompt = f"""Survey Title: {survey_dict['title']}
|
6
|
+
Survey Description: {survey_dict['description']}
|
7
|
+
|
8
|
+
Please answer each question in the following format:
|
9
|
+
|
10
|
+
"""
|
11
|
+
|
12
|
+
question_count = 1
|
13
|
+
for page in survey_dict['pages']:
|
14
|
+
for question in page['elements']:
|
15
|
+
prompt += f"Question {question_count}: {question['title']}\n"
|
16
|
+
|
17
|
+
# 根据不同类型的问题生成不同的提示
|
18
|
+
if question['type'] == 'radiogroup':
|
19
|
+
prompt += "Options: " + ", ".join(question['choices']) + "\n"
|
20
|
+
prompt += "Please select ONE option\n"
|
21
|
+
|
22
|
+
elif question['type'] == 'checkbox':
|
23
|
+
prompt += "Options: " + ", ".join(question['choices']) + "\n"
|
24
|
+
prompt += "You can select MULTIPLE options\n"
|
25
|
+
|
26
|
+
elif question['type'] == 'rating':
|
27
|
+
prompt += f"Rating range: {question.get('min_rating', 1)} - {question.get('max_rating', 5)}\n"
|
28
|
+
prompt += "Please provide a rating within the range\n"
|
29
|
+
|
30
|
+
elif question['type'] == 'matrix':
|
31
|
+
prompt += "Rows: " + ", ".join(question['rows']) + "\n"
|
32
|
+
prompt += "Columns: " + ", ".join(question['columns']) + "\n"
|
33
|
+
prompt += "Please select ONE column option for EACH row\n"
|
34
|
+
|
35
|
+
elif question['type'] == 'text':
|
36
|
+
prompt += "Please provide a text response\n"
|
37
|
+
|
38
|
+
elif question['type'] == 'boolean':
|
39
|
+
prompt += "Options: Yes, No\n"
|
40
|
+
prompt += "Please select either Yes or No\n"
|
41
|
+
|
42
|
+
prompt += "\nAnswer: [Your response here]\n\n---\n\n"
|
43
|
+
question_count += 1
|
44
|
+
|
45
|
+
# 添加总结提示
|
46
|
+
prompt += """Please ensure:
|
47
|
+
1. All required questions are answered
|
48
|
+
2. Responses match the question type requirements
|
49
|
+
3. Answers are clear and specific
|
50
|
+
|
51
|
+
Format your responses exactly as requested above."""
|
52
|
+
|
53
|
+
return prompt
|
@@ -1,6 +1,6 @@
|
|
1
1
|
Metadata-Version: 2.1
|
2
2
|
Name: pycityagent
|
3
|
-
Version: 2.0.
|
3
|
+
Version: 2.0.0a14
|
4
4
|
Summary: LLM-based城市环境agent构建库
|
5
5
|
License: MIT
|
6
6
|
Author: Yuwei Yan
|
@@ -20,6 +20,7 @@ Requires-Dist: aiohttp (==3.10.10)
|
|
20
20
|
Requires-Dist: aiomqtt (>=2.3.0,<3.0.0)
|
21
21
|
Requires-Dist: citystreetview (==1.2.4)
|
22
22
|
Requires-Dist: dashscope (==1.14.1)
|
23
|
+
Requires-Dist: fastavro (>=1.10.0,<2.0.0)
|
23
24
|
Requires-Dist: geojson (==3.1.0)
|
24
25
|
Requires-Dist: gradio (>=5.7.1,<6.0.0)
|
25
26
|
Requires-Dist: grpcio (==1.67.1)
|
@@ -28,6 +29,7 @@ Requires-Dist: mosstool (==1.0.24)
|
|
28
29
|
Requires-Dist: networkx (==3.2.1)
|
29
30
|
Requires-Dist: numpy (>=1.20.0,<2.0.0)
|
30
31
|
Requires-Dist: openai (>=1.58.1,<2.0.0)
|
32
|
+
Requires-Dist: pandavro (>=1.8.0,<2.0.0)
|
31
33
|
Requires-Dist: poetry (>=1.2.2)
|
32
34
|
Requires-Dist: protobuf (<=4.24.0)
|
33
35
|
Requires-Dist: pycitydata (==1.0.0)
|
@@ -1,7 +1,7 @@
|
|
1
1
|
pycityagent/__init__.py,sha256=n56bWkAUEcvjDsb7LcJpaGjlrriSKPnR0yBhwRfEYBA,212
|
2
|
-
pycityagent/agent.py,sha256=
|
2
|
+
pycityagent/agent.py,sha256=0ZqHXImR05ETA0vt9t5GDS4AgYzza3-Zwuua2OckwTw,22788
|
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=wcuNtcpkSijJwNkt2mXw3SshYy4SBy6qbvJ0VQ7Aovo,10854
|
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
|
@@ -36,7 +36,7 @@ pycityagent/llm/llm.py,sha256=BtxBvPK4tb8QlZIfxO5XJ73lKXwF8L31LqVbejWB8eo,15121
|
|
36
36
|
pycityagent/llm/llmconfig.py,sha256=4Ylf4OFSBEFy8jrOneeX0HvPhWEaF5jGvy1HkXK08Ro,436
|
37
37
|
pycityagent/llm/utils.py,sha256=hoNPhvomb1u6lhFX0GctFipw74hVKb7bvUBDqwBzBYw,160
|
38
38
|
pycityagent/memory/__init__.py,sha256=Hs2NhYpIG-lvpwPWwj4DydB1sxtjz7cuA4iDAzCXnjI,243
|
39
|
-
pycityagent/memory/const.py,sha256=
|
39
|
+
pycityagent/memory/const.py,sha256=6zpJPJXWoH9-yf4RARYYff586agCoud9BRn7sPERB1g,932
|
40
40
|
pycityagent/memory/memory.py,sha256=sDbaqr1Koqf_9joMtG9PmmVxJZ6Rq7nAZO6EO0OdVgo,18148
|
41
41
|
pycityagent/memory/memory_base.py,sha256=bd2q0qNu5hCRd2u4cPxE3bBA2OaoAD1oR4-vbRdbd_s,5594
|
42
42
|
pycityagent/memory/profile.py,sha256=s4LnxSPGSjIGZXHXkkd8mMa6uYYZrytgyQdWjcaqGf4,5182
|
@@ -46,25 +46,24 @@ pycityagent/memory/utils.py,sha256=wLNlNlZ-AY9VB8kbUIy0UQSYh26FOQABbhmKQkit5o8,8
|
|
46
46
|
pycityagent/message/__init__.py,sha256=TCjazxqb5DVwbTu1fF0sNvaH_EPXVuj2XQ0p6W-QCLU,55
|
47
47
|
pycityagent/message/messager.py,sha256=Iv4pK83JvHAQSZyGNACryPBey2wRoiok3Hb1eIwHbww,2506
|
48
48
|
pycityagent/simulation/__init__.py,sha256=jYaqaNpzM5M_e_ykISS_M-mIyYdzJXJWhgpfBpA6l5k,111
|
49
|
-
pycityagent/simulation/agentgroup.py,sha256=
|
50
|
-
pycityagent/simulation/
|
51
|
-
pycityagent/
|
52
|
-
pycityagent/
|
53
|
-
pycityagent/
|
54
|
-
pycityagent/
|
55
|
-
pycityagent/
|
56
|
-
pycityagent/simulation/ui/interface.py,sha256=cSQRQhuA5Gj-X7anuN9tmRpG6-I8QYOuswjIWUeSvio,21636
|
57
|
-
pycityagent/utils/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
49
|
+
pycityagent/simulation/agentgroup.py,sha256=ZJIeQbFmFwK3iUiwUpa4TG-k1Eb4vOTgy5ybv-CE1Rc,7797
|
50
|
+
pycityagent/simulation/simulation.py,sha256=rDQRgKmJkjfeQtvXyW9LeqBVVX0iFh6snBEsdg1bCag,15574
|
51
|
+
pycityagent/survey/__init__.py,sha256=rxwou8U9KeFSP7rMzXtmtp2fVFZxK4Trzi-psx9LPIs,153
|
52
|
+
pycityagent/survey/manager.py,sha256=N4-Q8vve4L-PLaFikAgVB4Z8BNyFDEd6WjEk3AyMTgs,1764
|
53
|
+
pycityagent/survey/models.py,sha256=Z2gHEazQRj0TkTz5qbh4Uy_JrU_FZGWpOLwjN0RoUrY,3547
|
54
|
+
pycityagent/utils/__init__.py,sha256=_0AV01UAHp4pTj2mlJ4m8LTH0toWL9j3DouNC6sDuUQ,249
|
55
|
+
pycityagent/utils/avro_schema.py,sha256=MsRG0CsYAn2UjSlSXgCF8-3076VywpALEmz1mW-UJB0,2789
|
58
56
|
pycityagent/utils/decorators.py,sha256=Gk3r41hfk6awui40tbwpq3C7wC7jHaRmLRlcJFlLQCE,3160
|
59
57
|
pycityagent/utils/parsers/__init__.py,sha256=AN2xgiPxszWK4rpX7zrqRsqNwfGF3WnCA5-PFTvbaKk,281
|
60
58
|
pycityagent/utils/parsers/code_block_parser.py,sha256=Cs2Z_hm9VfNCpPPll1TwteaJF-HAQPs-3RApsOekFm4,1173
|
61
59
|
pycityagent/utils/parsers/json_parser.py,sha256=FZ3XN1g8z4Dr2TFraUOoah1oQcze4fPd2m01hHoX0Mo,2917
|
62
60
|
pycityagent/utils/parsers/parser_base.py,sha256=k6DVqwAMK3jJdOP4IeLE-aFPm3V2F-St5qRBuRdx4aU,1742
|
61
|
+
pycityagent/utils/survey_util.py,sha256=Be9nptmu2JtesFNemPgORh_2GsN7rcDYGQS9Zfvc5OI,2169
|
63
62
|
pycityagent/workflow/__init__.py,sha256=EyCcjB6LyBim-5iAOPe4m2qfvghEPqu1ZdGfy4KPeZ8,551
|
64
63
|
pycityagent/workflow/block.py,sha256=6EmiRMLdOZC1wMlmLMIjfrp9TuiI7Gw4s3nnXVMbrnw,6031
|
65
64
|
pycityagent/workflow/prompt.py,sha256=tY69nDO8fgYfF_dOA-iceR8pAhkYmCqoox8uRPqEuGY,2956
|
66
65
|
pycityagent/workflow/tool.py,sha256=wD9WZ5rma6HCKugtHTwbShNE0f-Rjlwvn_1be3fCAsk,6682
|
67
66
|
pycityagent/workflow/trigger.py,sha256=t5X_i0WtL32bipZSsq_E3UUyYYudYLxQUpvxbgClp2s,5683
|
68
|
-
pycityagent-2.0.
|
69
|
-
pycityagent-2.0.
|
70
|
-
pycityagent-2.0.
|
67
|
+
pycityagent-2.0.0a14.dist-info/METADATA,sha256=O6cGdAHKuG6W0Ya_Du_wEP8_xfM4xmOetYWb6rPj-WM,7705
|
68
|
+
pycityagent-2.0.0a14.dist-info/WHEEL,sha256=Nq82e9rUAnEjt98J6MlVmMCZb-t9cYE2Ir1kpBmnWfs,88
|
69
|
+
pycityagent-2.0.0a14.dist-info/RECORD,,
|
@@ -1,40 +0,0 @@
|
|
1
|
-
from dataclasses import dataclass
|
2
|
-
from datetime import datetime
|
3
|
-
from typing import List, Optional
|
4
|
-
|
5
|
-
|
6
|
-
@dataclass
|
7
|
-
class InterviewRecord:
|
8
|
-
"""采访记录"""
|
9
|
-
|
10
|
-
timestamp: datetime
|
11
|
-
agent_name: str
|
12
|
-
question: str
|
13
|
-
response: str
|
14
|
-
blocking: bool
|
15
|
-
|
16
|
-
|
17
|
-
class InterviewManager:
|
18
|
-
"""采访管理器"""
|
19
|
-
|
20
|
-
def __init__(self):
|
21
|
-
self._history: List[InterviewRecord] = []
|
22
|
-
|
23
|
-
def add_record(self, agent_name: str, question: str, response: str, blocking: bool):
|
24
|
-
"""添加采访记录"""
|
25
|
-
record = InterviewRecord(
|
26
|
-
timestamp=datetime.now(),
|
27
|
-
agent_name=agent_name,
|
28
|
-
question=question,
|
29
|
-
response=response,
|
30
|
-
blocking=blocking,
|
31
|
-
)
|
32
|
-
self._history.append(record)
|
33
|
-
|
34
|
-
def get_agent_history(self, agent_name: str) -> List[InterviewRecord]:
|
35
|
-
"""获取指定智能体的采访历史"""
|
36
|
-
return [r for r in self._history if r.agent_name == agent_name]
|
37
|
-
|
38
|
-
def get_recent_history(self, limit: int = 10) -> List[InterviewRecord]:
|
39
|
-
"""获取最近的采访记录"""
|
40
|
-
return sorted(self._history, key=lambda x: x.timestamp, reverse=True)[:limit]
|