做 CapSolver 这类验证码处理平台的架构设计最让我头疼的从来不是单一模型训练而是“怎么把一堆不同流派、不同能力、不同成本的识别通道编排到一个统一系统里”。以前我们习惯用规则引擎硬编码发现验证码类型一变规则就要跟着改改到后面连维护的人自己都看不懂。后来我把大语言模型拉进调度链路让 LLM 不是去“直接识别验证码”而是做整个系统的决策中枢根据当前请求的上下文、图片信号和历史成功率自动选择最合适的识别策略。这个方向跑通之后系统的自适应能力和扩展性比之前好了不止一个量级。这篇文章我会把整套架构从决策层到执行层、从动态权重到工程容错完整拆开讲重点聊聊 LLM 在调度链路上的一些设计取舍、和 CNN 模型、外部 CapSolver 通道配合的细节以及我在生产环境里踩过的一些坑。如果你是做自动化流程、图像模型调度或者是想把自己的识别能力做成平台化服务的开发者这篇应该是可以直接拿来参考的。1. 先拆清楚核心问题调度层为什么比识别层更关键很多人在做验证码识别系统时会把精力全压在模型准确率上总觉得模型越强系统就越稳。但真到了规模化落地阶段你发现瓶颈根本不是模型本身而是调度。1.1 业务现状验证码类型多、质量波动、渠道复杂先还原一下我当时面临的真实场景。系统要处理的不只是某一种验证码而是会同时在线上收到好几种类型有纯扭曲文字的有滑块拖动的有图像点选的偶尔还有那种融合了语义理解的新式交互验证码。每一种类型内部还会继续分裂比如图像点选里可能要点“红绿灯”“人行横道”这种固定目标也可能让你点“包含文字描述的某个物体”难度天差地别。同时线上的图片质量也很不稳定。有的图片来自用户上传的截图分辨率够、光线正常有的则经过了压缩、缩放、裁剪字符边缘锯齿非常严重。这种输入抖动让单一固定模型很难稳定发挥。更现实的是不同识别通道的成本也不一样自研模型推理基本边际成本极低外部 CapSolver 这类通道按次计费、速率更快但存在单次调用费用。如果调度逻辑只是一股脑地分发流量成本会瞬间被拉高且响应时延也不可控。1.2 为什么单一规则引擎扛不住最开始我选的是规则引擎毕竟调度在业务系统里是个常见需求判断条件写清楚就行。但真实流量一上来问题就暴露了。规则引擎的维护粒度很粗。比如我写一条规则遇到 4 位扭曲字母就走 CNN 模型 A遇到滑块就走轨迹模型 B。第二天验证码服务商把字母背景加了新的纹理干扰或者滑块模板从横向拼图变成了纵向拼图A 和 B 的效果直线下降可是规则引擎感知不到这种变化。它只会按照老的策略继续导流直到人工从监控曲线里发现异常再手动调整。另外规则引擎很难处理“语义型”验证码。像“请依次点击所有包含交通工具的图片”这种指令属于自然语言理解范畴光靠 if-else 去枚举目标类别根本不现实。比较实际的方案是让 LLM 参与判断把图片上已经识别出来的目标候选清单和自然语言指令放到一起做推演得出一个最优执行组合。这个工作规则引擎做不了人肉介入又太慢必须有一个具备语义理解能力的决策模块。1.3 从“一个模型打天下”到“多通道组合拳”后面我想明白了一件事验证码识别的正确粒度不应该是一个单体模型去处理所有类型而是把能力拆成多个专用通道再由一个上层调度器根据请求特征做动态选择。好比一个综合医院不会让一个医生看所有病而是先分诊再让对应的专科医生接手。调度器就是那个预检分诊台。所谓 CapSolver AI-LLM 架构实际做的就是把“外部 CapSolver 通道”“内部自研 CNN 模型”“语义理解模型”“滑块轨迹模型”这些专科医生统一纳管让它们各自负责自己最擅长的部分。分工清晰之后模型的迭代也可以独立进行某个通道效果不好就单独替换不至于牵一发而动全身。这里其实对标的就是这两年行业内常说的“多智能体编排”思路只不过我们把 Agent 的决策换成了一套更聚焦的 LLM Router。它的核心目标是在给定一次验证请求的前提下选出一个或一组识别通道让综合成功率最高、时延尽量低、成本处在可控范围内。整个项目的重心也就从“识别算法调优”转移到了“路由策略调优”。2. 决策中枢的架构分层与数据流向设计架构设计有个很朴素的原则先划清边界再谈细节。决策中枢如果和识别执行混在一个进程里后期扩展任何一个通道都可能影响全局。所以我把系统拆成了接入层、决策层、执行层和反馈层四个部分。2.1 分层设计与模块职责接入层的作用是接收所有验证码识别请求做标准化和数据清洗。不同来源的验证码图片格式五花八门我需要统一转成规范尺寸的灰度图或三通道图并提取原始请求中的元数据如来源站点、验证码业务类型、期望的超时时间等这些信息会被一起交给决策层使用。决策层是这套架构里最有意思的部分也是 CapSolver AI-LLM 的落点。它不直接跑识别模型而是借助一个轻量级 LLM 路由模型完成三件事验证码类型判断、难度评估、执行策略推荐。执行层则包含多个被注册的通道连接器每个连接器封装一个具体的识别能力比如自研的 CNN 字符识别服务、目标检测服务、滑块轨迹生成服务以及外部 CapSolver API 通道。反馈层负责回收执行结果。每次请求结束后系统会记录这次请求用了哪个通道、耗时多久、是否成功并把这些数据异步写入时序数据库和消息队列用于后续动态路由权重更新和模型再训练。分层之后最大的好处是模块之间可以独立扩缩容。最近执行层某个通道因为别的原因被占满不会拖垮决策层决策层依然可以根据情况把流量导给其他通道。2.2 请求从进入到决策完成的完整流转我实际落地时把流程画得很直白就是下面这条链路客户端把验证码图片或者交互验证的初始配置提交到接入层。接入层先做指纹计算和基础质量检测如果发现是重复请求直接从 Redis 里返回上一次的可用结果不进入后续识别链路。新请求进入特征组装模块生成一个包含图像基础特征、业务约束、历史统计信息的 JSON 上下文。决策层根据缓存判断是否需要走 LLM。如果属于高频且稳定的业务类型直接走规则反射表如果上下文发生了明显变化才调用 LLM 做路由决策。LLM 输出一个结构化的策略池推荐结果调度中心再结合实时健康状态过滤掉当前不可用的通道。执行层并行去跑候选通道把各自的结果汇总选择置信度最高的结果返给接入层。反馈层异步记录结果用于后续更新该策略池在同类请求上的历史成功率。这套流程跑顺之后实际完成一个普通请求的最短路径在 200ms 左右大部分耗时花在执行层模型推理上决策层加进去的额外开销非常低。2.3 关键考量什么样的流量该走 LLM 决策LLM 推理并不是免费的即便用本地部署的小模型也会占用显存和延迟预算。所以我不会让每个请求都走 LLM 决策而是在前面加了一道快速通道过滤。什么流量适合直接走规则反射表呢我总结了几条经验结构高度固定、且在过去一段时间成功率稳定在预期阈值以上的类型。这些类型的上下文其实没有多少新鲜信息用规则引擎完全可以覆盖。比如某种黑白背景的 4 位数字验证码近一周成功率稳定在 98%就没有必要每次都去问 LLM直接路由到自研 CNN 通道即可。什么流量必须走 LLM 呢我遇到最多的情况是新的验证码样式刚上线还没有足够的成功率统计数据或者交互指令里包含复杂的语义逻辑或者同一张图上有多个候选目标需要按自然语言意图排序选择。这些场景都是规则引擎“没见过”的交给 LLM 做零样本推理更合理它可以根据已有知识储备推断出当前场景较合适的策略组合。我在设计时把决策层做成了可开关的如果某段时间 LLM 服务状态不佳系统可以降级成全规则模式虽然对未知类型的适应性变差但至少保证核心链路不中断。这种“快速通道为主、LLM 决策为辅”的架构是我在生产环境里验证过比较现实的一种平衡方案。3. 核心实现LLM Router 的建模与提示词工程LLM 决策中枢听起来很高级实际落地时挑战也很具体。最核心的问题是新链路到底要让 LLM 看什么、输出什么以及如何在保持效果的同时控制推理成本和延迟。3.1 LLM 的输入特征不要让它直接“看”原始验证码我踩过的第一个大坑是试图直接把验证码图片丢给多模态 LLM让它输出识别方案。想法是很自然但实际效果并不好。一方面大多数轻量级多模态模型在小尺寸扭曲字符上的细粒度识别能力并不比专门的 CNN 模型强另一方面图片推理的 token 开销很大延迟和成本都不可控。后来我调整了思路LLM Router 的输入不应该是原始像素而应该是结构化特征。它收到的是一份已经由前端处理模块提取并压缩好的 JSON 上下文。这份上下文一般包含这些字段图片基础信息宽高、文件大小、是否彩色、预估压缩率。类型预判信号由一个小型分类模型产出的概率分布比如 0.82 的概率是滑块类型0.15 的概率是图像点选。可选的文本序列候选如果前置 OCR 模型已经从图中提取了部分字符信息就把候选内容和置信度一起传过去。业务约束期望最大耗时、允许的最高成本档位。历史统计同类型请求在过去 1 小时走各通道的成功率与平均耗时。把原始图转换为这些特征之后LLM 的推理负担会小很多不再需要对每个像素做注意力计算只需要在低维特征空间做决策。实际测试下来路由决策用时可以控制在 50~100ms 内这对整体可用性影响就比较小了。3.2 提示词结构与 JSON 输出约束为了让 LLM 稳定输出结构化结果我先定义了一个统一的输出协议然后基于这个协议写提示词。输出 JSON 大致长这样{ verification_type: image_click, target_objects: [car, traffic_light], difficulty_level: 3, strategy_pool: [local_cnn_detect, capsolver_api], priority_channel: capsolver_api, reasoning_summary: 图像点选目标为车辆和红绿灯本地检测模型conf均低于0.75建议外部通道兜底 }我使用的系统提示词大概是这样的思路你是一个验证码识别调度路由决策器。你不会直接识别验证码图片而是根据上游系统提供的特征上下文选择最合适的识别策略通道。 你要输出一个 JSON包含 verification_type验证码类型、target_objects可能的目标对象列表、difficulty_level1-5、strategy_pool建议的通道池、priority_channel首选通道、reasoning_summary一句话的原因说明。 选型原则 1. 优先选择近1小时成功率最高的通道 2. 成本敏感的请求优先选本地模型 3. 本地模型置信度整体偏低时选择外部通道兜底 4. 对自然语言指令复杂的点选任务优先调用具备语义解析能力的服务。这里有一个很关键的工程细节提示词里要求 LLM “只输出 JSON”但模型在生成过程中偶尔会夹带解释性文字导致解析异常。所以我并没有完全依赖模型自觉而是在输出层加了一个格式修正器用正则把多余的说明语句剥离再尝试二次 JSON 解析。同时我要求模型开启 JSON Mode 或 Function Calling 能力如果使用的推理框架不支持就退化为“从 return 标记中截取片段”的方式。3.3 性能与成本控制小模型量化、超时保护与缓存反射表LLM 选型上我并没有直接上超大参数的云端模型而是选了能在本地用一张普通推理卡跑起来的量化开源模型。原因很简单虽然大模型理解能力更强但验证码路由决策所需的语义复杂度其实远没有到必须动用千亿级模型的地步中等规模模型完全能够胜任。换来的优势是单次推理成本极低、延迟稳定且数据不用出内网。为了更好控制成本我还在决策层加了一个反射缓存机制。刚才提到相同或高度相似的请求不用每次都走 LLM这个相似性不是按图片哈希算的而是按决策上下文的哈希值算的。只要验证码类型、目标关键词、难度分布、候选通道列表这几大核心特征一致就直接复用上一次的 LLM 决策结果不对策略池做重复推演。实际运行中反射缓存的命中率能到 40% 以上高峰期更高。这样一来 LLM 服务虽然是核心决策大脑但它并不需要每个请求都全量计算底层利用率反而更健康。4. 执行层落地的几个关键工程细节决策层说完了真正考验系统稳健性的还是执行层。LLM 推荐了一个策略池剩下的事情就是怎么把这个策略池变成一次稳定的成功识别这里面的学问不比决策层少。4.1 模型通道的注册与同时并发调用多通道架构必然要求执行层具备一个动态注册机制。每个识别能力只要遵循统一接口规范就能注册成一个通道连接器。接口规范我定义为入参是一个标准的验证码任务请求出参是一个置信度结果。对于自研模型接入比较直接模型服务化封装成独立微服务就可以。对 CapSolver 这类外部通道接入时重点处理两个问题鉴权和任务轮询。CapSolver 的调用模式通常是创建任务后客户端轮询获取结果。我把它封装成了异步 Worker通过 Redis 队列缓存任务状态如果轮询超过设定阈值就释放并失败降级。这里比较重要的经验是策略池里一旦出现外部通道和本地通道共存不要只调用首选通道而是尽量对可以并行的通道做并发调用取最优结果返回。因为有些时候本地模型在某个小类别上置信度低而外部通道刚好能处理得不错并发调用能显著提高单次请求的成功概概率代价是多消耗一些没有命中的额度。为控制成本我给并发策略设了一个开关只有在 LLM 判断难度中高时才开启并发简单任务只走首选。4.2 多结果合并策略与置信度计算并发调用带来了新问题多个通道都返回了结果听谁的我给每个结果都附加了一个统一的置信度分然后按优先级排序返回结果置信度分处理方式自研字符识别返回conf 0.9高直接采用本地目标检测返回conf 0.7~0.9中与外部通道结果比对CapSolver 通道返回自带任务成功率高与本地结果比对本地通道返回conf 0.6低基本不采用仅作兜底参考合并逻辑并不是简单地取最高分而是先看结果结构是否一致。对字符型验证码如果两个通道返回的字符串完全一致置信度可以直接叠加如果字符不一致但存在编辑距离为 1 的差异我会启用二次确认把两个候选放进一个轻量验证服务里做二次推理。对图像点选型验证码合并则看坐标框的重叠率重叠率高的框说明多个通道对目标达成了共识优先保留。这个合并策略实话说也经历过几次返工。最开始的版本是“有高分就用高分”后来发现某个本地目标检测模型对特定颜色目标存在系统性误检自信地给出了 0.93 的高分结果点错了目标。从那以后我增加了一条铁律不同类型模型之间必须做交叉验证不能只看单一分值。4.3 自研模型与 CapSolver 类外部通道的平衡这里展开聊聊大家都比较关心的成本和效果平衡问题。CapSolver 作为外部验证码处理通道在识别最新样式验证码方面有个很大的优势它背后接入了大量针对新类型的实时训练样本和人工兜底因此对新出现验证码的成功率通常比自研冷启动模型高。但它按次收费并且每次创建任务存在网络往返和队列等待时间。我的使用原则简单讲就是四句话本地能稳定解决的不上外部外部只在本地置信度低时兜底新类型上线初期可以大比例导给外部换成功率数据数据积累到一定程度后逐渐切回自研模型压低成本。比如我近期观察到某类图像点选验证码本地目标检测模型对“人行横道”这类结构化目标识别准确率可以从 82% 逐步提升到 95%但从 82 到 95 的过程需要几千个真实样本回流训练。这个过程里外部通道始终作为辅助验证来源等本地模型把成功率追上来之后外部通道的调用比例再自动降到 30% 以下。这套动态平衡策略本质上是把“成本”和“新鲜度”两个指标放在路由权重的公式里一起考量了。5. 自适应与稳定性基于反馈的动态路由系统能“自适应”才是 CapSolver AI-LLM 架构里最核心的产出。要做到自适应不是让 LLM 在单次请求上给出好策略而是让整条链路能够根据反馈结果持续更新路由决策。这是一个闭环问题。5.1 指标埋点与成功样本回收自适应的基础是数据回收。我在执行层埋了一套统一的回捞机制不分通道只要一次任务执行完成无论成功失败都向反馈队列写入一条结构化记录。记录里包含这些关键字段请求指纹、上下文的哈希、LLM 推荐是否启用、实际执行策略池、各通道返回的置信度、最终采用结果、耗时、是否成功以及失败码。这套数据被 ClickHouse 落库后既可以做实时指标监控也可以做离线样本分析。成功样本的回收还有一个更重要的用途自动生成用于模型再训练的正样本。之前很多数据要通过人工标注成本高且慢。有了回捞机制后被最终采用且通过后续业务校验确认为正确的结果会自动打上“高置信成功”的标签。一个业务侧的校验逻辑其实是天然的标签来源比人工标注重工量低很多。5.2 动态权重更新计算逻辑路权更新我采用的是一套加权滑动平均的计算方法而不是复杂的强化学习。原因很务实验证码场景波动快复杂算法训练成本高收敛速度反而不一定比简单统计更快。计算逻辑可以简化为下面这段伪代码def update_channel_score(channel, outcome, window1000): # 取出该通道最近 window 次请求的表现 recent load_recent_records(channel, window) if not recent: return default_score # 加权越近的请求权重越高 total_weight 0 score 0 for idx, record in enumerate(recent): weight 0.5 ** (len(recent) - idx) score weight * (1 if record.success else 0) total_weight weight score / total_weight # 把成本因子折算进去单位成本带来的成功率 cost_factor channel.cost_per_request return score / (1 cost_factor * cost_penalty)计算完各通道得分后路由时把得分做一次 softmax 归一化就能得到流量分配比例。这样处理的好处非常明显当某个本地 CNN 通道成功率达到 98% 且成本很低时权重会自然升高到接近 1流量会稳定导给它当它因为验证码样式升级而成功率骤降时权重会快速回落流量被转移到权重更高的 CapSolver 通道。整个过程不需要人工干预。5.3 灰度发布与自动熔断设计权重更新再智能也存在模型效果突变的可能所以必须有退路机制。我在发布新版本模型时不会直接全量切流量而是先把版本标记为 shadow 状态以一定比例参与执行但不影响最终结果主要用于采集它在真实流量上的表现数据。等表现稳定之后才转为 active。自动熔断是针对单通道故障设计的。比如某外部通道因为账号欠费或者接口升级导致大量任务失败如果继续导流只会白白浪费成本和机会所以我给每个通道设定了连续失败阈值一旦指标超限就自动熔断会暂停向该通道分配流量同时告警通知值班人员。有人会问流量被熔断之后怎么办这就要靠策略池的冗余性了。LLM 推荐的策略池里通常会包含至少两个通道当首选通道熔断时调度器会自动顺延到次选通道。在设计上策略池就是用来吸收这种单点故障的所以刚开始注册通道时就应该有意识地保障同类型能力至少有两条可用路径。6. 上线后踩过的坑与排查参考这套系统上线后踩过不少坑。我把一些比较典型的问题整理出来也算给正在做类似架构的同学一些参考。6.1 LLM 输出格式漂移导致调度异常现象某次上线后决策层偶尔返回 504 错误排查发现 LLM 服务正常但解析层频繁抛 JSON 解析异常。打开原始日志才发现模型在部分请求里输出了类似“好的下面是我的判断”这样的开头后面才跟着 JSON。更夸张的是有一次把 JSON 包在 Markdown 代码块里输出。解决方案干脆不信任裸 JSON 输出强制接入 JSON Mode。如果推理框架不支持 JSON Mode就在解析前直接用正则提取最外层的花括号片段再丢给 json.loads如果失败就返回“决策失败走默认路由”绝不让单次解析失败拖垮整条请求。6.2 图片样本质量波动导致误判现象执行层接入的本地模型在白天成功率正常到了晚间却下降明显。后来查了原因晚间采集的图片有更多低亮度场景图像增强的前置模块没有针对夜间样本做过优化导致传给模型的特征偏差很大。解决方案前置处理模块增加了一个质量感知分支。对亮度、对比度、噪点水平做统计一旦检测到低质量输入自动切换到专门针对该类样本的增强流程同时在上游上下文里显式标记 image_qualitylow让 LLM 决策时能感知到质量信号优先选择对噪声鲁棒性更强的 CapSolver 通道。6.3 多通道并发把上游接口打崩了现象为了避免本地模型单一失败并发策略开得比较激进。某次流量高峰系统同时向外部通道创建了大量任务直接把对方接口打到了限流。当然后续任务全部超时成功率反而不升反降。解决方案给外部通道加上本地令牌桶限流并且限制单请求并发创建的外部任务数上限。如果外部通道已经排队严重调度器会降低策略池中外部通道的排序权重不能让外部通道请求堆积影响整体时延。6.4 回调链路出现“重复结果”事故现象执行层对结果做回调时因为客户端超时重试导致上游同一个请求被提交了多次业务侧收到重复冗余结果部分流程出现幂等异常。解决方案接入层生成的请求指纹被下推到整个调用链路的幂等键执行层和回调层统一用这个指纹做去重。上游收到重复提交时先查 Redis如果已经存在同指纹处理结果直接返回旧结果不再新建任务。问题现象根因解决措施决策层 504 和 JSON 解析失败LLM 输出不可控包含多余文本启用 JSON Mode正则截取二次解析默认降级不同时段成功率波动大图片质量分布变化单一增强流程不适用增加质量感知分支在决策上下文中标注质量状态外部通道被限流并发策略过激请求堆积本地令牌桶限流动态降低外部通道排序权重上游收到重复结果客户端超时重试引发重复提交请求指纹幂等消费前统一去重架构跑到现在我个人的体会是验证码识别系统到了一定规模识别模型的单点精度其实只是基础真正的工程分水岭在上面那层“怎么选、怎么调度、怎么回捞、怎么自适应”的架构能力。LLM 在这里扮演的是一个很有意思的角色它不需要真的把人家的图认出来只负责用手头的特征信号把系统领到最优路径上就像一个见过很多场景的调度员。这种把“识别”和“决策”分开的做法让每一层都可以独立演进也更适合工程团队长期迭代。最后再分享一个适配过程中的建议不要一开始就追求全自动闭环先把一条最简单的链路跑通比如自研模型一个外部通道让回捞和权重计算都动起来再逐步把新模型接入策略池。每接入一个新通道时都带着灰度开关等确认它对整体成功率的贡献是正的再放开流量。这套打法的容错空间大得多上线风险也会低不少。
