feat(scenarios): 支持场景配置加载与首页展示

This commit is contained in:
2026-05-30 00:08:00 +08:00
parent 6291940734
commit 35b80929b0
13 changed files with 256 additions and 0 deletions

1
apps/__init__.py Normal file
View File

@@ -0,0 +1 @@

View File

@@ -0,0 +1 @@

6
apps/scenarios/apps.py Normal file
View File

@@ -0,0 +1,6 @@
from django.apps import AppConfig
class ScenariosConfig(AppConfig):
default_auto_field = "django.db.models.BigAutoField"
name = "apps.scenarios"

View File

@@ -0,0 +1,64 @@
from pathlib import Path
import yaml
from django.conf import settings
REQUIRED_FIELDS = [
("id",),
("name",),
("description",),
("agent", "role"),
("agent", "goal"),
("agent", "instructions"),
("rag", "enabled"),
("tools",),
("output", "type"),
("audit", "enabled"),
]
class ScenarioNotFound(KeyError):
pass
class ScenarioValidationError(ValueError):
pass
def _get_nested(config: dict, path: tuple[str, ...]):
value = config
for key in path:
if not isinstance(value, dict) or key not in value:
raise ScenarioValidationError("缺失必填字段: " + ".".join(path))
value = value[key]
return value
def validate_scenario(config: dict) -> dict:
for field_path in REQUIRED_FIELDS:
_get_nested(config, field_path)
return config
def _scenario_files() -> list[Path]:
config_dir = Path(settings.SCENARIO_CONFIG_DIR)
if not config_dir.exists():
return []
return sorted([*config_dir.glob("*.yaml"), *config_dir.glob("*.yml")])
def list_scenarios() -> list[dict]:
scenarios = []
for path in _scenario_files():
with path.open("r", encoding="utf-8") as file:
config = yaml.safe_load(file) or {}
scenarios.append(validate_scenario(config))
return scenarios
def get_scenario(scenario_id: str) -> dict:
for scenario in list_scenarios():
if scenario["id"] == scenario_id:
return scenario
raise ScenarioNotFound(f"场景不存在: {scenario_id}")

10
apps/scenarios/urls.py Normal file
View File

@@ -0,0 +1,10 @@
from django.urls import path
from . import views
app_name = "scenarios"
urlpatterns = [
path("", views.index, name="index"),
]

7
apps/scenarios/views.py Normal file
View File

@@ -0,0 +1,7 @@
from django.shortcuts import render
from .services import list_scenarios
def index(request):
return render(request, "scenarios/index.html", {"scenarios": list_scenarios()})

View File

@@ -0,0 +1,22 @@
id: document_review
name: 文档审核助手
description: 检查合同、制度或 SOP 中的风险点和缺失项
applicable_questions:
- 合同审核
- 制度审核
agent:
role: 文档审核专家
goal: 根据审核规则和知识库内容输出结构化审核意见
instructions:
- 不确定的问题必须标记为需人工复核
- 输出必须包含风险等级和修改建议
rag:
enabled: true
collection: document_review
top_k: 5
tools:
- check_required_fields
output:
type: document_review_report
audit:
enabled: true

22
configs/knowledge_qa.yaml Normal file
View File

@@ -0,0 +1,22 @@
id: knowledge_qa
name: 知识库问答助手
description: 用于 SOP、制度、客服知识库和内部文档问答
applicable_questions:
- SOP 问答
- 制度问答
agent:
role: 知识库问答专家
goal: 基于知识库内容回答用户问题
instructions:
- 回答必须优先基于检索内容
- 不确定时说明缺失信息
rag:
enabled: true
collection: knowledge_qa
top_k: 5
tools:
- generate_action_items
output:
type: general_answer
audit:
enabled: true

View File

@@ -0,0 +1,23 @@
id: quality_analysis
name: 质量异常分析助手
description: 用于分析生产质量异常、检索 SOP、生成处理建议
applicable_questions:
- 质量异常分析
- 缺陷原因定位
agent:
role: 质量管理专家
goal: 根据用户问题、知识库和工具结果,输出可执行的质量分析报告
instructions:
- 回答必须基于知识库或工具结果
- 涉及质量风险时给出风险等级
rag:
enabled: true
collection: quality_analysis
top_k: 5
tools:
- query_demo_records
- calculate_rate
output:
type: quality_report
audit:
enabled: true

24
configs/risk_audit.yaml Normal file
View File

@@ -0,0 +1,24 @@
id: risk_audit
name: 风险审核助手
description: 用于财务、采购、报销和合同风险审核
applicable_questions:
- 财务审核
- 采购审核
- 合同风险分析
agent:
role: 风险审核专家
goal: 识别业务材料中的风险点并给出审核建议
instructions:
- 风险点必须说明依据
- 缺失材料要单独列出
rag:
enabled: true
collection: risk_audit
top_k: 5
tools:
- check_required_fields
- calculate_rate
output:
type: risk_audit_report
audit:
enabled: true

View File

@@ -0,0 +1,23 @@
id: ticket_assistant
name: 工单处理助手
description: 用于客服、售后和运维工单的分类与回复建议
applicable_questions:
- 客服工单
- 售后工单
agent:
role: 工单处理专家
goal: 判断工单类别、优先级并生成处理建议
instructions:
- 需要人工处理时明确标记
- 回复建议要简洁可执行
rag:
enabled: true
collection: ticket_assistant
top_k: 5
tools:
- query_demo_records
- generate_action_items
output:
type: ticket_response
audit:
enabled: true

View File

@@ -0,0 +1,23 @@
<!doctype html>
<html lang="zh-CN">
<head>
<meta charset="utf-8">
<title>Universal Agent Demo Framework</title>
</head>
<body>
<h1>Universal Agent Demo Framework</h1>
<p>用于复试展示的通用 AI Agent Demo 框架。</p>
<main>
{% for scenario in scenarios %}
<section>
<h2>{{ scenario.name }}</h2>
<p>{{ scenario.description }}</p>
<p>场景 ID{{ scenario.id }}</p>
<p><a href="{% url 'chat:index' scenario.id %}">进入对话</a></p>
</section>
{% empty %}
<p>暂无可用场景,请检查 configs 目录。</p>
{% endfor %}
</main>
</body>
</html>

30
tests/test_scenarios.py Normal file
View File

@@ -0,0 +1,30 @@
import pytest
from apps.scenarios.services import (
ScenarioNotFound,
get_scenario,
list_scenarios,
)
def test_list_scenarios_loads_five_configs():
scenarios = list_scenarios()
assert [scenario["id"] for scenario in scenarios] == [
"document_review",
"knowledge_qa",
"quality_analysis",
"risk_audit",
"ticket_assistant",
]
def test_get_scenario_returns_full_agent_config():
scenario = get_scenario("quality_analysis")
assert scenario["agent"]["role"] == "质量管理专家"
assert scenario["rag"]["enabled"] is True
assert scenario["output"]["type"] == "quality_report"
def test_get_scenario_raises_clear_error_for_missing_id():
with pytest.raises(ScenarioNotFound, match="场景不存在"):
get_scenario("missing")