从0到1:企业级AI项目迭代日记 Vol.53|功能没做错,边界没接对
一个系统最常见的失败方式不是某个功能没做而是功能和功能之间的边界处理错了。用户绑定了凭证但运行时没有正确加载——绑定成功却什么都调不了知识库停用了但测试入口里它还在出现——状态更新了但状态没有被同步到所有消费方图片生成了但关联的用户标识是空的下载请求被鉴权拒绝——写入时缺了字段读取时就永久断链。每一个单独看都是小问题但用户感知到的是这东西坏了。这一轮的工作是把所有已上线功能的接缝重新走一遍。一、权限有三层配置只是第一层权限系统最容易犯的错误是把“配置了权限”等同于“权限生效了”。实际上权限有三层第一层是配置层定义谁能看、谁能用第二层是传递层配置需要在调用链路上被正确传递第三层是运行时执行层每次实际操作发生时需要再次校验。这轮发现的问题主要集中在第二层私有 Agent 的可见性配置写进去了但查询时没有带上过滤条件导致越权凭证绑定了但运行时加载工具的时候没有把凭证注入进来导致“能配不能用”。修复的方向不只是堵漏洞而是在第二层建立一个统一的权限传递通道——所有需要权限感知的查询和操作都走这个通道不允许绕过。同时在管理员视图里加了一个权限总览让配置层的状态可以被统一审查。二、状态机的问题谁负责通知谁知识库停用之后为什么测试页面里还能看到它根本原因是停用操作只更新了数据库里的状态字段但没有通知所有依赖这个状态的消费方。测试页面的查询没有过滤停用状态也没有订阅状态变更事件所以它看到的还是旧状态。这是分布式系统里一个经典的问题状态的更新和状态的同步是两件事。更新容易同步难。解决方式有两种一种是主动推送状态变更时通知所有消费方另一种是被动拉取消费方每次查询时都带上状态过滤。这轮采用的是后者——修复所有查询确保状态字段参与过滤。同时修了未绑定 Agent 时召回被清零的问题原因类似召回逻辑没有处理“没有绑定”这个状态分支默认走了一个错误的空逻辑。三、异步工作流的完整性保证生图是一个典型的异步流程用户发起请求任务在后台跑结果写回存储前端拉取展示。这条链路上的任何一个环节存储不完整下游都会断。这次发现的问题包括任务写回时用户标识字段为空导致下载时鉴权失败历史记录在会话切换时没有重新加载导致图片消失旧任务的结果被错误地关联到新会话上导致图片“错位”。这些问题的共同模式是写入时的字段完整性没有强校验读取时的还原逻辑没有处理异常情况。修复后的逻辑写入时强制校验关键字段任何字段缺失直接拒绝写入而不是写入空值读取时对历史状态做完整重建而不是依赖内存缓存。同时把大图查看、失败原因显示、下载链接生成这几个下游能力补齐让生图成为一个有完整生命周期的对象而不是“跑完就消失”的过程。四、流式输出是状态不是事件用户切换会话时正在流式输出的内容被中断切回来不接续——这个问题背后是一个认知上的错误把流式输出当成了事件流而不是状态。事件流是“发出去就结束”状态是“随时可以查询当前是什么”。如果流式输出是事件流那切换会话就意味着错过了自然无法恢复。如果它是状态切换会话再切回来只是重新订阅当前状态内容应该在。修复方向是把流式输出的内容持久化到会话状态里切换再切回时从状态恢复而不是重新触发。同期修了另一个问题内部中间件调用失败产生的错误没有被拦截直接串进了对话输出。用户会看到一段不知所云的系统错误文字出现在回答里。这个问题的本质是内部错误和用户可见内容没有严格隔离——任何内部异常都应该在边界处被捕获和转换不能直接透传。五、基础设施的债会在压力下集中还数据库连接池超限、跨租户隔离漏洞、全局环境变量做默认配置——这些问题在低负载下沉默在压力下集中爆发。连接池超限每个服务如果各自管理连接在并发高的时候总连接数会超过数据库的上限。解决方式是统一连接池管理所有服务共享同一个池而不是各自独占。跨租户隔离行级安全策略是正确的方向但它需要每次查询都显式声明身份。漏掉声明的查询在策略开启后就会静默返回空——不报错只是什么都读不到。这类问题特别难发现需要逐一检查所有查询路径。全局环境变量配置这是技术债的典型形态。早期为了方便把默认模型配置写死在环境变量里后来发现多租户场景下每个租户需要不同的默认模型。迁移到数据库配置是正确的但迁移过程需要加缓存、加锁、加回退逻辑防止迁移期间的不一致。系统从能演示到能稳定运行中间是这些工作。这是第五十三天。《从0到1企业级AI项目迭代日记》记录一个企业级 AI 项目从创意、架构到落地的真实过程。不讲神话只记录进化。如果你也在做企业 AI 落地欢迎留言来聊。或者把这篇转发给一个正在踩同样坑的朋友。