mehmandarov/llm-security-and-predictability
GitHub: mehmandarov/llm-security-and-predictability
一个以发票提取为示例场景的 LLM 应用工程教学项目,演示如何通过多层 Guardrails、确定性验证、自我纠正和多模型共识等模式,在概率性 LLM 之上构建安全、可预测的生产级系统。
Stars: 6 | Forks: 0
# 驯服混沌:LLM 驱动应用中的安全性与可预测性
此代码库包含了演讲 **“Taming the Chaos”** 的演示代码和示例。它演示了在概率性 LLM 之上构建安全、可预测系统的实用模式。
## 概述
LLM 本质上是概率性的 —— 非常适合发挥创造力,但用于业务逻辑却很危险。它们可能会泄露敏感数据、服从注入的指令、产生幻觉值,并且每次都给出不同的答案。本项目逐步介绍了“LLM 悲痛的 5 个阶段”,展示了如何从天真幼稚的实现走向经过生产环境加固的韧性系统 —— 保护输入和输出、使用确定性代码进行验证、通过反馈循环进行自我纠正,以及通过多模型共识建立信心。附加章节演示了通过“镜像测试”进行合成验证。
## 前置条件
* **Java 25+**
* **Maven**
* **Ollama**(可选 —— 用于端到端集成测试)
# 安装:https://ollama.com
ollama pull gemma4:e2b
ollama pull llama3.2:1b # 可选 —— 启用真正的多模型共识演示
## 快速开始
1. **克隆代码库:**
git clone
cd llm-security-and-predictability
2. **运行单元测试(无需 LLM):**
核心逻辑通过带有模拟模型的 JUnit 测试进行演示。
mvn test
3. **运行端到端测试(需要 Ollama):**
这些测试会访问真实的本地 Ollama 实例 —— 非常适合现场演示。
ollama serve # 在单独的终端中,如果尚未运行
mvn verify
如果无法连接到 Ollama,集成测试将被自动跳过。
## 项目结构
代码按 **章节** 进行组织,与演讲的叙述相对应:
* **第 1 章:基础(天真实现)**
* `SimpleInvoiceExtractor.java`:基本的 LangChain4j 服务接口。
* *经验教训:* 结构化输出并不等于验证。
* **第 2 章:攻击(安全与 Guardrails)**
* **第 1 层 —— 简单的 Guardrails:** `PromptInjectionGuardrail.java`(关键字黑名单 + 分隔符突破检测)、`InputLengthGuardrail.java`(阻止 prompt 填充)、`PiiGuardrail.java`(从输出中脱敏敏感数据)。
* **第 2 层 —— 金丝雀陷阱:** `CanaryTokenGuardrail.java` + `CanaryInvoiceExtractor.java` —— 嵌入一个秘密的“金丝雀” token 作为注入绊线。
* **第 3 层 —— 输出格式:** `OutputFormatGuardrail.java` —— 拒绝非 JSON 响应。
* **第 4 层 —— 分隔符三明治:** `SandwichedInvoiceExtractor.java` —— 将用户输入包裹在 `` / ` ` 分隔符中(OWASP 推荐)。测试表明,仅靠三明治并 **不能** 阻止突破 —— 这只是一种提示,而非强制执行。
* **第 5 层 —— 保安:** `IntentClassifier.java` —— 一个廉价的模型,在调用昂贵模型之前预先筛查恶意意图。
* **第 6 层 —— 坚固城堡:** `FortifiedInvoiceExtractor.java` —— 结合所有 guardrails。通过简单三明治提取器的同一个 `SANDWICH_BREAKOUT` 在这里会被捕获。
* *此外:* `SecureInvoiceExtractor.java`(连接了注入 + PII guardrails 的提取器)。
* *经验教训:* 永远不要相信输入;永远不要相信输出。纵深防御。
* **第 3 章:幻觉(确定性验证)**
* `StrictValidator.java`:*确定性地* 验证 schema(Jakarta Beans)和业务逻辑(数学、日期)。
* `OutputNormalizer.java`:规范化 LLM 输出(金额 → 2 位小数,货币 → 大写,字符串修剪空白),从而使“足够接近”变为完全相同。纯 Java,无 LLM。
* `InvoiceCalculatorTool.java`:带有 `@Tool` 注解的方法(`calculateTotal`、`calculateTax`)—— LLM 决定 *何时* 计算,但数学运算本身是确定性的 Java 代码。
* `ToolAwareInvoiceExtractor.java`:通过 `AiServices.builder().tools()` 连接了工具的提取器。
* `ExpressionEvaluator.java`:安全的算术计算器 —— LLM 生成公式,Java 执行它。拒绝代码注入尝试。
* `FormulaGenerator.java`:要求 LLM 输出算术表达式,而不是计算好的答案。
* *经验教训:* 相信代码,而不是模型。不要让 LLM 做数学运算 —— 给它一个计算器,或者让它编写公式并由你自己执行。
* **第 4 章:讨价还价(自我纠正)**
* `CorrectiveExtractor.java`:将验证错误反馈给 LLM 以进行第二次尝试。
* *经验教训:* 将运行时异常转化为成功的交易。
* **第 5 章:委员会(共识与确定性)**
* `MultiModelConsensus.java`:查询多个模型并使用多数投票来确保准确性。
* `StabilityAnalyzer.java`:运行 N 次提取并计算每个字段的同意百分比。
* `SafeExtractionPipeline.java`:顶点之作 —— 串联 提取 → 验证 → 自我纠正,并返回类型化的裁决:`ACCEPTED`、`NEEDS_REVIEW` 或 `REJECTED`。
* *经验教训:* 使用 Seeds、共识和稳定性度量将概率转化为可预测性。
* *附加内容:* 基于 Seed 的可重现性测试(`OllamaEndToEndIT.java`)验证 `temperature=0` + `seed=42` 是否产生相同结果。
* **附加内容:大结局(镜像测试)**
* `MirrorVerifier.java`:执行往返“重构”(JSON → 文本)以检测静默遗漏。
* *经验教训:* 当没有“黄金标准答案”时,根据原始事实验证提取结果。
## 关键技术
* **LangChain4j**:用于 LLM 集成和 AI Service 接口。
* **Ollama**:用于端到端测试的本地 LLM 推理(gemma4:e2b、llama3.2:1b)。
* **Jakarta Validation**:用于 schema 和约束验证。
* **JUnit 5 & Mockito**:用于对模式进行单元测试。
* **Maven Failsafe**:用于集成测试(`mvn verify`)。
## 测试结构
* `Chapter1Test` – `Chapter6Test`:每个章节的单元测试,模拟模型,速度快。
* `LLMValidationTalkTest.java`:整个演讲的单文件演练(按顺序包含所有章节)。
* `OllamaEndToEndIT.java`:针对真实 Ollama 实例的实时集成测试(23 个测试,`mvn verify`)。
* `InvoiceTestData.java`:共享测试数据 —— 干净的发票、注入攻击、`SANDWICH_BREAKOUT`、`PROMPT_STUFFING`、`MESSY_OCR`、`AMBIGUOUS_DATE`、PII 泄露等。
*注意:通用模型和共享代码位于 `com.mehmandarov.llmvalidation.model` 中。*
## 重要提示
代码、幻灯片和文本内容归 Rustam Mehmandarov 所有,未经事先同意不得复制。
标签:AI工程, AI风险缓解, API密钥检测, DLL 劫持, LangChain4j, 域名枚举, 大语言模型, 示例代码, 输入验证