""" MACP 业务服务层模块。 为平台各功能模块提供业务逻辑实现,基于内存假数据模拟完整业务流程。 包含以下服务: - TemplateService: 文档模板管理服务 - GjbValidatorService: GJB合规验证服务 - DocumentService: 文档管理服务 - SkillService: Skills编排服务 - MemoryService: 智能体记忆管理服务 - KnowledgeBaseService: 知识库交互服务 - ModelService: 大模型接入管理服务 - MultimodalService: 多模态处理服务 - SandboxService: 安全沙箱执行服务 - McpToolService: MCP工具管理服务 """ from __future__ import annotations import uuid from datetime import datetime from typing import Any, Optional from app.models import ( Document, DocumentStatus, FileType, GjbRule, GjbStandard, KnowledgeChunk, KnowledgeLevel, McpTool, MemoryEntry, MemoryType, ModelProfile, ModelStatus, MultimodalResult, Skill, Template, TemplateType, ValidationReport, Workflow, ) # ============================================================================= # 假数据初始化 # ============================================================================= def _init_mock_templates() -> dict[str, Template]: """初始化假数据模板库""" templates = {} t1 = Template( id="TPL-001", name="GJB438C 软件需求规格说明书模板", template_type=TemplateType.SRS, gjb_standard=GjbStandard.GJB438C, version="2.0", content_structure={ "sections": [ {"id": "1", "title": "范围", "required": True}, {"id": "2", "title": "引用文档", "required": True}, {"id": "3", "title": "需求", "required": True}, {"id": "3.1", "title": "功能需求", "required": True}, {"id": "3.2", "title": "接口需求", "required": True}, {"id": "4", "title": "合格性需求", "required": True}, {"id": "5", "title": "需求可追踪性", "required": False}, ] }, author="系统管理员", ) t2 = Template( id="TPL-002", name="GJB438C 软件设计说明书模板", template_type=TemplateType.SDD, gjb_standard=GjbStandard.GJB438C, version="1.5", content_structure={ "sections": [ {"id": "1", "title": "范围", "required": True}, {"id": "2", "title": "引用文档", "required": True}, {"id": "3", "title": "软件体系结构设计", "required": True}, {"id": "4", "title": "软件详细设计", "required": True}, ] }, author="系统管理员", ) templates[t1.id] = t1 templates[t2.id] = t2 return templates def _init_mock_rules() -> dict[str, GjbRule]: """初始化假数据GJB规则库""" rules = {} rule_data = [ ("R-001", "1", "文档必须包含'范围'章节,说明软件用途和背景", True, [TemplateType.SRS, TemplateType.SDD], "建议在文档开头添加'范围'章节"), ("R-002", "2", "文档必须包含'引用文档'章节,列出所有参考文献", True, [TemplateType.SRS, TemplateType.SDD], "建议添加'引用文档'章节并列出参考文献"), ("R-003", "3", "需求规格说明书必须包含功能需求和接口需求", True, [TemplateType.SRS], "请补充功能需求和接口需求章节"), ("R-004", "3.1", "每项功能需求必须有唯一标识符", True, [TemplateType.SRS], "请为功能需求添加唯一标识符"), ("R-005", "3.2", "接口需求应明确输入输出数据格式", True, [TemplateType.SRS], "请补充接口的数据格式说明"), ("R-006", "4", "文档应包含合格性需求章节", False, [TemplateType.SRS], "建议添加合格性需求章节"), ("R-007", "5", "需求应具备可追踪性", False, [TemplateType.SRS], "建议添加需求追踪矩阵"), ] for rid, sec, req, man, types, sug in rule_data: rules[rid] = GjbRule( id=rid, section=sec, requirement=req, mandatory=man, applicable_types=types, suggestion=sug, ) return rules def _init_mock_documents() -> dict[str, Document]: """初始化假数据文档库""" docs = {} d1 = Document( id="DOC-001", title="某型雷达系统软件需求规格说明书", template_id="TPL-001", current_version="1.0", content="# 1. 范围\n\n本文档描述了某型雷达系统的软件需求。\n\n# 2. 引用文档\n\n- GJB438C-2021\n\n# 3. 需求\n\n## 3.1 功能需求\n\n- FR-001: 目标探测功能\n- FR-002: 目标跟踪功能\n\n## 3.2 接口需求\n\n...", edit_history=[ {"version": "1.0", "editor": "张三", "timestamp": "2024-01-15T10:00:00Z"}, ], status=DocumentStatus.DRAFT, ) d2 = Document( id="DOC-002", title="某型通信系统软件设计说明书", template_id="TPL-002", current_version="1.0", content="# 1. 范围\n\n本文档描述了某型通信系统的软件设计。\n\n# 2. 引用文档\n\n- GJB438C-2021\n\n# 3. 软件体系结构设计\n\n采用分层架构设计。", status=DocumentStatus.SUBMITTED, ) docs[d1.id] = d1 docs[d2.id] = d2 return docs def _init_mock_skills() -> dict[str, Skill]: """初始化假数据Skills库""" skills = {} s1 = Skill( id="SK-001", name="文档合规审查", prompt_template="请根据GJB标准对以下文档进行合规性审查:{document_content}", associated_tools=["gjb_validator", "llm_analyzer"], input_definition={"document_id": "string"}, output_definition={"report": "object"}, orchestration_order=1, ) s2 = Skill( id="SK-002", name="文档智能生成", prompt_template="请根据模板{template_id}生成{title}文档初稿", associated_tools=["template_loader", "llm_generator"], input_definition={"template_id": "string", "title": "string"}, output_definition={"document": "object"}, orchestration_order=2, ) skills[s1.id] = s1 skills[s2.id] = s2 return skills def _init_mock_memories() -> dict[str, MemoryEntry]: """初始化假数据记忆库""" memories = {} m1 = MemoryEntry( id="MEM-001", session_id="SESS-001", summary="用户询问GJB438C标准要求", original_dialogue="用户:GJB438C标准对需求文档有什么要求?\n助手:GJB438C标准要求需求文档包含范围、引用文档、需求等章节。", memory_type=MemoryType.LONG_TERM, ) m2 = MemoryEntry( id="MEM-002", session_id="SESS-001", summary="用户创建了新文档模板", original_dialogue="用户:请创建一个新的SRS模板。\n助手:已创建完成,模板ID为TPL-003。", memory_type=MemoryType.SHORT_TERM, ) memories[m1.id] = m1 memories[m2.id] = m2 return memories def _init_mock_knowledge() -> dict[str, KnowledgeChunk]: """初始化假数据知识库""" chunks = {} c1 = KnowledgeChunk( id="KC-001", source_document="GJB438C-2021 军用软件文档编制要求", level=KnowledgeLevel.DOCUMENT, content="本标准规定了军用软件文档的编制要求,适用于军用软件的研制全过程。", metadata_tags=["GJB438C", "标准", "文档编制"], ) c2 = KnowledgeChunk( id="KC-002", source_document="GJB438C-2021 军用软件文档编制要求", level=KnowledgeLevel.CHAPTER, content="第4章 软件需求规格说明书编制要求:应包括范围、引用文档、需求等章节。", metadata_tags=["GJB438C", "SRS", "需求规格说明书"], ) c3 = KnowledgeChunk( id="KC-003", source_document="某型雷达系统软件需求规格说明书", level=KnowledgeLevel.PARAGRAPH, content="功能需求FR-001:系统应具备对空中目标的探测能力,探测距离不小于300km。", metadata_tags=["雷达系统", "功能需求", "FR-001"], ) chunks[c1.id] = c1 chunks[c2.id] = c2 chunks[c3.id] = c3 return chunks def _init_mock_models() -> dict[str, ModelProfile]: """初始化假数据模型配置""" models = {} mp1 = ModelProfile( id="MDL-001", vendor="华为", api_url="https://api.huawei.com/v1/chat", is_primary=True, weight=2, ) mp2 = ModelProfile( id="MDL-002", vendor="百度", api_url="https://api.baidu.com/v1/chat", is_primary=False, weight=1, ) models[mp1.id] = mp1 models[mp2.id] = mp2 return models def _init_mock_mcp_tools() -> dict[str, McpTool]: """初始化假数据MCP工具""" tools = {} t1 = McpTool( id="MCP-001", name="gjb_validator", description="GJB规则合规验证工具", json_schema={ "type": "object", "properties": { "document_id": {"type": "string", "description": "待验证文档ID"}, }, "required": ["document_id"], }, enabled=True, ) t2 = McpTool( id="MCP-002", name="llm_analyzer", description="大模型智能分析工具", json_schema={ "type": "object", "properties": { "prompt": {"type": "string", "description": "分析提示词"}, "model_id": {"type": "string", "description": "模型ID"}, }, "required": ["prompt"], }, enabled=True, ) tools[t1.id] = t1 tools[t2.id] = t2 return tools def _init_mock_workflows() -> dict[str, Workflow]: """初始化假数据工作流""" workflows = {} w1 = Workflow( id="WF-001", name="文档合规审查工作流", skill_ids=["SK-001", "SK-002"], logic={ "type": "sequential", "steps": [ {"skill_id": "SK-001", "on_success": "SK-002", "on_failure": "end"}, ], }, ) workflows[w1.id] = w1 return workflows # ============================================================================= # 服务类 # ============================================================================= class TemplateService: """文档模板管理服务(SRS-MACP_F-001)""" def __init__(self): self._templates = _init_mock_templates() def list_templates(self) -> list[Template]: """获取所有模板列表 Returns: list[Template]: 模板列表 """ return list(self._templates.values()) def get_template(self, template_id: str) -> Optional[Template]: """根据ID获取模板详情 Args: template_id: 模板ID Returns: Optional[Template]: 模板对象,不存在时返回None """ return self._templates.get(template_id) def create_template(self, template: Template) -> Template: """创建新模板 Args: template: 模板对象(不含ID时自动生成) Returns: Template: 创建后的模板 """ if not template.id: template.id = f"TPL-{uuid.uuid4().hex[:6].upper()}" template.created_at = datetime.now() self._templates[template.id] = template return template def delete_template(self, template_id: str) -> bool: """删除模板 Args: template_id: 模板ID Returns: bool: 是否删除成功 """ if template_id in self._templates: del self._templates[template_id] return True return False class DocumentService: """文档管理服务""" def __init__(self): self._documents = _init_mock_documents() def list_documents(self) -> list[Document]: """获取所有文档列表""" return list(self._documents.values()) def get_document(self, doc_id: str) -> Optional[Document]: """根据ID获取文档详情""" return self._documents.get(doc_id) def create_document(self, document: Document) -> Document: """创建新文档""" if not document.id: document.id = f"DOC-{uuid.uuid4().hex[:6].upper()}" document.created_at = datetime.now() document.updated_at = datetime.now() document.status = DocumentStatus.DRAFT self._documents[document.id] = document return document class GjbValidatorService: """GJB合规验证服务(SRS-MACP_F-002)""" def __init__(self): self._rules = _init_mock_rules() self._reports: dict[str, ValidationReport] = {} def list_rules(self) -> list[GjbRule]: """获取所有GJB规则""" return list(self._rules.values()) def validate_document(self, document: Document) -> ValidationReport: """对文档执行GJB合规验证 模拟校验文档结构、章节完整性与格式规范。 Args: document: 待验证文档 Returns: ValidationReport: 验证报告 """ report_id = f"VR-{uuid.uuid4().hex[:6].upper()}" non_compliant = [] compliant_count = 0 for rule in self._rules.values(): # 模拟校验逻辑:检查模板章节是否在文档内容中 found = rule.section in document.content or rule.requirement[:10] in document.content if found: compliant_count += 1 else: non_compliant.append({ "rule_id": rule.id, "section": rule.section, "requirement": rule.requirement, "severity": "critical" if rule.mandatory else "warning", "suggestion": rule.suggestion, }) report = ValidationReport( id=report_id, document_id=document.id, validated_at=datetime.now(), total_rules=len(self._rules), compliant_count=compliant_count, non_compliant_items=non_compliant, ) self._reports[report_id] = report return report def get_report(self, report_id: str) -> Optional[ValidationReport]: """获取验证报告""" return self._reports.get(report_id) class SkillService: """Skills流程编排服务(SRS-MACP_F-005)""" def __init__(self): self._skills = _init_mock_skills() self._workflows = _init_mock_workflows() def list_skills(self) -> list[Skill]: """获取所有Skill""" return list(self._skills.values()) def get_skill(self, skill_id: str) -> Optional[Skill]: """获取Skill详情""" return self._skills.get(skill_id) def create_skill(self, skill: Skill) -> Skill: """创建新Skill""" if not skill.id: skill.id = f"SK-{uuid.uuid4().hex[:6].upper()}" self._skills[skill.id] = skill return skill def execute_skill(self, skill_id: str, context: dict[str, Any]) -> dict[str, Any]: """模拟执行Skill Args: skill_id: Skill ID context: 执行上下文参数 Returns: dict: 执行结果 """ skill = self._skills.get(skill_id) if not skill: return {"success": False, "error": f"Skill {skill_id} not found"} return { "success": True, "skill_id": skill_id, "skill_name": skill.name, "result": f"已模拟执行Skill: {skill.name},输入上下文: {context}", "executed_at": datetime.now().isoformat(), } def list_workflows(self) -> list[Workflow]: """获取所有工作流""" return list(self._workflows.values()) def create_workflow(self, workflow: Workflow) -> Workflow: """创建工作流""" if not workflow.id: workflow.id = f"WF-{uuid.uuid4().hex[:6].upper()}" self._workflows[workflow.id] = workflow return workflow class MemoryService: """智能体记忆管理服务(SRS-MACP_F-006)""" def __init__(self): self._memories = _init_mock_memories() def list_memories(self, session_id: Optional[str] = None) -> list[MemoryEntry]: """获取记忆列表,可按会话ID过滤""" if session_id: return [m for m in self._memories.values() if m.session_id == session_id] return list(self._memories.values()) def get_memory(self, memory_id: str) -> Optional[MemoryEntry]: """获取记忆详情""" return self._memories.get(memory_id) def create_memory(self, memory: MemoryEntry) -> MemoryEntry: """创建记忆条目""" if not memory.id: memory.id = f"MEM-{uuid.uuid4().hex[:6].upper()}" memory.created_at = datetime.now() self._memories[memory.id] = memory return memory def delete_memory(self, memory_id: str) -> bool: """删除记忆""" if memory_id in self._memories: del self._memories[memory_id] return True return False def search_memories(self, query: str) -> list[MemoryEntry]: """基于语义相似度检索相关记忆(模拟实现)""" results = [] for mem in self._memories.values(): if query.lower() in mem.summary.lower() or query.lower() in mem.original_dialogue.lower(): results.append(mem) return results class KnowledgeBaseService: """知识库交互服务(SRS-MACP_F-008)""" def __init__(self): self._chunks = _init_mock_knowledge() def list_chunks(self, level: Optional[KnowledgeLevel] = None) -> list[KnowledgeChunk]: """获取知识库片段列表,可按层级过滤""" if level: return [c for c in self._chunks.values() if c.level == level] return list(self._chunks.values()) def get_chunk(self, chunk_id: str) -> Optional[KnowledgeChunk]: """获取知识库片段详情""" return self._chunks.get(chunk_id) def search_chunks(self, query: str, top_k: int = 5) -> list[KnowledgeChunk]: """搜索知识库片段(模拟向量检索)""" ranked = [] for chunk in self._chunks.values(): score = 0 if query.lower() in chunk.content.lower(): score += 3 for tag in chunk.metadata_tags: if query.lower() in tag.lower(): score += 2 if query.lower() in chunk.source_document.lower(): score += 1 if score > 0: ranked.append((score, chunk)) ranked.sort(key=lambda x: -x[0]) return [item[1] for item in ranked[:top_k]] class ModelService: """统一大模型接入服务(SRS-MACP_F-009)""" def __init__(self): self._models = _init_mock_models() def list_models(self) -> list[ModelProfile]: """获取所有模型配置""" return list(self._models.values()) def get_primary_model(self) -> Optional[ModelProfile]: """获取主用模型""" for model in self._models.values(): if model.is_primary: return model return None def health_check(self, model_id: str) -> dict[str, Any]: """模拟模型健康检查""" model = self._models.get(model_id) if not model: return {"status": "unknown", "error": "Model not found"} return { "model_id": model_id, "vendor": model.vendor, "status": model.health_status.value, "healthy": model.health_status == ModelStatus.ACTIVE, "checked_at": datetime.now().isoformat(), } def failover(self, failed_model_id: str) -> Optional[ModelProfile]: """模拟故障转移""" failed = self._models.get(failed_model_id) if failed: failed.health_status = ModelStatus.FAILOVER failed.is_primary = False # 找下一个可用模型 for model in self._models.values(): if model.id != failed_model_id and model.health_status == ModelStatus.ACTIVE: model.is_primary = True return model return None class MultimodalService: """多模态输入处理服务(SRS-MACP_F-010)""" def __init__(self): self._results: dict[str, MultimodalResult] = {} def process_file(self, file_name: str, file_type: FileType) -> MultimodalResult: """模拟多模态文件处理 根据文件类型模拟OCR识别、语音转写、关键帧提取等处理。 Args: file_name: 文件名 file_type: 文件类型 Returns: MultimodalResult: 处理结果 """ result_id = f"MTR-{uuid.uuid4().hex[:6].upper()}" result = MultimodalResult( id=result_id, file_type=file_type, file_name=file_name, extracted_at=datetime.now(), ) if file_type == FileType.IMAGE: result.ocr_text = f"[模拟OCR识别结果] 从图片 {file_name} 中识别到文本内容。" elif file_type == FileType.AUDIO: result.asr_transcript = f"[模拟ASR转写结果] 从音频 {file_name} 中转写出语音文本。" elif file_type == FileType.VIDEO: result.ocr_text = f"[模拟视频OCR] 从视频 {file_name} 中提取到字幕文本。" result.asr_transcript = f"[模拟视频ASR] 从视频 {file_name} 中转写出语音文本。" result.key_frames = [f"{file_name}_frame_001.jpg", f"{file_name}_frame_002.jpg"] self._results[result_id] = result return result def get_result(self, result_id: str) -> Optional[MultimodalResult]: """获取处理结果""" return self._results.get(result_id) class SandboxService: """安全沙箱执行服务(SRS-MACP_F-007)""" def __init__(self): self._execution_logs: list[dict[str, Any]] = [] def execute_code(self, code: str, language: str = "python") -> dict[str, Any]: """模拟沙箱环境执行代码 Args: code: 待执行代码 language: 编程语言 Returns: dict: 执行结果,包含输出、日志、执行时间等 """ execution_id = f"SBX-{uuid.uuid4().hex[:6].upper()}" log_entry = { "execution_id": execution_id, "language": language, "code_length": len(code), "started_at": datetime.now().isoformat(), "status": "completed", } # 模拟执行结果(沙箱内真实执行有安全风险,此处仅模拟) result = { "execution_id": execution_id, "language": language, "status": "completed", "stdout": f"[模拟沙箱执行] 已安全执行{language}代码,长度{len(code)}字符。\n资源限制:CPU 1核,内存 512MB,网络隔离。", "stderr": "", "execution_time_ms": 120, "resource_usage": { "cpu_percent": 45.0, "memory_mb": 128.0, }, "executed_at": datetime.now().isoformat(), } log_entry["status"] = "completed" log_entry["finished_at"] = datetime.now().isoformat() self._execution_logs.append(log_entry) return result def get_execution_logs(self) -> list[dict[str, Any]]: """获取沙箱执行日志""" return self._execution_logs class McpToolService: """MCP工具接入服务(SRS-MACP_F-004)""" def __init__(self): self._tools = _init_mock_mcp_tools() def list_tools(self) -> list[McpTool]: """获取所有MCP工具""" return list(self._tools.values()) def get_tool(self, tool_id: str) -> Optional[McpTool]: """获取MCP工具详情""" return self._tools.get(tool_id) def register_tool(self, tool: McpTool) -> McpTool: """注册MCP工具""" if not tool.id: tool.id = f"MCP-{uuid.uuid4().hex[:6].upper()}" tool.created_at = datetime.now() self._tools[tool.id] = tool return tool def delete_tool(self, tool_id: str) -> bool: """删除MCP工具""" if tool_id in self._tools: del self._tools[tool_id] return True return False def debug_tool(self, tool_id: str, input_data: dict[str, Any]) -> dict[str, Any]: """沙箱调试MCP工具 Args: tool_id: 工具ID input_data: 调试输入数据 Returns: dict: 调试结果 """ tool = self._tools.get(tool_id) if not tool: return {"success": False, "error": f"Tool {tool_id} not found"} return { "success": True, "tool_id": tool_id, "tool_name": tool.name, "input": input_data, "output": {"result": f"[模拟MCP工具调试] 工具 '{tool.name}' 执行成功"}, "debug_log": ["初始化工具上下文...", f"验证JSON Schema...", "执行工具逻辑...", "返回结果"], }