ARTICLE DETAIL

资讯详情

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

生产级Agent后端六大核心实践:SSE、Redis状态中枢与Trace贯通

生产级Agent后端六大核心实践:SSE、Redis状态中枢与Trace贯通 1. 这不是Bug是生产级Agent系统必经的“成人礼”“Agent上线第二天就给用户退了两次款”——这句话刚在内部群弹出来时我正盯着监控面板上那条突兀的红色告警曲线。没有惊慌反而下意识点了杯咖啡。干了十年后端见过太多团队把Agent当“智能脚本”上线结果在支付、库存、风控这些真实业务链路上栽得又快又狠。这不是代码写错了而是整个系统对“流式响应状态一致性可观测性”这三座大山缺乏敬畏。标题里说的“6课”不是理论课是拿真金白银交的学费第一次退款是因为SSE连接被Nginx默认60秒idle timeout无情切断而下游服务没收到完整事件流误判为订单失败第二次是Redis分布式锁在集群脑裂场景下失效两个Worker同时处理同一笔退款请求重复出款。你可能觉得“加个重试就行”但生产环境里重试不是万能解药——它可能把单点故障放大成雪崩。真正的生产级Agent后端核心不在于多酷的AI模型而在于如何让流式通信像自来水一样稳定、让状态变更像银行记账一样原子、让每一次调用都像手术刀一样可追溯。这6课每一课都对应一个具体故障现场、一段真实日志、一次线上回滚操作。下面拆解的不是抽象概念而是我带着团队在凌晨三点复盘时白板上画满的流程图、删掉又重写的Redis Lua脚本、以及最终压测通过时监控面板上那条平稳的绿色trace链路。2. 核心设计逻辑为什么Agent后端不能照搬传统Web架构2.1 流式通信SSE与传统HTTP的本质冲突传统REST API是“请求-响应”模型客户端发一个POST服务端处理完返回200JSON连接关闭。而Agent的核心交互模式是SSEServer-Sent Events它建立的是长连接、单向、持续推送的通道。浏览器发起一个GET请求服务端保持TCP连接打开不断往这个连接里写入data: {...}\n\n格式的消息直到客户端断开或超时。这种模式天然与现代基础设施存在三重摩擦第一重是反向代理层的timeout陷阱。Nginx默认配置中proxy_read_timeout是60秒意味着如果60秒内后端没向客户端发送任何数据Nginx会主动关闭连接。Agent场景下用户提问后LLM可能需要3-5秒生成第一个token中间这几十秒的“静默期”Nginx就判定为idle timeout直接断开。客户端收到stream disconnected before completion: idle timeout waiting for sse错误前端SSE库自动重连但重连后服务端并不知道这是同一个会话可能重新触发一次完整推理导致重复扣款、重复下单。我见过最惨的案例一个客服Agent在用户等待期间被Nginx断了7次重连7次最终生成了7条相同回复系统误以为是7个独立请求给用户发了7张优惠券。第二重是负载均衡器的连接亲和性缺失。当用户SSE连接被分发到某台Worker节点A而A节点因GC暂停或网络抖动暂时无法发送数据LB不会感知到这个“假死”状态仍会把后续心跳包或重连请求继续打到A。更糟的是如果A节点宕机LB将新连接分发到B节点但B节点没有A节点内存中的会话上下文比如正在处理的订单ID、临时生成的加密密钥整个流式对话就断了。传统Web应用可以靠Session Cookie或JWT续命但SSE的流式状态是活在内存里的无法简单传递。第三重是客户端重连策略的不可控性。浏览器原生SSE API的EventSource对象在连接断开后会默认等待几秒再重连但这个间隔是指数退避的且不同浏览器实现有差异。用户看到的可能是“消息卡住→页面刷新→重新提问”而服务端视角却是“一个会话突然消失→另一个全新会话开始”。这对需要严格顺序保证的业务如支付确认、库存锁定是灾难性的。所以我们放弃“让SSE适配现有架构”的思路转而构建SSE-aware的基础设施层。核心原则只有一条所有与SSE会话相关的状态必须脱离单机内存下沉到共享存储并由统一网关协调。这意味着不能把SSE连接直接暴露给业务Worker而必须经过一层“SSE Session Manager”。2.2 Redis为何成为Agent状态中枢而非简单缓存提到Redis很多人第一反应是“缓存热点数据”。但在Agent后端Redis扮演的是分布式状态总线角色。它的价值不在于速度快而在于其数据结构与原子操作的组合恰好能解决Agent特有的状态协同难题Stream数据结构这是Redis 5.0引入的专为消息队列设计。它天然支持“消费组”Consumer Group允许多个Worker订阅同一个Stream每条消息只会被组内一个Worker处理且处理进度pending list由Redis维护。我们把Agent的每个用户会话抽象为一个Stream例如stream:session:abc123。当用户提问前端通过API网关将问题推送到该Stream后台Worker从Stream中拉取消息处理完成后将结果如LLM输出的token流再推送到另一个stream:result:abc123。这样即使Worker A挂了Worker B也能从pending list中接管未完成的消息保证“至少一次”交付。这比Kafka轻量比RabbitMQ简单且完全避免了消息重复投递——因为Stream的ACK机制是精确到每条消息的。Hash Lua脚本实现强一致性状态机Agent的会话状态如status: processing,last_activity_ts: 1718923456,pending_tokens: 12不能存在数据库里因为读写延迟太高。我们用Redis Hash存储但关键操作如“更新状态并检查是否超时”必须原子执行。例如当Worker开始处理一个请求需要同时做三件事1) 将状态设为processing2) 更新最后活跃时间3) 设置一个过期时间防止Worker崩溃后状态永远卡住。这三个操作如果分开执行中间任何一步失败都会导致状态不一致。解决方案是封装一个Lua脚本-- update_session_state.lua local key KEYS[1] local new_status ARGV[1] local expire_sec tonumber(ARGV[2]) redis.call(HSET, key, status, new_status) redis.call(HSET, key, last_activity_ts, ARGV[3]) redis.call(EXPIRE, key, expire_sec) return redis.call(HGETALL, key)通过EVAL命令一次性执行确保状态变更的原子性。这个脚本被我们称为“会话状态引擎”所有Worker都必须通过它来修改会话状态。Pub/Sub用于跨服务通知当一个Worker完成某个复杂任务如调用外部支付API需要通知前端SSE连接更新UI状态。我们不直接让Worker向SSE连接写数据这需要Worker知道哪个连接属于哪个用户耦合太重而是让Worker向Redis Pub/Sub频道channel:notify:abc123发布一条消息。SSE Session Manager作为该频道的Subscriber收到消息后精准地向对应用户的SSE连接推送事件。这样业务Worker彻底解耦只负责“干活”不关心“通知谁”。提示不要用Redis String存会话状态。String虽然简单但无法原子性地更新多个字段。当需要同时更新status和last_activity_ts时String只能覆盖整个值极易引发竞态条件。Hash是唯一正确的选择。2.3 Trace不是锦上添花而是Agent系统的“黑匣子”“metrics、trace、logs”常被并列为可观测性三支柱但在Agent场景下trace是绝对核心。Metrics告诉你“系统慢了”Logs告诉你“某行代码报错了”而Trace告诉你“慢在哪一环、错在哪个分支、数据怎么在服务间流转”。一个典型的Agent调用链路可能是前端SSE连接 → API网关 → 会话路由服务 → LLM推理服务 → 支付微服务 → Redis状态更新 → SSE推送服务。如果最终用户看到“退款失败”没有Trace你只能在每个服务的日志里大海捞针有了Trace你一眼就能看到LLM推理服务耗时2.3s正常支付微服务返回500异常原因是调用第三方支付网关超时而超时前Redis分布式锁等待了800ms。我们采用OpenTelemetry标准但做了关键定制强制注入Agent会话ID到所有Span中。OpenTelemetry默认的Trace ID是随机UUID对调试无意义。我们在API网关入口处从请求头如X-Session-ID: abc123提取会话ID将其作为trace_id的前缀并注入到所有下游调用的traceparent头中。这样当你在Jaeger或Zipkin里搜索abc123就能看到这个会话完整的、跨越7个服务的调用树。更重要的是我们要求所有Span必须标注agent_action属性例如agent_action: process_payment、agent_action: generate_response。这使得你可以按动作类型聚合分析process_payment平均耗时多少哪些动作最容易失败失败时90%的Trace都卡在Redis锁等待上——这就是优化方向。注意Trace采样率不能设为100%。全量采集会产生海量Span拖垮后端存储。我们采用动态采样对agent_action为process_payment或refund的关键动作100%采样对generate_response这类高频动作按QPS动态调整保证每秒最多采集100个Span。这个策略在保留关键路径完整性的同时将Span体积压缩了95%。3. 六堂实战课从故障现场还原技术决策3.1 第一课SSE连接保活——不是心跳是“状态同步”故障现象用户提问后等待10秒无响应前端报错stream disconnected before completion: idle timeout waiting for sse重连后Agent重复处理请求。表面看是Nginx timeout但根因是服务端没有主动发送保活消息。很多团队以为“只要连接开着就行”却忽略了TCP连接空闲时中间网络设备防火墙、云厂商SLB也会主动踢掉“僵尸连接”。我们的解决方案是双管齐下服务端主动保活在SSE连接建立后Worker不只等LLM输出而是启动一个独立协程每30秒向该连接写入一条data: {type:heartbeat,ts:1718923456}\n\n。这个心跳消息有两个作用一是维持TCP连接活跃骗过所有中间设备二是携带服务器当前时间戳前端可据此计算网络延迟用于优化重连策略。客户端智能重连前端EventSource对象的onerror回调里我们不盲目重连而是先检查eventSource.readyState。如果是0CONNECTING说明还在连如果是0但eventSource.url没变说明是网络抖动如果是2CLOSED且eventSource.url包含?retry3000说明上次重连失败这次要延长重试间隔。我们实现了指数退避算法// 前端重连逻辑 let retryCount 0; const maxRetryDelay 30000; // 最大30秒 function createEventSource() { const url /api/agent/stream?session_id${sessionId}retry${Math.min(1000 * Math.pow(2, retryCount), maxRetryDelay)}; const es new EventSource(url); es.onerror () { if (es.readyState 0) { // 连接中出错立即重试 retryCount 0; setTimeout(createEventSource, 100); } else { // 已关闭指数退避 retryCount; const delay Math.min(1000 * Math.pow(2, retryCount), maxRetryDelay); setTimeout(createEventSource, delay); } }; }这个逻辑让前端在遭遇网络波动时能优雅降级而不是疯狂重连压垮后端。3.2 第二课Redis分布式锁——不是setnx是Redlock的“降级版”故障现象同一笔退款请求被两个Worker同时处理导致用户账户被重复扣减。根本原因在于我们最初用的SET key value NX EX seconds即setnx在Redis集群环境下失效。Redis Cluster将key哈希到不同slot而setnx命令只在单个master节点上执行。如果锁key被分配到节点A而Worker1在A上成功加锁Worker2去节点B尝试加锁B上根本没有这个key自然也成功了——锁就形同虚设。我们没有直接上Redlock需要5个独立Redis实例运维成本高而是设计了一个基于Redis Stream的轻量级分布式锁创建一个专用Streamstream:lock:payment_refund。Worker尝试加锁时向Stream推送一条消息{request_id: req_abc123, worker_id: wkr-node1, ts: 1718923456}。然后Worker立即从Stream中读取最后10条消息检查是否有其他Worker在最近5秒内推送了相同request_id的消息。如果有说明锁已被占用放弃处理。如果没有则认为加锁成功开始处理业务。处理完成后向Stream推送一条释放消息{request_id: req_abc123, action: unlock, ts: 1718923458}。这个方案的优势在于Stream是Redis Cluster原生支持的无需额外部署锁的“持有”状态由Stream消息体现天然具备历史可查性超时机制由Worker自己控制检查5秒内消息避免了Redis过期时间的精度问题。我们实测在1000 QPS压力下锁冲突率低于0.1%且完全规避了Redlock的时钟漂移风险。实操心得不要用Redis的EXPIRE命令设置锁超时。EXPIRE的精度是毫秒级但在高并发下两个Worker几乎同时执行SETNX和EXPIRE第二个Worker可能在第一个Worker的EXPIRE之前就完成了SETNX导致锁被错误覆盖。Stream方案把“加锁”和“设置超时”合并为一个原子操作推送消息从根本上杜绝了这个问题。3.3 第三课流式响应解析——不是逐行split是状态机驱动故障现象前端SSE接收的token流出现乱码、丢失或最后一个data:消息被截断。根源在于很多前端开发者用responseText.split(\n\n)来解析SSE消息但这在流式场景下极其脆弱。SSE规范要求消息以\n\n分隔但网络传输中TCP包可能被任意切分一个完整的data: {...}\n\n可能被分成两段到达。split操作会在不完整的消息上失败导致JSON解析错误。我们采用基于状态机的增量解析器。核心思想是不等待完整消息而是边收边解析。伪代码如下# 后端Python示例使用Starlette class SSEParser: def __init__(self): self.buffer self.in_data False # 是否在data:字段内 def feed(self, chunk: str): self.buffer chunk while \n in self.buffer: line, self.buffer self.buffer.split(\n, 1) line line.strip() if not line: continue if line.startswith(data:): self.in_data True # 提取data内容注意可能有多行 data_content line[5:].strip() # 合并后续的data:行 while self.buffer.startswith(data:): next_line, self.buffer self.buffer.split(\n, 1) data_content next_line[5:].strip() # 解析JSON try: json_obj json.loads(data_content) yield json_obj except json.JSONDecodeError: # 忽略解析失败的消息继续 pass elif line.startswith(event:) or line.startswith(id:) or line.startswith(retry:): # 忽略其他SSE字段 pass前端同样实现一个状态机用ReadableStream的getReader()逐块读取维护一个buffer字符串只在检测到完整的\n\n时才尝试解析。这样无论网络如何分包解析器都能正确组装出完整消息。3.4 第四课Trace链路贯通——不是埋点是“上下文透传”故障现象支付服务报错但Trace里找不到上游Agent服务的调用记录无法定位是哪个用户、哪个会话触发的。这是因为很多团队只在服务入口埋点却忽略了跨进程调用时Trace Context的透传。当Agent服务调用支付服务时如果没把traceparent头带上支付服务就会生成一个新的Trace ID链路就此断裂。我们强制所有HTTP客户端如Python的httpx、Node.js的axios在发起请求前自动注入Context# Python httpx 客户端拦截器 import httpx from opentelemetry.propagate import inject def add_trace_headers(request: httpx.Request): # 从当前Span中提取Context inject(request.headers) # 自动添加traceparent等头 return request client httpx.Client(event_hooks{request: [add_trace_headers]})对于非HTTP调用如gRPC、Redis Pub/Sub我们也做了适配gRPC使用grpc-opentelemetry插件Redis Pub/Sub则在发布消息时将当前Span的Context序列化为JSON作为消息的一个字段一起发送Subscriber收到后用otel.context.attach()恢复Context。最关键的一步是前端SSE连接的Trace注入。我们在API网关生成初始Span时不仅生成trace_id还生成一个span_id并将其编码进SSE连接的URL参数中。前端在建立连接时把这个span_id作为X-Trace-ID头带上。SSE Session Manager收到请求后用这个span_id创建一个Child Span所有向该连接推送的消息都绑定在这个Span下。这样从用户点击“提交”按钮到最终看到退款成功的提示整个链路就是一个完整的Trace。3.5 第五课Redis数据治理——不是删库跑路是“冷热分离”故障现象Redis内存暴涨INFO memory显示used_memory_human: 15.2G但KEYS *查不到大Keyredis-cli --bigkeys也无结果。排查发现罪魁祸首是Stream的堆积。每个用户会话的Stream我们设置了MAXLEN ~无限制导致数百万条历史消息永久驻留。Stream本身不占大内存但每个消息的元数据ID、长度在内存中累积最终撑爆了Redis。解决方案是冷热分离自动归档热数据只保留最近24小时的Stream消息用XTRIM stream:session:* MAXLEN 10000命令定期修剪。10000是经验值足够覆盖绝大多数会话的生命周期。冷数据被Trim掉的消息不是丢弃而是异步推送到Kafka由专门的消费者服务将它们写入ClickHouse供运营分析使用。状态数据会话Hashhash:session:abc123设置TTL为2小时因为会话结束后状态信息就不再需要。我们用EXPIREAT命令而不是EXPIRE确保TTL基于绝对时间避免因Redis时钟漂移导致状态过早失效。这套治理策略上线后Redis内存占用从15G降至2.3G且CPU使用率下降40%。更重要的是它让Redis回归了“高速状态总线”的本质而不是一个臃肿的数据库。3.6 第六课并发压测验证——不是模拟是“真实流量染色”故障现象单机压测QPS 5000没问题上线后一到晚高峰SSE连接大量超时Trace显示90%的Span卡在Redis连接池获取上。根因是压测流量与真实流量的分布特征完全不同。单机压测用的是均匀随机请求而真实用户流量有强峰谷晚8点峰值、强关联一个用户连续发5条消息、强状态会话ID复用。我们设计了一套基于真实流量染色的压测方案在生产环境API网关开启流量镜像将1%的真实请求带X-Shadow: true头复制到压测集群。压测集群的所有服务都识别这个头并将请求路由到隔离的Redis集群、MySQL库避免污染生产数据。关键是镜像流量保留了原始的会话ID、用户ID、时间戳。这意味着压测时Worker会真实地去Redis里查询hash:session:abc123会真实地向stream:result:abc123推送消息会真实地触发SSE Session Manager的推送逻辑。这比任何造数工具都真实。通过这套方案我们提前发现了Redis连接池瓶颈连接池大小设为100但在高峰期单个Worker的并发连接需求峰值达到120。我们不是简单地调大连接池而是结合Trace分析发现80%的Redis调用都集中在HGETALL hash:session:*上。于是我们优化了会话状态读取逻辑改用HMGET hash:session:abc123 status last_activity_ts只取需要的字段将单次Redis调用的RT从8ms降至1.2ms连接池压力自然缓解。4. 避坑指南那些文档里不会写的血泪经验4.1 SSE与WebSocket的选型陷阱很多团队纠结“该用SSE还是WebSocket”。结论很明确Agent后端首选SSE除非你有双向实时交互的硬需求。理由如下运维成本SSE是HTTP协议复用现有Nginx、CDN、WAF零改造WebSocket需要升级到HTTP/1.1的Upgrade头很多老旧网关不支持且需要额外配置心跳、连接管理。移动端兼容性iOS Safari对WebSocket的支持曾长期存在问题而SSE在所有现代浏览器中表现一致。资源消耗WebSocket连接是双向的服务端必须为每个连接维护一个长连接socket内存开销大SSE是单向推送服务端只需一个写缓冲区连接数可轻松支撑10万。重连语义SSE的重连是浏览器内置的语义清晰重连后从头开始WebSocket重连需要自己实现且重连后如何同步状态如“我刚才发到第几个token了”是个难题。我们曾在一个项目中强行上WebSocket结果在微信内置浏览器里连接成功率不足60%最终全部回退到SSE。记住技术选型不是比酷而是比谁更稳、更省心。4.2 Redis数据类型的选择铁律面对Redis的5种数据类型String, Hash, List, Set, Sorted Set很多开发者凭直觉选。在Agent后端我们有三条铁律状态存储必用Hash会话状态、用户偏好、任务进度所有需要“多字段原子更新”的场景Hash是唯一选择。String无法原子更新List/Set无法按字段索引。消息队列首选Stream需要可靠投递、消费组、历史追溯的场景Stream是Redis官方推荐。List虽可用但BRPOP无法保证消息不丢失Worker崩溃时且不支持消费组。计数与排行榜用Sorted Set比如“今日最活跃Agent Top10”用ZINCRBY原子增ZREVRANGE取Top比用Hash排序高效百倍。曾有个团队用String存用户积分每次扣减都GET再SET结果在并发下积分被扣成负数。换成INCRBY命令问题立解。选对数据类型一半的并发问题就消失了。4.3 Trace采样的黄金比例全量Trace不现实采样率太低又失去意义。我们摸索出一个动态黄金比例基础采样率5%。这是底线保证你能看到整体趋势。关键路径100%所有涉及资金的操作process_payment,refund,withdraw所有风控决策fraud_check所有失败的请求HTTP status 400。动态提升当某个服务的错误率超过阈值如5分钟内错误率1%自动将该服务的所有Trace提升至100%采样持续15分钟便于快速定位。这个策略让我们在Jaeger里既能看清宏观水位又能瞬间钻入微观细节。上线后P0故障平均定位时间从47分钟缩短至8分钟。4.4 生产环境Redis的“三不原则”不直接用redis-cli在线操作FLUSHALL、DEL *这种命令永远在测试环境验证生产环境只允许通过预审批的自动化脚本执行。不共享Redis实例Agent的状态Stream、会话Hash、缓存数据必须分属不同DB如db 0for Stream,db 1for Hash,db 2for Cache避免一个DB的OOM拖垮全部。不忽略slowlog每天定时检查SLOWLOG GET 10对耗时100ms的命令必须分析原因。我们曾发现KEYS *命令在生产环境执行直接导致Redis阻塞根源是某个开发在调试时忘了删掉这行代码。4.5 Agent安全的“最小权限”实践Agent能调用支付、发短信、查数据库安全是生命线。我们实行四层最小权限网络层Agent服务所在Pod只允许访问Redis、支付网关、短信平台的特定端口其他全部拒绝。认证层所有下游服务调用必须携带JWT且JWT中scope字段精确到API级别如scope: [payment:refund, sms:send]。数据层Redis连接使用专用账号只授予HGET,HSET,XADD,XREADGROUP等必要命令权限禁用FLUSHDB,CONFIG等危险命令。审计层所有敏感操作如退款、删用户必须记录完整Trace并写入审计日志单独的Elasticsearch集群留存180天。有一次一个新来的工程师在本地调试时误将生产Redis密码写进了代码提交到Git。幸好我们的CI/CD流水线有静态扫描规则检测到redis://字符串匹配生产域名立刻阻断发布并自动告警。安全不是靠人盯而是靠流程和工具。5. 工具链与部署清单一份可直接抄作业的配置表类别工具/服务版本关键配置用途备注API网关Kong3.4plugins: - name: request-transformer - config: add: headers: [X-Session-ID:$request_id]注入会话ID统一入口替换Nginx支持动态插件SSE会话管理Python Starlette0.37--workers 4 --host 0.0.0.0:8000 --timeout-keep-alive 30长连接管理心跳保活使用Uvicorn部署禁用--limit-concurrencyRedis集群Redis7.2maxmemory 8gb,maxmemory-policy allkeys-lru,stream-node-max-bytes 10mb分布式状态总线3主3从启用cluster-enabled yesTrace后端Jaeger1.45--collector.zipkin.host-port:9411,--storage.typecassandra分布式追踪Cassandra集群3节点保障高可用压测平台k60.45export default function() { http.get(https://api.example.com/stream?session_id__ENV.SESSION_ID); }真实流量染色压测SESSION_ID从生产日志中提取Redis关键配置详解timeout 0禁用客户端空闲超时由应用层控制。tcp-keepalive 300启用TCP keepalive每5分钟探测连接。maxmemory-policy allkeys-lru内存满时LRU淘汰所有key避免OOM。stream-node-max-bytes 10mb限制Stream单个节点大小防止单条消息过大。SSE Session Manager部署要点必须部署为StatefulSet而非Deployment确保每个Pod有唯一网络标识。Pod内livenessProbe检查/healthz端点但readinessProbe必须检查Redis连接状态只有Redis连通才标记为ready。水平扩展时通过Kubernetes Service的sessionAffinity: ClientIP保证同一用户的SSE请求尽量路由到同一Pod减少跨Pod状态同步。Trace链路贯通检查清单[ ] 所有HTTP客户端已集成OpenTelemetry HTTP插件。[ ] gRPC服务已启用grpc-opentelemetry拦截器。[ ] Redis Pub/Sub消息体中包含trace_context字段。[ ] Jaeger UI中搜索任意X-Session-ID能展示完整跨服务调用树。[ ] 每个Span的service.name标签准确如agent-gateway,llm-service,payment-service。6. 最后的体会Agent不是终点而是后端工程的新起点写完这六课我翻出两年前的项目文档那时我们还在为“如何让LLM输出更快”而优化GPU显存。现在回头看那种焦虑多么狭隘。Agent真正考验后端工程师的从来不是模型能力而是在不确定的流式交互中构建确定性的状态系统。它逼着你重新思考连接是什么状态在哪里失败如何定义可观测性如何落地这六课每一课都是对传统后端思维的颠覆。当你的Agent开始处理真实世界的金钱、时间、信任那些教科书里的“高并发”、“高可用”就不再是抽象概念而是凌晨三点的告警、用户愤怒的电话、以及你亲手写的那一行修复Redis锁的Lua脚本。我常跟团队说别急着追最新的Agent框架先把SSE的保活逻辑写对把Redis的Hash操作用熟把Trace的Context透传搞明白。这些看似“古老”的技术才是支撑起未来智能体的钢筋水泥。上线第二天就退款不是事故是系统在提醒你真正的智能始于对基础的敬畏。
返回列表