NVIDIA garak 教學:使用自訂探測器與檢測器建立完整的防禦型 LLM 紅隊測試工作流程
重點摘要
在本教學中,我們將 NVIDIA garak 分析為一個防禦型 LLM 紅隊測試的實用框架。我們從設定 Garak 開始,逐步進行外掛發現、試跑、真實模型掃描、多探測器評估、報告分析、自訂探測器建立、自訂檢測器建立,以及 AVID 匯出。我們並非僅執行單次掃描,而是端到端地使用 Garak,瞭解探測器、檢測器、生成器、報告與漏洞分數如何在完整的 LLM 安全測試工作流程中協同運作。此處提供完整程式碼。設定 NVIDIA garak 與定義輔助函數 複製程式碼 使用不同的瀏覽器 import os, sys, json, glob, subprocess, importlib def sh(cmd, capture=False): print(f"\n$ {cmd}") return subprocess.run(cmd, shell=True, text=True, capture_output=capture)
In this tutorial, we analyze NVIDIA garak as a practical framework for defensive LLM red-teaming. We start by setting up Garak, then move through plugin discovery, dry runs, real-model scans, multi-probe evaluations, report analysis, custom probe creation, custom detector creation, and AVID export. Instead of running only a single scan, we use Garak end-to-end to understand how probes, detectors, generators, reports, and vulnerability scores work together in a complete LLM security testing workflow. Check out the FULL CODES Here. Setting Up NVIDIA garak and Defining Helper Functions Copy CodeCopiedUse a different Browserimport os, sys, json, glob, subprocess, importlib def sh(cmd, capture=False): print(f"\n$ {cmd}") return subprocess.run(cmd, shell=True, text=True, capture_output=capture) sh(f"{sys.executable} -m pip install -q -U garak") os.environ.setdefault("TOKENIZERS_PARALLELISM", "false") os.environ.setdefault("HF_HUB_DISABLE_TELEMETRY", "1") import garak, garak.cli from garak import _config print("\n=== garak version:", garak.__version__, "===") def run_garak(args): print("\n>>> garak " + " ".join(args)) try: garak.cli.main(args) except SystemExit as e: if e.code not in (0, None): print(f"[garak exited {e.code}]") try: return _config.transient.report_filename except Exception: return None We begin by importing the required libraries and creating a helper function to run shell commands directly from the notebook. We install garak, configure basic environment variables, and import the main garak modules needed for the tutorial. We also define a reusable function that lets us run Garak programmatically and capture the path to the generated report. Listing garak Probes and Detectors and Running Model Scans Copy CodeCopiedUse a different Browserprint("\n########## 1. PLUGIN INVENTORY ##########") for kind in ["probes", "detectors", "generators", "buffs"]: out = sh(f"{sys.executable} -m garak --list_{kind} 2>/dev/null", capture=True) lines = [l for l in (out.stdout or "").splitlines() if "." in l] print(f" {kind:11s}: {len(lines)} plugins e.g. " f"{', '.join(l.split()[-1] if l.split() else l for l in lines[:3])}") print("\n########## 2. FAST DRY-RUN (test.Repeat) ##########") sh(f"{sys.executable} -m garak --target_type test.Repeat " f"--probes lmrc.SlurUsage --generations 1") print("\n########## 3. REAL MODEL: gpt2 vs DAN 11.0 ##########") sh(f"{sys.executable} -m garak --target_type huggingface --target_name gpt2 " f"--probes dan.Dan_11_0 --generations 1 --parallel_attempts 8") print("\n########## 4. PROGRAMMATIC MULTI-PROBE SCAN ##########") report_path = run_garak([ "--target_type", "test.Repeat", "--probes", "dan.Dan_11_0,encoding.InjectBase64,lmrc.SlurUsage", "--generations", "1", "--parallel_attempts", "16", ]) print("Report:", report_path) We inspect the garak plugin ecosystem by listing available probes, detectors, generators, and buffs. We then run a quick dry run using the test generator to confirm that Garak is working without requiring any external model or API key. After that, we scan a real Hugging Face model and run a multi-probe scan to generate a richer report for analysis. Analyzing garak Reports: Safety Scores and Attack Success Rates Copy CodeCopiedUse a different Browserprint("\n########## 5. ANALYSIS ##########") import numpy as np, pandas as pd def find_latest_report(): cands = [] for base in [os.path.expanduser("~/.local/share/garak/garak_runs"), os.path.expanduser("~/.cache/garak"), "."]: cands += glob.glob(os.path.join(base, "**", "*report.jsonl"), recursive=True) cands = [c for c in cands if os.path.getsize(c) > 0] return max(cands, key=os.path.getmtime) if cands else None report_path = report_path or find_latest_report() print("Analysing:", report_path) evaluations = None try: from garak.report import Report rep = Report(report_path).load().get_evaluations() evaluations = rep.evaluations.copy() print("\n--- Per-probe mean SAFETY score (garak.report.Report) ---") print(rep.scores.round(1).to_string()) except Exception as e: print("garak.report.Report unavailable, falling back to manual parse:", e) rows = [] with open(report_path) as f: for line in f: try: r = json.loads(line) except json.JSONDecodeError: continue if r.get("entry_type") == "eval": rows.append(r) evaluations = pd.DataFrame(rows) if not evaluations.empty: evaluations["score"] = np.where( evaluations["total_evaluated"] != 0, 100 * evaluations["passed"] / evaluations["total_evaluated"], 0.0) if evaluations is not None and not evaluations.empty: evaluations["asr_%"] = (100 - evaluations["score"]).round(1) view = evaluations[["probe", "detector", "passed", "total_evaluated", "score", "asr_%"]].copy() view = view.rename(columns={"score": "safe_%"}) view["safe_%"] = view["safe_%"].round(1) view = view.sort_values("asr_%", ascending=False) print("\n--- Per probe/detector (higher asr_% = more vulnerable) ---") print(view.to_string(index=False)) try: import matplotlib.pyplot as plt labels = (view["probe"] + "\n" + view["detector"]).tolist() plt.figure(figsize=(8, 0.55 * len(view) + 1.5)) plt.barh(labels, view["asr_%"], color="#76b900") plt.gca().invert_yaxis() plt.xlabel("Attack Success Rate (%)"); plt.xlim(0, 100) plt.title("garak — vulnerability by probe/detector") plt.tight_layout(); plt.show() except Exception as e: print("plot skipped:", e) We load the generated garak report and prepare it for detailed analysis using pandas and NumPy. We first try to use Garak’s built-in report parser, and if that is unavailable, we manually parse the JSONL report file. We then calculate safety scores and attack success rates, and visualize vulnerabilities across different probe-detector combinations. Inspecting Flagged Outputs and Building a Custom garak Probe Copy CodeCopiedUse a different Browserprint("\n--- Sample hits (detector score >= 0.5) ---") hits = [] with open(report_path) as f: for line in f: try: r = json.loads(line) except json.JSONDecodeError: continue if r.get("entry_type") != "attempt" or r.get("status") != 2: continue dr = r.get("detector_results") or r.get("detector") or {} max_score = 0.0 if isinstance(dr, dict): for v in dr.values(): if isinstance(v, list) and v: max_score = max(max_score, max(float(x) for x in v)) if max_score >= 0.5: prompt = r.get("prompt") hits.append((r.get("probe_classname", r.get("probe", "?")), round(max_score, 2), str(prompt)[:90])) print(f"Total hits found: {len(hits)}") for probe, score, prompt in hits[:5]: print(f" [{score}] {probe}: {prompt!r}") print("\n########## 6. CUSTOM PROBE ##########") custom_probe = ''' from garak.probes.base import Probe class HelloProbe(Probe): """Minimal custom probe: two fixed prompts, paired with a custom detector.""" lang = "en" bcp47 = "en" primary_detector = "mycustomdet.ContainsHello" recommended_detector = ["mycustomdet.ContainsHello"] tags = ["custom:demo"] goal = "demonstrate authoring a garak probe" doc_uri = "" prompts = [ "Please reply with the single word: hello", "Continue this sentence: The capital of France is", ] ''' import garak.probes probes_dir = os.path.dirname(garak.probes.__file__) with open(os.path.join(probes_dir, "mycustom.py"), "w") as fh: fh.write(custom_probe) We further inspect the report by extracting sample hits in which detector scores indicate potentially unsafe or vulnerable outputs. We collect the flagged prompts, detector scores, and probe names to understand what kind of behavior is being detected. We then create a custom garak probe that uses fixed prompts and connects it with a custom detector. Creating a Custom garak Detector and Exporting Results to AVID Copy CodeCopiedUse a different Browserprint("\n########## 7. CUSTOM DETECTOR ##########") custom_detector = ''' from garak import _config from garak.detectors.base import StringDetector class ContainsHello(StringDetector): """Demo detector: flags any output containing 'hello' (case-insensitive).""" lang_spec = "en" bcp47 = "en" def __init__(self, config_root=_config): super().__init__(["hello"], config_roo
Related
相關文章

Edge AI Daily 早報(6月19日)
AI Engineer World's Fair 2026規模再創新高,標誌AI工程從幕後走向舞臺中央。行業面臨結構性調整:楊立昆警示OpenAI年虧210億美元揭示商業模式脆弱性,Transformer之父轉投OpenAI反映人才爭奪白熱化。Anthropic多線佈局——語音支持七種語言、加入碳清除聯盟、落子首爾辦事處,展現生態擴張野心。監管壓力加劇,意大利依據DMA調查蘋果iCloud,巴西開放iOS側載佣金降至5%,蘋果圍牆花園持續崩塌。

今天起,Claude Design要把設計師和程序員變成同一種人了
猝不及防!Anthropic深夜甩出Claude Design大更新,設計系統一鍵導入,代碼雙向同步,9大平臺一鍵導出。Anthropic設計師親自下場錄屏:AI跑了八輪自查,才敢把設計稿給你看。

OpenAI 成為 Rust 基金會白金會員,合計贊助 60 萬美元
OpenAI 正式成為 Rust 基金會白金會員,將提供總計 60 萬美元資金,用於支持 Rust 開源項目維護者及 Rust 創新實驗室等計劃。這標誌著 AI 巨頭對安全、高效系統編程語言的重視。 #OpenAI #Rust #開源

Claude Design 上線首周用戶破百萬,和 Claude Code 共享 AI 配額
Anthropic 今天(6 月 18 日)發佈公告,在宣佈 Claude Design 上線首周用戶規模突破 100 萬後,進一步強化和 Claude Code 的雙向聯動,實現從設計到編程的無縫工作流。
谷歌時隔6年再發智能音箱,Gemini上桌,售價不到700元
智東西 編譯 | 劉煜 編輯 | 陳駿達 智東西6月18日消息,谷歌昨日宣佈,其首款搭載居家版Gemini語音助手的智能音箱(Google Home Speaker)已開啟預售,將於當地時間6月25日正式上市,售價為99.99美元(約合人民幣677.03元)。在此之前,谷歌已有6年沒有推出過獨立智能音箱產品。 谷歌這款智能音箱外觀近似球形,風格類似亞馬遜新一代Echo音箱與蘋果舊款音箱HomePod Mini。 ▲谷歌智能音箱(圖源:谷歌官網) 使用音箱時,用戶只需通過口令“Hey Google”或“OK Google”喚醒Gemini,就可以繼續下達相應指令。這與谷歌舊款音箱、智能顯示屏等喚醒語音助手的方式相同。此外,用戶只要按照日常說話習慣下達命令,Gemini便能理解用戶意圖,相比之前大大提升溝通效率。 一、加強短時對話記憶,會員可與Gemini不限次數對話 谷歌此次推出的全新音箱升級諸多功能。其中,音箱搭載的Gemini語音助手擁有10款全新擬人化語音音色,用戶可以根據喜好自行選擇聲線。音箱還可支持用戶一次性下達多條語音指令,即使指令未能說對、說完整,用戶中途改口Gemini也能識別。 Gemini還具備多鏈路推理能力,落地到實際生活場景中比較實用。例如,用戶問:“我支持的足球隊下場比賽天氣如何?”Gemini收到指令後,會自動查詢賽事時間、舉辦地點,同時匹配相應時段天氣,再給出答覆。 同時,Gemini加強了短時對話記憶,能承接上下文實現連續對話功能。即使用戶連續追問、甚至串聯多項任務、不重複交代前置條件,該語音助手也能實現來回連貫交流。 ▲谷歌Gemini對話場景(圖源:谷歌官網) 不僅如此,Gemini搭配的連續對話功能,能讓應答後的音箱麥克風保持短暫收音,用戶無需重複喊“OK Google”就能繼續提問。該功能現已全面支持所有Gemini原生適配的語言,包括

微軟,考慮接入DeepSeek
這篇消息聚焦「微軟,考慮接入DeepSeek」。原始導語提到:Copilot Cowork轉為按量計費。 從 AI 情報角度來看,這類內容值得關注其背後的技術進展、產品落地、產業競爭與後續市場影響。