1. 项目背景为什么要把开源版改成企业版先说一下这个事情的起点。WorkBuddy 是一款开源的 AI 工作助手工具本地部署之后可以直接跟模型对话、管理文档、做一些简单的 RPA 操作。开源版本身做得已经不错了——界面干净、支持多模型切换、有基础的会话管理功能拿来个人使用完全够。但放到企业场景里问题就一个个冒出来了。我所在的小组负责给公司内部搭一套 AI 生产力工具最开始直接用的开源版 WorkBuddy 做原型验证。跑了大概两周收集到的反馈集中在三个点上第一业务数据不能往外发给公网模型这是红线第二客户成功、研发、HR 几个部门都有自己的知识库和专属问答需求通用对话解决不了第三团队协作场景里成员之间需要互相指派任务、同步上下文单机版工具根本没法做到。说白了开源版解决的是一个人跟模型聊天的问题而企业需要的是一群人通过 AI 协作的能力。这个认知差就是整个改造项目的出发点。我们做这个企业版的时候给自己定了三个原则模型接入必须走公司内部网关所有请求不出内网日志可审计。权限模型要跟企业组织架构对齐不能是开源版那种谁登录都能问的粗放模式。协作能力是硬需求AI 生成的结果要能流转到人人要能 同事介入处理形成闭环。这篇文章我就把从选型评估、架构改造、模型接入、到 同事功能落地的完整过程梳理一遍中间会穿插一些踩坑记录和实测数据。如果你也在做类似的企业 AI 工具改造里面很多细节可以直接拿走用。2. 整体设计与改造思路2.1 先搞清楚开源版的架构短板在动手改代码之前我们花了大概一周时间把开源版 WorkBuddy 的代码结构摸了一遍。它的整体架构其实很典型前端是 Vue 3 WebSocket后端是 Python FastAPI中间挂了一层模型路由支持 OpenAPI 兼容接口。会话数据存在本地的 SQLite文件管理走本地磁盘。这套设计在个人场景下是没问题的但企业化改造就触及了很多结构性问题身份认证是独立的账号体系没有对接公司的 SSO。没有租户Tenant概念所有数据表都只有 user_id撑不起多部门隔离。模型路由只支持静态配置不能根据业务场景动态选择不同的大模型。知识库是全局的每个用户都能检索全部文档这在企业里是致命伤——法务的数据和销售的数据混在一起谁也接受不了。提示拿到任何一个开源工具准备做企业改造前第一步一定是画一张现状能力矩阵把认证、权限、数据隔离、审计、扩展性这五类能力逐项打分。很多项目的失败不是因为代码改不动而是因为你根本没想清楚要改哪几层。2.2 企业版的五层改造模型我把整个改造拆成了五层每一层单独推进、单独测试第一层是接入层解决身份问题。对接公司现有的 SSO 系统我们用的 OIDC 协议实现单点登录。同时引入组织架构同步任务每天凌晨从 HR 系统同步部门树和人员状态。第二层是模型层解决模型路由问题。在公司内部网关注册了三个模型实例一个通用的对话模型、一个代码生成模型、一个轻量级速答模型。路由规则做成可配置比如 HR 部门的请求优先走速答模型因为问题重复度高、需要便宜快研发部门的请求走代码模型。第三层是数据层重点做知识库隔离。每个文档上传时必须指定所属部门创建独立的向量索引空间。检索的时候根据当前用户的部门权限过滤权限不足的文档直接不进召回结果集。第四层是协作层这个就是 同事 功能的载体。在会话消息、AI 生成结果、任务卡片三个位置都支持 操作被 的人会收到站内通知点击可以直接跳到对应会话上下文。第五层是审计层所有对话记录、文件上传/下载、模型调用全部记录到独立的审计日志服务保留时长按公司数据合规要求设置为 180 天。这套五层模型的好处是每一层都能独立验收不会出现改一处崩全盘的情况。我们当时的开发节奏是每两周完成一个层的改造然后做一轮全员内测。2.3 为什么必须接公司自己的模型有朋友问过为什么不直接用公网的大模型 API速度快、效果好、还不用自己运维。这个问题在企业场景里根本不是技术选型题而是合规底线题。我们的业务群里曾经出现过一个真实事故某部门同事把一份含有客户手机号的 Excel 直接拖进了某个公网工具的对话窗口结果不到 24 小时就被安全团队拦截通报。从那之后公司对数据出内网卡得非常死——任何业务数据只要发往公网服务必须走审批流程而审批通过率低得可以忽略不计。所以企业版的第一优先级不是模型能力是安全边界。我们在公司内网部署了模型网关所有请求通过统一的 API 入口转发到私有化部署的模型服务公网请求完全断掉。为了弥补私有模型和公网大模型的差距我们在提示词层面做了大量调优同时在知识库召回上下了功夫——事实证明对于企业内部场景私有模型 强知识库的组合实际体验并不比公网大模型差太多。3. 核心细节解析模型接入、知识库隔离与提示词调优3.1 模型网关的接入方案模型网关是整个改造的地基它承担三个职责请求路由、鉴权校验、日志采集。我们使用的是开源版本身就支持的 OpenAI 兼容接口。公司内部部署的模型服务比如内网大模型框架通常都会暴露 /v1/chat/completions 这样的接口WorkBuddy 后端只需要把 base_url 改到网关地址即可。但光改地址不够因为需要支持动态路由所以我们在网关层加了一层逻辑# 路由规则示例根据部门ID动态选择模型 MODEL_ROUTES { hr: {model: fast-chat, max_tokens: 512}, rd: {model: code-model, max_tokens: 2048}, cs: {model: general-chat, max_tokens: 1024}, default: {model: general-chat, max_tokens: 1024}, } def resolve_model(user_id: str, dept_id: str, request_type: str): if request_type code: return MODEL_ROUTES[rd] return MODEL_ROUTES.get(dept_id, MODEL_ROUTES[default])这里有个非常容易被忽略的点路由不仅要看部门还要看请求类型。比如研发部的同事问一个法律合规问题这时候如果还走代码模型就不合适了。我们最终的方案是按请求内的指令词做一次轻量分类比如包含写代码debugreview等关键词的走代码模型其余走通用模型。网关鉴权部分用 JWT 透传方案。用户在 SSO 登录后拿到 token前端每次 WebSocket 连接都把 token 带上网关解析之后把 user_id、dept_id 写入请求上下文。这样模型层不需要关心身份逻辑只管处理会话职责单一、排查问题也方便。3.2 向量知识库的隔离改造开源版 WorkBuddy 原本用的是单库单集合的向量检索方案文档上传之后直接做切片、embedding然后存进同一个索引。企业内部用这个方案最大的问题就是权限无法收敛。我们的改造方案是一个部门一套索引 全局索引兜底。具体实现是在向量数据库的 Collection 命名中加入部门编码比如knowledge_hr knowledge_rd knowledge_cs knowledge_public检索的时候代码先根据请求里的 dept_id 拼出对应的 Collection 列表只在这些集合范围内做相似度搜索。全局索引knowledge_public存放全公司公开的制度文件、办公指南等内容所有人都能查。这个方案的代价是存储冗余——一份文档如果同时被多个部门引用就需要在多套索引里都存一份。但换来的是检索权限隔离的绝对可靠不用在每次查询时做文档级别的过滤。考虑到企业内部文档的总量级通常不大几万份文档撑死了存储冗余的代价完全可接受。切片参数我们也做了调整。开源版默认是 512 字一个 chunk、50 字 overlap这个参数对通用文档没问题但企业内部大量存在表格、工单记录这类结构化文本固定切片会把一条完整记录切断导致召回质量变差。我们最后用了一个基于规则的自适应切片先按 Markdown 标题和表格行做结构切分然后再按长度上限二次切割。拿一个常见制度文档做测试召回准确率从原来的 74% 提到了 89%效果提升非常明显。3.3 企业场景下的提示词工程很多人以为提示词是给大模型写作文其实在企业工具里提示词的本质是给模型配一套行为规范。我们给企业版设计了系统级提示词的三个层次第一层是基础角色设定固定写死用户不可见也不可修改。内容包括你是谁、你服务的对象是谁、数据来源范围、必须遵守的合规要求。比如你是某公司的内部 AI 工作助手回答内容仅基于知识库中已授权的文档不得臆造事实。第二层是部门级扩展指令由各部门的管理员在后台配置。比如人事部门配置了涉及考勤规则时必须先引用最新的制度版本编号研发部门配置了回答技术问题时可给出代码示例但必须标注适用版本。第三层是用户级上下文将当前会话的部门信息、历史摘要、当前检索的文档片段拼进消息。这套三层结构跑下来效果最好的是客服场景。客服问客户要求退款超期怎么办之前模型可能直接给一个通用流程但现在因为检索到了客服团队自己的 SOP 文档并且系统提示词明确要求必须引用文档编号回答输出的质量完全不是一个级别。注意部门级指令的配置界面一定要够简单。我们最初做了一个 JSON 编辑器让部门管理员填结果半个月过去了只有技术部门的人配过。后来改成模板化配置单选加填空的形式使用率才上来。企业工具的终端用户不是开发者交互设计要按业务人员的习惯来。4. 实操过程从零到一的完整落地步骤4.1 骨架改造从单用户到多租户的数据库迁移开源版 WorkBuddy 的数据库结构是单用户的我们第一步就是做多租户改造。这个听起来很吓人但实际上原表的字段并不复杂主要的表就三张users、conversations、messages。我们的做法是加一个organizations表然后在 users 表加org_id、dept_id字段conversations 和 messages 通过 user 间接关联到组织。不用把所有表都改掉——只要把访问入口收敛好底层的会话查询都通过当前用户的 org_id 过滤即可。数据库迁移脚本的关键逻辑如下# 迁移为现有用户创建默认组织 def migrate(): default_org create_organization(name默认组织) for user in get_all_users(): user.org_id default_org.id user.dept_id public user.save() create_dept_indexes()这里有一个教训千万不要为了赶进度把 org_id 做空值容忍。我们一开始允许 org_id 为空结果后来查数据的时候经常出现某些老数据看不到的诡异问题排查了整整一天最后发现是几条历史记录没关联到组织。后来我们加了启动时校验有空值直接标记异常并阻塞启动问题才彻底根治。4.2 对接 SSO 单点登录的完整过程SSO 对接我们推荐直接用 OIDC 授权码模式这套流程对接企业内部现有的统一身份平台非常成熟。整个流程是这样的用户在登录页点击企业统一登录跳转到公司的 OIDC 授权端点。用户在公司登录页面完成身份验证通常是账号密码加二次验证。平台回调到 WorkBuddy 后端的/auth/callback端点带上授权码。后端用授权码换取 token再从 token 中解析用户信息。如果用户不存在则自动创建账号并同步组织架构信息。这里要重点提醒一个细节企业内部 SSO 返回的用户信息里部门信息的字段命名五花八门。我们接的时候发现有的系统叫department有的叫dept_name还有的嵌套在extension里。一定不要在前端写死字段名而是在后端做一个映射层统一转成dept_id和dept_name。另外内部系统的 JWT 过期时间普遍设得比较短比如 30 分钟这意味着如果你只做了登录时的 token 校验用户挂机半小时后再回来点一下直接就 401 了。我们的方案是同时维护一个 refresh_token放在 HttpOnly Cookie 里WebSocket 连接断开时自动静默续期。这个体验问题非常影响口碑务必在项目排期里留出时间。4.3 私有模型的接入与参数实测模型接入的实操经验我单独拿出来讲因为这是坑最多的一环。首先是模型选择。我们对比了公司内网部署的几个模型实例最终选了一个 7B 参数的对话模型作为主力一个 3B 参数的速度模型作为速答。有人可能觉得 7B 太小了但在企业知识库 明确提示词约束的场景下7B 的表现在大多数业务问答里是够用的而且推理速度快、部署成本低。追求大参数的模型当然好但必须评估 GPU 成本和响应延迟。提示不要一上来就追求模型效果的最大化。先用一个小模型跑通全流程再逐步替换成更大更好的模型。小模型暴露出来的问题往往是最真实的交互体验问题而不是模型能力问题。其次是实测参数。我们用 100 条企业内部真实问答做了测试主要记录两个指标首 token 延迟TTFT和完整回复时间。7B 模型在单张消费级显卡上TTFT 大概在 0.4 秒左右完整回答 200 token 的回复约 2.8 秒。这个数据基本可以接受。但有一个参数必须调整就是max_tokens。开源版默认给到 4096这在企业场景下非常浪费——大部分业务问答的回答都不超过 300 token而生成大量的空话不仅慢还费 GPU。我们最终把默认卡在 1024只有在用户明确要求长文输出时才提高到 2048。4.4 前端 WebSocket 会话改造后端改完之后前端也要配合改。开源版的前端会话是一个简单的聊天窗口消息按时间排序没有会话分支的概念。企业版里我加了三样东西消息类型标识、 提及的富文本渲染、任务卡片的折叠交互。消息类型标识是在消息数据的 type 字段上扩展了枚举值从原来的text扩展到text、mention、task、system四种。这个改造看似简单但改动量不小因为前端的消息渲染组件原本只处理普通文本现在每种类型都要独立实现渲染逻辑。我建议如果改前端代码一定要从消息数据模型开始设计不要边写功能边补类型。我们最开始为了赶工期先做了文本类型的渲染再补 提及最后补任务卡片结果每次都要回去改消息渲染组件代码越改越乱。如果重来一次我会先花两天时间把消息数据模型和类型定义定下来再动 UI。5. 同事功能从单机工具到协作平台5.1 为什么企业场景需要 同事做这个功能之前我们其实在内部讨论过好几次有人觉得这是伪需求——AI 工具跟同事协作有什么关系后来是业务部门的真实使用场景说服了我。客服团队用 AI 助手回答了一个退款问题AI 给出的答案引用了过期的制度版本。这时候客服需要让主管介入确认如果没有 功能只能把 AI 的回复复制出来通过 IM 发给主管来回沟通成本很高而且上下文容易断。研发团队也遇到了类似问题AI 生成的代码片段在某个环境变量下会出问题需要 负责该模块的同事协助确认。在开源版里这个信息根本没有办法带上下文地流转。所以 同事 的本质是把 AI 生成的上下文与人际协作连接起来。用户看到一个 AI 输出觉得有问题或者需要人工介入直接 一个人对方看到的是同一段上下文、同一个会话、同一个文档引用不需要重新解释背景。这个体验的提升是非常直观的。5.2 数据模型与消息结构的扩展 功能的技术实现核心是数据模型设计。我们在消息表新增了mentions字段用 JSON 数组存储被提及的用户 ID 列表{ content: 这个退款流程需要 张三 确认一下最新的制度版本, mentions: [ {user_id: u_1001, offset: 4, length: 6} ] }offset 和 length 记录的是 文本在整条消息中的位置前端渲染时用它做高亮和跳转。存储提案信息而不是只存文本是为了保证即使后来用户改名了消息中的 关联依然有效。5.3 通知链路与一键跳转上下文被 的人怎么收到通知我们用了站内信 邮件兜底的双通道方案。用户登录系统后在侧边栏能看到未读的红点提醒。如果 发生在多人协作场景还会向被 的人推送一封邮件内容包含消息原文和会话链接。这里有一个很多人会忽略的体验细节通知链接不能只是跳到会话页面还必须定位到被提及的那条消息。我们是这么做的链接中带message_id参数前端加载完成后自动滚动到该消息的位置并做 3 秒的高亮闪烁。这个小小的交互设计让被通知的人搞清楚发生了什么事的时间从平均 30 秒降到了 5 秒以内。为了实现这个效果前端需要在收到通知时先打开 WebSocket 连接拉取会话的历史消息等该条消息渲染完成后再触发滚动和高亮。这个流程在处理长会话时有点小卡顿我们的优化方案是历史消息分页加载时优先加载目标消息附近的前后各 20 条其余消息后续按需加载。5.4 与组织架构联动的权限控制 功能不可能让用户随便 全公司任何人这既是隐私问题也是信息安全问题。我们做了一个限制用户只能 同一个租户下的人并且默认只能 本部门同事和跨部门的协作组成员。跨部门协作是通过协作项目这个实体来管理的双方人员在同一个协作项目下才能互相 。这个权限控制直接复用了前面做好的组织架构同步数据。每次 输入的时候前端会请求一个接口获取可 人员列表该列表按组织架构实时过滤。输入 之后的下拉框只会展示这个列表内的人从根本上杜绝了输入了名字但发不出去的尴尬。6. 常见问题与排查技巧实录6.1 会话消息丢失排查了两天才找到根因改造多租户后的第三周有用户反馈说会话消息偶发丢失重新打开会话后只有一半的消息还在。我们第一反应是数据库写入问题查了半天没发现任何异常。后来发现是我们把 SQLite 换成了独立数据库服务之后WebSocket 断开时的消息写入没有做幂等处理前端重连时会重发最后一次消息导致数据库出现重复数据而前端按时间倒序渲染时正好把重复的那条顶掉了。解决方法给每条消息加一个客户端生成的client_msg_id服务端存储时做唯一性校验重复的直接忽略。这类分布式系统里最常见的坑在改造单体应用时很容易被忽略。6.2 模型回答串了部门的知识库上线一个月后有人反馈HR 部门的同事问了一个考勤问题AI 的回答里引用了研发部门的文档。查下来发现问题是这样的——向量知识库设计时虽然检索范围已经通过 Collection 过滤了但提示词里拼接检索结果的时候没有把来源文档的部门信息一并留给模型模型在做回答时想当然地关联了自己记忆中相似的内容。解法是两点第一检索结果返回时强制携带source_dept字段第二系统提示词里明确规定只能引用结果中明确标注了文档编号的内容不得自行联想来源。再跑了一轮测试跨部门引用的问题基本消失了。6.3 通知延迟导致用户以为功能坏了 功能刚上线时有用户反映被 了根本没提醒。查日志发现通知消息发出去了但用户那边 WebSocket 连接已经断开而我们的通知推送依赖 WebSocket 在线状态断线用户只能等下一次登录时才能收到站内信邮件兜底逻辑又没有接上。改进方案通知服务不再依赖 WebSocket 在线状态消息写入后立即触发邮件发送站内信仅作为系统内聚合展示。同时把 WebSocket 的心跳机制从 30 秒一次改成 15 秒一次保证在线状态的准确性。6.4 常见问题速查表问题现象可能原因排查步骤解决方案SSO 登录后用户部门为空映射层字段名不匹配查看 OIDC 回调的原始用户信息增加字段名映射配置支持多别名向量检索召回率偏低切片策略不合理检查知识文档的召回结果片段切换为结构感知自适应切片模型回复内容过长max_tokens 未调整查看网关日志中的 token 统计将默认值改为 1024 同事没有提醒WebSocket 断线检查通知服务的心跳日志通知与在线状态解耦 邮件兜底消息重复出现前端重连重发查看数据库同一条消息的时间戳引入 client_msg_id 幂等写入会话列表打开很慢全量消息拉取查看接口耗时分布分页加载 目标消息优先加载6.5 给初次做企业版改造的团队三条建议第一先跑通一个最小闭环再铺开做。我们最开始的 2 周内只做了一个一个部门、一个模型、一个知识库的试点所有功能都是围绕这个场景设计的。跑通之后再横向扩展部门和功能风险会小很多。第二把安全合规的检查前置到设计阶段。不要等代码写完了再去做数据权限审查那时候改起来成本太高。我们在每个数据表设计的当天就过一遍权限矩阵确保谁能看到这个表里的哪几列是明确的。第三重视审计日志的价值。企业版上线后审计日志不仅是为了满足合规要求它还是排查疑难问题的重要工具。有一次用户反馈AI 莫名其妙回答了不在知识库里的内容我们就是靠审计日志还原了完整的请求链路才发现是一个部门级指令配错了关键词。7. 上线半年后的实际效果与心得到目前为止企业版已经在我们内部跑了半年接入的部门从最初的一个试点扩展到了七个日活用户稳定在三百人上下。说实话这个规模在企业内部工具里不算大但反馈的数据挺有价值客服团队的平均首响时间从原来的 8 分钟降到了 1 分半钟研发团队把 AI 工具接入了他们的日常技术查询流程后每天节省的碎片化时间大约在四十分钟左右。我个人体会最深的一点是改造一个开源工具技术上的难点其实只在最开始的一两周——架构摸透了、模型接上了后面的工作主要是围绕业务反馈做迭代。真正难的是让团队从用工具的心态转变成共建工具的心态。比如 同事功能上线前我们觉得这个功能已经做得很完善了但真正用起来后用户不断提出新的组合需求能不能 一个机器人来跑定时检查能不能在任务卡片里直接 责任人和截止时间——这些需求单个看都不难但它们都在揭示同一件事企业用户需要的不是一个更聪明的聊天机器人而是一个能嵌入他们工作流的协作节点。如果你也在做类似的事情我给的建议很直接不要一开始就追求大而全先解决一个部门最痛的那个问题。把那条链路做顺了、做好了再横向复制。开源版的好处就在于此——它给了你一个快速起步的底座但真正的价值永远是你围绕业务场景创造出来的那一层。