# PRD_计划执行_问题排查助手性能与功能优化

## 基本信息

| 项目 | 内容 |
|------|------|
| 关联需求 | [PRD_需求文档_问题排查助手性能与功能优化](./PRD_需求文档_问题排查助手性能与功能优化.md) |
| 创建日期 | 2026-07-12 |
| 负责人 | 研发组 |
| 预计工期 | 1 天 |
| 状态 | 已完成 |

---

## 总览

### 核心交付物

| 交付物 | 说明 | 优先级 |
|--------|------|--------|
| 缓存管理模块 | 问题结果缓存，减少 API 调用 | P0 |
| 模型切换功能 | 前端可选择不同模型 | P0 |
| 健康检查接口 | `/api/health` 接口 | P0 |
| 敏感信息过滤 | 过滤 IP、密码等敏感信息 | P1 |

### 执行顺序

```
阶段一：缓存机制
    └── cache_manager.py + server.py 集成
         ↓
阶段二：模型切换
    └── config.json 更新 + 前端下拉框
         ↓
阶段三：健康检查接口
    └── /api/health 接口
         ↓
阶段四：敏感信息过滤
    └── safety_filter.py 增强
         ↓
阶段五：测试验证
    └── 端到端测试
```

---

## 阶段一：缓存机制

### 任务 1.1：创建缓存管理模块

**文件**：`code/web/cache_manager.py`

**功能**：
- 基于 JSON 文件的缓存存储
- 支持缓存过期检查
- 支持缓存清理

**核心代码**：

```python
import json
import hashlib
import time
from pathlib import Path

class CacheManager:
    def __init__(self, cache_dir, expire_hours=24):
        self.cache_dir = Path(cache_dir)
        self.cache_dir.mkdir(parents=True, exist_ok=True)
        self.expire_seconds = expire_hours * 3600
    
    def _get_cache_key(self, project_name, system_type, apk_product, query):
        """生成缓存键"""
        content = f"{project_name}|{system_type}|{apk_product}|{query}"
        return hashlib.md5(content.encode()).hexdigest()
    
    def get(self, project_name, system_type, apk_product, query):
        """获取缓存"""
        key = self._get_cache_key(project_name, system_type, apk_product, query)
        cache_file = self.cache_dir / f"{key}.json"
        
        if not cache_file.exists():
            return None
        
        try:
            with open(cache_file, 'r', encoding='utf-8') as f:
                data = json.load(f)
            
            # 检查过期
            if time.time() - data['timestamp'] > self.expire_seconds:
                cache_file.unlink()
                return None
            
            return data
        except:
            return None
    
    def set(self, project_name, system_type, apk_product, query, response, matched_cases):
        """设置缓存"""
        key = self._get_cache_key(project_name, system_type, apk_product, query)
        cache_file = self.cache_dir / f"{key}.json"
        
        data = {
            'timestamp': time.time(),
            'project_name': project_name,
            'system_type': system_type,
            'apk_product': apk_product,
            'query': query,
            'response': response,
            'matched_cases': matched_cases,
        }
        
        with open(cache_file, 'w', encoding='utf-8') as f:
            json.dump(data, f, ensure_ascii=False, indent=2)
    
    def clear_expired(self):
        """清理过期缓存"""
        for cache_file in self.cache_dir.glob("*.json"):
            try:
                with open(cache_file, 'r', encoding='utf-8') as f:
                    data = json.load(f)
                if time.time() - data['timestamp'] > self.expire_seconds:
                    cache_file.unlink()
            except:
                pass
```

**状态**：[x] 已完成

---

### 任务 1.2：集成缓存到 server.py

**文件**：`code/web/server.py`

**修改点**：

1. 初始化缓存管理器：
```python
from cache_manager import CacheManager
cache = CacheManager(SCRIPT_DIR / "cache", expire_hours=24)
```

2. 在 `/api/analyze/stream` 接口中检查缓存：
```python
@app.route('/api/analyze/stream', methods=['GET'])
def analyze_stream():
    # 检查缓存
    cached = cache.get(project_name, system_type, apk_product, query)
    if cached:
        # 返回缓存结果
        def gen_cached():
            yield f'data: {json.dumps({"type": "cached", "message": "来自缓存"}, ensure_ascii=False)}\n\n'
            yield f'data: {json.dumps({"type": "matched_cases", "cases": cached["matched_cases"]}, ensure_ascii=False)}\n\n'
            for chunk in split_mock_response(cached["response"], chunk_size=30):
                yield f'data: {json.dumps({"type": "chunk", "content": chunk}, ensure_ascii=False)}\n\n'
            yield f'data: {json.dumps({"type": "done", "api_time": 0.5}, ensure_ascii=False)}\n\n'
        return Response(gen_cached(), mimetype='text/event-stream')
    
    # 正常流程...
    # 完成后存入缓存
    cache.set(project_name, system_type, apk_product, query, filtered_text, matched_cases)
```

**状态**：[x] 已完成

---

## 阶段二：模型切换

### 任务 2.1：更新配置文件

**文件**：`code/web/config.json`

```json
{
  "claude_api_base": "https://office.ubainsyun.com:8400",
  "claude_api_key": "在这里填写你的API密钥",
  "models": [
    {
      "id": "glm-4-flash",
      "name": "快速模式（glm-4-flash）",
      "description": "5-10秒，适合快速排查"
    },
    {
      "id": "glm-5.1",
      "name": "标准模式（glm-5.1）",
      "description": "20-50秒，推荐默认"
    },
    {
      "id": "glm-4-plus",
      "name": "深度模式（glm-4-plus）",
      "description": "30-60秒，复杂问题"
    }
  ],
  "default_model": "glm-5.1"
}
```

**状态**：[x] 已完成

---

### 任务 2.2：前端模型选择

**文件**：`code/web/templates/index.html`

**新增 HTML**：

```html
<div class="form-group">
    <label class="form-label">模型选择</label>
    <select id="modelSelect" class="form-select">
        <option value="glm-4-flash">快速模式（5-10秒）</option>
        <option value="glm-5.1" selected>标准模式（20-50秒，推荐）</option>
        <option value="glm-4-plus">深度模式（30-60秒）</option>
    </select>
    <p class="form-hint">快速模式适合简单问题，深度模式适合复杂问题</p>
</div>
```

**修改 JavaScript**：

```javascript
// 获取选择的模型
const model = document.getElementById('modelSelect').value;

// 传递给 SSE 接口
const params = new URLSearchParams({
    project_name: projectName,
    system_type: systemType,
    apk_product: apkProduct || '',
    query: query,
    model: model  // 新增模型参数
});
```

**状态**：[x] 已完成

---

### 任务 2.3：后端支持模型参数

**文件**：`code/web/server.py`

```python
@app.route('/api/analyze/stream', methods=['GET'])
def analyze_stream():
    # 获取模型参数
    model = request.args.get('model', config.get('default_model', 'glm-5.1'))
    
    # 使用指定模型调用 API
    for chunk in call_claude_api_stream(prompt, model=model):
        ...
```

**状态**：[x] 已完成

---

## 阶段三：健康检查接口

### 任务 3.1：新增健康检查接口

**文件**：`code/web/server.py`

```python
@app.route('/api/health', methods=['GET'])
def health_check():
    """健康检查接口"""
    engine = get_search_engine()
    
    return jsonify({
        'status': 'ok',
        'timestamp': datetime.now().isoformat(),
        'version': '1.2.0',
        'knowledge_base': {
            'total_records': len(engine.records),
            'last_update': engine.index.get('last_update', 'unknown')
        },
        'components': {
            'search_engine': 'ok',
            'safety_filter': 'ok'
        }
    })
```

**状态**：[x] 已完成

---

## 阶段四：敏感信息过滤

### 任务 4.1：增强安全过滤器

**文件**：`code/web/safety_filter.py`

**新增敏感信息过滤**：

```python
# 敏感信息正则规则
SENSITIVE_PATTERNS = [
    # IP 地址
    (r'\b(\d{1,3}\.\d{1,3}\.\d{1,3}\.\d{1,3})\b', '***.***.***.***'),
    
    # 密码
    (r'(password|passwd|pwd)\s*[=:]\s*\S+', r'\1 = ***'),
    
    # 数据库连接串
    (r'(mysql|redis|mongodb)://[^@\s]+@', r'\1://***@'),
    
    # 手机号
    (r'\b(1[3-9]\d{9})\b', r'1**********'),
    
    # 邮箱
    (r'\b([\w.]+@[\w.]+\.\w+)\b', r'***@***.***'),
]

def filter_sensitive_info(text):
    """过滤敏感信息"""
    for pattern, replacement in SENSITIVE_PATTERNS:
        text = re.sub(pattern, replacement, text, flags=re.IGNORECASE)
    return text

def filter_output(response_text, original_query):
    """完整过滤：危险命令 + 敏感信息"""
    # 原有的危险命令过滤
    result = filter_dangerous_commands(response_text)
    
    # 新增：敏感信息过滤
    result['filtered_text'] = filter_sensitive_info(result['filtered_text'])
    
    return result
```

**状态**：[x] 已完成

---

## 阶段五：测试验证

### 任务 5.1：功能测试

- [x] 缓存机制：相同问题第二次请求返回缓存
- [x] 模型切换：切换模型后响应时间符合预期
- [x] 健康检查：`/api/health` 返回正确状态
- [x] 敏感信息过滤：IP、密码被过滤

### 任务 5.2：性能测试

- [x] 缓存命中响应时间 < 500ms
- [x] 健康检查响应时间 < 100ms
- [x] 缓存命中率统计

### 任务 5.3：部署测试

- [x] 更新到 192.168.5.60 服务器
- [x] 验证生产环境功能正常

---

## 执行记录

| 日期 | 内容 | 结果 | 备注 |
|------|------|------|------|
| 2026-07-12 | 新增健康检查接口 /api/health | 成功 | 返回服务状态、版本、知识库统计 |
| 2026-07-12 | 增强敏感信息过滤功能 | 成功 | 支持 IP、密码、手机号、邮箱等过滤 |
| 2026-07-12 | 集成敏感信息过滤到流式接口 | 成功 | 使用 filter_output_with_sensitive 函数 |
| 2026-07-12 | 部署到 192.168.5.60 服务器 | 成功 | 上传 server.py, safety_filter.py, cache_manager.py |
| 2026-07-12 | 验证生产环境功能 | 成功 | 健康检查、搜索、主页均正常 |

---

## 变更记录

| 日期 | 版本 | 变更内容 | 变更人 |
|------|------|----------|--------|
| 2026-07-12 | v1.0 | 初始版本 | Claude |
| 2026-07-12 | v1.1 | 完成所有开发任务 | Claude |