{"version":"https://jsonfeed.org/version/1.1","title":"奇诺分享 | blog.ccino.cc - 重在分享","home_page_url":"https://blog.ccino.cc","feed_url":"https://blog-ccino-cc.pages.dev/json/","description":"","icon":"https://cdn-blog-ccino-cc.ccino.cc/blog-ccino-cc/production/images/channel-6ebae07c242a357bee27d74ca3e4b3ce.png","favicon":"https://cdn-blog-ccino-cc.ccino.cc/blog-ccino-cc/production/images/favicon-bdc56a1bbdd3d9f75032075380339348.jpg","authors":[{"name":"奇诺分享"}],"language":"zh-cn","items":[{"id":"VfgVvHNOzf6","title":"10分钟搭建一个自己的网文阅读器 安卓的“阅读”APP网页版 附带书源！","attachments":[{"url":"https://blog.ccino.org/p/build-your-own-online-literature-reader-in-10-minutes-androids-reading-app-web-version-comes-with-book-sources/","mime_type":"text/html"}],"url":"https://blog.ccino.cc/i/build-your-own-online-literature-reader-in-10-minutes-androids-reading-app-web-version-comes-with-book-sources/","external_url":"https://blog.ccino.org/p/build-your-own-online-literature-reader-in-10-minutes-androids-reading-app-web-version-comes-with-book-sources/","content_html":"<h2>&nbsp;介绍</h2><p><br></p><p>安卓手机 APP&nbsp;阅读3&nbsp;服务器版，桌面端。</p><p>后端&nbsp;Kotlin&nbsp;+&nbsp;Spring Boot&nbsp;+&nbsp;Vert.x&nbsp;+&nbsp;Coroutine&nbsp;；</p><p>前端&nbsp;Vue.js&nbsp;+&nbsp;Element。</p><p>GitHub 开源项目，支持 Docker 搭建。</p><h3>1.1 特点</h3><p><br></p><p>咕咕这边简单在网上也抄~~~~ 搜集了一些特点，供大家参考（翻译自&nbsp;<a href=\"https://github.com/hectorqin/reader\" rel=\"noopener noreferrer\" target=\"_blank\">GitHub</a>&nbsp;的&nbsp;README）：</p><ul><li>GitHub 完全开源，可以免费使用</li><li>Docker 搭建，10 分钟搞定</li><li>书架管理</li><li>搜索</li><li>书海</li><li>看书</li><li>移动端适配</li><li>换源</li><li>翻页方式</li><li>手势支持</li><li>自定义主题</li><li>自定义样式</li><li>WebDAV 同步</li><li>文字替换过滤</li><li>听书 &lt;仅部分浏览器支持，手机端会因为锁屏而失效&gt;</li><li>用户配置备份恢复</li><li>支持漫画</li><li>支持音频</li><li>书源失效检测</li><li>导入本地 TXT、EPUB、UMD 格式的书籍</li><li>书籍分组</li><li>RSS 订阅</li><li>定时更新书架</li><li>并发搜书</li><li>本地书仓</li></ul><h3>1.2 问题</h3><ul><li>部分使用了&nbsp;Javascript&nbsp;的书源可能会报错，不支持&nbsp;cookie&nbsp;和&nbsp;cache&nbsp;功能</li><li>不支持使用了&nbsp;webview&nbsp;的书源</li><li>不支持书源登录功能</li></ul><h3>1.3 技术栈</h3><p><br></p><p>后端&nbsp;Kotlin&nbsp;+&nbsp;Spring Boot&nbsp;+&nbsp;Vert.x&nbsp;+&nbsp;Coroutine&nbsp;；</p><p>前端&nbsp;Vue.js&nbsp;+&nbsp;Element</p><h3>1.4 免责声明（Disclaimer）</h3><p><br></p><p>阅读&nbsp;是一款提供网络文学搜索的工具，为广大网络文学爱好者提供一种方便、快捷舒适的试读体验。</p><p>当您搜索一本书的时，阅读会将该书的书名以关键词的形式提交到各个第三方网络文学网站。各第三方网站返回的内容与阅读无关，阅读对其概不负责，亦不承担任何法律责任。任何通过使用阅读而链接到的第三方网页均系他人制作或提供，您可能从第三方网页上获得其他服务，阅读对其合法性概不负责，亦不承担任何法律责任。第三方搜索引擎结果根据您提交的书名自动搜索获得并提供试读，不代表阅读赞成或被搜索链接到的第三方网页上的内容或立场。您应该对使用搜索引擎的结果自行承担风险。</p><p>阅读不做任何形式的保证：不保证第三方搜索引擎的搜索结果满足您的要求，不保证搜索服务不中断，不保证搜索结果的安全性、正确性、及时性、合法性。因网络状况、通讯线路、第三方网站等任何原因而导致您不能正常使用阅读，阅读不承担任何法律责任。阅读尊重并保护所有使用阅读用户的个人隐私权，您注册的用户名、电子邮件地址等个人资料，非经您亲自许可或根据相关法律、法规的强制性规定，阅读不会主动地泄露给第三方。</p><p>阅读致力于最大程度地减少网络文学阅读者在自行搜寻过程中的无意义的时间浪费，通过专业搜索展示不同网站中网络文学的最新章节。阅读在为广大小说爱好者提供方便、快捷舒适的试读体验的同时，也使优秀网络文学得以迅速、更广泛的传播，从而达到了在一定程度促进网络文学充分繁荣发展之目的。阅读鼓励广大小说爱好者通过阅读发现优秀网络小说及其提供商，并建议阅读正版图书。任何单位或个人认为通过阅读搜索链接到的第三方网页内容可能涉嫌侵犯其信息网络传播权，应该及时向阅读提出书面权力通知，并提供身份证明、权属证明及详细侵权情况证明。阅读在收到上述法律文件后，将会依法尽快断开相关链接内容。</p><h2>2. 项目展示</h2><p><br></p><p>GitHub 原项目地址：<a href=\"https://github.com/hectorqin/reader\" rel=\"noopener noreferrer\" target=\"_blank\">https://github.com/hectorqin/reader</a>（617&nbsp;1.9 k star）</p><p>Demo 地址 (服务器位于日本) : [https://reader.nxnow.online(此域名 7 月过期) 更换为](<a href=\"https://reader.nxnow.online/\" rel=\"noopener noreferrer\" target=\"_blank\">https://reader.nxnow.online</a>(%E 6%AD%A 4%E 5%9 F%9 F%E 5%90%8 D 7%E 6%9 C%88%E 8%BF%87%E 6%9 C%9 F)%E 6%9 B%B 4%E 6%8 D%A 2%E 4%B 8%BA)&nbsp;<a href=\"https://read.nxnow.top/\" rel=\"noopener noreferrer\" target=\"_blank\">https://read.nxnow.top</a></p><p>Demo 测试账号 / 密码分别为&nbsp;guest&nbsp;/&nbsp;guest123, 也可自行创建账号添加书源</p><p>直接丢几个图片：&nbsp;<img src=\"https://pic.ccino.win/OB/2024/10/1730251620202410300926460.png\">&nbsp;<img src=\"https://pic.ccino.win/OB/2024/10/1730251615202410300926493.png\"></p><p><img src=\"https://pic.ccino.win/OB/2024/10/1730251641202410300927188.png\"></p><p><img src=\"https://pic.ccino.win/OB/2024/10/1730251654202410300927728.png\"></p><p><img src=\"https://pic.ccino.win/OB/2024/10/1730251670202410300927227.png\"></p><h2>3. 搭建环境</h2><ul><li>服务器：腾讯香港轻量应用服务器 24 元 / 月 VPS 一台展示用的服务器是&nbsp;<a href=\"https://netcup-sonderangebote.de/\" rel=\"noopener noreferrer\" target=\"_blank\">Netcup</a>&nbsp;特价款，本期搭建用的是&nbsp;<a href=\"https://loll.cc/vultr\" rel=\"noopener noreferrer\" target=\"_blank\">Vultr</a>&nbsp;的服务器，按小时计费，可随时销毁（最好是选<strong>非大陆的服务器</strong>）（<a href=\"https://loll.cc/tx\" rel=\"noopener noreferrer\" target=\"_blank\">腾讯轻量购买链接</a>）<a href=\"https://loll.cc/hz\" rel=\"noopener noreferrer\" target=\"_blank\">Hetzner 注册免费得 25 欧试用金有效期一个月</a></li><li>系统：Debian 10（<a href=\"https://blog.laoda.de/archives/useful-script#dd%E7%9B%B8%E5%85%B3\" rel=\"noopener noreferrer\" target=\"_blank\">DD 脚本</a>&nbsp;非必需 DD 用原来的系统也 OK）</li><li>域名一枚，并做好解析到服务器上（<a href=\"https://blog.laoda.de/archives/namesilo\" rel=\"noopener noreferrer\" target=\"_blank\">域名购买、域名解析</a>&nbsp;<a href=\"https://www.bilibili.com/video/BV1Sy4y1k7kZ/\" rel=\"noopener noreferrer\" target=\"_blank\">视频教程</a>）</li><li>安装好 Docker、Docker-compose（<a href=\"https://blog.laoda.de/archives/hello-docker#5%E5%AE%89%E8%A3%85dockerdocker-compose\" rel=\"noopener noreferrer\" target=\"_blank\">相关脚本</a>）</li><li>【非必需】提前安装好宝塔面板海外版本 aapanel，并安装好 Nginx（<a href=\"https://forum.aapanel.com/d/9-aapanel-linux-panel-6812-installation-tutorial\" rel=\"noopener noreferrer\" target=\"_blank\">安装地址</a>）</li><li>【非必需本教程采用】安装好 Nginx Proxy Manager（<a href=\"https://blog.laoda.de/archives/nginxproxymanager\" rel=\"noopener noreferrer\" target=\"_blank\">相关教程</a>）</li></ul><h2>4. 搭建视频</h2><p><br></p><p>YouTube：<a href=\"https://youtu.be/Yjtrl4zii5g\" rel=\"noopener noreferrer\" target=\"_blank\">https://youtu.be/Yjtrl4zii5g</a></p><p>哔哩哔哩：<a href=\"https://www.bilibili.com/BV16F411G757\" rel=\"noopener noreferrer\" target=\"_blank\">https://www.bilibili.com/BV16F411G757</a></p><h2>5. 搭建方式</h2><h3>5.1 搭建</h3><p><br></p><p>服务器初始设置，参考</p><p><a href=\"https://blog.laoda.de/archives/vps-script\" rel=\"noopener noreferrer\" target=\"_blank\"><strong>新买了一台服务器 “必须” 要做的 6 件小事</strong></a></p><p><a href=\"https://blog.laoda.de/archives/hello-docker\" rel=\"noopener noreferrer\" target=\"_blank\">【Docker 系列】不用宝塔面板，小白一样可以玩转 VPS 服务器！</a></p><pre class=\"ql-syntax\" spellcheck=\"false\">sudo -i # 切换到root用户\n\napt update -y  # 升级packages\n\napt install wget curl sudo vim git  # Debian系统比较干净，安装常用的软件\n</pre><p><br></p><p>创建一下安装的目录：</p><pre class=\"ql-syntax\" spellcheck=\"false\">mkdir -p /root/data/docker_data/reader\n\ncd /root/data/docker_data/reader\n\nnano docker-compose.yml\n</pre><p><br></p><p>docker-compose.yml&nbsp;来源<a href=\"https://github.com/hectorqin/reader/blob/master/docker-compose.yaml\" rel=\"noopener noreferrer\" target=\"_blank\">官方仓库</a>，内容如下：</p><h4><strong>自用版本</strong></h4><pre class=\"ql-syntax\" spellcheck=\"false\">version: '3.1'\nservices:\n\n# reader 在线阅读\n# 本人在线演示(服务器位于日本) : https://reader.nxnow.online(此域名7月过期)更换为 https://read.nxnow.top\n# 在线演示测试账号/密码分别为guest/guest123,也可自行创建账号添加书源\n# 书源参考 : https://github.com/NZESupB/ReadSource,书源合集下选一个合集进入网页后, Ctrl+S 保存至本地后导入网站即可,或者点击远程书源,将链接导入即可\n\n#docker run -d --restart=always --name=reader -e \"SPRING_PROFILES_ACTIVE=prod\" -v $(pwd)/logs:/logs -v $(pwd)/storage:/storage -p 8080:8080 hectorqin/reader\n# 多用户版\n#docker run -d --restart=always --name=reader -v $(pwd)/logs:/logs -v $(pwd)/storage:/storage -p 8080:8080 hectorqin/reader java -jar /app/bin/reader.jar --spring.profiles.active=prod --reader.app.secure=true --reader.app.secureKey=管理密码 --reader.app.inviteCode=注册邀请码\n# 多用户版 使用环境变量\n#docker run -d --restart=always --name=reader -e \"SPRING_PROFILES_ACTIVE=prod\" -e \"READER_APP_SECURE=true\" -e \"READER_APP_SECUREKEY=管理密码\" -e \"READER_APP_INVITECODE=注册邀请码\" -v $(pwd)/logs:/logs -v $(pwd)/storage:/storage -p 8080:8080 hectorqin/reader\n\n# 自用版\n#  read_own:\n#    image: hectorqin/reader\n#    container_name: reader_own\n#    restart: always\n#    ports:\n#      - 4395:8080\n#    volumes:\n#      - /home/reader/logs:/logs\n#      - /home/reader/storage:/storage\n#    environment:\n#      - SPRING_PROFILES_ACTIVE=prod\n#      - READER_APP_CACHECHAPTERCONTENT=true #是否开启缓存章节内容 V2.0\n# 多用户版\n read_all:\n image: hectorqin/reader\n container_name: reader #容器名 可自行修改\n restart: always\n ports:\n - 4396:8080 #4396端口映射可自行修改\n volumes:\n - /root/data/docker_data/reader/logs:/logs #log映射目录 /root/data/docker_data/reader/logs 映射目录可自行修改\n - /root/data/docker_data/reader/storage:/storage #数据映射目录 /root/data/docker_data/reader/storage 映射目录可自行修改\n environment:\n - SPRING_PROFILES_ACTIVE=prod\n - READER_APP_SECURE=true #开启登录鉴权，开启后将支持多用户模式\n - READER_APP_CACHECHAPTERCONTENT=true #是否开启缓存章节内容 V2.0\n - READER_APP_SECUREKEY=adminpwd  #管理员密码  可自行修改\n      #- READER_APP_INVITECODE=registercode #注册邀请码,如需要取消注释 可自行修改\n# 自动更新docker\n watchtower:\n image: containrrr/watchtower\n container_name: watchtower\n restart: always\n    # 环境变量,设置为上海时区\n environment:\n - TZ=Asia/Shanghai\n volumes:\n - /var/run/docker.sock:/var/run/docker.sock\n command: reader watchtower --cleanup --schedule \"0 0 4 * * *\"\n    # 仅更新reader与watchtower容器,如需其他自行添加 '容器名' ,如:reader watchtower redis\n    # --cleanup 更新后清理旧版本镜像\n    # --schedule 自动检测更新 crontab定时(限定6位crontab) 此处代表凌晨4点整\n</pre><p><br></p><h4>视频演示版本</h4><pre class=\"ql-syntax\" spellcheck=\"false\">version: '3.1'\nservices:\n\n# reader 在线阅读\n# 第三方在线演示(服务器位于日本)：https://reader.nxnow.top\n# 在线演示测试账号/密码分别为guest/guest123，也可自行创建账号添加书源，不定期删除长期未登录账号\n# 书源参考 : https://github.com/NZESupB/ReadSource，书源合集下选一个合集进入网页后，Ctrl+S 保存至本地后导入网站即可，或者点击远程书源，将链接导入即可\n\n# 单用户版\n#docker run -d --restart=always --name=reader -e \"SPRING_PROFILES_ACTIVE=prod\" -v $(pwd)/logs:/logs -v $(pwd)/storage:/storage -p 8080:8080 hectorqin/reader\n# 多用户版\n#docker run -d --restart=always --name=reader -e \"SPRING_PROFILES_ACTIVE=prod\" -e \"READER_APP_SECURE=true\" -e \"READER_APP_SECUREKEY=管理密码\" -e \"READER_APP_INVITECODE=注册邀请码\" -v $(pwd)/logs:/logs -v $(pwd)/storage:/storage -p 8080:8080 hectorqin/reader\n\n reader:\n image: hectorqin/reader\n    #image: hectorqin/reader:openj9-latest #docker镜像，arm64架构和小内存机器可以使用\n container_name: reader #容器名 可自行修改\n restart: always\n    #限制镜像最大资源占用(可选) 低内存(&lt;=2G)可考虑限制 使用此方式启动需加--compatibility,例如   docker-compose --compatibility up -d\n    #deploy:\n      #resources:\n        #limits:  #最大限制资源\n          #cpus: '0.50' #cpu建议不做限制,保持注释即可,1表示100%\n          #memory: 1024M\n        #reservations:  #保留最小资源\n          #cpus: 0.25\n          #memory: 256M\n ports:\n - 4396:8080 #4396端口映射可自行修改\n volumes:\n - /root/data/docker_data/reader/logs:/logs #log映射目录 /home/reader/logs 映射目录可自行修改\n - /root/data/docker_data/reader/storage:/storage #数据映射目录 /home/reader/storage 映射目录可自行修改\n      #可配置默认书源,默认书源存放于数据映射目录中:storage/data/default/bookSource.json\n      #书源参考(大部分可通用):https://www.legado.top/blog/book-source\n      #wget -c https://namofree.gitee.io/yuedu3/legado3_booksource_by_Namo.json -O /home/reader/storage/data/default/bookSource.json\n environment:\n - SPRING_PROFILES_ACTIVE=prod\n - READER_APP_USERLIMIT=50 #用户上限,默认50\n - READER_APP_USERBOOKLIMIT=200 #用户书籍上限,默认200\n - READER_APP_CACHECHAPTERCONTENT=true #开启缓存章节内容 V2.0\n      # 下面都是多用户模式配置\n - READER_APP_SECURE=true #开启登录鉴权，开启后将支持多用户模式\n - READER_APP_SECUREKEY=adminpwd  #管理员密码  建议修改\n - READER_APP_INVITECODE=registercode #注册邀请码 建议修改,如不需要可注释或删除\n# 自动更新docker\n watchtower:\n image: containrrr/watchtower\n container_name: watchtower\n restart: always\n    # 环境变量,设置为上海时区\n environment:\n - TZ=Asia/Shanghai\n volumes:\n - /var/run/docker.sock:/var/run/docker.sock\n command: reader watchtower --cleanup --schedule \"0 0 4 * * *\"\n    # 仅更新reader与watchtower容器,如需其他自行添加 '容器名' ,如:reader watchtower nginx\n    # --cleanup 更新后清理旧版本镜像\n    # --schedule 自动检测更新 crontab定时(限定6位crontab) 此处代表凌晨4点整\n</pre><p><br></p><p>注意：VPS 的内存最好大于 1 G，可以再设置 1 G 的 SWAP，本项目内容存储在内存中～</p><p>设置 SWAP 可以用脚本:</p><pre class=\"ql-syntax\" spellcheck=\"false\">wget -O box.sh https://raw.githubusercontent.com/BlueSkyXN/SKY-BOX/main/box.sh &amp;&amp; chmod +x box.sh &amp;&amp; clear &amp;&amp; ./box.sh\n</pre><p><br></p><p><br></p><p>没问题的话，ctrl+x&nbsp;退出，按&nbsp;y&nbsp;保存，enter&nbsp;确认。</p><p>查看端口是否被占用，输入：</p><pre class=\"ql-syntax\" spellcheck=\"false\">lsof -i:4396  #查看4396端口是否被占用，如果被占用，重新自定义一个端口\n</pre><p><br></p><p><br></p><p>如果出现：</p><pre class=\"ql-syntax\" spellcheck=\"false\">-bash: lsof: command not found\n</pre><p><br></p><p><br></p><p>运行：</p><pre class=\"ql-syntax\" spellcheck=\"false\">apt install lsof  #安装lsof\n</pre><p><br></p><p><br></p><p>如果端口没有被占用，可以运行：</p><pre class=\"ql-syntax\" spellcheck=\"false\">docker-compose up -d\n</pre><p><br></p><p><br></p><p>访问：http:服务ip:4396&nbsp;即可。</p><p><strong>注意：</strong></p><p>1、不知道服务器 IP，可以直接在命令行输入：curl ip.sb，会显示当前服务器的 IP。</p><p>2、遇到访问不了的情况，请在宝塔面板的防火墙和服务商的后台防火墙里打开对应端口。</p><h3>5.2 更新</h3><pre class=\"ql-syntax\" spellcheck=\"false\">cd /root/data/docker_data/reader  # 进入docker-compose所在的文件夹\ndocker-compose pull    # 拉取最新的镜像\ndocker-compose up -d   # 重新更新当前镜像\n</pre><p><br></p><p>利用 Docker-compose 搭建的应用，更新非常容易～</p><h3>5.3 卸载</h3><pre class=\"ql-syntax\" spellcheck=\"false\">sudo -i  # 切换到root\ncd /root/data/docker_data/reader  # 进入docker-compose所在的文件夹\ndocker-compose down    # 停止容器，此时不会删除映射到本地的数据\ncd ~\nrm -rf /root/data/docker_data/reader  # 完全删除映射到本地的数据\n</pre><p><br></p><p>利用 Docker-compose 搭建的应用，删除也非常容易～</p><h2>6. 反向代理（必须）</h2><p><br></p><p><strong>此项目和别的项目不同，必须采用 https 形式，否则浏览器无法加密，无法使用。</strong></p><h3>6.1 利用 Nginx Proxy Manager</h3><p><br></p><p>在添加反向代理之前，确保你已经完成了域名解析，不会的可以看这个：<strong>域名一枚，并做好解析到服务器上</strong>（<a href=\"https://blog.laoda.de/archives/namesilo\" rel=\"noopener noreferrer\" target=\"_blank\">域名购买、域名解析</a>&nbsp;<a href=\"https://www.bilibili.com/video/BV1Sy4y1k7kZ/\" rel=\"noopener noreferrer\" target=\"_blank\">视频教程</a>）</p><p>之后，登陆 Nginx Proxy Manager（不会的看这个：<strong>安装 Nginx Proxy Manager</strong>（<a href=\"https://blog.laoda.de/archives/nginxproxymanager\" rel=\"noopener noreferrer\" target=\"_blank\">相关教程</a>））</p><p><strong>注意：</strong></p><p>Nginx Proxy Manager（以下简称 NPM）会用到&nbsp;80、443&nbsp;端口，所以本机不能占用（比如原来就有 Nginx）</p><p>直接丢几张图：&nbsp;<img src=\"https://pic.ccino.win/OB/2024/10/1730251706202410300928342.png\"></p><p><img src=\"https://pic.ccino.win/OB/2024/10/1730251718202410300928510.png\">&nbsp;注意填写对应的&nbsp;域名、IP&nbsp;和&nbsp;端口，按文章来的话，应该是&nbsp;4396</p><p><strong>IP 填写：</strong></p><p>如果 Nginx Proxy Manager 和 reader 在同一台服务器上，可以在终端输入：</p><pre class=\"ql-syntax\" spellcheck=\"false\">ip addr show docker0\n</pre><p><br></p><p><br></p><p>查看对应的 Docker 容器内部 IP。</p><p>否则直接填&nbsp;cryptgeon所在的服务器IP&nbsp;就行。&nbsp;<img src=\"https://pic.ccino.win/OB/2024/10/1730251738202410300928495.png\">&nbsp;再次打开，勾选这些：&nbsp;<img src=\"https://pic.ccino.win/OB/2024/10/1730251754202410300929869.png\"></p><p>然后就可以用域名来安装访问了。</p><h2>7. 使用教程</h2><p><br></p><p>看咕咕的视频，或者自己研究，看<a href=\"https://github.com/hectorqin/reader/blob/master/doc.md\" rel=\"noopener noreferrer\" target=\"_blank\">文档</a></p><h3>7.1 书源地址（请支持正版书籍）</h3><p><br></p><p><a href=\"https://github.com/XIU2/Yuedu\" rel=\"noopener noreferrer\" target=\"_blank\">https://github.com/XIU2/Yuedu</a></p><p>数量不多，因为我觉得质量比数量更重要（当然我也不保证质量就一定多么好哈），我以前也用过那些几百上千个的书源列表，杂七杂八太乱了，质量参差不齐，而且正常情况下大家也就主用几个书源罢了。。。</p><p>https://blog.3cat.cc/142.html</p><p>本书源集成了 1000 + 个源，基本涵盖全网！</p><p>为了长久使用，请勿滥用！</p><p>书源：</p><pre class=\"ql-syntax\" spellcheck=\"false\">https://shuyuan.mgz6.cc/shuyuan/84083d2be227881b0e3b90b9cd01c685.json\n</pre><p><br></p><p>json&nbsp;的就是可以直接导入的</p><p>网址：</p><p>http://yck.mumuceo.com/yuedu/shuyuan/index.html</p><p>里面有很多书源</p><h2>8. 结尾</h2><p><br></p><p>祝大家用得开心，有问题可以去 GitHub 提&nbsp;<a href=\"https://github.com/hectorqin/reader/issues\" rel=\"noopener noreferrer\" target=\"_blank\">Issues</a>，也可以在评论区互相交流探讨。</p><p>同时，有能力给项目做贡献的同学，也欢迎积极<a href=\"https://github.com/hectorqin/reader/issues\" rel=\"noopener noreferrer\" target=\"_blank\">加入到项目</a>中来，贡献自己的一份力量！</p><h2>9. 参考资料</h2><p><br></p><p>GitHub 原项目地址：<a href=\"https://github.com/hectorqin/reader\" rel=\"noopener noreferrer\" target=\"_blank\">https://github.com/hectorqin/reader</a>（617 star）</p><p><a href=\"https://hostloc.com/thread-1010700-1-3.html\" rel=\"noopener noreferrer\" target=\"_blank\">https://hostloc.com/thread-1010700-1-3.html</a></p><p><a href=\"https://github.com/XIU2/Yuedu\" rel=\"noopener noreferrer\" target=\"_blank\">https://github.com/XIU2/Yuedu</a></p><p><a href=\"https://blog.3cat.cc/142.html\" rel=\"noopener noreferrer\" target=\"_blank\">https://blog.3cat.cc/142.html</a></p><p><br></p>","content_text":" 介绍\n\n\n\n\n安卓手机 APP 阅读3 服务器版，桌面端。\n\n后端 Kotlin + Spring Boot + Vert.x + Coroutine ；\n\n前端 Vue.js + Element。\n\nGitHub 开源项目，支持 Docker 搭建。\n\n\n1.1 特点\n\n\n\n\n咕咕这边简单在网上也抄~~~~ 搜集了一些特点，供大家参考（翻译自 GitHub 的 README）：\n\n * GitHub 完全开源，可以免费使用\n * Docker 搭建，10 分钟搞定\n * 书架管理\n * 搜索\n * 书海\n * 看书\n * 移动端适配\n * 换源\n * 翻页方式\n * 手势支持\n * 自定义主题\n * 自定义样式\n * WebDAV 同步\n * 文字替换过滤\n * 听书 <仅部分浏览器支持，手机端会因为锁屏而失效>\n * 用户配置备份恢复\n * 支持漫画\n * 支持音频\n * 书源失效检测\n * 导入本地 TXT、EPUB、UMD 格式的书籍\n * 书籍分组\n * RSS 订阅\n * 定时更新书架\n * 并发搜书\n * 本地书仓\n\n\n1.2 问题\n\n * 部分使用了 Javascript 的书源可能会报错，不支持 cookie 和 cache 功能\n * 不支持使用了 webview 的书源\n * 不支持书源登录功能\n\n\n1.3 技术栈\n\n\n\n\n后端 Kotlin + Spring Boot + Vert.x + Coroutine ；\n\n前端 Vue.js + Element\n\n\n1.4 免责声明（DISCLAIMER）\n\n\n\n\n阅读 是一款提供网络文学搜索的工具，为广大网络文学爱好者提供一种方便、快捷舒适的试读体验。\n\n当您搜索一本书的时，阅读会将该书的书名以关键词的形式提交到各个第三方网络文学网站。各第三方网站返回的内容与阅读无关，阅读对其概不负责，亦不承担任何法律责任。任何通过使用阅读而链接到的第三方网页均系他人制作或提供，您可能从第三方网页上获得其他服务，阅读对其合法性概不负责，亦不承担任何法律责任。第三方搜索引擎结果根据您提交的书名自动搜索获得并提供试读，不代表阅读赞成或被搜索链接到的第三方网页上的内容或立场。您应该对使用搜索引擎的结果自行承担风险。\n\n阅读不做任何形式的保证：不保证第三方搜索引擎的搜索结果满足您的要求，不保证搜索服务不中断，不保证搜索结果的安全性、正确性、及时性、合法性。因网络状况、通讯线路、第三方网站等任何原因而导致您不能正常使用阅读，阅读不承担任何法律责任。阅读尊重并保护所有使用阅读用户的个人隐私权，您注册的用户名、电子邮件地址等个人资料，非经您亲自许可或根据相关法律、法规的强制性规定，阅读不会主动地泄露给第三方。\n\n阅读致力于最大程度地减少网络文学阅读者在自行搜寻过程中的无意义的时间浪费，通过专业搜索展示不同网站中网络文学的最新章节。阅读在为广大小说爱好者提供方便、快捷舒适的试读体验的同时，也使优秀网络文学得以迅速、更广泛的传播，从而达到了在一定程度促进网络文学充分繁荣发展之目的。阅读鼓励广大小说爱好者通过阅读发现优秀网络小说及其提供商，并建议阅读正版图书。任何单位或个人认为通过阅读搜索链接到的第三方网页内容可能涉嫌侵犯其信息网络传播权，应该及时向阅读提出书面权力通知，并提供身份证明、权属证明及详细侵权情况证明。阅读在收到上述法律文件后，将会依法尽快断开相关链接内容。\n\n\n2. 项目展示\n\n\n\n\nGitHub 原项目地址：https://github.com/hectorqin/reader（617 1.9 k star）\n\nDemo 地址 (服务器位于日本) : [https://reader.nxnow.online(此域名 7 月过期)\n更换为](https://reader.nxnow.online(%E 6%AD%A 4%E 5%9 F%9 F%E 5%90%8 D 7%E 6%9\nC%88%E 8%BF%87%E 6%9 C%9 F)%E 6%9 B%B 4%E 6%8 D%A 2%E 4%B\n8%BA) https://read.nxnow.top\n\nDemo 测试账号 / 密码分别为 guest / guest123, 也可自行创建账号添加书源\n\n直接丢几个图片： [https://pic.ccino.win/OB/2024/10/1730251620202410300926460.png] [https://pic.ccino.win/OB/2024/10/1730251615202410300926493.png]\n\n[https://pic.ccino.win/OB/2024/10/1730251641202410300927188.png]\n\n[https://pic.ccino.win/OB/2024/10/1730251654202410300927728.png]\n\n[https://pic.ccino.win/OB/2024/10/1730251670202410300927227.png]\n\n\n3. 搭建环境\n\n * 服务器：腾讯香港轻量应用服务器 24 元 / 月 VPS\n   一台展示用的服务器是 Netcup 特价款，本期搭建用的是 Vultr 的服务器，按小时计费，可随时销毁（最好是选非大陆的服务器）（腾讯轻量购买链接）Hetzner\n   注册免费得 25 欧试用金有效期一个月\n * 系统：Debian 10（DD 脚本 非必需 DD 用原来的系统也 OK）\n * 域名一枚，并做好解析到服务器上（域名购买、域名解析 视频教程）\n * 安装好 Docker、Docker-compose（相关脚本）\n * 【非必需】提前安装好宝塔面板海外版本 aapanel，并安装好 Nginx（安装地址）\n * 【非必需本教程采用】安装好 Nginx Proxy Manager（相关教程）\n\n\n4. 搭建视频\n\n\n\n\nYouTube：https://youtu.be/Yjtrl4zii5g\n\n哔哩哔哩：https://www.bilibili.com/BV16F411G757\n\n\n5. 搭建方式\n\n\n5.1 搭建\n\n\n\n\n服务器初始设置，参考\n\n新买了一台服务器 “必须” 要做的 6 件小事\n\n【Docker 系列】不用宝塔面板，小白一样可以玩转 VPS 服务器！\n\nsudo -i # 切换到root用户\n\napt update -y  # 升级packages\n\napt install wget curl sudo vim git  # Debian系统比较干净，安装常用的软件\n\n\n\n\n\n创建一下安装的目录：\n\nmkdir -p /root/data/docker_data/reader\n\ncd /root/data/docker_data/reader\n\nnano docker-compose.yml\n\n\n\n\n\ndocker-compose.yml 来源官方仓库，内容如下：\n\n自用版本\n\nversion: '3.1'\nservices:\n\n# reader 在线阅读\n# 本人在线演示(服务器位于日本) : https://reader.nxnow.online(此域名7月过期)更换为 https://read.nxnow.top\n# 在线演示测试账号/密码分别为guest/guest123,也可自行创建账号添加书源\n# 书源参考 : https://github.com/NZESupB/ReadSource,书源合集下选一个合集进入网页后, Ctrl+S 保存至本地后导入网站即可,或者点击远程书源,将链接导入即可\n\n#docker run -d --restart=always --name=reader -e \"SPRING_PROFILES_ACTIVE=prod\" -v $(pwd)/logs:/logs -v $(pwd)/storage:/storage -p 8080:8080 hectorqin/reader\n# 多用户版\n#docker run -d --restart=always --name=reader -v $(pwd)/logs:/logs -v $(pwd)/storage:/storage -p 8080:8080 hectorqin/reader java -jar /app/bin/reader.jar --spring.profiles.active=prod --reader.app.secure=true --reader.app.secureKey=管理密码 --reader.app.inviteCode=注册邀请码\n# 多用户版 使用环境变量\n#docker run -d --restart=always --name=reader -e \"SPRING_PROFILES_ACTIVE=prod\" -e \"READER_APP_SECURE=true\" -e \"READER_APP_SECUREKEY=管理密码\" -e \"READER_APP_INVITECODE=注册邀请码\" -v $(pwd)/logs:/logs -v $(pwd)/storage:/storage -p 8080:8080 hectorqin/reader\n\n# 自用版\n#  read_own:\n#    image: hectorqin/reader\n#    container_name: reader_own\n#    restart: always\n#    ports:\n#      - 4395:8080\n#    volumes:\n#      - /home/reader/logs:/logs\n#      - /home/reader/storage:/storage\n#    environment:\n#      - SPRING_PROFILES_ACTIVE=prod\n#      - READER_APP_CACHECHAPTERCONTENT=true #是否开启缓存章节内容 V2.0\n# 多用户版\n read_all:\n image: hectorqin/reader\n container_name: reader #容器名 可自行修改\n restart: always\n ports:\n - 4396:8080 #4396端口映射可自行修改\n volumes:\n - /root/data/docker_data/reader/logs:/logs #log映射目录 /root/data/docker_data/reader/logs 映射目录可自行修改\n - /root/data/docker_data/reader/storage:/storage #数据映射目录 /root/data/docker_data/reader/storage 映射目录可自行修改\n environment:\n - SPRING_PROFILES_ACTIVE=prod\n - READER_APP_SECURE=true #开启登录鉴权，开启后将支持多用户模式\n - READER_APP_CACHECHAPTERCONTENT=true #是否开启缓存章节内容 V2.0\n - READER_APP_SECUREKEY=adminpwd  #管理员密码  可自行修改\n      #- READER_APP_INVITECODE=registercode #注册邀请码,如需要取消注释 可自行修改\n# 自动更新docker\n watchtower:\n image: containrrr/watchtower\n container_name: watchtower\n restart: always\n    # 环境变量,设置为上海时区\n environment:\n - TZ=Asia/Shanghai\n volumes:\n - /var/run/docker.sock:/var/run/docker.sock\n command: reader watchtower --cleanup --schedule \"0 0 4 * * *\"\n    # 仅更新reader与watchtower容器,如需其他自行添加 '容器名' ,如:reader watchtower redis\n    # --cleanup 更新后清理旧版本镜像\n    # --schedule 自动检测更新 crontab定时(限定6位crontab) 此处代表凌晨4点整\n\n\n\n\n\n视频演示版本\n\nversion: '3.1'\nservices:\n\n# reader 在线阅读\n# 第三方在线演示(服务器位于日本)：https://reader.nxnow.top\n# 在线演示测试账号/密码分别为guest/guest123，也可自行创建账号添加书源，不定期删除长期未登录账号\n# 书源参考 : https://github.com/NZESupB/ReadSource，书源合集下选一个合集进入网页后，Ctrl+S 保存至本地后导入网站即可，或者点击远程书源，将链接导入即可\n\n# 单用户版\n#docker run -d --restart=always --name=reader -e \"SPRING_PROFILES_ACTIVE=prod\" -v $(pwd)/logs:/logs -v $(pwd)/storage:/storage -p 8080:8080 hectorqin/reader\n# 多用户版\n#docker run -d --restart=always --name=reader -e \"SPRING_PROFILES_ACTIVE=prod\" -e \"READER_APP_SECURE=true\" -e \"READER_APP_SECUREKEY=管理密码\" -e \"READER_APP_INVITECODE=注册邀请码\" -v $(pwd)/logs:/logs -v $(pwd)/storage:/storage -p 8080:8080 hectorqin/reader\n\n reader:\n image: hectorqin/reader\n    #image: hectorqin/reader:openj9-latest #docker镜像，arm64架构和小内存机器可以使用\n container_name: reader #容器名 可自行修改\n restart: always\n    #限制镜像最大资源占用(可选) 低内存(<=2G)可考虑限制 使用此方式启动需加--compatibility,例如   docker-compose --compatibility up -d\n    #deploy:\n      #resources:\n        #limits:  #最大限制资源\n          #cpus: '0.50' #cpu建议不做限制,保持注释即可,1表示100%\n          #memory: 1024M\n        #reservations:  #保留最小资源\n          #cpus: 0.25\n          #memory: 256M\n ports:\n - 4396:8080 #4396端口映射可自行修改\n volumes:\n - /root/data/docker_data/reader/logs:/logs #log映射目录 /home/reader/logs 映射目录可自行修改\n - /root/data/docker_data/reader/storage:/storage #数据映射目录 /home/reader/storage 映射目录可自行修改\n      #可配置默认书源,默认书源存放于数据映射目录中:storage/data/default/bookSource.json\n      #书源参考(大部分可通用):https://www.legado.top/blog/book-source\n      #wget -c https://namofree.gitee.io/yuedu3/legado3_booksource_by_Namo.json -O /home/reader/storage/data/default/bookSource.json\n environment:\n - SPRING_PROFILES_ACTIVE=prod\n - READER_APP_USERLIMIT=50 #用户上限,默认50\n - READER_APP_USERBOOKLIMIT=200 #用户书籍上限,默认200\n - READER_APP_CACHECHAPTERCONTENT=true #开启缓存章节内容 V2.0\n      # 下面都是多用户模式配置\n - READER_APP_SECURE=true #开启登录鉴权，开启后将支持多用户模式\n - READER_APP_SECUREKEY=adminpwd  #管理员密码  建议修改\n - READER_APP_INVITECODE=registercode #注册邀请码 建议修改,如不需要可注释或删除\n# 自动更新docker\n watchtower:\n image: containrrr/watchtower\n container_name: watchtower\n restart: always\n    # 环境变量,设置为上海时区\n environment:\n - TZ=Asia/Shanghai\n volumes:\n - /var/run/docker.sock:/var/run/docker.sock\n command: reader watchtower --cleanup --schedule \"0 0 4 * * *\"\n    # 仅更新reader与watchtower容器,如需其他自行添加 '容器名' ,如:reader watchtower nginx\n    # --cleanup 更新后清理旧版本镜像\n    # --schedule 自动检测更新 crontab定时(限定6位crontab) 此处代表凌晨4点整\n\n\n\n\n\n注意：VPS 的内存最好大于 1 G，可以再设置 1 G 的 SWAP，本项目内容存储在内存中～\n\n设置 SWAP 可以用脚本:\n\nwget -O box.sh https://raw.githubusercontent.com/BlueSkyXN/SKY-BOX/main/box.sh && chmod +x box.sh && clear && ./box.sh\n\n\n\n\n\n\n\n\n没问题的话，ctrl+x 退出，按 y 保存，enter 确认。\n\n查看端口是否被占用，输入：\n\nlsof -i:4396  #查看4396端口是否被占用，如果被占用，重新自定义一个端口\n\n\n\n\n\n\n\n\n如果出现：\n\n-bash: lsof: command not found\n\n\n\n\n\n\n\n\n运行：\n\napt install lsof  #安装lsof\n\n\n\n\n\n\n\n\n如果端口没有被占用，可以运行：\n\ndocker-compose up -d\n\n\n\n\n\n\n\n\n访问：http:服务ip:4396 即可。\n\n注意：\n\n1、不知道服务器 IP，可以直接在命令行输入：curl ip.sb，会显示当前服务器的 IP。\n\n2、遇到访问不了的情况，请在宝塔面板的防火墙和服务商的后台防火墙里打开对应端口。\n\n\n5.2 更新\n\ncd /root/data/docker_data/reader  # 进入docker-compose所在的文件夹\ndocker-compose pull    # 拉取最新的镜像\ndocker-compose up -d   # 重新更新当前镜像\n\n\n\n\n\n利用 Docker-compose 搭建的应用，更新非常容易～\n\n\n5.3 卸载\n\nsudo -i  # 切换到root\ncd /root/data/docker_data/reader  # 进入docker-compose所在的文件夹\ndocker-compose down    # 停止容器，此时不会删除映射到本地的数据\ncd ~\nrm -rf /root/data/docker_data/reader  # 完全删除映射到本地的数据\n\n\n\n\n\n利用 Docker-compose 搭建的应用，删除也非常容易～\n\n\n6. 反向代理（必须）\n\n\n\n\n此项目和别的项目不同，必须采用 https 形式，否则浏览器无法加密，无法使用。\n\n\n6.1 利用 NGINX PROXY MANAGER\n\n\n\n\n在添加反向代理之前，确保你已经完成了域名解析，不会的可以看这个：域名一枚，并做好解析到服务器上（域名购买、域名解析 视频教程）\n\n之后，登陆 Nginx Proxy Manager（不会的看这个：安装 Nginx Proxy Manager（相关教程））\n\n注意：\n\nNginx Proxy Manager（以下简称 NPM）会用到 80、443 端口，所以本机不能占用（比如原来就有 Nginx）\n\n直接丢几张图： [https://pic.ccino.win/OB/2024/10/1730251706202410300928342.png]\n\n[https://pic.ccino.win/OB/2024/10/1730251718202410300928510.png] 注意填写对应的 域名、IP 和 端口，按文章来的话，应该是 4396\n\nIP 填写：\n\n如果 Nginx Proxy Manager 和 reader 在同一台服务器上，可以在终端输入：\n\nip addr show docker0\n\n\n\n\n\n\n\n\n查看对应的 Docker 容器内部 IP。\n\n否则直接填 cryptgeon所在的服务器IP 就行。 [https://pic.ccino.win/OB/2024/10/1730251738202410300928495.png] 再次打开，勾选这些： [https://pic.ccino.win/OB/2024/10/1730251754202410300929869.png]\n\n然后就可以用域名来安装访问了。\n\n\n7. 使用教程\n\n\n\n\n看咕咕的视频，或者自己研究，看文档\n\n\n7.1 书源地址（请支持正版书籍）\n\n\n\n\nhttps://github.com/XIU2/Yuedu\n\n数量不多，因为我觉得质量比数量更重要（当然我也不保证质量就一定多么好哈），我以前也用过那些几百上千个的书源列表，杂七杂八太乱了，质量参差不齐，而且正常情况下大家也就主用几个书源罢了。。。\n\nhttps://blog.3cat.cc/142.html\n\n本书源集成了 1000 + 个源，基本涵盖全网！\n\n为了长久使用，请勿滥用！\n\n书源：\n\nhttps://shuyuan.mgz6.cc/shuyuan/84083d2be227881b0e3b90b9cd01c685.json\n\n\n\n\n\njson 的就是可以直接导入的\n\n网址：\n\nhttp://yck.mumuceo.com/yuedu/shuyuan/index.html\n\n里面有很多书源\n\n\n8. 结尾\n\n\n\n\n祝大家用得开心，有问题可以去 GitHub 提 Issues，也可以在评论区互相交流探讨。\n\n同时，有能力给项目做贡献的同学，也欢迎积极加入到项目中来，贡献自己的一份力量！\n\n\n9. 参考资料\n\n\n\n\nGitHub 原项目地址：https://github.com/hectorqin/reader（617 star）\n\nhttps://hostloc.com/thread-1010700-1-3.html\n\nhttps://github.com/XIU2/Yuedu\n\nhttps://blog.3cat.cc/142.html\n\n\n","image":"https://cdn-blog-ccino-cc.ccino.cc/blog-ccino-cc/production/images/item-4b62373e5a3903c294b912d0d976957f.jpg","date_published":"2024-11-23T08:37:31.888Z","_microfeed":{"is_audio":false,"is_document":false,"is_external_url":true,"is_video":false,"is_image":false,"web_url":"https://blog-ccino-cc.pages.dev/i/10-app-VfgVvHNOzf6/","json_url":"https://blog-ccino-cc.pages.dev/i/VfgVvHNOzf6/json/","rss_url":"https://blog-ccino-cc.pages.dev/i/VfgVvHNOzf6/rss/","guid":"VfgVvHNOzf6","status":"published","itunes:episodeType":"full","date_published_short":"Sat Nov 23 2024","date_published_ms":1732351051888}},{"id":"-eI_fmGPzzo","title":"Obsidian 加密备份 - Google Drive、Dropbox、OneDrive","attachments":[{"url":"https://cdn-blog-ccino-cc.ccino.cc/blog-ccino-cc/production/media/video-c16e8297c72756bc0fd38fedcd045d94.mp4","mime_type":"video/mp4","size_in_byte":637485,"duration_in_seconds":159}],"url":"https://blog.ccino.cc/i/obsidian-backup/","content_html":"<p>在使用 Evernote 几年后，我最终将所有笔记迁移到了 Obsidian，这样我就可以完全控制我的笔记，并且可以使用、移动或<a href=\"https://lopespm.com/machine_learning/2024/06/24/personal-llm.html\" rel=\"noopener noreferrer\" target=\"_blank\">利用</a>这种格式。因此，我的笔记将不再存在于云端和我的私人设备中，因此任何冗余和备份都需要由我通过个人定期备份来保证。</p><p>十多年的笔记都依赖于单一的冗余，这让我有些不安，所以我能想到的选择是：</p><ol><li>订阅<a href=\"https://obsidian.md/sync\" rel=\"noopener noreferrer\" target=\"_blank\">Obsidian 的同步服务，</a><em>每月</em>需支付 4 美元。我的加密笔记将绑定到 Obsidian 的云服务</li><li>拥有一个自定义解决方案，利用 Obsidian 出色的可定制性，压缩和加密我的所有笔记，并使用云服务来托管此存档。我可以灵活地选择我想要的任何云提供商。</li></ol><p>我选择了选项 2，使用 Google Drive 云服务，并在本文中分享您也可以使用的方法。</p><h1><strong>如何操作</strong></h1><p>这个想法很简单：每当 Obsidian 退出时，使用<a href=\"https://obsidian.md/plugins?search=obsidian-shellcommands\" rel=\"noopener noreferrer\" target=\"_blank\">obsidian-shellcommands</a>&nbsp;shell 插件运行自定义脚本。此事件是可配置的，但我发现应用程序退出事件对我的用例具有必要的周期性，因为我经常偶尔打开 Obsidian，在上面写字，然后立即退出应用程序。</p><h2><strong>步骤 1. 加密并备份所有笔记的自定义脚本</strong></h2><p>首先，将以下脚本保存到计算机中的文件夹中（例如，/Users/yourunixname/backups/my_backup_script.sh），并使用您自己的 Obsidian、备份目标文件夹和您自己的存档密码对其进行更新：</p><pre class=\"ql-syntax\" spellcheck=\"false\">/bin/zsh #!/bin/zsh 复制代码\n\nobsidian_notes_folder=\"&lt;your_obsidian_folder&gt;\" ; # 例如，/Users/yourusername/Library/Application Support/obsidian\nobsidian_notes_tar_archive=\"${obsidian_notes_folder}/obsidian_backup.tar.gz\" ；\nbackup_folder=\"&lt;folder_where_the_final_encrypted_backup_will_be_placed&gt;\"; # 例如，/Users/yourusername/Library/CloudStorage/GoogleDrive/MyDrive/backup_folder\n\necho \"开始压缩黑曜石音符...\" ;\n\n# 创建一个包含 obsidian 文件夹内所有内容的 .tar 档案\ntar -czf ${obsidian_notes_tar_archive} ${obsidian_notes_folder}/obsidian_backup\n\n# 将 .tar 档案压缩为加密的 .7z 文件，密码为“PasswordOfYourChoosing”\n# 在此示例中，使用了从 nix 包管理器 (https://nixos.org/) 安装的 7za，但您可以使用来自任何其他信誉良好的来源的 7za\n/Users/你的用户名/.nix-profile/bin/7za a -tzip -mem=AES256 -mx=0 -mmt=12 -pPasswordOfYourChoosing ${obsidian_notes_folder}/obsidian_backup.7z ${obsidian_notes_tar_archive} ；\n\n# 将 .7z 文件移动到备份文件夹（例如您的 Google Drive / Dropbox / OneDrive 文件夹）\nmv ${obsidian_notes_folder}/obsidian_backup.7z ${backup_folder}/obsidian_backup.7z；\n\necho “压缩完成并移至备份文件夹”\n</pre><p><a href=\"https://gist.github.com/lopespm/fcfceebc311d8ba364919cdf4fa61e8d\" rel=\"noopener noreferrer\" target=\"_blank\"><em>（从 GitHub Gists</em></a><em>下载此脚本）</em></p><p>这些注释大部分都是不言自明的，但本质上这就是脚本的作用：</p><ol><li>首先创建一个包含 obsidian 文件夹内所有内容的 .tar 档案</li><li>将 .tar 存档压缩为密码加密.7z文件。记得用你自己的私人密码更新密码</li><li>将文件移动.7z到目标文件夹，该文件夹可能是您选择的云存储同步文件夹使用的文件夹（例如您的 Google Drive/Dropbox/OneDrive 文件夹）。</li></ol><p>由于最终文件名3.始终相同，因此它将被重写，但您的云存储同步可能会跟踪不同版本的变化，这可能会逐渐增加您的配额使用量。如果这是一个问题，只需使用您的云存储界面定期清除它们即可。</p><p>chmod +x &lt;script_file_name&gt;您可以通过授予脚本执行权限 ( ) 并运行 来测试您的脚本./&lt;your_script_name&gt;。您的最终加密存档应出现在最终backup_folder</p><h2><strong>第 2 步：在 Obsidian 退出时运行脚本</strong></h2><p>现在我们已经准备好了脚本，如果能在 Obsidian 发生特定事件（例如 Obsidian 退出时）时自动运行，我们的工作就会轻松很多。使用<a href=\"https://obsidian.md/plugins?search=obsidian-shellcommands\" rel=\"noopener noreferrer\" target=\"_blank\">obsidian-shell 命令</a>可以轻松在 Obsidian 发生特定事件时运行 shell 脚本。设置步骤如下：</p><p><strong>1.</strong>前往 Obsidian -&gt; 设置。然后选择“社区插件”选项。</p><p><img src=\"https://lopespm.com/files/obsidian_backup/p0.png\"></p><p><br></p><p><strong>1.1.</strong>在“社区插件”选项中，单击“浏览”。在那里，搜索“Shell”。您要安装的是<a href=\"https://lopespm.com/notes/2024/09/11/(https://obsidian.md/plugins?search=obsidian-shellcommands)\" rel=\"noopener noreferrer\" target=\"_blank\">Jarkko Linnanvirta 的 Shell 命令</a></p><p><img src=\"https://lopespm.com/files/obsidian_backup/p2.png\"></p><p><img src=\"https://lopespm.com/files/obsidian_backup/p3.png\"></p><p><br></p><p><strong>2.</strong>插件安装完成后，再次转到 Obsidian -&gt; 设置。您应该在左下角的“社区插件”窗格下看到一个名为“Shell 命令”的选项。单击它。</p><p><img src=\"https://lopespm.com/files/obsidian_backup/p4.png\"></p><p><br></p><p><strong>3.1.</strong>在“Shell 命令”插件中，选择“Shell 命令”选项卡，在其中单击“新建 shell 命令”，然后单击创建的行，单击其相应的齿轮图标。这将向您显示一个带有多个选项卡的新模式。</p><p><strong>3.2.</strong>在此命令模式中，选择“环境”选项卡，然后在“默认 shell 命令”中插入类似以下内容：zsh /Users/yourunixname/backups/my_backup_script.sh。这假设您的脚本位于/Users/yourunixname/backups/my_backup_script.sh</p><p><img src=\"https://lopespm.com/files/obsidian_backup/p5.png\"></p><p><br></p><p><strong>3.3.</strong>仍在此命令模式中，选择“事件”选项卡。在那里，您可以选择何时执行脚本。例如，搜索“Obsidian quits”，然后启用它（右侧有一个切换按钮）</p><p><img src=\"https://lopespm.com/files/obsidian_backup/p6.png\"></p><p><img src=\"https://lopespm.com/files/obsidian_backup/p7.png\"></p><p><br></p><p>全部完成！执行这些步骤后，您的备份脚本将在您选择的时间运行，并且将创建加密备份并将其放置在您在上述<a href=\"https://lopespm.com/notes/2024/09/11/obsidian-backup.html#step-1-custom-script-that-encrypts-and-backs-up-all-notes\" rel=\"noopener noreferrer\" target=\"_blank\">步骤 1</a>中选择的位置。 🎉</p><p><br></p><p>本文出自：https://lopespm.com/notes/2024/09/11/obsidian-backup.html</p>","content_text":"在使用 Evernote 几年后，我最终将所有笔记迁移到了\nObsidian，这样我就可以完全控制我的笔记，并且可以使用、移动或利用这种格式。因此，我的笔记将不再存在于云端和我的私人设备中，因此任何冗余和备份都需要由我通过个人定期备份来保证。\n\n十多年的笔记都依赖于单一的冗余，这让我有些不安，所以我能想到的选择是：\n\n 1. 订阅Obsidian 的同步服务，每月需支付 4 美元。我的加密笔记将绑定到 Obsidian 的云服务\n 2. 拥有一个自定义解决方案，利用 Obsidian 出色的可定制性，压缩和加密我的所有笔记，并使用云服务来托管此存档。我可以灵活地选择我想要的任何云提供商。\n\n我选择了选项 2，使用 Google Drive 云服务，并在本文中分享您也可以使用的方法。\n\n\n如何操作\n\n这个想法很简单：每当 Obsidian 退出时，使用obsidian-shellcommands shell\n插件运行自定义脚本。此事件是可配置的，但我发现应用程序退出事件对我的用例具有必要的周期性，因为我经常偶尔打开\nObsidian，在上面写字，然后立即退出应用程序。\n\n\n步骤 1. 加密并备份所有笔记的自定义脚本\n\n首先，将以下脚本保存到计算机中的文件夹中（例如，/Users/yourunixname/backups/my_backup_script.sh），并使用您自己的\nObsidian、备份目标文件夹和您自己的存档密码对其进行更新：\n\n/bin/zsh #!/bin/zsh 复制代码\n\nobsidian_notes_folder=\"<your_obsidian_folder>\" ; # 例如，/Users/yourusername/Library/Application Support/obsidian\nobsidian_notes_tar_archive=\"${obsidian_notes_folder}/obsidian_backup.tar.gz\" ；\nbackup_folder=\"<folder_where_the_final_encrypted_backup_will_be_placed>\"; # 例如，/Users/yourusername/Library/CloudStorage/GoogleDrive/MyDrive/backup_folder\n\necho \"开始压缩黑曜石音符...\" ;\n\n# 创建一个包含 obsidian 文件夹内所有内容的 .tar 档案\ntar -czf ${obsidian_notes_tar_archive} ${obsidian_notes_folder}/obsidian_backup\n\n# 将 .tar 档案压缩为加密的 .7z 文件，密码为“PasswordOfYourChoosing”\n# 在此示例中，使用了从 nix 包管理器 (https://nixos.org/) 安装的 7za，但您可以使用来自任何其他信誉良好的来源的 7za\n/Users/你的用户名/.nix-profile/bin/7za a -tzip -mem=AES256 -mx=0 -mmt=12 -pPasswordOfYourChoosing ${obsidian_notes_folder}/obsidian_backup.7z ${obsidian_notes_tar_archive} ；\n\n# 将 .7z 文件移动到备份文件夹（例如您的 Google Drive / Dropbox / OneDrive 文件夹）\nmv ${obsidian_notes_folder}/obsidian_backup.7z ${backup_folder}/obsidian_backup.7z；\n\necho “压缩完成并移至备份文件夹”\n\n\n（从 GitHub Gists下载此脚本）\n\n这些注释大部分都是不言自明的，但本质上这就是脚本的作用：\n\n 1. 首先创建一个包含 obsidian 文件夹内所有内容的 .tar 档案\n 2. 将 .tar 存档压缩为密码加密.7z文件。记得用你自己的私人密码更新密码\n 3. 将文件移动.7z到目标文件夹，该文件夹可能是您选择的云存储同步文件夹使用的文件夹（例如您的 Google Drive/Dropbox/OneDrive\n    文件夹）。\n\n由于最终文件名3.始终相同，因此它将被重写，但您的云存储同步可能会跟踪不同版本的变化，这可能会逐渐增加您的配额使用量。如果这是一个问题，只需使用您的云存储界面定期清除它们即可。\n\nchmod +x <script_file_name>您可以通过授予脚本执行权限 ( ) 并运行\n来测试您的脚本./<your_script_name>。您的最终加密存档应出现在最终backup_folder\n\n\n第 2 步：在 OBSIDIAN 退出时运行脚本\n\n现在我们已经准备好了脚本，如果能在 Obsidian 发生特定事件（例如 Obsidian\n退出时）时自动运行，我们的工作就会轻松很多。使用obsidian-shell 命令可以轻松在 Obsidian 发生特定事件时运行 shell\n脚本。设置步骤如下：\n\n1.前往 Obsidian -> 设置。然后选择“社区插件”选项。\n\n[https://lopespm.com/files/obsidian_backup/p0.png]\n\n\n\n\n1.1.在“社区插件”选项中，单击“浏览”。在那里，搜索“Shell”。您要安装的是Jarkko Linnanvirta 的 Shell 命令\n\n[https://lopespm.com/files/obsidian_backup/p2.png]\n\n[https://lopespm.com/files/obsidian_backup/p3.png]\n\n\n\n\n2.插件安装完成后，再次转到 Obsidian -> 设置。您应该在左下角的“社区插件”窗格下看到一个名为“Shell 命令”的选项。单击它。\n\n[https://lopespm.com/files/obsidian_backup/p4.png]\n\n\n\n\n3.1.在“Shell 命令”插件中，选择“Shell 命令”选项卡，在其中单击“新建 shell\n命令”，然后单击创建的行，单击其相应的齿轮图标。这将向您显示一个带有多个选项卡的新模式。\n\n3.2.在此命令模式中，选择“环境”选项卡，然后在“默认 shell 命令”中插入类似以下内容：zsh\n/Users/yourunixname/backups/my_backup_script.sh。这假设您的脚本位于/Users/yourunixname/backups/my_backup_script.sh\n\n[https://lopespm.com/files/obsidian_backup/p5.png]\n\n\n\n\n3.3.仍在此命令模式中，选择“事件”选项卡。在那里，您可以选择何时执行脚本。例如，搜索“Obsidian quits”，然后启用它（右侧有一个切换按钮）\n\n[https://lopespm.com/files/obsidian_backup/p6.png]\n\n[https://lopespm.com/files/obsidian_backup/p7.png]\n\n\n\n\n全部完成！执行这些步骤后，您的备份脚本将在您选择的时间运行，并且将创建加密备份并将其放置在您在上述步骤 1中选择的位置。 🎉\n\n\n\n\n本文出自：https://lopespm.com/notes/2024/09/11/obsidian-backup.html","image":"https://cdn-blog-ccino-cc.ccino.cc/blog-ccino-cc/production/images/item-63e8cb4a91d0accac3481cca9439c4e2.png","date_published":"2024-10-28T02:17:12.681Z","_microfeed":{"is_audio":false,"is_document":false,"is_external_url":false,"is_video":true,"is_image":false,"web_url":"https://blog-ccino-cc.pages.dev/i/obsidian-google-drivedropboxonedrive--eI_fmGPzzo/","json_url":"https://blog-ccino-cc.pages.dev/i/-eI_fmGPzzo/json/","rss_url":"https://blog-ccino-cc.pages.dev/i/-eI_fmGPzzo/rss/","guid":"-eI_fmGPzzo","status":"published","duration_hhmmss":"00:02:39","itunes:episodeType":"full","date_published_short":"Sun Oct 27 2024","date_published_ms":1730081832681}},{"id":"6BWpUbnDbvd","title":"Memos：轻量、私有部署的多平台云备忘录","attachments":[{"url":"https://blog.ccino.org/p/memos-lightweight-privately-deployed-multi-platform-cloud-memo/","mime_type":"text/html"}],"url":"https://blog.ccino.cc/i/memos-lightweight-privately-deployed-multi-platform-cloud-memo/","external_url":"https://blog.ccino.org/p/memos-lightweight-privately-deployed-multi-platform-cloud-memo/","content_html":"<h2>Memos</h2><p><br></p><p>博客用来写一些长文、笔记、心得还是挺方便的，有时候也有一些简单的备忘录、心情想要记录下来，找了一圈发现一个宝藏程序：Memos，UI 风格非常简洁，跟 flomo 很像，轻量、支持私有化部署、完全免费、各个平台还有相应的客户端甚至还有网页插件，在此记录搭建流程和遇见的一些问题。</p><p><img src=\"https://pic.ccino.win/OB/2024/10/202410261137290.png\"></p><h2>搭建流程</h2><h4>Docker 部署</h4><pre class=\"ql-syntax\" spellcheck=\"false\">docker run -d --name memos -p 5230:5230 -v ~/.memos/:/var/opt/memos neosmemo/memos:latest\n</pre><p>Copy</p><p><br></p><p>安装成功之后 memos 会在本地&nbsp;127.0.0.1:5230&nbsp;运行</p><h4>Docker-compose 部署</h4><p><br></p><p>如果使用宝塔面板的话可以在&nbsp;<a href=\"https://www.composerize.com/\" rel=\"noopener noreferrer\" target=\"_blank\">https://www.composerize.com/</a>&nbsp;将 docker 命令转换为 docker-compose 或者&nbsp;<a href=\"https://github.com/usememos/memos/blob/main/docker-compose.yaml\" rel=\"noopener noreferrer\" target=\"_blank\">https://github.com/usememos/memos/blob/main/docker-compose.yaml</a></p><ol><li>创建工作目录</li></ol><pre class=\"ql-syntax\" spellcheck=\"false\">mkdir memos &amp;&amp; cd memos\nvi docker-compose.yaml\n</pre><p>Copy</p><ol><li>编辑 docker-compose. Yaml 文件</li></ol><pre class=\"ql-syntax\" spellcheck=\"false\">version: \"3.0\"\nservices:\n  memos:\n    image: neosmemo/memos:latest\n    restart: always \n    container_name: memos\n    volumes:\n      - ~/.memos/:/var/opt/memos\n    ports:\n      - 5230:5230\n</pre><p>Copy</p><ol><li>启动项目</li><li>docker-compose up -d</li><li>首次安装会提示注册用户，请记牢您的而密码。数据文件默认存储在 ~/. Memos 中。</li></ol><h4>配置 Nginx 反向代理</h4><p><br></p><p>可以使用宝塔的反向代理或者 Nginx Proxy Manager 的反向代理配置</p><pre class=\"ql-syntax\" spellcheck=\"false\">server {\n    server_name domain.com;\n    location / {\n        proxy_pass http://127.0.0.1:5230;\n        proxy_set_header Host $host;\n        proxy_set_header X-Forwarded-Proto $scheme;\n        proxy_set_header X-Real-IP $remote_addr;\n        proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;\n        client_max_body_size 0;\n        }\n}\n</pre><p>Copy</p><h2>项目更新</h2><p><br></p><p>Docker-compose</p><pre class=\"ql-syntax\" spellcheck=\"false\">cd {宿主机数据目录}/memos\ndocker stop memos &amp;&amp; docker rm memos\ncp -r ~/.memos/memos_prod.db ~/.memos/memos_prod.db.bak\ndocker pull neosmemo/memos:latest\ndocker-compose up -d #如果是docker-compose部署的使用该命令\ndocker image prune  # prune 命令用来删除不再使用的 docker 对象。删除所有未被 tag 标记和未被容器使用的镜像```\n</pre><p>Copy</p><p>Docker:</p><pre class=\"ql-syntax\" spellcheck=\"false\">docker stop memos\ncp ~/.memos/memos_prod.db ~/.memos_backup/  # 万事先备份，以防万一\ndocker rm -f memos\ndocker pull neosmemo/memos:latest  # 拉取最新镜像\ndocker run -d --name memos -p 5230:5230 -v ~/.memos/:/var/opt/memos neosmemo/memos:latest\n</pre><p>Copy</p><p>如果是宝塔部署的项目，记得要备份一下&nbsp;/www/server/panel/data/compose/{对应的docker目录}/{步骤1中的宿主机数据文件目录}&nbsp;中的数据库文件</p><h2>卸载</h2><pre class=\"ql-syntax\" spellcheck=\"false\">docker stop memos\ndocker rm -f memos  # 停止容器，此时不会删除映射到本地的数据\nrm -rf {宿主机数据目录}/memos  # 完全删除映射到本地的数据\n</pre><p>Copy</p><h2>遇见的问题</h2><ol><li>Memos 配置 cdn 和 cos 之后，上传大一点的文件会报 500, 起初以为是 nginx 反向代理规则的问题，排查后没问题，猜测跟 cdn 有关，就把域名直接解析源站试试，不出意外没有问题，然后再解析回 cdn，竟然也正常了</li><li>解决方案：</li><li>搞了半天，腾讯 CDN 里回源配置有个回源超时配置，延长回源加载时间和 TC 连接时间就可以了，我还问腾讯的售后有没有相关的配置，告诉我说没有 - -</li><li>Nginx 反向代理缓存和 CDN 只要启用任意一个，竟然会出现一个账号只要登录了，其他电脑打开网页也是登录状态</li><li>解决方案：</li><li>终于解决了，绕了一大圈发现是缓存了 go 文件，而用户身份信息就是 go 处理的，在 cdn 中配置规则不缓存 go 文件，nginx 设置了不缓存 go 文件好像不管用，只能先暂时关闭反向代理缓存了</li></ol><h2>相关地址</h2><ol><li>项目地址：<a href=\"https://github.com/usememos/memos\" rel=\"noopener noreferrer\" target=\"_blank\">https://github.com/usememos/memos</a></li><li>微信小程序 (可打包成 app)：<a href=\"https://github.com/Rabithua/memos_wmp\" rel=\"noopener noreferrer\" target=\"_blank\">https://github.com/Rabithua/memos_wmp</a></li><li>APP 版本 2：<a href=\"https://github.com/mudkipme/MoeMemosAndroid\" rel=\"noopener noreferrer\" target=\"_blank\">https://github.com/mudkipme/MoeMemosAndroid</a></li></ol><h2>参考网址</h2><p><br></p><p><a href=\"https://www.skyue.com/23021116.html/comment-page-1#comment-3340\" rel=\"noopener noreferrer\" target=\"_blank\">https://www.skyue.com/23021116.html/comment-page-1#comment-3340</a></p><p><a href=\"https://bore.vip/archives/d5e37958/#%E5%8D%95%E9%A1%B5%E9%83%A8%E7%BD%B2%E4%BB%A3%E7%A0%81\" rel=\"noopener noreferrer\" target=\"_blank\">https://bore.vip/archives/d5e37958/#%E5%8D%95%E9%A1%B5%E9%83%A8%E7%BD%B2%E4%BB%A3%E7%A0%81</a></p><p><a href=\"https://blog.laoda.de/archives/docker-install-memos/\" rel=\"noopener noreferrer\" target=\"_blank\">https://blog.laoda.de/archives/docker-install-memos/</a></p><p><a href=\"https://cmsblog.cn/1338.html\" rel=\"noopener noreferrer\" target=\"_blank\">https://cmsblog.cn/1338.html</a></p><p><a href=\"https://tech.yemengstar.com/?p=1014\" rel=\"noopener noreferrer\" target=\"_blank\">https://tech.yemengstar.com/?p=1014</a></p><p><a href=\"https://immmmm.com/hi-memos/\" rel=\"noopener noreferrer\" target=\"_blank\">https://immmmm.com/hi-memos/</a></p><p>本文出自：<a href=\"https://www.cgxblog.com/719.html\" rel=\"noopener noreferrer\" target=\"_blank\">Memos：轻量、私有部署的多平台云备忘录 - 南北阡陌</a></p>","content_text":"MEMOS\n\n\n\n\n博客用来写一些长文、笔记、心得还是挺方便的，有时候也有一些简单的备忘录、心情想要记录下来，找了一圈发现一个宝藏程序：Memos，UI 风格非常简洁，跟\nflomo 很像，轻量、支持私有化部署、完全免费、各个平台还有相应的客户端甚至还有网页插件，在此记录搭建流程和遇见的一些问题。\n\n[https://pic.ccino.win/OB/2024/10/202410261137290.png]\n\n\n搭建流程\n\nDOCKER 部署\n\ndocker run -d --name memos -p 5230:5230 -v ~/.memos/:/var/opt/memos neosmemo/memos:latest\n\n\nCopy\n\n\n\n\n安装成功之后 memos 会在本地 127.0.0.1:5230 运行\n\nDOCKER-COMPOSE 部署\n\n\n\n\n如果使用宝塔面板的话可以在 https://www.composerize.com/ 将 docker 命令转换为 docker-compose\n或者 https://github.com/usememos/memos/blob/main/docker-compose.yaml\n\n 1. 创建工作目录\n\nmkdir memos && cd memos\nvi docker-compose.yaml\n\n\nCopy\n\n 1. 编辑 docker-compose. Yaml 文件\n\nversion: \"3.0\"\nservices:\n  memos:\n    image: neosmemo/memos:latest\n    restart: always \n    container_name: memos\n    volumes:\n      - ~/.memos/:/var/opt/memos\n    ports:\n      - 5230:5230\n\n\nCopy\n\n 1. 启动项目\n 2. docker-compose up -d\n 3. 首次安装会提示注册用户，请记牢您的而密码。数据文件默认存储在 ~/. Memos 中。\n\n配置 NGINX 反向代理\n\n\n\n\n可以使用宝塔的反向代理或者 Nginx Proxy Manager 的反向代理配置\n\nserver {\n    server_name domain.com;\n    location / {\n        proxy_pass http://127.0.0.1:5230;\n        proxy_set_header Host $host;\n        proxy_set_header X-Forwarded-Proto $scheme;\n        proxy_set_header X-Real-IP $remote_addr;\n        proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;\n        client_max_body_size 0;\n        }\n}\n\n\nCopy\n\n\n项目更新\n\n\n\n\nDocker-compose\n\ncd {宿主机数据目录}/memos\ndocker stop memos && docker rm memos\ncp -r ~/.memos/memos_prod.db ~/.memos/memos_prod.db.bak\ndocker pull neosmemo/memos:latest\ndocker-compose up -d #如果是docker-compose部署的使用该命令\ndocker image prune  # prune 命令用来删除不再使用的 docker 对象。删除所有未被 tag 标记和未被容器使用的镜像```\n\n\nCopy\n\nDocker:\n\ndocker stop memos\ncp ~/.memos/memos_prod.db ~/.memos_backup/  # 万事先备份，以防万一\ndocker rm -f memos\ndocker pull neosmemo/memos:latest  # 拉取最新镜像\ndocker run -d --name memos -p 5230:5230 -v ~/.memos/:/var/opt/memos neosmemo/memos:latest\n\n\nCopy\n\n如果是宝塔部署的项目，记得要备份一下 /www/server/panel/data/compose/{对应的docker目录}/{步骤1中的宿主机数据文件目录} 中的数据库文件\n\n\n卸载\n\ndocker stop memos\ndocker rm -f memos  # 停止容器，此时不会删除映射到本地的数据\nrm -rf {宿主机数据目录}/memos  # 完全删除映射到本地的数据\n\n\nCopy\n\n\n遇见的问题\n\n 1. Memos 配置 cdn 和 cos 之后，上传大一点的文件会报 500, 起初以为是 nginx 反向代理规则的问题，排查后没问题，猜测跟 cdn\n    有关，就把域名直接解析源站试试，不出意外没有问题，然后再解析回 cdn，竟然也正常了\n 2. 解决方案：\n 3. 搞了半天，腾讯 CDN 里回源配置有个回源超时配置，延长回源加载时间和 TC 连接时间就可以了，我还问腾讯的售后有没有相关的配置，告诉我说没有 - -\n 4. Nginx 反向代理缓存和 CDN 只要启用任意一个，竟然会出现一个账号只要登录了，其他电脑打开网页也是登录状态\n 5. 解决方案：\n 6. 终于解决了，绕了一大圈发现是缓存了 go 文件，而用户身份信息就是 go 处理的，在 cdn 中配置规则不缓存 go 文件，nginx 设置了不缓存\n    go 文件好像不管用，只能先暂时关闭反向代理缓存了\n\n\n相关地址\n\n 1. 项目地址：https://github.com/usememos/memos\n 2. 微信小程序 (可打包成 app)：https://github.com/Rabithua/memos_wmp\n 3. APP 版本 2：https://github.com/mudkipme/MoeMemosAndroid\n\n\n参考网址\n\n\n\n\nhttps://www.skyue.com/23021116.html/comment-page-1#comment-3340\n\nhttps://bore.vip/archives/d5e37958/#%E5%8D%95%E9%A1%B5%E9%83%A8%E7%BD%B2%E4%BB%A3%E7%A0%81\n\nhttps://blog.laoda.de/archives/docker-install-memos/\n\nhttps://cmsblog.cn/1338.html\n\nhttps://tech.yemengstar.com/?p=1014\n\nhttps://immmmm.com/hi-memos/\n\n本文出自：Memos：轻量、私有部署的多平台云备忘录 - 南北阡陌","image":"https://cdn-blog-ccino-cc.ccino.cc/blog-ccino-cc/production/images/item-c9b377920f73f9023cb4aca5db7fdbf8.png","date_published":"2024-10-26T04:16:14.393Z","_microfeed":{"is_audio":false,"is_document":false,"is_external_url":true,"is_video":false,"is_image":false,"web_url":"https://blog-ccino-cc.pages.dev/i/memos-6BWpUbnDbvd/","json_url":"https://blog-ccino-cc.pages.dev/i/6BWpUbnDbvd/json/","rss_url":"https://blog-ccino-cc.pages.dev/i/6BWpUbnDbvd/rss/","guid":"6BWpUbnDbvd","status":"published","itunes:episodeType":"full","date_published_short":"Sat Oct 26 2024","date_published_ms":1729916174393}},{"id":"tMd8VMX18d9","title":"谷歌科学家万字长文：《改变你职业生涯的一篇文章，我如何运用人工智能完成工作》建议每个人都要读一遍","attachments":[{"url":"https://blog.ccino.org/p/how-i-use-artificial-intelligence-to-get-my-job-done-is-recommended-for-everyone-to-read/","mime_type":"text/html"}],"url":"https://blog.ccino.cc/i/how-i-use-artificial-intelligence-to-get-my-job-done-is-recommended-for-everyone-to-read/","external_url":"https://blog.ccino.org/p/how-i-use-artificial-intelligence-to-get-my-job-done-is-recommended-for-everyone-to-read/","content_html":"<p>在当今科技界, 关于人工智能是否被过度炒作的争论从未停息。然而, 很少有像谷歌 DeepMind 的安全研究专家和机器学习科学家 Nicholas Carlini 这样的专家，用亲身经历为我们提供了一个独特的视角。通过他的文章, 我们看到了大型语言模型 (LLM) 在实际应用中的强大能力和多样性。这些并非空洞的营销宣传, 而是切实可以改变工作方式、提高生产效率、激发创意的工具。</p><p>最近, Carlini 本人亲自撰写了一篇长达万字的文章, 详细介绍了他如何在日常生活和工作中使用 AI。这篇洞察力十足的文章不仅展示了 AI 技术的广泛应用, 更让我们得以一窥未来科技将如何重塑我们的生活方式。</p><p><img src=\"https://pic.ccino.win/OB/2024/10/202410152117121.png\"></p><p>Carlini 在文章中列举了 50 个他亲身使用 AI 的实例, 涵盖了工作效率、创意写作、编程辅助等多个方面。然而, 令人惊讶的是, 这 50 个例子仅仅是他所有 AI 应用场景的冰山一角 - 据他估计, 不到他实际使用情况的 2%。这一变革令人震撼, 它不仅体现了 AI 技术的深度和广度, 更凸显了我们可能还未充分认识到 AI 的潜力。</p><p>事实上, Carlini 的经历可能预示着一个更广泛的趋势: 随着 AI 技术的不断进步和普及, 我们可能正站在一个技术革命的风口浪尖。就像个人电脑和互联网彻底改变了我们的生活和工作方式一样, AI 可能会成为下一个推动社会变革的关键力量。</p><p>那么, 面对这样的前景, 我们应该如何看待 AI 技术的发展呢? 是抱有审慎的态度, 还是积极拥抱变革?</p><h2>原文如下：</h2><p><br></p><p><strong>作者：DeepMind 安全研究专家和机器学习科学家 Nicholas Carlini</strong></p><p>我不认为 “人工智能” 模型（我指的是大型语言模型）被过度炒作了。</p><p>确实，任何新技术都会吸引骗子。许多公司喜欢说他们在 “使用人工智能”，就像他们以前声称自己依靠“区块链” 一样。（正如我们一次又一次看到的那样）同样，我们可能正处于一个泡沫中。互联网在 2000 年泡沫破裂，但我们现在拥有的互联网应用，曾是科幻小说中的东西。</p><p>但我认为最近我们取得的进展不仅仅是炒作的原因是，过去一年中，我每周至少花几个小时与各种大型语言模型互动，并且对它们解决我给出的越来越困难的任务的能力印象深刻。因此，我可以说，这些模型使我在编写代码方面的速度提高了至少 50%，无论是研究项目还是个人的编程副业。</p><p>我在网上找到的大多数谈论 LLM 实用性的人要么过于乐观，声称所有工作将在三年内自动化，要么极度悲观，认为它们没有任何贡献，永远也不会有。</p><p>所以在这篇文章中，我只是想让讨论更加实际。我不会对未来进行任何论断。我只想列出一个列表，这是我（一名研究机器学习的程序员和研究科学家）与不同的大型语言模型进行的 50 次对话，这些对话显著提高了我进行研究的能力，并帮助我从事随机的编程副项目。其中包括：</p><ul><li>使用我之前从未使用过的技术构建整个网络应用程序。</li><li>教我如何使用各种框架，尽管我之前从未使用过它们。</li><li>将几十个程序转换为 C 或 Rust 以提高 10-100 倍的性能。</li><li>精简大型代码库，显著简化项目。</li><li>为我在过去一年中撰写的几乎每一篇研究论文编写初始实验代码。</li><li>自动化几乎所有单调的任务或一次性脚本。</li><li>几乎完全取代了帮助我设置和配置新软件包或项目的网络搜索。</li><li>在帮助我调试错误信息方面，网络搜索的替代率约为 50%。</li></ul><p>如果对这些应用实例进行分类，它们<strong>大致可以分为 “辅助学习” 和“自动化日常琐事”两类。前者对我来说至关重要，因为它让我能够轻松应对之前觉得有难度的任务；后者同样重要，因为它使我能集中精力去做我最擅长的事，解决真正的难题。</strong></p><p>关键是，这些例子展示的是我如何实际利用大型语言模型（LLMs）的。它们并不旨在炫耀技术的惊人能力，而是基于我解决工作需求的实际情况。这意味着，这些例子或许看起来不够引人注目，但实际上，我日常工作的很大一部分也都是平凡无奇的，而现今可用的 LLMs 几乎帮我自动化了所有这些工作。</p><p>通过这篇文章，我的目的是用一个接一个的实例，向你展示我在过去一年如何有效利用 LLMs 提升工作效率，直到你感到有些疲惫为止。要知道，尽管你可能已经看过不少例子，但实际上我所展示的，还不到我利用 LLMs 的总数的 2%。</p><h2>细微差别</h2><p><br></p><p>如果说互联网处理得不尽如人意的是什么，那一定是对细节的把握。我绝不认为现今的大型语言模型（LLMs）能够主宰世界，也不打算讨论未来的模型能做什么，不能做什么。我只想探讨，现阶段的模型对我是否真的有用。</p><p>你可能会问，为什么还需要一篇文章来证明语言模型的用途呢？这难道不是显而易见的事实吗？但事实上，无论是在学术界、软件工程领域还是媒体行业，都有不少人声称 LLMs 毫无用处，只是一时的炒作，几年后将默默无闻，对世界没有任何影响。我要反驳这些观点，因为目前的 LLMs 已经证明了它们的实用性。</p><p>但同时，我也需要说明，另有一批声音同样强烈的人士持相反观点，他们认为现有模型能够取代所有程序员，人们不应再学习编程，因为不久后大家都将面临失业。虽然反驳这些观点不是本文的主旨，但我必须明确，我并不支持这种极端的看法。</p><p>此外，我也不会主张 “目的正当化手段”，即便这些模型的训练确实存在诸多负面影响，我不认为应当因此而推广使用。</p><p>我充分意识到这些模型可能带来的负面后果，这些后果可能非常严重，包括但不限于散布错误信息、滥用、监控以及取代工作岗位（甚至有人担忧到人类灭绝的地步）。我将在不久的将来撰写一篇文章，深入探讨 LLMs 可能引发的危害，届时会在此放上链接。然而，这与语言模型是否有用的讨论是两码事——这正是我此处想要探讨的问题。</p><p>我也清楚你可能因为语言模型偏向于生成不准确的信息、重复已知事实、在面对复杂情况时可能彻底失败的倾向而不愿使用它们——可能我对这些局限性的了解比你更深。但本文不会讨论这些。因为我认为，尽管有这些不足，模型仍然是有用的。</p><p>此外，我也深知培训这些模型所涉及的伦理问题极具争议。你可能不赞成在未经许可的情况下使用人们的数据来训练它们（我对此的理解可能比你更深刻）。或许你关注的是那些为直接训练这些模型而获得微薄报酬的人。我承认这些都是问题，但本文也不打算讨论这些。</p><p><strong>正如我已经多次强调的：我在这里讨论的，仅仅是这些模型在当前状态下是否实际有用。</strong></p><h2>我的背景简介</h2><p><br></p><p>通常，我不是那种轻易相信任何事物的人。比如，尽管我经历了十年前信息安全界的加密货币热潮，我却从未参与撰写任何关于区块链的研究论文。我也从未拥有过任何比特币，因为在我看来，它们除了用于赌博和欺诈之外，没有任何实际价值。我一直持怀疑态度，每当有人向我宣称 “某项新技术将改变世界”，我的反应总是冷漠。</p><p>因此，当第一次有人告诉我人工智能将极大地提升我的工作效率并改变我的日常工作方式时，我同样持保留态度，我的回应是：“见到实际效果我才会信。”</p><p>此外，我是一名安全研究员。在过去近十年的工作中，我专注于展示人工智能模型在面对未曾训练过的各种环境时如何彻底失败。我证明了只需对机器学习模型的输入进行轻微扰动，就能使其输出完全错误的结果；或者这些模型往往只是记住了它们训练数据中的特定案例，并在实际应用时简单重复。我深知这些系统的种种局限。</p><p>然而，现在我却在这里说，我认为当前的大型语言模型是自互联网问世以来，给我的生产力带来的最大提升。坦白说，如果今天要我在使用互联网和一个最先进的语言模型之间选择一种工具来解决工作中随机一个编程任务，我可能会选择使用语言模型的次数超过一半。</p><h2>如何利用语言模型</h2><p><br></p><p>以下是我如何利用大型语言模型（LLMs）来提升工作效率。</p><p>你可能对我所述的使用案例不感兴趣，甚至认为它们有些荒谬。也可能这些案例与你的需求不相关，这一点我也能理解。但我只能从个人角度出发。这些使用实例都是我在过去一年里与 LLMs 的交流记录中直接摘录的。</p><h3>1、为我开发完整应用</h3><p><br></p><p>去年，我创建了一个测验，让人们评估 GPT-4 在处理几个特定任务上的表现。这个测验非常受欢迎，获得了超过一千万的页面浏览量。你可能想不到，我几乎是让 GPT-4 为我编写了这个应用的全部初始版本。这一过程是通过一连串的问题进行的，我从询问应用的基本架构开始，然后逐步扩展其功能。整个对话长达 30,000 字，极大地测试了当时的 GPT-4 模型的能力极限。如果你翻看这些对话，会发现从我描述需求并请求模型完成全部实现的信息，到我请求做出具体修改的信息（如 “不要比较平均分数，而是用核密度估计表示它是哪个百分位”），以及我提出一些完全不明确的问题时复制粘贴的错误信息（例如，“绘图错误：numpy. Linalg. LinAlgError: singular matrix”），还有我仅仅询问简单单次问题的情况（比如 “如何用 JavaScript 在页面上添加一个 iframe，内容是从字符串加载的？”）。</p><p>这种方法之所以有效，主要是因为语言模型擅长处理人们已经解决的问题，而这个测验的 99% 内容只不过是一些基础的 HTML 和 Python 后端服务器，这是任何人都可以编写的。这个测验之所以引人关注并受到喜爱，并非因为其背后的技术，而是因为测验内容本身。通过自动化所有重复性的部分，我能轻松地完成这个项目。</p><p>实际上，如果没有语言模型的帮助，我可能根本就不会去创建这个测验，因为我不愿意花时间从头编写整个网页应用。尽管我擅长编程！我相信，即使是现有的模型，也足以让大多数人仅凭提问就能解决他们以前无法解决的重要任务。</p><p>我还有一些类似的例子以后介绍，其中我让模型为我编写了整个应用，当这些应用发布时，我会明确指出它们是在语言模型的协助下完成的。</p><h3>2、作为新技术的向导</h3><p><br></p><p>我曾经总能赶上各种新兴框架的步伐。但是一个人的时间毕竟有限，因为我的职业特性，我大多数时间都在跟进最新的研究进展，而不是最新的 JavaScript 框架。</p><p>这意味着当我开始一个不属于我研究领域的新项目时，我通常有两个选择：一是利用我已知的技术，虽然这些技术可能已经过时十年或二十年，但对于小项目来说通常足够了；二是尝试学习新的（通常也是更好的）方法。</p><p>这就是语言模型派上用场的时候。对我而言较新的框架或工具，比如 Docker、Flexbox 或 React，对其他人来说可能已经相当熟悉了。世界上可能有成千上万的人已经非常了解这些技术。当前的语言模型也能做到这一点。</p><p>这意味着，我可以通过与语言模型的交互式学习，来掌握解决任务所需的任何知识，而不必依赖那些假设读者具备特定知识、目标明确的静态教程。</p><p>比如，今年早些时候，我在构建一个 LLM 评估框架时，希望能在一个受限环境中运行由 LLM 生成的代码，以避免它删除我的计算机上的随机文件等问题。Docker 是完成这一任务的理想工具，但我之前从未使用过。</p><p>重要的是，这个项目的目标并非是要学会使用 Docker，Docker 只是我实现目标所需的工具。我只需要掌握 Docker 的 10%，确保我能以最基本的方式安全使用它。</p><p>如果是在 90 年代，我可能需要购买一本关于 Docker 的书籍，从头开始学习，阅读前几章，然后试图跳读以找出如何实现我想要的功能。过去十年情况有所改善，我可能会在线搜索如何使用 Docker 的教程，跟着操作，然后搜索遇到的错误信息，看是否有人遇到相同的问题。</p><p>但在今天，我只需要请一个语言模型教我使用 Docker。</p><h3>3、开始新项目</h3><p><br></p><p>回想起来，我最初接触的编程语言是 Java。我非常喜欢编程，但有一件事我绝对讨厌：面对新项目的空白屏幕。特别是在使用 Java 时！甚至仅仅是让程序编译一个 hello world 程序——这个 “public static void main string args” 到底是做什么的？括号应该怎么放？哪些字母应该大写？为什么这里用花括号，那里用方括号？</p><p>所以我做了任何孩子都会做的事——我让我父亲帮我做了。</p><p>二十年过去了，我仍然不喜欢使用我不熟悉的框架开始新项目。仅仅是为了搞定样板代码就需要花费大量时间，而且我对我在做的事情毫无头绪。</p><p>例如，我最近想尝试编写一些 CUDA 代码，来评估一种简单的贪婪搜索在 GPU 上的性能与某人的高效优化的 CPU 实现相比。</p><p>但我不懂 CUDA 编程。我会写 C 语言，了解 GPU 的工作原理、内核的功能以及内存布局等，但实际编写向 GPU 发送任务的代码？我不知道该从哪里开始。因此，我直接请求模型为我编写 CUDA 程序的初稿。完美吗？当然不是！但这是一个起点。这正是我需要的。你会注意到这里的代码有很多错误！实际上，我完全可以接受。我不是在寻找完美的解决方案，而是一个起点，我可以从那里继续。如果未来的模型能做得更好，那将是惊人的。但我现在拥有的已经大有帮助了。</p><p>另一方面，对于我在家进行的一些其他个人项目，我正在使用一个树莓派 Pico W。这是我第一次使用它。我希望它能为我做一些事情，特别是一些网络相关的事情。再次，我确信我可以在网上找到有人描述如何做我想做的事情的好教程。但你最近看过互联网吗？前 5 个结果通常只是一些垃圾内容农场，它们有 2008 年的有缺陷的代码，仅为了搜索引擎优化而更新，但仍然不起作用。</p><p>因此，我直接请求一个语言模型教我如何做我想做的事情。我之前已经和微控制器打过交道，所以我或多或少了解它们的工作方式。但我之前从未使用过 Pico W。我只需要一些帮助来开始处理所有的依赖关系，然后我可以弄清楚剩下的部分。</p><p>我为新的微控制器编写的第一个 “hello world” 程序总是让一个 LED 闪烁。这可以让我测试我是否能够编译并上传代码到设备，是否正确设置了所有的引脚，并且基本上知道我在做什么。所以，让我们只是请求一个闪烁程序。（再次：这在互联网上存在吗？几乎可以肯定。但那样我就得去搜索它了。）一旦我有了这段代码并且运行起来，从这里我就知道该怎么做了。我知道 python 是如何工作的（信不信由你！）。因此，我可以从那里直接继续编辑东西，把特殊的 Micro Python 的东西处理掉。</p><p>当我遇到另一个需要我特别处理的问题时，我可以再次请求模型帮助我。例如，我接着只是让模型为我编写一个连接到 wifi 的脚本。</p><h3>4、代码简化</h3><p><br></p><p>作为安全研究员，我经常需要处理别人的研究项目，这些项目包含数千行代码，我必须弄懂它们的工作原理以便进行攻击。这听起来并不困难，如果每个人都编写清晰的代码，实际上也确实不该困难，但现实世界并非如此。研究人员通常没有动力发布整洁的代码。所以，人们往往会发布他们能用的任何杂乱代码。（我自己也不例外。）</p><p>我无法分享与研究相关的例子，但我可以分享我正在进行的一个个人项目的例子。据说我对康威的生命游戏有种不健康的痴迷。最近，我试图找到一种快速的方法，通过 Python 评估一些生命游戏模式。有一个很好的 C++ 工具叫 golly 可以做这个，但我不想将我的 Python 代码重写为 C++。</p><p>Golly 有一个 CLI 工具正好满足我的需求——我所需要的只是一种正确调用它的方法。这的第一步是简化支持约 50 种不同命令行选项的 C++ 代码，让它只执行我想要的操作。所以我将所有 500 行的 C++ 代码输入到 LLM 中，请求一个更简短的文件来完成相同的任务。</p><p>你知道吗？这完美地奏效了。然后，我请求一个围绕 C++ 代码的 Python 封装器。这同样有效。这是那些令人烦恼的任务之一，如果由我来做，我可能永远不会完成。但现在我可以请求别人帮我完成，我得到的东西比我原来的 Python 代码快了 100 倍。</p><p>我发现自己经常这样做。这里还有一个例子，我在用 Python 做同样的事情。</p><p>再说一次，这些任务并不复杂。但每次这样做，我都能节省大量时间。这就是我认为大型语言模型如今惊人的原因之一：它们的用途可能不够光鲜，也不会因为说 “这是我用大型语言模型简化日常工作的平凡方式” 而赢得网络赞誉，但这是实实在在的帮助。</p><h3>5、处理单调任务</h3><p><br></p><p>我必须完成的许多任务都是单调无聊的，不需要太多思考，但却必须做。</p><p>事实上，我发现自己之所以会拖延任务，往往是因为我知道完成这些任务会感到无聊和痛苦。LLMs 极大地减轻了这种痛苦，让我在开始任务时就知道自己只需要解决有趣的问题。因此，我想逐一介绍一些通过请求 LLMs 帮忙而解决的非常普通的问题。</p><p>例如，最近我需要拆解一个用 Python 3.9 编写的程序。大多数 Python 反编译器只支持到 Python 3.7，但无法在我处理的 3.9 版本上运行。</p><p>反编译实际上并不难，主要是在重建控制流程时避免出错。因此，我没有亲自花时间为几千个操作码和几百行代码进行这种转换，而是让 LLM 帮我做了。它做得非常好！效果远超我的预期。这里有三个不同的对话，在这些对话中我让模型帮我完成了这项工作。另一个例子是，当我需要将一些非结构化数据转换成结构化格式时。比如，我在做一个项目，需要列出一些书名和作者名。我在网上找到了一些非结构化格式的数据，然后让 LLM 帮我格式化。最近，我在写一篇关于如何破解某个防护的博客文章，想展示我不得不修改的代码完整差异。于是我粘贴了差异和之前如何将差异转换为 HTML 的示例，并让 LLM 按照之前的格式为我生成差异。此外，作为我的工作的一部分，我经常需要为我使用的资源生成引用。谷歌学术很容易引用论文，我可以直接复制粘贴。但引用网页稍显麻烦；我最近开始请求 LLM 帮我生成引用。（确保这是正确的！）我可以继续举出至少一百个类似的例子。但我想你已经明白了我的意思。</p><p>我完全明白这种任务可能会让人觉得 “就这？” 但我们要记住，五年前这些模型几乎不能连贯地写出一个段落，更别说为你解决整个问题了。</p><h3>6、使每个用户都成为 “高级用户”</h3><p><br></p><p>如果你曾经看过比你不那么熟练的人使用某个工具，这可能会有点让人难受。他们可能会花费几分钟甚至几小时的时间去完成一个本可以通过某种宏或在处理特定任务时巧妙使用并行应用程序来自动化的任务。</p><p>然而，学习执行这些操作所需的技巧需要时间，并且具有挑战性。</p><p>例如，我最近试图编写一个 Python 程序来处理来自 Apple Lisa 键盘的输入。我在线找到了一个人用 C 语言编写的相关代码，其中包含许多像 #define KEYNAME key_code 这样的语句，我想将它们转换为一个 Python 字典，将整数代码映射到对应的字符串。</p><p>我是一个 Emacs 用户。我知道如何在 Emacs 中解决这个问题，这甚至并不难。这是我刚记录的一些关键操作，可以达到这个效果：</p><p>C-h C-s #def [enter] M-f [delete] C-d M-f C-[space] M-f C-w C-a C-y : “M-f”, C-g C-] } C-[ {</p><p>尽管这对我几乎是自然的，但到目前为止，我已经花了超过一半的生活时间在 Emacs 上变得足够熟练，以至于这成为了自然反应。但你知道现在我连接了 LLM 到我的编辑器，我会怎么做吗？C-h C-h 请将这些 #define 重写为 {keycode: string, …} 的字典格式。</p><p>然后，文本就在我眼前被重写了！</p><p>正是在这样的情况下，我认为 LLMs 对非专家的潜在效用甚至高于专家。这个模型为每个人提高了起点，如果你之前完全不会做，现在突然能做很多事情。</p><h3>7、作为 API 参考</h3><p><br></p><p>真正的程序员想要理解某个工具的工作原理时会阅读参考手册。但我是个懒惰的程序员；我更愿意直接得到答案。因此，现在我向语言模型提问。</p><p>当我向人们展示这些例子时，有些人会变得有些防御性，他们说：“LLM 没有做任何你不能用已有工具完成的事情！” 你知道吗？他们说得对。但是，用搜索引擎能做的事，用一本关于该主题的实体书也能做；用一本实体书能做的事，通过阅读源代码也能做。</p><p>然而，每一种方式比前一种都简单。当事情变得更简单时，你会更频繁地做，而且方式上也会有所不同。</p><p>这就是我问 “哪个 $ 命令可以传递所有剩余参数” 并获得答案的例子。（紧接着是另一个 “我该如何使用这个东西” 的问题！）这实际上是我最常用 LLMs 的方法之一。我之所以不能给你展示更多这样的例子，是因为我在 Emacs 和我的 shell 中都内置了查询 LLMs 的工具。因此，当我想要做这些事情的 90% 的时间，我甚至不需要离开我的编辑器。</p><h3>8、搜索难以找到的内容</h3><p><br></p><p>在互联网上搜索内容曾经是一项需要学习的技能。你想在查询中包含哪些特定的词汇？它们应该是复数还是单数？过去时？你希望避免在页面上出现哪些词汇？我是想要 X 和 Y，还是 X 或 Y？</p><p>现在情况已经不同了。我想不起上次我在 Google 中使用 OR 的时间。我也想不起上次我使用减号 (-) 来移除结果子集的时间。在大多数情况下，今天你只需要写下你想要找的内容，搜索引擎就会为你找到。</p><p>但搜索引擎仍然不是 100% 的自然语言查询。它仍然有点像你在玩反向危险边缘游戏，试图使用答案中会有的关键词而不是问题。这是一项我认为我们几乎都忘记了我们学过的技能。</p><p>对于今天的一些简单任务（随着时间的推移会越来越多），语言模型只是更好。我可以直接输入 “所以我知道 + 对应于__add__，但是是什么”，它会告诉我答案是__inv__。这是用标准搜索引擎很难搜索到的东西。是的，我知道有方法可以问，这样我就能找到答案。可能如果我输入 “python 文档元类 “add””，我可以搜索页面上的并得到答案。但你知道还有什么有效吗？只要问 LLM 你的问题。</p><p>这样做任何一次只节省几十秒的时间，但当你在解决某个编码任务的过程中，已经试图同时记住一百万件事时，能够将你试图解决的问题倾倒出来并得到一个连贯的答案是令人惊讶的。</p><p>这并不是说他们今天在这方面已经完美。语言模型只有在线上被足够频繁地重复时才知道事情。“足够频繁” 是什么意思取决于模型，所以我确实需要花一些精力思考我是应该询问模型还是询问互联网。但模型只会变得更好。</p><p>或者，每当我遇到随机崩溃时，我会将模型转储我所看到的并要求解释，就像我在这里做的那样，当我只是输入 zsh 没有找到匹配的 “远程通配符传输问题”。或者，作为一个完全独立的例子，我去年在写一篇博客文章时，想要第一个字母大写，让其余的文字环绕它，就像我在这句话中所做的那样。现在这被称为下沉式大写字母。但我不知道这个。我只知道我想要的效果，所以问语言模型“我想让它看起来像一本华丽的书，文字围绕 O”，它给了我我想要的东西：这个任务是另一个属于“我只是因为 LLMs 才做的” 类别——我不会认为花大量时间弄清楚如何做是值得的。但因为我可以直接问模型，我就这样做了，它让我的帖子看起来更好一些。</p><h3>9、解决一次性任务</h3><p><br></p><p>有两种类型的程序。首先，你有一些你想要正确完成的程序；它们将会存在一段时间，因为你需要维护它们好几年，所以代码的清晰性很重要。然后，你有那些只存在大约 25 秒的程序；它们将帮助你完成某些任务，然后立即被丢弃。</p><p>在这些情况下，我根本不关心代码的质量，而且程序是完全独立的，我现在几乎专门使用 LLMs 来为我编写这些程序。</p><p>请注意：大多数这些情况再次出现，你会看着它们说 “就这？”。但就像我之前说的，我每天只有那么多小时来处理一个项目。如果我能节省编写一次性使用的程序的时间和精力，我会选择这样做。</p><p>可能最常见的例子是帮助我生成一些图表，以可视化我作为某些研究实验结果生成的一些数据。我有几十个这样的例子。可能接近一百而不是零。它们看起来基本上都一样，这里只是一个例子：或者，另一个类似的例子，当我有一种格式的数据并想将其转换为另一种格式的数据时。通常这是我只需要做一次的事情，一旦完成，我就会扔掉生成的脚本。但我还可以给你举出一千个其他例子。通常情况下，当我有一个足够简单的脚本想要编写时，我会直接请求 LLM 整体编写。例如，这里我请求 LLM 为我编写一个脚本，让它大声读出我的论文，这样我可以确保它们没有愚蠢的语法问题。在许多情况下，当我不太清楚我想要什么时，我也会从请求模型提供一些初始代码开始，然后从那里进行迭代。例如，这里有一个一次性任务，我只是需要快速处理一些数据。在 2022 年，我会花两分钟用 python 编写这个，然后等待几个小时因为它只运行一次——优化它所需的时间会比 python 程序运行的时间还要长。但现在呢？你敢打赌我会花同样的两分钟请求用 Rust 代码为我处理数据。或者这里是另一个例子，我请求模型为我下载一个数据集，并对其进行一些初始处理。这对我来说容易做吗？可能是。但这不是我想要考虑的任务；我想要考虑的是我将要用数据集做的研究。消除分心非常有价值，不仅仅是节省几分钟的时间。</p><p>另一次，我编写了一个程序，这样我就可以用小立方体 3 D 打印一些像素化的图像。为此，我想将 PNG 转换为 STL 文件；但这不是项目的重点。这只是沿途必须发生的事情。所以我请求 LLM 为我解决这个问题。或者，作为另一个例子，我最近想使用 Docker Compose 来设置一个新项目。我遇到了一些问题，只想让它运行起来，然后我会弄清楚出了什么问题。所以我只是来回几次，我所做的就是复制一个又一个错误消息，直到它最终给我一个有效的解决方案。我还会发现自己在很多情况下首先请求一个完整的解决方案，然后请求如何修改它的提示。在这次对话中，我首先请求一个解析 HTML 的程序，然后请求 API 参考或其他改进方式的提示。最近我一直在尝试做一些电子产品相关的事情，我有一个在 Arduino 上运行的 C 程序，但我希望它在树莓派 Pico 上以 MicroPython 运行。这个转换过程没有什么有趣的；它只需要完成。所以我没有亲自完成工作，只是请语言模型为我做。对于另一个项目，我需要用一些花哨的 ML 模型在一些交互循环中分类一些图像。我本可以自己编写，或者我可以直接请求模型为我做。</p><h3>10、解释事物给我听</h3><p><br></p><p>我最近开始对电子学产生了兴趣。我年轻的时候做过一些电子项目，并在大学期间上过几门相关课程。但现在我想进行实际的电子项目，我发现有许多我不了解的细节，这让开始任何项目都变得困难。</p><p>我可以去读一本关于实用电子学的书。我可能真的会在某个时候这么做，以便彻底理解这个主题。但我并不想把我的时间花在感觉自己在学习上。我从事电子学的部分原因是想从整天的阅读和写作中抽身而出。</p><p>这就是 LLMs 发挥出色的地方。它们可能不如世界上最出色的专家那样知识渊博，但成千上万的人可能知道我可能会提出的任何电子问题的答案。这意味着语言模型很可能也知道答案。它乐于为我提供所有问题的答案，使我可以享受乐趣而不必纠结于细节。虽然我完全可以通过在互联网上搜索来找到答案，但在忙碌一整天后，简单地让模型为我完成这些工作的便利性使我感到非常放松。</p><p>这里是一些例子，展示了我如何询问语言模型有关电子学中事物工作原理的基本问题。这些答案完美吗？谁知道呢。但你知道它们比什么更好吗？比我什么都不知道要好。</p><h3>11、解决具有已知解决方案的任务</h3><p><br></p><p>几乎所有事情都已经有人做过了。你想做的事情几乎没有什么是真正新颖的。语言模型特别擅长提供它们之前见过的事物的解决方案。</p><p>在最近的一个项目中，我需要提升一些 Python 代码的性能。因此，我（1）请求 LLM 将其重写为 C 语言，然后（2）请求它构建一个接口，以便我能从 Python 调用 C 代码。</p><p>这些任务都不是 “难” 的。将 Python 转换为 C 是我确信自己能在一两小时内完成的。尽管我不完全知道 Python 到 C 的 API 如何工作，但我相信我可以通过阅读文档来了解。但如果需要我自己来做，我永远不会去做。它不是关键路径上的一部分，我宁愿等待计算机解决问题，也不愿花时间加速那些我不经常需要运行的任务。</p><p>但是，将 Python 转换为 C 主要是简单程序的技术过程，而且有一个标准的 Python 到 C 的调用约定。所以，我直接请求 LLM 来帮我完成。</p><p>从那以后，我开始期待这是我可以做到的事情，几乎在任何时候，当我需要一些高速的代码时，我会用 Python 描述我想要的内容，并请求生成优化的 C 代码。其他时候，我做同样的事情，但如果我认为比较 Rust 输出和 C 输出的正确性更容易的话，我会请求 Rust 输出。或者，作为另一个例子，用 multiprocessing 库并行化 Python 函数并不困难。你需要编写一些样板代码，基本上它就会为你完成。但编写代码有点痛苦，会妨碍你想要完成的实际工作。现在，每当我需要做这个时，我只会请求一个 LLM 来帮我。还有很多时候，当我尝试测试某个 API 时，我最初会编写一个 curl 请求来开始。一旦它开始工作，我想以编程方式重复任务，我就会将它转换为 Python。以前，我通常会做些非常丑陋的事情，直接调用 os.Popen () 运行 curl 命令，但这并不理想。更好的方法是将其转换为 Python 的 requests 库；但这需要时间，所以我不会这么做。但现在我可以简单地请求一个 LLM 帮我完成，并在更短的时间内获得一个更干净的程序。对于即将到来的一个项目，我可能会在这里讨论，我需要了解人们通常用作简单无线电发射器的是哪些东西。因为我真正想要的是大多数人的答案，LLM 是一个完美的选择！</p><h3>12、修复常见错误</h3><p><br></p><p>在 2022 年之前，当我遇到某些流行工具或库的错误信息时，我通常会采取以下步骤：</p><ol><li>复制错误信息。</li><li>将其粘贴到 Google 搜索。</li><li>点击最顶部的 Stack Overflow 链接。</li><li>确认问题是否与我遇到的一致；如果不是，返回第 2 步。</li><li>如果无效，返回第 2 步，更换搜索词，祈祷等。</li></ol><p>因为，坦白说，通常出问题的工具与我最终想要解决的任务差距很大，我其实并不太关心如何使其工作，我只需要它能工作。2024 年现在这个流程是怎样的？</p><ol><li>复制错误信息。</li><li>向 LLM 询问：“我该如何修复这个错误？（错误）”</li><li>如果无效，反馈 “那不起作用”。</li></ol><p>我没有任何对话记录来展示这些例子。（或者说，我找了一个小时也没有找到。）但这实际上有一个很好的原因：我已经将它整合进我的工作流程中了。</p><p>我是一个 Emacs 用户。我设置了我的环境，每当我运行一个程序并且它以非零状态码结束时（意味着有错误发生），它会自动调用最新最快的 LLM，并要求它解释答案，同时请求一个可以直接应用来修复代码中 bug 的补丁。</p><p>现今的模型大多数情况下还不足以在这项任务上胜过我，但它们正在逐渐进步。偶尔，当 LLM 修复了一个我知道如果自己追踪可能非常困难的错误时，我会感到非常惊喜，尤其是当错误只是因为某处的小笔误。</p><h3>13、以及无数其他事情</h3><p><br></p><p>我上面提到的所有对话只占我过去一年与 LLMs 互动总数的不到 2%。我没有提供其他链接的原因并不是因为这些是模型失败的案例（尽管有很多这样的案例），而是因为：（1）许多互动重复了我已经提到的那些模式，或（2）它们不容易解释清楚发生了什么，也不容易让你自己看到它们为何对我有用。</p><p>我完全预期未来我使用这些模型的频率会持续增长。作为一个参考，我在 2024 年通过网页界面进行的 LLM 查询比 2023 年增加了 30%——而且我甚至无法计算 API 查询的增加，但我猜测至少增加了 2 到 3 倍。</p><h2>评价 LLMs 的能力，而非它们的局限</h2><p><br></p><p>我在面试求职者时获得的最佳建议之一是，根据他们能做什么而不是不能做什么来评价他们。</p><p>我怀疑我可以问你一些简单的问题，这可能会让你显得不够称职。比如极端的例子：世界上有十亿人讲普通话；我连数到十都做不到。如果有人给我一份小学水平的普通话考试，我肯定会考得很糟糕。</p><p>即便在计算机科学领域内，也有我完全不了解的领域。我关于如何构建 SQL 的知识仅限于如何编写有效的 SELECT 语句。这——字面上——是我唯一知道如何编写的语句。</p><p>因此，当我看到人们在网络上争论说 LLMs 只是炒作，因为 “它们甚至不能做 XXX” 时，我真的感到困惑。这里的 XX 可能是：</p><ul><li>… 计算句子中的单词数！</li><li>… 写一首每个单词都以字母 “a” 开头的诗</li><li>… 乘以两位数</li><li>… 从列表中随机选择一个元素</li></ul><p>因为你上次真正需要做这些事情，并且真诚地认为 LLM 是合适的工具是什么时候？</p><p>我不会因为我们不能在头脑中分割 64 位整数而认为人类完全无用——这对计算机来说是极其简单的任务——我也不认为因为你可以构造出 LLMs 无法解决的任务就应该抛弃 LLMs。显然这很容易——关键是你能找到它们能够提供价值的任务吗？</p><p>程序员已经很清楚，某些事物对不同的目的可能有用。想要编写操作系统？也许你应该使用 C 而不是 Python。没有人会说 “看 Python 多么愚蠢，你甚至不能强制变量对齐到 32 字节边界！” 这只是在错误的抽象级别上。语言模型也是一样。它们在非常高的抽象级别上操作；你不能期待它们解决甚至最简单的程序都能解决的任务。但你可以期望它们解决不同种类的任务。</p><h2>结论</h2><p><br></p><p>撰写这篇文章的初衷有两个。首先，正如我在文章开头所述，我想证明 LLMs 已经为我提供了大量的价值。此外，我注意到很多人表示喜欢使用 LLMs 的想法，但不知道它们能如何帮助自己。因此，如果你是这些人之一，希望通过我的使用案例能看到一些示例。</p><p>因为至少对我来说，LLMs 能做很多事。它们不能做所有事情，甚至不能做大多数事情。但当前的模型，正如它们现在存在的样子，为我提供了可观的价值。</p><p>在展示这些示例后，我经常收到的一种反驳是：“但这些任务很简单！任何计算机科学的本科生都能学会如何做到！” 确实，本科生可以通过几小时的搜索来告诉我如何正确地诊断 CUDA 错误以及可以重新安装哪些包。本科生可以通过几小时的工作重写那个程序为 C 语言。本科生可以通过几小时的学习相关教科书来教我任何我想知道的关于那个主题的内容。不幸的是，我没有一个随时可以回答我任何问题的神奇本科生。但我有语言模型。所以，虽然语言模型还不够好，不能解决我作为程序员工作中的有趣部分，而且当前模型只能解决简单的任务。</p><p>但五年前，LLM 最好的能做的就是写出看起来像英语的段落。当它们能从一个句子到下一个形成连贯的思想时，我们都感到惊讶。它们的实际用途几乎为零。然而，今天，它们已经提高了我在项目中编程方面的生产力至少 50%，并消除了足够多的繁琐工作，让我能够创建一些我否则永远不会尝试的东西。</p><p>因此，当人们说 “LLMs 只是炒作” 和“所有 LLM 都没有为任何人提供实际价值”时，很明显他们是错误的，因为它们为我提供了价值。现在，也许我是个例外。也许我是唯一找到了让这些模型有用的方法的人。</p><p>我只能代表我自己。</p><p><strong>但考虑到 LLMs 显著提高了我的生产力——一个在使用 LLM 之前已经有 20 年编程经验的人——我相信还有其他人也能从使用 AI 中受益。</strong></p><p><strong>原文链接：</strong></p><p><a href=\"https://nicholas.carlini.com/writing/2024/how-i-use-ai.html#intro\" rel=\"noopener noreferrer\" target=\"_blank\">https://nicholas.carlini.com/writing/2024/how-i-use-ai.html#intro</a></p>","content_text":"在当今科技界, 关于人工智能是否被过度炒作的争论从未停息。然而, 很少有像谷歌 DeepMind 的安全研究专家和机器学习科学家 Nicholas\nCarlini 这样的专家，用亲身经历为我们提供了一个独特的视角。通过他的文章, 我们看到了大型语言模型 (LLM)\n在实际应用中的强大能力和多样性。这些并非空洞的营销宣传, 而是切实可以改变工作方式、提高生产效率、激发创意的工具。\n\n最近, Carlini 本人亲自撰写了一篇长达万字的文章, 详细介绍了他如何在日常生活和工作中使用 AI。这篇洞察力十足的文章不仅展示了 AI 技术的广泛应用,\n更让我们得以一窥未来科技将如何重塑我们的生活方式。\n\n[https://pic.ccino.win/OB/2024/10/202410152117121.png]\n\nCarlini 在文章中列举了 50 个他亲身使用 AI 的实例, 涵盖了工作效率、创意写作、编程辅助等多个方面。然而, 令人惊讶的是, 这 50\n个例子仅仅是他所有 AI 应用场景的冰山一角 - 据他估计, 不到他实际使用情况的 2%。这一变革令人震撼, 它不仅体现了 AI 技术的深度和广度,\n更凸显了我们可能还未充分认识到 AI 的潜力。\n\n事实上, Carlini 的经历可能预示着一个更广泛的趋势: 随着 AI 技术的不断进步和普及,\n我们可能正站在一个技术革命的风口浪尖。就像个人电脑和互联网彻底改变了我们的生活和工作方式一样, AI 可能会成为下一个推动社会变革的关键力量。\n\n那么, 面对这样的前景, 我们应该如何看待 AI 技术的发展呢? 是抱有审慎的态度, 还是积极拥抱变革?\n\n\n原文如下：\n\n\n\n\n作者：DeepMind 安全研究专家和机器学习科学家 Nicholas Carlini\n\n我不认为 “人工智能” 模型（我指的是大型语言模型）被过度炒作了。\n\n确实，任何新技术都会吸引骗子。许多公司喜欢说他们在 “使用人工智能”，就像他们以前声称自己依靠“区块链”\n一样。（正如我们一次又一次看到的那样）同样，我们可能正处于一个泡沫中。互联网在 2000 年泡沫破裂，但我们现在拥有的互联网应用，曾是科幻小说中的东西。\n\n但我认为最近我们取得的进展不仅仅是炒作的原因是，过去一年中，我每周至少花几个小时与各种大型语言模型互动，并且对它们解决我给出的越来越困难的任务的能力印象深刻。因此，我可以说，这些模型使我在编写代码方面的速度提高了至少\n50%，无论是研究项目还是个人的编程副业。\n\n我在网上找到的大多数谈论 LLM 实用性的人要么过于乐观，声称所有工作将在三年内自动化，要么极度悲观，认为它们没有任何贡献，永远也不会有。\n\n所以在这篇文章中，我只是想让讨论更加实际。我不会对未来进行任何论断。我只想列出一个列表，这是我（一名研究机器学习的程序员和研究科学家）与不同的大型语言模型进行的\n50 次对话，这些对话显著提高了我进行研究的能力，并帮助我从事随机的编程副项目。其中包括：\n\n * 使用我之前从未使用过的技术构建整个网络应用程序。\n * 教我如何使用各种框架，尽管我之前从未使用过它们。\n * 将几十个程序转换为 C 或 Rust 以提高 10-100 倍的性能。\n * 精简大型代码库，显著简化项目。\n * 为我在过去一年中撰写的几乎每一篇研究论文编写初始实验代码。\n * 自动化几乎所有单调的任务或一次性脚本。\n * 几乎完全取代了帮助我设置和配置新软件包或项目的网络搜索。\n * 在帮助我调试错误信息方面，网络搜索的替代率约为 50%。\n\n如果对这些应用实例进行分类，它们大致可以分为 “辅助学习”\n和“自动化日常琐事”两类。前者对我来说至关重要，因为它让我能够轻松应对之前觉得有难度的任务；后者同样重要，因为它使我能集中精力去做我最擅长的事，解决真正的难题。\n\n关键是，这些例子展示的是我如何实际利用大型语言模型（LLMs）的。它们并不旨在炫耀技术的惊人能力，而是基于我解决工作需求的实际情况。这意味着，这些例子或许看起来不够引人注目，但实际上，我日常工作的很大一部分也都是平凡无奇的，而现今可用的\nLLMs 几乎帮我自动化了所有这些工作。\n\n通过这篇文章，我的目的是用一个接一个的实例，向你展示我在过去一年如何有效利用 LLMs\n提升工作效率，直到你感到有些疲惫为止。要知道，尽管你可能已经看过不少例子，但实际上我所展示的，还不到我利用 LLMs 的总数的 2%。\n\n\n细微差别\n\n\n\n\n如果说互联网处理得不尽如人意的是什么，那一定是对细节的把握。我绝不认为现今的大型语言模型（LLMs）能够主宰世界，也不打算讨论未来的模型能做什么，不能做什么。我只想探讨，现阶段的模型对我是否真的有用。\n\n你可能会问，为什么还需要一篇文章来证明语言模型的用途呢？这难道不是显而易见的事实吗？但事实上，无论是在学术界、软件工程领域还是媒体行业，都有不少人声称 LLMs\n毫无用处，只是一时的炒作，几年后将默默无闻，对世界没有任何影响。我要反驳这些观点，因为目前的 LLMs 已经证明了它们的实用性。\n\n但同时，我也需要说明，另有一批声音同样强烈的人士持相反观点，他们认为现有模型能够取代所有程序员，人们不应再学习编程，因为不久后大家都将面临失业。虽然反驳这些观点不是本文的主旨，但我必须明确，我并不支持这种极端的看法。\n\n此外，我也不会主张 “目的正当化手段”，即便这些模型的训练确实存在诸多负面影响，我不认为应当因此而推广使用。\n\n我充分意识到这些模型可能带来的负面后果，这些后果可能非常严重，包括但不限于散布错误信息、滥用、监控以及取代工作岗位（甚至有人担忧到人类灭绝的地步）。我将在不久的将来撰写一篇文章，深入探讨\nLLMs 可能引发的危害，届时会在此放上链接。然而，这与语言模型是否有用的讨论是两码事——这正是我此处想要探讨的问题。\n\n我也清楚你可能因为语言模型偏向于生成不准确的信息、重复已知事实、在面对复杂情况时可能彻底失败的倾向而不愿使用它们——可能我对这些局限性的了解比你更深。但本文不会讨论这些。因为我认为，尽管有这些不足，模型仍然是有用的。\n\n此外，我也深知培训这些模型所涉及的伦理问题极具争议。你可能不赞成在未经许可的情况下使用人们的数据来训练它们（我对此的理解可能比你更深刻）。或许你关注的是那些为直接训练这些模型而获得微薄报酬的人。我承认这些都是问题，但本文也不打算讨论这些。\n\n正如我已经多次强调的：我在这里讨论的，仅仅是这些模型在当前状态下是否实际有用。\n\n\n我的背景简介\n\n\n\n\n通常，我不是那种轻易相信任何事物的人。比如，尽管我经历了十年前信息安全界的加密货币热潮，我却从未参与撰写任何关于区块链的研究论文。我也从未拥有过任何比特币，因为在我看来，它们除了用于赌博和欺诈之外，没有任何实际价值。我一直持怀疑态度，每当有人向我宣称\n“某项新技术将改变世界”，我的反应总是冷漠。\n\n因此，当第一次有人告诉我人工智能将极大地提升我的工作效率并改变我的日常工作方式时，我同样持保留态度，我的回应是：“见到实际效果我才会信。”\n\n此外，我是一名安全研究员。在过去近十年的工作中，我专注于展示人工智能模型在面对未曾训练过的各种环境时如何彻底失败。我证明了只需对机器学习模型的输入进行轻微扰动，就能使其输出完全错误的结果；或者这些模型往往只是记住了它们训练数据中的特定案例，并在实际应用时简单重复。我深知这些系统的种种局限。\n\n然而，现在我却在这里说，我认为当前的大型语言模型是自互联网问世以来，给我的生产力带来的最大提升。坦白说，如果今天要我在使用互联网和一个最先进的语言模型之间选择一种工具来解决工作中随机一个编程任务，我可能会选择使用语言模型的次数超过一半。\n\n\n如何利用语言模型\n\n\n\n\n以下是我如何利用大型语言模型（LLMs）来提升工作效率。\n\n你可能对我所述的使用案例不感兴趣，甚至认为它们有些荒谬。也可能这些案例与你的需求不相关，这一点我也能理解。但我只能从个人角度出发。这些使用实例都是我在过去一年里与\nLLMs 的交流记录中直接摘录的。\n\n\n1、为我开发完整应用\n\n\n\n\n去年，我创建了一个测验，让人们评估 GPT-4 在处理几个特定任务上的表现。这个测验非常受欢迎，获得了超过一千万的页面浏览量。你可能想不到，我几乎是让\nGPT-4 为我编写了这个应用的全部初始版本。这一过程是通过一连串的问题进行的，我从询问应用的基本架构开始，然后逐步扩展其功能。整个对话长达 30,000\n字，极大地测试了当时的 GPT-4 模型的能力极限。如果你翻看这些对话，会发现从我描述需求并请求模型完成全部实现的信息，到我请求做出具体修改的信息（如\n“不要比较平均分数，而是用核密度估计表示它是哪个百分位”），以及我提出一些完全不明确的问题时复制粘贴的错误信息（例如，“绘图错误：numpy. Linalg.\nLinAlgError: singular matrix”），还有我仅仅询问简单单次问题的情况（比如 “如何用 JavaScript 在页面上添加一个\niframe，内容是从字符串加载的？”）。\n\n这种方法之所以有效，主要是因为语言模型擅长处理人们已经解决的问题，而这个测验的 99% 内容只不过是一些基础的 HTML 和 Python\n后端服务器，这是任何人都可以编写的。这个测验之所以引人关注并受到喜爱，并非因为其背后的技术，而是因为测验内容本身。通过自动化所有重复性的部分，我能轻松地完成这个项目。\n\n实际上，如果没有语言模型的帮助，我可能根本就不会去创建这个测验，因为我不愿意花时间从头编写整个网页应用。尽管我擅长编程！我相信，即使是现有的模型，也足以让大多数人仅凭提问就能解决他们以前无法解决的重要任务。\n\n我还有一些类似的例子以后介绍，其中我让模型为我编写了整个应用，当这些应用发布时，我会明确指出它们是在语言模型的协助下完成的。\n\n\n2、作为新技术的向导\n\n\n\n\n我曾经总能赶上各种新兴框架的步伐。但是一个人的时间毕竟有限，因为我的职业特性，我大多数时间都在跟进最新的研究进展，而不是最新的 JavaScript 框架。\n\n这意味着当我开始一个不属于我研究领域的新项目时，我通常有两个选择：一是利用我已知的技术，虽然这些技术可能已经过时十年或二十年，但对于小项目来说通常足够了；二是尝试学习新的（通常也是更好的）方法。\n\n这就是语言模型派上用场的时候。对我而言较新的框架或工具，比如 Docker、Flexbox 或\nReact，对其他人来说可能已经相当熟悉了。世界上可能有成千上万的人已经非常了解这些技术。当前的语言模型也能做到这一点。\n\n这意味着，我可以通过与语言模型的交互式学习，来掌握解决任务所需的任何知识，而不必依赖那些假设读者具备特定知识、目标明确的静态教程。\n\n比如，今年早些时候，我在构建一个 LLM 评估框架时，希望能在一个受限环境中运行由 LLM 生成的代码，以避免它删除我的计算机上的随机文件等问题。Docker\n是完成这一任务的理想工具，但我之前从未使用过。\n\n重要的是，这个项目的目标并非是要学会使用 Docker，Docker 只是我实现目标所需的工具。我只需要掌握 Docker 的\n10%，确保我能以最基本的方式安全使用它。\n\n如果是在 90 年代，我可能需要购买一本关于 Docker\n的书籍，从头开始学习，阅读前几章，然后试图跳读以找出如何实现我想要的功能。过去十年情况有所改善，我可能会在线搜索如何使用 Docker\n的教程，跟着操作，然后搜索遇到的错误信息，看是否有人遇到相同的问题。\n\n但在今天，我只需要请一个语言模型教我使用 Docker。\n\n\n3、开始新项目\n\n\n\n\n回想起来，我最初接触的编程语言是 Java。我非常喜欢编程，但有一件事我绝对讨厌：面对新项目的空白屏幕。特别是在使用 Java 时！甚至仅仅是让程序编译一个\nhello world 程序——这个 “public static void main string args”\n到底是做什么的？括号应该怎么放？哪些字母应该大写？为什么这里用花括号，那里用方括号？\n\n所以我做了任何孩子都会做的事——我让我父亲帮我做了。\n\n二十年过去了，我仍然不喜欢使用我不熟悉的框架开始新项目。仅仅是为了搞定样板代码就需要花费大量时间，而且我对我在做的事情毫无头绪。\n\n例如，我最近想尝试编写一些 CUDA 代码，来评估一种简单的贪婪搜索在 GPU 上的性能与某人的高效优化的 CPU 实现相比。\n\n但我不懂 CUDA 编程。我会写 C 语言，了解 GPU 的工作原理、内核的功能以及内存布局等，但实际编写向 GPU\n发送任务的代码？我不知道该从哪里开始。因此，我直接请求模型为我编写 CUDA\n程序的初稿。完美吗？当然不是！但这是一个起点。这正是我需要的。你会注意到这里的代码有很多错误！实际上，我完全可以接受。我不是在寻找完美的解决方案，而是一个起点，我可以从那里继续。如果未来的模型能做得更好，那将是惊人的。但我现在拥有的已经大有帮助了。\n\n另一方面，对于我在家进行的一些其他个人项目，我正在使用一个树莓派 Pico\nW。这是我第一次使用它。我希望它能为我做一些事情，特别是一些网络相关的事情。再次，我确信我可以在网上找到有人描述如何做我想做的事情的好教程。但你最近看过互联网吗？前\n5 个结果通常只是一些垃圾内容农场，它们有 2008 年的有缺陷的代码，仅为了搜索引擎优化而更新，但仍然不起作用。\n\n因此，我直接请求一个语言模型教我如何做我想做的事情。我之前已经和微控制器打过交道，所以我或多或少了解它们的工作方式。但我之前从未使用过 Pico\nW。我只需要一些帮助来开始处理所有的依赖关系，然后我可以弄清楚剩下的部分。\n\n我为新的微控制器编写的第一个 “hello world” 程序总是让一个 LED\n闪烁。这可以让我测试我是否能够编译并上传代码到设备，是否正确设置了所有的引脚，并且基本上知道我在做什么。所以，让我们只是请求一个闪烁程序。（再次：这在互联网上存在吗？几乎可以肯定。但那样我就得去搜索它了。）一旦我有了这段代码并且运行起来，从这里我就知道该怎么做了。我知道\npython 是如何工作的（信不信由你！）。因此，我可以从那里直接继续编辑东西，把特殊的 Micro Python 的东西处理掉。\n\n当我遇到另一个需要我特别处理的问题时，我可以再次请求模型帮助我。例如，我接着只是让模型为我编写一个连接到 wifi 的脚本。\n\n\n4、代码简化\n\n\n\n\n作为安全研究员，我经常需要处理别人的研究项目，这些项目包含数千行代码，我必须弄懂它们的工作原理以便进行攻击。这听起来并不困难，如果每个人都编写清晰的代码，实际上也确实不该困难，但现实世界并非如此。研究人员通常没有动力发布整洁的代码。所以，人们往往会发布他们能用的任何杂乱代码。（我自己也不例外。）\n\n我无法分享与研究相关的例子，但我可以分享我正在进行的一个个人项目的例子。据说我对康威的生命游戏有种不健康的痴迷。最近，我试图找到一种快速的方法，通过\nPython 评估一些生命游戏模式。有一个很好的 C++ 工具叫 golly 可以做这个，但我不想将我的 Python 代码重写为 C++。\n\nGolly 有一个 CLI 工具正好满足我的需求——我所需要的只是一种正确调用它的方法。这的第一步是简化支持约 50 种不同命令行选项的 C++\n代码，让它只执行我想要的操作。所以我将所有 500 行的 C++ 代码输入到 LLM 中，请求一个更简短的文件来完成相同的任务。\n\n你知道吗？这完美地奏效了。然后，我请求一个围绕 C++ 代码的 Python\n封装器。这同样有效。这是那些令人烦恼的任务之一，如果由我来做，我可能永远不会完成。但现在我可以请求别人帮我完成，我得到的东西比我原来的 Python 代码快了\n100 倍。\n\n我发现自己经常这样做。这里还有一个例子，我在用 Python 做同样的事情。\n\n再说一次，这些任务并不复杂。但每次这样做，我都能节省大量时间。这就是我认为大型语言模型如今惊人的原因之一：它们的用途可能不够光鲜，也不会因为说\n“这是我用大型语言模型简化日常工作的平凡方式” 而赢得网络赞誉，但这是实实在在的帮助。\n\n\n5、处理单调任务\n\n\n\n\n我必须完成的许多任务都是单调无聊的，不需要太多思考，但却必须做。\n\n事实上，我发现自己之所以会拖延任务，往往是因为我知道完成这些任务会感到无聊和痛苦。LLMs\n极大地减轻了这种痛苦，让我在开始任务时就知道自己只需要解决有趣的问题。因此，我想逐一介绍一些通过请求 LLMs 帮忙而解决的非常普通的问题。\n\n例如，最近我需要拆解一个用 Python 3.9 编写的程序。大多数 Python 反编译器只支持到 Python 3.7，但无法在我处理的 3.9\n版本上运行。\n\n反编译实际上并不难，主要是在重建控制流程时避免出错。因此，我没有亲自花时间为几千个操作码和几百行代码进行这种转换，而是让 LLM\n帮我做了。它做得非常好！效果远超我的预期。这里有三个不同的对话，在这些对话中我让模型帮我完成了这项工作。另一个例子是，当我需要将一些非结构化数据转换成结构化格式时。比如，我在做一个项目，需要列出一些书名和作者名。我在网上找到了一些非结构化格式的数据，然后让\nLLM 帮我格式化。最近，我在写一篇关于如何破解某个防护的博客文章，想展示我不得不修改的代码完整差异。于是我粘贴了差异和之前如何将差异转换为 HTML\n的示例，并让 LLM\n按照之前的格式为我生成差异。此外，作为我的工作的一部分，我经常需要为我使用的资源生成引用。谷歌学术很容易引用论文，我可以直接复制粘贴。但引用网页稍显麻烦；我最近开始请求\nLLM 帮我生成引用。（确保这是正确的！）我可以继续举出至少一百个类似的例子。但我想你已经明白了我的意思。\n\n我完全明白这种任务可能会让人觉得 “就这？” 但我们要记住，五年前这些模型几乎不能连贯地写出一个段落，更别说为你解决整个问题了。\n\n\n6、使每个用户都成为 “高级用户”\n\n\n\n\n如果你曾经看过比你不那么熟练的人使用某个工具，这可能会有点让人难受。他们可能会花费几分钟甚至几小时的时间去完成一个本可以通过某种宏或在处理特定任务时巧妙使用并行应用程序来自动化的任务。\n\n然而，学习执行这些操作所需的技巧需要时间，并且具有挑战性。\n\n例如，我最近试图编写一个 Python 程序来处理来自 Apple Lisa 键盘的输入。我在线找到了一个人用 C 语言编写的相关代码，其中包含许多像\n#define KEYNAME key_code 这样的语句，我想将它们转换为一个 Python 字典，将整数代码映射到对应的字符串。\n\n我是一个 Emacs 用户。我知道如何在 Emacs 中解决这个问题，这甚至并不难。这是我刚记录的一些关键操作，可以达到这个效果：\n\nC-h C-s #def [enter] M-f [delete] C-d M-f C-[space] M-f C-w C-a C-y : “M-f”, C-g\nC-] } C-[ {\n\n尽管这对我几乎是自然的，但到目前为止，我已经花了超过一半的生活时间在 Emacs 上变得足够熟练，以至于这成为了自然反应。但你知道现在我连接了 LLM\n到我的编辑器，我会怎么做吗？C-h C-h 请将这些 #define 重写为 {keycode: string, …} 的字典格式。\n\n然后，文本就在我眼前被重写了！\n\n正是在这样的情况下，我认为 LLMs 对非专家的潜在效用甚至高于专家。这个模型为每个人提高了起点，如果你之前完全不会做，现在突然能做很多事情。\n\n\n7、作为 API 参考\n\n\n\n\n真正的程序员想要理解某个工具的工作原理时会阅读参考手册。但我是个懒惰的程序员；我更愿意直接得到答案。因此，现在我向语言模型提问。\n\n当我向人们展示这些例子时，有些人会变得有些防御性，他们说：“LLM 没有做任何你不能用已有工具完成的事情！”\n你知道吗？他们说得对。但是，用搜索引擎能做的事，用一本关于该主题的实体书也能做；用一本实体书能做的事，通过阅读源代码也能做。\n\n然而，每一种方式比前一种都简单。当事情变得更简单时，你会更频繁地做，而且方式上也会有所不同。\n\n这就是我问 “哪个 $ 命令可以传递所有剩余参数” 并获得答案的例子。（紧接着是另一个 “我该如何使用这个东西” 的问题！）这实际上是我最常用 LLMs\n的方法之一。我之所以不能给你展示更多这样的例子，是因为我在 Emacs 和我的 shell 中都内置了查询 LLMs 的工具。因此，当我想要做这些事情的 90%\n的时间，我甚至不需要离开我的编辑器。\n\n\n8、搜索难以找到的内容\n\n\n\n\n在互联网上搜索内容曾经是一项需要学习的技能。你想在查询中包含哪些特定的词汇？它们应该是复数还是单数？过去时？你希望避免在页面上出现哪些词汇？我是想要 X 和\nY，还是 X 或 Y？\n\n现在情况已经不同了。我想不起上次我在 Google 中使用 OR 的时间。我也想不起上次我使用减号 (-)\n来移除结果子集的时间。在大多数情况下，今天你只需要写下你想要找的内容，搜索引擎就会为你找到。\n\n但搜索引擎仍然不是 100%\n的自然语言查询。它仍然有点像你在玩反向危险边缘游戏，试图使用答案中会有的关键词而不是问题。这是一项我认为我们几乎都忘记了我们学过的技能。\n\n对于今天的一些简单任务（随着时间的推移会越来越多），语言模型只是更好。我可以直接输入 “所以我知道 +\n对应于__add__，但是是什么”，它会告诉我答案是__inv__。这是用标准搜索引擎很难搜索到的东西。是的，我知道有方法可以问，这样我就能找到答案。可能如果我输入\n“python 文档元类 “add””，我可以搜索页面上的并得到答案。但你知道还有什么有效吗？只要问 LLM 你的问题。\n\n这样做任何一次只节省几十秒的时间，但当你在解决某个编码任务的过程中，已经试图同时记住一百万件事时，能够将你试图解决的问题倾倒出来并得到一个连贯的答案是令人惊讶的。\n\n这并不是说他们今天在这方面已经完美。语言模型只有在线上被足够频繁地重复时才知道事情。“足够频繁”\n是什么意思取决于模型，所以我确实需要花一些精力思考我是应该询问模型还是询问互联网。但模型只会变得更好。\n\n或者，每当我遇到随机崩溃时，我会将模型转储我所看到的并要求解释，就像我在这里做的那样，当我只是输入 zsh 没有找到匹配的\n“远程通配符传输问题”。或者，作为一个完全独立的例子，我去年在写一篇博客文章时，想要第一个字母大写，让其余的文字环绕它，就像我在这句话中所做的那样。现在这被称为下沉式大写字母。但我不知道这个。我只知道我想要的效果，所以问语言模型“我想让它看起来像一本华丽的书，文字围绕\nO”，它给了我我想要的东西：这个任务是另一个属于“我只是因为 LLMs 才做的”\n类别——我不会认为花大量时间弄清楚如何做是值得的。但因为我可以直接问模型，我就这样做了，它让我的帖子看起来更好一些。\n\n\n9、解决一次性任务\n\n\n\n\n有两种类型的程序。首先，你有一些你想要正确完成的程序；它们将会存在一段时间，因为你需要维护它们好几年，所以代码的清晰性很重要。然后，你有那些只存在大约 25\n秒的程序；它们将帮助你完成某些任务，然后立即被丢弃。\n\n在这些情况下，我根本不关心代码的质量，而且程序是完全独立的，我现在几乎专门使用 LLMs 来为我编写这些程序。\n\n请注意：大多数这些情况再次出现，你会看着它们说\n“就这？”。但就像我之前说的，我每天只有那么多小时来处理一个项目。如果我能节省编写一次性使用的程序的时间和精力，我会选择这样做。\n\n可能最常见的例子是帮助我生成一些图表，以可视化我作为某些研究实验结果生成的一些数据。我有几十个这样的例子。可能接近一百而不是零。它们看起来基本上都一样，这里只是一个例子：或者，另一个类似的例子，当我有一种格式的数据并想将其转换为另一种格式的数据时。通常这是我只需要做一次的事情，一旦完成，我就会扔掉生成的脚本。但我还可以给你举出一千个其他例子。通常情况下，当我有一个足够简单的脚本想要编写时，我会直接请求\nLLM 整体编写。例如，这里我请求 LLM\n为我编写一个脚本，让它大声读出我的论文，这样我可以确保它们没有愚蠢的语法问题。在许多情况下，当我不太清楚我想要什么时，我也会从请求模型提供一些初始代码开始，然后从那里进行迭代。例如，这里有一个一次性任务，我只是需要快速处理一些数据。在\n2022 年，我会花两分钟用 python 编写这个，然后等待几个小时因为它只运行一次——优化它所需的时间会比 python\n程序运行的时间还要长。但现在呢？你敢打赌我会花同样的两分钟请求用 Rust\n代码为我处理数据。或者这里是另一个例子，我请求模型为我下载一个数据集，并对其进行一些初始处理。这对我来说容易做吗？可能是。但这不是我想要考虑的任务；我想要考虑的是我将要用数据集做的研究。消除分心非常有价值，不仅仅是节省几分钟的时间。\n\n另一次，我编写了一个程序，这样我就可以用小立方体 3 D 打印一些像素化的图像。为此，我想将 PNG 转换为 STL\n文件；但这不是项目的重点。这只是沿途必须发生的事情。所以我请求 LLM 为我解决这个问题。或者，作为另一个例子，我最近想使用 Docker Compose\n来设置一个新项目。我遇到了一些问题，只想让它运行起来，然后我会弄清楚出了什么问题。所以我只是来回几次，我所做的就是复制一个又一个错误消息，直到它最终给我一个有效的解决方案。我还会发现自己在很多情况下首先请求一个完整的解决方案，然后请求如何修改它的提示。在这次对话中，我首先请求一个解析\nHTML 的程序，然后请求 API 参考或其他改进方式的提示。最近我一直在尝试做一些电子产品相关的事情，我有一个在 Arduino 上运行的 C\n程序，但我希望它在树莓派 Pico 上以 MicroPython\n运行。这个转换过程没有什么有趣的；它只需要完成。所以我没有亲自完成工作，只是请语言模型为我做。对于另一个项目，我需要用一些花哨的 ML\n模型在一些交互循环中分类一些图像。我本可以自己编写，或者我可以直接请求模型为我做。\n\n\n10、解释事物给我听\n\n\n\n\n我最近开始对电子学产生了兴趣。我年轻的时候做过一些电子项目，并在大学期间上过几门相关课程。但现在我想进行实际的电子项目，我发现有许多我不了解的细节，这让开始任何项目都变得困难。\n\n我可以去读一本关于实用电子学的书。我可能真的会在某个时候这么做，以便彻底理解这个主题。但我并不想把我的时间花在感觉自己在学习上。我从事电子学的部分原因是想从整天的阅读和写作中抽身而出。\n\n这就是 LLMs\n发挥出色的地方。它们可能不如世界上最出色的专家那样知识渊博，但成千上万的人可能知道我可能会提出的任何电子问题的答案。这意味着语言模型很可能也知道答案。它乐于为我提供所有问题的答案，使我可以享受乐趣而不必纠结于细节。虽然我完全可以通过在互联网上搜索来找到答案，但在忙碌一整天后，简单地让模型为我完成这些工作的便利性使我感到非常放松。\n\n这里是一些例子，展示了我如何询问语言模型有关电子学中事物工作原理的基本问题。这些答案完美吗？谁知道呢。但你知道它们比什么更好吗？比我什么都不知道要好。\n\n\n11、解决具有已知解决方案的任务\n\n\n\n\n几乎所有事情都已经有人做过了。你想做的事情几乎没有什么是真正新颖的。语言模型特别擅长提供它们之前见过的事物的解决方案。\n\n在最近的一个项目中，我需要提升一些 Python 代码的性能。因此，我（1）请求 LLM 将其重写为 C 语言，然后（2）请求它构建一个接口，以便我能从\nPython 调用 C 代码。\n\n这些任务都不是 “难” 的。将 Python 转换为 C 是我确信自己能在一两小时内完成的。尽管我不完全知道 Python 到 C 的 API\n如何工作，但我相信我可以通过阅读文档来了解。但如果需要我自己来做，我永远不会去做。它不是关键路径上的一部分，我宁愿等待计算机解决问题，也不愿花时间加速那些我不经常需要运行的任务。\n\n但是，将 Python 转换为 C 主要是简单程序的技术过程，而且有一个标准的 Python 到 C 的调用约定。所以，我直接请求 LLM 来帮我完成。\n\n从那以后，我开始期待这是我可以做到的事情，几乎在任何时候，当我需要一些高速的代码时，我会用 Python 描述我想要的内容，并请求生成优化的 C\n代码。其他时候，我做同样的事情，但如果我认为比较 Rust 输出和 C 输出的正确性更容易的话，我会请求 Rust 输出。或者，作为另一个例子，用\nmultiprocessing 库并行化 Python\n函数并不困难。你需要编写一些样板代码，基本上它就会为你完成。但编写代码有点痛苦，会妨碍你想要完成的实际工作。现在，每当我需要做这个时，我只会请求一个 LLM\n来帮我。还有很多时候，当我尝试测试某个 API 时，我最初会编写一个 curl 请求来开始。一旦它开始工作，我想以编程方式重复任务，我就会将它转换为\nPython。以前，我通常会做些非常丑陋的事情，直接调用 os.Popen () 运行 curl 命令，但这并不理想。更好的方法是将其转换为 Python 的\nrequests 库；但这需要时间，所以我不会这么做。但现在我可以简单地请求一个 LLM\n帮我完成，并在更短的时间内获得一个更干净的程序。对于即将到来的一个项目，我可能会在这里讨论，我需要了解人们通常用作简单无线电发射器的是哪些东西。因为我真正想要的是大多数人的答案，LLM\n是一个完美的选择！\n\n\n12、修复常见错误\n\n\n\n\n在 2022 年之前，当我遇到某些流行工具或库的错误信息时，我通常会采取以下步骤：\n\n 1. 复制错误信息。\n 2. 将其粘贴到 Google 搜索。\n 3. 点击最顶部的 Stack Overflow 链接。\n 4. 确认问题是否与我遇到的一致；如果不是，返回第 2 步。\n 5. 如果无效，返回第 2 步，更换搜索词，祈祷等。\n\n因为，坦白说，通常出问题的工具与我最终想要解决的任务差距很大，我其实并不太关心如何使其工作，我只需要它能工作。2024 年现在这个流程是怎样的？\n\n 1. 复制错误信息。\n 2. 向 LLM 询问：“我该如何修复这个错误？（错误）”\n 3. 如果无效，反馈 “那不起作用”。\n\n我没有任何对话记录来展示这些例子。（或者说，我找了一个小时也没有找到。）但这实际上有一个很好的原因：我已经将它整合进我的工作流程中了。\n\n我是一个 Emacs 用户。我设置了我的环境，每当我运行一个程序并且它以非零状态码结束时（意味着有错误发生），它会自动调用最新最快的\nLLM，并要求它解释答案，同时请求一个可以直接应用来修复代码中 bug 的补丁。\n\n现今的模型大多数情况下还不足以在这项任务上胜过我，但它们正在逐渐进步。偶尔，当 LLM\n修复了一个我知道如果自己追踪可能非常困难的错误时，我会感到非常惊喜，尤其是当错误只是因为某处的小笔误。\n\n\n13、以及无数其他事情\n\n\n\n\n我上面提到的所有对话只占我过去一年与 LLMs 互动总数的不到\n2%。我没有提供其他链接的原因并不是因为这些是模型失败的案例（尽管有很多这样的案例），而是因为：（1）许多互动重复了我已经提到的那些模式，或（2）它们不容易解释清楚发生了什么，也不容易让你自己看到它们为何对我有用。\n\n我完全预期未来我使用这些模型的频率会持续增长。作为一个参考，我在 2024 年通过网页界面进行的 LLM 查询比 2023 年增加了\n30%——而且我甚至无法计算 API 查询的增加，但我猜测至少增加了 2 到 3 倍。\n\n\n评价 LLMS 的能力，而非它们的局限\n\n\n\n\n我在面试求职者时获得的最佳建议之一是，根据他们能做什么而不是不能做什么来评价他们。\n\n我怀疑我可以问你一些简单的问题，这可能会让你显得不够称职。比如极端的例子：世界上有十亿人讲普通话；我连数到十都做不到。如果有人给我一份小学水平的普通话考试，我肯定会考得很糟糕。\n\n即便在计算机科学领域内，也有我完全不了解的领域。我关于如何构建 SQL 的知识仅限于如何编写有效的 SELECT\n语句。这——字面上——是我唯一知道如何编写的语句。\n\n因此，当我看到人们在网络上争论说 LLMs 只是炒作，因为 “它们甚至不能做 XXX” 时，我真的感到困惑。这里的 XX 可能是：\n\n * … 计算句子中的单词数！\n * … 写一首每个单词都以字母 “a” 开头的诗\n * … 乘以两位数\n * … 从列表中随机选择一个元素\n\n因为你上次真正需要做这些事情，并且真诚地认为 LLM 是合适的工具是什么时候？\n\n我不会因为我们不能在头脑中分割 64 位整数而认为人类完全无用——这对计算机来说是极其简单的任务——我也不认为因为你可以构造出 LLMs\n无法解决的任务就应该抛弃 LLMs。显然这很容易——关键是你能找到它们能够提供价值的任务吗？\n\n程序员已经很清楚，某些事物对不同的目的可能有用。想要编写操作系统？也许你应该使用 C 而不是 Python。没有人会说 “看 Python\n多么愚蠢，你甚至不能强制变量对齐到 32 字节边界！”\n这只是在错误的抽象级别上。语言模型也是一样。它们在非常高的抽象级别上操作；你不能期待它们解决甚至最简单的程序都能解决的任务。但你可以期望它们解决不同种类的任务。\n\n\n结论\n\n\n\n\n撰写这篇文章的初衷有两个。首先，正如我在文章开头所述，我想证明 LLMs 已经为我提供了大量的价值。此外，我注意到很多人表示喜欢使用 LLMs\n的想法，但不知道它们能如何帮助自己。因此，如果你是这些人之一，希望通过我的使用案例能看到一些示例。\n\n因为至少对我来说，LLMs 能做很多事。它们不能做所有事情，甚至不能做大多数事情。但当前的模型，正如它们现在存在的样子，为我提供了可观的价值。\n\n在展示这些示例后，我经常收到的一种反驳是：“但这些任务很简单！任何计算机科学的本科生都能学会如何做到！” 确实，本科生可以通过几小时的搜索来告诉我如何正确地诊断\nCUDA 错误以及可以重新安装哪些包。本科生可以通过几小时的工作重写那个程序为 C\n语言。本科生可以通过几小时的学习相关教科书来教我任何我想知道的关于那个主题的内容。不幸的是，我没有一个随时可以回答我任何问题的神奇本科生。但我有语言模型。所以，虽然语言模型还不够好，不能解决我作为程序员工作中的有趣部分，而且当前模型只能解决简单的任务。\n\n但五年前，LLM\n最好的能做的就是写出看起来像英语的段落。当它们能从一个句子到下一个形成连贯的思想时，我们都感到惊讶。它们的实际用途几乎为零。然而，今天，它们已经提高了我在项目中编程方面的生产力至少\n50%，并消除了足够多的繁琐工作，让我能够创建一些我否则永远不会尝试的东西。\n\n因此，当人们说 “LLMs 只是炒作” 和“所有 LLM\n都没有为任何人提供实际价值”时，很明显他们是错误的，因为它们为我提供了价值。现在，也许我是个例外。也许我是唯一找到了让这些模型有用的方法的人。\n\n我只能代表我自己。\n\n但考虑到 LLMs 显著提高了我的生产力——一个在使用 LLM 之前已经有 20 年编程经验的人——我相信还有其他人也能从使用 AI 中受益。\n\n原文链接：\n\nhttps://nicholas.carlini.com/writing/2024/how-i-use-ai.html#intro","image":"https://cdn-blog-ccino-cc.ccino.cc/blog-ccino-cc/production/images/item-4388bf289930d61cc18168629646edb2.png","date_published":"2024-10-15T13:38:45.813Z","_microfeed":{"is_audio":false,"is_document":false,"is_external_url":true,"is_video":false,"is_image":false,"web_url":"https://blog-ccino-cc.pages.dev/i/谷歌科学家万字长文-《改变你职业生涯的一篇文章-我如何运用人工智能完成工作》建议每个人都要读一遍-tMd8VMX18d9/","json_url":"https://blog-ccino-cc.pages.dev/i/tMd8VMX18d9/json/","rss_url":"https://blog-ccino-cc.pages.dev/i/tMd8VMX18d9/rss/","guid":"tMd8VMX18d9","status":"published","itunes:episodeType":"full","date_published_short":"Tue Oct 15 2024","date_published_ms":1728999525813}},{"id":"1khcE6hahR5","title":"放开那代码让我来Cursor 帮你写代码的奇妙之旅","attachments":[{"url":"https://blog.ccino.org/p/let-go-of-the-code-and-let-cursor-help-you-write-the-code./","mime_type":"text/html"}],"url":"https://blog.ccino.cc/i/let-go-of-the-code-and-let-cursor-help-you-write-the-code/","external_url":"https://blog.ccino.org/p/let-go-of-the-code-and-let-cursor-help-you-write-the-code./","content_html":"<p>让我们开门见山：编程很酷，但也很折磨人。那些长时间盯着屏幕，debug 无休止的日子，只有程序员懂得它的酸爽。而就在这个编程焦虑的世界中，<strong>Cursor</strong>&nbsp;横空出世，带着一系列魔法功能，如同你手中的一根智能魔杖，让写代码变得像煮速冻面一样简单。</p><p><strong>Cursor</strong>，一款基于 AI 的编程助手，号称可以从自然语言生成代码、自动修复 bug、甚至帮你写文档。它到底是怎样炼成的？又如何在编程界掀起一场技术革命？它会是程序员的终极福音，还是带来失业的寒风？今天，咱们就来聊聊这个神奇的 AI 工具。</p><h2>1.&nbsp;<strong>Cursor 的起源：AI 编程助手的崛起之路</strong></h2><p><br></p><p>话说，人工智能在过去十年大展拳脚，从会下围棋的&nbsp;<strong>AlphaGo</strong>&nbsp;到会画画的&nbsp;<strong>DALL·E</strong>，AI 无处不在。可程序员们却发现，AI 一边让他们失业（嗯，自动化），一边又离不开他们（嗯，调试代码）。于是，一个天才团队想：既然 AI 可以干那么多事情，为什么不帮程序员也省省力呢？</p><p>于是&nbsp;<strong>Cursor</strong>&nbsp;诞生了，它利用了 AI 的核心技术，尤其是自然语言处理（NLP）和大型语言模型（比如 GPT-4），来辅助程序员完成各种编程任务。最开始，它只能做些简单的代码补全和错误提示，简直就是个 “高效的智障小助手”。但随着技术的演进，它变得越来越聪明，能完成的任务也越来越多，逐渐从一个“代码秘书” 升级为一位“编程魔法师”。</p><h2>2.&nbsp;<strong>Cursor 的超能力：AI 编程助手的终极武器</strong></h2><p><br></p><p>你可能会问：“Cursor 到底能做什么？它能帮我写完项目吗？” 别急，它虽然还不至于替你全程搞定，但绝对能帮你省下不少头发。</p><h3>2.1&nbsp;<strong>代码自动补全：终于不用手敲那些烦人的细节了</strong></h3><p><br></p><p>还记得那些每次写循环或函数声明时都觉得眼睛发疼的日子吗？Cursor 直接解决了这个问题。它能根据你当前写的代码，自动为你补全后续代码。这就像是有个聪明的小助手，时刻站在你身边，看你码字，然后说：“哦，我懂你要干嘛！我来帮你补全后面的代码吧！”</p><p>例子：你写了一个&nbsp;for&nbsp;循环，Cursor 可能已经猜到你想遍历某个数组，然后自动补全了循环体内的逻辑。就像它比你还懂你一样。</p><h3>2.2&nbsp;<strong>自动错误检测与修复：Bug 猎人出动</strong></h3><p><br></p><p>Cursor 不仅是个高效码字小助手，还是个 “Bug 猎人”。它能自动识别代码中的错误，并提供修复建议。举个例子，当你写错了一个变量名，Cursor 会立刻弹出提醒：“老铁，这里有问题，帮你修复了。”</p><p>如果你曾因为一个逗号漏了半天没找出来，那你可能会立刻给 Cursor 磕头感恩。</p><h3>2.3&nbsp;<strong>从自然语言生成代码：程序员的‘心灵翻译器’</strong></h3><p><br></p><p>这是 Cursor 的杀手锏之一。它能将你的 “人话” 翻译成“机器话”。比如，你输入一句话：“我需要一个函数，计算所有奇数的平方和。” Cursor 就会基于你的描述生成相应的代码。想象一下：你不再需要脑补复杂的逻辑流程，只需说出需求，代码立马呈现。</p><h3>2.4&nbsp;<strong>代码优化建议：做你的编程导师</strong></h3><p><br></p><p>Cursor 不仅帮你写代码，还帮你<strong>写得更好</strong>。它可以提出代码优化建议，告诉你哪里可以使用更高效的算法或更简洁的语法，就像一个贴身的编程导师，随时随地为你指点迷津。</p><h3>2.5&nbsp;<strong>自动文档生成：帮你偷懒的终极工具</strong></h3><p><br></p><p>还在为写文档头疼吗？Cursor 自动帮你生成注释和文档说明。你写的每一行代码，它都能帮你配上优雅的注释，完美解决 “代码如诗，注释如坟” 的局面。</p><h3>2.6&nbsp;<strong>多语言支持：无论你在哪个‘编程国度’</strong></h3><p><br></p><p>Cursor 不仅局限于一种编程语言，它支持多种编程语言——Python、JavaScript、Java、C++，你能想到的主流语言，它基本都能搞定。这意味着，你可以在任何编程语言的环境下，享受 Cursor 的 AI 魔法。</p><h2>1.&nbsp;<strong>Cursor 的使用场景：编程的各个角落都有它的身影</strong></h2><h3>1.1&nbsp;<strong>快速原型开发：用它写代码，就像开了加速器</strong></h3><p><br></p><p>当你需要快速构建一个产品原型，Cursor 能帮你省掉大量的时间。它可以自动生成代码，帮你迅速验证想法。再也不需要花数小时从零开始搭建一个项目了，Cursor 可以帮你直接 “从头到尾” 生成基础逻辑。</p><h3>1.2&nbsp;<strong>复杂项目调试：它是你调试中的 “侦探助手”</strong></h3><p><br></p><p>大型项目中，调试代码是一件费时费力的事。Cursor 能够通过智能分析自动发现错误，甚至提出修复方案。在面对复杂的系统调试时，它就是你最强大的 AI“侦探助手”。</p><h3>1.3&nbsp;<strong>学习编程的新手福音：它是 “带你飞” 的导师</strong></h3><p><br></p><p>对于新手来说，Cursor 简直就是<strong>梦寐以求的老师</strong>。不会写代码？没关系，Cursor 可以把你的自然语言描述翻译成代码。不会调试？没问题，它自动帮你找出代码中的问题。它不仅能让你快速上手编程，还能帮你学会如何优化代码，事半功倍。</p><h3>1.4&nbsp;<strong>跨语言项目：开发者的多语言助手</strong></h3><p><br></p><p>如果你正在开发一个跨语言的项目，Cursor 的多语言支持让你不再需要每次切换语言时都得重新适应。它会帮你快速转换思维，不管是前端的 JavaScript，还是后端的 Python，都能轻松搞定。</p><h2>2. 让 Cursor 写一个 dns 服务器</h2><p><br></p><p>今天我们来使用 Cursor 写代码，体验一下他到底是超级牛批呢还是小菜鸡一枚。</p><ol><li>下载安装</li></ol><p><br></p><p>官网：https://www.cursor.com/</p><p>下载安装、注册登录这里略过，把内容留给有用的部分吧。</p><h3>2. 开始使用吧</h3><p><br></p><p>我这里随便新建一个文件夹 MYCURSOR&nbsp;<img src=\"https://pic.ccino.win/OB/2024/10/1728707028202410121223512.png\"></p><p>然后 Command + i 弹出对话框。</p><p><img src=\"https://pic.ccino.win/OB/2024/10/1728707050202410121224339.png\"></p><h3>3. 写一个 dns 服务器</h3><p><br></p><p>输入我想让他实现的功能</p><p>用 python 写一个简单 DNS 服务器，要求包含的功能:</p><ol><li>支持查询 A 记录、CNAME 记录。</li><li>可以设置上级 DNS 服务器，如设置上级 DNS 为 223.5.5.5。</li><li>从 dns_config. Txt 中读取内容，如果 dns_config. Txt 中没有对应的解析记录则去上级 DNS 服务器查询，如果有则返回结果，</li><li>Dns_config. Txt 内容格式如下：</li></ol><p><a href=\"https://www.abcd.com/\" rel=\"noopener noreferrer\" target=\"_blank\">www.abcd.com</a>&nbsp;A 1.2.3.4</p><p><a href=\"https://www.asdf.com/\" rel=\"noopener noreferrer\" target=\"_blank\">www.asdf.com</a>&nbsp;CNAME&nbsp;<a href=\"https://www.baidu.com/\" rel=\"noopener noreferrer\" target=\"_blank\">www.baidu.com</a></p><ol><li>我本地 IP 为 192.168.2.20，要求可以使用比如 nslookup&nbsp;<a href=\"https://www.abcd.com/\" rel=\"noopener noreferrer\" target=\"_blank\">www.abcd.com</a>&nbsp;192.168.2.20 进行查询。</li></ol><p><br></p><p><img src=\"https://pic.ccino.win/OB/2024/10/1728707084202410121224786.png\"></p><p>几秒后，在他一顿猛如虎的操作下给出了不知道靠不靠谱的内容，且来欣赏一番。</p><p><img src=\"https://pic.ccino.win/OB/2024/10/1728707107202410121225883.png\"></p><p>不仅写出来 simple_dns_server. Py 文件还是给出了使用说明。</p><p>初版</p><pre class=\"ql-syntax\" spellcheck=\"false\">import socket\nimport struct\nimport threading\nimport time\nfrom dnslib import DNSRecord, DNSHeader, DNSQuestion, RR, A, CNAME, QTYPE\n\n# 上级DNS服务器\nUPSTREAM_DNS = '223.5.5.5'\n# 本地IP\nLOCAL_IP = '192.168.2.20'\n# DNS配置文件\nCONFIG_FILE = 'dns_config.txt'\n\n# 读取DNS配置\ndef load_dns_config():\n    config = {}\n    with open(CONFIG_FILE, 'r') as f:\n        for line in f:\n            parts = line.strip().split()\n            if len(parts) == 3:\n                domain, record_type, value = parts\n                config[(domain, record_type)] = value\n    return config\n\n# 查询上级DNS服务器\ndef query_upstream(query):\n    sock = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)\n    sock.settimeout(5)\n    try:\n        sock.sendto(query.pack(), (UPSTREAM_DNS, 53))\n        data, _ = sock.recvfrom(1024)\n        return DNSRecord.parse(data)\n    except socket.timeout:\n        return None\n    finally:\n        sock.close()\n\n# 处理DNS请求\ndef handle_dns_request(data, addr, socket):\n    request = DNSRecord.parse(data)\n    reply = DNSRecord(DNSHeader(id=request.header.id, qr=1, aa=1, ra=1), q=request.q)\n\n    qname = str(request.q.qname)\n    qtype = request.q.qtype\n\n    dns_config = load_dns_config()\n    \n    if qtype == QTYPE.A:\n        if (qname, 'A') in dns_config:\n            reply.add_answer(RR(qname, QTYPE.A, rdata=A(dns_config[(qname, 'A')])))\n        elif (qname, 'CNAME') in dns_config:\n            cname = dns_config[(qname, 'CNAME')]\n            reply.add_answer(RR(qname, QTYPE.CNAME, rdata=CNAME(cname)))\n            if (cname, 'A') in dns_config:\n                reply.add_answer(RR(cname, QTYPE.A, rdata=A(dns_config[(cname, 'A')])))\n        else:\n            upstream_reply = query_upstream(request)\n            if upstream_reply:\n                reply = upstream_reply\n    elif qtype == QTYPE.CNAME:\n        if (qname, 'CNAME') in dns_config:\n            reply.add_answer(RR(qname, QTYPE.CNAME, rdata=CNAME(dns_config[(qname, 'CNAME')])))\n        else:\n            upstream_reply = query_upstream(request)\n            if upstream_reply:\n                reply = upstream_reply\n\n    socket.sendto(reply.pack(), addr)\n\n# 主函数\ndef main():\n    sock = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)\n    sock.bind((LOCAL_IP, 53))\n    print(f\"DNS服务器运行在 {LOCAL_IP}:53\")\n\n    while True:\n        data, addr = sock.recvfrom(1024)\n        threading.Thread(target=handle_dns_request, args=(data, addr, sock)).start()\n\nif __name__ == \"__main__\":\n    main()\n</pre><p><br></p><p>使用说明</p><p><img src=\"https://pic.ccino.win/OB/2024/10/1728707150202410121225582.png\"></p><h3>4. 接受 Cursor 生成的内容</h3><p><br></p><p>只需点击右下角 accept all 即可保持代码</p><p><img src=\"https://pic.ccino.win/OB/2024/10/1728707166202410121226270.png\"></p><p>可以看到左右文件栏中文件 simply_dn</p><p><img src=\"https://pic.ccino.win/OB/2024/10/1728707183202410121226029.png\"></p><h3>5. 生成配置文件</h3><p><br></p><p>接下来让他帮忙生成 dns_config. Txt 配置</p><p><img src=\"https://pic.ccino.win/OB/2024/10/1728707195202410121226315.png\"></p><p>文件内容如下：</p><p><img src=\"https://pic.ccino.win/OB/2024/10/1728707210202410121226564.png\"></p><h3>6. 实践是检验真理的唯一标准</h3><p><br></p><p>接下来见证奇迹的时刻到了，运行代码测试一下看是否可以达到预期目的</p><p>先来安装说明要求的 dnslib</p><pre class=\"ql-syntax\" spellcheck=\"false\">pip3 install dnslib               \nDefaulting to user installation because normal site-packages is not writeable\nCollecting dnslib\n  Downloading dnslib-0.9.25-py3-none-any.whl (63 kB)\n     |████████████████████████████████| 63 kB 63 kB/s \nInstalling collected packages: dnslib\nSuccessfully installed dnslib-0.9.25\nWARNING: You are using pip version 21.2.4; however, version 24.2 is available.\nYou should consider upgrading via the '/Library/Developer/CommandLineTools/usr/bin/python3 -m pip install --upgrade pip' command.\n</pre><p><br></p><p>然后启动 dns 服务器</p><pre class=\"ql-syntax\" spellcheck=\"false\">1\nsudo python3 simple_dns_server.py\n</pre><p><br></p><p><br></p><p><img src=\"https://pic.ccino.win/OB/2024/10/1728707233202410121227359.png\"></p><p>然后运行 nslookup 查询结果，</p><pre class=\"ql-syntax\" spellcheck=\"false\">nslookup www.example.com 192.168.2.20\n</pre><p>Copy</p><h3>7. 泼了一盆冷水</h3><p><br></p><p>按照配置文件 dns_config. Txt 中的配置&nbsp;<a href=\"https://www.example.com/\" rel=\"noopener noreferrer\" target=\"_blank\">www.example.com</a>&nbsp;A 192.0.2.1 应该查询的结果为 192.0.2.1 才符合预期，我们来试试</p><p><img src=\"https://pic.ccino.win/OB/2024/10/1728707259202410121227154.png\"></p><p>这明显是没有读取配置文件中的内容呢，把结果告诉 cursor 让他改进</p><h3>8. 让 Cursor 面壁思过</h3><p><br></p><p><img src=\"https://pic.ccino.win/OB/2024/10/1728707282202410121227490.png\"></p><p>可以他修改后代码有两处发生了变化，</p><p><img src=\"https://pic.ccino.win/OB/2024/10/1728707297202410121228664.png\"></p><p>点击 Accept all 接受修改</p><h3>9. 大功告成</h3><p><br></p><p>重新运行 dns 服务器</p><pre class=\"ql-syntax\" spellcheck=\"false\">sudo python3 simple_dns_server.py\n</pre><p>Copy</p><h4>9.1 然后再进行解析查询</h4><p><br></p><p><img src=\"https://pic.ccino.win/OB/2024/10/1728707315202410121228411.png\"></p><p>这次可以看到返回结果符合预期。</p><h4>9.2 再来测试一个 cname 的记录，&nbsp;</h4><pre class=\"ql-syntax\" spellcheck=\"false\">nslookup shop.example.com 192.168.2.20\n</pre><p>Copy</p><p><br></p><p><img src=\"https://pic.ccino.win/OB/2024/10/1728707323202410121228569.png\"></p><p>可以看到符合配置文件中的设置。</p><h3>9.3 查询一个配置文件中不存在的解析记录；</h3><pre class=\"ql-syntax\" spellcheck=\"false\">nslookup www.cnblogs.com 192.168.2.20\n</pre><p>Copy</p><p><br></p><p><img src=\"https://pic.ccino.win/OB/2024/10/1728707334202410121228788.png\"></p><p>可以看到查询配置中不存在的解析记录时，他会向上层 dns 查询。</p><p>至此, Cursor 已经按照要求完成了代码的编写。</p><p>最终代码如下：</p><pre class=\"ql-syntax\" spellcheck=\"false\">import socket\nimport struct\nimport threading\nimport time\nfrom dnslib import DNSRecord, DNSHeader, DNSQuestion, RR, A, CNAME, QTYPE\n\n# 上级DNS服务器\nUPSTREAM_DNS = '223.5.5.5'\n# 本地IP\nLOCAL_IP = '192.168.2.20'\n# DNS配置文件\nCONFIG_FILE = 'dns_config.txt'\n\n# 读取DNS配置\ndef load_dns_config():\n    config = {}\n    with open(CONFIG_FILE, 'r') as f:\n        for line in f:\n            parts = line.strip().split()\n            if len(parts) == 3:\n                domain, record_type, value = parts\n                config[(domain.lower(), record_type)] = value\n    return config\n\n# 查询上级DNS服务器\ndef query_upstream(query):\n    sock = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)\n    sock.settimeout(5)\n    try:\n        sock.sendto(query.pack(), (UPSTREAM_DNS, 53))\n        data, _ = sock.recvfrom(1024)\n        return DNSRecord.parse(data)\n    except socket.timeout:\n        return None\n    finally:\n        sock.close()\n\n# 处理DNS请求\ndef handle_dns_request(data, addr, socket):\n    request = DNSRecord.parse(data)\n    reply = DNSRecord(DNSHeader(id=request.header.id, qr=1, aa=1, ra=1), q=request.q)\n\n    qname = str(request.q.qname).lower().rstrip('.')\n    qtype = request.q.qtype\n\n    dns_config = load_dns_config()\n    \n    if qtype == QTYPE.A:\n        if (qname, 'A') in dns_config:\n            reply.add_answer(RR(qname, QTYPE.A, rdata=A(dns_config[(qname, 'A')])))\n        elif (qname, 'CNAME') in dns_config:\n            cname = dns_config[(qname, 'CNAME')]\n            reply.add_answer(RR(qname, QTYPE.CNAME, rdata=CNAME(cname)))\n            if (cname.lower(), 'A') in dns_config:\n                reply.add_answer(RR(cname, QTYPE.A, rdata=A(dns_config[(cname.lower(), 'A')])))\n        else:\n            upstream_reply = query_upstream(request)\n            if upstream_reply:\n                reply = upstream_reply\n    elif qtype == QTYPE.CNAME:\n        if (qname, 'CNAME') in dns_config:\n            reply.add_answer(RR(qname, QTYPE.CNAME, rdata=CNAME(dns_config[(qname, 'CNAME')])))\n        else:\n            upstream_reply = query_upstream(request)\n            if upstream_reply:\n                reply = upstream_reply\n\n    socket.sendto(reply.pack(), addr)\n\n# 主函数\ndef main():\n    sock = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)\n    sock.bind((LOCAL_IP, 53))\n    print(f\"DNS服务器运行在 {LOCAL_IP}:53\")\n\n    while True:\n        data, addr = sock.recvfrom(1024)\n        threading.Thread(target=handle_dns_request, args=(data, addr, sock)).start()\n\nif __name__ == \"__main__\":\n    main()\n</pre><p>Copy</p><p>Dns_config. Txt 内容如下：</p><p>www.example.com A 192.0.2.1 blog. Example. Com CNAME www.example.com Mail. Example. Com A 192.0.2.2 Ftp. Example. Com A 192.0.2.3 Shop. Example. Com CNAME store. Example. Com Store. Example. Com A 192.0.2.4 Api. Example. Com A 192.0.2.5 Cdn. Example. Com CNAME content. Example. Com Content. Example. Com A 192.0.2.6 support. Example. Com A 192.0.2.7</p><h3>10. 细思极恐</h3><p><br></p><p>我们一共给 cursor 说了三句话，他就把所有内容生成了，而且实现了我的要求</p><p>第一句话：说明我的需求。</p><p>第二句话：让他生成 dns_config. Txt 配置文件。</p><p>第三句话：把错误给他，让他改正。</p><p>你觉得 Cursor 强大吗，会不会有一天代替你呢</p><p>随着 AI 编程工具如 Cursor 的崛起，很多程序员会有一个现实的担忧：“这东西会不会取代我？” 虽然 Cursor 的确让编码变得更高效，但它并不会让程序员 “失业”。相反，它让程序员的工作方式发生转变，未来可能更多依赖于创新和战略性技能，而非简单的代码编写。</p><h2>1&nbsp;<strong>初级程序员面临的挑战：重复劳动的消亡</strong></h2><p><br></p><p>对不起，新手程序员，AI 有点 “不客气”。初入行的你，可能从事大量重复性工作，比如模板代码的编写、简单功能的实现、bug 修复等。这些任务通常不需要太多创造力，反而更依赖经验积累和耐心。而这些“繁琐任务” 正是 Cursor 的强项。</p><p><strong>例子</strong>： 假设你是一名刚入职的程序员，任务是为一个电商网站的后端构建用户注册模块。传统情况下，你需要：</p><ul><li>编写表单验证逻辑</li><li>设置数据库交互</li><li>实现输入数据的校验和处理</li></ul><p>而 Cursor 可以自动生成这些常见的模块和逻辑。你可能需要花几个小时完成的模板代码，它几秒钟就搞定。这意味着公司可能会减少初级程序员的人数需求，因为很多基础性代码已可以通过 AI 工具快速生成。</p><p><strong>但不要慌！机会依然存在。</strong></p><p>初级程序员面对的挑战不在于他们会被完全取代，而是<strong>角色和技能</strong>将需要重新定义。未来，入门级程序员需要更快掌握以下几个方面：</p><ul><li><strong>掌握 AI 工具的使用</strong>：如何与 Cursor 等 AI 工具协作，将成为基本技能。</li><li><strong>提升理解业务需求的能力</strong>：AI 可以生成代码，但理解业务需求并转化为技术实现仍需要人的介入。</li><li><strong>跨领域知识</strong>：例如理解 UI/UX 设计、产品开发等，让自己不只是 “代码工匠”，而是具备多样化技能的开发者。</li></ul><h2>2&nbsp;<strong>高级程序员需求上升：你要学会驾驭‘魔法’</strong></h2><p><br></p><p>相较于初级程序员，高级程序员的前景更为乐观。事实上，AI 工具如 Cursor 的广泛应用，将加速<strong>高级开发人才的需求</strong>。原因在于，AI 工具虽然能够生成代码，但高级开发工作中涉及的设计、架构和复杂决策仍需要人类的创造性思维和经验。</p><p><strong>例子</strong>： 如果你是一个高级架构师，在负责搭建一个微服务架构的大型企业系统。你需要考虑：</p><ul><li>系统的可扩展性和容错性</li><li>如何设计微服务之间的通信协议</li><li>如何优化数据存储和检索</li></ul><p>这些高级别的系统设计问题，AI 目前无法替代。Cursor 虽然能够帮助你快速生成一些基础的服务逻辑，但当涉及到性能优化、跨团队的技术协作以及复杂的架构设计时，还是得依赖你脑中的 “魔法公式”。</p><p>Cursor 成了&nbsp;<strong>“超级助手”</strong>，帮你自动生成一些琐碎代码，让你有更多的时间和精力去思考系统整体架构、前瞻性的技术规划、创新功能设计等高层次问题。</p><p>因此，高级程序员不仅不会被 Cursor 替代，反而能更加专注于发挥自己的创造力与经验，将 AI 当作工具，提高开发效率。</p><p><strong>重点在于：驾驭工具，而非被工具控制。</strong></p><p>要做到这一点，高级程序员需要进一步强化自己在以下领域的技能：</p><ul><li><strong>架构设计能力</strong>：了解复杂系统的架构，尤其是能够处理大规模、高并发的系统设计。</li><li><strong>战略性思维</strong>：不仅能写代码，更要思考代码对整个项目、产品和业务的影响。</li><li><strong>AI 工具的深度掌握</strong>：高级程序员需要不仅会使用 AI 工具，还能调整和定制这些工具来满足复杂需求。</li></ul><h2>3&nbsp;<strong>编程技能的演变：你不只是‘代码工匠’，还是‘技术指挥家’</strong></h2><p><br></p><p>随着 AI 工具的普及，未来程序员的角色将发生重大变化。编程已经不再是单纯的写代码——更多的是思考如何解决问题、如何设计复杂系统、如何与机器智能协作。这让程序员从 “代码工匠” 转型为“技术指挥家”。</p><p><strong>例子</strong>： 想象一下，你现在的角色不再是坐在电脑前一行行写代码，而是像指挥家一样，分配不同的 AI 工具去处理各个部分：</p><ul><li>让 Cursor 生成代码</li><li>让另一个 AI 工具进行代码审查</li><li>你自己负责把所有模块拼接成一个完整的系统</li></ul><p>在这种情境下，<strong>编程技能的演变</strong>非常关键：</p><ul><li><strong>战略性系统思维</strong>：未来的程序员不仅需要懂得如何写代码，还要懂得如何整体设计和把控技术策略。</li><li><strong>问题解决能力</strong>：AI 工具可以帮你生成代码，但它们解决不了你面临的所有问题——尤其是那些涉及深层业务逻辑和复杂算法的挑战。</li><li><strong>跨团队协作</strong>：随着 AI 工具的普及，开发团队将更加专注于创新性功能的设计和实现。如何与设计师、产品经理甚至 AI 工具紧密合作，将成为开发工作的重要部分。</li></ul><p><strong>技能的转型：掌握 AI 与技术的融合之道</strong></p><p>程序员们需要学习的不仅是编程语言，还有如何与 AI 共存与合作。未来的核心技能之一是<strong>如何将 AI 工具与自己的工作流程无缝结合</strong>，从</p><p>最终，Cursor 和其他 AI 编程工具带来的不仅是挑战，还有大量机会。虽然初级程序员可能面临某种程度的压力，但未来将涌现出更多高级开发需求，更多侧重于战略、架构和复杂问题解决的工作。</p><p>程序员的角色不会因为 AI 而消失，反而会变得更具创造性和价值。<strong>Cursor</strong>&nbsp;并不是程序员的 “替代者”，而是他们的“工具箱” 中又多了一件利器。掌握如何与 AI 工具共舞，将成为每一个程序员在未来技术世界中的核心竞争力。</p><p>所以，不用担心失业，关键在于如何利用 AI 工具变得更聪明、更高效，并在新技术浪潮中始终保持学习与进化的状态。</p><p>还不赶紧去试试&nbsp;<strong>Cursor</strong>？让它带你飞吧，感受 AI 编程助手的魔力！</p><p>本文出自：<a href=\"https://www.cnblogs.com/Sunzz/p/18425390\" rel=\"noopener noreferrer\" target=\"_blank\">“放开那代码让我来！\"——Cursor帮你写代码的奇妙之旅 - Sunzz - 博客园</a></p>","content_text":"让我们开门见山：编程很酷，但也很折磨人。那些长时间盯着屏幕，debug\n无休止的日子，只有程序员懂得它的酸爽。而就在这个编程焦虑的世界中，Cursor 横空出世，带着一系列魔法功能，如同你手中的一根智能魔杖，让写代码变得像煮速冻面一样简单。\n\nCursor，一款基于 AI 的编程助手，号称可以从自然语言生成代码、自动修复\nbug、甚至帮你写文档。它到底是怎样炼成的？又如何在编程界掀起一场技术革命？它会是程序员的终极福音，还是带来失业的寒风？今天，咱们就来聊聊这个神奇的 AI\n工具。\n\n\n1. CURSOR 的起源：AI 编程助手的崛起之路\n\n\n\n\n话说，人工智能在过去十年大展拳脚，从会下围棋的 AlphaGo 到会画画的 DALL·E，AI 无处不在。可程序员们却发现，AI\n一边让他们失业（嗯，自动化），一边又离不开他们（嗯，调试代码）。于是，一个天才团队想：既然 AI 可以干那么多事情，为什么不帮程序员也省省力呢？\n\n于是 Cursor 诞生了，它利用了 AI 的核心技术，尤其是自然语言处理（NLP）和大型语言模型（比如\nGPT-4），来辅助程序员完成各种编程任务。最开始，它只能做些简单的代码补全和错误提示，简直就是个\n“高效的智障小助手”。但随着技术的演进，它变得越来越聪明，能完成的任务也越来越多，逐渐从一个“代码秘书” 升级为一位“编程魔法师”。\n\n\n2. CURSOR 的超能力：AI 编程助手的终极武器\n\n\n\n\n你可能会问：“Cursor 到底能做什么？它能帮我写完项目吗？” 别急，它虽然还不至于替你全程搞定，但绝对能帮你省下不少头发。\n\n\n2.1 代码自动补全：终于不用手敲那些烦人的细节了\n\n\n\n\n还记得那些每次写循环或函数声明时都觉得眼睛发疼的日子吗？Cursor\n直接解决了这个问题。它能根据你当前写的代码，自动为你补全后续代码。这就像是有个聪明的小助手，时刻站在你身边，看你码字，然后说：“哦，我懂你要干嘛！我来帮你补全后面的代码吧！”\n\n例子：你写了一个 for 循环，Cursor 可能已经猜到你想遍历某个数组，然后自动补全了循环体内的逻辑。就像它比你还懂你一样。\n\n\n2.2 自动错误检测与修复：BUG 猎人出动\n\n\n\n\nCursor 不仅是个高效码字小助手，还是个 “Bug 猎人”。它能自动识别代码中的错误，并提供修复建议。举个例子，当你写错了一个变量名，Cursor\n会立刻弹出提醒：“老铁，这里有问题，帮你修复了。”\n\n如果你曾因为一个逗号漏了半天没找出来，那你可能会立刻给 Cursor 磕头感恩。\n\n\n2.3 从自然语言生成代码：程序员的‘心灵翻译器’\n\n\n\n\n这是 Cursor 的杀手锏之一。它能将你的 “人话” 翻译成“机器话”。比如，你输入一句话：“我需要一个函数，计算所有奇数的平方和。” Cursor\n就会基于你的描述生成相应的代码。想象一下：你不再需要脑补复杂的逻辑流程，只需说出需求，代码立马呈现。\n\n\n2.4 代码优化建议：做你的编程导师\n\n\n\n\nCursor\n不仅帮你写代码，还帮你写得更好。它可以提出代码优化建议，告诉你哪里可以使用更高效的算法或更简洁的语法，就像一个贴身的编程导师，随时随地为你指点迷津。\n\n\n2.5 自动文档生成：帮你偷懒的终极工具\n\n\n\n\n还在为写文档头疼吗？Cursor 自动帮你生成注释和文档说明。你写的每一行代码，它都能帮你配上优雅的注释，完美解决 “代码如诗，注释如坟” 的局面。\n\n\n2.6 多语言支持：无论你在哪个‘编程国度’\n\n\n\n\nCursor\n不仅局限于一种编程语言，它支持多种编程语言——Python、JavaScript、Java、C++，你能想到的主流语言，它基本都能搞定。这意味着，你可以在任何编程语言的环境下，享受\nCursor 的 AI 魔法。\n\n\n1. CURSOR 的使用场景：编程的各个角落都有它的身影\n\n\n1.1 快速原型开发：用它写代码，就像开了加速器\n\n\n\n\n当你需要快速构建一个产品原型，Cursor 能帮你省掉大量的时间。它可以自动生成代码，帮你迅速验证想法。再也不需要花数小时从零开始搭建一个项目了，Cursor\n可以帮你直接 “从头到尾” 生成基础逻辑。\n\n\n1.2 复杂项目调试：它是你调试中的 “侦探助手”\n\n\n\n\n大型项目中，调试代码是一件费时费力的事。Cursor 能够通过智能分析自动发现错误，甚至提出修复方案。在面对复杂的系统调试时，它就是你最强大的\nAI“侦探助手”。\n\n\n1.3 学习编程的新手福音：它是 “带你飞” 的导师\n\n\n\n\n对于新手来说，Cursor 简直就是梦寐以求的老师。不会写代码？没关系，Cursor\n可以把你的自然语言描述翻译成代码。不会调试？没问题，它自动帮你找出代码中的问题。它不仅能让你快速上手编程，还能帮你学会如何优化代码，事半功倍。\n\n\n1.4 跨语言项目：开发者的多语言助手\n\n\n\n\n如果你正在开发一个跨语言的项目，Cursor 的多语言支持让你不再需要每次切换语言时都得重新适应。它会帮你快速转换思维，不管是前端的\nJavaScript，还是后端的 Python，都能轻松搞定。\n\n\n2. 让 CURSOR 写一个 DNS 服务器\n\n\n\n\n今天我们来使用 Cursor 写代码，体验一下他到底是超级牛批呢还是小菜鸡一枚。\n\n 1. 下载安装\n\n\n\n\n官网：https://www.cursor.com/\n\n下载安装、注册登录这里略过，把内容留给有用的部分吧。\n\n\n2. 开始使用吧\n\n\n\n\n我这里随便新建一个文件夹\nMYCURSOR [https://pic.ccino.win/OB/2024/10/1728707028202410121223512.png]\n\n然后 Command + i 弹出对话框。\n\n[https://pic.ccino.win/OB/2024/10/1728707050202410121224339.png]\n\n\n3. 写一个 DNS 服务器\n\n\n\n\n输入我想让他实现的功能\n\n用 python 写一个简单 DNS 服务器，要求包含的功能:\n\n 1. 支持查询 A 记录、CNAME 记录。\n 2. 可以设置上级 DNS 服务器，如设置上级 DNS 为 223.5.5.5。\n 3. 从 dns_config. Txt 中读取内容，如果 dns_config. Txt 中没有对应的解析记录则去上级 DNS\n    服务器查询，如果有则返回结果，\n 4. Dns_config. Txt 内容格式如下：\n\nwww.abcd.com A 1.2.3.4\n\nwww.asdf.com CNAME www.baidu.com\n\n 1. 我本地 IP 为 192.168.2.20，要求可以使用比如 nslookup www.abcd.com 192.168.2.20 进行查询。\n\n\n\n\n[https://pic.ccino.win/OB/2024/10/1728707084202410121224786.png]\n\n几秒后，在他一顿猛如虎的操作下给出了不知道靠不靠谱的内容，且来欣赏一番。\n\n[https://pic.ccino.win/OB/2024/10/1728707107202410121225883.png]\n\n不仅写出来 simple_dns_server. Py 文件还是给出了使用说明。\n\n初版\n\nimport socket\nimport struct\nimport threading\nimport time\nfrom dnslib import DNSRecord, DNSHeader, DNSQuestion, RR, A, CNAME, QTYPE\n\n# 上级DNS服务器\nUPSTREAM_DNS = '223.5.5.5'\n# 本地IP\nLOCAL_IP = '192.168.2.20'\n# DNS配置文件\nCONFIG_FILE = 'dns_config.txt'\n\n# 读取DNS配置\ndef load_dns_config():\n    config = {}\n    with open(CONFIG_FILE, 'r') as f:\n        for line in f:\n            parts = line.strip().split()\n            if len(parts) == 3:\n                domain, record_type, value = parts\n                config[(domain, record_type)] = value\n    return config\n\n# 查询上级DNS服务器\ndef query_upstream(query):\n    sock = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)\n    sock.settimeout(5)\n    try:\n        sock.sendto(query.pack(), (UPSTREAM_DNS, 53))\n        data, _ = sock.recvfrom(1024)\n        return DNSRecord.parse(data)\n    except socket.timeout:\n        return None\n    finally:\n        sock.close()\n\n# 处理DNS请求\ndef handle_dns_request(data, addr, socket):\n    request = DNSRecord.parse(data)\n    reply = DNSRecord(DNSHeader(id=request.header.id, qr=1, aa=1, ra=1), q=request.q)\n\n    qname = str(request.q.qname)\n    qtype = request.q.qtype\n\n    dns_config = load_dns_config()\n    \n    if qtype == QTYPE.A:\n        if (qname, 'A') in dns_config:\n            reply.add_answer(RR(qname, QTYPE.A, rdata=A(dns_config[(qname, 'A')])))\n        elif (qname, 'CNAME') in dns_config:\n            cname = dns_config[(qname, 'CNAME')]\n            reply.add_answer(RR(qname, QTYPE.CNAME, rdata=CNAME(cname)))\n            if (cname, 'A') in dns_config:\n                reply.add_answer(RR(cname, QTYPE.A, rdata=A(dns_config[(cname, 'A')])))\n        else:\n            upstream_reply = query_upstream(request)\n            if upstream_reply:\n                reply = upstream_reply\n    elif qtype == QTYPE.CNAME:\n        if (qname, 'CNAME') in dns_config:\n            reply.add_answer(RR(qname, QTYPE.CNAME, rdata=CNAME(dns_config[(qname, 'CNAME')])))\n        else:\n            upstream_reply = query_upstream(request)\n            if upstream_reply:\n                reply = upstream_reply\n\n    socket.sendto(reply.pack(), addr)\n\n# 主函数\ndef main():\n    sock = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)\n    sock.bind((LOCAL_IP, 53))\n    print(f\"DNS服务器运行在 {LOCAL_IP}:53\")\n\n    while True:\n        data, addr = sock.recvfrom(1024)\n        threading.Thread(target=handle_dns_request, args=(data, addr, sock)).start()\n\nif __name__ == \"__main__\":\n    main()\n\n\n\n\n\n使用说明\n\n[https://pic.ccino.win/OB/2024/10/1728707150202410121225582.png]\n\n\n4. 接受 CURSOR 生成的内容\n\n\n\n\n只需点击右下角 accept all 即可保持代码\n\n[https://pic.ccino.win/OB/2024/10/1728707166202410121226270.png]\n\n可以看到左右文件栏中文件 simply_dn\n\n[https://pic.ccino.win/OB/2024/10/1728707183202410121226029.png]\n\n\n5. 生成配置文件\n\n\n\n\n接下来让他帮忙生成 dns_config. Txt 配置\n\n[https://pic.ccino.win/OB/2024/10/1728707195202410121226315.png]\n\n文件内容如下：\n\n[https://pic.ccino.win/OB/2024/10/1728707210202410121226564.png]\n\n\n6. 实践是检验真理的唯一标准\n\n\n\n\n接下来见证奇迹的时刻到了，运行代码测试一下看是否可以达到预期目的\n\n先来安装说明要求的 dnslib\n\npip3 install dnslib               \nDefaulting to user installation because normal site-packages is not writeable\nCollecting dnslib\n  Downloading dnslib-0.9.25-py3-none-any.whl (63 kB)\n     |████████████████████████████████| 63 kB 63 kB/s \nInstalling collected packages: dnslib\nSuccessfully installed dnslib-0.9.25\nWARNING: You are using pip version 21.2.4; however, version 24.2 is available.\nYou should consider upgrading via the '/Library/Developer/CommandLineTools/usr/bin/python3 -m pip install --upgrade pip' command.\n\n\n\n\n\n然后启动 dns 服务器\n\n1\nsudo python3 simple_dns_server.py\n\n\n\n\n\n\n\n\n[https://pic.ccino.win/OB/2024/10/1728707233202410121227359.png]\n\n然后运行 nslookup 查询结果，\n\nnslookup www.example.com 192.168.2.20\n\n\nCopy\n\n\n7. 泼了一盆冷水\n\n\n\n\n按照配置文件 dns_config. Txt 中的配置 www.example.com A 192.0.2.1 应该查询的结果为 192.0.2.1\n才符合预期，我们来试试\n\n[https://pic.ccino.win/OB/2024/10/1728707259202410121227154.png]\n\n这明显是没有读取配置文件中的内容呢，把结果告诉 cursor 让他改进\n\n\n8. 让 CURSOR 面壁思过\n\n\n\n\n[https://pic.ccino.win/OB/2024/10/1728707282202410121227490.png]\n\n可以他修改后代码有两处发生了变化，\n\n[https://pic.ccino.win/OB/2024/10/1728707297202410121228664.png]\n\n点击 Accept all 接受修改\n\n\n9. 大功告成\n\n\n\n\n重新运行 dns 服务器\n\nsudo python3 simple_dns_server.py\n\n\nCopy\n\n9.1 然后再进行解析查询\n\n\n\n\n[https://pic.ccino.win/OB/2024/10/1728707315202410121228411.png]\n\n这次可以看到返回结果符合预期。\n\n9.2 再来测试一个 CNAME 的记录， \n\nnslookup shop.example.com 192.168.2.20\n\n\nCopy\n\n\n\n\n[https://pic.ccino.win/OB/2024/10/1728707323202410121228569.png]\n\n可以看到符合配置文件中的设置。\n\n\n9.3 查询一个配置文件中不存在的解析记录；\n\nnslookup www.cnblogs.com 192.168.2.20\n\n\nCopy\n\n\n\n\n[https://pic.ccino.win/OB/2024/10/1728707334202410121228788.png]\n\n可以看到查询配置中不存在的解析记录时，他会向上层 dns 查询。\n\n至此, Cursor 已经按照要求完成了代码的编写。\n\n最终代码如下：\n\nimport socket\nimport struct\nimport threading\nimport time\nfrom dnslib import DNSRecord, DNSHeader, DNSQuestion, RR, A, CNAME, QTYPE\n\n# 上级DNS服务器\nUPSTREAM_DNS = '223.5.5.5'\n# 本地IP\nLOCAL_IP = '192.168.2.20'\n# DNS配置文件\nCONFIG_FILE = 'dns_config.txt'\n\n# 读取DNS配置\ndef load_dns_config():\n    config = {}\n    with open(CONFIG_FILE, 'r') as f:\n        for line in f:\n            parts = line.strip().split()\n            if len(parts) == 3:\n                domain, record_type, value = parts\n                config[(domain.lower(), record_type)] = value\n    return config\n\n# 查询上级DNS服务器\ndef query_upstream(query):\n    sock = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)\n    sock.settimeout(5)\n    try:\n        sock.sendto(query.pack(), (UPSTREAM_DNS, 53))\n        data, _ = sock.recvfrom(1024)\n        return DNSRecord.parse(data)\n    except socket.timeout:\n        return None\n    finally:\n        sock.close()\n\n# 处理DNS请求\ndef handle_dns_request(data, addr, socket):\n    request = DNSRecord.parse(data)\n    reply = DNSRecord(DNSHeader(id=request.header.id, qr=1, aa=1, ra=1), q=request.q)\n\n    qname = str(request.q.qname).lower().rstrip('.')\n    qtype = request.q.qtype\n\n    dns_config = load_dns_config()\n    \n    if qtype == QTYPE.A:\n        if (qname, 'A') in dns_config:\n            reply.add_answer(RR(qname, QTYPE.A, rdata=A(dns_config[(qname, 'A')])))\n        elif (qname, 'CNAME') in dns_config:\n            cname = dns_config[(qname, 'CNAME')]\n            reply.add_answer(RR(qname, QTYPE.CNAME, rdata=CNAME(cname)))\n            if (cname.lower(), 'A') in dns_config:\n                reply.add_answer(RR(cname, QTYPE.A, rdata=A(dns_config[(cname.lower(), 'A')])))\n        else:\n            upstream_reply = query_upstream(request)\n            if upstream_reply:\n                reply = upstream_reply\n    elif qtype == QTYPE.CNAME:\n        if (qname, 'CNAME') in dns_config:\n            reply.add_answer(RR(qname, QTYPE.CNAME, rdata=CNAME(dns_config[(qname, 'CNAME')])))\n        else:\n            upstream_reply = query_upstream(request)\n            if upstream_reply:\n                reply = upstream_reply\n\n    socket.sendto(reply.pack(), addr)\n\n# 主函数\ndef main():\n    sock = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)\n    sock.bind((LOCAL_IP, 53))\n    print(f\"DNS服务器运行在 {LOCAL_IP}:53\")\n\n    while True:\n        data, addr = sock.recvfrom(1024)\n        threading.Thread(target=handle_dns_request, args=(data, addr, sock)).start()\n\nif __name__ == \"__main__\":\n    main()\n\n\nCopy\n\nDns_config. Txt 内容如下：\n\nwww.example.com A 192.0.2.1 blog. Example. Com CNAME www.example.com Mail.\nExample. Com A 192.0.2.2 Ftp. Example. Com A 192.0.2.3 Shop. Example. Com CNAME\nstore. Example. Com Store. Example. Com A 192.0.2.4 Api. Example. Com A\n192.0.2.5 Cdn. Example. Com CNAME content. Example. Com Content. Example. Com A\n192.0.2.6 support. Example. Com A 192.0.2.7\n\n\n10. 细思极恐\n\n\n\n\n我们一共给 cursor 说了三句话，他就把所有内容生成了，而且实现了我的要求\n\n第一句话：说明我的需求。\n\n第二句话：让他生成 dns_config. Txt 配置文件。\n\n第三句话：把错误给他，让他改正。\n\n你觉得 Cursor 强大吗，会不会有一天代替你呢\n\n随着 AI 编程工具如 Cursor 的崛起，很多程序员会有一个现实的担忧：“这东西会不会取代我？” 虽然 Cursor\n的确让编码变得更高效，但它并不会让程序员 “失业”。相反，它让程序员的工作方式发生转变，未来可能更多依赖于创新和战略性技能，而非简单的代码编写。\n\n\n1 初级程序员面临的挑战：重复劳动的消亡\n\n\n\n\n对不起，新手程序员，AI 有点 “不客气”。初入行的你，可能从事大量重复性工作，比如模板代码的编写、简单功能的实现、bug\n修复等。这些任务通常不需要太多创造力，反而更依赖经验积累和耐心。而这些“繁琐任务” 正是 Cursor 的强项。\n\n例子： 假设你是一名刚入职的程序员，任务是为一个电商网站的后端构建用户注册模块。传统情况下，你需要：\n\n * 编写表单验证逻辑\n * 设置数据库交互\n * 实现输入数据的校验和处理\n\n而 Cursor\n可以自动生成这些常见的模块和逻辑。你可能需要花几个小时完成的模板代码，它几秒钟就搞定。这意味着公司可能会减少初级程序员的人数需求，因为很多基础性代码已可以通过\nAI 工具快速生成。\n\n但不要慌！机会依然存在。\n\n初级程序员面对的挑战不在于他们会被完全取代，而是角色和技能将需要重新定义。未来，入门级程序员需要更快掌握以下几个方面：\n\n * 掌握 AI 工具的使用：如何与 Cursor 等 AI 工具协作，将成为基本技能。\n * 提升理解业务需求的能力：AI 可以生成代码，但理解业务需求并转化为技术实现仍需要人的介入。\n * 跨领域知识：例如理解 UI/UX 设计、产品开发等，让自己不只是 “代码工匠”，而是具备多样化技能的开发者。\n\n\n2 高级程序员需求上升：你要学会驾驭‘魔法’\n\n\n\n\n相较于初级程序员，高级程序员的前景更为乐观。事实上，AI 工具如 Cursor 的广泛应用，将加速高级开发人才的需求。原因在于，AI\n工具虽然能够生成代码，但高级开发工作中涉及的设计、架构和复杂决策仍需要人类的创造性思维和经验。\n\n例子： 如果你是一个高级架构师，在负责搭建一个微服务架构的大型企业系统。你需要考虑：\n\n * 系统的可扩展性和容错性\n * 如何设计微服务之间的通信协议\n * 如何优化数据存储和检索\n\n这些高级别的系统设计问题，AI 目前无法替代。Cursor\n虽然能够帮助你快速生成一些基础的服务逻辑，但当涉及到性能优化、跨团队的技术协作以及复杂的架构设计时，还是得依赖你脑中的 “魔法公式”。\n\nCursor 成了 “超级助手”，帮你自动生成一些琐碎代码，让你有更多的时间和精力去思考系统整体架构、前瞻性的技术规划、创新功能设计等高层次问题。\n\n因此，高级程序员不仅不会被 Cursor 替代，反而能更加专注于发挥自己的创造力与经验，将 AI 当作工具，提高开发效率。\n\n重点在于：驾驭工具，而非被工具控制。\n\n要做到这一点，高级程序员需要进一步强化自己在以下领域的技能：\n\n * 架构设计能力：了解复杂系统的架构，尤其是能够处理大规模、高并发的系统设计。\n * 战略性思维：不仅能写代码，更要思考代码对整个项目、产品和业务的影响。\n * AI 工具的深度掌握：高级程序员需要不仅会使用 AI 工具，还能调整和定制这些工具来满足复杂需求。\n\n\n3 编程技能的演变：你不只是‘代码工匠’，还是‘技术指挥家’\n\n\n\n\n随着 AI\n工具的普及，未来程序员的角色将发生重大变化。编程已经不再是单纯的写代码——更多的是思考如何解决问题、如何设计复杂系统、如何与机器智能协作。这让程序员从\n“代码工匠” 转型为“技术指挥家”。\n\n例子： 想象一下，你现在的角色不再是坐在电脑前一行行写代码，而是像指挥家一样，分配不同的 AI 工具去处理各个部分：\n\n * 让 Cursor 生成代码\n * 让另一个 AI 工具进行代码审查\n * 你自己负责把所有模块拼接成一个完整的系统\n\n在这种情境下，编程技能的演变非常关键：\n\n * 战略性系统思维：未来的程序员不仅需要懂得如何写代码，还要懂得如何整体设计和把控技术策略。\n * 问题解决能力：AI 工具可以帮你生成代码，但它们解决不了你面临的所有问题——尤其是那些涉及深层业务逻辑和复杂算法的挑战。\n * 跨团队协作：随着 AI 工具的普及，开发团队将更加专注于创新性功能的设计和实现。如何与设计师、产品经理甚至 AI 工具紧密合作，将成为开发工作的重要部分。\n\n技能的转型：掌握 AI 与技术的融合之道\n\n程序员们需要学习的不仅是编程语言，还有如何与 AI 共存与合作。未来的核心技能之一是如何将 AI 工具与自己的工作流程无缝结合，从\n\n最终，Cursor 和其他 AI\n编程工具带来的不仅是挑战，还有大量机会。虽然初级程序员可能面临某种程度的压力，但未来将涌现出更多高级开发需求，更多侧重于战略、架构和复杂问题解决的工作。\n\n程序员的角色不会因为 AI 而消失，反而会变得更具创造性和价值。Cursor 并不是程序员的 “替代者”，而是他们的“工具箱” 中又多了一件利器。掌握如何与\nAI 工具共舞，将成为每一个程序员在未来技术世界中的核心竞争力。\n\n所以，不用担心失业，关键在于如何利用 AI 工具变得更聪明、更高效，并在新技术浪潮中始终保持学习与进化的状态。\n\n还不赶紧去试试 Cursor？让它带你飞吧，感受 AI 编程助手的魔力！\n\n本文出自：“放开那代码让我来！\"——Cursor帮你写代码的奇妙之旅 - Sunzz - 博客园","date_published":"2024-10-14T06:05:17.607Z","_microfeed":{"is_audio":false,"is_document":false,"is_external_url":true,"is_video":false,"is_image":false,"web_url":"https://blog-ccino-cc.pages.dev/i/cursor-1khcE6hahR5/","json_url":"https://blog-ccino-cc.pages.dev/i/1khcE6hahR5/json/","rss_url":"https://blog-ccino-cc.pages.dev/i/1khcE6hahR5/rss/","guid":"1khcE6hahR5","status":"published","itunes:episodeType":"full","date_published_short":"Mon Oct 14 2024","date_published_ms":1728885917607}},{"id":"mR7PNdXxt5F","title":"使用 Continue 部署免费的 AI 代码助手","attachments":[{"url":"https://ray-d-song.com/zh-cn/blog/continue-free-ai-code-assistant/","mime_type":"text/html"}],"url":"https://blog.ccino.cc/i/deploy-a-free-ai-code-assistant-with-continue/","external_url":"https://ray-d-song.com/zh-cn/blog/continue-free-ai-code-assistant/","content_html":"<p>过去的一年, 我一直在使用 GitHub Copilot 作为代码助手, 它提供了补全、问答等功能, 付费订阅 10 美元 / 月.</p><p>说实话这个价格并不算贵, 但最近 Copilot 对问答范围进行了限制, 导致很多与开发相关的问题无法得到解答.</p><p>后续我也尝试了 Codium AI, 响应速度不尽人意.</p><p>最近发现了一个开源的代码助手&nbsp;<a href=\"https://www.continue.dev/\" rel=\"noopener noreferrer\" target=\"_blank\">Continue</a>, 支持调用本地和云端的模型, 包括 Cloudflare 的 worker AI.</p><h2>介绍</h2><p><br></p><p>Continue 本质是个帮你向大模型发起请求的客户端.</p><p>当你编写代码的时候, 它会获取代码的上下文, 向模型发起请求, 获取结果后处理等待你的命令 (tab), 再拼接到代码中去.</p><h2>本地部署</h2><p><br></p><p>本地部署可以使用&nbsp;<a href=\"https://ollama.com/\" rel=\"noopener noreferrer\" target=\"_blank\">Ollama</a>&nbsp;或&nbsp;<a href=\"https://docs.continue.dev/reference/Model%20Providers/lmstudio\" rel=\"noopener noreferrer\" target=\"_blank\">lmstudio</a>.</p><p>这是两个非常方便的大模型集成工具, 同时支持 Mac、windows 以及 Linux, 帮助你快速在本地运行大模型.</p><p>这里我选择在 Mac 上运行 Ollama, 在官网 Ollama 可以下载到安装包, 安装过程中记得勾选命令行工具.</p><p><a href=\"https://ollama.com/\" rel=\"noopener noreferrer\" target=\"_blank\">Ollama</a>&nbsp;ollama. Com</p><p>Get up and running with large language models.</p><p><img src=\"https://ollama.com/public/og.png\"></p><h3>配置要求</h3><p><br></p><p>配置要求取决于你要使用的模型, 这里推荐 llama 3.</p><p>Llama 3 是 meta 开源的模型, 也是目前最强的开源模型, 可以获得比较好的补全效果.</p><p>模型有两个变体, 8 B 需要至少 16 g 的内存和 8 g 的显存, 70 B 则需要 64 g 以上的内存.</p><p>默认运行的 8 B 版本即可满足正常需要.</p><h3>运行模型</h3><p><br></p><p>安装完成后进入 terminal, 输入以下命令获取 llama 3 模型:</p><pre class=\"ql-syntax\" spellcheck=\"false\">1ollama run llama3\n</pre><p><br></p><p><br></p><p>模型拉取完成后就会自动运行.</p><p>测试用中文让他编写一个 python 版的 hello-world.</p><p><img src=\"https://r2.ray-d-song.com/2024/09/6d3a74f153d36136f7ae81586e8315fb.png\"></p><p>输入&nbsp;/bye&nbsp;可以退出运行.</p><h3>配置 Continue 插件</h3><p><br></p><p>安装&nbsp;<a href=\"https://marketplace.visualstudio.com/items?itemName=Continue.continue\" rel=\"noopener noreferrer\" target=\"_blank\">vscode plugin</a></p><p>安装完成左侧边栏会展示 Continue 的对话框:</p><p><img src=\"https://r2.ray-d-song.com/2024/09/ea18b185908bda96720414fcad005f10.png\"></p><p>这个时候是无法对话的, 因为还没有选择模型, 点击&nbsp;Select model&nbsp;-&gt;&nbsp;Add Model, 向下拉, 找到:</p><p><img src=\"https://r2.ray-d-song.com/2024/09/58d456bc4ba21cacbb2f2c2b076722de.png\"></p><p>然后再选择第一个&nbsp;Autodetect&nbsp;选项:</p><p><img src=\"https://r2.ray-d-song.com/2024/09/05191d0410e4f23c30285563b15c54dc.png\"></p><p>返回对话, 选择 Ollama 选项:</p><p><img src=\"https://r2.ray-d-song.com/2024/09/f82f1758e1ea1f85cf5cd9db3913e11d.png\"></p><p>确保你的模型正在后台运行, 接下来就可以开始对话了.</p><p><img src=\"https://r2.ray-d-song.com/2024/09/2880fe2110c014bc1b6e736d41bb0e71.png\"></p><h3>自动补全设置</h3><p><br></p><p>虽然现在已经可以完成对话, 但还是无法补全.</p><p><img src=\"https://r2.ray-d-song.com/2024/09/c072cad8863c2a50f9202e05e3442d44.png\"></p><p>因为补全默认使用的是&nbsp;Starcoder2 3b&nbsp;模型, Starcoder 是一个针对代码场景针对性强化的模型.</p><p>你可以使用以下命令启动该模型.</p><pre class=\"ql-syntax\" spellcheck=\"false\">1 1ollama run starcoder2\n</pre><p><br></p><p><br></p><p>模型启动后就可以触发补全.</p><p>如果你的机器性能不足以同时运行两个模型, 你也可以设置补全也使用&nbsp;llama3:</p><p>在插件侧边栏的角落, 找到这个小齿轮图标</p><p><img src=\"https://r2.ray-d-song.com/2024/09/d3b06fcec439af8e2cec4d360f2663c2.png\"></p><p>点击就会进入配置文件 (也可以打开~/. Continue/config. Json), 将&nbsp;tabAutocompleteModel&nbsp;字段的两个高亮行的值修改为&nbsp;llama3&nbsp;即可.</p><pre class=\"ql-syntax\" spellcheck=\"false\">1\"tabAutocompleteModel\": {\n2  \"title\": \"Starcoder2 3b\",\n3  \"provider\": \"ollama\",\n4  \"model\": \"starcoder2:3b\"\n5},\n</pre><p><br></p><p>如果你希望补全 markdown 之类的的文本文件, 那我建议使用 llama 3 进行补全</p><h2>Cloudflare worker AI</h2><p><br></p><p>有一些轻薄本机型不能在本地运行大模型, 也可以使用 Cloudflare worker 的 AI 模型.</p><p>Cloudflare 提供了免费额度, 不过是以&nbsp;神经元&nbsp;作为单位, 可以在<a href=\"https://ai.cloudflare.com/#pricing-calculator\" rel=\"noopener noreferrer\" target=\"_blank\">这个页面查看估算</a>.</p><p>每日的免费额度大体相当于 500 次调用, 每次 100 个入参字符和 500 个返回字符, 足以应对正常的开发场景.</p><p>首先根据&nbsp;<a href=\"https://developers.cloudflare.com/fundamentals/api/get-started/create-token/\" rel=\"noopener noreferrer\" target=\"_blank\">Cloudflare 的指引, 创建 key</a></p><p>然后打开配置文件, 增加一个&nbsp;models&nbsp;字段值, 并修改&nbsp;tabAutocompleteModel&nbsp;字段.</p><pre class=\"ql-syntax\" spellcheck=\"false\">1{\n2  \"models\": [\n3    {\n4      \"accountId\": \"YOUR CLOUDFLARE ACCOUNT ID\",\n5      \"apiKey\": \"YOUR CLOUDFLARE API KEY\",\n6      \"contextLength\": 2400,\n7      \"completionOptions\": {\n8        \"maxTokens\": 500\n9      },\n10      \"model\": \"@cf/meta/llama-3-8b-instruct\",\n11      \"provider\": \"cloudflare\",\n12      \"title\": \"Llama 3 8B\"\n13    }\n14  ],\n15  \"tabAutocompleteModel\": {\n16    \"accountId\": \"YOUR CLOUDFLARE ACCOUNT ID\",\n17    \"apiKey\": \"YOUR CLOUDFLARE API KEY\",\n18    \"model\": \"@hf/thebloke/deepseek-coder-6.7b-base-awq\",\n19    \"provider\": \"cloudflare\",\n20    \"title\": \"DeepSeek 7b\"\n21  },\n22}\n</pre><p><br></p><p>修改 accountId 为你自己的账户 id, apiKey 是你刚刚保存的 key 值.</p><p>账户 id 获取比较麻烦. 你必须要有一个 Cloudflare 下的域名或者一个 Worker, 具体可以参照官方文档和这两个页面:</p><ul><li><a href=\"https://developers.cloudflare.com/fundamentals/setup/find-account-and-zone-ids/\" rel=\"noopener noreferrer\" target=\"_blank\">官方文档</a></li><li><a href=\"https://docs.thorn.red/articles/6d89ow4vgfghgnn8\" rel=\"noopener noreferrer\" target=\"_blank\">参考 1</a></li><li><a href=\"https://community.cloudflare.com/t/where-can-i-find-my-account-id/164431\" rel=\"noopener noreferrer\" target=\"_blank\">参考 2</a></li></ul><p>model&nbsp;字段的值可以自行修改, 不同的模型需要的算力不同, 对&nbsp;神经元&nbsp;的计费方式也会有影响.</p><p>如果你发现免费额度不足以覆盖, 可以修改为低一级的模型.</p><p>点击链接可以看到完整的模型列表:</p><p><a href=\"https://developers.cloudflare.com/workers-ai/models\" rel=\"noopener noreferrer\" target=\"_blank\">Models | Cloudflare Workers AI docs</a>&nbsp;developers. Cloudflare. Com</p><p>Browse our entire catalog of models:</p><h2>总结</h2><p><br></p><p>Llama 3 有不错的对话效果, 但是速度比较慢, 代码补全也比较笨.</p><p>Starcoder 2 速度要快得多, 补全效果也更好, 但对文本的处理能力偏弱.</p><p>个人推荐使用 Cloudflare 的 llama 3 模型进行对话, 本地运行 starcoder 2 进行补全, 是比较完美的解决方案.</p><p>本文出自：<a href=\"https://ray-d-song.com/zh-cn/blog/continue-free-ai-code-assistant/\" rel=\"noopener noreferrer\" target=\"_blank\">使用 Continue 部署免费的 AI 代码助手</a></p>","content_text":"过去的一年, 我一直在使用 GitHub Copilot 作为代码助手, 它提供了补全、问答等功能, 付费订阅 10 美元 / 月.\n\n说实话这个价格并不算贵, 但最近 Copilot 对问答范围进行了限制, 导致很多与开发相关的问题无法得到解答.\n\n后续我也尝试了 Codium AI, 响应速度不尽人意.\n\n最近发现了一个开源的代码助手 Continue, 支持调用本地和云端的模型, 包括 Cloudflare 的 worker AI.\n\n\n介绍\n\n\n\n\nContinue 本质是个帮你向大模型发起请求的客户端.\n\n当你编写代码的时候, 它会获取代码的上下文, 向模型发起请求, 获取结果后处理等待你的命令 (tab), 再拼接到代码中去.\n\n\n本地部署\n\n\n\n\n本地部署可以使用 Ollama 或 lmstudio.\n\n这是两个非常方便的大模型集成工具, 同时支持 Mac、windows 以及 Linux, 帮助你快速在本地运行大模型.\n\n这里我选择在 Mac 上运行 Ollama, 在官网 Ollama 可以下载到安装包, 安装过程中记得勾选命令行工具.\n\nOllama ollama. Com\n\nGet up and running with large language models.\n\n[https://ollama.com/public/og.png]\n\n\n配置要求\n\n\n\n\n配置要求取决于你要使用的模型, 这里推荐 llama 3.\n\nLlama 3 是 meta 开源的模型, 也是目前最强的开源模型, 可以获得比较好的补全效果.\n\n模型有两个变体, 8 B 需要至少 16 g 的内存和 8 g 的显存, 70 B 则需要 64 g 以上的内存.\n\n默认运行的 8 B 版本即可满足正常需要.\n\n\n运行模型\n\n\n\n\n安装完成后进入 terminal, 输入以下命令获取 llama 3 模型:\n\n1ollama run llama3\n\n\n\n\n\n\n\n\n模型拉取完成后就会自动运行.\n\n测试用中文让他编写一个 python 版的 hello-world.\n\n[https://r2.ray-d-song.com/2024/09/6d3a74f153d36136f7ae81586e8315fb.png]\n\n输入 /bye 可以退出运行.\n\n\n配置 CONTINUE 插件\n\n\n\n\n安装 vscode plugin\n\n安装完成左侧边栏会展示 Continue 的对话框:\n\n[https://r2.ray-d-song.com/2024/09/ea18b185908bda96720414fcad005f10.png]\n\n这个时候是无法对话的, 因为还没有选择模型, 点击 Select model -> Add Model, 向下拉, 找到:\n\n[https://r2.ray-d-song.com/2024/09/58d456bc4ba21cacbb2f2c2b076722de.png]\n\n然后再选择第一个 Autodetect 选项:\n\n[https://r2.ray-d-song.com/2024/09/05191d0410e4f23c30285563b15c54dc.png]\n\n返回对话, 选择 Ollama 选项:\n\n[https://r2.ray-d-song.com/2024/09/f82f1758e1ea1f85cf5cd9db3913e11d.png]\n\n确保你的模型正在后台运行, 接下来就可以开始对话了.\n\n[https://r2.ray-d-song.com/2024/09/2880fe2110c014bc1b6e736d41bb0e71.png]\n\n\n自动补全设置\n\n\n\n\n虽然现在已经可以完成对话, 但还是无法补全.\n\n[https://r2.ray-d-song.com/2024/09/c072cad8863c2a50f9202e05e3442d44.png]\n\n因为补全默认使用的是 Starcoder2 3b 模型, Starcoder 是一个针对代码场景针对性强化的模型.\n\n你可以使用以下命令启动该模型.\n\n1 1ollama run starcoder2\n\n\n\n\n\n\n\n\n模型启动后就可以触发补全.\n\n如果你的机器性能不足以同时运行两个模型, 你也可以设置补全也使用 llama3:\n\n在插件侧边栏的角落, 找到这个小齿轮图标\n\n[https://r2.ray-d-song.com/2024/09/d3b06fcec439af8e2cec4d360f2663c2.png]\n\n点击就会进入配置文件 (也可以打开~/. Continue/config. Json),\n将 tabAutocompleteModel 字段的两个高亮行的值修改为 llama3 即可.\n\n1\"tabAutocompleteModel\": {\n2  \"title\": \"Starcoder2 3b\",\n3  \"provider\": \"ollama\",\n4  \"model\": \"starcoder2:3b\"\n5},\n\n\n\n\n\n如果你希望补全 markdown 之类的的文本文件, 那我建议使用 llama 3 进行补全\n\n\nCLOUDFLARE WORKER AI\n\n\n\n\n有一些轻薄本机型不能在本地运行大模型, 也可以使用 Cloudflare worker 的 AI 模型.\n\nCloudflare 提供了免费额度, 不过是以 神经元 作为单位, 可以在这个页面查看估算.\n\n每日的免费额度大体相当于 500 次调用, 每次 100 个入参字符和 500 个返回字符, 足以应对正常的开发场景.\n\n首先根据 Cloudflare 的指引, 创建 key\n\n然后打开配置文件, 增加一个 models 字段值, 并修改 tabAutocompleteModel 字段.\n\n1{\n2  \"models\": [\n3    {\n4      \"accountId\": \"YOUR CLOUDFLARE ACCOUNT ID\",\n5      \"apiKey\": \"YOUR CLOUDFLARE API KEY\",\n6      \"contextLength\": 2400,\n7      \"completionOptions\": {\n8        \"maxTokens\": 500\n9      },\n10      \"model\": \"@cf/meta/llama-3-8b-instruct\",\n11      \"provider\": \"cloudflare\",\n12      \"title\": \"Llama 3 8B\"\n13    }\n14  ],\n15  \"tabAutocompleteModel\": {\n16    \"accountId\": \"YOUR CLOUDFLARE ACCOUNT ID\",\n17    \"apiKey\": \"YOUR CLOUDFLARE API KEY\",\n18    \"model\": \"@hf/thebloke/deepseek-coder-6.7b-base-awq\",\n19    \"provider\": \"cloudflare\",\n20    \"title\": \"DeepSeek 7b\"\n21  },\n22}\n\n\n\n\n\n修改 accountId 为你自己的账户 id, apiKey 是你刚刚保存的 key 值.\n\n账户 id 获取比较麻烦. 你必须要有一个 Cloudflare 下的域名或者一个 Worker, 具体可以参照官方文档和这两个页面:\n\n * 官方文档\n * 参考 1\n * 参考 2\n\nmodel 字段的值可以自行修改, 不同的模型需要的算力不同, 对 神经元 的计费方式也会有影响.\n\n如果你发现免费额度不足以覆盖, 可以修改为低一级的模型.\n\n点击链接可以看到完整的模型列表:\n\nModels | Cloudflare Workers AI docs developers. Cloudflare. Com\n\nBrowse our entire catalog of models:\n\n\n总结\n\n\n\n\nLlama 3 有不错的对话效果, 但是速度比较慢, 代码补全也比较笨.\n\nStarcoder 2 速度要快得多, 补全效果也更好, 但对文本的处理能力偏弱.\n\n个人推荐使用 Cloudflare 的 llama 3 模型进行对话, 本地运行 starcoder 2 进行补全, 是比较完美的解决方案.\n\n本文出自：使用 Continue 部署免费的 AI 代码助手","date_published":"2024-10-13T08:37:38.668Z","_microfeed":{"is_audio":false,"is_document":false,"is_external_url":true,"is_video":false,"is_image":false,"web_url":"https://blog-ccino-cc.pages.dev/i/continue-ai-mR7PNdXxt5F/","json_url":"https://blog-ccino-cc.pages.dev/i/mR7PNdXxt5F/json/","rss_url":"https://blog-ccino-cc.pages.dev/i/mR7PNdXxt5F/rss/","guid":"mR7PNdXxt5F","status":"published","itunes:episodeType":"full","date_published_short":"Sun Oct 13 2024","date_published_ms":1728808658668}},{"id":"N3xXUfs6LUq","title":"书签「上上千」：用 Linkding 和 Trilium 自建服务轻松管理","attachments":[{"url":"https://blog.ccino.org/p/thousands-of-bookmarks-easily-manage-them-with-linkding-and-trilium-self-built-services/","mime_type":"text/html"}],"url":"https://blog.ccino.cc/i/thousands-of-bookmarks-easily-manage-them-with-linkding-and-trilium-self-built-services/","external_url":"https://blog.ccino.org/p/thousands-of-bookmarks-easily-manage-them-with-linkding-and-trilium-self-built-services/","content_html":"<h2>前言</h2><p><br></p><p>目前各大主流浏览器都会自带书签同步功能，无论是 PC 端还是移动端，只要登录一下账号就能轻松访问自己收藏的书签，可以说保存书签的成本越来越低。但是随着收藏内容的增多，使用书签的成本反而会直线上升。</p><p>因为最近在少数派上看到了几篇网络书签的相关文章，就也想和大家分享一下目前我自己使用的一套书签方案。因为经常查各种资料，所以收藏的书签很多很杂，数量上应该有几千个，直接导致传统的浏览器收藏夹基本没法用，最后只能选了自建网络书签服务这条路，在用过许多开源方案后选择了 Linkding 和 Trilium 相结合的方案。如果你的书签数量较少，或是不具备自建服务的条件的话，本篇文章可能对你意义不大，大家可以酌情阅读。</p><p>因为文章中用到的 Linkding 和 Trilium 都属于自建服务，每个人喜欢的安装方式可能都有差别，网上相关的安装教程多如牛毛，所以我在文中并不会介绍相关内容，而是专注于使用经验方面的分享。当然，安装的话我建议直接根据我下文中给出的官方文档中的步骤进行，以免网上中文教程版本滞后。</p><h2>传统书签收藏的痛点</h2><h3>查找困难</h3><p><br></p><p><strong>查找困难</strong>是传统浏览器书签收藏系统最大的痛点，究其本质我个人归纳为可用于查找的信息维度过少。少到什么程度呢，你可以试试导出自己浏览器的书签，一般会得到一个 html 文件，以 Edge 浏览器为例，通常一条书签记录会包含如下字段：</p><p>名称含义folder所在文件夹名称（可多层）href书签记录的网址add_date保存书签的时间戳（部分浏览器并不支持书签按时间排序）icon书签图标（对搜索而言为无效信息）title书签名称</p><p>看着有五个字段，但实际常被用于搜索的也就两条，文件夹名称和书签名称。从使用的角度来说，三层文件夹应该是普通人能接受的极限了（我自己只能接受一层文件夹）。单单靠少量的文件夹分类的话肯定是不现实的，因为当书签量级上来之后，单个文件夹中的书签数量不可避免地会增加，依旧会面临翻找困难的情况。这时候想要提高查找速度就必定要用到搜索，文件夹层级有限的情况下就只能在书签名称上做文章，较为可行的方法就是在书签名称的最后加关键词，加的越多越详细，后期搜索到的可能性就越高。理论上每次添加书签时都严格按照提前规划好的文件夹层级以及关键词命名规则的话，最后也能得到一份高度可用的收藏夹。</p><h3>整理困难</h3><p><br></p><p>但这就引申出其另一个痛点，<strong>整理困难</strong>。首先第一个困难就是文件夹的分类，事物的联系具有多样性，每一个书签指向的内容都不是非此即彼的，很难归纳出一个所有人通用的文件夹分层结构，最多在你长时间的使用中逐渐妥协为一个相对适合你自己使用的框架。但即便如此，你也总会遇到许多让你纠结其归属的书签，为了方便使用可能某一个书签需要同时在多个文件层级中保存。除了文件夹更需要费脑筋的是书签的命名，直接用默认的网页名称固然方便，但搜索的时候就难免力不从心。</p><p>浏览网页的时候看到感兴趣的内容随手点一下地址栏旁的收藏按钮，这应该是很多人使用收藏夹的本能，每次加个书签都深思熟虑其文件夹归属和详尽的关键词命名未免过于严苛，流程繁琐不说，更重要的是可能会打断自己的思路，长时间下来定期整理总是逃不掉的。这时候就不得不品味浏览器收藏夹那简陋的多选和拖拽移动功能了，少量使用体验完美，量大就是折磨了。如果追求高可用性，可能还需要单独修改每个书签的名称，光是想想就够头疼的。</p><h3>快照</h3><p><br></p><p>接下来的痛点严格来说算是我个人的痛点，和浏览器中的收藏夹系统本身无关，属于我希望它有但是它没有的功能。首当其冲的就是<strong>快照</strong>功能，虽然这个功能可以通过安装浏览器插件的形式轻松实现，但是插件保存的快照终究是另外存放的，需要自己手动保存不说，后期想与书签对上还免不了一番折腾，便利性上终究差点意思。</p><h3>多端同步</h3><p><br></p><p>最后一个痛点则是<strong>多端同步</strong>，虽然开篇就说了目前的浏览器都支持多端同步，但是很尴尬的一点就是，我在各个平台上使用的浏览器并不相同，电脑上还好说，直接用 Edge 就行，但是手机、平板之类的移动设备就比较麻烦了，可能会存在多个浏览器混用的情况，这时候依托于浏览器账户的收藏夹同步功能就捉襟见肘了。</p><h2>网络书签服务：<strong>Linkding</strong></h2><p><br></p><p><a href=\"https://sspai.com/link?target=https%3A%2F%2Flinkding.link%2F\" rel=\"noopener noreferrer\" target=\"_blank\">Linkding</a>&nbsp;是目前为止我用下来最好用的开源网络书签服务，基本解决了我上面提到的四个痛点。不过这个服务有几个比较明显的缺点，没法接受的话就不用看下去了。</p><ul><li>操作界面只有英文。当然，中文书签是支持的，只是设置、按钮等只有英文。我不太清楚有没有大佬封装过中文的镜像，暂时没有查找过。不过其实对使用的影响几乎没有，总共也就那么几个单词，并没有什么太高的门槛。</li><li>部分服务，类似书签图标等需要使用魔法或是自行配置相关参数。核心功能其实都是服务器本地运行的，不使用魔法体感下来影响不大。</li><li>默认界面比较朴实，基本就是一堆网页元素的简单堆积。理论上如果你懂 css 的话可以在设置中自定义 css 语句来修改显示效果，想要花里胡哨也是可以实现的。</li></ul><p><img src=\"https://pic.ccino.win/OB/2024/10/1728626485202410111401602.png\"></p><h3>标签系统</h3><h4>概念介绍</h4><p><br></p><p>先说一下 Linkding 最核心的标签（Tag）系统，与传统的文件夹系统不同，标签系统更加自由和发散。文件夹系统是线性的，一个书签只能从属于一个文件夹，一个子文件夹只能从属于一个父文件夹；而标签系统则是多对多的关系，一个书签可以同时拥有无数个标签，各个标签之间都是同级的，互相之间没有从属关系。</p><p><img src=\"https://pic.ccino.win/OB/2024/10/1728626500202410111401844.png\">&nbsp;乍一看好像区别不大，但实际使用时却是天壤之别。举一个简单的例子，假如我需要在书签收藏中找到 PyMongo 的官方文档，那在 Linkding 中只需要先点击「@文档」标签，再点击「mongodb」标签，就能轻松筛选出所有符合的结果。对于经常需要比对同一类型资料，或是「模糊搜索」的用户可谓是神器。</p><p><img src=\"https://pic.ccino.win/OB/2024/10/1728626513202410111401566.png\"></p><h4>使用技巧</h4><p><br></p><p>之前提到过 Linkding 界面只支持英文，这一点对其标签的排列顺序其实也存在着影响。观察 Tags 这一列就能发现，标签是按照其首字母分行排列的，对于英文标签这样排列非常合理，但是如果使用全中文标签的话就会遇到一个问题，那就是所有中文开头的标签都会被归类到同一行。</p><p>这个特性对中文标签来说非常致命，毕竟所有标签挤在一行想要快速找出其中一个的难度就会大大提高。不过解决的方法非常简单，就是根据每个中文标签开头字符的拼音为其添加一个首字母，类似「x 下载」这样的形式。进一步地，我们可以利用其排列特性来实现一些别的需求，比如说对于常用链接可以添加一个「! 常用」的标签，这样该标签就会默认出现在标签区域的第一行。</p><p><img src=\"https://pic.ccino.win/OB/2024/10/1728626524202410111402739.png\">&nbsp;标签机制因为其灵活性，刚开始使用的时候可能会有点无所适从，不知从何处入手，收藏的时候可能还会产生一些不必要的纠结，比如这个词要不要设置成标签等。这些其实都是正常情况，我刚开始用的时候也是这样，主要是怕用的久了之后杂乱的标签和之前的文件夹一样难以整理。作为一个长期使用过的用户，我给出两条参考建议：</p><ul><li>每个链接都要分配一个大类，比如可以根据其功能分成「@工具」「@论坛」「@娱乐」等，这些大类不必一次整理清楚，可以想到什么就写什么，不过为了和其他标签形成区分，推荐以「@」开头。当某一天你觉得无论是什么链接都能划分到这些大类之后，就可以开始尝试将它们合并精简，最终将大类个数控制在个位数即可。这些大类标签其实充当着传统书签系统中的一级文件夹的角色，如果你平常就有整理书签文件夹的习惯，可以直接把这些文件夹的名称套用过来。</li><li>除了大类标签外，每一个链接都尽可能地将所有其涉及的特征点作为标签加上去。加标签的时候不需要有任何的顾虑，所有你脑海中当时跳出来的词汇都可以作为标签加上去，多多益善。你加的标签越多，后续使用的时候找起来就越简单，唯一要注意的是同义词不要重复出现，能复用标签时就尽量不要再创建一个新的类似标签。</li></ul><h3>书签结构</h3><h4>概念介绍</h4><p><br></p><p>默认安装参数下，Linkding 的数据库使用的是 SQLite，所以书签数据是以「. Sqlite 3」后缀的文件格式保存的。只要用 SQLiteStudio 之类的软件打开就能查看和修改其中的数据。</p><p>我们可以先看一下 Linkding 中书签的所有字段：</p><p><img src=\"https://pic.ccino.win/OB/2024/10/1728626535202410111402994.png\"></p><p>名称含义id书签主键url书签记录的网址title书签名称description书签介绍website_title网页默认名称website_description网页默认介绍unread是否标为待阅读is_archived是否标为已归档（即不常用书签或失效书签）web_archive_snapshot_urlInternet Archive 中该网页的快照地址favicon_file图标文件保存地址notes关于该书签的笔记（支持 Markdown 语法）preview_image_file预览图片保存地址tags标签与书签的对应关系在一张单独的关系表中，上图中并没有体现</p><p>与标签相关的字段比较简单，就一张标签信息表和标签 - 书签多对多关系表。&nbsp;<img src=\"https://pic.ccino.win/OB/2024/10/1728626549202410111402676.png\"></p><p><img src=\"https://pic.ccino.win/OB/2024/10/1728626559202410111402405.png\"></p><p>常用的几个书签属性在使用浏览器插件收藏网页的时候就可以直接编辑。</p><p><img src=\"https://pic.ccino.win/OB/2024/10/1728626577202410111402186.png\"></p><p>与传统书签系统中可怜的两三个搜索维度相比，Linkding 明显领先太多了，表格中出现的字段内容基本都可以作为搜索元素来进行查找。也就是说，无论是标题、介绍，还是笔记中出现的文本都可以作为关键词来检索，在保持标题简单明了的前提下依旧可以获得远超传统书签的搜索维度。</p><h4>使用技巧</h4><p><br></p><p>因为书签的特征不再只有标题这一个维度，所以保存书签时一般不需要修改网页默认的名称，就算要修改也建议将其修改得更加简洁明了。为了便于后续搜索或再阅读，description 以及 notes 需要作为修改的重点。大部分情况下 description 可以使用网页默认描述，要手动编辑的话也尽量用客观的语句描述其实际内容。而 notes 则可以自由一点，并且其本身支持 Markdown 语法，显示效果也会好很多。以我自己为例给大家一些参考，某些知识类的文章可能要点就那么几句话或是几行代码，没什么单独开篇笔记收藏的必要，就可以直接写在 notes 中；找到合适的素材时可以在 notes 中记录下该素材的用法或当时的灵感思路等。总结一下就是三点：</p><ul><li>title：简洁明了，一般保持默认即可。</li><li>description：客观描述网页内容，一般保持默认即可。</li><li>notes：记录网页中的核心知识点、收藏的理由、灵感思路等，一般留空即可。</li></ul><p>Unread 和 is_archived 这两个字段很好理解，前者就是很多浏览器自带的待阅读功能，后者则是归档功能，就是把已经失效或是长时间不会再用到的书签隐藏到一个单独的分类中。两者都可以通过首页的 Bookmarks 按钮快速切换，各自都有其独立的分区。</p><p><img src=\"https://pic.ccino.win/OB/2024/10/1728626612202410111403269.png\">&nbsp;归档这个功能我很喜欢，相较于直接把失效书签删除，把它们关小黑屋总有一种一朝一日它们还能重见天日的错觉。反正也不占什么空间，留着当个念想总是好的。</p><p>相较于归档，待阅读的花样就多一点。我一般不把它作为待阅读列表使用，而是更接近近期常用的概念，或者说是素材库，把搜索出来也许会用的书签统一标为待阅读。Linkding 的批处理功能是相当优秀的，如下图所示，归档、删除、增加标签、移除标签、标记为已读、标记为未读，这六个核心功能都支持批处理操作，效率远超浏览器收藏夹。</p><p><img src=\"https://pic.ccino.win/OB/2024/10/1728626625202410111403005.png\"></p><h4>拓展</h4><p><br></p><p>上面提到过的三张表 bookmarks_bookmark、bookmarks_tag、bookmarks_bookmark_tags 是 Linkding 中最核心的数据表，它们都在 Linkding 数据文件夹的 db. Sqlite 3 这一数据库文件中。因为该文件中数据都是公开的，没有加密一说，操作空间就很大了。</p><p><img src=\"https://pic.ccino.win/OB/2024/10/1728626634202410111403134.png\">&nbsp;Linkding 虽然自带导出书签为 html 文件的功能，但是因为其本身并没有文件夹的概念，所以使用其导出的文件导入到 Edge 浏览器就会存在水土不服的情况。我的服务器有一次宕机之后系统出了点问题，但书签每天都要用，就写了个 python 脚本将其按照特定的要求导出为 Edge 可识别的 html 文件应急。具体的导出要求是以「@」开头的标签为文件夹，将所有书签分类放置在其中，同时将每个书签的标签汇总后添加到书签名称的最后。脚本用起来非常简单，与 db. Sqlite 3 文件放置在同一目录下运行即可。</p><p>这里给出这个案例有多方面的考虑，一是向大家展示一下 Linkding 这种用单个数据库文件存储数据对于个人用户的便利性，因为需要备份的数据就那么几个，甚至以后 Linkding 不再维护，用户依旧可以很轻松地转移到任何其他平台；二是给大家提供另一种 Linkding 的使用思路，可以将其作为单纯的整理工具，使用其强大的批处理界面以及优秀的数据库结构来整理汇总自己的书签，最后导出到更常使用的平台。</p><pre class=\"ql-syntax\" spellcheck=\"false\">import datetime\nimport sqlite3\nfrom typing import Optional\n\nfrom dateutil.parser import parse\nfrom lxml import etree\n\nclass DatabaseManager:\n\n    \n\"\"\"\n\n\n    用于读取书签数据库文件\n\n\n    \"\"\"\n\n    \ndef __init__(self, db_file: str):\n        self.conn = sqlite3.connect(db_file)\n        self.cursor = self.conn.cursor()\n\n    def __enter__(self):\n        return self\n\n    def __exit__(self, exc_type, exc_val, exc_tb):\n        self.cursor.close()\n        self.conn.close()\n\n    def __iter__(self):\n        self.cursor.execute(\"select * from bookmarks_bookmark\")\n        return self\n\n    def __next__(self):\n        # 获取单条书签数据\n        tem = self.cursor.fetchone()\n        if tem is None:\n            raise StopIteration\n        # 根据书签 id 查找对应的标签\n        c = self.conn.cursor()\n        cmd = \"select bookmarks_tag.name from bookmarks_bookmark_tags \" \\\n              \"join bookmarks_tag on bookmarks_bookmark_tags.tag_id = bookmarks_tag.id \" \\\n              \"where bookmarks_bookmark_tags.bookmark_id = {}\"\n        tags = c.execute(cmd.format(tem[0])).fetchall()\n        tags = [tag[0] for tag in tags]\n        res = list(tem)\n        # 将标签数据以 list 的形式添加到原本书签数据的最后\n        res.append(tags)\n        c.close()\n        return res\n\ndef timestr_to_timestamp(timestr: str) -&gt; int:\n\n    \n\"\"\"\n\n\n    将字符串形式的时间转为时间戳\n\n\n    \n:param\n timestr:\n\n\n    \n:return\n:\n\n\n    \"\"\"\n\n    \nreturn int(parse(timestr + \"+0000\").timestamp())\n\nclass HtmlGenerator:\n\n    \n\"\"\"\n\n\n    用于构造 Edge 浏览器支持导入的书签文件\n\n\n    主要参考 Edge 浏览器导出的 html 文件格式\n\n\n    \"\"\"\n\n    \ndef __init__(self):\n        self.root = etree.Element(\"DT\")\n        # 当前时间戳，用于后续一些需要时间戳的场景\n        # self.t = str(int(datetime.datetime.now(datetime.UTC).timestamp()))  # 高版本python\n        self.t = str(int(datetime.datetime.utcnow().timestamp()))\n        h3 = etree.SubElement(self.root, \"H3\",\n                              {\n                                  \"ADD_DATE\": self.t,\n                                  \"LAST_MODIFIED\": self.t,\n                                  \"PERSONAL_TOOLBAR_FOLDER\": \"true\",\n                              })\n        h3.text = \"收藏夹栏\"\n        # 后续用于存放各文件夹和书签的元素\n        self.dl = etree.SubElement(self.root, \"DL\")\n        # 存放各书签文件夹对应的元素\n        self.folder = {}\n\n    def generate_folder(self, name: str):\n\n        \n\"\"\"\n\n\n        创建一个书签文件夹对应的元素\n\n\n        \n:param\n name:\n\n\n        \n:return\n:\n\n\n        \"\"\"\n\n        \ndt = etree.SubElement(self.dl, \"dt\")\n        h3 = etree.SubElement(dt, \"H3\",\n                              {\n                                  \"ADD_DATE\": self.t,\n                                  \"LAST_MODIFIED\": self.t,\n                              })\n        h3.text = name\n        dl = etree.SubElement(dt, \"DL\")\n        self.folder[name] = dl\n\n    def add_bookmark(self, url: str, add_date: str, title: str, folder: Optional[str] = None):\n\n        \n\"\"\"\n\n\n        添加书签到指定父元素中\n\n\n        \n:param\n url:\n\n\n        \n:param\n add_date:\n\n\n        \n:param\n title:\n\n\n        \n:param\n folder:\n\n\n        \n:return\n:\n\n\n        \"\"\"\n\n        \nif folder is None:\n            dt = etree.SubElement(self.dl, \"dt\")\n        else:\n            dt = etree.SubElement(self.folder[folder], \"dt\")\n        a = etree.SubElement(dt, \"A\", {\n            \"HREF\": url,\n            \"ADD_DATE\": add_date,\n        })\n        a.text = title\n\n    def tostring(self):\n\n        \n\"\"\"\n\n\n        将根元素转化为字符串\n\n\n        \n:return\n:\n\n\n        \"\"\"\n\n        \nreturn etree.tostring(self.root, encoding=\"utf-8\", pretty_print=True).decode(\"utf-8\")\n\n    def handle_one(self, data: list):\n\n        \n\"\"\"\n\n\n        将数据库中导出的单条书签数据转为 html 元素\n\n\n        \n:param\n data:\n\n\n        \n:return\n:\n\n\n        \"\"\"\n\n        \n# 从原始数据中提取出部分需要的数据\n        url = data[1]\n        title = data[2]\n        add_date = str(timestr_to_timestamp(data[7]))\n        tags = data[17]\n        # 将所有标签合并为一个整体，添加到书签名的最后，方便后续在浏览器中查找\n        tag = \" \".join(tags)\n        if tag != \"\":\n            tag = \"【{}】\".format(tag)\n        title += tag\n        # 对于没有标签的书签直接将其置于收藏夹根目录下\n        if len(tags) == 0:\n            self.add_bookmark(url, add_date, title)\n            return\n        # 根据标签中带“@\"的标签名来对书签进行分类，归于不同的文件夹中\n        for tag in tags:\n            if \"@\" in tag and tag[0] == \"@\":\n                if self.folder.get(tag) is None:\n                    self.generate_folder(tag)\n                self.add_bookmark(url, add_date, title, tag)\n\n    def export_to_edge(self, db: str, path: str):\n\n        \n\"\"\"\n\n\n        导出为 Edge 浏览器支持的 html 文件\n\n\n        \n:param\n db:\n\n\n        \n:param\n path:\n\n\n        \n:return\n:\n\n\n        \"\"\"\n\n        \nwith DatabaseManager(db) as d:\n            for data in d:\n                self.handle_one(data)\n        html_template = \"\"\"&lt;!DOCTYPE NETSCAPE-Bookmark-file-1&gt;\n&lt;!-- This is an automatically generated file.\n     It will be read and overwritten.\n     DO NOT EDIT! --&gt;\n&lt;META HTTP-EQUIV=\"Content-Type\" CONTENT=\"text/html; charset=UTF-8\"&gt;\n&lt;TITLE&gt;Bookmarks&lt;/TITLE&gt;\n&lt;H1&gt;Bookmarks&lt;/H1&gt;\n&lt;DL&gt;{}&lt;/DL&gt;\"\"\"\n        with open(path, \"w\", encoding=\"utf-8\") as f:\n            f.write(html_template.format(self.tostring()))\n\nif __name__ == '__main__':\n    h = HtmlGenerator()\n    h.export_to_edge(\"db.sqlite3\", \"bookmarks.html\")\n</pre><p>Copy</p><h3>快照</h3><p><br></p><p>以书签系统为核心的网络书签服务其实有很多，有不少甚至在颜值上吊打 Linkding，但最后还是 Linkding 获得了我的青睐，很关键的一点就是它提供的双重快照系统。</p><p><img src=\"https://pic.ccino.win/OB/2024/10/1728626656202410111404001.png\">&nbsp;上面那个 Internet Archive 是一个在线服务，开启后服务器会调用该网站的 API 接口将你收藏的网址链接提交上去，然后该网站就会抓取该时刻书签网址上的内容进行保存。Internet Archive 是一个很有名的非营利性数字图书馆，保存着互联网上不计其数的网页内容，有兴趣的可以去了解捐赠一波。</p><p>当然，这个快照的局限性非常大，我一般不作为主力使用。首先，想要正常访问 Internet Archive 的话需要使用一点魔法；其次它的抓取对于一些需要登录才能访问相关数据的网站基本没用，就国内互联网这个环境，很多抓取都没有意义。</p><p>下面这个快照则是纯正的本地快照，不过该功能只有特定版本的 Docker 镜像才会开启，安装时最好选择&nbsp;<a href=\"https://sspai.com/link?target=https%3A%2F%2Flinkding.link%2Finstallation%2F\" rel=\"noopener noreferrer\" target=\"_blank\">latest-plus</a>&nbsp;版本。该功能开启后服务器会自动抓取书签的网页内容，和 Internet Archive 类似，需要登录验证的网页同样会抓取失败。</p><p><img src=\"https://pic.ccino.win/OB/2024/10/1728626664202410111404623.png\">&nbsp;不过这个是有解决办法的，因为 Linkding 同时提供了手动上传文件作为快照的备选方案。点开书签的详情页，会有一个「Upload file」的按钮，点击即可上传本地文件。这里对于文件的格式没有任何要求，但还是推荐上传可以用浏览器直接打开的 pdf、txt、html 等格式，不然点击浏览按钮后会直接对该文件进行下载。对于文章类的网页推荐直接 Ctrl+P 打印为 pdf 上传，其他网页则推荐使用浏览器插件打包为单个 html 文件后上传。</p><p><img src=\"https://pic.ccino.win/OB/2024/10/1728626674202410111404266.png\">&nbsp;对于一些比较重要的文章其实我个人会更加倾向于直接全文收藏到 Trilium 笔记中，算是对网页快照的一个补强，具体内容会在 Trilium 章节中详细介绍。</p><h3>其他</h3><p><br></p><p>Linkding 设置里面还有两个可能会用到的功能，这里给大家介绍一下。首先是 Auto Tagging，顾名思义，就是可以根据你预设的规则在收藏书签时自动为其加上某些标签，这里以哔哩哔哩的专栏为例。&nbsp;<img src=\"https://pic.ccino.win/OB/2024/10/1728626719202410111405595.png\"></p><p>另一个是 Custom CSS，自定义页面的 CSS，也就是可以更改页面布局。这个需要一点专业知识才能玩转，普通用户可以试着改改字体大小颜色等，比如说把书签颜色改成猛男粉。</p><p><img src=\"https://pic.ccino.win/OB/2024/10/1728626682202410111404405.png\">&nbsp;<img src=\"https://pic.ccino.win/OB/2024/10/1728626772202410111406430.png\"></p><p>最后提一下手机端如何收藏书签的问题，无论是安卓还是 iOS 都有类似 PC 端浏览器插件的实现方式，具体参考<a href=\"https://sspai.com/link?target=https%3A%2F%2Flinkding.link%2Fhow-to%2F\" rel=\"noopener noreferrer\" target=\"_blank\">官方指南</a>。</p><h2>个人笔记服务：Trilium</h2><p><br></p><p><a href=\"https://sspai.com/link?target=https%3A%2F%2Fgithub.com%2Fbaddate%2Ftrilium%2Fwiki\" rel=\"noopener noreferrer\" target=\"_blank\">Trilium</a>&nbsp;是一个可以多端同步的笔记软件。市面上开源的笔记软件其实很多，Trilium 应该算是比较小众的，主要是功能太多，没有耐心很难体会到它的妙处。我其实只能算是它的初级用户，只会一些比较简单的操作，许多高级特性都没有尝试过。Trilium 的功能完全可以单独胜任个人数据库构建的重任，但下文中我只会简单介绍一个与书签相关的特性。</p><p>之前介绍过 Linkding 的快照功能，里面说了一些特殊情况下的网页备份方法，但对于图文类的文章我其实很少使用其手动上传功能。主要原因是我个人更加喜欢用 Markdown 的格式来记录图文数据，于知识记录而言，pdf 和 html 终究是不够纯粹和方便。</p><p>将网页转化为纯粹的图文数据这一需求曾经困扰过我很久，我尝试过很多不同的开源项目或浏览器插件，结果都不尽如人意，总是会存在一些小问题，直到偶然尝试了一下 Trilium 的浏览器插件这一问题才算是落下帷幕。</p><p><img src=\"https://pic.ccino.win/OB/2024/10/1728626786202410111406659.png\">&nbsp;插件上有一个选项是「Save whole page」，可以直接把当前网页上的图文保存为一篇本地笔记，识别率相当高，基本不需要修改就可以得到一篇排版正确的笔记，不过它只会保存文章的主体或是帖子的主楼，也就是说评论和跟帖并不会被保存下来。绝大部分博客类的网站都能完美适配，部分站点可能会存在图片无法显示的问题，不想一张张图片手动复制粘贴的话可以参照 Linkding 快照部分讲解的方法。</p><p>这项功能完美补全了 Linkding 在待阅读方面的短板。我目前对于一篇有价值的网页文章的基本操作流程就是先用 Linkding 的浏览器插件保存书签，然后使用 Trilium 的浏览器插件保存图文到笔记系统。保存书签是为了方便后续深入阅读时可以随时查看有价值的评论或跟帖，保存图文则是将其作为待阅读的素材，后续阅读时可以很方便的删改整理为自己的笔记。</p><p>插件上另一个常用的选项是「Save windows’ tabs as a list」，可以将当前浏览器窗口中所有 Tab 页的网页链接以列表的形式保存到笔记中，设计这个功能的人绝对是懂工作的，后续想要回到同样的工作场景只需要打开对应笔记点击列表中的链接就能完美复原浏览器状态。</p><p><img src=\"https://pic.ccino.win/OB/2024/10/1728626794202410111406881.png\">&nbsp;这可以算是一个额外的专项任务收藏夹，可以将一些日常工作场景下会打开的链接提前收藏好，需要用到时就能快速开始该场景下的工作，结合上文中我提到过的使用 Linkding 的 unread 列表来作为近期任务工作区，可以极大的提高工作效率。</p><h2>后记</h2><p><br></p><p>Linkding 和 Trilium 这种本地服务最重要的就是保证数据安全，如果想要使用我的这套方案请务必设置好服务器数据的<strong>定期备份</strong>，数据一旦丢失，想要再次补全的代价实在过于高昂，请务必慎重。</p><p>本文出自：<a href=\"https://sspai.com/post/92796\" rel=\"noopener noreferrer\" target=\"_blank\">书签「上上千」：用 Linkding 和 Trilium 自建服务轻松管理 - 少数派</a></p><p><br></p>","content_text":"前言\n\n\n\n\n目前各大主流浏览器都会自带书签同步功能，无论是 PC\n端还是移动端，只要登录一下账号就能轻松访问自己收藏的书签，可以说保存书签的成本越来越低。但是随着收藏内容的增多，使用书签的成本反而会直线上升。\n\n因为最近在少数派上看到了几篇网络书签的相关文章，就也想和大家分享一下目前我自己使用的一套书签方案。因为经常查各种资料，所以收藏的书签很多很杂，数量上应该有几千个，直接导致传统的浏览器收藏夹基本没法用，最后只能选了自建网络书签服务这条路，在用过许多开源方案后选择了\nLinkding 和 Trilium 相结合的方案。如果你的书签数量较少，或是不具备自建服务的条件的话，本篇文章可能对你意义不大，大家可以酌情阅读。\n\n因为文章中用到的 Linkding 和 Trilium\n都属于自建服务，每个人喜欢的安装方式可能都有差别，网上相关的安装教程多如牛毛，所以我在文中并不会介绍相关内容，而是专注于使用经验方面的分享。当然，安装的话我建议直接根据我下文中给出的官方文档中的步骤进行，以免网上中文教程版本滞后。\n\n\n传统书签收藏的痛点\n\n\n查找困难\n\n\n\n\n查找困难是传统浏览器书签收藏系统最大的痛点，究其本质我个人归纳为可用于查找的信息维度过少。少到什么程度呢，你可以试试导出自己浏览器的书签，一般会得到一个\nhtml 文件，以 Edge 浏览器为例，通常一条书签记录会包含如下字段：\n\n名称含义folder所在文件夹名称（可多层）href书签记录的网址add_date保存书签的时间戳（部分浏览器并不支持书签按时间排序）icon书签图标（对搜索而言为无效信息）title书签名称\n\n看着有五个字段，但实际常被用于搜索的也就两条，文件夹名称和书签名称。从使用的角度来说，三层文件夹应该是普通人能接受的极限了（我自己只能接受一层文件夹）。单单靠少量的文件夹分类的话肯定是不现实的，因为当书签量级上来之后，单个文件夹中的书签数量不可避免地会增加，依旧会面临翻找困难的情况。这时候想要提高查找速度就必定要用到搜索，文件夹层级有限的情况下就只能在书签名称上做文章，较为可行的方法就是在书签名称的最后加关键词，加的越多越详细，后期搜索到的可能性就越高。理论上每次添加书签时都严格按照提前规划好的文件夹层级以及关键词命名规则的话，最后也能得到一份高度可用的收藏夹。\n\n\n整理困难\n\n\n\n\n但这就引申出其另一个痛点，整理困难。首先第一个困难就是文件夹的分类，事物的联系具有多样性，每一个书签指向的内容都不是非此即彼的，很难归纳出一个所有人通用的文件夹分层结构，最多在你长时间的使用中逐渐妥协为一个相对适合你自己使用的框架。但即便如此，你也总会遇到许多让你纠结其归属的书签，为了方便使用可能某一个书签需要同时在多个文件层级中保存。除了文件夹更需要费脑筋的是书签的命名，直接用默认的网页名称固然方便，但搜索的时候就难免力不从心。\n\n浏览网页的时候看到感兴趣的内容随手点一下地址栏旁的收藏按钮，这应该是很多人使用收藏夹的本能，每次加个书签都深思熟虑其文件夹归属和详尽的关键词命名未免过于严苛，流程繁琐不说，更重要的是可能会打断自己的思路，长时间下来定期整理总是逃不掉的。这时候就不得不品味浏览器收藏夹那简陋的多选和拖拽移动功能了，少量使用体验完美，量大就是折磨了。如果追求高可用性，可能还需要单独修改每个书签的名称，光是想想就够头疼的。\n\n\n快照\n\n\n\n\n接下来的痛点严格来说算是我个人的痛点，和浏览器中的收藏夹系统本身无关，属于我希望它有但是它没有的功能。首当其冲的就是快照功能，虽然这个功能可以通过安装浏览器插件的形式轻松实现，但是插件保存的快照终究是另外存放的，需要自己手动保存不说，后期想与书签对上还免不了一番折腾，便利性上终究差点意思。\n\n\n多端同步\n\n\n\n\n最后一个痛点则是多端同步，虽然开篇就说了目前的浏览器都支持多端同步，但是很尴尬的一点就是，我在各个平台上使用的浏览器并不相同，电脑上还好说，直接用 Edge\n就行，但是手机、平板之类的移动设备就比较麻烦了，可能会存在多个浏览器混用的情况，这时候依托于浏览器账户的收藏夹同步功能就捉襟见肘了。\n\n\n网络书签服务：LINKDING\n\n\n\n\nLinkding 是目前为止我用下来最好用的开源网络书签服务，基本解决了我上面提到的四个痛点。不过这个服务有几个比较明显的缺点，没法接受的话就不用看下去了。\n\n * 操作界面只有英文。当然，中文书签是支持的，只是设置、按钮等只有英文。我不太清楚有没有大佬封装过中文的镜像，暂时没有查找过。不过其实对使用的影响几乎没有，总共也就那么几个单词，并没有什么太高的门槛。\n * 部分服务，类似书签图标等需要使用魔法或是自行配置相关参数。核心功能其实都是服务器本地运行的，不使用魔法体感下来影响不大。\n * 默认界面比较朴实，基本就是一堆网页元素的简单堆积。理论上如果你懂 css 的话可以在设置中自定义 css 语句来修改显示效果，想要花里胡哨也是可以实现的。\n\n[https://pic.ccino.win/OB/2024/10/1728626485202410111401602.png]\n\n\n标签系统\n\n概念介绍\n\n\n\n\n先说一下 Linkding\n最核心的标签（Tag）系统，与传统的文件夹系统不同，标签系统更加自由和发散。文件夹系统是线性的，一个书签只能从属于一个文件夹，一个子文件夹只能从属于一个父文件夹；而标签系统则是多对多的关系，一个书签可以同时拥有无数个标签，各个标签之间都是同级的，互相之间没有从属关系。\n\n[https://pic.ccino.win/OB/2024/10/1728626500202410111401844.png] 乍一看好像区别不大，但实际使用时却是天壤之别。举一个简单的例子，假如我需要在书签收藏中找到\nPyMongo 的官方文档，那在 Linkding\n中只需要先点击「@文档」标签，再点击「mongodb」标签，就能轻松筛选出所有符合的结果。对于经常需要比对同一类型资料，或是「模糊搜索」的用户可谓是神器。\n\n[https://pic.ccino.win/OB/2024/10/1728626513202410111401566.png]\n\n使用技巧\n\n\n\n\n之前提到过 Linkding 界面只支持英文，这一点对其标签的排列顺序其实也存在着影响。观察 Tags\n这一列就能发现，标签是按照其首字母分行排列的，对于英文标签这样排列非常合理，但是如果使用全中文标签的话就会遇到一个问题，那就是所有中文开头的标签都会被归类到同一行。\n\n这个特性对中文标签来说非常致命，毕竟所有标签挤在一行想要快速找出其中一个的难度就会大大提高。不过解决的方法非常简单，就是根据每个中文标签开头字符的拼音为其添加一个首字母，类似「x\n下载」这样的形式。进一步地，我们可以利用其排列特性来实现一些别的需求，比如说对于常用链接可以添加一个「!\n常用」的标签，这样该标签就会默认出现在标签区域的第一行。\n\n[https://pic.ccino.win/OB/2024/10/1728626524202410111402739.png] 标签机制因为其灵活性，刚开始使用的时候可能会有点无所适从，不知从何处入手，收藏的时候可能还会产生一些不必要的纠结，比如这个词要不要设置成标签等。这些其实都是正常情况，我刚开始用的时候也是这样，主要是怕用的久了之后杂乱的标签和之前的文件夹一样难以整理。作为一个长期使用过的用户，我给出两条参考建议：\n\n * 每个链接都要分配一个大类，比如可以根据其功能分成「@工具」「@论坛」「@娱乐」等，这些大类不必一次整理清楚，可以想到什么就写什么，不过为了和其他标签形成区分，推荐以「@」开头。当某一天你觉得无论是什么链接都能划分到这些大类之后，就可以开始尝试将它们合并精简，最终将大类个数控制在个位数即可。这些大类标签其实充当着传统书签系统中的一级文件夹的角色，如果你平常就有整理书签文件夹的习惯，可以直接把这些文件夹的名称套用过来。\n * 除了大类标签外，每一个链接都尽可能地将所有其涉及的特征点作为标签加上去。加标签的时候不需要有任何的顾虑，所有你脑海中当时跳出来的词汇都可以作为标签加上去，多多益善。你加的标签越多，后续使用的时候找起来就越简单，唯一要注意的是同义词不要重复出现，能复用标签时就尽量不要再创建一个新的类似标签。\n\n\n书签结构\n\n概念介绍\n\n\n\n\n默认安装参数下，Linkding 的数据库使用的是 SQLite，所以书签数据是以「. Sqlite 3」后缀的文件格式保存的。只要用 SQLiteStudio\n之类的软件打开就能查看和修改其中的数据。\n\n我们可以先看一下 Linkding 中书签的所有字段：\n\n[https://pic.ccino.win/OB/2024/10/1728626535202410111402994.png]\n\n名称含义id书签主键url书签记录的网址title书签名称description书签介绍website_title网页默认名称website_description网页默认介绍unread是否标为待阅读is_archived是否标为已归档（即不常用书签或失效书签）web_archive_snapshot_urlInternet\nArchive 中该网页的快照地址favicon_file图标文件保存地址notes关于该书签的笔记（支持 Markdown\n语法）preview_image_file预览图片保存地址tags标签与书签的对应关系在一张单独的关系表中，上图中并没有体现\n\n与标签相关的字段比较简单，就一张标签信息表和标签 -\n书签多对多关系表。 [https://pic.ccino.win/OB/2024/10/1728626549202410111402676.png]\n\n[https://pic.ccino.win/OB/2024/10/1728626559202410111402405.png]\n\n常用的几个书签属性在使用浏览器插件收藏网页的时候就可以直接编辑。\n\n[https://pic.ccino.win/OB/2024/10/1728626577202410111402186.png]\n\n与传统书签系统中可怜的两三个搜索维度相比，Linkding\n明显领先太多了，表格中出现的字段内容基本都可以作为搜索元素来进行查找。也就是说，无论是标题、介绍，还是笔记中出现的文本都可以作为关键词来检索，在保持标题简单明了的前提下依旧可以获得远超传统书签的搜索维度。\n\n使用技巧\n\n\n\n\n因为书签的特征不再只有标题这一个维度，所以保存书签时一般不需要修改网页默认的名称，就算要修改也建议将其修改得更加简洁明了。为了便于后续搜索或再阅读，description\n以及 notes 需要作为修改的重点。大部分情况下 description 可以使用网页默认描述，要手动编辑的话也尽量用客观的语句描述其实际内容。而 notes\n则可以自由一点，并且其本身支持 Markdown\n语法，显示效果也会好很多。以我自己为例给大家一些参考，某些知识类的文章可能要点就那么几句话或是几行代码，没什么单独开篇笔记收藏的必要，就可以直接写在 notes\n中；找到合适的素材时可以在 notes 中记录下该素材的用法或当时的灵感思路等。总结一下就是三点：\n\n * title：简洁明了，一般保持默认即可。\n * description：客观描述网页内容，一般保持默认即可。\n * notes：记录网页中的核心知识点、收藏的理由、灵感思路等，一般留空即可。\n\nUnread 和 is_archived\n这两个字段很好理解，前者就是很多浏览器自带的待阅读功能，后者则是归档功能，就是把已经失效或是长时间不会再用到的书签隐藏到一个单独的分类中。两者都可以通过首页的\nBookmarks 按钮快速切换，各自都有其独立的分区。\n\n[https://pic.ccino.win/OB/2024/10/1728626612202410111403269.png] 归档这个功能我很喜欢，相较于直接把失效书签删除，把它们关小黑屋总有一种一朝一日它们还能重见天日的错觉。反正也不占什么空间，留着当个念想总是好的。\n\n相较于归档，待阅读的花样就多一点。我一般不把它作为待阅读列表使用，而是更接近近期常用的概念，或者说是素材库，把搜索出来也许会用的书签统一标为待阅读。Linkding\n的批处理功能是相当优秀的，如下图所示，归档、删除、增加标签、移除标签、标记为已读、标记为未读，这六个核心功能都支持批处理操作，效率远超浏览器收藏夹。\n\n[https://pic.ccino.win/OB/2024/10/1728626625202410111403005.png]\n\n拓展\n\n\n\n\n上面提到过的三张表 bookmarks_bookmark、bookmarks_tag、bookmarks_bookmark_tags 是 Linkding\n中最核心的数据表，它们都在 Linkding 数据文件夹的 db. Sqlite 3\n这一数据库文件中。因为该文件中数据都是公开的，没有加密一说，操作空间就很大了。\n\n[https://pic.ccino.win/OB/2024/10/1728626634202410111403134.png] Linkding\n虽然自带导出书签为 html 文件的功能，但是因为其本身并没有文件夹的概念，所以使用其导出的文件导入到 Edge\n浏览器就会存在水土不服的情况。我的服务器有一次宕机之后系统出了点问题，但书签每天都要用，就写了个 python 脚本将其按照特定的要求导出为 Edge 可识别的\nhtml 文件应急。具体的导出要求是以「@」开头的标签为文件夹，将所有书签分类放置在其中，同时将每个书签的标签汇总后添加到书签名称的最后。脚本用起来非常简单，与\ndb. Sqlite 3 文件放置在同一目录下运行即可。\n\n这里给出这个案例有多方面的考虑，一是向大家展示一下 Linkding 这种用单个数据库文件存储数据对于个人用户的便利性，因为需要备份的数据就那么几个，甚至以后\nLinkding 不再维护，用户依旧可以很轻松地转移到任何其他平台；二是给大家提供另一种 Linkding\n的使用思路，可以将其作为单纯的整理工具，使用其强大的批处理界面以及优秀的数据库结构来整理汇总自己的书签，最后导出到更常使用的平台。\n\nimport datetime\nimport sqlite3\nfrom typing import Optional\n\nfrom dateutil.parser import parse\nfrom lxml import etree\n\nclass DatabaseManager:\n\n    \n\"\"\"\n\n\n    用于读取书签数据库文件\n\n\n    \"\"\"\n\n    \ndef __init__(self, db_file: str):\n        self.conn = sqlite3.connect(db_file)\n        self.cursor = self.conn.cursor()\n\n    def __enter__(self):\n        return self\n\n    def __exit__(self, exc_type, exc_val, exc_tb):\n        self.cursor.close()\n        self.conn.close()\n\n    def __iter__(self):\n        self.cursor.execute(\"select * from bookmarks_bookmark\")\n        return self\n\n    def __next__(self):\n        # 获取单条书签数据\n        tem = self.cursor.fetchone()\n        if tem is None:\n            raise StopIteration\n        # 根据书签 id 查找对应的标签\n        c = self.conn.cursor()\n        cmd = \"select bookmarks_tag.name from bookmarks_bookmark_tags \" \\\n              \"join bookmarks_tag on bookmarks_bookmark_tags.tag_id = bookmarks_tag.id \" \\\n              \"where bookmarks_bookmark_tags.bookmark_id = {}\"\n        tags = c.execute(cmd.format(tem[0])).fetchall()\n        tags = [tag[0] for tag in tags]\n        res = list(tem)\n        # 将标签数据以 list 的形式添加到原本书签数据的最后\n        res.append(tags)\n        c.close()\n        return res\n\ndef timestr_to_timestamp(timestr: str) -> int:\n\n    \n\"\"\"\n\n\n    将字符串形式的时间转为时间戳\n\n\n    \n:param\n timestr:\n\n\n    \n:return\n:\n\n\n    \"\"\"\n\n    \nreturn int(parse(timestr + \"+0000\").timestamp())\n\nclass HtmlGenerator:\n\n    \n\"\"\"\n\n\n    用于构造 Edge 浏览器支持导入的书签文件\n\n\n    主要参考 Edge 浏览器导出的 html 文件格式\n\n\n    \"\"\"\n\n    \ndef __init__(self):\n        self.root = etree.Element(\"DT\")\n        # 当前时间戳，用于后续一些需要时间戳的场景\n        # self.t = str(int(datetime.datetime.now(datetime.UTC).timestamp()))  # 高版本python\n        self.t = str(int(datetime.datetime.utcnow().timestamp()))\n        h3 = etree.SubElement(self.root, \"H3\",\n                              {\n                                  \"ADD_DATE\": self.t,\n                                  \"LAST_MODIFIED\": self.t,\n                                  \"PERSONAL_TOOLBAR_FOLDER\": \"true\",\n                              })\n        h3.text = \"收藏夹栏\"\n        # 后续用于存放各文件夹和书签的元素\n        self.dl = etree.SubElement(self.root, \"DL\")\n        # 存放各书签文件夹对应的元素\n        self.folder = {}\n\n    def generate_folder(self, name: str):\n\n        \n\"\"\"\n\n\n        创建一个书签文件夹对应的元素\n\n\n        \n:param\n name:\n\n\n        \n:return\n:\n\n\n        \"\"\"\n\n        \ndt = etree.SubElement(self.dl, \"dt\")\n        h3 = etree.SubElement(dt, \"H3\",\n                              {\n                                  \"ADD_DATE\": self.t,\n                                  \"LAST_MODIFIED\": self.t,\n                              })\n        h3.text = name\n        dl = etree.SubElement(dt, \"DL\")\n        self.folder[name] = dl\n\n    def add_bookmark(self, url: str, add_date: str, title: str, folder: Optional[str] = None):\n\n        \n\"\"\"\n\n\n        添加书签到指定父元素中\n\n\n        \n:param\n url:\n\n\n        \n:param\n add_date:\n\n\n        \n:param\n title:\n\n\n        \n:param\n folder:\n\n\n        \n:return\n:\n\n\n        \"\"\"\n\n        \nif folder is None:\n            dt = etree.SubElement(self.dl, \"dt\")\n        else:\n            dt = etree.SubElement(self.folder[folder], \"dt\")\n        a = etree.SubElement(dt, \"A\", {\n            \"HREF\": url,\n            \"ADD_DATE\": add_date,\n        })\n        a.text = title\n\n    def tostring(self):\n\n        \n\"\"\"\n\n\n        将根元素转化为字符串\n\n\n        \n:return\n:\n\n\n        \"\"\"\n\n        \nreturn etree.tostring(self.root, encoding=\"utf-8\", pretty_print=True).decode(\"utf-8\")\n\n    def handle_one(self, data: list):\n\n        \n\"\"\"\n\n\n        将数据库中导出的单条书签数据转为 html 元素\n\n\n        \n:param\n data:\n\n\n        \n:return\n:\n\n\n        \"\"\"\n\n        \n# 从原始数据中提取出部分需要的数据\n        url = data[1]\n        title = data[2]\n        add_date = str(timestr_to_timestamp(data[7]))\n        tags = data[17]\n        # 将所有标签合并为一个整体，添加到书签名的最后，方便后续在浏览器中查找\n        tag = \" \".join(tags)\n        if tag != \"\":\n            tag = \"【{}】\".format(tag)\n        title += tag\n        # 对于没有标签的书签直接将其置于收藏夹根目录下\n        if len(tags) == 0:\n            self.add_bookmark(url, add_date, title)\n            return\n        # 根据标签中带“@\"的标签名来对书签进行分类，归于不同的文件夹中\n        for tag in tags:\n            if \"@\" in tag and tag[0] == \"@\":\n                if self.folder.get(tag) is None:\n                    self.generate_folder(tag)\n                self.add_bookmark(url, add_date, title, tag)\n\n    def export_to_edge(self, db: str, path: str):\n\n        \n\"\"\"\n\n\n        导出为 Edge 浏览器支持的 html 文件\n\n\n        \n:param\n db:\n\n\n        \n:param\n path:\n\n\n        \n:return\n:\n\n\n        \"\"\"\n\n        \nwith DatabaseManager(db) as d:\n            for data in d:\n                self.handle_one(data)\n        html_template = \"\"\"<!DOCTYPE NETSCAPE-Bookmark-file-1>\n<!-- This is an automatically generated file.\n     It will be read and overwritten.\n     DO NOT EDIT! -->\n<META HTTP-EQUIV=\"Content-Type\" CONTENT=\"text/html; charset=UTF-8\">\n<TITLE>Bookmarks</TITLE>\n<H1>Bookmarks</H1>\n<DL>{}</DL>\"\"\"\n        with open(path, \"w\", encoding=\"utf-8\") as f:\n            f.write(html_template.format(self.tostring()))\n\nif __name__ == '__main__':\n    h = HtmlGenerator()\n    h.export_to_edge(\"db.sqlite3\", \"bookmarks.html\")\n\n\nCopy\n\n\n快照\n\n\n\n\n以书签系统为核心的网络书签服务其实有很多，有不少甚至在颜值上吊打 Linkding，但最后还是 Linkding\n获得了我的青睐，很关键的一点就是它提供的双重快照系统。\n\n[https://pic.ccino.win/OB/2024/10/1728626656202410111404001.png] 上面那个 Internet\nArchive 是一个在线服务，开启后服务器会调用该网站的 API\n接口将你收藏的网址链接提交上去，然后该网站就会抓取该时刻书签网址上的内容进行保存。Internet Archive\n是一个很有名的非营利性数字图书馆，保存着互联网上不计其数的网页内容，有兴趣的可以去了解捐赠一波。\n\n当然，这个快照的局限性非常大，我一般不作为主力使用。首先，想要正常访问 Internet Archive\n的话需要使用一点魔法；其次它的抓取对于一些需要登录才能访问相关数据的网站基本没用，就国内互联网这个环境，很多抓取都没有意义。\n\n下面这个快照则是纯正的本地快照，不过该功能只有特定版本的 Docker\n镜像才会开启，安装时最好选择 latest-plus 版本。该功能开启后服务器会自动抓取书签的网页内容，和 Internet Archive\n类似，需要登录验证的网页同样会抓取失败。\n\n[https://pic.ccino.win/OB/2024/10/1728626664202410111404623.png] 不过这个是有解决办法的，因为\nLinkding 同时提供了手动上传文件作为快照的备选方案。点开书签的详情页，会有一个「Upload\nfile」的按钮，点击即可上传本地文件。这里对于文件的格式没有任何要求，但还是推荐上传可以用浏览器直接打开的 pdf、txt、html\n等格式，不然点击浏览按钮后会直接对该文件进行下载。对于文章类的网页推荐直接 Ctrl+P 打印为 pdf 上传，其他网页则推荐使用浏览器插件打包为单个 html\n文件后上传。\n\n[https://pic.ccino.win/OB/2024/10/1728626674202410111404266.png] 对于一些比较重要的文章其实我个人会更加倾向于直接全文收藏到\nTrilium 笔记中，算是对网页快照的一个补强，具体内容会在 Trilium 章节中详细介绍。\n\n\n其他\n\n\n\n\nLinkding 设置里面还有两个可能会用到的功能，这里给大家介绍一下。首先是 Auto\nTagging，顾名思义，就是可以根据你预设的规则在收藏书签时自动为其加上某些标签，这里以哔哩哔哩的专栏为例。 [https://pic.ccino.win/OB/2024/10/1728626719202410111405595.png]\n\n另一个是 Custom CSS，自定义页面的\nCSS，也就是可以更改页面布局。这个需要一点专业知识才能玩转，普通用户可以试着改改字体大小颜色等，比如说把书签颜色改成猛男粉。\n\n[https://pic.ccino.win/OB/2024/10/1728626682202410111404405.png] [https://pic.ccino.win/OB/2024/10/1728626772202410111406430.png]\n\n最后提一下手机端如何收藏书签的问题，无论是安卓还是 iOS 都有类似 PC 端浏览器插件的实现方式，具体参考官方指南。\n\n\n个人笔记服务：TRILIUM\n\n\n\n\nTrilium 是一个可以多端同步的笔记软件。市面上开源的笔记软件其实很多，Trilium\n应该算是比较小众的，主要是功能太多，没有耐心很难体会到它的妙处。我其实只能算是它的初级用户，只会一些比较简单的操作，许多高级特性都没有尝试过。Trilium\n的功能完全可以单独胜任个人数据库构建的重任，但下文中我只会简单介绍一个与书签相关的特性。\n\n之前介绍过 Linkding 的快照功能，里面说了一些特殊情况下的网页备份方法，但对于图文类的文章我其实很少使用其手动上传功能。主要原因是我个人更加喜欢用\nMarkdown 的格式来记录图文数据，于知识记录而言，pdf 和 html 终究是不够纯粹和方便。\n\n将网页转化为纯粹的图文数据这一需求曾经困扰过我很久，我尝试过很多不同的开源项目或浏览器插件，结果都不尽如人意，总是会存在一些小问题，直到偶然尝试了一下\nTrilium 的浏览器插件这一问题才算是落下帷幕。\n\n[https://pic.ccino.win/OB/2024/10/1728626786202410111406659.png] 插件上有一个选项是「Save\nwhole\npage」，可以直接把当前网页上的图文保存为一篇本地笔记，识别率相当高，基本不需要修改就可以得到一篇排版正确的笔记，不过它只会保存文章的主体或是帖子的主楼，也就是说评论和跟帖并不会被保存下来。绝大部分博客类的网站都能完美适配，部分站点可能会存在图片无法显示的问题，不想一张张图片手动复制粘贴的话可以参照\nLinkding 快照部分讲解的方法。\n\n这项功能完美补全了 Linkding 在待阅读方面的短板。我目前对于一篇有价值的网页文章的基本操作流程就是先用 Linkding 的浏览器插件保存书签，然后使用\nTrilium\n的浏览器插件保存图文到笔记系统。保存书签是为了方便后续深入阅读时可以随时查看有价值的评论或跟帖，保存图文则是将其作为待阅读的素材，后续阅读时可以很方便的删改整理为自己的笔记。\n\n插件上另一个常用的选项是「Save windows’ tabs as a list」，可以将当前浏览器窗口中所有 Tab\n页的网页链接以列表的形式保存到笔记中，设计这个功能的人绝对是懂工作的，后续想要回到同样的工作场景只需要打开对应笔记点击列表中的链接就能完美复原浏览器状态。\n\n[https://pic.ccino.win/OB/2024/10/1728626794202410111406881.png] 这可以算是一个额外的专项任务收藏夹，可以将一些日常工作场景下会打开的链接提前收藏好，需要用到时就能快速开始该场景下的工作，结合上文中我提到过的使用\nLinkding 的 unread 列表来作为近期任务工作区，可以极大的提高工作效率。\n\n\n后记\n\n\n\n\nLinkding 和 Trilium\n这种本地服务最重要的就是保证数据安全，如果想要使用我的这套方案请务必设置好服务器数据的定期备份，数据一旦丢失，想要再次补全的代价实在过于高昂，请务必慎重。\n\n本文出自：书签「上上千」：用 Linkding 和 Trilium 自建服务轻松管理 - 少数派\n\n\n","date_published":"2024-10-11T06:19:01.399Z","_microfeed":{"is_audio":false,"is_document":false,"is_external_url":true,"is_video":false,"is_image":false,"web_url":"https://blog-ccino-cc.pages.dev/i/linkding-trilium-N3xXUfs6LUq/","json_url":"https://blog-ccino-cc.pages.dev/i/N3xXUfs6LUq/json/","rss_url":"https://blog-ccino-cc.pages.dev/i/N3xXUfs6LUq/rss/","guid":"N3xXUfs6LUq","status":"published","itunes:episodeType":"full","date_published_short":"Fri Oct 11 2024","date_published_ms":1728627541399}},{"id":"j-lAtZ2gXyq","title":"实现本地 RAG 服务：整合 Open WebUI、Ollama 和 Qwen2.5","attachments":[{"url":"https://cuterwrite.top/p/integrate-open-webui-ollama-qwen25-local-rag/","mime_type":"text/html"}],"url":"https://blog.ccino.cc/i/implementing-local-rag-services-integrating-open-webui-ollama-and-qwen2.5/","external_url":"https://cuterwrite.top/p/integrate-open-webui-ollama-qwen25-local-rag/","content_html":"<h2>引言</h2><p><br></p><p>在构建信息检索和生成式 AI 应用时，Retrieval-Augmented Generation (RAG) 模型凭借其能够从知识库中检索相关信息并生成准确答案的强大能力，受到越来越多开发者的青睐。然而，实现端到端的本地 RAG 服务，需求的不只是合适的模型，还需要集成强大的用户界面和高效的推理框架。</p><p>在构建本地 RAG 服务时，利用易于部署的 Docker 方式，可以极大简化模型管理和服务集成。这里我们依赖 Open WebUI 提供的用户界面与模型推理服务，再通过 Ollama 来引入&nbsp;bge-m3&nbsp;embedding 模型以实现文档向量化方式的检索功能，从而帮助 Qwen 2.5 完成更精准的答案生成。</p><p>本文我们将讨论如何通过 Docker 快速启动 Open WebUI，同步 Ollama 的 RAG 能力，并结合 Qwen 2.5 模型实现高效的文档检索与生成系统。</p><h2><a href=\"https://blog.ccino.org/p/implementing-local-rag-services-integrating-open-webui-ollama-and-qwen2.5/#%e9%a1%b9%e7%9b%ae%e6%a6%82%e8%a7%88\" rel=\"noopener noreferrer\" target=\"_blank\">#</a>&nbsp;项目概览</h2><p><br></p><p>该项目将使用以下关键工具：</p><ol><li><strong>Open WebUI</strong>&nbsp;: 提供用户与模型交互的 web 界面。</li><li><strong>Ollama</strong>&nbsp;: 用于管理 embedding 和大语言模型的模型推理任务。其中 Ollama 中的&nbsp;bge-m3&nbsp;模型将用于文档检索，Qwen 2.5 将负责回答生成。</li><li><strong>Qwen 2.5</strong>&nbsp;: 模型部分使用阿里推出的 Qwen 2.5 系列，为检索增强生成服务提供自然语言生成。</li></ol><p>为了实现 RAG 服务，我们需要以下步骤：</p><ol><li>部署 Open WebUI 作为用户交互界面。</li><li>配置 Ollama 以高效调度 Qwen 2.5 系列模型。</li><li>使用 Ollama 配置的名为&nbsp;bge-m3&nbsp;的 embedding 模型实现检索向量化处理。</li></ol><h2><a href=\"https://blog.ccino.org/p/implementing-local-rag-services-integrating-open-webui-ollama-and-qwen2.5/#%e9%83%a8%e7%bd%b2-open-webui\" rel=\"noopener noreferrer\" target=\"_blank\">#</a>&nbsp;部署 Open WebUI</h2><p><br></p><p>Open WebUI 提供了一个简洁的 Docker 化解决方案，用户无需手动配置大量依赖，直接通过 Docker 启动 Web 界面。</p><p>首先，服务器上需要确保已经安装了&nbsp;<a href=\"https://docs.docker.com/get-docker/\" rel=\"noopener noreferrer\" target=\"_blank\">Docker</a>&nbsp;，如果没有安装，可以通过以下命令进行快速安装：</p><p>然后创建一个目录用于保存 Open WebUI 的数据，这样数据不会在项目更新后丢失：</p><p>接下来，我们可以通过以下命令来启动 Open WebUI ：</p><p>如果想要运行支持 Nvidia GPU 的 Open WebUI ，可以使用以下命令：</p><p>这里我们将 Open WebUI 的服务暴露在机器的 3000 端口，可以通过浏览器访问&nbsp;http://localhost:3000&nbsp;即可使用（远程访问则使用公网 ip ，开放 3000 端口）。/DATA/open-webui 是数据存储目录，你可以根据需要调整这个路径。</p><p>当然除了 Docker 安装方式外，你也可以通过 pip 、源码编译、Podman 等方式安装 Open WebUI 。更多安装方式请参考&nbsp;<a href=\"https://docs.openwebui.com/getting-started\" rel=\"noopener noreferrer\" target=\"_blank\">Open WebUI 官方文档</a>&nbsp;。</p><h3><a href=\"https://blog.ccino.org/p/implementing-local-rag-services-integrating-open-webui-ollama-and-qwen2.5/#%e5%9f%ba%e7%a1%80%e8%ae%be%e7%bd%ae\" rel=\"noopener noreferrer\" target=\"_blank\">#</a>&nbsp;基础设置</h3><ol><li>输入要注册的账号信息，<strong>设置强密码！！！</strong></li></ol><p><br></p><p>重要</p><p>第一个注册的用户将被自动设置为系统管理员，所以请确保你是第一个注册的用户。</p><ol><li>点击左下角头像，选择管理员面板</li><li>点击面板中的设置</li><li>关闭允许新用户注册（可选）</li><li>点击右下角保存</li></ol><p><img src=\"https://cuterwrite-1302252842.file.myqcloud.com/img/2024-09-21_openwebui_admin.webp\"></p><h2><a href=\"https://blog.ccino.org/p/implementing-local-rag-services-integrating-open-webui-ollama-and-qwen2.5/#%e9%85%8d%e7%bd%ae-ollama-%e5%92%8c-qwen25\" rel=\"noopener noreferrer\" target=\"_blank\">#</a>&nbsp;配置 Ollama 和 Qwen 2.5</h2><h3><a href=\"https://blog.ccino.org/p/implementing-local-rag-services-integrating-open-webui-ollama-and-qwen2.5/#%e9%83%a8%e7%bd%b2-ollama\" rel=\"noopener noreferrer\" target=\"_blank\">#</a>&nbsp;部署 Ollama</h3><p><br></p><p>在本地服务器上安装 Ollama。目前 Ollama 提供多种安装方式，请参考 Ollama 的<a href=\"https://ollama.com/\" rel=\"noopener noreferrer\" target=\"_blank\">官方文档</a>&nbsp;下载安装最新的&nbsp;0.3.11&nbsp;版本（Qwen 2.5 在该版本才开始支持）。安装细节可以参考我之前写的一篇文章：<a href=\"https://cuterwrite.top/p/ollama/\" rel=\"noopener noreferrer\" target=\"_blank\">Ollama：从入门到进阶</a>&nbsp;。</p><p>启动 Ollama 服务（如果是 Docker 方式启动则不需要，但必须暴露 11434 端口）：</p><p>Ollama 服务启动后，可以通过访问地址&nbsp;http://localhost:11434&nbsp;连接到 Ollama 服务。</p><p><a href=\"https://ollama.com/library\" rel=\"noopener noreferrer\" target=\"_blank\">Ollama Library</a>&nbsp;提供了语义向量模型 (&nbsp;bge-m3&nbsp;) 以及各大文本生成模型（包括 Qwen 2.5）。下一步我们将配置 Ollama 以适应本项目对文档检索和问答生成的需求。</p><h3><a href=\"https://blog.ccino.org/p/implementing-local-rag-services-integrating-open-webui-ollama-and-qwen2.5/#%e4%b8%8b%e8%bd%bd-qwen25-%e6%a8%a1%e5%9e%8b\" rel=\"noopener noreferrer\" target=\"_blank\">#</a>&nbsp;下载 Qwen 2.5 模型</h3><p><br></p><p>通过 Ollama 安装 Qwen 2.5，你可以直接在命令行中运行&nbsp;ollama pull&nbsp;命令来下载 Qwen 2.5 模型，比如要下载 Qwen 2.5 的 72 B 模型，可以使用以下命令：</p><p>该命令将从 Ollama 的模型仓库抓取 Qwen 2.5 模型，并准备运行环境。</p><p>Qwen 2.5 提供了多种模型尺寸，包括 72 B、32 B、14 B、7 B、3 B、1.5 B、0.5 B 等，你可以根据自己的需求和 GPU 显存大小选择合适的模型。我采用的是 4 x V 100 的服务器，所以可以直接选择 72 B 模型。如果要求吐字速度快且能接收微小的性能损失的话，可以使用&nbsp;q4_0&nbsp;量化版本&nbsp;qwen2.5:72b-instruct-q4_0&nbsp;；如果能接受吐字速度慢一些，可以使用&nbsp;qwen2.5:72b-instruct-q5_K_M&nbsp;。对于 4 x V 100 的服务器，虽然&nbsp;q5_K_M&nbsp;模型的 token 生成明显卡顿，但是为了试验一下 Qwen 2.5 的性能，我还是选择了&nbsp;q5_K_M&nbsp;模型。</p><p>对于显存较少的个人电脑，推荐使用 14 B 或 7 B 模型，通过以下命令下载：</p><p>或者</p><p>如果你同时启动好了 Open WebUI 和 Ollama 服务，那么也可以在管理员面板中下载模型。</p><p><img src=\"https://cuterwrite-1302252842.file.myqcloud.com/img/2024-09-21_download_qwen2-5-7b.webp\"></p><h3><a href=\"https://blog.ccino.org/p/implementing-local-rag-services-integrating-open-webui-ollama-and-qwen2.5/#%e4%b8%8b%e8%bd%bd-bge-m3-%e6%a8%a1%e5%9e%8b\" rel=\"noopener noreferrer\" target=\"_blank\">#</a>&nbsp;下载 bge-m 3 模型</h3><p><br></p><p>在 Ollama 中下载&nbsp;bge-m3&nbsp;模型，该模型用于文档向量化处理。在命令行中运行以下命令下载模型（或者在 Open WebUI 界面下载）：</p><p>到这里，我们已经完成了 Ollama 的配置，接下来我们将在 Open WebUI 中配置 RAG 服务。</p><h2><a href=\"https://blog.ccino.org/p/implementing-local-rag-services-integrating-open-webui-ollama-and-qwen2.5/#rag-%e9%9b%86%e6%88%90%e4%b8%8e%e9%85%8d%e7%bd%ae\" rel=\"noopener noreferrer\" target=\"_blank\">#</a>&nbsp;RAG 集成与配置</h2><h3><a href=\"https://blog.ccino.org/p/implementing-local-rag-services-integrating-open-webui-ollama-and-qwen2.5/#%e5%9c%a8-open-webui-%e4%b8%ad%e9%85%8d%e7%bd%ae-ollama-%e7%9a%84-rag-%e6%8e%a5%e5%8f%a3\" rel=\"noopener noreferrer\" target=\"_blank\">#</a>&nbsp;在 Open WebUI 中配置 Ollama 的 RAG 接口</h3><h4><a href=\"https://blog.ccino.org/p/implementing-local-rag-services-integrating-open-webui-ollama-and-qwen2.5/#%e8%ae%bf%e9%97%ae-open-webui-%e7%ae%a1%e7%90%86%e7%95%8c%e9%9d%a2\" rel=\"noopener noreferrer\" target=\"_blank\">#</a>&nbsp;访问 Open WebUI 管理界面</h4><p><br></p><p>启动 Open WebUI 之后，你可以直接通过 Web 浏览器访问服务地址，登录你的管理员账户，然后进入管理员面板。</p><h4><a href=\"https://blog.ccino.org/p/implementing-local-rag-services-integrating-open-webui-ollama-and-qwen2.5/#%e8%ae%be%e7%bd%ae-ollama-%e6%8e%a5%e5%8f%a3\" rel=\"noopener noreferrer\" target=\"_blank\">#</a>&nbsp;设置 Ollama 接口</h4><p><br></p><p>在 Open WebUI 的管理员面板中，点击<strong>设置</strong>，你会看到外部连接的选项，确保 Ollama API 的地址为&nbsp;host.docker.internal:11434&nbsp;，然后点击右边的&nbsp;<strong>verify connection</strong>&nbsp;按钮确认 Ollama 服务是否正常连接。</p><p><img src=\"https://cuterwrite-1302252842.file.myqcloud.com/img/2024-09-21_ollama_api.webp\"></p><h4><a href=\"https://blog.ccino.org/p/implementing-local-rag-services-integrating-open-webui-ollama-and-qwen2.5/#%e8%ae%be%e7%bd%ae%e8%af%ad%e4%b9%89%e5%90%91%e9%87%8f%e6%a8%a1%e5%9e%8b\" rel=\"noopener noreferrer\" target=\"_blank\">#</a>&nbsp;设置语义向量模型</h4><p><br></p><p>在 Open WebUI 的管理员面板中，点击<strong>设置</strong>，接着点击<strong>文档</strong>，依次完成以下步骤：</p><ol><li>设置语义向量模型引擎为 Ollama 。</li><li>设置语义向量模型为&nbsp;bge-m3:latest&nbsp;。</li><li>其余设置可以保持默认，这里我设置了文件最大上传大小为 10 MB，最大上传数量为 3，Top K 设置为 5，块大小和块重叠分别设置为 1500 和 100 ，并开启 PDF 图像处理。</li><li>点击右下角保存。</li></ol><p><img src=\"https://cuterwrite-1302252842.file.myqcloud.com/img/2024-09-21_openwebui_embedding.webp\"></p><h3><a href=\"https://blog.ccino.org/p/implementing-local-rag-services-integrating-open-webui-ollama-and-qwen2.5/#%e6%b5%8b%e8%af%95-rag-%e6%9c%8d%e5%8a%a1\" rel=\"noopener noreferrer\" target=\"_blank\">#</a>&nbsp;测试 RAG 服务</h3><p><br></p><p>现在，你已经实现了完整的本地 RAG 系统。你可以在 Open WebUI 的主界面中输入任意自然语言问题，然后上传响应文档，系统会调用语义向量模型向量化文档，再使用 Qwen 2.5 模型检索文档生成答案并返回给用户。</p><p>在 Open WebUI 的用户聊天界面，上传你要检索的文档，然后输入你的问题，点击发送，Open WebUI 将会调用 Ollama 的&nbsp;bge-m3&nbsp;模型进行文档向量化处理，然后调用 Qwen 2.5 模型进行问答生成。</p><p>这里我上传了一个简单的&nbsp;txt&nbsp;文件（由 GPT 生成的文本），内容如下：</p><p>然后分别提了三个问题：</p><ol><li>艾文在森林中遇到的奇异生物是什么？</li><li>艾文在洞穴中找到的古老石板上刻的是什么？</li><li>艾文在祭坛中心发现了什么宝藏？</li></ol><p>下图是回答结果：</p><p><img src=\"https://cuterwrite-1302252842.file.myqcloud.com/img/2024-09-21_qwen2-5-QA.webp\"></p><h2><a href=\"https://blog.ccino.org/p/implementing-local-rag-services-integrating-open-webui-ollama-and-qwen2.5/#%e6%80%bb%e7%bb%93\" rel=\"noopener noreferrer\" target=\"_blank\">#</a>&nbsp;总结</h2><p><br></p><p>借助 Open WebUI 和 Ollama，我们可以轻松搭建一个高效、直观的本地 RAG 系统。通过将&nbsp;bge-m3&nbsp;语义向量模型用于文本向量化，再结合 Qwen 2.5 生成模型，用户可以在一个统一的 Web 界面中进行文档检索与增强生成任务的高效互动。不但保护了数据隐私，还大幅提升了生成式 AI 的本地化应用能力。</p><p>本文出自：<a href=\"https://cuterwrite.top/p/integrate-open-webui-ollama-qwen25-local-rag/\" rel=\"noopener noreferrer\" target=\"_blank\">实现本地 RAG 服务：整合 Open WebUI、Ollama 和 Qwen2.5</a></p>","content_text":"引言\n\n\n\n\n在构建信息检索和生成式 AI 应用时，Retrieval-Augmented Generation (RAG)\n模型凭借其能够从知识库中检索相关信息并生成准确答案的强大能力，受到越来越多开发者的青睐。然而，实现端到端的本地 RAG\n服务，需求的不只是合适的模型，还需要集成强大的用户界面和高效的推理框架。\n\n在构建本地 RAG 服务时，利用易于部署的 Docker 方式，可以极大简化模型管理和服务集成。这里我们依赖 Open WebUI\n提供的用户界面与模型推理服务，再通过 Ollama 来引入 bge-m3 embedding 模型以实现文档向量化方式的检索功能，从而帮助 Qwen 2.5\n完成更精准的答案生成。\n\n本文我们将讨论如何通过 Docker 快速启动 Open WebUI，同步 Ollama 的 RAG 能力，并结合 Qwen 2.5\n模型实现高效的文档检索与生成系统。\n\n\n# 项目概览\n\n\n\n\n该项目将使用以下关键工具：\n\n 1. Open WebUI : 提供用户与模型交互的 web 界面。\n 2. Ollama : 用于管理 embedding 和大语言模型的模型推理任务。其中 Ollama 中的 bge-m3 模型将用于文档检索，Qwen 2.5\n    将负责回答生成。\n 3. Qwen 2.5 : 模型部分使用阿里推出的 Qwen 2.5 系列，为检索增强生成服务提供自然语言生成。\n\n为了实现 RAG 服务，我们需要以下步骤：\n\n 1. 部署 Open WebUI 作为用户交互界面。\n 2. 配置 Ollama 以高效调度 Qwen 2.5 系列模型。\n 3. 使用 Ollama 配置的名为 bge-m3 的 embedding 模型实现检索向量化处理。\n\n\n# 部署 OPEN WEBUI\n\n\n\n\nOpen WebUI 提供了一个简洁的 Docker 化解决方案，用户无需手动配置大量依赖，直接通过 Docker 启动 Web 界面。\n\n首先，服务器上需要确保已经安装了 Docker ，如果没有安装，可以通过以下命令进行快速安装：\n\n然后创建一个目录用于保存 Open WebUI 的数据，这样数据不会在项目更新后丢失：\n\n接下来，我们可以通过以下命令来启动 Open WebUI ：\n\n如果想要运行支持 Nvidia GPU 的 Open WebUI ，可以使用以下命令：\n\n这里我们将 Open WebUI 的服务暴露在机器的 3000\n端口，可以通过浏览器访问 http://localhost:3000 即可使用（远程访问则使用公网 ip ，开放 3000\n端口）。/DATA/open-webui 是数据存储目录，你可以根据需要调整这个路径。\n\n当然除了 Docker 安装方式外，你也可以通过 pip 、源码编译、Podman 等方式安装 Open WebUI 。更多安装方式请参考 Open WebUI\n官方文档 。\n\n\n# 基础设置\n\n 1. 输入要注册的账号信息，设置强密码！！！\n\n\n\n\n重要\n\n第一个注册的用户将被自动设置为系统管理员，所以请确保你是第一个注册的用户。\n\n 1. 点击左下角头像，选择管理员面板\n 2. 点击面板中的设置\n 3. 关闭允许新用户注册（可选）\n 4. 点击右下角保存\n\n[https://cuterwrite-1302252842.file.myqcloud.com/img/2024-09-21_openwebui_admin.webp]\n\n\n# 配置 OLLAMA 和 QWEN 2.5\n\n\n# 部署 OLLAMA\n\n\n\n\n在本地服务器上安装 Ollama。目前 Ollama 提供多种安装方式，请参考 Ollama 的官方文档 下载安装最新的 0.3.11 版本（Qwen 2.5\n在该版本才开始支持）。安装细节可以参考我之前写的一篇文章：Ollama：从入门到进阶 。\n\n启动 Ollama 服务（如果是 Docker 方式启动则不需要，但必须暴露 11434 端口）：\n\nOllama 服务启动后，可以通过访问地址 http://localhost:11434 连接到 Ollama 服务。\n\nOllama Library 提供了语义向量模型 ( bge-m3 ) 以及各大文本生成模型（包括 Qwen 2.5）。下一步我们将配置 Ollama\n以适应本项目对文档检索和问答生成的需求。\n\n\n# 下载 QWEN 2.5 模型\n\n\n\n\n通过 Ollama 安装 Qwen 2.5，你可以直接在命令行中运行 ollama pull 命令来下载 Qwen 2.5 模型，比如要下载 Qwen 2.5\n的 72 B 模型，可以使用以下命令：\n\n该命令将从 Ollama 的模型仓库抓取 Qwen 2.5 模型，并准备运行环境。\n\nQwen 2.5 提供了多种模型尺寸，包括 72 B、32 B、14 B、7 B、3 B、1.5 B、0.5 B 等，你可以根据自己的需求和 GPU\n显存大小选择合适的模型。我采用的是 4 x V 100 的服务器，所以可以直接选择 72 B\n模型。如果要求吐字速度快且能接收微小的性能损失的话，可以使用 q4_0 量化版本 qwen2.5:72b-instruct-q4_0 ；如果能接受吐字速度慢一些，可以使用 qwen2.5:72b-instruct-q5_K_M 。对于\n4 x V 100 的服务器，虽然 q5_K_M 模型的 token 生成明显卡顿，但是为了试验一下 Qwen 2.5\n的性能，我还是选择了 q5_K_M 模型。\n\n对于显存较少的个人电脑，推荐使用 14 B 或 7 B 模型，通过以下命令下载：\n\n或者\n\n如果你同时启动好了 Open WebUI 和 Ollama 服务，那么也可以在管理员面板中下载模型。\n\n[https://cuterwrite-1302252842.file.myqcloud.com/img/2024-09-21_download_qwen2-5-7b.webp]\n\n\n# 下载 BGE-M 3 模型\n\n\n\n\n在 Ollama 中下载 bge-m3 模型，该模型用于文档向量化处理。在命令行中运行以下命令下载模型（或者在 Open WebUI 界面下载）：\n\n到这里，我们已经完成了 Ollama 的配置，接下来我们将在 Open WebUI 中配置 RAG 服务。\n\n\n# RAG 集成与配置\n\n\n# 在 OPEN WEBUI 中配置 OLLAMA 的 RAG 接口\n\n# 访问 OPEN WEBUI 管理界面\n\n\n\n\n启动 Open WebUI 之后，你可以直接通过 Web 浏览器访问服务地址，登录你的管理员账户，然后进入管理员面板。\n\n# 设置 OLLAMA 接口\n\n\n\n\n在 Open WebUI 的管理员面板中，点击设置，你会看到外部连接的选项，确保 Ollama API\n的地址为 host.docker.internal:11434 ，然后点击右边的 verify connection 按钮确认 Ollama 服务是否正常连接。\n\n[https://cuterwrite-1302252842.file.myqcloud.com/img/2024-09-21_ollama_api.webp]\n\n# 设置语义向量模型\n\n\n\n\n在 Open WebUI 的管理员面板中，点击设置，接着点击文档，依次完成以下步骤：\n\n 1. 设置语义向量模型引擎为 Ollama 。\n 2. 设置语义向量模型为 bge-m3:latest 。\n 3. 其余设置可以保持默认，这里我设置了文件最大上传大小为 10 MB，最大上传数量为 3，Top K 设置为 5，块大小和块重叠分别设置为 1500 和\n    100 ，并开启 PDF 图像处理。\n 4. 点击右下角保存。\n\n[https://cuterwrite-1302252842.file.myqcloud.com/img/2024-09-21_openwebui_embedding.webp]\n\n\n# 测试 RAG 服务\n\n\n\n\n现在，你已经实现了完整的本地 RAG 系统。你可以在 Open WebUI\n的主界面中输入任意自然语言问题，然后上传响应文档，系统会调用语义向量模型向量化文档，再使用 Qwen 2.5 模型检索文档生成答案并返回给用户。\n\n在 Open WebUI 的用户聊天界面，上传你要检索的文档，然后输入你的问题，点击发送，Open WebUI 将会调用 Ollama\n的 bge-m3 模型进行文档向量化处理，然后调用 Qwen 2.5 模型进行问答生成。\n\n这里我上传了一个简单的 txt 文件（由 GPT 生成的文本），内容如下：\n\n然后分别提了三个问题：\n\n 1. 艾文在森林中遇到的奇异生物是什么？\n 2. 艾文在洞穴中找到的古老石板上刻的是什么？\n 3. 艾文在祭坛中心发现了什么宝藏？\n\n下图是回答结果：\n\n[https://cuterwrite-1302252842.file.myqcloud.com/img/2024-09-21_qwen2-5-QA.webp]\n\n\n# 总结\n\n\n\n\n借助 Open WebUI 和 Ollama，我们可以轻松搭建一个高效、直观的本地 RAG 系统。通过将 bge-m3 语义向量模型用于文本向量化，再结合\nQwen 2.5 生成模型，用户可以在一个统一的 Web 界面中进行文档检索与增强生成任务的高效互动。不但保护了数据隐私，还大幅提升了生成式 AI\n的本地化应用能力。\n\n本文出自：实现本地 RAG 服务：整合 Open WebUI、Ollama 和 Qwen2.5","image":"https://cdn-blog-ccino-cc.ccino.cc/blog-ccino-cc/production/images/item-2ac21fca3291605b72ada659246e0e8b.png","date_published":"2024-10-09T05:33:06.511Z","_microfeed":{"is_audio":false,"is_document":false,"is_external_url":true,"is_video":false,"is_image":false,"web_url":"https://blog-ccino-cc.pages.dev/i/rag-open-webuiollama-qwen25-j-lAtZ2gXyq/","json_url":"https://blog-ccino-cc.pages.dev/i/j-lAtZ2gXyq/json/","rss_url":"https://blog-ccino-cc.pages.dev/i/j-lAtZ2gXyq/rss/","guid":"j-lAtZ2gXyq","status":"published","itunes:episodeType":"full","date_published_short":"Wed Oct 09 2024","date_published_ms":1728451986511}},{"id":"RfaEeSmqnno","title":"Google旗下最新Blogger注册及使用教程（下载+注册流程+常见问题汇总），靠它赚美金也可以的","url":"https://blog.ccino.cc/i/googles-blogger-registration-and-usage-tutorial/","content_html":"<p>爱浏览博客或者写博客的朋友，可能都知道 Blogger 这个平台，它是 Google 旗下一家大型的博客服务网站，仅需拥有一个 Google 账户即可将成千上万的博文、照片以及更多内容妥善地存储在 Blogger 中。除了撰写 / 浏览博客外，你还可以通过 Blogger 赚钱。那么如何注册并使用 Blogger 呢？为了方便大家顺利拥有一个 Blogger 账号并很好的使用它，小编这里就整理了一篇最新最详细的 Blogger 注册及使用教程，包含下载，注册流程以及常见问题汇总。满满的干货，需要的朋友，赶紧来 GET 吧。</p><h2><strong>Blogger 简介</strong></h2><p><br></p><p><strong>【官方网站】：</strong><a href=\"https://blog.ccino.org/p/googles-blogger-registration-and-usage-tutorial/#\" rel=\"noopener noreferrer\" target=\"_blank\"><strong>https://www.blogger.com/</strong></a></p><p>Blogger 原是 Google 旗下一家大型的博客服务网站。它也是第一家大规模博客服务的提供商，由旧金山一家名为 Pyra Labs 的小型公司于 1999 年 8 月创办。它就好比国内新浪提供的新浪博客、搜狐提供的个人博客、更类似于网易的自媒体、百度的自媒体。有几十套漂亮的免费主题供你选、如果不满意还可以直接在后台修改 HTML 代码、改成你喜欢的主题！利用 Blogger，你可以将成千上万的博文、照片以及更多内容妥善地存储在 Google 中。</p><p><img src=\"https://pic.ccino.win/OB/2024/10/202410071342270.png\"></p><h3><strong>Blogger&nbsp;优点</strong></h3><p><br></p><p>①最突出的优点就是简洁但功能强大，Blogger 没有多余而花哨的功能，必要的功能一个都不差。</p><p>②Bloger 自由性最大的地方在于其模板可以自定义，也就是说你可以修改模板里的任何内容，包括 Google 的广告，这给那些懂 Html 和 CSS 的 Blogger 提供了很大的自由度。</p><p>③用户还可以利用 Blogger 内置的分析功能，了解哪些博客比较受欢迎。您可以了解受众群体来自哪里，以及他们对哪些内容感兴趣。此外，你还可以将博客直接关联到 Google Analytics（分析），进一步查看详细信息。</p><p>④此外，Google AdSense 会自动在您的博客上显示相关的定位广告，你只需发布自己感兴趣的内容，即可赚取收益。Blogger 还提供免费域名、如果你有独立域名、你也可以用你自己的独立域名。</p><h3><strong>Blogger 下载</strong></h3><p><br></p><p>下载 Blogger 应用，即可随时随地开始撰写博文。</p><ul><li>撰写博文（可保存为草稿或立即发布）</li><li>编辑现有博文</li><li>查看您所保存和发布的博文列表</li><li>切换帐号 / 博客（如果您有多个帐号 / 博客）</li><li>嵌入图库中的图片或直接通过该应用拍摄的照片</li><li>向博文添加标签</li></ul><p><a href=\"https://blog.ccino.org/p/googles-blogger-registration-and-usage-tutorial/#\" rel=\"noopener noreferrer\" target=\"_blank\"><strong>Google Paly</strong></a><strong>；</strong><a href=\"https://blog.ccino.org/p/googles-blogger-registration-and-usage-tutorial/#\" rel=\"noopener noreferrer\" target=\"_blank\"><strong>Apple Store</strong></a></p><h3><strong>准备工作</strong></h3><p><br></p><p>Blogger 已在中国内地被永久性屏蔽。所以不能以正常步骤在我国大陆使用。所以在准备注册并使用 Blogger 之前，你需要做好以下准备。</p><p><strong>1. 稳定的科学上网工具</strong></p><p>这些平台有些还有免费试用，大家不妨去看看！</p><p><strong>2. 准备好 Google 账号</strong></p><p>注册 Blogger 网站的话，必须使用 Google 账户才能注册哟。</p><p>如果你还没有 Google 账号，但有&nbsp;<strong>Google Voice</strong>，<strong>Textnow</strong>&nbsp;这类虚拟手机号码的话，可以试试用虚拟号码注册一个 Google 账户。如果自己注册不成功，那就找渠道购买一个吧！</p><p><strong>3. 准备好浏览器</strong></p><p>个人比较推荐使用&nbsp;<a href=\"https://blog.ccino.org/p/googles-blogger-registration-and-usage-tutorial/#\" rel=\"noopener noreferrer\" target=\"_blank\"><strong>Chrome 谷歌浏览器</strong></a>（建议安装翻译插件，可自动帮你把英文页面翻译成中文），其次是&nbsp;<a href=\"https://blog.ccino.org/p/googles-blogger-registration-and-usage-tutorial/#\" rel=\"noopener noreferrer\" target=\"_blank\"><strong>Firefox 火狐浏览器</strong></a>，也可以使用&nbsp;<a href=\"https://blog.ccino.org/p/googles-blogger-registration-and-usage-tutorial/#\" rel=\"noopener noreferrer\" target=\"_blank\"><strong>Bing</strong></a><strong>&nbsp;浏览器</strong>。</p><h3><strong>Blogger 注册流程</strong></h3><p><br></p><p><strong>第一步：</strong><a href=\"https://blog.ccino.org/p/googles-blogger-registration-and-usage-tutorial/#\" rel=\"noopener noreferrer\" target=\"_blank\"><strong>https://www.blogger.com/</strong></a>，进入官网首页，之后点击 “创建博客”，或者右上角 “登陆”。&nbsp;<img src=\"https://pic.ccino.win/OB/2024/10/202410071343915.png\"></p><p><strong>第二步</strong>：如果你登录了 Google 账户的话，会弹出这样的页面，你可以直接点击你账户，就可以登陆 Blogger 了。（如果遇到点击这个选项没有反应，也可以点下方【使用其他账号】然后重新输入一下账号，就可以正常登录。）</p><p><img src=\"https://pic.ccino.win/OB/2024/10/202410071343265.png\"></p><p>选择完之后，需要你再次输入密码，验证你的账号才能登陆成功哦！&nbsp;<img src=\"https://pic.ccino.win/OB/2024/10/202410071344083.png\"></p><p>但如果你自己还没有一个 Google 账号，可以点击 “使用其他账号”-“创建账号”-“为我自己创建”，然后输入你的相关信息，设置密码，从而创建一个 Google 账号。拥有一个 Google 账号，便可以使用 Google 所有产品和服务了。&nbsp;<img src=\"https://pic.ccino.win/OB/2024/10/202410071344541.png\"></p><p><img src=\"https://pic.ccino.win/OB/2024/10/202410071345439.png\"></p><p><strong>第三步</strong>：设置博客名称。如果没有想好，也可以暂时跳过，设置好了的话，就点击 “下一步”。&nbsp;<img src=\"https://pic.ccino.win/OB/2024/10/202410071345383.png\"></p><p><strong>第四步</strong>：为你的博客选择一个 URL，这个网址是人们如何在线找到您的博客的方式。</p><p><img src=\"https://pic.ccino.win/OB/2024/10/202410071346110.png\"></p><p>如何添加域名，可以&nbsp;<a href=\"https://support.google.com/blogger/answer/1233387-hl=zh-Hans&amp;ref_topic=6321959\" rel=\"noopener noreferrer\" target=\"_blank\">https://support.google.com/blogger/answer/1233387-hl=zh-Hans&amp;ref_topic=6321959</a>&nbsp;进入页面查看详情。</p><p><strong>第五步</strong>：之后，你将为你的博客署名。以上步骤完成之后，便可以进入 Blogger 的使用界面。接下来将为大家介绍一些常见的功能及使用指南。</p><h3><strong>Blogger 功能介绍及使用教程</strong></h3><p><br></p><p><strong>1. 开启让搜索引擎检索</strong></p><p>开启让搜索引擎检索，人们将更容易用搜索引擎（如 Google 和 Bing）找到你的博客：</p><ul><li>允许搜索引擎列出您的博客。</li><li>在整个网站中使用关键字，使博客显示在搜索结果的较前位置。</li></ul><p><br></p><p>具体操作流程：</p><ol><li><a href=\"https://blog.ccino.org/p/googles-blogger-registration-and-usage-tutorial/#\" rel=\"noopener noreferrer\" target=\"_blank\">登录到 Blogger</a>。</li><li>在左上方，选择要在搜索引擎中列出的博客。</li><li>点击左侧菜单中的设置。</li><li>在 “隐私权” 下方，开启让搜索引擎检索。</li></ol><p><strong>针对您博客的搜索引擎优化 (SEO) 提示</strong></p><p>您可以在博文和页面的标题和文本中使用相关的关键字，也可以指定搜索引擎不要在搜索结果中显示哪些页面、博文和链接。</p><p><strong>2.&nbsp;备份或导入博客</strong></p><p><strong>备份博客内容</strong></p><p>要获取博客的博文、页面和评论的 .xml 文件，请执行以下操作：</p><ul><li><a href=\"https://blog.ccino.org/p/googles-blogger-registration-and-usage-tutorial/#\" rel=\"noopener noreferrer\" target=\"_blank\">登录到 Blogger</a>。</li><li>在左上方，点击要备份的博客。</li><li>点击左侧菜单中的设置。</li><li>在 “管理博客” 下方，点击备份内容下载。</li></ul><p><strong>保存博客的主题背景副本</strong></p><ul><li><a href=\"https://blog.ccino.org/p/googles-blogger-registration-and-usage-tutorial/#\" rel=\"noopener noreferrer\" target=\"_blank\">登录到 Blogger</a>。</li><li>在左上方，点击要保存的博客。</li><li>在左侧的菜单中，点击主题背景。</li><li>点击右上方的 “更多” 图标。</li></ul><p><strong>将博文和评论导入到您的博客</strong></p><p>重要提示：你每天可以导入的文件数量存在上限，但文件大小不受限制。</p><p>要导入博文和评论的 .xml 文件，请执行以下操作：</p><ul><li><a href=\"https://blog.ccino.org/p/googles-blogger-registration-and-usage-tutorial/#\" rel=\"noopener noreferrer\" target=\"_blank\">登录到 Blogger</a>。</li><li>在左上方，点击要导入内容的博客。</li><li>点击左侧菜单中的设置。</li><li>在 “管理博客” 下方，点击导入内容</li><li>如果您不想自动发布已导入的内容，请关闭自动发布已导入的所有博文和页面。</li><li>选择要从计算机导入的. Xml 文件。</li><li>点击打开。</li></ul><p><strong>3.&nbsp;添加团队成员</strong></p><p>要管理博客和发布内容，受邀的作者和管理员需要使用 Google 帐号。要邀请读者访问您的博客，请按以下步骤操作：</p><ul><li><a href=\"https://blog.ccino.org/p/googles-blogger-registration-and-usage-tutorial/#\" rel=\"noopener noreferrer\" target=\"_blank\">登录到 Blogger</a>。</li><li>在左上方，选择博客。</li><li>点击左侧菜单中的设置。</li><li>点击 “权限” 下方的邀请更多读者。</li><li>输入您要添加的用户的电子邮件地址。</li><li>点击发送。</li></ul><p><strong>4. 添加作者</strong></p><p>要邀请新的作者，请执行以下操作：</p><ul><li><a href=\"https://blog.ccino.org/p/googles-blogger-registration-and-usage-tutorial/#\" rel=\"noopener noreferrer\" target=\"_blank\">登录到 Blogger</a>。</li><li>在左上方，选择博客。</li><li>点击左侧菜单中的设置。</li><li>点击 “权限” 下方的邀请更多作者。</li><li>输入您要添加的作者的电子邮件地址。</li><li>点击发送。对方接受邀请后，系统会将其列为作者。</li></ul><p><strong>5. 将博客设为不公开</strong></p><p>如果有人知道某个图片的网址，那么在您将博客设为不公开之后，这些人仍可查看该图片。</p><p>默认情况下，您的博客是完全公开的，任何人都可以在网上阅读。</p><p>要更改哪些人可以访问您的博客，请按以下步骤操作：</p><p>1.&nbsp;<a href=\"https://blog.ccino.org/p/googles-blogger-registration-and-usage-tutorial/#\" rel=\"noopener noreferrer\" target=\"_blank\">登录到 Blogger</a>。</p><ol><li>在左上方，选择博客。</li><li>点击左侧菜单中的设置。</li><li>点击 “权限” 下方的读者权限，然后从以下选项中选择：</li></ol><ul><li>公开：网络上的任何人都可以访问和阅读您的博客。</li><li>仅作者可见：您博客的所有作者均可访问和阅读您的博客。</li><li>自定义读者：只有您邀请的读者才能访问和阅读您的博客。</li></ul><p><strong>6.&nbsp;启用 “共享” 按钮</strong></p><p>重要提示：您的博客必须设为公开状态才能使用 “共享” 按钮。</p><p>要让其他人通过电子邮件和社交媒体网站分享您的博客，您可以启用 “共享” 按钮：</p><ol><li><a href=\"https://blog.ccino.org/p/googles-blogger-registration-and-usage-tutorial/#\" rel=\"noopener noreferrer\" target=\"_blank\">登录到 Blogger</a>。</li><li>在左上方，选择博客。</li><li>点击左侧菜单中的布局。</li><li>在 “页面主体” 部分，点击 “博文” 旁边的 “修改” 图标。</li><li>勾选 “显示共享按钮” 旁边的复选框。</li></ol><h3><strong>其他常见问题</strong></h3><p><br></p><p><strong>1. 怎么删除和恢复微博？</strong></p><p>你必须是管理员，才能删除博客。删除博客后，您可以在短时间内恢复该博客。</p><p><strong>删除博客</strong></p><ul><li>登录到&nbsp;Blogger。</li><li>在左上方，点击要删除的博客。</li><li>点击左侧菜单中的设置。</li><li>在 “管理博客” 下方，点击移除博客</li></ul><p><strong>恢复已删除的博客</strong></p><p>删除博客后，短期之内您仍可恢复该博客。要恢复博客，请执行以下操作：</p><ul><li>登录到&nbsp;Blogger。</li><li>在左上方的 “已移至回收站的博客” 下方，点击您要恢复的博客</li></ul><p><strong>永久删除</strong></p><p>博客重要提示：永久删除博客后，所有的博客信息、博文和页面都会被删除。您将无法恢复永久删除的博客或再次使用该博客的网址。</p><p>要在删除博客后再将其永久删除，请按照以下步骤操作：</p><ul><li>在左上方的 “已移至回收站的博客” 下方，点击要永久删除的博客。</li></ul><p><br></p><p><strong>2. 在你的博客上如何显示广告？</strong></p><p>如需通过 Blogger 赚取收入，您可以在自己的博客页面上显示 AdSense 和其他广告。</p><p><strong>注册 AdSense</strong></p><ul><li>重要提示：请务必先完成所有步骤，然后再点击重定向。</li><li>登录到 Blogger。</li><li>在左上方，选择博客。</li><li>点击左侧菜单中的收入然后创建 AdSense 帐号。</li><li>如果您的博客目前不具备相关资格，请了解如何满足使用 AdSense 的条件。</li><li>选择与您的 Blogger 帐号相关联的 Google 电子邮件地址。</li><li>填写 AdSense 表单，然后点击创建帐号。</li><li>输入您的付款明细，并验证电话号码。</li><li>点击提交。</li></ul><p>系统会自动将您转至 Blogger 以完成此流程。如果没有跳转，请点击重定向。</p><p>提示：如果您在完成上述步骤时被中断，请重新执行第 1 至 5 步。如果您的 AdSense 首页连续几天都显示 “我们正在为您完成配置”，请点击接受关联。</p><p><strong>在博文之间显示广告</strong></p><ul><li>登录到 Blogger。</li><li>在左上方，选择要显示广告的博客。</li><li>点击左侧菜单中的布局。</li><li>根据不同的模板，选择以下操作：</li><li>在 “页面主体” 下的 “博文” 中，点击修改。</li><li>在 “主体” 下的 “博文” 中，点击修改。</li><li>勾选 “在博文之间显示广告” 旁边的复选框。</li><li>选择广告格式、颜色以及广告显示的频率。</li><li>点击保存。</li></ul><p>如需保存排列方式，请点击右下方的 “保存” 图标保存。</p><p><strong>在专栏中显示广告</strong></p><ul><li>登录到 Blogger。</li><li>在左上方，选择要显示广告的博客。</li><li>点击左侧菜单中的布局。</li><li>在 “边栏” 下方，点击添加小工具。</li><li>点击 “AdSense” 旁边的 “添加” 图标添加。</li><li>配置 AdSense，然后点击保存。</li><li>如需保存排列方式，请点击右下方的 “保存” 图标保存。</li></ul><p><strong>显示来自其他广告服务的广告</strong></p><ul><li>登录到 Blogger。</li><li>在左上方，选择要显示广告的博客。</li><li>点击左侧菜单中的布局。</li><li>将光标指向要显示广告的区域，然后点击添加小工具。</li><li>在弹出式窗口中，点击 “HTML/Javascript” 旁边的 “添加” 图标添加。</li><li>可选：输入标题。</li><li>将广告服务网站的代码复制并粘贴到 “内容” 部分中。</li><li>点击保存。</li><li>如需保存排列方式，请点击右下方的 “保存” 图标保存。</li></ul><p><strong>查看您的 Blogger 收入报告</strong></p><ul><li>登录到 Blogger。</li><li>在左上方，选择要查看的博客。</li><li>点击左侧菜单中的收入然后查看收入。</li></ul><p>提示：如需了解报告中各个术语的含义，请使用 AdSense 词汇表。</p><p><strong>如果 AdSense 未启用，应采取什么措施</strong></p><p>如需检查您的帐号是否仍然有效，请访问 AdSense。</p><ul><li>登录到 Blogger。</li><li>在左上方，选择博客。</li><li>点击左侧菜单中的布局。</li><li>点击 “AdSense” 小工具下方的修改然后保存。</li><li>针对您博客上的每个 AdSense 小工具，重复第 4 步的操作。</li><li>点击 “博文” 小工具底部的修改然后保存。</li><li>如需保存排列方式，请点击右下方的 “保存” 图标保存。</li></ul><p><strong>设置 ads. Txt 文件</strong></p><p>如果您的博客通过第三方提供商获利，或者您已在博客中手动集成了 AdSense，那么您需要自行设置 ads. Txt 文件的内容。详细了解 AdSense 的 ads. Txt。</p><ul><li>登录到 Blogger。</li><li>在左上方，选择您要设置的博客。</li><li>点击左侧菜单中的设置。</li><li>在 “获利” 下方，开启启用自定义 ads. Txt。</li><li>点击自定义 ads. Txt。</li><li>从第三方获利提供商处复制设置，并将其粘贴到文本框中。</li><li>点击保存。</li></ul><p>提示：如需查看 ads. Txt 文件的内容，请转到 http://&lt;您的博客地址&gt;/ads. Txt。</p><p><strong>3.&nbsp;如何使用站点 Feed 在其他网站上分享自己的内容？</strong></p><p>当您更新网页内容时，站点 Feed 会向其他网站上的用户发送通知。</p><ul><li>选择要分享的内容</li><li>登录到 Blogger。</li><li>在左上方，选择博客。</li><li>点击左侧菜单中的设置。</li><li>在 “站点 Feed” 下方，点击允许博客 Feed，选择要分享的内容量。都允许：显示每篇博文的全部内容。</li><li>在继续阅读标示之前：显示继续阅读标示之前的内容。</li><li>摘要：显示每篇博文开头部分的摘录。</li><li>自定义：针对博文、评论 Feed 或每篇博文的评论 Feed 设置高级选项。</li><li>都不允许：完全停用站点 Feed。</li><li>点击保存。</li></ul><p><strong>使用 Blogger Feed 分享内容</strong></p><p>重要提示：站点 Feed 不适用于不公开的博客。</p><ul><li>要分享博客，您可以将下列网址添加到 Feed 阅读器中。</li></ul><p><br></p><p><strong>4. 如何通过其他 Feed 服务提供商分享内容？</strong></p><p>如果您使用的是 FeedBurner 或其他服务，请重定向您的 Feed 以完成设置：</p><ul><li>从相应服务中复制站点 Feed。</li><li>登录到 Blogger。</li><li>在左上方，选择博客。</li><li>点击左侧菜单中的设置。</li><li>在 “站点 Feed” 下方，点击博文 Feed 重定向网址。</li><li>粘贴网址。</li><li>点击保存。</li></ul><p><strong>5. 如何将 Google Analytics（分析）与 Blogger 结合使用？</strong></p><p><strong>第 1 步：注册 Google Analytics（分析）</strong></p><ul><li><a href=\"https://blog.ccino.org/p/googles-blogger-registration-and-usage-tutorial/#\" rel=\"noopener noreferrer\" target=\"_blank\">注册 Google Analytics（分析）帐号</a>。</li><li>找到您的 Google Analytics（分析）跟踪 ID。</li></ul><p><br></p><p><strong>第 2 步：向 Blogger 添加 Google Analytics（分析）跟踪</strong></p><p>你最长可能需要等待 24 小时，相应数据才会显示在 Google Analytics（分析）中。</p><ul><li><a href=\"https://blog.ccino.org/p/googles-blogger-registration-and-usage-tutorial/#\" rel=\"noopener noreferrer\" target=\"_blank\">登录到 Blogger</a>。</li><li>在左上方，选择要分析的博客。</li><li>点击左侧菜单中的设置。</li><li>在 “基本” 下方，点击&nbsp;Google Analytics（分析）媒体资源 ID。</li><li>输入您的&nbsp;Google Analytics（分析）跟踪 ID。请确保按以下格式输入 ID：UA-XXXXX-XX。</li><li>点击保存。</li></ul><p>本文出自：<a href=\"https://www.shakingcloud.com/article/content/202301/172.html\" rel=\"noopener noreferrer\" target=\"_blank\">Google旗下最新Blogger注册及使用教程（下载+注册流程+常见问题汇总），靠它赚美金也可以的</a></p>","content_text":"爱浏览博客或者写博客的朋友，可能都知道 Blogger 这个平台，它是 Google 旗下一家大型的博客服务网站，仅需拥有一个 Google\n账户即可将成千上万的博文、照片以及更多内容妥善地存储在 Blogger 中。除了撰写 / 浏览博客外，你还可以通过 Blogger 赚钱。那么如何注册并使用\nBlogger 呢？为了方便大家顺利拥有一个 Blogger 账号并很好的使用它，小编这里就整理了一篇最新最详细的 Blogger\n注册及使用教程，包含下载，注册流程以及常见问题汇总。满满的干货，需要的朋友，赶紧来 GET 吧。\n\n\nBLOGGER 简介\n\n\n\n\n【官方网站】：https://www.blogger.com/\n\nBlogger 原是 Google 旗下一家大型的博客服务网站。它也是第一家大规模博客服务的提供商，由旧金山一家名为 Pyra Labs 的小型公司于 1999\n年 8\n月创办。它就好比国内新浪提供的新浪博客、搜狐提供的个人博客、更类似于网易的自媒体、百度的自媒体。有几十套漂亮的免费主题供你选、如果不满意还可以直接在后台修改\nHTML 代码、改成你喜欢的主题！利用 Blogger，你可以将成千上万的博文、照片以及更多内容妥善地存储在 Google 中。\n\n[https://pic.ccino.win/OB/2024/10/202410071342270.png]\n\n\nBLOGGER 优点\n\n\n\n\n①最突出的优点就是简洁但功能强大，Blogger 没有多余而花哨的功能，必要的功能一个都不差。\n\n②Bloger 自由性最大的地方在于其模板可以自定义，也就是说你可以修改模板里的任何内容，包括 Google 的广告，这给那些懂 Html 和 CSS 的\nBlogger 提供了很大的自由度。\n\n③用户还可以利用 Blogger 内置的分析功能，了解哪些博客比较受欢迎。您可以了解受众群体来自哪里，以及他们对哪些内容感兴趣。此外，你还可以将博客直接关联到\nGoogle Analytics（分析），进一步查看详细信息。\n\n④此外，Google AdSense 会自动在您的博客上显示相关的定位广告，你只需发布自己感兴趣的内容，即可赚取收益。Blogger\n还提供免费域名、如果你有独立域名、你也可以用你自己的独立域名。\n\n\nBLOGGER 下载\n\n\n\n\n下载 Blogger 应用，即可随时随地开始撰写博文。\n\n * 撰写博文（可保存为草稿或立即发布）\n * 编辑现有博文\n * 查看您所保存和发布的博文列表\n * 切换帐号 / 博客（如果您有多个帐号 / 博客）\n * 嵌入图库中的图片或直接通过该应用拍摄的照片\n * 向博文添加标签\n\nGoogle Paly；Apple Store\n\n\n准备工作\n\n\n\n\nBlogger 已在中国内地被永久性屏蔽。所以不能以正常步骤在我国大陆使用。所以在准备注册并使用 Blogger 之前，你需要做好以下准备。\n\n1. 稳定的科学上网工具\n\n这些平台有些还有免费试用，大家不妨去看看！\n\n2. 准备好 Google 账号\n\n注册 Blogger 网站的话，必须使用 Google 账户才能注册哟。\n\n如果你还没有 Google 账号，但有 Google Voice，Textnow 这类虚拟手机号码的话，可以试试用虚拟号码注册一个 Google\n账户。如果自己注册不成功，那就找渠道购买一个吧！\n\n3. 准备好浏览器\n\n个人比较推荐使用 Chrome 谷歌浏览器（建议安装翻译插件，可自动帮你把英文页面翻译成中文），其次是 Firefox\n火狐浏览器，也可以使用 Bing 浏览器。\n\n\nBLOGGER 注册流程\n\n\n\n\n第一步：https://www.blogger.com/，进入官网首页，之后点击 “创建博客”，或者右上角\n“登陆”。 [https://pic.ccino.win/OB/2024/10/202410071343915.png]\n\n第二步：如果你登录了 Google 账户的话，会弹出这样的页面，你可以直接点击你账户，就可以登陆 Blogger\n了。（如果遇到点击这个选项没有反应，也可以点下方【使用其他账号】然后重新输入一下账号，就可以正常登录。）\n\n[https://pic.ccino.win/OB/2024/10/202410071343265.png]\n\n选择完之后，需要你再次输入密码，验证你的账号才能登陆成功哦！ [https://pic.ccino.win/OB/2024/10/202410071344083.png]\n\n但如果你自己还没有一个 Google 账号，可以点击 “使用其他账号”-“创建账号”-“为我自己创建”，然后输入你的相关信息，设置密码，从而创建一个\nGoogle 账号。拥有一个 Google 账号，便可以使用 Google\n所有产品和服务了。 [https://pic.ccino.win/OB/2024/10/202410071344541.png]\n\n[https://pic.ccino.win/OB/2024/10/202410071345439.png]\n\n第三步：设置博客名称。如果没有想好，也可以暂时跳过，设置好了的话，就点击\n“下一步”。 [https://pic.ccino.win/OB/2024/10/202410071345383.png]\n\n第四步：为你的博客选择一个 URL，这个网址是人们如何在线找到您的博客的方式。\n\n[https://pic.ccino.win/OB/2024/10/202410071346110.png]\n\n如何添加域名，可以 https://support.google.com/blogger/answer/1233387-hl=zh-Hans&ref_topic=6321959 进入页面查看详情。\n\n第五步：之后，你将为你的博客署名。以上步骤完成之后，便可以进入 Blogger 的使用界面。接下来将为大家介绍一些常见的功能及使用指南。\n\n\nBLOGGER 功能介绍及使用教程\n\n\n\n\n1. 开启让搜索引擎检索\n\n开启让搜索引擎检索，人们将更容易用搜索引擎（如 Google 和 Bing）找到你的博客：\n\n * 允许搜索引擎列出您的博客。\n * 在整个网站中使用关键字，使博客显示在搜索结果的较前位置。\n\n\n\n\n具体操作流程：\n\n 1. 登录到 Blogger。\n 2. 在左上方，选择要在搜索引擎中列出的博客。\n 3. 点击左侧菜单中的设置。\n 4. 在 “隐私权” 下方，开启让搜索引擎检索。\n\n针对您博客的搜索引擎优化 (SEO) 提示\n\n您可以在博文和页面的标题和文本中使用相关的关键字，也可以指定搜索引擎不要在搜索结果中显示哪些页面、博文和链接。\n\n2. 备份或导入博客\n\n备份博客内容\n\n要获取博客的博文、页面和评论的 .xml 文件，请执行以下操作：\n\n * 登录到 Blogger。\n * 在左上方，点击要备份的博客。\n * 点击左侧菜单中的设置。\n * 在 “管理博客” 下方，点击备份内容下载。\n\n保存博客的主题背景副本\n\n * 登录到 Blogger。\n * 在左上方，点击要保存的博客。\n * 在左侧的菜单中，点击主题背景。\n * 点击右上方的 “更多” 图标。\n\n将博文和评论导入到您的博客\n\n重要提示：你每天可以导入的文件数量存在上限，但文件大小不受限制。\n\n要导入博文和评论的 .xml 文件，请执行以下操作：\n\n * 登录到 Blogger。\n * 在左上方，点击要导入内容的博客。\n * 点击左侧菜单中的设置。\n * 在 “管理博客” 下方，点击导入内容\n * 如果您不想自动发布已导入的内容，请关闭自动发布已导入的所有博文和页面。\n * 选择要从计算机导入的. Xml 文件。\n * 点击打开。\n\n3. 添加团队成员\n\n要管理博客和发布内容，受邀的作者和管理员需要使用 Google 帐号。要邀请读者访问您的博客，请按以下步骤操作：\n\n * 登录到 Blogger。\n * 在左上方，选择博客。\n * 点击左侧菜单中的设置。\n * 点击 “权限” 下方的邀请更多读者。\n * 输入您要添加的用户的电子邮件地址。\n * 点击发送。\n\n4. 添加作者\n\n要邀请新的作者，请执行以下操作：\n\n * 登录到 Blogger。\n * 在左上方，选择博客。\n * 点击左侧菜单中的设置。\n * 点击 “权限” 下方的邀请更多作者。\n * 输入您要添加的作者的电子邮件地址。\n * 点击发送。对方接受邀请后，系统会将其列为作者。\n\n5. 将博客设为不公开\n\n如果有人知道某个图片的网址，那么在您将博客设为不公开之后，这些人仍可查看该图片。\n\n默认情况下，您的博客是完全公开的，任何人都可以在网上阅读。\n\n要更改哪些人可以访问您的博客，请按以下步骤操作：\n\n1. 登录到 Blogger。\n\n 1. 在左上方，选择博客。\n 2. 点击左侧菜单中的设置。\n 3. 点击 “权限” 下方的读者权限，然后从以下选项中选择：\n\n * 公开：网络上的任何人都可以访问和阅读您的博客。\n * 仅作者可见：您博客的所有作者均可访问和阅读您的博客。\n * 自定义读者：只有您邀请的读者才能访问和阅读您的博客。\n\n6. 启用 “共享” 按钮\n\n重要提示：您的博客必须设为公开状态才能使用 “共享” 按钮。\n\n要让其他人通过电子邮件和社交媒体网站分享您的博客，您可以启用 “共享” 按钮：\n\n 1. 登录到 Blogger。\n 2. 在左上方，选择博客。\n 3. 点击左侧菜单中的布局。\n 4. 在 “页面主体” 部分，点击 “博文” 旁边的 “修改” 图标。\n 5. 勾选 “显示共享按钮” 旁边的复选框。\n\n\n其他常见问题\n\n\n\n\n1. 怎么删除和恢复微博？\n\n你必须是管理员，才能删除博客。删除博客后，您可以在短时间内恢复该博客。\n\n删除博客\n\n * 登录到 Blogger。\n * 在左上方，点击要删除的博客。\n * 点击左侧菜单中的设置。\n * 在 “管理博客” 下方，点击移除博客\n\n恢复已删除的博客\n\n删除博客后，短期之内您仍可恢复该博客。要恢复博客，请执行以下操作：\n\n * 登录到 Blogger。\n * 在左上方的 “已移至回收站的博客” 下方，点击您要恢复的博客\n\n永久删除\n\n博客重要提示：永久删除博客后，所有的博客信息、博文和页面都会被删除。您将无法恢复永久删除的博客或再次使用该博客的网址。\n\n要在删除博客后再将其永久删除，请按照以下步骤操作：\n\n * 在左上方的 “已移至回收站的博客” 下方，点击要永久删除的博客。\n\n\n\n\n2. 在你的博客上如何显示广告？\n\n如需通过 Blogger 赚取收入，您可以在自己的博客页面上显示 AdSense 和其他广告。\n\n注册 AdSense\n\n * 重要提示：请务必先完成所有步骤，然后再点击重定向。\n * 登录到 Blogger。\n * 在左上方，选择博客。\n * 点击左侧菜单中的收入然后创建 AdSense 帐号。\n * 如果您的博客目前不具备相关资格，请了解如何满足使用 AdSense 的条件。\n * 选择与您的 Blogger 帐号相关联的 Google 电子邮件地址。\n * 填写 AdSense 表单，然后点击创建帐号。\n * 输入您的付款明细，并验证电话号码。\n * 点击提交。\n\n系统会自动将您转至 Blogger 以完成此流程。如果没有跳转，请点击重定向。\n\n提示：如果您在完成上述步骤时被中断，请重新执行第 1 至 5 步。如果您的 AdSense 首页连续几天都显示 “我们正在为您完成配置”，请点击接受关联。\n\n在博文之间显示广告\n\n * 登录到 Blogger。\n * 在左上方，选择要显示广告的博客。\n * 点击左侧菜单中的布局。\n * 根据不同的模板，选择以下操作：\n * 在 “页面主体” 下的 “博文” 中，点击修改。\n * 在 “主体” 下的 “博文” 中，点击修改。\n * 勾选 “在博文之间显示广告” 旁边的复选框。\n * 选择广告格式、颜色以及广告显示的频率。\n * 点击保存。\n\n如需保存排列方式，请点击右下方的 “保存” 图标保存。\n\n在专栏中显示广告\n\n * 登录到 Blogger。\n * 在左上方，选择要显示广告的博客。\n * 点击左侧菜单中的布局。\n * 在 “边栏” 下方，点击添加小工具。\n * 点击 “AdSense” 旁边的 “添加” 图标添加。\n * 配置 AdSense，然后点击保存。\n * 如需保存排列方式，请点击右下方的 “保存” 图标保存。\n\n显示来自其他广告服务的广告\n\n * 登录到 Blogger。\n * 在左上方，选择要显示广告的博客。\n * 点击左侧菜单中的布局。\n * 将光标指向要显示广告的区域，然后点击添加小工具。\n * 在弹出式窗口中，点击 “HTML/Javascript” 旁边的 “添加” 图标添加。\n * 可选：输入标题。\n * 将广告服务网站的代码复制并粘贴到 “内容” 部分中。\n * 点击保存。\n * 如需保存排列方式，请点击右下方的 “保存” 图标保存。\n\n查看您的 Blogger 收入报告\n\n * 登录到 Blogger。\n * 在左上方，选择要查看的博客。\n * 点击左侧菜单中的收入然后查看收入。\n\n提示：如需了解报告中各个术语的含义，请使用 AdSense 词汇表。\n\n如果 AdSense 未启用，应采取什么措施\n\n如需检查您的帐号是否仍然有效，请访问 AdSense。\n\n * 登录到 Blogger。\n * 在左上方，选择博客。\n * 点击左侧菜单中的布局。\n * 点击 “AdSense” 小工具下方的修改然后保存。\n * 针对您博客上的每个 AdSense 小工具，重复第 4 步的操作。\n * 点击 “博文” 小工具底部的修改然后保存。\n * 如需保存排列方式，请点击右下方的 “保存” 图标保存。\n\n设置 ads. Txt 文件\n\n如果您的博客通过第三方提供商获利，或者您已在博客中手动集成了 AdSense，那么您需要自行设置 ads. Txt 文件的内容。详细了解 AdSense 的\nads. Txt。\n\n * 登录到 Blogger。\n * 在左上方，选择您要设置的博客。\n * 点击左侧菜单中的设置。\n * 在 “获利” 下方，开启启用自定义 ads. Txt。\n * 点击自定义 ads. Txt。\n * 从第三方获利提供商处复制设置，并将其粘贴到文本框中。\n * 点击保存。\n\n提示：如需查看 ads. Txt 文件的内容，请转到 http://<您的博客地址>/ads. Txt。\n\n3. 如何使用站点 Feed 在其他网站上分享自己的内容？\n\n当您更新网页内容时，站点 Feed 会向其他网站上的用户发送通知。\n\n * 选择要分享的内容\n * 登录到 Blogger。\n * 在左上方，选择博客。\n * 点击左侧菜单中的设置。\n * 在 “站点 Feed” 下方，点击允许博客 Feed，选择要分享的内容量。都允许：显示每篇博文的全部内容。\n * 在继续阅读标示之前：显示继续阅读标示之前的内容。\n * 摘要：显示每篇博文开头部分的摘录。\n * 自定义：针对博文、评论 Feed 或每篇博文的评论 Feed 设置高级选项。\n * 都不允许：完全停用站点 Feed。\n * 点击保存。\n\n使用 Blogger Feed 分享内容\n\n重要提示：站点 Feed 不适用于不公开的博客。\n\n * 要分享博客，您可以将下列网址添加到 Feed 阅读器中。\n\n\n\n\n4. 如何通过其他 Feed 服务提供商分享内容？\n\n如果您使用的是 FeedBurner 或其他服务，请重定向您的 Feed 以完成设置：\n\n * 从相应服务中复制站点 Feed。\n * 登录到 Blogger。\n * 在左上方，选择博客。\n * 点击左侧菜单中的设置。\n * 在 “站点 Feed” 下方，点击博文 Feed 重定向网址。\n * 粘贴网址。\n * 点击保存。\n\n5. 如何将 Google Analytics（分析）与 Blogger 结合使用？\n\n第 1 步：注册 Google Analytics（分析）\n\n * 注册 Google Analytics（分析）帐号。\n * 找到您的 Google Analytics（分析）跟踪 ID。\n\n\n\n\n第 2 步：向 Blogger 添加 Google Analytics（分析）跟踪\n\n你最长可能需要等待 24 小时，相应数据才会显示在 Google Analytics（分析）中。\n\n * 登录到 Blogger。\n * 在左上方，选择要分析的博客。\n * 点击左侧菜单中的设置。\n * 在 “基本” 下方，点击 Google Analytics（分析）媒体资源 ID。\n * 输入您的 Google Analytics（分析）跟踪 ID。请确保按以下格式输入 ID：UA-XXXXX-XX。\n * 点击保存。\n\n本文出自：Google旗下最新Blogger注册及使用教程（下载+注册流程+常见问题汇总），靠它赚美金也可以的","image":"https://cdn-blog-ccino-cc.ccino.cc/blog-ccino-cc/production/images/item-a4164b97f33fb721ac460c6f6bc42ac3.png","date_published":"2024-10-07T05:55:22.938Z","_microfeed":{"web_url":"https://blog-ccino-cc.pages.dev/i/googleblogger-RfaEeSmqnno/","json_url":"https://blog-ccino-cc.pages.dev/i/RfaEeSmqnno/json/","rss_url":"https://blog-ccino-cc.pages.dev/i/RfaEeSmqnno/rss/","guid":"RfaEeSmqnno","status":"published","itunes:episodeType":"full","date_published_short":"Mon Oct 07 2024","date_published_ms":1728280522938}},{"id":"p2Od2jl0y7Y","title":"肥羊的IPTV等直播源代理Docker部署教程","attachments":[{"url":"https://blog.ccino.org/p/feiyang-iptv-and-other-live-source-proxy-docker-deployment-tutorial/","mime_type":"text/html"}],"url":"https://blog.ccino.cc/i/feiyang-iptv-and-other-live-source-proxy-docker-deployment-tutorial/","external_url":"https://blog.ccino.org/p/feiyang-iptv-and-other-live-source-proxy-docker-deployment-tutorial/","content_html":"<h2>一、推荐使用 Docker 一键运行，并配置 watchtower 监听 Docker 镜像更新，直接一劳永逸：</h2><h3>1，使用 Docker 一键配置 allinone</h3><pre class=\"ql-syntax\" spellcheck=\"false\">docker run -d --restart unless-stopped --net=host --privileged=true -p 35455:35455 --name allinone youshandefeiyang/allinone\n</pre><p><br></p><h3>2，一键配置 watchtower 每天凌晨两点自动监听 allinone 镜像更新，同步 GitHub 仓库：</h3><pre class=\"ql-syntax\" spellcheck=\"false\">docker run -d --name watchtower --restart unless-stopped -v /var/run/docker.sock:/var/run/docker.sock containrrr/watchtower allinone -c --schedule \"0 0 2 * * *\"\n</pre><p><br></p><h2>二、直接运行：</h2><p><br></p><p>首先去 action 中下载对应平台二进制执行文件，然后解压并直接执行</p><pre class=\"ql-syntax\" spellcheck=\"false\">chmod 777 allinone &amp;&amp; ./allinone\n</pre><p><br></p><p><br></p><p>建议搭配进程守护工具进行使用，windows 直接双击运行！</p><h2>三、详细使用方法</h2><h2><strong>Ysptp 和 Itv 聚合 M 3 U 获取：</strong></h2><p><br></p><p><strong>声明：如果你是在公网服务器部署，不愿意开启聚合 TV 直播服务，在运行裸程序或者 Docker 时，加上参数 -tv=false 即可不开启直播服务，具体可</strong><a href=\"https://t.me/feiyangofficalchannel/922\" rel=\"noopener noreferrer\" target=\"_blank\"><strong>点击参考命令范例</strong></a></p><pre class=\"ql-syntax\" spellcheck=\"false\">http://你的IP:35455/tv.m3u\n</pre><p><br></p><h2><strong>BiliBili、虎牙、斗鱼、YY 实时 M 3 U 获取：</strong></h2><h3>BiliBili 生活：</h3><pre class=\"ql-syntax\" spellcheck=\"false\">http://你的IP:35455/bililive.m3u\n</pre><p><br></p><h3>虎牙一起看：</h3><pre class=\"ql-syntax\" spellcheck=\"false\">http://你的IP:35455/huyayqk.m3u\n</pre><p><br></p><h3>斗鱼一起看：</h3><pre class=\"ql-syntax\" spellcheck=\"false\">http://你的IP:35455/douyuyqk.m3u\n</pre><p><br></p><h3>YY 轮播：</h3><pre class=\"ql-syntax\" spellcheck=\"false\">http://你的IP:35455/yylunbo.m3u\n</pre><p><br></p><h3>如果使需要自定义 M 3 U 文件中的前缀域名，可以传入 url 参数（需要注意的是，当域名中含有特殊字符时，需要对链接进行 urlencode 处理）：</h3><pre class=\"ql-syntax\" spellcheck=\"false\">http://你的IP:35455/xxxyqk.m3u?url=http://192.168.10.1:35455\n</pre><p><br></p><h2><strong>抖音：</strong></h2><h3>默认最高画质，浏览器打开并复制&nbsp;(live.douyin.com/)xxxxxx，只需要复制后面的 xxxxx 即可（可选 flv 和 hls 两种种流媒体传输方式，默认 flv）：</h3><pre class=\"ql-syntax\" spellcheck=\"false\">http://你的IP:35455/douyin/xxxxx(?stream=hls)\n</pre><p><br></p><h2><strong>斗鱼：</strong></h2><h3>1，可选 m 3 u 8 和 flv 以及 xs 三种流媒体传输方式【&nbsp;(www.douyu.com/)xxxxxx 或 (www.douyu.com/xx/xx?rid=)xxxxxx，默认 flv】：</h3><pre class=\"ql-syntax\" spellcheck=\"false\">http://你的IP:35455/douyu/xxxxx(?stream=flv)\n</pre><p><br></p><h2><strong>BiliBili&nbsp;(live.bilibili.com/)xxxxxx：</strong></h2><h3>1，平台 platform 参数选择（默认 web，如果有问题，可以切换 h 5 平台）：</h3><pre class=\"ql-syntax\" spellcheck=\"false\">\"flv\"   =&gt; \"FLV\"\n\"hls\"    =&gt; \"M3U8\"\n</pre><p><br></p><h3>2，线路 line 参数选择（默认线路二，如果卡顿 / 看不了，请切换线路一或者三，一般直播间只会提供两条线路，所以建议线路一 / 二之间切换）：</h3><pre class=\"ql-syntax\" spellcheck=\"false\">\"first\"  =&gt; \"线路一\"\n\"second\" =&gt; \"线路二\"\n\"third\"  =&gt; \"线路三\"\n</pre><p><br></p><h3>3，画质 quality 参数选择（默认原画，可以看什么画质去直播间看看，能选什么画质就能加什么参数，参数错误一定不能播放）：</h3><pre class=\"ql-syntax\" spellcheck=\"false\">\"4\" =&gt; \"原画质\"\n\"3\" =&gt; \"低画质\"\n</pre><p><br></p><h3>4，最后的代理链接示例：</h3><pre class=\"ql-syntax\" spellcheck=\"false\">http://你的IP:35455/bilibili/xxxxxx(?platform=hls&amp;line=first&amp;quality=4)\n</pre><p><br></p><h2><strong>虎牙&nbsp;(huya.com/)xxxxxx：</strong></h2><h3>1，查看可用 CDN：</h3><pre class=\"ql-syntax\" spellcheck=\"false\">http://你的IP:35455/huya/xxxxx?type=display\n</pre><p><br></p><h3>2，切换媒体类型（默认 flv，可选 flv、hls）：</h3><pre class=\"ql-syntax\" spellcheck=\"false\">http://你的IP:35455/huya/xxxxx?media=hls\n</pre><p><br></p><h3>3，切换 CDN（默认 hwcdn，可选 hycdn、alicdn、txcdn、hwcdn、hscdn、wscdn，具体可先访问 1 获取）：</h3><pre class=\"ql-syntax\" spellcheck=\"false\">http://你的IP:35455/huya/xxxxx?cdn=alicdn\n</pre><p><br></p><h3>4，最后的代理链接示例：</h3><pre class=\"ql-syntax\" spellcheck=\"false\">http://你的IP:35455/huya/xxxxx(?media=xxx&amp;cdn=xxx)\n</pre><p><br></p><h2><strong>YouTube:</strong></h2><pre class=\"ql-syntax\" spellcheck=\"false\">https://www.youtube.com/watch?v=cK4LemjoFd0\nRid: cK4LemjoFd0\nhttp://你的IP:35455/youtube/cK4LemjoFd0(?quality=1080/720...)\n</pre><p><br></p><h2><strong>YY（默认最高画质，参数为 4）:</strong></h2><pre class=\"ql-syntax\" spellcheck=\"false\">https://www.yy.com/xxxx\nhttp://你的IP:35455/yy/xxxx(?quality=1/2/3/4...)\n</pre><p><br></p><h2>更多平台后续会酌情添加</h2>","content_text":"一、推荐使用 DOCKER 一键运行，并配置 WATCHTOWER 监听 DOCKER 镜像更新，直接一劳永逸：\n\n\n1，使用 DOCKER 一键配置 ALLINONE\n\ndocker run -d --restart unless-stopped --net=host --privileged=true -p 35455:35455 --name allinone youshandefeiyang/allinone\n\n\n\n\n\n\n2，一键配置 WATCHTOWER 每天凌晨两点自动监听 ALLINONE 镜像更新，同步 GITHUB 仓库：\n\ndocker run -d --name watchtower --restart unless-stopped -v /var/run/docker.sock:/var/run/docker.sock containrrr/watchtower allinone -c --schedule \"0 0 2 * * *\"\n\n\n\n\n\n\n二、直接运行：\n\n\n\n\n首先去 action 中下载对应平台二进制执行文件，然后解压并直接执行\n\nchmod 777 allinone && ./allinone\n\n\n\n\n\n\n\n\n建议搭配进程守护工具进行使用，windows 直接双击运行！\n\n\n三、详细使用方法\n\n\nYSPTP 和 ITV 聚合 M 3 U 获取：\n\n\n\n\n声明：如果你是在公网服务器部署，不愿意开启聚合 TV 直播服务，在运行裸程序或者 Docker 时，加上参数 -tv=false\n即可不开启直播服务，具体可点击参考命令范例\n\nhttp://你的IP:35455/tv.m3u\n\n\n\n\n\n\nBILIBILI、虎牙、斗鱼、YY 实时 M 3 U 获取：\n\n\nBILIBILI 生活：\n\nhttp://你的IP:35455/bililive.m3u\n\n\n\n\n\n\n虎牙一起看：\n\nhttp://你的IP:35455/huyayqk.m3u\n\n\n\n\n\n\n斗鱼一起看：\n\nhttp://你的IP:35455/douyuyqk.m3u\n\n\n\n\n\n\nYY 轮播：\n\nhttp://你的IP:35455/yylunbo.m3u\n\n\n\n\n\n\n如果使需要自定义 M 3 U 文件中的前缀域名，可以传入 URL 参数（需要注意的是，当域名中含有特殊字符时，需要对链接进行 URLENCODE 处理）：\n\nhttp://你的IP:35455/xxxyqk.m3u?url=http://192.168.10.1:35455\n\n\n\n\n\n\n抖音：\n\n\n默认最高画质，浏览器打开并复制 (LIVE.DOUYIN.COM/)XXXXXX，只需要复制后面的 XXXXX 即可（可选 FLV 和 HLS\n两种种流媒体传输方式，默认 FLV）：\n\nhttp://你的IP:35455/douyin/xxxxx(?stream=hls)\n\n\n\n\n\n\n斗鱼：\n\n\n1，可选 M 3 U 8 和 FLV 以及 XS 三种流媒体传输方式【 (WWW.DOUYU.COM/)XXXXXX 或\n(WWW.DOUYU.COM/XX/XX?RID=)XXXXXX，默认 FLV】：\n\nhttp://你的IP:35455/douyu/xxxxx(?stream=flv)\n\n\n\n\n\n\nBILIBILI (LIVE.BILIBILI.COM/)XXXXXX：\n\n\n1，平台 PLATFORM 参数选择（默认 WEB，如果有问题，可以切换 H 5 平台）：\n\n\"flv\"   => \"FLV\"\n\"hls\"    => \"M3U8\"\n\n\n\n\n\n\n2，线路 LINE 参数选择（默认线路二，如果卡顿 / 看不了，请切换线路一或者三，一般直播间只会提供两条线路，所以建议线路一 / 二之间切换）：\n\n\"first\"  => \"线路一\"\n\"second\" => \"线路二\"\n\"third\"  => \"线路三\"\n\n\n\n\n\n\n3，画质 QUALITY 参数选择（默认原画，可以看什么画质去直播间看看，能选什么画质就能加什么参数，参数错误一定不能播放）：\n\n\"4\" => \"原画质\"\n\"3\" => \"低画质\"\n\n\n\n\n\n\n4，最后的代理链接示例：\n\nhttp://你的IP:35455/bilibili/xxxxxx(?platform=hls&line=first&quality=4)\n\n\n\n\n\n\n虎牙 (HUYA.COM/)XXXXXX：\n\n\n1，查看可用 CDN：\n\nhttp://你的IP:35455/huya/xxxxx?type=display\n\n\n\n\n\n\n2，切换媒体类型（默认 FLV，可选 FLV、HLS）：\n\nhttp://你的IP:35455/huya/xxxxx?media=hls\n\n\n\n\n\n\n3，切换 CDN（默认 HWCDN，可选 HYCDN、ALICDN、TXCDN、HWCDN、HSCDN、WSCDN，具体可先访问 1 获取）：\n\nhttp://你的IP:35455/huya/xxxxx?cdn=alicdn\n\n\n\n\n\n\n4，最后的代理链接示例：\n\nhttp://你的IP:35455/huya/xxxxx(?media=xxx&cdn=xxx)\n\n\n\n\n\n\nYOUTUBE:\n\nhttps://www.youtube.com/watch?v=cK4LemjoFd0\nRid: cK4LemjoFd0\nhttp://你的IP:35455/youtube/cK4LemjoFd0(?quality=1080/720...)\n\n\n\n\n\n\nYY（默认最高画质，参数为 4）:\n\nhttps://www.yy.com/xxxx\nhttp://你的IP:35455/yy/xxxx(?quality=1/2/3/4...)\n\n\n\n\n\n\n更多平台后续会酌情添加","date_published":"2024-10-06T11:49:27.794Z","_microfeed":{"is_audio":false,"is_document":false,"is_external_url":true,"is_video":false,"is_image":false,"web_url":"https://blog-ccino-cc.pages.dev/i/iptvdocker-p2Od2jl0y7Y/","json_url":"https://blog-ccino-cc.pages.dev/i/p2Od2jl0y7Y/json/","rss_url":"https://blog-ccino-cc.pages.dev/i/p2Od2jl0y7Y/rss/","guid":"p2Od2jl0y7Y","status":"published","itunes:episodeType":"full","date_published_short":"Sun Oct 06 2024","date_published_ms":1728215367794}},{"id":"aGCkTkpFq6N","title":"Hugo博客使用utterances作为评论系统","attachments":[{"url":"https://blog.ccino.org/p/hugo-blog-uses-utterances-as-comment-system/","mime_type":"text/html"}],"url":"https://blog.ccino.cc/i/hugo-blog-uses-utterances-as-comment-system/","external_url":"https://blog.ccino.org/p/hugo-blog-uses-utterances-as-comment-system/","content_html":"<p>Hugo 博客上的评论插件一直试了好多，但网上关于 hugo 的评论插件是太少了，找了好长时间都没有值得考究的。如： Disqus、gitment、gitalk、来比力… 这些都是国外的，加载速度确实不敢恭维，就目前为止，Disqus 官网还被墙了，gitment 由于其开发者很久没有维护，gitalk 还算行，gitment、gitalk 这两个评论服务是通过 GitHub 提供的 API 创建含有相应标签的 issue。</p><ol><li>国内其实也有很多评论插件，如：有言（官网已经挂了）、畅言（搜狐出品）。还有来比力（韩国的，之前 hexo 博客用到过，加载速度也好慢），Valine（这个评论插件确实不错，但注册账号后还要用身份证号码信息认证，认证完后还要用支付宝扫码验证是不是本人的，基于安全考虑，暂时还是先不要用这个，不知道一个评论插件还要这么多程序）</li><li>其实第三方插件也还是要承担些安全责任的，发表不好的言论都是有些关系的。</li><li>Hugo 推荐 utterances 和 gitalk 评论插件来使用。</li></ol><h2>Utterances 是一款基于 GitHub issues 的评论工具</h2><p><br></p><p>相比同类的工具 gitment、gitalk 以及 disqus 评论工具，优点如下：</p><ol><li>极其轻量</li><li>加载非常快</li><li>配置比较简单</li><li>Disqus 这个工具配置也比较简单，也是免费的。但是，广告多，而且加载也比较慢。体验了一把 utterances 后，马上切换到 utterances。</li><li>Utterances 是基于 github issue，拥有多种主题的开源免费小组件。</li></ol><h2>Hugo 博客配置使用 utterances</h2><ul><li>首先在 github 上创建一个空仓库，如：hugoblogtalks，并且初始化设置，仓库必须是 Public，而不是私有的，这样我们的读者才可以查看以及发表评论。&nbsp;<img src=\"https://pic.ccino.win/OB/2024/10/202410052029629.png\"></li><li>接着就是在 hugo 的配置文件（config. Toml）中启用 utterances，打开 config. Toml，添加如下：</li></ul><pre class=\"ql-syntax\" spellcheck=\"false\">## 配置 utteranc评论,教程参考 https://utteranc.es/\n[params.utteranc]\n  enable = true\n  repo = \"yunxd/hugoblogtalks\" ##换成自己得\n  issueTerm = \"pathname\"\n  theme = \"github-light\"\n</pre><p><br></p><p>Repo 的格式为：github 用户名 / 创建的仓库名</p><h2>Github 上安装 utterances</h2><ul><li>首先必须在 github 上进行安装 utterances，访问&nbsp;<a href=\"https://cloud.tencent.com/developer/tools/blog-entry?target=https%3A%2F%2Fgithub.com%2Fapps%2Futterances&amp;source=article&amp;objectId=1834230\" rel=\"noopener noreferrer\" target=\"_blank\">utterances 应用程序</a>&nbsp;然后点击 Install 按钮进行安装。</li><li>在这里可以选择可以关联的存储库，可以选择我们所拥有的库 (也包括未来建立的库) 或者某一个仓库，这里只选择某一个需要进行评论的库，这样比较好。</li></ul><p><img src=\"https://pic.ccino.win/OB/2024/10/202410052030935.png\"></p><ul><li>安装完成即可，随后访问&nbsp;<a href=\"https://cloud.tencent.com/developer/tools/blog-entry?target=https%3A%2F%2Fgithub.com%2Fapps%2Futterances&amp;source=article&amp;objectId=1834230\" rel=\"noopener noreferrer\" target=\"_blank\">utterances 应用程序</a>&nbsp;就不再是安装而是是执行配置项目。</li></ul><p><br></p><p><img src=\"https://pic.ccino.win/OB/2024/10/202410052031467.png\"></p><p><img src=\"https://pic.ccino.win/OB/2024/10/202410052031677.png\"></p><ul><li>此时服务端配置已经完成，接着访问博客测试下评论。&nbsp;<a href=\"https://cloud.tencent.com/developer/tools/blog-entry?target=http%3A%2F%2Fhugo.xdr630.top%2F&amp;source=article&amp;objectId=1834230\" rel=\"noopener noreferrer\" target=\"_blank\">http://hugo.xdr630.top/</a></li></ul><p><br></p><p><img src=\"https://pic.ccino.win/OB/2024/10/202410052032764.png\"></p><p><a href=\"https://blog.ccino.org/tags/hugo/\" rel=\"noopener noreferrer\" target=\"_blank\">Hugo</a></p><p><a href=\"https://blog.ccino.org/tags/github/\" rel=\"noopener noreferrer\" target=\"_blank\">Github</a></p><p><br></p>","content_text":"Hugo 博客上的评论插件一直试了好多，但网上关于 hugo 的评论插件是太少了，找了好长时间都没有值得考究的。如：\nDisqus、gitment、gitalk、来比力… 这些都是国外的，加载速度确实不敢恭维，就目前为止，Disqus 官网还被墙了，gitment\n由于其开发者很久没有维护，gitalk 还算行，gitment、gitalk 这两个评论服务是通过 GitHub 提供的 API 创建含有相应标签的\nissue。\n\n 1. 国内其实也有很多评论插件，如：有言（官网已经挂了）、畅言（搜狐出品）。还有来比力（韩国的，之前 hexo\n    博客用到过，加载速度也好慢），Valine（这个评论插件确实不错，但注册账号后还要用身份证号码信息认证，认证完后还要用支付宝扫码验证是不是本人的，基于安全考虑，暂时还是先不要用这个，不知道一个评论插件还要这么多程序）\n 2. 其实第三方插件也还是要承担些安全责任的，发表不好的言论都是有些关系的。\n 3. Hugo 推荐 utterances 和 gitalk 评论插件来使用。\n\n\nUTTERANCES 是一款基于 GITHUB ISSUES 的评论工具\n\n\n\n\n相比同类的工具 gitment、gitalk 以及 disqus 评论工具，优点如下：\n\n 1. 极其轻量\n 2. 加载非常快\n 3. 配置比较简单\n 4. Disqus 这个工具配置也比较简单，也是免费的。但是，广告多，而且加载也比较慢。体验了一把 utterances 后，马上切换到\n    utterances。\n 5. Utterances 是基于 github issue，拥有多种主题的开源免费小组件。\n\n\nHUGO 博客配置使用 UTTERANCES\n\n * 首先在 github 上创建一个空仓库，如：hugoblogtalks，并且初始化设置，仓库必须是\n   Public，而不是私有的，这样我们的读者才可以查看以及发表评论。 [https://pic.ccino.win/OB/2024/10/202410052029629.png]\n * 接着就是在 hugo 的配置文件（config. Toml）中启用 utterances，打开 config. Toml，添加如下：\n\n## 配置 utteranc评论,教程参考 https://utteranc.es/\n[params.utteranc]\n  enable = true\n  repo = \"yunxd/hugoblogtalks\" ##换成自己得\n  issueTerm = \"pathname\"\n  theme = \"github-light\"\n\n\n\n\n\nRepo 的格式为：github 用户名 / 创建的仓库名\n\n\nGITHUB 上安装 UTTERANCES\n\n * 首先必须在 github 上进行安装 utterances，访问 utterances 应用程序 然后点击 Install 按钮进行安装。\n * 在这里可以选择可以关联的存储库，可以选择我们所拥有的库 (也包括未来建立的库) 或者某一个仓库，这里只选择某一个需要进行评论的库，这样比较好。\n\n[https://pic.ccino.win/OB/2024/10/202410052030935.png]\n\n * 安装完成即可，随后访问 utterances 应用程序 就不再是安装而是是执行配置项目。\n\n\n\n\n[https://pic.ccino.win/OB/2024/10/202410052031467.png]\n\n[https://pic.ccino.win/OB/2024/10/202410052031677.png]\n\n * 此时服务端配置已经完成，接着访问博客测试下评论。 http://hugo.xdr630.top/\n\n\n\n\n[https://pic.ccino.win/OB/2024/10/202410052032764.png]\n\nHugo\n\nGithub\n\n\n","image":"https://cdn-blog-ccino-cc.ccino.cc/blog-ccino-cc/production/images/item-bad1bb7ea67265e34cd297349098908f.png","date_published":"2024-10-05T12:52:42.687Z","_microfeed":{"is_audio":false,"is_document":false,"is_external_url":true,"is_video":false,"is_image":false,"web_url":"https://blog-ccino-cc.pages.dev/i/hugoutterances-aGCkTkpFq6N/","json_url":"https://blog-ccino-cc.pages.dev/i/aGCkTkpFq6N/json/","rss_url":"https://blog-ccino-cc.pages.dev/i/aGCkTkpFq6N/rss/","guid":"aGCkTkpFq6N","status":"published","itunes:episodeType":"full","date_published_short":"Sat Oct 05 2024","date_published_ms":1728132762687}},{"id":"wRYR61gQSWh","title":"Hugo by Github with Cloudflare Pages","attachments":[{"url":"https://immmmm.com/hugo-github-cloudflare/","mime_type":"text/html"}],"url":"https://blog.ccino.cc/i/Hugo by Github with Cloudflare Pages","external_url":"https://immmmm.com/hugo-github-cloudflare/","content_html":"<p>在线预览：<a href=\"https://lmm.pages.dev/\" rel=\"noopener noreferrer\" target=\"_blank\">https://lmm.pages.dev</a></p><p>部署全程 Web 端操作，无需本地安装 Hugo 、配置 Git 、连通 Github 等操作。浏览器 OS，飞起！</p><p><br></p><h3>Github 导入仓库</h3><p>点击导入&nbsp;<a href=\"https://github.com/new/import\" rel=\"noopener noreferrer\" target=\"_blank\">https://github.com/new/import</a></p><p><br></p><p><img src=\"https://r2.immmmm.com/2022/01/cf-hugo-1.png\"></p><pre class=\"ql-syntax\" spellcheck=\"false\">https://github.com/lmm214/cloudflare-hugo-theme-stack\n</pre><p>填入已配置好主题 (&nbsp;<a href=\"https://github.com/CaiJimmy/hugo-theme-stack\" rel=\"noopener noreferrer\" target=\"_blank\">hugo-theme-stack</a>&nbsp;) 的仓库，再给自己的取个仓库名，设置公开、私人都可以。</p><p><img src=\"https://r2.immmmm.com/2022/01/cf-hugo-2.png\"><img src=\"https://r2.immmmm.com/2022/01/cf-hugo-3.png\">仓库导入完成</p><h3>Cloudflare Pages 创建项目</h3><p><img src=\"https://r2.immmmm.com/2022/01/cf-hugo-4.png\"></p><p>Cloudflare Dash&nbsp;<a href=\"https://dash.cloudflare.com/\" rel=\"noopener noreferrer\" target=\"_blank\">https://dash.cloudflare.com/</a>&nbsp;点导航栏&nbsp;Pages&nbsp;，点&nbsp;创建项目，授权刚创建的 Github 仓库。</p><p><img src=\"https://r2.immmmm.com/2022/01/cf-hugo-5.png\"><img src=\"https://r2.immmmm.com/2022/01/cf-hugo-6.png\">修改项目名称</p><p>项目名称可以自定义，我改成了&nbsp;lmm&nbsp;，CF 会分配对应的二级域名供访问。生产分支默认&nbsp;main&nbsp;即可。</p><p><img src=\"https://r2.immmmm.com/2022/01/cf-hugo-7.png\"></p><p>框架预设选择&nbsp;Hugo&nbsp;，添加环境变量，指定高版本的&nbsp;HUGO_VERSION&nbsp;为&nbsp;0.92.0</p><p><img src=\"https://r2.immmmm.com/2022/01/cf-hugo-8.png\"></p><p><img src=\"https://r2.immmmm.com/2022/01/cf-hugo-9.png\">Pages 创建完成</p><p>撒花 🎉</p><p><br></p><h3>然后，然后</h3><p>了解主题配置&nbsp;<a href=\"https://docs.stack.jimmycai.com/zh/configuration/\" rel=\"noopener noreferrer\" target=\"_blank\">https://docs.stack.jimmycai.com/zh/configuration/</a>&nbsp;，修改站点信息。</p><p>日常发文在&nbsp;content/post&nbsp;文件夹下新增 md 文件即可。</p>","content_text":"在线预览：https://lmm.pages.dev\n\n部署全程 Web 端操作，无需本地安装 Hugo 、配置 Git 、连通 Github 等操作。浏览器 OS，飞起！\n\n\n\n\n\nGITHUB 导入仓库\n\n点击导入 https://github.com/new/import\n\n\n\n\n[https://r2.immmmm.com/2022/01/cf-hugo-1.png]\n\nhttps://github.com/lmm214/cloudflare-hugo-theme-stack\n\n\n填入已配置好主题 ( hugo-theme-stack ) 的仓库，再给自己的取个仓库名，设置公开、私人都可以。\n\n[https://r2.immmmm.com/2022/01/cf-hugo-2.png][https://r2.immmmm.com/2022/01/cf-hugo-3.png]仓库导入完成\n\n\nCLOUDFLARE PAGES 创建项目\n\n[https://r2.immmmm.com/2022/01/cf-hugo-4.png]\n\nCloudflare Dash https://dash.cloudflare.com/ 点导航栏 Pages ，点 创建项目，授权刚创建的 Github\n仓库。\n\n[https://r2.immmmm.com/2022/01/cf-hugo-5.png][https://r2.immmmm.com/2022/01/cf-hugo-6.png]修改项目名称\n\n项目名称可以自定义，我改成了 lmm ，CF 会分配对应的二级域名供访问。生产分支默认 main 即可。\n\n[https://r2.immmmm.com/2022/01/cf-hugo-7.png]\n\n框架预设选择 Hugo ，添加环境变量，指定高版本的 HUGO_VERSION 为 0.92.0\n\n[https://r2.immmmm.com/2022/01/cf-hugo-8.png]\n\n[https://r2.immmmm.com/2022/01/cf-hugo-9.png]Pages 创建完成\n\n撒花 🎉\n\n\n\n\n\n然后，然后\n\n了解主题配置 https://docs.stack.jimmycai.com/zh/configuration/ ，修改站点信息。\n\n日常发文在 content/post 文件夹下新增 md 文件即可。","date_published":"2024-10-05T00:02:29.424Z","_microfeed":{"is_audio":false,"is_document":false,"is_external_url":true,"is_video":false,"is_image":false,"web_url":"https://blog-ccino-cc.pages.dev/i/hugo-by-github-with-cloudflare-pages-wRYR61gQSWh/","json_url":"https://blog-ccino-cc.pages.dev/i/wRYR61gQSWh/json/","rss_url":"https://blog-ccino-cc.pages.dev/i/wRYR61gQSWh/rss/","guid":"wRYR61gQSWh","status":"published","itunes:episodeType":"full","date_published_short":"Fri Oct 04 2024","date_published_ms":1728086549424}},{"id":"N4KE5hrTVPH","title":"使用 Obsidian 一键发布博客","attachments":[{"url":"https://yaofun.top/posts/2024/03/%E4%BD%BF%E7%94%A8-obsidian-%E4%B8%80%E9%94%AE%E5%8F%91%E5%B8%83%E5%8D%9A%E5%AE%A2/","mime_type":"text/html"}],"url":"https://blog.ccino.cc/i/Use -obsidian- to publish a blog in one click","external_url":"https://yaofun.top/posts/2024/03/%E4%BD%BF%E7%94%A8-obsidian-%E4%B8%80%E9%94%AE%E5%8F%91%E5%B8%83%E5%8D%9A%E5%AE%A2/","content_html":"<p>博客荒废好久了！之前也陆陆续续使用过一些博客程序，要么发布部署有些麻烦，要么使用第三方服务不太稳定，一段时间就停止维护了。</p><p>偶然的机会看到文章：&nbsp;<a href=\"https://lillianwho.com/posts/obsidian-hugo-cloudflare/\" rel=\"noopener noreferrer\" target=\"_blank\">obsidian 配合 hugo、cloudflare：让发布博客简单到不可思议</a>，心动入坑，花了近一周的时间研究折腾，终于把基于 obsidian 的博客写作与发布工作流搭建起来了，现在可以愉快的一键发布啦。</p><p>从 0 到 1 折腾踩坑，整个过程好似「<strong>晨兴理荒秽，带月荷锄归</strong>」，但没有陶渊明那样悠然自得。折腾的心累，年纪再大点肯定折腾不动的，这里首先强烈建议怕麻烦同学的劝退。</p><p><br></p><h2>博客方案</h2><p>折腾前我整理出自己的博客需求如下：</p><ul><li>简单快捷：使用 Obsidian 写完，一键发布</li><li>稳定靠谱：减少第三方依赖，尽量使用靠谱的第三方服务，例如 github</li><li>图片能够顺畅加载：之前直接使用 github 做图床，国内访问很慢</li></ul><p>参考一些大佬的配置，最终的选型如下：</p><ul><li>Obsidian + obsidian git 插件 ：markdown 写作，obsidian git 插件自动上传 github</li><li>Hugo：静态网站构建工具</li><li>Github + Github Action ：源码托管，自动化编译（<em>emmm，github 应该不会倒闭</em>）</li><li>Cloudflare Page：静态网站托管 （也可以直接用 github page 托管）</li><li>Picgo + jsdelivr：基于 github 的图床 、cdn 加速</li></ul><p>最终达到的效果是： 在 obsidian 写文章，通过快捷键一件上传到 git 之后，自动构建发布。 图片在 obsidian 可以自动上传到 github 图床，并且转为 cdn jsdelivr 加速。如下图所示：</p><p><img src=\"https://cdn.jsdelivr.net/gh/goby-ao/picgo@main/img/20240606232431.png\"></p><p>本站源码：</p><ul><li><a href=\"https://github.com/goby-ao/yaofun.top/\" rel=\"noopener noreferrer\" target=\"_blank\">GitHub - goby-ao/yaofun.top: my blog</a></li></ul><p>参考与致谢：</p><ul><li><a href=\"https://immmmm.com/tags/%E6%8A%98%E8%85%BE/\" rel=\"noopener noreferrer\" target=\"_blank\">折腾 :: 木木木木木</a></li><li><a href=\"https://lillianwho.com/\" rel=\"noopener noreferrer\" target=\"_blank\">Lillian Who</a></li></ul><p><br></p><h3>折腾记录0 准备工作</h3><ul><li>能科学上网</li><li>github 账号、熟悉基本命令</li><li>cloudflare 账号</li><li>个人域名</li></ul><h3>1 本地初始化 hugo 博客</h3><p><strong>1.1 hugo 安装</strong></p><p>windows 上安装 hugo 需要先安装 go，在 macOS 上安装 hugo 比较简单可以自动安装所需依赖。</p><p><br></p><pre class=\"ql-syntax\" spellcheck=\"false\">brew update\nbrew install hugo\n</pre><p>Bash</p><p><strong>1.2 hugo 博客初始化</strong></p><p><br></p><pre class=\"ql-syntax\" spellcheck=\"false\">hugo new site yaofun.top\n</pre><p>Bash</p><p>例如 /home/yao/blog 下执行命令，会 生成 yaofun.top 子目录： /home/yao/blog/yaofun.top</p><p><strong>1.3 安装 hugo 主题</strong></p><p>我安装的是 hello-friend-ng 主题：</p><p><br></p><pre class=\"ql-syntax\" spellcheck=\"false\">$ git submodule add https://github.com/rhazdon/hugo-theme-hello-friend-ng.git themes/hello-friend-ng\n</pre><p>Bash</p><p><strong>1.4 本地运行与调试</strong></p><p>拷贝 theme 下的 exmapleSite 下的 content 目录和 .toml 配置到 hugo 根目录（/home/yao/blog/yaofun.top）</p><p>执行 hugo server 即可用 http://localhost:1313 查看预览效果，修改静态代码后它会自动重新编译构建。</p><p>你可以调试 hugo 的配置，折腾成自己满意的效果。</p><p><br></p><h3>2 自动化发布与部署</h3><p>本地博客搭建完毕后，可以开始配置自动化部署。</p><p><strong>2.1 博客及源码上传到 github 仓库</strong></p><ol><li>github 新建一个仓库 ，git clone 到本地</li><li>把博客源码（/home/yao/blog/yaofun.top/*&nbsp;）拷贝到本地 git 仓库里面</li><li>通过 git push 到 github</li></ol><p>至此可以通过 git 把源码推送到 github 仓库</p><p><strong>2.2 博客源码自动化编译部署</strong></p><ol><li>新建一个仓库，存放 hugo 生成的博客静态文件（这一步也可以用同仓库，不同分支）</li><li>配置 github action&nbsp;<a href=\"https://github.com/goby-ao/goby-ao.github.io\" rel=\"noopener noreferrer\" target=\"_blank\">github action</a>&nbsp;实现：</li><li class=\"ql-indent-1\">hugo 编译生成静态代码 ./public；</li><li class=\"ql-indent-1\">推送 ./public 静态代码到刚才新建的&nbsp;<a href=\"https://github.com/goby-ao/goby-ao.github.io\" rel=\"noopener noreferrer\" target=\"_blank\">github 静态文件仓库</a></li></ol><p><strong>2.3 cloudflare page 托管静态网站</strong></p><ol><li>cloudflare 在 workers &amp; pages 里面新建一个 page，指向静态代码的仓库</li><li>build 里面什么都不用配置，因为 github action 已经完成了 hugo 编译</li><li>配置自定义域名</li></ol><p>至此完成基本的自动化发布流程：</p><p>本地 -&gt; github 源码库 -&gt; github 静态仓库 -&gt; cloudflare page 托管</p><p><br></p><h3>3 图床与图床加速</h3><p>文本没问题了，还差一个图片如何展示，并且国内也能顺畅展示的问题。</p><p><strong>3.1 picgo + github 图床</strong>&nbsp;下载 picgo ，设置 github 图床，此步略过不表</p><p><strong>3.2 图床加速</strong>&nbsp;picgo 配置自定义域名，例如我的是：https://cdn.jsdelivr.net/gh/goby-ao/picgo@main 配置规则：github 用户名，仓库名，分支要换成自己的。默认图片在仓库的 img 目录。</p><p><br></p><h3>4 obsidian 配置</h3><p>以上工作做好之后，就可以配置 obsidian 了，实现拼图的最后一步 - 使用 obsidian 一键发布。</p><ol><li>obsidian 创建新的 vault ，地址为 hugo 源码目录</li><li>安装 obsidian 插件&nbsp;<em>git</em>：在 obsidian 中一键 commit 、push 代码</li><li>安装 obsidian 插件&nbsp;<em>Image Auto Upload</em>：实现贴图自动上传图床，生成链接</li><li>安装 obsidian 插件&nbsp;<em>Templater</em>&nbsp;：配置博客模板 header</li></ol><pre class=\"ql-syntax\" spellcheck=\"false\">---\ndate: &lt;% tp.file.creation_date(\"YYYY-MM-DD\") %&gt;\ntitle: &lt;% tp.file.title %&gt;\ntags: ['&lt;% tp.system.suggester(item =&gt; item, Object.keys(app.metadataCache.getTags()).map(x =&gt; x.replace(\"#\", \"\"))) %&gt;']\n---\n</pre><p>YAML</p><ol><li>安装 obsidian 插件&nbsp;<em>quickadd</em>&nbsp;：更便捷的创建博客文件</li></ol><p><br></p><h3>5 折腾美化</h3><p><strong>5.1 图片排版</strong></p><p>参考：<a href=\"https://immmmm.com/about-images-gird/\" rel=\"noopener noreferrer\" target=\"_blank\">Hugo 多图排版这样来：：木木木木木</a>&nbsp;在&nbsp;themes/hello-friend-ng/assets/scss/_single.scss&nbsp;里面加入以下 css 代码：</p><p><br></p><pre class=\"ql-syntax\" spellcheck=\"false\">.post-content p:has(&gt; img:nth-child(2)){column-count:2;column-gap:8px;margin:6px 0;}\n.post-content p:has(&gt; img:nth-child(3)){column-count:3;}\n.post-content p:has(&gt; img:nth-child(4)){column-count:4;}\n.post-content p:has(&gt; img:nth-child(5)){column-count:5;}\n.post-content p:has(&gt; img:nth-child(6)){column-count:4;}\n.post-content p:has(&gt; img:nth-child(2)) img{display:inherit;}\n.post-content p:has(&gt; img:nth-child(6)) img{margin-bottom:8px;}\n</pre><p>CSS</p><p>效果如下：</p><p><img src=\"https://cdn.jsdelivr.net/gh/goby-ao/picgo@main/img/20240325181403.png\"></p><p>实际显示：</p><p><img src=\"https://cdn.jsdelivr.net/gh/goby-ao/picgo@main/img/IMG_2019.jpeg\"><img src=\"https://cdn.jsdelivr.net/gh/goby-ao/picgo@main/img/IMG_1696.jpeg\"></p><p><strong>5.2 图片灯箱效果</strong></p><p>图片点击后灯箱效果，使用：<a href=\"https://tokinx.github.io/ViewImage/\" rel=\"noopener noreferrer\" target=\"_blank\">ViewImage.js - 2kb minimalist lightbox.</a></p><ol><li>下载&nbsp;view-image.min.js&nbsp;到 static/js/ 下</li><li>在 themes/hello-friend-ng/layouts/partials/javascript.html 下加入代码：</li></ol><p><br></p><pre class=\"ql-syntax\" spellcheck=\"false\">&lt;script src=\"/js/view-image.min.js\"&gt;&lt;/script&gt;\n&lt;script&gt;\n    window.ViewImage &amp;&amp; ViewImage.init('.content img');\n&lt;/script&gt;\n</pre><p>JavaScript</p><h3>6 其他问题</h3><p><strong>中文字数统计不准</strong></p><p>在 hugo.toml 里加入一行：hasCJKLanguage = true</p><p><br></p><h2>为什么要写博客 ？</h2><p>督促自己思考，沉淀，进步。在互联网上有一片属于自己的 Secret Garden。</p>","content_text":"博客荒废好久了！之前也陆陆续续使用过一些博客程序，要么发布部署有些麻烦，要么使用第三方服务不太稳定，一段时间就停止维护了。\n\n偶然的机会看到文章： obsidian 配合 hugo、cloudflare：让发布博客简单到不可思议，心动入坑，花了近一周的时间研究折腾，终于把基于\nobsidian 的博客写作与发布工作流搭建起来了，现在可以愉快的一键发布啦。\n\n从 0 到 1\n折腾踩坑，整个过程好似「晨兴理荒秽，带月荷锄归」，但没有陶渊明那样悠然自得。折腾的心累，年纪再大点肯定折腾不动的，这里首先强烈建议怕麻烦同学的劝退。\n\n\n\n\n\n博客方案\n\n折腾前我整理出自己的博客需求如下：\n\n * 简单快捷：使用 Obsidian 写完，一键发布\n * 稳定靠谱：减少第三方依赖，尽量使用靠谱的第三方服务，例如 github\n * 图片能够顺畅加载：之前直接使用 github 做图床，国内访问很慢\n\n参考一些大佬的配置，最终的选型如下：\n\n * Obsidian + obsidian git 插件 ：markdown 写作，obsidian git 插件自动上传 github\n * Hugo：静态网站构建工具\n * Github + Github Action ：源码托管，自动化编译（emmm，github 应该不会倒闭）\n * Cloudflare Page：静态网站托管 （也可以直接用 github page 托管）\n * Picgo + jsdelivr：基于 github 的图床 、cdn 加速\n\n最终达到的效果是： 在 obsidian 写文章，通过快捷键一件上传到 git 之后，自动构建发布。 图片在 obsidian 可以自动上传到 github\n图床，并且转为 cdn jsdelivr 加速。如下图所示：\n\n[https://cdn.jsdelivr.net/gh/goby-ao/picgo@main/img/20240606232431.png]\n\n本站源码：\n\n * GitHub - goby-ao/yaofun.top: my blog\n\n参考与致谢：\n\n * 折腾 :: 木木木木木\n * Lillian Who\n\n\n\n\n\n折腾记录0 准备工作\n\n * 能科学上网\n * github 账号、熟悉基本命令\n * cloudflare 账号\n * 个人域名\n\n\n1 本地初始化 HUGO 博客\n\n1.1 hugo 安装\n\nwindows 上安装 hugo 需要先安装 go，在 macOS 上安装 hugo 比较简单可以自动安装所需依赖。\n\n\n\n\nbrew update\nbrew install hugo\n\n\nBash\n\n1.2 hugo 博客初始化\n\n\n\n\nhugo new site yaofun.top\n\n\nBash\n\n例如 /home/yao/blog 下执行命令，会 生成 yaofun.top 子目录： /home/yao/blog/yaofun.top\n\n1.3 安装 hugo 主题\n\n我安装的是 hello-friend-ng 主题：\n\n\n\n\n$ git submodule add https://github.com/rhazdon/hugo-theme-hello-friend-ng.git themes/hello-friend-ng\n\n\nBash\n\n1.4 本地运行与调试\n\n拷贝 theme 下的 exmapleSite 下的 content 目录和 .toml 配置到 hugo\n根目录（/home/yao/blog/yaofun.top）\n\n执行 hugo server 即可用 http://localhost:1313 查看预览效果，修改静态代码后它会自动重新编译构建。\n\n你可以调试 hugo 的配置，折腾成自己满意的效果。\n\n\n\n\n\n2 自动化发布与部署\n\n本地博客搭建完毕后，可以开始配置自动化部署。\n\n2.1 博客及源码上传到 github 仓库\n\n 1. github 新建一个仓库 ，git clone 到本地\n 2. 把博客源码（/home/yao/blog/yaofun.top/* ）拷贝到本地 git 仓库里面\n 3. 通过 git push 到 github\n\n至此可以通过 git 把源码推送到 github 仓库\n\n2.2 博客源码自动化编译部署\n\n 1. 新建一个仓库，存放 hugo 生成的博客静态文件（这一步也可以用同仓库，不同分支）\n 2. 配置 github action github action 实现：\n 3. hugo 编译生成静态代码 ./public；\n 4. 推送 ./public 静态代码到刚才新建的 github 静态文件仓库\n\n2.3 cloudflare page 托管静态网站\n\n 1. cloudflare 在 workers & pages 里面新建一个 page，指向静态代码的仓库\n 2. build 里面什么都不用配置，因为 github action 已经完成了 hugo 编译\n 3. 配置自定义域名\n\n至此完成基本的自动化发布流程：\n\n本地 -> github 源码库 -> github 静态仓库 -> cloudflare page 托管\n\n\n\n\n\n3 图床与图床加速\n\n文本没问题了，还差一个图片如何展示，并且国内也能顺畅展示的问题。\n\n3.1 picgo + github 图床 下载 picgo ，设置 github 图床，此步略过不表\n\n3.2 图床加速 picgo 配置自定义域名，例如我的是：https://cdn.jsdelivr.net/gh/goby-ao/picgo@main\n配置规则：github 用户名，仓库名，分支要换成自己的。默认图片在仓库的 img 目录。\n\n\n\n\n\n4 OBSIDIAN 配置\n\n以上工作做好之后，就可以配置 obsidian 了，实现拼图的最后一步 - 使用 obsidian 一键发布。\n\n 1. obsidian 创建新的 vault ，地址为 hugo 源码目录\n 2. 安装 obsidian 插件 git：在 obsidian 中一键 commit 、push 代码\n 3. 安装 obsidian 插件 Image Auto Upload：实现贴图自动上传图床，生成链接\n 4. 安装 obsidian 插件 Templater ：配置博客模板 header\n\n---\ndate: <% tp.file.creation_date(\"YYYY-MM-DD\") %>\ntitle: <% tp.file.title %>\ntags: ['<% tp.system.suggester(item => item, Object.keys(app.metadataCache.getTags()).map(x => x.replace(\"#\", \"\"))) %>']\n---\n\n\nYAML\n\n 1. 安装 obsidian 插件 quickadd ：更便捷的创建博客文件\n\n\n\n\n\n5 折腾美化\n\n5.1 图片排版\n\n参考：Hugo 多图排版这样来：：木木木木木 在 themes/hello-friend-ng/assets/scss/_single.scss 里面加入以下\ncss 代码：\n\n\n\n\n.post-content p:has(> img:nth-child(2)){column-count:2;column-gap:8px;margin:6px 0;}\n.post-content p:has(> img:nth-child(3)){column-count:3;}\n.post-content p:has(> img:nth-child(4)){column-count:4;}\n.post-content p:has(> img:nth-child(5)){column-count:5;}\n.post-content p:has(> img:nth-child(6)){column-count:4;}\n.post-content p:has(> img:nth-child(2)) img{display:inherit;}\n.post-content p:has(> img:nth-child(6)) img{margin-bottom:8px;}\n\n\nCSS\n\n效果如下：\n\n[https://cdn.jsdelivr.net/gh/goby-ao/picgo@main/img/20240325181403.png]\n\n实际显示：\n\n[https://cdn.jsdelivr.net/gh/goby-ao/picgo@main/img/IMG_2019.jpeg][https://cdn.jsdelivr.net/gh/goby-ao/picgo@main/img/IMG_1696.jpeg]\n\n5.2 图片灯箱效果\n\n图片点击后灯箱效果，使用：ViewImage.js - 2kb minimalist lightbox.\n\n 1. 下载 view-image.min.js 到 static/js/ 下\n 2. 在 themes/hello-friend-ng/layouts/partials/javascript.html 下加入代码：\n\n\n\n\n<script src=\"/js/view-image.min.js\"></script>\n<script>\n    window.ViewImage && ViewImage.init('.content img');\n</script>\n\n\nJavaScript\n\n\n6 其他问题\n\n中文字数统计不准\n\n在 hugo.toml 里加入一行：hasCJKLanguage = true\n\n\n\n\n\n为什么要写博客 ？\n\n督促自己思考，沉淀，进步。在互联网上有一片属于自己的 Secret Garden。","date_published":"2024-10-04T23:59:39.484Z","_microfeed":{"is_audio":false,"is_document":false,"is_external_url":true,"is_video":false,"is_image":false,"web_url":"https://blog-ccino-cc.pages.dev/i/obsidian-N4KE5hrTVPH/","json_url":"https://blog-ccino-cc.pages.dev/i/N4KE5hrTVPH/json/","rss_url":"https://blog-ccino-cc.pages.dev/i/N4KE5hrTVPH/rss/","guid":"N4KE5hrTVPH","status":"published","itunes:episodeType":"full","date_published_short":"Fri Oct 04 2024","date_published_ms":1728086379484}},{"id":"QaUFyeEgUFJ","title":"obsidian 配合 hugo、cloudflare：让发布博客简单到不可思议","attachments":[{"url":"https://lillianwho.com/posts/obsidian-hugo-cloudflare/","mime_type":"text/html"}],"url":"https://blog.ccino.cc/i/obsidian-hugo-cloudflare/","external_url":"https://lillianwho.com/posts/obsidian-hugo-cloudflare/","content_html":"<p><img src=\"https://pic.ccino.win/OB/2024/10/202410070919391.png\"></p><p>自从我更换博客系统到 hugo 之后，我越来越少发博客了。原因除了最近写得少之外，就是发布起来不如之前使用 Gridea 那么方便了。</p><p>参考了几篇文章，现在的发布流程为：</p><ol><li>Obsidian 创建文章并写作。</li><li>利用 obsidian-git 插件将文件自动同步到 github</li><li>Cloudflare 自动拉取 github 仓库，并自动构建为 hugo 项目，相当于省略了本地执行&nbsp;hugo -D&nbsp;的过程。</li></ol><p>旧的发布方式为：</p><ol><li>Obsidian 中写作文章</li><li>复制文件到 hugo 项目的 content/posts 文件夹</li><li>然后执行 hugo -D 命令，生成静态文件</li><li>再到 public 文件夹下使用 git 将已生成的静态文件上传到 github。Github page 作为博客展示。</li></ol><p>这个过程是非常麻烦的。</p><p>今天看到木木的博客中提到的 Cloudflare，就想着改造一下。改造过程如下。</p><h3>使用到的 obsidian 插件</h3><h4>Image auto upload plugin</h4><p><br></p><p>用于自动上传图片到图床。需要配合 picgo 使用。</p><h4>Quickadd</h4><p><br></p><p>用于快速创建一篇新博客。</p><p>下面是我的设置：</p><ol><li>创建一个&nbsp;Template&nbsp;类型的 quickadd 命令</li></ol><p><br></p><p><img src=\"https://pic.ccino.win/OB/2024/10/202410070920783.png\"></p><ol><li>在根目录新建一个&nbsp;_Templates&nbsp;文件夹，并创建模板文件&nbsp;hugo博客模板</li></ol><pre class=\"ql-syntax\" spellcheck=\"false\">1\n2\n3\n4\n5\n6\n7\n8\n9\n---\ntitle: {{NAME}}\ntags: [{{VALUE:tag？}}]\ndate: {{DATE:YYYY-MM-DD HH:mm:ss}}\ndraft: true\nhideInList: false\nisTop: false\nfeature: \n---\n</pre><p>Copy</p><p>Published 字段是发布，默认设置为 false，草稿。等到文章写完并修改无误后，再修改为 true 进行发布。</p><ol><li>设置 quickadd 命令：</li></ol><p><br></p><p><img src=\"https://pic.ccino.win/OB/2024/10/202410070920356.png\"></p><h4>Obsidian-git</h4><p><br></p><p>用于自动备份文件到 github。</p><p>插件设置修改如下：</p><p><img src=\"https://pic.ccino.win/OB/2024/10/202410070921551.png\"></p><h3>启用 Cloudflare</h3><p><br></p><p>打开&nbsp;<a href=\"https://dash.cloudflare.com/\" rel=\"noopener noreferrer\" target=\"_blank\">Cloudflare Dash</a>&nbsp;导航栏&nbsp;Pages&nbsp;，点&nbsp;创建项目，授权 Github 项目，选择博客所在仓库，选择正确的分支。</p><p>添加环境变量，指定高版本&nbsp;HUGO_VERSION&nbsp;为&nbsp;0.92.0</p><p>然后部署即可。</p><p>部署完成后就可以使用 cloudflare 的二级域名访问博客了。如果你像我一样有自己的独立域名，那么可以进行域名绑定。</p><h3>Cloudflare 绑定独立域名</h3><p><br></p><p>首先第一步，把自己的域名托管到 cloudflare。参考：&nbsp;<a href=\"https://www.back2me.cn/skills/cloudflare.html\" rel=\"noopener noreferrer\" target=\"_blank\">如何将域名托管到 cloudflare</a>&nbsp;这篇文章。</p><p>然后打开导航栏 Pages ，在右侧找到刚刚的博客站点，在设置或者部署中找到 【自定义域】，设置自定义域名，输入之前托管进来的域名，按照指引完成绑定。</p><p><img src=\"https://pic.ccino.win/OB/2024/10/202410070921759.png\"></p><p>以上，所有设置都已完成。</p><p>现在我要发布一篇新博客时，只要在 obsidian 中打开 hugo 博客的这个库，然后使用 quickadd 新建一篇博客，写上内容，然后把 yaml 中的&nbsp;published&nbsp;字段值改为&nbsp;true&nbsp;即可（从草稿改为发布）。等待 3 分钟后 obsidian-git 插件自动同步到 github，博客就自动更新发布好了。</p><p>这篇文章就是使用新方式发布的，优雅不是一点点。</p><h3>参考</h3><ul><li>木木木木木：&nbsp;<a href=\"https://immmmm.com/hugo-with-obsidian/\" rel=\"noopener noreferrer\" target=\"_blank\">Hugo With Obsidian</a></li><li>木木木木木：&nbsp;<a href=\"https://immmmm.com/hi-cloudflare/\" rel=\"noopener noreferrer\" target=\"_blank\">Hi , Cloudflare Pages</a></li><li><a href=\"https://blog.zhangyingwei.com/posts/2022m4d11h19m42s28/\" rel=\"noopener noreferrer\" target=\"_blank\">Hugo 博客写作最佳实践</a></li><li><a href=\"https://www.back2me.cn/skills/cloudflare.html\" rel=\"noopener noreferrer\" target=\"_blank\">如何将域名托管到 cloudflare</a></li></ul><p><a href=\"https://blog.ccino.org/tags/obsidian/\" rel=\"noopener noreferrer\" target=\"_blank\">Obsidian</a></p><p><a href=\"https://blog.ccino.org/tags/hugo/\" rel=\"noopener noreferrer\" target=\"_blank\">Hugo</a></p><p><a href=\"https://blog.ccino.org/tags/github/\" rel=\"noopener noreferrer\" target=\"_blank\">Github</a></p><p><a href=\"https://blog.ccino.org/tags/blog/\" rel=\"noopener noreferrer\" target=\"_blank\">Blog</a></p><ul><li><br></li></ul>","content_text":"[https://pic.ccino.win/OB/2024/10/202410070919391.png]\n\n自从我更换博客系统到 hugo 之后，我越来越少发博客了。原因除了最近写得少之外，就是发布起来不如之前使用 Gridea 那么方便了。\n\n参考了几篇文章，现在的发布流程为：\n\n 1. Obsidian 创建文章并写作。\n 2. 利用 obsidian-git 插件将文件自动同步到 github\n 3. Cloudflare 自动拉取 github 仓库，并自动构建为 hugo 项目，相当于省略了本地执行 hugo -D 的过程。\n\n旧的发布方式为：\n\n 1. Obsidian 中写作文章\n 2. 复制文件到 hugo 项目的 content/posts 文件夹\n 3. 然后执行 hugo -D 命令，生成静态文件\n 4. 再到 public 文件夹下使用 git 将已生成的静态文件上传到 github。Github page 作为博客展示。\n\n这个过程是非常麻烦的。\n\n今天看到木木的博客中提到的 Cloudflare，就想着改造一下。改造过程如下。\n\n\n使用到的 OBSIDIAN 插件\n\nIMAGE AUTO UPLOAD PLUGIN\n\n\n\n\n用于自动上传图片到图床。需要配合 picgo 使用。\n\nQUICKADD\n\n\n\n\n用于快速创建一篇新博客。\n\n下面是我的设置：\n\n 1. 创建一个 Template 类型的 quickadd 命令\n\n\n\n\n[https://pic.ccino.win/OB/2024/10/202410070920783.png]\n\n 1. 在根目录新建一个 _Templates 文件夹，并创建模板文件 hugo博客模板\n\n1\n2\n3\n4\n5\n6\n7\n8\n9\n---\ntitle: {{NAME}}\ntags: [{{VALUE:tag？}}]\ndate: {{DATE:YYYY-MM-DD HH:mm:ss}}\ndraft: true\nhideInList: false\nisTop: false\nfeature: \n---\n\n\nCopy\n\nPublished 字段是发布，默认设置为 false，草稿。等到文章写完并修改无误后，再修改为 true 进行发布。\n\n 1. 设置 quickadd 命令：\n\n\n\n\n[https://pic.ccino.win/OB/2024/10/202410070920356.png]\n\nOBSIDIAN-GIT\n\n\n\n\n用于自动备份文件到 github。\n\n插件设置修改如下：\n\n[https://pic.ccino.win/OB/2024/10/202410070921551.png]\n\n\n启用 CLOUDFLARE\n\n\n\n\n打开 Cloudflare Dash 导航栏 Pages ，点 创建项目，授权 Github 项目，选择博客所在仓库，选择正确的分支。\n\n添加环境变量，指定高版本 HUGO_VERSION 为 0.92.0\n\n然后部署即可。\n\n部署完成后就可以使用 cloudflare 的二级域名访问博客了。如果你像我一样有自己的独立域名，那么可以进行域名绑定。\n\n\nCLOUDFLARE 绑定独立域名\n\n\n\n\n首先第一步，把自己的域名托管到 cloudflare。参考： 如何将域名托管到 cloudflare 这篇文章。\n\n然后打开导航栏 Pages ，在右侧找到刚刚的博客站点，在设置或者部署中找到 【自定义域】，设置自定义域名，输入之前托管进来的域名，按照指引完成绑定。\n\n[https://pic.ccino.win/OB/2024/10/202410070921759.png]\n\n以上，所有设置都已完成。\n\n现在我要发布一篇新博客时，只要在 obsidian 中打开 hugo 博客的这个库，然后使用 quickadd 新建一篇博客，写上内容，然后把 yaml\n中的 published 字段值改为 true 即可（从草稿改为发布）。等待 3 分钟后 obsidian-git 插件自动同步到\ngithub，博客就自动更新发布好了。\n\n这篇文章就是使用新方式发布的，优雅不是一点点。\n\n\n参考\n\n * 木木木木木： Hugo With Obsidian\n * 木木木木木： Hi , Cloudflare Pages\n * Hugo 博客写作最佳实践\n * 如何将域名托管到 cloudflare\n\nObsidian\n\nHugo\n\nGithub\n\nBlog\n\n * \n   ","date_published":"2024-10-03T08:58:52.654Z","_microfeed":{"is_audio":false,"is_document":false,"is_external_url":true,"is_video":false,"is_image":false,"web_url":"https://blog-ccino-cc.pages.dev/i/obsidian-hugocloudflare-QaUFyeEgUFJ/","json_url":"https://blog-ccino-cc.pages.dev/i/QaUFyeEgUFJ/json/","rss_url":"https://blog-ccino-cc.pages.dev/i/QaUFyeEgUFJ/rss/","guid":"QaUFyeEgUFJ","status":"published","itunes:episodeType":"full","date_published_short":"Thu Oct 03 2024","date_published_ms":1727945932654}},{"id":"vxsVgLq828x","title":"使用Obsidian一键发布到hugo博客","attachments":[{"url":"https://yaofun.top/posts/2024/03/%E4%BD%BF%E7%94%A8-obsidian-%E4%B8%80%E9%94%AE%E5%8F%91%E5%B8%83%E5%8D%9A%E5%AE%A2/","mime_type":"text/html"}],"url":"https://blog.ccino.cc/i/Use Obsidian to publish to hugo blog with one click/","external_url":"https://yaofun.top/posts/2024/03/%E4%BD%BF%E7%94%A8-obsidian-%E4%B8%80%E9%94%AE%E5%8F%91%E5%B8%83%E5%8D%9A%E5%AE%A2/","content_html":"<p>博客荒废好久了！之前也陆陆续续使用过一些博客程序，要么发布部署有些麻烦，要么使用第三方服务不太稳定，一段时间就停止维护了。</p><p>偶然的机会看到文章：&nbsp;<a href=\"https://lillianwho.com/posts/obsidian-hugo-cloudflare/\" rel=\"noopener noreferrer\" target=\"_blank\">obsidian 配合 hugo、cloudflare：让发布博客简单到不可思议</a>，心动入坑，花了近一周的时间研究折腾，终于把基于 obsidian 的博客写作与发布工作流搭建起来了，现在可以愉快的一键发布啦。</p><p>从 0 到 1 折腾踩坑，整个过程好似「<strong>晨兴理荒秽，带月荷锄归</strong>」，但没有陶渊明那样悠然自得。折腾的心累，年纪再大点肯定折腾不动的，这里首先强烈建议怕麻烦同学的劝退。</p><p><br></p><h2>博客方案</h2><p>折腾前我整理出自己的博客需求如下：</p><ul><li>简单快捷：使用 Obsidian 写完，一键发布</li><li>稳定靠谱：减少第三方依赖，尽量使用靠谱的第三方服务，例如 github</li><li>图片能够顺畅加载：之前直接使用 github 做图床，国内访问很慢</li></ul><p>参考一些大佬的配置，最终的选型如下：</p><ul><li>Obsidian + obsidian git 插件 ：markdown 写作，obsidian git 插件自动上传 github</li><li>Hugo：静态网站构建工具</li><li>Github + Github Action ：源码托管，自动化编译（<em>emmm，github 应该不会倒闭</em>）</li><li>Cloudflare Page：静态网站托管 （也可以直接用 github page 托管）</li><li>Picgo + jsdelivr：基于 github 的图床 、cdn 加速</li></ul><p>最终达到的效果是： 在 obsidian 写文章，通过快捷键一件上传到 git 之后，自动构建发布。 图片在 obsidian 可以自动上传到 github 图床，并且转为 cdn jsdelivr 加速。如下图所示：</p><p><img src=\"https://cdn.jsdelivr.net/gh/goby-ao/picgo@main/img/20240606232431.png\"></p><p>本站源码：</p><ul><li><a href=\"https://github.com/goby-ao/yaofun.top/\" rel=\"noopener noreferrer\" target=\"_blank\">GitHub - goby-ao/yaofun.top: my blog</a></li></ul><p>参考与致谢：</p><ul><li><a href=\"https://immmmm.com/tags/%E6%8A%98%E8%85%BE/\" rel=\"noopener noreferrer\" target=\"_blank\">折腾 :: 木木木木木</a></li><li><a href=\"https://lillianwho.com/\" rel=\"noopener noreferrer\" target=\"_blank\">Lillian Who</a></li></ul><p><br></p><h3>折腾记录0 准备工作</h3><ul><li>能科学上网</li><li>github 账号、熟悉基本命令</li><li>cloudflare 账号</li><li>个人域名</li></ul><h3>1 本地初始化 hugo 博客</h3><p><strong>1.1 hugo 安装</strong></p><p>windows 上安装 hugo 需要先安装 go，在 macOS 上安装 hugo 比较简单可以自动安装所需依赖。</p><p><br></p><pre class=\"ql-syntax\" spellcheck=\"false\">brew update\nbrew install hugo\n</pre><p>Bash</p><p><strong>1.2 hugo 博客初始化</strong></p><p><br></p><pre class=\"ql-syntax\" spellcheck=\"false\">hugo new site yaofun.top\n</pre><p>Bash</p><p>例如 /home/yao/blog 下执行命令，会 生成 yaofun.top 子目录： /home/yao/blog/yaofun.top</p><p><strong>1.3 安装 hugo 主题</strong></p><p>我安装的是 hello-friend-ng 主题：</p><p><br></p><pre class=\"ql-syntax\" spellcheck=\"false\">$ git submodule add https://github.com/rhazdon/hugo-theme-hello-friend-ng.git themes/hello-friend-ng\n</pre><p>Bash</p><p><strong>1.4 本地运行与调试</strong></p><p>拷贝 theme 下的 exmapleSite 下的 content 目录和 .toml 配置到 hugo 根目录（/home/yao/blog/yaofun.top）</p><p>执行 hugo server 即可用 http://localhost:1313 查看预览效果，修改静态代码后它会自动重新编译构建。</p><p>你可以调试 hugo 的配置，折腾成自己满意的效果。</p><p><br></p><h3>2 自动化发布与部署</h3><p>本地博客搭建完毕后，可以开始配置自动化部署。</p><p><strong>2.1 博客及源码上传到 github 仓库</strong></p><ol><li>github 新建一个仓库 ，git clone 到本地</li><li>把博客源码（/home/yao/blog/yaofun.top/*&nbsp;）拷贝到本地 git 仓库里面</li><li>通过 git push 到 github</li></ol><p>至此可以通过 git 把源码推送到 github 仓库</p><p><strong>2.2 博客源码自动化编译部署</strong></p><ol><li>新建一个仓库，存放 hugo 生成的博客静态文件（这一步也可以用同仓库，不同分支）</li><li>配置 github action&nbsp;<a href=\"https://github.com/goby-ao/goby-ao.github.io\" rel=\"noopener noreferrer\" target=\"_blank\">github action</a>&nbsp;实现：</li><li class=\"ql-indent-1\">hugo 编译生成静态代码 ./public；</li><li class=\"ql-indent-1\">推送 ./public 静态代码到刚才新建的&nbsp;<a href=\"https://github.com/goby-ao/goby-ao.github.io\" rel=\"noopener noreferrer\" target=\"_blank\">github 静态文件仓库</a></li></ol><p><strong>2.3 cloudflare page 托管静态网站</strong></p><ol><li>cloudflare 在 workers &amp; pages 里面新建一个 page，指向静态代码的仓库</li><li>build 里面什么都不用配置，因为 github action 已经完成了 hugo 编译</li><li>配置自定义域名</li></ol><p>至此完成基本的自动化发布流程：</p><p>本地 -&gt; github 源码库 -&gt; github 静态仓库 -&gt; cloudflare page 托管</p><p><br></p><h3>3 图床与图床加速</h3><p>文本没问题了，还差一个图片如何展示，并且国内也能顺畅展示的问题。</p><p><strong>3.1 picgo + github 图床</strong>&nbsp;下载 picgo ，设置 github 图床，此步略过不表</p><p><strong>3.2 图床加速</strong>&nbsp;picgo 配置自定义域名，例如我的是：https://cdn.jsdelivr.net/gh/goby-ao/picgo@main 配置规则：github 用户名，仓库名，分支要换成自己的。默认图片在仓库的 img 目录。</p><p><br></p><h3>4 obsidian 配置</h3><p>以上工作做好之后，就可以配置 obsidian 了，实现拼图的最后一步 - 使用 obsidian 一键发布。</p><ol><li>obsidian 创建新的 vault ，地址为 hugo 源码目录</li><li>安装 obsidian 插件&nbsp;<em>git</em>：在 obsidian 中一键 commit 、push 代码</li><li>安装 obsidian 插件&nbsp;<em>Image Auto Upload</em>：实现贴图自动上传图床，生成链接</li><li>安装 obsidian 插件&nbsp;<em>Templater</em>&nbsp;：配置博客模板 header</li></ol><pre class=\"ql-syntax\" spellcheck=\"false\">---\ndate: &lt;% tp.file.creation_date(\"YYYY-MM-DD\") %&gt;\ntitle: &lt;% tp.file.title %&gt;\ntags: ['&lt;% tp.system.suggester(item =&gt; item, Object.keys(app.metadataCache.getTags()).map(x =&gt; x.replace(\"#\", \"\"))) %&gt;']\n---\n</pre><p>YAML</p><ol><li>安装 obsidian 插件&nbsp;<em>quickadd</em>&nbsp;：更便捷的创建博客文件</li></ol><p><br></p><h3>5 折腾美化</h3><p><strong>5.1 图片排版</strong></p><p>参考：<a href=\"https://immmmm.com/about-images-gird/\" rel=\"noopener noreferrer\" target=\"_blank\">Hugo 多图排版这样来：：木木木木木</a>&nbsp;在&nbsp;themes/hello-friend-ng/assets/scss/_single.scss&nbsp;里面加入以下 css 代码：</p><p><br></p><pre class=\"ql-syntax\" spellcheck=\"false\">.post-content p:has(&gt; img:nth-child(2)){column-count:2;column-gap:8px;margin:6px 0;}\n.post-content p:has(&gt; img:nth-child(3)){column-count:3;}\n.post-content p:has(&gt; img:nth-child(4)){column-count:4;}\n.post-content p:has(&gt; img:nth-child(5)){column-count:5;}\n.post-content p:has(&gt; img:nth-child(6)){column-count:4;}\n.post-content p:has(&gt; img:nth-child(2)) img{display:inherit;}\n.post-content p:has(&gt; img:nth-child(6)) img{margin-bottom:8px;}\n</pre><p>CSS</p><p>效果如下：</p><p><img src=\"https://cdn.jsdelivr.net/gh/goby-ao/picgo@main/img/20240325181403.png\"></p><p>实际显示：</p><p><img src=\"https://cdn.jsdelivr.net/gh/goby-ao/picgo@main/img/IMG_2019.jpeg\"><img src=\"https://cdn.jsdelivr.net/gh/goby-ao/picgo@main/img/IMG_1696.jpeg\"></p><p><strong>5.2 图片灯箱效果</strong></p><p>图片点击后灯箱效果，使用：<a href=\"https://tokinx.github.io/ViewImage/\" rel=\"noopener noreferrer\" target=\"_blank\">ViewImage.js - 2kb minimalist lightbox.</a></p><ol><li>下载&nbsp;view-image.min.js&nbsp;到 static/js/ 下</li><li>在 themes/hello-friend-ng/layouts/partials/javascript.html 下加入代码：</li></ol><p><br></p><pre class=\"ql-syntax\" spellcheck=\"false\">&lt;script src=\"/js/view-image.min.js\"&gt;&lt;/script&gt;\n&lt;script&gt;\n    window.ViewImage &amp;&amp; ViewImage.init('.content img');\n&lt;/script&gt;\n</pre><p>JavaScript</p><h3>6 其他问题</h3><p><strong>中文字数统计不准</strong></p><p>在 hugo.toml 里加入一行：hasCJKLanguage = true</p><p><br></p><h2>为什么要写博客 ？</h2><p>督促自己思考，沉淀，进步。在互联网上有一片属于自己的 Secret Garden。</p>","content_text":"博客荒废好久了！之前也陆陆续续使用过一些博客程序，要么发布部署有些麻烦，要么使用第三方服务不太稳定，一段时间就停止维护了。\n\n偶然的机会看到文章： obsidian 配合 hugo、cloudflare：让发布博客简单到不可思议，心动入坑，花了近一周的时间研究折腾，终于把基于\nobsidian 的博客写作与发布工作流搭建起来了，现在可以愉快的一键发布啦。\n\n从 0 到 1\n折腾踩坑，整个过程好似「晨兴理荒秽，带月荷锄归」，但没有陶渊明那样悠然自得。折腾的心累，年纪再大点肯定折腾不动的，这里首先强烈建议怕麻烦同学的劝退。\n\n\n\n\n\n博客方案\n\n折腾前我整理出自己的博客需求如下：\n\n * 简单快捷：使用 Obsidian 写完，一键发布\n * 稳定靠谱：减少第三方依赖，尽量使用靠谱的第三方服务，例如 github\n * 图片能够顺畅加载：之前直接使用 github 做图床，国内访问很慢\n\n参考一些大佬的配置，最终的选型如下：\n\n * Obsidian + obsidian git 插件 ：markdown 写作，obsidian git 插件自动上传 github\n * Hugo：静态网站构建工具\n * Github + Github Action ：源码托管，自动化编译（emmm，github 应该不会倒闭）\n * Cloudflare Page：静态网站托管 （也可以直接用 github page 托管）\n * Picgo + jsdelivr：基于 github 的图床 、cdn 加速\n\n最终达到的效果是： 在 obsidian 写文章，通过快捷键一件上传到 git 之后，自动构建发布。 图片在 obsidian 可以自动上传到 github\n图床，并且转为 cdn jsdelivr 加速。如下图所示：\n\n[https://cdn.jsdelivr.net/gh/goby-ao/picgo@main/img/20240606232431.png]\n\n本站源码：\n\n * GitHub - goby-ao/yaofun.top: my blog\n\n参考与致谢：\n\n * 折腾 :: 木木木木木\n * Lillian Who\n\n\n\n\n\n折腾记录0 准备工作\n\n * 能科学上网\n * github 账号、熟悉基本命令\n * cloudflare 账号\n * 个人域名\n\n\n1 本地初始化 HUGO 博客\n\n1.1 hugo 安装\n\nwindows 上安装 hugo 需要先安装 go，在 macOS 上安装 hugo 比较简单可以自动安装所需依赖。\n\n\n\n\nbrew update\nbrew install hugo\n\n\nBash\n\n1.2 hugo 博客初始化\n\n\n\n\nhugo new site yaofun.top\n\n\nBash\n\n例如 /home/yao/blog 下执行命令，会 生成 yaofun.top 子目录： /home/yao/blog/yaofun.top\n\n1.3 安装 hugo 主题\n\n我安装的是 hello-friend-ng 主题：\n\n\n\n\n$ git submodule add https://github.com/rhazdon/hugo-theme-hello-friend-ng.git themes/hello-friend-ng\n\n\nBash\n\n1.4 本地运行与调试\n\n拷贝 theme 下的 exmapleSite 下的 content 目录和 .toml 配置到 hugo\n根目录（/home/yao/blog/yaofun.top）\n\n执行 hugo server 即可用 http://localhost:1313 查看预览效果，修改静态代码后它会自动重新编译构建。\n\n你可以调试 hugo 的配置，折腾成自己满意的效果。\n\n\n\n\n\n2 自动化发布与部署\n\n本地博客搭建完毕后，可以开始配置自动化部署。\n\n2.1 博客及源码上传到 github 仓库\n\n 1. github 新建一个仓库 ，git clone 到本地\n 2. 把博客源码（/home/yao/blog/yaofun.top/* ）拷贝到本地 git 仓库里面\n 3. 通过 git push 到 github\n\n至此可以通过 git 把源码推送到 github 仓库\n\n2.2 博客源码自动化编译部署\n\n 1. 新建一个仓库，存放 hugo 生成的博客静态文件（这一步也可以用同仓库，不同分支）\n 2. 配置 github action github action 实现：\n 3. hugo 编译生成静态代码 ./public；\n 4. 推送 ./public 静态代码到刚才新建的 github 静态文件仓库\n\n2.3 cloudflare page 托管静态网站\n\n 1. cloudflare 在 workers & pages 里面新建一个 page，指向静态代码的仓库\n 2. build 里面什么都不用配置，因为 github action 已经完成了 hugo 编译\n 3. 配置自定义域名\n\n至此完成基本的自动化发布流程：\n\n本地 -> github 源码库 -> github 静态仓库 -> cloudflare page 托管\n\n\n\n\n\n3 图床与图床加速\n\n文本没问题了，还差一个图片如何展示，并且国内也能顺畅展示的问题。\n\n3.1 picgo + github 图床 下载 picgo ，设置 github 图床，此步略过不表\n\n3.2 图床加速 picgo 配置自定义域名，例如我的是：https://cdn.jsdelivr.net/gh/goby-ao/picgo@main\n配置规则：github 用户名，仓库名，分支要换成自己的。默认图片在仓库的 img 目录。\n\n\n\n\n\n4 OBSIDIAN 配置\n\n以上工作做好之后，就可以配置 obsidian 了，实现拼图的最后一步 - 使用 obsidian 一键发布。\n\n 1. obsidian 创建新的 vault ，地址为 hugo 源码目录\n 2. 安装 obsidian 插件 git：在 obsidian 中一键 commit 、push 代码\n 3. 安装 obsidian 插件 Image Auto Upload：实现贴图自动上传图床，生成链接\n 4. 安装 obsidian 插件 Templater ：配置博客模板 header\n\n---\ndate: <% tp.file.creation_date(\"YYYY-MM-DD\") %>\ntitle: <% tp.file.title %>\ntags: ['<% tp.system.suggester(item => item, Object.keys(app.metadataCache.getTags()).map(x => x.replace(\"#\", \"\"))) %>']\n---\n\n\nYAML\n\n 1. 安装 obsidian 插件 quickadd ：更便捷的创建博客文件\n\n\n\n\n\n5 折腾美化\n\n5.1 图片排版\n\n参考：Hugo 多图排版这样来：：木木木木木 在 themes/hello-friend-ng/assets/scss/_single.scss 里面加入以下\ncss 代码：\n\n\n\n\n.post-content p:has(> img:nth-child(2)){column-count:2;column-gap:8px;margin:6px 0;}\n.post-content p:has(> img:nth-child(3)){column-count:3;}\n.post-content p:has(> img:nth-child(4)){column-count:4;}\n.post-content p:has(> img:nth-child(5)){column-count:5;}\n.post-content p:has(> img:nth-child(6)){column-count:4;}\n.post-content p:has(> img:nth-child(2)) img{display:inherit;}\n.post-content p:has(> img:nth-child(6)) img{margin-bottom:8px;}\n\n\nCSS\n\n效果如下：\n\n[https://cdn.jsdelivr.net/gh/goby-ao/picgo@main/img/20240325181403.png]\n\n实际显示：\n\n[https://cdn.jsdelivr.net/gh/goby-ao/picgo@main/img/IMG_2019.jpeg][https://cdn.jsdelivr.net/gh/goby-ao/picgo@main/img/IMG_1696.jpeg]\n\n5.2 图片灯箱效果\n\n图片点击后灯箱效果，使用：ViewImage.js - 2kb minimalist lightbox.\n\n 1. 下载 view-image.min.js 到 static/js/ 下\n 2. 在 themes/hello-friend-ng/layouts/partials/javascript.html 下加入代码：\n\n\n\n\n<script src=\"/js/view-image.min.js\"></script>\n<script>\n    window.ViewImage && ViewImage.init('.content img');\n</script>\n\n\nJavaScript\n\n\n6 其他问题\n\n中文字数统计不准\n\n在 hugo.toml 里加入一行：hasCJKLanguage = true\n\n\n\n\n\n为什么要写博客 ？\n\n督促自己思考，沉淀，进步。在互联网上有一片属于自己的 Secret Garden。","date_published":"2024-10-03T07:10:53.425Z","_microfeed":{"is_audio":false,"is_document":false,"is_external_url":true,"is_video":false,"is_image":false,"web_url":"https://blog-ccino-cc.pages.dev/i/obsidianhugo-vxsVgLq828x/","json_url":"https://blog-ccino-cc.pages.dev/i/vxsVgLq828x/json/","rss_url":"https://blog-ccino-cc.pages.dev/i/vxsVgLq828x/rss/","guid":"vxsVgLq828x","status":"published","itunes:episodeType":"full","date_published_short":"Thu Oct 03 2024","date_published_ms":1727939453425}},{"id":"ez56GfIVr5S","title":"VPS 基本配置：新买的 VPS 怎么整备和调教？","attachments":[{"url":"https://www.guai.win/notes/0003-new-vps","mime_type":"text/html"}],"url":"https://blog.ccino.cc/i/new-vps","external_url":"https://www.guai.win/notes/0003-new-vps","content_html":"<p>拿到新机器，咱们二话不说，先 ping 一下 IP，能否连通。连不通的话，可能是被封锁了，新机一般可以免费换 IP，换完接着 ping。</p><p>连通后看看延迟和丢包，延迟太大就得考虑。可能这个机房，它不适合你。</p><p>大家的地区，运营商，节点线路都不大一样，冷暖自知。</p><p>能接受的话，就 ssh 上去，开始跑测试脚本，是骡子是马，还得看数据。跑完测试觉得机子不行，应该还能赶在截止时间之前退单退款。</p><p><em>有些鸡贼的商家，使用流量超过 10G，就不给退单退款，这种就别跑测速了。</em></p><p>测速脚本一跑，几十个 GB 就没了，到时候退不了，只能砸自己手里。</p><p><strong>· · ·</strong></p><p><br></p><h2><strong>一、脚本测试</strong></h2><p>先别急着装什么面板，测完性能再说，直接上脚本。</p><h3>1. yabs 脚本</h3><p>yabs 是一个比较常用的综合测试脚本，权威性比较高，一般用来看最后的 cpu 得分。</p><pre class=\"ql-syntax\" spellcheck=\"false\">wget -qO- yabs.sh | bash\n\n</pre><h3>2. bench.sh 一键测试脚本</h3><p>bench.sh 也是一个综合测试脚本，历史比较久，大佬一直在维护。</p><p>原作者链接：<a href=\"https://github.com/teddysun/across/blob/master/bench.sh\" rel=\"noopener noreferrer\" target=\"_blank\">github.com/teddysun/across</a></p><pre class=\"ql-syntax\" spellcheck=\"false\">wget -qO- bench.sh | bash\n\n</pre><h3>3. 线路测试</h3><p>besttrace 是专门测试回国线路的脚本，可以看到连接国内几大城市的节点线路和对应延迟。</p><pre class=\"ql-syntax\" spellcheck=\"false\">wget -qO- git.io/besttrace | bash\n\n</pre><h3>4. 网速测试</h3><p>network-speed，测试 vps 到全球的连接速度，节点比较多，比较耗流量，成功测完得 20G 往上。</p><pre class=\"ql-syntax\" spellcheck=\"false\">wget -qO- network-speed.xyz | bash\n\n</pre><h3>5. 硬盘测试</h3><p>这条测试是调用系统的命令，看看到底是 HDD、SSD 还是 NVME 硬盘。</p><pre class=\"ql-syntax\" spellcheck=\"false\">dd if=/dev/zero of=256 bs=64K count=4K oflag=dsync\n\n</pre><p>测试结果参考：1-2M/s 的是 HDD；20-30M/s 就普通的 SSD；80M/s 以上的是好机子。</p><h3>6. 内存超售检测</h3><p>部分商家会超售内存，可以用该脚本检测。</p><p>至于超售 CPU、带宽，这个就比较稀松平常了。</p><p>（脚本的安全性未知，建议看完重装系统部分，再决定要不要跑。）</p><pre class=\"ql-syntax\" spellcheck=\"false\">curl https://raw.githubusercontent.com/uselibrary/memoryCheck/main/memoryCheck.sh | bash\n\n</pre><h3>7. 流媒体解锁和 OpenAI 解锁测试</h3><p>检测 VPS 的 IP 是否能够用来开通流媒体、OpenAI 等订阅服务。（同上，脚本安全性未知。）</p><pre class=\"ql-syntax\" spellcheck=\"false\">bash &lt;(curl -L -s https://netflix.dad/detect-script)\n\n</pre><h3>8. IP 检测</h3><p>如果对 IP 这个比较看重，怪文再推荐两个小工具，简单调查一下手上的 IP。</p><h4>1) IP 位置</h4><p><a href=\"https://whatismyipaddress.com/ip\" rel=\"noopener noreferrer\" target=\"_blank\">whatismyipaddress.com</a></p><p>whatismyipaddress 这个网站可以查到你 VPS 的 IP 所在地，数据比较精准。</p><h4>2) IP 纯净度</h4><p><a href=\"https://scamalytics.com/ip\" rel=\"noopener noreferrer\" target=\"_blank\">scamalytics.com</a></p><p>这个网站会返回一个分数和一个风险等级，欺诈风险分数一般低于 50 就好。当然，分数越低越好，0 分最好。</p><p><em>scamalytics 可以查到 IP 的纯净程度，这点很重要！</em></p><p>IP 越干净，越容易过流媒体等服务的订阅风控，也不容易弹 Google 和 Cloudflare 的人机验证弹窗和过渡页，代理的时候体验会更好。</p><p>比如，注册 OpenAI 账号，开通 Plus 订阅，都是很看重 IP 欺诈分的，分数太高的话，申请 API 和 Plus 可能不给通过。</p><p>当然，也可以选择其他方式进行代偿，比如套 wrap，或者用其他优质节点开通服务，后面换回原 IP 继续用，基本没啥问题，除非原 IP 被墙了。</p><p><strong>· · ·</strong></p><p><br></p><h2><strong>二、更新系统</strong></h2><h3>1. 重装系统</h3><p>建议先跑完测试脚本，再重装系统，也是为了规避脚本的安全性问题。可别以为大家用的脚本就很安全哦，还是别轻信外部脚本的好。</p><p>有些人喜欢跑各种整合过的一键脚本，也不看脚本里面写的啥，难搞。</p><p>一般来说，VPS 厂商给你的默认操作系统，很可能不是你想要的发行版或者版本号，建议用厂商的面板重装下系统。</p><p>不太建议自己装些厂商没提供的系统，容易被抓到把柄。</p><h4>系统的选择</h4><p>服务器端发行版选择很多，Debian，Ubuntu，Rocky Linux，AlmaLinux，Fedora 等等，甚至 CentOS，OpenSUSE Leap 都行，选自己熟悉的就好。</p><p>关于各个发行版的特点和取舍，这里先占个坑，后面来填吧。</p><p>反正有条件的直接上 Red Hat Enterprise Linux（RHEL）就好，免费的哪有这个好啊。</p><p>Ubuntu 因为资料好找，使用起来方便，稳定性也不错，所以怪文一直用 Ubuntu。</p><p>但自从 Canonical 往 Ubuntu 22.04.3 LTS 里面塞广告，有些广告还无法被移除，吃相有点难看，这种行径和国内厂商有得一拼了。</p><p>目前还在寻找平替，因为系统还没换，下面的 shell 命令，基本都是基于 Ubuntu 的版本。</p><p>如果装的是 Fedora 和 RHEL 系的发行版，比如 CentOS 之类的，请将下面命令中的 apt 用 yum 替换。</p><h3>2. 更新列表升级应用</h3><pre class=\"ql-syntax\" spellcheck=\"false\">apt update &amp;&amp; apt upgrade &amp;&amp; apt full-upgrade &amp;&amp; apt autoremove\n\n</pre><h3>3. 基础工具包</h3><pre class=\"ql-syntax\" spellcheck=\"false\">apt install vim curl unzip ufw\n\n</pre><p><strong>· · ·</strong></p><p><br></p><h2><strong>三、安全配置</strong></h2><p>小白最容易忽略的就是安全配置，弱密码什么的就不说了，说一下核心的配置。</p><h3>1. 添加新用户</h3><p>不建议每次都用 root 账号登录，创建新用户并给 root 权限比较好。</p><p>首先，添加新用户，并自动创建对应的组，然后根据提示设置密码：</p><pre class=\"ql-syntax\" spellcheck=\"false\">adduser 新用户名\n\n</pre><p>设置新密码之后，继续。除了用户名，其他敲回车默认即可。</p><p>添加新用户到 sudo 用户组，就可以使用 sudo 命令了：</p><pre class=\"ql-syntax\" spellcheck=\"false\">usermod -aG sudo 新用户名\nCopy\n// 相关补充\nexit            // 用户退出登录\npasswd p        // 重设密码\n\n</pre><h3>2. 修改 SSH 登录端口</h3><p>更改端口前，建议先把 22 端口也添加上，等 ssh 能通过其他端口连接后，再注释掉22端口。</p><p>先打开配置文件：</p><pre class=\"ql-syntax\" spellcheck=\"false\">vim /etc/ssh/sshd_config\n\n</pre><p>找到这行：</p><blockquote><em>Port 22</em></blockquote><p>然后在后面追加想要的端口号：</p><blockquote><em>Port 端口号</em></blockquote><p>端口号的范围是 0 到 65535，数字别超了。</p><p>加完端口重启 SSHD 服务，让配置生效：</p><pre class=\"ql-syntax\" spellcheck=\"false\">systemctl restart sshd.service\n\n</pre><p>如遇到 ssh 连接总自己断开，还可以继续添加添加配置：</p><blockquote><em>ClientAliveInterval 30</em></blockquote><blockquote><em>ClientAliveCountMax 6</em></blockquote><h3>3. SSH 免密登录</h3><p>Windows 用户在 /.ssh 目录生成公钥和私钥：</p><pre class=\"ql-syntax\" spellcheck=\"false\">ssh-keygen\n\n</pre><p>再在 vps 上的生成用户的公钥和私钥：</p><pre class=\"ql-syntax\" spellcheck=\"false\">ssh-keygen\n\n</pre><p>创建 authorized_keys 文件，把 Win 上的公钥复制进 authorized_keys：</p><pre class=\"ql-syntax\" spellcheck=\"false\">cd /当前用户目录/.ssh\nvim authorized_keys\n\n</pre><p>给 authorized_keys 文件设置权限：</p><pre class=\"ql-syntax\" spellcheck=\"false\">chmod 600 /当前用户目录/.ssh/authorized_keys\n\n</pre><p>重启 ssh：</p><pre class=\"ql-syntax\" spellcheck=\"false\">systemctl restart sshd\n\n</pre><p>最后，在本地测试配置是否成功：</p><pre class=\"ql-syntax\" spellcheck=\"false\">ssh 用户名@IP\n\n</pre><p>再在 SSH 工具里配置一下登录凭证，就可以免密登录了。</p><h3>4. 限制 root 用户直接 ssh 登录或者密码登录</h3><p>修改配置文件：</p><pre class=\"ql-syntax\" spellcheck=\"false\">vim /etc/ssh/sshd_config\n\n</pre><p>加入内容（限制 root 用户直接 ssh 登录，换言之，后面得用新用户登录）：</p><blockquote><em>PermitRootLogin no</em></blockquote><p>如果只是想限制 root 用户密码登录，只能用密钥登录，则需要修改配置文件内容为：</p><blockquote><em>PermitRootLogin without-password</em></blockquote><p>使修改生效：</p><pre class=\"ql-syntax\" spellcheck=\"false\">systemctl restart sshd\n\n</pre><p>修改文件权限，把 .ssh 目录设为 700：</p><pre class=\"ql-syntax\" spellcheck=\"false\">chmod 700 /root/.ssh\n\n</pre><h3>5. 启用防火墙</h3><p>检查防火墙的状态：</p><pre class=\"ql-syntax\" spellcheck=\"false\">ufw status verbose\n\n</pre><p>所有可用的应用程序配置：</p><pre class=\"ql-syntax\" spellcheck=\"false\">ufw app list\n\n</pre><p>查找指定配置文件包含的防火墙规则：</p><pre class=\"ql-syntax\" spellcheck=\"false\">ufw app info 'Nginx Full'\n\n</pre><p>启用防火墙之前，先允许 ssh 应用的默认端口：</p><pre class=\"ql-syntax\" spellcheck=\"false\">ufw allow ssh\n\n</pre><p>如果 ssh 监听其他端口，就打开那个端口：</p><pre class=\"ql-syntax\" spellcheck=\"false\">ufw allow 端口\n\n</pre><p>还可以指定协议：</p><pre class=\"ql-syntax\" spellcheck=\"false\">ufw allow 端口/协议\n\n</pre><p>启用防火墙（启用之前一定要保证，SSH 的端口已经开了，不然登出之后就登不进来了！）</p><pre class=\"ql-syntax\" spellcheck=\"false\">ufw enable\n\n</pre><p>删除防火墙规则：</p><pre class=\"ql-syntax\" spellcheck=\"false\">ufw delete allow 端口/tcp\nufw delete 防火墙规则编号\n\n</pre><p>关闭防火墙：</p><pre class=\"ql-syntax\" spellcheck=\"false\">ufw disable\n\n</pre><p>重置防火墙设置并停止：</p><pre class=\"ql-syntax\" spellcheck=\"false\">ufw reset\n\n</pre><p><strong>· · ·</strong></p><p><br></p><h2><strong>四、基本配置</strong></h2><h3>1. 设置时区</h3><p>先看下系统的时区：</p><pre class=\"ql-syntax\" spellcheck=\"false\">timedatectl show\n\n</pre><p>再按需修改，下面东八区的代号：</p><pre class=\"ql-syntax\" spellcheck=\"false\">timedatectl set-timezone Asia/Shanghai\n\n</pre><h3>2. 修改登录欢迎语（可选项）</h3><p>Ubuntu 20.04 登录总提示 New release '22.04.3 LTS' available，如果不想升级，可以关掉登录后的动态消息。</p><pre class=\"ql-syntax\" spellcheck=\"false\">vim /etc/pam.d/sshd\n\n</pre><p>然后注释掉下面这两行：</p><blockquote><em>session optional pam_motd.so motd=/run/motd.dynamic</em></blockquote><blockquote><em>session optional pam_motd.so noupdate</em></blockquote><p>Ubuntu 22.04 每次登录都弹个很长的欢迎语，里面还有广告，难受。</p><p>好消息是，动态消息也可以像上面 Ubuntu 20.04 那样彻底禁止掉。</p><p>坏消息是，其他夹在 apt 命令中的广告就不那么好弄了。</p><p>不想完全关闭动态消息的话，也有些能做的事，比如欢迎语里面的 K8S 推广内容倒是可以手动关闭。</p><pre class=\"ql-syntax\" spellcheck=\"false\">vim /etc/default/motd-news\n\n</pre><p>把文件里的 ENABLED=1 改成 ENABLED=0 就行。</p><h3>3. 装面板（可选项）</h3><p>如果手上的机器多，或者对命令行终端不感冒，也可以考虑装个面板，比如：1Panel、宝塔之类的。</p><p>面板安装和使用，又可以写一篇长文了，有需要的手动搜一下吧。</p><p><strong>· · ·</strong></p><p><br></p><h2><strong>五、科学配置（可选项）</strong></h2><h3>1. BBR 加速</h3><p>如果系统不支持 BBR2 或者 BBR Plus，配置 BBR 加速就行了。</p><pre class=\"ql-syntax\" spellcheck=\"false\">echo \"net.core.default_qdisc=fq\" &gt;&gt; /etc/sysctl.conf\necho \"net.ipv4.tcp_congestion_control=bbr\" &gt;&gt; /etc/sysctl.conf\nsysctl -p\n\n</pre><p>验证是否成功：</p><pre class=\"ql-syntax\" spellcheck=\"false\">sysctl net.ipv4.tcp_available_congestion_control\nlsmod | grep bbr\n\n</pre><p>如果系统支持 BBR2 或者 BBR Plus 的话，可以用一键脚本装内核，开启 BBR2，至于安全性，那就不清楚了。</p><h3>2. WARP</h3><p>WARP 是 Cloudflare 提供的网络流量安全及加速服务，它基于 WireGuard，能够帮你通过连接到 Cloudflare 的边缘节点，实现隐私保护及链路优化。</p><h3>3. WireGuard</h3><p>如果要用 WARP，那肯定要装 WireGuard。</p><p>WARP 的安装，又可以写一篇博文了，有需要的手动搜一下吧。</p><h3>4. V2Ray</h3><p>这个之前整过，就写出来把，用的是官方脚本：<a href=\"https://github.com/v2fly/fhs-install-v2ray\" rel=\"noopener noreferrer\" target=\"_blank\">https://github.com/v2fly/fhs-install-v2ray</a></p><p>V2Ray 的安装倒是很简单：</p><pre class=\"ql-syntax\" spellcheck=\"false\">bash &lt;(curl -L https://raw.githubusercontent.com/v2fly/fhs-install-v2ray/master/install-release.sh)\n\n</pre><p>安装后就可以启动了，但是建议先改下配置文件。（配置内容根据官方的模板，做下调整就好）</p><pre class=\"ql-syntax\" spellcheck=\"false\">vim /usr/local/etc/v2ray/config.json\n\n</pre><p>改好配置后，先启动测试下：</p><pre class=\"ql-syntax\" spellcheck=\"false\">sudo systemctl daemon-reload\nsudo systemctl start v2ray\n\n</pre><p>查看服务状态：</p><pre class=\"ql-syntax\" spellcheck=\"false\">systemctl status v2ray\n\n</pre><p>开机自启，让系统启动时引导 v2ray 启动：</p><pre class=\"ql-syntax\" spellcheck=\"false\">sudo systemctl enable v2ray\n\n</pre><p>反悔了，不让系统启动时引导 v2ray 启动：</p><pre class=\"ql-syntax\" spellcheck=\"false\">systemctl disable v2ray\n\n</pre><p>停止服务：</p><pre class=\"ql-syntax\" spellcheck=\"false\">systemctl stop v2ray\n\n</pre><p>重新加载：</p><pre class=\"ql-syntax\" spellcheck=\"false\">systemctl reload v2ray\n\n</pre><p>v2ray 默认的配置路径：</p><blockquote><em>/usr/local/etc/v2ray/config.json</em></blockquote><p>v2ray 默认的日志路径：</p><blockquote><em>/var/log/v2ray/access.log</em></blockquote><blockquote><em>/var/log/v2ray/error.log</em></blockquote><p>当然，如果嫌手动配置麻烦的话，也可以选择一键安装和引导配置的脚本，比如下面的这个（安全性未知）：</p><pre class=\"ql-syntax\" spellcheck=\"false\">bash &lt;(wget -qO- -o- https://git.io/v2ray.sh)\n\n</pre><p><strong>· · ·</strong></p><p><br></p><h2><strong>六、推荐软件（可选项）</strong></h2><h3>1. Docker 安装</h3><p>现在干点啥都喜欢上 Docker，服务基本都扔 Docker 里面了，有需要的建议先安上，挺省心的。</p><pre class=\"ql-syntax\" spellcheck=\"false\">curl -fsSL https://get.docker.com -o get-docker.sh\nsudo sh get-docker.sh\n\n</pre><p>看看安装是否成功：</p><pre class=\"ql-syntax\" spellcheck=\"false\">docker --version\n\n</pre><p>补充一下，这个脚本是官方的，会自动安装 Docker Compose 组件，调用命令是 docker compose。</p><p>如果看到别人用的命令是 docker-compose，那替换成&nbsp;docker compose&nbsp;就好，不必再装一个 docker-compose。</p><h3>2. Nginx 安装</h3><p>把 Nginx 放 Docker 当然也可以，但是对于这种入口级别的中间件，个人还是习惯用原生的。</p><p>安装非常的简单：</p><pre class=\"ql-syntax\" spellcheck=\"false\">sudo apt install nginx\n\n</pre><p>看看成功没有：</p><pre class=\"ql-syntax\" spellcheck=\"false\">nginx -V\n\n</pre><p>启动 Nginx：</p><pre class=\"ql-syntax\" spellcheck=\"false\">sudo systemctl daemon-reload\nsudo systemctl start nginx\n\n</pre><p>测一下：在浏览器通过 ip 加 80 端口访问，是否是 Nginx 默认欢迎页。</p><p>配置的话，一般直接去 /etc/nginx/conf.d 添加个文件，配一下就好了。</p><p>有静态网站，可以直接扔 /var/www/&nbsp;里面，也很方便。</p><p>Nginx 的维护，基本靠系统控制命令就能实现：</p><p>查看服务状态：</p><pre class=\"ql-syntax\" spellcheck=\"false\">systemctl status nginx\n\n</pre><p>如果只修改了配置，Nginx 可以在不中断服务的情况下热加载，这个很好用：</p><pre class=\"ql-syntax\" spellcheck=\"false\">sudo systemctl reload nginx\n\n</pre><p>开启自启，让系统启动时引导 Nginx 启动：</p><pre class=\"ql-syntax\" spellcheck=\"false\">systemctl enable nginx\n\n</pre><p>反悔了，不让系统启动时引导 Nginx 启动：</p><pre class=\"ql-syntax\" spellcheck=\"false\">systemctl disable nginx\n\n</pre><p>停用 nginx：</p><pre class=\"ql-syntax\" spellcheck=\"false\">systemctl stop nginx\n\n</pre><h3>3. Nvm 安装</h3><p>Node.js 也是火的不行，经常看到一些开源工具，都是放 Node 里面跑。</p><p>有需要的也可以装上，但不建议直接装 Node.js，版本问题什么的太操心了，直接上 Nvm。</p><p>还有一点非常的坑，Node.js 对用户的权限要求有点变态，个人建议用 root 用户安装，启动也是。之前只用其他用户 sudo 安装，结果各种问题。</p><p>从 Github 拉取官方正式版：</p><pre class=\"ql-syntax\" spellcheck=\"false\">curl -o- https://raw.githubusercontent.com/nvm-sh/nvm/v0.39.5/install.sh | bash\n\n</pre><p>安装 Nvm：</p><pre class=\"ql-syntax\" spellcheck=\"false\">source ~/.bashrc\n\n</pre><p>再用 Nvm 安装 Node.js：</p><pre class=\"ql-syntax\" spellcheck=\"false\">nvm install node\n\n</pre><p>看看安装成功没有：</p><pre class=\"ql-syntax\" spellcheck=\"false\">nvm --version\nnode --version\nnpm --version\n\n</pre><p>后面就可以 npm 一把梭了，当然也可以装 yarn 和 pm2，看个人喜好。</p><p><strong>· · ·</strong></p><p><br></p><h2><strong>七、小结</strong></h2><p>最后，终于来到最关键的部分了。其实上面这些整备的过程，一个脚本就可以搞定。</p><p>可以说是一次编写，多次运行，以后拿到新 VPS，直接复制过去就能跑。</p><p>整备的脚本，发个出来，给大家做个参考吧：</p><h4>1) 软件更新</h4><pre class=\"ql-syntax\" spellcheck=\"false\">apt update &amp;&amp; apt upgrade &amp;&amp; apt full-upgrade &amp;&amp; apt autoremove\n\n</pre><p>如果不想一直蹲那儿按确认，可以强制自动确认（风险还是有的，自己权衡吧）：</p><pre class=\"ql-syntax\" spellcheck=\"false\">apt update -y &amp;&amp; apt upgrade -y &amp;&amp; apt full-upgrade -y &amp;&amp; apt autoremove -y\n\n</pre><h4>2) 创建和运行脚本</h4><p>把脚本复制进去（参考脚本在后面，往下翻）：</p><pre class=\"ql-syntax\" spellcheck=\"false\">vi ready.sh\n\n</pre><p>给执行权限：</p><pre class=\"ql-syntax\" spellcheck=\"false\">chmod +x ready.sh\n\n</pre><p>运行脚本：</p><pre class=\"ql-syntax\" spellcheck=\"false\">source ready.sh\n\n</pre><p>脚本 ready.sh 的参考内容如下：</p><pre class=\"ql-syntax\" spellcheck=\"false\">#!/bin/bash\n\n# 一键整备脚本\n\n# 注意：命令里面的汉字部分，需要根据实际替换掉，根据自己的需要来取舍\n\n# 1) 软件更新：建议提前单独执行，可能需要更新内核，重启机器什么的\n\n# 2) 创建脚本：把脚本放到 VPS 上，赋予执行权限并执行。然后后面的步骤就可以完全自动化了\n\n# 3) 安装基础工具\napt install ufw curl unzip\n\n# 4) 添加新用户\n\n# 添加用户和用户组\nusername=\"新用户\"\npassword=\"密码\"\nuseradd -m -s /bin/bash -G sudo \"$username\"\n\n# 设置用户密码\necho \"$username:$password\" | chpasswd\n\n\n# 5) 修改 SSH 登录端口\n\nsed -i '/#Port 22/a Port 22\\nPort 端口号' /etc/ssh/sshd_config\n\n# 重启 sshd\nsystemctl restart sshd\n\n\n# 6) shh 免密连 vps\n\npublic_key=\"本地公钥\"\n\n# 生成服务器用户的公钥和私钥\nssh-keygen\n\n# 写入本地的私钥到文件\ncat &lt;&lt;EOF &gt; /当前用户目录/.ssh/authorized_keys\n$public_key\nEOF\n\n# 重启 sshd\nchmod 600 /当前用户目录/.ssh/authorized_keys\nsystemctl restart sshd\n\n\n# 7) 限制 root 用户用用密码登录（如果有设置 shh 免密登录的话）\n\n# 使用 sed 替换文件中的内容\nfile=\"/etc/ssh/sshd_config\"\nsearch=\"PermitRootLogin yes\"\nreplace=\"PermitRootLogin without-password\"\nsed -i \"s/$search/$replace/\" \"$file\"\n\n# 重启 sshd\nchmod 700 /当前用户目录/.ssh\nsystemctl restart sshd\n\n\n# 8) 设置防火墙\n\n# 开端口\nufw allow ssh\nufw allow 22\nufw allow 端口号\n\n# 启用防火墙\nufw enable\n\n\n# 9) 设置时区\ntimedatectl set-timezone 时区\n\n\n# 10) 配置 BBR 加速\necho \"net.core.default_qdisc=fq\" &gt;&gt; /etc/sysctl.conf\necho \"net.ipv4.tcp_congestion_control=bbr\" &gt;&gt; /etc/sysctl.conf\nsysctl -p\n\n</pre><p>跑完脚本验证一下配置，比如：新用户登录，root 用户无密码登录等，没问题就可以开始装应用干活了。</p><p><strong>· · ·</strong></p><p><br></p><h2><strong>七、相关</strong></h2><h3>补充说明</h3><p>如果是境内 VPS，可能需要镜像网站才能访问某些服务，有些额外的坑。</p>","content_text":"拿到新机器，咱们二话不说，先 ping 一下 IP，能否连通。连不通的话，可能是被封锁了，新机一般可以免费换 IP，换完接着 ping。\n\n连通后看看延迟和丢包，延迟太大就得考虑。可能这个机房，它不适合你。\n\n大家的地区，运营商，节点线路都不大一样，冷暖自知。\n\n能接受的话，就 ssh 上去，开始跑测试脚本，是骡子是马，还得看数据。跑完测试觉得机子不行，应该还能赶在截止时间之前退单退款。\n\n有些鸡贼的商家，使用流量超过 10G，就不给退单退款，这种就别跑测速了。\n\n测速脚本一跑，几十个 GB 就没了，到时候退不了，只能砸自己手里。\n\n· · ·\n\n\n\n\n\n一、脚本测试\n\n先别急着装什么面板，测完性能再说，直接上脚本。\n\n\n1. YABS 脚本\n\nyabs 是一个比较常用的综合测试脚本，权威性比较高，一般用来看最后的 cpu 得分。\n\nwget -qO- yabs.sh | bash\n\n\n\n\n2. BENCH.SH 一键测试脚本\n\nbench.sh 也是一个综合测试脚本，历史比较久，大佬一直在维护。\n\n原作者链接：github.com/teddysun/across\n\nwget -qO- bench.sh | bash\n\n\n\n\n3. 线路测试\n\nbesttrace 是专门测试回国线路的脚本，可以看到连接国内几大城市的节点线路和对应延迟。\n\nwget -qO- git.io/besttrace | bash\n\n\n\n\n4. 网速测试\n\nnetwork-speed，测试 vps 到全球的连接速度，节点比较多，比较耗流量，成功测完得 20G 往上。\n\nwget -qO- network-speed.xyz | bash\n\n\n\n\n5. 硬盘测试\n\n这条测试是调用系统的命令，看看到底是 HDD、SSD 还是 NVME 硬盘。\n\ndd if=/dev/zero of=256 bs=64K count=4K oflag=dsync\n\n\n\n测试结果参考：1-2M/s 的是 HDD；20-30M/s 就普通的 SSD；80M/s 以上的是好机子。\n\n\n6. 内存超售检测\n\n部分商家会超售内存，可以用该脚本检测。\n\n至于超售 CPU、带宽，这个就比较稀松平常了。\n\n（脚本的安全性未知，建议看完重装系统部分，再决定要不要跑。）\n\ncurl https://raw.githubusercontent.com/uselibrary/memoryCheck/main/memoryCheck.sh | bash\n\n\n\n\n7. 流媒体解锁和 OPENAI 解锁测试\n\n检测 VPS 的 IP 是否能够用来开通流媒体、OpenAI 等订阅服务。（同上，脚本安全性未知。）\n\nbash <(curl -L -s https://netflix.dad/detect-script)\n\n\n\n\n8. IP 检测\n\n如果对 IP 这个比较看重，怪文再推荐两个小工具，简单调查一下手上的 IP。\n\n1) IP 位置\n\nwhatismyipaddress.com\n\nwhatismyipaddress 这个网站可以查到你 VPS 的 IP 所在地，数据比较精准。\n\n2) IP 纯净度\n\nscamalytics.com\n\n这个网站会返回一个分数和一个风险等级，欺诈风险分数一般低于 50 就好。当然，分数越低越好，0 分最好。\n\nscamalytics 可以查到 IP 的纯净程度，这点很重要！\n\nIP 越干净，越容易过流媒体等服务的订阅风控，也不容易弹 Google 和 Cloudflare 的人机验证弹窗和过渡页，代理的时候体验会更好。\n\n比如，注册 OpenAI 账号，开通 Plus 订阅，都是很看重 IP 欺诈分的，分数太高的话，申请 API 和 Plus 可能不给通过。\n\n当然，也可以选择其他方式进行代偿，比如套 wrap，或者用其他优质节点开通服务，后面换回原 IP 继续用，基本没啥问题，除非原 IP 被墙了。\n\n· · ·\n\n\n\n\n\n二、更新系统\n\n\n1. 重装系统\n\n建议先跑完测试脚本，再重装系统，也是为了规避脚本的安全性问题。可别以为大家用的脚本就很安全哦，还是别轻信外部脚本的好。\n\n有些人喜欢跑各种整合过的一键脚本，也不看脚本里面写的啥，难搞。\n\n一般来说，VPS 厂商给你的默认操作系统，很可能不是你想要的发行版或者版本号，建议用厂商的面板重装下系统。\n\n不太建议自己装些厂商没提供的系统，容易被抓到把柄。\n\n系统的选择\n\n服务器端发行版选择很多，Debian，Ubuntu，Rocky Linux，AlmaLinux，Fedora 等等，甚至 CentOS，OpenSUSE\nLeap 都行，选自己熟悉的就好。\n\n关于各个发行版的特点和取舍，这里先占个坑，后面来填吧。\n\n反正有条件的直接上 Red Hat Enterprise Linux（RHEL）就好，免费的哪有这个好啊。\n\nUbuntu 因为资料好找，使用起来方便，稳定性也不错，所以怪文一直用 Ubuntu。\n\n但自从 Canonical 往 Ubuntu 22.04.3 LTS 里面塞广告，有些广告还无法被移除，吃相有点难看，这种行径和国内厂商有得一拼了。\n\n目前还在寻找平替，因为系统还没换，下面的 shell 命令，基本都是基于 Ubuntu 的版本。\n\n如果装的是 Fedora 和 RHEL 系的发行版，比如 CentOS 之类的，请将下面命令中的 apt 用 yum 替换。\n\n\n2. 更新列表升级应用\n\napt update && apt upgrade && apt full-upgrade && apt autoremove\n\n\n\n\n3. 基础工具包\n\napt install vim curl unzip ufw\n\n\n\n· · ·\n\n\n\n\n\n三、安全配置\n\n小白最容易忽略的就是安全配置，弱密码什么的就不说了，说一下核心的配置。\n\n\n1. 添加新用户\n\n不建议每次都用 root 账号登录，创建新用户并给 root 权限比较好。\n\n首先，添加新用户，并自动创建对应的组，然后根据提示设置密码：\n\nadduser 新用户名\n\n\n\n设置新密码之后，继续。除了用户名，其他敲回车默认即可。\n\n添加新用户到 sudo 用户组，就可以使用 sudo 命令了：\n\nusermod -aG sudo 新用户名\nCopy\n// 相关补充\nexit            // 用户退出登录\npasswd p        // 重设密码\n\n\n\n\n2. 修改 SSH 登录端口\n\n更改端口前，建议先把 22 端口也添加上，等 ssh 能通过其他端口连接后，再注释掉22端口。\n\n先打开配置文件：\n\nvim /etc/ssh/sshd_config\n\n\n\n找到这行：\n\n> Port 22\n\n然后在后面追加想要的端口号：\n\n> Port 端口号\n\n端口号的范围是 0 到 65535，数字别超了。\n\n加完端口重启 SSHD 服务，让配置生效：\n\nsystemctl restart sshd.service\n\n\n\n如遇到 ssh 连接总自己断开，还可以继续添加添加配置：\n\n> ClientAliveInterval 30\n\n> ClientAliveCountMax 6\n\n\n3. SSH 免密登录\n\nWindows 用户在 /.ssh 目录生成公钥和私钥：\n\nssh-keygen\n\n\n\n再在 vps 上的生成用户的公钥和私钥：\n\nssh-keygen\n\n\n\n创建 authorized_keys 文件，把 Win 上的公钥复制进 authorized_keys：\n\ncd /当前用户目录/.ssh\nvim authorized_keys\n\n\n\n给 authorized_keys 文件设置权限：\n\nchmod 600 /当前用户目录/.ssh/authorized_keys\n\n\n\n重启 ssh：\n\nsystemctl restart sshd\n\n\n\n最后，在本地测试配置是否成功：\n\nssh 用户名@IP\n\n\n\n再在 SSH 工具里配置一下登录凭证，就可以免密登录了。\n\n\n4. 限制 ROOT 用户直接 SSH 登录或者密码登录\n\n修改配置文件：\n\nvim /etc/ssh/sshd_config\n\n\n\n加入内容（限制 root 用户直接 ssh 登录，换言之，后面得用新用户登录）：\n\n> PermitRootLogin no\n\n如果只是想限制 root 用户密码登录，只能用密钥登录，则需要修改配置文件内容为：\n\n> PermitRootLogin without-password\n\n使修改生效：\n\nsystemctl restart sshd\n\n\n\n修改文件权限，把 .ssh 目录设为 700：\n\nchmod 700 /root/.ssh\n\n\n\n\n5. 启用防火墙\n\n检查防火墙的状态：\n\nufw status verbose\n\n\n\n所有可用的应用程序配置：\n\nufw app list\n\n\n\n查找指定配置文件包含的防火墙规则：\n\nufw app info 'Nginx Full'\n\n\n\n启用防火墙之前，先允许 ssh 应用的默认端口：\n\nufw allow ssh\n\n\n\n如果 ssh 监听其他端口，就打开那个端口：\n\nufw allow 端口\n\n\n\n还可以指定协议：\n\nufw allow 端口/协议\n\n\n\n启用防火墙（启用之前一定要保证，SSH 的端口已经开了，不然登出之后就登不进来了！）\n\nufw enable\n\n\n\n删除防火墙规则：\n\nufw delete allow 端口/tcp\nufw delete 防火墙规则编号\n\n\n\n关闭防火墙：\n\nufw disable\n\n\n\n重置防火墙设置并停止：\n\nufw reset\n\n\n\n· · ·\n\n\n\n\n\n四、基本配置\n\n\n1. 设置时区\n\n先看下系统的时区：\n\ntimedatectl show\n\n\n\n再按需修改，下面东八区的代号：\n\ntimedatectl set-timezone Asia/Shanghai\n\n\n\n\n2. 修改登录欢迎语（可选项）\n\nUbuntu 20.04 登录总提示 New release '22.04.3 LTS' available，如果不想升级，可以关掉登录后的动态消息。\n\nvim /etc/pam.d/sshd\n\n\n\n然后注释掉下面这两行：\n\n> session optional pam_motd.so motd=/run/motd.dynamic\n\n> session optional pam_motd.so noupdate\n\nUbuntu 22.04 每次登录都弹个很长的欢迎语，里面还有广告，难受。\n\n好消息是，动态消息也可以像上面 Ubuntu 20.04 那样彻底禁止掉。\n\n坏消息是，其他夹在 apt 命令中的广告就不那么好弄了。\n\n不想完全关闭动态消息的话，也有些能做的事，比如欢迎语里面的 K8S 推广内容倒是可以手动关闭。\n\nvim /etc/default/motd-news\n\n\n\n把文件里的 ENABLED=1 改成 ENABLED=0 就行。\n\n\n3. 装面板（可选项）\n\n如果手上的机器多，或者对命令行终端不感冒，也可以考虑装个面板，比如：1Panel、宝塔之类的。\n\n面板安装和使用，又可以写一篇长文了，有需要的手动搜一下吧。\n\n· · ·\n\n\n\n\n\n五、科学配置（可选项）\n\n\n1. BBR 加速\n\n如果系统不支持 BBR2 或者 BBR Plus，配置 BBR 加速就行了。\n\necho \"net.core.default_qdisc=fq\" >> /etc/sysctl.conf\necho \"net.ipv4.tcp_congestion_control=bbr\" >> /etc/sysctl.conf\nsysctl -p\n\n\n\n验证是否成功：\n\nsysctl net.ipv4.tcp_available_congestion_control\nlsmod | grep bbr\n\n\n\n如果系统支持 BBR2 或者 BBR Plus 的话，可以用一键脚本装内核，开启 BBR2，至于安全性，那就不清楚了。\n\n\n2. WARP\n\nWARP 是 Cloudflare 提供的网络流量安全及加速服务，它基于 WireGuard，能够帮你通过连接到 Cloudflare\n的边缘节点，实现隐私保护及链路优化。\n\n\n3. WIREGUARD\n\n如果要用 WARP，那肯定要装 WireGuard。\n\nWARP 的安装，又可以写一篇博文了，有需要的手动搜一下吧。\n\n\n4. V2RAY\n\n这个之前整过，就写出来把，用的是官方脚本：https://github.com/v2fly/fhs-install-v2ray\n\nV2Ray 的安装倒是很简单：\n\nbash <(curl -L https://raw.githubusercontent.com/v2fly/fhs-install-v2ray/master/install-release.sh)\n\n\n\n安装后就可以启动了，但是建议先改下配置文件。（配置内容根据官方的模板，做下调整就好）\n\nvim /usr/local/etc/v2ray/config.json\n\n\n\n改好配置后，先启动测试下：\n\nsudo systemctl daemon-reload\nsudo systemctl start v2ray\n\n\n\n查看服务状态：\n\nsystemctl status v2ray\n\n\n\n开机自启，让系统启动时引导 v2ray 启动：\n\nsudo systemctl enable v2ray\n\n\n\n反悔了，不让系统启动时引导 v2ray 启动：\n\nsystemctl disable v2ray\n\n\n\n停止服务：\n\nsystemctl stop v2ray\n\n\n\n重新加载：\n\nsystemctl reload v2ray\n\n\n\nv2ray 默认的配置路径：\n\n> /usr/local/etc/v2ray/config.json\n\nv2ray 默认的日志路径：\n\n> /var/log/v2ray/access.log\n\n> /var/log/v2ray/error.log\n\n当然，如果嫌手动配置麻烦的话，也可以选择一键安装和引导配置的脚本，比如下面的这个（安全性未知）：\n\nbash <(wget -qO- -o- https://git.io/v2ray.sh)\n\n\n\n· · ·\n\n\n\n\n\n六、推荐软件（可选项）\n\n\n1. DOCKER 安装\n\n现在干点啥都喜欢上 Docker，服务基本都扔 Docker 里面了，有需要的建议先安上，挺省心的。\n\ncurl -fsSL https://get.docker.com -o get-docker.sh\nsudo sh get-docker.sh\n\n\n\n看看安装是否成功：\n\ndocker --version\n\n\n\n补充一下，这个脚本是官方的，会自动安装 Docker Compose 组件，调用命令是 docker compose。\n\n如果看到别人用的命令是 docker-compose，那替换成 docker compose 就好，不必再装一个 docker-compose。\n\n\n2. NGINX 安装\n\n把 Nginx 放 Docker 当然也可以，但是对于这种入口级别的中间件，个人还是习惯用原生的。\n\n安装非常的简单：\n\nsudo apt install nginx\n\n\n\n看看成功没有：\n\nnginx -V\n\n\n\n启动 Nginx：\n\nsudo systemctl daemon-reload\nsudo systemctl start nginx\n\n\n\n测一下：在浏览器通过 ip 加 80 端口访问，是否是 Nginx 默认欢迎页。\n\n配置的话，一般直接去 /etc/nginx/conf.d 添加个文件，配一下就好了。\n\n有静态网站，可以直接扔 /var/www/ 里面，也很方便。\n\nNginx 的维护，基本靠系统控制命令就能实现：\n\n查看服务状态：\n\nsystemctl status nginx\n\n\n\n如果只修改了配置，Nginx 可以在不中断服务的情况下热加载，这个很好用：\n\nsudo systemctl reload nginx\n\n\n\n开启自启，让系统启动时引导 Nginx 启动：\n\nsystemctl enable nginx\n\n\n\n反悔了，不让系统启动时引导 Nginx 启动：\n\nsystemctl disable nginx\n\n\n\n停用 nginx：\n\nsystemctl stop nginx\n\n\n\n\n3. NVM 安装\n\nNode.js 也是火的不行，经常看到一些开源工具，都是放 Node 里面跑。\n\n有需要的也可以装上，但不建议直接装 Node.js，版本问题什么的太操心了，直接上 Nvm。\n\n还有一点非常的坑，Node.js 对用户的权限要求有点变态，个人建议用 root 用户安装，启动也是。之前只用其他用户 sudo 安装，结果各种问题。\n\n从 Github 拉取官方正式版：\n\ncurl -o- https://raw.githubusercontent.com/nvm-sh/nvm/v0.39.5/install.sh | bash\n\n\n\n安装 Nvm：\n\nsource ~/.bashrc\n\n\n\n再用 Nvm 安装 Node.js：\n\nnvm install node\n\n\n\n看看安装成功没有：\n\nnvm --version\nnode --version\nnpm --version\n\n\n\n后面就可以 npm 一把梭了，当然也可以装 yarn 和 pm2，看个人喜好。\n\n· · ·\n\n\n\n\n\n七、小结\n\n最后，终于来到最关键的部分了。其实上面这些整备的过程，一个脚本就可以搞定。\n\n可以说是一次编写，多次运行，以后拿到新 VPS，直接复制过去就能跑。\n\n整备的脚本，发个出来，给大家做个参考吧：\n\n1) 软件更新\n\napt update && apt upgrade && apt full-upgrade && apt autoremove\n\n\n\n如果不想一直蹲那儿按确认，可以强制自动确认（风险还是有的，自己权衡吧）：\n\napt update -y && apt upgrade -y && apt full-upgrade -y && apt autoremove -y\n\n\n\n2) 创建和运行脚本\n\n把脚本复制进去（参考脚本在后面，往下翻）：\n\nvi ready.sh\n\n\n\n给执行权限：\n\nchmod +x ready.sh\n\n\n\n运行脚本：\n\nsource ready.sh\n\n\n\n脚本 ready.sh 的参考内容如下：\n\n#!/bin/bash\n\n# 一键整备脚本\n\n# 注意：命令里面的汉字部分，需要根据实际替换掉，根据自己的需要来取舍\n\n# 1) 软件更新：建议提前单独执行，可能需要更新内核，重启机器什么的\n\n# 2) 创建脚本：把脚本放到 VPS 上，赋予执行权限并执行。然后后面的步骤就可以完全自动化了\n\n# 3) 安装基础工具\napt install ufw curl unzip\n\n# 4) 添加新用户\n\n# 添加用户和用户组\nusername=\"新用户\"\npassword=\"密码\"\nuseradd -m -s /bin/bash -G sudo \"$username\"\n\n# 设置用户密码\necho \"$username:$password\" | chpasswd\n\n\n# 5) 修改 SSH 登录端口\n\nsed -i '/#Port 22/a Port 22\\nPort 端口号' /etc/ssh/sshd_config\n\n# 重启 sshd\nsystemctl restart sshd\n\n\n# 6) shh 免密连 vps\n\npublic_key=\"本地公钥\"\n\n# 生成服务器用户的公钥和私钥\nssh-keygen\n\n# 写入本地的私钥到文件\ncat <<EOF > /当前用户目录/.ssh/authorized_keys\n$public_key\nEOF\n\n# 重启 sshd\nchmod 600 /当前用户目录/.ssh/authorized_keys\nsystemctl restart sshd\n\n\n# 7) 限制 root 用户用用密码登录（如果有设置 shh 免密登录的话）\n\n# 使用 sed 替换文件中的内容\nfile=\"/etc/ssh/sshd_config\"\nsearch=\"PermitRootLogin yes\"\nreplace=\"PermitRootLogin without-password\"\nsed -i \"s/$search/$replace/\" \"$file\"\n\n# 重启 sshd\nchmod 700 /当前用户目录/.ssh\nsystemctl restart sshd\n\n\n# 8) 设置防火墙\n\n# 开端口\nufw allow ssh\nufw allow 22\nufw allow 端口号\n\n# 启用防火墙\nufw enable\n\n\n# 9) 设置时区\ntimedatectl set-timezone 时区\n\n\n# 10) 配置 BBR 加速\necho \"net.core.default_qdisc=fq\" >> /etc/sysctl.conf\necho \"net.ipv4.tcp_congestion_control=bbr\" >> /etc/sysctl.conf\nsysctl -p\n\n\n\n跑完脚本验证一下配置，比如：新用户登录，root 用户无密码登录等，没问题就可以开始装应用干活了。\n\n· · ·\n\n\n\n\n\n七、相关\n\n\n补充说明\n\n如果是境内 VPS，可能需要镜像网站才能访问某些服务，有些额外的坑。","date_published":"2024-10-02T11:48:20.312Z","_microfeed":{"is_audio":false,"is_document":false,"is_external_url":true,"is_video":false,"is_image":false,"web_url":"https://blog-ccino-cc.pages.dev/i/vps-vps-ez56GfIVr5S/","json_url":"https://blog-ccino-cc.pages.dev/i/ez56GfIVr5S/json/","rss_url":"https://blog-ccino-cc.pages.dev/i/ez56GfIVr5S/rss/","guid":"ez56GfIVr5S","status":"published","itunes:episodeType":"full","date_published_short":"Wed Oct 02 2024","date_published_ms":1727869700312}},{"id":"BXU1zEmNsAr","title":"使用 Cloudflare ZeroTrust 搭建 WebSSH","attachments":[{"url":"https://josephcz.xyz/technology/network/cloudflare-webssh-zerotrust/","mime_type":"text/html"}],"url":"https://blog.ccino.cc/cloudflare-webssh-zerotrust","external_url":"https://josephcz.xyz/technology/network/cloudflare-webssh-zerotrust/","content_html":"<p><a href=\"https://zikin.org/\" rel=\"noopener noreferrer\" target=\"_blank\">Zikin</a>&nbsp;在《<a href=\"https://zikin.org/protection-ssh-with-cloudflare-zero-trust/\" rel=\"noopener noreferrer\" target=\"_blank\">使用 Cloudflare Zero Trust 保护 SSH</a>》一文中介绍了如何使用 Cloudflare ZeroTrust 保护 SSH 的方法，同时也提供了&nbsp;<a href=\"https://zikin.org/cloudflare-zero-trust/\" rel=\"noopener noreferrer\" target=\"_blank\">对于网页的保护方法</a>。本文主要补充使用 Cloudflare SSH CA 替代 SSH 公钥的使用方法，以及在部署实践中值得注意的地方。</p><p>使用 Cloudflare SSH CA 替代 SSH 公钥，可以使用 GitHub 账户或其他 OpenID Connect 方式登录您的服务器 SSH，而无需私钥或 SSH 密码。这样可以避免私钥泄漏、忘记携带私钥，对于团队统一管理多个服务器、多个用户、分配不同的服务器权限也十分有用。</p><p>由于此方法也无需在 Cloudflare WebSSH 界面输入私钥或密码，对于不信任 Cloudflare 传输私钥的场景，也可以使用此方法。</p><h2><strong>部署中的注意事项</strong></h2><ol><li>对于非 HTTP 应用，Tunnel 需要占用整个域名——不能将 WebSSH 部署在子路径下。</li><li>对于多个服务器部署，不可以将多个服务器连接到同一个 Tunnel。需要为每个服务器创建单独的 Tunnel。</li><li>对于多个服务器部署，不可以多个域名共用一个 Application。</li><li>不可用使用四级及以上域名（如&nbsp;server.ssh.example.com）。Cloudflare 不会为四级及以上域名签发 SSL 证书。必须使用三级域名（如&nbsp;server-ssh.example.com）。</li><li>对于 Arch Linux，删除 Tunnel 之后需要执行&nbsp;rm /etc/systemd/system/cloudflared*&nbsp;删除残留的服务文件。</li><li>即是使用 Passkey 登录 GitHub 来通过 Cloudflare ZeroTrust 认证，Cloudflare 依然不认为登录方式为&nbsp;fido2——除非您使用付费的登录管理平台（如 Okta、Duo 等）。因此请不要在登录策略中添加登录方式为&nbsp;fido2&nbsp;的要求。</li><li>您的邮箱前缀（如&nbsp;luotianyi@example.com&nbsp;的前缀是&nbsp;luotianyi）必须与您的 Linux 用户名相同。否则 Cloudflare ZeroTrust 认证会失败。</li></ol><h2><strong>将服务器连接到 Cloudflare Tunnel</strong></h2><p>此部分的操作可参照 Zikin 的&nbsp;<a href=\"https://zikin.org/protection-ssh-with-cloudflare-zero-trust/\" rel=\"noopener noreferrer\" target=\"_blank\">使用 Cloudflare Zero Trust 保护 SSH</a>。鉴于 Cloudflare ZeroTrust 控制台有所变化，此处做一定的简述。</p><p>操作步骤如下：</p><ol><li>打开&nbsp;<a href=\"https://one.dash.cloudflare.com/\" rel=\"noopener noreferrer\" target=\"_blank\">Cloudflare ZeroTrust 控制台</a>。</li><li>左侧侧边栏选择&nbsp;<strong>Access</strong>&nbsp;-&gt;&nbsp;<strong>Tunnels</strong>。点击按钮&nbsp;<strong>Create a tunnel</strong>。</li><li>按照 Cloudflare 的提示，在服务器上安装 Cloudflare Tunnel。</li></ol><ul><li class=\"ql-indent-1\">对于 Arch Linux，可以直接使用命令&nbsp;pacman -S cloudflared&nbsp;安装并使用命令&nbsp;cloudflared service install &lt;token&gt;&nbsp;安装。</li></ul><ol><li>在&nbsp;<em>Add public hostname for</em>&nbsp;页面，设置用于 WebSSH 的子域名。</li></ol><ul><li class=\"ql-indent-1\"><strong>Service</strong>&nbsp;部分&nbsp;<strong>Type</strong>&nbsp;选择 SSH</li><li><strong>Service</strong>&nbsp;部分&nbsp;<strong>URL</strong>&nbsp;填写&nbsp;127.0.0.1:22，其中&nbsp;22&nbsp;为 SSH 服务的端口号</li><li class=\"ql-indent-1\">完成 Tunnel 的创建。</li></ul><ol><li>左侧边栏选择&nbsp;<strong>Access</strong>&nbsp;-&gt;&nbsp;<strong>Applications</strong>。点击按钮&nbsp;<strong>Create an application</strong>。</li><li><strong>Select Type</strong>&nbsp;页面：类型选择 Self-hosted，进入下一页。</li><li><strong>Configure App</strong>&nbsp;页面：</li></ol><ul><li class=\"ql-indent-1\"><strong>Application Name</strong>&nbsp;随便选择</li><li class=\"ql-indent-1\"><strong>Application Domain</strong>&nbsp;设置为刚刚填写的、用于 WebSSH 的子域名</li></ul><ol><li><strong>Add Policy</strong>&nbsp;页面：</li></ol><ul><li class=\"ql-indent-1\"><strong>Policy Name</strong>&nbsp;随便选择</li><li><strong>Configure Rules</strong></li><li class=\"ql-indent-2\"><strong>Selector</strong>&nbsp;选择&nbsp;<strong>Emails</strong></li><li class=\"ql-indent-1\"><strong>Value</strong>&nbsp;填写你自己的电子邮箱（如果您使用 GitHub 登录，应该填写你的 GitHub Primary Email）</li></ul><ol><li><strong>Setup</strong>&nbsp;页面</li></ol><ul><li class=\"ql-indent-1\">在&nbsp;<strong>Additional settings</strong>&nbsp;中选择&nbsp;<strong>Enable automatic cloudflared authentication</strong></li><li class=\"ql-indent-1\">开启&nbsp;<strong>Browser rendering</strong></li></ul><p><strong>注意本文和引用 Zikin 文章中的不同之处</strong></p><p>在&nbsp;<strong>Add Policy</strong>&nbsp;页面，如果您希望统一管理自己的多个账号，或多个组织中的不同账号，请参见 Zikin 文中的方法创建 Group。如果您的 WebSSH 仅供自己使用、且验证方式唯一，使用本文的方法可以简化操作。</p><p><img src=\"https://cdn-blog-ccino-cc.ccino.cc/blog-ccino-cc/production/media/rich-editor/items/BXU1zEmNsAr/image-a400dad8614db24d24e9dbbce6c7d122.jpg\"></p><p>Add Policy 页面的 Configure Rules 配置</p><p>在&nbsp;<strong>Setup</strong>&nbsp;页面，对于&nbsp;<strong>Enable automatic cloudflared authentication</strong>&nbsp;配置：</p><ul><li>如果不需要 Cloudflare SSH CA 管理 SSH 认证（不自动登录 SSH），则与 Zikin 文章中保持一致，不打开此项目</li><li>如果需要 Cloudflare SSH CA 管理 SSH 认证（自动登录 SSH），则需要打开此项目</li></ul><p><img src=\"https://cdn-blog-ccino-cc.ccino.cc/blog-ccino-cc/production/media/rich-editor/items/BXU1zEmNsAr/image-ce6455e902569ff399bf8e0578bb73d9.jpg\"></p><p>Setup 页面的配置</p><h2><strong>配置 Cloudflare SSH CA</strong></h2><p>在完成上述配置后，便可以创建 Cloudflare SSH CA 并配置认证。</p><p>操作步骤如下：</p><ol><li>打开&nbsp;<a href=\"https://one.dash.cloudflare.com/\" rel=\"noopener noreferrer\" target=\"_blank\">Cloudflare ZeroTrust 控制台</a></li><li>左侧侧边栏选择&nbsp;<strong>Access</strong>&nbsp;-&gt;&nbsp;<strong>Service Auth</strong>。</li><li>在上方 Tab 选择栏中，选择&nbsp;<strong>SSH</strong></li><li>在&nbsp;<strong>Application</strong>&nbsp;下拉菜单，选择前一节中创建的 Application。</li></ol><p><img src=\"https://cdn-blog-ccino-cc.ccino.cc/blog-ccino-cc/production/media/rich-editor/items/BXU1zEmNsAr/image-0f4841cd998604c025d7a553533ec74a.jpg\"></p><p>为 Application 创建 SSH CA</p><p>5.点击&nbsp;<strong>Generate Certificate</strong>&nbsp;按钮，生成 SSH CA。</p><p>6.生成成功后，在下方会看到已经生成 SSH CA 的 Application 列表。点击 Application 的名称，复制弹出窗口中的 Public Key。此 Public Key 即为 SSH CA 公钥。</p><p><img src=\"https://cdn-blog-ccino-cc.ccino.cc/blog-ccino-cc/production/media/rich-editor/items/BXU1zEmNsAr/image-aaedc1d1e17919660d690d5168b1313a.jpg\"></p><p>复制 SSH CA 公钥</p><h2><strong>配置服务器</strong></h2><p>登录服务器，编辑&nbsp;/etc/ssh/sshd_config。确保您已经开启了&nbsp;PubkeyAuthentication：</p><pre class=\"ql-syntax\" spellcheck=\"false\">PubkeyAuthentication yes\n</pre><p>同时，配置 Cloudflare SSH CA 的路径：</p><pre class=\"ql-syntax\" spellcheck=\"false\">TrustedUserCAKeys /etc/ssh/cloudflare-ca.pub\n</pre><p>然后，将您在上一步中复制的 SSH CA 公钥保存到&nbsp;/etc/ssh/cloudflare-ca.pub。您可以修改 Cloudflare SSH CA 的存放路径，只要保证&nbsp;sshd&nbsp;有访问权限即可。</p><p>然后，重新启动 SSH 服务：</p><pre class=\"ql-syntax\" spellcheck=\"false\">systemctl restart sshd\n</pre><ul><li><em>注：不同发行版的 SSH 服务名称可能不同，可能为&nbsp;ssh、sshd、openssh-server&nbsp;或其他名称。对于不使用 Systemd 的发行版，请参考其手册或文档。</em></li></ul><p>此时打开您设置的 WebSSH 域名，即可看到 Cloudflare ZeroTrust 的登录界面。登录后，无需输入私钥或密码，即可访问您的服务器 Shell。</p><h2><strong>Cloudlfare 相关的文档</strong></h2><ol><li>Cloudflare 社区上对于多个服务器连接到同一个 Tunnel 的讨论：<a href=\"https://community.cloudflare.com/t/multiple-ssh-servers-under-same-tunnel-name/420170\" rel=\"noopener noreferrer\" target=\"_blank\">Multiple SSH servers under same tunnel name?</a></li><li>Cloudflare 文档：<a href=\"https://developers.cloudflare.com/cloudflare-one/connections/connect-networks/use-cases/ssh/\" rel=\"noopener noreferrer\" target=\"_blank\">SSH</a></li><li>Cloudflare 文档：<a href=\"https://developers.cloudflare.com/cloudflare-one/identity/users/short-lived-certificates/\" rel=\"noopener noreferrer\" target=\"_blank\">Configure short-lived certificates</a></li></ol>","content_text":"Zikin 在《使用 Cloudflare Zero Trust 保护 SSH》一文中介绍了如何使用 Cloudflare ZeroTrust 保护 SSH\n的方法，同时也提供了 对于网页的保护方法。本文主要补充使用 Cloudflare SSH CA 替代 SSH 公钥的使用方法，以及在部署实践中值得注意的地方。\n\n使用 Cloudflare SSH CA 替代 SSH 公钥，可以使用 GitHub 账户或其他 OpenID Connect 方式登录您的服务器\nSSH，而无需私钥或 SSH 密码。这样可以避免私钥泄漏、忘记携带私钥，对于团队统一管理多个服务器、多个用户、分配不同的服务器权限也十分有用。\n\n由于此方法也无需在 Cloudflare WebSSH 界面输入私钥或密码，对于不信任 Cloudflare 传输私钥的场景，也可以使用此方法。\n\n\n部署中的注意事项\n\n 1. 对于非 HTTP 应用，Tunnel 需要占用整个域名——不能将 WebSSH 部署在子路径下。\n 2. 对于多个服务器部署，不可以将多个服务器连接到同一个 Tunnel。需要为每个服务器创建单独的 Tunnel。\n 3. 对于多个服务器部署，不可以多个域名共用一个 Application。\n 4. 不可用使用四级及以上域名（如 server.ssh.example.com）。Cloudflare 不会为四级及以上域名签发 SSL\n    证书。必须使用三级域名（如 server-ssh.example.com）。\n 5. 对于 Arch Linux，删除 Tunnel 之后需要执行 rm\n    /etc/systemd/system/cloudflared* 删除残留的服务文件。\n 6. 即是使用 Passkey 登录 GitHub 来通过 Cloudflare ZeroTrust 认证，Cloudflare\n    依然不认为登录方式为 fido2——除非您使用付费的登录管理平台（如 Okta、Duo 等）。因此请不要在登录策略中添加登录方式为 fido2 的要求。\n 7. 您的邮箱前缀（如 luotianyi@example.com 的前缀是 luotianyi）必须与您的 Linux 用户名相同。否则\n    Cloudflare ZeroTrust 认证会失败。\n\n\n将服务器连接到 CLOUDFLARE TUNNEL\n\n此部分的操作可参照 Zikin 的 使用 Cloudflare Zero Trust 保护 SSH。鉴于 Cloudflare ZeroTrust\n控制台有所变化，此处做一定的简述。\n\n操作步骤如下：\n\n 1. 打开 Cloudflare ZeroTrust 控制台。\n 2. 左侧侧边栏选择 Access -> Tunnels。点击按钮 Create a tunnel。\n 3. 按照 Cloudflare 的提示，在服务器上安装 Cloudflare Tunnel。\n\n * 对于 Arch Linux，可以直接使用命令 pacman -S cloudflared 安装并使用命令 cloudflared service\n   install <token> 安装。\n\n 1. 在 Add public hostname for 页面，设置用于 WebSSH 的子域名。\n\n * Service 部分 Type 选择 SSH\n * Service 部分 URL 填写 127.0.0.1:22，其中 22 为 SSH 服务的端口号\n * 完成 Tunnel 的创建。\n\n 1. 左侧边栏选择 Access -> Applications。点击按钮 Create an application。\n 2. Select Type 页面：类型选择 Self-hosted，进入下一页。\n 3. Configure App 页面：\n\n * Application Name 随便选择\n * Application Domain 设置为刚刚填写的、用于 WebSSH 的子域名\n\n 1. Add Policy 页面：\n\n * Policy Name 随便选择\n * Configure Rules\n * Selector 选择 Emails\n * Value 填写你自己的电子邮箱（如果您使用 GitHub 登录，应该填写你的 GitHub Primary Email）\n\n 1. Setup 页面\n\n * 在 Additional settings 中选择 Enable automatic cloudflared authentication\n * 开启 Browser rendering\n\n注意本文和引用 Zikin 文章中的不同之处\n\n在 Add Policy 页面，如果您希望统一管理自己的多个账号，或多个组织中的不同账号，请参见 Zikin 文中的方法创建 Group。如果您的 WebSSH\n仅供自己使用、且验证方式唯一，使用本文的方法可以简化操作。\n\n[https://cdn-blog-ccino-cc.ccino.cc/blog-ccino-cc/production/media/rich-editor/items/BXU1zEmNsAr/image-a400dad8614db24d24e9dbbce6c7d122.jpg]\n\nAdd Policy 页面的 Configure Rules 配置\n\n在 Setup 页面，对于 Enable automatic cloudflared authentication 配置：\n\n * 如果不需要 Cloudflare SSH CA 管理 SSH 认证（不自动登录 SSH），则与 Zikin 文章中保持一致，不打开此项目\n * 如果需要 Cloudflare SSH CA 管理 SSH 认证（自动登录 SSH），则需要打开此项目\n\n[https://cdn-blog-ccino-cc.ccino.cc/blog-ccino-cc/production/media/rich-editor/items/BXU1zEmNsAr/image-ce6455e902569ff399bf8e0578bb73d9.jpg]\n\nSetup 页面的配置\n\n\n配置 CLOUDFLARE SSH CA\n\n在完成上述配置后，便可以创建 Cloudflare SSH CA 并配置认证。\n\n操作步骤如下：\n\n 1. 打开 Cloudflare ZeroTrust 控制台\n 2. 左侧侧边栏选择 Access -> Service Auth。\n 3. 在上方 Tab 选择栏中，选择 SSH\n 4. 在 Application 下拉菜单，选择前一节中创建的 Application。\n\n[https://cdn-blog-ccino-cc.ccino.cc/blog-ccino-cc/production/media/rich-editor/items/BXU1zEmNsAr/image-0f4841cd998604c025d7a553533ec74a.jpg]\n\n为 Application 创建 SSH CA\n\n5.点击 Generate Certificate 按钮，生成 SSH CA。\n\n6.生成成功后，在下方会看到已经生成 SSH CA 的 Application 列表。点击 Application 的名称，复制弹出窗口中的 Public\nKey。此 Public Key 即为 SSH CA 公钥。\n\n[https://cdn-blog-ccino-cc.ccino.cc/blog-ccino-cc/production/media/rich-editor/items/BXU1zEmNsAr/image-aaedc1d1e17919660d690d5168b1313a.jpg]\n\n复制 SSH CA 公钥\n\n\n配置服务器\n\n登录服务器，编辑 /etc/ssh/sshd_config。确保您已经开启了 PubkeyAuthentication：\n\nPubkeyAuthentication yes\n\n\n同时，配置 Cloudflare SSH CA 的路径：\n\nTrustedUserCAKeys /etc/ssh/cloudflare-ca.pub\n\n\n然后，将您在上一步中复制的 SSH CA 公钥保存到 /etc/ssh/cloudflare-ca.pub。您可以修改 Cloudflare SSH CA\n的存放路径，只要保证 sshd 有访问权限即可。\n\n然后，重新启动 SSH 服务：\n\nsystemctl restart sshd\n\n\n * 注：不同发行版的 SSH 服务名称可能不同，可能为 ssh、sshd、openssh-server 或其他名称。对于不使用 Systemd\n   的发行版，请参考其手册或文档。\n\n此时打开您设置的 WebSSH 域名，即可看到 Cloudflare ZeroTrust 的登录界面。登录后，无需输入私钥或密码，即可访问您的服务器\nShell。\n\n\nCLOUDLFARE 相关的文档\n\n 1. Cloudflare 社区上对于多个服务器连接到同一个 Tunnel 的讨论：Multiple SSH servers under same\n    tunnel name?\n 2. Cloudflare 文档：SSH\n 3. Cloudflare 文档：Configure short-lived certificates","date_published":"2024-10-02T06:36:35.532Z","_microfeed":{"is_audio":false,"is_document":false,"is_external_url":true,"is_video":false,"is_image":false,"web_url":"https://blog-ccino-cc.pages.dev/i/cloudflare-zerotrust-webssh-BXU1zEmNsAr/","json_url":"https://blog-ccino-cc.pages.dev/i/BXU1zEmNsAr/json/","rss_url":"https://blog-ccino-cc.pages.dev/i/BXU1zEmNsAr/rss/","guid":"BXU1zEmNsAr","status":"published","itunes:episodeType":"full","date_published_short":"Wed Oct 02 2024","date_published_ms":1727850995532}}],"_microfeed":{"microfeed_version":"0.1.2","base_url":"https://blog-ccino-cc.pages.dev","categories":[],"subscribe_methods":[{"name":"RSS","type":"rss","url":"https://blog-ccino-cc.pages.dev/rss/","image":"https://blog-ccino-cc.pages.dev/assets/brands/subscribe/rss.png","enabled":true,"editable":false,"id":"7-6m9ozpyuG"},{"name":"JSON","type":"json","url":"https://blog-ccino-cc.pages.dev/json/","image":"https://blog-ccino-cc.pages.dev/assets/brands/subscribe/json.png","enabled":true,"editable":false,"id":"kFHio_oK78v"}],"description_text":"","copyright":"©2024","itunes:type":"episodic","items_sort_order":"newest_first"}}