From 35b80929b08debe9cf101b2cd5bb407537855591 Mon Sep 17 00:00:00 2001 From: bruce Date: Sat, 30 May 2026 00:08:00 +0800 Subject: [PATCH] =?UTF-8?q?feat(scenarios):=20=E6=94=AF=E6=8C=81=E5=9C=BA?= =?UTF-8?q?=E6=99=AF=E9=85=8D=E7=BD=AE=E5=8A=A0=E8=BD=BD=E4=B8=8E=E9=A6=96?= =?UTF-8?q?=E9=A1=B5=E5=B1=95=E7=A4=BA?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- apps/__init__.py | 1 + apps/scenarios/__init__.py | 1 + apps/scenarios/apps.py | 6 ++++ apps/scenarios/services.py | 64 ++++++++++++++++++++++++++++++++++ apps/scenarios/urls.py | 10 ++++++ apps/scenarios/views.py | 7 ++++ configs/document_review.yaml | 22 ++++++++++++ configs/knowledge_qa.yaml | 22 ++++++++++++ configs/quality_analysis.yaml | 23 ++++++++++++ configs/risk_audit.yaml | 24 +++++++++++++ configs/ticket_assistant.yaml | 23 ++++++++++++ templates/scenarios/index.html | 23 ++++++++++++ tests/test_scenarios.py | 30 ++++++++++++++++ 13 files changed, 256 insertions(+) create mode 100644 apps/__init__.py create mode 100644 apps/scenarios/__init__.py create mode 100644 apps/scenarios/apps.py create mode 100644 apps/scenarios/services.py create mode 100644 apps/scenarios/urls.py create mode 100644 apps/scenarios/views.py create mode 100644 configs/document_review.yaml create mode 100644 configs/knowledge_qa.yaml create mode 100644 configs/quality_analysis.yaml create mode 100644 configs/risk_audit.yaml create mode 100644 configs/ticket_assistant.yaml create mode 100644 templates/scenarios/index.html create mode 100644 tests/test_scenarios.py diff --git a/apps/__init__.py b/apps/__init__.py new file mode 100644 index 0000000..8b13789 --- /dev/null +++ b/apps/__init__.py @@ -0,0 +1 @@ + diff --git a/apps/scenarios/__init__.py b/apps/scenarios/__init__.py new file mode 100644 index 0000000..8b13789 --- /dev/null +++ b/apps/scenarios/__init__.py @@ -0,0 +1 @@ + diff --git a/apps/scenarios/apps.py b/apps/scenarios/apps.py new file mode 100644 index 0000000..c35f15e --- /dev/null +++ b/apps/scenarios/apps.py @@ -0,0 +1,6 @@ +from django.apps import AppConfig + + +class ScenariosConfig(AppConfig): + default_auto_field = "django.db.models.BigAutoField" + name = "apps.scenarios" diff --git a/apps/scenarios/services.py b/apps/scenarios/services.py new file mode 100644 index 0000000..2120932 --- /dev/null +++ b/apps/scenarios/services.py @@ -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}") diff --git a/apps/scenarios/urls.py b/apps/scenarios/urls.py new file mode 100644 index 0000000..6284dee --- /dev/null +++ b/apps/scenarios/urls.py @@ -0,0 +1,10 @@ +from django.urls import path + +from . import views + + +app_name = "scenarios" + +urlpatterns = [ + path("", views.index, name="index"), +] diff --git a/apps/scenarios/views.py b/apps/scenarios/views.py new file mode 100644 index 0000000..496fc53 --- /dev/null +++ b/apps/scenarios/views.py @@ -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()}) diff --git a/configs/document_review.yaml b/configs/document_review.yaml new file mode 100644 index 0000000..9d2c593 --- /dev/null +++ b/configs/document_review.yaml @@ -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 diff --git a/configs/knowledge_qa.yaml b/configs/knowledge_qa.yaml new file mode 100644 index 0000000..525c75c --- /dev/null +++ b/configs/knowledge_qa.yaml @@ -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 diff --git a/configs/quality_analysis.yaml b/configs/quality_analysis.yaml new file mode 100644 index 0000000..cb61b3d --- /dev/null +++ b/configs/quality_analysis.yaml @@ -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 diff --git a/configs/risk_audit.yaml b/configs/risk_audit.yaml new file mode 100644 index 0000000..f0d3b64 --- /dev/null +++ b/configs/risk_audit.yaml @@ -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 diff --git a/configs/ticket_assistant.yaml b/configs/ticket_assistant.yaml new file mode 100644 index 0000000..f6e7e91 --- /dev/null +++ b/configs/ticket_assistant.yaml @@ -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 diff --git a/templates/scenarios/index.html b/templates/scenarios/index.html new file mode 100644 index 0000000..c848d37 --- /dev/null +++ b/templates/scenarios/index.html @@ -0,0 +1,23 @@ + + + + + Universal Agent Demo Framework + + +

Universal Agent Demo Framework

+

用于复试展示的通用 AI Agent Demo 框架。

+
+ {% for scenario in scenarios %} +
+

{{ scenario.name }}

+

{{ scenario.description }}

+

场景 ID:{{ scenario.id }}

+

进入对话

+
+ {% empty %} +

暂无可用场景,请检查 configs 目录。

+ {% endfor %} +
+ + diff --git a/tests/test_scenarios.py b/tests/test_scenarios.py new file mode 100644 index 0000000..bca3b92 --- /dev/null +++ b/tests/test_scenarios.py @@ -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")