<?xml version="1.0" encoding="UTF-8"?><rss xmlns:dc="http://purl.org/dc/elements/1.1/" xmlns:content="http://purl.org/rss/1.0/modules/content/" xmlns:atom="http://www.w3.org/2005/Atom" version="2.0"><channel><title><![CDATA[是我吖，地方]]></title><description><![CDATA[Blog Space]]></description><link>https://me.bcw2.top:3443</link><image><url>https://file.bcw2.top:3443/f/0zsd/bcw0613.jpg</url><title>是我吖，地方</title><link>https://me.bcw2.top:3443</link></image><generator>Shiro (https://github.com/mix-space-lts/Shiro)</generator><lastBuildDate>Mon, 07 Sep 2026 19:37:45 GMT</lastBuildDate><atom:link href="https://me.bcw2.top:3443/feed" rel="self" type="application/rss+xml"/><pubDate>Mon, 07 Sep 2026 19:37:44 GMT</pubDate><language><![CDATA[zh-CN]]></language><item><title><![CDATA[地铁站]]></title><description><![CDATA[<div><blockquote>该渲染由 Shiro API 生成，可能存在排版问题，最佳体验请前往：<a href="https://me.bcw2.top:3443/notes/8">https://me.bcw2.top:3443/notes/8</a></blockquote><div><p>那是一个普通的周末。那是一个普通的阴天。那是一次普通的机会，我们出去玩，去街上转悠。</p><p>但我至今忘不了出那个地铁口时的场景。</p><p>那是一座普通的地铁站出口。那是一个普通的路口。那是一个普通的广场。普通到和我在合肥上课、在哈尔滨参加活动时下车的感觉都一样。</p><p>呵，我在哈尔滨都没有做过地铁。</p><p>我其实不知道为什么我会记得这个场景。我甚至都不知道它为什么会躺在我的备忘录里成为一篇待写的手记。</p><p>但它就普普通通的在那儿。</p><p>于是我就普普通通的写了。</p></div><p style="text-align:right"><a href="https://me.bcw2.top:3443/notes/8#comments">看完了？说点什么呢</a></p></div>]]></description><link>https://me.bcw2.top:3443/notes/8</link><guid isPermaLink="true">https://me.bcw2.top:3443/notes/8</guid><dc:creator><![CDATA[bcw222]]></dc:creator><pubDate>Tue, 21 Jul 2026 06:53:39 GMT</pubDate></item><item><title><![CDATA[从UGC到产业化：游戏评论的路在何方]]></title><description><![CDATA[<div><blockquote>该渲染由 Shiro API 生成，可能存在排版问题，最佳体验请前往：<a href="https://me.bcw2.top:3443/posts/game/from-ugc-to-industrialization-future-of-game-criticism">https://me.bcw2.top:3443/posts/game/from-ugc-to-industrialization-future-of-game-criticism</a></blockquote><div><p>事情的背景是我在b站偶然刷到一条影评家对某篇奥斯卡奖电影的痛批。我一直觉得，游戏和电影这两种媒介之间存在着一种微妙的不对称。同样是娱乐，同样逐渐被承认为新兴艺术形式，电影拥有从《电影手册》到三大电影节的完整批评体系，而游戏——尤其是那些明明具有电影般叙事表达力的作品——似乎始终没有得到应得的严肃对待。</p><p>这里需要先做一个区分：我说的并不是那些以竞技和换皮为核心的长线运营作品；如果我记得写了的话，关联阅读里应该会有一篇，讲的就是我对游戏本质不同的看法。我想说的是，这些作品和那些以营收为生命线的手游，根本不是一个物种。那些游戏的本质是用劳动密集的美工，流水线式的亚文化换长期稳定的氪金流水；而我想说的是，像星际拓荒那样从大学毕设到现象级，像无人深空那样一次买断十年更新的手作经典。游戏本身天然带着空中更新的动态和初体验震撼的静态感，而我们讨论的就是那些不同于电影那样一锤子买卖的长期作品，也不至于流水换流水的买断生意。</p><p>但问题在于，不管这类游戏多引人深思，批评界仍然会下意识地将它归入&quot;游戏&quot;这个大类，和长线运营或者罐头游戏放在一起讨论。电影的媒介从一开始就自带艺术属性，再烂的商业片也是艺术；反观游戏界，哪怕游戏再好、玩家基数再多，却迟迟没有严肃客观的批评体系建立起来。很多时候，游戏媒体被那些高营收的抽卡手游和大厂堆料的工业罐头的宣发预算绑架，变成了导购渠道；而在学术界，整个环境依然没有把游戏批评当成一门正经的学问。</p><h2 id="">从一段聊天记录说起</h2><p>前阵子我把 迷走深空：碎舰师 安利给了一个朋友。这款游戏的开场有一首童声念的诗：</p><blockquote><p>每天走进工作区，</p><p>辛勤劳作挣所需，</p><p>我向上天常祈祷，</p><p>愿父归来乐陶陶，</p><p>若有一日他归尘，</p><p>但求福伴后继人。</p></blockquote>
<p>我问他，你觉得为什么这段诗要用童声？</p><p>朋友说，他有一种末日感。</p><p>我没有满足于这个答案。这个游戏的环境是太空，特点是科技，但它的背景真的是未来吗？我觉得不是，是过去。与其说它是未来科技巨头垄断一切，不如说是换了皮的封建剥削。LYNX公司把碎舰包装成宏大事业，而求职者连基础服务都负担不起。所谓克隆提取技术，说明里白纸黑字写着，该过程会摧毁你的原始身体。在童声里，唯一暂时逃过一劫的，是还没有劳动能力的孩子。朋友说那是末日，我说那更像一种绝望与无力，只不过坐标轴被拉大了而已。我们甚至停下来讨论，用末日这个词描述感受到底准不准。不是抬杠，而是想警惕语言本身对思维的扭曲。</p><p>后来我又看到一条网上评论：在切割船板的时候，听到那德州风味的木吉他，衬在深空的白噪音上，时不时被焊枪的声音盖过去，心里的情绪真的难以言喻。音乐表层处处是科技、未来、太空的进步，内里却是和现实相近、甚至倒退回奴隶制和封建时代的社会关系。</p><p>我想说的是，以上这些，就是游戏评论。没有人布置作业，没有评分，没有稿费，两个玩家加上一个匿名网友，在深夜里做的事情，和影评人的拉片、文本细读、主题阐释，没有任何本质区别。</p><p>玩家社区的评价从来不是单一的。有人从叙事层面切入，像我和朋友聊碎舰师那样，把游戏当成文本分析它的隐喻和意识形态。有人关注玩法设计，讨论关卡结构是不是自洽，机制有没有传达它想传达的东西。有人在意音乐和美术的整体氛围，就像那位网友注意到木吉他和白噪音的叠置。还有人纯粹从个人情感出发——&quot;我有一种末日感&quot;——这本身就是一种非常真实的批评直觉，只是没有被打磨成学术语言而已。</p><p>问题是，这种多元的、自下而上的评价体系，始终没有被正视为&quot;批评&quot;。它被当成&quot;感想&quot;、&quot;心得&quot;、&quot;测评&quot;——好像只要是从玩家嘴里说出来的，就不配被叫做&quot;批评&quot;。</p><p>这些文字的质量，就我个人而言，很难给出中肯的评价。但我自认为这些至少满怀玩家心血的东西，不应该只能屈尊于诸如 Steam 小黑盒这样的宣发平台，和&quot;优化不行&quot;&quot;闪退&quot;&quot;建议打折再买&quot;的评论混在一起。我们其实早就在做游戏批评了。只是我们没意识到自己在做这件事，整个行业也没意识到。而始于 UGC（User-generated content）但不止 UGC，是我接下来想说的期许。</p><h2 id="">学术界为什么还没正视这件事</h2><p>电影有影评体系，这不是天上掉下来的。它是一百多年来，学术界、媒体、产业三方慢慢建起来的。大学里有电影研究专业，学术期刊上有电影理论论文，电影节上有专门的批评单元，《电影手册》这种刊物可以靠纯学术话语影响一代导演。</p><p>游戏呢？游戏研究在绝大多数高校里根本不存在，或者被塞在&quot;数字媒体&quot;&quot;交互设计&quot;这类专业下面当一门选修课。你很难找到一个叫&quot;游戏批评学&quot;的学科方向。更重要的是，学术界对游戏的理解本身就存在分裂：一部分人把它当软件工程在研究，另一部分人把它当社会现象在观察，真正从&quot;游戏作为一种艺术媒介&quot;的角度去严肃分析其交互叙事、视听语言和机制隐喻的研究，少之又少。</p><p>这不完全是学术界的问题。游戏这个媒介本身就比电影复杂——电影是线性的、被动的，你分析镜头、叙事、表演就行。游戏的核心是交互，玩家的行为本身就是内容的一部分，传统的&quot;作者-作品-观众&quot;分析框架根本套不上去。批评的方法论得重新发明，这太难了。</p><p>但更难的是另一个原因：游戏行业太赚钱了。这句话听起来像夸，但其实是阻碍。</p><p>电影行业也赚钱，但电影有一套独立于票房之外的艺术认证机制。一部文艺片票房惨淡，不影响它在戛纳拿奖，不影响影评人认真对待它。与此同时，主流的游戏评测生态被商业逻辑彻底绑定了。你需要明白一个关键事实：营收占比和玩家基数、产品质量完全是两码事。那些以叙事和艺术表达为核心的优秀作品，玩家群体极其庞大，每年产出的高质量作品数量也不少。但那些抽卡手游、长线网游营收太高了，厂商的宣发预算极其夸张，游戏媒体得罪不起大金主——否则连提前评测的资格都没了。所以大多数游戏媒体最后都变成了导购或宣发渠道，很难沉下心做严肃的、甚至带点批判性的艺术评论。</p><p>玩家社区的习惯也加剧了这个问题。主流话语里，游戏讨论更常见的是通关攻略、剧情梳理、bug吐槽、版本强度排行。这些当然有价值，但它们不是批评。我们很少看到有哪个玩家社区会把一款游戏像影迷拉片那样逐帧拆解，去分析它的关卡设计如何隐喻、交互叙事如何传达主题、音画语言如何制造反差。不是没人做，而是没有形成气候，没有形成体系。</p><p>所以现状就很尴尬：玩家自己做的批评散落在碎片里，学术界还没真正下场，而最有钱的媒体在做导购。三个层面都在各自的轨道上跑，没有交汇，没有合力。</p><h2 id="">路在何方</h2><h3 id="">路在脚下</h3><p>但我不是一个悲观的人。回到我和朋友的那段聊天。</p><p>那天晚上，一个普通玩家对另一个普通玩家，用几千字拆解了一款游戏的政治隐喻、叙事结构和音乐设计。没有稿费，没有发表平台，没有任何功利目的。他就是有话想说。</p><p>这就是 UGC 的力量。当产业和学术都还没准备好正视游戏批评的时候，玩家自己已经开始了。Steam 评论区、知乎、B 站、个人博客、播客——这些散落在各处的文字和声音，其实就是游戏批评的原始形态。以Steam评论为例。它是UGC，是用户生成内容，本质上是大众化的。但它的机制设计很巧妙：推荐与不推荐的二元判断降低了写作门槛，但评论本身没有字数上限。一个玩家可以写三百字短评，也可以写三千字长文。更重要的是，&quot;有价值&quot;投票机制让真正有洞见的评论浮上来，而不是像社交媒体那样纯粹比拼转发量。</p><p>它们当然不完美。质量参差不齐，缺乏编辑把关，很容易被情绪和阵营对立裹挟。但它们的价值在于，它们证明了游戏批评的需求是真实存在的。不是&quot;玩家需要有人告诉他们值不值得买&quot;，而是&quot;玩家需要有人帮他们把玩完游戏之后那些说不清的感受，变成能说清的语言&quot;。</p><p>我和朋友做到了这一点。Steam 上那些长评也在帮更多人做到这一点。</p><p>所以路在脚下——个人的表达自由，Steam 评论区，这些就是我们已有的东西。它们不应该是游戏批评的终点，而是起点。每一个认真分析一款游戏的玩家，都在为这个领域添一块砖。</p><h3 id="">路在远方</h3><p>但如果只停留在 UGC 层面，游戏批评永远只能是散兵游勇。正如 Steam 的&quot;有价值&quot;投票很容易被&quot;有用/搞笑/钓鱼&quot;等情绪性二选一投票扭曲，真正的长文批评往往被笑话评论踩在脚下。</p><p>我们需要的远方是什么？</p><p>第一，独立的批评平台。这不是说再做一个游戏评分网站，而是需要一些真正独立于厂商之外、以批评本身为核心价值的阵地。它可以是媒体，可以是学术期刊，可以是一个社区。关键是它的评价体系不以流量和带货为导向，而是以批评的深度和独立性为标准。这个平台能不能商业化我不确定，但它至少需要拥有说真话的权力和传播力。TGA现在是最有影响力的行业盛典，但它的核心定位仍然是商业秀和玩家狂欢，缺乏真正独立、严肃、只探讨交互内核和视听语言的批评维度。这不是说TGA不好，而是说批评需要另一种生态。</p><p>第二，学术界应该入场。我知道这句话说出来容易，操作起来很难——没有学科基础、没有师资、没有方法论。但所有学科都是从零开始的。电影研究在 1950 年代之前也没什么像样的学术地位。当足够多的高质量作品出现了，当足够多的玩家已经开始自发地做分析了，学术界再装看不见，就有点说不过去了。</p><p>游戏不是没有值得研究的东西。《星际拓荒》的叙事结构与认知哲学，《死亡搁浅》的连接机制与社会隐喻，《冰汽时代》的对话系统与意识形态批评，《碎舰师》的劳动异化与音乐反差——这些东西的深度，不输给任何一部被写进论文的电影。</p><p>游戏作为一种交互媒介，其叙事方式、机制隐喻、视听语言和情感传达方式，都与电影有本质差异，不能简单套用电影理论。学界需要发展出真正适用于交互媒介的批评方法论——如何分析一个关卡设计的政治隐喻？如何评价一种交互机制对玩家共情的操控？如何理解玩家行为本身作为&quot;文本&quot;的批评价值？这些问题比传统影视批评复杂得多，但也正因为复杂，才更需要严肃的学术投入。</p><p>第三，也是最难的：行业需要接受批评。这不是说厂商要&quot;虚心听取玩家意见&quot;——那是客服逻辑，不是批评逻辑。我指的是，整个行业需要承认，游戏可以被严肃地、甚至尖锐地批评，这不会伤害游戏的价值，反而会提升它。就像电影行业已经学会的那样，被骂的艺术片也是艺术片。《明日方舟：终末地》的流水线系统做得稀烂，但却能给只玩抽卡二游的玩家“我也能玩自动化”的信心，给抠抠搜搜的男大原价入库《幸福工厂》的勇气。任何作品都不可能做到十全十美，但一针见血的批评绝非贬损，而是对热爱的事物日臻完美的期许。</p><h2 id="">结语</h2><p>前几天我又听了一遍《碎舰师》的原声。还是那把木吉他，还是精切器那令人发毛的金属气化声，还是拉出反应堆后定时熔毁的 Tension Remix。</p><p>我突然想到一件事：那种音乐里读出来的&quot;过去和未来都不值得想象&quot;，其实也可以反过来理解。正因为两头都不行，所以路只能在脚下，只能在现在。</p><p>游戏评论的路也是一样。往回看，没有百年积淀的影评传统可以照搬。往前看，成熟的批评平台和学术体系还远没有成型。但往回和往前都不重要。重要的是现在——Steam 上那些长评还在被写出来，私聊窗口里还有人在认真分析一首童声诗，播客里还有人花两个小时讨论一款独立游戏的叙事设计。</p><p>这些就是路。这些已经是路了。从UGC到产业化，中间隔着的不是能力的鸿沟，而仅仅是承认的延迟。深夜的聊天记录不会被收进任何选集，但它们是真实存在的批评现场。等学术界回头的那一天，他们要做的大概不是从零开始建造，而是为已经发生的一切正名。</p><p>只是我们还需要更多人承认，这条路值得走。</p></div><p style="text-align:right"><a href="https://me.bcw2.top:3443/posts/game/from-ugc-to-industrialization-future-of-game-criticism#comments">看完了？说点什么呢</a></p></div>]]></description><link>https://me.bcw2.top:3443/posts/game/from-ugc-to-industrialization-future-of-game-criticism</link><guid isPermaLink="true">https://me.bcw2.top:3443/posts/game/from-ugc-to-industrialization-future-of-game-criticism</guid><dc:creator><![CDATA[bcw222]]></dc:creator><pubDate>Tue, 21 Jul 2026 04:11:24 GMT</pubDate></item><item><title><![CDATA[做个好梦]]></title><description><![CDATA[<div><blockquote>该渲染由 Shiro API 生成，可能存在排版问题，最佳体验请前往：<a href="https://me.bcw2.top:3443/notes/7">https://me.bcw2.top:3443/notes/7</a></blockquote><div><p>又做梦了。
上次做一个让我有回忆的梦还是是梦到自己变成了mc里的玩家，在似乎是跑酷地图的一个世界里开神龙套乱飞；也是我在那次梦醒来之后用语音完整地记下了梦到的内容（也许会放上来），并萌生了写一篇讨论梦的博客的想法。谁知录音积压在备忘录还没来得及写，就又做了另一个梦。既然刚醒，脑子里或许还残留着一点做梦的思维模式（甚至回忆之前的梦也更熟悉一些），就一鼓作气把这篇手记写了吧。</p><p>===</p><p>其实近年来，我感觉到我的做梦频率并不如从前高。或许是因为过去的记忆犹如近大远小般被压缩显得密度更高，但我觉得学业压力的上涨使得我少有彻底放松休息的机会才是主要原因。在还热乎的记忆里，几乎每次做梦都是在那种可有可无的睡眠中，比如午睡或是下午/晚上提早睡，睡到晚上睡觉前就醒。听说做梦是在浅层睡眠期间，也许正是因为这种是“额外”的睡眠，大脑比晚上睡觉时更活跃，所以更容易做梦？我不确定。但总而言之，最近我甚至拿会不会做梦来判断我睡得足不足——至少对于我，连续忙活了好几天，倒头就睡的那种睡眠是不会伴随梦的。希望以后我都能睡饱，“做个好梦”吧。</p><p>突然想到一则故事。一段甜腻酸臭的爱情，一方生命所剩无几而另一方青涩笨拙（酸臭在这儿）地回应；入夜时分，后者每每向前者道晚安，前者都应以“好梦”；直到那最后一次，前者再无遗憾的口中道出的“晚安”似是在宽慰自己的长眠。但凡借力生命价值的桥段往往都能博取泪花一片；但无论如何，也许这正说明梦是尚有变数，而晚则落定当安吧。</p><p>那么我究竟梦到了什么呢？尚能想起来的部分就只有（尤其是开头已经不太记得了）：熊孩子干坏事被我抓到把柄，但直到惊动记者发布会家长仍在事不关己地游荡；而当我迫使身为大领导的家长站到台前，他却在台下记者反应过来前便大事化小小事化了借口忙碌遁走了。气极的我跑出会场，才发现地下（物理）会场上方的校园里正举行国庆或是什么仪式——又是形式主义盖过实干？无奈的我在校园里转悠，转到道路左侧的人行连廊中断的一处左转弯路段，道路两旁的施工围栏遮挡着本就尘土飞扬的视野，时常驶过的大车使我心惊胆战。最后我不得不伸出右臂不停挥舞，毕竟撞到手臂总好过撞大运咯~吧。好容易到了下一段人行连廊入口，师傅满口答应我会向上汇报这段缺失，但我远去时又说只是底层反馈并不抱太大希望。想来可笑：我当时感到一阵寒意，原来随处可见的学生会办传送阵营造了直达天听的假象，但本以为是学生会核心的传送目的地或许只是一个对外联络处；我都没有反应过来，为什么都随处可见传送阵了还没发现是梦啊喂！直到我醒来才发现，原来以为嗓子哑了喊不出来是因为梦中没能真正喊出口而缺少喊叫时喉咙的感觉反馈。</p><p>不过我发现，做梦时经常会感觉到环境无比熟悉，清醒后才会发现有太多的不合理。这也许是梦的本质决定：本就是自己脑海中的场景拼接而成，刚刚用过来构造场景的记忆自然会感觉无比熟悉。</p><p>有时就是这样：越是实际可行的建议越容易被否决。倒也不必归咎于可行的建议持有更大的利益冲突，我更倾向于解释为过分可行的建议会给“利益相关方”更大的即时冲击，而留有讨论余地不仅能让他人有时间接受，更能使“我认为合理”的方案在讨论中成熟为“大家都能接受”的。</p><p>也许我并没有说清楚我梦到了什么，但这个问题的答案或许已经无关紧要。且不论对梦的内容尚且清楚同时已经清醒的时间往往只有刚醒过来的那十几分钟；上文那样一字不落的描述下来我想也其实并没有人多感兴趣：究其根本梦本就是不可描述的存在啊。人与人之间的交流依赖语言，而为保持高效在语言之外的许多内容便要仰赖基于个人经验的联想了。现实生活中的交流尚且无碍：大家都生活在地球上，有着相似的环境感知，因而语言之外的不言自明所指尚且相仿；然而在一切皆有可能的梦境，基于现实生活的联想便成人际交流间最大的阻碍。尤其是像我这样不善表达，日常交流中都要常常依靠“你应该懂我意思吧”的人，首段mc那样的描述已经是我以为最有效的表达了。</p><p>何况为什么要搞清楚一切的意义呢？我上次还在想，也许在mc中畅游是对自由的渴望，抑或对生活不满的映照？但回顾刚才梦到的，似乎并不是处处都有相应的现实写照吧。又是意义！又有人要骂虚无主义了。这只是手记，何必事事上纲上线呢。</p><p>所以梦究竟是什么呢？借用AI对话的管理，就是大脑偶尔单开一个会话吧；AI随时重置，人生不能重来。愿你在这花花世界，也能安心归家，做个好梦。</p></div><p style="text-align:right"><a href="https://me.bcw2.top:3443/notes/7#comments">看完了？说点什么呢</a></p></div>]]></description><link>https://me.bcw2.top:3443/notes/7</link><guid isPermaLink="true">https://me.bcw2.top:3443/notes/7</guid><dc:creator><![CDATA[bcw222]]></dc:creator><pubDate>Mon, 20 Jul 2026 12:40:11 GMT</pubDate></item><item><title><![CDATA[SSR：前期的神，后期大坑]]></title><description><![CDATA[<div><blockquote>该渲染由 Shiro API 生成，可能存在排版问题，最佳体验请前往：<a href="https://me.bcw2.top:3443/posts/tech/ssr-god-early-hell-later">https://me.bcw2.top:3443/posts/tech/ssr-god-early-hell-later</a></blockquote><div><p>嘿嘿，我又来喷前端了~</p><p>背景信息是大家现在在看的这个博客（Mix Space + Shiro）在 LLM 越来越贵的现在被原开发者 <a href="https://github.com/Innei">@Innei</a> 从闭源 <a href="https://github.com/Innei/Shiroi">Shiroi</a> 到直接弃坑变 <a href="https://github.com/Innei/Yohaku">Yohaku</a>，前后端同步更新加上不再维护的 <a href="https://github.com/Innei/Shiro">Shiro</a> 仍然小bug不断，迫使我不得不自己 fork 大型魔改。然后在被各种缓存和优化问题拍打得体无完肤后，我忍不了了：</p><hr/><h2 id="ssr-">SSR 的背景</h2><p>React 变成 Vercel 的技术壁垒的破事儿也众所周知了，这里不再赘述；我们就简单说说 SSR 是怎么把你和 Vercel 强绑定的。</p><h3 id="">计算</h3><p>Vercel 的核心卖点之一是分布式渲染——你的 SSR 请求被扔到离用户最近的边缘节点上执行，TTFB 短，渲染快。这给了你一个错觉：<strong>好像不需要怎么优化缓存，反正每次请求都是边缘节点实时渲染，快得很。</strong></p><p>但问题在于，这个&quot;快&quot;是 Vercel 的基础设施在扛，不是你的代码在扛。一旦你离开这套体系，SSR 的 CPU 压力立刻变成单点瓶颈——一台机器上跑 Node，每个请求都要完整渲染一遍 React 组件树，QPS 一高就开始排队。分布式渲染把计算压力分散到了全球几百个边缘节点上，而你自己部署就是把这些压力全堆到一两个源站上，差距不是线性的，是指数级的。</p><p>你以前不用操心的事情，现在全都要自己操心。</p><h3 id="">网络</h3><p>Vercel 的边缘网络是它真正的护城河。用户在任何地方访问，都能就近命中一个 POP 节点，TLS 握手在毫秒级完成。这个网络基础设施不是你买两台 ECS、挂个 Nginx 就能复现的。</p><p>SSR 的场景特别吃网络延迟——因为每次请求都要回源渲染，不像纯静态资源可以提前推到 CDN。所以&quot;边缘计算&quot;对你来说不只是计算在哪里跑的问题，更是用户到你的服务器之间的网络路径有多短的问题。你自己部署 Node，用户在广东访问你北京的机房，RTT 30-50ms 起步，加上 SSR 渲染时间，TTFB 直接破百毫秒。</p><p>Vercel 卖的不是算力，是<strong>算力+网络的一体化交付</strong>。你离开它，这两个都得自己补。</p><h3 id="">开发</h3><p>自带 CI/CD、一键部署、Preview Deployment、域名自动配 HTTPS…… 这些开发体验确实是 Vercel 的早期优势。但说实话，到 2024 年了，GitHub Actions + Docker + 任意容器平台的组合已经足够成熟，CI/CD 不再是什么稀缺能力。这个曾经的主要卖点，现在已经不构成绑定的理由了。</p><p>真正的绑定还是在计算和网络这两层——你的代码架构是按 SSR 设计的，部署时就绕不开&quot;谁来分布式渲染&quot;和&quot;谁来边缘加速&quot;这两个问题。</p><hr/><h2 id="ssr-">SSR 的历史</h2><h3 id="-ssr">当年的 “SSR”</h3><p>传统 Perl、JSP、ASP.NET，以至不那么老的 PHP、Django——哪个不是服务端渲染？服务器收到请求，拼好 HTML，扔回浏览器，完事。</p><p>这套东西跑了十几年，没有人管它叫&quot;SSR&quot;。因为它就是唯一的前端模式，不需要一个名字来和别的模式区分。</p><blockquote><p><strong>SSR 这个词本身不是什么新概念</strong>，它是被 React 生态重新发明出来的营销词汇。</p></blockquote>
<h3 id="node--spa-">Node 前期 SPA 的坑</h3><p>然后 Node.js 来了，React/Vue 来了，SPA（Single Page Application）来了。浏览器下载一个 JS bundle，在客户端渲染整个页面，路由切换不刷新，体验丝滑。</p><p>但 SPA 的坑很快暴露：</p><ul><li><strong>首页白屏</strong>：浏览器要先下载几百 KB 的 JS bundle，然后执行，然后才渲染出页面。JS bundle 没下载完之前，页面是空的。用户盯着白屏等，体验极差。</li><li><strong>SEO 直接死掉</strong>：爬虫拿到的 HTML 是个空壳 <code>&lt;div id=&quot;root&quot;&gt;&lt;/div&gt;</code>，内容全靠 JS 执行后才有。Google 爬虫虽然能执行 JS，但抓取效率和权重评估都受影响，百度更别提了。</li><li><strong>无 JS 环境直接爆炸</strong>：禁用 JS、爬虫不支持 JS、低端设备 JS 执行慢，老旧 WebView 没法更新——这些场景下 SPA 就是白板。</li></ul><p>这时候大家回头一看：哎？PHP 那套跑得不是挺好的吗？于是 SSR 又被请回来了。</p><h3 id="-spa--ssr">后 SPA 时代的 SSR</h3><p>Next.js / Nuxt.js 横空出世，提出了&quot;现代 SSR&quot;的概念：<strong>首屏在服务端渲染 HTML，之后在客户端 hydrate（水合），接管后续交互变成 SPA。</strong> 同构 JavaScript——一套代码既跑在服务器上也跑在浏览器上。</p><p>听起来完美：SEO 有了，首屏速度有了，SPA 的交互体验也保住了。</p><p>但，代价是什么呢？</p><ol start="1"><li><p><strong>技术上越来越重</strong>：同一份组件，服务器跑一遍生成 HTML，浏览器再跑一遍做 hydration——等于做了两遍渲染。hydration 本身也有不小的 CPU 开销，低端设备上尤其明显。</p></li><li><p><strong>同构模糊了运行时的边界</strong>：你写的是&quot;一套代码&quot;，但它跑在两个截然不同的环境里。这个抽象在初期确实省事，但一旦你需要精细控制——哪段逻辑只在服务端跑、哪个组件不要水合、哪个 hook 在两端行为不同——心智模型立刻就裂开了。</p></li></ol><p>这不是 SSR 这个模式的问题，而是<strong>&quot;既要 SSR 的首屏和 SEO，又要 SPA 的交互体验&quot;这个缝合过程本身的成本</strong>。技术演进到这里，复杂度自然会上来。</p><hr/><h2 id="">终极大坑：过度追求同构</h2><p>前面说了性能层面的代价，但我想说的核心问题是<strong>认知成本</strong>。</p><p>同构的卖点是&quot;一套代码跑两端&quot;。前期开发确实快——你写一个组件，服务端渲染 HTML，客户端水合接管，看起来自然而然就 work 了。</p><p>但两端的环境<strong>本质上就是不同的</strong>。你强行用一层抽象去抹平差异，等于在中间塞了一个巨大的&quot;假装它们一样&quot;的胶水层。</p><p>一旦你需要做任何精细控制：</p><ul><li>这段逻辑只在服务端跑（直接查数据库）</li><li>这个组件不要水合（纯展示，省 CPU）</li><li>这个组件水合但它的子组件不要</li><li>这个 hook 在服务端和客户端行为不一样</li><li>服务端和客户端渲染结果不一致导致 hydration mismatch（排查起来极其痛苦，错误信息模糊）</li></ul><p>你就裂开了：你不再只是写 React 组件，你得时刻清楚<strong>&quot;我现在写的这段代码，到底在哪个运行时里、什么时候被执行、被执行几次&quot;</strong>。这个上下文切换的负担非常大。</p><p>对比传统 PHP 模式：后端就是后端，前端就是前端，各写各的，边界清晰。你优化的时候不需要考虑&quot;这段代码会不会在另一个运行时里产生副作用&quot;。简单粗暴，但<strong>认知成本可控</strong>。</p><p>同构用抽象帮你省了初期的胶水代码，代价是把复杂度转移到了<strong>运行时行为的不确定性</strong>上。一旦你需要精细控制，这层抽象就从&quot;帮你省事&quot;变成&quot;挡你的路&quot;。这也是为什么后来 React Server Components、Astro（默认不水合、显式标记才水合）这些新方向都在试图把这个代价降下来——让同构从&quot;默认全开&quot;变成&quot;按需付出&quot;。</p><blockquote><p><strong>不只是客户端和服务端的同构有问题，Node.js 本身也有这个倾向。</strong> 前端开发写 JS，后端开发也写 JS，全栈门槛确实降低了。<strong>Node.js 本身</strong>作为一个服务端运行时，和浏览器共享了 JS 语言层，但网络 API、文件系统、进程模型、模块系统全都不一样。<strong>全栈 JS</strong> 的全栈在于只需要会一个语言的语法就能写两套完全不同的系统，但在 AI 接管底层语法实现的当下，这层优势正在变得日益薄弱。</p></blockquote>
<p>本质上这是一个<strong>&quot;前期开发效率 vs 后期优化复杂度&quot;的权衡</strong>。同构用抽象帮你省了初期的胶水代码，但把复杂度转移到了真正理解运行时行为的不确定性上。</p><hr/><h2 id="-vercel-">如何去 Vercel 化</h2><h3 id="">浅层：换边缘计算提供商</h3><p>换个地方继续受同样的苦。阿里云函数计算、AWS Lambda——本质上还是 Serverless 那套，只是换了个厂商。你确实摆脱了 Vercel，但你仍然面临：</p><ul><li>冷启动问题（Next.js 镜像不小，冷启动几百毫秒到几秒）</li><li>成本不可控（SSR 请求时长 × 调用次数，流量上去了费用可能远超预期）</li><li>需要定制化适配（函数计算有自己的 handler 模式，不是直接 <code>next start</code>）</li></ul><h3 id="">深层：换设计思路</h3><p>真正的问题不是&quot;谁来替我跑 SSR&quot;，而是<strong>你到底需不需要 SSR</strong>。这才是真正的解法。别 all-in xxx.js 全栈了——</p><h4 id="">前后端分离</h4><p>后端该用 Go/Java/Python 就用，做你擅长的事——API、鉴权、数据库。Next.js/React 退回到它真正擅长的领域：<strong>前端</strong>。</p><h4 id="">动静态分离</h4><p>如果你的内容是公开的、需要被搜索引擎索引，那 SEO 是硬需求。但一个纯内容展示的页面，为什么不直接静态生成？上 CDN 直接缓存，零回源，全球秒开，不比 SSR 香多了？</p><p>如果你的内容是动态的、高度个性化的，这确实是实时渲染的主场，但这类页面（个人中心、推荐流）恰恰是<strong>不需要 SEO 的</strong>，用 CSR SPA 就完事了。</p><p>只有你的内容两者各占一半：公开的、需要被搜索引擎索引；但又实时更新，不想让陈旧数据留太久——那SSR确实是唯一的解药——当且仅当你完全看不上（找不到地方学）PHP/Django 这种稍显老旧但仍旧坚挺的框架的时候。</p><p><strong>前后端分离不是倒退，是把复杂度放到它该在的地方。</strong> 后端老老实实写 API，前端按需选择渲染策略，就不会把所有东西塞进一个同构的 SSR 框架里，然后在hydration mismatch 和缓存策略的泥潭里挣扎。</p><h2 id="">结语</h2><p>SSR 不是原罪，但在错误的地方过度使用同构全栈架构是。别被 Vercel 的丝滑体验惯坏了——等你真要把项目迁出来的时候，才会发现那些&quot;理所当然&quot;的能力背后，是你一个人根本复制不了的基础设施；全量 SSR + 全栈同构，就是前期的神、后期大坑。</p></div><p style="text-align:right"><a href="https://me.bcw2.top:3443/posts/tech/ssr-god-early-hell-later#comments">看完了？说点什么呢</a></p></div>]]></description><link>https://me.bcw2.top:3443/posts/tech/ssr-god-early-hell-later</link><guid isPermaLink="true">https://me.bcw2.top:3443/posts/tech/ssr-god-early-hell-later</guid><dc:creator><![CDATA[bcw222]]></dc:creator><pubDate>Sun, 19 Jul 2026 08:09:11 GMT</pubDate></item><item><title><![CDATA[Agentic 时代，AI的本质是共享经济]]></title><description><![CDATA[<div><blockquote>该渲染由 Shiro API 生成，可能存在排版问题，最佳体验请前往：<a href="https://me.bcw2.top:3443/posts/tech/agentic-era-ai-is-sharing-economy">https://me.bcw2.top:3443/posts/tech/agentic-era-ai-is-sharing-economy</a></blockquote><div><p>终于抽出空来写一篇个人对当下AI时代观察的综述。这篇文章的目的是给对AI一知半解，但愿意抽出空来专心了解的人——比如高考完不久的我那些同学们——一篇系统化、深入浅出的认知对齐；但只要你愿意相信，我的观察并不烂大街，便大可以放心的往下看。
这篇文章分为三个部分：</p><ol start="1"><li><strong>AI是什么</strong>：AI的本质和历史渊源</li><li><strong>LLM哪儿来的</strong>：2023年以来LLM的爆火</li><li><strong>Agent怎么用</strong>：如今agent如何重构我们的生产生活</li></ol><h2 id="ai">AI是什么</h2><h3 id="mark-i-">Mark I 感知机</h3><p>AI 的历史可以追溯到 1958 年 Rosenblatt 的 Mark I 感知机。</p><p>它由弗兰克·罗森布拉特（Frank Rosenblatt）于 1958 年在康奈尔航空实验室（Cornell Aeronautical Laboratory）完成，由 400 个光电元件组成一个 20×20 的“视网膜”阵列作为输入，通过可调电阻（电位器）来模拟可变的突触权重，由一组“关联单元”和“响应单元”组成输出，最终由一个电流计给出分类结果。通过人工调节电位器的电阻值（左拧拧右拧拧，<strong>哪边</strong>能让电流计更偏向正确的一侧就往哪边拧<strong>一点</strong>），罗森布拉特发现对于字母识别二分类任务，这组器件总能达到明显高于 50% —— 不是瞎猜 —— 的正确率。尽管 1969 年，马文·明斯基（Marvin Minsky）和西摩尔·帕珀特（Seymour Papert）在《感知机》一书中证明了单层感知机（Single Layer Perceptron, SLP）只能解决线性可分问题（比如无法解决 XOR 问题），Mark I 感知机的出现仍然是证明了 AI 可行的一个重要里程碑。</p><h3 id="multi-layer-perceptron-mlp">多层感知机（Multi Layer Perceptron, MLP）</h3><p>接下来是理论研究：</p><p>1989 年 George Cybenko 和 1991 年 Kurt Hornik 等人通过泛函分析严格证明了：只要 MLP 包含至少一个具有足够多神经元的隐藏层，并使用合适的非线性激活函数，它就可以以任意精度逼近任何连续函数。这个定理在数学上证明了 MLP 的潜力：即 MLP 可以拟合任何函数。</p><h4 id="">函数</h4><p>但等等，为什么是函数？AI 看起来并不是随便画的函数图像啊？</p><p>要回答这个问题，我们可以想想 AI 的特征：每个 AI 都可以被抽象为在特定的<strong>输入</strong>下，求得<strong>输出</strong>的方法，即输入与输出的<em>映射关系</em>。高中数学告诉我们，函数就是定义域到值域的映射关系。换言之，设计一个 AI，除了解决<strong>输入（预处理）</strong>和<strong>输出（后处理）</strong>问题，唯一需要做的事情就是找到那个符合我们要求的函数。怎么找？基于精确代码的符号主义知识库专家系统给出了解析式，由此诞生了入机感十足但确实算 AI 的“上个年代的人工智能”。而不追求可解释性，追求暴力美学的数值法，交出了训练 MLP 的答案。</p><h4 id="-backpropagation-bp">反向传播 （Backpropagation, BP）</h4><p>怎么训练？首先你要有数据。仍然是高中数学，使用统计学方法可以从给定的数据中找出变量间的关系，而在 MLP 里，我们初始化（怎么初始化？你就权当是随机吧；这个话题单开一篇文章都讲不完，暂且按下不表）一组参数后，便得到了一个——你别管它能输出啥——模型：只要输入数据，就能输出答案。现在我们唯一要做的就是把它输出的答案变成正确的。</p><p>你怎么知道输出的答案正确与否？为了用上数学方法，我们不能肉眼盯着每个输出噪声——会瞎的，信我。我们需要另外一个<strong>损失函数</strong>，输入模型的输出（<strong>预测输出</strong>）和应当的输出（<strong>Ground Truth</strong>），输出他们的差异（<strong>损失，Loss</strong>）。怎么设计？你就权当是做差吧，这不重要（仍然写不下）。</p><p>接下来就很简单了——对每组数据，我们都希望损失尽可能小。</p><p>怎么办？仍然是高中数学：求导。不过这里不太一样：具体来说，我们对模型的每个参数求偏导，然后根据它（按<strong>学习率</strong>）稍微动一点每个参数（<strong>梯度下降</strong>），再换一组数据重复上述流程。学习率决定了每次动多少——动多了训练更快但容易<strong>过拟合</strong>（失去泛化能力，针对训练数据效果好但对没见过的数据抓瞎）；动少了训练很慢浪费算力。不断调整参数，我们就能逼近我们想要的函数。</p><p>当然，实际情况比这复杂得多——模型不一定只是 MLP，损失函数可能是另一个模型，梯度下降不一定只按这组数据的梯度，学习率可能是动态的，甚至为了找出模型的具体样子（<strong>超参数</strong>），我们可能先随机（<strong>蒙特卡洛搜索</strong>）试不同的搭配……加之随机性，训练模型甚至被戏称为炼丹。但这些都不重要。重要的是，我们已经有了一个<strong>模型</strong>，现在我们可以往上安输入输出了。</p><blockquote><p>尽管我预言，未来的大模型会像现在的网站开发一样，变成人人皆可训练的基础元件，但现在，还是让我们看看别人做过些什么吧。</p></blockquote>
<h2 id="llmlarge-language-models-">LLM（Large Language Models，大语言模型） 哪儿来的</h2><h3 id="llm-">LLM 的出现</h3><p>LLM 的出现是标志着 AI 从专用组件（<strong>垂直领域</strong>）变成通用智能的另一个重要里程碑。得益于<del>廉价劳动力</del>积累的大规模数据和<del>老黄发力的</del>算力提升，我们得以尝试将训练数据扩大到史无前例的规模。当规模达到艾伦·图灵（Alan Turing）在他 1950 年的著名论文 《Computing Machinery and Intelligence》（提出图灵测试的那篇）中预测实现能通过“模仿游戏”的机器智能需要的十亿（10^9）级时，我们得到了轰动一时的 GPT-3.5。事实上这个说法并不完全准确：GPT-3.5 的参数量还要高上一个数量级，而且也没有那么聪明。但图灵的预言惊人地准在于：如今能对话的 LLM，的确至少得是十亿级的参数量。我们姑且当他是浪漫的巧合吧~</p><p>然而，OpenAI 带来的不止是 GPT。之所以闭源模型且自称 “Open”，是因为他们公开的论文（Scaling Law, Kaplan et al. 2020）证明了这条路是可行的：于是 LLM 开始井喷。</p><h3 id="llm-">LLM 的发展</h3><p>这个章节的后半部分有点 SOTA (<a href="https://openrouter.ai/state-of-ai">State of the AI</a>) 的感觉（时效性很强），因此我全删了。有兴趣的可以翻翻 <a href="http://artificialanalysis.ai">Artificial Analysis</a> 的评测之类的东西获取最新信息；我们这里就讲点“历史”故事吧。</p><h4 id="-llm">闭源 LLM</h4><p>OpenAI 尽管自诩 “Open”，但实际上从来没有开放过自家商业模型的权重；后来推出的 oss 性能仍与商业模型相差甚远，尤其是持续迭代的当下，开场即终章的 oss 系列根本无法与十天半个月出新的开源系列竞争。然而，OpenAI 也确实做到了某种意义上的 “Open”：不同于传统学界的惯用做法，他们开放了自己的论文，任何人，只要能访问互联网，都可以合规地获取阅读Scaling Law 的论文。尽管这并没有什么用：有财力训练大模型的人不差订阅期刊的钱；而普通人也并不需要读（可能也不一定读得懂）这些论文。然而，事实上的确在此之后，LLM开始井喷：如果 OpenAI 将其作为商业机密，开源复刻或许不会那么快；但大家看到了可信可复现的数据，证明到达一定的规模模型便可产生通用智能，于是大家敢于投入资源，很快便有了<del>性能拉胯的
Bard</del> (大换血后改名 Gemini)和我们接下来的主角：LLaMA (Large Language Model Meta AI)。</p><h4 id="-llm">开源 LLM</h4><p>首先澄清一点，开源 LLM 并不是从数据到检查点到模型的全套开源，而是指模型的最终状态开放使用。唯一开放过中间检查点的是小红书的 dots.llm，但过拟合了没人用；指令微调数据则大多是商业机密。</p><p>OpenAI 的 ChatGPT 推出后，Meta 仰仗雄厚的财力迅速跟进，开源（权重）了 LLaMA (1), 2, 3, 然后 4 就亖了（也许是因为杨立昆（Yann LeCun，法国人）推崇“更好的算法”结果分散了精力导致应用体验不佳？）。</p><p>LLaMA 2 仍不支持中文，社区少量后训练微调效果也没有严谨验证，因此尽管 LLaMA 已成开源界基线，国内却鲜见应用；中国的大模型大多从开源起步，加之本土数据优势，同时期的 Qwen 一经推出便迅速吸纳了大量用户；诸如 DeepSeek 的低价策略甚至反向对海外市场造成了不小的冲击（虽然笔者看着 DeepSeek 母公司幻方量化庞大的 A100 保有量以及业务性质仍然对当时相比智谱阿里等并无明显优势的 V3 系列对金融市场造成的动荡的根因表示怀疑，毕竟狂吹的比如 MoE 技术“创新”法国的 Mistral 早就推出过了）。</p><p>同时期：</p><ul><li>专注节省存储便于本地部署的llama.cpp系列兴起，并受到了广大<em>玩家</em>欢迎</li></ul><p>这里特别强调玩家是因为llama.cpp系列推理框架并发性能和计算效率相比生产级推理框架相距甚远。</p><p>本地部署AI模型这条路大概这样：</p>
<table><thead><tr><th>模型类型</th><th>优势</th><th>劣势</th></tr></thead><tbody><tr><td>LLM</td><td>终于不用给 API Provider 交钱了！</td><td>想部署旗舰级模型，要花的初始资金可能超乎你想象；不管是最新一代还是淘洋垃圾，稍微算算TCO（总拥有成本）就知道这根本划不来</td></tr><tr><td>LLM</td><td>但如果你有非常强的隐私/数据不上云需求（以及相配套的经费），这是唯一可行的方案</td><td>模型更新以运维也很烦，大概率需要专人/大量精力维护；而且 Transformer 的特性决定了想摊薄成本必须靠多请求并发；何况现在为了进一步摊薄并发成本的 MoE 模型已经占据市场主流</td></tr><tr><td>LLM(llama.cpp-like)</td><td>消费级硬件就能玩得转</td><td>就像上面说的那样，只能当玩具</td></tr><tr><td>SLM(Small Language Model, 参数量相当小的LLM)</td><td>消费级硬件就能推理性能不错</td><td>模型太小，也只能当玩具</td></tr><tr><td>以 LLM 为 Backbone 的其他模型</td><td>一般 Backbone 其实是个 SLM，闲置显存够的话体验会不错，用上 LLM Backbone 之后效果通常也不错</td><td>随着模型变大、效果变好，迟早会有API Provider，本地部署仍然没优势</td></tr><tr><td>传统的模型</td><td>如果你要调试/研究模型的话，本地几乎是必须的——如果你colab就够用，那当我没说</td><td>没用上 LLM Backbone 的模型几乎都是愁论文的学生的目标，很快你那就不是 SOTA（State Of The Art, 当前表现最好的（模型）） 了</td></tr></tbody></table><ul><li>有 AI 界的 GitHub 之称的 Hugging Face 惨遭众所周知——HF 官方甚至还在第一时间检查了自己的基础设施，但像 Space 这种免备案可交互网页托管是无论如何都无法合规化的。尽管如此，Space 提供的从模型到应用的转变，仍然是基础模型取得突破后，我们下一节的主角——</li></ul><h4 id="">聊天应用</h4><p>基础模型只能用于概率预测补全，而想要与之交互就要明确：我要补全的是<strong>对话</strong>内容（<code>-chat</code>）；更进一步说，即<strong>指令</strong>微调（<code>-instruct</code>）。这一时期，AI 的应用以聊天工具为主，且由于模型仍然不大，存储的事实知识有限，因而更偏向于非事实性问答；然而，许多人没有意识到这一点，因此当越来越多人踩了坑之后，<strong>幻觉</strong>这一不明觉厉的名词诞生了。进而，我们尝试改进，例如强制先搜索相关知识，然后注入上下文再由 LLM 生成回答（RAG）。这一时期，我们看到 LangChain 等框架的涌现，然而过度的抽象化导致开发成本不降反升。据笔者观察，如今仍在使用此类框架的，更多是当时唯一选择的存量代码，而非刻意的技术选型。尽管如此，这仍标志着新的产品形态的诞生：智能体。</p><h4 id="">智能体（早期）</h4><p>早期的智能体我泛指所有强制进行上下文注入，而非提供裸模型的问答的对话应用。此时智能体尚未脱离对话这一交互形式，但系统提示词里的工作流动态的上下文注入已使其脱离了纯概率预测的范畴，进而催生了一波提示词工程热（卖课的赚到了 AI 的第一桶金）。然而强制的上下文注入仍然使得模型并无法自主选择所要查询的内容；很多时候模型甚至难以区分自己的上下文是查询到的还是用户指定的抑或是系统提示词，并由此导致了提示词注入攻击等草台班子问题。因此我们希望将工具调用的决策权卸载给模型，而这随着模型对原生工具调用的支持逐渐普及得以实现；这进一步使得 LangChain 等智能体框架的实际应用意义下降。然而工具调用本身却仍没有一个统一的标准，直到模型上下文协议得到广泛应用，标志着协议层面的通信设计日趋成熟；压力给到工具的设计本身。</p><h4 id="agentic">Agentic</h4><p>如今 开源的 闭源的 Lock in 的 BYOK 的 智能体工具一大堆，本地的 上云的 执行环境数不胜数；商业化的兴起反过来印证了一个事实：AI已经从玩具，走向真正的生产力工具了。</p><p>随之而来的便是焦虑：如果 AI 把我干的事情都干了，那我干啥？这个问题我已经听到过无数个版本了；共性是：都是从财经媒体/博流量的自媒体，或是各种“别人”的角度，刻意制造的焦虑，而非亲身体验。</p><p>答案也很简单：</p><ol start="1"><li>它没这能力。硬要佐证的话，AA（Artificial Analysis） 的 Omniscience Hallucination Rate 和 Accuracy 几乎是正相关可以算一个——模式匹配的本质使得无关模型大小，训练数据的设计就决定了越敢于自主决策的模型越不知道自己在干嘛；</li><li>它没你便宜。很讽刺的一件事，AI 原本是为了替代人类工作而发明的；而如果用 AI 工作反而更贵，在动物保护组织保护牛保护马不保护牛马的当下，为什么还要用 AI 呢？然而事实如此：即使在战略要求低价吸引用户的当下，比肩高级程序员的 AI 也将向你要求比肩高级程序员的开支。那么，在流程、合规性、版权等方面仍有隐性成本的背景下，何不雇用体制成熟的人类员工呢？因此听到 Anthropic 呼吁暂停 AI 研发时，<em>我的内心毫无波澜，甚至有一丝想笑</em>：如果这话是开源社区提出来的，哪怕是 LeCun 这种致力于下一代架构的，或许还值得认真对待；商业公司？哪有永恒的敌人，只有永恒的利润。</li></ol><blockquote><p>这也就是所谓<em>人口红利</em>可笑的一面：看似人人享福的机遇，实则不过是人人受压迫的大棒之下的甜枣。</p><p>这好吗？因人而异，取决于你的价值选择。至少对博主来说，答案是肯定的。</p></blockquote>
<p>何况，<strong>Agentic 时代，AI的本质是共享经济</strong>。为什么这么说？模型提供商需要你的数据后训练微调，代理工具需要你的使用反馈优化提示词设计，社区需要你的宣传将 AI 工具介绍给更多人...一切数据都会回流到推理服务商手上（何况很多人并不接理论上更隐私的 API 而是用户协议都不看就直接用终端 APP），而主动上交隐私甚至是促进模型迭代的必要条件！不止 Claude Code，所有智能体代理都是相应模型提供商开发的效果最好。有人说这只是因为针对性优化的提示词；但反过来想，这何尝不可能是特调的后训练微调？细思极恐。</p><hr/><p>当然也不必过于悲观，正是在重复劳动逐渐被替代的背景下，我们才得以进一步提升自己的能力；换言之，AI的发展呼吁我们下一章的主角：<strong>新文科</strong></p><h2 id="agent">Agent怎么用</h2><h3 id="">新文科</h3><p>首先叠个甲，我不是文科生，以下是<del>我自己脑子想想的</del>我切实感到自己的局限，展望未来的<strong>个人观点</strong>。</p><p>其次，我们下面讨论的<strong>新文科</strong>，不是教育界现在已经在推的、形式主义化为文科加编程的<em>新文科</em>，而是有重新定义技术的野心，有踏出艺术的勇气的<strong>新</strong>新文科。</p><h4 id="">行业现状</h4><p>这并不是（是？）空穴来风：当 AI 走向应用层的逆转不可阻挡，能力需求的多元化日益显著。拿一个人人都能做，没人能做好的需求举例，提示词工程里，计算机科学背景的以结构化思维和链式推理设计解决基础的<em>智能</em>问题，然而还有一类创意写作、戏剧、心理学背景的以传统工科训练覆盖不到的叙事结构、情感节奏、文化语境能力解决终端用户更看重的<em>语气</em>问题。大量纯工科出身的 AI 产品经理，卡在&quot;技术能做什么&quot;而跳不出&quot;用户为什么需要&quot;；而真正以修辞学/情感色彩差异化的的产品，在基础工作由AI接管的当下反而打出了差异化竞争的王牌。</p><h4 id="">教育对齐</h4><p>既然行业有人才缺口，我们自然把目光投向高等教育。</p><h5 id="ai">AI伦理与治理（最成熟）</h5><p>基于与已有法学伦理学的强关联，这一领域的响应最快、落地最多。例如：</p><table><thead><tr><th>例子</th><th>内容</th></tr></thead><tbody><tr><td>算法公平性与偏见</td><td>训练数据中的<strong>歧视</strong>如何被模型放大</td></tr><tr><td>AI可解释性</td><td>从技术和<strong>哲学</strong>两个维度理解“黑箱”</td></tr><tr><td>AI治理与监管框架</td><td>GDPR、《生成式AI服务管理办法》等的<strong>法理</strong>基础</td></tr><tr><td>价值对齐</td><td>如何让模型目标与人类<strong>价值观</strong>一致</td></tr></tbody></table><p>响应的也往往是头部学府，例如：斯坦福的 CS 181W: Computers, Ethics, and Public Policy、清华的 AI伦理课程、中国政法大学等法学院校的AI与法律模块。</p><h5 id="">数字人文与计算社会科学（正在扩张）</h5><p>这是人文学科“主动拥抱”AI的路径：</p><table><thead><tr><th>例子</th><th>内容</th></tr></thead><tbody><tr><td>计算语言学 / 语料库文学研究</td><td>用NLP方法做文本分析，对古籍、文学进行大规模<strong>量化</strong>研究</td></tr><tr><td>数字历史学</td><td>利用AI<strong>辅助</strong>史料整理、古文识别</td></tr><tr><td>计算传播学</td><td>用大模型<strong>分析</strong>舆论、话语框架</td></tr><tr><td>AI艺术与美学</td><td>利用大模型<strong>创作并评价</strong>艺术</td></tr></tbody></table><p>同样，人大、武大、南大等已有&quot;数字人文&quot;本科方向或硕士项目；国外UCLA、UCL的数字人文项目更是标杆。</p><h5 id="">底层认知重构（最前沿但最小众）</h5><p>这是更深层的对齐——不满足于&quot;AI+某个具体人文学科&quot;，而是追问&quot;大模型改变了人类认知和知识生产的底层逻辑&quot;：</p><table><thead><tr><th>例子</th><th>内容</th></tr></thead><tbody><tr><td>大模型时代的认识论</td><td><strong>知识权威</strong>从人转移到模型意味着什么</td></tr><tr><td>认知科学与AI</td><td>大模型的<strong>认知机制</strong>与人类认知的类比与差异</td></tr><tr><td>媒介哲学与AI</td><td>大模型挤占传统互联网成为新的认知<strong>媒介</strong>意味着什么</td></tr></tbody></table><p>这类课程尽管难以建制化，主要散落在哲学系、认知科学系的专题研讨课中，缺乏系统性的专业设置；但也<strong>最触及人与 AI 关系的核心</strong>。</p><h4 id="">前进阻力</h4><p>然而，更多高校管理层对生成式AI的理解仍停留在“防作弊”层面，而即使有范式变革的远见，也往往面临能贯通技术与人文的双栖师资极度稀缺和学术惯性和行政壁垒导致跨学科课程难以开设的困境。</p><p>更何况在就业压力下，学生的选课策略同样是高度功利的，“AI伦理”不如“Prompt Engineering”好写进简历；人文学科课程在AI热潮中反而容易被边缘化，成为可有可无的点缀。</p><h3 id="">那，我们能做什么呢？</h3><p>仍然叠甲：每个人的习惯不同，适合自己的就是最好的；下面只是分享一下博主自己使用的经验，仅供参考。</p><h4 id="">明确能力边界</h4><p>首先，要认清AI的能力边界。</p><p>简单举几个例子：不要指望没带工具的聊天里给出的信息永远是正确的，不要指望支持光学字符识别的VLM看得出来中文和日文字体的区别，不要指望支持长上下文的模型在几十万行代码之间保留相同的注释风格。</p><p>我不可能在上面枚举出所有技术限制；授人以鱼不如授人以渔，要想在指派任务时反应得过来会不会触及什么底层限制，最好的办法就是对AI的底层机制有一定的了解。例如，知道基于自回归的模型（尤其是不开思考时）不可能根据后面的生成调整前面的回答，基于分词的LLM不可能数得清strawberry里有几个r，诸如此类。</p><h4 id="">分层划定功能</h4><p>其次，要善用别人特调过的工具。我一般分三类：</p><table><thead><tr><th>层级</th><th>用途</th><th>特点</th><th>例子</th></tr></thead><tbody><tr><td>日常聊天</td><td>聊天，问问题，做决策，偶尔干点小活</td><td>了解你的喜好，记忆你的状态，熟悉你的偏好；有丰富的工具，但使用频率不高</td><td>Open WebUI, OpenClaw, 各类聊天软件</td></tr><tr><td>长程辅助</td><td>干苦力，替代重复劳动，用来节约时间</td><td>用不那么贵的长上下文模型，大量工具调用，用着熟悉、舒心</td><td>Claude Code, Codex. Copilot, Open Code, Kilo, 各种特调智能体</td></tr><tr><td>临时问答</td><td>查查资料，确认信息</td><td>通常是网页里免费的AI助手，打开那个平台才会想着用，没了也不碍事儿</td><td>搜索引擎或者文档说明里自带的AI版块</td></tr></tbody></table><h4 id="">承担审阅责任</h4><p>最后，记住：AI会犯错，永远审阅它的结果。如果有大纲或计划之类的东西，读它确保AI理解了你在干什么；如果是代码或文本，不光要能过编译或读得通，你也得看一遍逻辑有没有暗病；如果涉及事实信息，要么同时用多个模型比较回答是不是大差不差，要么自己上网搜索/拿着权威资料确认一遍。</p><h2 id="">结语</h2><p>AI 的故事讲完了，但 AI 的发展仍在继续；生态的日新月异，产品的飞速迭代都要求我们持续学习。谨以此文献给每个愿意听我掰扯的人，我们下篇博文，再见。</p></div><p style="text-align:right"><a href="https://me.bcw2.top:3443/posts/tech/agentic-era-ai-is-sharing-economy#comments">看完了？说点什么呢</a></p></div>]]></description><link>https://me.bcw2.top:3443/posts/tech/agentic-era-ai-is-sharing-economy</link><guid isPermaLink="true">https://me.bcw2.top:3443/posts/tech/agentic-era-ai-is-sharing-economy</guid><dc:creator><![CDATA[bcw222]]></dc:creator><pubDate>Fri, 10 Jul 2026 13:42:16 GMT</pubDate></item><item><title><![CDATA[AI Transparency]]></title><description><![CDATA[<div><blockquote>该渲染由 Shiro API 生成，可能存在排版问题，最佳体验请前往：<a href="https://me.bcw2.top:3443/posts/blog/ai-transparency">https://me.bcw2.top:3443/posts/blog/ai-transparency</a></blockquote><div><p>Gen AI 时代，抗拒AI是愚蠢的，滥用AI是可耻的。为了规范AI使用边界，本博客中的文章可能标有如下标签：</p><ul><li><strong>AI-ASSISTED</strong>：本文间接包含AI生成内容作为人工写作的参考，如果存在错误则即使不用AI博主也会在这里犯错（说不定错的更离谱），可放心食用；</li><li><strong>AI-REFINED</strong>：本文为博主完成草稿后全篇由AI润色的产物，除博主能力所限外还可能存在LLM幻觉导致的错误和文风一股入机味，但应该无伤大雅~</li><li><strong>MAN-MADE</strong>：本文为博主纯手搓；</li><li>目前没有直接包含AI生成内容的计划——想看这种东西为什么不直接把想看的当提示词发给AI让它生成呢？</li></ul><p><strong>博主郑重承诺：我们不制造电子垃圾，任何标签均为如实声明</strong></p></div><p style="text-align:right"><a href="https://me.bcw2.top:3443/posts/blog/ai-transparency#comments">看完了？说点什么呢</a></p></div>]]></description><link>https://me.bcw2.top:3443/posts/blog/ai-transparency</link><guid isPermaLink="true">https://me.bcw2.top:3443/posts/blog/ai-transparency</guid><dc:creator><![CDATA[bcw222]]></dc:creator><pubDate>Wed, 01 Jul 2026 16:45:35 GMT</pubDate></item><item><title><![CDATA[【系统化速查表 第二弹】装命令行工具配环境变量]]></title><description><![CDATA[<div><blockquote>该渲染由 Shiro API 生成，可能存在排版问题，最佳体验请前往：<a href="https://me.bcw2.top:3443/posts/tech/environment-variable-for-command-line-interface">https://me.bcw2.top:3443/posts/tech/environment-variable-for-command-line-interface</a></blockquote><div><h2 id="">环境变量是什么</h2><p><strong>环境变量（Environment Variable）</strong>：操作系统提供给进程的键值对。进程启动时继承父进程的整个环境，可用于配置程序行为、传递路径信息等。常见如 <code>HOME</code>、<code>USER</code>、<code>SHELL</code>、<code>LANG</code>。</p><p><strong>PATH</strong>：最核心的环境变量，告诉 Shell 在哪些目录中<strong>按顺序</strong>搜索可执行文件。敲一条命令时，Shell 挨个扫 PATH 里的目录——先找到的就执行，后面的不再查。</p><p><strong>动态库搜索路径</strong>：程序运行时查找共享库（Linux <code>.so</code>、macOS <code>.dylib</code>、Windows <code>.dll</code>）的机制。逻辑与 PATH 类似但<strong>机制因平台差异极大</strong>，是最容易踩跨平台坑的领域。</p>
<h2 id="">我什么时候会用到它</h2><p>抛开应用程序主动用它传配置不谈，最常用的场景就是手动安装命令行工具。处于某些原因我们不想用包管理器或安装器安装程序时（它们通常会帮你配置好环境变量）而是直接下载一个压缩包的可执行文件使用时，我们要告诉 Shell 在哪里找到我们下载的可执行文件，即<strong>配置环境</strong>。通常情况下，只需要将可执行文件所在的目录添加到 <code>PATH</code> 环境变量里即可。那么，怎么添加？——</p><h2 id="">怎么读</h2><p>环境变量的读取范围分四层，搞不清这个是&quot;设了但不生效&quot;的第一大原因：</p><table><thead><tr><th> 层级 </th><th> 覆盖范围 </th><th> 典型用途 </th><th> 生效时机 </th></tr></thead><tbody><tr><td> <strong>系统级</strong> </td><td> 所有用户、所有进程 </td><td> 公共工具路径、系统级配置 </td><td> 需重启/新登录/广播通知 </td></tr><tr><td> <strong>用户级</strong> </td><td> 单个用户的所有进程 </td><td> 用户信息、个人工具 </td><td> 需新开 Shell 或新登录 </td></tr><tr><td> <strong>Shell级</strong> </td><td> 当前 Shell 及其子进程 </td><td> 临时测试 </td><td> 对当前 Shell 接下来的所有操作生效，关闭 Shell 即消失 </td></tr><tr><td> <strong>进程级</strong> </td><td> 仅当前运行命令 </td><td> 一次性操作 </td><td> 对当前运行命令生效，当前命令退出即消失 </td></tr></tbody></table><p>同名变量在多层设置时，层级越窄优先级越高： 进程级 &gt; Shell级 &gt; 用户级 &gt; 系统级）</p><table><thead><tr><th style="text-align:center"> 层级 </th><th style="text-align:center"> Linux/macOS bash/zsh </th><th style="text-align:center"> Windows CMD/PowerShell </th></tr></thead><tbody><tr><td style="text-align:center"> 系统 </td><td style="text-align:center"> <code>/etc/</code> 下写文件 </td><td style="text-align:center"> <code>setx /M</code> 或面板 </td></tr><tr><td style="text-align:center"> 用户 </td><td style="text-align:center"> <code>~/</code> 下写文件 </td><td style="text-align:center"> <code>setx</code> 或面板 </td></tr><tr><td style="text-align:center"> Shell </td><td style="text-align:center"> <code>export</code> </td><td style="text-align:center"> <code>set</code> / <code>$env:</code> </td></tr><tr><td style="text-align:center"> 进程 </td><td style="text-align:center"> <code>VAR=val cmd</code> </td><td style="text-align:center"> <strong>不支持</strong> </td></tr></tbody></table><p>这里的行为，与其说是平台特定，不如说是 Shell 特定：比如，如果你在 Windows 上用 Bash，那其实更应该参考 Linux 的设置。</p><p>为什么写文件可以修改环境变量？这是 Shell 的设计机制：Shell 启动时会按一定规则运行特定的初始化脚本，而作为所有命令的入口，所有的 Shell 都设置了的环境，相当于就是整个用户/系统的环境。</p><p>特例：</p><ul><li><strong>Linux</strong> <code>/etc/environment</code> 是用户登录时由 PAM 读取的，格式为 <code>KEY=VALUE</code></li><li><strong>macOS</strong> <code>/etc/paths</code> 只管 <code>$PATH</code>，每行一个路径，不是 <code>KEY=VALUE</code> 格式；<code>/etc/paths.d/*</code> 同理（每个文件一行一个路径）</li></ul><h3 id="shell--login--non-loginlinuxmacos-">Shell 的 Login / Non-login——Linux/macOS 最大的坑</h3><p>Shell 启动方式决定它读取哪些配置文件，这直接决定你的环境变量在哪个文件里写才能生效：</p><table><thead><tr><th> </th><th> Login Shell </th><th> Non-login Shell </th></tr></thead><tbody><tr><td> <strong>Interactive</strong> </td><td> SSH 登录、<code>su -</code>、<strong>macOS Terminal.app 默认</strong> </td><td> <strong>Linux GUI 终端默认</strong>、<code>su</code> </td></tr><tr><td> <strong>Non-interactive</strong> </td><td> <code>bash -l script.sh</code> </td><td> <code>bash script.sh</code>、cron 任务 </td></tr></tbody></table><h3 id="bash-">bash 读取顺序</h3><table><thead><tr><th> Shell 类型 </th><th> 读取文件 </th></tr></thead><tbody><tr><td> Login </td><td> <code>/etc/profile</code> → (<code>~/.bash_profile</code> &gt; <code>~/.bash_login</code> &gt; <code>~/.profile</code>)（只读找到的第一个） </td></tr><tr><td> Interactive non-login </td><td> <code>/etc/bash.bashrc</code> → <code>~/.bashrc</code> </td></tr><tr><td> Non-interactive </td><td> 读取 <code>$BASH_ENV</code> 指定的文件（通常不设此变量） </td></tr></tbody></table><p>bash 的 Login Shell 优先级列表 <code>~/.bash_profile</code> &gt; <code>~/.bash_login</code> &gt; <code>~/.profile</code> 诞生于 Unix 历史的“兼容期”，它是为了解决<strong>多流派 Shell 用户的迁移问题</strong>，而非让你把三个文件都用上。</p><ol start="1"><li><strong><code>~/.profile</code> 是 Bourne Shell (<code>sh</code>) 的老祖宗标准</strong>：
在当年，所有 Unix 系统的默认 Shell 都是 <code>sh</code>，大家都把环境变量写在 <code>~/.profile</code> 里。直到今天，如果你用的是 Ubuntu，它的底层系统默认 Shell 是 <code>dash</code>，<code>dash</code> 启动时依然只认 <code>~/.profile</code>。</li><li><strong><code>~/.bash_login</code> 是对 C Shell (<code>csh</code>) 的妥协</strong>：当年有一大批写 <code>~/.login</code> 的 csh 用户转投 Bash，为了照顾他们的习惯，Bash 加了这个支持。</li><li><strong><code>~/.bash_profile</code> 是 Bash 的“亲儿子”</strong>：Bash 诞生增加了很多 <code>sh</code> 不支持的高级语法（比如数组、特殊的字符串处理）。如果用户把这些高级语法写进 <code>~/.profile</code>，当系统其他组件用 <code>sh</code> 或 <code>dash</code> 读取这个文件时，就会<strong>直接报错</strong>。</li></ol><p><strong>为什么设计为互斥隔离（只读找到的第一个）？</strong>
如果 Bash 依次把这三个全读一遍，往往会引发灾难（比如环境变量 <code>PATH=...:$PATH</code> 被重复追加三次、打印了三次登录欢迎语、或者因为语法不兼容报错）。在 Bash 开发者的视角里：</p><blockquote><p>如果你写了专属的 <code>.bash_profile</code>，我就按你的来；如果你没写，我就去看你是不是习惯用 <code>.bash_login</code>；如果你连这都没写，那我就去读最普通的 <code>.profile</code> 保底。千万别读两次。</p></blockquote>
<p>但这导致了一个巨大的痛点：<strong>用户必须在 login 和 non-login 两个文件里维护两套配置</strong>。为了解决这个问题，现代的 Linux 发行版（如 Ubuntu, CentOS, Debian）在默认给用户生成家目录（从 <code>/etc/skel/</code> 拷贝）时，动了一个非常聪明的“手脚”——<strong>强制桥接</strong>。现代发行版真实的 Login Shell 读取顺序变成了一个<strong>嵌套链条</strong>：</p><ol start="1"><li><strong>bash</strong> 加载 <code>/etc/profile</code> -&gt; 遍历并 <code>source /etc/profile.d/*.sh</code> （用于配置系统级环境变量）和<strong>去读</strong> <code>/etc/bash.bashrc</code></li><li><strong>bash</strong> 去找并读取 (<code>~/.bash_profile</code> &gt; <code>~/.bash_login</code> &gt; <code>~/.profile</code>) -&gt; <strong>去读</strong> <code>~/.bashrc</code></li></ol><p>然而，想象如下情景：</p><ol start="1"><li>系统默认只有 <code>~/.profile</code>（里面包含了调用 <code>bashrc</code> 的桥接代码）。</li><li>你为了配置环境变量，上网随便搜了一篇教程，自己新建了一个 <code>~/.bash_profile</code> 文件并在里面写了 <code>export PATH=...</code>。</li><li><strong>灾难发生</strong>：由于 Bash 的优先级是 <code>~/.bash_profile</code> &gt; <code>~/.profile</code>，你的 Login Shell 发现了你新建的文件，就不会再去读系统的 <code>~/.profile</code> 了。</li><li><strong>结果</strong>：你新建的文件里没有那段“桥接代码”，导致你的 <code>~/.bashrc</code> 彻底被 Login Shell 抛弃，以前在 <code>bashrc</code> 里配的所有别名（alias）和变量在 SSH 登录时全部失效。</li></ol><p>因此，上网查教程时要特别小心，<strong>尤其是你发现它在用<code>&gt;&gt;</code>（流追加符）这种看似无害的东西修改配置文件时</strong>，因为你永远不知道它究竟是简单的追加还是默默新建了一个优先级更高的配置。</p>
<h3 id="zsh-macos-catalina-">zsh 读取顺序（macOS Catalina+ 默认）</h3><table><thead><tr><th> Shell 类型 </th><th> 读取文件 </th></tr></thead><tbody><tr><td> Login </td><td> <code>zshenv</code> → <code>zprofile</code> → <code>zshrc</code> → <code>zlogin</code>（系统+用户各一套） </td></tr><tr><td> Interactive non-login </td><td> <code>zshenv</code> → <code>zshrc</code> </td></tr><tr><td> Non-interactive </td><td> <code>zshenv</code> </td></tr></tbody></table><p>zsh 无论是否 login，<strong>都读</strong> <code>~/.zshrc</code>，所以 zsh 下环境变量只需放在 <code>~/.zshrc</code> 就够了——比 bash 简单得多。</p><hr/><h2 id="">怎么写</h2><h3 id="windows">Windows</h3><table><thead><tr><th> 层级 </th><th> 设置方法 </th><th> 写入位置 </th><th> 注意 </th></tr></thead><tbody><tr><td> <strong>系统级</strong> </td><td> <code>setx /M VAR &quot;value&quot;</code>（需管理员权限）<br/>或：系统属性 → 高级 → 环境变量 → 系统变量 </td><td> 注册表 <code>HKLM\SYSTEM\CurrentControlSet\Control\Session Manager\Environment</code> </td><td> 已运行的程序不受影响，需重启或新开程序 </td></tr><tr><td> <strong>用户级</strong> </td><td> <code>setx VAR &quot;value&quot;</code><br/>或：系统属性 → 高级 → 环境变量 → 用户变量 </td><td> 注册表 <code>HKCU\Environment</code> </td><td> 当前 cmd 窗口不生效，新开窗口才生效 </td></tr><tr><td> <strong>Shell级</strong> </td><td> cmd: <code>set KEY=value</code><br/>PowerShell: <code>$env:KEY = &quot;value&quot;</code> </td><td> 仅当前进程内存 </td><td> 关闭 Shell 即消失</td></tr></tbody></table><p>Windows GUI 程序改完环境变量后不会自动感知，需要重启该程序。虽然系统会广播 <code>WM_SETTINGCHANGE</code> 消息，但<strong>很多程序忽略此消息</strong>。可能可以通过重启程序或重新登录生效，但既然都是 Windows 了，最可靠的方式还是<strong>重启系统</strong>。</p><hr/><h3 id="linux">Linux</h3><table><thead><tr><th> 层级 </th><th> 文件 </th><th> 格式 </th><th> 说明 </th></tr></thead><tbody><tr><td> <strong>系统级</strong> </td><td> <code>/etc/environment</code> </td><td> <code>KEY=VALUE</code>（纯键值对） </td><td> PAM 读取，<strong>不能用 Shell 语法</strong>（不能写 <code>export</code>、不能 <code>$VAR</code> 引用），对所有用户所有 Shell 生效 </td></tr><tr><td> </td><td> <code>/etc/profile</code> </td><td> Shell 脚本 </td><td> login shell 时读取 </td></tr><tr><td> </td><td> <code>/etc/profile.d/*.sh</code> </td><td> Shell 脚本 </td><td> <code>/etc/profile</code> 自动 source 此目录下所有 <code>.sh</code>——<strong>加变量新建文件就可以，最好不要直接改 <code>/etc/profile</code></strong> </td></tr><tr><td> <strong>用户级</strong> </td><td> <code>~/.profile</code> </td><td> Shell 脚本 </td><td> login shell 时读取（被 bash/zsh 专属文件优先取代） </td></tr><tr><td> </td><td> <code>~/.bash_profile</code> </td><td> Shell 脚本 </td><td> bash login shell 优先读此文件，有它就不读 <code>~/.profile</code> </td></tr><tr><td> </td><td> <code>~/.bashrc</code> </td><td> Shell 脚本 </td><td> bash interactive non-login 时读取 </td></tr><tr><td> </td><td> <code>~/.zshrc</code> </td><td> Shell 脚本 </td><td> zsh interactive <strong>总是读取</strong>——zsh 下放变量的首选位置 </td></tr><tr><td> <strong>Shell级</strong> </td><td> 直接执行 <code>export VAR=value</code> </td><td> Shell 命令 </td><td> 即时生效，关闭即失 </td></tr></tbody></table>
<h5 id="-etcprofile--etcprofiled">为什么有 <code>/etc/profile</code> 还要 <code>/etc/profile.d/</code>？</h5><p>系统包更新可能覆盖 <code>/etc/profile</code> ，但 <code>/etc/profile.d/</code> 下的文件独立存在不受影响。<code>/etc/</code> 下很多配置其实都有 <code>xxx</code> 和 <code>xxx.d/</code> ——以便分离默认配置和用户配置。</p><h5 id="gui--bashrc">GUI 应用（从桌面环境启动的）<strong>不读 <code>~/.bashrc</code></strong>。</h5><p>它们的进程由 display manager (如 GDM) 创建，继承的是 systemd 用户会话环境。给 GUI 应用设环境变量的正确方式：</p><pre class="language-ini lang-ini"><code class="language-ini lang-ini"># ~/.config/environment.d/my-vars.conf  (systemd 用户环境)
JAVA_HOME=/usr/lib/jvm/java-11
PATH=/home/you/.local/bin:${PATH}
</code></pre><p>此目录下的 <code>.conf</code> 文件格式同 <code>/etc/environment</code>（纯 <code>KEY=VALUE</code>），由 systemd-user 启动时读取，仅对 GUI 应用生效。</p><hr/><h3 id="macos">macOS</h3><table><thead><tr><th> 层级 </th><th> 文件/机制 </th><th> 说明 </th></tr></thead><tbody><tr><td> <strong>系统级</strong> </td><td> <code>/etc/paths</code> </td><td> PATH 专用，每行一个目录（macOS 独有机制） </td></tr><tr><td> </td><td> <code>/etc/paths.d/*</code> </td><td> PATH 模块化扩展，每文件每行一个目录 </td></tr><tr><td> </td><td> <code>/etc/profile</code> → <code>/etc/profile.d/*.sh</code> </td><td> 其他环境变量（同 Linux） </td></tr><tr><td> </td><td> <code>/etc/zshrc</code>、<code>/etc/zprofile</code> </td><td> zsh 系统级配置 </td></tr><tr><td> <strong>用户级</strong> </td><td> <code>~/.zshrc</code> </td><td> zsh interactive <strong>总被读取</strong>——放环境变量的首选位置 </td></tr><tr><td> </td><td> <code>~/.zprofile</code> </td><td> zsh login shell 读取（Terminal.app 默认是 login shell） </td></tr><tr><td> <strong>Shell级</strong> </td><td> <code>export VAR=value</code> </td><td> 即时生效，关闭即失 </td></tr></tbody></table><h5 id="macos-catalina-1015--shell--bash--zsh">macOS Catalina (10.15) 起<strong>默认 Shell 从 bash 改为 zsh</strong>。</h5><p>旧版升级后，<code>~/.bash_profile</code> 里的配置在新终端中<strong>完全不生效</strong>。迁移：把 bash 配置搬到 <code>~/.zprofile</code>（login 级）+ <code>~/.zshrc</code>（interactive 级）。</p><h5 id="macos--path-">macOS 的 <strong><code>PATH</code> 机制独特</strong>。</h5><p>不在 <code>/etc/environment</code> 里（此文件不存在），而是用 <code>/etc/paths</code> + <code>/etc/paths.d/</code>。加系统级 PATH 目录的正确做法：</p><pre class="language-bash lang-bash"><code class="language-bash lang-bash"># 加一个文件到 /etc/paths.d/
sudo tee /etc/paths.d/homebrew &lt;&lt;EOF
/opt/homebrew/bin
EOF
</code></pre><p>注意这里用 <code>tee</code> 而非流重定向符，是因为 <code>sudo</code> 只管命令运行权限，而流是由 Shell 处理的——用 <code>sudo</code> 不就是因为 Shell 权限不够嘛。你要用 <code>sudo sh -c &#x27;echo /opt/homebrew/bin &gt;&gt; /etc/paths.d/homebrew&#x27;</code> 也是一样的。</p><h5 id="gui--docklaunchpad--shell-">GUI 应用（从 Dock/Launchpad 启动）<strong>不继承 Shell 环境变量</strong>。</h5><p>macOS 的 GUI 应用由 <code>launchd</code> 启动，<code>launchd</code> 的环境来自系统级配置，不读 <code>~/.zshrc</code>。给 GUI 应用设环境变量的正确方式：</p><pre class="language-zsh lang-zsh"><code class="language-zsh lang-zsh">launchctl setenv KEY value
launchctl unsetenv KEY
</code></pre>
<hr/><h2 id="path-">PATH 设置最佳实践</h2><table><thead><tr><th> 原则 </th><th> 为什么 </th></tr></thead><tbody><tr><td> <strong>层级越窄越好</strong> </td><td> 个人工具放用户级 PATH，别放系统级影响所有人 </td></tr><tr><td> <strong>追加而非覆盖</strong> </td><td> 设 PATH 时务必保留原有：<code>$PATH:newdir</code>（Linux/macOS）或 <code>%PATH%;newdir</code>（Windows cmd） </td></tr><tr><td> <strong>顺序就是优先级</strong> </td><td> PATH 中靠前的目录先匹配。<code>~/bin</code> 里有个同名程序放在 PATH 前面就会覆盖系统的——可能是你想要的，也可能是灾难 </td></tr><tr><td> <strong>永远不放 <code>.</code>（当前目录）</strong> </td><td> 安全风险：某恶意目录下放个 <code>ls</code>，你 cd 进去敲 <code>ls</code> 就执行了恶意程序。Windows 早期默认把 <code>.</code> 放 PATH 最前面，这是历史安全漏洞 </td></tr><tr><td> <strong>Linux/macOS 个人工具放 <code>~/.local/bin</code></strong> </td><td> XDG 标准目录，现代发行版已默认将其加入 PATH。旧惯例 <code>~/bin</code> 也能用但不是标准 </td></tr></tbody></table><h3 id="-path-">各平台追加 PATH 的安全写法</h3><pre class="language-bash lang-bash"><code class="language-bash lang-bash"># Linux/macOS — 在 ~/.bashrc 或 ~/.zshrc 中
export PATH=&quot;$HOME/.local/bin:$PATH&quot;    # 前置（优先使用个人版本）
export PATH=&quot;$PATH:/opt/some-tool/bin&quot;  # 后置（系统版本优先，仅补充）
</code></pre><pre class="language-powershell lang-powershell"><code class="language-powershell lang-powershell"># Windows PowerShell — 用户级永久追加
[Environment]::SetEnvironmentVariable(
    &quot;PATH&quot;,
    [Environment]::GetEnvironmentVariable(&quot;PATH&quot;, &quot;User&quot;) + &quot;;C:\Tools\Bin&quot;,
    &quot;User&quot;
)
</code></pre>
<hr/><h2 id="">动态库搜索——跨平台差异最大的领域</h2><h3 id="">三平台机制速览</h3><table><thead><tr><th> </th><th> Windows </th><th> Linux </th><th> macOS </th></tr></thead><tbody><tr><td> <strong>库文件格式</strong> </td><td> <code>.dll</code> </td><td> <code>.so</code> </td><td> <code>.dylib</code> / <code>.framework</code> </td></tr><tr><td> <strong>环境变量</strong> </td><td> <code>PATH</code>（<strong>兼管 exe 和 DLL</strong>） </td><td> <code>LD_LIBRARY_PATH</code> </td><td> <code>DYLD_LIBRARY_PATH</code> / <code>DYLD_FRAMEWORK_PATH</code> </td></tr><tr><td> <strong>系统级配置</strong> </td><td> PATH 系统变量 </td><td> <code>/etc/ld.so.conf</code> + <code>ldconfig</code> </td><td> 无直接等效（用编译时嵌入路径） </td></tr><tr><td> <strong>编译时嵌入</strong> </td><td> 无标准机制 </td><td> <code>-Wl,-rpath,/dir</code> </td><td> <code>@rpath</code> / <code>@loader_path</code> / <code>@executable_path</code> </td></tr><tr><td> <strong>设计哲学</strong> </td><td> 搜索顺序固定，应用目录优先 </td><td> ldconfig 缓存为主，环境变量为辅 </td><td> <strong>路径编进库本身</strong>，不依赖环境变量 </td></tr></tbody></table><hr/><h3 id="windows-dll-">Windows DLL 搜索顺序</h3><h5 id="windows--path--dll-">Windows 的 PATH <strong>同时管可执行文件搜索和 DLL 搜索</strong>.</h5><p>这是独一无二的：<code>PATH</code> 里加个目录，不光影响命令查找，还影响 DLL 加载。<code>PATH</code> 越宽，DLL 搜索范围也越宽，可能加载到错误版本 DLL（DLL Hell 的根源）。这也是为什么 Windows 下按个 <code>Tab</code> 会冒出来一堆不能直接运行的<code>.dll</code>.</p><h5 id="safedllsearchmode-windows-vista-"><strong>SafeDllSearchMode 开启时</strong>（Windows Vista+ 默认）：</h5><table><thead><tr><th> 顺序 </th><th> 搜索位置 </th></tr></thead><tbody><tr><td> 1 </td><td> <strong>应用程序所在目录</strong>（最高优先——Windows 避免 DLL Hell 的主要机制） </td></tr><tr><td> 2 </td><td> 系统目录 <code>C:\Windows\System32</code> </td></tr><tr><td> 3 </td><td> 16位系统目录（历史遗留） </td></tr><tr><td> 4 </td><td> Windows 目录 <code>C:\Windows</code> </td></tr><tr><td> 5 </td><td> PATH 中的目录 </td></tr><tr><td> 6 </td><td> 当前工作目录 </td></tr></tbody></table><h5 id="safedllsearchmode--path-">SafeDllSearchMode 关闭时（仅旧系统），当前目录排在 PATH 前面——这是安全隐患。</h5><p><strong>最佳实践</strong>：DLL 放在应用程序自己的目录下——这是搜索顺序第一位，最可靠且不影响其他程序。<strong>尽量不要依赖 PATH 来找 DLL</strong>。
这也就是为什么 Windows 下 FFmpeg 那么容易出问题：FFmpeg 的协议使其经常没法再分发，只能手动补齐。</p><hr/><h3 id="linux-">Linux 动态库搜索</h3><p><strong>搜索顺序：</strong></p><table><thead><tr><th> 顺序 </th><th> 来源 </th><th> 说明 </th></tr></thead><tbody><tr><td> 1 </td><td> <code>DT_RPATH</code>（旧） </td><td> 在 <code>LD_LIBRARY_PATH</code> <strong>之前</strong>搜索，可覆盖——<strong>已被 RUNPATH 取代</strong> </td></tr><tr><td> 2 </td><td> <code>LD_LIBRARY_PATH</code> </td><td> 环境变量 </td></tr><tr><td> 3 </td><td> <code>DT_RUNPATH</code>（新） </td><td> 在 <code>LD_LIBRARY_PATH</code> <strong>之后</strong>搜索，不可覆盖——现代工具链默认 </td></tr><tr><td> 4 </td><td> <code>ldconfig</code> 缓存 </td><td> <code>/etc/ld.so.cache</code>，由 <code>ldconfig</code> 命令从 <code>/etc/ld.so.conf</code> 构建 </td></tr><tr><td> 5 </td><td> 默认路径 </td><td> <code>/lib</code>、<code>/usr/lib</code>、<code>/lib64</code>、<code>/usr/lib64</code> 等 </td></tr></tbody></table><p><strong>设置方法：</strong></p><table><thead><tr><th> 层级 </th><th> 方法 </th><th> 说明 </th></tr></thead><tbody><tr><td> <strong>系统级</strong> </td><td> 在 <code>/etc/ld.so.conf.d/</code> 加 <code>.conf</code> 文件，然后 <code>sudo ldconfig</code> </td><td> 模块化管理——<strong>不要直接改 <code>/etc/ld.so.conf</code></strong> </td></tr><tr><td> <strong>用户级</strong> </td><td> 无标准机制 </td><td> 可在 <code>~/.bashrc</code> 里设 <code>LD_LIBRARY_PATH</code>，但<strong>不推荐永久使用</strong> </td></tr><tr><td> <strong>Shell级</strong> </td><td> <code>export LD_LIBRARY_PATH=/new/dir:$LD_LIBRARY_PATH</code> </td><td> 临时使用 </td></tr><tr><td> <strong>编译时</strong> </td><td> <code>gcc -Wl,-rpath,/dir</code> 或 <code>-Wl,-rpath,$ORIGIN/lib</code> </td><td> 写进可执行文件，最可靠 </td></tr></tbody></table><h5 id="ldlibrarypath-"><code>LD_LIBRARY_PATH</code> 应当只临时使用。</h5><p><code>LD_LIBRARY_PATH</code> 不应永久写入 Shell 配置。它会覆盖 ldconfig 缓存和默认路径，可能导致系统工具加载到错误版本的库（比如你的 <code>LD_LIBRARY_PATH</code> 里有个旧版 <code>libssl</code>，系统 <code>curl</code> 就可能加载它然后崩掉）。</p><h5 id="rpath-vs-runpath-"><code>RPATH</code> vs <code>RUNPATH</code> 行为不同。</h5><p><code>RPATH</code> 在 <code>LD_LIBRARY_PATH</code> 之前搜索（可被 RPATH 覆盖），<code>RUNPATH</code> 在之后搜索（LD<em>LIBRARY</em>PATH 可覆盖 RUNPATH）。现代 binutils 默认用 <code>RUNPATH</code>。如果你需要编译时路径覆盖环境变量，要显式用 <code>-Wl,--disable-new-dtags</code>（强制 RPATH）——但这通常不是你想要的。</p><h5 id="origin--rpath-"><code>$ORIGIN</code> 是一个特殊的 rpath 值，展开为可执行文件所在目录。</h5><p>用于可移植部署（可执行文件和库放同一目录树），但 <strong><code>$ORIGIN</code> 必须写进 rpath 字面量</strong>，不能在 Shell 里展开——gcc 命令中要写成：</p><pre class="language-bash lang-bash"><code class="language-bash lang-bash">gcc -Wl,-rpath,&#x27;$ORIGIN/lib&#x27;  main.c  # 单引号保护 $ORIGIN 不被 Shell 展开
</code></pre>
<hr/><h3 id="macos-">macOS 动态库搜索</h3><p><strong>搜索顺序：</strong></p><table><thead><tr><th> 顺序 </th><th> 来源 </th><th> 说明 </th></tr></thead><tbody><tr><td> 1 </td><td> <code>DYLD_LIBRARY_PATH</code> / <code>DYLD_FRAMEWORK_PATH</code> </td><td> 环境变量覆盖（由于SIP， 对系统进程无效） </td></tr><tr><td> 2 </td><td> <code>@rpath</code> 展开 </td><td> 可执行文件中 <code>LC_RPATH</code> 指定的目录列表 </td></tr><tr><td> 3 </td><td> <code>@loader_path</code> / <code>@executable_path</code> </td><td> 相对于加载者/主程序的目录 </td></tr><tr><td> 4 </td><td> install name 绝对路径 </td><td> 库自身声明的路径 </td></tr><tr><td> 5 </td><td> <code>DYLD_FALLBACK_LIBRARY_PATH</code> </td><td> 默认 <code>/usr/local/lib:/usr/lib</code> </td></tr></tbody></table><p><strong>macOS 的 <code>@</code> 路径魔术——这是理解 macOS 动态库的关键：</strong></p><table><thead><tr><th> 语法 </th><th> 展开为 </th><th> 用途 </th></tr></thead><tbody><tr><td> <code>@executable_path</code> </td><td> 主程序所在目录 </td><td> 主程序找自己的库 </td></tr><tr><td> <code>@loader_path</code> </td><td> 加载此库的那个文件所在目录 </td><td> 库找自己依赖的库（递归场景） </td></tr><tr><td> <code>@rpath</code> </td><td> 可执行文件中 <code>LC_RPATH</code> 列表中的目录 </td><td> 最灵活——主程序定义搜索列表，库只需声明&quot;在 rpath 里找我&quot; </td></tr></tbody></table><p>macOS 的设计哲学：<strong>库的搜索路径编译进库本身</strong>（install name），而不是依赖环境变量。库的 install name 通常形如 <code>@rpath/libfoo.dylib</code>，加载时 dyld 用主程序的 rpath 列表展开 <code>@rpath</code> 为具体目录。</p><p><strong>设置方法：</strong></p><table><thead><tr><th> 层级 </th><th> 方法 </th><th> 说明 </th></tr></thead><tbody><tr><td> <strong>系统级</strong> </td><td> 无环境变量机制 </td><td> macOS 设计上不支持——靠编译时嵌入 </td></tr><tr><td> <strong>Shell级</strong> </td><td> <code>export DYLD_LIBRARY_PATH=/dir</code> </td><td> 临时用，<strong>SIP会对系统进程剥离此变量</strong> </td></tr><tr><td> <strong>编译时</strong> </td><td> <code>gcc -Wl,-rpath,/dir</code> 或 <code>install_name_tool</code> </td><td> 最可靠的方式 </td></tr><tr><td> <strong>修改已编译库</strong> </td><td> <code>install_name_tool -change old_path new_path libfoo.dylib</code> </td><td> macOS 独有，改库的 install name 不需要重新编译 </td></tr></tbody></table>
<p>Homebrew 安装的库使用绝对路径 install name（<code>/opt/homebrew/lib/libfoo.dylib</code> 或 <code>/usr/local/lib/libfoo.dylib</code>），Homebrew 在编译时通过 <code>-rpath</code> 让可执行文件能找到它们。如果你<strong>手动编译程序链接 Homebrew 库</strong>，必须传：</p><pre class="language-bash lang-bash"><code class="language-bash lang-bash">gcc -L/opt/homebrew/lib -Wl,-rpath,/opt/homebrew/lib main.c -lfoo
</code></pre>
<p>漏了 <code>-rpath</code> 就能编译但运行时找不到库——这是 macOS 上 <code>dyld: Library not loaded</code> 报错的第一大原因。</p><h5 id="installnametool--macos-"><code>install_name_tool</code> 是 macOS 独有的神器：</h5><p>可以修改已编译 <code>.dylib</code> 的 install name 和已编译可执行文件的 rpath，<strong>不需要重新编译</strong>：</p><pre class="language-zsh lang-zsh"><code class="language-zsh lang-zsh"># 修改库声明自己的路径
install_name_tool -id @rpath/libfoo.dylib libfoo.dylib

# 修改可执行文件中对某库的引用路径
install_name_tool -change /old/path/libfoo.dylib @rpath/libfoo.dylib myapp

# 给可执行文件追加 rpath
install_name_tool -add_rpath /opt/homebrew/lib myapp
</code></pre>
<hr/><p><strong>经验交流</strong>：每个平台的环境和动态库问题都有大量隐坑，尤其是 Windows 的 DLL 搜索顺序和 macOS 的 install name 体系——如果你有踩坑经验或补充，欢迎评论区分享。</p></div><p style="text-align:right"><a href="https://me.bcw2.top:3443/posts/tech/environment-variable-for-command-line-interface#comments">看完了？说点什么呢</a></p></div>]]></description><link>https://me.bcw2.top:3443/posts/tech/environment-variable-for-command-line-interface</link><guid isPermaLink="true">https://me.bcw2.top:3443/posts/tech/environment-variable-for-command-line-interface</guid><dc:creator><![CDATA[bcw222]]></dc:creator><pubDate>Sun, 19 Apr 2026 05:35:32 GMT</pubDate></item><item><title><![CDATA[【系统化速查表 第〇弹】序言]]></title><description><![CDATA[<div><blockquote>该渲染由 Shiro API 生成，可能存在排版问题，最佳体验请前往：<a href="https://me.bcw2.top:3443/posts/tech/systematic-cheat-sheet-prelogue">https://me.bcw2.top:3443/posts/tech/systematic-cheat-sheet-prelogue</a></blockquote><div><h2 id="">是什么</h2><p>本系列博文旨在解决就事论事解决问题不全面系统，翻书查教程冗长且可能过时的痛点，将凝练的知识系统性地组织起来，供入门用户建立认知框架，高级用户预知经验坑点。</p><h2 id="">为什么</h2><p>博主深受其害：</p><ul><li>碎片化搜答案：一个自以为是特例的报错，翻了半天社区讨论终于修好了，长舒一口气顺手清了浏览历史，结果没过几天同方向的问题又出现了，本可以复用的基础搜索又得全部重来；</li><li>硬啃完整教程：想用个项目，结果单开一个网页的 Wiki / Documention 翻半天找不到要用的点，回 GitHub 文档仓库一搜发现压根没写，定睛一看原来维护者在 issue 里说 not-planned</li></ul><p>对于低频场景，稍微花点时间跳读一下可能无伤大雅；但当连自己都注意到了经常在做无用功，那不妨跟我一起总结经验教训。</p><h2 id="">怎么办</h2><ul><li>聚焦坑点：不再把坑点当补充，而是作为正文主要部分</li><li>标记时间：博文自带最后修改时间标记，可能过时的提醒你谨慎阅读</li></ul><h2 id="">适合谁</h2><p><strong>适合：</strong></p><ul><li><strong>速查的高级用户</strong>：直接翻目录或 Ctrl+F，找你要的那节，参数表格和命令示例都在</li><li><strong>想系统了解的小白</strong>：从头读，每个参数都有解释</li></ul><p><strong>不适合：</strong></p><ul><li><p><strong>想看保姆式教程的人</strong>：场景千变万化，要全列出来这就不是速查表了；但根因殊途同归，本系列博文尽管没法告诉你怎么做但至少尽可能教会你为什么；</p></li><li><p><strong>只想要不二法门的人</strong>：授人以鱼不如授人以渔，适合的才是最好的，天天看杠精叫嚣“诶你这个通用解决方案不如我在我自己特定场景下调整过的在我自己的特定场景下的表现更好”有意思吗？与其给你个大多数时候够用的通用解决方案，不如教会你怎么针对自己的特定场景优化。</p></li></ul><h2 id="">说在最后</h2><p>作为经验交流，博主个人的经验总是有限的，在这里也鼓励大家在评论区讨论分享自己的经验之谈。</p></div><p style="text-align:right"><a href="https://me.bcw2.top:3443/posts/tech/systematic-cheat-sheet-prelogue#comments">看完了？说点什么呢</a></p></div>]]></description><link>https://me.bcw2.top:3443/posts/tech/systematic-cheat-sheet-prelogue</link><guid isPermaLink="true">https://me.bcw2.top:3443/posts/tech/systematic-cheat-sheet-prelogue</guid><dc:creator><![CDATA[bcw222]]></dc:creator><pubDate>Sun, 19 Apr 2026 02:50:00 GMT</pubDate></item><item><title><![CDATA[【系统化速查表 第一弹】FFmpeg 视频二压实战指南]]></title><description><![CDATA[<div><blockquote>该渲染由 Shiro API 生成，可能存在排版问题，最佳体验请前往：<a href="https://me.bcw2.top:3443/posts/tech/ffmpeg-video-compress-practical-guide">https://me.bcw2.top:3443/posts/tech/ffmpeg-video-compress-practical-guide</a></blockquote><div><h2 id="">谁应该看这篇文章？</h2><p><strong>适合：</strong></p><ul><li><strong>速查的高级用户</strong>：直接翻目录或 Ctrl+F，找你要的那节，参数表格和命令示例都在</li><li><strong>想系统了解的小白</strong>：从头读，每个参数都有解释，假定你不熟悉命令行</li></ul><p><strong>不适合：</strong></p><ul><li><p><strong>想抬杠的</strong>：关于哪种编码器在哪种内容下 VMAF 更高，这个问题没有金标准。根据信息论的基本原理，不可能存在一种编码既无损画质又极低码率——一切都是 trade-off，只能对特定视频源做 VMAF 实测才能得出有效结论。如果你热衷这类深度分析，出门左转这篇文章更适合你：<a href="https://zhuanlan.zhihu.com/p/1913258114746122747">视频压缩深度测试（知乎）</a></p></li><li><p><strong>只盯着存储成本的人</strong>：除了存储成本，还有——<strong>编码时间成本</strong>（压个视频让电脑满载几百个小时值不值）、<strong>解码平台限制成本</strong>（目标设备不支持AV1还得转回来再看）、<strong>编解码电费成本</strong>（显卡满载的功耗感人）、<strong>调参时间成本</strong>（每种不同的视频源都得反复测 VMAF 和调参）。存储是成本，这些也是成本，甚至还有<strong>你看这篇文章本身的时间成本</strong>，都应该考虑在内。</p></li></ul><hr/><h2 id="-ffmpeg-cli-gui-">为什么用 FFmpeg CLI，而不是 GUI 工具？</h2><p>市面上有很多 GUI 封装工具——小丸、格式工厂、ShanaEncoder、HandBrake、FFmpegFreeUI 等等，它们的本质大多是给 FFmpeg 套了个图形界面，把你在界面里选的参数翻译成 FFmpeg 命令在后台运行。尽管并非所有 GUI 工具都基于 FFmpeg（例如 HandBrake 部分功能直接调用显卡厂商 SDK），但功能上大差不差。</p><p>GUI 工具的问题：</p><ul><li><strong>参数覆盖不全</strong>：GUI 不可能把 FFmpeg 所有参数都列出来，总有你需要但界面没有的选项</li><li><strong>停更风险与沉没成本</strong>：很多小工具停更了你就傻了，学会的操作逻辑也迁移不走；FFmpeg 本身几乎不可能停更，它已经是音视频处理领域的基础设施级的存在了，掌握了就是你的</li><li><strong>自由度受限</strong>：脚本化、批量处理、条件判断，CLI 天然支持，GUI 永远差一口气</li></ul><p>并不是说 GUI 工具不好，它们的确很方便（我自己也经常用 LosslessCut）；但掌握 FFmpeg CLI 之后，GUI 工具对你来说就指是&quot;便利品&quot;，而不是&quot;必需品&quot;，你永远有托底的杀手锏。</p><hr/><h2 id="-ffmpeg">安装 FFmpeg</h2><p>请直接参考官方指南 <a href="https://ffmpeg.org/download.html">Download FFmpeg</a></p><p>如果用包管理器装，环境变量应该会自动配好，直接运行 <code>ffmpeg</code> 就可以；如果直接下载静态构建手动安装，记得手动添加<a href="https://me.bcw2.top:3443/posts/tech/environment-variable-for-command-line-interface">环境变量</a>或把命令中的 <code>ffmpeg</code> 改成相应路径。</p><p><strong>验证安装：</strong> 打开终端或命令提示符，输入 <code>ffmpeg -version</code>，能看到版本信息就成功了。</p><hr/><h2 id="">一、基础概念</h2><p>在输参数之前，有几个概念必须搞清楚。</p><h3 id="11--vs-">1.1 容器格式 vs 编码格式</h3><p>我们平时说的&quot;MP4 文件&quot;，严格来说由两层组成：</p><ul><li><strong>容器格式（封装格式）</strong>：文件的&quot;外壳&quot;，即文件扩展名所代表的格式。常见的有 <code>.mp4</code>、<code>.mkv</code>、<code>.mov</code>。容器负责把视频流、音频流、字幕流&quot;装在一起&quot;</li><li><strong>编码格式</strong>：视频流本身的压缩标准。同样是 <code>.mp4</code> 容器，里面的视频流可能是 H.264，也可能是 H.265，或者 AV1</li></ul><p>&quot;这是个 MP4&quot;只说明了容器，没说明编码。用 <code>ffprobe</code>（FFmpeg 三件套之一，和 <code>ffplay</code> 一样应该会一起装好）可以直接查看文件的元信息。</p><h3 id="12-">1.2 编码格式与编码器</h3><p><strong>编码格式</strong>只是标准，<strong>编码器</strong>才是执行者（按照标准把视频数据实际编码出来）。</p><p>常用对照表：</p><table><thead><tr><th> 编码格式 </th><th> CPU 软件编码器 </th><th> NVIDIA GPU </th><th> Intel GPU </th><th> AMD GPU </th></tr></thead><tbody><tr><td> H.264 </td><td> <code>libx264</code> </td><td> <code>h264_nvenc</code> </td><td> <code>h264_qsv</code> </td><td> <code>h264_amf</code> </td></tr><tr><td> H.265 (HEVC) </td><td> <code>libx265</code> </td><td> <code>hevc_nvenc</code> </td><td> <code>hevc_qsv</code> </td><td> <code>hevc_amf</code> </td></tr><tr><td> AV1 </td><td> <code>libsvtav1</code> </td><td> <code>av1_nvenc</code>（40系+） </td><td> <code>av1_qsv</code>（Arc/14代核显+） </td><td> <code>av1_amf</code>（RX7000+） </td></tr></tbody></table><p>带 <code>lib</code> 前缀的是纯软件编码器（用 CPU 计算），其余为硬件编码器（调用显卡内专用编解码单元）。</p><p><strong>关于显卡编码速度的常见误解</strong>：显卡的&quot;3D 游戏性能&quot;与视频编码速度几乎无关。NVIDIA 的 NVENC 编码单元是独立的专用硬件——4060 和 4090 在单个编码任务上速度基本一致，因为两者都只有一两个 NVENC 单元，是 NVENC 单元数量和代际决定速度，而不是 CUDA 核心数量。</p><h3 id="13-">1.3 码率（比特率）</h3><p>码率 = 视频每秒传输的数据量，单位是 kbps（千比特/秒）或 Mbps（兆比特/秒）。</p><ul><li>1080p 普通内容（教程、生活录像）：1-3 Mbps 通常够用</li><li>1080p 高动态游戏录屏：3-6 Mbps</li><li>4K 内容：8-20 Mbps</li></ul><h3 id="14-">1.4 像素格式</h3><ul><li><code>yuv420p</code>：8bit，兼容性最广，几乎所有设备都支持</li><li><code>yuv420p10le</code>：10bit，色彩过渡更细腻，低码率下反而质量可能更好（不平滑的色彩过渡本身就是高频噪声会浪费码率），用 HEVC/AV1 且设备兼容时推荐</li></ul><hr/><h2 id="">二、命令行基础语法</h2><h3 id="21-">2.1 命令的基本结构</h3><pre class="language-bash lang-bash"><code class="language-bash lang-bash">ffmpeg -i 输入文件.mp4 [各种参数] 输出文件.mp4
</code></pre>
<ul><li><code>ffmpeg</code>：调用 FFmpeg 程序</li><li><code>-i</code>：<strong>input</strong> 的缩写，指定输入文件，后面跟文件路径</li><li>各种参数：每个选项都是 <code>-参数名 参数值</code> 的格式</li><li>最后一个路径（没有 <code>-</code> 开头的参数在前）通常是输出文件</li></ul><h3 id="22-bash--powershell">2.2 反斜杠换行（bash / PowerShell）</h3><pre class="language-bash lang-bash"><code class="language-bash lang-bash">ffmpeg -i 输入.mp4 \
  -c:v hevc_nvenc \
  -c:a copy     \
  输出.mp4
</code></pre>
<p>行末的 <code>\</code> 表示&quot;这行还没写完，下一行是接续&quot;。这只是为了让很长的命令看起来更清晰，效果完全等同于写成一行。</p><p>Windows CMD 中不能用 <code>` 换行，要用 </code>^<code>；Powershell 要用</code><code> </code> ``，且前面要由空格；本文示例统一用 bash 格式，Windows 用户请自行替换，或者不换行直接写成一行。</p><h3 id="23-">2.3 环境变量</h3><pre class="language-bash lang-bash"><code class="language-bash lang-bash"># bash 写法（Linux / macOS / Git Bash）
export VIDEO=&quot;https://example.com/video.mp4?sign=abc123xyz&quot;
ffmpeg -i &quot;$VIDEO&quot; 输出.mp4

# PowerShell 写法
$VIDEO = &quot;https://example.com/video.mp4?sign=abc123xyz&quot;
ffmpeg -i &quot;$VIDEO&quot; 输出.mp4
</code></pre>
<p><code>export VIDEO=&quot;...&quot;</code> 是 bash 中<strong>设置变量</strong>的写法。之后用 <code>$VIDEO</code>，shell 会自动把它替换成那段 URL，避免重复输入。这个变量只在当前终端窗口的会话中有效，关闭窗口就消失了。</p><h3 id="24--gpu">2.4 指定使用哪块 GPU</h3><pre class="language-bash lang-bash"><code class="language-bash lang-bash">CUDA_VISIBLE_DEVICES=1 ffmpeg -i 输入.mp4 -c:v hevc_nvenc 输出.mp4
</code></pre>
<p><code>CUDA_VISIBLE_DEVICES=1</code> 是一个<strong>临时环境变量</strong>，写在命令前，只对这条命令生效。它告诉 CUDA 只把编号为 <code>1</code> 的 GPU（编号从 0 开始）暴露给这个进程，FFmpeg 就只能用那块显卡。多卡机器分配任务时使用。</p><hr/><h2 id="">三、最基础的输入输出</h2><h3 id="31-">3.1 查看文件信息</h3><pre class="language-bash lang-bash"><code class="language-bash lang-bash">ffprobe 文件.mp4
</code></pre>
<p>FFProbe 会打印输入文件的元信息（分辨率、帧率、编码格式、时长、码率等），然后退出。这是快速查看文件信息的常用方式。</p><h3 id="32-">3.2 直接转封装（不重新编码）</h3><pre class="language-bash lang-bash"><code class="language-bash lang-bash">ffmpeg -i 输入.mkv -c copy 输出.mp4
</code></pre>
<ul><li><code>-c</code>：<strong>codec</strong>，指定编码器</li><li><code>copy</code>：对该流直接复制（不重编码）</li><li>速度极快，因为只是换了个容器&quot;壳&quot;，但仅适用于目标容器兼容源编码的情形，否则会报错。</li></ul><h3 id="33-">3.3 覆盖已有文件</h3><pre class="language-bash lang-bash"><code class="language-bash lang-bash">ffmpeg -i 输入.mp4 -c:v hevc_nvenc -c:a copy -y 输出.mp4
</code></pre>
<ul><li><code>-y</code>：如果输出文件已存在，<strong>直接覆盖</strong>，不交互询问。不加这个参数时，FFmpeg 会停下来问你要不要覆盖</li></ul><h3 id="34-">3.4 指定编码器</h3><pre class="language-bash lang-bash"><code class="language-bash lang-bash">ffmpeg -i 输入.mp4 -c:v hevc_nvenc -c:a copy 输出.mp4
</code></pre>
<ul><li><code>-c</code>：<strong>codec</strong>，指定编码器</li><li><code>:v</code>：<strong>video</strong>，对视频</li><li><code>:a</code>：<strong>audio</strong>，对音频</li><li><code>copy</code>：对该流直接复制（不重编码）</li></ul><h3 id="35-">3.5 指定码率</h3><pre class="language-bash lang-bash"><code class="language-bash lang-bash">ffmpeg -i 输入.mp4 -c:v hevc_nvenc -b:v 2M -c:a copy 输出.mp4
</code></pre>
<ul><li><code>-b</code>：<strong>bitrate</strong>，指定码率</li><li><code>2M</code>: 2Mbps，<code>M</code> 大写</li></ul><hr/><h2 id="">四、时间裁剪与剪切</h2><p>这是高频操作，和编码参数无关，单独讲清楚。</p><h3 id="41-">4.1 时间参数说明</h3><table><thead><tr><th> 参数 </th><th> 含义 </th></tr></thead><tbody><tr><td> <code>-ss 30</code> </td><td> 从第 30 秒<strong>开始</strong>处理（也支持 <code>00:00:30</code> 格式） </td></tr><tr><td> <code>-to 120</code> </td><td> 输出<strong>截止到</strong>第 120 秒（相对于输入文件起点） </td></tr><tr><td> <code>-t 90</code> </td><td> 输出<strong>持续 90 秒</strong> </td></tr></tbody></table><p><code>-to</code> 和 <code>-t</code> 的区别：<code>-ss 30 -to 120</code> 等于 <code>-ss 30 -t 90</code>，结果一样（从 30s 到 120s，共 90s），但逻辑语义不同，按需选用。</p><h3 id="42-">4.2 关键帧剪切（快，不精确）</h3><pre class="language-bash lang-bash"><code class="language-bash lang-bash">ffmpeg -ss 30 -i 输入.mp4 -to 90 -c copy 输出.mp4
</code></pre>
<p><strong><code>-ss</code> 放在 <code>-i</code> 前面</strong>：FFmpeg 先快速 seek 到靠近 30s 的关键帧，然后再开始解码。由于关键帧不一定恰好在 30s，实际起点可能略早几秒。</p><p><code>-c copy</code> 直接复制流，<strong>不重新编码</strong>，速度接近瞬间完成，适合快速提取片段。</p><h3 id="43-">4.3 重编码剪切（精确，较慢）</h3><pre class="language-bash lang-bash"><code class="language-bash lang-bash">ffmpeg -i 输入.mp4 -ss 30 -to 120 -c:v hevc_nvenc -c:a aac 输出.mp4
</code></pre>
<p><strong><code>-ss</code> 放在 <code>-i</code> 后面</strong>：FFmpeg 从头解码，精确丢弃前 30s，起点<strong>精确到帧</strong>。代价是需要重新编码，速度相应变慢。</p><p>适合场景：本身就要重压缩的任务（剪切和二压合并成一步，不需要先切再压）。</p><blockquote><p><strong>建议</strong>：如果本来就要二压，直接用 <code>-ss</code>（放 <code>-i</code> 后）同时做剪切和压缩，一步到位。如果只是想快速切片段留着，用 <code>-c copy</code>，秒出结果，精度够用就行。</p></blockquote>
<hr/><h2 id="">五、视频参数</h2><h3 id="51-">5.1 分辨率缩放</h3><pre class="language-bash lang-bash"><code class="language-bash lang-bash">ffmpeg -i 输入.mp4 -vf &quot;scale=-2:1080&quot; 输出.mp4
</code></pre>
<ul><li><code>-vf</code>：<strong>video filter</strong>，视频滤镜，后面跟引号包裹的滤镜表达式</li><li><code>scale=-2:1080</code>：缩放滤镜
<ul><li>第一个值 <code>-2</code>：宽度<strong>自动计算</strong>，保持原始宽高比，且结果能被 2 整除（编码器要求）</li><li>第二个值 <code>1080</code>：目标高度 1080 像素</li><li>写 <code>scale=1920:1080</code> 则强制指定宽和高（不保证宽高比）</li><li>写 <code>scale=1280:-2</code> 则固定宽，高度自动算</li></ul></li></ul><h3 id="52-">5.2 帧率</h3><pre class="language-bash lang-bash"><code class="language-bash lang-bash">ffmpeg -i 输入.mp4 -r 30 输出.mp4
</code></pre>
<ul><li><code>-r</code>：设置输出帧率（Frames Per Second）。不加 <code>-r</code> 则保持输入帧率</li></ul><h3 id="53-">5.3 像素格式</h3><pre class="language-bash lang-bash"><code class="language-bash lang-bash">ffmpeg -i 输入.mp4 -pix_fmt yuv420p10le 输出.mp4
</code></pre>
<ul><li><code>-pix_fmt</code>：<strong>pixel format</strong>
<ul><li><code>yuv420p</code>：8bit，兼容性最好，保守推荐</li><li><code>yuv420p10le</code>：10bit，HEVC/AV1 推荐</li></ul></li></ul><hr/><h2 id="">六、音频参数</h2><h3 id="61-">6.1 直接复制音频（最推荐）</h3><pre class="language-bash lang-bash"><code class="language-bash lang-bash">-c:a copy
</code></pre>
<p>源文件音频质量没问题的情况下，优先复制，无质量损失，而且快。</p><h3 id="62-">6.2 重新编码音频</h3><pre class="language-bash lang-bash"><code class="language-bash lang-bash">ffmpeg -i 输入.mp4 -c:a aac -b:a 128k 输出.mp4
</code></pre>
<ul><li><code>-b:a 128k</code>：音频目标<strong>比特率</strong> 128 kbps</li></ul><p>常用音频编码器：</p><table><thead><tr><th> 编码器 </th><th> 特点 </th></tr></thead><tbody><tr><td> <code>aac</code> </td><td> 最通用，MP4 容器标配，128k-320k </td></tr><tr><td> <code>libopus</code> </td><td> 同质量体积最小，推荐用于 MKV </td></tr><tr><td> <code>flac</code> </td><td> 无损 </td></tr><tr><td> <code>libmp3lame</code> </td><td> MP3，兼容性好，用 <code>-q:a 0</code>（最佳质量） </td></tr><tr><td> <code>copy</code> </td><td> 直接复制，优先选这个 </td></tr></tbody></table><h3 id="63-">6.3 选择特定音轨</h3><pre class="language-bash lang-bash"><code class="language-bash lang-bash">ffmpeg -i 输入.mkv -map 0:v:0 -map 0:a:1 -c copy 输出.mp4
</code></pre>
<ul><li><code>-map</code>：手动指定要输出哪条流
<ul><li><code>0</code>：第 1 个输入文件（从 0 计数）</li><li><code>v:0</code>：该文件的第 1 条视频流</li><li><code>a:1</code>：该文件的第 2 条音频流（比如日语音轨）</li></ul></li><li>不加 <code>-map</code> 时，FFmpeg 自动选每种类型中&quot;最佳&quot;的一条</li></ul><hr/><h2 id="">七、码率控制模式</h2><p>这是视频编码最核心的部分，直接决定画质和文件大小。</p><h3 id="71-crf-cpu-">7.1 CRF 模式（CPU 编码器专用）</h3><pre class="language-bash lang-bash"><code class="language-bash lang-bash">ffmpeg -i 输入.mp4 -c:v libx265 -crf 24 -c:a copy 输出.mp4
</code></pre>
<ul><li><code>-crf</code>：<strong>Constant Rate Factor</strong>，恒定质量因子</li><li>数值越小画质越好文件越大，越大越糊越小</li><li>推荐范围：</li></ul><table><thead><tr><th> 编码器 </th><th> 推荐 CRF 范围 </th><th> 通用中点 </th></tr></thead><tbody><tr><td> <code>libx264</code> </td><td> 18-28 </td><td> 23-24 </td></tr><tr><td> <code>libx265</code> </td><td> 20-28 </td><td> 24-26 </td></tr><tr><td> <code>libsvtav1</code> </td><td> 28-38 </td><td> 32-34 </td></tr></tbody></table><blockquote><p><strong>重要</strong>：CRF 是 CPU 软件编码器专用，GPU 硬件编码器<strong>不支持 <code>-crf</code></strong>。</p></blockquote>
<h3 id="72-cq-nvenc-">7.2 CQ 模式（NVENC 的恒定质量）</h3><pre class="language-bash lang-bash"><code class="language-bash lang-bash">ffmpeg -i 输入.mp4 -c:v hevc_nvenc -rc:v vbr -cq:v 28 -b:v 0 -c:a copy 输出.mp4
</code></pre>
<ul><li><code>-rc:v vbr</code>：<strong>rate control</strong>，码率控制模式，<code>vbr</code> 是可变码率</li><li><code>-cq:v 28</code>：NVENC 的恒定质量值（类似 CRF，但数值对应关系不同）</li><li><code>-b:v 0</code>：目标码率设为 0，配合 CQ 使用，表示不额外限制码率</li></ul><p><strong>CQ 与 CRF 的对应关系（近似）</strong>：</p><pre class=""><code class="">libx264 -crf 24  ≈  h264_nvenc   -cq 28~30
libx265 -crf 24  ≈  hevc_nvenc   -cq 28~30
libsvtav1 -crf 34 ≈ av1_nvenc    -cq 34~36
</code></pre><blockquote>
<p>⚠️ <strong>最常见的误区</strong>：根据 CPU 编码 <code>-crf 24</code> 的经验，在用 GPU 编码时也选 <code>-cq 24</code>。这相当于 CPU 的 <code>-crf 18</code>（画质极好但文件极大），被误以为&quot;GPU 编码出来的文件比 CPU 还大&quot;——其实只是 CQ 值选错了。</p></blockquote>
<h3 id="73-global-qualityintel-qsv">7.3 Global Quality（Intel QSV）</h3><pre class="language-bash lang-bash"><code class="language-bash lang-bash">ffmpeg -i 输入.mp4 -c:v hevc_qsv -global_quality 24 -c:a copy 输出.mp4
</code></pre>
<ul><li>Intel QSV 的恒定质量参数，取值逻辑与 CPU CRF 更接近（24 对 24）</li></ul><h3 id="74-qp-amd-amf">7.4 QP 模式（AMD AMF）</h3><pre class="language-bash lang-bash"><code class="language-bash lang-bash">ffmpeg -i 输入.mp4 -c:v hevc_amf -qp_i 28 -qp_p 28 -c:a copy 输出.mp4
</code></pre>
<ul><li><code>-qp_i</code>：I 帧（关键帧）的量化参数</li><li><code>-qp_p</code>：P 帧的量化参数，通常与 <code>-qp_i</code> 设相同值</li></ul><h3 id="75-vbr-">7.5 VBR 码率模式（有明确码率上限时）</h3><pre class="language-bash lang-bash"><code class="language-bash lang-bash">ffmpeg -i 输入.mp4 -c:v hevc_nvenc \
  -rc:v vbr -b:v 2M -maxrate 3M -bufsize 5M \
  -c:a copy 输出.mp4
</code></pre>
<ul><li><code>-b:v 2M</code>：视频目标码率 <strong>2 Mbps</strong></li><li><code>-maxrate 3M</code>：<strong>最大码率</strong>上限，峰值不超过 3 Mbps</li><li><code>-bufsize 5M</code>：<strong>缓冲区大小</strong>，控制码率波动幅度（建议设为 maxrate 的 1.5-2 倍）</li><li>注意：<code>M</code> 必须大写，小写 <code>m</code> 在某些情况下不生效</li></ul><hr/><h2 id="nvenc-">八、NVENC 高级参数详解</h2><p>以下参数针对 NVIDIA 显卡编码（<code>hevc_nvenc</code> / <code>av1_nvenc</code>）。</p><h3 id="81-">8.1 编码预设与调优</h3><pre class="language-bash lang-bash"><code class="language-bash lang-bash">-preset p7 -tune:v hq
</code></pre>
<ul><li><code>-preset</code>：<strong>编码速度预设</strong>，<code>p1</code>（最快）到 <code>p7</code>（最慢/最优质）。越慢压缩效率越高，同等画质文件更小。日常推荐 <code>p4</code>（平衡）到 <code>p7</code>（质量优先）</li><li><code>-tune:v hq</code>：调优目标为<strong>高质量</strong>（High Quality）；<code>ull</code> 是超低延迟（直播场景）；<code>lossless</code> 是无损</li></ul><h3 id="82-">8.2 多遍编码</h3><pre class="language-bash lang-bash"><code class="language-bash lang-bash">-multipass 2
</code></pre>
<ul><li><code>-multipass 2</code>：两遍编码，第一遍分析，第二遍编码，压缩效率更高。编码时间增加约 30-50%，但文件更小或画质更好</li></ul><h3 id="83-">8.3 前瞻帧数</h3><pre class="language-bash lang-bash"><code class="language-bash lang-bash">-rc-lookahead 32
</code></pre>
<ul><li><code>-rc-lookahead</code>：向前预看多少帧来辅助码率分配决策。范围 0-32，值越大压缩越好，速度越慢，推荐设 <code>32</code></li></ul><h3 id="84-aq">8.4 自适应量化（AQ）</h3><pre class="language-bash lang-bash"><code class="language-bash lang-bash">-spatial-aq 1 -temporal-aq 1 -aq-strength 10
</code></pre>
<ul><li><code>-spatial-aq 1</code>：<strong>空间自适应量化</strong>，开启（<code>1</code>）后，根据画面区域复杂度分配码率——纹理丰富的地方多给，大面积纯色的地方少给</li><li><code>-temporal-aq 1</code>：<strong>时间自适应量化</strong>，根据帧间运动量分配码率——运动剧烈的帧多给</li><li><code>-aq-strength</code>：AQ 强度，范围 1-15，推荐 8-12；值过高会导致码率分配失衡</li></ul><h3 id="85-gop-">8.5 GOP 结构</h3><pre class="language-bash lang-bash"><code class="language-bash lang-bash">-g 300 -bf 4 -b_ref_mode 2
</code></pre>
<ul><li><code>-g 300</code>：<strong>GOP 长度</strong>（关键帧间隔），每隔 300 帧放一个关键帧。GOP 越短，跳转越精确但压缩效率略降；推荐设为帧率 × 10（30fps 就设 300，60fps 就设 600）</li><li><code>-bf 4</code>：<strong>B 帧数量</strong>，范围 0-4。B 帧通过参考前后帧提升压缩效率，设 4 是上限</li><li><code>-b_ref_mode 2</code>：B 帧参考模式，<code>2</code> 是金字塔模式，压缩效率最高，推荐</li></ul><h3 id="86-">8.6 分块多单元编码</h3><pre class="language-bash lang-bash"><code class="language-bash lang-bash">-split_encode_mode 2
</code></pre>
<ul><li><code>-split_encode_mode 2</code>：尝试调用多个 NVENC 编码单元（如 4090 有两个单元）。但实际调用几个取决于 FFmpeg 调度，不保证双倍速度</li></ul><h3 id="87-hevc-10bit-profile">8.7 HEVC 10bit Profile</h3><pre class="language-bash lang-bash"><code class="language-bash lang-bash">-c:v hevc_nvenc -profile:v main10 -pix_fmt yuv420p10le
</code></pre>
<ul><li><code>-profile:v main10</code>：使用 HEVC Main10 Profile，支持 10bit 输出，必须配合 <code>-pix_fmt yuv420p10le</code> 使用</li></ul><hr/><h2 id="">九、硬件加速平台</h2><h3 id="nvidia-nvenc">NVIDIA NVENC</h3><pre class="language-bash lang-bash"><code class="language-bash lang-bash"># HEVC 10bit，质量优先
ffmpeg -i 输入.mp4 -c:v hevc_nvenc -pix_fmt yuv420p10le -profile:v main10 \
  -preset p7 -tune:v hq -rc:v vbr -cq:v 28 -b:v 0 \
  -multipass 2 -rc-lookahead 32 \
  -spatial-aq 1 -temporal-aq 1 -aq-strength 10 \
  -g 300 -bf 4 -b_ref_mode 2 \
  -c:a copy 输出.mp4

# AV1（需要 40 系及以上显卡）
ffmpeg -i 输入.mp4 -c:v av1_nvenc -pix_fmt yuv420p10le \
  -preset p7 -tune:v hq -rc:v vbr -cq:v 34 -b:v 0 \
  -multipass 2 -rc-lookahead 32 \
  -spatial-aq 1 -temporal-aq 1 -aq-strength 10 \
  -g 300 -bf 4 -b_ref_mode 2 \
  -c:a copy 输出.mp4
</code></pre>
<p>代际说明：16/20/30 系是第七代 NVENC；40 系是第八代，新增 AV1 硬编；50 系是第九代，速度略有提升。<strong>AV1 硬编是 40 系的重要分水岭。</strong></p><h3 id="intel-quick-sync-qsv">Intel Quick Sync (QSV)</h3><pre class="language-bash lang-bash"><code class="language-bash lang-bash">ffmpeg -i 输入.mp4 -c:v hevc_qsv -preset medium -global_quality 24 -c:a copy 输出.mp4
ffmpeg -i 输入.mp4 -c:v av1_qsv  -preset medium -global_quality 28 -c:a copy 输出.mp4
</code></pre>
<p>AV1 硬编需要 Intel Arc 独显或 Ultra 100 系列（14 代之后）核显。</p><h3 id="amd-amf">AMD AMF</h3><pre class="language-bash lang-bash"><code class="language-bash lang-bash">ffmpeg -i 输入.mp4 -c:v hevc_amf -preset balanced -qp_i 28 -qp_p 28 -c:a copy 输出.mp4
ffmpeg -i 输入.mp4 -c:v av1_amf  -preset balanced -qp_i 32 -qp_p 32 -c:a copy 输出.mp4
</code></pre>
<p>AV1 硬编需要 RX 7000 系列独显或 Ryzen 7000 系列的 700M 核显（如 7840H 的 780M）。</p><h3 id="apple-videotoolboxmacos">Apple VideoToolbox（macOS）</h3><pre class="language-bash lang-bash"><code class="language-bash lang-bash">ffmpeg -i 输入.mp4 -c:v h264_videotoolbox 输出.mp4
ffmpeg -i 输入.mp4 -c:v hevc_videotoolbox -profile:v main10 输出.mp4
</code></pre>
<p>M 系列芯片支持 H.264 和 HEVC 硬编。AV1 编码支持情况视 FFmpeg 版本而定，请查阅对应版本文档。</p><hr/><h2 id="hev1--hvc1">十、hev1 与 hvc1：播放兼容性的那个坑</h2><p>HEVC 视频在 MP4/MOV 容器中有两种打包标签，影响播放兼容性：</p><ul><li><strong><code>hvc1</code></strong>：参数集（SPS/PPS）内联在容器 Sample Entry 中，视频开头一次性写入</li><li><strong><code>hev1</code></strong>：参数集可散布在码流中，每隔一段重复</li></ul><p><strong>FFmpeg 默认生成 <code>hev1</code>。</strong> 如需改为兼容性更好的 <code>hvc1</code>：</p><pre class="language-bash lang-bash"><code class="language-bash lang-bash">ffmpeg -i 输入.mp4 -c:v hevc_nvenc -tag:v hvc1 -c:a copy 输出.mp4
</code></pre>
<ul><li><code>-tag:v hvc1</code>：强制将视频流的 codec tag 设为 <code>hvc1</code></li></ul><p><strong>各平台兼容情况：</strong></p><table><thead><tr><th> 平台 / 场景 </th><th> <code>hvc1</code> </th><th> <code>hev1</code> </th></tr></thead><tbody><tr><td> Windows Media Foundation </td><td> ✅ </td><td> ✅ </td></tr><tr><td> iOS / Safari（部分版本） </td><td> ✅ </td><td> ⚠️ 可能失败 </td></tr><tr><td> 大多数 Android </td><td> ✅ </td><td> ✅ </td></tr><tr><td> Web 播放器 </td><td> ✅ 推荐 </td><td> ⚠️ 兼容性略差 </td></tr><tr><td> VLC / PotPlayer </td><td> ✅ </td><td> ✅ </td></tr></tbody></table><p><strong>来自实际踩坑的建议</strong>：视频要在 iOS/Safari 播放，或者上传到 Web 播放器，加 <code>-tag:v hvc1</code>。用 <code>hevc_nvenc</code> 等硬件编码器有时会自动用 <code>hvc1</code>，用 <code>libx265</code> 软编默认是 <code>hev1</code>，最稳是<strong>显式指定</strong>。</p><hr/><h2 id="url--url-">十一、URL 作为输入，以及为什么不建议 URL 输出</h2><h3 id="111-url-">11.1 URL 输入</h3><pre class="language-bash lang-bash"><code class="language-bash lang-bash">ffmpeg -i &quot;https://example.com/video.mp4?sign=abc123&amp;token=xyz&quot; 输出.mp4
</code></pre>
<p><strong>为什么 URL 必须加引号？</strong> URL 中常含 <code>?</code>、<code>&amp;</code>、<code>=</code> 等字符，在 shell 里有特殊含义（<code>&amp;</code> 表示后台运行命令）。不加引号，shell 会在 FFmpeg 看到这个 URL 之前就把它&quot;拆解&quot;，导致参数传错或命令直接出错。</p><p>规则：<strong>URL 永远加双引号</strong>。</p><p>使用环境变量存储 URL（避免反复复制粘贴超长签名链接）：</p><pre class="language-bash lang-bash"><code class="language-bash lang-bash">export VIDEO=&quot;https://your-server.com/path/to/video.mp4?sign=very_long_token_here&quot;
ffmpeg -i &quot;$VIDEO&quot; -to 120 -c:v hevc_nvenc -c:a copy 输出.mp4
</code></pre>
<p>注意 <code>&quot;$VIDEO&quot;</code> 也要有引号，防止变量本身展开后的内容被 shell 解析。</p><h3 id="112--url-">11.2 为什么不建议 URL 输出</h3><p>FFmpeg 写入 MP4 文件时，必须在编码完成后<strong>回到文件开头写入 moov box</strong>（元数据，包含帧索引等信息），这个操作需要输出目标支持随机写入（seek）。</p><p>WebDAV 等网络存储协议通常<strong>不支持 seek 写入</strong>，会导致写入失败，或者输出的文件能写进去但无法正常播放。</p><p><strong>结论</strong>：</p><ul><li><strong>输入</strong>：URL 可以用，注意加引号</li><li><strong>输出</strong>：强烈建议输出到本地文件，完成后再上传。如果一定要直出，可以改用 MKV 格式（MKV 对 seek 要求更低），但兼容性不如 MP4</li></ul><hr/><h2 id="">十二、视频变速</h2><h3 id="121---">12.1 视频加速 / 减速</h3><pre class="language-bash lang-bash"><code class="language-bash lang-bash">ffmpeg -i 输入.mp4 -vf &quot;setpts=0.5*PTS&quot; -c:a copy 输出.mp4
</code></pre>
<ul><li><code>setpts</code>：修改视频帧的<strong>显示时间戳</strong>（Presentation TimeStamp）</li><li><code>setpts=0.5*PTS</code>：时间戳乘以 0.5 → 视频 <strong>2 倍速</strong></li><li><code>setpts=2.0*PTS</code>：时间戳乘以 2 → 视频 <strong>0.5 倍速</strong>（慢动作）</li><li>规律：<code>setpts = (1 ÷ 目标倍速) × PTS</code></li></ul><h3 id="122---">12.2 音频加速 / 减速</h3><pre class="language-bash lang-bash"><code class="language-bash lang-bash">ffmpeg -i 输入.mp4 -c:v copy -af &quot;atempo=2.0&quot; 输出.mp4
</code></pre>
<ul><li><code>atempo=2.0</code>：音频 2 倍速</li><li><code>atempo=0.5</code>：音频 0.5 倍速</li><li>⚠️ <code>atempo</code> 取值范围是 <strong>0.5-2.0</strong>，超出范围需要串联多个：</li></ul><pre class="language-bash lang-bash"><code class="language-bash lang-bash"># 4 倍速音频（超出范围，串联两个 atempo）
-af &quot;atempo=2.0,atempo=2.0&quot;
</code></pre>
<h3 id="123-">12.3 音视频同步变速</h3><pre class="language-bash lang-bash"><code class="language-bash lang-bash">ffmpeg -i 输入.mp4 \
  -vf &quot;setpts=0.5*PTS&quot; \
  -af &quot;atempo=2.0&quot; \
  -c:v libx265 -crf 24 \
  输出.mp4
</code></pre>
<p>同时调整音视频速度，保持同步。注意变速后视频流发生了改变，不能用 <code>-c:v copy</code>。</p><hr/><h2 id="">十三、视频拼接与合并</h2><h3 id="131-">13.1 文件列表拼接法（推荐）</h3><p>新建文本文件 <code>list.txt</code>：</p><pre class=""><code class="">file &#x27;片段1.mp4&#x27;
file &#x27;片段2.mp4&#x27;
file &#x27;片段3.mp4&#x27;
</code></pre>
<pre class="language-bash lang-bash"><code class="language-bash lang-bash">ffmpeg -f concat -safe 0 -i list.txt -c copy 输出.mp4
</code></pre>
<ul><li><code>-f concat</code>：使用 concat <strong>解复用器</strong>，按列表顺序读取文件</li><li><code>-safe 0</code>：允许列表中使用相对路径（默认出于安全只允许绝对路径）</li><li><code>-c copy</code>：直接复制流，不重编码，速度极快</li></ul><p><strong>前提</strong>：所有片段的编码格式、分辨率、帧率必须完全一致，否则拼接结果会花屏或音视频不同步。格式不一致时，去掉 <code>-c copy</code> 改为指定编码器重新编码，代价是时间更长。</p><h3 id="132-filtercomplex-concat-">13.2 filter_complex concat 滤镜（格式不一致或需要重编码时）</h3><pre class="language-bash lang-bash"><code class="language-bash lang-bash">ffmpeg -i 片段1.mp4 -i 片段2.mp4 -i 片段3.mp4 \
  -filter_complex &quot;[0:v][0:a][1:v][1:a][2:v][2:a]concat=n=3:v=1:a=1[outv][outa]&quot; \
  -map &quot;[outv]&quot; -map &quot;[outa]&quot; \
  -c:v hevc_nvenc -cq:v 28 -rc:v vbr -b:v 0 -c:a aac \
  输出.mp4
</code></pre>
<ul><li><code>-filter_complex</code>：<strong>复杂滤镜图</strong>，支持多输入处理</li><li><code>[0:v][0:a]</code>：第 1 个输入文件的视频流和音频流（方括号是标签，用于在滤镜图中引用）</li><li><code>[1:v][1:a]</code>：第 2 个输入文件……以此类推</li><li><code>concat=n=3:v=1:a=1</code>：拼接 <code>n=3</code> 段，输出 <code>v=1</code> 条视频流和 <code>a=1</code> 条音频流</li><li><code>[outv][outa]</code>：给滤镜输出命名，供后续 <code>-map</code> 引用</li><li><code>-map &quot;[outv]&quot;</code> 和 <code>-map &quot;[outa]&quot;</code>：选这两条流作为最终输出</li></ul><h3 id="133-">13.3 时长不同时的对齐处理</h3><p><strong>场景：音频比视频短，补静音至视频时长</strong></p><pre class="language-bash lang-bash"><code class="language-bash lang-bash">ffmpeg -i 视频.mp4 -i 音频.mp3 \
  -filter_complex &quot;[1:a]apad[a]&quot; \
  -map 0:v -map &quot;[a]&quot; \
  -shortest \
  输出.mp4
</code></pre>
<ul><li><code>apad</code>：对音频末尾<strong>填充静音</strong></li><li><code>-shortest</code>：以最短流为准截断输出，防止无限延伸</li></ul><p><strong>场景：填充到指定时长</strong></p><pre class="language-bash lang-bash"><code class="language-bash lang-bash">-filter_complex &quot;[1:a]apad=whole_dur=300[a]&quot;
</code></pre>
<ul><li><code>whole_dur=300</code>：音频填充/截断到恰好 300 秒</li></ul><p><strong>场景：视频比音频短，补黑帧</strong></p><pre class="language-bash lang-bash"><code class="language-bash lang-bash">ffmpeg -i 视频.mp4 \
  -vf &quot;tpad=stop=-1:stop_mode=black&quot; \
  -af &quot;apad&quot; \
  -c:v libx265 -crf 24 \
  输出.mp4
</code></pre>
<ul><li><code>tpad=stop=-1:stop_mode=black</code>：在视频末尾补充黑帧（<code>-1</code> 表示无限补，配合 <code>-shortest</code> 或音频时长决定何时停止）</li><li><code>stop_mode=clone</code>：改为复制最后一帧（冻结画面）</li></ul><hr/><h2 id="vmaf-">十四、VMAF 质量评估</h2><p>VMAF（Video Multimethod Assessment Fusion）是 Netflix 开发的视频质量客观评估标准，模拟人眼感知，分数 0-100，<strong>95 分以上</strong>通常认为肉眼几乎无法区分与原片的差别。已内置在 FFmpeg 中，无需额外安装。</p><h3 id="141-">14.1 基本用法</h3><pre class="language-bash lang-bash"><code class="language-bash lang-bash">ffmpeg -i 压缩后.mp4 -i 原始视频.mp4 \
  -filter_complex &quot;[0:v][1:v]libvmaf=model=version=vmaf_v0.6.1:log_path=&#x27;vmaf_log.txt&#x27;:n_threads=20&quot; \
  -f null -
</code></pre>
<ul><li><code>[0:v]</code>：<strong>被测</strong>视频（压缩后）</li><li><code>[1:v]</code>：<strong>参考</strong>视频（原始），顺序不能搞反</li><li><code>model=version=vmaf_v0.6.1</code>：使用内置的 1080p 模型</li><li><code>log_path=&#x27;vmaf_log.txt&#x27;</code>：结果写入日志文件</li><li><code>n_threads=20</code>：用 20 线程并行计算（设成你的 CPU 核心数即可）</li><li><code>-f null -</code>：不生成任何输出文件，只运行滤镜计算</li></ul><h3 id="142-4k--4k-">14.2 4K 内容使用 4K 模型</h3><pre class="language-bash lang-bash"><code class="language-bash lang-bash">-filter_complex &quot;[0:v][1:v]libvmaf=model=version=vmaf_4k_v0.6.1:log_path=&#x27;vmaf_log.txt&#x27;:n_threads=20&quot;
</code></pre>
<h3 id="143-">14.3 帧对齐问题</h3><p>如果测出异常低的分数（比如平时都是 95 分，突然跳到 70 分），多半是帧没有对齐。在两个 <code>-i</code> 前各加 <code>-r 帧率</code> 强制同步：</p><pre class="language-bash lang-bash"><code class="language-bash lang-bash">ffmpeg -r 30 -i 压缩后.mp4 -r 30 -i 原始视频.mp4 \
  -filter_complex &quot;[0:v][1:v]libvmaf=model=version=vmaf_v0.6.1:log_path=&#x27;vmaf_log.txt&#x27;:n_threads=20&quot; \
  -f null -
</code></pre>
<p>另外，两个视频必须<strong>帧数和时长相同</strong>，建议测试前用 <code>-to</code> 裁剪到相同长度。</p><hr/><h2 id="">十五、完整命令参考</h2><h3 id="151-1080p-hevc-nvenc">15.1 1080p HEVC 二压（NVENC，质量优先，兼容性标签）</h3><pre class="language-bash lang-bash"><code class="language-bash lang-bash">ffmpeg -i 输入.mp4 \
  -vf &quot;scale=-2:1080&quot; \
  -c:v hevc_nvenc -pix_fmt yuv420p10le -profile:v main10 \
  -preset p7 -tune:v hq \
  -rc:v vbr -cq:v 28 -b:v 0 \
  -multipass 2 -rc-lookahead 32 \
  -spatial-aq 1 -temporal-aq 1 -aq-strength 10 \
  -g 300 -bf 4 -b_ref_mode 2 \
  -split_encode_mode 2 \
  -tag:v hvc1 \
  -c:a copy \
  -y 输出.mp4
</code></pre>
<h3 id="152-1080p-av1-nvenc-40-">15.2 1080p AV1 二压（NVENC 40 系，质量优先）</h3><pre class="language-bash lang-bash"><code class="language-bash lang-bash">ffmpeg -i 输入.mp4 \
  -vf &quot;scale=-2:1080&quot; \
  -c:v av1_nvenc -pix_fmt yuv420p10le \
  -preset p7 -tune:v hq \
  -rc:v vbr -cq:v 34 -b:v 0 \
  -multipass 2 -rc-lookahead 32 \
  -spatial-aq 1 -temporal-aq 1 -aq-strength 10 \
  -g 300 -bf 4 -b_ref_mode 2 \
  -split_encode_mode 2 \
  -c:a copy \
  -y 输出.mp4
</code></pre>
<h3 id="153-hevc-">15.3 HEVC 码率上限模式（适合有明确码率要求的场景）</h3><pre class="language-bash lang-bash"><code class="language-bash lang-bash">ffmpeg -i 输入.mp4 \
  -c:v hevc_nvenc -pix_fmt yuv420p10le \
  -preset p7 -tune:v hq \
  -rc:v vbr -b:v 2M -maxrate 3M -bufsize 5M \
  -multipass 2 -rc-lookahead 32 \
  -spatial-aq 1 -temporal-aq 1 -aq-strength 10 \
  -c:a copy \
  -y 输出.mp4
</code></pre>
<h3 id="154-url--2--720p">15.4 URL 输入，剪前 2 分钟，降至 720p</h3><pre class="language-bash lang-bash"><code class="language-bash lang-bash">export VIDEO=&quot;https://your-server.com/path/video.mp4?sign=token_here&quot;

ffmpeg -i &quot;$VIDEO&quot; \
  -to 120 \
  -vf &quot;scale=-2:720&quot; \
  -c:v hevc_nvenc -pix_fmt yuv420p10le \
  -preset p4 -tune:v hq \
  -rc:v vbr -cq:v 28 -b:v 0 \
  -c:a copy \
  -y 输出_720p.mp4
</code></pre>
<h3 id="155--ss---i-">15.5 精确剪切并二压（起点帧精确，-ss 在 -i 后）</h3><pre class="language-bash lang-bash"><code class="language-bash lang-bash">ffmpeg -i 输入.mp4 \
  -ss 00:01:30 -to 00:05:00 \
  -c:v hevc_nvenc -pix_fmt yuv420p10le \
  -preset p7 -tune:v hq \
  -rc:v vbr -cq:v 28 -b:v 0 \
  -c:a copy \
  -y 片段.mp4
</code></pre>
<h3 id="156-">15.6 关键帧快速剪切（秒出，不重编码）</h3><pre class="language-bash lang-bash"><code class="language-bash lang-bash">ffmpeg -ss 00:01:30 -i 输入.mp4 -to 00:03:30 -c copy -y 片段.mp4
</code></pre>
<h3 id="157--gpu-">15.7 双 GPU 同时跑两个任务</h3><pre class="language-bash lang-bash"><code class="language-bash lang-bash"># 终端 1（使用 GPU 0）
CUDA_VISIBLE_DEVICES=0 ffmpeg -i 视频A.mp4 \
  -c:v av1_nvenc -rc:v vbr -cq:v 34 -b:v 0 -preset p7 \
  -c:a copy -y 输出A.mp4

# 终端 2（使用 GPU 1）
CUDA_VISIBLE_DEVICES=1 ffmpeg -i 视频B.mp4 \
  -c:v av1_nvenc -rc:v vbr -cq:v 34 -b:v 0 -preset p7 \
  -c:a copy -y 输出B.mp4
</code></pre>
<h3 id="158-vmaf-">15.8 VMAF 对比参考（草稿示例中的参数）</h3><pre class="language-bash lang-bash"><code class="language-bash lang-bash">ffmpeg -i 参考视频.mp4 -i 被测视频.mp4 \
  -filter_complex &quot;[0:v][1:v]libvmaf=model=version=vmaf_v0.6.1:log_path=&#x27;vmaf_log.txt&#x27;:n_threads=20&quot; \
  -f null -
</code></pre>
<hr/><h2 id="">十六、常见问题与坑点</h2><h3 id="-impossible-to-convert-between-the-formats">❌ 报错：Impossible to convert between the formats</h3><p>完整错误：<code>Impossible to convert between the formats supported by the filter &#x27;Parsed_null_0&#x27; and the filter &#x27;auto_scale_0&#x27;</code></p><p><strong>原因</strong>：<code>-pix_fmt p010le</code> 与 <code>scale</code> 等滤镜不兼容</p><p><strong>解决</strong>：把 <code>-pix_fmt p010le</code> 改为 <code>-pix_fmt yuv420p10le</code></p><hr/><h3 id="--webdav---">❌ 输出到 WebDAV 失败 / 输出文件损坏无法播放</h3><p><strong>原因</strong>：MP4 写入完成后需要 seek 回文件头写 moov box，WebDAV 不支持 seek 写入</p><p><strong>解决</strong>：</p><ol start="1"><li>输出到本地，完成后手动上传（最稳）</li><li>改用 MKV 格式输出（对 seek 要求更低）</li><li>使用 rclone mount 并确认挂载方式支持 seek</li></ol><hr/><h3 id="-hevc--safari--ios-">❌ HEVC 在 Safari / iOS 不播放</h3><p>把 <code>-tag:v hev1</code> 改为 <code>-tag:v hvc1</code>（或显式加上 <code>-tag:v hvc1</code>）</p><hr/><h3 id="-">❌ 拼接后音视频不同步</h3><ul><li>检查各片段帧率是否一致，不一致先用 <code>-r</code> 统一</li><li><code>-c copy</code> 拼接要求时间戳连续，如果各片段来源复杂，改为重新编码</li><li>最稳方案：拼接时不用 <code>-c copy</code>，同时编码，代价是时间更长</li></ul><hr/><h3 id="-cq-">❌ CQ 模式下文件比预期大很多</h3><ul><li>检查 CQ 值是否选错（参考上方 CRF/CQ 对应表）</li><li>加 <code>-maxrate</code> 和 <code>-bufsize</code> 限制峰值码率</li><li>检查 <code>-b:v 0</code> 是否有加（不加的话 NVENC 可能以默认码率为目标）</li></ul><hr/><h2 id="">十七、参数速查表</h2><table><thead><tr><th> 参数 </th><th> 含义 </th><th> 常用值/说明 </th></tr></thead><tbody><tr><td> <code>-i 文件</code> </td><td> 输入文件 </td><td> 本地路径或 URL（URL 加引号） </td></tr><tr><td> <code>-c:v</code> </td><td> 视频编码器 </td><td> <code>hevc_nvenc</code> <code>av1_nvenc</code> <code>libx265</code> <code>copy</code> </td></tr><tr><td> <code>-c:a</code> </td><td> 音频编码器 </td><td> <code>aac</code> <code>copy</code> <code>libopus</code> </td></tr><tr><td> <code>-vf &quot;...&quot;</code> </td><td> 视频滤镜 </td><td> <code>scale=-2:1080</code> <code>setpts=0.5*PTS</code> </td></tr><tr><td> <code>-af &quot;...&quot;</code> </td><td> 音频滤镜 </td><td> <code>atempo=2.0</code> <code>apad</code> </td></tr><tr><td> <code>-pix_fmt</code> </td><td> 像素格式 </td><td> <code>yuv420p10le</code>（10bit推荐）<code>yuv420p</code>（8bit兼容） </td></tr><tr><td> <code>-ss 时间</code> </td><td> 起始时间 </td><td> 放 <code>-i</code> 前：快速seek；放 <code>-i</code> 后：精确seek </td></tr><tr><td> <code>-to 时间</code> </td><td> 截止时间（绝对） </td><td> <code>120</code> <code>00:02:00</code> </td></tr><tr><td> <code>-t 时长</code> </td><td> 持续时长（相对） </td><td> <code>90</code> </td></tr><tr><td> <code>-r 帧率</code> </td><td> 输出帧率 </td><td> <code>24</code> <code>30</code> <code>60</code> </td></tr><tr><td> <code>-y</code> </td><td> 覆盖已有输出 </td><td> 不加则交互询问 </td></tr><tr><td> <code>-map</code> </td><td> 选择特定流 </td><td> <code>0:v:0</code> <code>0:a:1</code> </td></tr><tr><td> <code>-preset</code> </td><td> 编码速度预设 </td><td> NVENC: <code>p1</code>-<code>p7</code>；CPU: <code>fast/medium/slow</code> </td></tr><tr><td> <code>-tune:v</code> </td><td> 调优目标 </td><td> <code>hq</code>（高质量）<code>ull</code>（低延迟） </td></tr><tr><td> <code>-rc:v vbr</code> </td><td> 码率控制：可变码率 </td><td> 配合 <code>-cq:v</code> 或 <code>-b:v</code> 使用 </td></tr><tr><td> <code>-cq:v</code> </td><td> NVENC 恒定质量 </td><td> HEVC: <code>24-32</code>；AV1: <code>30-40</code> </td></tr><tr><td> <code>-crf</code> </td><td> CPU 恒定质量 </td><td> x264/x265: <code>18-28</code>；svtav1: <code>28-38</code> </td></tr><tr><td> <code>-global_quality</code> </td><td> QSV 恒定质量 </td><td> <code>20-28</code>，接近 CRF 逻辑 </td></tr><tr><td> <code>-qp_i</code> <code>-qp_p</code> </td><td> AMF 量化参数 </td><td> <code>24-32</code>，两者设相同值 </td></tr><tr><td> <code>-b:v</code> </td><td> 目标码率 </td><td> <code>2M</code> <code>500k</code>（M 要大写） </td></tr><tr><td> <code>-maxrate</code> </td><td> 最大码率 </td><td> <code>3M</code> </td></tr><tr><td> <code>-bufsize</code> </td><td> 缓冲区大小 </td><td> <code>5M</code>（建议为 maxrate 的 1.5-2 倍） </td></tr><tr><td> <code>-profile:v main10</code> </td><td> HEVC 10bit Profile </td><td> 配合 <code>-pix_fmt yuv420p10le</code> </td></tr><tr><td> <code>-tag:v hvc1</code> </td><td> 强制 HEVC 兼容标签 </td><td> Web/iOS 分发推荐 </td></tr><tr><td> <code>-g</code> </td><td> GOP 长度 </td><td> <code>300</code>（帧率×10） </td></tr><tr><td> <code>-bf</code> </td><td> B 帧数量 </td><td> <code>4</code>（上限） </td></tr><tr><td> <code>-b_ref_mode 2</code> </td><td> B 帧金字塔参考 </td><td> 推荐 </td></tr><tr><td> <code>-multipass 2</code> </td><td> 两遍编码 </td><td> 提升压缩效率 </td></tr><tr><td> <code>-rc-lookahead 32</code> </td><td> 前瞻帧数 </td><td> <code>32</code>（最大值） </td></tr><tr><td> <code>-spatial-aq 1</code> </td><td> 空间自适应量化 </td><td> <code>1</code> 开启 </td></tr><tr><td> <code>-temporal-aq 1</code> </td><td> 时间自适应量化 </td><td> <code>1</code> 开启 </td></tr><tr><td> <code>-aq-strength</code> </td><td> AQ 强度 </td><td> <code>8-12</code> </td></tr><tr><td> <code>-split_encode_mode 2</code> </td><td> 多 NVENC 单元分块 </td><td> 不保证效果 </td></tr><tr><td> <code>-f null -</code> </td><td> 不输出文件 </td><td> 仅用于 VMAF 等测试计算 </td></tr><tr><td> <code>CUDA_VISIBLE_DEVICES=N</code> </td><td> 指定第 N+1 块 GPU </td><td> 写在命令前，临时生效 </td></tr></tbody></table><hr/><p><em>持续更新。后续可补充：字幕烧录、色彩空间转换（HDR→SDR）、批处理脚本、更多滤镜参数等。</em></p></div><p style="text-align:right"><a href="https://me.bcw2.top:3443/posts/tech/ffmpeg-video-compress-practical-guide#comments">看完了？说点什么呢</a></p></div>]]></description><link>https://me.bcw2.top:3443/posts/tech/ffmpeg-video-compress-practical-guide</link><guid isPermaLink="true">https://me.bcw2.top:3443/posts/tech/ffmpeg-video-compress-practical-guide</guid><dc:creator><![CDATA[bcw222]]></dc:creator><pubDate>Mon, 06 Apr 2026 15:32:15 GMT</pubDate></item><item><title><![CDATA[DEATH STRANDING - Don't Be So Serious]]></title><description><![CDATA[<div><blockquote>该渲染由 Shiro API 生成，可能存在排版问题，最佳体验请前往：<a href="https://me.bcw2.top:3443/posts/game/death-stranding-dont-be-so-serious">https://me.bcw2.top:3443/posts/game/death-stranding-dont-be-so-serious</a></blockquote><div><style>  .paragraph {
    text-indent: 0 !important;
  }
</style><audio controls="" autoPlay="" loop=""><source src="https://file.team.silveridge.cn:3443/f/EVvH9/Don%27t%20Be%20So%20Serious.mp3" type="audio/mpeg"/><p>Don&#x27;t Be So Serious - Low Roar</p></audio><p>Composed by：Ryan Joseph Karazija/Andrew Scheps/Mike Lindsay</p><p>Wake<br/>Heavy pulse<br/>Bloody eyes<br/>Sweaty clothes</p><p>New routine<br/>That I&#x27;ve learned<br/>To understand and know</p><p>Play my tricks<br/>Fragile mind<br/>Rest your head<br/>On me</p><p>Shut my eyes<br/>I&#x27;m not here<br/>There must be some mistake</p><p>Don&#x27;t be so serious<br/>(×4)</p><p>Watch your words<br/>Turn to dust<br/>As we forget<br/>As we move on</p><p>Still<br/>I refuse<br/>To let you slip away</p><p>Don&#x27;t be so serious<br/>(×4)</p><p>Magnets<br/>(×n)</p></div><p style="text-align:right"><a href="https://me.bcw2.top:3443/posts/game/death-stranding-dont-be-so-serious#comments">看完了？说点什么呢</a></p></div>]]></description><link>https://me.bcw2.top:3443/posts/game/death-stranding-dont-be-so-serious</link><guid isPermaLink="true">https://me.bcw2.top:3443/posts/game/death-stranding-dont-be-so-serious</guid><dc:creator><![CDATA[bcw222]]></dc:creator><pubDate>Wed, 01 Apr 2026 06:39:03 GMT</pubDate></item></channel></rss>