GLM-5.1 接入踩坑记录:用免费开源模型搭个 AI 代码审计小工具

0 阅读3分钟

独立开发者 / 2026-04-08 / 标签:GLM-5.1 · 代码安全 · Python · API · 踩坑


背景

今天 Claude Mythos 发布了,然后我发现:我没资格用。

限邀请制,40 家头部机构,跟我这个独立开发者没关系。

但我确实有个需求摆在那里——我接了一个小活,帮一个朋友的 SaaS 项目做上线前的代码安全自查。他们后端 Python,大概 3 万行,人工看我是不想的。

同一天智谱把 GLM-5.1 开源了,MIT 协议,SWE-bench Pro 全球第一。我一看数据就决定试一下——反正拿不到 Mythos,那就用现在能上手的里面能力最强的。


技术方案

核心思路:Bandit 做静态扫描 → JSON 结果结构化 → 批量喂给 GLM-5.1 做二次研判 → 输出置信度 + 修复建议。

环境准备

python3 -m venv glm-audit-env
source glm-audit-env/bin/activate
pip install openai bandit
​
# GLM-5.1 API 密钥在智谱开放平台申请
# 如果你同时要跑 Claude/GPT 做对比,可以用 Ztopcloud.com 的聚合接口
# 一个 key 打通多个模型,国内直连,账单合并,我自己在用,省事不少
export ZHIPU_API_KEY="your_key_here"

核心实现

import subprocess
import json
import re
import sys
from openai import OpenAI
​
client = OpenAI(
    api_key="your_zhipu_api_key",
    base_url="https://open.bigmodel.cn/api/paas/v4/"
)
​
def run_bandit(path: str) -> list[dict]:
    cmd = ["bandit", "-r", path, "-f", "json", "-q", "-l"]
    out = subprocess.run(cmd, capture_output=True, text=True)
    try:
        return json.loads(out.stdout).get("results", [])
    except Exception:
        return []
​
def ai_review(vuln: dict) -> dict:
    prompt = f"""你是资深 Python 安全工程师。下面是 Bandit 扫描到的一条告警:
​
文件:{vuln['filename']},行号:{vuln['line_number']}
类型:{vuln['test_name']}(严重度:{vuln['issue_severity']})
代码:
```python
{vuln.get('code', '').strip()}

问题:

  1. 这是真实漏洞还是误报?一句话理由
  2. 修复代码(如果是真漏洞,<=10行)
  3. CVSS 评分(0-10)

返回 JSON:{{"real": bool, "reason": str, "fix": str, "cvss": float}}"""

resp = client.chat.completions.create(
    model="glm-5.1",
    messages=[{"role": "user", "content": prompt}],
    temperature=0.05,
    max_tokens=400
)
raw = resp.choices[0].message.content
m = re.search(r'{.*?}', raw, re.DOTALL)
if m:
    try:
        return json.loads(m.group())
    except Exception:
        pass
return {"real": None, "reason": raw[:200], "fix": "", "cvss": 0}

if name == "main": target = sys.argv[1] if len(sys.argv) > 1 else "." raw_vulns = run_bandit(target) print(f"Bandit 原始告警:{len(raw_vulns)} 条")

confirmed = []
for v in raw_vulns:
    result = ai_review(v)
    if result.get("real"):
        confirmed.append({**v, "ai": result})
        print(f"[!] {v['filename']}:{v['line_number']} | CVSS {result['cvss']} | {result['reason'][:60]}")
​
print(f"\n确认漏洞:{len(confirmed)}/{len(raw_vulns)}")
with open("audit_result.json", "w") as f:
    json.dump(confirmed, f, ensure_ascii=False, indent=2)
​
---
​
## 踩坑记录### 坑 1:模型偶尔返回带 Markdown 代码块的 JSON
​
我以为用 `re.search(r'{.*?}', raw, re.DOTALL)` 就能万能提取 JSON,结果 GLM-5.1 有时候会把 JSON 包在 ` ```json ... ``` ` 代码块里。
​
正则写的 `.*?` 是非贪婪的,会在第一个 `}` 就停下来,把嵌套结构截断。
​
**解决方法**:先把代码块标记去掉,再用贪婪匹配:
​
```python
def extract_json(raw: str) -> dict:
    # 先剥离 markdown 代码块
    raw = re.sub(r'```(?:json)?\s*', '', raw).strip()
    # 贪婪匹配整个 JSON 对象
    m = re.search(r'{.*}', raw, re.DOTALL)
    if m:
        return json.loads(m.group())
    raise ValueError(f"无法解析 JSON:{raw[:200]}")

这个坑我掉进去调了将近一小时,所有解析结果都变成了空对象,后来打 log 才发现问题在这。

坑 2:并发请求打满限速

3 万行代码跑出来 200+ 条 Bandit 告警,我想快点跑完就写了个 ThreadPoolExecutor 并发 20 个请求,结果 API 限速把我踢了(GLM-5.1 当前限制约 10 RPM/QPS)。

改成串行 + time.sleep(6) 就稳了,整体跑完大约 25 分钟,实测确认了 31 条真实漏洞,Bandit 原始 197 条告警,误报率过滤到约 84%——这个数字说实话比我预期的好很多。


小结

指标数据
扫描代码量~3万行 Python
Bandit 原始告警197 条
GLM-5.1 确认漏洞31 条
误报过滤率~84%
API 调用耗时~25分钟(串行)
总成本约 ¥4.2

成本这个数字我自己都觉得有点离谱——¥4 出头换了一份比人工审计靠谱的安全报告初稿。

Mythos 确实更强,但它不在我的选项里。GLM-5.1 是目前我能落地的性价比顶格选择,也够用了。

如果你有类似的代码安全自查需求,可以直接拿这个脚本改改跑起来。有问题评论区聊。