1. 为什么AI编码代理的上下文边界是个要命的问题
1.1 从一个真实的翻车场景说起
去年下半年,我帮一个做金融SaaS的团队做代码审计。他们内部部署了一套AI编码代理,接入了公司自研的代码仓库,本意是让代理帮忙做代码补全、单元测试生成和PR摘要。上线两周后,安全团队在一次例行检查中发现,代理的上下文窗口里居然混进了生产环境的数据库连接串、一份未脱敏的客户对账样本,还有一段标注着"内部机密"的风控规则伪代码。
问题出在哪?不是模型本身泄露,而是上下文边界没有做隔离。代理在响应一个"帮我优化这段查询逻辑"的请求时,检索模块把整个工作区的相关文件都塞进了上下文,包括那些本不该被这个任务看到的敏感文件。模型没有恶意,它只是忠实地基于上下文做了推理,然后把敏感信息以"建议"的形式吐了出来,最终落到了日志和聊天记录里。
这件事之后我意识到一个很现实的问题:大家把大量精力花在了模型选型、提示词工程、工具链集成上,却很少有人认真设计"什么信息可以进入上下文"这道闸门。而这道闸门,恰恰是AI编码代理能否在企业里安全落地的分水岭。
1.2 上下文边界到底指什么
先把概念说清楚。AI编码代理的"上下文",指的是在一次任务执行过程中,被送入模型推理窗口的全部信息集合。它通常包括这几类:
- 任务指令:用户输入的原始需求,比如"给这个函数加参数校验"。
- 代码片段:当前文件、相关依赖文件、被引用模块的源码。
- 检索结果:从代码库、文档库、Issue系统里召回的片段。
- 工具返回:执行shell命令、读文件、查数据库等工具调用的输出。
- 历史记忆:多轮对话的累积、之前任务的摘要。
- 环境元数据:路径、分支名、提交信息、配置项。
所谓"上下文边界",就是决定上述每一类信息中,哪些能进、哪些不能进、以什么形式进、进来之后如何被约束的一整套机制。它不是单一开关,而是贯穿检索、组装、推理、输出、落盘全链路的策略集合。
1.3 机密安全为什么必须前置到边界层
很多人有个误区:觉得只要模型部署在私有环境、数据不出内网,就安全了。这个想法在"单次问答"场景下勉强成立,但在"代理"场景下会迅速崩塌。原因有三:
第一,代理是主动检索的。它不像聊天机器人那样被动等你贴代码,它会自己决定去读哪些文件、跑哪些命令。这意味着敏感信息的暴露面从"你主动给的"扩大到了"它能碰到的"。
第二,代理的输出会落盘。补全的代码会写进文件,生成的测试会提交到仓库,PR摘要会发到协作平台。一旦敏感信息进入输出,它就被持久化了,清理成本极高。
第三,代理是多轮累积的。一个任务可能来回十几轮,每一轮的上下文都在叠加。第一轮不小心带进来的一行密钥,可能在第十轮被模型"联想"出来用在别处。
所以机密安全不能靠"事后审计"或"输出过滤"来兜底,必须在信息进入上下文之前就完成判定和裁剪。这就是"上下文边界"要解决的核心命题,也是零信任思路在AI编码场景里的具体落地:不默认任何文件、任何工具输出是安全的,每一次进入上下文都要经过验证。
1.4 这篇文章适合谁看
如果你正在做以下任何一件事,这篇内容应该对你有直接帮助:
- 正在企业内部部署或评估AI编码代理,需要过安全合规这一关。
- 是团队里负责AI工具链的工程师,想搞清楚上下文组装环节的风险点。
- 是安全或平台团队成员,被要求给AI编码工具出一份可落地的管控方案。
- 是独立开发者,虽然规模小,但接入了包含密钥、客户数据的私有仓库,同样需要边界意识。
我会按"设计思路—核心机制—实操落地—问题排查"的顺序展开,尽量给出可以直接抄作业的配置和代码骨架。涉及具体参数的地方我会说明取值理由,涉及取舍的地方我会讲清楚为什么这么选。
2. 上下文边界的整体设计与思路拆解
2.1 核心设计原则:默认拒绝,按需放行
设计上下文边界,第一条原则必须是默认拒绝。也就是说,任何信息在未经明确判定之前,都不应该进入上下文。这跟防火墙的默认策略是一个道理:白名单放行,其余一律拦截。
为什么不能反过来用黑名单?因为敏感信息的形态太多变了。你没法穷举所有密钥格式、所有客户数据模式、所有内部代号。黑名单永远滞后,而白名单只需要你定义清楚"什么任务是可信的、什么来源是可信的"。
具体到实现上,默认拒绝意味着:
- 检索模块召回的文件,必须经过来源校验和内容扫描才能进上下文。
- 工具调用的输出,必须经过结构化解析和字段级过滤才能进上下文。
- 历史记忆的复用,必须经过敏感度重评估才能进上下文。
2.2 分层防御:把边界拆成四道闸
单一检查点很容易被绕过,所以我习惯把上下文边界拆成四层,每层负责不同的判定维度:
| 层级 | 名称 | 主要职责 | 拦截时机 |
|---|---|---|---|
| L1 | 来源准入 | 判断文件/数据源是否在授权范围内 | 检索前 |
| L2 | 内容扫描 | 识别密钥、凭证、PII等敏感模式 | 组装前 |
| L3 | 任务相关性 | 判断信息是否与当前任务真正相关 | 组装时 |
| L4 | 输出约束 | 防止敏感信息通过生成结果外泄 | 输出前 |
这四层不是简单的串联,而是各有侧重。L1解决"能不能碰",L2解决"碰了能不能带进来",L3解决"带进来是不是必要",L4解决"带进来之后会不会漏出去"。任何一层单独做都不够,必须组合。
2.3 为什么选择本地过滤而非云端过滤
热词里提到"本地过滤",这是个关键取舍。上下文过滤放在本地还是云端,直接决定了安全模型的有效性。
云端过滤的问题是:要过滤,就得先把内容传上去。这本身就违背了"敏感信息不出本地"的初衷。哪怕云端服务商承诺不存储,传输链路和内存里的明文依然是风险面。而且云端过滤通常只能做通用模式匹配,没法结合你本地的权限体系做细粒度判定。
本地过滤的优势在于:
- 敏感信息在离开本机之前就被拦截,物理上不出边界。
- 可以结合本地权限系统,比如某个文件只有A组能看,那代理以A组身份运行时才允许召回。
- 过滤规则可以完全自定义,适配公司内部的命名规范和机密分级。
- 延迟可控,不依赖网络往返。
代价是本地需要维护一套规则引擎和扫描器,有一定运维成本。但对于任何涉及机密代码的场景,这个成本是必须付的。我的建议是:只要仓库里有任何一行不该公开的代码,过滤就必须放在本地。
2.4 零信任在代理场景的具体含义
零信任的核心是"永不信任,始终验证"。放到AI编码代理的上下文边界上,它意味着几个反直觉的做法:
- 不信任代理自己的判断。代理说"我需要读这个文件",不代表它真的需要,也不代表这个文件它能读。检索请求必须经过独立于模型的策略引擎审批。
- 不信任历史上下文。上一轮通过检查的信息,这一轮要重新检查。因为任务可能已经切换,权限可能已经变化。
- 不信任工具输出。工具返回的内容默认是不可信的,必须解析成结构化字段后逐字段判定,而不是整段塞进上下文。
- 不信任"内部"标签。很多团队给文件打个"internal"就放心了,但内部文件里照样可能有硬编码密钥。标签是元数据,不是内容保证。
这套思路落地后,代理的行为会变得"束手束脚",但这恰恰是安全的代价。你要在"代理能力"和"信息暴露面"之间找平衡点,而不是无脑放开。
2.5 边界设计要避免的三个坑
在讲具体实现之前,先说说我踩过的坑,帮你省点时间。
第一个坑:只做正则匹配。很多人觉得写几个正则匹配api_key=、password=就够了。实测下来,正则能覆盖的可能只有六成。密钥可能是base64编码的、可能拆成多行拼接的、可能藏在测试fixture里的。必须正则加熵检测加语义识别组合使用。
第二个坑:过滤粒度太粗。有的方案是"整个文件命中敏感词就整个文件不进上下文"。这会导致代理几乎没法工作,因为很多正常代码文件里也会出现"password"这个词。正确做法是行级或片段级过滤,把敏感行替换成占位符,保留其余部分。
第三个坑:忽略工具输出的结构化。代理执行git log、env、cat config这类命令时,输出里可能混着大量敏感信息。如果直接把stdout整段塞进上下文,等于把整个环境暴露了。必须对工具输出做解析,只提取任务需要的字段。
3. 核心机制解析与关键实现细节
3.1 来源准入:给每个数据源打上信任标签
来源准入是L1层,解决"代理能碰哪些数据"的问题。实现上,我建议给每个数据源维护一份信任档案,包含以下字段:
source_id: repo_finance_core type: git_repository path: /repos/finance-core trust_level: restricted allowed_roles: [finance_dev, security_audit] sensitivity: high scan_required: true allowed_tools: [read_file, grep] denied_patterns: - "**/secrets/**" - "**/*.pem" - "**/prod-config/**"这份档案的作用是:当代理发起检索请求时,策略引擎先查档案,判断当前任务身份是否有权访问该源,以及该源下哪些路径是禁区。
trust_level我一般分四档:public(可自由召回)、internal(需扫描后召回)、restricted(需角色匹配且扫描)、secret(禁止进入上下文,只能通过脱敏摘要间接使用)。这个分级要跟公司现有的数据分级体系对齐,不要另起炉灶。
allowed_tools这个字段容易被忽略,但很重要。有些数据源允许读,但不允许执行。比如生产配置目录,代理可以读脱敏后的模板,但绝不能执行里面的脚本。把工具权限也纳入来源档案,能堵住一类绕过。
3.2 内容扫描:正则、熵、语义三管齐下
L2层的内容扫描是技术含量最高的部分。我的实现是三层叠加:
第一层,正则模式库。覆盖常见凭证格式:
PATTERNS = [ # 通用密钥赋值 (r'(?i)(api[_-]?key|secret|token|password|passwd|pwd)\s*[:=]\s*["\']?([A-Za-z0-9_\-]{16,})', 'generic_secret'), # 私钥块 (r'-----BEGIN (RSA|EC|OPENSSH|PGP) PRIVATE KEY-----', 'private_key'), # 云厂商凭证 (r'(?i)(AKIA|ASIA)[0-9A-Z]{16}', 'aws_access_key'), # 连接串 (r'(?i)(mongodb|postgres|mysql|redis)://[^\s]+:[^\s]+@', 'connection_string'), # JWT (r'eyJ[A-Za-z0-9_\-]+\.eyJ[A-Za-z0-9_\-]+\.[A-Za-z0-9_\-]+', 'jwt_token'), ]第二层,熵检测。正则抓不到的高熵字符串,用香农熵来兜底。经验阈值是:长度超过20、熵值超过4.5的字符串,标记为疑似密钥。这个阈值我调过很多次,4.5是个比较平衡的点,再低会误报正常哈希值,再高会漏掉一些短密钥。
import math from collections import Counter def shannon_entropy(s): if not s: return 0 counts = Counter(s) length = len(s) return -sum((c/length) * math.log2(c/length) for c in counts.values()) def is_high_entropy(token, min_len=20, threshold=4.5): return len(token) >= min_len and shannon_entropy(token) >= threshold第三层,语义识别。前两层都是基于形态的,会漏掉"看起来正常但语义敏感"的内容,比如客户姓名列表、内部项目代号、未公开的财务数字。这一层需要结合命名实体识别和自定义词典。我的做法是维护一份内部敏感词表(项目代号、客户名、内部系统名),加上通用的PII识别(身份证、手机号、邮箱、银行卡)。
三层扫描的结果要合并去重,然后按敏感级别决定处理方式:高危直接拦截整段,中危替换成占位符,低危记录告警但放行。
3.3 任务相关性:不是所有能读的都需要读
L3层解决"过度召回"问题。代理为了保险,往往倾向于多读文件,但这会放大暴露面。相关性判定我用了两个信号:
信号一,显式引用。如果当前任务涉及的文件import了某个模块,或者调用了某个函数,那这个模块是高相关的。这个可以通过静态分析拿到,准确率高。
信号二,语义相似度。对任务描述和候选文件做向量化,算余弦相似度。阈值我一般设在0.75,低于这个值的不进上下文。这个阈值偏保守,宁可少召回,让代理多轮去要,也不要一次性灌进去。
两个信号取并集,但显式引用的优先级更高。另外要加一条规则:单次任务的上下文总量设上限,比如不超过模型窗口的60%,留出余量给工具输出和多轮累积。超限时按相关性排序截断。
3.4 输出约束:防止敏感信息从出口溜走
L4层是最容易被忽视的。很多人觉得信息进来时过滤了,出去就安全了。但模型可能在推理过程中"重组"出敏感信息,比如把分散的片段拼成一个完整的密钥,或者把脱敏后的数据还原。
输出约束我做了三件事:
- 输出扫描:对模型生成的每一段内容跑一遍L2的扫描器,命中敏感模式就拦截并告警。
- 占位符还原控制:如果上下文里用了占位符(比如
<REDACTED_SECRET>),要确保模型不会把它当成真实值去用,输出里如果出现占位符要替换回说明文字。 - 落盘前二次检查:代理写文件、提交代码、发PR之前,对最终产物再跑一次扫描。这是最后一道闸。
3.5 上下文组装的安全骨架
把四层串起来,一次完整的上下文组装流程大致是这样:
def build_secure_context(task, agent_identity): # L1: 来源准入 candidate_sources = resolve_sources(task) authorized = [s for s in candidate_sources if policy_engine.is_authorized(agent_identity, s)] # L2 + L3: 扫描与相关性 selected = [] for source in authorized: fragments = retrieve(source, task) for frag in fragments: if not is_relevant(frag, task, threshold=0.75): continue scan_result = scanner.scan(frag.content) if scan_result.level == 'high': audit_log.record(frag, scan_result) continue elif scan_result.level == 'medium': frag.content = redact(frag.content, scan_result.hits) selected.append(frag) # 总量控制 selected = truncate_by_relevance(selected, max_ratio=0.6) # 组装 context = assemble(selected) return context def finalize_output(generated, task): # L4: 输出约束 scan_result = scanner.scan(generated) if scan_result.level == 'high': raise SecurityViolation("output contains sensitive data") return restore_placeholders(generated)这个骨架不复杂,但每一层都要有独立的日志和告警,方便事后追溯。我特别强调审计日志要记录被拦截的内容摘要和原因,不然出了问题根本查不到是哪一步漏的。
4. 实操落地:从零搭一套本地过滤的上下文边界
4.1 环境与依赖准备
这套东西我建议用Python实现,生态成熟,扫描库多。核心依赖:
pip install presidio-analyzer presidio-anonymizer # PII识别 pip install detect-secrets # 密钥检测 pip install sentence-transformers # 语义相似度 pip install pyyaml # 策略配置detect-secrets是Yelp开源的,插件化设计,支持自定义规则,比手写正则省事。presidio做PII识别很成熟,支持中文需要额外加载语言模型。sentence-transformers用来算相关性,模型选paraphrase-multilingual-MiniLM-L12-v2,体积小、支持中文、推理快。
如果团队规模小,不想引入这么多依赖,可以只保留detect-secrets加自写正则,PII部分用简单规则替代。但语义相关性这块我建议别省,它是控制过度召回的关键。
4.2 策略配置文件的写法
策略配置是整个边界的"大脑",我用YAML维护,方便非开发人员review。一个完整的配置示例:
version: 1 default_policy: deny sources: - id: main_repo path: /workspace/main trust_level: internal scan: true allowed_roles: ["*"] denied_paths: - "**/.env*" - "**/secrets/**" - "**/*.pem" - "**/credentials/**" - id: prod_config path: /workspace/prod-config trust_level: restricted scan: true allowed_roles: ["sre", "security"] allowed_tools: ["read_file"] redact_fields: ["db_password", "api_key", "jwt_secret"] scanner: entropy_threshold: 4.5 min_token_length: 20 pii_entities: ["PHONE_NUMBER", "EMAIL_ADDRESS", "ID_CARD", "BANK_CARD"] custom_terms_file: /etc/agent/sensitive_terms.txt relevance: similarity_threshold: 0.75 max_context_ratio: 0.6 explicit_reference_boost: true output: scan_on_generate: true scan_on_persist: true block_on_high: true几个关键点说明一下。default_policy: deny是默认拒绝,没在sources里列出的路径一律不可访问。denied_paths用glob模式,比正则好维护。redact_fields针对结构化配置,按字段名脱敏,比整段替换精准。custom_terms_file放内部敏感词,一行一个,支持热更新。
4.3 扫描器的实现与调参
扫描器是核心组件,我把它拆成三个独立的检测器,各自返回命中列表,最后合并:
class ContextScanner: def __init__(self, config): self.entropy_threshold = config['entropy_threshold'] self.min_token_length = config['min_token_length'] self.patterns = load_patterns() self.custom_terms = load_terms(config['custom_terms_file']) self.pii_analyzer = build_pii_analyzer(config['pii_entities']) def scan(self, text): hits = [] hits.extend(self._scan_patterns(text)) hits.extend(self._scan_entropy(text)) hits.extend(self._scan_pii(text)) hits.extend(self._scan_custom_terms(text)) return self._merge_and_grade(hits) def _scan_entropy(self, text): hits = [] for token in re.findall(r'[A-Za-z0-9+/=_\-]{20,}', text): if is_high_entropy(token, self.min_token_length, self.entropy_threshold): hits.append(Hit(token, 'high_entropy', 'medium')) return hits调参经验:熵阈值4.5是起点,如果你的代码库里有很多base64编码的正常数据(比如图片内联),会误报,这时候要么提高阈值到5.0,要么加白名单排除已知的正常高熵串。PII识别在中文场景下,手机号和身份证的召回率不错,但姓名识别容易误报,建议姓名类只告警不拦截。
4.4 相关性判定的落地
相关性用向量相似度实现,但要注意性能。每次任务都对全库算相似度不现实,我的做法是先用关键词粗筛,再对候选集算向量:
class RelevanceFilter: def __init__(self, model_name, threshold): self.model = SentenceTransformer(model_name) self.threshold = threshold self.cache = {} def filter(self, task_desc, candidates): task_vec = self._encode(task_desc) result = [] for cand in candidates: if cand.is_explicit_reference: result.append(cand) continue cand_vec = self._encode(cand.summary) sim = cosine_similarity(task_vec, cand_vec) if sim >= self.threshold: cand.relevance_score = sim result.append(cand) return sorted(result, key=lambda c: c.relevance_score, reverse=True)cand.summary不是文件全文,而是文件的功能摘要(可以用函数签名、docstring、注释生成)。对摘要算相似度比全文快得多,也更准,因为摘要去掉了实现细节的噪声。
4.5 工具输出的结构化处理
工具输出是最容易漏的地方。我的原则是:任何工具的输出都不直接进上下文,必须先解析成结构化字段。以几个常见工具为例:
| 工具 | 原始输出风险 | 结构化处理 |
|---|---|---|
env | 全部环境变量,含密钥 | 只提取任务声明的变量名,其余丢弃 |
git log | 提交信息可能含敏感描述 | 只取commit hash和作者,信息做扫描 |
cat config.yaml | 配置全文 | 按字段白名单提取,敏感字段脱敏 |
ls -la | 文件名可能含敏感信息 | 文件名过扫描,命中则只显示数量 |
curl | 响应可能含凭证 | 只取状态码和指定字段,body扫描 |
实现上给每个工具配一个解析器,解析器负责把原始输出转成{field: value},然后走统一的字段级扫描。这样即使工具输出里有敏感信息,也不会整段进上下文。
4.6 审计与告警的配置
审计日志我建议单独存,不要跟应用日志混在一起。每条记录包含:时间戳、任务ID、代理身份、被拦截/脱敏的片段摘要(注意摘要本身也要脱敏)、命中规则、处理动作。摘要只存前50字符加哈希,避免日志本身成为泄露源。
告警策略分两级:高危命中实时告警到安全群,中危命中按小时聚合。告警要带上下文,比如"代理X在任务Y中尝试读取Z文件,命中规则W",方便快速定位。
5. 常见问题与排查技巧实录
5.1 代理频繁报"上下文不足"怎么办
这是最常见的抱怨。代理因为过滤太严,拿不到足够信息,任务做不下去。排查思路:
先看是L1还是L3拦的。如果是L1,说明来源准入太紧,检查allowed_roles是否配错,或者denied_paths是否误伤了正常路径。如果是L3,说明相关性阈值太高,可以适当降到0.7,或者放宽max_context_ratio到0.7。
但要注意,不要为了可用性无脑放宽。更好的做法是让代理"多轮要":第一轮拿高相关的,不够再发起第二轮检索,每轮都过边界。这样既保证安全,又能逐步补全信息。
5.2 误报太多,正常代码被拦
误报主要来自熵检测和PII识别。排查方法:
- 把被拦的片段收集起来,看命中哪条规则。
- 如果是熵检测误报,把该模式加入白名单(比如已知的测试用哈希)。
- 如果是PII误报,调整识别器的置信度阈值,或者把该实体类型从拦截降级为告警。
- 如果是自定义词误报,检查词表里是否有过于宽泛的词,比如"test"这种。
我一般会维护一个false_positive_patterns列表,定期review,把确认的误报模式加进去。
5.3 工具输出绕过过滤
有次发现代理执行python -c "print(open('.env').read())",把.env内容通过stdout带出来了。这类绕过很隐蔽,因为命令本身不敏感,敏感的是输出。
对策是工具输出必须走扫描,且扫描在工具执行后立即进行,不依赖命令白名单。另外,对能读文件的工具(read_file、cat、python等),要限制其可访问路径,跟L1的来源准入共用一套策略。命令白名单只能作为辅助,不能作为唯一防线。
5.4 多轮对话中敏感信息累积泄露
多轮场景下,第一轮脱敏的片段,可能在第五轮被模型"回忆"出来。这是因为历史上下文里可能残留了脱敏前的版本,或者模型从多个脱敏片段里推断出了原值。
对策有两个:一是历史上下文定期重扫描,每轮开始前对累积的历史跑一遍扫描,发现新的敏感命中就清理。二是对脱敏片段做不可逆处理,不要用<REDACTED>这种可推断的占位符,而是用语义无关的标记,比如<FIELD_A>,让模型无法从占位符本身推断内容。
5.5 性能开销太大
全量扫描加向量计算,在大仓库上确实慢。优化手段:
- 扫描结果缓存,同一文件内容不变则复用上次结果,用文件哈希做key。
- 向量计算批处理,一次encode多个候选,比逐个快很多。
- 相关性粗筛用关键词倒排索引,只对粗筛结果算向量。
- 扫描器用多进程,正则和熵检测可以并行。
实测下来,一个十万文件的仓库,首次全量扫描约几分钟,之后增量扫描基本在秒级。这个开销对代理场景是可以接受的。
5.6 常见问题速查表
| 现象 | 可能原因 | 排查动作 | 解决方向 |
|---|---|---|---|
| 代理拿不到信息 | L1/L3过严 | 看拦截日志定位层级 | 调角色或阈值 |
| 正常代码被拦 | 熵/PII误报 | 收集命中片段 | 加白名单或降级 |
| 敏感信息仍泄露 | 工具输出未扫描 | 检查工具解析器 | 补结构化处理 |
| 多轮后泄露 | 历史未重扫 | 查历史上下文 | 定期重扫+不可逆脱敏 |
| 响应慢 | 全量扫描 | 看耗时分布 | 缓存+批处理+粗筛 |
| 告警太多 | 阈值太敏感 | 统计命中分布 | 分级告警+聚合 |
5.7 几条压箱底的经验
最后分享几条实操中总结的、文档里不会写的经验。
经验一:边界规则要版本化。策略配置改动要像代码一样走review和版本管理,每次改动记录原因。我见过因为有人随手改了denied_paths导致敏感目录被放开的案例。
经验二:定期做红队测试。让安全同学模拟恶意任务,尝试让代理读出敏感文件。我每季度做一次,每次都能发现新的绕过路径。测试用例要覆盖:路径穿越、编码混淆、工具链组合、多轮诱导。
经验三:脱敏要保留语义。把密钥替换成<REDACTED>会让模型无法理解代码意图,影响补全质量。更好的做法是替换成同类型的假值,比如把真实密钥换成sk-test-xxxxxxxx,模型能理解这是密钥,但拿不到真值。
经验四:给代理一个"最小权限身份"。代理运行时的身份权限,应该是完成任务所需的最小集合,而不是复用某个开发者的全量权限。这样即使边界被绕过,损失也有限。
经验五:日志本身要脱敏。审计日志里记录的片段摘要,如果直接存原文,日志系统就成了新的泄露源。摘要只存哈希和前若干字符,且这些字符也要过扫描。
这套上下文边界的方案,我在三个团队落地过,从几十人的创业公司到上千人的企业都有。核心逻辑是一样的:默认拒绝、分层防御、本地过滤、全程审计。规模越大,策略配置和审计的部分越重;规模越小,扫描器和相关性判定可以简化。但"信息进上下文之前必须过闸"这条底线,任何规模都不能省。