feat(scenarios): 支持场景配置加载与首页展示
This commit is contained in:
1
apps/__init__.py
Normal file
1
apps/__init__.py
Normal file
@@ -0,0 +1 @@
|
|||||||
|
|
||||||
1
apps/scenarios/__init__.py
Normal file
1
apps/scenarios/__init__.py
Normal file
@@ -0,0 +1 @@
|
|||||||
|
|
||||||
6
apps/scenarios/apps.py
Normal file
6
apps/scenarios/apps.py
Normal file
@@ -0,0 +1,6 @@
|
|||||||
|
from django.apps import AppConfig
|
||||||
|
|
||||||
|
|
||||||
|
class ScenariosConfig(AppConfig):
|
||||||
|
default_auto_field = "django.db.models.BigAutoField"
|
||||||
|
name = "apps.scenarios"
|
||||||
64
apps/scenarios/services.py
Normal file
64
apps/scenarios/services.py
Normal 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
10
apps/scenarios/urls.py
Normal 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
7
apps/scenarios/views.py
Normal 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()})
|
||||||
22
configs/document_review.yaml
Normal file
22
configs/document_review.yaml
Normal 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
22
configs/knowledge_qa.yaml
Normal 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
|
||||||
23
configs/quality_analysis.yaml
Normal file
23
configs/quality_analysis.yaml
Normal 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
24
configs/risk_audit.yaml
Normal 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
|
||||||
23
configs/ticket_assistant.yaml
Normal file
23
configs/ticket_assistant.yaml
Normal 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
|
||||||
23
templates/scenarios/index.html
Normal file
23
templates/scenarios/index.html
Normal 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
30
tests/test_scenarios.py
Normal 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")
|
||||||
Reference in New Issue
Block a user