# 问题排查助手 Web 服务 - 技术实现文档

## 基本信息

| 项目 | 内容 |
|------|------|
| 文档类型 | 技术实现文档 |
| 创建日期 | 2026-07-11 |
| 最后更新 | 2026-07-12 |
| 部署地址 | http://192.168.5.60:8088 |
| 代码位置 | `.claude/skills/Troubleshoot/code/web/` |

---

## 一、系统概述

### 1.1 功能定位

为现场实施同事提供一个 Web 页面，输入问题后自动：
1. 从历史问题库中匹配相似案例
2. 结合排查套路生成步骤指引
3. 输出经过安全过滤的排查步骤

### 1.2 核心特性

- **渐进式响应**：先快速返回匹配案例，再追加 AI 分析结果
- **安全过滤**：只输出只读操作，禁止危险命令
- **知识库驱动**：基于 357 条历史问题记录
- **离线降级**：API 不可用时降级为纯搜索模式

---

## 二、技术架构

### 2.1 整体架构图

```
┌─────────────────────────────────────────────────────────────┐
│                     用户浏览器                                │
│  ┌─────────────────────────────────────────────────────────┐ │
│  │  index.html                                              │ │
│  │  - 表单输入（项目名称、系统类型、APK产品、问题描述）         │ │
│  │  - 渐进式显示：先显示匹配案例，再追加 AI 结果               │ │
│  │  - Markdown 渲染 + CSS 样式                              │ │
│  └─────────────────────────────────────────────────────────┘ │
└─────────────────────────┬───────────────────────────────────┘
                          │ HTTP POST
                          ▼
┌─────────────────────────────────────────────────────────────┐
│                   Flask 后端 (server.py)                     │
│  ┌──────────────┐  ┌──────────────┐  ┌──────────────┐      │
│  │ /api/search  │  │ /api/analyze │  │ /api/submit  │      │
│  │ 搜索匹配案例  │  │ AI深度分析   │  │ 问题入库     │      │
│  │ (<3秒)      │  │ (20-50秒)    │  │              │      │
│  └──────┬───────┘  └──────┬───────┘  └──────────────┘      │
│         │                 │                                  │
│         ▼                 ▼                                  │
│  ┌──────────────┐  ┌──────────────┐  ┌──────────────┐      │
│  │search_engine │  │safety_filter │  │Prompt构建    │      │
│  │.py           │  │.py           │  │              │      │
│  │TF-IDF搜索    │  │三层安全过滤   │  │              │      │
│  └──────────────┘  └──────────────┘  └──────────────┘      │
└─────────────────────────┬───────────────────────────────────┘
                          │
        ┌─────────────────┼─────────────────┐
        ▼                 ▼                 ▼
┌───────────────┐ ┌───────────────┐ ┌───────────────┐
│ 搜索索引.json  │ │ SKILL.md      │ │ Claude API    │
│ (357条记录)   │ │ 排查套路      │ │ (GLM-5.1)     │
│               │ │ (5000字片段)  │ │               │
└───────────────┘ └───────────────┘ └───────────────┘
```

### 2.2 技术栈

| 层级 | 技术 | 说明 |
|------|------|------|
| 前端 | HTML + CSS + JavaScript | 原生实现，无框架依赖 |
| 后端 | Flask 2.3+ | Python Web 框架 |
| 搜索 | TF-IDF + 余弦相似度 | scikit-learn 风格自实现 |
| 安全 | 正则黑名单 + 白名单 | 三层过滤机制 |
| AI | Claude API (GLM-5.1) | 通过中转 API 调用 |
| 数据 | JSON 索引 + Markdown | 357 条历史问题记录 |

---

## 三、核心模块实现

### 3.1 搜索引擎（search_engine.py）

#### 功能
从 357 条历史问题中检索最相关的案例。

#### 算法流程
```
用户输入 → 分词 → TF-IDF向量化 → 余弦相似度计算 → 关键词加分 → 排序返回
```

#### 核心代码逻辑
```python
class SearchEngine:
    def __init__(self):
        # 加载索引
        self.index = load_search_index()
        self.records = self.index.get('records', [])
        
        # 预计算 TF-IDF 向量
        self.doc_tokens = [tokenize(r.get('full_text', '')) for r in self.records]
        self.idf = compute_idf(self.doc_tokens)
        self.doc_vectors = [compute_tfidf(compute_tf(tokens), self.idf) 
                           for tokens in self.doc_tokens]
    
    def search(self, query, top_k=5, project_filter=None):
        # 1. 查询向量化
        query_tokens = tokenize(query)
        query_vector = compute_tfidf(compute_tf(query_tokens), self.idf)
        
        # 2. 计算相似度
        scores = []
        for i, doc_vector in enumerate(self.doc_vectors):
            similarity = cosine_similarity(query_vector, doc_vector)
            
            # 3. 关键词加分
            keyword_bonus = sum(0.05 for kw in self.records[i].get('keywords', []) 
                               if kw.lower() in query.lower())
            
            # 4. 项目匹配加分
            project_bonus = 0.1 if project_filter and \
                self.records[i].get('project', '').lower() == project_filter.lower() else 0
            
            final_score = min(1.0, similarity + keyword_bonus + project_bonus)
            scores.append((i, final_score))
        
        # 5. 排序返回
        scores.sort(key=lambda x: x[1], reverse=True)
        return [{'rank': rank, 'score': score, 'record': self.records[idx]} 
                for rank, (idx, score) in enumerate(scores[:top_k], start=1)]
```

#### 中文分词策略
```python
def tokenize(text):
    # 中文词组（2-4字）
    cn_words = re.findall(r'[一-龥]{2,4}', text)
    # 英文单词
    en_words = re.findall(r'[a-zA-Z]{2,}', text.lower())
    # 数字
    numbers = re.findall(r'\d+', text)
    # 过滤停用词
    return [t for t in cn_words + en_words + numbers if t not in STOPWORDS]
```

---

### 3.2 安全过滤器（safety_filter.py）

#### 设计原则
**只允许只读操作，禁止任何修改系统的命令。**

#### 三层过滤机制

```
┌─────────────────────────────────────────────────────────────┐
│  第一层：危险命令黑名单（正则匹配）                            │
│  - 删除类：rm -rf, docker rm, DROP, DELETE, TRUNCATE        │
│  - 重启类：reboot, docker restart, systemctl restart        │
│  - 停止类：docker stop, kill, pkill                         │
│  - 修改类：chmod 777, chown, 写入配置文件                    │
│  命中则删除整行，替换为安全提示                                │
└─────────────────────────┬───────────────────────────────────┘
                          ▼
┌─────────────────────────────────────────────────────────────┐
│  第二层：相关性检查                                           │
│  - 检查排查步骤是否与用户问题相关                              │
│  - 关键词交集低于阈值时标记为可疑                              │
└─────────────────────────┬───────────────────────────────────┘
                          ▼
┌─────────────────────────────────────────────────────────────┐
│  第三层：白名单验证                                           │
│  - 允许：docker ps, docker logs, cat, grep, nc -z, curl     │
│  - 允许：df -h, free -h, ss -tln, ps aux                    │
│  - 允许：查看状态、查看日志、连通性测试、系统信息              │
└─────────────────────────────────────────────────────────────┘
```

#### 危险命令黑名单（部分）
```python
DANGEROUS_PATTERNS = [
    # 删除操作
    (r'\brm\s+(-rf?|--recursive)', '危险：递归删除文件'),
    (r'\bdocker\s+rm', '危险：删除容器'),
    (r'\bDROP\s+(TABLE|DATABASE)', '危险：删除数据库表/库'),
    
    # 重启/停止操作
    (r'\breboot\b', '危险：重启系统'),
    (r'\bdocker\s+(restart|stop)', '危险：重启/停止容器'),
    (r'\bkill\b', '危险：终止进程'),
    
    # 修改操作
    (r'\bchmod\s+777\b', '危险：开放所有权限'),
    (r'\b>\/etc\/', '危险：覆盖系统配置文件'),
]
```

#### 白名单模式（部分）
```python
ALLOWED_PATTERNS = [
    # 查看状态
    r'\bdocker\s+ps\b',
    r'\bsystemctl\s+status\b',
    r'\bss\s+-tln',
    
    # 查看日志
    r'\bdocker\s+logs\b',
    r'\btail\b',
    r'\bjournalctl\b',
    
    # 连通性测试
    r'\bnc\s+-z\b',
    r'\bcurl\b',
    r'\bping\b',
    
    # 系统信息
    r'\bdf\s+-h\b',
    r'\bfree\s+-h\b',
    r'\bps\s+(aux|ef)',
]
```

#### 过滤函数
```python
def filter_output(response_text, original_query):
    """
    对 AI 生成的排查输出执行完整安全检查。
    
    返回：
    {
        "safe": True/False,           # 整体是否安全
        "filtered_text": "...",       # 过滤后的安全文本
        "warnings": [...],            # 警告列表
        "removed_lines": [...],       # 被移除的行
    }
    """
    # 逐行检查
    for line in response_text.split('\n'):
        # 第一层：危险命令检查
        has_danger, danger_warnings = check_dangerous(line)
        if has_danger:
            # 替换为安全提示
            safe_lines.append("⚠️ [原输出包含危险操作，已自动过滤] 如需执行相关操作，请联系研发组。")
            continue
        
        safe_lines.append(line)
    
    return {
        'safe': len(removed_lines) == 0,
        'filtered_text': '\n'.join(safe_lines),
        'removed_lines': removed_lines,
    }
```

---

### 3.3 Flask 后端（server.py）

#### API 接口设计

| 接口 | 方法 | 功能 | 响应时间 |
|------|------|------|----------|
| `/` | GET | 主页面 | <100ms |
| `/api/projects` | GET | 获取项目列表 | <100ms |
| `/api/search` | POST | 搜索匹配案例 | <3s |
| `/api/analyze` | POST | AI 深度分析 | 20-50s |
| `/api/troubleshoot` | POST | 一键排查（搜索+分析） | 20-50s |
| `/api/submit` | POST | 提交问题入库 | <1s |

#### 渐进式两阶段请求流程

```
前端点击"帮我排查"
       │
       ├─────► POST /api/search（阶段一，快）
       │            │
       │            ▼
       │       返回匹配案例（<3s）
       │            │
       │            ▼
       │       前端立即显示匹配案例
       │            │
       ├────────────┴─────► POST /api/analyze（阶段二，慢）
       │                          │
       │                          ▼
       │                     调用 Claude API（20-50s）
       │                          │
       │                          ▼
       │                     安全过滤
       │                          │
       │                          ▼
       │                     返回排查步骤
       │                          │
       └──────────────────────────┴─────► 前端追加显示结果
```

#### 核心路由实现

**搜索接口**
```python
@app.route('/api/search', methods=['POST'])
def search_cases():
    data = request.get_json()
    project_name = data.get('project_name', '').strip()
    query = data.get('query', '').strip()
    
    # 搜索匹配
    engine = get_search_engine()
    matched_cases = engine.search(query, top_k=5, project_filter=project_name)
    
    return jsonify({
        'success': True,
        'matched_cases': format_matched_cases(matched_cases),
        'matched_count': len(matched_cases),
        'search_time': round(time.time() - start_time, 2),
    })
```

**分析接口**
```python
@app.route('/api/analyze', methods=['POST'])
def analyze():
    data = request.get_json()
    
    # 1. 构建 Prompt
    prompt = build_prompt(query, project_name, system_type, apk_product, matched_cases)
    
    # 2. 调用 Claude API
    raw_response = call_claude_api(prompt)
    
    # 3. 安全过滤
    filter_result = safety_filter.filter_output(raw_response, query)
    
    # 4. 审计日志
    log_audit({...})
    
    return jsonify({
        'success': True,
        'response': filter_result['filtered_text'],
        'filter_warnings': filter_result.get('removed_lines', []),
    })
```

---

### 3.4 Prompt 构建（server.py）

#### System Prompt（角色定义）
```markdown
你是一个自动化测试团队的问题排查专家助手。你面对的是现场实施同事，他们可能不熟悉 Linux 命令和系统架构。

## 核心规则（必须遵守）

1. **只读操作原则**：给出的排查步骤必须是只读操作（查看状态、查看日志、探测连通性、查看配置）
2. **安全原则**：绝对不允许给出修改、删除、重启、停止、写入等操作
3. **相关性原则**：每步操作必须直接关联用户描述的问题，不允许泛化
4. **排序原则**：步骤按最可能→最不可能排序
5. **概率原则**：给出各可能根因的概率估计
6. **语言原则**：用简单易懂的语言，说明每步"查什么"、"查到什么说明是什么问题"
7. **危险操作原则**：如果需要执行危险操作来修复，用"请联系研发组执行：[具体操作]"代替

## 禁止的命令（违反则整条回复作废）

- 删除类：rm、docker rm、DROP、DELETE、TRUNCATE
- 重启类：reboot、systemctl restart、docker restart、shutdown
- 停止类：systemctl stop、docker stop、kill、pkill
- 修改类：chmod 777、chown、写入配置文件、iptables 修改
- 清空类：FLUSHALL、FLUSHDB

## 允许的命令（可放心输出）

- 查看状态：docker ps、systemctl status、ss -tln
- 查看日志：docker logs、tail、journalctl
- 连通性测试：nc -z、curl、ping
- 系统信息：df -h、free -h、date、ps aux
- 配置查看：cat、grep、ls
```

#### User Prompt 模板
```markdown
## 用户查询

项目名称：{project_name}
系统类型：{system_type}
APK产品：{apk_product}
问题描述：{user_query}

## 历史相似案例

{matched_cases}

## 排查套路参考

{skill_md_excerpt}

---

请根据以上信息，生成排查步骤和根因分析。输出格式：

### 📋 匹配到的历史案例
（简要列出匹配的案例）

### 🔬 排查步骤（只读操作）
Step 1: ...
Step 2: ...

### 🎯 最可能的根因（按概率排序）
- XX%：根因描述
...

### ⚠️ 注意事项
（提醒现场同事注意的事项）
```

#### 上下文增强策略

| 输入字段 | 对 Prompt 的影响 |
|----------|-----------------|
| 项目名称 | 优先匹配该项目的案例；同项目加分 10% |
| 系统类型=标准版预定2.0 | 排查域聚焦：EMQX、Nacos、门口屏、无纸化、数据库 |
| 系统类型=运维集控 | 排查域聚焦：协议透传、中间件、设备管理、MQTT |
| APK产品=门口屏5.0 | 版本特征：设备主动上报、MQTT默认凭据、只同步参会人脸 |

---

### 3.5 前端实现（index.html）

#### 页面结构
```
┌─────────────────────────────────────────┐
│  🔍 问题排查助手                          │
│  基于 357 条历史问题记录 · AI 智能分析    │
├─────────────────────────────────────────┤
│  项目名称 * [ 厦门银行____________ ▼ ]   │
│  系统类型 * [ 标准版预定2.0      ▼ ]     │
│  APK 产品   [ 门口屏5.0__________ ]      │
│  问题描述 * [ ____________________ ]     │
│             [ ____________________ ]     │
│                                          │
│        [ 🔬 帮我排查 ]  [ 🔄 清空 ]       │
├─────────────────────────────────────────┤
│  ✅ 排查诊断结果                          │
│  耗时 XX.XXs                             │
│                                          │
│  📋 匹配到的历史案例                      │
│  [92%] 厦门银行-门口屏MQTT绑定失败        │
│  [75%] 展厅-门口屏不显示会议信息          │
│                                          │
│  🔬 排查步骤（只读操作）                   │
│  Step 1: 检查门口屏版本...               │
│  Step 2: 查看设备日志...                 │
│                                          │
│  🎯 最可能的根因（按概率排序）             │
│  - 70%：旧版门口屏无默认MQTT凭证          │
│                                          │
│  ⚠️ 注意事项                             │
│  - 排查时只读操作，不要随意重启服务        │
│                                          │
│  [ 📝 提交记录入库 ]                      │
└─────────────────────────────────────────┘
```

#### 渐进式加载实现
```javascript
document.getElementById('troubleshootForm').addEventListener('submit', async (e) => {
    // 阶段一：搜索匹配案例
    const searchResp = await fetch('/api/search', {
        method: 'POST',
        body: JSON.stringify({ project_name, query }),
    });
    const searchData = await searchResp.json();
    
    // 立即显示匹配案例
    showMatchedCases(searchData.matched_cases);
    
    // 阶段二：AI 深度分析
    const analyzeResp = await fetch('/api/analyze', {
        method: 'POST',
        body: JSON.stringify({ project_name, system_type, apk_product, query, matched_cases }),
    });
    const analyzeData = await analyzeResp.json();
    
    // 追加显示 AI 结果
    showAIResponse(analyzeData.response);
});
```

#### Markdown 渲染
```javascript
function renderMarkdown(text) {
    // 标题
    text = text.replace(/^### (.+)$/gm, '<h3>$1</h3>');
    text = text.replace(/^## (.+)$/gm, '<h3>$1</h3>');
    
    // 代码块
    text = text.replace(/`([^`]+)`/g, '<code>$1</code>');
    
    // 列表
    text = text.replace(/^- (.+)$/gm, '<li>$1</li>');
    
    // 段落
    text = text.replace(/\n\n/g, '</p><p>');
    return '<p>' + text + '</p>';
}
```

---

## 四、数据文件

### 4.1 搜索索引（搜索索引.json）

```json
{
  "last_update": "2026-07-11 14:38:04",
  "total": 357,
  "records": [
    {
      "id": "1",
      "file": "问题记录/日常/001-outlook上预约exchage私服的.md",
      "title": "outlook上预约exchage私服的会议，无法同步到展厅",
      "phenomenon": "outlook上预约exchage私服的会议，无法同步到展厅",
      "project": "",
      "source": "日常",
      "status": "临时处理",
      "category": ["时区/时间", "第三方对接", "数据同步"],
      "keywords": ["exchange", "同步"],
      "full_text": "# outlook上预约exchage私服的会议..."
    }
  ],
  "project_names": ["厦门银行", "招商局", "展厅", ...],
  "categories": {
    "服务/容器异常": [0, 3, 5, ...],
    "MQTT/EMQX": [1, 4, ...]
  }
}
```

### 4.2 配置文件（config.json）

```json
{
  "claude_api_base": "https://office.ubainsyun.com:8400",
  "claude_api_key": "在这里填写你的API密钥",
  "claude_model": "glm-5.1",
  "system_types": [
    {"value": "std20", "label": "标准版预定2.0"},
    {"value": "ops", "label": "标准版运维集控系统"},
    {"value": "new_unified", "label": "新统一平台"},
    {"value": "unified", "label": "统一平台"}
  ]
}
```

---

## 五、性能分析

### 5.1 响应时间分解

| 阶段 | 操作 | 耗时 |
|------|------|------|
| 搜索 | 索引加载（启动时） | 一次性 |
| 搜索 | TF-IDF 向量化 | <100ms |
| 搜索 | 相似度计算 | <500ms |
| 搜索 | 排序返回 | <100ms |
| **搜索总计** | | **<1s** |
| AI | Prompt 构建 | <50ms |
| AI | Claude API 调用 | 20-50s |
| AI | 安全过滤 | <10ms |
| **AI 总计** | | **20-50s** |

### 5.2 主要瓶颈

**Claude API 调用**（占 99% 时间）：
- 网络延迟：请求发到中转 API 再转发
- 模型推理：GLM-5.1 生成 1000+ 字需要时间
- Prompt 长度：SKILL.md 片段（5000字）+ 匹配案例

---

## 六、部署说明

### 6.1 目录结构

```
/opt/troubleshoot/
├── SKILL.md                    # 排查套路文档
├── 搜索索引.json                # 搜索索引
├── 问题记录/                    # Markdown 知识库
│   ├── 日常/
│   └── 项目/
└── web/
    ├── server.py               # Flask 后端
    ├── search_engine.py        # 搜索引擎
    ├── safety_filter.py        # 安全过滤器
    ├── config.json             # 配置文件
    ├── audit.log               # 审计日志
    └── templates/
        └── index.html          # 前端页面
```

### 6.2 启动命令

```bash
cd /opt/troubleshoot/web
nohup python server.py > /tmp/troubleshoot.log 2>&1 &
```

### 6.3 访问地址

```
http://192.168.5.60:8088/
```

---

## 七、维护指南

### 7.1 更新知识库

```bash
# 1. 更新 Excel 源文件
# 2. 转换为 Markdown
python xlsx_to_md.py

# 3. 重建索引
python build_index.py

# 4. 重启服务
pkill -f "python.*server.py"
cd /opt/troubleshoot/web && nohup python server.py > /tmp/troubleshoot.log 2>&1 &
```

### 7.2 查看日志

```bash
# 服务日志
tail -f /tmp/troubleshoot.log

# 审计日志
tail -f /opt/troubleshoot/web/audit.log
```

---

## 八、已知问题与优化方向

### 8.1 已知问题

| 问题 | 说明 |
|------|------|
| AI 响应慢 | 20-50 秒，主要受 Claude API 延迟影响 |
| 无流式返回 | 用户需等待完整响应才能看到结果 |

### 8.2 优化方向

1. **流式返回（SSE）**：让 AI 边生成边返回
2. **缓存机制**：相同问题缓存结果
3. **模型切换**：支持更快的模型（如 glm-4-flash）
4. **Prompt 精简**：只传相关 SKILL.md 片段

---

## 九、关联文档

- [PRD_需求文档_问题知识库智能排查助手.md](./PRD_需求文档_问题知识库智能排查助手.md)
- [PRD_计划执行_问题知识库智能排查助手.md](./PRD_计划执行_问题知识库智能排查助手.md)
- [部署指南_问题排查助手Web服务.md](./部署指南_问题排查助手Web服务.md)
- [用户使用手册_问题排查助手.md](./用户使用手册_问题排查助手.md)