more_agent/app/services.py

756 lines
25 KiB
Python
Raw 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.

"""
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...", "执行工具逻辑...", "返回结果"],
}