需求背景
存在一个工业的故障库,其以及一些业务领域的库表设计(点检,维修记录,租户场景表)等.
包括
-
故障类型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: 系统现在可以处理故障诊断查询
- 数据库表结构设计
故障库核心表
-- 故障类型表
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)
);
- 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:"当前设备出现了哪些问题?"
这个查询会触发:
- 语义分析识别为故障分析领域
- 执行相关MCP工具查询设备状态 (目前设备状态处于事后的事故记录库,在交给MCP tool做业务领域的关联之前,需要有事故记录库的数据做辅助验证, MCP tool决策里面的条件暂时写死成
1. 检索智能体信息
- 查询 设备主表 设备别名表 设备故障表
- 智能体信息, 设备ID, 设备类型, 点位名称, 点位类型, 时间, 指标报警等级, 指标趋势, 状态分, 故障类型, 诊断分析.
状态分机,故障类型, 诊断分析, 发现问题, 维护建议,剩余寿命
- 状态分级 x 时间
- 30天内正常 对比180-30天内最大异常等级
- 正常 selet * from
解释: 这里的正常指的是根据设备的事故记录库进行推理
如果要先根据部件归因输出设备维度的汇总建议, 需要建立 "设备-部件-故障"和 " 故障-现象-原因"的知识图谱.
lightRAG(根据LLM提取语义向量构建知识图谱): blog.csdn.net/huang9604/a…
- 故障分析器分析检测到的问题
- 在
response.PreventionAdvice
中返回对应的行动建议
场景2:"在我当前的租户场景中,三个月的台账中关联相关的点检记录,设备是否发生损坏,如果出现怎么预防"
这个复杂查询会:
-
语义处理器提取关键词["三个月","台账","点检记录","设备损坏","预防"]
-
分类到点检和维护业务领域
-
并发执行多个MCP工具(如
query_rms_trend_3months
,check_equipment_damage
) -
关联分析结果
-
在
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 "正常"
}