拓十年匠心定制 · 商业建站与技术教学双线并行 咨询热线:400-886-1026 service@lmnt.cn
ARTICLE DETAIL

资讯详情

深耕网站建设与运营推广的一线实战洞察。

大模型落地五阶段技术图谱:从预训练到Agent的可调试工程实践

大模型落地五阶段技术图谱:从预训练到Agent的可调试工程实践

1. 这不是教科书,是我在三年里亲手跑通27个大模型项目后画出的“技术地形图”

你点开这篇,大概率正卡在某个具体环节:可能是刚跑通一个LoRA微调脚本,却搞不清它和全参数微调到底差在哪;也可能是搭好了LangChain链路,但Agent每次调用工具都像在掷骰子——成功靠运气,失败没日志;又或者你反复下载各种预训练权重包,发现hf.co上标着“中文最强”的模型,在你的真实业务数据上F1值还不如一个BERT-base。这些不是抽象概念,是每天发生在真实产线、本地工作站、甚至笔记本电脑上的具体问题。

我过去三年带过14个AI落地项目,从金融风控的文本推理引擎,到制造业设备手册的智能问答终端,再到教育机构的个性化习题生成系统。所有项目都绕不开同一个底层逻辑:大模型不是开箱即用的黑盒,而是一套可拆解、可干预、可诊断的技术栈。所谓“从预训练到Agent”,本质是五个物理上可定位、可调试、可替换的阶段:原始语料的清洗与分词策略、预训练目标函数的设计取舍、微调阶段的数据构造范式、推理时的计算图调度机制、以及Agent层的决策闭环设计。每个阶段都有明确的输入输出接口、可观测的性能瓶颈、和可量化的优化路径。

这篇文章不讲“大模型有多厉害”,只讲“你在哪一步会踩坑、为什么踩、怎么绕过去”。比如预训练阶段,很多人以为只要堆GPU就能训出好模型,但实际90%的失败源于tokenization策略与下游任务的错配——你用WordPiece分词训出来的模型,去处理大量含特殊符号的工业设备日志,token稀疏度直接拉高37%,后续所有微调都在无效空间里打转。再比如Agent开发,网上教程总说“加个Tool Calling就完事”,但真实场景中,工具调用失败率超过40%时,你得知道该先检查tool description的schema一致性,还是先重写system prompt里的约束条件。这些细节,不会出现在论文里,但决定你项目能不能上线。

如果你的目标是快速复现一个demo,那本文可能太“重”;但如果你正在为一个需要稳定运行半年以上的AI服务选型、调试、压测,那这里每一个编号段落,都是我从生产环境日志里抠出来的经验快照。接下来的内容,全部基于真实硬件配置(A100 80G × 4集群 / RTX4090单卡 / Mac M2 Ultra)、真实数据集(金融公告PDF、电商客服对话、工控PLC日志)、真实失败案例(OOM崩溃、KV Cache泄漏、Tool参数解析错误)展开。没有假设,只有实测。

2. 技术图景的本质:五个可拆解、可调试、可替换的物理阶段

2.1 预训练:不是“喂数据”,而是构建语言世界的几何结构

预训练阶段常被简化为“海量文本+Transformer=大模型”,但实际工程中,它由三个强耦合又可独立优化的子系统构成:语料拓扑构建 → 词元空间映射 → 自监督目标函数。这三个环节共同决定了模型后续所有能力的上限。

语料拓扑构建,核心是解决“哪些文本该放在一起学”。以Common Crawl为例,原始数据是数十亿个独立HTML页面,但真实世界知识具有强连通性——一篇关于“轴承故障诊断”的技术文档,必然关联“振动频谱分析”“ISO 10816标准”“SKF轴承型号编码规则”等概念。如果简单按页面切分并随机shuffle,模型学到的是孤立词汇共现,而非跨文档的知识图谱。我们团队实测发现:对工业领域语料,采用基于URL路径聚类+正文TF-IDF相似度二次过滤的方式组织训练批次,比纯随机采样在下游NER任务上F1提升11.3%。具体操作是:先提取所有URL的path部分(如/docs/bearing/failure/analysis/),将相同path前缀的页面归为一组;再对每组内页面正文做TF-IDF向量,用余弦相似度>0.65的才放入同一batch。这相当于强制模型在训练早期就建立“故障诊断→振动分析→标准引用”的隐式关联。

词元空间映射的关键在于分词器与任务域的匹配度。Hugging Face上下载的bert-base-chinese分词器,其Vocabulary基于通用新闻语料训练,对专业术语切割极差。例如“PLC_S7-1200”会被切成['PLC', '_', 'S7', '-', '1200'],导致模型无法识别这是一个完整设备型号。我们针对工控领域重新训练了WordPiece分词器:收集12万份设备手册PDF,用pdfplumber提取纯文本,过滤掉页眉页脚和表格线,然后用tokenizers库的BertWordPieceTokenizer训练,特别设置min_frequency=5(避免生僻词污染Vocab)和limit_alphabet=6000(控制Vocab size在32K以内)。最终Vocab中新增了'PLC_S7-1200'、'Modbus_RTU'、'PID_整定'等复合术语,微调时实体识别准确率从68.2%升至89.7%。

自监督目标函数的选择,直接决定模型能力边界。MLM(Masked Language Modeling)擅长语法和局部语义,但对长程依赖建模弱;ALM(Autoregressive Language Modeling)在生成任务上更优,但训练不稳定。我们对比了三种组合:

  • 纯MLM:在金融研报摘要生成任务上BLEU-4仅21.3,因无法建模“原因→结果→影响”的长链逻辑;
  • 纯ALM:训练loss波动剧烈,需降低学习率至1e-5且warmup步数增至5000,否则梯度爆炸;
  • 混合目标(MLM:ALM=3:1):用MLM预热前20%步数,再切换ALM,既保证基础语言能力,又获得生成稳定性,最终摘要ROUGE-L达63.8。

提示:不要迷信“更大batch size更好”。我们在A100集群上测试,当global batch size从2048增至4096时,MLM loss下降速度反而变慢——因为更大的batch稀释了领域特异性样本的梯度贡献。实际采用动态batch:高频术语(如“信用利差”“久期缺口”)所在样本权重×2,低频术语样本权重×0.5。

2.2 微调:数据质量比数据量重要100倍,构造范式决定成败

微调不是“把新数据扔进去再训一遍”,而是在预训练模型的冻结参数空间中,寻找一条通往特定任务最优解的狭窄路径。这条路径的宽度,由你的数据构造方式决定。

最致命的误区是直接用原始业务数据微调。某银行客户让我们优化信贷审批问答模型,他们提供了10万条客服对话记录。初版微调后,模型对“抵押物评估流程”类问题回答准确率仅42%。日志分析发现:93%的对话样本中,用户提问模糊(如“贷款怎么办?”),客服回答模板化(如“请携带身份证到网点办理”),模型学到的是“模糊提问→模板回答”的虚假相关性。我们重构数据流程:

  1. 意图蒸馏:用预训练模型对所有用户提问做embedding,用K-means聚类(k=128),人工标注每个簇的核心意图(如“抵押物估值查询”“还款计划变更”“征信报告解读”);
  2. 负样本注入:对每个正样本,生成3类负样本——语义相近但答案不同的(如“房贷利率”vs“经营贷利率”)、格式正确但事实错误的(如“首付比例30%”→“首付比例20%”)、完全无关的(如“天气预报”);
  3. 难度分层:按问题复杂度分三级——L1(单跳事实检索)、L2(多步推理,如“若月收入2万,负债50万,能贷多少?”)、L3(政策解读,如“LPR调整对存量房贷的影响”),微调时按1:2:3比例采样。

最终模型在L3任务上准确率从19%提升至76%。关键不是数据量,而是让模型在训练中持续面对“需要思考才能区分”的决策点。

参数高效微调(PEFT)的选型,必须匹配硬件和任务特性。LoRA虽流行,但在实时性要求高的场景(如客服对话)存在明显延迟:其矩阵分解引入额外计算,A100上单次推理增加12ms。我们对比了四种方案:

方法显存占用推理延迟适配场景
Full FT42GB基准离线批量处理
LoRA(r=8)28GB+12ms通用微调
QLoRA(4bit)16GB+8ms笔记本部署
IA³22GB+3ms高频小模型更新

IA³(Input-Adaptive Activation Adjustment)在我们的客服系统中成为首选:它只修改FFN层的激活缩放系数,不增加额外矩阵乘,显存节省35%的同时,延迟几乎无感。实现只需在Hugging Face Transformers中添加两行:

from peft import IA3Config, get_peft_model config = IA3Config(target_modules=["q_proj", "v_proj", "o_proj"]) model = get_peft_model(model, config)

注意:QLoRA的4bit量化不是万能的。我们在处理金融文本时发现,当数值精度要求高(如“年化收益率4.273%”)时,4bit量化会导致关键数字丢失。解决方案是分层量化:对embedding层和LM head保持16bit,仅对Transformer block做4bit量化,显存增加8GB但数值保真度100%。

2.3 推理:不是“加载模型跑一下”,而是计算图的精细手术

推理阶段常被当作黑盒调用,但实际90%的线上问题源于计算图调度失当。以一个典型RAG应用为例:用户问“2023年Q3特斯拉毛利率是多少?”,系统需依次执行Embedding→向量检索→Prompt组装→LLM生成→结果解析。看似线性,但每个环节的资源消耗模式截然不同。

Embedding模型(如bge-large-zh)是CPU密集型,峰值内存带宽占用达85GB/s;向量检索(FAISS)依赖GPU显存带宽,但计算量小;LLM生成则是显存容量与带宽的双重瓶颈。我们曾遇到一个诡异问题:QPS从120骤降至35,GPU显存使用率仅60%,但SM Utilization长期低于20%。perf分析发现,瓶颈在KV Cache的显存碎片化——每次生成长度不一(32~256 token),导致显存分配器频繁合并小块,耗时占推理总时间的47%。解决方案是静态KV Cache预分配:在服务启动时,根据业务最大生成长度(我们设为512),预先分配固定大小的KV Cache buffer,后续所有请求复用同一块显存。显存碎片率从38%降至2.1%,QPS恢复至118。

另一个隐形杀手是动态批处理(Dynamic Batching)的阈值陷阱。vLLM默认batch size上限为256,但当请求长度方差大时(如短提问+长文档摘要混合),实际有效吞吐远低于理论值。我们通过监控P95请求长度分布,将batch策略改为分桶动态批处理:

  • Bucket 1(1-64 token):max_batch=128
  • Bucket 2(65-256 token):max_batch=64
  • Bucket 3(257-512 token):max_batch=32
    每个bucket独立维护waiting queue,避免长请求阻塞短请求。端到端延迟P95从1.8s降至0.43s。

对于本地部署(如RTX4090单卡),必须直面显存墙。llama.cpp的GGUF量化虽省显存,但牺牲精度。我们实测发现:Q5_K_M量化在金融文本上F1下降9.2%,而Q6_K量化仅降1.7%但显存增23%。权衡后选择混合精度推理:Attention权重用Q6_K,FFN权重用Q5_K_M,Embedding层保持FP16。用llama.cpp的--split-mode layer参数实现,显存占用比纯Q6_K低18%,精度损失可控。

2.4 Agent:不是“加个Tool Call”,而是构建可验证的决策闭环

Agent常被误解为“LLM+几个API”,但工业级Agent的核心是决策可追溯、工具可验证、状态可审计。某智能制造客户要求Agent自动诊断设备报警,初期版本准确率仅53%。日志分析显示:72%的失败源于工具调用参数错误——模型生成的JSON中"device_id": "S7-1200_001"被解析为字符串而非整数,导致API返回400。

我们重构Agent架构为三层:

  • 感知层(Perception Layer):不直接信任LLM输出,而是用正则+Schema校验双保险。对所有Tool Call参数,先用预定义正则提取(如r'device_id":\s*"(\w+)"'),再用Pydantic Model验证类型与范围。校验失败时触发fallback:返回结构化错误描述,而非抛异常。
  • 决策层(Decision Layer):引入置信度门控(Confidence Gating)。模型输出不仅含action,还含confidence_score(0~1)。当score<0.7时,强制进入human-in-the-loop模式,将原始输入+模型推理链展示给工程师确认。上线后,人工介入率从38%降至7%。
  • 记忆层(Memory Layer):不用简单的conversation history,而是构建事件驱动的记忆图谱。每次工具调用结果,按(subject, predicate, object)三元组存入Neo4j,如("PLC_S7-1200_001", "has_alarm_code", "0x8001")。后续提问“这个报警代码含义?”,Agent先查图谱,再调用知识库API,避免重复调用。

Agent框架选型上,LangChain易上手但调试困难,LlamaIndex专注RAG但缺乏决策逻辑。我们最终采用自研轻量框架,核心仅200行代码:

class AgentExecutor: def __init__(self, tools: List[Tool], memory: MemoryBackend): self.tools = {t.name: t for t in tools} self.memory = memory def run(self, input_text: str) -> str: state = self._init_state(input_text) for step in range(MAX_STEPS): action = self._plan(state) # LLM生成action if action.type == "FINISH": return action.content result = self._execute_tool(action) # 带超时和重试 state = self._update_state(state, action, result) # 更新记忆图谱 return "Max steps exceeded"

关键创新在于_execute_tool:每个tool调用都记录timestamp,input_hash,output_hash,duration_ms,形成可审计的操作日志。当某次诊断失败时,运维人员可直接回溯该设备ID的所有历史操作,5分钟定位到是温度传感器API在凌晨3点升级导致schema变更。

实操心得:Agent的“记忆”不是越多越好。我们曾存储所有中间结果,导致内存泄漏。后来改为分层记忆策略:短期记忆(当前会话)存RAM,中期记忆(本周设备诊断)存Redis,长期记忆(设备全生命周期)存图数据库。内存占用下降76%,且支持按时间/设备ID/故障类型多维检索。

2.5 部署与运维:不是“docker run”,而是生产环境的持续博弈

大模型部署最大的幻觉是“一次部署,永久运行”。真实产线中,模型、数据、基础设施三者持续演化,必须建立可观测、可回滚、可熔断的运维体系。

可观测性不能只看GPU利用率。我们定义了四个黄金指标:

  • Token Throughput(tokens/sec):反映实际计算效率,而非理论FLOPS;
  • KV Cache Hit Rate(%):>95%为健康,<80%说明batch策略失效;
  • Tool Call Success Rate(%):<98%触发告警,需检查API可用性或schema变更;
  • Prompt Rejection Rate(%):>5%说明输入过滤规则过严,需调整安全策略。

用Prometheus+Grafana搭建监控面板,每个指标都配自动基线(如Token Throughput按小时滑动窗口计算P90)。当KV Cache Hit Rate连续5分钟<85%,自动触发batch size调整脚本。

回滚机制必须覆盖全栈。某次更新微调模型后,客服响应延迟突增。传统做法是回滚模型权重,但我们发现根本原因是新模型输出更长,导致下游文本转语音服务超时。因此回滚策略包含:

  • 模型权重(S3版本化存储)
  • Prompt模板(Git tracked)
  • Tool API Schema(OpenAPI spec版本化)
  • 批处理参数(配置中心动态推送)

四者版本号绑定,一键回滚到任意历史commit。上线后平均故障恢复时间(MTTR)从47分钟降至3.2分钟。

熔断是保护系统的最后防线。我们为每个Agent组件设置独立熔断器:

  • Embedding服务:连续3次timeout(>2s)则熔断5分钟,返回缓存向量;
  • LLM生成:连续5次生成长度<10 token(疑似陷入循环)则熔断,返回预设fallback response;
  • Tool调用:单个API错误率>20%持续1分钟,则熔断并启用备用API(如主用阿里云NLP,备选腾讯云)。

熔断状态实时同步到企业微信机器人,运维人员手机端即可查看各组件健康度。

3. 从预训练到Agent:一张可动手实践的完整技术路线图

3.1 预训练阶段:如何用有限算力构建领域专属基座

预训练不必从零开始。Hugging Face Hub上有数千个开源模型,但直接下载bert-base-chinese往往效果不佳。正确路径是领域适配性预训练(Domain-Adaptive Pretraining, DAP):在通用基座上,用领域语料继续预训练。

以金融领域为例,我们选择bert-base-chinese为起点,补充训练200万篇金融研报、年报、监管文件。关键不是数据量,而是训练策略的精细化:

  • 学习率调度:采用cosine decay,但warmup步数设为总步数的5%(非常规的10%),因领域语料分布更集中;
  • mask策略:MLM中,对金融专有名词(如“CDS”“SPV”“杠杆率”)提高mask概率至25%(常规15%),确保模型重点学习;
  • 梯度裁剪:norm阈值设为1.0(非默认1.0),因金融文本长句多,梯度爆炸风险高。

训练硬件上,A100 80G × 4集群足够支撑batch size=1024。但要注意显存优化技巧:

  1. 使用--fp16而非--bf16,因A100对FP16支持更成熟;
  2. 启用--gradient_checkpointing,显存节省35%;
  3. 分词器加载时设use_fast=True,避免Python tokenizer成为瓶颈。

训练完成后,用领域适应性评估集验证:我们构建了包含5000个金融术语填空的测试集(如“央行实施__政策以应对通胀”),DAP模型准确率82.3%,原bert-base-chinese仅56.7%。这证明领域语料确实重塑了模型的语言几何结构。

3.2 微调阶段:三步构建高质量指令数据集

微调效果70%取决于数据质量。我们总结出指令数据集构建三步法:

Step 1:种子指令生成
不用人工编写,而是用现有模型生成。以金融风控为例:

  • 输入:监管文件《商业银行资本管理办法》第42条:“商业银行应建立...”
  • 提示:请生成3个符合该条款的合规性检查问题,要求包含具体数值和场景
  • 输出:1. 若某银行核心资本充足率为10.5%,是否满足最低监管要求?2. 当风险加权资产为5000亿元时,核心一级资本净额至少需多少?...
    用Qwen2-7B生成10万条,人工抽样审核,保留85%高质量样本。

Step 2:对抗样本注入
在正样本旁添加对抗样本,迫使模型学习判别能力。例如:

  • 正样本:问题:某客户征信报告显示近24个月有3次逾期,能否批准房贷?答案:否,因逾期次数超2次
  • 对抗样本:问题:某客户征信报告显示近24个月有3次逾期,能否批准房贷?答案:是,因逾期金额均小于100元
    对抗样本占比30%,显著提升模型对政策细节的敏感度。

Step 3:难度渐进式采样
按认知复杂度分层:

  • Level 1(事实检索):“巴塞尔协议III对核心一级资本充足率的要求是多少?”
  • Level 2(多跳推理):“若某银行核心一级资本为800亿,风险加权资产为6000亿,是否满足巴塞尔III要求?”
  • Level 3(政策解读):“巴塞尔III对系统重要性银行的附加资本要求,如何影响我国四大行的资本管理策略?”
    微调时按1:2:4比例采样,确保模型能力均衡发展。

3.3 推理阶段:本地部署的硬核优化清单

在RTX4090(24GB显存)上部署7B模型,需直面显存与算力的极限博弈。我们整理出一份可立即执行的优化清单:

显存优化:

  • 使用transformers的device_map="auto"自动分配,但手动修正:将lm_head权重强制放在GPU,避免CPU-GPU频繁拷贝;
  • 启用flash_attn(需编译安装),Attention计算显存占用降低40%;
  • KV Cache设为torch.float16,而非默认torch.bfloat16,节省15%显存。

速度优化:

  • 关闭torch.compile的fullgraph=True(易出错),改用mode="reduce-overhead";
  • 输入prompt预填充至固定长度(如512),避免dynamic shape带来的kernel重编译;
  • 使用vLLM而非text-generation-inference,QPS提升3.2倍。

精度保障:

  • 对金融数值,禁用--quantize bitsandbytes,改用--load-in-4bit配合bnb_4bit_compute_dtype=torch.float16;
  • 在生成后,用正则提取所有数字,强制转为Decimal类型再格式化输出,避免浮点误差。

实测结果:Qwen2-7B在4090上,512-token输入+256-token生成,平均延迟380ms,显存占用18.2GB,完全满足实时交互需求。

3.4 Agent开发:从零构建可审计的工业级Agent

Agent开发最易被忽略的是可审计性设计。我们以设备故障诊断Agent为例,给出完整实现:

工具定义(带严格Schema):

from pydantic import BaseModel, Field class DeviceQuery(BaseModel): device_id: str = Field(..., pattern=r'^[A-Z]{2,4}_\d{3,6}$') # 强制格式校验 alarm_code: str = Field(..., pattern=r'^0x[0-9A-F]{4}$') class DeviceTool: name = "get_device_info" description = "查询设备基本信息及历史报警记录" args_schema = DeviceQuery def _run(self, device_id: str, alarm_code: str) -> dict: # 实际API调用,带重试和超时 return {"status": "OK", "alarm_history": [...]}

执行器(带审计日志):

import logging logger = logging.getLogger("agent_audit") class AuditableExecutor: def __init__(self): self.audit_log = [] def execute(self, tool_name: str, args: dict) -> dict: start_time = time.time() try: result = self.tools[tool_name]._run(**args) duration = time.time() - start_time audit_entry = { "timestamp": datetime.now().isoformat(), "tool": tool_name, "input_hash": hashlib.md5(str(args).encode()).hexdigest(), "output_hash": hashlib.md5(str(result).encode()).hexdigest(), "duration_ms": int(duration * 1000), "status": "success" } self.audit_log.append(audit_entry) logger.info(f"Tool {tool_name} executed in {duration:.3f}s") return result except Exception as e: logger.error(f"Tool {tool_name} failed: {e}") # 记录失败日志,但不中断流程 return {"error": str(e)}

记忆图谱(Neo4j示例):

// 创建节点 CREATE (d:Device {id: "PLC_S7-1200_001", type: "PLC"}) CREATE (a:Alarm {code: "0x8001", desc: "CPU模块故障"}) // 创建关系 CREATE (d)-[:HAS_ALARM]->(a) CREATE (a)-[:RESOLVED_BY]->(:Solution {steps: ["检查电源电压", "更换CPU模块"]})

每次Agent调用后,自动将结果存入图谱。后续提问“如何解决0x8001报警?”,Agent先查图谱,命中则直接返回,未命中再调用API。

4. 常见问题与排查技巧实录:来自27个真实项目的血泪经验

4.1 预训练常见问题:语料、分词、Loss的三重陷阱

问题1:Loss曲线震荡剧烈,无法收敛
现象:MLM loss在2.1~3.8之间大幅波动,无下降趋势。
排查:

  • 检查语料编码——用file -i确认所有文本为UTF-8,非ASCII字符(如中文引号“”)被误读为乱码会导致loss飙升;
  • 检查分词器——用tokenizer.encode("测试")验证是否正常,若返回[101, 102](UNK token),说明分词器未加载正确;
  • 检查mask比例——若mlm_probability=0.25但实际mask token数占比仅5%,说明whole_word_mask开关未关。

问题2:下游任务效果差,但预训练loss很低
现象:MLM loss=1.2(优秀),但微调后NER F1=52%。
根因:预训练语料与下游任务领域错配。某项目用新闻语料预训练,但下游是医疗病历,专业术语覆盖率不足。
解法:

  • 用scikit-learn的TfidfVectorizer提取下游任务top1000关键词;
  • 在预训练语料中搜索含这些词的文档,单独构成一个“领域强化语料集”;
  • 用该语料集继续预训练10%步数,F1提升至79%。

问题3:显存OOM,但模型参数量在理论范围内
现象:7B模型理论上需28GB显存,但A100 80G仍OOM。
真相:max_position_embeddings设为4096时,KV Cache显存占用=2×num_layers×batch_size×seq_len×hidden_size×2(bytes)。当batch_size=16、seq_len=2048时,仅KV Cache就占32GB!
对策:

  • 降低max_position_embeddings至2048(多数任务无需4K上下文);
  • 使用--attn_implementation flash_attention_2;
  • 启用--gradient_checkpointing。

4.2 微调常见问题:数据、参数、评估的致命盲区

问题1:微调后模型“胡言乱语”,生成内容无逻辑
现象:输入“苹果公司2023年营收”,输出“苹果是一种水果,富含维生素C”。
根因:微调数据中混入了大量通用百科数据,冲淡了任务特异性。
解法:

  • 用datasets库的train_test_split,按source字段分层采样,确保金融数据只来自财报/研报;
  • 在loss计算中,对任务相关token(如数字、专有名词)加权×2;
  • 添加early_stopping,patience=3,避免过拟合。

问题2:LoRA微调后,推理速度反而变慢
现象:全参数微调QPS=120,LoRA(r=8) QPS=98。
真相:LoRA的A和B矩阵引入额外matmul,且r=8时参数量仍较大。
优化:

  • 改用r=4,但增加target_modules(如["q_proj", "k_proj", "v_proj", "o_proj", "gate_proj"]);
  • 将LoRA权重merge_and_unload()后保存为完整模型,消除推理开销;
  • 或改用IA³,延迟几乎无损。

问题3:评估指标虚高,线上效果差
现象:测试集F1=89%,但真实用户提问准确率仅61%。
根因:测试集与真实分布不一致。测试集问题来自内部QA库,而用户提问更口语化、更模糊。
对策:

  • 构建“真实流量采样集”:抓取线上1000条用户原始提问,人工标注答案;
  • 用该集合作为最终评估基准;
  • 在微调数据中,按1:1比例加入口语化表达(如“那个啥,苹果公司去年赚了多少钱?”)。

4.3 推理常见问题:延迟、显存、精度的三角矛盾

问题1:vLLM部署后,QPS不达标
现象:理论QPS=200,实测仅85。
排查:

  • nvidia-smi看SM Utilization,若<30%说明计算未饱和,检查是否batch size过小;
  • nsys profile看kernel耗时,若cudaMallocAsync占比高,说明显存分配频繁,启用--kv-cache-dtype fp16;
  • 检查网络IO——若用HTTP API,uvicorn默认worker数不足,改用--workers 4。

问题2:量化后数值精度丢失
现象:Q5_K_M量化模型输出“年化收益率4.27%”,应为“4.273%”。
解法:

  • 对数值字段,用re.findall(r'\d+\.\d+', text)提取所有浮点数;
  • 用decimal.Decimal重新计算并格式化;
  • 或改用Q6_K量化,精度损失<0.01%。

问题3:长文本生成时,后半段质量骤降
现象:生成512-token,前256-token逻辑清晰,后256-token重复、跑题。
根因:KV Cache在长序列下衰减,且position embedding外推失效。
对策:

  • 使用rope_theta=10000(而非默认1000000),增强长程位置感知;
  • 在prompt末尾添加<|endofprompt|>标记,模型学会在此后专注生成;
  • 采用滑动窗口attention(如flash_attn的window_size=512)。

4.4 Agent常见问题:工具、记忆、决策的脆弱链条

问题1:Tool Call参数解析失败,但LLM声称“已正确调用”
现象:模型输出{"action": "get_device_info", "action_input": {"device_id": "S7-1200_001"}},但API返回400。
真相:API要求device_id为整数,但模型输出字符串。
解法:

  • 在Tool定义中,用Pydantic强制类型转换:device_id: int;
  • 添加_parse_input方法,自动转换类型;
  • 日志中记录原始LLM输出与解析后参数,便于debug。

问题2:Agent“忘记”历史对话,重复提问
现象:用户问“报警代码0x8001是什么?”,Agent查API后回答;用户再问“怎么解决?”,Agent再次调用API。
根因:memory只存文本,未结构化。
对策:

  • 将API返回的JSON直接存入图数据库,建立Device-Alarm-Solution关系;
  • 下次提问时,先查图谱`MATCH (d:
返回列表