用開發(fā)中的敏感數(shù)據(jù)脫敏:Agent Skills架構(gòu)設(shè)計(jì)實(shí)戰(zhàn))
干過幾年Web后端的人轉(zhuǎn)去做大模型應(yīng)用開發(fā)最先撞上的墻往往不是什么模型調(diào)優(yōu)、Prompt技巧而是數(shù)據(jù)安全。我之前接手過一個(gè)客服工單智能助手的需求說白了就是讓 Agent 去讀工單數(shù)據(jù)庫(kù)然后自動(dòng)幫客戶查進(jìn)度、解答疑問。剛開始覺得這事兒不難查詢結(jié)果拼進(jìn) Prompt 給大模型就完事了。結(jié)果聯(lián)調(diào)的時(shí)候翻車了——大模型在回復(fù)里把客戶的完整手機(jī)號(hào)、家庭住址一字不差地復(fù)述了出來。那一刻我才意識(shí)到過去我們?cè)?Web 應(yīng)用里攢下的那套數(shù)據(jù)防護(hù)經(jīng)驗(yàn)放在 Agent Skills 面前幾乎全部失效。這篇文章就想聊聊我做 Agent Skills 敏感數(shù)據(jù)脫敏架構(gòu)設(shè)計(jì)的全過程。從 Web 開發(fā)者的視角出發(fā)講清楚 Agent 場(chǎng)景下數(shù)據(jù)鏈路到底發(fā)生了什么變化、敏感數(shù)據(jù)暴露在哪幾個(gè)環(huán)節(jié)、脫敏架構(gòu)怎么分層落地以及我在這個(gè)過程中踩過的坑和最終的排查經(jīng)驗(yàn)。如果你也是從 Web 開發(fā)轉(zhuǎn)向 AI 應(yīng)用開發(fā)正在做 Agent、做 Skill 封裝、做 RAG 這類方向這篇文章應(yīng)該對(duì)你有用。1. 轉(zhuǎn)型視角下的Agent數(shù)據(jù)鏈路和我們熟悉的Web鏈路到底差在哪1.1 Web開發(fā)者心里那套“祖?zhèn)鳌钡臄?shù)據(jù)安全打法老一代 Web 后端對(duì)數(shù)據(jù)安全的直覺基本圍繞“接口鑒權(quán)、字段過濾、前端展示控制”這幾板斧。比如查詢客戶信息后端接口嚴(yán)格校驗(yàn)登錄態(tài)返回 JSON 之前把敏感字段摘掉前端表格里手機(jī)號(hào)中間四位打星號(hào)數(shù)據(jù)庫(kù)查詢?nèi)繀?shù)化防注入。這套邏輯成立的前提是數(shù)據(jù)從后端到前端是一條單向、短命的傳輸鏈路。請(qǐng)求來了后端查庫(kù)把該給的字段給出去響應(yīng)結(jié)束這條數(shù)據(jù)路徑就斷開了。你只要保證接口不進(jìn)黑名單字段、前端不顯示明文字段問題基本就控制住了。到了 Agent 場(chǎng)景這個(gè)前提直接崩了。Agent 不是簡(jiǎn)單地“請(qǐng)求—響應(yīng)”而是會(huì)把外部數(shù)據(jù)取回來之后放進(jìn)上下文Context里供模型推理、多輪對(duì)話、工具調(diào)用反復(fù)使用。數(shù)據(jù)在推理鏈路中的生命周期被拉長(zhǎng)了好幾倍而模型必須真正“看懂”這些數(shù)據(jù)才能干活。這就意味著你不能簡(jiǎn)單地把敏感字段藏起來——藏起來模型就看不懂了。1.2 Agent Skills把數(shù)據(jù)鏈路拉長(zhǎng)成了什么樣子先解釋一下 Agent Skills 是什么??梢园?Skill 理解成 Agent 的一項(xiàng)專項(xiàng)能力封裝一個(gè) Skill 內(nèi)部通常包含技能描述、參數(shù)定義、對(duì)應(yīng)的工具調(diào)用邏輯比如查數(shù)據(jù)庫(kù)、調(diào)外部 API以及返回?cái)?shù)據(jù)的處理方式。Agent 會(huì)根據(jù)用戶意圖自主決定調(diào)用哪個(gè) Skill然后把 Skill 返回的內(nèi)容拼進(jìn)上下文繼續(xù)推理。問題就出在這個(gè)“拼進(jìn)上下文”。我在做客服工單助手時(shí)Skill 返回的數(shù)據(jù)結(jié)構(gòu)大概是這樣的{ ticket_id: TK20250113002, customer_name: 張三, customer_phone: 13812345678, customer_address: 某某市某某區(qū)某某街道88號(hào), issue_desc: 客戶反饋訂單未按時(shí)送達(dá)多次聯(lián)系客服未果, handler: 李四 }在 Web 時(shí)代這段 JSON 返回給前端之前我可以直接把customer_phone摘掉。前端不需要手機(jī)號(hào)頁(yè)面只顯示工單標(biāo)題和狀態(tài)就行。但在 Agent 場(chǎng)景里模型需要根據(jù)工單內(nèi)容去判斷“這個(gè)客戶是不是在問物流問題”“要不要給他升級(jí)處理”甚至需要核對(duì)“這個(gè)手機(jī)號(hào)對(duì)應(yīng)的客戶是不是本人”。你如果直接把手機(jī)號(hào)碼干掉模型碰上復(fù)雜一點(diǎn)的對(duì)話就懵了。更麻煩的是上下文滯留。Agent 的對(duì)話是多輪的客戶先說“我要查訂單”然后又提供了自己的手機(jī)號(hào)模型把手機(jī)號(hào)記在上下文里。哪怕你 Skill 返回的數(shù)據(jù)做了脫敏只要對(duì)話歷史里曾經(jīng)出現(xiàn)過明文后續(xù)某輪模型就可能把明文回憶出來。1.3 敏感需要重新定義Web 時(shí)代對(duì)“敏感數(shù)據(jù)”的理解基本是靜態(tài)的身份證、手機(jī)號(hào)、銀行卡、住址這些字段打上標(biāo)簽統(tǒng)一加密脫敏。到了 Agent 場(chǎng)景敏感數(shù)據(jù)的口徑必須做動(dòng)態(tài)擴(kuò)展。因?yàn)槟P涂梢越Y(jié)合多條信息推斷出敏感結(jié)論。舉個(gè)例子工單里沒有客戶的完整姓名只有“張先生”三個(gè)字加上地址“某某小區(qū) 12 棟”再結(jié)合客戶自述“我住 12 棟 3 樓”模型可能直接推算出門牌號(hào)。這個(gè)時(shí)候單純對(duì)字段脫敏是不夠的你得有一套內(nèi)容級(jí)識(shí)別和分級(jí)策略讓模型在推理過程中用到的數(shù)據(jù)“夠用但不過分”。提示轉(zhuǎn)型 AI 應(yīng)用開發(fā)后請(qǐng)把“敏感數(shù)據(jù)”從字段清單升級(jí)為“識(shí)別—分級(jí)—改寫—審計(jì)”一條鏈路的架構(gòu)設(shè)計(jì)。這不是過度設(shè)計(jì)是 Agent 推理特性決定的底線要求。2. 設(shè)計(jì)脫敏架構(gòu)前先盯住Agent Skills的四個(gè)暴露點(diǎn)脫敏架構(gòu)設(shè)計(jì)的第一步不是寫代碼而是把 Agent 調(diào)用 Skill 時(shí)數(shù)據(jù)可能經(jīng)過的所有路徑盤一遍。我梳理下來Agent Skills 的敏感數(shù)據(jù)暴露點(diǎn)主要有四個(gè)。2.1 暴露點(diǎn)一入站參數(shù)Agent 調(diào)用 Skill 時(shí)需要在參數(shù)里帶上用戶信息??蛻粽f“幫我查一下我 13812345678 這個(gè)號(hào)碼的訂單”Agent 就會(huì)把手機(jī)號(hào)作為入?yún)鹘o查詢 Skill。如果 Skill 內(nèi)部直接拿這個(gè)參數(shù)查庫(kù)那么明文手機(jī)號(hào)至少會(huì)出現(xiàn)在Agent 的對(duì)話上下文里Skill 的請(qǐng)求日志里底層數(shù)據(jù)庫(kù)查詢?nèi)罩纠铩_@個(gè)環(huán)節(jié)一般容易被忽略因?yàn)榇蠹谊P(guān)注點(diǎn)都在“返回?cái)?shù)據(jù)的脫敏”但入站參數(shù)同樣會(huì)泄露敏感信息。2.2 暴露點(diǎn)二上下文滯留這是 Agent 場(chǎng)景和 Web 場(chǎng)景最大的差異。Web 后端處理完請(qǐng)求響應(yīng)結(jié)束數(shù)據(jù)生命周期就結(jié)束了。但 Agent 的上下文是多輪累積的明文數(shù)據(jù)一旦進(jìn)入上下文除非你主動(dòng)清理否則它會(huì)一直待在那兒。我在測(cè)試中發(fā)現(xiàn)模型在第三輪對(duì)話時(shí)復(fù)述了第一輪出現(xiàn)過的完整身份證號(hào)而整個(gè)過程中我并沒有把身份證號(hào)當(dāng)作任何 Skill 的返回值。原因就是第一輪用戶在咨詢時(shí)自己發(fā)了一段文字里面有身份證號(hào)模型把它存進(jìn)了上下文后面又被“調(diào)用”了出來。2.3 暴露點(diǎn)三第三方工具返回結(jié)果Agent 通常不會(huì)只調(diào)自己的 Skill還會(huì)調(diào)用搜索工具、天氣工具、地圖工具甚至外部知識(shí)庫(kù)。這類第三方返回的內(nèi)容不受你控制里面很可能夾帶個(gè)人隱私。比如搜索結(jié)果里出現(xiàn)某用戶的社交賬號(hào)主頁(yè)里面就有手機(jī)號(hào)地圖工具返回某個(gè)地址詳情附帶聯(lián)系人電話。這些數(shù)據(jù)進(jìn)入上下文后同樣面臨泄漏風(fēng)險(xiǎn)。2.4 暴露點(diǎn)四模型出站復(fù)述模型有一種讓人頭疼的能力就是一字不差地復(fù)述輸入中的字符串。明明 Skill 只需要返回“密碼重置鏈接已發(fā)送”但模型在給用戶解釋時(shí)可能會(huì)把短信驗(yàn)證碼、銀行卡號(hào)、家庭住址這些原文完整說出來。這個(gè)暴露點(diǎn)可以說是整個(gè)鏈路里最容易被忽略但后果最直接的。針對(duì)這四個(gè)暴露點(diǎn)我最終把脫敏架構(gòu)拆成了四層識(shí)別層、策略層、改寫層、審計(jì)層。每一層解決一個(gè)特定方向的問題下面詳細(xì)展開。3. 分層脫敏架構(gòu)落地識(shí)別、分級(jí)、改寫、審計(jì)3.1 第一層敏感數(shù)據(jù)識(shí)別器脫敏的前提是知道哪些內(nèi)容是敏感的。Web 時(shí)代常用的做法是配置黑名單字段Agent 場(chǎng)景下這個(gè)粒度遠(yuǎn)遠(yuǎn)不夠。我的方案是多渠道識(shí)別規(guī)則識(shí)別用正則識(shí)別手機(jī)號(hào)、身份證號(hào)、銀行卡號(hào)、郵箱等模式固定的數(shù)據(jù)實(shí)體識(shí)別用命名實(shí)體識(shí)別NER模型識(shí)別姓名、地址、組織名等無固定格式的實(shí)體字段標(biāo)注對(duì)于自研 Skill 的返回結(jié)果在字段定義階段就給敏感字段打標(biāo)簽。代碼層面一個(gè)輕量識(shí)別器的做法大概是這樣的import re SENSITIVE_PATTERNS { phone: r\b1[3-9]\d{9}\b, id_card: r\b\d{17}[\dXx]\b, bank_card: r\b\d{16,19}\b, email: r\b[\w.-][\w-]\.[\w.-]\b, } def scan_sensitive(text: str): hits [] for name, pattern in SENSITIVE_PATTERNS.items(): for match in re.finditer(pattern, text): hits.append({ type: name, value: match.group(), start: match.start(), end: match.end(), }) return hits在實(shí)際項(xiàng)目里我建議不要只依賴正則。因?yàn)榭蛻舻刂愤@種信息格式極其自由“某某市某某區(qū)某某路 88 號(hào) 3 棟 201”這種長(zhǎng)串正則寫不出來。我當(dāng)時(shí)接了一個(gè) NER 服務(wù)專門識(shí)別PER人名、LOC地點(diǎn)、ORG機(jī)構(gòu)名識(shí)別精度八九成配合規(guī)則識(shí)別基本夠用。3.2 第二層分級(jí)與策略引擎識(shí)別出敏感內(nèi)容之后不能一刀切全脫敏。不同場(chǎng)景下模型對(duì)數(shù)據(jù)的需求強(qiáng)度不一樣。我習(xí)慣把敏感數(shù)據(jù)分成三級(jí)級(jí)別定義示例處理方式L1模型推理必須的、不構(gòu)成隱私風(fēng)險(xiǎn)的內(nèi)容工單狀態(tài)、物流節(jié)點(diǎn)、商品名稱原樣保留L2非必要但模型偶爾會(huì)引用的個(gè)人信息姓名、城市級(jí)別地址變形脫敏保留語義L3一旦泄露后果嚴(yán)重的強(qiáng)隱私數(shù)據(jù)手機(jī)號(hào)、身份證號(hào)、銀行卡號(hào)、詳細(xì)門牌地址可逆加密/完全隱藏分級(jí)策略要外置成配置不要寫死在代碼里。我當(dāng)時(shí)用了一個(gè) JSON 配置文件業(yè)務(wù)方可以隨時(shí)調(diào)整{ field_policy: { customer_name: MASK_KEEP_LAST, customer_phone: REPLACE_RANDOM, customer_address: MASK_RETAIN_CITY, issue_desc: KEEP_ALL }, type_policy: { phone: REPLACE_RANDOM, id_card: REPLACE_ENCRYPTED, email: MASK_KEEP_PREFIX } }策略引擎根據(jù)這個(gè)配置決定對(duì)某一類型/字段的數(shù)據(jù)執(zhí)行哪種脫敏策略。常見策略包括MASK_KEEP_LAST保留后幾位其余打碼比如張*;MASK_RETAIN_CITY保留城市名隱藏詳細(xì)門牌地址REPLACE_RANDOM替換成隨機(jī)生成的假數(shù)據(jù)保持格式一致性REPLACE_ENCRYPTED用格式保留加密生成可逆的密文。這里特別說一下格式保留可逆加密。它是脫敏方案里一個(gè)比較優(yōu)雅的工具——加密后的結(jié)果在格式上和原文保持一致。比如身份證號(hào)加密后還是一串 18 位的數(shù)字從“長(zhǎng)得像身份證號(hào)”這個(gè)角度模型完全可以正常理解這個(gè)字段的含義。但密文無法還原出真實(shí)內(nèi)容只有持有密鑰的后端服務(wù)才具備解密的權(quán)限。這樣既保證了模型對(duì)數(shù)據(jù)結(jié)構(gòu)的理解又實(shí)現(xiàn)了強(qiáng)隱私保護(hù)。日常做客服工單場(chǎng)景時(shí)后臺(tái)人工審計(jì)如果需要看真實(shí)數(shù)據(jù)可以通過安全網(wǎng)關(guān)單獨(dú)解密查看而 Agent 推理鏈路里拿到的永遠(yuǎn)是密文。3.3 第三層上下文改寫器識(shí)別和分級(jí)完成之后接下來是核心環(huán)節(jié)在數(shù)據(jù)從 Skill 返回準(zhǔn)備進(jìn)入上下文之前插入一個(gè)改寫器。改寫器的作用是對(duì)即將進(jìn)入模型上下文的內(nèi)容做清洗和替換。改寫器要處理的不僅是 Skill 返回的 JSON還包括用戶自己發(fā)來的文本消息里的敏感內(nèi)容。我實(shí)現(xiàn)的上下文改寫器核心邏輯是先對(duì)文本做敏感掃描再按策略執(zhí)行脫敏最后把脫敏后的文本放進(jìn)上下文。class MaskingRewrite: def __init__(self, policy_engine): self.policy_engine policy_engine def rewrite_text(self, text: str) - str: hits scan_sensitive(text) if not hits: return text # 按命中位置倒序替換避免破壞索引 result list(text) for hit in sorted(hits, keylambda x: x[start], reverseTrue): policy self.policy_engine.get_policy(hit[type]) masked_value self.policy_engine.mask(hit[value], policy) result[hit[start]:hit[end]] masked_value return .join(result)這里有一個(gè)關(guān)鍵設(shè)計(jì)缺省禁止。也就是說如果一段文本里出現(xiàn)了敏感內(nèi)容但策略引擎沒有明確配置對(duì)應(yīng)的脫敏策略默認(rèn)行為是攔截而不是放行。寧可讓模型多問一輪“請(qǐng)?zhí)峁┍匾畔ⅰ币膊荒茏屆魑牧镞M(jìn)上下文。再展開說一個(gè)實(shí)操細(xì)節(jié)替換算法不能破壞索引位置。我第一次實(shí)現(xiàn)時(shí)用的是str.replace()結(jié)果出現(xiàn)了問題——當(dāng)一段文本里有兩個(gè)完全相同的手機(jī)號(hào)時(shí)replace會(huì)把兩個(gè)都替換掉但我的代碼邏輯只期望替換第一個(gè)導(dǎo)致后續(xù)位置偏移。后來我才改成“倒序切片重建法”先把文本轉(zhuǎn)成字符數(shù)組從后往前替換索引永遠(yuǎn)不亂。3.4 第四層輸出審計(jì)脫敏架構(gòu)還有一個(gè)容易被遺漏的出口Agent 的最終回復(fù)。模型在生成回復(fù)時(shí)可能會(huì)把上下文里被脫敏的內(nèi)容“重新生成”出來。比如脫敏后的手機(jī)號(hào)是138****1234模型看多了這個(gè)格式回復(fù)時(shí)有可能編一個(gè)類似的號(hào)碼出來更危險(xiǎn)的是如果某一輪操作失誤導(dǎo)致明文進(jìn)了上下文模型可能在回復(fù)中原樣復(fù)述。輸出審計(jì)層要做的事情是在 Agent 回復(fù)發(fā)給用戶之前再掃描一遍發(fā)現(xiàn)任何符合敏感模式的內(nèi)容就用統(tǒng)一的脫敏模板替換掉或者直接攔截回復(fù)要求重新生成。我在輸出審計(jì)層還做了一個(gè)輕量級(jí)的泄露檢測(cè)功能把所有命中敏感模式的文本片段記錄到審計(jì)日志里附帶上命中的信息來源是哪一輪對(duì)話、哪個(gè) Skill 返回的。這樣一旦出現(xiàn)問題排查鏈路會(huì)容易很多。4. 一套可抄作業(yè)的參考實(shí)現(xiàn)從識(shí)別到審計(jì)的完整編排前面把分層架構(gòu)講完了這一節(jié)我直接給出一套能夠跑通的核心編排代碼大家可以按這個(gè)結(jié)構(gòu)快速在自己項(xiàng)目里搭一個(gè)雛形。class DataMaskingPipeline: def __init__(self, policy_engine, rewriter, auditor): self.policy_engine policy_engine self.rewriter rewriter self.auditor auditor def process_skill_response(self, skill_name: str, raw_data: str, user_id: str) - str: # 第一步識(shí)別 hits scan_sensitive(raw_data) if not hits: return raw_data # 第二步分級(jí)與改寫 rewritten self.rewriter.rewrite_text(raw_data) # 第三步記錄審計(jì)信息 self.auditor.log( skill_nameskill_name, user_iduser_id, hit_countlen(hits), hit_types[h[type] for h in hits], timestampdatetime.now(), ) return rewritten實(shí)際項(xiàng)目中這個(gè) Pipeline 會(huì)被掛在 Skill 調(diào)用鏈的外層。我推薦的做法是做一個(gè)統(tǒng)一的數(shù)據(jù)安全網(wǎng)關(guān)所有 Skill 的對(duì)外 API 都通過這個(gè)網(wǎng)關(guān)進(jìn)出。網(wǎng)關(guān)負(fù)責(zé)入站參數(shù)清洗、出站響應(yīng)改寫、日志審計(jì)三件事。從執(zhí)行順序上看Agent 每調(diào)用一次 Skill數(shù)據(jù)流的完整鏈路是用戶消息進(jìn)入 Agent 編排層編排層決定調(diào)用某個(gè) SkillSkill 請(qǐng)求先過安全網(wǎng)關(guān)入站參數(shù)里的敏感信息被脫敏/標(biāo)記Skill 執(zhí)行業(yè)務(wù)邏輯返回原始結(jié)果原始結(jié)果再過安全網(wǎng)關(guān)敏感識(shí)別器掃描改寫器執(zhí)行脫敏脫敏后的結(jié)果進(jìn)入模型上下文模型生成回復(fù)前輸出審計(jì)層掃描回復(fù)返回給用戶。不要小看中間多出來的幾層調(diào)用它們帶來的性能損耗在可接受范圍內(nèi)。我實(shí)測(cè)下來一次正則掃描加脫敏改寫大約在 5 到 10 毫秒對(duì)一個(gè)對(duì)話交互來說這個(gè)開銷完全可以接受。注意的是Skill 返回的數(shù)據(jù)如果是結(jié)構(gòu)化的 JSON不能簡(jiǎn)單地把 JSON 轉(zhuǎn)成字符串再做脫敏。那樣做會(huì)讓字段和策略的映射關(guān)系丟失。正確做法是遍歷 JSON 的每個(gè)字段依據(jù)字段名匹配策略。def process_json(obj, field_policy): if isinstance(obj, dict): clean {} for k, v in obj.items(): if k in field_policy: clean[k] mask_value(v, field_policy[k]) else: clean[k] process_json(v, field_policy) return clean elif isinstance(obj, list): return [process_json(item, field_policy) for item in obj] else: return obj這里還要提醒一個(gè)容易踩坑的地方字段策略要盡量用“字段名 內(nèi)容類型”雙重匹配。因?yàn)橛行┳侄坞m然叫note但里面的內(nèi)容可能是用戶留言包含手機(jī)號(hào)有些字段叫name但可能存的是公司全稱不含個(gè)人隱私。所以不管是字段策略還是內(nèi)容掃描建議都過一遍識(shí)別器寧可多掃一次不要漏一次。5. 常見問題與排查技巧實(shí)錄脫敏架構(gòu)上線之后問題并不會(huì)消失只會(huì)換個(gè)姿勢(shì)出現(xiàn)。我整理了幾個(gè)在模擬項(xiàng)目X和實(shí)際客服工單助手開發(fā)中反復(fù)遇到的問題和對(duì)應(yīng)的排查思路希望對(duì)你有參考價(jià)值。5.1 脫敏后模型答非所問現(xiàn)象把客戶姓名替換成張*之后模型在回復(fù)里開始頻繁出現(xiàn)“關(guān)于您的姓名信息系統(tǒng)暫時(shí)無法識(shí)別”這樣的傻話。原因脫敏粒度過大把模型做語義理解所需的關(guān)鍵信息也抹掉了。排查思路先確認(rèn)模型回答的質(zhì)量問題是不是只出現(xiàn)在含有敏感字段的對(duì)話中。如果是把策略從MASK_KEEP_LAST換成REPLACE_RANDOM也就是用假名替代真名比如“張三”變成“李四”。模型只需要知道“這是一個(gè)人的名字”并不需要知道具體的真實(shí)姓名隨機(jī)替換比打星號(hào)更能保留語義。這個(gè)技巧在客服工單場(chǎng)景中非常實(shí)用。5.2 明文從檢索結(jié)果里繞過脫敏層現(xiàn)象某輪對(duì)話里出現(xiàn)了明文手機(jī)號(hào)但我明明已經(jīng)在 Skill 返回的數(shù)據(jù)上掛了脫敏組件。原因這個(gè)手機(jī)號(hào)不是來自 Skill 的返回值而是來自 RAG 檢索召回的文檔內(nèi)容。RAG 鏈路里的文檔片段走的是另一條數(shù)據(jù)通道沒有經(jīng)過安全網(wǎng)關(guān)。排查思路把安全網(wǎng)關(guān)從“Skill 專用”升級(jí)為“所有數(shù)據(jù)出口強(qiáng)制走網(wǎng)關(guān)”包括 RAG 檢索器、外部知識(shí)庫(kù)調(diào)用、甚至系統(tǒng)提示詞里動(dòng)態(tài)拼接的用戶資料都必須統(tǒng)一過一遍識(shí)別和改寫。我用了一個(gè)很笨但有效的辦法把數(shù)據(jù)安全網(wǎng)關(guān)做成了項(xiàng)目里所有外部數(shù)據(jù)源的唯一出口任何數(shù)據(jù)要進(jìn)入模型上下文必須經(jīng)過這個(gè)出口。這樣至少不會(huì)出現(xiàn)旁路繞過。5.3 日志里出現(xiàn)明文現(xiàn)象安全掃描顯示對(duì)話記錄和模型輸出都沒有明文但運(yùn)維查日志時(shí)發(fā)現(xiàn)Skill 的調(diào)用日志里完整記錄了入?yún)⑹謾C(jī)號(hào)。原因脫敏只作用在“上下文數(shù)據(jù)鏈路”上但調(diào)試日志、監(jiān)控日志記錄的是原始 API 請(qǐng)求是獨(dú)立的一條數(shù)據(jù)通道。排查思路日志脫敏必須作為一個(gè)獨(dú)立組件來對(duì)待。所有日志輸出之前統(tǒng)一走日志脫敏過濾器對(duì)手機(jī)號(hào)、身份證、地址這類字段打碼。我建議在日志框架層面全局掛一個(gè)過濾器而不是每個(gè)業(yè)務(wù)模塊手動(dòng)打碼。因?yàn)槿斯な謩?dòng)打碼必然會(huì)有遺漏公共組件處理才能做到全覆蓋。5.4 明明脫敏了模型還是猜出原始號(hào)碼現(xiàn)象手機(jī)號(hào)已經(jīng)被替換成隨機(jī)號(hào)碼模型對(duì)話中也沒有出現(xiàn)過明文但有一次模型在回復(fù)中對(duì)客戶說“這個(gè)手機(jī)號(hào)是上次和您核對(duì)的那個(gè)號(hào)碼吧”而客戶上次并沒有報(bào)過號(hào)。原因模型基于對(duì)話上下文做了推理。前一輪客戶說了“我是 138 開頭的號(hào)”脫敏后的號(hào)碼恰好也以 138 開頭模型就自行串起來了。排查思路對(duì)高敏感字段脫敏后的格式要與原數(shù)據(jù)保持足夠距離。比如手機(jī)號(hào)這種強(qiáng)隱私信息在條件允許的情況下不要用“保留前三位、后四位”這種模式脫敏而是直接生成一個(gè)完全隨機(jī)的新號(hào)碼。寧可讓模型少一點(diǎn)“熟悉感”也不能讓它有推斷空間。5.5 常見問題速查表現(xiàn)象可能原因排查方向脫敏后模型答非所問脫敏粒度過大將打碼策略改為隨機(jī)替換對(duì)話中出現(xiàn)明文數(shù)據(jù)走了旁路RAG/外部工具所有數(shù)據(jù)出口統(tǒng)一走安全網(wǎng)關(guān)日志出現(xiàn)明文日志鏈路獨(dú)立于脫敏鏈路日志框架掛全局脫敏過濾器模型猜出原始敏感信息脫敏保留了可推斷片段高敏字段用完全隨機(jī)替換脫敏后數(shù)據(jù)格式錯(cuò)亂替換算法破壞索引改用倒序切片重建法6. 從Web轉(zhuǎn)Agent安全思維上必須做出的三個(gè)轉(zhuǎn)變6.1 從攔截到改寫Web 時(shí)代防數(shù)據(jù)泄露的主流思路是“不返回、不展示”。敏感字段對(duì)前端就是不給對(duì)日志就是不記。到了 Agent 時(shí)代這個(gè)思路失靈了因?yàn)槟P托枰獢?shù)據(jù)來完成推理。你不可能把一個(gè)沒有地址的工單給模型卻期望它準(zhǔn)確判斷“客戶是不是因?yàn)榕渌偷刂酚姓`才投訴”。所以安全手段從“攔截刪除”變成了“智能改寫”——給你必要的信息但給的不是真實(shí)信息。6.2 從單點(diǎn)防護(hù)到全鏈路審計(jì)Web 場(chǎng)景下前端展示、后端存儲(chǔ)、日志記錄各管一攤每一攤都有單獨(dú)的防護(hù)措施。Agent 場(chǎng)景下數(shù)據(jù)要經(jīng)過入站參數(shù)、Skill 返回、上下文滯留、模型輸出、日志記錄五六個(gè)環(huán)節(jié)任何一個(gè)環(huán)節(jié)漏了整個(gè)防線就破了。所以脫敏架構(gòu)必須覆蓋全鏈路任何一條數(shù)據(jù)路徑都不能繞過安全網(wǎng)關(guān)。6.3 從靜態(tài)清單到動(dòng)態(tài)策略Web 時(shí)代的敏感字段清單很穩(wěn)定基本是上線時(shí)配置好就不用動(dòng)了。Agent 場(chǎng)景下的敏感內(nèi)容識(shí)別會(huì)隨著業(yè)務(wù)變化而漂移。比如某天業(yè)務(wù)方在工單里新增了“客戶偏好”字段里面的內(nèi)容可能是客戶自述的健康狀況、家庭情況這種東西靠正則識(shí)別不了靠字段清單也需要同步更新。所以策略引擎必須做得足夠靈活配置化、可視化讓業(yè)務(wù)方能隨時(shí)調(diào)整而不是每次改策略都要發(fā)布一次代碼。個(gè)人體會(huì)是Web 開發(fā)者轉(zhuǎn) AI 應(yīng)用開發(fā)最大的優(yōu)勢(shì)在于數(shù)據(jù)庫(kù)模型、API 設(shè)計(jì)、系統(tǒng)架構(gòu)這些基本功扎實(shí)最大的短板是容易把“數(shù)據(jù)安全”想簡(jiǎn)單了。Agent 的數(shù)據(jù)鏈路遠(yuǎn)比我們熟悉的 Web 鏈路復(fù)雜敏感數(shù)據(jù)脫敏也不只是寫幾個(gè)工具函數(shù)而是要當(dāng)成一個(gè)獨(dú)立的架構(gòu)組件來設(shè)計(jì)。如果讓我重新來過我會(huì)在項(xiàng)目啟動(dòng)的第一天就把數(shù)據(jù)安全網(wǎng)關(guān)的骨架搭好而不是等到聯(lián)調(diào)發(fā)現(xiàn)模型開始“復(fù)讀”客戶手機(jī)號(hào)了才來補(bǔ)窟窿。希望這篇文章能給同樣在做 Agent Skills 開發(fā)的同行們一些參考少走一些我走過的彎路。