
1. 项目概述这不是“又一个LLM服务框架”而是生产环境里真正能扛住流量的底座“高效服务大语言模型”——这八个字听起来像一句正确的废话。但如果你正在把一个7B参数的Qwen或13B的Llama3模型从本地Jupyter Notebook推到公司内部API网关再面对每天5000次并发请求、平均响应延迟必须压在800ms以内、GPU显存不能持续超过85%、还要支持动态批处理和流式输出你就会发现Hugging Face的transformerspipeline组合在开发阶段很香上线第一天就可能被压垮FastAPI裸跑模型连最基础的请求排队、超时熔断、显存隔离都得自己一行行补vLLM确实快但它的调度器对小规模集群不友好配置项多到让人怀疑人生而且当你需要同时挂载多个不同精度FP16/INT4、不同tokenizer、甚至混用推理引擎vLLM llama.cpp的模型时它就不再是“开箱即用”而是“开箱即填坑”。这个标题背后的真实需求不是“怎么把模型跑起来”而是“怎么让LLM在真实业务场景中稳定、低延迟、高吞吐、可运维地持续提供服务”。它面向的是AI基础设施工程师、MLOps平台建设者、以及那些被产品催着上线、又被SRE半夜电话叫醒查OOM的算法工程师。它解决的核心矛盾是学术推理框架追求极致吞吐与理论最优而工业级服务框架必须在性能、资源、弹性、可观测性、安全边界之间做精密权衡。我过去三年在三家不同规模公司落地过12个LLM服务模块踩过的坑几乎都指向同一个结论没有银弹只有取舍。而这个框架的设计哲学就是把所有关键取舍点显性化、模块化、可配置化——不是给你一个黑盒而是给你一套“决策地图”。它不承诺“一键部署万能模型”但承诺当你面对一个新模型、一种新业务流量模式、一次突发的GPU故障时你能快速判断该调哪个参数、换哪个组件、加哪层缓冲。它不替代vLLM或TGI而是站在它们之上构建一层统一的抽象与治理层。关键词里的“Efficiently”在这里不是指单次推理的FLOPs利用率而是指整个服务生命周期的工程效率部署效率、调试效率、扩容效率、故障恢复效率。接下来的内容我会完全基于真实生产环境中的架构图、配置片段、监控截图和告警日志来展开不讲原理图只讲你明天就能改、后天就能上线的实操逻辑。2. 整体架构设计与核心取舍逻辑为什么放弃“大一统”选择“分层解耦”2.1 架构全景四层分离每层只解决一类问题我们最终落地的框架不是单体服务而是一个由四个明确职责层组成的松耦合系统接入层Ingress Layer负责HTTPS终止、JWT鉴权、请求路由、速率限制Rate Limiting、请求重写如将/v1/chat/completions映射到具体模型实例。这里我们弃用了Nginx直接采用Envoy作为边缘代理。原因很实际Nginx的Lua扩展在高并发下GC压力大且对gRPC-Web协议支持弱而Envoy原生支持gRPC、HTTP/2、WebSocket并可通过xDS API实现动态配置热更新——当你要灰度发布一个新模型版本时不需要重启任何进程只需推送一条新的路由规则。调度层Orchestration Layer这是框架的“大脑”但它不做模型推理。它只做三件事① 根据模型名称、版本、精度标签如qwen2-7b-int4查找可用的推理实例② 基于实时指标GPU显存使用率、请求队列长度、P95延迟进行智能负载均衡③ 实现跨实例的请求批处理Cross-Instance Batching。这里的关键取舍是我们没有自研调度器而是将Kubernetes的Horizontal Pod AutoscalerHPA与一个轻量级自定义调度器用Go写的2000行代码结合。HPA负责长期扩缩容分钟级自定义调度器负责秒级流量调度。这样既避免了重复造轮子又保留了对业务语义的理解能力——比如当检测到某模型的input_length 2048请求占比突增调度器会主动将新请求导向显存更大的节点而不是盲目均摊。推理层Inference Layer这才是真正跑模型的地方。我们不绑定单一引擎而是支持三种后端并存vLLM用于高吞吐、长上下文8K tokens场景如文档摘要、代码生成llama.cpp用于CPU-only或低功耗GPU如T4场景主打INT4量化与内存友好适合嵌入式或边缘侧Triton Inference Server用于需要严格A/B测试、模型版本灰度、或需集成自定义CUDA算子的场景。每个后端都封装为标准的gRPC服务对外暴露统一的Generate接口。这意味着上层调度器完全不知道下面跑的是vLLM还是llama.cpp——它只认model_id和backend_type标签。这种解耦让我们在一次线上事故中受益巨大某天vLLM因一个特定tokenizer bug导致部分请求卡死我们仅用3分钟就将所有qwen2-7b流量切到llama.cpp后端用户无感而修复vLLM版本则花了两天。治理层Governance Layer这是最容易被忽视、却最影响长期稳定性的部分。它包含统一指标采集通过Prometheus Exporter暴露所有层级的关键指标接入层QPS、调度层路由成功率、推理层GPU Util、各模型P99延迟结构化日志每个请求生成唯一request_id贯穿四层日志字段固定model_id,input_tokens,output_tokens,latency_ms,error_code便于ELK聚合分析配置中心所有模型的启动参数max_model_len,gpu_memory_utilization,quantization、限流阈值、熔断策略全部从Consul动态加载无需重启服务。提示很多团队把“治理”等同于“加监控”这是巨大误区。真正的治理是让监控数据能直接驱动决策。比如当model_qwen2_7b_p99_latency_ms连续5分钟 1200ms且gpu_memory_utilization 70%系统会自动触发consul kv put model/qwen2-7b/config/max_model_len 4096强制缩短最大上下文长度以降低KV Cache压力——这个动作是自动的不是靠人盯屏。2.2 关键取舍背后的硬核计算为什么是8个并发连接而不是16框架默认为每个推理实例配置8个gRPC客户端连接池。这个数字不是拍脑袋定的而是经过三次压测迭代得出的第一轮理论计算假设单卡A10 GPU显存为24GBvLLM默认gpu_memory_utilization0.9可用显存≈21.6GB。Qwen2-7b-INT4模型加载后约占用6.2GB剩余约15.4GB可用于KV Cache。按vLLM论文公式KV Cache内存 ≈2 * batch_size * seq_len * num_layers * hidden_size * sizeof(dtype)。代入典型值batch_size8,seq_len2048,num_layers32,hidden_size4096,dtypefloat16计算得KV Cache ≈ 8.2GB。此时显存余量尚可。第二轮实测瓶颈用Locust模拟8并发P95延迟稳定在620ms。但当我们提升到16并发时延迟跳变到1450ms且nvidia-smi显示GPU Util从78%飙升至99%而vLLM日志频繁出现[WARNING] BlockManagerV2: Out of memory while allocating blocks。根本原因是vLLM的PagedAttention机制虽高效但块分配本身有CPU开销16并发下CPU线程争抢加剧导致GPU等待时间拉长。第三轮业务验证我们抓取了线上一周的真实流量分布92%的请求input_tokens 512但output_tokens方差极大100~4000。这意味着高并发并不能线性提升吞吐因为大量请求在等待长输出生成。最终我们采用“动态连接数”策略基础连接池设为8当avg_output_tokens 2000时自动扩容至12当error_rate 0.5%时自动缩容回4。这个策略让平均延迟下降23%GPU Util波动范围从±15%收窄至±5%。这个例子说明所谓“高效”不是堆参数而是让每个数字都有物理意义和业务依据。后面所有配置项我都会给出类似的计算过程或实测依据。3. 核心细节解析与实操要点从零搭建一个可运行的最小闭环3.1 环境准备为什么坚持用Ubuntu 22.04 CUDA 12.1操作系统和CUDA版本的选择直接影响后续所有组件的兼容性与稳定性。我们曾尝试在CentOS 7上部署结果在vLLM编译阶段卡在nvcc版本不匹配也试过Ubuntu 20.04 CUDA 11.8但在启用FlashAttention-2时遇到内核panic。最终锁定Ubuntu 22.04 LTS CUDA 12.1原因如下Ubuntu 22.04内核版本5.15对NVIDIA驱动470系列支持完善且systemd的cgroup v2默认启用这对容器化GPU资源隔离至关重要。vLLM官方Docker镜像也是基于此构建。CUDA 12.1这是目前vLLM、llama.cpp、Triton三者共同支持的最高稳定版本。CUDA 12.2虽新但llama.cpp的某些INT4 kernel尚未适配CUDA 12.0则缺少对Hopper架构H100的完整支持。安装步骤必须严格遵循NVIDIA官方指南尤其注意两点安装NVIDIA驱动时务必禁用nouveau驱动在/etc/modprobe.d/blacklist-nouveau.conf中添加blacklist nouveau和options nouveau modeset0然后执行sudo update-initramfs -uCUDA Toolkit安装后必须将/usr/local/cuda-12.1/bin加入PATH并将/usr/local/cuda-12.1/lib64加入LD_LIBRARY_PATH——很多“ImportError: libcudart.so.12 not found”错误根源都在这里。注意不要用apt install nvidia-cuda-toolkit这是Debian维护的阉割版缺少nvcc编译器和关键库会导致vLLM源码编译失败。必须从NVIDIA官网下载.run文件安装。3.2 推理层实操vLLM部署的5个致命细节vLLM是当前推理层的主力但它的默认配置离生产可用还有距离。以下是我们在真实集群中必须修改的5个关键点① 启动命令必须显式指定--enable-chunked-prefill原因默认关闭。当用户输入很长如上传PDF文本vLLM会一次性加载全部token极易OOM。开启后vLLM将长输入分块预填充显存占用下降40%以上。实测处理128K token输入时未开启报OOM开启后P95延迟仅增加180ms。②--max-num-seqs不能等于--max-num-batched-tokens / --max-model-len这是新手最常犯的错误。--max-num-seqs是并发请求数上限--max-num-batched-tokens是总token数上限。若模型max_model_len4096你设--max-num-batched-tokens32768则理论最大并发为8。但实际应设为--max-num-seqs6——预留2个slot给长请求的padding。否则当6个请求都是4096长度时第7个请求会因无slot排队而非因token数超限。③ 必须配置--block-size16而非默认32Block size决定KV Cache的内存粒度。16是vLLM在A10/A100上实测的最佳值太小如8导致块管理开销剧增太大如64则浪费显存一个块未用满即被锁定。我们用nvidia-smi dmon -s u监控16时gpu_mem_util曲线最平滑。④ 日志级别必须设为INFO禁用DEBUGDEBUG日志会记录每个token的生成过程单次请求产生数MB日志在高并发下迅速打爆磁盘IO。生产环境必须--log-levelINFO且通过--log-requests仅记录请求元信息。⑤ 健康检查端点必须重写vLLM自带/health返回{healthy: true}但这只检查进程存活。我们必须添加/healthz其逻辑为向自身/generate发送一个promptHello的请求校验返回output是否非空且latency 500ms。K8s Liveness Probe调用此端点才能真正感知推理能力。一个完整的生产级vLLM启动命令示例python -m vllm.entrypoints.api_server \ --model Qwen/Qwen2-7b-Instruct \ --tensor-parallel-size 1 \ --pipeline-parallel-size 1 \ --dtype half \ --quantization awq \ --awq-ckpt-path ./qwen2-7b-awq.pt \ --max-model-len 4096 \ --max-num-batched-tokens 32768 \ --max-num-seqs 6 \ --block-size 16 \ --enable-chunked-prefill \ --gpu-memory-utilization 0.85 \ --host 0.0.0.0 \ --port 8000 \ --log-level INFO \ --log-requests \ --disable-log-stats3.3 调度层实操如何用200行Go代码实现智能路由调度层的核心是ModelRouter它接收来自Envoy的请求含model_id,version,priority返回目标推理实例的endpoint。我们不用K8s Service DNS轮询因为那无法感知实例健康状态。以下是ModelRouter的关键逻辑// 伪代码实际为Go实现 func (r *ModelRouter) Route(req *Request) (*Endpoint, error) { // Step 1: 根据model_id和version筛选候选实例 candidates : r.store.GetInstancesByModel(req.ModelID, req.Version) // Step 2: 过滤掉不健康的实例连续3次健康检查失败 candidates filterUnhealthy(candidates) // Step 3: 按权重排序——权重 (1 - gpuUtil) * 0.6 (1 - queueLen/100) * 0.4 // 这里体现业务语义GPU利用率比队列长度更重要因为前者是硬瓶颈 sort.Slice(candidates, func(i, j int) bool { w1 : (1 - candidates[i].GpuUtil) * 0.6 (1 - float64(candidates[i].QueueLen)/100) * 0.4 w2 : (1 - candidates[j].GpuUtil) * 0.6 (1 - float64(candidates[j].QueueLen)/100) * 0.4 return w1 w2 // 权重高的排前面 }) // Step 4: 高优先级请求如管理后台走Top1普通请求按权重随机选择带权重轮询 if req.Priority high { return candidates[0].Endpoint, nil } return weightedRandomSelect(candidates), nil }这个设计解决了三个痛点避免雪崩当某实例GPU Util 95%其权重趋近于0新请求几乎不会被分配平滑扩容新扩容的实例GPU Util为0权重最高自然承接最多流量业务分级客服系统调用的/v1/chat接口带priorityhighheader永远走最优实例保障SLA。实操心得权重公式中的系数0.6/0.4不是固定的。我们通过A/B测试发现对Qwen2-7bGPU Util权重应更高但对Llama3-8b因KV Cache更吃内存queueLen权重需调至0.55。这印证了“没有通用最优解只有场景最优解”。4. 实操过程与核心环节实现从单机验证到K8s集群部署的全链路4.1 单机验证5分钟跑通第一个请求这是建立信心的关键一步。不要一上来就搞K8s先确保单机流程无误。步骤1准备模型与量化文件从Hugging Face Hub下载Qwen2-7b-Instruct使用autoawq工具量化pip install autoawq python -c from awq import AutoAWQForCausalLM from transformers import AutoTokenizer model AutoAWQForCausalLM.from_pretrained(Qwen/Qwen2-7b-Instruct, safetensorsTrue) tokenizer AutoTokenizer.from_pretrained(Qwen/Qwen2-7b-Instruct) model.quantize(tokenizer, quant_config{zero_point: True, q_group_size: 128, w_bit: 4, version: GEMM}) model.save_quantized(./qwen2-7b-awq) tokenizer.save_pretrained(./qwen2-7b-awq) 生成的qwen2-7b-awq目录即为量化后模型。步骤2启动vLLM服务使用前文的启动命令确保端口8000开放。启动后访问http://localhost:8000/docsSwagger UI应正常加载。步骤3发送测试请求用curl验证curl -X POST http://localhost:8000/generate \ -H Content-Type: application/json \ -d { prompt: 你好请用中文写一首关于春天的五言绝句, sampling_params: {temperature: 0.7, top_p: 0.95, max_tokens: 256} } | jq .text预期输出类似春眠不觉晓处处闻啼鸟。\n夜来风雨声花落知多少。如果返回500或超时立即检查nvidia-smi——90%的问题是显存不足或CUDA版本不匹配。步骤4启动调度层简易版写一个Python脚本simple_router.py只做静态路由from flask import Flask, request, jsonify import requests app Flask(__name__) app.route(/v1/chat/completions, methods[POST]) def route(): data request.get_json() # 简单转发到本地vLLM resp requests.post(http://localhost:8000/generate, jsondata) return jsonify(resp.json()), resp.status_code if __name__ __main__: app.run(host0.0.0.0, port8001)然后用curl -X POST http://localhost:8001/v1/chat/completions -d {...}测试。这证明了“调度-推理”链路打通。注意单机验证时务必用jq解析JSON避免肉眼判断。我们曾因一个隐藏的UTF-8 BOM字符导致prompt解析失败排查了2小时。4.2 K8s集群部署YAML清单的关键字段解读当单机验证通过即可推进到K8s。我们不使用Helm Chart而是手写YAML因为这样才能精确控制每一个资源约束。以下是vllm-deployment.yaml的核心片段apiVersion: apps/v1 kind: Deployment metadata: name: vllm-qwen2-7b spec: replicas: 2 # 至少2副本避免单点故障 selector: matchLabels: app: vllm-qwen2-7b template: metadata: labels: app: vllm-qwen2-7b spec: # 关键1GPU资源请求必须等于limit禁止超卖 containers: - name: vllm image: vllm/vllm-openai:latest resources: limits: nvidia.com/gpu: 1 # 严格限定1张GPU requests: nvidia.com/gpu: 1 # 关键2设置GPU亲和性避免跨NUMA节点 env: - name: NVIDIA_VISIBLE_DEVICES value: 0 # 关键3挂载模型和量化文件使用hostPath或NFS volumeMounts: - name: model-storage mountPath: /models/qwen2-7b-awq volumes: - name: model-storage hostPath: path: /data/models/qwen2-7b-awq type: DirectoryOrCreate # 关键4容忍GPU节点污点 tolerations: - key: nvidia.com/gpu operator: Exists effect: NoSchedule # 关键5节点亲和性只调度到有A10的节点 affinity: nodeAffinity: requiredDuringSchedulingIgnoredDuringExecution: nodeSelectorTerms: - matchExpressions: - key: accelerator operator: In values: [a10]Service配置要点必须创建Headless ServiceclusterIP: None因为vLLM实例间需要直接通信P2P。同时为Envoy Ingress创建一个ClusterIP Service只暴露8000端口。HPA配置逻辑我们不基于CPU/Memory而是基于自定义指标vllm_gpu_utilizationapiVersion: autoscaling/v2 kind: HorizontalPodAutoscaler metadata: name: vllm-qwen2-7b-hpa spec: scaleTargetRef: apiVersion: apps/v1 kind: Deployment name: vllm-qwen2-7b minReplicas: 2 maxReplicas: 8 metrics: - type: Pods pods: metric: name: vllm_gpu_utilization target: type: AverageValue averageValue: 750m # 75%750m表示75%当平均GPU Util 75%持续5分钟HPA触发扩容。4.3 Envoy Ingress配置如何让LLM API像RESTful一样优雅Envoy的配置是整个接入层的灵魂。我们不使用复杂的xDS而是用静态envoy.yaml并通过ConfigMap挂载。核心是http_filters和routesstatic_resources: listeners: - name: listener_0 address: socket_address: { address: 0.0.0.0, port_value: 8000 } filter_chains: - filters: - name: envoy.filters.network.http_connection_manager typed_config: type: type.googleapis.com/envoy.extensions.filters.network.http_connection_manager.v3.HttpConnectionManager stat_prefix: ingress_http codec_type: AUTO route_config: name: local_route virtual_hosts: - name: backend domains: [*] routes: # 将OpenAI兼容API路由到调度层 - match: { prefix: /v1/chat/completions } route: { cluster: scheduler_cluster, timeout: 120s } - match: { prefix: /v1/completions } route: { cluster: scheduler_cluster, timeout: 120s } # 将健康检查路由到vLLM实例绕过调度 - match: { prefix: /healthz } route: { cluster: vllm_cluster, timeout: 5s } http_filters: - name: envoy.filters.http.jwt_authn typed_config: type: type.googleapis.com/envoy.extensions.filters.http.jwt_authn.v3.JwtAuthentication providers: auth0: issuer: https://your-domain.auth0.com/ audiences: [llm-api] local_jwks: inline_string: {...} # JWT公钥 rules: - match: { prefix: / } requires: { provider_name: auth0 } - name: envoy.filters.http.ratelimit typed_config: type: type.googleapis.com/envoy.extensions.filters.http.ratelimit.v3.RateLimit domain: llm_api rate_limit_service: transport_api_version: V3 grpc_service: envoy_grpc: cluster_name: ratelimit_cluster这个配置实现了统一鉴权所有API请求必须携带Auth0签发的JWT细粒度限流ratelimit_cluster对接Redis按user_id或api_key限流超时分级Chat接口120秒允许长思考健康检查5秒快速失败路径重写透明用户调用/v1/chat/completions调度层收到的仍是同一路径无需额外解析。5. 常见问题与排查技巧实录那些让你凌晨三点爬起来的真问题5.1 典型问题速查表问题现象根本原因快速定位命令解决方案vLLM启动报错OSError: libcudart.so.12: cannot open shared object fileCUDA路径未加入LD_LIBRARY_PATHecho $LD_LIBRARY_PATH在启动脚本开头添加export LD_LIBRARY_PATH/usr/local/cuda-12.1/lib64:$LD_LIBRARY_PATHcurl请求返回503Envoy日志显示upstream_reset_before_response_started{connection_failure}vLLM实例未启动或端口未监听kubectl exec -it pod -- netstat -tuln | grep 8000检查vLLM容器日志kubectl logs pod -c vllm常见于模型路径错误P95延迟突然升高至3s但GPU Util仅60%KV Cache碎片化严重vLLM无法分配连续块kubectl logs pod | grep Out of memory while allocating blocks重启vLLM实例临时缓解长期方案减小--block-size或启用--enable-chunked-prefill调度层路由到实例A但实例A的/healthz返回500实例A的/healthz探针逻辑有bug或依赖服务如Redis不可用curl http://instance-A-ip:8000/healthz检查/healthz代码确保只依赖自身推理能力不依赖外部服务批量请求时部分请求返回context length exceeded但单个请求正常动态批处理时vLLM按max_model_len截断但不同请求input_length差异大kubectl logs pod | grep exceeded统一客户端max_tokens或在调度层对长请求单独路由5.2 独家避坑技巧来自血泪教训技巧1永远在vLLM启动命令中加--disable-log-stats这个参数默认开启会每秒打印一行统计日志如avg_prompt_throughput12.3 tokens/s。在高并发下这些日志会占满stdout buffer导致vLLM主进程阻塞。我们曾因此出现“服务假死”curl能连上但无响应。关闭后CPU占用下降15%延迟抖动消失。技巧2用nvidia-smi dmon -s u -d 1代替watch -n 1 nvidia-smiwatch命令有200ms左右的刷新延迟且输出格式不固定难以脚本解析。dmon是NVIDIA官方工具-s u只显示GPU Util-d 1每秒采样输出为纯数字流可直接被Prometheus抓取。我们用它构建了GPU Util的实时热力图一眼看出哪台机器过载。技巧3为每个模型创建独立的K8s Namespace不要把所有模型塞进default命名空间。我们为qwen2-7b、llama3-8b、phi3-3.8b各建Namespace并在其中部署专属的Prometheus Exporter。这样做的好处是当llama3-8b出问题时kubectl top pods -n llama3-8b能立刻看到所有相关Pod不会被其他模型的Pod干扰监控告警也能按Namespace精准通知负责人。技巧4在调度层日志中强制注入X-Request-IDEnvoy默认不传递X-Request-ID导致四层日志无法关联。必须在Envoy配置中添加http_filters: - name: envoy.filters.http.router typed_config: type: type.googleapis.com/envoy.extensions.filters.http.router.v3.Router dynamic_stats: true generate_request_id: true # 关键然后在调度层Go代码中读取r.Header.Get(X-Request-ID)并写入日志。这是实现全链路追踪的基石。技巧5定期清理vLLM的/tmp目录vLLM在启动时会解压模型到/tmp/vllm-xxxx但异常退出时不清理。我们遇到过/tmp分区被占满100GB导致新Pod无法启动。解决方案在K8s Deployment的lifecycle.postStart中添加清理命令lifecycle: postStart: exec: command: [/bin/sh, -c, rm -rf /tmp/vllm-*]6. 模型服务治理的终极实践让LLM API像水电一样可靠6.1 可观测性不是“看图”而是“决策依据”我们部署了三套监控看板每一套都对应一个明确的运维动作SLA看板Grafana只显示model_id_p99_latency_ms和model_id_error_rate。阈值红线是硬编码的p99 1000ms且error_rate 0.3%。一旦越线企业微信机器人自动值班SRE并创建Jira工单标题为“[URGENT] model_id SLA breach at”。资源看板Grafana显示container_gpu_utilization、container_memory_usage_bytes、vllm_num_requests_waiting。当vllm_num_requests_waiting 50持续2分钟自动触发kubectl scale deploy vllm-model --replicas3。质量看板自研这是最有价值的部分。我们用LangChain的LLMEvalChain定期每小时对每个模型发起10个标准测试题如“解释量子纠缠”、“写Python冒泡排序”记录output_length、relevance_score用另一个小模型打分、hallucination_flag正则匹配“我不知道”、“无法回答”等。当relevance_score 0.85或hallucination_flag 30%系统自动邮件通知模型负责人“模型 内容质量下降请检查微调数据或提示词”。这个质量看板让我们提前3天发现了Qwen2-7b的一个隐性bug它在回答数学题时对“求导”概念的回答准确率从92%骤降至61%而延迟和错误率毫无变化。人工抽检才发现模型把d/dx x^2错答为x漏了系数2。没有这个看板这个问题可能要等用户投诉才暴露。6.2 安全边界不是“防黑客”而是“防误用”LLM服务最大的安全风险从来不是外部攻击而是内部误用。我们设置了三层防护输入层清洗Envoy的ext_authz过滤器调用一个Python服务对prompt做三重检查长度限制len(prompt) 128000防止DoS敏感词过滤使用AC自动机匹配root password、ssh private key等命中则返回400语法检查用tree-sitter解析JSON/YAML确保结构合法防止注入。输出层脱敏在调度层对response.text做正则替换将匹配[A-Z]{2}\d{8}模拟身份证号、\d{11}手机号的字符串替换为