从踩坑到成长:一次真实项目中的技术探索与反思
引言:为什么想写这篇文章?

我是一个有着五年工作经验的 Coze 工程师,经历过从零搭建平台、优化性能瓶颈,也处理过线上事故。今天想和大家分享一次让我印象深刻的“踩坑”经历 —— 它不是一次简单的 Bug 修复,而是一场关于技术选型、工程实践和团队协作的综合性挑战。
在这个项目中,我们尝试用一套新的 AI 对话引擎架构来替代老系统,以支持更高并发、更低延迟的用户交互体验。听起来挺理想,但在实际落地过程中,我们踩了不少坑,也学到了很多宝贵的教训。
希望这篇文章不仅能带大家看到我们解决问题的过程,也能引发一些对于当前 AI 架构下工程化实践的思考。
项目背景:我们要做什么?

我们当时负责的是一个面向企业客户的智能客服系统,核心功能是通过对话引擎实现自动回复、意图识别、上下文管理等能力。随着客户数量增长以及对响应质量的要求提升,原有的基于规则+简单模型的系统已经无法满足需求。
于是我们决定引入一个新的基于 LLM(大语言模型)的对话引擎框架 —— 也就是 Coze 的早期形态,并开始构建一套可插拔、易扩展、高可用的新系统。
目标很明确:
- 提升对话理解能力和回答准确性;
- 支持灵活的对话流程配置;
- 实现模块化设计便于后续维护;
- 满足高并发场景下的性能需求。
但理想丰满,现实骨感。
遇到的挑战:你以为的“换套框架”,其实是个大坑
第一坑:低估了模型推理的资源消耗
起初,我们认为只要把模型跑起来,加个服务层就能解决。结果上线前测试才发现 —— 一个 Qwen2.5 推理在 GPU 上单次请求居然要 3s 多!更别说并发一上来,GPU 资源直接被打满。
原因分析:
- 模型太大(7B),没有做量化或蒸馏处理
- 请求未做批量合并(batching)
- 没有使用高效的推理引擎(如 vLLM、Triton)
第二坑:服务调度与超时问题频发
我们用了 gRPC 做远程调用,但是经常出现 timeout、connection reset 等异常。
根本原因:
- 没有合理设置重试策略
- 缺乏负载均衡机制
- 推理节点挂掉后未及时熔断
第三坑:对话状态一致性难以保证
对话流程中有大量上下文需要保存,包括用户的多轮对话记录、临时状态数据等。我们最初使用 Redis 存储 session,但由于网络延迟和并发操作,出现了多次状态错乱。
我们是怎么解决这些问题的?
下面我将从几个关键环节,详细讲讲我们的解决方案和实现思路。
1. 推理性能优化:从单次推理到批处理 + 服务编排
方案选择与权衡
我们调研了几种方案:
| 方案 | 优势 | 劣势 |
|---|---|---|
| 直接调用 Transformers API | 快速原型开发 | 性能差,不支持并发 |
| 使用 vLLM 进行推理 | 支持 batching 和并行 | 配置较复杂 |
| Triton Inference Server | 支持异步、动态 batch、多种模型格式 | 学习成本高 |
| 自研中间代理进行合并 | 完全可控 | 开发维护成本高 |

最终我们选择了 vLLM + FastAPI 中间层 的组合。一方面它开源且社区活跃,另一方面可以快速部署,也不失扩展性。
核心代码片段
from vllm import LLM, SamplingParams
# 初始化模型
llm = LLM(model="qwen/Qwen2.5", trust_remote_code=True)
# 设置采样参数
sampling_params = SamplingParams(temperature=0.8, top_p=0.95, max_tokens=512)
# 批量推理调用
def batch_generate(prompts):
outputs = llm.generate(prompts, sampling_params)
return [output.text for output in outputs]
当然这是最简版本,我们在实际中还做了 token 缓存、prompt 编码复用、并发控制等多个层面的优化。
2. 分布式服务与熔断机制改进
我们用 K8s 部署服务,结合 Istio 做服务治理,并集成了 Hystrix 风格的熔断逻辑。
核心做法:
- 服务注册发现统一使用 Consul
- 请求链路增加 Retry、CircuitBreaker 中间件
- Prometheus 监控指标接入告警系统
示例配置(FastAPI + Starlette-Middleware)
from fastapi.middleware import Middleware
from starlette.middleware.cachedintervals import IntervalRateLimitMiddleware
app.add_middleware(
IntervalRateLimitMiddleware,
interval=60,
limit_per_interval=1000
)
@app.middleware("http")
async def add_circuit_breaker(request: Request, call_next):
try:
response = await circuit_breaker.execute(call_next(request))
return response
except CircuitBreakerError:
return JSONResponse(status_code=503, content={"error": "Service Unavailable"})
3. Session 状态管理重构
为了解决对话状态一致性问题,我们引入了一个轻量的 “对话状态机” 概念,同时将 session 数据存在本地内存缓存中,并配合 Redis 作为持久化存储。
实现逻辑:
- 每个对话流拥有独立的状态 ID
- 初始会话进入时从 Redis 加载上下文
- 每轮对话结束后更新内存缓存
- 定期异步落盘,避免每次访问都走网络 I/O
示例伪代码
class SessionManager:
def __init__(self):
self.cache = LRUCache(max_size=10000)
def get_session(self, session_id):
if session_id in self.cache:
return self.cache[session_id]
else:
# 从 Redis 获取
session = redis.get(session_id)
self.cache.set(session_id, session)
return session
def update_session(self, session_id, state):
self.cache.update(session_id, state)
# 定期异步刷新到 Redis
踩坑经验总结:那些年我们一起掉过的坑
🐞 1. 不要盲目相信模型输出
我们曾有一段时间完全信任模型的输出内容,在业务侧直接展示给用户。结果有一次模型返回了敏感词,被客户投诉,影响非常不好。
教训:
- 所有输出必须经过敏感词过滤和服务端校验
- 增加关键词替换、内容审核中间件
- 设立白名单/黑名单机制
🔥 2. 上线之前一定要做压力测试!
我们一开始没太重视压测,结果灰度发布当天就出现了雪崩效应。服务 A 挂了,导致服务 B 无限等待,连锁反应整片微服务瘫痪。
改进措施:
- 使用 Locust 做接口压测
- 模拟极端情况下的失败场景
- 加入限流、熔断、降级机制
💔 3. 技术文档缺失毁一生
项目初期大家都忙于编码,没人写文档。后来新同事接手的时候,只能靠看代码猜逻辑。一度造成多个 bug 是因为理解错误导致的重复修改。
建议:
- 每个模块都要有清晰的文档说明
- 推荐使用 MkDocs 或 Docusaurus
- Git 提交记录也要规范,方便回溯
效果总结:这次改造带来的收益

经过两个月的折腾,整个系统焕然一新:
| 指标 | 改造前 | 改造后 |
|---|---|---|
| 单轮响应时间 | ~3.5s | ~0.7s |
| 同时并发数 | ~200 QPS | ~1500 QPS |
| 故障率 | 高(频繁超时) | < 0.5% |
| 维护成本 | 高(无文档) | 明显下降 |
更重要的是,现在的架构更加清晰,各模块职责分明,未来扩展也更有底气。
经验分享:给同行朋友们的一些建议
✅ 1. 技术选型别图快,要图稳
很多人喜欢追热点,看到哪个新技术火就想上马。但工程落地要考虑稳定性、成熟度、社区生态等多个因素。比如我们在对比 vLLM 和 HuggingFace TGI 的时候,最终还是选了前者,因为它更适合我们这种偏实时推理的场景。
✅ 2. 性能优化要从底层抓起
不要只停留在“加缓存”或者“换个模型”的表层优化。真正的性能瓶颈往往在硬件利用率、并发控制、模型结构设计上。有时候一个 kernel 的改动,就能带来几十倍的效率提升。
✅ 3. 写文档、建规范、做 Code Review,这些都不是形式主义
刚开始觉得文档麻烦、Code Review 耽误进度,但后来发现这些都是防止项目失控的关键步骤。尤其是多人协作的项目,如果没有统一的代码风格和流程规范,很容易陷入“一团乱麻”。
✅ 4. 埋点监控必须从第一天做起
我们项目中期才加上日志埋点,结果排查问题非常痛苦。建议所有服务一开始就集成以下基础能力:
- 日志收集(ELK)
- 接口埋点追踪(OpenTelemetry)
- 错误码分类与统计
- 机器资源监控(Prometheus + Grafana)
写在最后:技术成长是一次次“踩坑”的累积
写这篇文章的时候,我翻出了当时的日志记录和 Slack 消息,一幕幕历历在目。有些是我们深夜调试的问题,有些是凌晨三点被报警叫醒的崩溃时刻。
但也正是这些坑,让我们从“写代码的人”慢慢成长为“懂系统的开发者”。
如果你也在做类似的事情,希望这篇记录能给你一些参考和鼓励。记住一句话:每一个让你痛苦的技术难题,最终都会成为你最有价值的经验资产。
如果这篇文章对你有所帮助,欢迎留言交流,一起探讨更多 AI 工程落地的实战心得。
📅 作者信息:
Coze 工程师 | 5年 AI 系统开发经验
GitHub:@coderzhao
邮箱:zhaocoding@example.com
💡 欢迎关注我的专栏《AI落地笔记》,持续分享一线 AI 工程实践经验。

评论 0