ARTICLE DETAIL

资讯详情

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

高并发智能客服的LangChain实践:流控、排队与语义降级

高并发智能客服的LangChain实践:流控、排队与语义降级 做智能客服这行最怕的不是模型答错而是模型还没答呢整个服务先被流量冲垮了。我有一年在电商大促期间值班眼看着监控面板上的QPS从几十冲到几百机器人回复从秒回变成几十秒超时用户排队队列越滚越长后台工单爆炸最后所有流量只能切给人工客服那场面实在不想经历第二次。用LangChain搭一个能跑通文档问答的客服demo确实不难难的是把它放到生产环境里扛住真实的并发压力。今天这篇东西我把实际踩过的坑、用过的方案和最终沉淀下来的做法整理出来核心围绕三个关键词展开流控、排队、语义降级。如果你正准备用LangChain做智能客服或者已经在线上被流量折磨过这篇文章应该能帮你省掉不少弯路。1. 高并发智能客服的流量从哪来为什么要分层治理1.1 一条客服请求在系统里经历了什么一次典型的LangChain客服请求链路大概是这样用户消息进入会话管理器然后交给检索器从向量库或知识库里召回相关文档拼装Prompt之后调用大模型最后把答案返回给前端。看起来简单但每个环节都可能成为瓶颈。我拆解过一个实际客服系统的瓶颈分布主要卡在三个地方。第一是模型提供方的接口配额OpenAI、通义、文心这类商用模型都有每分钟请求数或Token数限制这是最硬的墙。第二是中间数据链路包括向量检索的QPS、知识库接口的响应速度如果企业内部知识库挂在OpenWiki或者自建Wiki系统上检索接口一旦扛不住整个链路都会等在那里。第三是会话状态管理多轮对话要把历史记录存下来Redis和内存的读写也会在高并发下出现延迟抖动。很多团队一开始只盯着大模型本身觉得只要模型够聪明客服就靠谱结果压测时模型还没打满前面这些中间环节先被拖崩了。所以做高并发客服系统的第一步不是优化Prompt而是把整条链路的瓶颈都摸清楚。1.2 不能把压力全部转嫁给大模型有一种很自然的想法既然大模型那么强所有请求都发给它不就行了真这么干线上大概三天就会出事故。原因有三个。成本扛不住。大模型按Token计费一次客服对话可能要消耗几千Token并发一高账单几天就能跑出一个可观的数字。延迟扛不住。商用模型接口响应时间通常在一到三秒高峰期会更高如果所有请求都直接打到模型上用户等十几秒都算正常。稳定性更扛不住。模型提供方的限流策略是黑盒的你没法控制它什么时候返回429或者5xx一旦触发限流所有请求都会跟着失败。理解这事可以用收费站的例子收费站如果不控制进入高速的车流量所有车都挤在入口反而谁也别想走。大模型就是高速路它的通行能力是固定的调用方必须自己做好流量整形把请求均匀地放进去而不是一股脑全塞给它。1.3 分层治理的整体设计既然瓶颈分散在不同环节治理也必须分层做。我最终沉淀下来的方案分成三层层级核心组件职责网关层Sentinel / Nginx接口级QPS限制、热点参数限流、并发线程数控制应用层令牌桶 Redis队列 降级策略平滑流量、排队削峰、LLM不可用时语义降级模型层LangChain Retry Fallback控制超时、重试退避、多模型切换网关层解决的是“别让太猛的流量打进来”应用层解决的是“打进来的流量怎么排队处理”模型层解决的是“模型挂了怎么兜底”。三层各管各的事不要混在一起。这套设计和LangChain本身能力强弱关系不大核心是把架构思维想清楚。很多教程只教你怎么调LangChain的API但到了生产环境真正决定系统能不能扛住高并发的恰恰是这些工程化能力。2. 核心机制一令牌桶流控与LangChain侧并发治理2.1 令牌桶算法到底在干什么流控最常用的算法是令牌桶。它的思路不复杂系统以固定速率往桶里放令牌桶的容量有限每个请求进来要拿一个令牌才能继续处理桶里没令牌了就直接拒绝或排队等待。我用停车场栏杆来类比这个逻辑。栏杆后面有一块能停N辆车的缓冲区每秒钟有一辆车从这个缓冲区放出去。如果一下子来了十辆车缓冲区放不下后面的车就只能在外面的路上排队。缓冲区就是令牌桶每秒放行的车辆数就是速率限制。实际实现时可以用现成的库不用重复造轮子。在Python项目里我常用pyrate-limiter或者自己写个简单的异步令牌桶核心代码不超过五十行import asyncio import time class TokenBucket: def __init__(self, rate: float, capacity: int): self.rate rate # 每秒放入令牌数 self.capacity capacity # 桶容量 self.tokens capacity self.updated_at time.monotonic() async def acquire(self): while True: now time.monotonic() self.tokens min( self.capacity, self.tokens (now - self.updated_at) * self.rate ) self.updated_at now if self.tokens 1: self.tokens - 1 return await asyncio.sleep(0.01)为什么强调用令牌桶而不是简单的计数器因为令牌桶允许短时间内的突发流量只要桶里有存货就能放行适合客服这种偶尔来一波集中咨询的场景。计数器限流则是一刀切超过阈值直接拒绝体验上生硬很多。这里顺便提一个我看过很多遍的类比CAN总线协议里的流控帧有个参数叫STmin表示发送端两个连续帧之间的最小间隔。它的本质也是限制发送速率防止接收方处理不过来。流控不管在哪个技术栈里核心思路都是相通的控制发送方的速度保护接收方的处理能力。2.2 网关层参考Sentinel做接口级流量治理如果项目是基于Spring Cloud或者微服务架构网关层我很推荐参考阿里巴巴开源的Sentinel来做流量治理。Sentinel的流控规则支持QPS阈值、并发线程数控制还有三种流量控制效果快速失败、预热模式、排队等待。以智能客服的对话接口为例我会对/api/chat配置这样的规则QPS阈值为200超过阈值的请求进入排队等待模式超时时间设置为5秒。核心配置大概这样# sentinel flow rule 配置 resource: /api/chat grade: 1 # 1表示按QPS count: 200 # 每秒钟最多放行200个请求 controlBehavior: 2 # 2表示排队等待 maxQueueingTimeMs: 5000 # 排队最长等待5秒这个配置解决的是“量”的问题。如果某次大促瞬间涌入几千个请求Sentinel会按照每秒200的速率放行多余请求在网关层排队5秒内排不到就直接返回“当前咨询人数较多”。这样后面的LangChain服务和模型接口都不会被打爆。不过Sentinel只做接口级治理还不够。客服场景有个特殊点同一个用户连续发送的消息最好能单独限制。比如一个用户可能因为操作失误疯狂点发送按钮一秒钟发了几十条重复消息这种流量如果也放过去既浪费模型额度又把队列塞满。Sentinel的热点参数限流正好解决这个问题可以对请求参数中的userId做热点维度限流每秒最多5次resource: /api/chat grade: 1 count: 5 paramIdx: 0 # 请求参数中userId所在的位置2.3 应用层LangChain调用侧的并发控制网关层挡完一轮之后到了应用层还要再做一次并发控制。原因很简单网关限流保护的是接口本身但LangChain内部还会调用检索服务、向量数据库、大模型API这些下游资源的并发上限可能比网关QPS限制还要低。我在应用层用的是最原始的信号量方案。把同时进入LangChain链路的请求数限制在固定数量比如20个剩下的请求先在内存队列里等。这样做的目的是确保同一时刻不会有过多的请求去抢大模型的连接池。import asyncio from langchain_openai import ChatOpenAI from langchain.schema import HumanMessage llm ChatOpenAI( modelgpt-4o-mini, temperature0.2, request_timeout15, # 单次请求超时15秒 ) sem asyncio.Semaphore(20) async def chat_with_llm(user_input: str) - str: async with sem: response await llm.ainvoke([HumanMessage(contentuser_input)]) return response.content信号量的数量怎么定我一般根据模型接口允许的并发数和单次请求的平均响应时间反推。如果模型侧允许100个并发单次请求平均1秒那信号量设成80左右留出20的余量给突发延迟。不要贪心设满否则一旦模型响应变慢所有请求会同时积压在这80个信号量上最后集体超时。重试策略也要在这个层面做。LangChain自带重试机制但默认策略在高并发场景是灾难。我建议自己控制重试重试最多3次避免无限重试使用指数退避每次重试间隔翻倍并加随机抖动只在网络错误、超时、限流HTTP 429时重试业务逻辑错误不要重试重试多少次都不会成功import random import asyncio async def chat_with_retry(llm, messages, max_retries3): for attempt in range(max_retries): try: return await llm.ainvoke(messages) except Exception as e: if attempt max_retries - 1: raise wait_time 2 ** attempt random.uniform(0, 0.5) await asyncio.sleep(wait_time)3. 核心机制二排队策略与用户体验的平衡3.1 用排队论估算等待时长流量超过处理能力之后排队是必然的。问题是怎么让排队的体验不那么糟糕。至少用户要能知道“我前面还有多少人、大概等多久”而不是看到一片空白或者直接报错。排队论的M/M/c模型特别适合分析这种情况。把客服请求看成顾客每个LangChain worker看成服务台请求按泊松过程到达服务时间服从指数分布。核心参数就三个到达率λ、单个worker的服务率μ、worker数量c。我写过一个非常简单的Python脚本用来估算不同参数下的排队情况import math def erlang_c(c, a): # Erlang C 公式所有服务台都忙的概率 sum_terms sum(a**k / math.factorial(k) for k in range(c)) term (a**c / math.factorial(c)) * (c / (c - a)) return term / (sum_terms term) # 参数每秒20个请求 # 每个worker每秒能处理5个请求(平均服务时间200ms) lam 20 # 到达率 mu 5 # 服务率 c 6 # worker数量 a lam / mu rho lam / (c * mu) # 服务强度 print(f服务强度 rho {rho:.2f}) if rho 1: c_prob erlang_c(c, a) wq c_prob / (c * mu - lam) # 平均等待时间 print(f等待概率 {c_prob:.2%}) print(f平均等待时间 {wq*1000:.0f} 毫秒) else: print(系统已过载排队时间会无限增长)这个脚本跑出来的数字很有参考意义。当服务强度ρ小于0.6时等待概率很低系统很空闲当ρ超过0.8等待概率快速上升一旦ρ超过1意味着请求到达的速度超过系统处理能力队列会无限增长无解。所以排队策略的第一步是监控这个ρ值。如果服务强度长期高于0.8就该扩容worker了。3.2 基于Redis的排队队列实现很多人一说到排队就想到上MQKafka、RabbitMQ一股脑都用上。其实对于智能客服这种场景Redis的List结构完全够用而且更轻量。我的实现思路是酱紫请求先入Redis Listworker从List里BRPOP取任务同时用一个Hash记录每个用户的排队信息前端轮询时查询自己在队列中的位置。import redis import json import time r redis.Redis(hostlocalhost, port6379, decode_responsesTrue) def enqueue_request(user_id, session_id, content): task { user_id: user_id, session_id: session_id, content: content, timestamp: time.time(), } # LPUSH BRPOP 实现队列 r.lpush(chat:queue, json.dumps(task)) # 记录该用户排队信息 r.hset(fchat:queue:info:{user_id}, mapping{ status: waiting, enqueue_time: time.time(), }) def get_queue_position(user_id): # 扫描队列统计在该用户之前的人数 queue_items r.lrange(chat:queue, 0, -1) position 0 for item in queue_items: task json.loads(item) if task[user_id] user_id: return position position 1 return NoneRedis List天然支持LPUSH和BRPOP组合BRPOP会阻塞等待worker空闲时才会取到任务不会空转。最重要的是Redis的LLEN命令可以O(1)获取队列长度前端拿这个数据做排队进度展示非常方便。排队策略里有个细节容易被忽略超时机制。用户排了十秒还没处理到前端应该主动提示“当前排队人数较多是否继续等待”而不是让用户一直等下去。我会在用户排队超过5秒时启动降级逻辑超过10秒直接返回一个可用的兜底答案或者转人工入口。3.3 会话黏滞与优先级处理智能客服和普通HTTP接口不一样同一个用户的多轮问题是强关联的。用户问“我的订单发货了吗”机器人回答完用户追问“那退货运费呢”这需要知道上文提到的订单号。如果把同一个用户的多个请求分散到不同worker处理上下文就断了。解决方法是会话黏滞。每个请求进入队列时带上session_id消费端的worker在处理前先判断当前是否已有其他worker在处理同一个session如果是就把请求重新塞回队列或者等待。实际项目里我用Redis的分布式锁来做会话粒度互斥def process_task(user_id, session_id): lock_key flock:session:{session_id} # 尝试获取锁过期时间设置为60秒 acquired r.set(lock_key, 1, nxTrue, ex60) if not acquired: # 该会话正在被其他worker处理重新入队 r.lpush(chat:queue, json.dumps({ user_id: user_id, session_id: session_id, retry: True, })) return try: # 从Redis中读取该session的历史对话 history r.lrange(fhistory:{session_id}, 0, -1) # 调用LangChain处理 answer process_with_langchain(history, user_input) # 更新历史 r.rpush(fhistory:{session_id}, json.dumps({ role: user, content: user_input })) r.rpush(fhistory:{session_id}, json.dumps({ role: assistant, content: answer })) finally: # 处理完释放锁 r.delete(lock_key)优先级问题也不能忽视。普通用户和VIP用户排在一个队伍里VIP用户一定会投诉。我用Redis ZSet做优先级队列分数就是优先级加上排队序号VIP用户分数低优先被取走。实操上我建议优先级不要分太多级别两三级就够VIP、普通、异常用户。分级太多会让普通用户永远等不到服务反而引发大规模投诉。4. 核心机制三语义降级让服务“能答而不是报错”4.1 降级不是简单返回系统繁忙流控和排队解决的是“流量进来怎么处理”的问题但还有一类场景需要单独设计模型不可用了怎么办。这里说的“不可用”不只是大模型接口挂掉还包括超时、限流、网络抖动。很多团队的降级策略非常简单粗暴——直接返回“系统繁忙请稍后再试”。这种降级在用户眼里等于没处理问题没解决还要再问一遍体验极差。我早期踩过的坑就是这里。有一次模型供应商限流线上所有请求降级成固定文案结果客服工单量在那个小时暴增了三倍因为用户得不到答案全都转人工了。后来我把降级思路换成了“尽力作答”即使不能调大模型也要想办法给用户一个尽量有用的答案。降级不是断崖式切断而是分级别逐步降每一级都尽量保留一部分服务能力。4.2 LangChain的with_fallbacks怎么用LangChain自带的with_fallbacks是天然为这种情况设计的。它的作用是为一个调用链绑定多个备用实现主链失败时自动尝试备链。在客服场景里我配置过三级降级链路from langchain_openai import ChatOpenAI from langchain_ollama import ChatOllama # 主模型商用模型效果最好 primary_llm ChatOpenAI( modelgpt-4o-mini, temperature0.2, request_timeout10, ) # 一级降级本地部署的开源模型效果稍差但完全可控 fallback_llm ChatOllama( modelqwen2.5:7b, temperature0.2, request_timeout10, ) # 二级降级基于知识库检索的规则回答 from langchain_core.runnables import RunnableLambda def rule_based_answer(query: str) - str: # 从知识库检索最相似FAQ返回预置答案 faq search_faq(query) if faq: return faq[answer] return 当前人工客服繁忙已记录您的诉求稍后短信回复。 chat_chain ( primary_llm .with_fallbacks([fallback_llm, RunnableLambda(rule_based_answer)]) )这段代码的意思是先试商用大模型失败后试本地模型再不行就走FAQ知识库检索。每降一级回答质量差一些但至少用户在多数场景下能得到答案而不是一句“系统繁忙”。要注意的是with_fallbacks触发条件是主链抛异常判断哪些异常需要触发降级这点要格外小心。业务侧的自定义异常不应该触发模型降级否则一些本来不应该重试的用户请求会把降级链路也打满。4.3 RunnableBranch做意图路由降级除了模型降级还有一层更重要、也更容易被忽略的降级——意图层的降级。大模型是负责理解用户意图的核心如果大模型不可用我们可以退而求其次用规则和关键词匹配来理解意图再走对应的处理逻辑。LangChain的RunnableBranch可以实现这个效果。它类似于编程语言里的switch-case根据条件把请求路由到不同的处理链from langchain_core.runnables import RunnableBranch, RunnableLambda def classify_intent_with_rules(query: str) - str: # 基于规则的意图分类不需要大模型 if 发货 in query or 物流 in query: return track_order if 退款 in query or 退货 in query: return refund if 人工 in query or 客服 in query: return human return general_faq # 规则意图识别作为降级方案 fallback_classifier RunnableLambda(classify_intent_with_rules) # 各意图对应的处理链 order_chain RunnableLambda(lambda query: query_order_status(query)) refund_chain RunnableLambda(lambda query: query_refund_policy(query)) faq_chain RunnableLambda(lambda query: search_faq_answer(query)) human_chain RunnableLambda(lambda query: 正在为您转接人工客服请稍候...) # 意图路由分支 branch RunnableBranch( (lambda x: x[intent] track_order, order_chain), (lambda x: x[intent] refund, refund_chain), (lambda x: x[intent] human, human_chain), faq_chain # 默认分支 ) def get_intent_and_route(query: str): try: # 优先用LLM识别意图 intent llm_intent_classify(query) except Exception: # 大模型不可用时用规则分类 intent classify_intent_with_rules(query) return branch.invoke({intent: intent, query: query})这套规则降级方案能覆盖多少场景以电商客服为例大概60%的常见问题都集中在物流查询、退款政策、发票开具、活动咨询这几个意图上写十几条关键词规则就能覆盖大部分。对于剩下的长尾问题降级后引导用户留下联系方式转人工处理比抛出一句“系统繁忙”强太多。我还做了一层更细的降级多轮上下文缩减。当模型超时不可避免时尝试把历史对话从完整列表缩减到最近一轮或两轮再发给模型。很多客服问题的回答只需要依赖最近一轮对话历史太长反而拖慢响应速度。5. 实操一个可复现的高并发客服骨架5.1 核心环境与依赖准备上面讲了很多方案最终要落到代码上。我整理了一个精简但完整的高并发客服骨架包含流控、排队、降级三部分可以直接参考改造。依赖清单大致如下pip install langchain langchain-openai redis pyrate-limiter这个骨架用的是LangChain框架配合FastAPI提供HTTP接口Redis负责队列管理pyrate-limiter做令牌桶限流。不依赖太重的基础设施本地就能跑起来验证逻辑。5.2 流控排队降级完整链路代码下面是核心代码框架。为了不拖沓我直接给出可运行的简化版本import asyncio import json import time import redis from fastapi import FastAPI, Request from pyrate_limiter import BucketFullException, Duration, Rate, Limiter, MemoryListStorage from langchain_openai import ChatOpenAI from langchain.schema import HumanMessage app FastAPI() r redis.Redis(hostlocalhost, port6379, decode_responsesTrue) # 1. 流控每秒钟最多放行30个请求桶容量50 limiter Limiter( Rate(30, Duration.SECOND), storageMemoryListStorage(), bucket_namechat_bucket, ) sem asyncio.Semaphore(20) # 2. LLM实例 llm ChatOpenAI( modelgpt-4o-mini, temperature0.2, request_timeout10, ) # 3. 降级备用模型 from langchain_ollama import ChatOllama fallback_llm ChatOllama(modelqwen2.5:7b, temperature0.2) # 业务降级函数 def business_degrade(user_id: str, query: str) - dict: if 物流 in query or 发货 in query: return {answer: 请提供订单号我为您查询物流信息。, degraded: True} if 退款 in query or 退货 in query: return {answer: 您可以在订单页面申请售后审核周期1-3个工作日。, degraded: True} return {answer: 当前咨询量较大已记录您的需求人工客服将尽快回复。, degraded: True} # 核心处理函数 async def handle_message(user_id: str, content: str) - dict: # 流控令牌桶拦截 try: limiter.try_acquire(user_id) except BucketFullException: return { answer: 当前咨询人数较多请稍后重试。, queue_info: None, } # 排队入Redis队列 task {user_id: user_id, content: content, ts: time.time()} r.lpush(chat:queue, json.dumps(task)) # 模拟worker消费真实项目中这里是独立进程/线程组 async with sem: queue_item r.rpop(chat:queue) if not queue_item: return {answer: 系统繁忙请稍后重试。, queue_info: None} task json.loads(queue_item) query task[content] try: # 主链路LangChain LLM response await llm.ainvoke([HumanMessage(contentquery)]) return {answer: response.content, degraded: False} except Exception: try: # 降级链路1本地模型 response await fallback_llm.ainvoke([HumanMessage(contentquery)]) return {answer: response.content, degraded: True} except Exception: # 降级链路2规则回答 return business_degrade(user_id, query) app.post(/api/chat) async def chat(request: Request): body await request.json() user_id body.get(user_id) content body.get(content) if not user_id or not content: return {error: missing params} result await handle_message(user_id, content) return result这个骨架把本章前面讲的三块机制串起来了令牌桶限制进入频率Redis List做排队线程信号量限制并发LLM异常时依次降级到本地模型和规则回答。放到真实项目中worker消费逻辑应该独立成一个进程池而不是和HTTP请求处理耦合在一起但整体思路是一致的。5.3 容量评估与压测观察点部署之前一定要做容量估算。有个简单公式可以先用起来并发窗口数 QPS × 平均响应时间秒。举个例子目标QPS是50单次请求平均耗时1秒那系统至少要能承受50个并发请求同时处理。如果平均耗时2秒并发窗口就是100。这个数算出来之后再看你的worker数量够不够支撑不够就扩容或者调低目标QPS。压测时重点观察四个指标指标健康阈值超标处理P95响应时间小于3秒增加worker数量或优化检索链路排队长度不超过队列容量80%开启更严格的流控降级比例小于5%超过5%说明容量不足需要扩容错误率小于0.1%检查超时和重试策略我见过很多团队压测只关心QPS能不能扛住忽略降级比例。其实降级比例更能反映真实体验——如果10%的请求都降级成规则回答了用户感受到的“智能客服”基本就是个人工智障口碑会崩得很快。6. 常见问题与排查技巧实录6.1 重试风暴重试不当反而拖垮系统重试是LangChain和高并发场景里最容易埋雷的地方。我之前在压测时遇到过现象模型接口明明已经返回限流错误但所有请求都在同一时间点重试导致限流更加严重形成恶性循环。后来排查发现默认重试策略是高并发场景的灾难。不加退避、不加重试上限、不做随机抖动就会产生“重试风暴”。我的对策很简单重试上限3次指数退避从2秒开始每次加0到0.5秒的随机抖动把同时重试的请求冲散。另外要区分哪些错误值得重试。网络超时、连接断开、限流错误可以重试模型返回的业务错误比如ContextLengthExceeded、InvalidRequest重试一万次也没用必须直接走降级。6.2 超时设置与流控参数容易忽视超时设置如果全链路不统一会出很隐蔽的问题。前端请求超时5秒网关超时10秒LangChain调用大模型的超时也设10秒那前端已经超时断开了后端还在默默等待资源白白浪费。正确的做法是超时时间形成梯度前端超时最短网关次之应用调用模型的最长。比如前端3秒网关5秒模型调用8到10秒。这样请求在每一层都能被有效切断不会出现无效请求一直占用资源的情况。流控参数也值得检查。回到之前提过的STmin类比一旦设置过小发送端会因为发送过快导致接收端丢帧网络协议层面同样需要限速。应用层流控参数一定要结合服务端的实际处理能力来定不要凭感觉随便填。6.3 LangChain还是LangGraph别一上来就上重框架这个话题在很多LangChain面试题里出现但施工时很容易踩坑。简单单轮问答、文档检索、ChatPromptTemplate加一个LLM用LangChain就够多轮复杂对话、状态机流转、人工介入审核、需要循环和条件跳转的工作流才值得上LangGraph。早期我也犯过“技术越复杂越好”的毛病客服系统第一版就用上了LangGraph的状态图结果维护成本极高。后来发现大部分用户请求是单轮或双轮根本不需要图编排用LangChain的链式调用就够了。技术选型要根据业务复杂度来不是越重越好。6.4 RunnableParallel不是“高并发”的银弹面试里常见的一个问题LangChain里的RunnableParallel能提高并发能力吗它能做的是让多个互不依赖的步骤并行执行。比如同时做知识库检索和意图识别两个操作可以在同一个请求里并行跑缩短单次响应时间。但它是“单个请求内部并行”而不是“多个请求同时处理”。高并发是系统级的QPS能力要靠架构层面的水平扩容、流控、排队来保证一个RunnableParallel改变不了这个事实。把单请求从串行改成并行可以减少响应时间却不会让系统单位时间能处理的请求数变多。这个问题理清楚很多架构设计上的误区就能避开。最后分享一点个人体会。很多人以为智能客服的核心是大模型够不够聪明实际做下来发现真正决定体验的反而是工程上的细节排队时用户能不能看到进度模型挂了能不能给出兜底答案重试会不会引发雪崩。先把流控、排队、降级这套地基打扎实再上LangChain做智能增强系统才能稳稳跑住业务。能跑在生产环境里的客服系统靠的不是某一个炫酷模型而是这套一层一层兜住的工程能力。
返回列表