团队里三个项目都要接同一套AI能力:客服分流、工单建议、内容审核。一开始各项目各自调API,结果三套接入代码长得不一样,错误处理五花八门,谁踩坑谁单独修。我看不下去,决定把它封成一个内部npm包。
这篇记一下封装时几个真实的取舍,给想干同样事的人省点纠结。
底层能力本身怎么来
我们的AI能力不是自己训的。三个智能体都搭在一个零代码平台上——客服分流、工单建议、内容审核各是一个,平台各给一个API。我的SDK做的事,本质是把「调这三个API」这件事抽象成体面的JS接口。
所以SDK的核心不是模型逻辑,是工程化:统一鉴权、统一错误、统一流式、类型友好。
取舍一:要不要把key打进包里
绝对不。key一旦进了前端能拿到的包,等于裸奔。我的SDK只接受一个endpoint——指向各项目自己的后端转发层,key由后端持有。SDK在浏览器里跑也安全。
import { createAgent } from '@team/ai-sdk';
const agent = createAgent({
endpoint: '/api/ai', // 指向本项目后端转发
scene: 'ticket-suggest',
});
const res = await agent.run({ ticket });
Node环境用的人可以传key,但浏览器场景一律走转发,文档里写死这条规矩。
取舍二:流式做成默认还是可选
三个场景里,工单建议是长文本、适合流式,分流和审核是短结果、一把返回更利落。我纠结过要不要统一。
最后做成run()返回完整结果、stream()返回异步迭代器,两个方法。别为了「统一」硬把短场景也搞成流式,调用方还得自己拼回完整串,反人类。
for await (const chunk of agent.stream({ ticket })) {
appendToUI(chunk.text);
}
取舍三:错误怎么暴露
最初我把后端的原始错误直接抛出去,结果各项目又开始各自try/catch猜错误码,回到老问题。后来SDK统一把错误归成三类:InputError(你参数错了)、ServiceError(AI侧出问题,可重试)、NetworkError。调用方只认这三类,重试逻辑我在SDK里内置——ServiceError自动退避重试两次。
一个上线后才发现的脏细节
平台侧的智能体偶尔会返回带尾随空白和markdown代码围栏的JSON(```json ... ```)。三个项目本来各自写正则剥离,写法还不一样。我把「剥围栏 + trim + 容错parse」收进SDK,统一处理。这种脏活收一次,三个项目永远不用再碰。
封完包,新项目接AI从「研究怎么调」变成「装个包、配个endpoint」,半天的活儿压成十分钟。
底层那几个智能体都是在讯飞 这类MaaS平台上零代码搭的,你只要把它当成稳定的能力源,SDK这层专心做工程化抽象就行——分工清楚,两边都轻。