普通视图

发现新文章,点击刷新页面。
昨天 — 2025年12月10日首页

知识库创业复盘:从闭源到开源,这3个教训价值百万

作者 徐小夕
2025年12月10日 18:33

"把知识库代码公开到 github 上的那天,我失眠到凌晨四点。

不是因为激动,是害怕——害怕一年心血被一键复制,害怕客户部署了就没有采购的欲望了。

120 天后再回头看,正是那次'裸奔',才救活了公司,也重塑了我对商业护城河的所有认知。

今天把决策前后最痛的 3 个教训掏出来,能帮你省下至少 100 万试错成本。"

关注我的朋友也许了解,我们24年年初,上线了一款知识库产品——橙子轻文档。

图片

github地址:

我一直觉得「在线办公工具」是个矛盾体:要么功能全但收费贵(比如某钉、某飞),要么免费但功能零散(比如单独的在线文档、独立的思维导图工具)。所以我们决定花半年时间打造 OfficeHub 这个项目,把「文档 + 表格 + 思维导图 + AI + 知识库」完美的融合成一个办公智能体。

图片

核心定位:基于 Web 的开源在线办公协作平台,集成文档编辑、思维导图、电子表格、AI 创作、模板管理和知识库功能。 简单说,OfficeHub 想做的是「办公工具界的瑞士军刀」:不用切换多个平台,一个系统搞定从内容创作到知识沉淀的全流程。

图片

但是上线运营了3个月之后,虽然积累了上千的用户使用,但是也发现了很多商业上的问题。接下来我会系统复盘一下,橙子轻文档创业的一些经验和教训。

01 教训一:把"闭源"当护城河,其实是给自己挖坟

1. 自嗨式壁垒

  • 我们曾花 2 个月自研「文档搭建引擎」,体验类比 Notion,PPT 里写满"技术壁垒"。
  • 闭源 6 个月,GitHub 只有 12 个 star,其中 3 个还是团队成员小号。

2. 客户用脚投票

  • 企业版客单价 3 万,成交周期 60 天,我天天被客户问:"有没有 API?能不能二开?"
  • 一次 POC,客户 IT 总监直接甩出飞书知识库组件:"你们不开放,我就不买。"

3. 致命瞬间

去年 9 月,账上现金只剩 2 个月,一位也在创业的朋友问我:

"如果 Notion 明天开源,你们还剩什么?" 我答不上来,那一刻我知道:闭源不是墙,是牢笼。


02 教训二:开源不是"做慈善",是"放鱼饵"

图片

上面是我们开源了编译版之后的 github 截图。

1. 120 天数据对比

指标 闭源末期 开源 +120 天
新增线索 68 条 300 条
企业版演示 1-3 次/月 10 次/月

2. 鱼饵配方

我们把知识库编译版代码开源了,让用户可以直接本地安装和部署,保证数据的绝对安全,同时还能在本地环境体验,测试。

  • Apache 模块 → 审计日志、合规,企业刚需;
  • 云原生插件 → 只给二进制,订阅解锁。

开发者爽了,企业慌了,我们笑了。开源第 7 天,一家头部券商主动约演示:"我们发现本地体验不错,想二次开发,买商业源码授权多少钱?"

这个时候我才发现,让客户能用起来,比说产品亮点说1万遍都有用。

虽然开源后,很多没有二次开发需求的企业,直接用我们的源码部署到自己服务器上作为私有知识库使用,但是这间接的提高了我们产品的影响力,进而带来了真正有二次开发需求的潜在客户。

03 教训三:开源节奏错了,同样会死

1. 一步错,全盘输

同期友商 B 直接把 100% 代码 GPL 开源,结果:

  • 企业客户担心"被传染",不敢用;
  • 社区开发者发现没"高级功能",star 数暴涨却无人续费;
  • 3 个月后资金链断裂,核心团队被大厂打包挖走。

2. 三把尺子,决定"先开哪块"

尺子 先开源 后闭源
技术壁垒低 ✓ 编辑器 UI ✗ 协同算法
获客需求强 ✓ 导入工具 ✗ 审计日志
合规风险高 ✗ 国密加密 ✓ 私有部署脚本

一句话:让开发者爽在先,让企业怕在后。

3. 节奏表

  • 0-3 个月:放编辑器 + 基础 API,拉 star、拉社群;
  • 3-6 个月:放权限框架 50%,留 GPL 暗钩,销售跟进;
  • 6 个月后:逐步闭源高阶合规模块,推出订阅制。

04 彩蛋:开源那晚,我们在代码里藏了一句 joke

// If you find this line, call cxzk_168,// we will send you a crate of oranges.

结果真有 3 个开发者联系我,我们按约寄出橙子,其中一人后来成了橙子轻文档的客户。

你看,诚意永远是最便宜的 PR。

05 如果你只能记住三句话

  1. 闭源不是护城河,是拦路墙,越早推倒越省钱。
  2. 开源不是全裸,是"比基尼"——露得恰到好处,才让人想花钱看里面。
  3. 节奏 > 决心,先让社区爽,再让客户怕,最后让公司活下来。

把代码公开那天,我以为会失去一切;
120 天后才发现,真正的护城河,是与客户一起进化和成长。

愿这 3 条价值百万的教训,帮你少失眠 180 个夜晚。

橙子已熟,等你来摘。

github地址:

目 橙子轻文档 由于用户量过多,目前只做为演示使用,切勿传个人敏感数据。

如果大家想线上管理知识笔记,大家可以移步我们的新产品——FlowmixAI

图文并茂-手把手教宝子们3分钟用 GitHub Pages 搭建免费网站 (保姆级教程)

2025年12月10日 00:02

@[TOC]( 图文并茂手把手教宝子们3分钟用 GitHub Pages 搭建你的专属网站 保姆级教程)

宝子们又来看我啦~欢迎!👋

是不是一直想拥有一个属于自己的网页?放放作品集、写写碎碎念,或者单纯用来分享一个个人网站? 但是一听到“服务器”、“域名”、“部署”这些词就头大,而且还不想花钱?💸

来来来,今天教大家用 GitHub Pages “白嫖”一个静态网站!不需要你是程序猿,只要会点鼠标,三分钟就能搞定!✨


🌟 第一步:准备好你的 GitHub 账号

首先,你要有一个 GitHub 账号。 如果你还没有,去 github.com 注册一个,起个好听的英文名哦! image


🌟 第二步:创建一个特殊的仓库 (Repository),配置网站

登录后,点击右上角的 + 号,选择 New repository

image

确保选中 Public (公开),然后点击最下面的绿色按钮 Create repository。搞定!✅ image


🌟 第三步:把你的网站放上去哦

现在仓库是空的,我们需要放一个网页进去。 为了演示方便,我们直接在网页上操作:

  1. 点击蓝色的链接 creating a new file
  2. 文件名填 index.html (这是网页的“大门”)。
  3. 在下面的大框框里,随便写点什么!比如:

4.点击 Commit ... 保存。

image

image

<!DOCTYPE html>
<html lang="zh">
<head>
 <meta charset="UTF-8">
 <title>WebGL 手势控制爱心粒子</title>
 <style>
     body { margin: 0; overflow: hidden; background-color: #000; }
     #canvas-container { width: 100vw; height: 100vh; position: absolute; top: 0; left: 0; z-index: 1; }
     /* 隐藏摄像头视频流,只用于后台分析 */
     .input_video { display: none; }
     #loading {
         position: absolute; top: 50%; left: 50%; transform: translate(-50%, -50%);
         color: white; font-family: sans-serif; font-size: 24px; pointer-events: none; z-index: 10;
     }
 </style>
 <!-- 引入 Three.js -->
 <script src="https://cdnjs.cloudflare.com/ajax/libs/three.js/r128/three.min.js"></script>
 <!-- 引入 MediaPipe Hands -->
 <script src="https://cdn.jsdelivr.net/npm/@mediapipe/camera_utils/camera_utils.js" crossorigin="anonymous"></script>
 <script src="https://cdn.jsdelivr.net/npm/@mediapipe/control_utils/control_utils.js" crossorigin="anonymous"></script>
 <script src="https://cdn.jsdelivr.net/npm/@mediapipe/drawing_utils/drawing_utils.js" crossorigin="anonymous"></script>
 <script src="https://cdn.jsdelivr.net/npm/@mediapipe/hands/hands.js" crossorigin="anonymous"></script>
</head>
<body>

<div id="loading">正在加载模型与摄像头...<br>请允许摄像头权限</div>
<div id="canvas-container"></div>
<video class="input_video"></video>

<script>
 // --- 1. Three.js 场景初始化 ---
 const scene = new THREE.Scene();
 const camera = new THREE.PerspectiveCamera(75, window.innerWidth / window.innerHeight, 0.1, 1000);
 camera.position.z = 30;

 const renderer = new THREE.WebGLRenderer({ antialias: true, alpha: true });
 renderer.setSize(window.innerWidth, window.innerHeight);
 document.getElementById('canvas-container').appendChild(renderer.domElement);

 // --- 2. 创建爱心粒子系统 ---
 const particleCount = 3000; // 粒子数量
 const geometry = new THREE.BufferGeometry();
 const positions = new Float32Array(particleCount * 3);
 const targetPositions = new Float32Array(particleCount * 3); // 存储爱心形状的目标位置
 const randomPositions = new Float32Array(particleCount * 3); // 存储散开时的随机位置

 // 爱心方程函数
 function getHeartPosition(t, scale = 1) {
     const x = 16 * Math.pow(Math.sin(t), 3);
     const y = 13 * Math.cos(t) - 5 * Math.cos(2 * t) - 2 * Math.cos(3 * t) - Math.cos(4 * t);
     const z = 0; 
     return { x: x * scale, y: y * scale, z: z * scale };
 }

 for (let i = 0; i < particleCount; i++) {
     // 生成爱心形状的目标点
     // 为了让爱心立体一点,我们随机分布t,并在Z轴加一点随机扰动
     const t = Math.random() * Math.PI * 2;
     const scale = 0.5; 
     const heartPos = getHeartPosition(t, scale);
     
     // 填充爱心内部 (随机缩放)
     const r = Math.sqrt(Math.random()); 
     
     targetPositions[i * 3] = heartPos.x * r;
     targetPositions[i * 3 + 1] = heartPos.y * r;
     targetPositions[i * 3 + 2] = (Math.random() - 0.5) * 5; // Z轴厚度

     // 生成散开的随机位置 (爆炸效果)
     randomPositions[i * 3] = (Math.random() - 0.5) * 100;
     randomPositions[i * 3 + 1] = (Math.random() - 0.5) * 60;
     randomPositions[i * 3 + 2] = (Math.random() - 0.5) * 50;

     // 初始位置设为散开状态
     positions[i * 3] = randomPositions[i * 3];
     positions[i * 3 + 1] = randomPositions[i * 3 + 1];
     positions[i * 3 + 2] = randomPositions[i * 3 + 2];
 }

 geometry.setAttribute('position', new THREE.BufferAttribute(positions, 3));

 // 粒子材质
 const material = new THREE.PointsMaterial({
     color: 0xff69b4, // 热粉色
     size: 0.4,
     transparent: true,
     opacity: 0.8,
     blending: THREE.AdditiveBlending
 });

 const particles = new THREE.Points(geometry, material);
 scene.add(particles);

 // --- 3. 交互逻辑变量 ---
 let gatherFactor = 0; // 0 = 完全散开, 1 = 完全聚合成爱心
 let targetGatherFactor = 0; // 目标聚合度,由手势控制

 // --- 4. MediaPipe Hands 配置 ---
 const videoElement = document.getElementsByClassName('input_video')[0];

 function onResults(results) {
     document.getElementById('loading').style.display = 'none';

     if (results.multiHandLandmarks && results.multiHandLandmarks.length > 0) {
         const landmarks = results.multiHandLandmarks[0];

         // 计算手掌开合程度
         // 简单算法:计算拇指指尖(4)与其他四指指尖(8,12,16,20)到手腕(0)的平均距离
         const wrist = landmarks[0];
         const fingerTips = [4, 8, 12, 16, 20];
         let totalDist = 0;

         fingerTips.forEach(idx => {
             const tip = landmarks[idx];
             const dist = Math.sqrt(
                 Math.pow(tip.x - wrist.x, 2) + 
                 Math.pow(tip.y - wrist.y, 2)
             );
             totalDist += dist;
         });

         const avgDist = totalDist / 5;

         // 经验阈值:
         // 握拳时,指尖距离手腕很近 (avgDist 约 0.1 - 0.2)
         // 张开时,指尖距离手腕较远 (avgDist 约 0.4 - 0.6)
         // 我们做一个映射:握拳(distance small) -> 聚合(factor 1), 张开 -> 散开(factor 0)
         
         // 动态调整这些阈值以适应摄像头的距离
         const closeThreshold = 0.25; 
         const openThreshold = 0.5;

         let normalized = (avgDist - closeThreshold) / (openThreshold - closeThreshold);
         normalized = 1 - Math.min(Math.max(normalized, 0), 1); // 反转:距离越小(握拳),值越大(1)

         targetGatherFactor = normalized; 

     } else {
         // 如果没有检测到手,默认缓慢散开
         targetGatherFactor = 0;
     }
 }

 const hands = new Hands({locateFile: (file) => {
     return `https://cdn.jsdelivr.net/npm/@mediapipe/hands/${file}`;
 }});

 hands.setOptions({
     maxNumHands: 1,
     modelComplexity: 1,
     minDetectionConfidence: 0.5,
     minTrackingConfidence: 0.5
 });

 hands.onResults(onResults);

 const cameraUtils = new Camera(videoElement, {
     onFrame: async () => {
         await hands.send({image: videoElement});
     },
     width: 640,
     height: 480
 });
 cameraUtils.start();

 // --- 5. 动画循环 ---
 function animate() {
     requestAnimationFrame(animate);

     // 粒子自身旋转动画
     particles.rotation.y += 0.002;

     // 平滑过渡聚合系数 (Lerp)
     gatherFactor += (targetGatherFactor - gatherFactor) * 0.05;

     // 更新粒子位置
     const posAttr = particles.geometry.attributes.position;
     const currentPositions = posAttr.array;

     for (let i = 0; i < particleCount; i++) {
         const idx = i * 3;
         
         // 目标位置插值:从 randomPositions 过渡到 targetPositions
         const tx = randomPositions[idx] + (targetPositions[idx] - randomPositions[idx]) * gatherFactor;
         const ty = randomPositions[idx+1] + (targetPositions[idx+1] - randomPositions[idx+1]) * gatherFactor;
         const tz = randomPositions[idx+2] + (targetPositions[idx+2] - randomPositions[idx+2]) * gatherFactor;

         // 增加一点动态浮动效果
         currentPositions[idx] += (tx - currentPositions[idx]) * 0.1;
         currentPositions[idx+1] += (ty - currentPositions[idx+1]) * 0.1;
         currentPositions[idx+2] += (tz - currentPositions[idx+2]) * 0.1;
     }

     posAttr.needsUpdate = true;
     renderer.render(scene, camera);
 }

 animate();

 // 窗口大小调整适配
 window.addEventListener('resize', () => {
     camera.aspect = window.innerWidth / window.innerHeight;
     camera.updateProjectionMatrix();
     renderer.setSize(window.innerWidth, window.innerHeight);
 });
</script>
</body>
</html>


🌟 第四步:配置GitHub Pages

  • 在GitHub仓库页面:
    1. 点击"Settings"选项卡
    1. 在左侧菜单中选择"Pages"
    1. 在"Source"部分,选择要部署的分支(通常是main)
    1. 选择根目录(/root)或文档目录(/docs)
    1. 点击"Save"
    1. 等待几分钟,网站将部署完成 这里是最最关键的一步!⚠️ imageimage

🌟 第五步:见证奇迹的时刻!🎉

保存好之后,其实 GitHub 已经在后台偷偷帮你部署啦! 稍等几十秒(有时候可能要一两分钟,喝口水的时间~ ☕️)。

查看搭建状态

点击仓库上方的 Actions -> 可以查看你的网站搭建状态

imageimageimage

查看网站

点击仓库上方的 Settings (设置) ⚙️ -> 左侧栏找到 Pages

恭喜你!!点击那个链接,你就看到了你刚刚写的网页啦!🌏 不管你在地球的哪个角落,只要有网,都能访问这个链接! imageimage


💖 Conclusion | 结语

  • That's all for today~ - | 今天就写到这里啦~

  • Guys, ( ̄ω ̄( ̄ω ̄〃 ( ̄ω ̄〃)ゝ See you tomorrow~~ | 小伙伴们,( ̄ω ̄( ̄ω ̄〃 ( ̄ω ̄〃)ゝ我们明天再见啦~~

  • Everyone, be happy every day! 大家要天天开心哦

  • Welcome everyone to point out any mistakes in the article~ | 欢迎大家指出文章需要改正之处~

  • Learning has no end; win-win cooperation | 学无止境,合作共赢

  • Welcome all the passers-by, boys and girls, to offer better suggestions! ~~~ | 欢迎路过的小哥哥小姐姐们提出更好的意见哇~~

image

❌
❌