公司动态
AI担忧的工程解法:RAG与Agent安全实战指南
最近扎克伯格发文反驳“AI 担忧”这件事在开发者圈子里掀起了不少讨论。他公开表达了一个观点AI 的风险不应该靠“少数公司垄断和隐藏模型”来解决开源和更广泛的参与反而能让问题被更快发现、被更及时修正。这个说法有人认同也有人反对。但抛开商业立场不谈有一个问题值得所有做 AI 应用开发的工程师认真想一想普通开发者面对 AI 风险到底能做什么这个问题如果只停留在“AI 会不会毁灭人类”的哲学争论上其实对写代码没有帮助。真正有价值的视角是把这些“担忧”翻译成工程问题。模型幻觉怎么抑制用户 Prompt 注入怎么防生成的敏感内容怎么拦截AI Agent 乱调用工具怎么办本地部署 AI 模型资源不够怎么优化这些问题才是我们每天要面对的“AI 风险”。本文不讨论宏大叙事也不站队而是从 AI 工程实践的角度梳理 AI 担忧背后真实存在的技术问题并给出一套可落地的防御方案。你会看到AI 担忧背后的几个真实工程问题环境准备和基础选型大模型应用、RAG、AI Agent 开发的核心原理一个完整的企业知识库问答助手案例常见报错与排查思路生产环境下的安全与治理建议。如果你正在做 AI 应用开发、准备本地部署 AI 模型或者刚接触 AI Agent 想快速上手这篇内容应该能帮你少走不少弯路。1. “AI 担忧”到底是什么技术人视角的翻译1.1 公众担忧的几个常见来源公开讨论中AI 担忧主要围绕以下几点工作替代自动化是否会大规模取代人类岗位人工智能失控大模型会不会产生自我意识做出不可控行为深度伪造AI 生成图片、视频、语音被用于诈骗和虚假信息传播数据隐私用户对话数据被采集、泄露、滥用偏见与歧视模型输出内容存在性别、种族、地域偏见幻觉问题模型一本正经地编造不存在的事实。这些担忧有些是合理的有些则被过度渲染。但作为开发者我们最关心的不是“AI 会不会造反”而是“这个模型输出的内容可不可信我用它做出来的系统安不安全”。1.2 把担忧翻译成工程问题把上面对应到技术开发的日常其实就是一组具体诉求公众担忧对应的工程问题AI 失控模型输出不可控需要约束输出格式和边界深度伪造缺少内容溯源和检测机制数据隐私用户数据被用于训练或第三方处理偏见与歧视缺乏评估集无法量化输出公平性幻觉缺少知识来源校验无法追查答案出处工作替代自动化流程缺少人工审核环节换句话说公众讨论的是“AI 会不会伤害世界”工程师讨论的是“我如何让模型行为可预期”。前者是愿景问题后者是代码问题。1.3 开源与闭源之争背后的开发含义扎克伯格在发文中强调开源 AI 的价值认为让更多人检查和改进模型比把模型关在笼子里更安全。这个观点在技术社区里有一定代表性因为闭源模型的外部审计成本很高一旦出现问题开发者只能等供应商修复无法自己定位。不过从工程角度开源模型并不是“安全”的代名词。它只是给了你检查的能力但你要不要检查、有没有能力检查是另一回事。实际项目里无论开源闭源都需要做同样一套安全治理输入过滤、输出审核、权限控制、审计日志。这也是本文后面要展开的重点。2. 环境准备与基础选型先搭一个可控的 AI 开发环境在开始写 AI 应用之前先把环境准备好。这里不会绑定某一家厂商的具体版本因为大模型领域版本迭代太快硬写死版本号反而容易误导你。下面以常见环境为例重点演示配置思路具体版本按你的实际环境调整即可。2.1 基础运行环境建议准备以下环境操作系统Windows 10/11、Ubuntu 20.04 或 macOS 12Python3.10 或 3.11推荐使用虚拟环境包管理工具pip 或 poetryIDEVS Code 或 PyCharm建议安装 Python 插件以及 Cursor AI 这类 AI 编程辅助工具Docker可选用于本地部署模型或隔离服务环境。检查 Python 版本python --version建议创建独立的虚拟环境避免系统全局依赖冲突mkdir ai-engineering-guide cd ai-engineering-guide python -m venv venv # Windows venv\Scripts\activate # macOS / Linux source venv/bin/activate2.2 Python 依赖安装根据你的实际需求选择依赖。下面这份依赖覆盖了 API 调用、向量检索、本地模型调用、Web 服务四个方向pip install openai python-dotenv pip install sentence-transformers faiss-cpu numpy pip install fastapi uvicorn pip install pydantic如果要做本地模型部署可以选择性安装 ollama、llama.cpp 或 vLLM 等推理工具。这些工具版本更新很快请以官方文档为准。# 示例本地部署量化模型 pip install llama-cpp-python注意sentence-transformers 会自动下载模型权重首次运行需要联网且下载时间取决于网络环境。如果下载失败可以手动下载后放到缓存目录也可以换用国内的模型镜像源但具体配置请以你实际使用的模型库为准。2.3 项目结构规划一个稍微工程化的 AI 应用建议按这样的结构组织ai-engineering-guide/ ├── app/ │ ├── __init__.py │ ├── main.py # FastAPI 入口 │ ├── config.py # 配置读取 │ ├── llm_client.py # 大模型调用封装 │ ├── retriever.py # 向量检索模块 │ ├── ingestion.py # 文档入库模块 │ ├── safety.py # 输入输出安全过滤 │ └── agent.py # AI Agent 工具调度 ├── data/ │ ├── documents/ # 原始文档 │ └── vector_store/ # 向量索引 ├── .env # 环境变量不要提交仓库 ├── .env.example # 环境变量模板 └── requirements.txt这样的结构把配置、模型调用、检索、安全过滤拆开后续不管是换模型还是加工具改动范围都更小。3. 核心原理拆解让 AI 应用更可信的五个关键点3.1 Prompt 设计控制模型的第一步Prompt提示词是我们与模型交互的主要方式。它不只是一个“输入问题”而是通过指令约束模型的角色、输出格式、回答边界。一个工程化的 Prompt 通常包含以下几部分角色设定告诉模型它是什么身份任务描述明确要完成什么任务输入数据需要处理的用户问题或上下文输出约束格式、长度、是否允许猜测边界兜底不知道的时候怎么回答。下面是一个简单的示例你是一个企业知识库助手。请根据提供的资料回答问题。 要求 1. 只能使用给定资料中的信息作答 2. 如果资料中没有答案请回复“资料中未找到相关内容” 3. 回答不超过 200 字 4. 不要编造事实。 资料 {context} 问题 {question}这个 Prompt 做了两件事一是用“只能使用给定资料”约束模型减少幻觉二是用“资料中未找到相关内容”给模型一个安全的未知出口。3.2 RAG用检索对抗幻觉RAGRetrieval-Augmented Generation检索增强生成是目前缓解大模型幻觉的主流方案。核心思想是不直接让模型凭记忆回答而是先从外部知识库中检索相关内容再把检索结果作为上下文交给模型生成答案。RAG 的完整链路是离线阶段清洗文档 - 切分成文本块 - 向量化 - 存入向量数据库在线阶段用户提问 - 问题向量化 - 检索 top-k 相关文本块 - 拼接到 Prompt - 模型生成回答。这样做的好处很明显模型的知识库可以随时更新不用重新训练回答可以附带引用来源方便审计未知问题会被“资料中没有”拦截降低幻觉概率。3.3 模型评估没有评估就没有安全感很多人觉得模型输出“看起来不错”就足够了但工程化系统不能靠感觉。我们需要建立一套评估机制用一批固定的测试问题反复测试模型的输出质量。评估维度建议覆盖维度说明正确性答案是否与参考答案一致完整性是否遗漏关键信息安全性是否输出违反安全策略的内容格式合规是否符合要求的 JSON 或结构稳定性同样的问题多次回答是否一致评估可以手动做也可以逐步用 LLM 作为裁判实现半自动评估甚至引入人工抽检。评估集是 AI 工程里最值得投入的基础设施。3.4 AI Agent能力越大越要控制AI Agent智能体是近两年最热的方向之一。简单说Agent 不只是“回答你的问题”而是“为了完成你的目标自主决定调用哪些工具、执行哪些动作”。一个最小化的 Agent 循环是接收用户任务模型决定需要调用哪个工具如搜索、计算器、数据库程序执行工具并返回结果模型根据工具结果生成下一步动作或最终回答。这个过程中最大的风险是“不可控的执行”。模型可能因为被恶意 Prompt 注入调用了不该调用的工具。因此 Agent 设计必须遵循最小权限原则只暴露必要工具工具本身要做授权校验关键操作需要人工确认。3.5 内容安全双向防线AI 应用的安全防线主要包括输入过滤拦截恶意 Prompt 注入、敏感信息探测、违法内容请求输出过滤检测模型生成的敏感内容、个人信息、违规链接权限隔离不同用户不能越权访问其他用户的数据审计日志记录谁在什么时间向模型发起了什么请求模型返回了什么。很多 AI 事故的根源不是模型本身“变坏了”而是应用层没有做好拦截。安全应该是一道工程防线而不是模型自带的神秘技能。4. 完整实战构建一个企业内部知识库问答助手下面我们动手做一个最小可运行的企业知识库问答助手。这个案例会覆盖文档入库、向量检索、大模型生成、安全过滤四个核心模块。4.1 创建项目结构按前面规划创建目录mkdir -p app data/documents data/vector_store编写requirements.txtopenai sentence-transformers faiss-cpu numpy fastapi uvicorn python-dotenv pydantic安装依赖pip install -r requirements.txt4.2 配置环境变量创建.env.example# 大模型 API 配置按实际供应商填写 LLM_API_KEYyour_api_key_here LLM_BASE_URLhttps://api.example.com/v1 LLM_MODELyour_model_name # 向量模型名称可换用本地模型 EMBEDDING_MODELBAAI/bge-small-zh-v1.5 # 服务端口 APP_PORT8000复制为.env并填写真实配置cp .env.example .env注意.env文件绝对不能提交到 Git 仓库。在.gitignore中加入.env venv/ data/vector_store/ __pycache__/4.3 编写配置模块app/config.py负责读取环境变量import os from dotenv import load_dotenv load_dotenv() class Settings: llm_api_key: str os.getenv(LLM_API_KEY, ) llm_base_url: str os.getenv(LLM_BASE_URL, https://api.example.com/v1) llm_model: str os.getenv(LLM_MODEL, your_model_name) embedding_model: str os.getenv(EMBEDDING_MODEL, BAAI/bge-small-zh-v1.5) app_port: int int(os.getenv(APP_PORT, 8000)) settings Settings()这里启动时加载一次环境变量避免在代码里到处硬编码密钥。4.4 编写大模型调用封装app/llm_client.pyfrom openai import OpenAI from app.config import settings # OpenAI 兼容客户端 client OpenAI( api_keysettings.llm_api_key, base_urlsettings.llm_base_url, ) def chat_with_context(question: str, context: str) - str: 把检索到的 context 和用户问题拼接到 Prompt 中。 system_prompt ( 你是一个企业知识库助手。请根据提供的资料回答问题。\n 要求\n 1. 只能使用给定资料中的信息作答\n 2. 如果资料中没有答案请回复“资料中未找到相关内容”\n 3. 回答不超过 200 字\n 4. 不要编造事实。 ) user_prompt f资料\n{context}\n\n问题\n{question} response client.chat.completions.create( modelsettings.llm_model, messages[ {role: system, content: system_prompt}, {role: user, content: user_prompt}, ], temperature0.2, ) return response.choices[0].message.content.strip()温度设置为 0.2是为了让输出更稳定。对需要精确回答的场景温度越低越好。4.5 编写文档入库模块app/ingestion.py负责读取文档、切分文本、生成向量并写入 FAISS 索引import os import numpy as np import faiss from sentence_transformers import SentenceTransformer from app.config import settings VECTOR_INDEX_PATH data/vector_store/knowledge.index DOCS_PATH data/vector_store/docs.txt # 加载向量模型 model SentenceTransformer(settings.embedding_model) def load_documents(doc_dir: str) - list[str]: 读取目录下所有 .txt 文件按行切分成文本块示例逻辑。 chunks [] for filename in os.listdir(doc_dir): if not filename.endswith(.txt): continue filepath os.path.join(doc_dir, filename) with open(filepath, r, encodingutf-8) as f: content f.read() # 简单按段落切分生产环境建议按标题、句子长度等策略切分 paragraphs [p.strip() for p in content.split(\n) if p.strip()] chunks.extend(paragraphs) return chunks def build_index(doc_dir: str): 对文档生成向量索引并把原文保存下来。 chunks load_documents(doc_dir) if not chunks: print(没有加载到任何文档) return embeddings model.encode(chunks, normalize_embeddingsTrue) dim embeddings.shape[1] index faiss.IndexFlatIP(dim) index.add(embeddings) faiss.write_index(index, VECTOR_INDEX_PATH) with open(DOCS_PATH, w, encodingutf-8) as f: for chunk in chunks: f.write(chunk.replace(\n, ) \n) print(f入库完成共 {len(chunks)} 个文本块) if __name__ __main__: build_index(data/documents)这里的入库策略是“按段落切分”比较粗糙。生产环境建议使用更精细的切分策略按标题层级切分、按固定长度滑动窗口切分、保留段落上下文并计算 token 长度避免超过模型上下文限制。4.6 编写检索模块app/retriever.pyimport faiss import numpy as np from sentence_transformers import SentenceTransformer from app.config import settings from app.ingestion import VECTOR_INDEX_PATH, DOCS_PATH model SentenceTransformer(settings.embedding_model) index faiss.read_index(VECTOR_INDEX_PATH) with open(DOCS_PATH, r, encodingutf-8) as f: all_chunks [line.strip() for line in f if line.strip()] def retrieve(query: str, top_k: int 3) - list[str]: 根据用户问题检索最相关的 top_k 个文本块。 query_vec model.encode([query], normalize_embeddingsTrue) scores, indices index.search(query_vec, top_k) results [] for i in indices[0]: if i len(all_chunks): results.append(all_chunks[i]) return resultsFAISS 的IndexFlatIP是内积索引配合归一化向量后等价于余弦相似度检索适合中小规模知识库。当文档量达到百万级时再考虑 IVF、HNSW 等近似最近邻索引。4.7 编写安全过滤模块app/safety.py做一个简单的双向过滤示例import re # 简单敏感词列表生产环境应使用更完善的内容安全服务 BLOCKED_USER_WORDS [帮我越权, 绕过认证, 获取他人隐私] BLOCKED_OUTPUT_WORDS [内部密钥, 管理员密码] def validate_input(text: str) - bool: 输入过滤返回 False 表示拦截。 for word in BLOCKED_USER_WORDS: if word in text: return False return True def validate_output(text: str) - bool: 输出过滤返回 False 表示拦截。 for word in BLOCKED_OUTPUT_WORDS: if word in text: return False return True在实际项目中这里应该接入更完整的内容安全能力包括但不限于Prompt 注入检测、个人信息识别、敏感词变体识别、图片/音频内容检测。单纯的关键词匹配只能作为兜底不能作为唯一防线。4.8 编写 FastAPI 入口app/main.pyfrom fastapi import FastAPI from pydantic import BaseModel from app.llm_client import chat_with_context from app.retriever import retrieve from app.safety import validate_input, validate_output app FastAPI(title企业内部知识库问答助手) class QueryRequest(BaseModel): question: str class QueryResponse(BaseModel): answer: str sources: list[str] app.post(/ask, response_modelQueryResponse) def ask(request: QueryRequest): # 1. 输入过滤 if not validate_input(request.question): return QueryResponse( answer抱歉该问题不在允许范围内请重新提问。, sources[], ) # 2. 向量检索 contexts retrieve(request.question, top_k3) if not contexts: return QueryResponse( answer知识库中没有检索到相关内容。, sources[], ) context_text \n.join(contexts) # 3. 大模型生成 answer chat_with_context(request.question, context_text) # 4. 输出过滤 if not validate_output(answer): return QueryResponse( answer抱歉生成内容未通过安全检测请稍后重试或换个问法。, sources[], ) return QueryResponse(answeranswer, sourcescontexts)4.9 运行与验证先准备好测试文档把下面的内容保存为data/documents/company_policy.txt公司年假政策入职满 1 年员工享受 5 天年假满 3 年享受 10 天年假。年假需提前 3 天申请。 远程办公政策员工每周可在周三申请远程办公。远程办公需保证工作时间内可联系。 报销政策单笔报销金额 1000 元以内需部门主管审批超过 1000 元需财务总监审批。执行入库python -m app.ingestion预期输出入库完成共 3 个文本块启动服务uvicorn app.main:app --host 0.0.0.0 --port 8000然后使用 curl 测试curl -X POST http://localhost:8000/ask \ -H Content-Type: application/json \ -d {question: 年假几天 }预期输出是一个 JSON结构类似{ answer: 根据公司政策入职满1年员工享受5天年假满3年享受10天年假。, sources: [ 公司年假政策入职满 1 年员工享受 5 天年假满 3 年享受 10 天年假。年假需提前 3 天申请。 ] }再试一个知识库之外的问题curl -X POST http://localhost:8000/ask \ -H Content-Type: application/json \ -d {question: 今天股票行情如何 }预期模型会回答“资料中未找到相关内容”或者知识库没有检索到相关内容时系统直接返回提示。这正是 RAG 降低幻觉的核心价值模型不会凭训练数据里的记忆乱编。5. AI Agent 开发如何安全地让模型调用工具知识库问答是 RAG 的典型场景但很多业务还需要 AI Agent 去执行任务比如查数据库、发送邮件、调用第三方 API。这部分我们做一个最小安全示例。5.1 定义工具函数app/agent.pyimport json from typing import Callable # 工具注册表 TOOLS {} def register_tool(name: str, description: str, func: Callable): TOOLS[name] { description: description, func: func, } def get_weather(city: str) - str: 模拟查询天气实际项目请替换为真实天气 API。 return f{city}今天多云气温 18-25 摄氏度。 def calc(expression: str) - str: 安全计算只允许数字和四则运算符。 allowed_chars set(0123456789-*/(). ) if not set(expression).issubset(allowed_chars): return 表达式包含非法字符 try: result eval(expression, {__builtins__: {}}, {}) return str(result) except Exception: return 计算失败注意上面calc使用eval只是为了演示实际生产环境应该使用ast.literal_eval或更安全的方法绝不能直接 eval 任意用户输入。注册工具register_tool(get_weather, 查询某个城市的天气, get_weather) register_tool(calc, 计算数学表达式, calc)5.2 模型决定调用哪个工具你是一个智能助手。你可以使用以下工具完成任务。请严格按 JSON 格式返回 {name: 工具名, arguments: {参数名: 参数值}} 如果不需要调用工具请直接返回回答文本。 可用工具 - get_weather: 查询某个城市的天气参数 city - calc: 计算数学表达式参数 expression当模型返回 JSON 时程序解析 JSON并且只在工具注册表中查找未注册的工具不执行。这样就避免模型“发明”工具。import json def run_agent_with_tool_call(model_response: str): try: data json.loads(model_response) tool_name data.get(name) args data.get(arguments, {}) if tool_name not in TOOLS: return 不允许调用该工具 tool TOOLS[tool_name] result tool[func](**args) return result except json.JSONDecodeError: # 模型直接返回文本不调用工具 return model_response这里的关键安全点是工具白名单 参数校验。用户无法直接执行任意代码模型也只能访问注册过的能力。6. 常见问题与排查思路做 AI 应用开发遇到问题不要慌很多都是老问题。下面整理一份高频排查清单。问题现象常见原因解决思路模型回答与知识库无关检索 top_k 太小或检索质量差增大 top_k检查文本切分是否合理更换向量模型模型反复回答“资料中未找到”context 被截断或检索为空检查文档是否入库检查检索阈值查看日志确认 context 内容输出 JSON 格式不稳定模型温度过高或 Prompt 约束不足降低 temperature在 Prompt 中给出 JSON 示例使用结构化输出API 调用报 401API Key 错误或环境变量未生效检查 .env 是否加载检查密钥是否过期避免在代码里硬编码向量检索很慢索引类型不适合或数据量过大使用 HNSW/IVF 索引分批向量化考虑 GPU 推理用户通过 Prompt 注入绕过限制未做输入过滤或过滤规则太简单增加输入检测强调模型忽略注入指令上线前做红队测试本地部署模型显存不足模型参数量太大或未量化使用量化版本模型减小 batch size换轻量模型回答内容包含敏感词输出过滤缺失接入内容安全服务建立敏感词库加入人工抽检排查时建议按“数据链路”顺序来输入 - 检索 - 上下文 - 模型 - 输出。先在每一步打印日志确认卡在哪个环节再针对性地修复。7. 最佳实践与工程建议7.1 密钥与权限管理大模型 API Key 是最高危的敏感信息。建议使用环境变量或密钥管理服务存储绝不硬编码在代码里为不同的环境开发、测试、生产使用不同的 Key生产环境对 Key 做最小权限设置避免一个 Key 拥有所有模型和服务的权限定期轮换 Key并审计调用记录。7.2 数据隐私与合规用户输入往往会包含业务敏感信息。上线前要明确用户的对话数据是否会被第三方模型供应商用于训练如果不能接受选择私有化部署或使用数据隔离方案是否对用户输入做脱敏处理例如手机号、身份证号、地址等信息是否明确告知用户数据用途在合规框架下设计隐私政策。如果使用本地部署的 AI 模型数据不出内网隐私风险会小很多但推理性能和硬件成本是新的挑战。7.3 AI Agent 的权限边界Agent 能调用工具就相当于程序外挂了一个“可以自由行动的手”。这要求工具注册表只包含必要工具未注册工具一律不可调用高风险操作发送邮件、删除数据、转账必须加入二次确认记录 Agent 的完整决策链路包括模型输出、工具参数、执行结果对工具调用设置超时和失败重试策略避免死循环。7.4 内容安全的层次化防御不要只依赖一层过滤。推荐采用第一层输入过滤拦截明显恶意或违规的用户请求第二层Prompt 强化告诉模型拒绝执行和自身指令冲突的内容第三层输出过滤对模型生成内容做合规检测第四层人工抽检对高频、高风险场景做人工复核。7.5 可观测性与日志审计AI 应用的日志比普通应用更关键因为模型输出具有不确定性。每条请求建议记录用户标识输入内容检索命中的文档拼接后的 Prompt注意脱敏模型输出模型调用耗时命中哪一层安全过滤是否有人工复核。有了这些日志才能快速定位问题也才能在安全事件发生时还原现场。7.6 灰度发布与回滚模型更新、Prompt 修改、向量库重建都属于高风险变更不能直接全量发布。建议先在测试环境用小批量流量验证观察输出正确率、安全拦截率、响应时间三个指标一旦出现指标异常立即回滚到上一个稳定版本给每个版本打标签方便对比。8. 总结与学习路线回到开头扎克伯格那篇文章。他说的“AI 担忧”如果落到工程层面其实是一个清晰的行动清单开放检查、公开评估、稳健治理。对普通开发者来说我们不需要等待某个组织给出“安全结论”而应该在自己负责的代码里把可控性、可评估性、可审计性一点一点做起来。本文通过一个完整的企业知识库问答案例演示了 RAG 从文档入库到在线检索的完整链路也展示了 AI Agent 的基本安全设计思路并整理了常见问题排查和最佳实践。你已经具备了自己搭建一个可信 AI 应用的基础能力。接下来可以沿着这几个方向继续深入学习更多 Agent 开发框架尝试自己写一个带工具调用的 Agent深入研究向量检索算法理解 HNSW、IVF 等索引的适用场景研究模型评估方法建立自己的测试集和自动化评估脚本深入了解模型微调、量化与本地部署探索更可控、更私密的部署方案关注 Prompt 注入攻防学习如何对 AI 应用做安全测试。AI 领域的新框架、新模型层出不穷但底层工程能力是相通的数据怎么管理、接口怎么封装、风险怎么拦截、日志怎么留痕。把这些基本功练扎实你面对任何新的 AI 技术都不会慌。现在可以动手了用你自己的文档跑通第一个 RAG 问答系统。