资讯动态

LLM Agent敏感数据脱敏:三种方案让工具调用不停摆

发布时间:2026/8/31 9:43:40 来源:尧图企业网站定制
最近在给一个电商客服 Agent 做数据合规改造时遇到一个很典型的问题Agent 需要读取用户订单、核对手机号、查询收货地址然后调用 CRM 和物流接口去处理售后。功能链路本身没问题但安全评审一看就发现了问题——用户的姓名、手机号、完整地址全部原样进入了 Prompt还伴随工具调用参数出现在日志里。更麻烦的是如果后续接入了第三方数据分析平台或者模型上下文被用于二次训练那这些敏感数据就不再只是“内部风险”而是真正的合规事故。但如果只是简单地在提示词里写一句“不要泄露敏感信息”模型根本不会理你它该生成什么参数还是生成什么参数。真正难的不是脱敏而是脱敏之后工具调用不能断裂。这篇文章会围绕 LLM Agent 工作流中的敏感数据处理讲清楚为什么常规做法会破坏工具调用以及一套完整的“脱敏—工具调用—还原”的工程方案。读完你可以直接拿这套思路去改造自己的 Agent 项目。1. 这篇文章真正要解决的问题在聊方案之前先说清一个很多人踩过的坑。很多团队处理 Agent 敏感数据时第一反应是“在 Prompt 里加一句‘请勿泄露用户隐私’”。这个做法不能说完全没用但它解决的不是架构问题而是概率问题。你没有办法保证模型每一次都会遵守更没有办法保证上游数据在进入 Prompt 之前就已经被脱敏。另一个常见做法是把敏感字段直接切掉。比如把用户手机号从 prompt 里去掉只保留姓名。但这么做很容易导致工具调用失败。原因是工具函数可能需要手机号作为唯一标识去查订单。你不在 prompt 里给它手机号它要么胡编一个要么报参数缺失。这叫做“为脱敏而脱敏”不仅没解决数据安全还把流程打断了。所以这篇文章真正要解决的问题是三个如何在敏感数据进入模型上下文之前把它替换为不可逆或不直观的占位符。如何让模型在不知道真实敏感值的情况下仍然可以正确生成工具调用参数。如何在工具调用真正执行时把占位符还原成真实值并确保日志、回调、下游接口都不再暴露明文。这其实是一个“入口脱敏—链路透传—出口还原”的问题。它不是某一个函数能解决的而是要在 Agent Workflow 的完整链路里设计一套数据映射机制。2. LLM Agent 工作流中的敏感数据与工具调用基础概念2.1 敏感数据的类型划分给模型上下文里的敏感数据做分类是设计脱敏策略的第一步。不同类别处理方式完全不同。第一类是身份标识类数据。包括姓名、身份证号、手机号、邮箱、家庭住址。这类数据辨识度最高最容易触发合规问题也是脱敏优先级最高的。第二类是关联标识类数据。比如订单号、用户 ID、会员卡号。这类数据单独看不算敏感但是一旦与第一类数据关联就能定位到具体的人。所以很多公司把这类数据当作“准敏感数据”在处理时同样需要保护。第三类是认证凭据类数据。包括 API Key、Token、数据库密码、第三方接口的签名参数。这类数据一旦进入模型上下文风险不等同于隐私泄露而是等于把系统后门暴露给了模型和日志系统。第四类是外部工具 Payload 中的业务数据。比如调用支付接口时的金额、调用物流接口时的包裹编号。这类数据如果不做区分很容易在日志里留下完整交易快照。2.2 工具调用的内部结构要理解为什么脱敏会破坏工具调用必须先看模型生成工具调用时到底在做什么。所谓 tool call简单说就是模型输出一段结构化指令“你要调用哪个函数、传什么参数”。这段指令的结构上有三个关键点函数名、参数 JSON、以及参数与上下文的关系。{ tool: query_user_order, arguments: { user_id: U-12345, mobile: 13812345678 } }模型是根据 prompt 中的信息来填这个参数 JSON 的。如果 prompt 里没有手机号模型就无法填出手机号。如果 prompt 里给的是脱敏后的手机号模型填的就是脱敏后的值。工具端拿脱敏后的值去查库自然查不到。这就是“脱敏破坏工具调用”的本质。不是脱敏本身有错而是脱敏层没有和还原层配套。2.3 常规方案的缺陷方案效果缺陷Prompt 中声明“不要泄露敏感信息”有一定约束力无法保证模型 100% 遵守也无法防止日志泄露直接删除敏感字段上下文不再有敏感值工具缺参调用直接失败全字段固定替换简单工具无法区分不同用户的数据导致查询结果错乱手动打码再还原可控人工成本高且在长链路中极易漏改看得出单点方案都撑不起完整的工作流。真正可行的做法是需要一套机制脱敏、替换、调用、还原每一步都要有确定性。3. 环境准备与演示场景说明这一节先准备一个可复现的演示环境后面所有方案都跑在这套环境里。3.1 环境依赖Python 3.10 openai1.0.0 python-dotenv pydantic在项目目录下创建虚拟环境并安装依赖python -m venv venv source venv/bin/activate # Windows 下执行 venv\Scripts\activate pip install openai python-dotenv pydantic同时准备一个.env文件用来存放模型 API Key 等信息。注意这里的.env文件本身应该加入.gitignore绝对不能提交到仓库。OPENAI_API_KEYsk-xxxxxxxxxxxxxxxx MODEL_NAMEgpt-4o-mini说明本文的示例以 OpenAI API 格式为准但思路完全适用于通义千问、文心一言、DeepSeek、Ollama 本地模型等任何兼容 OpenAI 接口格式的 LLM。版本号请以实际项目为准。3.2 演示场景假设我们正在做一个订单售后 Agent它的任务是根据用户提供的手机号从后台查到最近订单然后调用物流接口查询最新的物流状态。这个场景里手机号是敏感数据订单号是准敏感数据物流信息是业务数据。我们要做的是让模型在上下文中看到“脱敏后的手机号”但它生成的工具调用参数中仍然能正确携带真实手机号去调用后台接口。为了区分脱敏前后我定义了一个简单的脱敏规则用MASK_MOBILE_XXXX形式的占位符代表手机号占位符与真实手机号通过一个内存映射表维护。4. 方案一数据外部化与引用替换第一个方案是最基础的思路也是后续方案的地基。核心思想是不把敏感值直接放进 Prompt而是放进一个外部存储在 Prompt 中只保留一个引用 ID。4.1 核心流程用户输入中携带敏感数据。Agent 框架在构建 Prompt 之前先调用脱敏模块把敏感字段提取出来存入临时存储。Prompt 中只保留脱敏后的引用标记例如{{user:mobile:001}}。模型生成工具调用时看到的是引用标记。工具执行层在执行的真实调用前通过引用标记反查存储替换回真实值。这种方式的好处是模型本身永远不会直接接触到真实的手机号它接触的只是一个引用符。就算 Prompt 被 dump、被日志记录、被第三方平台抓取里面也没有真实手机号。4.2 代码实现# 文件路径agent_sensitive_demo/external_store.py from typing import Dict, Optional class ReferenceStore: 一个简单的引用-真实值映射存储生产环境建议替换为 Redis 并设置过期时间。 def __init__(self): self._storage: Dict[str, str] {} def set(self, ref_id: str, real_value: str) - str: self._storage[ref_id] real_value return ref_id def get(self, ref_id: str) - Optional[str]: return self._storage.get(ref_id) def pop(self, ref_id: str) - Optional[str]: return self._storage.pop(ref_id, None) def clear_expired(self) - None: 简化版清理方法生产环境应根据时间戳自动过期。 self._storage.clear() # 全局引用存储 reference_store ReferenceStore()# 文件路径agent_sensitive_demo/mask_utils.py import re from .external_store import reference_store PATTERN_MOBILE re.compile(r(1[3-9]\d{9})) def mask_mobile_in_text(text: str) - str: 把文本中的手机号替换为引用占位符并在存储中记录映射。 def _replace(match: re.Match) - str: mobile match.group(0) ref_id fREF_MOBILE_{len(reference_store._storage) 1} reference_store.set(ref_id, mobile) return f{{{{user:mobile:{ref_id}}}}} return PATTERN_MOBILE.sub(_replace, text) def restore_refs_in_text(text: str) - str: 把文本中的引用占位符还原为真实值。 def _replace(match: re.Match) - str: ref_id match.group(1) real_value reference_store.get(ref_id) return real_value if real_value is not None else match.group(0) return re.sub(r\{\{user:mobile:(REF_MOBILE_\d)\}\}, _replace, text)4.3 关键说明这个方案的正确之处在于脱敏不是简单地把手机号替换成*********而是替换成一个可反查的引用标记。模型不知道REF_MOBILE_1是什么但它知道这个引用标记出现在上下文里再配合工具描述中对参数的说明它就能把这个引用标记原样填到工具调用的参数里。然后在工具执行层通过restore_refs_in_text把引用标记还原成真实手机号。模型没有见过明文手机号但最终发出的 API 请求却携带了真实手机号。这就实现了“安全脱敏”与“工具调用”的共存。5. 方案二工具参数级脱敏与逐级还原方案一能覆盖大部分场景但它有一个局限如果你的工具调用参数不是直接从文本里复制而是需要模型根据上下文推理生成那么简单的引用替换还不够。比如模型需要从一段文本中提取出“下单人手机号”并填到query_by_mobile函数的mobile参数里直接做文本替换后的占位符可能干扰模型的 json 输出格式。所以第二个方案更细一步在工具调用的参数层做脱敏与还原。5.1 设计思路在构建模型消息时我们不只是脱敏文本而是对将要传入工具的参数 Schema 打上“敏感标记”。模型生成工具调用后不直接把原始 JSON 丢给执行器而是先经过一个“参数还原层”。还原层根据 Schema 中的敏感字段配置匹配参数值并把引用标记替换成真实值。这个“Schema 打标 后置还原”的最大好处是模型生成过程完全不见明文敏感值但工具执行层拿到的永远是真实值。日志中如果记录的是模型原始输出那也只有脱敏后的引用符不会出现明文。5.2 代码实现# 文件路径agent_sensitive_demo/param_redactor.py import json from typing import Any, Dict, List from .external_store import reference_store # 标记一个字段属于敏感字段需要脱敏还原 SENSITIVE_FIELDS [mobile, phone, id_card, address, email] class ToolArgumentRedactor: def __init__(self, sensitive_fields: List[str] | None None): self.sensitive_fields sensitive_fields or SENSITIVE_FIELDS def redact_arguments(self, args: Dict[str, Any]) - Dict[str, Any]: 执行脱敏遍历参数若命中敏感字段生成引用占位符。 redacted {} for key, value in args.items(): if key in self.sensitive_fields and isinstance(value, str): ref_id fARG_{key.upper()}_{len(reference_store._storage) 1} reference_store.set(ref_id, value) redacted[key] f{{{{ref:{ref_id}}}}} else: redacted[key] value return redacted def restore_arguments(self, args: Dict[str, Any]) - Dict[str, Any]: 执行还原遍历参数若包含引用标记替换为真实值。 restored {} for key, value in args.items(): if isinstance(value, str) and value.startswith({{ref:): ref_id value[len({{ref:):-2] real_value reference_store.get(ref_id) restored[key] real_value if real_value is not None else value else: restored[key] value return restored# 文件路径agent_sensitive_demo/tool_call_pipeline.py import json from .param_redactor import ToolArgumentRedactor def on_model_tool_call(tool_name: str, raw_args: dict) - dict: 模型生成工具调用后先进行参数脱敏再记录日志 真正执行前再还原参数。 redactor ToolArgumentRedactor() # 1. 脱敏参数用于日志记录和下游展示 redacted_args redactor.redact_arguments(raw_args) print( 模型原始工具调用已脱敏 ) print(json.dumps({tool: tool_name, arguments: redacted_args}, ensure_asciiFalse)) # 2. 还原参数用于真实工具执行 restored_args redactor.restore_arguments(redacted_args) print( 工具执行层实际收到参数已还原 ) print(json.dumps({tool: tool_name, arguments: restored_args}, ensure_asciiFalse)) return restored_args # 模拟模型生成工具调用的流程 if __name__ __main__: model_output { tool: query_order_by_mobile, arguments: { mobile: 13812345678, order_range: 30d } } final_args on_model_tool_call( model_output[tool], model_output[arguments] )运行效果 模型原始工具调用已脱敏 {tool: query_order_by_mobile, arguments: {mobile: {{ref:ARG_MOBILE_1}}, order_range: 30d}} 工具执行层实际收到参数已还原 {tool: query_order_by_mobile, arguments: {mobile: 13812345678, order_range: 30d}}这里真正容易踩坑的地方是redact_arguments和restore_arguments必须是成对使用。如果你只脱敏不还原工具调用会失败如果你只还原不脱敏日志里又会留下明文。很多团队就是在联调时少写了其中一步导致线上数据忽明忽暗。6. 方案三敏感操作拦截与二次确认脱敏和还原解决了“数据入口”的问题但 Agent 工作流里还有一类更隐蔽的风险模型可能会调用写操作或敏感操作比如发送短信、扣款、删除缓存、修改用户资料。这些操作即使参数脱敏了风险等级也远高于查询类操作。所以第三个方案不是围绕“脱敏”本身而是围绕“权限边界”在工具执行层加一道拦截器当参数命中高危操作时直接中断执行并请求用户确认。6.1 设计思路在工具函数外层包装一个装饰器或中间件。当工具被调用时先检查两个条件工具名是否属于高危工具列表。工具参数是否满足预设条件。如果满足则返回一个“等待确认”的中间状态不再继续执行真实逻辑。用户确认后再放行。6.2 代码实现# 文件路径agent_sensitive_demo/sensitive_guard.py from functools import wraps from typing import Callable, Dict, Any, List, Optional HIGH_RISK_TOOLS [ send_sms_code, deduct_balance, delete_user_cache, update_user_phone, modify_order_address, ] class SensitiveGuard: def __init__(self, risk_tools: Optional[List[str]] None): self.risk_tools risk_tools or HIGH_RISK_TOOLS self.pending_confirmations: Dict[str, Dict[str, Any]] {} def check_before_call(self, tool_name: str, args: Dict[str, Any]) - Dict[str, Any]: 在调用真实工具前检查。 如果属于高危操作返回拦截响应而不是真正执行。 if tool_name in self.risk_tools: request_id fREQ_{abs(hash(tool_name str(args)))} self.pending_confirmations[request_id] { tool_name: tool_name, args: args, } return { status: need_confirmation, request_id: request_id, message: f工具 {tool_name} 属于敏感操作需要用户确认后才可执行。, args_summary: {k: (*** if k in [mobile, id_card, address] else v) for k, v in args.items()} } return {status: allowed, tool_name: tool_name, args: args} def confirm(self, request_id: str) - Dict[str, Any]: 用户确认后拿到原始参数执行真实逻辑。 pending self.pending_confirmations.pop(request_id, None) if pending is None: return {status: error, message: 确认请求不存在或已过期} # 这里应该调用真实工具 return self._execute_real_tool(pending[tool_name], pending[args]) def _execute_real_tool(self, tool_name: str, args: Dict[str, Any]) - Dict[str, Any]: # 简化示例真实项目中应绑定实际的工具执行函数 return { status: executed, tool_name: tool_name, args_after_restore: args, } # 使用示例 guard SensitiveGuard() def handle_tool_call(tool_name: str, args: Dict[str, Any]): result guard.check_before_call(tool_name, args) if result[status] need_confirmation: print(f[拦截] {result[message]}) print(f[摘要] {result[args_summary]}) # 模拟用户确认 confirm_result guard.confirm(result[request_id]) print(f[确认后执行] {confirm_result}) else: print(f[放行] {tool_name} 参数: {args})这个方案的意义在于脱敏负责“让数据不出现在不该出现的地方”拦截负责“让操作不发生在不该发生的时候”。很多 Agent 安全事故不是模型看到了敏感数据而是模型在用户没有确认的情况下就把敏感操作执行了。7. 三种方案的对比与选型建议方案解决的问题适用场景代价生产建议方案一引用替换Prompt 文本中不出现明文敏感值从用户文本中提取敏感信息后回填工具参数需要维护引用存储与过期策略适合查询类工具调用方案二参数级脱敏还原工具调用参数在日志中不暴露明文工具参数结构明确、字段可枚举需要定义敏感字段 Schema适合 B 端系统、对审计要求高的场景方案三敏感操作拦截防止模型擅自执行高危操作写操作、扣款、发短信、删除操作增加用户确认链路影响自动化程度适合有资金、隐私、数据变更风险的系统实际项目中这三者不是三选一而是分层组合。我的建议是所有进入 Prompt 的用户数据先跑一遍方案一的引用替换。所有工具调用参数必须经过方案二的参数级脱敏与还原。所有高危写操作必须经过方案三的拦截确认。8. 完整示例电商售后 Agent 的敏感数据改造把三个方案串起来跑一个完成的“查询物流状态”的例子。8.1 用户输入与数据流用户输入帮我查一下手机号 13812345678 最近的订单物流状态处理流程从用户输入中提取手机号存到引用存储生成引用占位符。向模型发送的消息里手机号被替换为{{user:mobile:REF_MOBILE_1}}。模型生成工具调用参数中的 mobile 为{{ref:ARG_MOBILE_1}}。还原层把引用标记替换为真实手机号调用真实订单查询接口。查询结果返回给模型模型生成最终回复给用户。8.2 完整代码# 文件路径agent_sensitive_demo/full_pipeline.py from .external_store import reference_store from .mask_utils import mask_mobile_in_text, restore_refs_in_text from .param_redactor import ToolArgumentRedactor from .sensitive_guard import SensitiveGuard def query_order_by_mobile(mobile: str, order_range: str 30d): 模拟真实工具根据手机号查询订单。 # 实际项目中这里会请求真实订单服务 return { orders: [ {order_id: ORD-202401-001, status: shipped, logistics: 顺丰速运 SF123} ], query_mobile_tail: mobile[-4:], # 只返回尾号避免再次泄露完整手机号 } def agent_flow(user_input: str): # Step 1: 脱敏用户输入 masked_input mask_mobile_in_text(user_input) print(f[1] 脱敏后的用户输入: {masked_input}) # Step 2: 构造消息并发送给模型 messages [ {role: system, content: 你是一个订单查询助手。你的工具 query_order_by_mobile 需要参数 mobile。}, {role: user, content: masked_input}, ] print(f[2] 发送给模型的消息: {messages}) # Step 3: 模拟模型生成工具调用 # 实际项目中这里调用 openai.ChatCompletion.create(messagesmessages) model_tool_call { tool: query_order_by_mobile, arguments: { mobile: {{ref:ARG_MOBILE_1}}, order_range: 30d } } print(f[3] 模型生成的工具调用: {model_tool_call}) # Step 4: 参数级脱敏记录 还原执行 redactor ToolArgumentRedactor() redacted_args redactor.redact_arguments(model_tool_call[arguments]) restored_args redactor.restore_arguments(redacted_args) print(f[4] 工具实际执行参数: {restored_args}) tool_result query_order_by_mobile(**restored_args) print(f[5] 工具执行结果: {tool_result}) # Step 5: 反馈给模型生成回复 final_answer f您的订单 {tool_result[orders][0][order_id]} 状态是 {tool_result[orders][0][status]}物流公司是 {tool_result[orders][0][logistics]}。 print(f[6] 最终回复: {final_answer}) if __name__ __main__: agent_flow(帮我查一下手机号 13812345678 最近的订单物流状态)8.3 运行验证python -m agent_sensitive_demo.full_pipeline预期输出参考[1] 脱敏后的用户输入: 帮我查一下手机号 {{user:mobile:REF_MOBILE_1}} 最近的订单物流状态 [2] 发送给模型的消息: [{role: system, content: 你是一个订单查询助手。你的工具 query_order_by_mobile 需要参数 mobile。}, {role: user, content: 帮我查一下手机号 {{user:mobile:REF_MOBILE_1}} 最近的订单物流状态}] [3] 模型生成的工具调用: {tool: query_order_by_mobile, arguments: {mobile: {{ref:ARG_MOBILE_1}}, order_range: 30d}} [4] 工具实际执行参数: {mobile: 13812345678, order_range: 30d} [5] 工具执行结果: {orders: [{order_id: ORD-202401-001, status: shipped, logistics: 顺丰速运 SF123}], query_mobile_tail: 5678} [6] 最终回复: 您的订单 ORD-202401-001 状态是 shipped物流公司是 顺丰速运 SF123。注意第[5]步工具结果返回时我们刻意只返回手机尾号而不是完整手机号。这是另一个容易被忽略的泄露点——模型通过工具结果重新拿到敏感字段后会把它们再次拼接到回复文本和后续工具调用中。所以工具返回值里的敏感信息也要做收口处理。9. 常见问题与排查方法问题现象可能原因排查方式解决方案模型把占位符当成真实值传给工具工具查不到数据还原层未执行或还原层执行时机不对检查工具调用日志中“还原后参数”是否仍含{{ref:确认还原逻辑在真实工具执行前调用脱敏后模型输出 JSON 格式错误占位符中出现了引号或反斜杠破坏 JSON 结构检查占位符格式避免包含、\、换行符使用纯字母数字加下划线的占位符明文手机号仍出现在日志日志记录了工具还原后的参数或记录了模型输入原文全链路搜索手机号正则定位日志输出位置在日志输出前统一走脱敏序列化器二次会话中引用存储丢失导致还原失败Redis 过期时间太短或内存存储被清理检查 reference store 的保存时间将引用表改为 Redis 并设置合理 TTL会话恢复时同步恢复映射模型拒绝在参数中填占位符反而编造假手机号工具描述没有说明占位符的语义模型无法理解检查 tool schema 的 description 字段在工具描述中增加“mobile 参数可以直接使用上下文中的引用占位符不要编造号码”高危操作被模型自动执行没有确认链路缺少敏感操作拦截层检查敏感操作工具是否走SensitiveGuard为写操作工具统一加拦截器默认拒绝用户确认后放行10. 最佳实践与工程建议第一不要把脱敏做成“一次性字符串替换”要设计成“统一样式、集中管理”的机制。所有敏感字段的脱敏规则、敏感字段名、还原逻辑应该收敛在一个模块里。不同业务线都引用同一套机制才能避免出现“A 部门脱敏了B 部门忘了”的情况。第二日志系统中必须加脱敏过滤器。很多团队只记得脱敏 Prompt忘了日志系统里记录的是模型输入、模型输出、工具调用参数。建议在日志框架层写一个全局 Filter对mobile、id_card、address、authorization等字段做正则匹配一旦命中就用***替换在落盘前完成兜底。第三工具返回结果也要脱敏。模型读完工具返回结果后是可以把它继续作为上下文传给下一轮工具调用的。如果工具返回结果里有完整手机号那么下一轮仍然会泄露。所以工具端应当只返回必要字段。比如查询订单后只返回“姓名脱敏 手机尾号 订单状态”。第四敏感操作默认拒绝最小权限放行。这里真正容易踩坑的地方是把“拦截”做成“提示”不是真正拒绝。正确做法是SensitiveGuard命中敏感工具后立刻返回need_confirmation状态不让真实逻辑继续执行。只有用户明确确认后才进入执行分支。第五对引用存储设置合理的 TTL 和清理策略。会话结束后引用映射表如果不清理不仅浪费内存还会成为“明文敏感值仓库”。建议每次会话结束时主动调用清理方法或设置 TTL 默认不超过会话时长。第六注意模型服务的版本差异。有的模型调用工具时会自己加注释、加前缀字符串这会导致占位符匹配失败。建议在脱敏占位符格式设计时避免使用容易与自然语言混淆的字符组合。推荐格式{{ref:ARG_MOBILE_1}}这种格式在大多数模型中都能稳定保留。第七安全审计不能只看最终日志还要看工具出参、模型反馈、异常堆栈。真实事故往往发生在异常路径工具抛异常时框架打印了完整参数模型生成失败重试时重试日志记录了原始输入本地调试时IDE 控制台打出了.env内容。这些场景都需要在代码评审时覆盖到。11. 总结与后续学习方向这篇文章的核心观点可以概括为一句话LLM Agent 工作流处理敏感数据不是靠“提示词约束”而是靠“脱敏、映射、还原、拦截”这套工程机制。让模型接触不到真实敏感值同时通过引用标记与还原层保证工具调用不中断再通过敏感操作拦截层把写操作的风险限制在用户确认范围内。如果接下来你想继续深入可以考虑这几个方向如何把引用存储从内存迁移到 Redis并设计分布式会话下的共享映射。如何对接开源链路追踪系统在 trace 中同样保证敏感字段脱敏。如何做自动化的敏感数据血统分析构建“数据从哪个工具来、到哪个工具去”的流向图。如何结合 IAM 体系对工具调用做更细粒度的权限控制而不是只在函数级做拦截。建议你先把文中的最小示例跑通再把它接入你现有 Agent 项目的工具调用管线。先覆盖查询类工具再覆盖写操作类工具最后补日志脱敏。这样的改造节奏对线上系统最稳。如果这篇文章对你有帮助建议收藏备用。后续在接入真实项目时遇到敏感数据还原失败、工具调用参数丢失等问题可以回来对照检查。

读完文章,也想定制专属网站?

尧图设计师 24 小时内与您沟通定制方案

免费获取报价