
这套项目复盘我拖了大半年才动笔。AIStarter和PanelAI这两个产品一个负责把AI任务跑起来一个负责把跑起来的任务管清楚两个项目在同一家公司里并行演进经历了从单体工具到平台化架构的完整重构过程。这次复盘不聊广告语只聊我踩过的坑、删掉的重写、重构前后的决策逻辑以及那些当时觉得正确、回头看其实可以做更好的判断。写出来主要是想给正在做AI工具链产品重构的同行一个参考。尤其是那些手上同时管着“调度执行器”和“管理面板”两条产品线的同学你们早期感受到的别扭大概率不是错觉而是架构分裂的前兆。1. AIStarter最初的设计边界能启动不等于能用好1.1 原始的形态和它解决的问题AIStarter最早不是平台更像一个高级脚手架。用户填写模型名称、上传提示词模板、选定输入文件系统就会自动拉起推理任务把结果打包返回。听起来很直接但早期版本事实上解决了三件事本地模型服务的接入、推理任务的批量触发、以及提示词模板的版本管理。这三件事在2023年初是实打实的痛点。当时的团队都在各自写推理脚本每个脚本里都硬编码着模型路径和API地址换一个模型就要改代码。AIStarter把变量抽出来用一套Web界面统一管理本质上就是给团队内部用的低代码调度器。这时候的架构非常简单FastAPI提供REST接口Celery处理异步任务Redis做消息中间件PostgreSQL存任务元数据和模板文件。单机部署一套代码跑完所有逻辑。体量小的时候没有任何问题即便同时跑上百个推理任务Celery Worker拆开就行。但问题在用户量涨起来之后慢慢显现。首先是任务类型开始分化有人要跑Stable Diffusion批量生成图片有人要调LLM做文本分类还有人要跑OCR预处理。不同任务的资源占用差异极大GPU饱和大户和轻量文本任务挤在同一个队列里互相拖累。其次是任务依赖关系出现用户想先做数据清洗再喂给大模型做推理然后自动把结果回填到数据库里。这种多步骤工作流在Celery里表达起来极其痛苦。1.2 “跑得起来”和“管得好”是两套能力我在重构启动器的时候反复被一个认知纠正AIStarter的核心竞争力被团队误判为“执行效率”用户真正花的钱买的其实是“确定性”。所谓确定性包含三层任务到底跑没跑成功、跑失败时是谁的责任、以及某一次结果是用哪个版本的模板和模型生成的。结果当时的产品只实现了第一层后两层完全没有。任务失败就红了但为什么失败、失败之后能否重试、重试用的是不是同一套参数全都靠用户自己登录服务器查日志。有一段时间AIStarter的留存率上不去问题不在产品速度慢而是出了问题之后用户只能干瞪眼。这个阶段让我意识到任何偏底层的AI工具如果不能在“可观测性”上做到和“可执行性”同等级别的体验那它本质上还是一个半成品。PanelAI最开始就是以补齐这块短板为目标立项的结果做着做着牵扯出来的管理问题远比预想的多。2. PanelAI的诞生逻辑控制面板不是附属品是分布式系统的必需品2.1 多模型、多密钥、多版本带来的管理灾难当AIStarter支持的模型源从两三个变成二三十个用户画像也从个人开发者扩展到小型团队配置管理的复杂度开始指数级上升。每个人手里可能同时持有多个服务商的API Key有的按量付费有的按Token付费有的内部私有化部署。密钥散落在各人的环境变量里谁泄露了无法追溯谁的额度用超了也不预警。PanelAI在1.0版本做的就是密钥托管和用量聚合把散落在各处的Key集中到一个面板里通过统一网关转发请求由网关记录每一次调用属于哪个项目、哪个人、哪条链路。到这一步它就不是传统意义上的“后台管理页面”而是一个真正的基础设施组件。这里有个典型的架构决策值得展开讲当时我们讨论过两个方向一是把管理能力内嵌进AIStarter直接做一个全功能模块二是拆出一个独立服务专门负责控制面和数据面。最后选了后者理由很朴素——AIStarter的执行链路将来大概率要容器化和边缘化让调度组件背着管理数据库跑既拖慢启动又让两类团队的工作发生耦合。事实也证明这个决定是划算的PanelAI独立演进后后来做多租户隔离时基本不用改动AIStarter的核心调度代码。2.2 双产品并行时的接口契约与版本协同两条产品线并行之后最痛苦的不是写代码而是定契约。AIStarter负责把任务发给执行引擎执行引擎回调PanelAI做状态同步PanelAI再反向对AIStarter下发控制指令。整个链路涉及三套协议任务定义协议、状态上报协议、控制指令协议。我们早期采用的办法是共享一份OpenAPI定义文件所有变更都必须在同一份Schema上做扩展。加字段可以删字段必须提前两个版本做deprecation。允许实现进度不一样但接口演化方向必须对齐。中间经历过一次改名风波原定的task_type字段AIStarter那边理解为“执行引擎类型”PanelAI理解为“业务场景类型”同一份字段出现两套枚举值。这是典型的“分布式产品线的命名冲突”修复成本只在当时看似不高改字段涉及迁移旧数据、升级所有客户端代价被严重低估。这次事故之后我们建立了语义化命名规范前缀必须带命名空间engine.kind、scene.type、meta.owner。丑但不会错。3. 架构演进的核心技术决策从单体姿态切换成两张皮、三层结构3.1 引入事件驱动替换任务队列重构的导火索是线上出现过一次长任务饿死短任务的案例。Celery的默认队列是先进先出一个超长的视频推理任务堵在队列头后面十几个轻量的文本分类任务全部排队等待。虽然可以用优先级队列缓解但当混合任务依赖关系和资源类型之后任何排队策略都显得僵硬。我们最终把调度核心从“队列”抽换成了“事件流”。所有任务状态变更不再通过轮询获取而是模型服务、执行器、面板三者之间通过事件总线广播。任务进入执行器时发布task.started执行器内部步骤完成时发布step.completed全部成功发布task.succeeded失败发布task.failed。事件流经过订阅层写入PanelAI的时序数据库同时也触发AIStarter的补偿动作。选型上我们用了NATS理由很直接轻量、支持JetStream持久化、配置简单运维成本比Kafka低一个量级团队规模不到十个人没必要上重型消息集群。如果你同样在做中间件选型我的建议是先画清楚事件的消费方有几个。如果下游只有日志存数和状态更新Kafka有点重量级如果下游要做复杂事件溯源和流处理NATS的生态支持又会让你后悔。3.2 网关层到底需要拦截什么PanelAI的重心逐渐落到API网关和数据平面上。网关不只是转发它承担了三件必须做的事。第一件是鉴权与密钥替换。用户的原始Key只存于加密库调用时网关读取并注入真实Key日志和任务详情里永远不出现明文。这一点在后来的安全审计里拉回了不少分数。第二件是限流与配额。每个租户有月度额度网关侧做令牌桶拦截超额请求直接返回429不再进入执行链路。第三件是响应缓存。同一个提示词模板配上同参在大版本模型迭代之前缓存命中率能到15%省下的成本对中小团队很可观。三层网管的结构大概是这样的第一层接入网关处理TLS、路由、Header标准化第二层策略网关处理鉴权、限流、密钥注入第三层服务网关处理具体任务的协议转换与模型适配网关层是我整个重构过程中建议优先级放最高的组件。因为任何业务逻辑都可以以后再补但所有请求都从同一个入口过一遍后续做统计、审计、配额、缓存才有一个插入点。3.3 模型抽象层与适配器的长期价值运营后期我们发现AIStarter的代码里对具体模型服务的调用散落各处有的代码块直接用OpenAI的client有的用HTTP调用本地vLLM接口有的调HuggingFace的InferenceEndpoint。每一处都在自己组装请求体、解析返回值、处理错误码。这导致每对接一个新模型就要改五六个文件。架构重构时我们加了一个专门的适配器层所有上游模型服务都实现统一接口。核心抽象是三个方法build_request、parse_response、translate_error。模型差异都被封装在这三层里。业务代码只面对一个统一的ModelClient调用格式统一成client.run(task_spec)。加上这层抽象之后新增模型供应商的工时从三到四天压缩到半天到一天而且很多模型可以走通用模板零开发接入。这个抽象带来的间接收益是成本控制变得可编程了所有请求都汇总到统一出口按服务商维度做成本归因就只是SQL聚合的事了。4. 产品重构逻辑从功能堆叠到业务闭环4.1 用户动线从“先选模型”变为“先选场景”技术上重构成功的标志是架构清晰不崩产品上重构成功的标志是用户自作主张地走了新路径。AIStarter旧版的用户动线是典型的工具型思维打开页面、新建任务、选择模型、填写参数、运行。这个路径默认用户已经知道该用什么模型而真实用户往往只知道“我要做的是把客服工单自动分类”。1.x重构版本把入口改成了场景模板工单分类、文章摘要、图片批量生成、语音转文字。用户进来先选场景再选模型。模型选择被降级为高级选项默认值由系统根据效果、速度、价格三个维度自动推荐。这套变化看似是交互微调底层其实是产品责任的变化。旧版把“选模型”的责任推给用户新版把“选对模型”的责任揽到自己身上。为了让推荐逻辑有数据支撑PanelAI的用量统计反过来成了AIStarter推荐模块的输入源同一场景下哪个模型的历史成功率最高、平均延迟最短、Token消耗最低算法会计算综合推荐分。4.2 多租户隔离与角色权限的重构产品演进到企业级服务之后单用户的权限模型彻底不够用了。早期PanelAI只有两种角色管理员和普通用户。企业客户一进来就有了需求“我们公司三个部门都有自己的项目项目之间不能互相看到数据部门里的模型Key是共用的但每个人每天能调用的额度不同。”这一轮支撑多租户的数据模型改动很大主要围绕四类对象做设计org组织最高层级包含所有项目和成员project项目资源隔离的基本单位绑定模型配置member成员绑定角色支持自定义角色权限usage用量记录每一笔调用记录归属某个项目与成员权限判断不再放在应用层散点判断而是统一收敛到网关的授权中间件。每个请求头里携带JWT网关先解析租户上下文再从策略库查询该角色允许调用的资源范围。这个方案虽然IO次数多但逻辑单一排查权限问题非常快。4.3 从“执行工具”到“算力资产”的认知转变产品重构过程中最抽象但也最关键的一步是被迫重新定义AIStarter到底是什么。最初它被当作任务执行工具使用价值在于省事后来被看作流程自动化平台使用价值在于串联业务再后来PanellAI把用量数据补全后两个产品联合起来更像一套算力资产管理台每一次模型调用背后都有真金白银的成本谁在用、用了多少、产生了什么价值都应该有迹可循。于是新版本中加入了两件事成本预估与成本归因。创建任务时系统根据输入长度、输出上限、模型单价预估本次调用成本任务完成后实际账单回传标注在哪条工作流的哪一步消耗了多少钱。客户第一次看到这个功能时反响很大他们反馈说“终于知道每月账单是怎么跑出来的了”。这一步也让产品的付费模式从按席位收费平滑过渡到按调用量计费。席位费适合工具调用量计费才适合基础设施。这也是我认为本产品重构中最关键的一次商业模式验证。5. 重构过程踩过的坑连续三次事故逼出来的工程纪律5.1 迁移存量数据时低估了状态机的复杂度AIStarter旧数据里有大量历史任务其中有相当一部分处于pending、running、failed这类中间态。迁移时如果简单地把原始字段搬过去新系统里的状态机根本解释不了这些旧状态。比如旧版本里failed数据没有区分“用户取消失败”和“执行超时失败”这两种状态在后来的重试和计费逻辑里处理完全不同。我们第一版迁移脚本直接平移数据上线后统计报表里凭空出现了几千条无法归类状态的脏数据。从技术上说这不是一个SQL能解决的必须针对历史状态写专门的映射规则区分死信、可重试、已补单。这个教训让我明白迁移的难度不是数据量大而是历史数据的语义不能被新系统理解。建议任何团队做状态机阵型调整时把“状态迁移映射表”作为评审必交材料。5.2 双写双读方案没有预留好回滚开关当时为了做到平滑过渡核心表采用双写方案新老系统并行写入读取时优先读新库失败时回退读老库。理论上这是稳妥的但实际执行中忽略了“开关”本身也是要部署的东西。有一个晚上上线时发现新库的排序结果和旧库有差异业务上不能接受我们决定回滚到旧库读。没想到回滚开关配置在配置中心里而配置中心的客户端缓存了旧值十分钟才生效。这十分钟内一部分请求读新库一部分读旧库报表对不上运营部门直接投诉。事后我们把所有发布开关从配置中心拉出来独立成一个本地文件兜底并且规定任何双写方案上线必须附回滚演练记录。说得难听点没有回滚预案的双写就是给自己埋雷。5.3 拆服务拆出来的分布式事务问题把单体里的计费模块拆成独立服务后计费逻辑需要同时更新用量表、余额表和账单明细表。单体时代这是本地事务非常容易保证一致性拆出来后变成了跨服务操作立刻面临分布式事务难题。我们试过两阶段提交太重试过本地消息表逻辑繁琐最终用了Saga模式通过事件补偿来维持最终一致性。这期间发生过一次比较严重的扣费漏记用量表的调用计数已经增加但账单表生成的记录失败因为没有完整的补偿逻辑用户发现自己调用次数增加了但并没有生成账单引发了对账差异。后来我们把账单生成与用量扣减改成同一条事件消息触发并且都要求幂等键。每个调用请求带一个全局唯一的request_id消费端去重才算真正止血。现在我倾向于直接给结论跨服务数据一致性优先用本地消息表Saga组合少碰分布式锁。不要试图让多个服务共享一个数据库矛盾爆发时会让你怀疑人生。6. 重构后沉淀下来的一套可复用的演进路径6.1 从API网关起步的分阶段推进顺序如果重新来一遍我会把演进节奏压缩得更清晰。不是推翻重来而是沿着一条明确的路径做增量重构第一阶段1-2周统一请求入口做好鉴权和审计第二阶段1个月把任务调度从队列换成事件流补齐状态机第三阶段1-2个月抽取模型适配器收敛所有模型调用第四阶段连续推进构建控制面板给调度层提供可观测性第五阶段业务驱动做多租户、配额、计费闭环这个顺序是严格按照“先有入口、再做流动、后做抽象、再做可视化、最后做商业化”的逻辑排下来的。反过来做就会遇到这种情况控制面板漂亮地展示着数据但入口不统一导致数据不准空有界面没有用。6.2 架构演进中容易忽视的非技术问题技术上想清楚之后产品重构真正难啃的是非技术问题。我举三个真实发生过的情况第一个是团队沟通成本。后端拆了三套服务前端还能像以前一样只连一个API吗不行了。前端团队需要同时对接多个服务的API错误码、日志追踪、联调环境都变了。没有在统一网关上做聚合前端就要自己拼数据效率奇低。第二个是文档滞后。重构过程中API不断调整文档更新永远赶不上代码变更。我们试过要求开发随手改文档做不到。后来强制要求每个接口必须带OpenAPI标注由CI检查是否与实现一致不一致就构建失败。这个办法蠢但非常有效。第三个是客户感知管理。灰度发布时有一批老用户看到了新界面但不知道菜单去哪了在线反馈刷爆了客服群。后来每次前端改版提前两周就要公布菜单迁移对照表并把旧入口保留一个完整版本周期。6.3 适合直接抄走的复盘checklist结尾分享一份我每次做产品架构复盘都会过的检查清单没有一个花哨的框架都是我拿事故换来的入口层是否收敛所有请求能否追溯到一条完整链路状态机是否完整失败是否有重试策略重试是否幂等抽象层是否隔离换一个模型供应商需要改几个文件数据一致性如何处理每一条业务规则是否都有补偿路径配置是否可动态变更是否具备快速回滚手段观测是否闭环用户操作行为与系统实际状态是否可对齐团队是否知道新架构的边界出现问题时能否在5分钟内定位到所属服务把这个清单过一遍比讨论一百遍“我们的架构是否优雅”实用得多。AIStarter和PanelAI从不优雅但它们现在跑得稳、查得清、改得动这对工具类产品来说就是最好的状态。