Files
DEMO-AGENT/apps/scenarios/services.py

111 lines
3.1 KiB
Python
Raw Permalink Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
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 normalize_scenario(config)
def normalize_scenario(config: dict) -> dict:
"""补齐页面和其它模块常用的派生字段,避免模板层重复判断。"""
normalized = dict(config)
normalized["applicable_questions"] = list(config.get("applicable_questions") or [])
normalized["rag"] = dict(config.get("rag", {}))
normalized["rag"]["enabled"] = bool(normalized["rag"].get("enabled"))
normalized["tools"] = list(config.get("tools") or [])
normalized["tool_count"] = len(normalized["tools"])
normalized["is_enabled"] = True
return normalized
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 _read_yaml_file(path: Path) -> dict:
with path.open("r", encoding="utf-8") as file:
return yaml.safe_load(file) or {}
def _collect_scenario_load_result() -> tuple[list[dict], list[dict]]:
"""
统一读取配置目录中的所有场景文件。
返回值:
- scenarios: 校验通过的场景列表
- issues: 非法 YAML / 缺字段等错误摘要,供首页展示
"""
scenarios = []
issues = []
for path in _scenario_files():
try:
config = _read_yaml_file(path)
scenarios.append(validate_scenario(config))
except (yaml.YAMLError, ScenarioValidationError) as exc:
issues.append(
{
"file_name": path.name,
"message": str(exc),
}
)
return scenarios, issues
def list_scenarios() -> list[dict]:
# 首页每次读取最新 YAML便于复试现场快速改题。
scenarios, _issues = _collect_scenario_load_result()
return scenarios
def list_scenario_issues() -> list[dict]:
"""返回配置异常摘要,便于页面明确提示而不是直接 500。"""
_scenarios, issues = _collect_scenario_load_result()
return issues
def get_scenario(scenario_id: str) -> dict:
for scenario in list_scenarios():
if scenario["id"] == scenario_id:
return scenario
raise ScenarioNotFound(f"场景不存在: {scenario_id}")