feat(audit): 增加场景筛选与日志摘要展示
This commit is contained in:
@@ -2,6 +2,7 @@ from django.db import models
|
||||
|
||||
|
||||
class AgentAuditLog(models.Model):
|
||||
# 审计状态需要同时服务数据库检索和前端展示。
|
||||
STATUS_SUCCESS = "success"
|
||||
STATUS_FAILED = "failed"
|
||||
|
||||
@@ -25,6 +26,19 @@ class AgentAuditLog(models.Model):
|
||||
def __str__(self) -> str:
|
||||
return f"{self.scenario_name or self.scenario_id} #{self.pk}"
|
||||
|
||||
def get_status_display_text(self) -> str:
|
||||
"""返回更适合页面展示的中文状态。"""
|
||||
return {
|
||||
self.STATUS_SUCCESS: "执行成功",
|
||||
self.STATUS_FAILED: "执行失败",
|
||||
}.get(self.status, self.status)
|
||||
|
||||
def get_user_input_summary(self, max_length: int = 28) -> str:
|
||||
"""在列表页展示用户输入摘要,避免长文本撑破表格。"""
|
||||
if len(self.user_input) <= max_length:
|
||||
return self.user_input
|
||||
return f"{self.user_input[:max_length]}..."
|
||||
|
||||
|
||||
class DemoBusinessRecord(models.Model):
|
||||
scenario_id = models.CharField(max_length=100, db_index=True)
|
||||
|
||||
@@ -4,8 +4,19 @@ from .models import AgentAuditLog
|
||||
|
||||
|
||||
def log_list(request):
|
||||
# 列表页支持按场景筛选,方便演示时快速定位同一类场景的执行记录。
|
||||
scenario_id = (request.GET.get("scenario_id") or "").strip()
|
||||
logs = AgentAuditLog.objects.all()
|
||||
return render(request, "audit/log_list.html", {"logs": logs})
|
||||
if scenario_id:
|
||||
logs = logs.filter(scenario_id=scenario_id)
|
||||
return render(
|
||||
request,
|
||||
"audit/log_list.html",
|
||||
{
|
||||
"logs": logs,
|
||||
"selected_scenario_id": scenario_id,
|
||||
},
|
||||
)
|
||||
|
||||
|
||||
def log_detail(request, log_id: int):
|
||||
|
||||
@@ -7,6 +7,12 @@
|
||||
<span class="eyebrow">执行留痕</span>
|
||||
<h1 class="page-title">审计日志</h1>
|
||||
<p class="page-lead">每次 Agent 执行都会记录模型、检索片段、工具调用和最终结果,方便演示链路可解释性。</p>
|
||||
{% if selected_scenario_id %}
|
||||
<p style="margin-top: 14px;">
|
||||
<span class="meta-badge">当前筛选场景:{{ selected_scenario_id }}</span>
|
||||
<a class="button" href="{% url 'audit:list' %}">清空筛选</a>
|
||||
</p>
|
||||
{% endif %}
|
||||
</header>
|
||||
|
||||
<article class="panel">
|
||||
@@ -15,9 +21,11 @@
|
||||
<tr>
|
||||
<th>ID</th>
|
||||
<th>场景</th>
|
||||
<th>输入摘要</th>
|
||||
<th>状态</th>
|
||||
<th>模型</th>
|
||||
<th>耗时</th>
|
||||
<th>创建时间</th>
|
||||
<th>详情</th>
|
||||
</tr>
|
||||
</thead>
|
||||
@@ -26,13 +34,15 @@
|
||||
<tr>
|
||||
<td>{{ log.id }}</td>
|
||||
<td>{{ log.scenario_name }}</td>
|
||||
<td>{{ log.status }}</td>
|
||||
<td>{{ log.get_user_input_summary }}</td>
|
||||
<td>{{ log.get_status_display_text }}</td>
|
||||
<td>{{ log.model_name }}</td>
|
||||
<td>{{ log.latency_ms }} ms</td>
|
||||
<td>{{ log.created_at|date:"Y-m-d H:i" }}</td>
|
||||
<td><a class="button" href="{% url 'audit:detail' log.id %}">查看详情</a></td>
|
||||
</tr>
|
||||
{% empty %}
|
||||
<tr><td colspan="6">暂无审计日志,先去执行一次对话吧。</td></tr>
|
||||
<tr><td colspan="8">暂无审计日志,先去执行一次对话吧。</td></tr>
|
||||
{% endfor %}
|
||||
</tbody>
|
||||
</table>
|
||||
|
||||
@@ -27,6 +27,41 @@ def test_audit_list_page_shows_log(client, db):
|
||||
assert "知识库问答助手" in response.content.decode("utf-8")
|
||||
|
||||
|
||||
def test_audit_list_can_filter_by_scenario(client, db):
|
||||
create_audit_log(
|
||||
"knowledge_qa",
|
||||
"知识库问答助手",
|
||||
"制度问题",
|
||||
AgentResult(answer="回答一", status="success"),
|
||||
)
|
||||
create_audit_log(
|
||||
"quality_analysis",
|
||||
"质量异常分析助手",
|
||||
"质量问题",
|
||||
AgentResult(answer="回答二", status="success"),
|
||||
)
|
||||
|
||||
response = client.get(reverse("audit:list"), {"scenario_id": "knowledge_qa"})
|
||||
|
||||
content = response.content.decode("utf-8")
|
||||
assert response.status_code == 200
|
||||
assert "知识库问答助手" in content
|
||||
assert "质量异常分析助手" not in content
|
||||
|
||||
|
||||
def test_audit_list_page_shows_user_input_summary(client, db):
|
||||
create_audit_log(
|
||||
"knowledge_qa",
|
||||
"知识库问答助手",
|
||||
"这是一个比较长的用户输入,用于确认列表页会展示输入摘要。",
|
||||
AgentResult(answer="回答", status="success"),
|
||||
)
|
||||
|
||||
response = client.get(reverse("audit:list"))
|
||||
|
||||
assert "这是一个比较长的用户输入" in response.content.decode("utf-8")
|
||||
|
||||
|
||||
def test_create_audit_log_masks_api_keys_from_error_message(db):
|
||||
result = AgentResult(
|
||||
answer="",
|
||||
|
||||
Reference in New Issue
Block a user