pycityagent 2.0.0a13__py3-none-any.whl → 2.0.0a15__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/__init__.py +14 -0
- pycityagent/agent.py +164 -63
- pycityagent/economy/econ_client.py +2 -0
- pycityagent/environment/simulator.py +5 -4
- pycityagent/memory/const.py +1 -0
- pycityagent/memory/memory.py +8 -7
- pycityagent/memory/memory_base.py +6 -4
- pycityagent/message/messager.py +8 -7
- pycityagent/simulation/agentgroup.py +136 -14
- pycityagent/simulation/simulation.py +212 -42
- pycityagent/survey/manager.py +58 -0
- pycityagent/survey/models.py +120 -0
- pycityagent/utils/__init__.py +7 -0
- pycityagent/utils/avro_schema.py +110 -0
- pycityagent/utils/survey_util.py +53 -0
- pycityagent/workflow/tool.py +0 -3
- {pycityagent-2.0.0a13.dist-info → pycityagent-2.0.0a15.dist-info}/METADATA +3 -1
- {pycityagent-2.0.0a13.dist-info → pycityagent-2.0.0a15.dist-info}/RECORD +20 -21
- 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.0a13.dist-info → pycityagent-2.0.0a15.dist-info}/WHEEL +0 -0
@@ -1,602 +0,0 @@
|
|
1
|
-
import tempfile
|
2
|
-
import time
|
3
|
-
from typing import TYPE_CHECKING, Any, Awaitable, Callable
|
4
|
-
|
5
|
-
import gradio as gr
|
6
|
-
|
7
|
-
from ..survey.models import QuestionType
|
8
|
-
|
9
|
-
if TYPE_CHECKING:
|
10
|
-
from ..simulation import AgentSimulation
|
11
|
-
|
12
|
-
|
13
|
-
class InterviewUI:
|
14
|
-
"""模拟器界面"""
|
15
|
-
|
16
|
-
def __init__(self, simulation: "AgentSimulation"):
|
17
|
-
self.simulation = simulation
|
18
|
-
self.interface = None
|
19
|
-
|
20
|
-
def create_interface(self) -> gr.Blocks:
|
21
|
-
"""创建界面"""
|
22
|
-
with gr.Blocks(title="智能体模拟器", theme=gr.themes.Ocean()) as interface:
|
23
|
-
with gr.Tabs():
|
24
|
-
# 采访标签页
|
25
|
-
with gr.Tab("采访"):
|
26
|
-
self._create_interview_tab()
|
27
|
-
|
28
|
-
# 问卷中心标签页
|
29
|
-
with gr.Tab("问卷中心"):
|
30
|
-
self._create_survey_center_tab()
|
31
|
-
|
32
|
-
# 问卷标签页
|
33
|
-
with gr.Tab("问卷"):
|
34
|
-
self._create_survey_tab()
|
35
|
-
|
36
|
-
# 状态标签页
|
37
|
-
with gr.Tab("状态"):
|
38
|
-
self._create_status_tab()
|
39
|
-
|
40
|
-
# 设置启动参数
|
41
|
-
interface.launch_kwargs = {"quiet": True} # type:ignore
|
42
|
-
|
43
|
-
return interface
|
44
|
-
|
45
|
-
def _create_interview_tab(self):
|
46
|
-
"""创建采访标签页"""
|
47
|
-
# 上部:智能体选择和控制
|
48
|
-
with gr.Group():
|
49
|
-
with gr.Row():
|
50
|
-
agent_dropdown = gr.Dropdown(
|
51
|
-
choices=self._get_agent_names(),
|
52
|
-
label="选择智能体",
|
53
|
-
interactive=True,
|
54
|
-
)
|
55
|
-
blocking_checkbox = gr.Checkbox(
|
56
|
-
label="阻塞控制", value=False, interactive=True
|
57
|
-
)
|
58
|
-
refresh_btn = gr.Button("刷新智能体列表")
|
59
|
-
|
60
|
-
# 阻塞状态显示
|
61
|
-
block_status = gr.Textbox(label="阻塞状态", value="", interactive=False)
|
62
|
-
|
63
|
-
# 下部:对话区域
|
64
|
-
with gr.Group():
|
65
|
-
chatbot = gr.Chatbot(
|
66
|
-
label="对话历史", type="messages", height=400 # 设置固定高度
|
67
|
-
)
|
68
|
-
|
69
|
-
with gr.Row():
|
70
|
-
question = gr.Textbox(
|
71
|
-
label="提问", placeholder="请输入您的问题...", lines=2
|
72
|
-
)
|
73
|
-
send_btn = gr.Button("发送", variant="primary")
|
74
|
-
|
75
|
-
with gr.Row():
|
76
|
-
clear_btn = gr.Button("清空对话")
|
77
|
-
export_btn = gr.Button("导出对话")
|
78
|
-
|
79
|
-
# 导出对话文件组件
|
80
|
-
export_file = gr.File(label="导出的对话记录", visible=True)
|
81
|
-
|
82
|
-
# 事件处理
|
83
|
-
refresh_btn.click(self._get_agent_names, outputs=agent_dropdown)
|
84
|
-
|
85
|
-
def toggle_block(agent_name, blocking):
|
86
|
-
if not agent_name:
|
87
|
-
return "请先选择智能体"
|
88
|
-
status = self.simulation.toggle_agent_block(agent_name, blocking)
|
89
|
-
return status
|
90
|
-
|
91
|
-
blocking_checkbox.change(
|
92
|
-
toggle_block,
|
93
|
-
inputs=[agent_dropdown, blocking_checkbox],
|
94
|
-
outputs=[block_status],
|
95
|
-
)
|
96
|
-
|
97
|
-
def update_block_status(agent_name):
|
98
|
-
if not agent_name:
|
99
|
-
return False, ""
|
100
|
-
is_blocked = agent_name in self.simulation._blocked_agents
|
101
|
-
status = f"智能体 {agent_name} {'已阻塞' if is_blocked else '未阻塞'}"
|
102
|
-
return is_blocked, status
|
103
|
-
|
104
|
-
agent_dropdown.change(
|
105
|
-
update_block_status,
|
106
|
-
inputs=[agent_dropdown],
|
107
|
-
outputs=[blocking_checkbox, block_status],
|
108
|
-
)
|
109
|
-
|
110
|
-
send_btn.click(
|
111
|
-
self._handle_interview,
|
112
|
-
inputs=[chatbot, agent_dropdown, question],
|
113
|
-
outputs=[chatbot, question],
|
114
|
-
)
|
115
|
-
|
116
|
-
clear_btn.click(lambda: None, outputs=chatbot)
|
117
|
-
|
118
|
-
def export_chat(agent_name):
|
119
|
-
"""导出对话历史"""
|
120
|
-
if not agent_name:
|
121
|
-
return None
|
122
|
-
|
123
|
-
history = self.simulation.export_chat_history(agent_name)
|
124
|
-
# 创建临时文件并写入内容
|
125
|
-
with tempfile.NamedTemporaryFile(
|
126
|
-
mode="w", delete=False, suffix=".json", encoding="utf-8"
|
127
|
-
) as f:
|
128
|
-
f.write(history)
|
129
|
-
return f.name
|
130
|
-
|
131
|
-
export_btn.click(fn=export_chat, inputs=[agent_dropdown], outputs=[export_file])
|
132
|
-
|
133
|
-
def _get_survey_table(self) -> list | None:
|
134
|
-
"""获取问卷列表数据"""
|
135
|
-
surveys = self.simulation.get_surveys()
|
136
|
-
if not surveys:
|
137
|
-
return None
|
138
|
-
return [[s.id, s.title, len(s.questions)] for s in surveys]
|
139
|
-
|
140
|
-
def _get_survey_choices(self) -> list[str]:
|
141
|
-
"""获取问卷选择列表"""
|
142
|
-
surveys = self.simulation.get_surveys()
|
143
|
-
if not surveys:
|
144
|
-
return []
|
145
|
-
return [s.title for s in surveys]
|
146
|
-
|
147
|
-
def _create_survey_center_tab(self):
|
148
|
-
"""创建问卷中心标签页"""
|
149
|
-
# 状态控制
|
150
|
-
survey_editor_visible = gr.Checkbox(value=False, visible=False)
|
151
|
-
|
152
|
-
# 上部分:问卷列表展示
|
153
|
-
gr.Markdown("### 问卷列表")
|
154
|
-
with gr.Row():
|
155
|
-
refresh_btn = gr.Button("刷新列表")
|
156
|
-
new_survey_btn = gr.Button("新建问卷", variant="primary")
|
157
|
-
|
158
|
-
# 问卷列表展示
|
159
|
-
survey_table = gr.DataFrame(
|
160
|
-
value=self._get_survey_table(),
|
161
|
-
label="当前问卷列表",
|
162
|
-
headers=["ID", "标题", "问题数量"],
|
163
|
-
interactive=False,
|
164
|
-
)
|
165
|
-
|
166
|
-
# 问卷编辑区域
|
167
|
-
with gr.Column(visible=False) as survey_editor:
|
168
|
-
gr.Markdown("### 创建新问卷")
|
169
|
-
# 基本信息
|
170
|
-
survey_title = gr.Textbox(
|
171
|
-
label="问卷标题", placeholder="请输入问卷标题...", interactive=True
|
172
|
-
)
|
173
|
-
survey_desc = gr.Textbox(
|
174
|
-
label="问卷说明",
|
175
|
-
lines=3,
|
176
|
-
placeholder="请输入问卷说明...",
|
177
|
-
interactive=True,
|
178
|
-
)
|
179
|
-
|
180
|
-
# 问题编辑
|
181
|
-
with gr.Group():
|
182
|
-
gr.Markdown("### 问题编辑")
|
183
|
-
question_type = gr.Radio(
|
184
|
-
choices=[t.value for t in QuestionType],
|
185
|
-
label="问题类型",
|
186
|
-
value=QuestionType.TEXT.value,
|
187
|
-
)
|
188
|
-
question_content = gr.Textbox(
|
189
|
-
label="问题内容", lines=2, placeholder="请输入问题内容..."
|
190
|
-
)
|
191
|
-
|
192
|
-
# 选择题选项编辑
|
193
|
-
with gr.Group(visible=False) as choice_editor:
|
194
|
-
gr.Markdown("#### 选项管理")
|
195
|
-
with gr.Row():
|
196
|
-
option_input = gr.Textbox(
|
197
|
-
label="选项内容", placeholder="请输入选项内容..."
|
198
|
-
)
|
199
|
-
add_option_btn = gr.Button("添加选项")
|
200
|
-
current_options = gr.Dataframe(
|
201
|
-
headers=["选项内容"], value=[], label="当前选项列表"
|
202
|
-
)
|
203
|
-
remove_option_btn = gr.Button("删除最后一个选项")
|
204
|
-
|
205
|
-
# 评分题设置
|
206
|
-
with gr.Group(visible=False) as rating_editor:
|
207
|
-
gr.Markdown("#### 评分设置")
|
208
|
-
with gr.Row():
|
209
|
-
min_score = gr.Number(label="最小分值", value=1, minimum=0)
|
210
|
-
max_score = gr.Number(label="最大分值", value=5, minimum=1)
|
211
|
-
|
212
|
-
add_question_btn = gr.Button("添加问题", variant="primary")
|
213
|
-
|
214
|
-
# 问题列表预览
|
215
|
-
questions_preview = gr.Markdown("### 当前问题列表\n暂无问题")
|
216
|
-
questions_data = gr.JSON(visible=False) # 存储问题数据
|
217
|
-
|
218
|
-
with gr.Row():
|
219
|
-
save_survey_btn = gr.Button("保存问卷", variant="primary")
|
220
|
-
clear_btn = gr.Button("清空")
|
221
|
-
|
222
|
-
# 辅助函数
|
223
|
-
def _get_survey_table(self) -> list | None:
|
224
|
-
"""获取问卷列表数据"""
|
225
|
-
surveys = self.simulation._surveys
|
226
|
-
if not surveys:
|
227
|
-
return None
|
228
|
-
return [[s["id"], s["title"], len(s["questions"])] for s in surveys]
|
229
|
-
|
230
|
-
# 事件处理函数
|
231
|
-
def refresh_table():
|
232
|
-
"""刷新问卷列表"""
|
233
|
-
return self._get_survey_table()
|
234
|
-
|
235
|
-
def toggle_editor(show):
|
236
|
-
return {
|
237
|
-
survey_editor: gr.update(visible=show),
|
238
|
-
survey_title: gr.update(value=""),
|
239
|
-
survey_desc: gr.update(value=""),
|
240
|
-
# ... 其他需要重置的组件
|
241
|
-
}
|
242
|
-
|
243
|
-
# 绑定事件
|
244
|
-
refresh_btn.click(fn=refresh_table, outputs=[survey_table])
|
245
|
-
|
246
|
-
new_survey_btn.click(
|
247
|
-
fn=lambda: toggle_editor(True),
|
248
|
-
outputs=[
|
249
|
-
survey_editor,
|
250
|
-
survey_title,
|
251
|
-
survey_desc,
|
252
|
-
# ... 其他需要更新的组件
|
253
|
-
],
|
254
|
-
)
|
255
|
-
|
256
|
-
# 在事件绑定之前添加函数定义
|
257
|
-
def update_question_editors(q_type):
|
258
|
-
"""更新问题编辑器的显示状态"""
|
259
|
-
is_choice = q_type in [
|
260
|
-
QuestionType.SINGLE_CHOICE.value,
|
261
|
-
QuestionType.MULTIPLE_CHOICE.value,
|
262
|
-
]
|
263
|
-
is_rating = q_type in [QuestionType.RATING.value, QuestionType.LIKERT.value]
|
264
|
-
|
265
|
-
return {
|
266
|
-
choice_editor: gr.update(visible=is_choice),
|
267
|
-
rating_editor: gr.update(visible=is_rating),
|
268
|
-
question_content: gr.update(),
|
269
|
-
current_options: gr.update(value=[]),
|
270
|
-
min_score: gr.update(value=1),
|
271
|
-
max_score: gr.update(value=5),
|
272
|
-
}
|
273
|
-
|
274
|
-
# 然后是原有的事件绑定
|
275
|
-
question_type.change(
|
276
|
-
update_question_editors,
|
277
|
-
inputs=[question_type],
|
278
|
-
outputs=[
|
279
|
-
choice_editor,
|
280
|
-
rating_editor,
|
281
|
-
question_content,
|
282
|
-
current_options,
|
283
|
-
min_score,
|
284
|
-
max_score,
|
285
|
-
],
|
286
|
-
)
|
287
|
-
|
288
|
-
def add_option(option_text, current_options):
|
289
|
-
"""添加选项到列表"""
|
290
|
-
if not option_text:
|
291
|
-
return current_options, ""
|
292
|
-
|
293
|
-
# 正确处理 DataFrame
|
294
|
-
if current_options is None or current_options.empty:
|
295
|
-
new_options = [[option_text]]
|
296
|
-
else:
|
297
|
-
new_options = current_options.values.tolist()
|
298
|
-
# 添加新选项
|
299
|
-
new_options.append([option_text])
|
300
|
-
|
301
|
-
return new_options, ""
|
302
|
-
|
303
|
-
add_option_btn.click(
|
304
|
-
add_option,
|
305
|
-
inputs=[option_input, current_options],
|
306
|
-
outputs=[current_options, option_input],
|
307
|
-
)
|
308
|
-
|
309
|
-
# 在事件绑定之前添加函数定义
|
310
|
-
def add_question(
|
311
|
-
q_type, content, options, min_score, max_score, current_questions
|
312
|
-
):
|
313
|
-
"""添加问题到列表"""
|
314
|
-
if not content:
|
315
|
-
return current_questions, "### 当前问题列表\n请输入问题内容"
|
316
|
-
|
317
|
-
questions = current_questions or []
|
318
|
-
question = {"type": q_type, "content": content, "required": True}
|
319
|
-
|
320
|
-
if q_type in [
|
321
|
-
QuestionType.SINGLE_CHOICE.value,
|
322
|
-
QuestionType.MULTIPLE_CHOICE.value,
|
323
|
-
]:
|
324
|
-
# 正确处理 DataFrame 选项,并添加字母标号
|
325
|
-
if options is not None and not options.empty:
|
326
|
-
raw_options = [opt[0] for opt in options.values.tolist()]
|
327
|
-
# 为选项添加字母标号 (a, b, c...)
|
328
|
-
question["options"] = [
|
329
|
-
f"{chr(97 + i)}. {opt}" for i, opt in enumerate(raw_options)
|
330
|
-
]
|
331
|
-
else:
|
332
|
-
question["options"] = []
|
333
|
-
elif q_type in [QuestionType.RATING.value, QuestionType.LIKERT.value]:
|
334
|
-
question["min_rating"] = min_score
|
335
|
-
question["max_rating"] = max_score
|
336
|
-
|
337
|
-
questions.append(question)
|
338
|
-
|
339
|
-
# 更新预览,包含选项显示
|
340
|
-
preview = "### 当前问题列表\n"
|
341
|
-
for i, q in enumerate(questions, 1):
|
342
|
-
preview += f"\n{i}. {q['content']}"
|
343
|
-
if q.get("options"):
|
344
|
-
preview += "\n " + "\n ".join(q["options"])
|
345
|
-
elif q.get("min_rating") is not None:
|
346
|
-
preview += f"\n (评分范围: {q['min_rating']}-{q['max_rating']})"
|
347
|
-
|
348
|
-
return questions, preview
|
349
|
-
|
350
|
-
# 然后是事件绑定
|
351
|
-
add_question_btn.click(
|
352
|
-
add_question,
|
353
|
-
inputs=[
|
354
|
-
question_type,
|
355
|
-
question_content,
|
356
|
-
current_options,
|
357
|
-
min_score,
|
358
|
-
max_score,
|
359
|
-
questions_data,
|
360
|
-
],
|
361
|
-
outputs=[questions_data, questions_preview],
|
362
|
-
)
|
363
|
-
|
364
|
-
def save_survey(title, desc, questions):
|
365
|
-
"""保存问卷"""
|
366
|
-
if not title:
|
367
|
-
return [
|
368
|
-
False, # survey_editor_visible
|
369
|
-
"", # survey_title
|
370
|
-
"", # survey_desc
|
371
|
-
"", # question_content
|
372
|
-
[], # current_options
|
373
|
-
None, # questions_data
|
374
|
-
"### 当前问题列表\n暂无问题", # questions_preview
|
375
|
-
None, # survey_table
|
376
|
-
"请输入问卷标题", # status message
|
377
|
-
]
|
378
|
-
|
379
|
-
if not questions:
|
380
|
-
return [
|
381
|
-
False, # survey_editor_visible
|
382
|
-
"", # survey_title
|
383
|
-
"", # survey_desc
|
384
|
-
"", # question_content
|
385
|
-
[], # current_options
|
386
|
-
None, # questions_data
|
387
|
-
"### 当前问题列表\n暂无问题", # questions_preview
|
388
|
-
None, # survey_table
|
389
|
-
"请添加至少一个问题", # status message
|
390
|
-
]
|
391
|
-
|
392
|
-
# 保存问卷并更新列表
|
393
|
-
self.simulation.create_survey(
|
394
|
-
title=title, description=desc, questions=questions
|
395
|
-
)
|
396
|
-
|
397
|
-
return [
|
398
|
-
False, # survey_editor_visible
|
399
|
-
"", # survey_title
|
400
|
-
"", # survey_desc
|
401
|
-
"", # question_content
|
402
|
-
[], # current_options
|
403
|
-
None, # questions_data
|
404
|
-
"### 当前问题列表\n暂无问题", # questions_preview
|
405
|
-
self._get_survey_table(), # survey_table
|
406
|
-
"问卷保存成功", # status message
|
407
|
-
]
|
408
|
-
|
409
|
-
save_survey_btn.click(
|
410
|
-
save_survey,
|
411
|
-
inputs=[survey_title, survey_desc, questions_data],
|
412
|
-
outputs=[
|
413
|
-
survey_editor_visible,
|
414
|
-
survey_title,
|
415
|
-
survey_desc,
|
416
|
-
question_content,
|
417
|
-
current_options,
|
418
|
-
questions_data,
|
419
|
-
questions_preview,
|
420
|
-
survey_table, # 更新问卷列表
|
421
|
-
gr.Markdown(), # 状态消息
|
422
|
-
],
|
423
|
-
)
|
424
|
-
|
425
|
-
# 添加刷新按钮事件
|
426
|
-
refresh_btn.click(
|
427
|
-
lambda: self._get_survey_table(),
|
428
|
-
outputs=[gr.Dropdown()], # 更新所有问卷拉列表
|
429
|
-
)
|
430
|
-
|
431
|
-
def _create_survey_tab(self):
|
432
|
-
"""问卷标签页"""
|
433
|
-
gr.Markdown("### 问卷提交")
|
434
|
-
agent_select = gr.Dropdown(
|
435
|
-
choices=self._get_agent_names(), label="选择智能体", interactive=True
|
436
|
-
)
|
437
|
-
survey_select = gr.Dropdown(
|
438
|
-
choices=self._get_survey_choices() or [], # 当��回None时使用���列表
|
439
|
-
label="选择问卷",
|
440
|
-
interactive=True,
|
441
|
-
value=None,
|
442
|
-
)
|
443
|
-
refresh_btn = gr.Button("刷新列表")
|
444
|
-
submit_btn = gr.Button("提交问卷", variant="primary")
|
445
|
-
response_display = gr.Textbox(label="回答内容", lines=10, interactive=False)
|
446
|
-
|
447
|
-
# 添加刷新事件处理
|
448
|
-
def refresh_lists():
|
449
|
-
return {
|
450
|
-
agent_select: gr.Dropdown(
|
451
|
-
choices=self._get_agent_names(), interactive=True
|
452
|
-
),
|
453
|
-
survey_select: gr.Dropdown(
|
454
|
-
choices=self._get_survey_choices() or [],
|
455
|
-
interactive=True,
|
456
|
-
value=None,
|
457
|
-
),
|
458
|
-
}
|
459
|
-
|
460
|
-
refresh_btn.click(refresh_lists, outputs=[agent_select, survey_select])
|
461
|
-
|
462
|
-
# 绑定提交事件
|
463
|
-
async def submit_survey(survey_title, agent_name):
|
464
|
-
"""提交问卷"""
|
465
|
-
if not survey_title or not agent_name:
|
466
|
-
return "请选择问卷和智能体"
|
467
|
-
|
468
|
-
# 通过标题查找问卷ID
|
469
|
-
surveys = self.simulation.get_surveys()
|
470
|
-
survey_id = None
|
471
|
-
survey = None
|
472
|
-
for s in surveys:
|
473
|
-
if s.title == survey_title:
|
474
|
-
survey_id = s.id
|
475
|
-
survey = s
|
476
|
-
break
|
477
|
-
|
478
|
-
if not survey_id:
|
479
|
-
return f"找不到指定的问卷, survey_title: {survey_title}, agent_name: {agent_name}"
|
480
|
-
|
481
|
-
response = await self.simulation.submit_survey(agent_name, survey_id)
|
482
|
-
return response
|
483
|
-
|
484
|
-
submit_btn.click(
|
485
|
-
submit_survey,
|
486
|
-
inputs=[survey_select, agent_select],
|
487
|
-
outputs=[response_display],
|
488
|
-
)
|
489
|
-
|
490
|
-
def _create_status_tab(self):
|
491
|
-
"""创建状态标签页"""
|
492
|
-
with gr.Row():
|
493
|
-
total_runtime = gr.Textbox(
|
494
|
-
label="总运行时间",
|
495
|
-
value=self.simulation.get_total_runtime(),
|
496
|
-
interactive=False,
|
497
|
-
)
|
498
|
-
refresh_status_btn = gr.Button("刷新状态")
|
499
|
-
|
500
|
-
# 添加全局阻塞控制
|
501
|
-
with gr.Row():
|
502
|
-
block_all_btn = gr.Button("阻塞所有智能体", variant="secondary")
|
503
|
-
unblock_all_btn = gr.Button("解除所有阻塞", variant="secondary")
|
504
|
-
|
505
|
-
with gr.Row():
|
506
|
-
status_table = gr.DataFrame(
|
507
|
-
self._get_agent_status(),
|
508
|
-
label="智能体状态",
|
509
|
-
headers=["名称", "类型", "状态", "运行时间", "操作"],
|
510
|
-
)
|
511
|
-
|
512
|
-
# 单个智能体阻塞控制
|
513
|
-
with gr.Row():
|
514
|
-
agent_select = gr.Dropdown(
|
515
|
-
choices=self._get_agent_names(), label="选择智能体", interactive=True
|
516
|
-
)
|
517
|
-
block_status = gr.Textbox(label="阻塞状态", value="", interactive=False)
|
518
|
-
toggle_block_btn = gr.Button("切换阻塞状态")
|
519
|
-
|
520
|
-
self.status_table = status_table
|
521
|
-
|
522
|
-
def update_status():
|
523
|
-
total_time = self.simulation.get_total_runtime()
|
524
|
-
status = self._get_agent_status()
|
525
|
-
return total_time, status, self._get_agent_names()
|
526
|
-
|
527
|
-
# 更新事件处理
|
528
|
-
refresh_status_btn.click(
|
529
|
-
update_status, outputs=[total_runtime, status_table, agent_select]
|
530
|
-
)
|
531
|
-
|
532
|
-
# 全局阻塞控制
|
533
|
-
def block_all():
|
534
|
-
for name in self.simulation._agents.keys():
|
535
|
-
self.simulation.toggle_agent_block(name, True)
|
536
|
-
return update_status()
|
537
|
-
|
538
|
-
def unblock_all():
|
539
|
-
for name in self.simulation._agents.keys():
|
540
|
-
self.simulation.toggle_agent_block(name, False)
|
541
|
-
return update_status()
|
542
|
-
|
543
|
-
block_all_btn.click(
|
544
|
-
block_all, outputs=[total_runtime, status_table, agent_select]
|
545
|
-
)
|
546
|
-
unblock_all_btn.click(
|
547
|
-
unblock_all, outputs=[total_runtime, status_table, agent_select]
|
548
|
-
)
|
549
|
-
|
550
|
-
# 单个智能体阻塞控制
|
551
|
-
def toggle_block(agent_name):
|
552
|
-
if not agent_name:
|
553
|
-
return "请先选择智能体", None, None, None
|
554
|
-
is_blocked = agent_name in self.simulation._blocked_agents
|
555
|
-
self.simulation.toggle_agent_block(agent_name, not is_blocked)
|
556
|
-
status = f"智能体 {agent_name} {'已解除阻塞' if is_blocked else '已阻塞'}"
|
557
|
-
return status, *update_status()
|
558
|
-
|
559
|
-
toggle_block_btn.click(
|
560
|
-
toggle_block,
|
561
|
-
inputs=[agent_select],
|
562
|
-
outputs=[block_status, total_runtime, status_table, agent_select],
|
563
|
-
)
|
564
|
-
|
565
|
-
def _get_agent_names(self) -> list[str]:
|
566
|
-
"""获取有智能体名称"""
|
567
|
-
return list(self.simulation._agents.keys())
|
568
|
-
|
569
|
-
def _get_agent_status(self) -> list[list]:
|
570
|
-
"""获取智能体状态信息"""
|
571
|
-
status = []
|
572
|
-
for name, agent in self.simulation._agents.items():
|
573
|
-
status.append(
|
574
|
-
[
|
575
|
-
name,
|
576
|
-
agent._type.value,
|
577
|
-
"暂停中" if name in self.simulation._blocked_agents else "运行中",
|
578
|
-
self.simulation.get_agent_runtime(name),
|
579
|
-
]
|
580
|
-
)
|
581
|
-
return status
|
582
|
-
|
583
|
-
async def _handle_interview(self, history, agent_name, question):
|
584
|
-
"""处理采访请求"""
|
585
|
-
if not agent_name:
|
586
|
-
return (
|
587
|
-
history + [{"role": "system", "content": "请先选择一个智能体"}],
|
588
|
-
question,
|
589
|
-
)
|
590
|
-
|
591
|
-
if not question.strip():
|
592
|
-
return history, question
|
593
|
-
|
594
|
-
response = await self.simulation.interview_agent(agent_name, question)
|
595
|
-
return (
|
596
|
-
history
|
597
|
-
+ [
|
598
|
-
{"role": "user", "content": question},
|
599
|
-
{"role": "assistant", "content": response},
|
600
|
-
],
|
601
|
-
"",
|
602
|
-
)
|
File without changes
|
File without changes
|