Elevatus:AI 驱动的多租户招聘 SaaS
主导 Elevatus 的路线图执行和 microservices 迁移--一个服务中东企业和政府的云招聘平台,用 Camunda BPMN 实现每个租户独立的工作流、Keycloak 联合身份,以及 2018 年就在生产里跑的 AI。
Elevatus 是真实存在的,今天还在 elevatus.jobs 上跑着。中东的政府用它做公务员招聘轮次,有一千人 HR 部门的企业用它。两者—不同的租户,截然不同的合规包络—运行在同一个核心平台上。
我从 2018 年到 2020 年在 Talentera(现在是 Elevatus)的 Roadmap 团队担任技术团队负责人和 IC 架构师。我构建了什么、带领了什么、在哪里犯了错误—这就是这篇案例研究的内容。
起点:AOL/Navi 服务器上的 TCL/TK
不,这不是拼写错误。
我加入的时候,部分代码库还在 AOL/Navi web 服务器上的 Tcl/Tk 上跑—一个在产品需要处理企业 SLA 的同时正在失去支持的技术栈。我们还背着一个 PHP 7 单体、一个越来越大的 Vue.js 前端,以及早期没有统一服务间合同的 Java 8 服务。
迁移不是绿地。永远不是。你在服务真实租户—不能忍受周一早上宕机的政府部委—的同时,拆耦一个从未被设计为可拆耦的系统。方法是 Strangler Fig:新能力从第一天起就作为有界上下文 microservices 落地;遗留界面逐步被替换。路由层(Nginx,后来是 API 网关)让这些切换对客户端不可见。
领域驱动设计作为强制函数
DDD 是我们用来与产品团队谈判服务边界的语言。这不是学术练习—它是让工程师能够说「不,那个功能属于候选人域,如果你把它放在招聘人员域里,我们会用两年时间来解开它」的机制。在多产品、多 squad 的环境里,这个词汇表的价值超过了架构图。
涌现出来的有界上下文—候选人、申请、工作流、身份、分析—整洁地映射到团队所有权。当一个政府客户要求一个自定义招聘阶段时,工作流团队拥有这个需求,不需要碰候选人或分析。这就是你要找的胜利:变更隔离。
Camunda BPMN:每个租户的工作流,不是全局的
这是我对那段时期最满意的决策。
招聘工作流是真正可变的。政府部委的公务员招聘流程可能有 14 个阶段、三个审批委员会和法律规定的强制等待期。初创公司的流程可能是 5 个阶段,一周内跑完。你无法把任何一个写死,也无法构建一个覆盖两者而不让其中一个无法使用的配置 UI。
你能做的是把两个都建模为 BPMN 流程定义,每个租户存储一份,在共享的 Camunda 引擎上执行。为新企业客户添加新的工作流变体变成了一次 BPMN 文件 deploy,不是一个 sprint。这句话在你的销售团队向一个大型政府客户承诺平台足够灵活到能处理他们采购要求时,承担了很多重量。
Camunda 的决策也免费给了我们可审计性。政府客户关心可审计性的方式企业客户不会,不是因为他们的开发者要求更高,而是因为他们的法务团队要求如此。每一次工作流转换都是一个 Camunda 事件,审计轨迹是一次查询,不是一次取证练习。
Keycloak 和联合身份问题
多租户在一个用户存在于多个租户上下文的那一刻开始变复杂。
在 MENA 招聘市场,候选人会广泛申请。一个有资质的工程师可能同时在三个招聘轮次里处于活跃状态—一个在科技公司,一个在政府部委,一个在地区银行—全部在同一个平台上。同一个邮件地址,同一个人,三个完全分离的租户上下文、三个完全分离的申请状态,以及一个明确要求—三个租户中没有任何一个能知道候选人也在向其他两个申请。
答案是带每个租户独立 realm 和跨 realm 令牌交换的 Keycloak。候选人的核心档案住在共享身份空间,申请数据住在租户 realm。认证是租户范围的—你不能把一个租户会话的凭据意外带到另一个租户里。Keycloak 的 realm 联合让这变得可操作,而不需要从头构建身份联合,那种工作在第二年的边缘情况涌现时会产生安全事件。
2018 年的「AI 驱动」,诚实地说
Elevatus 的宣传包含 AI 驱动的招聘。这是真的,也比听起来稍微复杂一些。
GPT-3 还不存在。我们有的是 ML 驱动的 CV 解析和评分、基于加权标准的候选人排名,以及早期的视频面试分析。CV 解析是在生产里运作良好的那块—从阿拉伯语、英语、法语和印地语的非结构化简历里做结构化提取,归一化成排名模型能按职位标准评分的候选人档案。
每个租户的模型个性化是架构上有意思的部分。政府部委的招聘人员决策(证书验证和监管合规主导)塑造的排名模型,与科技公司的决策(作品集和展示出来的技能主导)训练出的模型有实质性的不同。这是正确的结果—但它也是一个如果你只跑一个全局模型就不存在的存储和服务问题。多租户应用到 ML 基础设施是它自己的约束层。
视频面试分析是有效的。它也在回头看时,是一个在 2018 年需要比我们拥有的更严格的偏见和公平工具的系统。我们的政府客户要求对「这个模型在人口群体之间是否公平地对待候选人」有明确答案—不是因为他们在哲学上走在时代前面,而是因为他们的多样性指令把这变成了一个合同问题。「模型说这样」不是部长办公室会接受的答案。我们很早学到了这点,这个领域后来也追上了。
移动层:我会做不同的事
先是 Ionic,后来是 Flutter。两者都是把 Web 体验适配到移动端,而且这一点很明显。
候选人在移动端的体验—申请、追踪状态、在手机上做视频面试—有和招聘人员在桌面端的体验不同的 UX 约束:审阅数百个候选人、做批量决策、运行招聘轮次。我们把移动端建成了一个移植版本,它永远没有移动端原生设计的产品那么好。
如果我今天来做这件事,候选人 UX 和招聘人员 UX 会是共享后端的两个独立产品。API 层从一开始就会以两个消费者为设计对象。这是每个人都会以艰难方式学到一次的 API 优先 lesson,我现在学到了。
我负责的事
- 跨多个 squad 的路线图执行和 sprint 规划
- DDD 推动和有界上下文边界决策
- Camunda BPMN 集成架构和每租户工作流模型
- Keycloak realm 联合设计
- Microservices 迁移排序(Strangler Fig 执行)
- 跨服务通信的 RabbitMQ 事件拓扑
- 跨 squad 的 Jenkins 流水线标准化
政府实际在使用这个。在你经历了采购流程、安全审查、定制化谈判和「实际使用」所需的集成工作之后,这句话落地的重量不同。这不是和友好早期用户的 MVP,而是为不能容忍宕机的机构运行关键流程的生产软件。
这就是我们建的东西。