项目里要接 AI 大模型了?我把多厂商网关、场景配额、调用日志一次性做成了脚手架
项目里要接 AI 大模型了我把多厂商网关、场景配额、调用日志一次性做成了脚手架导读业务方提需求给客服加个 AI 助手“给简历做个 OCR 识别”一个接一个。最早是各业务自己 new 一个 SDK 调密钥散在 yml 里出了事还没法查是谁调的。后来我把 AI 调用收敛成了一个独立模块多厂商 SPI、场景配置、配额限流、调用日志全在里面。先交代一下当时的状态。项目里要接大模型的地方越来越多智能客服、简历 OCR、内容润色、知识库问答。一开始图省事每个功能直接引 SDKDeepSeek 的、通义的、OpenAI 兼容的一起上。半年后发现三个问题密钥管理混乱有人把 key 写进 yml 提交了、没法审计谁在什么时候调了多少 token 完全不知道、换厂商要改业务代码。于是我在脚手架里加了qkl-ai模块核心思路就一句话业务方不关心用的是哪家大模型只认场景编码。SPI 抽象所有厂商长一个样先把各家 SDK 的差异吃掉。定义一个AiModelClient接口流式、非流式、对话、Embedding 都收敛进去publicinterfaceAiModelClient{/** 厂商编码如 DASHSCOPE / DOUBAO / OPENAI */StringproviderCode();/** 非流式对话 */ChatResultchat(ChatRequestrequest);/** 流式对话SSE */voidchatStream(ChatRequestrequest,StreamCallbackcallback);/** 文本向量化RAG 用 */Listembed(Listtexts);/** 连通性测试后台测试连接按钮用 */ConnectivityTestResulttestConnectivity();}实现类各自继承一个AbstractOpenAiCompatibleClient——现在主流厂商都兼容 OpenAI 协议改个 baseUrl 和鉴权头就行真正要手写协议适配的很少。我用的是 DashScope、Doubao、OpenAI 三家全部走这个抽象ComponentpublicclassDoubaoCompatibleClientextendsAbstractOpenAiCompatibleClient{OverridepublicStringproviderCode(){returnDOUBAO;}OverrideprotectedStringresolveBaseUrl(AiProviderprovider){returnhttps://ark.cn-beijing.volces.com/api/v3;}OverrideprotectedMapresolveHeaders(AiProviderprovider){returnMap.of(Authorization,Bearer provider.getApiKey());}}客户端注册进AiModelClientRegistry按providerCode取业务层永远不知道具体实现是谁。场景配置业务方只传场景编码接口暴露给业务方的就一个方法chat(scene, messages)。场景对应哪家厂商、哪个模型、什么系统提示词、温度多少全在qkl_ai_scene表里配// 场景实体关键字段privateStringscene;// 场景编码CUSTOMER_SERVICE / RESUME_OCR / CONTENT_POLISHprivateStringproviderCode;// 走哪家厂商privateStringmodelCode;// 模型编码privateStringsystemPrompt;// 系统提示词privateIntegertemperature;// 采样温度privateIntegerstatus;// 1 启用 0 停用加一个新场景运营在后台插一条记录开发一行代码都不用改。之前那种新场景要改代码发版的日子算是过去了。配额 限流防的是失控不是防人AI 调用是要花钱的必须有两道闸。第一道是租户维度日 token 配额第二道是Redis 窗口限流publicvoidconsume(StringtenantId,inttokens){AiQuotaquotagetOrCreate(tenantId);resetIfNewDay(quota);// 跨天自动归零longusedquota.getDailyTokenUsed()null?0L:quota.getDailyTokenUsed();quota.setDailyTokenUsed(usedtokens);quotaMapper.updateById(quota);}publicvoidassertAllowed(){StringidentitycurrentUser();StringkeyRedisKeys.rateApi(ai.chat,user,identity);LongcurrentredisTemplate.opsForValue().increment(key);if(current!nullcurrent1){redisTemplate.expire(key,Duration.ofSeconds(Math.max(1,aiProperties.getRateLimitPeriod())));}if(current!nullcurrentaiProperties.getRateLimitCount()){thrownewQklBizException(ErrorCode.RATE_LIMITED,操作太频繁请稍后再试);}}配额超了就抛当日额度已用完前端友好提示限流超了就 429 类错误防止脚本刷。密钥加密 调用日志出事能追责密钥不落 yml存库的时候加密用AiSecretCipher包了一层解密只在真正发起调用时进行privateStringdecryptSecret(AiProviderprovider){returnsecretCipher.decrypt(provider.getApiKeyEncrypted());}每次调用无论成败都落qkl_ai_call_log场景、模型、输入输出 token 数、耗时、结果状态、失败原因。日志保留 90 天出了事故翻日志就能还原谁、什么时候、调了什么、花了多少 token。踩坑记录总开关一关整个模块静默消失问题现象本地没配 AI 相关配置启动项目后调用/admin/ai/scene接口返回70001页面上一脸懵。排查过程翻代码发现qkl.ai.enable默认是falseAiAutoConfiguration用ConditionalOnProperty控制开关没开时整个模块的 Bean 都不注册管理端接口统一返回业务码 70001模块未启用。定位思路这个设计本身没问题——AI 密钥是敏感配置不该让没开通的租户看到界面。但报错信息太含糊前端看不出是没启用还是接口坏了。最终解决管理端接口把 70001 的 message 改成AI 模块未启用请在配置中设置 qkl.ai.enabletrue前端根据错误码弹出明确提示。另外约定生产环境密钥必须走配置中心/环境变量注入 cryptoSecret空 salt 回退逻辑只允许开发环境用防止线上密钥可逆。可直接复用的清单厂商差异用SPI 抽象 注册中心吃掉业务层只认场景编码场景表配厂商模型提示词新场景后台配置即上线配额按租户按天跨天自动归零限流用 Redis increment expire 窗口密钥加密存库、调用时解密绝不进 yml 明文每次调用落日志保留 90 天审计追责全靠它模块总开关用ConditionalOnProperty控制未启用时错误提示要友好。这套 AI 网关是 qkl-boot 脚手架里的标准模块。项目源码https://gitee.com/gzqkl/qkl-boot

相关新闻

深入理解5g<四十五>基带调制

深入理解5g<四十五>基带调制

5G的基带调制技术是一套**自适应、高灵活性的体系**。它不再像前几代那样采用单一的固定方案,而是通过**自适应调制编码(AMC)**,根据瞬时信道质量,动态地在**QPSK、16QAM、64QAM、256QAM甚至1024QAM**等多种方案间切换…

2026/10/8 5:51:20 阅读更多 →
单片机C库运行时libspace与多任务安全实战指南

单片机C库运行时libspace与多任务安全实战指南

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

2026/10/8 5:51:20 阅读更多 →
国庆 7 天写了上万行 Rust 代码:一个二本转码生的技术长征与极客信念

国庆 7 天写了上万行 Rust 代码:一个二本转码生的技术长征与极客信念

2026 年 10 月 7 日,深夜十一点四十分。 众创空间整栋大楼的中央空调已经在半小时前关闭,空气渐渐冷却下来。窗外,南宁的高铁站方向只有零星的汽车尾灯在夜色中拖曳出红色的光轨。长假结束了,明天清晨,这座城市将重新恢…

2026/10/8 5:51:18 阅读更多 →

最新新闻

多模态无监督持续后训练:视觉依赖感知框架解析

多模态无监督持续后训练:视觉依赖感知框架解析

多模态模型的持续更新一直有个很现实的问题:新数据来了,直接继续训练容易忘掉旧能力;不做训练,新场景又用不上。如果数据还没有人工标注,问题会更麻烦。这次我们看的这个框架,名字叫A Visual Dependence-Aw…

2026/10/9 10:33:01 阅读更多 →
SpringBoot+SpringCloud电商源码实战:微服务启动顺序与避坑指南

SpringBoot+SpringCloud电商源码实战:微服务启动顺序与避坑指南

简介:这是一套面向计算机相关专业在校学生与教师的电商系统课程设计/毕业设计源码包,基于Spring Boot与Spring Cloud构建,采用Spring Security、MyBatis、Redis、Docker、Elasticsearch等技术栈,并运用分布式微服务架构&#xff0…

2026/10/9 10:33:01 阅读更多 →
惠普战66拔掉耳机后扬声器无声

惠普战66拔掉耳机后扬声器无声

机型 HP ZHAN 66 Pro A 14 G4 | Windows 10 | 声卡 Realtek ALC236帖主的问题最终还是借助 Cursor 得以修复,下附 Cursor 总结的具体的问题表现、排查过程及结论,供有需要的同仁参考。一、问题描述耳机插上以后,声音正常。耳机拔掉以后&a…

2026/10/9 10:33:01 阅读更多 →
从临时Subagent到持久化AI团队:状态恢复与审计追踪设计

从临时Subagent到持久化AI团队:状态恢复与审计追踪设计

这次我们来看一个很有意思的项目:Show HN: Turn ad-hoc subagents into durable, accountable AI teams。从标题就能看出,它解决的不是“再做一个 Agent”,而是更现实的问题:平时随手创建的临时 Subagent 一到任务结束就丢了&…

2026/10/9 10:33:01 阅读更多 →
RIGOL DS1000系列LabVIEW驱动实战:从RS232/GPIB通信到自动化测试集成

RIGOL DS1000系列LabVIEW驱动实战:从RS232/GPIB通信到自动化测试集成

简介:这份资源面向使用普源DS1000系列示波器、希望借助LabVIEW实现远程控制与数据采集的工程师与测试人员,重点解决RS232串行通信和GPIB总线两种接口下的驱动调用问题。压缩包共54个文件,约570KB,以42个vi虚拟仪器文件为核心&…

2026/10/9 10:33:01 阅读更多 →
图书馆预约系统小程序源码拆解:Java+微信小程序+MySQL三层架构

图书馆预约系统小程序源码拆解:Java+微信小程序+MySQL三层架构

简介:这是一套基于微信小程序的图书馆预约系统毕业设计项目,面向计算机相关专业学生,适用于毕业设计或课程设计场景。系统采用微信小程序开发工具、MySQL数据库与Java的B/S架构实现,完整覆盖管理员、用户、员工三类角色&#xff1…

2026/10/9 10:32:00 阅读更多 →

日新闻

Java时间API实战:LocalDate、Date与ZonedDateTime的转换与避坑指南

Java时间API实战:LocalDate、Date与ZonedDateTime的转换与避坑指南

Java时间API这个话题,隔三差五就会在群里被翻出来讨论一次。上周还有个同事线上处理一个订单超时问题,排查到最后发现是ZonedDateTime序列化后时区丢了,用户在下单当天晚上看到的时间整整差了8个小时。这类问题几乎每个做Java开发的人都遇到过…

2026/10/9 0:00:49 阅读更多 →
EasyTier实践:从NAT穿透到子网代理的异地组网部署与排错

EasyTier实践:从NAT穿透到子网代理的异地组网部署与排错

前几个月我手头有好几台机器需要互相访问:办公室台式机、家里 NAS、还有一台云主机。如果只是偶尔传个文件倒还好,问题是工作场景经常要在几处环境之间来回切换,每次都先登录跳板机再层层代理,实在折腾。我先后试过端口映射、自建…

2026/10/9 0:00:49 阅读更多 →
AI Agent工程实战:从七要素到七个决策点的系统设计指南

AI Agent工程实战:从七要素到七个决策点的系统设计指南

AI Agent 这个词在过去一年里被反复提及,但真正动手搭过一套能跑起来的 Agent 系统的人都知道,从"知道它是什么"到"让它稳定干活"之间隔着一整套工程决策。我前后参与过几个 Agent 项目的落地,从最初用现成框架拼装&…

2026/10/9 0:01:50 阅读更多 →

周新闻

KT148A语音芯片外挂8002D功放的工程实践指南

KT148A语音芯片外挂8002D功放的工程实践指南

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

2026/10/8 15:26:32 阅读更多 →
LLC谐振变换器增益公式推导:从FHA等效到完整归一化表达式

LLC谐振变换器增益公式推导:从FHA等效到完整归一化表达式

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

2026/10/8 15:26:40 阅读更多 →
ARM架构深度解析:从RISC设计理念到交叉编译实战

ARM架构深度解析:从RISC设计理念到交叉编译实战

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

2026/10/9 10:11:06 阅读更多 →

月新闻

我发现了一个新思路:用 Remotion + Claude Code 像写代码一样自动化生成短视频

我发现了一个新思路:用 Remotion + Claude Code 像写代码一样自动化生成短视频

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

2026/10/8 21:13:17 阅读更多 →
Windows下 Codex 中 Chrome 和 Computer Use 插件不可用问题排查及解决参考方式:TaoToken 统一 Key 配置与验证

Windows下 Codex 中 Chrome 和 Computer Use 插件不可用问题排查及解决参考方式:TaoToken 统一 Key 配置与验证

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

2026/10/8 15:26:17 阅读更多 →
黑夜航拍船只数据集训练YOLOV5模型全流程解析

黑夜航拍船只数据集训练YOLOV5模型全流程解析

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

2026/10/9 6:17:20 阅读更多 →