初始化
This commit is contained in:
50
app/services/__init__.py
Normal file
50
app/services/__init__.py
Normal file
@@ -0,0 +1,50 @@
|
||||
# Import mock data service
|
||||
from .mock_data import (
|
||||
get_mock_documents,
|
||||
get_mock_quick_questions,
|
||||
get_mock_retrieval,
|
||||
get_mock_rag_answer,
|
||||
get_mock_compliance_result,
|
||||
get_mock_compliance_chat_response,
|
||||
MOCK_SYSTEM_STATS,
|
||||
MOCK_SYSTEM_CONFIG,
|
||||
)
|
||||
|
||||
# Try importing real services (may fail if dependencies not installed)
|
||||
try:
|
||||
from .llm import llm_service, LLMService
|
||||
from .embedding import embedding_service, EmbeddingService
|
||||
from .milvus import milvus_service, MilvusService
|
||||
from .document import DocumentService, get_document_service
|
||||
_real_services_available = True
|
||||
except ImportError:
|
||||
_real_services_available = False
|
||||
llm_service = None
|
||||
LLMService = None
|
||||
embedding_service = None
|
||||
EmbeddingService = None
|
||||
milvus_service = None
|
||||
MilvusService = None
|
||||
DocumentService = None
|
||||
get_document_service = None
|
||||
|
||||
__all__ = [
|
||||
# Mock data services
|
||||
"get_mock_documents",
|
||||
"get_mock_quick_questions",
|
||||
"get_mock_retrieval",
|
||||
"get_mock_rag_answer",
|
||||
"get_mock_compliance_result",
|
||||
"get_mock_compliance_chat_response",
|
||||
"MOCK_SYSTEM_STATS",
|
||||
"MOCK_SYSTEM_CONFIG",
|
||||
# Real services (may be None if not available)
|
||||
"llm_service",
|
||||
"LLMService",
|
||||
"embedding_service",
|
||||
"EmbeddingService",
|
||||
"milvus_service",
|
||||
"MilvusService",
|
||||
"DocumentService",
|
||||
"get_document_service",
|
||||
]
|
||||
64
app/services/document.py
Normal file
64
app/services/document.py
Normal file
@@ -0,0 +1,64 @@
|
||||
import os
|
||||
from typing import List, Optional
|
||||
from PyPDF2 import PdfReader
|
||||
from docx import Document
|
||||
import pdfplumber
|
||||
|
||||
|
||||
class DocumentService:
|
||||
def __init__(self, raw_dir: str, parsed_dir: str):
|
||||
self.raw_dir = raw_dir
|
||||
self.parsed_dir = parsed_dir
|
||||
|
||||
def parse_pdf(self, file_path: str) -> str:
|
||||
"""解析PDF文件"""
|
||||
text = ""
|
||||
try:
|
||||
with pdfplumber.open(file_path) as pdf:
|
||||
for page in pdf.pages:
|
||||
page_text = page.extract_text()
|
||||
if page_text:
|
||||
text += page_text + "\n"
|
||||
except Exception:
|
||||
reader = PdfReader(file_path)
|
||||
for page in reader.pages:
|
||||
text += page.extract_text() + "\n"
|
||||
|
||||
return text.strip()
|
||||
|
||||
def parse_docx(self, file_path: str) -> str:
|
||||
"""解析Word文件"""
|
||||
doc = Document(file_path)
|
||||
text = ""
|
||||
for paragraph in doc.paragraphs:
|
||||
text += paragraph.text + "\n"
|
||||
return text.strip()
|
||||
|
||||
def parse_txt(self, file_path: str) -> str:
|
||||
"""解析TXT文件"""
|
||||
with open(file_path, "r", encoding="utf-8") as f:
|
||||
return f.read().strip()
|
||||
|
||||
def parse_document(self, file_path: str) -> str:
|
||||
"""根据文件类型解析文档"""
|
||||
ext = os.path.splitext(file_path)[1].lower()
|
||||
|
||||
if ext == ".pdf":
|
||||
return self.parse_pdf(file_path)
|
||||
elif ext in [".docx", ".doc"]:
|
||||
return self.parse_docx(file_path)
|
||||
elif ext == ".txt":
|
||||
return self.parse_txt(file_path)
|
||||
else:
|
||||
raise ValueError(f"Unsupported file format: {ext}")
|
||||
|
||||
def save_parsed_text(self, doc_id: str, text: str) -> str:
|
||||
"""保存解析后的文本"""
|
||||
parsed_path = os.path.join(self.parsed_dir, f"{doc_id}.txt")
|
||||
with open(parsed_path, "w", encoding="utf-8") as f:
|
||||
f.write(text)
|
||||
return parsed_path
|
||||
|
||||
|
||||
def get_document_service(raw_dir: str, parsed_dir: str) -> DocumentService:
|
||||
return DocumentService(raw_dir, parsed_dir)
|
||||
33
app/services/embedding.py
Normal file
33
app/services/embedding.py
Normal file
@@ -0,0 +1,33 @@
|
||||
import dashscope
|
||||
from dashscope import TextEmbedding
|
||||
from typing import List
|
||||
|
||||
|
||||
class EmbeddingService:
|
||||
def __init__(self):
|
||||
from app.core.config import settings
|
||||
self.model = settings.embedding_model
|
||||
self.dimension = settings.embedding_dim
|
||||
dashscope.api_key = settings.dashscope_api_key
|
||||
|
||||
def embed_texts(self, texts: List[str]) -> List[List[float]]:
|
||||
"""批量文本嵌入"""
|
||||
response = TextEmbedding.call(
|
||||
model=self.model,
|
||||
input=texts,
|
||||
)
|
||||
|
||||
if response.status_code == 200:
|
||||
embeddings = []
|
||||
for item in response.output.embeddings:
|
||||
embeddings.append(item.embedding)
|
||||
return embeddings
|
||||
raise Exception(f"Embedding failed: {response.code}")
|
||||
|
||||
def embed_single(self, text: str) -> List[float]:
|
||||
"""单个文本嵌入"""
|
||||
embeddings = self.embed_texts([text])
|
||||
return embeddings[0]
|
||||
|
||||
|
||||
embedding_service = EmbeddingService()
|
||||
58
app/services/llm.py
Normal file
58
app/services/llm.py
Normal file
@@ -0,0 +1,58 @@
|
||||
import dashscope
|
||||
from dashscope import Generation
|
||||
from typing import AsyncGenerator, Optional, Generator
|
||||
|
||||
|
||||
class LLMService:
|
||||
def __init__(self):
|
||||
from app.core.config import settings
|
||||
self.model = settings.llm_model
|
||||
dashscope.api_key = settings.dashscope_api_key
|
||||
|
||||
def generate_stream(
|
||||
self,
|
||||
prompt: str,
|
||||
system_prompt: Optional[str] = None,
|
||||
) -> Generator[str, None, None]:
|
||||
"""流式生成文本"""
|
||||
messages = []
|
||||
if system_prompt:
|
||||
messages.append({"role": "system", "content": system_prompt})
|
||||
messages.append({"role": "user", "content": prompt})
|
||||
|
||||
responses = Generation.call(
|
||||
model=self.model,
|
||||
messages=messages,
|
||||
result_format="message",
|
||||
stream=True,
|
||||
)
|
||||
|
||||
for response in responses:
|
||||
if response.status_code == 200:
|
||||
content = response.output.choices[0].message.content
|
||||
if content:
|
||||
yield content
|
||||
|
||||
async def generate(
|
||||
self,
|
||||
prompt: str,
|
||||
system_prompt: Optional[str] = None,
|
||||
) -> str:
|
||||
"""一次性生成文本"""
|
||||
messages = []
|
||||
if system_prompt:
|
||||
messages.append({"role": "system", "content": system_prompt})
|
||||
messages.append({"role": "user", "content": prompt})
|
||||
|
||||
response = Generation.call(
|
||||
model=self.model,
|
||||
messages=messages,
|
||||
result_format="message",
|
||||
)
|
||||
|
||||
if response.status_code == 200:
|
||||
return response.output.choices[0].message.content
|
||||
raise Exception(f"LLM generation failed: {response.code}")
|
||||
|
||||
|
||||
llm_service = LLMService()
|
||||
158
app/services/milvus.py
Normal file
158
app/services/milvus.py
Normal file
@@ -0,0 +1,158 @@
|
||||
from pymilvus import (
|
||||
connections,
|
||||
Collection,
|
||||
FieldSchema,
|
||||
CollectionSchema,
|
||||
DataType,
|
||||
utility,
|
||||
)
|
||||
from typing import List, Optional
|
||||
|
||||
|
||||
class MilvusService:
|
||||
def __init__(self):
|
||||
from app.core.config import settings
|
||||
self.host = settings.milvus_host
|
||||
self.port = settings.milvus_port
|
||||
self.regulations_collection_name = settings.regulations_collection
|
||||
self.compliance_collection_name = settings.compliance_collection
|
||||
self._connected = False
|
||||
|
||||
def connect(self):
|
||||
"""连接Milvus"""
|
||||
if not self._connected:
|
||||
connections.connect(
|
||||
alias="default",
|
||||
host=self.host,
|
||||
port=self.port,
|
||||
)
|
||||
self._connected = True
|
||||
|
||||
def disconnect(self):
|
||||
"""断开连接"""
|
||||
if self._connected:
|
||||
connections.disconnect("default")
|
||||
self._connected = False
|
||||
|
||||
def create_regulations_collection(self):
|
||||
"""创建法规文档集合"""
|
||||
from app.core.config import settings
|
||||
self.connect()
|
||||
|
||||
if utility.has_collection(self.regulations_collection_name):
|
||||
return Collection(self.regulations_collection_name)
|
||||
|
||||
fields = [
|
||||
FieldSchema(name="id", dtype=DataType.INT64, is_primary=True, auto_id=True),
|
||||
FieldSchema(name="embedding", dtype=DataType.FLOAT_VECTOR, dim=settings.embedding_dim),
|
||||
FieldSchema(name="doc_name", dtype=DataType.VARCHAR, max_length=256),
|
||||
FieldSchema(name="clause_id", dtype=DataType.VARCHAR, max_length=64),
|
||||
FieldSchema(name="chapter", dtype=DataType.VARCHAR, max_length=128),
|
||||
FieldSchema(name="source_file", dtype=DataType.VARCHAR, max_length=256),
|
||||
FieldSchema(name="chunk_index", dtype=DataType.INT64),
|
||||
FieldSchema(name="content", dtype=DataType.VARCHAR, max_length=65535),
|
||||
FieldSchema(name="token_count", dtype=DataType.INT64),
|
||||
]
|
||||
|
||||
schema = CollectionSchema(
|
||||
fields=fields,
|
||||
description="法规文档向量集合",
|
||||
)
|
||||
|
||||
collection = Collection(
|
||||
name=self.regulations_collection_name,
|
||||
schema=schema,
|
||||
)
|
||||
|
||||
index_params = {
|
||||
"metric_type": "COSINE",
|
||||
"index_type": "IVF_FLAT",
|
||||
"params": {"nlist": 128},
|
||||
}
|
||||
collection.create_index(field_name="embedding", index_params=index_params)
|
||||
|
||||
return collection
|
||||
|
||||
def insert_chunks(
|
||||
self,
|
||||
embeddings: List[List[float]],
|
||||
metadata: List[dict],
|
||||
) -> List[int]:
|
||||
"""插入向量数据"""
|
||||
collection = Collection(self.regulations_collection_name)
|
||||
collection.load()
|
||||
|
||||
data = [
|
||||
embeddings,
|
||||
[m.get("doc_name", "") for m in metadata],
|
||||
[m.get("clause_id", "") for m in metadata],
|
||||
[m.get("chapter", "") for m in metadata],
|
||||
[m.get("source_file", "") for m in metadata],
|
||||
[m.get("chunk_index", 0) for m in metadata],
|
||||
[m.get("content", "") for m in metadata],
|
||||
[m.get("token_count", 0) for m in metadata],
|
||||
]
|
||||
|
||||
result = collection.insert(data)
|
||||
collection.flush()
|
||||
return result.primary_keys
|
||||
|
||||
def search(
|
||||
self,
|
||||
query_embedding: List[float],
|
||||
top_k: int = 10,
|
||||
) -> List[dict]:
|
||||
"""向量检索"""
|
||||
collection = Collection(self.regulations_collection_name)
|
||||
collection.load()
|
||||
|
||||
search_params = {"metric_type": "COSINE", "params": {"nprobe": 16}}
|
||||
|
||||
results = collection.search(
|
||||
data=[query_embedding],
|
||||
anns_field="embedding",
|
||||
param=search_params,
|
||||
limit=top_k,
|
||||
output_fields=["doc_name", "clause_id", "chapter", "content", "chunk_index"],
|
||||
)
|
||||
|
||||
hits = []
|
||||
for hit in results[0]:
|
||||
hits.append({
|
||||
"id": hit.id,
|
||||
"score": hit.score,
|
||||
"doc_name": hit.entity.get("doc_name"),
|
||||
"clause_id": hit.entity.get("clause_id"),
|
||||
"chapter": hit.entity.get("chapter"),
|
||||
"content": hit.entity.get("content"),
|
||||
"chunk_index": hit.entity.get("chunk_index"),
|
||||
})
|
||||
|
||||
return hits
|
||||
|
||||
def get_collection_stats(self) -> dict:
|
||||
"""获取集合统计"""
|
||||
self.connect()
|
||||
|
||||
if not utility.has_collection(self.regulations_collection_name):
|
||||
return {"exists": False}
|
||||
|
||||
collection = Collection(self.regulations_collection_name)
|
||||
collection.load()
|
||||
|
||||
return {
|
||||
"exists": True,
|
||||
"name": self.regulations_collection_name,
|
||||
"count": collection.num_entities,
|
||||
}
|
||||
|
||||
def health_check(self) -> bool:
|
||||
"""健康检查"""
|
||||
try:
|
||||
self.connect()
|
||||
return True
|
||||
except Exception:
|
||||
return False
|
||||
|
||||
|
||||
milvus_service = MilvusService()
|
||||
425
app/services/mock_data.py
Normal file
425
app/services/mock_data.py
Normal file
@@ -0,0 +1,425 @@
|
||||
"""
|
||||
Mock数据服务 - 提供预设假数据供前后端对接测试
|
||||
"""
|
||||
|
||||
from datetime import datetime
|
||||
from typing import Dict, List, Any
|
||||
import uuid
|
||||
|
||||
# 预设法规文档列表
|
||||
MOCK_DOCUMENTS: List[Dict[str, Any]] = [
|
||||
{
|
||||
"id": "doc-001",
|
||||
"name": "道路交通安全法.pdf",
|
||||
"chunks": 156,
|
||||
"status": "indexed",
|
||||
"created_at": datetime(2026, 5, 10, 10, 0, 0),
|
||||
},
|
||||
{
|
||||
"id": "doc-002",
|
||||
"name": "机动车登记规定.docx",
|
||||
"chunks": 89,
|
||||
"status": "indexed",
|
||||
"created_at": datetime(2026, 5, 10, 11, 0, 0),
|
||||
},
|
||||
{
|
||||
"id": "doc-003",
|
||||
"name": "电动自行车规范.pdf",
|
||||
"chunks": 42,
|
||||
"status": "indexed",
|
||||
"created_at": datetime(2026, 5, 10, 12, 0, 0),
|
||||
},
|
||||
{
|
||||
"id": "doc-004",
|
||||
"name": "GB 38031-2020 电动汽车安全要求.pdf",
|
||||
"chunks": 128,
|
||||
"status": "indexed",
|
||||
"created_at": datetime(2026, 5, 10, 13, 0, 0),
|
||||
},
|
||||
{
|
||||
"id": "doc-005",
|
||||
"name": "C-NCAP管理规则(2021版).pdf",
|
||||
"chunks": 95,
|
||||
"status": "indexed",
|
||||
"created_at": datetime(2026, 5, 10, 14, 0, 0),
|
||||
},
|
||||
]
|
||||
|
||||
# 预设快捷问题
|
||||
MOCK_QUICK_QUESTIONS: List[Dict[str, str]] = [
|
||||
{"id": "q1", "question": "电动自行车需要上牌照吗?", "category": "车辆登记"},
|
||||
{"id": "q2", "question": "新能源汽车有哪些补贴政策?", "category": "新能源"},
|
||||
{"id": "q3", "question": "车辆年检的规定是什么?", "category": "年检"},
|
||||
{"id": "q4", "question": "驾驶证过期了怎么处理?", "category": "驾驶证"},
|
||||
]
|
||||
|
||||
# 预设检索结果
|
||||
MOCK_RETRIEVAL_RESULTS: List[Dict[str, Any]] = [
|
||||
{
|
||||
"id": "chunk-001",
|
||||
"score": 0.95,
|
||||
"preview": "根据《道路交通安全法》第十八条规定,电动自行车经公安机关交通管理部门登记后,方可上道路行驶...",
|
||||
"doc_name": "道路交通安全法",
|
||||
"clause": "第十八条",
|
||||
"content": "根据《道路交通安全法》第十八条规定,电动自行车经公安机关交通管理部门登记后,方可上道路行驶。电动自行车应当符合国家标准,最高设计车速不超过二十五公里每小时,整车质量不超过五十五千克。",
|
||||
},
|
||||
{
|
||||
"id": "chunk-002",
|
||||
"score": 0.88,
|
||||
"preview": "电动自行车需符合GB17761-2018国家标准,包括最高车速、整车质量、脚踏骑行能力等要求...",
|
||||
"doc_name": "电动自行车规范",
|
||||
"clause": "第4条",
|
||||
"content": "电动自行车需符合GB17761-2018国家标准。主要技术要求包括:最高设计车速不超过25km/h,整车质量不超过55kg,具有脚踏骑行能力,蓄电池标称电压不超过48V,电动机额定连续输出功率不超过400W。",
|
||||
},
|
||||
{
|
||||
"id": "chunk-003",
|
||||
"score": 0.82,
|
||||
"preview": "机动车登记规定:初次申领机动车号牌、行驶证的,机动车所有人应当向住所地的车辆管理所申请注册登记...",
|
||||
"doc_name": "机动车登记规定",
|
||||
"clause": "第5条",
|
||||
"content": "机动车登记规定:初次申领机动车号牌、行驶证的,机动车所有人应当向住所地的车辆管理所申请注册登记。申请注册登记的,应当提交机动车所有人的身份证明、购车发票等机动车来历证明、机动车整车出厂合格证明或者进口机动车进口凭证。",
|
||||
},
|
||||
{
|
||||
"id": "chunk-004",
|
||||
"score": 0.75,
|
||||
"preview": "驾驶电动自行车上道路行驶,应当佩戴安全头盔,遵守道路交通安全法律法规...",
|
||||
"doc_name": "道路交通安全法",
|
||||
"clause": "第76条",
|
||||
"content": "驾驶电动自行车上道路行驶,应当佩戴安全头盔,遵守道路交通安全法律法规。电动自行车不得逆向行驶,不得在机动车道内行驶,最高车速不得超过规定的限速。",
|
||||
},
|
||||
{
|
||||
"id": "chunk-005",
|
||||
"score": 0.68,
|
||||
"preview": "电动汽车动力电池安全要求:电池系统发生热失控后,应在5分钟内不起火不爆炸...",
|
||||
"doc_name": "GB 38031-2020",
|
||||
"clause": "第7条",
|
||||
"content": "电动汽车动力电池安全要求(GB 38031-2020):电池系统发生热失控后,应在5分钟内不起火不爆炸,为乘员预留逃生时间。电池包需通过针刺、过充、短路等安全测试。",
|
||||
},
|
||||
]
|
||||
|
||||
# 预设RAG问答答案模板(按关键词匹配)
|
||||
MOCK_RAG_ANSWERS: Dict[str, Dict[str, Any]] = {
|
||||
"电动自行车": {
|
||||
"text": "根据《道路交通安全法》及相关规范,电动自行车上路需满足以下条件:\n\n1. 符合国家标准 GB17761-2018\n2. 经公安机关交通管理部门登记\n3. 最高设计车速不超过 25km/h\n4. 整车质量不超过 55kg\n5. 具有脚踏骑行能力\n6. 蓄电池标称电压不超过 48V\n\n行驶时还需佩戴安全头盔,不得逆向行驶或在机动车道内行驶。",
|
||||
"retrieval_ids": ["chunk-001", "chunk-002", "chunk-004"],
|
||||
},
|
||||
"驾驶证": {
|
||||
"text": "驾驶证申请流程如下:\n\n1. 到驾校报名并参加培训\n2. 通过科目一(理论考试)\n3. 通过科目二(场地驾驶技能考试)\n4. 通过科目三(道路驾驶技能考试)\n5. 通过科目四(安全文明驾驶常识考试)\n6. 领取驾驶证\n\n初次申领需到住所地车辆管理所申请注册登记。",
|
||||
"retrieval_ids": ["chunk-003"],
|
||||
},
|
||||
"超速": {
|
||||
"text": "超速处罚标准(根据《道路交通安全法》):\n\n- 超速10%以下:警告\n- 超速10%-20%:罚款50-200元\n- 超速20%-50%:罚款200-500元,记3-6分\n- 超速50%以上:罚款500-2000元,记12分,可吊销驾驶证\n\n机动车驾驶人违反道路交通安全法律、法规将处警告或二十元以上二百元以下罚款。",
|
||||
"retrieval_ids": ["chunk-001"],
|
||||
},
|
||||
"年检": {
|
||||
"text": "车辆年检规定:\n\n- 小型私家车:6年内免检(每2年申领标志),6-10年每2年检验,10年以上每年检验\n- 车辆需携带行驶证、交强险保单\n- 检验项目:灯光、制动、排放等\n\n机动车所有人的住所迁出车辆管理所管辖区域的,需在登记证书上签注变更事项。",
|
||||
"retrieval_ids": ["chunk-003"],
|
||||
},
|
||||
"电池": {
|
||||
"text": "电动汽车电池安全标准(GB 38031-2020):\n\n1. 热失控要求:电池系统发生热失控后,应在5分钟内不起火不爆炸,为乘员预留逃生时间\n2. 电池包需通过针刺、过充、短路等安全测试\n3. 充电系统应具备过充保护功能,当电池SOC达到100%时应自动停止充电\n4. 充电接口应符合GB/T 18487.1标准要求\n\n以上要求确保电动汽车的整车安全性。",
|
||||
"retrieval_ids": ["chunk-005"],
|
||||
},
|
||||
"碰撞": {
|
||||
"text": "正面碰撞测试要求(C-NCAP管理规则):\n\n1. 正面100%重叠刚性壁障碰撞试验\n2. 碰撞速度:50km/h\n3. 试验后要求:\n - 车门应能打开\n - 燃油系统无泄漏\n - 座椅及安全带功能正常\n\n此测试用于评估车辆在正面碰撞事故中对乘员的保护能力。",
|
||||
"retrieval_ids": [],
|
||||
},
|
||||
"AEB": {
|
||||
"text": "AEB(自动紧急制动系统)测试标准:\n\n1. 系统应在检测到前方障碍物时主动减速或停车\n2. 测试场景分为三种:\n - 目标车静止\n - 目标车移动\n - 目标车制动\n3. AEB功能是C-NCAP评分的重要加分项\n\n该系统对提升车辆主动安全性能具有重要意义。",
|
||||
"retrieval_ids": [],
|
||||
},
|
||||
"高速公路": {
|
||||
"text": "高速公路安全距离规定:\n\n1. 车速超过100km/h时,与同车道前车保持100米以上距离\n2. 车速低于100km/h时,距离可适当缩短\n3. 执行紧急任务的警车、消防车、救护车、工程救险车不受行驶速度限制\n\n保持安全距离是预防追尾事故的关键措施。",
|
||||
"retrieval_ids": [],
|
||||
},
|
||||
}
|
||||
|
||||
# 预设合规分析结果
|
||||
MOCK_COMPLIANCE_RESULT: Dict[str, Any] = {
|
||||
"task_id": "task-001",
|
||||
"dashboard": {
|
||||
"score": 78,
|
||||
"high_risk_count": 2,
|
||||
"medium_risk_count": 1,
|
||||
"low_risk_count": 0,
|
||||
"need_fix_segments": 3,
|
||||
"status": "warning",
|
||||
"status_label": "需优化",
|
||||
},
|
||||
"segments": [
|
||||
{
|
||||
"id": 1,
|
||||
"index": 1,
|
||||
"intent": "车身结构设计",
|
||||
"start_pos": 45,
|
||||
"end_pos": 230,
|
||||
"content": "车身采用高强度钢铝混合结构,A柱和B柱使用热成型钢板,厚度2.5mm。车顶结构设计满足GB 26112-2010抗压强度要求,正面碰撞能量吸收区域采用渐进式变形设计,确保碰撞时能量有效分散。",
|
||||
"risk_level": "high",
|
||||
"regulations": [
|
||||
{
|
||||
"id": 1,
|
||||
"name": "GB 26112-2010",
|
||||
"clause": "第4.2条",
|
||||
"score": 0.95,
|
||||
"match_keyword": "车顶抗压强度",
|
||||
"category": "high",
|
||||
"full_content": "车顶结构应能承受相当于车辆整备质量1.5倍的载荷,载荷分布应均匀,试验后车顶变形量不超过规定值。",
|
||||
},
|
||||
{
|
||||
"id": 2,
|
||||
"name": "C-NCAP管理规则",
|
||||
"clause": "第3.1条",
|
||||
"score": 0.88,
|
||||
"match_keyword": "正面碰撞",
|
||||
"category": "high",
|
||||
"full_content": "正面碰撞试验速度为50km/h,碰撞后车门应能打开,燃油系统无泄漏,座椅及安全带功能正常。",
|
||||
},
|
||||
{
|
||||
"id": 3,
|
||||
"name": "GB 11551-2014",
|
||||
"clause": "第5条",
|
||||
"score": 0.72,
|
||||
"match_keyword": "碰撞能量吸收",
|
||||
"category": "medium",
|
||||
"full_content": "车辆正面碰撞时应有效保护乘员,碰撞能量应通过车身结构合理分散。",
|
||||
},
|
||||
{
|
||||
"id": 4,
|
||||
"name": "机动车安全技术条件",
|
||||
"clause": "第12条",
|
||||
"score": 0.58,
|
||||
"match_keyword": "A柱强度",
|
||||
"category": "medium",
|
||||
"full_content": "A柱应具备足够的抗变形能力,材料强度应符合相关标准要求。",
|
||||
},
|
||||
],
|
||||
},
|
||||
{
|
||||
"id": 2,
|
||||
"index": 2,
|
||||
"intent": "动力系统配置",
|
||||
"start_pos": 298,
|
||||
"end_pos": 425,
|
||||
"content": "搭载永磁同步电机,最大功率150kW,峰值扭矩310Nm。电池组采用三元锂离子电池,容量75kWh,能量密度180Wh/kg。充电接口支持快充(30分钟充至80%)和慢充(8小时充满),符合GB/T 18487.1-2015标准。",
|
||||
"risk_level": "medium",
|
||||
"regulations": [
|
||||
{
|
||||
"id": 5,
|
||||
"name": "GB/T 18487.1-2015",
|
||||
"clause": "第6条",
|
||||
"score": 0.94,
|
||||
"match_keyword": "充电接口标准",
|
||||
"category": "high",
|
||||
"full_content": "电动汽车传导充电接口应符合GB/T 18487.1标准要求,充电系统应具备过充保护功能。",
|
||||
},
|
||||
{
|
||||
"id": 6,
|
||||
"name": "GB/T 31484-2015",
|
||||
"clause": "第4条",
|
||||
"score": 0.85,
|
||||
"match_keyword": "电池能量密度",
|
||||
"category": "high",
|
||||
"full_content": "动力电池能量密度不低于120Wh/kg,电池系统需通过热失控测试。",
|
||||
},
|
||||
{
|
||||
"id": 7,
|
||||
"name": "新能源汽车生产企业准入",
|
||||
"clause": "第8条",
|
||||
"score": 0.65,
|
||||
"match_keyword": "电机功率",
|
||||
"category": "medium",
|
||||
"full_content": "驱动电机应符合相关技术标准,功率参数应在规定范围内。",
|
||||
},
|
||||
{
|
||||
"id": 8,
|
||||
"name": "电动汽车安全要求",
|
||||
"clause": "第7条",
|
||||
"score": 0.45,
|
||||
"match_keyword": "充电时间",
|
||||
"category": "low",
|
||||
"full_content": "充电系统应具备过充保护功能,当电池SOC达到100%时应自动停止充电。",
|
||||
},
|
||||
],
|
||||
},
|
||||
{
|
||||
"id": 3,
|
||||
"index": 3,
|
||||
"intent": "安全配置设计",
|
||||
"start_pos": 570,
|
||||
"end_pos": 725,
|
||||
"content": "配备6个安全气囊(前排双气囊、侧气囊、侧气帘),采用预紧式安全带。ABS系统采用博世第9代ESP,具备碰撞预警功能(FCW)和自动紧急制动(AEB)。方向盘集成驾驶员疲劳监测摄像头。",
|
||||
"risk_level": "low",
|
||||
"regulations": [
|
||||
{
|
||||
"id": 9,
|
||||
"name": "GB 27887-2011",
|
||||
"clause": "第5条",
|
||||
"score": 0.92,
|
||||
"match_keyword": "安全气囊",
|
||||
"category": "high",
|
||||
"full_content": "乘用车应配备驾驶员和乘客安全气囊,气囊系统应符合相关技术标准。",
|
||||
},
|
||||
{
|
||||
"id": 10,
|
||||
"name": "GB/T 26991-2011",
|
||||
"clause": "第3条",
|
||||
"score": 0.78,
|
||||
"match_keyword": "ABS系统",
|
||||
"category": "medium",
|
||||
"full_content": "车辆应配备防抱死制动系统,系统性能应符合相关标准要求。",
|
||||
},
|
||||
{
|
||||
"id": 11,
|
||||
"name": "C-NCAP管理规则",
|
||||
"clause": "第4.2条",
|
||||
"score": 0.71,
|
||||
"match_keyword": "AEB自动制动",
|
||||
"category": "medium",
|
||||
"full_content": "主动安全配置评分包含AEB功能,AEB系统应能有效检测障碍物并主动减速。",
|
||||
},
|
||||
{
|
||||
"id": 12,
|
||||
"name": "机动车运行安全技术条件",
|
||||
"clause": "第15条",
|
||||
"score": 0.38,
|
||||
"match_keyword": "疲劳监测",
|
||||
"category": "low",
|
||||
"full_content": "建议配备驾驶员状态监测系统,及时发现驾驶员疲劳或分心状态。",
|
||||
},
|
||||
],
|
||||
},
|
||||
],
|
||||
"priority_actions": [
|
||||
{
|
||||
"regulation": "GB 26112-2010 第4.2条",
|
||||
"issue": "缺少车顶抗压强度测试数据",
|
||||
"suggestion": "补充车顶抗压强度具体测试数据,确保满足1.5倍整备质量载荷要求",
|
||||
"severity": "high",
|
||||
},
|
||||
{
|
||||
"regulation": "GB/T 31484-2015 第4条",
|
||||
"issue": "缺少电池热失控测试报告",
|
||||
"suggestion": "补充电池热失控测试报告,验证5分钟内不起火不爆炸",
|
||||
"severity": "high",
|
||||
},
|
||||
{
|
||||
"regulation": "C-NCAP管理规则 第3.1条",
|
||||
"issue": "缺少碰撞后车门开启性能数据",
|
||||
"suggestion": "提供碰撞后车门开启性能测试数据",
|
||||
"severity": "medium",
|
||||
},
|
||||
],
|
||||
}
|
||||
|
||||
# 预设合规对话响应模板
|
||||
MOCK_COMPLIANCE_CHAT_RESPONSES: Dict[str, Dict[str, str]] = {
|
||||
"车身结构设计": {
|
||||
"compliance": "根据当前分析,车身结构设计部分存在以下合规问题:\n\n1. GB 26112-2010要求车顶承受1.5倍整备质量载荷,目前设计声明满足要求但缺少测试数据\n2. C-NCAP正面碰撞后车门应能打开,需提供碰撞测试报告\n\n建议补充相关测试数据以提升合规评分。",
|
||||
"interpretation": "GB 26112-2010 第4.2条具体要求解读:\n\n车顶抗压强度测试是车辆被动安全的重要指标。该标准要求车顶结构能够承受相当于车辆整备质量1.5倍的均匀分布载荷,试验后车顶变形量不得超过规定限值。\n\n热成型钢板(22MnB5材料)抗拉强度约1500-1650 MPa,理论上能满足要求,但需通过实际测试验证。",
|
||||
"suggestion": "针对车身结构设计的修改建议:\n\n1. 补充车顶抗压强度测试报告\n2. 提供A柱材料认证证书\n3. 完善正面碰撞能量吸收设计说明\n4. 添加碰撞后车门开启性能数据\n\n这些补充材料可有效提升合规评分。",
|
||||
},
|
||||
"动力系统配置": {
|
||||
"compliance": "动力系统配置整体合规性良好,主要检查点:\n\n1. 电池能量密度180Wh/kg超过最低要求120Wh/kg ✓\n2. 充电接口符合GB/T 18487.1标准 ✓\n3. 快充30分钟充至80%符合行业标准 ✓\n\n需补充电池热失控测试报告。",
|
||||
"interpretation": "GB/T 31484-2015对动力电池的要求解读:\n\n1. 能量密度:不低于120Wh/kg(您的设计180Wh/kg满足要求)\n2. 循环寿命:不少于1000次循环后容量保持率≥80%\n3. 安全测试:需通过针刺、过充、短路等测试\n\n建议补充循环寿命测试数据。",
|
||||
"suggestion": "动力系统配置改进建议:\n\n1. 补充电池热失控测试报告\n2. 提供循环寿命测试数据\n3. 添加充电系统过充保护功能说明\n4. 完善电池管理系统(BMS)技术文档",
|
||||
},
|
||||
"安全配置设计": {
|
||||
"compliance": "安全配置设计合规性评估:\n\n1. 安全气囊配置满足GB 27887-2011要求 ✓\n2. ABS/ESP系统符合标准 ✓\n3. AEB功能是C-NCAP加分项 ✓\n\n驾驶员疲劳监测是建议配置,不强制要求。",
|
||||
"interpretation": "C-NCAP主动安全评分规则解读:\n\nAEB(自动紧急制动)系统是C-NCAP评分的重要加分项,最高可获得额外加分。测试场景包括:\n- 目标车静止场景\n- 目标车移动场景\n- 目标车制动场景\n\n建议完善AEB系统测试数据以获取更高评分。",
|
||||
"suggestion": "安全配置优化建议:\n\n1. 提供AEB系统测试数据\n2. 补充FCW预警功能测试报告\n3. 添加安全气囊展开时间数据\n4. 完善驾驶员疲劳监测系统说明(如有)",
|
||||
},
|
||||
}
|
||||
|
||||
# 预设系统统计数据
|
||||
MOCK_SYSTEM_STATS: Dict[str, int] = {
|
||||
"docs": 5,
|
||||
"chunks": 510,
|
||||
"vectors": 510,
|
||||
"segments": 0,
|
||||
}
|
||||
|
||||
# 预设系统配置
|
||||
MOCK_SYSTEM_CONFIG: Dict[str, Any] = {
|
||||
"llm": {
|
||||
"model": "qwen-max",
|
||||
},
|
||||
"embedding": {
|
||||
"model": "text-embedding-v3",
|
||||
"dimension": 1536,
|
||||
},
|
||||
"milvus": {
|
||||
"host": "localhost",
|
||||
"port": 19530,
|
||||
},
|
||||
"retrieval": {
|
||||
"vector_top_k": 10,
|
||||
"final_top_k": 5,
|
||||
},
|
||||
}
|
||||
|
||||
|
||||
def get_mock_documents() -> List[Dict[str, Any]]:
|
||||
"""获取预设法规文档列表"""
|
||||
return MOCK_DOCUMENTS
|
||||
|
||||
|
||||
def get_mock_quick_questions() -> List[Dict[str, str]]:
|
||||
"""获取预设快捷问题"""
|
||||
return MOCK_QUICK_QUESTIONS
|
||||
|
||||
|
||||
def get_mock_retrieval(query: str, top_k: int = 5) -> List[Dict[str, Any]]:
|
||||
"""根据查询关键词返回预设检索结果"""
|
||||
results = []
|
||||
for keyword, data in MOCK_RAG_ANSWERS.items():
|
||||
if keyword in query:
|
||||
for retrieval_id in data.get("retrieval_ids", []):
|
||||
for item in MOCK_RETRIEVAL_RESULTS:
|
||||
if item["id"] == retrieval_id:
|
||||
results.append({
|
||||
"id": item["id"],
|
||||
"score": item["score"],
|
||||
"preview": item["preview"],
|
||||
"doc_name": item["doc_name"],
|
||||
"clause": item["clause"],
|
||||
})
|
||||
break
|
||||
if not results:
|
||||
results = MOCK_RETRIEVAL_RESULTS[:top_k]
|
||||
return results[:top_k]
|
||||
|
||||
|
||||
def get_mock_rag_answer(query: str) -> str:
|
||||
"""根据查询关键词返回预设答案"""
|
||||
for keyword, data in MOCK_RAG_ANSWERS.items():
|
||||
if keyword in query:
|
||||
return data["text"]
|
||||
return "抱歉,暂未找到与您问题直接相关的法规内容。请尝试更具体的问题,或联系交通管理部门获取详细信息。\n\n您可以尝试询问:电动自行车、驾驶证、超速处罚、年检、电池安全、碰撞测试、AEB系统、高速公路规则等话题。"
|
||||
|
||||
|
||||
def get_mock_compliance_result(task_id: str) -> Dict[str, Any]:
|
||||
"""获取预设合规分析结果"""
|
||||
result = MOCK_COMPLIANCE_RESULT.copy()
|
||||
result["task_id"] = task_id
|
||||
return result
|
||||
|
||||
|
||||
def get_mock_compliance_chat_response(intent: str, query: str) -> str:
|
||||
"""获取预设合规对话响应"""
|
||||
responses = MOCK_COMPLIANCE_CHAT_RESPONSES.get(intent, {})
|
||||
if "合规" in query or "符合" in query:
|
||||
return responses.get("compliance", "根据相关法规分析,该段落的合规性需进一步评估。")
|
||||
elif "解读" in query or "什么" in query or "如何" in query:
|
||||
return responses.get("interpretation", "法规要求详细解读如下...")
|
||||
elif "修改" in query or "建议" in query or "完善" in query:
|
||||
return responses.get("suggestion", "建议进行以下修改以提升合规性...")
|
||||
return f"关于您的问题,{intent}部分涉及多条相关法规。您可以进一步询问合规性评估或修改建议。"
|
||||
|
||||
|
||||
def generate_task_id() -> str:
|
||||
"""生成任务ID"""
|
||||
return f"task-{uuid.uuid4().hex[:8]}"
|
||||
|
||||
|
||||
def generate_doc_id() -> str:
|
||||
"""生成文档ID"""
|
||||
return f"doc-{uuid.uuid4().hex[:8]}"
|
||||
Reference in New Issue
Block a user