工业故障分析 ---- 基于AI Agent workflow多轮对话的框架探索

20 阅读23分钟

需求背景

存在一个工业的故障库,其以及一些业务领域的库表设计(点检,维修记录,租户场景表)等.

包括

  • 故障类型fault_type(轴承)

  • 严重程度status_level(正常,注意,警告,危险)

  • 发现问题(question) : "频谱存在一定的轴承异常相关的特征频率,并存在轻微的劣化确实,可能存在早期的轴承损伤。 维护建议(建议择机检查轴承以及相关部件的磨损,根据检查结果进行维护,注意轴承的噪音,温度变化"

假设用户提出一个问题,比如"当前设备出现了哪些问题?" ,需要根据问题结合故障库给出行动建议列表。在行动建议明确后,用户重新提出问题,比如"在我当前的租户场景中,三个月的台账中关联相关的点检记录,设备是否发生损坏,如果出现怎么预防"。可以根据问题中的关键字提取相关的语义向量,对应到数据库表的schema上进行意图理解

并写SQL封装成对应的mcp tool, 根据不同业务领域分组toolset

比如点检业务领域下,发生三个月,一个月的RMS特征值趋势异常波动,这算是两个mcp tool. 在LLM模型收到用户提出的"在我当前的租户场景中,三个月的台账中关联相关的点检记录,设备是否发生损坏,如果出现怎么预防",会自动找到对应的mcp toolset业务领域,组装不同的mcp tool进行查询

整体架构设计

基本架构: github.com/shaowenchen…

  • 业务领域工具集分组:将MCP工具按照点检、维修、故障分析等业务领域进行分组管理

  • 语义向量匹配:使用向量嵌入技术进行意图理解和工具选择

  • 自动查询组装:根据用户问题自动选择和组装相关的MCP工具

  • 知识库驱动:基于工业故障库提供专业的故障诊断和预防建议

  • 租户隔离:支持多租户场景下的数据隔离和权限控制

sequenceDiagram    
    participant User as 用户    
    participant API as API服务器    
    participant SemanticProcessor as 语义处理器    
    participant QueryOrchestrator as 查询协调器    
    participant FaultManager as 故障管理器    
    participant MCPServer as MCP服务器    
    participant Database as 数据库    
    participant LLM as LLM服务    
    participant VectorEngine as 向量引擎    
    participant PreventionAdvisor as 预防建议器    
    
    Note over User,PreventionAdvisor: 用户查询:"当前设备出现了哪些问题?"    
        
    User->>API: POST /api/fault-diagnosis    
    API->>QueryOrchestrator: ProcessUserQuery(tenantID, query)    
        
    QueryOrchestrator->>SemanticProcessor: ExtractSemanticVector(query)    
    SemanticProcessor->>VectorEngine: Encode(query)    
    VectorEngine-->>SemanticProcessor: vector[]    
    SemanticProcessor-->>QueryOrchestrator: semanticVector    
        
    QueryOrchestrator->>SemanticProcessor: ClassifyBusinessDomain(query)    
    SemanticProcessor->>VectorEngine: CalculateSimilarity(vector, domainVectors)    
    VectorEngine-->>SemanticProcessor: domainScores    
    SemanticProcessor-->>QueryOrchestrator: domain="fault_analysis", confidence=0.85    
        
    QueryOrchestrator->>FaultManager: GetDomainTools(domain)    
    FaultManager-->>QueryOrchestrator: faultAnalysisTools[]    
        
    QueryOrchestrator->>QueryOrchestrator: selectRelevantTools(query, tools)    
    QueryOrchestrator->>QueryOrchestrator: extractQueryParameters(query, tenantID)    
        
    par 并发执行MCP工具    
        QueryOrchestrator->>MCPServer: ExecuteTool("check_equipment_status")    
        MCPServer->>Database: SELECT * FROM inspection_records WHERE status IN ('warning','danger')    
        Database-->>MCPServer: equipmentStatusData    
        MCPServer-->>QueryOrchestrator: toolResult1    
    and    
        QueryOrchestrator->>MCPServer: ExecuteTool("query_fault_history")    
        MCPServer->>Database: SELECT * FROM fault_knowledge_base WHERE keywords MATCH query    
        Database-->>MCPServer: faultHistoryData    
        MCPServer-->>QueryOrchestrator: toolResult2    
    end    
        
    QueryOrchestrator->>QueryOrchestrator: aggregateResults(toolResults)    
    QueryOrchestrator-->>API: queryResult    
        
    API->>PreventionAdvisor: GeneratePreventionAdvice(queryResult)    
    PreventionAdvisor->>PreventionAdvisor: detectAnomalies(queryResult)    
    PreventionAdvisor->>Database: matchSimilarCases(anomalies)    
    Database-->>PreventionAdvisor: similarCases[]    
        
    PreventionAdvisor->>LLM: GenerateAdvice(context)    
    LLM-->>PreventionAdvisor: preventionAdvice    
    PreventionAdvisor-->>API: advice    
        
    API-->>User: 故障诊断结果 + 预防建议

复杂查询处理流程时序图

sequenceDiagram    
    participant User as 用户    
    participant API as API服务器    
    participant QueryOrchestrator as 查询协调器    
    participant SemanticProcessor as 语义处理器    
    participant MCPToolset as MCP工具集    
    participant InspectionTools as 点检工具    
    participant MaintenanceTools as 维修工具    
    participant Database as 数据库    
    
    Note over User,Database: 复杂查询:"在我当前的租户场景中,三个月的台账中关联相关的点检记录,设备是否发生损坏,如果出现怎么预防"    
        
    User->>API: POST /api/fault-diagnosis (复杂查询)    
    API->>QueryOrchestrator: ProcessUserQuery(tenantID, complexQuery)    
        
    QueryOrchestrator->>SemanticProcessor: ExtractKeywords(complexQuery)    
    SemanticProcessor-->>QueryOrchestrator: keywords=["三个月","台账","点检记录","设备损坏","预防"]    
        
    QueryOrchestrator->>SemanticProcessor: ClassifyBusinessDomain(keywords)    
    SemanticProcessor-->>QueryOrchestrator: domains=["inspection", "maintenance"]    
        
    QueryOrchestrator->>MCPToolset: GetToolsByDomains(domains)    
    MCPToolset-->>QueryOrchestrator: selectedTools[]    
        
    par 点检业务领域工具执行    
        QueryOrchestrator->>InspectionTools: query_rms_trend_3months(tenantID)    
        InspectionTools->>Database: SELECT inspection_date, rms_value FROM inspection_records WHERE tenant_id=? AND inspection_date >= DATE_SUB(NOW(), INTERVAL 3 MONTH)    
        Database-->>InspectionTools: rmsData[]    
        InspectionTools-->>QueryOrchestrator: rmsTrendResult    
    and    
        QueryOrchestrator->>InspectionTools: check_equipment_damage(tenantID)    
        InspectionTools->>Database: SELECT equipment_code, status FROM inspection_records WHERE tenant_id=? AND status IN ('warning','danger')    
        Database-->>InspectionTools: damageData[]    
        InspectionTools-->>QueryOrchestrator: damageCheckResult    
    and    
        QueryOrchestrator->>MaintenanceTools: query_maintenance_history(tenantID, 3months)    
        MaintenanceTools->>Database: SELECT * FROM maintenance_records WHERE tenant_id=? AND maintenance_date >= DATE_SUB(NOW(), INTERVAL 3 MONTH)    
        Database-->>MaintenanceTools: maintenanceHistory[]    
        MaintenanceTools-->>QueryOrchestrator: maintenanceResult    
    end    
        
    QueryOrchestrator->>QueryOrchestrator: correlateResults(rmsTrend, damageCheck, maintenance)    
    QueryOrchestrator-->>API: correlatedAnalysis    
        
    API-->>User: 综合分析结果 + 预防措施建议

MCP注册工具表示

sequenceDiagram    
    participant System as 系统启动    
    participant FaultManager as 故障管理器    
    participant MCPServer as MCP服务器    
    participant Database as 数据库    
    participant ToolRegistry as 工具注册表    
    
    System->>FaultManager: Initialize()    
    FaultManager->>Database: LoadBusinessDomainConfig()    
    Database-->>FaultManager: domainConfigs[]    
        
    loop 为每个业务领域    
        FaultManager->>ToolRegistry: RegisterDomainTools(domain)    
            
        loop 为每个工具    
            ToolRegistry->>MCPServer: AddTool(toolName, toolOptions)    
            MCPServer->>MCPServer: CreateToolHandler(sqlTemplate, vectorFields)    
                
            Note over MCPServer: 工具处理器包含SQL模板和向量字段    
                
            MCPServer->>ToolRegistry: ToolRegistered(toolName)    
        end    
            
        ToolRegistry-->>FaultManager: DomainToolsRegistered(domain)    
    end    
        
    FaultManager-->>System: InitializationComplete    
        
    Note over System,ToolRegistry: 系统现在可以处理故障诊断查询
  1. 数据库表结构设计

故障库核心表

-- 故障类型表
CREATE TABLE fault_types (  
    id BIGINT PRIMARY KEY AUTO_INCREMENT,  
    fault_type VARCHAR(100) NOT NULL COMMENT '故障类型如轴承',  
    category VARCHAR(50) NOT NULL COMMENT '故障分类',  
    description TEXT COMMENT '故障描述',  
    created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP  
);  
  
-- 严重程度表
CREATE TABLE severity_levels (  
    id BIGINT PRIMARY KEY AUTO_INCREMENT,  
    level_code VARCHAR(20) NOT NULL COMMENT '等级代码',  
    level_name VARCHAR(50) NOT NULL COMMENT '正常/注意/警告/危险',  
    threshold_value DECIMAL(10,4) COMMENT '阈值',  
    color_code VARCHAR(7) COMMENT '颜色代码'  
);  
  
-- 故障库主表
CREATE TABLE fault_knowledge_base (  
    id BIGINT PRIMARY KEY AUTO_INCREMENT,  
    fault_type_id BIGINT NOT NULL,  
    severity_level_id BIGINT NOT NULL,  
    question TEXT NOT NULL COMMENT '发现问题描述',  
    maintenance_suggestion TEXT NOT NULL COMMENT '维护建议',  
    keywords TEXT COMMENT '关键词,用于语义匹配',  
    vector_embedding BLOB COMMENT '语义向量',  
    created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,  
    FOREIGN KEY (fault_type_id) REFERENCES fault_types(id),  
    FOREIGN KEY (severity_level_id) REFERENCES severity_levels(id)  
);

业务领域表

-- 租户表
CREATE TABLE tenants (  
    id BIGINT PRIMARY KEY AUTO_INCREMENT,  
    tenant_code VARCHAR(50) NOT NULL UNIQUE,  
    tenant_name VARCHAR(100) NOT NULL,  
    created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP  
);  
  
-- 设备表
CREATE TABLE equipment (  
    id BIGINT PRIMARY KEY AUTO_INCREMENT,  
    tenant_id BIGINT NOT NULL,  
    equipment_code VARCHAR(100) NOT NULL,  
    equipment_name VARCHAR(200) NOT NULL,  
    equipment_type VARCHAR(50),  
    location VARCHAR(200),  
    FOREIGN KEY (tenant_id) REFERENCES tenants(id)  
);  
  
-- 点检记录表
CREATE TABLE inspection_records (  
    id BIGINT PRIMARY KEY AUTO_INCREMENT,  
    tenant_id BIGINT NOT NULL,  
    equipment_id BIGINT NOT NULL,  
    inspection_date DATETIME NOT NULL,  
    inspector VARCHAR(100),  
    rms_value DECIMAL(10,6) COMMENT 'RMS特征值',  
    vibration_data JSON COMMENT '振动数据',  
    temperature DECIMAL(5,2),  
    noise_level DECIMAL(5,2),  
    status VARCHAR(20) DEFAULT 'normal',  
    remarks TEXT,  
    FOREIGN KEY (tenant_id) REFERENCES tenants(id),  
    FOREIGN KEY (equipment_id) REFERENCES equipment(id)  
);  
  
-- 维修记录表
CREATE TABLE maintenance_records (  
    id BIGINT PRIMARY KEY AUTO_INCREMENT,  
    tenant_id BIGINT NOT NULL,  
    equipment_id BIGINT NOT NULL,  
    maintenance_date DATETIME NOT NULL,  
    maintenance_type VARCHAR(50) COMMENT '保养/维修/更换',  
    fault_type_id BIGINT,  
    description TEXT,  
    cost DECIMAL(10,2),  
    technician VARCHAR(100),  
    FOREIGN KEY (tenant_id) REFERENCES tenants(id),  
    FOREIGN KEY (equipment_id) REFERENCES equipment(id),  
    FOREIGN KEY (fault_type_id) REFERENCES fault_types(id)  
);
  1. MCP工具扩展方案

基于现有的MCP工具实现 pipelinerun.go:221-267 ,我们需要扩展业务领域特定的工具集:

业务领域工具集定义

 // pkg/copilot/fault_diagnosis.go
package copilot  
  
type FaultDiagnosisManager struct {  
    dbConnection *sql.DB  
    vectorEngine VectorSearchEngine  
    businessDomains map[string][]MCPTool  
}  
  
type BusinessDomain string  
  
const (  
    DomainInspection  BusinessDomain = "inspection"   //检修
    DomainMaintenance BusinessDomain = "maintenance"   //维护
    DomainFaultAnalysis BusinessDomain = "fault_analysis"  //故障分析
)  
  
type FaultMCPTool struct {  
    Name string  
    Domain BusinessDomain  
    Description string  
    SQLTemplate string  
    VectorFields []string  
}

点检业务领域工具集

var InspectionDomainTools = []FaultMCPTool{  
    {  
        Name: "query_rms_trend_3months",  
        Domain: DomainInspection,  
        Description: "查询设备三个月内RMS特征值趋势",  
        SQLTemplate: `  
            SELECT inspection_date, rms_value, equipment_code   
            FROM inspection_records ir   
            JOIN equipment e ON ir.equipment_id = e.id   
            WHERE ir.tenant_id = ? AND ir.inspection_date >= DATE_SUB(NOW(), INTERVAL 3 MONTH)  
            ORDER BY inspection_date DESC  
        `,  
        VectorFields: []string{"三个月", "RMS", "特征值", "趋势"},  
    },  
    {  
        Name: "check_equipment_damage",  
        Domain: DomainInspection,   
        Description: "检查设备是否发生损坏",  
        SQLTemplate: `  
            SELECT e.equipment_code, ir.status, ir.inspection_date, ir.remarks  
            FROM inspection_records ir  
            JOIN equipment e ON ir.equipment_id = e.id  
            WHERE ir.tenant_id = ? AND ir.status IN ('warning', 'danger')  
            ORDER BY ir.inspection_date DESC  
        `,  
        VectorFields: []string{"设备", "损坏", "检查", "状态"},  
    },  
}

语义向量处理和意图理解

扩展现有的意图理解功能 llm.go:145-191 :

 // pkg/copilot/semantic_processor.go
type SemanticProcessor struct {  
    vectorizer VectorEngine  
    intentClassifier IntentClassifier  
}  
  
func (sp *SemanticProcessor) ExtractSemanticVector(query string) ([]float64, error) {  
    // 使用嵌入模型提取语义向量
    return sp.vectorizer.Encode(query)  
}  
  
func (sp *SemanticProcessor) ClassifyBusinessDomain(query string) (BusinessDomain, float64, error) {  
    vector, err := sp.ExtractSemanticVector(query)  
    if err != nil {  
        return "", 0, err  
    }  
      
    // 计算与各业务领域关键词的相似度
    domainScores := make(map[BusinessDomain]float64)  
      
    for domain, tools := range sp.getBusinessDomainTools() {  
        score := sp.calculateDomainSimilarity(vector, tools)  
        domainScores[domain] = score  
    }  
      
    // 返回最高分的业务领域
    return sp.getBestMatch(domainScores)  
}

增强的MCP服务器实现

基于现有的MCP服务器 api-copilot.go:90-131 ,增加故障诊断能力:

 // pkg/server/fault_mcp_server.go
func NewFaultDiagnosisMCPServer(dbConfig DatabaseConfig, vectorConfig VectorConfig) (*server.SSEServer, error) {  
    faultManager, err := NewFaultDiagnosisManager(dbConfig, vectorConfig)  
    if err != nil {  
        return nil, err  
    }  
      
    mcpServer := server.NewMCPServer(  
        "Fault Diagnosis MCP Server",  
        "1.0.0",  
        server.WithResourceCapabilities(true, true),  
        server.WithLogging(),  
    )  
      
    // 注册业务领域工具集
    err = faultManager.RegisterBusinessDomainTools(mcpServer)  
    if err != nil {  
        return nil, err  
    }  
      
    return server.NewSSEServer(mcpServer), nil  
}

工具自动组装和查询逻辑

 // pkg/copilot/query_orchestrator.go
type QueryOrchestrator struct {  
    semanticProcessor *SemanticProcessor  
    faultManager *FaultDiagnosisManager  
    mcpClient *MCPClient  
}  
  
func (qo *QueryOrchestrator) ProcessUserQuery(tenantID string, query string) (*QueryResult, error) {  
    // 1. 语义向量提取和意图分类
    domain, confidence, err := qo.semanticProcessor.ClassifyBusinessDomain(query)  
    if err != nil {  
        return nil, err  
    }  
      
    // 2. 获取对应业务领域的工具集
    domainTools := qo.faultManager.GetDomainTools(domain)  
      
    // 3. 基于关键词匹配选择最相关的工具
    selectedTools := qo.selectRelevantTools(query, domainTools)  
      
    // 4. 组装查询参数
    queryParams := qo.extractQueryParameters(query, tenantID)  
      
    // 5. 并发执行多个MCP工具
    results := make(chan ToolResult, len(selectedTools))  
    for _, tool := range selectedTools {  
        go func(t FaultMCPTool) {  
            result := qo.executeMCPTool(t, queryParams)  
            results <- result  
        }(tool)  
    }  
      
    // 6. 聚合结果并生成响应
    return qo.aggregateResults(results, len(selectedTools))  
}

故障预防建议生成(预防行动建议)

 // pkg/copilot/prevention_advisor.go
type PreventionAdvisor struct {  
    knowledgeBase *FaultKnowledgeBase  
    llmClient LLMClient  
}  
  
func (pa *PreventionAdvisor) GeneratePreventionAdvice(queryResult *QueryResult) (*PreventionAdvice, error) {  
    // 1. 分析查询结果中的异常模式
    anomalies := pa.detectAnomalies(queryResult)  
      
    // 2. 从故障库中匹配相似案例
    similarCases := pa.matchSimilarCases(anomalies)  
      
    // 3. 结合LLM生成个性化建议
    context := pa.buildAdviceContext(queryResult, similarCases)  
    advice := pa.llmClient.GenerateAdvice(context)  
      
    return advice, nil  
}

集成流程

在现有的Copilot处理流程 api-copilot.go:40-83 基础上,增加故障诊断分支:

func PostFaultDiagnosisCopilot(c *gin.Context) {  
    // 解析输入
    var req FaultDiagnosisRequest  
    if err := c.ShouldBindJSON(&req); err != nil {  
        showError(c, "Invalid request: "+err.Error())  
        return  
    }  
      
    // 初始化查询协调器
    orchestrator := NewQueryOrchestrator(globalSemanticProcessor, globalFaultManager)  
      
    // 处理用户查询
    result, err := orchestrator.ProcessUserQuery(req.TenantID, req.Query)  
    if err != nil {  
        showError(c, err.Error())  
        return  
    }  
      
    // 生成预防建议
    if req.IncludePreventionAdvice {  
        advisor := NewPreventionAdvisor(globalKnowledgeBase, globalLLMClient)  
        advice, _ := advisor.GeneratePreventionAdvice(result)  
        result.PreventionAdvice = advice  
    }  
      
    showData(c, result)  
}

语义向量提取实现

首先,我们需要创建一个语义处理器来处理向量提取:

 // pkg/copilot/semantic_processor.go
package copilot  
  
import (  
    "context"  
    "encoding/json"  
    "fmt"  
    "math"  
    "strings"  
    "github.com/sashabaranov/go-openai"  
)  
  
type SemanticProcessor struct {  
    client *openai.Client  
    model  string  
    domainKeywords map[BusinessDomain][]string  
}  
  
type BusinessDomain string  
  
const (  
    DomainInspection    BusinessDomain = "inspection"  
    DomainMaintenance   BusinessDomain = "maintenance"  
    DomainFaultAnalysis BusinessDomain = "fault_analysis"  
)  
  
func NewSemanticProcessor(endpoint, key, model string) *SemanticProcessor {  
    config := openai.DefaultConfig(key)  
    if endpoint != "" {  
        config.BaseURL = endpoint  
    }  
      
    return &SemanticProcessor{  
        client: openai.NewClientWithConfig(config),  
        model:  model,  
        domainKeywords: map[BusinessDomain][]string{  
            // todo 这里需要把对应的特征值语料替换成设备
            DomainInspection: {"点检", "检查", "巡检", "RMS", "振动", "温度", "噪音", "特征值", "趋势"},  
            DomainMaintenance: {"维修", "保养", "更换", "维护", "修理", "台账", "记录", "历史"},  
            DomainFaultAnalysis: {"故障", "异常", "损坏", "问题", "诊断", "分析", "预防", "建议"},  
        },  
    }  
}  
  
// 提取语义向量
func (sp *SemanticProcessor) ExtractSemanticVector(query string) ([]float64, error) {  
    resp, err := sp.client.CreateEmbeddings(  
        context.Background(),  
        openai.EmbeddingRequest{  
            Input: []string{query},  
            Model: openai.AdaEmbeddingV2, // 或使用其他嵌入模型
        },  
    )  
    if err != nil {  
        return nil, fmt.Errorf("failed to create embeddings: %w", err)  
    }  
      
    if len(resp.Data) == 0 {  
        return nil, fmt.Errorf("no embeddings returned")  
    }  
      
    return resp.Data[0].Embedding, nil  
}

意图分类实现

 // 业务领域分类
func (sp *SemanticProcessor) ClassifyBusinessDomain(query string) (BusinessDomain, float64, error) {  
    queryVector, err := sp.ExtractSemanticVector(query)  
    if err != nil {  
        return "", 0, err  
    }  
      
    domainScores := make(map[BusinessDomain]float64)  
      
    // 计算与各业务领域关键词的相似度
    for domain, keywords := range sp.domainKeywords {  
        score := sp.calculateDomainSimilarity(query, queryVector, keywords)  
        domainScores[domain] = score  
    }  
      
    // 返回最高分的业务领域
    bestDomain, confidence := sp.getBestMatch(domainScores)  
    return bestDomain, confidence, nil  
}  
  
// 计算领域相似度
func (sp *SemanticProcessor) calculateDomainSimilarity(query string, queryVector []float64, keywords []string) float64 {  
    var totalScore float64  
    var matchCount int  
      
    // 关键词匹配得分
    queryLower := strings.ToLower(query)  
    for _, keyword := range keywords {  
        if strings.Contains(queryLower, strings.ToLower(keyword)) {  
            totalScore += 1.0  
            matchCount++  
        }  
    }  
      
    // 语义向量相似度得分
    if len(queryVector) > 0 {  
        keywordText := strings.Join(keywords, " ")  
        keywordVector, err := sp.ExtractSemanticVector(keywordText)  
        if err == nil {  
            similarity := sp.cosineSimilarity(queryVector, keywordVector)  
            totalScore += similarity * 2.0 // 语义相似度权重更高
        }  
    }  
      
    // 归一化得分
    if matchCount > 0 {  
        totalScore = totalScore / float64(len(keywords)) * float64(matchCount)  
    }  
      
    return totalScore  
}  
  
// 余弦相似度计算
func (sp *SemanticProcessor) cosineSimilarity(a, b []float64) float64 {  
    if len(a) != len(b) {  
        return 0  
    }  
      
    var dotProduct, normA, normB float64  
    for i := range a {  
        dotProduct += a[i] * b[i]  
        normA += a[i] * a[i]  
        normB += b[i] * b[i]  
    }  
      
    if normA == 0 || normB == 0 {  
        return 0  
    }  
      
    return dotProduct / (math.Sqrt(normA) * math.Sqrt(normB))  
}  
  
// 获取最佳匹配
func (sp *SemanticProcessor) getBestMatch(scores map[BusinessDomain]float64) (BusinessDomain, float64) {  
    var bestDomain BusinessDomain  
    var bestScore float64  
      
    for domain, score := range scores {  
        if score > bestScore {  
            bestScore = score  
            bestDomain = domain  
        }  
    }  
      
    return bestDomain, bestScore  
}

关键词提取实现

 // 提取查询关键词
func (sp *SemanticProcessor) ExtractKeywords(query string) ([]string, error) {  
    // 使用LLM提取关键词
    prompt := fmt.Sprintf(`  
请从以下查询中提取关键词,特别关注时间、业务领域、操作类型等信息:  
查询:%s  
  
请以JSON格式返回关键词列表:  
{"keywords": ["关键词1", "关键词2", ...]}  
`, query)  
      
    resp, err := sp.client.CreateChatCompletion(  
        context.Background(),  
        openai.ChatCompletionRequest{  
            Model: sp.model,  
            Messages: []openai.ChatCompletionMessage{  
                {  
                    Role:    openai.ChatMessageRoleUser,  
                    Content: prompt,  
                },  
            },  
            Temperature: 0.1,  
        },  
    )  
      
    if err != nil {  
        return nil, fmt.Errorf("failed to extract keywords: %w", err)  
    }  
      
    if len(resp.Choices) == 0 {  
        return nil, fmt.Errorf("no response from LLM")  
    }  
      
    // 解析JSON响应
    var result struct {  
        Keywords []string `json:"keywords"`  
    }  
      
    content := resp.Choices[0].Message.Content  
    if err := json.Unmarshal([]byte(content), &result); err != nil {  
        // 如果JSON解析失败,使用简单的字符串分割
        return sp.simpleKeywordExtraction(query), nil  
    }  
      
    return result.Keywords, nil  
}  
  
// 简单关键词提取(备用方案)
func (sp *SemanticProcessor) simpleKeywordExtraction(query string) []string {  
    // 预定义的关键词模式  (todo 需要把特征值相关的设备信息统一替换成设备)
    patterns := []string{  
        "三个月", "一个月", "半年", "一年",  
        "点检", "维修", "保养", "检查",  
        "设备", "故障", "损坏", "异常",  
        "RMS", "振动", "温度", "噪音",  
        "预防", "建议", "台账", "记录",  
    }  
      
    var keywords []string  
    queryLower := strings.ToLower(query)  
      
    for _, pattern := range patterns {  
        if strings.Contains(queryLower, strings.ToLower(pattern)) {  
            keywords = append(keywords, pattern)  
        }  
    }  
      
    return keywords  
}

集成到查询协调器

 // pkg/copilot/query_orchestrator.go
type QueryOrchestrator struct {  
    semanticProcessor *SemanticProcessor  
    faultManager      *FaultDiagnosisManager  
    logger           *log.Logger  
}  
  
func NewQueryOrchestrator(endpoint, key, model string, faultManager *FaultDiagnosisManager) *QueryOrchestrator {  
    return &QueryOrchestrator{  
        semanticProcessor: NewSemanticProcessor(endpoint, key, model),  
        faultManager:      faultManager,  
        logger:           log.New(os.Stdout, "[QueryOrchestrator] ", log.LstdFlags),  
    }  
}  
  
func (qo *QueryOrchestrator) ProcessUserQuery(tenantID string, query string) (*QueryResult, error) {  
    qo.logger.Printf("Processing query: %s", query)  
      
    // 1. 提取语义向量
    semanticVector, err := qo.semanticProcessor.ExtractSemanticVector(query)  
    if err != nil {  
        return nil, fmt.Errorf("failed to extract semantic vector: %w", err)  
    }  
      
    // 2. 分类业务领域
    domain, confidence, err := qo.semanticProcessor.ClassifyBusinessDomain(query)  
    if err != nil {  
        return nil, fmt.Errorf("failed to classify business domain: %w", err)  
    }  
      
    qo.logger.Printf("Classified domain: %s (confidence: %.2f)", domain, confidence)  
      
    // 3. 提取关键词
    keywords, err := qo.semanticProcessor.ExtractKeywords(query)  
    if err != nil {  
        qo.logger.Printf("Warning: failed to extract keywords: %v", err)  
        keywords = []string{} // 继续处理,使用空关键词列表
    }  
      
    // 4. 获取相关工具
    domainTools := qo.faultManager.GetDomainTools(domain)  
    selectedTools := qo.selectRelevantTools(query, semanticVector, domainTools)  
      
    // 5. 执行查询
    return qo.executeQuery(tenantID, selectedTools, keywords)  
}  
  
// 选择相关工具
func (qo *QueryOrchestrator) selectRelevantTools(query string, queryVector []float64, tools []FaultMCPTool) []FaultMCPTool {  
    var selectedTools []FaultMCPTool  
    threshold := 0.3 // 相似度阈值
      
    for _, tool := range tools {  
        // 计算工具与查询的相似度
        toolText := tool.Description + " " + strings.Join(tool.VectorFields, " ")  
        toolVector, err := qo.semanticProcessor.ExtractSemanticVector(toolText)  
        if err != nil {  
            continue  
        }  
          
        similarity := qo.semanticProcessor.cosineSimilarity(queryVector, toolVector)  
        if similarity > threshold {  
            selectedTools = append(selectedTools, tool)  
            qo.logger.Printf("Selected tool: %s (similarity: %.2f)", tool.Name, similarity)  
        }  
    }  
      
    return selectedTools  
}

使用示例

 // 在API处理函数中使用
func PostFaultDiagnosisCopilot(c *gin.Context) {  
    var req struct {  
        TenantID string `json:"tenant_id"`  
        Query    string `json:"query"`  
    }  
      
    if err := c.ShouldBindJSON(&req); err != nil {  
        c.JSON(400, gin.H{"error": err.Error()})  
        return  
    }  
      
    // 初始化组件
    orchestrator := NewQueryOrchestrator(  
        GlobalConfig.Copilot.Endpoint,  
        GlobalConfig.Copilot.Key,  
        GlobalConfig.Copilot.Model,  
        globalFaultManager,  
    )  
      
    // 处理查询
    result, err := orchestrator.ProcessUserQuery(req.TenantID, req.Query)  
    if err != nil {  
        c.JSON(500, gin.H{"error": err.Error()})  
        return  
    }  
      
    c.JSON(200, result)  
}

数控机床场景将提供PostFaultDiagnosisCopilot的具体实现。

数控机床故障诊断API实现

用户提出一个问题,比如"当前设备出现了哪些问题?" ,需要根据问题结合故障库给出行动建议列表。在行动建议明确后,用户重新提出问题,比如"在我当前的租户场景中,三个月的台账中关联相关的点检记录,设备是否发生损坏,如果出现怎么预防"

推荐列表的返回数据会在CNCPreventionAdvice结构体当中返回, 分为三个层次

  • ImmediateActions: 立即行动建议

  • ShortTermPlan: 短期计划建议

  • LongTermPlan: 长期计划建议

场景1:"当前设备出现了哪些问题?"

这个查询会触发:

  1. 语义分析识别为故障分析领域
  2. 执行相关MCP工具查询设备状态 (目前设备状态处于事后的事故记录库,在交给MCP tool做业务领域的关联之前,需要有事故记录库的数据做辅助验证, MCP tool决策里面的条件暂时写死成
1. 检索智能体信息

   - 查询 设备主表 设备别名表 设备故障表
       - 智能体信息, 设备ID, 设备类型, 点位名称, 点位类型, 时间, 指标报警等级, 指标趋势, 状态分, 故障类型, 诊断分析.
       状态分机,故障类型, 诊断分析, 发现问题, 维护建议,剩余寿命
          - 状态分级 x 时间 
            - 30天内正常 对比180-30天内最大异常等级 
                - 正常 selet * from 

                  解释: 这里的正常指的是根据设备的事故记录库进行推理

如果要先根据部件归因输出设备维度的汇总建议, 需要建立 "设备-部件-故障"和 " 故障-现象-原因"的知识图谱.

lightRAG(根据LLM提取语义向量构建知识图谱): blog.csdn.net/huang9604/a…

  1. 故障分析器分析检测到的问题
  2. response.PreventionAdvice 中返回对应的行动建议

场景2:"在我当前的租户场景中,三个月的台账中关联相关的点检记录,设备是否发生损坏,如果出现怎么预防"

这个复杂查询会:

  1. 语义处理器提取关键词["三个月","台账","点检记录","设备损坏","预防"]

  2. 分类到点检和维护业务领域

  3. 并发执行多个MCP工具(如query_rms_trend_3months, check_equipment_damage

  4. 关联分析结果

  5. CNCPreventionAdvice 中返回综合的预防建议

// pkg/server/api-cnc-fault-diagnosis.go
package server

import (
    "fmt"
    "log"
    "strings"
    "time"
    
    "github.com/gin-gonic/gin"
    opscopilot "github.com/shaowenchen/ops/pkg/copilot"
    opslog "github.com/shaowenchen/ops/pkg/log"
)

// 数控机床故障诊断请求结构
type CNCFaultDiagnosisRequest struct {
    TenantID    string `json:"tenant_id" binding:"required"`
    Query       string `json:"query" binding:"required"`
    MachineID   string `json:"machine_id,omitempty"`
    MachineType string `json:"machine_type,omitempty"` // 如:车床、铣床、加工中心
    IncludePreventionAdvice bool `json:"include_prevention_advice,omitempty"`
}

// 数控机床故障诊断响应结构(行动列表)
type CNCFaultDiagnosisResponse struct {
    MachineInfo      *CNCMachineInfo      `json:"machine_info,omitempty"`
    FaultAnalysis    *CNCFaultAnalysis    `json:"fault_analysis"`
    InspectionData   *CNCInspectionData   `json:"inspection_data,omitempty"`
    MaintenanceHist  *CNCMaintenanceHist  `json:"maintenance_history,omitempty"`
    PreventionAdvice *CNCPreventionAdvice `json:"prevention_advice,omitempty"`
    ExecutionTime    time.Duration        `json:"execution_time"`
}

type CNCMachineInfo struct {
    MachineID   string `json:"machine_id"`
    MachineName string `json:"machine_name"`
    MachineType string `json:"machine_type"`
    Location    string `json:"location"`
    Status      string `json:"status"`
}

type CNCFaultAnalysis struct {
    DetectedFaults []CNCFault `json:"detected_faults"`
    SeverityLevel  string     `json:"severity_level"`
    RiskScore      float64    `json:"risk_score"`
    Summary        string     `json:"summary"`
}

type CNCFault struct {
    FaultType    string  `json:"fault_type"`    // 主轴故障、刀具磨损、导轨异常等
    Component    string  `json:"component"`     // 主轴、刀塔、X轴导轨等
    Severity     string  `json:"severity"`      // 正常、注意、警告、危险
    Confidence   float64 `json:"confidence"`
    Description  string  `json:"description"`
    Symptoms     []string `json:"symptoms"`
}

type CNCInspectionData struct {
    SpindleData    *SpindleInspection    `json:"spindle_data,omitempty"`
    AxisData       *AxisInspection       `json:"axis_data,omitempty"`
    ToolData       *ToolInspection       `json:"tool_data,omitempty"`
    VibrationData  *VibrationInspection  `json:"vibration_data,omitempty"`
}

type SpindleInspection struct {
    RPM         float64 `json:"rpm"`
    Temperature float64 `json:"temperature"`
    Vibration   float64 `json:"vibration"`
    Current     float64 `json:"current"`
    Status      string  `json:"status"`
}

type AxisInspection struct {
    XAxisPosition float64 `json:"x_axis_position"`
    YAxisPosition float64 `json:"y_axis_position"`
    ZAxisPosition float64 `json:"z_axis_position"`
    XAxisError    float64 `json:"x_axis_error"`
    YAxisError    float64 `json:"y_axis_error"`
    ZAxisError    float64 `json:"z_axis_error"`
}

type ToolInspection struct {
    ToolNumber   int     `json:"tool_number"`
    ToolLife     float64 `json:"tool_life"`
    WearLevel    float64 `json:"wear_level"`
    LastChanged  string  `json:"last_changed"`
}

type VibrationInspection struct {
    XAxisRMS float64 `json:"x_axis_rms"`
    YAxisRMS float64 `json:"y_axis_rms"`
    ZAxisRMS float64 `json:"z_axis_rms"`
    Frequency []float64 `json:"frequency"`
    Amplitude []float64 `json:"amplitude"`
}

type CNCMaintenanceHist struct {
    RecentMaintenance []CNCMaintenanceRecord `json:"recent_maintenance"`
    NextScheduled     *CNCMaintenanceRecord  `json:"next_scheduled,omitempty"`
}

type CNCMaintenanceRecord struct {
    Date        string  `json:"date"`
    Type        string  `json:"type"`        // 预防性维护、故障维修、刀具更换
    Component   string  `json:"component"`
    Description string  `json:"description"`
    Cost        float64 `json:"cost,omitempty"`
    Technician  string  `json:"technician"`
}

//行动建议列表
type CNCPreventionAdvice struct {
    ImmediateActions []string `json:"immediate_actions"` //立即行动建议
    ShortTermPlan    []string `json:"short_term_plan"` //短期行动建议
    LongTermPlan     []string `json:"long_term_plan"` //长期行动建议
    RecommendedParts []string `json:"recommended_parts,omitempty"`
    EstimatedCost    float64  `json:"estimated_cost,omitempty"`
}

主要API处理函数

func PostCNCFaultDiagnosisCopilot(c *gin.Context) {
    startTime := time.Now()
    
    var req CNCFaultDiagnosisRequest
    if err := c.ShouldBindJSON(&req); err != nil {
        showError(c, "Invalid request: "+err.Error())
        return
    }
    
    logger := opslog.NewLogger().SetVerbose("debug").SetStd().SetFlag().Build()
    logger.Info.Printf("Processing CNC fault diagnosis for tenant: %s, query: %s", req.TenantID, req.Query)
    
    // 初始化语义处理器和查询协调器
    orchestrator := NewCNCQueryOrchestrator(
        GlobalConfig.Copilot.Endpoint,
        GlobalConfig.Copilot.Key,
        GlobalConfig.Copilot.Model,
        logger,
    )
    
    // 处理数控机床特定查询
    result, err := orchestrator.ProcessCNCQuery(req)
    if err != nil {
        logger.Error.Printf("Failed to process CNC query: %v", err)
        showError(c, err.Error())
        return
    }
    
    result.ExecutionTime = time.Since(startTime)
    logger.Info.Printf("CNC fault diagnosis completed in %v", result.ExecutionTime)
    
    showData(c, result)
}

数控机床查询协调器

// pkg/copilot/cnc_query_orchestrator.go
package copilot

import (
    "context"
    "fmt"
    "strings"
    "time"
)

type CNCQueryOrchestrator struct {
    semanticProcessor *CNCSemanticProcessor
    mcpToolManager    *CNCMCPToolManager
    faultAnalyzer     *CNCFaultAnalyzer
    logger           *log.Logger
}

func NewCNCQueryOrchestrator(endpoint, key, model string, logger *log.Logger) *CNCQueryOrchestrator {
    return &CNCQueryOrchestrator{
        semanticProcessor: NewCNCSemanticProcessor(endpoint, key, model),
        mcpToolManager:    NewCNCMCPToolManager(),
        faultAnalyzer:     NewCNCFaultAnalyzer(),
        logger:           logger,
    }
}

func (co *CNCQueryOrchestrator) ProcessCNCQuery(req CNCFaultDiagnosisRequest) (*CNCFaultDiagnosisResponse, error) {
    co.logger.Info.Printf("Processing CNC query: %s", req.Query)
    
    // 1. 语义分析和意图识别
    intent, confidence, err := co.semanticProcessor.AnalyzeCNCIntent(req.Query)
    if err != nil {
        return nil, fmt.Errorf("failed to analyze intent: %w", err)
    }
    
    co.logger.Info.Printf("Detected intent: %s (confidence: %.2f)", intent, confidence)
    
    // 2. 选择相关的MCP工具
    selectedTools := co.mcpToolManager.SelectCNCTools(intent, req.MachineType)
    
    // 3. 并发执行数控机床专用工具
    toolResults := co.executeCNCTools(req, selectedTools)
    
    // 4. 构建响应
    response := &CNCFaultDiagnosisResponse{
        FaultAnalysis: co.faultAnalyzer.AnalyzeFaults(toolResults),
    }
    
    // 5. 填充机床信息
    if req.MachineID != "" {
        response.MachineInfo = co.getMachineInfo(req.TenantID, req.MachineID)
    }
    
    // 6. 填充检测数据
    response.InspectionData = co.buildInspectionData(toolResults)
    
    // 7. 填充维护历史
    response.MaintenanceHist = co.buildMaintenanceHistory(toolResults)
    
    // 8. 生成预防建议
    if req.IncludePreventionAdvice {
        response.PreventionAdvice = co.generatePreventionAdvice(response.FaultAnalysis, toolResults)
    }
    
    return response, nil
}

数控机床语义处理器

// pkg/copilot/cnc_semantic_processor.go
package copilot

type CNCSemanticProcessor struct {
    *SemanticProcessor
    cncDomainKeywords map[CNCDomain][]string
}

type CNCDomain string

const (
    CNCDomainSpindle     CNCDomain = "spindle"      // 主轴相关
    CNCDomainAxis        CNCDomain = "axis"         // 轴系相关  
    CNCDomainTool        CNCDomain = "tool"         // 刀具相关
    CNCDomainCoolant     CNCDomain = "coolant"      // 冷却系统
    CNCDomainControl     CNCDomain = "control"      // 控制系统
    CNCDomainVibration   CNCDomain = "vibration"    // 振动分析
)

func NewCNCSemanticProcessor(endpoint, key, model string) *CNCSemanticProcessor {
    return &CNCSemanticProcessor{
        SemanticProcessor: NewSemanticProcessor(endpoint, key, model),
        cncDomainKeywords: map[CNCDomain][]string{
            CNCDomainSpindle: {"主轴", "转速", "RPM", "主轴电机", "主轴轴承", "主轴温度", "主轴振动"},
            CNCDomainAxis: {"X轴", "Y轴", "Z轴", "导轨", "丝杠", "轴承", "定位精度", "重复精度"},
            CNCDomainTool: {"刀具", "刀片", "刀柄", "刀塔", "换刀", "刀具磨损", "刀具寿命"},
            CNCDomainCoolant: {"冷却液", "切削液", "冷却系统", "润滑", "油温", "流量"},
            CNCDomainControl: {"数控系统", "伺服", "编码器", "报警", "程序", "参数"},
            CNCDomainVibration: {"振动", "频谱", "RMS", "加速度", "位移", "速度", "共振"},
        },
    }
}

func (csp *CNCSemanticProcessor) AnalyzeCNCIntent(query string) (CNCDomain, float64, error) {
    queryVector, err := csp.ExtractSemanticVector(query)
    if err != nil {
        return "", 0, err
    }
    
    domainScores := make(map[CNCDomain]float64)
    
    // 计算与各CNC领域的相似度
    for domain, keywords := range csp.cncDomainKeywords {
        score := csp.calculateCNCDomainSimilarity(query, queryVector, keywords)
        domainScores[domain] = score
    }
    
    // 返回最高分的领域
    bestDomain, confidence := csp.getBestCNCMatch(domainScores)
    return bestDomain, confidence, nil
}

func (csp *CNCSemanticProcessor) getBestCNCMatch(scores map[CNCDomain]float64) (CNCDomain, float64) {
    var bestDomain CNCDomain
    var bestScore float64
    
    for domain, score := range scores {
        if score > bestScore {
            bestScore = score
            bestDomain = domain
        }
    }
    
    return bestDomain, bestScore
}

数控机床MCP工具管理器

// pkg/copilot/cnc_mcp_tool_manager.go
package copilot

type CNCMCPToolManager struct {
    tools map[CNCDomain][]CNCMCPTool
}

type CNCMCPTool struct {
    Name        string
    Domain      CNCDomain
    MachineType string // 适用的机床类型:车床、铣床、加工中心等
    Description string
    SQLTemplate string
    Handler     func(context.Context, CNCToolRequest) (*CNCToolResult, error)
}

type CNCToolRequest struct {
    TenantID    string
    MachineID   string
    MachineType string
    TimeRange   string
    Parameters  map[string]interface{}
}

type CNCToolResult struct {
    ToolName string
    Data     interface{}
    Status   string
    Error    error
}

func NewCNCMCPToolManager() *CNCMCPToolManager {
    manager := &CNCMCPToolManager{
        tools: make(map[CNCDomain][]CNCMCPTool),
    }
    manager.initializeCNCTools()
    return manager
}

func (ctm *CNCMCPToolManager) initializeCNCTools() {  
    // 主轴相关工具
    ctm.tools[CNCDomainSpindle] = []CNCMCPTool{  
        {  
            Name:        "query_spindle_status_3months",  
            Domain:      CNCDomainSpindle,  
            MachineType: "all",  
            Description: "查询主轴三个月内状态数据",  
            SQLTemplate: `  
                SELECT inspection_date, spindle_rpm, spindle_temperature, spindle_vibration, spindle_current  
                FROM cnc_spindle_inspection   
                WHERE tenant_id = ? AND machine_id = ?   
                AND inspection_date >= DATE_SUB(NOW(), INTERVAL 3 MONTH)  
                ORDER BY inspection_date DESC  
            `,  
            Handler: ctm.handleSpindleStatusQuery,  
        },  
        {  
            Name:        "analyze_spindle_vibration_trend",  
            Domain:      CNCDomainSpindle,  
            MachineType: "all",  
            Description: "分析主轴振动趋势异常",  
            SQLTemplate: `  
                SELECT inspection_date, spindle_vibration,   
                       AVG(spindle_vibration) OVER (ORDER BY inspection_date ROWS BETWEEN 6 PRECEDING AND CURRENT ROW) as moving_avg  
                FROM cnc_spindle_inspection   
                WHERE tenant_id = ? AND machine_id = ?  
                AND inspection_date >= DATE_SUB(NOW(), INTERVAL 1 MONTH)  
                ORDER BY inspection_date DESC  
            `,  
            Handler: ctm.handleSpindleVibrationAnalysis,  
        },  
    }  
  
    // 轴系相关工具
    ctm.tools[CNCDomainAxis] = []CNCMCPTool{  
        {  
            Name:        "query_axis_positioning_accuracy",  
            Domain:      CNCDomainAxis,  
            MachineType: "all",  
            Description: "查询轴系定位精度数据",  
            SQLTemplate: `  
                SELECT inspection_date, x_axis_error, y_axis_error, z_axis_error,  
                       x_axis_position, y_axis_position, z_axis_position  
                FROM cnc_axis_inspection   
                WHERE tenant_id = ? AND machine_id = ?  
                AND inspection_date >= DATE_SUB(NOW(), INTERVAL 3 MONTH)  
                ORDER BY inspection_date DESC  
            `,  
            Handler: ctm.handleAxisAccuracyQuery,  
        },  
        {  
            Name:        "detect_axis_wear_pattern",  
            Domain:      CNCDomainAxis,  
            MachineType: "all",   
            Description: "检测轴系磨损模式",  
            SQLTemplate: `  
                SELECT axis_name, wear_level, lubrication_status, temperature,  
                       inspection_date, remarks  
                FROM cnc_axis_wear_inspection   
                WHERE tenant_id = ? AND machine_id = ?  
                AND inspection_date >= DATE_SUB(NOW(), INTERVAL 1 MONTH)  
                AND wear_level > 0.3  
                ORDER BY wear_level DESC  
            `,  
            Handler: ctm.handleAxisWearDetection,  
        },  
    }  
  
    // 刀具相关工具
    ctm.tools[CNCDomainTool] = []CNCMCPTool{  
        {  
            Name:        "query_tool_life_status",  
            Domain:      CNCDomainTool,  
            MachineType: "all",  
            Description: "查询刀具寿命状态",  
            SQLTemplate: `  
                SELECT tool_number, tool_type, current_life, max_life,   
                       wear_level, last_changed_date, usage_hours  
                FROM cnc_tool_management   
                WHERE tenant_id = ? AND machine_id = ?  
                AND (current_life/max_life > 0.8 OR wear_level > 0.7)  
                ORDER BY current_life/max_life DESC  
            `,  
            Handler: ctm.handleToolLifeQuery,  
        },  
        {  
            Name:        "analyze_tool_wear_trend",  
            Domain:      CNCDomainTool,  
            MachineType: "all",  
            Description: "分析刀具磨损趋势",  
            SQLTemplate: `  
                SELECT tool_number, inspection_date, wear_level, cutting_force,  
                       surface_roughness, tool_vibration  
                FROM cnc_tool_wear_inspection   
                WHERE tenant_id = ? AND machine_id = ?  
                AND inspection_date >= DATE_SUB(NOW(), INTERVAL 2 MONTH)  
                ORDER BY tool_number, inspection_date DESC  
            `,  
            Handler: ctm.handleToolWearAnalysis,  
        },  
    }  
  
    // 振动分析工具
    ctm.tools[CNCDomainVibration] = []CNCMCPTool{  
        {  
            Name:        "analyze_vibration_spectrum",  
            Domain:      CNCDomainVibration,  
            MachineType: "all",  
            Description: "分析振动频谱特征",  
            SQLTemplate: `  
                SELECT inspection_date, x_axis_rms, y_axis_rms, z_axis_rms,  
                       frequency_data, amplitude_data, dominant_frequency  
                FROM cnc_vibration_analysis   
                WHERE tenant_id = ? AND machine_id = ?  
                AND inspection_date >= DATE_SUB(NOW(), INTERVAL 1 MONTH)  
                AND (x_axis_rms > 2.0 OR y_axis_rms > 2.0 OR z_axis_rms > 2.0)  
                ORDER BY inspection_date DESC  
            `,  
            Handler: ctm.handleVibrationSpectrumAnalysis,  
        },  
    }  
}

MCP工具处理器实现

 // 主轴状态查询处理器
func (ctm *CNCMCPToolManager) handleSpindleStatusQuery(ctx context.Context, req CNCToolRequest) (*CNCToolResult, error) {  
    db := ctm.getDBConnection()  
      
    rows, err := db.QueryContext(ctx, ctm.tools[CNCDomainSpindle][0].SQLTemplate, req.TenantID, req.MachineID)  
    if err != nil {  
        return &CNCToolResult{  
            ToolName: "query_spindle_status_3months",  
            Status:   "error",  
            Error:    err,  
        }, nil  
    }  
    defer rows.Close()  
  
    var spindleData []SpindleInspection  
    for rows.Next() {  
        var data SpindleInspection  
        var inspectionDate string  
          
        err := rows.Scan(&inspectionDate, &data.RPM, &data.Temperature, &data.Vibration, &data.Current)  
        if err != nil {  
            continue  
        }  
          
        // 根据数据判断状态
        data.Status = ctm.evaluateSpindleStatus(data)  
        spindleData = append(spindleData, data)  
    }  
  
    return &CNCToolResult{  
        ToolName: "query_spindle_status_3months",  
        Data:     spindleData,  
        Status:   "success",  
    }, nil  
}  
  
// 主轴振动分析处理器
func (ctm *CNCMCPToolManager) handleSpindleVibrationAnalysis(ctx context.Context, req CNCToolRequest) (*CNCToolResult, error) {  
    db := ctm.getDBConnection()  
      
    rows, err := db.QueryContext(ctx, ctm.tools[CNCDomainSpindle][1].SQLTemplate, req.TenantID, req.MachineID)  
    if err != nil {  
        return &CNCToolResult{  
            ToolName: "analyze_spindle_vibration_trend",  
            Status:   "error",  
            Error:    err,  
        }, nil  
    }  
    defer rows.Close()  
  
    type VibrationTrendData struct {  
        Date       string  `json:"date"`  
        Vibration  float64 `json:"vibration"`  
        MovingAvg  float64 `json:"moving_avg"`  
        Anomaly    bool    `json:"anomaly"`  
    }  
  
    var trendData []VibrationTrendData  
    for rows.Next() {  
        var data VibrationTrendData  
          
        err := rows.Scan(&data.Date, &data.Vibration, &data.MovingAvg)  
        if err != nil {  
            continue  
        }  
          
        // 检测异常:当前值超过移动平均值的20%
        data.Anomaly = data.Vibration > data.MovingAvg*1.2  
        trendData = append(trendData, data)  
    }  
  
    return &CNCToolResult{  
        ToolName: "analyze_spindle_vibration_trend",  
        Data:     trendData,  
        Status:   "success",  
    }, nil  
}  
  
// 轴系精度查询处理器
func (ctm *CNCMCPToolManager) handleAxisAccuracyQuery(ctx context.Context, req CNCToolRequest) (*CNCToolResult, error) {  
    db := ctm.getDBConnection()  
      
    rows, err := db.QueryContext(ctx, ctm.tools[CNCDomainAxis][0].SQLTemplate, req.TenantID, req.MachineID)  
    if err != nil {  
        return &CNCToolResult{  
            ToolName: "query_axis_positioning_accuracy",  
            Status:   "error",  
            Error:    err,  
        }, nil  
    }  
    defer rows.Close()  
  
    var axisData []AxisInspection  
    for rows.Next() {  
        var data AxisInspection  
        var inspectionDate string  
          
        err := rows.Scan(&inspectionDate, &data.XAxisError, &data.YAxisError, &data.ZAxisError,  
                        &data.XAxisPosition, &data.YAxisPosition, &data.ZAxisPosition)  
        if err != nil {  
            continue  
        }  
          
        axisData = append(axisData, data)  
    }  
  
    return &CNCToolResult{  
        ToolName: "query_axis_positioning_accuracy",  
        Data:     axisData,  
        Status:   "success",  
    }, nil  
}  
  
// 刀具寿命查询处理器
func (ctm *CNCMCPToolManager) handleToolLifeQuery(ctx context.Context, req CNCToolRequest) (*CNCToolResult, error) {  
    db := ctm.getDBConnection()  
      
    rows, err := db.QueryContext(ctx, ctm.tools[CNCDomainTool][0].SQLTemplate, req.TenantID, req.MachineID)  
    if err != nil {  
        return &CNCToolResult{  
            ToolName: "query_tool_life_status",  
            Status:   "error",  
            Error:    err,  
        }, nil  
    }  
    defer rows.Close()  
  
    type ToolLifeData struct {  
        ToolNumber      int     `json:"tool_number"`  
        ToolType        string  `json:"tool_type"`  
        CurrentLife     float64 `json:"current_life"`  
        MaxLife         float64 `json:"max_life"`  
        WearLevel       float64 `json:"wear_level"`  
        LastChangedDate string  `json:"last_changed_date"`  
        UsageHours      float64 `json:"usage_hours"`  
        LifePercentage  float64 `json:"life_percentage"`  
        Status          string  `json:"status"`  
    }  
  
    var toolData []ToolLifeData  
    for rows.Next() {  
        var data ToolLifeData  
          
        err := rows.Scan(&data.ToolNumber, &data.ToolType, &data.CurrentLife, &data.MaxLife,  
                        &data.WearLevel, &data.LastChangedDate, &data.UsageHours)  
        if err != nil {  
            continue  
        }  
          
        data.LifePercentage = data.CurrentLife / data.MaxLife  
        data.Status = ctm.evaluateToolStatus(data.LifePercentage, data.WearLevel)  
        toolData = append(toolData, data)  
    }  
  
    return &CNCToolResult{  
        ToolName: "query_tool_life_status",  
        Data:     toolData,  
        Status:   "success",  
    }, nil  
}

故障分析器实现(应产品需求, 这里需要暂时替换成设备相关的实体)

 // pkg/copilot/cnc_fault_analyzer.go
package copilot  
  
type CNCFaultAnalyzer struct {  
    faultRules map[CNCDomain][]FaultRule  
}  
  
type FaultRule struct {  
    Name        string  
    Condition   func(interface{}) bool  
    FaultType   string  
    Component   string  
    Severity    string  
    Description string  
}  
  
func NewCNCFaultAnalyzer() *CNCFaultAnalyzer {  
    analyzer := &CNCFaultAnalyzer{  
        faultRules: make(map[CNCDomain][]FaultRule),  
    }  
    analyzer.initializeFaultRules()  
    return analyzer  
}  
  
func (cfa *CNCFaultAnalyzer) initializeFaultRules() {  
    // 主轴故障规则
    cfa.faultRules[CNCDomainSpindle] = []FaultRule{  
        {  
            Name: "主轴温度过高",  
            Condition: func(data interface{}) bool {  
                if spindleData, ok := data.([]SpindleInspection); ok {  
                    for _, d := range spindleData {  
                        if d.Temperature > 80.0 {  
                            return true  
                        }  
                    }  
                }  
                return false  
            },  
            FaultType:   "温度异常",  
            Component:   "主轴",  
            Severity:    "警告",  
            Description: "主轴温度超过正常范围,可能存在轴承磨损或润滑不良",  
        },  
        {  
            Name: "主轴振动异常",  
            Condition: func(data interface{}) bool {  
                if spindleData, ok := data.([]SpindleInspection); ok {  
                    for _, d := range spindleData {  
                        if d.Vibration > 3.0 {  
                            return true  
                        }  
                    }  
                }  
                return false  
            },  
            FaultType:   "振动异常",  
            Component:   "主轴",  
            Severity:    "危险",  
            Description: "主轴振动值异常,可能存在不平衡或轴承损坏",  
        },  
    }  
  
    // 刀具故障规则
    cfa.faultRules[CNCDomainTool] = []FaultRule{  
        {  
            Name: "刀具磨损严重",  
            Condition: func(data interface{}) bool {  
                if toolData, ok := data.([]interface{}); ok {  
                    for _, item := range toolData {  
                        if tool, ok := item.(map[string]interface{}); ok {  
                            if wearLevel, exists := tool["wear_level"].(float64); exists && wearLevel > 0.8 {  
                                return true  
                            }  
                        }  
                    }  
                }  
                return false  
            },  
            FaultType:   "刀具磨损",  
            Component:   "刀具",  
            Severity:    "警告",  
            Description: "刀具磨损程度严重,需要及时更换",  
        },  
        {  
            Name: "刀具寿命即将到期",  
            Condition: func(data interface{}) bool {  
                if toolData, ok := data.([]interface{}); ok {  
                    for _, item := range toolData {  
                        if tool, ok := item.(map[string]interface{}); ok {  
                            if lifePercentage, exists := tool["life_percentage"].(float64); exists && lifePercentage > 0.9 {  
                                return true  
                            }  
                        }  
                    }  
                }  
                return false  
            },  
            FaultType:   "刀具寿命",  
            Component:   "刀具",  
            Severity:    "注意",  
            Description: "刀具寿命即将到期,建议安排更换",  
        },  
    }  
  
    // 轴系故障规则
    cfa.faultRules[CNCDomainAxis] = []FaultRule{  
        {  
            Name: "轴系定位精度超差",  
            Condition: func(data interface{}) bool {  
                if axisData, ok := data.([]AxisInspection); ok {  
                    for _, d := range axisData {  
                        if math.Abs(d.XAxisError) > 0.01 || math.Abs(d.YAxisError) > 0.01 || math.Abs(d.ZAxisError) > 0.01 {  
                            return true  
                        }  
                    }  
                }  
                return false  
            },  
            FaultType:   "精度异常",  
            Component:   "轴系",  
            Severity:    "警告",  
            Description: "轴系定位精度超出允许范围,可能影响加工质量",  
        },  
    }  
}  
  
func (cfa *CNCFaultAnalyzer) AnalyzeFaults(toolResults map[string]*CNCToolResult) *CNCFaultAnalysis {  
    var detectedFaults []CNCFault  
    var maxSeverity string = "正常"  
    var totalRiskScore float64  
  
    // 遍历所有工具结果,应用故障规则
    for _, result := range toolResults {  
        if result.Status != "success" || result.Data == nil {  
            continue  
        }  
  
        // 根据工具名称确定领域
        domain := cfa.getDomainByToolName(result.ToolName)  
        rules := cfa.faultRules[domain]  
  
        for _, rule := range rules {  
            if rule.Condition(result.Data) {  
                fault := CNCFault{  
                    FaultType:   rule.FaultType,  
                    Component:   rule.Component,  
                    Severity:    rule.Severity,  
                    Confidence:  0.85, // 基于规则的置信度
                    Description: rule.Description,  
                    Symptoms:    cfa.extractSymptoms(result.Data, rule),  
                }  
                detectedFaults = append(detectedFaults, fault)  
  
                // 更新最高严重程度
                if cfa.getSeverityLevel(rule.Severity) > cfa.getSeverityLevel(maxSeverity) {  
                    maxSeverity = rule.Severity  
                }  
  
                // 累加风险分数
                totalRiskScore += cfa.calculateRiskScore(rule.Severity)  
            }  
        }  
    }  
  
    return &CNCFaultAnalysis{  
        DetectedFaults: detectedFaults,  
        SeverityLevel:  maxSeverity,  
        RiskScore:      totalRiskScore,  
        Summary:        cfa.generateSummary(detectedFaults),  
    }  
}

辅助方法实现

func (cfa *CNCFaultAnalyzer) getDomainByToolName(toolName string) CNCDomain {  
    domainMap := map[string]CNCDomain{  
        "query_spindle_status_3months":     CNCDomainSpindle,  
        "analyze_spindle_vibration_trend":  CNCDomainSpindle,  
        "query_axis_positioning_accuracy":  CNCDomainAxis,  
        "detect_axis_wear_pattern":         CNCDomainAxis,  
        "query_tool_life_status":           CNCDomainTool,  
        "analyze_tool_wear_trend":          CNCDomainTool,  
        "analyze_vibration_spectrum":       CNCDomainVibration,  
    }  
      
    if domain, exists := domainMap[toolName]; exists {  
        return domain  
    }  
    return CNCDomainSpindle // 默认值
}  
  
func (cfa *CNCFaultAnalyzer) getSeverityLevel(severity string) int {  
    levels := map[string]int{  
        "正常": 0,  
        "注意": 1,  
        "警告": 2,  
        "危险": 3,  
    }  
    return levels[severity]  
}  
  
func (cfa *CNCFaultAnalyzer) calculateRiskScore(severity string) float64 {  
    scores := map[string]float64{  
        "正常": 0.0,  
        "注意": 0.3,  
        "警告": 0.6,  
        "危险": 1.0,  
    }  
    return scores[severity]  
}  
  
func (cfa *CNCFaultAnalyzer) extractSymptoms(data interface{}, rule FaultRule) []string {  
    var symptoms []string  
      
    switch rule.Component {  
    case "主轴":  
        if spindleData, ok := data.([]SpindleInspection); ok {  
            for _, d := range spindleData {  
                if d.Temperature > 80.0 {  
                    symptoms = append(symptoms, fmt.Sprintf("主轴温度: %.1f°C", d.Temperature))  
                }  
                if d.Vibration > 3.0 {  
                    symptoms = append(symptoms, fmt.Sprintf("主轴振动: %.2f mm/s", d.Vibration))  
                }  
            }  
        }  
    case "刀具":  
        symptoms = append(symptoms, "刀具磨损严重", "切削质量下降")  
    case "轴系":  
        symptoms = append(symptoms, "定位精度超差", "重复精度不稳定")  
    }  
      
    return symptoms  
}  
  
func (cfa *CNCFaultAnalyzer) generateSummary(faults []CNCFault) string {  
    if len(faults) == 0 {  
        return "设备运行正常,未发现异常"  
    }  
      
    var summary strings.Builder  
    summary.WriteString(fmt.Sprintf("检测到 %d 个故障问题:", len(faults)))  
      
    for i, fault := range faults {  
        if i < 3 { // 只显示前3个主要问题
            summary.WriteString(fmt.Sprintf(" %s(%s)", fault.FaultType, fault.Severity))  
            if i < len(faults)-1 && i < 2 {  
                summary.WriteString(",")  
            }  
        }  
    }  
      
    return summary.String()  
}

完成查询协调器的执行方法

func (co *CNCQueryOrchestrator) executeCNCTools(req CNCFaultDiagnosisRequest, tools []CNCMCPTool) map[string]*CNCToolResult {  
    results := make(map[string]*CNCToolResult)  
    resultChan := make(chan struct {  
        name string  
        result *CNCToolResult  
    }, len(tools))  
      
    // 并发执行所有工具
    for _, tool := range tools {  
        go func(t CNCMCPTool) {  
            toolReq := CNCToolRequest{  
                TenantID:    req.TenantID,  
                MachineID:   req.MachineID,  
                MachineType: req.MachineType,  
                TimeRange:   "3months", // 默认时间范围
                Parameters:  make(map[string]interface{}),  
            }  
              
            result, err := t.Handler(context.Background(), toolReq)  
            if err != nil {  
                result = &CNCToolResult{  
                    ToolName: t.Name,  
                    Status:   "error",  
                    Error:    err,  
                }  
            }  
              
            resultChan <- struct {  
                name string  
                result *CNCToolResult  
            }{t.Name, result}  
        }(tool)  
    }  
      
    // 收集结果
    for i := 0; i < len(tools); i++ {  
        res := <-resultChan  
        results[res.name] = res.result  
    }  
      
    return results  
}  
  
func (co *CNCQueryOrchestrator) getMachineInfo(tenantID, machineID string) *CNCMachineInfo {  
    // 这里应该从数据库查询机床信息
    return &CNCMachineInfo{  
        MachineID:   machineID,  
        MachineName: "CNC-001",  
        MachineType: "加工中心",  
        Location:    "车间A-01",  
        Status:      "运行中",  
    }  
}  
  
func (co *CNCQueryOrchestrator) buildInspectionData(toolResults map[string]*CNCToolResult) *CNCInspectionData {  
    inspectionData := &CNCInspectionData{}  
      
    // 从工具结果中提取检测数据
    if spindleResult, exists := toolResults["query_spindle_status_3months"]; exists && spindleResult.Status == "success" {  
        if spindleData, ok := spindleResult.Data.([]SpindleInspection); ok && len(spindleData) > 0 {  
            inspectionData.SpindleData = &spindleData[0] // 取最新数据
        }  
    }  
      
    if axisResult, exists := toolResults["query_axis_positioning_accuracy"]; exists && axisResult.Status == "success" {  
        if axisData, ok := axisResult.Data.([]AxisInspection); ok && len(axisData) > 0 {  
            inspectionData.AxisData = &axisData[0] // 取最新数据
        }  
    }  
      
    return inspectionData  
}  
  
func (co *CNCQueryOrchestrator) buildMaintenanceHistory(toolResults map[string]*CNCToolResult) *CNCMaintenanceHist {  
    // 这里应该查询维护历史数据
    return &CNCMaintenanceHist{  
        RecentMaintenance: []CNCMaintenanceRecord{  
            {  
                Date:        "2024-06-15",  
                Type:        "预防性维护",  
                Component:   "主轴",  
                Description: "主轴轴承润滑保养",  
                Technician:  "张师傅",  
            },  
        },  
    }  
}  
  
func (co *CNCQueryOrchestrator) generatePreventionAdvice(faultAnalysis *CNCFaultAnalysis, toolResults map[string]*CNCToolResult) *CNCPreventionAdvice {  
    advice := &CNCPreventionAdvice{  
        ImmediateActions: []string{},  
        ShortTermPlan:    []string{},  
        LongTermPlan:     []string{},  
    }  
      
    for _, fault := range faultAnalysis.DetectedFaults {  
        switch fault.Severity {  
        case "危险":  
            advice.ImmediateActions = append(advice.ImmediateActions,   
                fmt.Sprintf("立即停机检查%s", fault.Component))  
        case "警告":  
            advice.ShortTermPlan = append(advice.ShortTermPlan,   
                fmt.Sprintf("安排%s维护保养", fault.Component))  
        case "注意":  
            advice.LongTermPlan = append(advice.LongTermPlan,   
                fmt.Sprintf("监控%s状态变化", fault.Component))  
        }  
    }  
      
    return advice  
}

工具管理器的辅助方法

func (ctm *CNCMCPToolManager) SelectCNCTools(intent CNCDomain, machineType string) []CNCMCPTool {  
    var selectedTools []CNCMCPTool  
      
    if tools, exists := ctm.tools[intent]; exists {  
        for _, tool := range tools {  
            if tool.MachineType == "all" || tool.MachineType == machineType {  
                selectedTools = append(selectedTools, tool)  
            }  
        }  
    }  
      
    return selectedTools  
}  
  
func (ctm *CNCMCPToolManager) getDBConnection() *sql.DB {  
    // 这里应该返回实际的数据库连接
    // 为了示例,返回nil
    return nil  
}  
  
func (ctm *CNCMCPToolManager) evaluateSpindleStatus(data SpindleInspection) string {  
    if data.Temperature > 80.0 || data.Vibration > 3.0 {  
        return "异常"  
    } else if data.Temperature > 70.0 || data.Vibration > 2.0 {  
        return "注意"  
    }  
    return "正常"  
}  
  
func (ctm *CNCMCPToolManager) evaluateToolStatus(lifePercentage, wearLevel float64) string {  
    if lifePercentage > 0.9 || wearLevel > 0.8 {  
        return "需要更换"  
    } else if lifePercentage > 0.8 || wearLevel > 0.6 {  
        return "注意"  
    }  
    return "正常"  
}