本文记录的是一套已在产线运行的工业质检系统——从需求约束推导技术方案,从 NEU-DET 基线微调到 mAP@0.5 0.82,用 LangGraph 构建可控的质检 Agent,中间经历了域偏移、光照漂移等六场硬仗,最后落到生产环境的算力规划与上线成效。写代码只占 20% 的工作量,剩下 80% 在数据、微调与工程判断里。

一、需求场景:一条质检线上的真实痛点

合作方是湖北一家金属制品厂,产线质检完全靠人工目检,痛点非常具体: 

        1. 检不快。 每件工件目检约 20 秒,产线节拍是每 3 秒一件,质检员长期超负荷,只能抽检——抽检率不足 40%,漏检流出意味着整批客诉风险。

        2. 判不准、判不稳。 网状龟裂、非金属夹杂等缺陷的判定高度依赖老师傅经验。同一件工件,甲班判 NG、乙班判 OK 的情况每周都有;老师傅休假,判定标准就漂移。

        3. 只会判“有没有”,不会判“怎么办”。 即使检出缺陷,严重程度、是否波及同批次、工艺上可能是什么原因——这些决策信息仍要层层上报人工分析,从检出到批次处置平均耗时 4 小时以上。

基于以上,与厂方共同确定了系统的验收口径(这也是所有技术决策的推导起点):

需求约束

量化指标

技术影响

替代人工全检

单件端到端 ≤1s

锁定轻量检测模型 + 量化部署

判定标准统一

漏检率(crazing 类)≤8%,误报率 ≤5%

决定了微调与负样本策略

给出处置依据

自动输出严重级别 + 处置建议 + 归因分析

引入 LangGraph 质检 Agent

数据持续积累

模型指标随运行时间不衰减

设计难例回流闭环

弹性扩展

检测量峰值 3 倍波动

推理服务无状态化、独立扩容

        最终系统形态:质检员上传/抓拍工件图像 → 0.8s 内返回缺陷框、置信度、严重级别 → Agent 自动生成包含归因分析与批次处置建议的结构化质检报告 → 记录落库进入批次追溯。检测 5 类典型缺陷(划痕、斑块、网状龟裂、非金属夹杂)+ 正常品判定。

二、基线建立:先量化“通用模型差多少”

微调叙事要有说服力,前提是严格可复现的 baseline:

数据:NEU-DET 公开数据集 1,800 张(6 类钢材表面缺陷),train/val 8:2;测试集 480 张独立封存,全程只在最终评估解封一次,避免测试集泄漏

评估协议:mAP@0.5 / mAP@0.5:0.95 + 按类 AP 拆解 + 两个业务指标——FNR@conf=0.5(漏检,对应质检风险)、FPR(误报,对应人工复核成本)

基线:COCO 预训练 YOLOv8n 直接推理

基线结果(封存测试集):

指标

值

业务含义

mAP@0.5

0.463

—

crazing AP@0.5

0.38

漏检率 47%,不可接受

scratches AP@0.5

0.31

小目标几乎失效

FPR(负样本 300 张)

21.70%

每五张正常品误报一张

两个关键失败模式,也是后续微调的靶子:

  1. crazing 系统性低召回:网状龟裂是弱纹理、无封闭边界目标,COCO 预训练特征(强边缘/强纹理为主)对其表征不足。PR 曲线显示 recall 到 0.6 时 precision 已跌至 0.35。
  2. 误报 87% 集中在金属反光带:训练集中背景图像占比为零,模型没有学到“背景抑制”。

三、微调主体:三个阶段的完整配置

3.1 阶段一:数据重构(预期收益最大)

        负样本注入:产线采集 600 张正常品(空标注),按 1:3 混入训练集。实测 FPR 21.7% → 4.3%,mAP@0.5 反升 1.8 个点(误报候选挤压后 NMS 更干净)。

        小目标增强:scratches/inclusion 图像级 oversample 2.5×;Copy-paste 增强——细划痕 patch 以 α-blend(α∈[0.7,1.0])粘贴至其他图的金属纹理区域,粘贴前做亮度直方图匹配。对 scratches AP 贡献 +6.2。

        类别不均衡:不用过采样硬怼,损失端处理——cls 损失权重 0.5→1.0,按有效样本数(effective number,β=0.999)加权。

3.2 阶段二:分阶段解冻微调

# Phase 1: 特征适配 —— 冻结 backbone(v8n 共 10 个 module),只训 neck+head
model.train(data='neudet.yaml', epochs=30, freeze=10,
            lr0=0.01, optimizer='SGD', mosaic=1.0, imgsz=640)

# Phase 2: 联合微调 —— 全解冻,降学习率
model.train(resume=True, epochs=120, freeze=0, lr0=0.001,
            cos_lr=True, multi_scale=True, close_mosaic=20)

# Phase 3: 精修 —— 关强增强,小学习率收敛
model.train(resume=True, epochs=20, lr0=0.0001, mosaic=0.0)

freeze 取值做了消融(小数据 <5k 场景):

freeze

mAP@0.5

结论

0(全解冻)

0.771

浅层通用特征被小数据冲垮,loss 震荡

10

0.804

最优

22(全冻结)

0.758

head 容量不足以重建缺陷纹理

3.3 阶段三:标签分配与 NMS 的针对性调整

TAL assigner topk 10→13,缓解极端长宽比目标(划痕 w:h>20:1)正样本不足,scratches AP +3.1

dfl 增益 1.5→2.0——框定位精度直接影响后续缺陷面积量化(后文 6.3 会讲到这个参数的出处)

按类参数化 NMS:crazing 是密集网状目标,标准 IoU=0.7 会误合并相邻裂纹,单独放宽至 0.85(实现在推理服务,不动模型)

最终结果(封存测试集一次性评估):

类别

基线

最终

Δ

crazing

0.38

0.79

0.41

scratches

0.31

0.74

0.43

inclusion

0.52

0.86

0.34

patches

0.61

0.9

0.29

mAP@0.5

0.463

0.821

0.358

FPR

0.217

0.043

−17.4pp

四、部署量化:精度-延迟权衡

        产线要求视觉环节 ≤150ms(GPU 推理节点),决策链:

        FP32 原始推理 310ms → 导出 ONNX(opset 12,simplify)→ 240ms

        INT8 PTQ(TensorRT,500 张产线图熵校准)→ 118ms

        量化回归:mAP@0.5 掉 1.4 点(0.821→0.807),crazing 召回 92%→90.8%,仍高于 8% 漏检红线

        工程判断:没有为追回 1.4 点做 QAT——已满足 SLA,QAT 的训练维护成本与收益不匹配。落地是满足约束,不是刷指标。

五、质检 Agent:基于 LangGraph 的可控工作流

        这是系统的差异化壁垒。YOLO 输出只是 [{"cls":"crazing","conf":0.85},...]——是数据,不是结论。要让系统输出质检员能签字的报告,需要归因分析、知识检索、处置决策三种能力的编排。

5.1 为什么不用 ReAct,而用 LangGraph

        最初我们用 LangChain 的 ReAct Agent 快速验证了可行性。但产线运行两周后,三个问题暴露出来:

        执行路径不可控:ReAct 是“LLM 自主决策下一步”,但质检是有严格流程的领域任务——必须先查规则、再查知识库、最后查批次历史。ReAct 偶尔会跳步(比如没查知识库直接归因),导致归因证据不可溯;

        状态不可持久:质检是一个多轮、可能中断的过程(质检员复核后可能要求补充分析)。ReAct 的状态只存在单次会话中,刷新即丢,无法实现“检出 → 人工复核 → 补充归因 → 出最终报告”这样的中断续接;

        无法观测:ReAct 的思考链是黑盒,出了问题无法回放定位是哪一步工具调用出错。

        这三个问题指向同一个答案:我们需要的是可控的工作流,不是自主智能体。于是将 Agent 迁移到了 LangGraph——一个基于状态图构建可控、可持久化 Agent 工作流的框架。

5.2 LangGraph 架构设计:质检工作流即状态图

        LangGraph 的核心抽象是状态图:每个节点是一个处理步骤(可以是 LLM 调用、工具调用或纯代码),边定义了执行顺序,状态对象在节点间传递并持久化。这正好匹配质检流程的刚性要求。

关键设计点:

        流程即代码,不靠 LLM 自觉:工作流的边由代码定义,LLM 只在节点内部做内容生成(如归因分析),节点执行顺序由图结构保证——彻底杜绝跳步。

        状态持久化,支持中断续接:每个状态变更通过 Checkpointer 持久化(生产环境用 Redis)。质检员复核高风险件时,工作流暂停;复核反馈后,从 Checkpoint 恢复状态,继续执行——多轮人机协作成为原生能力。

        每一步可观测:LangGraph 的执行轨迹(哪些节点执行了、工具返回了什么、状态如何变化)完整记录,配合 LangSmith,任何一次误判都能回放到具体节点的具体输入输出。

5.3 核心节点实现

from langgraph.graph import StateGraph, END
from typing import TypedDict, Annotated
import operator

class QualityState(TypedDict):
    defects: list          # YOLO 结构化输出
    batch: dict            # 批次上下文
    rules: dict            # 严重级别规则(severity_rules 节点写入)
    knowledge: list        # 知识库条目(knowledge_search 节点写入)
    analysis: dict         # 归因分析(LLM 节点生成)
    action: str            # 最终处置(规则仲裁节点写入)
    messages: Annotated[list, operator.add]  # 执行轨迹

def severity_rules_node(state: QualityState):
    """节点:规则查询 —— 纯代码,无 LLM 调用"""
    rules = rules_engine.describe(state["defects"][0]["cls"])
    return {"rules": rules, "messages": [f"已查询 {state['defects'][0]['cls']} 规则"]}

def knowledge_search_node(state: QualityState):
    """节点:知识库检索 —— 向量检索,无 LLM"""
    query = f"{state['defects'][0]['cls']} 工艺归因"
    docs = vectorstore.similarity_search(query, k=3, filter={"source": "process_kb"})
    knowledge = [{"id": d.metadata["entry_id"], "text": d.page_content} for d in docs]
    return {"knowledge": knowledge, "messages": [f"检索到 {len(docs)} 条知识"]}

def analysis_node(state: QualityState):
    """节点:归因分析 —— LLM 节点,但输入输出被严格约束"""
    prompt = f"""基于以下缺陷检测结果和知识库条目,输出归因分析。
    缺陷: {state['defects']}
    知识库: {state['knowledge']}
    规则: {state['rules']}
    只允许引用提供的知识库条目ID,禁止编造。"""
    response = llm.invoke(prompt)
    return {"analysis": parse_analysis(response), "messages": ["归因分析完成"]}

def rules_arbitration_node(state: QualityState):
    """节点:规则仲裁 —— 纯代码,LLM 结论与规则冲突时以规则为准"""
    llm_action = state["analysis"].get("action", "复检")
    rule_action = rules_engine.judge(state["defects"], state["batch"])
    final_action = rule_action if llm_action != rule_action else llm_action
    return {"action": final_action, "messages": [f"仲裁完成: {final_action}"]}

# 构建工作流图
workflow = StateGraph(QualityState)
workflow.add_node("severity_rules", severity_rules_node)
workflow.add_node("knowledge_search", knowledge_search_node)
workflow.add_node("analysis", analysis_node)
workflow.add_node("rules_arbitration", rules_arbitration_node)
workflow.add_node("batch_history", batch_history_node)

workflow.set_entry_point("severity_rules")
workflow.add_edge("severity_rules", "knowledge_search")
workflow.add_edge("knowledge_search", "analysis")
workflow.add_edge("analysis", "rules_arbitration")
workflow.add_edge("rules_arbitration", "batch_history")
workflow.add_edge("batch_history", END)

# 生产环境:Redis Checkpointer 实现状态持久化
from langgraph.checkpointers.redis import RedisSaver
checkpointer = RedisSaver(redis_url="redis://prod-redis:6379")
app = workflow.compile(checkpointer=checkpointer)

5.4 对比 ReAct 版本的质变

维度

ReAct 版本

LangGraph 版本

业务影响

流程可控性

LLM 自主决策,偶发跳步

图结构强制流程,节点顺序固定

归因证据 100% 可溯,合规性达标

状态持久化

单轮会话,中断即丢

Checkpoint 持久化,复核后可续接

实现“AI 初判 → 人工复核 → AI 终判”闭环

可观测性

思考链黑盒

每节点输入输出记录,可完整回放

误判定位从“猜测”到“回放”

成本控制

ReAct 循环 token 消耗不可控

LLM 节点数量固定,成本可精确预算

Token 预算可控

        其中状态持久化是落地最关键的能力:质检员驳回一个高风险判定时,工作流不是从零开始,而是从 Checkpoint 恢复、注入复核反馈、继续执行。生产环境选择 LangGraph 而非继续用 ReAct,本质是选择可控性优先于自主性——在质检这种有严格合规要求的场景,流程可预测比“智能涌现”更重要。

5.5 输出约束与防幻觉铁律

        系统 Prompt 铁律 + 三层校验:

        root_cause 每一条归因必须引用 knowledge_search 返回的条目编号
        禁止输出任何数字(缺陷数、置信度由系统注入)
        action 只能从枚举选择:放行 / 复检 / 批次冻结 / 退货评估

        1.JSON Schema 解析失败 → 自动重试(最多 2 次)

        2.正则扫描裸数字 → 命中即重试

        3.evidence 条目编号与知识库实际返回比对,引用不存在的条目 → 判定为幻觉,降级为纯模板报告

        规则仲裁冲突时以规则引擎为准,差异写入 agent_audit 表——实测仲裁触发率约 4%,这些 case 复盘后大多转化为规则库或知识库的补充条目,Agent 的错误本身成了系统持续进化的数据源。

5.6 调用策略与成本控制

        低风险(OK / 单一轻微缺陷)→ 纯模板报告,0 LLM 调用,延迟 <50ms
        高风险(crazing / 多缺陷 / 质检员查看详情)→ 触发 LangGraph 工作流,实测 ~1.2s

        日均触发约 300 次,Token 成本经 LangSmith 追踪核算;同时 LangSmith 记录了每次工作流的完整 Trace——每次误判都可以回放到具体是哪个节点检索不准或推理跑偏。

5.7 持续学习闭环(回流三通道)

        conf ∈ [0.4,0.6) 灰色带 → 自动入标注队列(每周约 40 张)
        质检员改判样本           → 最高价值回流(模型错+人是对的)
        未知低置信形态           → 触发人工审核 + 新类别评估

 

每两周增量微调一次(lr0=0.0005),强制过回归测试集——新老指标双不降才允许 /model/reload 灰度上线。上线三周 crazing AP 0.79→0.83,全部来自回流。

        归因有知识库编号可溯,判定有规则引擎背书,批次结论有真实查库数据——报告里没有一句话是 LLM“自由发挥”的。这就是 LangGraph 工作流在质检场景的落地价值:把 LLM 的语言能力关进结构化约束的笼子里,只放出来做它擅长的归因与组织。

六、落地攻坚:产线现实教会我们的六件事

        方案纸面自洽,产线现实毒打。以下六个问题全部是上线后真实暴露、逐一定位的——这一章的含金量,某种程度上比微调那章更高。

6.1 域偏移:第一天就翻车的问题

现象:内网测试集上 mAP 0.82 的模型,接到产线实拍图像后 crazing 召回直接掉到 0.6 以下。

根因:NEU-DET 的图像是实验室环境采集——均匀光照、固定拍摄距离;产线相机是工业相机 + 顶置环形光源,金属表面有油膜反光、光照随车间日夜波动,图像分布整体偏移,训练集和推理集根本不是同一个分布。

解法:

短期:把 600 张产线实拍图加入训练集做联合微调(第三章的数据构成即来源于此),召回恢复到 0.79;

长期:推理服务前置轻量域判定——每天用最近 200 张产线图的特征统计对比基线,偏移超阈值自动告警提示“该做增量微调了”。域漂移被从一个隐形杀手变成了一个可监控指标。

教训:公开数据集只配做冷启动,第一周就要开始攒产线自己的数据。

6.2 光照漂移:模型随时间“变笨”

现象:上线第二周,同样的工件、同样的工位,下午的误报率比上午高出约 8 个点。

根因:车间西侧下午受日光影响,成像色温和亮度漂移,落在训练分布之外。

解法:

源头:推动产线加装遮光罩 + 光源固定功率(工程手段优先于算法手段,这是落地铁律);

算法兜底:训练时加大 HSV 颜色扰动增强(±15%),并用每日回流数据持续覆盖新光照条件。

6.3 边缘抖动:同一工件两次检测框不一致

现象:质检员复核时发现,静止工件的两次抓拍,检测框位置抖动 3~5px,偶发框形态突变。

根因:小目标边界本就模糊 + INT8 量化噪声放大了定位抖动。此问题的定位过程正是得益于 LangGraph 的执行轨迹回放——从工作流 Trace 一路回溯到原始检测框序列才发现抖动模式。

解法:

时序平滑:同工位同一工件 2 帧检测,框做 IoU 加权融合(IoU>0.6 才融合);

dfl 增益上调至 2.0(第三章 3.3 提到的参数,出处就在这里)。

这个问题必须修:下游的缺陷面积量化直接读框,框抖 3px,面积波动 5%,报告里的量化结论就不稳定。

6.4 一次真实的误杀事故:油膜被当成斑块

现象:上线第三天,一批 47 件正常品中 11 件被判 NG,全部是 patches 类,质检员直接投诉到厂长。

根因复盘(LangSmith Trace 回放 + 图像比对):

误报图像集中在涂油工位下游,油膜在特定角度的反光纹理与斑块特征高度相似;

我们的负样本采集自未涂油工位——负样本没有覆盖油膜这个混淆源,属于负样本采样偏差。模型没错,是我们的数据错了。

解法:重新按工位分层采集负样本(每个工位都要有),油膜样本补了 180 张,两天后 FPR 回到 4% 以内。

教训:负样本不是“正常的图”就够,必须覆盖全部混淆源——采集时要问“产线上什么东西长得像缺陷”,而不是随手拍正常品。

七、架构与算力配置

7.1 架构(算法与业务解耦)

        两周内模型从 v8n → ONNX → TensorRT 切换,Java 与前端全程无感知——接口契约第一天锁死。

【插图 5(可选):系统整体架构图,如打算画一张放这里】

7.2 服务器算力配置(生产环境)

        按产线检测节拍倒推容量:峰值 3 倍波动、日均 3,000+ 件、单件视觉推理 ≤150ms,规划如下:

节点

配置

用途与容量依据

推理节点

GPU 服务器 1 台:2× NVIDIA A30(24GB),2× Xeon Silver 4314 / 128GB RAM / NVMe

YOLOv8n INT8(TensorRT)单卡实测 118ms/张、单实例 15 QPS;双卡 4 实例,支撑 60 QPS,为峰值需求的 3 倍冗余

质检 Agent 节点

LangGraph 编排服务(8 vCPU / 16GB)+ 私有化 LLM 推理(4× A10)

仅高风险触发,日均约 300 次;内网部署数据不出域

业务节点(Java)

虚机/容器:8 vCPU / 16GB ×2 实例

无状态双实例 + Nginx 负载均衡

数据层

MySQL 8(主备)+ Redis + MinIO 集群

检测记录按月分表;Redis 兼任 Agent Checkpointer 存储

训练环境

独立 1× A30 训练机

全部微调 + 消融实验约 3 天跑完;增量微调每两周一次

7.3 两个容量规划结论

推理算力按“峰值 × 3 冗余”配置,而非按均值:双卡 4 实例保证任何单卡故障时 SLA 仍可维持(降级为半容量,不中断产线)。

算力投入跟着业务量走:初期检测量 <5,000 件/天时,增量微调可夜间复用推理卡;检测量翻倍后再独立采购训练节点——不做一次性超配。

八、落地成效:上线前后到底省了多少

作为承接方,不披露客户内部财务数据,但以下对比指标来自系统运行数据与客户质检部的共同确认,可以完整公开。

8.1 上线前后核心指标对比

指标

上线前(人工抽检)

上线后(系统全检)

变化

检测覆盖率

抽检约 40%

100% 全检

质控模式改变

单件检测耗时

~20s

0.8s

提速 25 倍

缺陷返工率

逐年上升趋势

转为下降,三个月累计下降约四成

见 8.2

检出 → 批次处置

平均 4 小时

5 分钟内(Agent 报告 + 冻结建议)

48 倍

需人工复核比例

100%

<10%(仅高风险件)

释放 90% 目检工作量

判定一致性

班次间标准漂移

100% 统一规则判定

争议基本消失

Agent 框架

—

LangChain ReAct → LangGraph(可靠性升级)

见第五章

8.2 返工率为什么是“从上升转为下降”
        上线前返工率逐年上升,原因在模式本身:抽检漏掉的高危缺陷(尤其龟裂类批次性问题)流出后在下游甚至客户端才暴露,返工范围已扩大到整批;且问题定位平均要 4 小时以上,期间产线按原工艺继续生产,同一原因的缺陷被持续制造出来。
上线后趋势逆转,来自三个机制叠加:
        全检拦截:批次性缺陷在产线端第一件就被检出,不再流入下游;
批次冻结前置:归因建议 5 分钟内冻结,关闭“持续生产 4 小时”的扩大窗口——同一批次受影响的返工件数量减少了一个数量级;
        归因到工艺:报告直接指向“冷却速率过快”类根因,生产侧当天整改,同类缺陷不在后续批次重复出现。
        三个月累计,客户确认缺陷返工件数下降约 40%,且趋势仍在收敛——归因整改的工艺条目越攒越多,预计随知识库和回流继续积累还会进一步改善。
8.3 成本账(口径:可观察运营数据)
        质检人力结构:目检工作量下降 90% 以上,释放人力转入复核与知识库运营,未裁减一人——AI 做全检、人做复核与数据回流,落地阻力小得多;
        返工成本:返工件数下降约四成,仅此一项,系统投入预计一年内收回(具体回收周期双方按合同口径核算);
        隐性收益:漏检客诉趋零、处置争议消失,客户在回访中明确表示“实际收益大于合同里的验收指标”。
结语
        复盘整条路径:需求约束推导方案、数据工程与分阶段微调拿回模型上限、INT8 量化满足产线延迟、LangGraph 构建可控可持久可观测的质检工作流、与域偏移和油膜误杀这些产线现实的六场硬仗、双卡冗余承载全部负载。
        技术指标只是过程:mAP 提升 35 个点、单件 0.8s、处置 5 分钟、返工件数三个月下降约四成——AI 落地的价值,最终要同时写在模型评估表和客户的运营趋势图上。
小模型解决“看见”,LangGraph 工作流解决“看懂”,规则引擎做仲裁兜底——三者各司其职,才是一套能上产线。

欢迎指导!

Logo

更多推荐