我们把 CRM 开源了:一个给小团队用、AI 只起草不落库的客户管理系统
Daedalus CRM 是我们三条产品线里的应用层。这篇讲它为什么从 SaaS 改成开源、五个 AI 功能为什么都不直接写库、"少即是多"在代码里具体长什么样,以及克隆下来两条命令怎么跑起来。

先说结论
仓库在 github.com/BeckY824/daedalus-crm,AGPL-3.0。有 Docker 就能跑:
git clone https://github.com/BeckY824/daedalus-crm.git && cd daedalus-crm
docker compose up -d
打开 http://localhost:3000,用户名 admin,密码 admin123。没有任何东西要配:会话密钥自动生成并存在数据卷里,数据库自动建表,升级时迁移自动跑。数据是一份 SQLite 文件,在你自己的机器上。
它是给谁的
1–20 人的销售团队。我们最早是给教培 / 升学咨询机构做的:客户在微信里,成交靠试听和转介绍,团队里没有人会去写 SQL。所以默认措辞是"学员、院校 / 年级 / 专业、已试听、与家人商议"。
但数据模型本身是通用的销售漏斗——线索 → 客户 → 跟进 → 商机 → 签约。于是我们把措辞从代码里抽出来放进设置页:客户叫什么、三个档案字段叫什么、下拉里有哪些选项、状态显示成什么,管理员改一下,全站同步。一个设计工作室把"学员"改成"客户"、"院校"改成"公司",就能用。
为什么从 SaaS 改成开源
我们一度按 SaaS 的路子做:订阅、企业版、多租户。停下来的原因很简单——小团队最在意的是数据在谁手里,以及第二年还要不要续费。一个 5 个人的机构,客户名单就是全部家当,放在别人的云上心里不踏实;而每年续费的决策成本,对他们来说比买断高得多。
开源自托管把这两个问题一起解决了:数据在自己的服务器上,代码可以看、可以改;AGPL 保证了改了并对外提供服务的人要把改动公开。我们靠部署、定制和上层的工作流产品挣钱,不靠锁住数据。
AI 只起草,不落库
系统里有五个 AI 功能。它们遵守同一条规则:AI 产出的东西永远先摆在人面前,人核对后走原有的保存流程。AI 没有直接写库的路径。
| 功能 | 在哪 | 做什么 |
|---|---|---|
| 跟进速记 | 新建跟进弹窗 | 口述或粘贴微信聊天记录,AI 预填类型、要点、下次计划、待办 |
| 临战简报 | 客户详情 | 联系前一键生成:故事线、卡在哪、建议谈什么、风险 |
| 问数据 | 数据复盘 | 自然语言问业务数字。AI 只把问题翻译成受限的查询规格,不写 SQL |
| 盯盘提醒 | 数据首页 | 被遗忘的客户、停滞的商机、逾期的计划。检测是纯规则,AI 只在你点"起草"时写话术 |
| 转介绍雷达 | 渠道管理 | 谁在帮你带人、下一个该请谁开口。排序是纯规则,AI 只写邀请 |
为什么这么保守?因为模型的输出是不可信输入。它会把枚举拼错、编造一个不存在的联系人 id、把"下周三晚上"换算成一个不合法的时间。我们写了一层清洗:能收敛到系统认识的取值就收敛,收敛不了的宁可置空让人补填,也不能让脏值进库污染统计。"问数据"更极端——模型只能输出一个受限的查询规格(指标、分组维度、时间范围),由代码去执行;它问"明天能签几单",系统回答"这是预测不是查询",而不是查未来某天得出"0 笔"。
接什么模型也不重要。设置页填接口地址、Key、模型名,任何 OpenAI 兼容接口都行:DeepSeek、OpenAI、本地 Ollama、各类中转站。Key 用会话密钥加密后存库,界面只回显尾 4 位,数据库备份里没有明文。不配 Key,AI 入口整体隐藏,其余功能照常。

少即是多,在代码里长什么样
这是我们做产品的原则,落到这个仓库里是几件具体的事:
- 跟进速记就是一个文本框加一个按钮。没有模型选择器,没有温度滑块,没有"高级选项"。
- 盯盘不是评分黑盒。规则写在一个 100 多行的纯函数里:意向较高的客户 18 天没人碰,权重 5;已试听的,权重 4。销售不信黑盒分数,但"意向较高的学员 18 天没人碰"这句话自己会说服人。同一个客户命中多条信号只留最高的一条——盯盘清单的敌人是噪音。
- 推荐归属在录入时固化。推荐人、渠道归属(推荐链往上第二代)、渠道负责人三者独立记录。改上游不会追溯性地改动已有客户的业绩,因为业绩一旦能被"改上游"重算,就没人敢信报表了。
- 并发按字段合并。两人同时编辑同一个客户,改不同字段自动合并,改同一字段才拦下并说明撞了哪几项。最高频的路径——乙只是给这个客户录了条跟进——不能误伤甲的保存。
- 不做的也写清楚。自定义字段、自定义状态流转、多套模板切换,那是另一个量级的功能,设置页上直接写了"不做"。
工程纪律
开源意味着别人会读你的代码、在你没见过的机器上跑它。有几条纪律是这个仓库一直守着的:
- 数据库迁移只增不改不删。每条语句幂等,容器每次启动全部重跑,不记录"执行到哪了"。旧版本镜像能跑在新库上,回滚不用动数据。
- 提交前三道自动关卡。类型检查、代码检查、单元测试走 git 钩子,推送即跑;E2E 在 CI 上单独跑。234 条单元测试里有一批直接调真实的 Server Action——并发闸门成立与否取决于判断和写入是不是同一条语句,绕过 action 就测不到。
- 不带默认密钥出厂。会话密钥首次启动生成,存在数据卷里。CI 里跑一遍密钥扫描,扫全部历史。
- 时区写死在容器里。这是开源后 CI 教我们的:GitHub 的机器是 UTC,服务端渲染的日期和浏览器对不上,React 水合直接报警。本地永远复现不了。
开源前我们做了一件事:把 git 历史整个重来。旧的提交里有服务器地址和真实的员工姓名,脱敏不是"改掉最新版",是让它们从来没进过仓库。
接下来
路线图在仓库里。近期是 PostgreSQL 支持和更细的权限;不在计划里的是移动端和多租户。欢迎提 issue,尤其欢迎"我不是教培,这个术语改不过来"这类反馈——那正是我们想验证的事。