ARTICLE DETAIL

资讯详情

深耕郑州网站建设与运营推广的一线实战洞察。

LLM Agent中敏感数据脱敏与工具调用的安全实践

LLM Agent中敏感数据脱敏与工具调用的安全实践 在 LLM Agent 工作流里敏感数据经常会以两种方式泄露一种是被拼进 prompt 后直接发送给大模型另一种是在工具调用参数里被打包出去。很多团队本地跑 Agent 时觉得没问题一旦把 Agent 接到真实业务系统就会发现账号、手机号、身份证号、密钥、内网地址全都在对话历史里明文出现。更麻烦的是如果为了安全给数据做了脱敏工具调用又经常报错——LLM 生成的是脱敏后的占位符工具却要求真实参数两边对不上任务直接失败。这次我们重点聊一个实际问题如何在 LLM Agent 工作流中处理敏感数据同时不破坏 tool calls。核心思路不是“永远不要用敏感数据”而是“LLM 永远不要直接接触明文敏感数据工具执行时才在最小范围内还原真实值”。下面的内容会覆盖脱敏策略、Agent 调用链设计、接口层处理、批量任务场景、测试方法和排查清单适合正在做 Agent 应用落地、又担心数据合规的团队。1. 核心能力速览能力项说明核心问题Agent 在调用工具时需要真实参数但大模型对话链路和处理日志不应暴露明文敏感数据关键设计脱敏后入 prompt工具执行前还原还原动作只在可信执行层发生支持技术栈主流的 Agent 框架均可适配如 LangChain、LlamaIndex、自研编排层示例以 Python pydantic 为例环境要求Python 3.9Agent 框架依赖Redis/数据库用于临时映射KMS 或环境变量管理密钥是否影响工具调用需要额外实现脱敏映射和还原逻辑但设计正确后不会破坏参数结构是否支持批量任务支持建议为每个任务分配 trace_id统一管理脱敏映射是否提供 API取决于 Agent 服务自身建议在 API 网关层做统一脱敏和审计适合场景金融、医疗、企业内部系统、客服知识库、自动化运维等含敏感字段的业务场景不适合场景对实时性要求极高且没有中间存储条件的环境以及需要模型本身理解敏感字段语义的场景注意这里的方案属于通用工程实践不是某个开源项目的安装包所以不存在“一键启动”。但你可以把这套模式直接落地到现有 Agent 项目里。2. 适用场景与使用边界LLM Agent 的典型工作流是用户输入自然语言 - Agent 规划 - 调用工具 - 拿到结果 - 继续推理 - 返回回答。这个链路里至少有三个环节会碰到敏感数据用户原始输入可能包含敏感字段。工具返回结果可能包含业务侧的真实数据。中间态的 prompt、日志、debug 信息可能被打印到控制台或采集到日志平台。这套方案主要适用于客服机器人查询订单、物流、会员信息但不希望模型记住用户手机号。企业内部 Copilot 读取数据库记录或内部系统 API但需要对模型隐藏员工隐私字段。自动化运维 Agent 执行脚本时需要在命令行里传入 token但不想让模型看到 token 明文。批量数据处理流水线中Agent 需要逐条调用工具处理敏感内容。使用边界需要明确脱敏不等于加密。脱敏后模型无法推理真实值但脱敏映射表本身需要额外保护。模型提供方如果与业务方是同一个信任域可能允许在私有化部署场景下直接传明文但这同样建议走审计日志。涉及个人隐私、金融数据、医疗数据时必须先确认数据来源合法、使用用途合规并且获得必要授权。禁止用这套方案绕过外部数据保护要求。脱敏只是技术手段不能替代安全制度。3. 环境准备与前置条件开始之前先确认你的 Agent 项目是否满足以下基础条件。没有统一版本约束但建议按清单检查。项目建议操作系统Linux / macOS / Windows 均可生产环境建议 Linux语言版本Python 3.9如果使用 pydantic v2 则需 3.8Agent 框架LangChain、LlamaIndex 或自研按项目实际选择LLM 服务支持标准 Chat Completions 或兼容接口均可本地部署或远程 API 都可以密钥管理环境变量、Vault、KMS、云厂商密钥管理服务任选一种临时存储Redis、内存字典、数据库表用于存放脱敏映射关系日志系统需要在日志采集前过滤敏感字段GPU本方案本身不依赖 GPU属于工程层逻辑是否使用 GPU 取决于 LLM 推理方式部署前检查脚本大致如下# 检查 Python 版本 python --version # 检查当前环境是否为虚拟环境 which python # 检查关键依赖 pip show pydantic langchain openai redis 2/dev/null | grep -E Name|Version如果缺少依赖按项目实际情况安装示例# 示例具体版本按你的项目要求 pip install pydantic langchain openai redis注意不要把敏感数据直接写死在.env并提交到 Git。.env文件本身也要加入.gitignore。4. 数据脱敏与工具调用的基本模式处理敏感数据不破坏 tool calls关键在于把“模型看到的”和“工具实际执行时用的”分开。4.1 核心思路一般流程是入口层识别敏感字段。用占位符替换敏感字段占位符必须唯一、可还原、类型一致。Agent LLM 只接触脱敏后的文本和参数。Agent 决定调用工具时工具参数中仍然携带占位符。进入工具执行层之前根据映射关系将占位符还原成真实值。工具执行完成如果返回结果包含敏感字段对返回结果再次脱敏再交给 LLM。全程记录审计日志日志中只保留脱敏后的内容。4.2 脱敏映射设计脱敏映射要解决“还原”问题。最常见的做法是import uuid import re from typing import Dict, Any class DataMasker: def __init__(self): self._mapping: Dict[str, str] {} def _mask_value(self, real_value: str, prefix: str) - str: # 生成唯一占位符保持可辨识性 placeholder f__{prefix}_{uuid.uuid4().hex[:8]}__ self._mapping[placeholder] real_value return placeholder def mask_text(self, text: str, rules: Dict[str, str]) - str: masked text for pattern, prefix in rules.items(): def _replace(match): return self._mask_value(match.group(0), prefix) masked re.sub(pattern, _replace, masked) return masked def mask_json(self, obj: Any, sensitive_keys: list[str]) - Any: if isinstance(obj, dict): new_obj {} for key, value in obj.items(): if key in sensitive_keys and isinstance(value, str): new_obj[key] self._mask_value(value, key) else: new_obj[key] self.mask_json(value, sensitive_keys) return new_obj elif isinstance(obj, list): return [self.mask_json(item, sensitive_keys) for item in obj] return obj def unmask(self, masked_value: str) - str: return self._mapping.get(masked_value, masked_value) def parse_tool_args(self, args: dict, sensitive_keys: list[str]) - dict: # 还原工具参数 result {} for k, v in args.items(): if k in sensitive_keys and isinstance(v, str): result[k] self.unmask(v) else: result[k] v return result def clear(self): self._mapping.clear()这里的关键是占位符必须是完整字符串不能破坏 JSON 结构。比如phone: __phone_ab12cd34__仍然是合法字符串LLM 可以把它当作参数原样传给工具。工具执行层拿到后还原成真实验证码。4.3 Agent 工具调用流程示例下面以 Python self-masker 的方式演示一个 “查询订单” 工具。前提是假设 Agent 框架支持在工具执行前插入自定义逻辑。import json from pydantic import BaseModel, Field masker DataMasker() SENSITIVE_KEYS [user_id, phone, id_card] class OrderQueryInput(BaseModel): user_id: str Field(description用户唯一ID占位符形式) order_id: str Field(description订单号) def order_query_tool(user_id: str, order_id: str) - dict: # 工具执行层先还原脱敏参数 real_user_id masker.unmask(user_id) # 这里调用真实业务 API比如查订单系统 # 以下仅作为示例 result {order_id: order_id, status: PAID, buyer_phone: 13800138000} # 返回给 LLM 之前对敏感字段再次脱敏 masked_result masker.mask_json(result, [buyer_phone]) return masked_result # 模拟 Agent 传入脱敏后的参数 tool_args { user_id: __user_id_00000000__, order_id: ORD20250101 } # 实际调用工具 output order_query_tool(**tool_args) print(json.dumps(output, ensure_asciiFalse))这段示例演示了“工具内部还原”和“返回结果再脱敏”但没有把明文 user_id 放回 prompt。4.4 不破坏 tool calls 的设计要点要让 LLM 生成的工具调用参数仍然有效有四个点必须注意占位符不要去重每个敏感值都要映射到唯一占位符。占位符格式要稳定建议用__字段名_随机串__避免模型误改。工具的参数 schema 描述中可以明确说“user_id 为脱敏占位符不需要解释真实值直接透传”。还原逻辑必须只发生在工具执行边界不要在模型上下文里还原。如果你在 LangChain 中使用tool装饰器可以在args_schema中使用字符串类型然后在底层函数内部调用masker.unmask()。这会保证模型侧看不到真实值工具侧又拿得到真实值。5. 功能测试与效果验证把方案写完后不能只看代码能跑还要验证“模型确实没有接触明文”“工具调用没有被破坏”。建议用下面这套测试流程。5.1 基础脱敏验证测试目的确认敏感字段在脱敏后不会出现在 prompt 中且占位符能还原。def test_mask_and_unmask(): masker DataMasker() text 我的手机号是13800138000请帮我查订单 masked masker.mask_text(text, {r1[3-9]\d{9}: phone}) print(脱敏后:, masked) assert 13800138000 not in masked # 解析出占位符并还原 match __import__(re).search(r__phone_[0-9a-f]__, masked) assert match assert masker.unmask(match.group(0)) 138001380005.2 Agent 调用链验证测试目的确认 Agent 在规划阶段看不到明文工具执行时能还原。# 在 Agent 项目中启动服务示例命令需按实际项目调整 python agent_service.py --config ./config.yaml然后构造请求{ message: 帮我查用户 13800138000 的订单, trace_id: trace-test-001 }观察日志输出prompt 日志里应该只有__phone_xxxxxxxx__不应该有13800138000。工具调用记录里user_id字段应该是脱敏占位符。工具执行的内部日志中还原后的真实值只出现在工具执行日志里且必须确认该日志不会被 LLM 上下文捕获。最终返回给用户的内容里如果需要显示手机号则应由业务系统格式化成138****8000由模板层完成不要走 LLM 生成。5.3 失败注入测试测试目的验证占位符还原失败时不会导致工具调用静默失败。import pytest def test_unmask_missing_placeholder(): masker DataMasker() # 不存在映射时返回原字符串 assert masker.unmask(__phone_unknown__) __phone_unknown__如果还原失败返回原占位符说明工具参数不对应该在工具内部继续抛错而不是把占位符当作真实值去查询。否则会出现“工具调通了但查不到数据”的隐蔽问题。5.4 批量任务验证批量任务通常来自文件或者队列。建议每条数据生成独立 masker 实例或独立 trace_id。批量处理时不能共享同一个映射表否则会导致不同批次的数据串号。# 示例批量任务处理 def process_batch(records): for record in records: masker DataMasker() masked_record masker.mask_json(record, [phone, email]) # 发送给 Agent运行工具调用 # 处理完成后清理映射 masker.clear()测试时重点观察批量任务是否在随机点出现占位符混乱。工具执行是否因为还原错误而失败。最终输出是否错位。6. 接口 API 与批量任务如果 Agent 服务对外提供 HTTP API敏感数据会在两个地方出现请求体里和响应体里。建议在 API 网关层做统一处理。6.1 请求参数脱敏外部用户可能会直接传明文敏感字段到 Agent 服务。可以在服务入口处先做脱敏再进入 Agent 编排。# FastAPI 示例 from fastapi import FastAPI, Request from pydantic import BaseModel app FastAPI() class QueryRequest(BaseModel): message: str user_id: str phone: str app.post(/agent/query) async def agent_query(req: QueryRequest): # 入口脱敏 masked_phone masker.mask_text(req.phone, {r1[3-9]\d{9}: phone}) # 后续 Agent 流程只使用 masked_phone而不是 req.phone ...注意不能只依赖拦截器。业务代码里如果继续使用原始req.phone那脱敏就是假的。比较好的办法是在请求模型进入业务逻辑前就把明文字段替换成脱敏值后续代码只认脱敏值。6.2 返回值脱敏工具返回结果中如果包含敏感字段在写入 LLM 上下文之前要统一脱敏。如果整个 Agent 返回给用户时需要明文再由最外层 API 根据用户权限决定是否放行。6.3 批量任务设计批量任务的建议每个任务配置一个trace_id。脱敏映射关系以trace_id为 key 保存到 Redis设置 TTL。批量任务结束时清理映射。失败任务要保留映射方便排查但需要加密存储。日志中间件统一过滤敏感字段。# Redis 映射存储示例 import redis r redis.Redis(hostlocalhost, port6379, db0) trace_id batch_20250101_001 def store_placeholder(trace_id, real_value, placeholder, ttl3600): key fmask:{trace_id}:{placeholder} r.setex(key, ttl, real_value) def get_real_value(trace_id, placeholder): key fmask:{trace_id}:{placeholder} return r.get(key)6.4 调用 LLM 时的请求模板不管 Agent 框架是什么最终都要构造 prompt 或 messages 发给 LLM。下面是标准 Chat Completions 接口的示例这里只展示脱敏后的数据import requests url http://your-llm-service/v1/chat/completions headers {Authorization: Bearer your_token} payload { model: your-agent-model, messages: [ { role: system, content: 你是订单助手。用户数据已经脱敏只识别占位符不要尝试解释占位符含义。 }, { role: user, content: 用户 __user_id_abcd1234__ 的订单状态是什么 } ], temperature: 0 } resp requests.post(url, jsonpayload, timeout60) print(resp.json())注意这里的your_token是示例不要把真实 token 写到博客或仓库里。7. 资源占用与性能观察脱敏和加密会带来额外计算开销但通常不会成为瓶颈。实际耗时主要看三点正则匹配的规则数量。脱敏字段的数量。加解密算法的选择。如果使用对称加密如 AES而不是简单的映射加密和解密会有微秒级开销对单次 Agent 调用影响不大。但如果批量任务达到每秒上千次就要考虑预取映射或批量解密。观察性能的方法是在脱敏函数前后打点统计耗时。记录工具执行真实耗时。分别统计有脱敏和无脱敏时的端到端延迟。观察 LLM 生成的 prompt token 数量。脱敏占位符虽然比明文短但会占用一定 token尤其当敏感字段很多时会影响模型推理效果。降低性能影响的建议只对真正敏感的字段脱敏不要把所有字段都脱敏。使用预编译正则。映射表直接放进程内存不用每次请求查 Redis。对工具执行层使用函数级缓存避免同一请求内重复还原。8. 常见问题与排查方法在实际使用中出错最多的地方不是脱敏算法本身而是 Agent 框架的调用链过于混乱。下面整理了一批常见问题。问题现象可能原因排查方式解决方案工具的某个必填参数变成了脱敏占位符但工具没有还原还原逻辑放错了位置查看工具执行前的参数日志在工具入口调用unmaskLLM 生成工具参数时把占位符拆开或加了解释prompt 中没说明占位符规则检查 messages 内容在 system prompt 中明确“占位符是合法参数直接透传不要修改”还原失败工具拿到的是占位符原文映射表未同步到执行节点检查映射表内容和 trace_id使用 Redis 统一存储映射日志里出现了明文敏感数据日志打印了未脱敏的原始对象grep 日志中手机号/姓名关键词增加日志脱敏 filter统一在日志输出前清洗批量任务中不同任务的占位符互相覆盖使用了全局共享 masker 实例检查并发代码每个任务独立实例或使用 trace_id 隔离脱敏后的文本语义丢失模型回答问题变得很奇怪脱敏规则过严模型无法理解字段含义查看模型输出在提示词中增加字段语义说明例如“__user_id__表示用户编号无需解析”接口返回给用户时看不到手机号返回结果也被脱敏了检查 API 返回逻辑在最外层根据用户权限还原并格式化调用外部工具时token 被包含在 JSON 参数中模型参数 schema 设计不合理检查工具参数定义不要把密钥作为工具参数改为从工具内部缓存或密钥库读取Agent 在多次调用工具时重复请求真实值每次还原都查一次外部存储查看调用链路同一次请求内部使用本地缓存排查通用流程打开 debug 日志但注意日志系统本身是否安全。在脱敏前、脱敏后、工具执行前、工具执行后各打一条结构化日志。用 trace_id 串联整条调用链。如果某个工具调用失败优先看第 3 条日志中参数是否已还原。确认 LLM 的推理日志不会回传给模型或发送到公网分析服务。9. 最佳实践与使用建议工程落地上建议按下面这套思路来推进。9.1 先做数据分类再做脱敏不是所有数据都需要脱敏。可以先按敏感级别分类级别示例处理方式高敏感身份证号、银行卡号、密码、token禁止进入 prompt工具调用时使用临时凭证中敏感手机号、邮箱、地址脱敏后进入 prompt工具执行前还原低敏感用户名、订单号可进入 prompt但仍需脱敏展示非敏感商品名称、时间格式正常使用9.2 最小权限原则工具调用时需要的权限越低越好。比如查询订单只需要user_id就不要把整个用户对象传给工具。密钥管理要遵循最小权限不同工具可以使用不同的 token避免一个 token 泄露导致全局崩盘。9.3 分层保护脱敏只是第一层。还要做应用层加密映射表、缓存里的敏感数据使用字段级加密。传输层安全Agent 服务与 LLM、工具服务之间使用加密通道。存储层保护日志平台、数据库备份中进行脱敏或加密。运行环境隔离敏感数据还原动作只允许在可信执行节点完成。9.4 审计与追踪每一条工具调用都应该有审计日志。日志字段至少包含{ trace_id: trace-test-001, timestamp: 2025-01-01T00:00:00Z, agent_action: call_tool, tool_name: order_query, tool_input_masked: { user_id: __user_id_abcd1234__, order_id: ORD20250101 }, tool_input_real: 已加密存储不在日志中展示, tool_result_masked: { status: PAID }, user_permission: order:read }审计日志不应该记录真实值如果必须记录需要加密且严格限制访问权限。9.5 合规提醒不要用这套方案规避法律法规。如果你的业务涉及个人隐私、金融数据、医疗数据需要先咨询合规负责人。本地部署、私有化推理、数据不动点部署等措施都值得考虑。10. 总结与下一步在 LLM Agent 工作流中处理敏感数据最值得尝试的点是“脱敏映射 工具执行前还原 全链路审计”这套模式。它不需要替换 Agent 框架也不需要换模型只要在入参、出参和工具边界加一层控制就能显著降低敏感数据进入 prompt 的概率。最先应该验证的是一个小场景比如让 Agent 查询订单同时把 user_id 和手机号脱敏。先写一个单元测试证明脱敏后没有明文、工具参数能正常还原再串上真实模型做一次完整调用。最有可能踩的坑是“还原逻辑放错位置导致工具拿到占位符”以及“日志中仍然出现明文”。把这两个问题盯住大部分项目就能稳定跑起来。后续可以继续扩展的方向包括将脱敏规则做成配置化按租户或按业务线区分。使用 KMS 加密映射表增强还原层安全。在 Agent 编排层增加数据流出检查凡是离开内部网络的字段都强制走脱敏。对 LLM 的输出做敏感信息检测防止模型生成明文。建议先收藏这套思路等真正做 Agent 生产化时从最小的数据流开始改造。
返回列表