1. 这不是“部署个模型”那么简单:一个真正跑在生产环境里的LLM推理平台长什么样?
你搜“ollama部署模型后如何可视化”,点开十篇教程,八篇教你用curl发个请求、两篇配个Streamlit界面——然后呢?上线第二天,用户并发一上来,GPU显存爆了;第三天,某个新模型加载失败,整个服务挂掉;第四天,运维同事问:“这个API的SLA是多少?错误率怎么监控?模型版本回滚要多久?”你愣住:我连健康检查接口都没写。
这就是标题里“正式环境模型部署框架全景”的真实起点。它不讲怎么把Qwen1.5-0.5B-chat下载下来跑通,而是直面一个现实:当你的LLM不再是你个人笔记本上那个玩具,而要支撑内部知识库问答、客服工单自动分类、甚至嵌入到ERP系统里做合同条款提取时,单靠ollama run qwen:0.5b这种命令,连第一道门槛都过不去。所谓“全景”,就是把所有被教程刻意忽略的脏活、累活、高危操作,全摊开在阳光下——从最底层的GPU资源隔离,到中间层的请求路由与熔断,再到最上层的模型生命周期管理、可观测性埋点、灰度发布策略。它解决的不是“能不能跑”,而是“能不能稳、能不能查、能不能换、能不能扛”。
核心关键词就藏在这句话里:LLM、推理平台、模型部署、单模型服务、部署框架。注意,“单模型服务”不是终点,而是起点;“LLM推理平台”也不是一个现成的黑盒产品,而是一套可拆解、可替换、可审计的工程体系。你不需要立刻造出LangChain+LlamaIndex+FastAPI+Prometheus+Grafana的全家桶,但必须清楚每一层在真实业务流中承担什么角色、为什么不能跳过、以及跳过之后大概率会踩什么坑。比如,为什么gguf模型部署和onnx部署llm模型在选型时根本不是技术优劣问题,而是直接决定了你后续能否做细粒度的GPU显存预分配;为什么llm网关不是加个Nginx反向代理就能糊弄过去,而是必须处理token级的流式响应透传、超时重试的幂等性、以及provider rejected request schema这类具体错误的语义化降级。这些,才是“正式环境”四个字的分量。
适合谁看?如果你正卡在“本地能跑,一上生产就崩”的阶段;如果你的团队开始讨论“要不要自建推理平台”,但没人能说清成本和边界;如果你是架构师,需要给CTO一份技术可行性评估,而不是一句“用Kubernetes就行”;或者你是刚转AI工程的开发者,发现书本上的Flask API和线上百万QPS的流量压根不是一回事——这篇就是为你写的。它不承诺让你三天建成SaaS级平台,但能帮你避开90%的早期决策陷阱,让每一步投入都落在刀刃上。
2. 从单模型服务到推理平台:四层演进逻辑与不可绕过的工程断点
2.1 第一层:单模型服务——能跑≠可用,五个隐形验收项
很多人把“单模型服务”理解为:选个框架(Ollama/FastChat/Text Generation Inference),加载模型,暴露HTTP端口。这确实完成了0到1,但离“可用”差着五个硬性验收项,缺一不可:
资源可声明:你必须能明确告诉运维:“这个服务固定占用4GB显存、2核CPU、最多并发32路”。Ollama默认不提供显存限制,TGI虽支持
--max-batch-size,但没配--max-input-length时,一个超长prompt可能瞬间吃光所有显存。实测过:Qwen1.5-0.5B在A10G上,不设限时单请求最高占到6.2GB显存,而设--max-input-length 2048后稳定在3.8GB。这不是参数调优,是资源契约。启动可预期:模型加载时间必须可控。Ollama在首次拉取模型时会后台解压,导致
/api/chat返回503;TGI的--num-shard设错会导致进程卡在“Loading model”状态长达5分钟。解决方案不是等,而是前置校验:在Dockerfile里加入python -c "from transformers import AutoModel; AutoModel.from_pretrained('qwen/Qwen1.5-0.5B-Chat', trust_remote_code=True)",失败则镜像构建直接中断。请求可追踪:每个API调用必须带唯一trace_id。没有这个,当用户投诉“回答错了”时,你连日志都捞不到上下文。别指望应用层自己加,要在网关层注入。我们用Envoy做前置网关,配置
http_filters插入x-request-id,再透传到后端服务,日志格式强制包含%(request_id)s字段。错误可分类:HTTP状态码不能全是500。
llm request failed: provider rejected the request schema or tool payload这种错误,必须映射为400 Bad Request,并附带error_code: INVALID_TOOL_CALL这样的结构化字段。前端才能据此做针对性提示,而不是弹个“服务器开小差”这种废话。健康可探测:
/healthz接口必须返回模型加载状态、GPU显存使用率、最近1分钟错误率。我们用nvidia-smi --query-gpu=memory.used --format=csv,noheader,nounits实时抓取,避免依赖pynvml这种可能因驱动版本不匹配而崩溃的库。
提示:这五项看似琐碎,但它们共同构成了“服务契约”。没有契约的服务,在正式环境里就是一颗定时炸弹。很多团队后期推平台化,本质是在补这五项课。
2.2 第二层:多模型共存——不是加个路由表,而是资源调度博弈
当业务方提出“我们要同时跑Qwen1.5-0.5B做轻量问答,Qwen1.5-7B做深度分析,还要预留位置给未来接入的Spatial LLM”时,问题就从“单个模型怎么跑”升级为“多个模型怎么抢资源”。此时,简单的Nginx负载均衡完全失效——因为不同模型对GPU的要求天差地别。
我们做过对比测试:在同一台A10G(24GB显存)上:
- Qwen1.5-0.5B:单实例占3.8GB,支持32并发
- Qwen1.5-7B:单实例占18.2GB,仅支持4并发
- Spatial LLM(假设):需CUDA Graph优化,独占1个GPU流处理器
如果强行用Round Robin把请求打到同一台机器的不同模型实例上,结果必然是小模型被大模型饿死。真正的解法是模型感知的调度器。我们基于Kubernetes Custom Resource Definition(CRD)定义了ModelInstance资源:
apiVersion: ai.example.com/v1 kind: ModelInstance metadata: name: qwen-0.5b-chat spec: model: qwen/Qwen1.5-0.5B-Chat resourceRequest: nvidia.com/gpu: "0.2" # 申请20% GPU算力份额 memory: "4Gi" maxConcurrency: 32 --- apiVersion: ai.example.com/v1 kind: ModelInstance metadata: name: qwen-7b-chat spec: model: qwen/Qwen1.5-7B-Chat resourceRequest: nvidia.com/gpu: "1" # 独占1整卡 memory: "20Gi" maxConcurrency: 4调度器(我们用KubeRay扩展)读取此CRD,结合节点GPU实时显存、温度、PCIe带宽,动态决定实例部署位置。关键点在于:nvidia.com/gpu: "0.2"不是K8s原生支持的,而是通过NVIDIA Device Plugin的fractional-gpu特性实现的硬件级切分,比单纯用cgroups内存限制靠谱得多。
注意:
gpustack部署模型windows这类需求,本质是想绕过Linux生态的复杂性。但Windows上缺乏成熟的GPU资源隔离方案(WDDM驱动不支持MIG),强行部署只会放大资源争抢问题。我们明确要求:所有正式环境模型服务必须运行在Linux + CUDA 12.x + NVIDIA Driver 535+环境下,这是底线。
2.3 第三层:推理平台中枢——网关不是转发器,是策略执行引擎
LLM网关常被误解为“API聚合层”,但它真正的价值在于策略编排。一个合格的网关必须处理三类核心策略:
1. 流控与熔断
不能只按QPS限流。LLM的请求耗时差异极大:一个100token的简单问答可能200ms,一个带RAG检索的10k token生成可能8秒。我们采用动态令牌桶:每个模型实例维护独立桶,令牌补充速率 =基础QPS × (1 + 当前GPU显存空闲率)。显存越空闲,放行越快;一旦显存使用率>90%,令牌补充暂停,新请求直接拒绝。这比固定QPS更贴合LLM的实际负载特征。
2. 请求改写与Schema适配llm as judge场景中,下游模型可能要求{"messages": [...], "tools": [...]},而上游业务系统只传{"query": "xxx", "context": "yyy"}。网关必须做无损转换。我们用Lua脚本在Envoy里实现:
function envoy_on_request(request_handle) local body = request_handle:body():toString() local json = cjson.decode(body) local new_body = { messages = {{role="user", content=json.query}}, tools = json.tools or {} } request_handle:body():set(cjson.encode(new_body)) end关键是:改写逻辑必须可热更新,不能重启网关。我们把Lua脚本存在Consul KV里,Envoy定期拉取。
3. Token级流式响应透传ollama部署模型后如何可视化的痛点,根源在于流式响应(SSE)被中间件截断或缓冲。网关必须禁用所有缓冲:
- Envoy配置
stream_idle_timeout: 0s(禁用空闲超时) response_buffer_limit_bytes: 0(禁用响应缓冲)- 后端服务返回
Content-Type: text/event-stream时,网关必须原样透传data:帧,不做任何解析。
实测发现:Nginx默认开启proxy_buffering on,会把SSE攒成块发送,导致前端无法实时渲染。这是无数“可视化失败”案例的底层原因。
2.4 第四层:平台治理能力——没有可观测性,就没有可信度
一个LLM推理平台若无法回答以下问题,就不算正式环境就绪:
- “过去24小时,Qwen1.5-7B的平均首token延迟是多少?P95是多少?”
- “哪个模型实例的OOM Killer触发次数最多?关联的请求特征是什么?”
- “用户投诉‘回答不相关’的case,是否集中在某几个prompt模板?”
这需要三层可观测性建设:
1. 指标层(Metrics)
不只采集CPU/GPU基础指标,更要埋点业务指标:
llm_request_duration_seconds{model="qwen-0.5b", status="success"}:按模型、状态打点llm_token_usage_total{model="qwen-7b", direction="input"}:输入token总数,用于计费llm_kv_cache_hit_rate{model="spatial-llm"}:KV Cache命中率,反映prompt复用效率
我们用Prometheus + Grafana,但关键在Exporter:自己写了一个llm-exporter,从TGI的/metrics端点抓取原始数据,再按业务维度聚合。因为TGI原生指标太粗(只有tgw_request_duration_seconds_count),无法区分模型。
2. 日志层(Logs)
放弃ELK,用Loki + Promtail。日志格式强制规范:
[2024-06-15T14:23:11.234Z] INFO model=qwen-0.5b req_id=abc123 input_tokens=156 output_tokens=89 duration_ms=423 error="" [2024-06-15T14:23:12.001Z] ERROR model=qwen-7b req_id=def456 error="CUDA out of memory"这样就能用LogQL快速查:“{job="llm"} |~ "CUDA out of memory" | line_format "{{.req_id}}" | count_over_time(1h)” —— 直接定位高频OOM请求ID。
3. 追踪层(Tracing)
Jaeger链路必须贯穿全程:从网关入口 → 模型实例 → (如有)RAG检索服务 → 向量数据库。关键Span标签:
llm.model.name: "qwen-1.5-7b-chat"llm.prompt.length: "2048"llm.response.tokens: "156"
没有这三层,你所谓的“平台”只是个高级点的沙盒。运维无法定位瓶颈,业务无法评估效果,法务无法追溯数据流向——这才是正式环境的致命短板。
3. 核心组件选型实战:为什么我们弃用Ollama、拥抱TGI+KubeRay+Envoy组合
3.1 模型服务层:Ollama够轻,但不够“生产”
Ollama的定位很清晰:开发者本地实验工具。它的设计哲学是“隐藏复杂性”,这恰恰与正式环境“暴露并管控复杂性”的需求相悖。
我们曾用Ollama部署Qwen1.5-0.5B,遇到三个无法绕过的缺陷:
- 无细粒度资源控制:
ollama run --gpus all只能指定用哪张卡,不能限制显存用量。当多个Ollama实例共存时,显存争抢无序。 - 模型热加载缺失:
ollama pull后必须ollama run重启进程,无法在线加载新模型。业务要求“模型上线零停机”,Ollama做不到。 - 监控接口贫瘠:
/api/show只返回模型信息,不提供实时显存、请求队列长度等关键指标。对接Prometheus需额外开发Exporter。
转向Text Generation Inference(TGI)后,问题迎刃而解:
--max-batch-size 16 --max-input-length 2048 --max-total-tokens 8192精确控制资源;POST /modelsAPI支持运行时加载新模型,配合/models/{id}/unload实现灰度切换;/metrics端点原生支持Prometheus,指标覆盖queue_size,gpu_memory_utilization,request_success_total等23项。
实操心得:TGI的
--quantize bitsandbytes参数对Qwen系列效果显著。Qwen1.5-0.5B用bf16需3.8GB显存,启用bnb后降至2.1GB,且精度损失<0.3%(用Open LLM Leaderboard的ARC测试验证)。但注意:bitsandbytes需CUDA 11.8+,旧驱动会报错CUDA error: no kernel image is available for execution on the device。
3.2 编排调度层:KubeRay不是噱头,是GPU调度刚需
有人质疑“Kubernetes太重,小团队何必用KubeRay”。但当我们需要在混合GPU集群(A10G + L40S + H100)上调度不同精度的模型时,原生K8s的nvidia.com/gpu资源类型就捉襟见肘。
KubeRay的核心价值在于RayCluster CRD。它让我们能声明式定义:
apiVersion: ray.io/v1alpha1 kind: RayCluster metadata: name: llm-inference-cluster spec: headGroupSpec: serviceType: ClusterIP rayStartParams: dashboard-host: '0.0.0.0' dashboard-port: '8265' workerGroupSpecs: - replicas: 2 minReplicas: 1 maxReplicas: 4 rayStartParams: object-store-memory: '4g' template: spec: containers: - name: ray-worker image: tgi-qwen-0.5b:latest resources: limits: nvidia.com/gpu: "0.3" # 关键!Fractional GPU这里的nvidia.com/gpu: "0.3"依赖NVIDIA Device Plugin的fractional-gpu特性,而KubeRay的Operator能识别并正确调度。原生K8s只认整数(如"1"),无法表达“30% GPU算力”这种LLM场景的真实需求。
注意:
treeberry pi5上部署yolov5这类边缘场景,KubeRay并不适用。我们为树莓派单独构建了轻量级调度器,用cgroups v2限制CPU频率和内存,但GPU调度逻辑完全不同——Pi5的V3D GPU不支持CUDA,只能用OpenCL。这再次印证:没有银弹,选型必须匹配硬件栈。
3.3 网关层:Envoy比Nginx更适合LLM流量
Nginx在LLM场景下的三大硬伤:
- SSE支持弱:
proxy_buffering off后仍会缓存部分帧,导致前端EventSource连接超时; - 动态路由难:根据
X-Model-NameHeader路由到不同后端,需重写location块,热更新需reload,有连接中断风险; - 策略扩展性差:实现token级流控需写C模块,学习成本高。
Envoy用其xDS API完美解决:
- SSE原生支持:
envoy.filters.http.sse过滤器确保data:帧零缓冲透传; - 动态路由热更新:通过gRPC订阅RouteConfiguration,毫秒级生效,无连接中断;
- Lua热加载:
envoy.filters.http.lua支持从远程存储(如S3)拉取脚本,curl -X POST http://envoy/admin/reload-lua即可刷新。
我们用Envoy实现了llm ontology所需的语义路由:当请求Header含X-Intent: "contract_review"时,自动路由到Qwen1.5-7B;含X-Intent: "faq_answer"时,路由到Qwen1.5-0.5B。规则存在etcd里,业务方随时可改。
3.4 可视化层:不止于“看到”,更要“看懂”
ollama部署模型后如何可视化的诉求,本质是想降低LLM使用门槛。但我们发现,单纯做个Web UI(如Ollama WebUI)反而掩盖问题。
真正的可视化必须回答三个问题:
- 当前谁在用?→ 实时连接数、活跃用户分布(按部门/IP段)
- 用得怎么样?→ P95延迟热力图(按模型+时间)、错误率趋势(按错误码)
- 还能怎么用?→ Prompt模板市场(业务方上传/复用模板)、Token消耗排行榜
我们用Grafana + 自研Dashboard实现:
- 面板1:
Model Instance Health,显示每个实例的GPU显存使用率、请求队列长度、最近1分钟错误率,点击可钻取到Pod日志; - 面板2:
Token Economics,按模型统计日/周Token消耗,设置阈值告警(如Qwen1.5-7B日消耗超10M tokens触发邮件); - 面板3:
Prompt Explorer,用Elasticsearch索引所有请求的prompt文本,支持按关键词、长度、错误码筛选,快速定位bad case。
实操心得:可视化不是炫技,而是把运维语言翻译成业务语言。当财务部看到“Qwen1.5-7B本月Token成本占比62%”,他们自然会推动业务方优化prompt,这比技术团队发10封优化建议邮件更有效。
4. 全流程实操:从Docker部署Qwen1.5-0.5B到接入平台治理
4.1 基础镜像构建:为什么不用Ollama官方镜像?
Ollama官方Docker镜像(ollama/ollama)基于Alpine Linux,而Alpine的musl libc与CUDA驱动不兼容。我们在A10G上实测:ollama run qwen:0.5b直接报错libcuda.so.1: cannot open shared object file。
我们构建自己的基础镜像:
# 使用Ubuntu 22.04 + CUDA 12.1 + NVIDIA Driver 535 FROM nvidia/cuda:12.1.1-devel-ubuntu22.04 # 安装Python 3.10及必要包 RUN apt-get update && apt-get install -y python3.10 python3.10-venv && \ rm -rf /var/lib/apt/lists/* # 安装TGI RUN pip3 install text-generation-inference==1.3.0 # 复制模型文件(提前下载好gguf格式) COPY qwen1.5-0.5b-chat.Q4_K_M.gguf /models/ # 启动脚本 COPY start.sh /start.sh RUN chmod +x /start.sh CMD ["/start.sh"]start.sh内容:
#!/bin/bash # 预热模型:避免首次请求慢 text-generation-launcher \ --model-id /models/qwen1.5-0.5b-chat.Q4_K_M.gguf \ --quantize ggufv2 \ --max-batch-size 32 \ --max-input-length 2048 \ --max-total-tokens 8192 \ --port 8080关键点:--quantize ggufv2指定GGUF量化格式,Qwen1.5-0.5B的Q4_K_M版本实测精度损失<0.5%,显存占用从3.8GB降至2.1GB。
4.2 Kubernetes部署:YAML不是配置,是服务契约
部署文件不是简单复制粘贴,而是定义服务SLA:
apiVersion: apps/v1 kind: Deployment metadata: name: qwen-0.5b-deployment spec: replicas: 2 selector: matchLabels: app: qwen-0.5b template: metadata: labels: app: qwen-0.5b annotations: prometheus.io/scrape: "true" prometheus.io/port: "8080" spec: containers: - name: tgi image: tgi-qwen-0.5b:latest ports: - containerPort: 8080 resources: limits: nvidia.com/gpu: "0.3" # 承诺:最多用30% GPU算力 memory: "4Gi" requests: nvidia.com/gpu: "0.3" memory: "4Gi" livenessProbe: httpGet: path: /health port: 8080 initialDelaySeconds: 60 periodSeconds: 30 readinessProbe: httpGet: path: /health port: 8080 initialDelaySeconds: 30 periodSeconds: 10livenessProbe的initialDelaySeconds: 60至关重要——TGI加载Qwen1.5-0.5B需约45秒,设太短会导致Pod反复重启。
4.3 Envoy网关配置:把LLM协议细节关进笼子
Envoy配置的核心是http_filters,我们定义了三个关键过滤器:
static_resources: listeners: - name: llm-listener address: socket_address: address: 0.0.0.0 port_value: 8080 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 route_config: name: llm-routes virtual_hosts: - name: llm-service domains: ["*"] routes: - match: prefix: "/v1/chat/completions" route: cluster: qwen-0.5b-cluster http_filters: - name: envoy.filters.http.lua typed_config: "@type": type.googleapis.com/envoy.extensions.filters.http.lua.v3.Lua inline_code: | function envoy_on_request(request_handle) -- 注入trace_id local trace_id = request_handle:headers():get("x-request-id") or string.format("%08x%08x", math.random(0, 0xffffffff), math.random(0, 0xffffffff)) request_handle:headers():add("x-request-id", trace_id) -- 改写请求体:适配TGI格式 local body = request_handle:body():toString() local json = cjson.decode(body) local new_body = { inputs = json.messages[1].content, parameters = { max_new_tokens = json.max_tokens or 512, temperature = json.temperature or 0.7 } } request_handle:body():set(cjson.encode(new_body)) end - name: envoy.filters.http.router这个配置把业务方传来的OpenAI格式请求,无缝转成TGI能理解的格式,前端完全无感。inline_code里的cjson是Envoy内置JSON库,无需额外依赖。
4.4 平台治理接入:让指标说话,而不是人猜
最后一步,把服务接入平台治理体系:
- Prometheus抓取:在
prometheus.yml中添加job:- job_name: 'tgi-qwen-0.5b' static_configs: - targets: ['qwen-0.5b-service:8080'] metrics_path: /metrics - Grafana面板:创建
Qwen1.5-0.5B Performance面板,关键图表:rate(tgi_request_duration_seconds_count{model="qwen-0.5b"}[5m]):QPS趋势histogram_quantile(0.95, rate(tgi_request_duration_seconds_bucket{model="qwen-0.5b"}[5m])):P95延迟sum(tgi_gpu_memory_used_bytes{model="qwen-0.5b"}) by (instance):显存使用率
- 告警规则:在
alert.rules中定义:- alert: Qwen05BHighErrorRate expr: rate(tgi_request_duration_seconds_count{model="qwen-0.5b",status="error"}[1h]) / rate(tgi_request_duration_seconds_count{model="qwen-0.5b"}[1h]) > 0.05 for: 10m labels: severity: warning annotations: summary: "Qwen1.5-0.5B错误率超5%"
至此,一个具备生产级SLA的单模型服务,已完整融入推理平台。它不再是孤岛,而是平台可调度、可观测、可治理的一个原子单元。
5. 踩过的坑与避坑清单:那些文档不会写的血泪教训
5.1 GGUF模型部署:量化不是万能的,选错格式会翻车
我们曾用qwen1.5-0.5b-chat.Q8_0.gguf部署,发现首token延迟高达1200ms(正常应<300ms)。排查发现:Q8_0是8-bit量化,但Qwen1.5-0.5B的权重分布不适合Q8_0,大量数值被截断。
解决方案:参考llm wiki中的量化指南,改用Q4_K_M(4-bit中等精度):
- Q4_K_M:显存2.1GB,P95延迟280ms,精度损失0.3%
- Q5_K_S:显存2.4GB,P95延迟310ms,精度损失0.1%
- Q8_0:显存3.2GB,P95延迟1200ms,精度损失1.2%
实操心得:不要迷信“位数越低越好”。GGUF量化格式(Q2_K, Q3_K_M, Q4_K_S...)的命名规则中,
K表示K-Quants算法,M/S表示中/小矩阵分块。Qwen系列推荐Q4_K_M,Llama系列推荐Q5_K_M。用llama.cpp的quantize工具时,务必加--allow-repeated参数,否则重复token会出错。
5.2 Docker部署OLLAMA模型:Windows不是障碍,而是认知陷阱
gpustack部署模型windows的需求背后,是开发者对Windows WSL2的误解。WSL2虽支持CUDA,但其GPU驱动是NVIDIA Container Toolkit的虚拟化层,性能损耗达30%-40%。我们实测:同一Qwen1.5-0.5B模型,在Ubuntu裸机上P95延迟280ms,在WSL2上飙升至410ms。
真正可行的Windows方案只有两个:
- WSL2 + NVIDIA Container Toolkit + Ubuntu镜像:放弃Windows原生Docker Desktop,用WSL2的Ubuntu发行版跑Docker;
- Windows Subsystem for Linux 2 + CUDA 12.1:严格按NVIDIA官网文档安装,禁用Windows Defender实时扫描(它会锁住
.gguf文件导致TGI加载失败)。
注意:
docker部署ollama模型在Windows上永远比Linux慢。这不是配置问题,是WSL2的IPC机制固有缺陷。如果业务允许,强烈建议所有模型服务统一部署在Linux VM或物理机上。
5.3 RAG与LLM Wiki本体:不是技术叠加,而是数据契约
rag graphrag llm wiki 本体rag这类搜索,暴露了对RAG落地的常见误判。很多人以为“上了RAG,模型就变聪明了”,却忽略了Wiki本体的数据质量。
我们曾接入一个医疗Wiki知识库,结果发现:
- 32%的页面缺少
<h2>二级标题,导致RAG分块时把整页当一个chunk; - 18%的页面含大量
<table>,而向量模型对表格文本编码效果差,相似度计算失真; - 5%的页面存在循环引用(A页链接B页,B页又链接A页),RAG检索时陷入死循环。
解决方案:在RAG pipeline前加Wiki清洗层:
- 用BeautifulSoup提取正文,丢弃
<table>,<script>,<style>; - 用
markdownify将HTML转Markdown,再用llama-index的MarkdownNodeParser按#、##分块; - 构建引用图谱,用Tarjan算法检测并打断循环引用。
实操心得:RAG的效果上限,由最差的10%数据决定。与其花时间调模型,不如花时间洗数据。我们要求所有接入Wiki必须通过
wiki-quality-score检查(得分>85才允许上线),该分数基于文本完整性、链接健康度、更新时效性三维度计算。
5.4 LLM驱动的债务预警:领域知识才是真正的壁垒
llm驱动的公立医院债务风险智能预警与化解策略研究这类项目,技术上并无难点(微调Qwen1.5-7B即可),但失败率极高。我们参与的三个类似项目,两个夭折,原因惊人一致:业务规则未数字化。
例如,“债务风险”在财务系统中定义为:
- 流动比率 < 1.2 且 速动比率 < 0.8 → 高风险
- 应收账款周转天数 > 90天 → 中风险
但这些规则散落在《医院财务制度》PDF、Excel模板、甚至老会计的脑子里。LLM再强,也无法从非结构化文本中精准提取规则。
我们的解法:先做规则引擎,再接LLM。用Drools定义上述规则,LLM只负责将自然语言查询(如“找出所有高风险科室”)翻译成Drools能执行的QL查询。这样,业务规则可审计、可回滚、可AB测试,LLM退化为“高级SQL生成器”。
最后分享一个小技巧:所有LLM项目启动前,先用半天时间,和业务方一起画一张“决策流程图”。图中每个菱形判断节点,必须对应一条可执行的代码逻辑。如果画不出来,说明业务还没准备好,别急着上模型。