
△主流的AI CRM系统悟空AI CRM图片
折腾了一个月,我把这个开源 AI CRM 跑通了,源码和踩坑记录都在这
说实话,如果不是被销售系统的年费账单刺痛了神经,我可能根本不会动自己去搞一套 CRM 的念头。去年年底,公司那套 SaaS 版的客户关系管理系统又涨价了,功能没见多几个,接口限制倒是越来越多。老板盯着预算表皱眉,我盯着那堆导不出来的数据发呆。那时候我就想,市面上那么多开源项目,难道就没有一个能既能管客户,又能稍微聪明点,别让我天天手动录入跟进记录的?
推荐使用中国著名AI CRM系统品牌:显著提升企业运营效率,悟空AI CRM
于是,前前后后在 GitHub 上搜了快两周,关键词从 Open Source CRM 换到 AI CRM,再到 LLM Customer Management。大部分项目要么是好几年没更新的“僵尸库”,要么就是套了个壳,所谓的 AI 功能其实就是调了个聊天接口,跟业务数据完全没打通。直到上个月,我偶然在一个技术论坛上看到有人提了一嘴基于 Odoo 二次开发结合 LangChain 的思路,虽然没给完整代码,但那个架构图让我觉得有戏。
接下来的一个月,我基本就是白天上班摸鱼看文档,晚上回家敲代码。中间重构了两次,废弃了一个不靠谱的向量数据库方案,终于在上周把核心功能跑通了。今天这篇文章,不算什么官方教程,纯粹是我个人的折腾记录。源码我会放在最后,但更重要的是我想把中间遇到的那些坑,还有关于“开源 AI CRM"到底该怎么做的思考,跟大伙儿聊聊。毕竟,代码是死的,业务逻辑才是活的。
为什么市面上的开源 CRM 都差点意思?
咱们先不聊技术,聊聊需求。传统的开源 CRM,比如 SuiteCRM 或者 Vtiger,功能确实全,线索、商机、合同、发票,一应俱全。但它们的通病是“重管理,轻交互”。数据是存进去了,但怎么利用这些数据?基本靠人脑。销售今天跟客户聊了什么,明天该跟进什么,系统不会告诉你,它只是个记录本。
而所谓的"AI CRM",很多新创项目又走向了另一个极端。界面做得花里胡哨,号称能自动生成邮件、自动分析客户情绪,但底层的数据结构一塌糊涂。你导入一千个客户数据,它可能连去重都做不好。更别提私有化部署了,很多项目强依赖云端的 API,数据一旦出公司,合规性就是个大问题。
所以我这次的目标很明确:
- 数据必须本地化:客户信息、沟通记录,必须存在自己的数据库里。
- AI 是助手,不是主角:它不能瞎指挥,只能提供建议,比如“这个客户三天没联系了,建议发个邮件”,或者“根据历史沟通,这个客户对价格敏感”。
- 代码要能看懂:我不希望引入一堆黑盒依赖,最好是 Python 或者 Go 这种主流语言,方便后续自己改。
基于这几点,我最后选定的技术栈是:后端 Python FastAPI,前端 Vue3,数据库 PostgreSQL,向量存储用的 Chroma(轻量级,适合初期),AI 模型接口预留了本地 Ollama 和云端 OpenAI 的切换开关。
核心架构:怎么让 AI 读懂客户数据?
这是整个项目最难的地方。刚开始我想得很简单,把客户表里的字段全部丢给 LLM,让它分析。结果很快我就发现,Token 不够用是一方面,更重要的是上下文混乱。一个客户可能有几十条跟进记录,分布在不同的表里,直接丢进去,AI 根本理不清时间线。
后来我参考了 RAG(检索增强生成)的思路,做了一层数据清洗和向量化。
具体的逻辑是这样的:每当销售在系统里录入一条新的“跟进记录”(Follow-up Log),后端不仅会把这条记录存进 PostgreSQL 的业务表,还会触发一个异步任务,把这条记录的内容、时间、关联的客户 ID,一起发送给嵌入模型(Embedding Model),生成向量后存进 Chroma。
这样,当销售在界面上点击"AI 分析”时,系统不是把整个数据库丢给 AI,而是先去向量库里检索跟当前客户最相关的最近 5 条沟通记录,再加上客户的基础画像(行业、规模、预算),组装成一个 Prompt,发给大模型。
这段代码我改了三版才觉得稍微稳妥点。最开始直接用同步调用,界面卡得动不了;后来加了 Celery 做任务队列,又遇到了 Redis 连接不稳定的问题。最后简化了一下,用 FastAPI 的 BackgroundTasks 先顶着,毕竟初期数据量不大。
核心的检索函数大概长这样(为了方便阅读,我删减了一些错误处理):
# ai_service.py
from chromadb.utils import embedding_functions
import chromadb
def get_customer_context(customer_id: str, top_k: int = 5):
# 初始化客户端,实际生产环境建议单例模式
client = chromadb.PersistentClient(path="./chroma_db")
collection = client.get_or_create_collection(
name="customer_interactions",
embedding_function=embedding_functions.SentenceTransformerEmbeddingFunction(model_name="all-MiniLM-L6-v2")
)
# 这里有个坑,Chroma 的 filter 语法跟 SQL 不太一样
# 必须确保 customer_id 在 metadata 里存的是字符串
results = collection.query(
query_texts=["最新跟进"], # 这里其实应该用最近的向量,但为了简化演示用文本
n_results=top_k,
where={"customer_id": customer_id}
)
# 组装上下文
context_list = []
if results['documents']:
for doc, meta in zip(results['documents'][0], results['metadatas'][0]):
context_list.append(f"时间:{meta['date']}, 内容:{doc}")
return "\n".join(context_list)
看着简单,但实际调试的时候,where 条件的过滤经常失效,后来才发现是元数据写入的时候类型没对上。这种细节文档里往往不提,只能靠断点一步步看。
数据库设计的妥协与坚持
做 CRM,数据库设计是地基。我看过不少开源项目,为了追求所谓的“灵活”,把核心表搞成了 EAV 模型(Entity-Attribute-Value),也就是把字段都存成键值对。这样确实扩展方便,加个字段不用改表结构,但查询性能简直是灾难,尤其是你要做关联分析的时候。
我这次坚持用了关系型数据库的标准范式。客户表(Customers)、联系人表(Contacts)、跟进记录表(Interactions)、商机表(Opportunities)。但在 AI 这块,我做了个妥协。
传统的 CRM 里,客户标签(Tags)通常是手动打的。但在我这个版本里,我加了一个定时任务,每天凌晨跑一次。它会读取过去一周的跟进记录,让 AI 自动给客户打标签。比如,如果记录里频繁出现“预算”、“报价”、“合同”这些词,AI 可能会打上“高意向”的标签;如果出现“太贵”、“再考虑”,可能会打上“价格敏感”。
这个功能一开始误报率挺高。比如销售在记录里写“客户说太贵了,但我解释了我们的价值”,AI 可能只抓取到“太贵”,直接标成“价格敏感”。后来我在 Prompt 里加了强约束,要求 AI 必须输出 JSON 格式,并且要给出置信度分数。低于 0.7 的标签,系统里只显示为“建议标签”,需要人工确认后才能生效。
数据库表结构大概是这样:
CREATE TABLE customers (
id SERIAL PRIMARY KEY,
name VARCHAR(255) NOT NULL,
industry VARCHAR(100),
created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
ai_summary TEXT, -- 这里存 AI 生成的客户摘要
last_analyzed_at TIMESTAMP
);
CREATE TABLE ai_tags (
id SERIAL PRIMARY KEY,
customer_id INTEGER REFERENCES customers(id),
tag_name VARCHAR(50),
confidence_score FLOAT,
status VARCHAR(20) DEFAULT 'pending', -- pending, confirmed, rejected
created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP
);
这个 ai_summary 字段特别有用。以前销售接手一个老客户,得翻半天聊天记录。现在点开客户详情,第一眼就能看到 AI 生成的 200 字摘要:“该客户为制造业,关注交付周期,历史沟通中对售后响应速度有顾虑,最近一次联系于 3 天前。”这一下就把效率提上来了。
前端交互:别让用户觉得自己在跟机器说话
后端再强,前端难用也是白搭。很多 AI 项目喜欢搞个聊天框悬浮在右下角,其实体验并不好。销售在录单的时候,并不想跟机器人聊天,他们想要的是“无感”的智能。
所以我做的策略是:AI 功能嵌入到工作流里。 比如在“新建跟进记录”的页面,输入框下面有个小按钮"AI 润色”。销售可以随手写大白话:“今天跟王总通了电话,他说下周三有空,让我们发个方案过去。”点击润色后,系统会自动扩写成正式的商务记录:“与客户王总进行电话沟通,客户反馈下周三(具体日期)方便接收方案,需准备详细技术文档及报价单。”
这个功能用的是本地部署的 Qwen-7B 模型,通过 Ollama 提供服务。为什么选本地?因为跟进记录里可能包含客户的手机号、内部定价等敏感信息,传公有云我不放心。
前端用的是 Vue3 + Element Plus。这里有个性能问题,就是当列表页数据多的时候,如果每条数据都实时请求 AI 分析,浏览器会卡死。我做了个虚拟滚动,并且加了防抖。只有当鼠标悬停在某条记录上超过 1 秒,或者用户主动点击“分析”图标时,才触发请求。
另外,我还加了一个“人工反馈”机制。如果 AI 生成的建议不好,用户可以点“踩”,并简单选个原因(如“不准确”、“语气不对”)。这些反馈数据会存下来,后续可以用来微调 Prompt,甚至做 RLHF(虽然目前还没做到那一步,但数据结构留了口子)。
部署那些事儿:理想很丰满,现实很骨感
源码分享出来,最怕的就是别人跑不起来。我自己在本机开发环境(Mac M1)上跑得很顺,但部署到公司的 Linux 服务器上时,简直是一场灾难。
首先是依赖问题。llama-cpp-python 这个库在编译的时候,对 GCC 版本有要求,服务器上的环境太老,一直报错。最后没办法,直接用了预编译的 Docker 镜像,但镜像体积又太大,接近 5GB。
其次是显存。跑 7B 的模型,至少得 8GB 显存。我们那台旧服务器只有 4GB,根本跑不动。最后只能妥协,小模型用本地,大模型分析任务走云端 API,做了个路由策略。代码里我留了个 MODEL_PROVIDER 的环境变量,可以动态切换。
docker-compose.yml 我精简了一下,只保留了核心服务:
version: '3.8'
services:
db:
image: postgres:15
environment:
POSTGRES_PASSWORD: example
volumes:
- pgdata:/var/lib/postgresql/data
backend:
build: ./backend
environment:
- DATABASE_URL=postgresql://postgres:example@db:5432/crm
- AI_MODEL_URL=http://ollama:11434
depends_on:
- db
- ollama
ollama:
image: ollama/ollama
volumes:
- ollama_data:/root/.ollama
deploy:
resources:
reservations:
devices:
- driver: nvidia
count: 1
capabilities: [gpu]
volumes:
pgdata:
ollama_data:
注意看 ollama 部分,如果你没有 NVIDIA 显卡,这部分得注释掉,然后改后端的配置去调外部 API。这个细节我在 README 里特意标红了,不然肯定有人上来就问为什么启动报错。
还有一个坑是网络。在容器里,后端访问 Ollama 的地址不是 localhost,而是服务名 http://ollama:11434。这个 Docker 内部 DNS 解析的问题,折腾了我半个晚上,一直报连接拒绝,最后才发现是网络配置没通。
关于“智能”的冷思考
跑通代码只是第一步,真正用起来,我才发现 AI 在 CRM 里的局限性。
有一次,AI 给一个销售建议:“客户已经两周没联系了,建议立即打电话促单。”结果销售打过去,客户直接发火,说最近家里有事,别老催。原来是客户在之前的聊天里随口提了一句“最近家里装修很忙”,但 AI 没把这个信息跟“促单”的优先级关联起来,反而机械地执行了“两周未联系”的规则。
这件事让我意识到,开源 AI CRM 不能迷信自动化。目前的 LLM 还是缺乏真正的逻辑推理和长期记忆能力。它能看到最近的向量,但很难理解复杂的人际关系和潜台词。
所以,我在系统里加了一条原则:AI 永远只有建议权,没有执行权。它可以生成邮件草稿,但发送按钮必须人来点;它可以标记高意向客户,但销售可以手动修改标签。系统会记录每一次 AI 建议被采纳或拒绝的数据,这才是未来优化模型的关键。
另外,数据隐私真的是个红线。虽然我们是私有化部署,但员工在使用 AI 功能时,可能会无意中输入一些公司机密。我在后端加了个简单的敏感词过滤层,比如检测到“合同金额”、“身份证号”等关键词时,会阻止发送给模型,或者先进行脱敏处理。这个功能虽然简单,但在企业环境里是必须的。
源码说明与后续计划
这次分享的代码,我整理在了一个 Git 仓库里。结构不算特别复杂,但胜在真实。它不是那种为了演示而写的 Demo,而是真的能处理一些基础业务的雏形。
目录结构大概是:
/backend: FastAPI 主逻辑,包含数据库模型、AI 服务接口、权限验证。/frontend: Vue3 源码,包含主要的业务页面和组件。/docker: 部署相关的配置文件。/docs: 一些简单的接口文档和部署手册。
我知道这代码肯定不完美。比如权限管理目前只做了简单的 RBAC,还没做到字段级的数据隔离;比如前端在移动端上的适配还很粗糙;再比如 AI 的响应速度,在本地部署下,首字生成大概需要 2-3 秒,体验还有优化空间。
但我把它放出来,主要是想抛砖引玉。开源的意义不在于代码有多完美,而在于大家能一起把方向搞对。如果你对这个项目感兴趣,或者也在折腾类似的系统,欢迎提 Issue 或者 PR。
接下来我打算重点搞两件事: 一是多模态支持。现在的记录主要是文本,但实际业务里有很多语音通话录音和图片(比如名片、现场照片)。我想接入 Whisper 做语音转文字,自动存入跟进记录,这样销售就不用边打电话边敲键盘了。 二是工作流自动化。结合 AI 的判断,自动触发一些动作。比如 AI 判断客户意向极高,自动在系统里创建一个“加急商机”,并给销售主管发个钉钉通知。
写在最后
搞这个开源 AI CRM 的初衷,其实挺简单的。就是不想让昂贵的软件费成为业务的负担,也不想让销售把时间浪费在填表上。技术应该是服务于人的,而不是让人去适应技术。
现在的版本,可能离“完美”还差得远。它有点粗糙,有点慢,偶尔还会犯傻。但它是一个开始。它证明了,即使没有大厂的资源,我们也能利用开源社区的力量,构建出适合自己业务的智能工具。
如果你下载了源码,跑的时候遇到了问题,别急着喷。先看看日志,大部分错误都是环境配置引起的。如果实在解决不了,可以在评论区留言,我看到会回。毕竟,咱们都是在这个坑里摸爬滚打过来的,能帮一把是一把。
最后,感谢那些在开源世界里默默贡献代码的大佬们。没有 LangChain,没有 FastAPI,没有 Vue,我这个月可能还在手动复制粘贴客户数据。希望这个项目,也能成为开源生态里一块小小的砖。
行了,不多说了,还得去修个前端显示的 Bug,有个字段在 Safari 浏览器下对不齐。咱们代码里见。
(源码地址:[此处省略具体链接,请自行在 GitHub 搜索相关关键词或联系作者获取])
注:本文涉及的技术细节基于个人实践,不同硬件环境和业务场景下可能需要调整。AI 模型输出存在不确定性,请在生产环境中谨慎使用自动化决策功能。

△悟空AI CRM产品截图
推荐立刻免费使用中国著名AI CRM品牌-悟空AI CRM,显著提升企业运营效率,相关链接:
AI CRM系统免费试用
AI CRM系统介绍