[{"data":1,"prerenderedAt":5050},["ShallowReactive",2],{"site-settings":3,"writing-\u002Fwriting\u002F2026-07-26-why-vector-database":103,"writing-navigation":297},{"site":4,"home":19,"writing":41,"services":49,"about":84},{"id":5,"title":6,"description":7,"extension":8,"footer":9,"location":13,"meta":14,"name":6,"stem":15,"theme":16,"__hash__":18},"site\u002Fsettings\u002Fsite.yml","Quilibra","这是我自己部署的小主页，用于记录想法并提供一些服务的入口。","yml",{"icp":10,"policeRecord":11,"credit":12},"皖ICP备2026023522号-1","皖公网安备34012102000966号","DESIGNED & BUILT BY GPT-5.6 IN CODEX","China · UTC+8",{},"settings\u002Fsite",{"defaultAccent":17},"#000000","jtRrbdN47gDyGs_u2ubGZhFFByRvoiraZTjokx9SF0A",{"id":20,"dock":21,"extension":8,"headline":25,"introduction":28,"kicker":29,"latestWriting":30,"meta":36,"modules":37,"stem":38,"systemsLabel":39,"__hash__":40},"homeSettings\u002Fsettings\u002Fhome.yml",{"writing":22,"services":23,"about":24},"文章","服务","关于",{"prefix":26,"emphasis":27},"你好，这里是","Equilibra","这是我自己部署的个人主页，用于记录我的一些事情并维护我的一些服务。","个人主页 \u002F 持续施工中",{"enabled":31,"title":32,"linkLabel":33,"emptyLabel":34,"limit":35},true,"最近写下","全部文章","暂无文章",3,{},{"services":31,"rgbAssembly":31},"settings\u002Fhome","SYSTEMS","mLQiZfULI1ARi0lqmzbNtwFTadrRDtSbuzkF2UyyPzU",{"id":42,"emptyLabel":43,"extension":8,"intro":44,"meta":45,"seoDescription":46,"stem":47,"__hash__":48},"writingSettings\u002Fsettings\u002Fwriting.yml","没有找到匹配的文章。","随笔、学习、交易，以及一些观察。",{},"随笔、学习、交易与观察。","settings\u002Fwriting","YEMHVxKRvAlcMP_kI5F46TdJR19p9ErI69cG5BbP_j8",{"id":50,"accessNotes":51,"accessTitle":54,"extension":8,"intro":55,"items":56,"meta":80,"seoDescription":81,"stem":82,"__hash__":83},"servicesSettings\u002Fsettings\u002Fservices.yml",[52,53],"公开入口由 VPS 上的 Nginx 提供 HTTPS 与反向代理。","家中服务通过私网连接，不直接暴露 。","访问说明","统一入口，分别运行。尚未开放的服务会在域名和登录保护完成后上线。",[57,63,69,75],{"name":58,"description":59,"href":60,"state":61,"icon":62,"showOnHome":31},"Code","浏览器里的开发环境","https:\u002F\u002Fcode.quilibra.cn","online","code",{"name":64,"description":65,"href":66,"state":67,"icon":68,"showOnHome":31},"Photos","照片备份和浏览","https:\u002F\u002Fphotos.example.com","soon","image",{"name":70,"description":71,"href":72,"state":73,"icon":74,"showOnHome":31},"NAS","文件与存储管理","https:\u002F\u002Fnas.example.com","private","hard-drive",{"name":76,"description":77,"href":78,"state":61,"icon":79,"showOnHome":31},"Status","服务可用性与故障记录","#","activity",{},"开发环境、相册、NAS 和运行状态。","settings\u002Fservices","yLhlQNGotM4SYxIDWlMMScw4erJoBdsGRFf_uffFabk",{"id":85,"contactCopy":86,"contactLabel":87,"currentFocus":88,"extension":8,"focusLabel":89,"introduction":90,"meta":91,"seoDescription":92,"stem":93,"topicsLabel":94,"writingTopics":95,"__hash__":102},"aboutSettings\u002Fsettings\u002Fabout.yml","不是，你联系我干嘛？","联系","正在上班当牛马。","正在做的事","这是我自己部署的个人主页，用于记录我的想法并维护我的一些服务。",{},"一些基本信息，以及这个网站目前用来做什么。","settings\u002Fabout","关注的主题",[96,97,98,99,100,101],"看盘看线","捣鼓电脑","折腾NAS","踢踢野球","看看科普","玩币练胆","ZVRx6CC1-GDQUGPl4C4XUfgy30ovFv6LdaXNegQjWAo",{"id":104,"title":105,"body":106,"category":280,"date":281,"description":282,"extension":283,"hidden":284,"meta":285,"navigation":31,"path":286,"publishedAt":287,"seo":288,"slug":289,"stem":290,"tags":291,"updated":295,"__hash__":296},"writing\u002Fwriting\u002F2026-07-26-why-vector-database.md","向量库到底有什么用",{"type":107,"value":108,"toc":271},"minimark",[109,114,118,129,132,149,153,156,170,173,177,180,183,204,211,214,217,220,238,241,244,268],[110,111,113],"h2",{"id":112},"sql-存个相同列-join-一下不就行了","SQL 存个相同列 join 一下不就行了？",[115,116,117],"p",{},"这是我最初的问题。看起来很合理：embedding 不就是一串数字吗，存个字段，SQL join 一下就完事了。",[115,119,120,121,124,125,128],{},"但 SQL 擅长的是 ",[62,122,123],{},"WHERE A = B"," 这种精确匹配。Embedding 是高维浮点数组，代表语义特征。两句话语义相似，它们的向量数值却绝不相等。",[62,126,127],{},"WHERE Va = Vb"," 直接返回空。",[115,130,131],{},"所以向量库真正在做的事情有两层：",[133,134,135,143],"ol",{},[136,137,138,142],"li",{},[139,140,141],"strong",{},"数学计算","：用余弦相似度等算法算高维空间的\"距离\"，而不是判等。",[136,144,145,148],{},[139,146,147],{},"专属索引（ANN 算法）","：传统 B-Tree 索引对高维数据失效，会退化成全表扫描。向量库用 HNSW 之类的索引在高维空间建\"地图\"，做到毫秒级近似最近邻检索。",[110,150,152],{"id":151},"那-rag-到底是怎么跑的","那 RAG 到底是怎么跑的",[115,154,155],{},"搞清楚向量库的作用之后，整个 RAG 链路就顺了：",[133,157,158,161,164,167],{},[136,159,160],{},"用户提问 -> 经过 Embedding 模型 -> 转成向量",[136,162,163],{},"拿向量去向量库里找最近的文档片段",[136,165,166],{},"找到的相关资料 + 用户原问题一起拼成 Prompt 发给大模型",[136,168,169],{},"大模型结合资料回答，减少幻觉",[115,171,172],{},"核心就是：输入给大模型之前，先用向量检索把相关上下文捞回来，拼在一起再喂进去。",[110,174,176],{"id":175},"全量召回还是选几条","全量召回还是选几条？",[115,178,179],{},"这是第二个直觉问题：如果数据库里相关的东西很多，比如 50 页文本都沾边，全部塞进大模型？还是挑几条最相关的？",[115,181,182],{},"答案是必须精选截断。原因有三个：",[184,185,186,192,198],"ul",{},[136,187,188,191],{},[139,189,190],{},"上下文窗口有限","：大模型的 Context Window 有上限，塞不下就是塞不下。",[136,193,194,197],{},[139,195,196],{},"中间迷失","：上下文太长时，大模型容易忽略中间的内容。",[136,199,200,203],{},[139,201,202],{},"Token 费用","：输入越长越贵。",[115,205,206,207,210],{},"所以标准做法是设置 ",[62,208,209],{},"top_k","（比如 limit=5），只取相似度最高的前 N 个片段。入库之前也先把长文本切成小块（比如每段 500 字），精准匹配局部内容，避免带入无关段落。",[110,212,213],{"id":213},"但我的数据量没那么大",[115,215,216],{},"说到这里我反应过来了--我的复盘日志根本没多少数据。几万字而已，全塞进去不就完了？",[115,218,219],{},"确实可以。小数据量下\"全上下文注入\"更简单有效：",[184,221,222,228],{},[136,223,224,227],{},[139,225,226],{},"逻辑完整","：保留完整的时间线和因果链，不会因为切片断章取义。",[136,229,230,233,234,237],{},[139,231,232],{},"架构极简","：直接 SQL ",[62,235,236],{},"SELECT *","，全部喂给大模型，不需要向量索引和切片逻辑。",[110,239,240],{"id":240},"那向量库还有没有意义",[115,242,243],{},"有。即便是小数据量，向量库（或 SQL 的向量插件）仍然有几个不可替代的能力：",[184,245,246,256,262],{},[136,247,248,251,252,255],{},[139,249,250],{},"模糊语义匹配","：日志里写的是\"肝到两点\"或\"披星戴月\"，向量检索能匹配到\"加班\"的语义。传统 SQL ",[62,253,254],{},"LIKE"," 搜不到这种。",[136,257,258,261],{},[139,259,260],{},"扩展性","：日志会越积越多，一两年后可能就塞不进上下文窗口了。向量检索的成本随数据量亚线性增长，全量注入则是线性上升。",[136,263,264,267],{},[139,265,266],{},"聚类与挖掘","：基于向量距离可以做无标签的主题归类和情绪分析，这是全量注入给大模型做不到的。",[115,269,270],{},"所以比较务实的方案是：短周期（比如近一周）的分析直接 SQL 全量提取，喂给大模型；跨年度的特定问题搜索走向量匹配。两条路各管各的，不冲突。",{"title":272,"searchDepth":273,"depth":273,"links":274},"",2,[275,276,277,278,279],{"id":112,"depth":273,"text":113},{"id":151,"depth":273,"text":152},{"id":175,"depth":273,"text":176},{"id":213,"depth":273,"text":213},{"id":240,"depth":273,"text":240},"学习","2026-07-26","从\"SQL 加个列不就行了\"到理解向量检索和 RAG 的完整链路，以及小数据量场景下到底该怎么选。","md",false,{},"\u002Fwriting\u002F2026-07-26-why-vector-database","2026-07-26T00:09:14+08:00",{"title":105,"description":282},"why-vector-database","writing\u002F2026-07-26-why-vector-database",[292,293,294],"向量数据库","Embedding","RAG",null,"hdqIEfJHF7KE9cYD971ShCFoCR9FbJVBIvdlCmdrbf4",[298,650,1114,4575,4741,4935],{"id":299,"title":300,"body":301,"category":634,"date":635,"description":636,"extension":283,"hidden":284,"meta":637,"navigation":31,"path":638,"publishedAt":639,"seo":640,"slug":641,"stem":642,"tags":643,"updated":295,"__hash__":649},"writing\u002Fwriting\u002F2026-08-03-yen-depreciation-cognition-drilldown.md","从日元贬值到行为金融：一次认知逐层下钻",{"type":107,"value":302,"toc":623},[303,309,313,316,319,322,325,332,336,339,342,345,351,354,357,363,367,370,377,391,394,397,404,408,411,414,420,423,429,435,441,448,452,455,458,461,464,470,474,477,480,487,490,497,500,506,510,513,516,519,545,551,555,558,564,570,576,582,588,595,599,602,608,611,614,617],[304,305,306],"blockquote",{},[115,307,308],{},"一条\"日元贬值\"的新闻，可以被当成财经快讯听完即忘，也可以作为入口，一路下探到货币本质、利率定价、行为金融和国家制度设计。本文记录的是后者--一次通过连续追问凿穿金融概念表层的思考过程。",[110,310,312],{"id":311},"一日元为什么跌表层是利差底层是人口","一、日元为什么跌：表层是利差，底层是人口",[115,314,315],{},"2026年7月底，日元跌到近40年最低点，一度突破1美元兑163日元。日本政府单日动用约528亿美元干预，美国也罕见地参与了联合干预--这是2011年以来首次。",[115,317,318],{},"直接原因很清楚：美日利差。美国联邦基金利率约3.5%，日本政策利率仅约1%，差了2.5个百分点。资金天然从低息货币流向高息货币--借日元换美元吃利差，这是经典的套息交易（carry trade）。卖日元的人多了，日元就跌。",[115,320,321],{},"但\"利差大\"只是把问题往后推了一步。为什么日本不敢加息？因为日本政府债务超过GDP的220%，大幅加息意味着利息支出吞噬财政空间。为什么美国能加息？因为2021-2023年通胀高企，美联储必须收紧。为什么日本通胀来了却不像美国那样加息？因为日本经济长期疲软，加息可能直接把经济打回通缩。",[115,323,324],{},"再往下：日本经济为什么疲软？人口老龄化、劳动力萎缩、产业竞争力被中韩追赶。这些是慢变量，不会因为一次干预或一次加息就改变。",[115,326,327,328,331],{},"所以日元贬值的根因，不在汇率层面，而在人口结构和产业竞争力层面。",[139,329,330],{},"金融现象的表层是数字，底层是人口、技术和制度。"," 这条规律不只适用于日元--理解任何资产的长期走势，最终都会落到这几个慢变量上。",[110,333,335],{"id":334},"二汇率是什么从菜市场到欠条","二、汇率是什么：从\"菜市场\"到\"欠条\"",[115,337,338],{},"理解了\"为什么跌\"，下一个问题是\"这东西到底是什么\"。",[115,340,341],{},"一个有效的具象方式：把货币当成菜市场里的菜。美元是猪肉（全球硬通货），日元是卷心菜（量大、利息低）。\"1美元=163日元\"就是\"1斤猪肉换163斤卷心菜\"。美元加息了（大家都抢美元去存钱），猪肉涨价；或者日本央行大量印钞（卷心菜丰收），菜跌价。兑换比例就这么变了。",[115,343,344],{},"这个比喻抓住了供需关系，但有一个需要警惕的边界：它可能让人误以为货币是一种\"商品\"。实际上，货币不是商品，而是一张欠条。",[115,346,347,348],{},"1971年美元与黄金脱钩后，全球货币都不再锚定实物。纸币背后没有金库，电子支付连纸都没有。它的价值来自一个更抽象的东西：",[139,349,350],{},"国家信用，具体表现为税收驱动的需求。",[115,352,353],{},"想象一家公司不发现金，只发\"内部饭票\"。饭票有价值，是因为公司规定交水电费、租宿舍只收饭票。你为了住宿舍，就必须用劳动去换饭票。国家版的故事一样：法律规定交税只能用本国货币，全国几亿人为了交税和还贷，必须去赚这种\"积分\"。只要国家机器在运转，这张纸就有强制性信用。",[115,355,356],{},"如果国家疯狂印饭票而食堂里的鸡没变多，每张饭票换到的鸡丝就变少了--这是对内贬值（通胀）。如果外面的人觉得这家公司未来不行了，不愿意用原价买它的饭票--这是对外贬值（汇率下跌）。",[115,358,359,362],{},[139,360,361],{},"对内贬值看货币供给与商品供给的匹配，对外贬值看全球资本对该国未来的信心。两者可以独立发生。"," 日本的情况是：对内通胀终于有所抬头，但对外信心严重不足--因为利率太低，资本不愿意持有日元。",[110,364,366],{"id":365},"三利率的底层不是央行在定是人口和技术在定","三、利率的底层：不是央行在定，是人口和技术在定",[115,368,369],{},"汇率短期跟着利率跑。但利率是谁定的？",[115,371,372,373,376],{},"不是央行拍的板。央行只是跟随一个更底层的\"水位线\"--经济学称之为",[139,374,375],{},"自然利率","（natural rate of interest），即既不刺激也不抑制经济的平衡点。这个水位由两个慢变量决定：",[184,378,379,385],{},[136,380,381,384],{},[139,382,383],{},"人口结构","：干活的人多不多。劳动力萎缩，经济增长动力不足，自然利率就低。日本是典型--老龄化严重，利率从高位一路降到零甚至负值。",[136,386,387,390],{},[139,388,389],{},"生产率","：干活的效率高不高。技术进步快，单位产出提升，经济能承受更高的资金成本，自然利率就高。美国凭借AI和科技革命，至今仍能维持相对较高的利率。",[115,392,393],{},"央行在这个水位线上下做微调：通胀高了加息压制需求，经济冷了降息刺激需求。但央行的自由度有限。如果自然利率本身在下行（人口老化、增长放缓），央行长期只能跟着往下走。",[115,395,396],{},"这个框架对中国有直接意义。中国正在经历人口结构转型--劳动年龄人口在2012年前后见顶，此后持续下降。如果人口结构决定自然利率的方向，那么中国长期利率大概率会进入下行通道。但中国有一个日本当年没有的缓冲：科技产业升级。电动车、光伏、AI在提高单人产出，用机器替代人。这意味着利率下行的速度可能比日本慢，但方向很难逆转。",[115,398,399,400,403],{},"这个判断对个人的直接含义：",[139,401,402],{},"如果你的房贷利率是3%-4%，在未来利率可能降至1%-2%的环境下，它就是一个\"高息包袱\"。"," 除非你有收益率超过这个利率的投资渠道，否则提前还贷在数学上等价于锁定了一个确定的高收益。",[110,405,407],{"id":406},"四套息交易逻辑越简单容错率越低","四、套息交易：逻辑越简单，容错率越低",[115,409,410],{},"套息交易的逻辑简单到一句话：借低息货币，买高息货币，吃利差。",[115,412,413],{},"和研究个股相比，它的\"变量数量\"少得多。研究一家公司要看财报、管理层、行业竞争、原材料价格、销售渠道，还要面对信息不对称。而套息交易的核心变量只有一个：两国国债收益率之差。只要这个差在，钱就会流，方向就确定。",[115,415,416,417],{},"但金融市场有一个铁律：",[139,418,419],{},"越是逻辑直白的交易，赚起来越考验风控。",[115,421,422],{},"原因有三层：",[115,424,425,428],{},[139,426,427],{},"杠杆放大波动。"," 套息交易的利差通常只有2-3个百分点，不加杠杆收益微薄。所以参与者几乎都用杠杆，10倍、50倍很常见。日元如果突然反向升值1%，10倍杠杆就是亏10%--吃两年的利差，一天亏完。波动再大就会穿仓，不光亏光本金，还倒欠券商。",[115,430,431,434],{},[139,432,433],{},"逻辑透明导致踩踏。"," 所有人都知道\"借日元买美元赚利息\"，全球累积了数万亿美元的套利盘。一旦日本央行突然加息或出现黑天鹅事件，所有人同时平仓：卖掉美元资产，抢回日元还债。日元急速暴涨，套利者两边挨打--美元资产跌了，换回日元还的钱也变多了。这种踩踏在金融史上被称为\"寡妇制造者交易\"（widow-maker trade）。",[115,436,437,440],{},[139,438,439],{},"赚的是息差，亏的是本金。"," 股票赚的是企业成长的钱，蛋糕在变大。套利赚的是\"定价偏差\"，市场平稳时像收租，但波动率一上来，那点利息根本不够覆盖汇率亏损。",[115,442,443,444,447],{},"简言之：",[139,445,446],{},"股票像种地--看天看地，收成慢，但地还在；汇率套利像走钢丝--路是直的，一眼看到头，但容错率极低。"," 简单不等于容易。正因为简单，它吸引的人更多，踩踏时也更惨烈。",[110,449,451],{"id":450},"五汇率高不等于国力强","五、汇率高不等于国力强",[115,453,454],{},"一个常见的直觉误区：既然1美元能换163日元，那美元\"贵\"，说明美国比日本强。推而广之，哪个国家汇率最高，哪个国家国力最强。",[115,456,457],{},"这个推论不成立。科威特第纳尔1个能换约3.3美元，但没人会认为科威特国力是美国的3倍。英镑比美元\"贵\"，也不意味着英国比美国强。",[115,459,460],{},"汇率数字的高低，本质上是\"计量单位\"的差异。日元面值大，是因为日本历史上经历过严重通胀，货币单位被刻度得很大。这就像有的东西按克卖，有的按吨卖，不代表按克卖的东西更贵重。",[115,462,463],{},"评价国家强弱，看的是\"总购买力\"而非\"单价\"。按购买力平价（PPP）计算，中国的GDP已经超过美国，但人民币兑美元汇率才7点多。制造业大国--日本、德国、中国--没有一个是汇率最高的。高汇率国家往往是资源型经济体（科威特、挪威），产业结构单一，恰恰不是\"国力强\"的表现。",[115,465,466,469],{},[139,467,468],{},"汇率反映的不是国力绝对值，而是全球资本对该国未来利息和通胀的预期。"," 对一个国家来说，汇率太高意味着出口商品变贵、竞争力下降。最有利的状态不是\"最高\"，而是\"稳定\"。",[110,471,473],{"id":472},"六为什么人明知正确却选择错误","六、为什么人明知正确，却选择错误",[115,475,476],{},"所有长期逻辑都指向\"确定性的正确\"：定投指数、提前还贷、配置黄金、减少高息负债。但大多数散户依然选择追涨杀跌、加杠杆、炒短线。",[115,478,479],{},"为什么？",[115,481,482,483,486],{},"行为经济学给出的一个解释是",[139,484,485],{},"双曲贴现","（hyperbolic discounting）：人对\"近期的等待\"比\"远期的等待\"更不耐烦。现在给你100块还是明天给你110块？多数人选现在。365天后给你100块还是366天后给你110块？多数人愿意多等一天。同样是多等一天换10块，但\"现在\"这个时间节点会让人产生断崖式的厌恶。",[115,488,489],{},"双曲贴现的核心特征是\"时间不一致性\"：今天的自己和未来的自己偏好不同。今天你决定\"下个月开始存钱\"，但下个月到了，你又决定\"再等一个月\"。这种偏好反转，才是双曲贴现区别于普通\"时间偏好\"的关键。",[115,491,492,493,496],{},"但这个模型不能解释全部。还有一个更朴素的因素：",[139,494,495],{},"人的寿命有限，而\"长期正确\"需要的等待时间可能超过个人的时间预算。"," 一个40岁的人知道定投20年后会有可观回报，但60岁才兑现的收益，对他当下的房贷、孩子学费、父母医疗费毫无帮助。长期正确解决不了短期的现金流压力。",[115,498,499],{},"这不是不理性，而是一个约束条件下的理性选择。当流动性紧张时，\"长期最优\"让位于\"短期存活\"是合理的。问题出在：很多人即使不缺流动性，也依然无法执行长期策略。这才是双曲贴现真正起作用的领域--不是因为等不起，而是因为大脑天然不认\"20年后\"的账。",[115,501,502,505],{},[139,503,504],{},"应对方式不是\"更自律\"，而是\"减少需要自律的场景\"。"," 自动扣款让钱在到手之前就被锁定，不给大脑的即时反应系统留出决策窗口。把\"20年后退休\"替换为\"给孩子存大学学费\"或\"55岁环游世界\"这样的具体画面，让远期目标变得可感知。这些不是心灵鸡汤，而是针对认知偏差的结构性对策。",[110,507,509],{"id":508},"七制度作为人性的补丁","七、制度作为人性的补丁",[115,511,512],{},"如果个体的短视是结构性的，社会的应对也必须是结构性的。",[115,514,515],{},"中国的五险一金就是这样一个\"强制性纠偏系统\"。养老保险对抗\"等老了再存\"的拖延，医疗保险对抗\"我身体好\"的盲目，公积金对抗\"月光族\"的生存危机。国家知道你管不住未来的自己，所以在你年轻时强行没收你的\"短视权\"。",[115,517,518],{},"全球各国都在做类似的事，但路径不同：",[184,520,521,527,533,539],{},[136,522,523,526],{},[139,524,525],{},"新加坡","（中央公积金 CPF）：完全个人账户制，钱在自己名下，多存多得，回报率稳定在4%以上。本质是\"对自己负责\"。",[136,528,529,532],{},[139,530,531],{},"德国、日本","（现收现付制）：年轻人缴费供养退休者，考验人口结构。本质是\"代际互助\"。",[136,534,535,538],{},[139,536,537],{},"美国","（兜底制）：社保缴费比例仅12.4%（雇主雇员各6.2%），是主要国家中最低的，大头靠个人投资和401(k)。本质是\"基本兜底，自求多福\"。",[136,540,541,544],{},[139,542,543],{},"中国","：混合体。社会统筹（现收现付）+ 个人账户（积累制），但个人账户长期空账运行--你缴的钱并没有真正存在你名下，而是被用来支付当代退休人员。",[115,546,547,548],{},"这里有一个值得深思的矛盾。制度设计上写了\"你的钱在你账户里\"，实际上账户是空的。这与新加坡CPF的真金白银有本质区别。如果年轻人发现自己缴的钱并没有真正存下来，制度本身的信用就会受损，反而可能加剧短视行为--\"既然钱没存下来，不如现在就花掉\"。",[139,549,550],{},"制度对抗短视的有效性，最终取决于它能否维持\"代际契约\"的可信度。",[110,552,554],{"id":553},"八普通人能做什么","八、普通人能做什么",[115,556,557],{},"在国内合规框架内，普通人想应对汇率波动，大致有这几条路：",[115,559,560,563],{},[139,561,562],{},"QDII基金。"," 用人民币买基金，机构帮你换汇投资海外。人民币贬值时，海外资产换回人民币会多出一笔汇兑收益。门槛低，支付宝就能买。但约半数QDII限购，场内ETF溢价可能超20%，赎回到账需7-10天。",[115,565,566,569],{},[139,567,568],{},"结构性存款。"," 保本浮动收益，收益挂钩汇率走势。判断对了拿高息，错了拿低息，但不亏本金。是境内最接近\"押注汇率方向\"的合规工具，通常1万人民币起。",[115,571,572,575],{},[139,573,574],{},"黄金。"," 以美元计价，人民币贬值时国内金价通常上涨。适合配置5%-10%作为对冲。",[115,577,578,581],{},[139,579,580],{},"外币定存。"," 直接换美元存定期，部分银行利率可达3%-3.6%。但致命风险是汇率反向：如果人民币升值幅度超过利差，利息会被完全吞噬甚至本金亏损。",[115,583,584,587],{},[139,585,586],{},"港卡渠道。"," 持有香港银行卡可以接触更多产品（外币联系存款、跨境理财通、美元定存），但2026年开户门槛已大幅提高，且外汇保证金交易在内地仍属灰色地带。",[115,589,590,591,594],{},"核心原则：",[139,592,593],{},"用本来就要花的\"刚需外币\"去做，赚了是白捡，亏了也无所谓。"," 如果判断不了方向，最稳妥的\"套利\"是提前还掉高息负债--它等于一个保本保息、无汇率风险的确定收益。",[110,596,598],{"id":597},"九金融的底层逻辑是时间","九、金融的底层逻辑是时间",[115,600,601],{},"回头看这条思考路径：从\"日元为什么跌\"到\"汇率是什么\"到\"货币是什么\"到\"利率谁定的\"到\"人为什么不按长期逻辑行动\"到\"国家如何对抗人性短视\"。每一步都是被\"为什么\"推着往下走的。",[115,603,604,605],{},"这条路径揭示了一件事：",[139,606,607],{},"金融市场的底层逻辑只有两个字--\"时间\"。",[115,609,610],{},"汇率是两国未来的贴现。利率是人口和技术的影子价格。套息交易赚的是时间差，亏的是时间到了时的剧烈结算。双曲贴现是大脑对时间的非线性估值。五险一金是制度对个人时间偏好的强制纠正。",[115,612,613],{},"谁能忍受\"无聊\"和\"等待\"，谁就拿到了那笔被多数人放弃的确定性。这不是鸡汤，而是上述每一层逻辑共同指向的结论。",[615,616],"hr",{},[115,618,619],{},[620,621,622],"em",{},"本文基于一段 AI 辅助对话的思考路径整理改写，涉及的数据和判断请以权威来源为准，不构成投资建议。",{"title":272,"searchDepth":273,"depth":273,"links":624},[625,626,627,628,629,630,631,632,633],{"id":311,"depth":273,"text":312},{"id":334,"depth":273,"text":335},{"id":365,"depth":273,"text":366},{"id":406,"depth":273,"text":407},{"id":450,"depth":273,"text":451},{"id":472,"depth":273,"text":473},{"id":508,"depth":273,"text":509},{"id":553,"depth":273,"text":554},{"id":597,"depth":273,"text":598},"观察","2026-08-03","从一条日元贬值新闻出发，通过连续追问层层下探到货币本质、利率定价、行为金融和国家制度设计，展示金融概念表层与底层逻辑之间的认知路径。",{},"\u002Fwriting\u002F2026-08-03-yen-depreciation-cognition-drilldown","2026-08-03T22:38:00+08:00",{"title":300,"description":636},"yen-depreciation-cognition-drilldown","writing\u002F2026-08-03-yen-depreciation-cognition-drilldown",[644,645,646,647,648,485],"汇率","行为金融","货币","利率","套息交易","kIenjYoPU_FJ1T9-wg1getkmWiFJP8itZnPcBDVN0A0",{"id":651,"title":652,"body":653,"category":280,"date":1096,"description":1097,"extension":283,"hidden":284,"meta":1098,"navigation":31,"path":1099,"publishedAt":1100,"seo":1101,"slug":1102,"stem":1103,"tags":1104,"updated":295,"__hash__":1113},"writing\u002Fwriting\u002F2026-07-29-etf-premium-discount-cost-of-carry.md","从ETF到商品期货：申赎机制、折溢价与持有成本模型",{"type":107,"value":654,"toc":1075},[655,659,663,666,680,683,686,689,749,752,755,769,780,783,797,800,804,807,814,817,840,843,846,866,869,880,883,886,890,893,896,911,914,940,946,949,954,957,961,1012,1015,1035,1038,1052,1055,1061,1064,1071],[110,656,658],{"id":657},"etf与lof申赎机制决定折溢价","ETF与LOF：申赎机制决定折溢价",[660,661,662],"h3",{"id":662},"底层机制差异",[115,664,665],{},"ETF和LOF虽然都是场内可交易的开放式基金，但申赎机制截然不同：",[184,667,668,674],{},[136,669,670,673],{},[139,671,672],{},"ETF：实物申赎，T+0瞬时套利。"," 机构投资者用一篮子股票换取基金份额，最小申赎单位通常为50万或100万份，门槛在百万级别。每日盘前基金公司公布PCF（Portfolio Composition File）清单，列明当日申赎对应的一篮子股票明细。",[136,675,676,679],{},[139,677,678],{},"LOF：现金申赎，跨系统转托管需T+2日。"," 门槛低（1份起），普通投资者即可参与，但套利存在时滞和净值波动风险。",[115,681,682],{},"这个差异是两者折溢价收敛速度天差地别的根本原因。",[660,684,685],{"id":685},"现金替代的四种标志",[115,687,688],{},"ETF实物申赎中，并非每只成分股都能完美交收。PCF清单对每只股票标注现金替代标志：",[690,691,692,705],"table",{},[693,694,695],"thead",{},[696,697,698,702],"tr",{},[699,700,701],"th",{},"标志",[699,703,704],{},"含义",[706,707,708,719,729,739],"tbody",{},[696,709,710,716],{},[711,712,713],"td",{},[139,714,715],{},"禁止",[711,717,718],{},"必须用股票实物交收",[696,720,721,726],{},[711,722,723],{},[139,724,725],{},"允许",[711,727,728],{},"申购可现金替代，赎回必须给股票，多退少补",[696,730,731,736],{},[711,732,733],{},[139,734,735],{},"必须",[711,737,738],{},"申赎均必须用现金，针对停牌或待剔除股票",[696,740,741,746],{},[711,742,743],{},[139,744,745],{},"退补",[711,747,748],{},"必须现金替代，按基金公司实际买卖成本多退少补",[660,750,751],{"id":751},"折溢价的形成",[115,753,754],{},"场内基金有两个价格：",[184,756,757,763],{},[136,758,759,762],{},[139,760,761],{},"IOPV（参考净值）","：实时计算的基金份额参考价值",[136,764,765,768],{},[139,766,767],{},"市价","：二级市场交易价格",[115,770,771,772,775,776,779],{},"当市价 > IOPV，即为",[139,773,774],{},"溢价","；反之则为",[139,777,778],{},"折价","。",[115,781,782],{},"套利机制是折溢价收敛的驱动力：",[184,784,785,791],{},[136,786,787,790],{},[139,788,789],{},"溢价套利","：T日买入一篮子股票 → 申购ETF份额 → 卖出ETF，全流程T+0完成",[136,792,793,796],{},[139,794,795],{},"折价套利","：T日买入ETF → 赎回为一篮子股票 → 卖出股票，同样T+0",[115,798,799],{},"正因为ETF套利链条畅通，折溢价通常控制在1%以内。而LOF因T+2转托管时滞，套利者需承担两天净值波动风险，折溢价常达5%以上。",[110,801,803],{"id":802},"跨境etf全现金替代与qdii额度约束","跨境ETF：全现金替代与QDII额度约束",[660,805,806],{"id":806},"一个常见误区",[115,808,809,810,813],{},"\"我有了一篮子股票，如何申购跨境ETF？\"——这个问题本身存在误区。跨境ETF的成分股在境外，境内投资者无法持有和交收这些股票，因此",[139,811,812],{},"跨境ETF不能实物申购，只能全现金替代","。投资者交付现金，由基金公司代为买入境外成分股。",[660,815,816],{"id":816},"现金申购流程",[133,818,819,822,825,828,831,834,837],{},[136,820,821],{},"确认券商具备跨境ETF申赎资格",[136,823,824],{},"T日盘前查阅PCF清单（现金替代标志全为\"退补\"）",[136,826,827],{},"核对当日QDII额度与开放状态（可能限购，极端情况单日仅100元）",[136,829,830],{},"计算所需现金（现金替代款 + 预估现金差额），最小单位50万份",[136,832,833],{},"T日交易时段通过券商端提交委托",[136,835,836],{},"T+1日日终份额确认，T+2日可用",[136,838,839],{},"基金公司代买完成后，进行现金差额多退少补",[660,841,842],{"id":842},"三层结构与溢价固化",[115,844,845],{},"跨境ETF的运作涉及三层主体：",[184,847,848,854,860],{},[136,849,850,853],{},[139,851,852],{},"外汇局","：审批QDII额度（总量约1708亿美元）",[136,855,856,859],{},[139,857,858],{},"基金公司","：在额度内换汇，代投资者买入境外成分股",[136,861,862,865],{},[139,863,864],{},"境内投资者","：只能通过现金提交申购申请",[115,867,868],{},"当额度充裕时，套利机制正常运行。但当额度耗尽时：",[133,870,871,874,877],{},[136,872,873],{},"基金公司暂停或限额申购（单日低至10元）",[136,875,876],{},"一级市场申购通道堵死，套利者无法创造新份额",[136,878,879],{},"二级市场资金只能抢存量份额，溢价飙升至10%-50%+",[115,881,882],{},"此外，现金申赎 + 代买模式下，跨时区汇率波动和境外开盘价差导致退补款不可预知，套利收益无法锁定，进一步抑制套利者进场意愿，固化高溢价。",[115,884,885],{},"典型场景：QDII ETF因外汇额度受限，一级市场申购通道关闭，套利链条断裂，溢价长期高达10%-40%。",[110,887,889],{"id":888},"商品期货持有成本模型与期限结构","商品期货：持有成本模型与期限结构",[660,891,892],{"id":892},"持有成本定价公式",[115,894,895],{},"商品期货的理论价格由持有成本模型决定：",[897,898,902],"pre",{"className":899,"code":900,"language":901,"meta":272,"style":272},"language-plain shiki shiki-themes github-light github-dark","F = S · e^((r + u − y) · T)\n","plain",[62,903,904],{"__ignoreMap":272},[905,906,909],"span",{"class":907,"line":908},"line",1,[905,910,900],{},[115,912,913],{},"其中：",[184,915,916,922,928,934],{},[136,917,918,921],{},[139,919,920],{},"S","：现货价格",[136,923,924,927],{},[139,925,926],{},"r","：无风险利率（资金占用成本）",[136,929,930,933],{},[139,931,932],{},"u","：仓储保险成本率",[136,935,936,939],{},[139,937,938],{},"y","：便利收益率（convenience yield）",[115,941,942,945],{},[139,943,944],{},"便利收益","是持有实物库存的隐性收益——当供应紧张时，手上有货意味着生产不断线、客户不流失，这种\"随时可用\"的价值就是便利收益。它与库存水平成反比：库存越低，便利收益越高。",[660,947,948],{"id":948},"基差及其收敛",[115,950,951],{},[139,952,953],{},"基差 = 现货价格(S) − 期货价格(F)",[115,955,956],{},"随着交割日临近，持仓成本（r+u−y）T逐渐衰减趋零，期货价格向现货价格收敛，基差必然趋近于零。否则将触发无风险套利：买低卖高，交割平仓。",[660,958,960],{"id":959},"期限结构contango与backwardation","期限结构：Contango与Backwardation",[690,962,963,978],{},[693,964,965],{},[696,966,967,970,973,976],{},[699,968,969],{},"结构",[699,971,972],{},"形态",[699,974,975],{},"成因",[699,977,704],{},[706,979,980,996],{},[696,981,982,987,990,993],{},[711,983,984],{},[139,985,986],{},"Contango",[711,988,989],{},"S \u003C F近 \u003C F远",[711,991,992],{},"r+u > y，库存充足",[711,994,995],{},"常态结构，持有成本主导",[696,997,998,1003,1006,1009],{},[711,999,1000],{},[139,1001,1002],{},"Backwardation",[711,1004,1005],{},"S > F近 > F远",[711,1007,1008],{},"r+u \u003C y，库存紧张",[711,1010,1011],{},"供应短缺，现货溢价",[660,1013,1014],{"id":1014},"品种实例",[184,1016,1017,1023,1029],{},[136,1018,1019,1022],{},[139,1020,1021],{},"黄金","：投资性商品，便利收益y≈0，恒为Contango结构",[136,1024,1025,1028],{},[139,1026,1027],{},"原油","：消费性商品，便利收益波动剧烈，常在Contango与Backwardation间切换。紧缺时深度Backwardation，过剩时转Contango",[136,1030,1031,1034],{},[139,1032,1033],{},"农产品","：受季节性收获周期影响，仓储成本u与便利收益y交替主导曲线形态",[660,1036,1037],{"id":1037},"套利操作",[184,1039,1040,1046],{},[136,1041,1042,1045],{},[139,1043,1044],{},"正向套利（Cash and Carry）","：当F实际 > F理论时，借钱买入现货 + 卖出期货，锁定差价",[136,1047,1048,1051],{},[139,1049,1050],{},"反向套利（Reverse Cash and Carry）","：当F实际 \u003C F理论时，卖空现货 + 买入期货。但对消费性商品，卖空现货难以操作",[660,1053,1054],{"id":1054},"远期与期货的凸性调整",[115,1056,1057,1058,779],{},"期货合约每日盯市（mark-to-market），而远期合约到期才结算。当标的资产价格与利率正相关时，期货价格略高于远期价格，这一差异称为",[139,1059,1060],{},"凸性调整",[110,1062,1063],{"id":1063},"串联",[115,1065,1066,1067,1070],{},"三个主题看似分散，实则共享一条逻辑主线：",[139,1068,1069],{},"机制决定效率，效率决定价格形态","。ETF的T+0实物申赎使折溢价瞬间收敛；跨境ETF的额度瓶颈使套利链条断裂、溢价固化；商品期货的持有成本模型则将仓储、利率、库存预期统一为一个数学框架，解释了曲线形态的成因。理解机制本身，比记住某个具体结论更重要。",[1072,1073,1074],"style",{},"html .default .shiki span {color: var(--shiki-default);background: var(--shiki-default-bg);font-style: var(--shiki-default-font-style);font-weight: var(--shiki-default-font-weight);text-decoration: var(--shiki-default-text-decoration);}html .shiki span {color: var(--shiki-default);background: var(--shiki-default-bg);font-style: var(--shiki-default-font-style);font-weight: var(--shiki-default-font-weight);text-decoration: var(--shiki-default-text-decoration);}html .dark .shiki span {color: var(--shiki-dark);background: var(--shiki-dark-bg);font-style: var(--shiki-dark-font-style);font-weight: var(--shiki-dark-font-weight);text-decoration: var(--shiki-dark-text-decoration);}html.dark .shiki span {color: var(--shiki-dark);background: var(--shiki-dark-bg);font-style: var(--shiki-dark-font-style);font-weight: var(--shiki-dark-font-weight);text-decoration: var(--shiki-dark-text-decoration);}",{"title":272,"searchDepth":273,"depth":273,"links":1076},[1077,1082,1087,1095],{"id":657,"depth":273,"text":658,"children":1078},[1079,1080,1081],{"id":662,"depth":35,"text":662},{"id":685,"depth":35,"text":685},{"id":751,"depth":35,"text":751},{"id":802,"depth":273,"text":803,"children":1083},[1084,1085,1086],{"id":806,"depth":35,"text":806},{"id":816,"depth":35,"text":816},{"id":842,"depth":35,"text":842},{"id":888,"depth":273,"text":889,"children":1088},[1089,1090,1091,1092,1093,1094],{"id":892,"depth":35,"text":892},{"id":948,"depth":35,"text":948},{"id":959,"depth":35,"text":960},{"id":1014,"depth":35,"text":1014},{"id":1037,"depth":35,"text":1037},{"id":1054,"depth":35,"text":1054},{"id":1063,"depth":273,"text":1063},"2026-07-29","ETF实物申赎与LOF现金申赎的机制差异决定折溢价收敛速度，跨境ETF的QDII额度约束固化高溢价，商品期货持有成本模型统一解释期限结构形态。",{},"\u002Fwriting\u002F2026-07-29-etf-premium-discount-cost-of-carry","2026-07-29T10:51:17+08:00",{"title":652,"description":1097},"etf-premium-discount-cost-of-carry","writing\u002F2026-07-29-etf-premium-discount-cost-of-carry",[1105,1106,1107,1108,1109,1110,1111,1112],"ETF","LOF","跨境ETF","QDII","商品期货","持有成本模型","期限结构","折溢价","P-nL6PDPMB-K1dYMhb05-mjrddhVCF2rRa7sbCY8CXo",{"id":1115,"title":1116,"body":1117,"category":280,"date":281,"description":4562,"extension":283,"hidden":284,"meta":4563,"navigation":31,"path":4564,"publishedAt":4565,"seo":4566,"slug":4567,"stem":4568,"tags":4569,"updated":635,"__hash__":4574},"writing\u002Fwriting\u002F2026-07-26-quilibra-complete-technical-handbook.md","Quilibra 个人主页完整技术手册：从页面架构到内容发布与生产部署",{"type":107,"value":1118,"toc":4490},[1119,1122,1125,1145,1149,1152,1155,1163,1179,1193,1196,1200,1203,1223,1226,1229,1233,1236,1330,1333,1339,1345,1359,1363,1366,1372,1375,1408,1423,1427,1433,1467,1485,1496,1502,1548,1555,1559,1562,1626,1645,1652,1687,1700,1704,1710,1742,1760,1799,1802,1813,1817,1820,1843,1862,1881,1884,1888,1891,1919,1922,1926,1933,1990,2001,2004,2030,2033,2039,2045,2049,2060,2066,2073,2083,2106,2110,2120,2123,2143,2146,2186,2192,2196,2207,2210,2213,2227,2248,2254,2278,2289,2293,2304,2307,2334,2345,2348,2363,2367,2370,2386,2389,2395,2409,2416,2434,2438,2448,2455,2458,2472,2486,2493,2497,2508,2514,2522,2528,2535,2539,2542,2628,2631,2637,2664,2687,2700,2710,2714,2723,2726,2748,2751,2759,2765,2771,2778,2785,2799,2803,2810,2813,2836,2855,2858,2862,2869,2875,2884,2897,2901,2904,2910,2913,2919,2922,2928,2931,2937,2943,2955,2987,2994,3000,3025,3028,3033,3042,3047,3059,3079,3083,3089,3095,3108,3125,3134,3153,3156,3162,3166,3202,3267,3273,3276,3282,3293,3304,3308,3359,3370,3376,3380,3383,3389,3392,3434,3454,3465,3476,3480,3483,3487,3493,3524,3528,3534,3568,3572,3578,3616,3619,3622,3626,3629,3646,3653,3656,3659,3700,3705,3709,3712,3719,3736,3739,3743,3747,3756,3759,3768,3771,3780,3783,3797,3800,3813,3816,3823,3826,3840,3843,3849,3853,3857,3876,3879,3882,3909,3914,3918,3998,4007,4010,4100,4103,4106,4134,4145,4149,4153,4176,4179,4195,4199,4210,4213,4221,4224,4230,4234,4244,4248,4257,4267,4271,4277,4281,4284,4290,4293,4299,4309,4313,4316,4319,4322,4325,4328,4334,4337,4341,4344,4398,4401,4421,4424,4427,4487],[115,1120,1121],{},"这是一份给自己看的项目说明书。它主要以 2026 年 7 月 27 日的实际代码和 VPS 配置为准，从浏览器里看到的页面一直追到内容文件、Git 分支、构建产物、静态服务器和版本化 release。以后忘了某段代码为什么存在、主页颜色为什么能保存、CMS 保存为什么只出现在测试站，或者 3000、3100、3101 三个端口分别在做什么，可以从这里重新建立完整认识。",[115,1123,1124],{},"本文记录的是一个具体版本的实现，不是一套永远不变的规范。最可靠的事实来源始终是仓库中的代码；本文的作用，是把分散在 Vue、CSS、YAML、脚本和运维文件里的设计意图串起来。",[304,1126,1127],{},[115,1128,1129,1130,1133,1134,1137,1138,1141,1142,1144],{},"2026 年 8 月 3 日更新：网站已经移除长期 ",[62,1131,1132],{},"staging"," 分支、远程测试站和 ",[62,1135,1136],{},"promote"," 流程，改为 CMS 直接提交 ",[62,1139,1140],{},"master","、代码在本地功能分支验证后推送 ",[62,1143,1140],{},"。下文关于双环境发布的内容保留为历史架构记录，当前操作以仓库 README 和运维手册为准。",[110,1146,1148],{"id":1147},"一先用一句话理解整个网站","一、先用一句话理解整个网站",[115,1150,1151],{},"Quilibra（名字也是AI起的捏） 是一个由 Nuxt 生成的静态个人网站：页面结构写在 Vue 中，主页设置写在 YAML 中，文章写在 Markdown 中；发布时把这些源文件编译成普通 HTML、CSS、JavaScript 和图片，再由一个很小的 Node 静态服务器提供给浏览器。",[115,1153,1154],{},"它的静态生成核心是：",[897,1156,1161],{"className":1157,"code":1159,"language":1160,"meta":272},[1158],"language-text","Vue \u002F CSS \u002F YAML \u002F Markdown\n           |\n           | Nuxt generate\n           v\n      .output\u002Fpublic\n           |\n           | 复制到带时间戳的 release\n           v\n  releases\u002F\u003Crelease-id>\n           |\n           | 原子切换 current 符号链接\n           v\n     current -> 当前 release\n           |\n           | Node 静态服务器 :3000\n           v\n        浏览器 \u002F 反向代理\n","text",[62,1162,1159],{"__ignoreMap":272},[115,1164,1165,1166,1168,1169,1172,1173,1168,1175,1178],{},"现在这套链路有两份互不覆盖的实例：",[62,1167,1140],{}," 构建到 ",[62,1170,1171],{},"~\u002F.local\u002Fshare\u002Fpersonal-site\u002Fcurrent"," 并由 3000 提供正式站，",[62,1174,1132],{},[62,1176,1177],{},"~\u002F.local\u002Fshare\u002Fpersonal-site-staging\u002Fcurrent"," 并由 3100 提供测试站。两边都使用相同的生成和原子切换逻辑，但仓库、成功状态、release 目录和服务端口完全独立。",[115,1180,1181,1182],{},"这里最重要的边界是：",[139,1183,1184,1185,1188,1189,1192],{},"Git 分支中的源码是长期事实，",[62,1186,1187],{},".output\u002Fpublic"," 是可重新生成的临时产物，各环境 ",[62,1190,1191],{},"current"," 指向的 release 才是该环境当前真正提供的版本。",[115,1194,1195],{},"因此，修改 Markdown 或 YAML 只改变源码；只有重新生成并切换 release，线上页面才会改变。网站没有生产数据库，也没有在访问文章时临时渲染 Markdown。",[110,1197,1199],{"id":1198},"二为什么选择静态优先","二、为什么选择静态优先",[115,1201,1202],{},"个人主页的读取远多于写入，而且文章、简介和服务入口都不需要按访客实时计算。静态生成正好适合这种负载：",[184,1204,1205,1208,1211,1214,1217,1220],{},[136,1206,1207],{},"访问时不查询数据库，也不运行 Vue 服务端渲染逻辑；",[136,1209,1210],{},"HTML 已经在发布阶段生成，首屏可以直接返回完整内容；",[136,1212,1213],{},"生产服务只需要读取文件，故障面比常驻 Nuxt 服务小；",[136,1215,1216],{},"每个 release 都是一份独立、完整、可回滚的目录；",[136,1218,1219],{},"Markdown 和 YAML 都在 Git 中，内容、配置和代码可以一起审查与恢复；",[136,1221,1222],{},"CMS 只是源码编辑器，不会成为网站运行时依赖。",[115,1224,1225],{},"代价也很明确：每次内容变化都要重新生成站点；文章越多，生成时间越长；需要登录、评论、实时状态或用户数据时，必须接入独立服务，而不能假装静态文件可以承担动态业务。",[115,1227,1228],{},"当前项目主动接受这个取舍，因为稳定、透明和容易备份比“保存后数据库立刻生效”更重要。",[110,1230,1232],{"id":1231},"三技术栈与各自职责","三、技术栈与各自职责",[115,1234,1235],{},"当前主要依赖如下：",[690,1237,1238,1248],{},[693,1239,1240],{},[696,1241,1242,1245],{},[699,1243,1244],{},"技术",[699,1246,1247],{},"当前职责",[706,1249,1250,1258,1266,1274,1282,1290,1298,1306,1314,1322],{},[696,1251,1252,1255],{},[711,1253,1254],{},"Nuxt 4",[711,1256,1257],{},"应用框架、路由、预渲染、页面头信息和构建产物",[696,1259,1260,1263],{},[711,1261,1262],{},"Vue 3",[711,1264,1265],{},"组件、响应式状态、生命周期和浏览器交互",[696,1267,1268,1271],{},[711,1269,1270],{},"TypeScript",[711,1272,1273],{},"页面、组件、配置、内容脚本和测试的静态类型检查",[696,1275,1276,1279],{},[711,1277,1278],{},"Nuxt Content 3",[711,1280,1281],{},"读取 YAML\u002FMarkdown、建立集合、查询文章并渲染正文",[696,1283,1284,1287],{},[711,1285,1286],{},"Zod",[711,1288,1289],{},"约束站点设置与文章字段",[696,1291,1292,1295],{},[711,1293,1294],{},"Lucide Vue",[711,1296,1297],{},"返回、搜索、服务、调色等界面图标",[696,1299,1300,1303],{},[711,1301,1302],{},"原生 CSS",[711,1304,1305],{},"全部视觉、响应式布局、悬浮反馈和字标动画",[696,1307,1308,1311],{},[711,1309,1310],{},"Sveltia CMS",[711,1312,1313],{},"在浏览器中编辑仓库里的 YAML、Markdown 和上传资源",[696,1315,1316,1319],{},[711,1317,1318],{},"Playwright",[711,1320,1321],{},"真实浏览器中的交互、布局、响应式和回归测试",[696,1323,1324,1327],{},[711,1325,1326],{},"Node.js",[711,1328,1329],{},"内容工具、同步与发布脚本、生产和测试静态服务器",[115,1331,1332],{},"没有引入 Tailwind、组件库、动画框架、运行时数据库或外部字体。页面使用系统无衬线字体和系统等宽字体回退，这减少了字体请求，也避免了字体加载完成时重新排版。",[115,1334,1335,1338],{},[62,1336,1337],{},"package.json"," 中最常用的入口是：",[897,1340,1343],{"className":1341,"code":1342,"language":1160,"meta":272},[1158],"npm run dev              本地临时 Nuxt 开发服务\nnpm run generate         生成静态站点\nnpm run typecheck        Nuxt\u002FVue TypeScript 检查\nnpm run content:validate 校验全部文章与站点设置\nnpm run test:content     内容校验器与受限 writer 的单元测试\nnpm run test:release     release 工具的单元测试\nnpm run test:e2e         Playwright 浏览器测试\nnpm run publish          发布未提交的受管内容\nnpm run deploy           部署已经提交且工作区干净的代码版本\nnpm run sync             拉取指定 GitHub 分支并自动检查、构建和部署\nnpm run promote          把测试站确认过的 CMS 内容安全发布到生产\nnpm run rollback         切换回指定历史 release\n",[62,1344,1342],{"__ignoreMap":272},[115,1346,1347,1350,1351,1354,1355,1358],{},[62,1348,1349],{},"build"," 与 ",[62,1352,1353],{},"preview"," 仍是 Nuxt 的常规命令，但当前生产链路以 ",[62,1356,1357],{},"generate"," 和自有静态服务器为准。",[110,1360,1362],{"id":1361},"四目录结构与所有权边界","四、目录结构与所有权边界",[115,1364,1365],{},"理解这个项目，先要知道“改什么就去哪里”。",[897,1367,1370],{"className":1368,"code":1369,"language":1160,"meta":272},[1158],"personal-site\u002F\n├── app\u002F\n│   ├── app.vue                         全站外壳、主题初始化、动态 favicon\n│   ├── assets\u002Fcss\u002Fmain.css             全站视觉与响应式规则\n│   ├── components\u002F\n│   │   ├── ArtisticWordmark.vue        可编辑艺术字与重播控制\n│   │   ├── RgbAssembly.vue             RGB 滑杆、预设与重置\n│   │   ├── ServiceIcon.vue             服务图标名称到 Lucide 的映射\n│   │   └── ServiceStatusDot.vue        服务状态点\n│   ├── composables\u002F\n│   │   ├── useAccentTheme.ts           主题色状态、混色、对比色与持久化\n│   │   ├── useSiteSettings.ts          并行读取并组合五份站点设置\n│   │   └── useWordmarkAnimation.ts     跨组件字标重播信号\n│   ├── config\u002F                         文章分类、服务状态和图标枚举\n│   ├── pages\u002F                          首页、文章、服务、关于页面\n│   └── error.vue                       错误页面\n├── content\u002F\n│   ├── settings\u002F                       全站与四个页面的独立 YAML 配置\n│   └── writing\u002F*.md                    正式文章\n├── public\u002F\n│   ├── admin\u002F                          Sveltia CMS 入口与字段配置\n│   ├── brand\u002F                          字标位图资源\n│   ├── uploads\u002F                        文章上传资源\n│   └── favicon.svg                     静态 favicon 回退\n├── scripts\u002F\n│   ├── content\u002Fvalidate.ts             内容结构校验\n│   ├── content\u002Fwrite.ts                受限文章写入、更新与删除\n│   ├── release.ts                      测试、生成、提交、发布和回滚\n│   ├── promote.ts                      staging 内容安全合并到 master\n│   ├── sync.ts                         GitHub 拉取、代理、构建与部署\n│   ├── webhook.ts                      GitHub 签名校验、事件队列与部署触发\n│   └── static-server.mjs               生产与测试静态文件服务器\n├── tests\u002F                              单元、发布和浏览器测试\n├── ops\u002F\n│   ├── openclaw\u002F...\u002FSKILL.md           OpenClaw 文章发布 Skill\n│   ├── nginx\u002F                           正式域名、测试域名和默认拒绝站点\n│   └── systemd\u002F                         静态、Webhook 与同步服务\n├── content.config.ts                   Nuxt Content 集合\n├── content.schema.ts                   站点设置 Schema\n├── nuxt.config.ts                      Nuxt 与静态预渲染配置\n└── playwright.config.ts                E2E 临时服务与浏览器配置\n",[62,1371,1369],{"__ignoreMap":272},[115,1373,1374],{},"可以把这些文件分成四层：",[133,1376,1377,1383,1389,1398],{},[136,1378,1379,1382],{},[62,1380,1381],{},"content\u002F"," 是可频繁修改的数据层。",[136,1384,1385,1388],{},[62,1386,1387],{},"app\u002F"," 是页面表现和交互层。",[136,1390,1391,1350,1394,1397],{},[62,1392,1393],{},"scripts\u002F",[62,1395,1396],{},"ops\u002F"," 是发布和运行层。",[136,1399,1400,1403,1404,1407],{},[62,1401,1402],{},".output\u002F","、",[62,1405,1406],{},".nuxt*"," 是构建缓存或产物，不是手工维护的源码。",[115,1409,1410,1411,1414,1415,1418,1419,1422],{},"CMS 默认只管理 ",[62,1412,1413],{},"content\u002Fsettings\u002F"," 中五份固定 YAML、",[62,1416,1417],{},"content\u002Fwriting\u002F"," 和 ",[62,1420,1421],{},"public\u002Fuploads\u002F","。Vue、CSS、CMS 配置、脚本或测试发生变化时，应当视为代码变更，走完整代码部署流程；设置目录中的未知文件也不会自动进入内容发布白名单。",[110,1424,1426],{"id":1425},"五nuxt-配置与构建数据库","五、Nuxt 配置与构建数据库",[115,1428,1429,1432],{},[62,1430,1431],{},"nuxt.config.ts"," 做了几件关键的事：",[184,1434,1435,1442,1448,1451,1454,1461,1464],{},[136,1436,1437,1438,1441],{},"加载 ",[62,1439,1440],{},"@nuxt\u002Fcontent","；",[136,1443,1444,1445,1441],{},"全局引入 ",[62,1446,1447],{},"app\u002Fassets\u002Fcss\u002Fmain.css",[136,1449,1450],{},"关闭开发工具；",[136,1452,1453],{},"设置中文页面语言和移动端 viewport；",[136,1455,1456,1457,1460],{},"使用 ",[62,1458,1459],{},"out-in"," 页面切换过渡；",[136,1462,1463],{},"允许 Nitro 爬取内部链接并预渲染，遇到错误时直接让构建失败；",[136,1465,1466],{},"把 Nuxt Content 的本地数据库放到与构建目录对应的位置。",[115,1468,1469,1470,1418,1473,1476,1477,1480,1481,1484],{},"Nuxt Content 3 在构建过程中会建立 SQLite 内容索引。开发环境默认使用 ",[62,1471,1472],{},".nuxt",[62,1474,1475],{},".data\u002Fcontent\u002Fcontents.sqlite","；release 构建传入 ",[62,1478,1479],{},"NUXT_BUILD_DIR=.nuxt-release","，E2E 使用 ",[62,1482,1483],{},".nuxt-e2e","。把三种构建目录分开，是为了避免开发服务器、正式生成和 Playwright 同时读写同一个内容缓存。",[115,1486,1487,1488,1491,1492,1495],{},"这里的 SQLite 是",[139,1489,1490],{},"构建时索引","，不是生产文章数据库。它帮助 Nuxt Content 查询 Markdown；静态页面生成完成后，生产服务器只负责输出构建结果。遇到 ",[62,1493,1494],{},"no such table: _content_writing"," 时，通常是本地 Nuxt Content 缓存被并发构建或中断破坏，不代表文章源文件丢失。",[115,1497,1498,1501],{},[62,1499,1500],{},"content.config.ts"," 定义了六个集合：",[184,1503,1504,1526],{},[136,1505,1506,1403,1509,1403,1512,1403,1515,1403,1518,1521,1522,1525],{},[62,1507,1508],{},"site",[62,1510,1511],{},"homeSettings",[62,1513,1514],{},"writingSettings",[62,1516,1517],{},"servicesSettings",[62,1519,1520],{},"aboutSettings","：五个 ",[62,1523,1524],{},"data"," 集合，各自读取同名职责的 YAML；",[136,1527,1528,1531,1532,1535,1536,1539,1540,1543,1544,1547],{},[62,1529,1530],{},"writing","：",[62,1533,1534],{},"page"," 类型，读取 ",[62,1537,1538],{},"writing\u002F**\u002F*.md","，因此每篇文章会拥有可路由的 ",[62,1541,1542],{},"path"," 和可供 ",[62,1545,1546],{},"ContentRenderer"," 使用的正文。",[115,1549,1550,1551,1554],{},"E2E 测试通过 ",[62,1552,1553],{},"NUXT_CONTENT_CWD=tests\u002Ffixtures\u002Fcontent"," 把文章集合切换到固定 fixture，测试不会依赖真实文章的标题和数量，也不会改动生产内容。",[110,1556,1558],{"id":1557},"六站点设置五份-yaml-如何保持页面边界","六、站点设置：五份 YAML 如何保持页面边界",[115,1560,1561],{},"站点设置按后台入口拆成五份文件：",[690,1563,1564,1574],{},[693,1565,1566],{},[696,1567,1568,1571],{},[699,1569,1570],{},"文件",[699,1572,1573],{},"管理内容",[706,1575,1576,1586,1596,1606,1616],{},[696,1577,1578,1583],{},[711,1579,1580],{},[62,1581,1582],{},"site.yml",[711,1584,1585],{},"站点名称、浏览器标题、SEO、位置、默认主题色、备案和署名",[696,1587,1588,1593],{},[711,1589,1590],{},[62,1591,1592],{},"home.yml",[711,1594,1595],{},"首页介绍、标题、最近文章、服务矩阵、RGB 模块和底部入口",[696,1597,1598,1603],{},[711,1599,1600],{},[62,1601,1602],{},"writing.yml",[711,1604,1605],{},"文章列表页介绍、SEO 和空状态",[696,1607,1608,1613],{},[711,1609,1610],{},[62,1611,1612],{},"services.yml",[711,1614,1615],{},"服务列表、状态、图标、主页显示和访问说明",[696,1617,1618,1623],{},[711,1619,1620],{},[62,1621,1622],{},"about.yml",[711,1624,1625],{},"关于页介绍、当前状态、关注主题和联系文案",[115,1627,1628,1629,1632,1633,1636,1637,1640,1641,1644],{},"拆分不只是 CMS 菜单分组。如果多个表单只声明同一 YAML 的部分字段，保存其中一个表单可能覆盖另一个表单未声明的字段；因此每个入口必须拥有独立源文件。",[62,1630,1631],{},"useSiteSettings()"," 使用固定异步数据键 ",[62,1634,1635],{},"site-settings","，通过 ",[62,1638,1639],{},"Promise.all"," 并行查询五个 data collection，再返回结构化的 ",[62,1642,1643],{},"site\u002Fhome\u002Fwriting\u002Fservices\u002Fabout"," 对象。任何一份缺失都会立即抛出 500，而不是让页面带着部分设置运行。",[115,1646,1647,1648,1651],{},"这些配置不是任意 YAML。",[62,1649,1650],{},"content.schema.ts"," 为每个文件提供独立严格 Schema，例如：",[184,1653,1654,1660,1663,1666,1675,1678,1684],{},[136,1655,1656,1657,1441],{},"默认强调色必须是 ",[62,1658,1659],{},"#RRGGBB",[136,1661,1662],{},"最新文章数量只能是 1 到 3；",[136,1664,1665],{},"服务数量只能是 1 到 12；",[136,1667,1668,1669,1403,1671,1403,1673,1441],{},"服务状态只能是 ",[62,1670,61],{},[62,1672,73],{},[62,1674,67],{},[136,1676,1677],{},"图标只能使用代码中已有的枚举；",[136,1679,1680,1681,1683],{},"服务链接只能是 ",[62,1682,78],{}," 或完整的 HTTPS URL；",[136,1685,1686],{},"对象使用严格模式，拼错字段不会被静默忽略。",[115,1688,1689,1692,1693,1695,1696,1699],{},[62,1690,1691],{},"public\u002Fadmin\u002Fconfig.yml"," 的表单选项、",[62,1694,1650],{}," 的运行时约束、",[62,1697,1698],{},"app\u002Fconfig\u002Fsite.ts"," 的 TypeScript 枚举应该同步修改。只改 CMS 下拉框，不改 Schema，保存后会校验失败；只改 Schema，不改组件映射，界面可能没有正确图标。",[110,1701,1703],{"id":1702},"七全站外壳与水合边界","七、全站外壳与水合边界",[115,1705,1706,1709],{},[62,1707,1708],{},"app\u002Fapp.vue"," 是每个路由外面的共同外壳。它负责：",[184,1711,1712,1715,1718,1721,1727,1730,1736,1739],{},[136,1713,1714],{},"在页面加载时取得站点设置；",[136,1716,1717],{},"判断当前是否为主页；",[136,1719,1720],{},"初始化和恢复主题色；",[136,1722,1723,1724,1441],{},"把主题 CSS 变量挂到 ",[62,1725,1726],{},".site-shell",[136,1728,1729],{},"为非主页显示“返回”按钮与主题预设切换按钮；",[136,1731,1732,1733,1441],{},"设置标题模板、站点描述、Open Graph 信息和 ",[62,1734,1735],{},"theme-color",[136,1737,1738],{},"根据当前强调色实时生成 data URL favicon；",[136,1740,1741],{},"显示 Nuxt 页面加载进度条。",[115,1743,1744,1747,1748,1751,1752,1755,1756,1759],{},[62,1745,1746],{},"hydrated"," 初始为 ",[62,1749,1750],{},"false","，在 ",[62,1753,1754],{},"onMounted"," 完成主题恢复和本地监听注册后变成 ",[62,1757,1758],{},"true","，最终反映为：",[897,1761,1765],{"className":1762,"code":1763,"language":1764,"meta":272,"style":272},"language-html shiki shiki-themes github-light github-dark","\u003Cdiv class=\"site-shell\" data-hydrated=\"true\">\n","html",[62,1766,1767],{"__ignoreMap":272},[905,1768,1769,1773,1777,1781,1784,1788,1791,1793,1796],{"class":907,"line":908},[905,1770,1772],{"class":1771},"sVt8B","\u003C",[905,1774,1776],{"class":1775},"s9eBZ","div",[905,1778,1780],{"class":1779},"sScJk"," class",[905,1782,1783],{"class":1771},"=",[905,1785,1787],{"class":1786},"sZZnC","\"site-shell\"",[905,1789,1790],{"class":1779}," data-hydrated",[905,1792,1783],{"class":1771},[905,1794,1795],{"class":1786},"\"true\"",[905,1797,1798],{"class":1771},">\n",[115,1800,1801],{},"这个属性既是测试可观察的“客户端已经接管页面”标志，也是字标动画的播放闸门。CSS 动画先建立，但在页面完成水合前保持暂停，防止服务端 HTML 刚显示就消耗掉一段动画，等 JavaScript 接管时只剩最终帧。",[115,1803,1804,1805,1808,1809,1812],{},"非主页的全局返回入口固定回到 ",[62,1806,1807],{},"\u002F","，文本只有“返回”，图标和 ",[62,1810,1811],{},"aria-label"," 补充了方向与完整语义。它与文章正文中的“返回文章”属于不同层级：前者返回网站主页，后者返回文章列表。",[110,1814,1816],{"id":1815},"八主页是一张单屏工作台","八、主页是一张单屏工作台",[115,1818,1819],{},"主页不是按多个营销区块向下滚动，而是一张固定在一个视口高度内的工作台。核心区域包括：",[184,1821,1822,1825,1828,1831,1834,1837,1840],{},[136,1823,1824],{},"顶部站点标识、上海时区时钟、指针坐标和主题按钮；",[136,1826,1827],{},"左上介绍、普通标题前缀与 Quilibra 字标；",[136,1829,1830],{},"右上服务矩阵；",[136,1832,1833],{},"左下最近三篇文章；",[136,1835,1836],{},"右下 RGB 调色器；",[136,1838,1839],{},"底部中央文章、服务、关于三个入口；",[136,1841,1842],{},"两侧备案与署名。",[115,1844,1845,1846,1849,1850,1853,1854,1857,1858,1861],{},"首页在 ",[62,1847,1848],{},"setup"," 阶段并行取得站点设置和最近文章。文章查询按 ",[62,1851,1852],{},"date DESC"," 排序，再按 YAML 中的 ",[62,1855,1856],{},"limit"," 截取。服务矩阵先筛选 ",[62,1859,1860],{},"showOnHome: true","，按 YAML 顺序最多取四个；服务页仍然展示完整列表。",[115,1863,1864,1865,1868,1869,1872,1873,1876,1877,1880],{},"顶部时钟通过 ",[62,1866,1867],{},"Intl.DateTimeFormat"," 固定使用 ",[62,1870,1871],{},"Asia\u002FShanghai","，每 30 秒更新一次。指针坐标不是每次 ",[62,1874,1875],{},"pointermove"," 都直接改 DOM：事件只保存最新坐标，然后在下一帧 ",[62,1878,1879],{},"requestAnimationFrame"," 中统一计算。元素边界在挂载与窗口缩放时缓存，触摸指针被忽略。这样可以减少高频事件中的布局读取和响应式更新。",[115,1882,1883],{},"工作台没有为了装饰引入 canvas 或 WebGL。网格、面板、条纹、阴影和入场效果全部由 CSS 完成，浏览器可以直接合成绝大多数变换。",[110,1885,1887],{"id":1886},"九响应式布局为何不只是按比例缩小","九、响应式布局为何不只是“按比例缩小”",[115,1889,1890],{},"主页有大量绝对定位元素，因此响应式的目标不是把桌面页面机械缩成手机版，而是维护清晰的空间约束：",[184,1892,1893,1900,1903,1906,1909,1916],{},[136,1894,1895,1896,1899],{},"桌面端通过 ",[62,1897,1898],{},"clamp()","、视口高度和稳定的面板高度控制上下关系；",[136,1901,1902],{},"最近文章与 RGB 面板在宽桌面上对齐顶部和底部；",[136,1904,1905],{},"小屏笔记本缩小间距和模块尺寸，仍避免重叠；",[136,1907,1908],{},"768 像素附近的紧凑布局隐藏无法舒适操作的 RGB 面板；",[136,1910,1911,1912,1915],{},"手机端重新排列主页元素，并使用 ",[62,1913,1914],{},"overflow: clip"," 防止装饰或过渡产生横向滚动；",[136,1917,1918],{},"固定格式组件使用明确的 grid 列、最小宽度和高度，标题悬浮时不会重新计算可用宽度。",[115,1920,1921],{},"Playwright 会在 768×1024、1024×768、1440×900 和 3440×1440 上读取真实元素几何信息，验证介绍不压住服务矩阵、上方面板不压住底部导航、RGB 与最近文章对齐、页面没有横向溢出。这里测试的是关系，而不是容易因一个像素改动就失效的整页截图。",[110,1923,1925],{"id":1924},"十主题色从一个-rgb-值扩展成整套界面","十、主题色从一个 RGB 值扩展成整套界面",[115,1927,1928,1929,1932],{},"主题逻辑集中在 ",[62,1930,1931],{},"useAccentTheme.ts","。唯一的核心状态是：",[897,1934,1938],{"className":1935,"code":1936,"language":1937,"meta":272,"style":272},"language-ts shiki shiki-themes github-light github-dark","interface RgbColor {\n  r: number\n  g: number\n  b: number\n}\n","ts",[62,1939,1940,1952,1965,1974,1984],{"__ignoreMap":272},[905,1941,1942,1946,1949],{"class":907,"line":908},[905,1943,1945],{"class":1944},"szBVR","interface",[905,1947,1948],{"class":1779}," RgbColor",[905,1950,1951],{"class":1771}," {\n",[905,1953,1954,1958,1961],{"class":907,"line":273},[905,1955,1957],{"class":1956},"s4XuR","  r",[905,1959,1960],{"class":1944},":",[905,1962,1964],{"class":1963},"sj4cs"," number\n",[905,1966,1967,1970,1972],{"class":907,"line":35},[905,1968,1969],{"class":1956},"  g",[905,1971,1960],{"class":1944},[905,1973,1964],{"class":1963},[905,1975,1977,1980,1982],{"class":907,"line":1976},4,[905,1978,1979],{"class":1956},"  b",[905,1981,1960],{"class":1944},[905,1983,1964],{"class":1963},[905,1985,1987],{"class":907,"line":1986},5,[905,1988,1989],{"class":1771},"}\n",[115,1991,1992,1993,1996,1997,2000],{},"三个内置预设是红、绿、蓝。站点设置里的 ",[62,1994,1995],{},"theme.defaultAccent"," 可以是任意合法十六进制颜色，并不要求与预设一致。当前颜色存放在 Nuxt ",[62,1998,1999],{},"useState"," 中，因此同一客户端的页面切换不会创建互相冲突的主题实例。",[115,2002,2003],{},"主题计算分为几步：",[133,2005,2006,2009,2018,2021,2027],{},[136,2007,2008],{},"把每个通道四舍五入并限制在 0 到 255。",[136,2010,2011,2012,2015,2016,779],{},"生成 CSS ",[62,2013,2014],{},"rgb(r g b)"," 与大写 ",[62,2017,1659],{},[136,2019,2020],{},"把强调色分别与白色、画布色和黑色混合，得到 soft、faint、canvas、grid、strong 等衍生色。",[136,2022,2023,2024,779],{},"计算 sRGB 相对亮度，对比黑白文字的对比度，自动选择更清晰的 ",[62,2025,2026],{},"--accent-ink",[136,2028,2029],{},"把结果以 CSS 变量挂到站点外壳，按钮、悬浮底色、网格、字标和 favicon 共同消费这些变量。",[115,2031,2032],{},"主题变量的意义大致如下：",[897,2034,2037],{"className":2035,"code":2036,"language":1160,"meta":272},[1158],"--accent          原始强调色\n--accent-ink      强调色背景上的黑或白文字\n--accent-soft     大面积悬浮反馈\n--accent-faint    更浅的面板背景\n--accent-canvas   轻微带主题倾向的页面画布\n--accent-grid     网格与线条色\n--accent-strong   向黑色混合后的强调色\n",[62,2038,2036],{"__ignoreMap":272},[115,2040,2041,2042,2044],{},"这样做比在 CSS 中到处硬编码“红色按钮、浅红背景、深红边框”更可靠。换成极亮或极暗的自定义颜色时，文字对比度仍会自动调整。E2E 会直接把颜色调到纯白和纯黑，验证 ",[62,2043,2026],{}," 分别变成深色和白色。",[110,2046,2048],{"id":2047},"十一主题持久化与旧黄色迁移","十一、主题持久化与旧黄色迁移",[115,2050,2051,2052,2055,2056,2059],{},"客户端挂载时，",[62,2053,2054],{},"restoreSavedAccent()"," 从 ",[62,2057,2058],{},"localStorage"," 读取：",[897,2061,2064],{"className":2062,"code":2063,"language":1160,"meta":272},[1158],"site-accent-rgb   完整 RGB 对象\nsite-accent       red \u002F green \u002F blue \u002F custom 等名称\n",[62,2065,2063],{"__ignoreMap":272},[115,2067,2068,2069,2072],{},"恢复后，深度监听 ",[62,2070,2071],{},"accentRgb","，后续每次变化都会重新保存。由于服务端无法读取浏览器存储，静态 HTML 首先使用 YAML 中的默认颜色；客户端挂载后再恢复访客上次的选择。",[115,2074,2075,2076,1350,2079,2082],{},"旧版本曾使用黄色默认主题。为了避免已经访问过网站的浏览器永远被旧缓存锁在黄色，恢复逻辑专门识别 ",[62,2077,2078],{},"{255,255,2}",[62,2080,2081],{},"yellow"," 的组合，删除这两个旧值并回到当前 YAML 默认色。这是一次小型数据迁移：不是清空所有人的自定义配色，只清除能够明确识别的旧默认值。",[115,2084,2085,2086,2089,2090,2093,2094,2097,2098,2101,2102,2105],{},"动态 favicon 也跟随主题和站点名称。",[62,2087,2088],{},"app.vue"," 在内存中生成一个包含主题背景色与 ",[62,2091,2092],{},"site.name"," 首字符的 SVG，再编码为 ",[62,2095,2096],{},"data:image\u002Fsvg+xml","。因此 CMS 修改站点名称并重新构建后，标签页字母会同步改变；图标底色、字色和 ",[62,2099,2100],{},"meta[name=theme-color]"," 则继续与 RGB 面板同步。",[62,2103,2104],{},"public\u002Ffavicon.svg"," 只作为静态回退。",[110,2107,2109],{"id":2108},"十二rgb-调色器的交互细节","十二、RGB 调色器的交互细节",[115,2111,2112,2115,2116,2119],{},[62,2113,2114],{},"RgbAssembly.vue"," 不是独立维护另一份颜色，它调用同一个 ",[62,2117,2118],{},"useAccentTheme()","，因此滑杆、预设按钮、页面按钮和 favicon 始终共享状态。",[115,2121,2122],{},"三个 range input 分别控制 R、G、B：",[184,2124,2125,2131,2134,2137,2140],{},[136,2126,2127,2130],{},[62,2128,2129],{},"input"," 事件实时更新颜色，拖动时页面连续渐变；",[136,2132,2133],{},"每条轨道通过 CSS 变量计算已填充百分比；",[136,2135,2136],{},"输出区显示十六进制、十进制通道值，并采用自动对比文字；",[136,2138,2139],{},"点击 RED、GREEN、BLUE 立即加载预设；",[136,2141,2142],{},"重置按钮恢复 YAML 中的默认颜色，而不是写死某个预设。",[115,2144,2145],{},"字标动画不能在滑杆的每一个像素变化时重播，否则会不断重建动画并显得卡顿。组件因此区分“预览变化”和“提交变化”：",[184,2147,2148,2155,2161,2171,2178],{},[136,2149,2150,2151,2154],{},"指针按下后标记 ",[62,2152,2153],{},"pointerActive","，并捕获当前 pointer；",[136,2156,2157,2158,2160],{},"拖动中的 ",[62,2159,2129],{}," 只改变颜色；",[136,2162,2163,2166,2167,2170],{},[62,2164,2165],{},"pointerup"," 或 ",[62,2168,2169],{},"pointercancel"," 才请求重播一次字标；",[136,2172,2173,2174,2177],{},"键盘调整 range 时由 ",[62,2175,2176],{},"change"," 请求重播；",[136,2179,2180,2181,1418,2183,2185],{},"一个零延时抑制标志防止同一次鼠标提交同时触发 ",[62,2182,2165],{},[62,2184,2176],{},"，造成双播。",[115,2187,2188,2189],{},"预设与重置属于离散操作，所以点击时立即换色并重播。这个模型可以概括为：",[139,2190,2191],{},"连续操作实时预览、松手提交动画；离散操作立即提交。",[110,2193,2195],{"id":2194},"十三quilibra-字标是可编辑的艺术字","十三、Quilibra 字标是可编辑的艺术字",[115,2197,2198,2199,2202,2203,2206],{},"当前字标不再依赖固定图片，而是由 ",[62,2200,2201],{},"content\u002Fsettings\u002Fhome.yml"," 中的 ",[62,2204,2205],{},"headline.emphasis"," 生成真实文本。CMS 的“首页 \u002F 艺术字文字”可以直接修改内容，Schema 将长度限制为 20 个字符；组件还会按照中英文字符宽度自动缩小过长文字，避免挤出首屏。",[115,2208,2209],{},"字形使用随构建产物一起发布的 Cormorant Garamond 600 Italic。页面只引入拉丁字符子集，既保留高对比衬线斜体的艺术感，也不依赖 Google Fonts 等站外服务；无法覆盖的字符会回退到本机衬线字体。",[115,2211,2212],{},"动画仍然采用“RGB 套色从偏移到合拢”，没有模拟笔尖逐笔书写。字标由四层相同文本组成：",[184,2214,2215,2218,2221,2224],{},[136,2216,2217],{},"红层从左下附近偏移进入；",[136,2219,2220],{},"绿层从上方偏移进入；",[136,2222,2223],{},"蓝层从右下附近偏移进入；",[136,2225,2226],{},"最终层使用当前主题色并带极轻的阴影。",[115,2228,2229,2230,2233,2234,1403,2237,1418,2240,2243,2244,2247],{},"前三层使用 ",[62,2231,2232],{},"mix-blend-mode: multiply","、动态模糊、透明度和不同位移。动画前段保留可见错版，中段快速靠近，后段降低彩色层透明度并让最终主题层聚焦。四层都只改变 ",[62,2235,2236],{},"opacity",[62,2238,2239],{},"transform",[62,2241,2242],{},"filter","，播放时临时声明 ",[62,2245,2246],{},"will-change","，结束后不长期占用合成资源。",[115,2249,2250,2253],{},[62,2251,2252],{},"ArtisticWordmark.vue"," 的加载控制解决了“首次打开没有动画”和字体中途替换的问题：",[133,2255,2256,2262,2269,2272,2275],{},[136,2257,2258,2261],{},[62,2259,2260],{},"@fontsource\u002Fcormorant-garamond"," 把字体文件交给 Vite 打包并生成带哈希的静态资源。",[136,2263,2264,2265,2268],{},"组件调用 ",[62,2266,2267],{},"document.fonts.load()","，等待艺术字体真正可用于绘制。",[136,2270,2271],{},"只有字体就绪、组件仍存活、页面可见且请求仍是最新时才开始播放。",[136,2273,2274],{},"字体加载失败时继续使用衬线回退字体，不阻断内容显示。",[136,2276,2277],{},"开启减少动态效果时直接展示最终状态。",[115,2279,2280,2281,2284,2285,2288],{},"每次重播都会增加 ",[62,2282,2283],{},"animationRun","，并把它放进四层的 Vue ",[62,2286,2287],{},"key","。这会让浏览器得到新的动画元素，可靠地从第 0 帧开始，而不是依赖移除 class 后强制读取布局。",[110,2290,2292],{"id":2291},"十四字标在什么时候重播","十四、字标在什么时候重播",[115,2294,2295,2296,2299,2300,2303],{},"全局 ",[62,2297,2298],{},"useWordmarkAnimation()"," 只维护一个数字信号。任何组件调用 ",[62,2301,2302],{},"requestReplay()","，数字加一；字标组件 watch 到变化后重播。这个很小的事件通道避免 RGB 组件直接引用字标 DOM。",[115,2305,2306],{},"当前重播场景包括：",[184,2308,2309,2312,2315,2318,2321,2324,2327],{},[136,2310,2311],{},"首次进入主页并且艺术字体加载完成；",[136,2313,2314],{},"点击主题预设或重置；",[136,2316,2317],{},"RGB 滑杆松手或键盘提交；",[136,2319,2320],{},"从文章、服务、关于页面返回主页，主页组件重新挂载；",[136,2322,2323],{},"浏览器标签页从隐藏变为可见；",[136,2325,2326],{},"窗口重新获得焦点；",[136,2328,2329,2330,2333],{},"浏览器的 ",[62,2331,2332],{},"pageshow","，包括可能来自往返缓存的恢复。",[115,2335,2336,1403,2339,1418,2342,2344],{},[62,2337,2338],{},"visibilitychange",[62,2340,2341],{},"focus",[62,2343,2332],{}," 可能在同一次切回中连续到达。组件使用一个 animation frame 合并激活请求，并设置 200ms 的最小间隔，防止一次切回连续播两三遍。页面变为隐藏时会取消待播放帧、使旧异步请求失效并结束当前播放。",[115,2346,2347],{},"这里还有两个无障碍与容错原则：",[184,2349,2350,2356],{},[136,2351,2352,2355],{},[62,2353,2354],{},"prefers-reduced-motion: reduce"," 时不播放套色，直接显示最终字标；",[136,2357,2358,2359,2362],{},"可读文字仍存在于 ",[62,2360,2361],{},"sr-only"," 中，视觉 mask 不承担标题语义。",[110,2364,2366],{"id":2365},"十五服务矩阵图标与状态点","十五、服务矩阵、图标与状态点",[115,2368,2369],{},"每个服务在 YAML 中有六个字段：名称、说明、链接、状态、图标、是否显示在主页。",[115,2371,2372,2375,2376,1350,2378,2381,2382,2385],{},[62,2373,2374],{},"ServiceIcon.vue"," 把业务名称映射到 Lucide 图标。NAS 使用独立的 ",[62,2377,74],{},[62,2379,2380],{},"HardDrive","，不会复用影音播放图标。未知值虽然有 ",[62,2383,2384],{},"Server"," 回退，但正常内容会在 Schema 阶段被拒绝，因此回退只是组件级防御。",[115,2387,2388],{},"状态共有三种：",[897,2390,2393],{"className":2391,"code":2392,"language":1160,"meta":272},[1158],"online   在线\nprivate  仅限本人\nsoon     准备中\n",[62,2394,2392],{"__ignoreMap":272},[115,2396,2397,2400,2401,2404,2405,2408],{},[62,2398,2399],{},"ServiceStatusDot.vue"," 把状态写入 ",[62,2402,2403],{},"data-state","，CSS 用属性选择器决定颜色，",[62,2406,2407],{},"title"," 提供文字说明。主页和服务页都复用这个组件，因此 CMS 修改 YAML 状态后，重新构建的两个页面会同步变化，不需要分别维护配色。",[115,2410,2411,2412,2415],{},"状态点表达的是",[139,2413,2414],{},"人工配置的展示状态","，不是实时健康检查。它不会主动请求 Code、Photos、NAS 或 Status，也不会自动从监控系统同步。如果未来需要实时状态，应由独立状态 API 提供可信结果，再决定是在客户端请求还是构建前抓取；不能仅把绿色小点误解为自动监控。",[115,2417,2418,2419,2421,2422,2425,2426,2429,2430,2433],{},"链接为 ",[62,2420,78],{}," 时，主页服务节点转到 ",[62,2423,2424],{},"\u002Fservices","，服务页显示为不可点击行；完整 HTTPS 链接则在新标签页打开，并带 ",[62,2427,2428],{},"noopener noreferrer","。Schema 禁止 ",[62,2431,2432],{},"javascript:"," 和非 HTTPS 外部地址，避免 CMS 字段直接变成危险链接。",[110,2435,2437],{"id":2436},"十六文章列表筛选与搜索","十六、文章列表、筛选与搜索",[115,2439,2440,2443,2444,2447],{},[62,2441,2442],{},"\u002Fwriting"," 在构建阶段只查询 ",[62,2445,2446],{},"hidden = false"," 的文章，并按日期倒序排列。首页最近文章使用同样条件，浏览器端再对已经加载的公开数组做筛选，不需要为每个关键词请求服务器。",[115,2449,2450,2451,2454],{},"分类来自共享常量：随笔、学习、交易、观察。列表额外添加“全部”。搜索会把标题、摘要和标签拼成小写字符串，再做简单的 ",[62,2452,2453],{},"includes"," 匹配。这适合当前文章规模，特点是实现透明、中文可用、没有索引服务；但它不是分词搜索，也不会搜索正文。",[115,2456,2457],{},"文章行显示日期、标题、摘要、分类和箭头。悬浮一篇时：",[184,2459,2460,2463,2466,2469],{},[136,2461,2462],{},"只给当前行增加浅主题色背景；",[136,2464,2465],{},"日期与正文整体平移 8px；",[136,2467,2468],{},"箭头向右上轻移；",[136,2470,2471],{},"其他文章保持完全不变。",[115,2473,2474,2475,1403,2478,2481,2482,2485],{},"标题容器的 grid 列不会在 hover 时改变，所以长标题不会因为悬浮突然获得更小宽度而换行。主页“最近写下”的标题使用 ",[62,2476,2477],{},"minmax(0,1fr)",[62,2479,2480],{},"overflow:hidden","、省略号和 ",[62,2483,2484],{},"white-space:nowrap","，确保固定高度面板不被长标题撑开。",[115,2487,2488,2489,2492],{},"搜索结果区使用 ",[62,2490,2491],{},"aria-live=\"polite\"","，筛选后的篇数和空状态可以被辅助技术感知。",[110,2494,2496],{"id":2495},"十七文章详情与相邻导航","十七、文章详情与相邻导航",[115,2498,2499,2500,2503,2504,2507],{},"动态路由文件是 ",[62,2501,2502],{},"app\u002Fpages\u002Fwriting\u002F[...slug].vue","。它使用当前 ",[62,2505,2506],{},"route.path"," 查询对应文章；找不到时立即抛出带中文说明的 404。",[115,2509,2510,2511,2513],{},"详情查询本身也要求 ",[62,2512,2446],{},"，所以隐藏文章不只是从列表消失，直接访问原 URL 也会得到真正的 404。详情页同时取得按日期倒序排列的全部公开文章，用当前文章的数组位置计算：",[184,2515,2516,2519],{},[136,2517,2518],{},"数组中下一项是时间上更旧的“上一篇”；",[136,2520,2521],{},"数组中上一项是时间上更新的“下一篇”。",[115,2523,2524,2525,2527],{},"页面展示分类、发布日期、可选的更新日期、标题、摘要、标签与正文。正文由 Nuxt Content 的 ",[62,2526,1546],{}," 生成。SEO 标题使用全站模板变成“文章标题 · Quilibra”，描述来自 frontmatter，Open Graph 类型设为 article。",[115,2529,2530,2531,2534],{},"正文样式集中在 ",[62,2532,2533],{},".article-prose","：限制阅读宽度、提高行高、分级处理标题、列表、引用、代码、链接和图片。文章页面可以自由使用 Markdown，不需要为每篇文章编写 Vue 模板。",[110,2536,2538],{"id":2537},"十八文章-frontmatter-合约","十八、文章 frontmatter 合约",[115,2540,2541],{},"一篇新文章最小结构如下：",[897,2543,2547],{"className":2544,"code":2545,"language":2546,"meta":272,"style":272},"language-yaml shiki shiki-themes github-light github-dark","---\ntitle: 清楚的文章标题\ndescription: 一句话说明文章实际记录了什么。\ndate: 2026-07-26\nhidden: false\ncategory: 学习\ntags:\n  - Nuxt\n---\n","yaml",[62,2548,2549,2554,2564,2574,2584,2594,2605,2614,2623],{"__ignoreMap":272},[905,2550,2551],{"class":907,"line":908},[905,2552,2553],{"class":1779},"---\n",[905,2555,2556,2558,2561],{"class":907,"line":273},[905,2557,2407],{"class":1775},[905,2559,2560],{"class":1771},": ",[905,2562,2563],{"class":1786},"清楚的文章标题\n",[905,2565,2566,2569,2571],{"class":907,"line":35},[905,2567,2568],{"class":1775},"description",[905,2570,2560],{"class":1771},[905,2572,2573],{"class":1786},"一句话说明文章实际记录了什么。\n",[905,2575,2576,2579,2581],{"class":907,"line":1976},[905,2577,2578],{"class":1775},"date",[905,2580,2560],{"class":1771},[905,2582,2583],{"class":1963},"2026-07-26\n",[905,2585,2586,2589,2591],{"class":907,"line":1986},[905,2587,2588],{"class":1775},"hidden",[905,2590,2560],{"class":1771},[905,2592,2593],{"class":1963},"false\n",[905,2595,2597,2600,2602],{"class":907,"line":2596},6,[905,2598,2599],{"class":1775},"category",[905,2601,2560],{"class":1771},[905,2603,2604],{"class":1786},"学习\n",[905,2606,2608,2611],{"class":907,"line":2607},7,[905,2609,2610],{"class":1775},"tags",[905,2612,2613],{"class":1771},":\n",[905,2615,2617,2620],{"class":907,"line":2616},8,[905,2618,2619],{"class":1771},"  - ",[905,2621,2622],{"class":1786},"Nuxt\n",[905,2624,2626],{"class":907,"line":2625},9,[905,2627,2553],{"class":1779},[115,2629,2630],{},"文件名必须是：",[897,2632,2635],{"className":2633,"code":2634,"language":1160,"meta":272},[1158],"YYYY-MM-DD-english-kebab.md\n",[62,2636,2634],{"__ignoreMap":272},[115,2638,2639,2640,1403,2642,1403,2644,1403,2646,1403,2649,1403,2652,1403,2654,1403,2656,779,2658,2660,2661,2663],{},"允许字段只有 ",[62,2641,2407],{},[62,2643,2568],{},[62,2645,2578],{},[62,2647,2648],{},"updated",[62,2650,2651],{},"slug",[62,2653,2588],{},[62,2655,2599],{},[62,2657,2610],{},[62,2659,2588],{}," 只能是布尔值，省略时按 ",[62,2662,1750],{}," 处理。发布新文章时：",[184,2665,2666,2671,2677,2682],{},[136,2667,2668,2670],{},[62,2669,2578],{}," 必须是中国时区当天；",[136,2672,2673,2674,2676],{},"文件名日期必须与 ",[62,2675,2578],{}," 一致；",[136,2678,2679,2680,1441],{},"不能包含 ",[62,2681,2648],{},[136,2683,2684,2686],{},[62,2685,2651],{}," 如果存在，必须与文件名后半段一致。",[115,2688,2689,2690,2692,2693,2695,2696,2699],{},"修订文章时保留原文件名和 ",[62,2691,2578],{},"，把 ",[62,2694,2648],{}," 设置为中国时区当天。writer 默认保留现有隐藏状态，只有修订内容明确给出 ",[62,2697,2698],{},"hidden: false"," 才会恢复。正文结构是自由的，但不能为空。若使用二级标题“来源与延伸阅读”，该节必须至少有一个 HTTP 或 HTTPS 链接。",[115,2701,2702,2705,2706,2709],{},[62,2703,2704],{},"scripts\u002Fcontent\u002Fvalidate.ts"," 使用 ",[62,2707,2708],{},"gray-matter"," 和 YAML 解析 frontmatter，再用 Markdown AST 检查正文与来源节。使用结构化解析而不是正则扫描全文，可以区分标题、链接与普通文本，并给出确定的错误。",[110,2711,2713],{"id":2712},"十九sveltia-cms-到底是什么","十九、Sveltia CMS 到底是什么",[115,2715,2716,2719,2720,2722],{},[62,2717,2718],{},"\u002Fadmin\u002Findex.html"," 只加载 Sveltia CMS 的前端脚本，",[62,2721,1691],{}," 描述后台字段。它不是另一个网站后端，也不存文章副本。",[115,2724,2725],{},"后台能维护：",[184,2727,2728,2731,2734,2737,2740,2743],{},[136,2729,2730],{},"站点、首页与栏目文案；",[136,2732,2733],{},"默认强调色和主页模块开关；",[136,2735,2736],{},"服务的增删、顺序、URL、状态、图标和主页显示；",[136,2738,2739],{},"文章的新建、搜索、筛选、修改和删除；",[136,2741,2742],{},"文章的隐藏和恢复；",[136,2744,2745,2747],{},[62,2746,1421],{}," 中的文章资源。",[115,2749,2750],{},"CMS 有两种工作方式：",[133,2752,2753,2756],{},[136,2754,2755],{},"本地仓库模式：Chromium 通过 File System Access API 直接编辑本机选择的仓库。",[136,2757,2758],{},"GitHub 模式：用户经 GitHub 登录后，CMS 根据配置向私有仓库提交内容。",[115,2760,2761,2762,2764],{},"无论哪种模式，“保存”都只意味着源文件或远程仓库出现改动。它不会直接覆盖某个静态目录。远程 CMS 当前固定写入私人仓库的 ",[62,2763,1132],{}," 分支，完整链路是：",[897,2766,2769],{"className":2767,"code":2768,"language":1160,"meta":272},[1158],"dev.quilibra.cn\u002Fadmin\n        |\n        | GitHub OAuth 保存\n        v\nGitHub staging\n        |\n        | GitHub push Webhook，HMAC-SHA256 签名\n        v\nVPS 事件队列 -> staging 同步 -> 快速或完整构建 -> staging release -> 127.0.0.1:3100\n        |\n        | 人工确认测试页面后 npm run promote\n        v\nGitHub master\n        |\n        | GitHub push Webhook，HMAC-SHA256 签名\n        v\nVPS 事件队列 -> master 同步 -> 快速或完整构建 -> production release -> 127.0.0.1:3000\n",[62,2770,2768],{"__ignoreMap":272},[115,2772,2773,2774,2777],{},"因此，在 CMS 打开“隐藏文章”并保存，只会先让测试站列表和原 URL 发生变化，正式站保持不动。确认后执行 ",[62,2775,2776],{},"npm run promote","，生产才接收同一个内容状态。这是分支、构建目录、release 和进程都真实隔离的预发布环境，不是给同一份生产内容换了一个域名。",[115,2779,2780,2781,2784],{},"GitHub OAuth 只控制“谁能通过 CMS 读写私人仓库”。本项目不使用 GitHub Actions 部署，也不开放公网 SSH；GitHub 只向现有 HTTPS 精确路径发送带签名的 push 通知，VPS 收到后再通过 Sing-box HTTP 代理主动拉取对应分支。Webhook secret 只在 VPS 和 GitHub 仓库设置中保存。私人仓库存放网站源码、文章、公开图片和不含密钥的配置；OAuth secret、访问令牌、Webhook secret、部署私钥、OpenClaw 会话、cookie 和 ",[62,2782,2783],{},".env"," 不进入仓库。",[115,2786,2787,2788,2791,2792,2795,2796,2798],{},"远程后台统一通过 ",[62,2789,2790],{},"https:\u002F\u002Fdev.quilibra.cn\u002Fadmin\u002Findex.html"," 使用。Nginx 在 TLS 之外再加一层 HTTP Basic Auth，并发送 ",[62,2793,2794],{},"noindex","；GitHub OAuth 负责仓库权限，Basic Auth 负责挡住后台入口，两者职责不同。后台路径和 ",[62,2797,2794],{}," 本身都不是访问控制。",[110,2800,2802],{"id":2801},"二十受限-writer-为什么存在","二十、受限 writer 为什么存在",[115,2804,2805,2806,2809],{},"普通编辑器可以修改仓库中的任何文件，但自动化发布工具不应该拥有同样宽的写入自由。",[62,2807,2808],{},"scripts\u002Fcontent\u002Fwrite.ts"," 把 OpenClaw 的写入范围压缩为三个明确操作：create、update、delete。",[115,2811,2812],{},"新建流程只接受草稿目录中的一个普通 Markdown 文件名和一个无日期 slug。writer 会检查：",[184,2814,2815,2818,2821,2824,2827,2830,2833],{},[136,2816,2817],{},"草稿名不能包含目录或路径穿越；",[136,2819,2820],{},"草稿必须是普通文件，不能是符号链接；",[136,2822,2823],{},"草稿不超过 1 MiB；",[136,2825,2826],{},"slug 必须是小写英文、数字和连字符；",[136,2828,2829],{},"frontmatter 与正文必须通过完整校验；",[136,2831,2832],{},"日期必须是中国时区当天；",[136,2834,2835],{},"目标文章不能已经存在。",[115,2837,2838,2839,2842,2843,2846,2847,2850,2851,2854],{},"写入时先以 ",[62,2840,2841],{},"wx"," 模式建立权限为 ",[62,2844,2845],{},"0640"," 的随机临时文件并 ",[62,2848,2849],{},"fsync","，再用硬链接创建最终文件，利用文件系统的 ",[62,2852,2853],{},"EEXIST"," 保证不会覆盖已有新文章。更新时先验证现有文件和发布日期，再用 rename 原子替换。删除只接受精确的完整 dated slug，并拒绝目录、符号链接和不存在的目标。",[115,2856,2857],{},"这不是为了让日常写作变复杂，而是为了让“从聊天自动发布”仍有可审计的硬边界。Skill 中的文字规则可能被模型误解，writer 的路径、日期和文件类型检查则由操作系统与代码强制执行。",[110,2859,2861],{"id":2860},"二十一openclaw-发布-skill-的实际状态机","二十一、OpenClaw 发布 Skill 的实际状态机",[115,2863,2864,2865,2868],{},"仓库只维护一个 ",[62,2866,2867],{},"personal-site-publisher"," Skill。它先读取文章合约，再按以下过程工作：",[897,2870,2873],{"className":2871,"code":2872,"language":1160,"meta":272},[1158],"链接或素材\n   |\n   v\n确定标题、摘要、日期、slug、分类、标签\n   |\n   v\n在私聊中返回完整草稿，不写仓库\n   |\n   +-- 修改：... --> 修改内存草稿\n   +-- 放弃 ------> 清除内存草稿\n   +-- 发布 ------> 保存到受限草稿目录\n                         |\n                         v\n                   content:write\n                         |\n                         v\n                  npm run publish\n                         |\n                         v\n              返回文章 URL、commit、release\n",[62,2874,2872],{"__ignoreMap":272},[115,2876,2877,2880,2881,779],{},[62,2878,2879],{},"直接发布"," 只跳过人工预览，不跳过 writer、内容校验和静态生成。更新文章必须保留 dated URL；删除必须先展示目标信息，再收到完全匹配的 ",[62,2882,2883],{},"确认删除 \u003Cdated-slug>",[115,2885,2886,2887,1403,2890,2166,2893,2896],{},"发布 Skill 还规定：只接受所有者私聊中的发布命令；把网页、对话和搜索结果视为不可信内容；不把 API key、cookie、账号信息、私人对话细节和精确私人资产写入文章；不能用任意编辑器、",[62,2888,2889],{},"rm",[62,2891,2892],{},"git add",[62,2894,2895],{},"git commit"," 绕过 writer 和 release 脚本。",[110,2898,2900],{"id":2899},"二十二本地发布自动同步与环境晋级","二十二、本地发布、自动同步与环境晋级",[115,2902,2903],{},"当前有五个相关入口，名字接近但权限完全不同。",[660,2905,2907],{"id":2906},"npm-run-publish",[62,2908,2909],{},"npm run publish",[115,2911,2912],{},"用于受信本地流程中尚未提交的内容改动，例如 OpenClaw Skill。它只允许以下路径：",[897,2914,2917],{"className":2915,"code":2916,"language":1160,"meta":272},[1158],"content\u002Fsettings\u002Fsite.yml\ncontent\u002Fsettings\u002Fhome.yml\ncontent\u002Fsettings\u002Fwriting.yml\ncontent\u002Fsettings\u002Fservices.yml\ncontent\u002Fsettings\u002Fabout.yml\ncontent\u002Fwriting\u002F**\npublic\u002Fuploads\u002F**\n",[62,2918,2916],{"__ignoreMap":272},[115,2920,2921],{},"它验证内容、生成静态站、自动创建本地 Git commit、复制 release 并切换本地默认生产目录。若工作区混入 Vue、CSS、脚本、CMS 配置或文档改动，会拒绝执行。它是受限本地发布入口，不是远程 CMS 的预发布流程。",[660,2923,2925],{"id":2924},"npm-run-deploy",[62,2926,2927],{},"npm run deploy",[115,2929,2930],{},"用于已经提交的代码版本。它要求工作区完全干净，不创建 commit，并依次运行：",[897,2932,2935],{"className":2933,"code":2934,"language":1160,"meta":272},[1158],"类型检查\n内容与 writer 单元测试\nrelease 单元测试\n静态生成\n浏览器 E2E\n创建 release\n原子切换 current\n清理过旧 release\n",[62,2936,2934],{"__ignoreMap":272},[660,2938,2940],{"id":2939},"npm-run-sync",[62,2941,2942],{},"npm run sync",[115,2944,2945,2948,2949,2951,2952,2954],{},[62,2946,2947],{},"scripts\u002Fsync.ts"," 是两个 systemd oneshot 服务共用的自动同步器。环境变量决定它跟踪 ",[62,2950,1140],{}," 还是 ",[62,2953,1132],{},"、使用哪一份独立仓库、把 release 写到哪个数据根目录。每轮会：",[133,2956,2957,2960,2963,2973,2978,2984],{},[136,2958,2959],{},"从 Sing-box 配置读取带认证的本机 HTTP 代理；",[136,2961,2962],{},"重试 GitHub fetch，只接受能够快进的远端历史；",[136,2964,2965,2966,2969,2970,1441],{},"根据 ",[62,2967,2968],{},"package-lock.json"," 哈希决定是否运行 ",[62,2971,2972],{},"npm ci",[136,2974,2975,2976,1441],{},"比较上次已部署提交与目标提交：只有受管内容变化时执行快速内容部署，否则执行完整 ",[62,2977,2927],{},[136,2979,2980,2981,1441],{},"只有部署成功才原子写入 ",[62,2982,2983],{},"deployed-commit",[136,2985,2986],{},"失败时保留旧 release，由持久化 Webhook 事件延迟重试。",[115,2988,2989,2990,2993],{},"纯文章、站点设置和上传资源变化仍会执行内容校验与静态生成，但跳过未变化代码的类型检查、单元测试和 E2E。生产和测试同步服务使用同一个 ",[62,2991,2992],{},"flock"," 文件串行执行，防止 2 GB 内存的 VPS 同时跑两份 Nuxt 构建和 Playwright。",[660,2995,2997],{"id":2996},"npm-run-webhook",[62,2998,2999],{},"npm run webhook",[115,3001,3002,3005,3006,3009,3010,3013,3014,3017,3018,3021,3022,779],{},[62,3003,3004],{},"scripts\u002Fwebhook.ts"," 只监听 ",[62,3007,3008],{},"127.0.0.1:3110","，Nginx 把 ",[62,3011,3012],{},"https:\u002F\u002Fquilibra.cn\u002F_hooks\u002Fgithub"," 的精确 POST 路由转给它。接收器先验证 HMAC-SHA256 签名、仓库名、push 类型和 ",[62,3015,3016],{},"staging\u002Fmaster"," 分支，再把事件以权限 ",[62,3019,3020],{},"0600"," 写入用户状态目录并返回 ",[62,3023,3024],{},"202",[115,3026,3027],{},"每个 GitHub delivery 都有独立队列文件。若构建期间出现第二次 push，第一轮完成后会再启动一轮同步；进程重启时也会恢复未完成事件。失败使用延迟重试，不需要周期性轮询 GitHub。Webhook 请求只能映射到两个固定 systemd unit，不能携带或执行任意命令。",[660,3029,3031],{"id":3030},"npm-run-promote",[62,3032,2776],{},[115,3034,3035,3036,3038,3039,3041],{},"这是远程 CMS 从测试进入生产的唯一显式关口。它要求本地 ",[62,3037,1140],{}," 干净且等于最新远端，确认远端 ",[62,3040,1132],{}," 的精确提交已经成功部署到测试站，并拒绝 staging 中的 Vue、CSS、脚本、文档或 CMS 配置改动。允许晋级的只有：",[897,3043,3045],{"className":3044,"code":2916,"language":1160,"meta":272},[1158],[62,3046,2916],{"__ignoreMap":272},[115,3048,3049,3050,3053,3054,1418,3056,3058],{},"检查通过后，脚本在临时 Git worktree 中把 staging 合并到最新 master，再用一次 ",[62,3051,3052],{},"git push --atomic"," 让远端 ",[62,3055,1140],{},[62,3057,1132],{}," 同时指向同一个合并提交。若任一引用不能更新，两边都不更新。GitHub 随后发送 master Webhook 并触发正式部署。",[115,3060,3061,3062,3065,3066,3068,3069,3072,3073,3075,3076,3078],{},"因此，修改本文这样的文章有两种合法路径：本地受信流程可用 ",[62,3063,3064],{},"publish","；远程 CMS 则保存到 staging、检查测试站、再运行 ",[62,3067,1136],{},"。修改 Vue\u002FCSS 并已经提交后使用 ",[62,3070,3071],{},"deploy"," 或由 master 同步器部署。不要用 ",[62,3074,3064],{}," 偷渡代码，也不要让 ",[62,3077,3071],{}," 带着未提交文件上线。",[110,3080,3082],{"id":3081},"二十三静态-release-如何做到切换时不中断","二十三、静态 release 如何做到切换时不中断",[115,3084,3085,3088],{},[62,3086,3087],{},"scripts\u002Frelease.ts"," 用 UTC 时间生成 release 名称，例如：",[897,3090,3093],{"className":3091,"code":3092,"language":1160,"meta":272},[1158],"20260726T144914-134Z\n",[62,3094,3092],{"__ignoreMap":272},[115,3096,3097,3098,3100,3101,3104,3105,3107],{},"生成结束后，把 ",[62,3099,1187],{}," 递归复制到 ",[62,3102,3103],{},"releases\u002F\u003Cid>","。激活时不是先删 ",[62,3106,1191],{}," 再新建，而是：",[133,3109,3110,3113,3116],{},[136,3111,3112],{},"创建一个带进程号的临时符号链接；",[136,3114,3115],{},"让临时链接指向新 release；",[136,3117,3118,3119,3122,3123,779],{},"用 ",[62,3120,3121],{},"rename"," 把临时链接原子替换成 ",[62,3124,1191],{},[115,3126,3127,3128,3130,3131,3133],{},"对访问者来说，某个请求要么读取旧 release，要么读取新 release，不会遇到 ",[62,3129,1191],{}," 暂时不存在的中间状态。静态 Node 进程也不需要重启，因为它每次请求都从 ",[62,3132,1191],{}," 路径解析文件。",[115,3135,3136,3139,3140,3143,3144,3147,3148,1418,3151,779],{},[62,3137,3138],{},"PERSONAL_SITE_DATA_ROOT"," 决定 release 根目录。生产不设置时使用 ",[62,3141,3142],{},"~\u002F.local\u002Fshare\u002Fpersonal-site","；测试同步服务显式设置为 ",[62,3145,3146],{},"~\u002F.local\u002Fshare\u002Fpersonal-site-staging","。因此相同的 release 代码可以复用，但两个环境不会碰到对方的 ",[62,3149,3150],{},"releases\u002F",[62,3152,1191],{},[115,3154,3155],{},"脚本默认保留最近五个 release，并额外保证当前激活版本不被删掉。由于当前版本有可能是手工回滚到的较旧目录，保留“当前版本”比单纯保留时间最新的五个更重要。",[115,3157,3158,3159,3161],{},"回滚只调用同一个原子激活函数，把 ",[62,3160,1191],{}," 指回已存在的 release。它不修改 Git、不重新构建、不删除较新的版本，也不重启生产服务。",[110,3163,3165],{"id":3164},"二十四静态服务器nginx-与-https","二十四、静态服务器、Nginx 与 HTTPS",[115,3167,3168,3171,3172,1403,3175,1403,3178,1418,3181,3184,3185,3187,3188,1403,3191,1403,3194,3197,3198,3201],{},[62,3169,3170],{},"scripts\u002Fstatic-server.mjs"," 使用 Node 自带的 ",[62,3173,3174],{},"http",[62,3176,3177],{},"fs",[62,3179,3180],{},"stream",[62,3182,3183],{},"zlib","。监听地址、端口和 ",[62,3186,1191],{}," 路径分别由 ",[62,3189,3190],{},"PERSONAL_SITE_HOST",[62,3192,3193],{},"PERSONAL_SITE_PORT",[62,3195,3196],{},"PERSONAL_SITE_ROOT"," 控制。两个 systemd unit 都把 host 设为 ",[62,3199,3200],{},"127.0.0.1","，所以 3000 和 3100 不直接接受公网连接。它没有框架中间件，职责非常窄：",[184,3203,3204,3207,3220,3226,3233,3236,3239,3242,3245,3248,3255,3261,3264],{},[136,3205,3206],{},"只接受 GET 和 HEAD，其他方法返回 405；",[136,3208,3209,3210,1350,3213,3216,3217,3219],{},"解码 URL 后用 ",[62,3211,3212],{},"resolve",[62,3214,3215],{},"relative"," 检查目标仍在 ",[62,3218,1191],{}," 内，阻止目录穿越；",[136,3221,3222,3223,1441],{},"请求目录时返回该目录的 ",[62,3224,3225],{},"index.html",[136,3227,3228,3229,3232],{},"找不到文件时返回预生成的 ",[62,3230,3231],{},"404.html"," 和 404 状态；",[136,3234,3235],{},"根据扩展名发送正确的 Content-Type；",[136,3237,3238],{},"对大于等于 1 KiB 的文本资源协商 Brotli 或 gzip；",[136,3240,3241],{},"Brotli 使用质量 4，gzip 使用级别 6，在 CPU 与体积间取中间值；",[136,3243,3244],{},"使用读取流和 pipeline，不把大文件一次性读进内存；",[136,3246,3247],{},"根据文件大小和修改时间生成弱 ETag；",[136,3249,3250,3251,3254],{},"命中 ",[62,3252,3253],{},"If-None-Match"," 时返回 304；",[136,3256,3257,3260],{},[62,3258,3259],{},"_nuxt\u002F"," 哈希资源缓存一年并标记 immutable；",[136,3262,3263],{},"HTML、图片等非哈希路径使用 no-cache，确保 release 切换后会重新验证；",[136,3265,3266],{},"收到 SIGINT 或 SIGTERM 时停止接受新连接并正常退出。",[115,3268,3269,3270,3272],{},"如果 ",[62,3271,1191],{}," 不可用，服务器返回 503，而不是把文件系统错误暴露给访问者。若响应流已经开始后出错，则销毁连接，避免发送一半文件后再拼接错误文本。",[115,3274,3275],{},"它不会处理 TLS、OAuth 或反向代理。公网入口由 Nginx 负责：",[897,3277,3280],{"className":3278,"code":3279,"language":1160,"meta":272},[1158],"quilibra.cn \u002F www.quilibra.cn -> HTTPS -> 127.0.0.1:3000\ndev.quilibra.cn               -> HTTPS + Basic Auth -> 127.0.0.1:3100\n公网 IP或未知 Host             -> 默认站点拒绝\n",[62,3281,3279],{"__ignoreMap":272},[115,3283,3284,3285,3288,3289,3292],{},"根域名和 ",[62,3286,3287],{},"www"," 共用正式站，证书由 Certbot 申请并续期；测试子域名使用自己的证书和密码文件。Nginx 把真实 Host、客户端地址和转发协议传给上游，测试站额外发送 ",[62,3290,3291],{},"X-Robots-Tag: noindex, nofollow, noarchive","。腾讯云安全组只需要开放 80 和 443，3000、3100 不对公网放行。",[115,3294,3295,3296,3299,3300,3303],{},"页脚备案号来自全站设置 ",[62,3297,3298],{},"content\u002Fsettings\u002Fsite.yml","，当前 ICP 号链接到 ",[62,3301,3302],{},"https:\u002F\u002Fbeian.miit.gov.cn\u002F","。备案展示、DNS、TLS 证书和反向代理是四个不同层次：备案号不会自动配置 HTTPS，证书也不会自动创建 DNS 记录。",[110,3305,3307],{"id":3306},"二十五300031003101-分别是什么","二十五、3000、3100、3101 分别是什么",[690,3309,3310,3323],{},[693,3311,3312],{},[696,3313,3314,3317,3320],{},[699,3315,3316],{},"端口",[699,3318,3319],{},"用途",[699,3321,3322],{},"生命周期",[706,3324,3325,3336,3348],{},[696,3326,3327,3330,3333],{},[711,3328,3329],{},"3000",[711,3331,3332],{},"当前生产静态 release",[711,3334,3335],{},"systemd 长期运行",[696,3337,3338,3341,3346],{},[711,3339,3340],{},"3100",[711,3342,3343,3345],{},[62,3344,1132],{}," 当前静态 release",[711,3347,3335],{},[696,3349,3350,3353,3356],{},[711,3351,3352],{},"3101",[711,3354,3355],{},"Playwright 隔离测试服务",[711,3357,3358],{},"测试自动启动并结束",[115,3360,3361,3362,3365,3366,3369],{},"3100 过去曾运行当前工作区的 Nuxt Dev，容易受首次编译、热更新和 Nuxt Content 缓存损坏影响。现在它与 3000 一样只运行静态服务器，从独立 staging release 读取文件，空闲常驻约几十 MB，并由 ",[62,3363,3364],{},"https:\u002F\u002Fdev.quilibra.cn"," 提供远程预览。需要真正的热更新时，在开发机或 VPS 的另一个空闲端口临时运行 ",[62,3367,3368],{},"npm run dev","，不要覆盖常驻 3100。",[115,3371,3372,3373,3375],{},"3101 使用独立内容 fixture 和 ",[62,3374,1483],{},"，不能拿它当日常预览端口。生产 3000、预发布 3100 和测试 3101 可以同时存在，因为 release、构建目录和端口都隔离。",[110,3377,3379],{"id":3378},"二十六systemd-用户服务","二十六、systemd 用户服务",[115,3381,3382],{},"当前有五个相关 unit：",[897,3384,3387],{"className":3385,"code":3386,"language":1160,"meta":272},[1158],"personal-site.service                 3000 生产静态服务\npersonal-site-dev.service             3100 staging 静态服务\npersonal-site-webhook.service         3110 签名 Webhook 接收与事件排队\npersonal-site-sync.service            master 单次同步与部署\npersonal-site-staging-sync.service    staging 单次同步与部署\n",[62,3388,3386],{"__ignoreMap":272},[115,3390,3391],{},"两个静态服务的关键设置包括：",[184,3393,3394,3400,3406,3412,3418,3427],{},[136,3395,3396,3399],{},[62,3397,3398],{},"Type=simple","：Node 进程本身就是主进程；",[136,3401,3402,3405],{},[62,3403,3404],{},"After\u002FWants=network-online.target","：网络就绪后启动；",[136,3407,3408,3411],{},[62,3409,3410],{},"Restart=on-failure"," 与 5 秒延迟：异常退出后自动恢复；",[136,3413,3414,3417],{},[62,3415,3416],{},"KillSignal=SIGINT","：与静态服务器的优雅关闭逻辑对应；",[136,3419,3420,1403,3423,3426],{},[62,3421,3422],{},"NoNewPrivileges=true",[62,3424,3425],{},"PrivateTmp=true","：缩小运行时权限；",[136,3428,3429,3430,3433],{},"安装到用户级 ",[62,3431,3432],{},"default.target","，不要求整个服务以 root 运行。",[115,3435,3436,3437,3439,3440,3443,3444,1350,3447,3449,3450,3453],{},"Webhook 服务是 ",[62,3438,3398],{}," 常驻轻量进程，只监听回环地址；两个同步服务仍是 ",[62,3441,3442],{},"Type=oneshot","，仅在收到对应分支的 push 后运行。生产和测试分别拥有独立 ",[62,3445,3446],{},"sync-repo",[62,3448,2983],{},"，但用同一个 ",[62,3451,3452],{},"\u002Fhome\u002Fuser\u002F.local\u002Fstate\u002Fpersonal-site-deploy.lock"," 串行构建。仓库没有周期性 GitHub timer；有事件时才 fetch，依赖锁变化时才安装依赖，纯内容提交使用快速构建。",[115,3455,3456,3457,3460,3461,3464],{},"unit 当前把 Node 可执行文件写成 NVM 下的明确路径。升级 Node 或切换 NVM 版本后，shell 中的 ",[62,3458,3459],{},"node"," 可能已经变化，但 systemd 仍引用旧路径，这是需要主动检查的运维陷阱。修改 unit 后必须 ",[62,3462,3463],{},"daemon-reload","，而普通文章或页面 release 不需要重启静态服务。",[115,3466,3467,3468,3471,3472,3475],{},"用户级服务要在 VPS 重启且用户未登录时自动启动，需要为运行网站的 Linux 用户启用 linger。管理它时应使用相同用户的 ",[62,3469,3470],{},"systemctl --user","，不应使用 ",[62,3473,3474],{},"sudo systemctl --user"," 去连接 root 的用户总线。",[110,3477,3479],{"id":3478},"二十七测试体系究竟覆盖什么","二十七、测试体系究竟覆盖什么",[115,3481,3482],{},"测试按风险边界分成三组。",[660,3484,3486],{"id":3485},"内容和-writer-单元测试","内容和 writer 单元测试",[115,3488,3489,3492],{},[62,3490,3491],{},"test:content"," 检查：",[184,3494,3495,3498,3501,3504,3507,3512,3515,3518,3521],{},[136,3496,3497],{},"合法文章和四种分类可以通过；",[136,3499,3500],{},"非法日期、分类、文件名、重复标签和空正文会失败；",[136,3502,3503],{},"来源节必须包含公共 HTTP(S) 链接；",[136,3505,3506],{},"CMS 可选 slug 必须与文件名一致；",[136,3508,3509,3511],{},[62,3510,2588],{}," 只能是布尔值，writer 更新时默认保留已有隐藏状态；",[136,3513,3514],{},"站点 YAML 满足 Schema；",[136,3516,3517],{},"writer 拒绝覆盖、穿越、符号链接和错误修订日期；",[136,3519,3520],{},"更新保留发布日期；",[136,3522,3523],{},"删除只接受精确存在的 dated slug。",[660,3525,3527],{"id":3526},"release-单元测试","release 单元测试",[115,3529,3530,3533],{},[62,3531,3532],{},"test:release"," 在临时目录中检查：",[184,3535,3536,3541,3544,3547,3550,3553,3556,3559,3562,3565],{},[136,3537,3538,3540],{},[62,3539,1191],{}," 能原子切换；",[136,3542,3543],{},"清理历史版本时保留激活 release；",[136,3545,3546],{},"release 名称不能路径穿越；",[136,3548,3549],{},"受管内容路径不会扩大到应用代码；",[136,3551,3552],{},"自动同步失败后不写成功状态，后续重试仍会部署；",[136,3554,3555],{},"Webhook 拒绝无效签名、其他仓库和其他分支；",[136,3557,3558],{},"构建期间到达的第二个 push 会保留并追加同步；",[136,3560,3561],{},"纯内容提交走快速构建，代码提交走完整构建；",[136,3563,3564],{},"staging 未成功部署时不能晋级，晋级后 master 与 staging 原子对齐；",[136,3566,3567],{},"子命令失败时错误信息保留退出码。",[660,3569,3571],{"id":3570},"playwright-浏览器测试","Playwright 浏览器测试",[115,3573,3574,3577],{},[62,3575,3576],{},"test:e2e"," 在 Chromium 中检查：",[184,3579,3580,3583,3586,3589,3592,3595,3598,3601,3604,3607,3610,3613],{},[136,3581,3582],{},"首页成功水合且没有控制台错误；",[136,3584,3585],{},"动态 favicon、theme-color 和 RGB 输出同步；",[136,3587,3588],{},"自定义主题在纯黑纯白下仍可读；",[136,3590,3591],{},"旧黄色缓存会迁移；",[136,3593,3594],{},"mask 没加载完时字标不偷跑，加载后正常播放；",[136,3596,3597],{},"标签页激活、窗口 focus、换色、滑杆松手和返回主页会重播；",[136,3599,3600],{},"拖动期间不会反复播放；",[136,3602,3603],{},"服务状态、NAS 图标、最近文章、文章详情和更新日期正确；",[136,3605,3606],{},"隐藏文章不出现在公开查询中，原 URL 返回 404；",[136,3608,3609],{},"文章悬浮只影响当前行，长标题宽度不跳变；",[136,3611,3612],{},"手机页面无横向溢出，RGB 在小屏隐藏；",[136,3614,3615],{},"多种桌面尺寸下关键模块互不重叠。",[115,3617,3618],{},"Playwright 配置允许一次重试，等待时间 10 秒，临时 Nuxt 服务启动最长等待 120 秒。响应式几何测试启用 reduced motion，避免入场动画中的瞬时位置影响布局判断。",[115,3620,3621],{},"测试不是越多越好。这里保留的是曾经真实出错、或者一旦回归就很难靠类型系统发现的行为；字体文件数量、每个 CSS 颜色和每一段正文不需要分别写测试。",[110,3623,3625],{"id":3624},"二十八当前性能画像与做过的优化","二十八、当前性能画像与做过的优化",[115,3627,3628],{},"在当前版本的一次本机测量中：",[184,3630,3631,3634,3637,3640,3643],{},[136,3632,3633],{},"主 CSS 约 31.1 KB，gzip 后约 6.9 KB；",[136,3635,3636],{},"外部或本地字体文件由 98 个减少到 0；",[136,3638,3639],{},"首页一次传输约 252 KB；",[136,3641,3642],{},"模拟 4G 和 4 倍 CPU 降速时，LCP 约 0.75 秒，完整 load 约 1.37 秒；",[136,3644,3645],{},"CLS 为 0。",[115,3647,3648,3649,3652],{},"VPS 空闲状态下，生产与测试两个 Node 静态服务合计约 50 MB；Webhook 接收器也是单一轻量 Node 进程，不执行构建。整机仍有约 1.1 GB 可用内存。完整 Nuxt + Playwright 部署的峰值接近 1 GB，但两个同步器通过共享锁串行执行；内容快速构建不启动 Playwright。生产、测试和开发三份仓库合计约 1.5 GB，主要空间来自各自的 ",[62,3650,3651],{},"node_modules","；这是用磁盘换取工作区、缓存和发布故障互不污染。",[115,3654,3655],{},"这些数字是环境相关快照，不是永久承诺。浏览器缓存、网络、CPU、文章数量和依赖升级都会改变结果。",[115,3657,3658],{},"当前主要性能措施包括：",[184,3660,3661,3664,3667,3670,3677,3682,3685,3688,3691,3694,3697],{},[136,3662,3663],{},"静态预渲染，HTML 首次响应包含页面内容；",[136,3665,3666],{},"移除大批字体资源，改用系统字体；",[136,3668,3669],{},"只预加载首屏真正依赖的 30 KB 字标 mask；",[136,3671,3672,3673,3676],{},"字标等待 ",[62,3674,3675],{},"decode()","，避免资源未准备好时动画丢失；",[136,3678,3679,3681],{},[62,3680,1879],{}," 合并高频指针更新；",[136,3683,3684],{},"缓存元素边界，避免每次 pointermove 读取布局；",[136,3686,3687],{},"连续调色只改颜色，松手才重播动画；",[136,3689,3690],{},"用 transform\u002Fopacity 处理大多数动效；",[136,3692,3693],{},"生产服务器流式响应并支持 Brotli、gzip、ETag 和长期哈希缓存；",[136,3695,3696],{},"固定组件尺寸，避免动态文本造成布局偏移；",[136,3698,3699],{},"分离本地开发、生产同步、测试同步和 E2E 的工作目录与 Nuxt Content 数据库。",[115,3701,3702,3704],{},[62,3703,1187],{}," 整体约 5.4 MB，其中包括 Nuxt Content 的客户端 SQLite\u002FWASM 相关资源；这不等于每个访客首屏都会下载整个目录。分析性能时应看浏览器实际请求和传输体积，不应把产物目录大小直接当作首页流量。",[110,3706,3708],{"id":3707},"二十九为什么首次交互偶尔会过一会儿才正常","二十九、为什么首次交互偶尔会“过一会儿才正常”",[115,3710,3711],{},"这个问题曾同时表现为字标不播放和 RGB 滑杆暂时失灵，根因方向不是滑杆 CSS 本身，而是客户端水合尚未完成或 Nuxt Dev 仍在首次编译。静态 HTML 可以先显示，但 Vue 事件只有在 JavaScript 加载、执行并成功水合后才生效。",[115,3713,3714,3715,3718],{},"判断方法是观察 ",[62,3716,3717],{},".site-shell[data-hydrated=\"true\"]","。如果页面已经显示但该属性迟迟没有变成 true，应检查：",[184,3720,3721,3724,3727,3730,3733],{},[136,3722,3723],{},"浏览器控制台是否有 JavaScript 错误；",[136,3725,3726],{},"本地 Nuxt Dev 是否还在首次编译；",[136,3728,3729],{},"本地内容 SQLite 缓存是否损坏；",[136,3731,3732],{},"大型依赖或资源是否阻塞客户端入口；",[136,3734,3735],{},"当前访问的是临时 Nuxt Dev，还是已经生成的 3000\u002F3100 静态站。",[115,3737,3738],{},"目前通过移除字体、减轻入口资源、预加载 mask、分离内容数据库和明确动画水合闸门改善了这个过程。远程测试站也已经改成静态 release，不再边访问边编译，因此它与正式站具有相同的水合特征。",[110,3740,3742],{"id":3741},"三十常见修改应该改哪里","三十、常见修改应该改哪里",[660,3744,3746],{"id":3745},"修改个人介绍栏目文案或服务","修改个人介绍、栏目文案或服务",[115,3748,3749,3750,3752,3753,3755],{},"优先通过 CMS 中对应的页面入口，或编辑 ",[62,3751,1413],{}," 下对应 YAML；服务列表位于 ",[62,3754,1612],{},"。若新增服务状态或图标类型，还要同步修改 Schema、配置枚举、CMS 选项、组件映射、CSS 和测试。",[660,3757,3758],{"id":3758},"修改默认颜色",[115,3760,3761,3762,3764,3765,3767],{},"修改 ",[62,3763,1995],{},"。它可以是任意 ",[62,3766,1659],{},"。已有访客若保存过自定义颜色，会继续看到自己的选择；要做全体迁移，需要像旧黄色迁移一样明确识别并处理旧值，不能无条件清空 localStorage。",[660,3769,3770],{"id":3770},"修改红绿蓝预设",[115,3772,3761,3773,3775,3776,3779],{},[62,3774,1931],{}," 的 ",[62,3777,3778],{},"accentPresets","。RGB 面板和非主页调色按钮都复用它。修改后应测试纯黑纯白对比、localStorage 恢复和字标重播。",[660,3781,3782],{"id":3782},"修改艺术字",[115,3784,3785,3786,3775,3788,3790,3791,3793,3794,779],{},"在 CMS 中打开“站点设置 \u002F 首页”，修改“艺术字文字”即可。它最终写入 ",[62,3787,1592],{},[62,3789,2205],{},"，保存到测试分支后由 Webhook 自动重新构建测试站。字形由 ",[62,3792,1431],{}," 引入，字体栈、字号和四层颜色位于 ",[62,3795,3796],{},"main.css",[660,3798,3799],{"id":3799},"修改字标动画",[115,3801,3802,3803,3805,3806,3809,3810,3812],{},"播放条件在 ",[62,3804,2252],{},"，跨组件触发在 ",[62,3807,3808],{},"useWordmarkAnimation.ts","，视觉关键帧在 ",[62,3811,3796],{},"。不要把三者混成一个大组件：字体状态、事件状态和视觉时间线是不同责任。",[660,3814,3815],{"id":3815},"修改文章分类",[115,3817,3818,3819,3822],{},"至少同步 ",[62,3820,3821],{},"app\u002Fconfig\u002Farticles.ts","、Nuxt Content Schema、CMS 选项、校验器测试与界面测试。文章中的旧分类是否迁移，需要单独决定。",[660,3824,3825],{"id":3825},"修改文章样式",[115,3827,3828,3829,3832,3833,3835,3836,3839],{},"文章列表看 ",[62,3830,3831],{},".article-row","，正文看 ",[62,3834,2533],{},"，详情头部看 ",[62,3837,3838],{},".article-hero","。长标题、手机宽度、hover 前后几何和其他文章 opacity 是必须复查的回归点。",[660,3841,3842],{"id":3842},"修改生产或测试服务",[115,3844,3845,3846,3848],{},"静态响应逻辑改 ",[62,3847,3170],{},"；监听端口、release 根目录、Node 路径和重启策略改对应 systemd unit。同步分支、代理、独立仓库和部署锁改两个 sync unit。只有静态服务器或 unit 变化需要重启\u002F重载，普通 release 切换不需要。",[110,3850,3852],{"id":3851},"三十一日常发布配方","三十一、日常发布配方",[660,3854,3856],{"id":3855},"通过远程-cms-修改内容","通过远程 CMS 修改内容",[133,3858,3859,3865,3868,3873],{},[136,3860,3861,3862,3864],{},"打开 ",[62,3863,2790],{}," 并保存。",[136,3866,3867],{},"等待 Webhook 触发 staging 构建，检查测试站主页、列表和具体 URL。",[136,3869,3870,3871,779],{},"在干净且最新的 master 工作区运行 ",[62,3872,2776],{},[136,3874,3875],{},"等待 master Webhook 触发生产构建，再检查正式站。",[115,3877,3878],{},"保存、预览、晋级和正式部署是四个明确阶段。隐藏文章也遵循同一流程。",[660,3880,3881],{"id":3881},"本地受信流程直接发布内容",[897,3883,3887],{"className":3884,"code":3885,"language":3886,"meta":272,"style":272},"language-bash shiki shiki-themes github-light github-dark","npm run content:validate\nnpm run publish\n","bash",[62,3888,3889,3900],{"__ignoreMap":272},[905,3890,3891,3894,3897],{"class":907,"line":908},[905,3892,3893],{"class":1779},"npm",[905,3895,3896],{"class":1786}," run",[905,3898,3899],{"class":1786}," content:validate\n",[905,3901,3902,3904,3906],{"class":907,"line":273},[905,3903,3893],{"class":1779},[905,3905,3896],{"class":1786},[905,3907,3908],{"class":1786}," publish\n",[115,3910,3911,3913],{},[62,3912,3064],{}," 成功后会输出 commit、release 和 current 路径。它用于 OpenClaw 等本地受限流程，会直接切换默认生产 release；是否推送 GitHub 是另一项操作。普通远程 CMS 编辑不要使用这条路径绕过 staging。",[660,3915,3917],{"id":3916},"发布-vuecss脚本或配置代码","发布 Vue、CSS、脚本或配置代码",[897,3919,3921],{"className":3884,"code":3920,"language":3886,"meta":272,"style":272},"npm run typecheck\nnpm run test:content\nnpm run test:release\nnpm run test:e2e\nnpm run generate\ngit diff --check\n# 明确检查并提交本次文件\ngit push origin master\n",[62,3922,3923,3932,3941,3950,3959,3968,3979,3985],{"__ignoreMap":272},[905,3924,3925,3927,3929],{"class":907,"line":908},[905,3926,3893],{"class":1779},[905,3928,3896],{"class":1786},[905,3930,3931],{"class":1786}," typecheck\n",[905,3933,3934,3936,3938],{"class":907,"line":273},[905,3935,3893],{"class":1779},[905,3937,3896],{"class":1786},[905,3939,3940],{"class":1786}," test:content\n",[905,3942,3943,3945,3947],{"class":907,"line":35},[905,3944,3893],{"class":1779},[905,3946,3896],{"class":1786},[905,3948,3949],{"class":1786}," test:release\n",[905,3951,3952,3954,3956],{"class":907,"line":1976},[905,3953,3893],{"class":1779},[905,3955,3896],{"class":1786},[905,3957,3958],{"class":1786}," test:e2e\n",[905,3960,3961,3963,3965],{"class":907,"line":1986},[905,3962,3893],{"class":1779},[905,3964,3896],{"class":1786},[905,3966,3967],{"class":1786}," generate\n",[905,3969,3970,3973,3976],{"class":907,"line":2596},[905,3971,3972],{"class":1779},"git",[905,3974,3975],{"class":1786}," diff",[905,3977,3978],{"class":1963}," --check\n",[905,3980,3981],{"class":907,"line":2607},[905,3982,3984],{"class":3983},"sJ8bj","# 明确检查并提交本次文件\n",[905,3986,3987,3989,3992,3995],{"class":907,"line":2616},[905,3988,3972],{"class":1779},[905,3990,3991],{"class":1786}," push",[905,3993,3994],{"class":1786}," origin",[905,3996,3997],{"class":1786}," master\n",[115,3999,4000,4001,4003,4004,4006],{},"master Webhook 会触发同步器再次执行完整 ",[62,4002,2927],{},"，前面的命令适合在提交前尽早发现问题。需要在 VPS 立即部署当前干净提交时也可以手工运行 ",[62,4005,2927],{},"。代码版本只进入 master 时，测试站可能暂时仍运行上一份代码；下一次内容晋级会把最新 master 与 staging 合并并重新对齐两边。",[660,4008,4009],{"id":4009},"查看当前生产状态",[897,4011,4013],{"className":3884,"code":4012,"language":3886,"meta":272,"style":272},"systemctl --user is-active personal-site.service\nsystemctl --user is-active personal-site-dev.service\nreadlink -f ~\u002F.local\u002Fshare\u002Fpersonal-site\u002Fcurrent\nreadlink -f ~\u002F.local\u002Fshare\u002Fpersonal-site-staging\u002Fcurrent\ncurl -fsS -o \u002Fdev\u002Fnull -w '%{http_code} %{content_type}\\n' http:\u002F\u002F127.0.0.1:3000\u002F\ncurl -fsS -o \u002Fdev\u002Fnull -w '%{http_code} %{content_type}\\n' http:\u002F\u002F127.0.0.1:3100\u002F\n",[62,4014,4015,4029,4040,4051,4060,4083],{"__ignoreMap":272},[905,4016,4017,4020,4023,4026],{"class":907,"line":908},[905,4018,4019],{"class":1779},"systemctl",[905,4021,4022],{"class":1963}," --user",[905,4024,4025],{"class":1786}," is-active",[905,4027,4028],{"class":1786}," personal-site.service\n",[905,4030,4031,4033,4035,4037],{"class":907,"line":273},[905,4032,4019],{"class":1779},[905,4034,4022],{"class":1963},[905,4036,4025],{"class":1786},[905,4038,4039],{"class":1786}," personal-site-dev.service\n",[905,4041,4042,4045,4048],{"class":907,"line":35},[905,4043,4044],{"class":1779},"readlink",[905,4046,4047],{"class":1963}," -f",[905,4049,4050],{"class":1786}," ~\u002F.local\u002Fshare\u002Fpersonal-site\u002Fcurrent\n",[905,4052,4053,4055,4057],{"class":907,"line":1976},[905,4054,4044],{"class":1779},[905,4056,4047],{"class":1963},[905,4058,4059],{"class":1786}," ~\u002F.local\u002Fshare\u002Fpersonal-site-staging\u002Fcurrent\n",[905,4061,4062,4065,4068,4071,4074,4077,4080],{"class":907,"line":1986},[905,4063,4064],{"class":1779},"curl",[905,4066,4067],{"class":1963}," -fsS",[905,4069,4070],{"class":1963}," -o",[905,4072,4073],{"class":1786}," \u002Fdev\u002Fnull",[905,4075,4076],{"class":1963}," -w",[905,4078,4079],{"class":1786}," '%{http_code} %{content_type}\\n'",[905,4081,4082],{"class":1786}," http:\u002F\u002F127.0.0.1:3000\u002F\n",[905,4084,4085,4087,4089,4091,4093,4095,4097],{"class":907,"line":2596},[905,4086,4064],{"class":1779},[905,4088,4067],{"class":1963},[905,4090,4070],{"class":1963},[905,4092,4073],{"class":1786},[905,4094,4076],{"class":1963},[905,4096,4079],{"class":1786},[905,4098,4099],{"class":1786}," http:\u002F\u002F127.0.0.1:3100\u002F\n",[660,4101,4102],{"id":4102},"回滚",[115,4104,4105],{},"先列出确实存在的 release，再执行：",[897,4107,4109],{"className":3884,"code":4108,"language":3886,"meta":272,"style":272},"npm run rollback -- \u003Cexact-release-id>\n",[62,4110,4111],{"__ignoreMap":272},[905,4112,4113,4115,4117,4120,4123,4126,4129,4132],{"class":907,"line":908},[905,4114,3893],{"class":1779},[905,4116,3896],{"class":1786},[905,4118,4119],{"class":1786}," rollback",[905,4121,4122],{"class":1963}," --",[905,4124,4125],{"class":1944}," \u003C",[905,4127,4128],{"class":1786},"exact-release-i",[905,4130,4131],{"class":1771},"d",[905,4133,1798],{"class":1944},[115,4135,4136,4137,4139,4140,4142,4143,779],{},"回滚后再次检查 ",[62,4138,1191],{}," 和 3000 返回值。不要手动删除 ",[62,4141,1191],{},"，也不要把它指向仓库的 ",[62,4144,1187],{},[110,4146,4148],{"id":4147},"三十二故障排查顺序","三十二、故障排查顺序",[660,4150,4152],{"id":4151},"cms-保存后测试站看不到","CMS 保存后测试站看不到",[115,4154,4155,4156,4158,4159,4161,4162,1403,4165,4168,4169,4172,4173,4175],{},"先确认提交是否进入 GitHub ",[62,4157,1132],{},"，并在 GitHub Recent Deliveries 中确认 Webhook 返回 ",[62,4160,3024],{},"；再检查 ",[62,4163,4164],{},"personal-site-webhook.service",[62,4166,4167],{},"personal-site-staging-sync.service"," 日志、",[62,4170,4171],{},"~\u002F.local\u002Fstate\u002Fpersonal-site-staging-sync\u002Fdeployed-commit"," 和测试 ",[62,4174,1191],{},"。构建失败不会覆盖旧预览；事件文件会保留并延迟重试，也可以修复后手工启动同步服务。",[660,4177,4178],{"id":4178},"测试站正确但正式站没变化",[115,4180,4181,4182,4184,4185,4187,4188,4191,4192,4194],{},"确认是否实际运行过 ",[62,4183,2776],{},"、远端 master 是否已更新以及 master Webhook 是否返回 ",[62,4186,3024],{},"，再检查 ",[62,4189,4190],{},"personal-site-sync.service"," 和生产 ",[62,4193,2983],{},"。CMS 保存只更新测试站是正常设计，不是同步故障。",[660,4196,4198],{"id":4197},"rgb-不能拖按钮不能点动画不播放","RGB 不能拖、按钮不能点、动画不播放",[115,4200,4201,4202,4205,4206,4209],{},"先看 ",[62,4203,4204],{},"data-hydrated","，再看控制台错误和网络请求。若只发生在临时 Nuxt Dev 的第一次编译，等待完成后重载；若持续发生，停止那个临时开发进程，运行 ",[62,4207,4208],{},"npx nuxt cleanup"," 后重启开发端口。常驻 3000 和 3100 都是静态服务器，不使用这份开发缓存。",[660,4211,4212],{"id":4212},"切回标签页没有动画",[115,4214,4215,4216,1403,4218,4220],{},"检查浏览器是否启用 reduced motion、mask 图片是否加载失败，以及 ",[62,4217,2338],{},[62,4219,2341],{}," 是否触发。一次切回只播一次是正常防抖，不应去掉 200ms 合并后让三个事件连续触发。",[660,4222,4223],{"id":4223},"换色先闪回旧颜色",[115,4225,4226,4227,4229],{},"检查静态默认色、Nuxt ",[62,4228,1999],{}," 初始值和 localStorage 恢复顺序。服务端 HTML无法知道客户端保存颜色，完全避免首帧差异需要在页面渲染前执行极小的内联主题恢复脚本；当前实现选择保持静态生成简单，在水合时恢复。",[660,4231,4233],{"id":4232},"服务状态点不随-cms-变化","服务状态点不随 CMS 变化",[115,4235,4236,4237,4240,4241,4243],{},"确认 YAML 中 ",[62,4238,4239],{},"state"," 已保存、值属于三个枚举，并检查正在看的环境：CMS 保存后测试站应先变化，正式站要等 promote。若 DOM 属性已变而颜色没变，检查 CSS 状态选择器；若属性没变，检查对应环境的 ",[62,4242,1191],{}," 是否仍是旧 release。",[660,4245,4247],{"id":4246},"_3000-无法访问","3000 无法访问",[115,4249,4250,4251,4253,4254,4256],{},"检查用户级 systemd 状态、日志、Node 固定路径、端口占用和 ",[62,4252,1191],{}," 链接。服务器进程存在但 ",[62,4255,1191],{}," 失效时会返回 503。不要在未确认进程归属前直接杀端口进程。",[115,4258,4259,4260,4263,4264,4266],{},"3100 使用相同排查方法，但服务是 ",[62,4261,4262],{},"personal-site-dev.service","，数据根目录是 ",[62,4265,3146],{},"。正式站正常而测试站异常时，不要重启 3000。",[660,4268,4270],{"id":4269},"管理页面提示只允许-https","管理页面提示只允许 HTTPS",[115,4272,4273,4274,4276],{},"这通常来自浏览器安全上下文要求，而不是 Git remote 配错。Git remote 只决定仓库同步地址，不会给 HTTP 页面增加 HTTPS。远程后台应使用已经配置可信证书和 Basic Auth 的 ",[62,4275,2790],{},"；本地仓库模式则从 localhost 打开。",[110,4278,4280],{"id":4279},"三十三安全与备份边界","三十三、安全与备份边界",[115,4282,4283],{},"至少要区分三类数据：",[897,4285,4288],{"className":4286,"code":4287,"language":1160,"meta":272},[1158],"Git 仓库       源码、文章、站点设置、公开静态资源\nOpenClaw 目录  Skill、草稿、会话和本地配置\nrelease 目录   生产与测试当前及历史静态构建结果\n",[62,4289,4287],{"__ignoreMap":272},[115,4291,4292],{},"Git 提交是版本历史，不是异地备份。私人远程仓库可以备份源码与文章，但 OpenClaw 配置和 release 数据需要单独备份到受控存储。",[115,4294,4295,4296,4298],{},"永远不要提交或写入公开文章的内容包括：API key、OAuth secret、部署私钥、QQ token、cookie、扫码状态、",[62,4297,2783],{},"、私人服务密码、内部地址清单和不适合公开的资产信息。",[115,4300,4301,4302,4305,4306,4308],{},"CMS 的 ",[62,4303,4304],{},"\u002Fadmin\u002F"," 路径和 ",[62,4307,2794],{}," 只能减少搜索引擎收录，不能阻止陌生人访问。当前远程后台同时依靠可信 HTTPS、Nginx Basic Auth 和 GitHub OAuth；生产\u002F测试上游只监听回环地址，公网 IP 与未知 Host 由默认 Nginx 站点拒绝。真正的保护来自这些边界和最小权限凭据，而不是隐藏 URL。",[110,4310,4312],{"id":4311},"三十四维护这套代码时应坚持的原则","三十四、维护这套代码时应坚持的原则",[115,4314,4315],{},"第一，内容、表现和发布边界分开。能在 YAML 解决的文案不要硬编码进 Vue；能由共享组件表达的状态不要在两个页面复制；生产切换不要混进页面代码。",[115,4317,4318],{},"第二，把浏览器状态当成显式状态。主题色、资源是否解码、页面是否可见、指针是否仍按下、水合是否完成，都应有清楚的变量和生命周期，而不是靠延时猜测。",[115,4320,4321],{},"第三，动画必须有静态最终状态。资源失败、JavaScript 失败或用户减少动态效果时，标题仍要可读，布局仍要成立。",[115,4323,4324],{},"第四，自动化写入必须比人工编辑权限更窄。Skill 负责工作流，writer 负责硬边界，validator 负责内容合约，release 负责上线，四者不能互相冒充。",[115,4326,4327],{},"第五，生产部署应该是“生成一个完整新版本，然后切换”，而不是在访问者正在读取的目录里逐个覆盖文件。原子 release 是这个小网站最值得长期保留的工程设计之一。",[115,4329,4330,4331,4333],{},"第六，测试站和正式站必须由分支、仓库、release 与进程共同隔离。域名不同不等于环境隔离，CMS 保存也不等于生产发布；",[62,4332,1136],{}," 是有校验的明确边界。",[115,4335,4336],{},"第七，测试真实的关系和历史 bug。主题对比、滑杆松手、标签页重播、长标题 hover、隐藏文章 404、服务状态同步和多视口不重叠，都来自实际使用中的问题，比为了提高数字而堆大量脆弱测试更有价值。",[110,4338,4340],{"id":4339},"三十五重新上手时的最短阅读路径","三十五、重新上手时的最短阅读路径",[115,4342,4343],{},"如果未来很久没有维护这个项目，按下面顺序读，可以最快恢复上下文：",[133,4345,4346,4352,4360,4369,4375,4380,4392,4395],{},[136,4347,4348,4349,4351],{},"看 ",[62,4350,1413],{}," 的五份 YAML，知道全站和各页面当前展示什么。",[136,4353,4348,4354,1418,4357,4359],{},[62,4355,4356],{},"app\u002Fpages\u002Findex.vue",[62,4358,3796],{}," 的主页部分，理解首屏结构。",[136,4361,4348,4362,1403,4364,1403,4366,4368],{},[62,4363,1931],{},[62,4365,2114],{},[62,4367,2252],{},"，理解最复杂的交互。",[136,4370,4371,4372,4374],{},"看两个 writing 页面和 ",[62,4373,1500],{},"，理解文章查询与渲染。",[136,4376,4348,4377,4379],{},[62,4378,1650],{},"、validator 和 writer，理解内容边界。",[136,4381,4348,4382,1403,4385,1403,4388,4391],{},[62,4383,4384],{},"release.ts",[62,4386,4387],{},"sync.ts",[62,4389,4390],{},"promote.ts"," 和 systemd unit，理解双环境发布链路。",[136,4393,4394],{},"看 Nginx 模板与运维文档，理解域名、HTTPS、Basic Auth 和回环上游。",[136,4396,4397],{},"最后看测试，把“哪些行为不能再坏”快速过一遍。",[115,4399,4400],{},"只要能重新回答下面五个问题，就已经掌握了这个项目：",[184,4402,4403,4406,4409,4412,4415,4418],{},[136,4404,4405],{},"当前页面内容的唯一来源在哪里？",[136,4407,4408],{},"浏览器中的主题色如何从 RGB 状态传到 CSS、字标和 favicon？",[136,4410,4411],{},"为什么 CMS 保存后只改变测试站？",[136,4413,4414],{},"publish、deploy、sync 和 promote 的权限、检查与 Git 行为有什么不同？",[136,4416,4417],{},"一个新 release 如何在不重启 3000 的情况下接管生产请求？",[136,4419,4420],{},"3000 与 3100 为什么不会覆盖彼此的仓库和 current？",[115,4422,4423],{},"这些问题就是 Quilibra 当前技术架构的骨架。",[110,4425,4426],{"id":4426},"来源与延伸阅读",[184,4428,4429,4438,4445,4452,4459,4466,4473,4480],{},[136,4430,4431],{},[4432,4433,4437],"a",{"href":4434,"rel":4435},"https:\u002F\u002Fnuxt.com\u002Fdocs",[4436],"nofollow","Nuxt 文档",[136,4439,4440],{},[4432,4441,4444],{"href":4442,"rel":4443},"https:\u002F\u002Fcontent.nuxt.com\u002F",[4436],"Nuxt Content 文档",[136,4446,4447],{},[4432,4448,4451],{"href":4449,"rel":4450},"https:\u002F\u002Fvuejs.org\u002Fguide\u002Fextras\u002Fcomposition-api-faq.html",[4436],"Vue Composition API",[136,4453,4454],{},[4432,4455,4458],{"href":4456,"rel":4457},"https:\u002F\u002Fplaywright.dev\u002Fdocs\u002Fintro",[4436],"Playwright 文档",[136,4460,4461],{},[4432,4462,4465],{"href":4463,"rel":4464},"https:\u002F\u002Fgithub.com\u002Fsveltia\u002Fsveltia-cms",[4436],"Sveltia CMS 文档",[136,4467,4468],{},[4432,4469,4472],{"href":4470,"rel":4471},"https:\u002F\u002Fdeveloper.mozilla.org\u002Fen-US\u002Fdocs\u002FWeb\u002FCSS\u002Fmask",[4436],"MDN：CSS mask",[136,4474,4475],{},[4432,4476,4479],{"href":4477,"rel":4478},"https:\u002F\u002Fdeveloper.mozilla.org\u002Fen-US\u002Fdocs\u002FWeb\u002FAPI\u002FPage_Visibility_API",[4436],"MDN：Page Visibility API",[136,4481,4482],{},[4432,4483,4486],{"href":4484,"rel":4485},"https:\u002F\u002Fdeveloper.mozilla.org\u002Fen-US\u002Fdocs\u002FWeb\u002FHTTP\u002FCaching",[4436],"MDN：HTTP 缓存",[1072,4488,4489],{},"html pre.shiki code .sVt8B, html code.shiki .sVt8B{--shiki-default:#24292E;--shiki-dark:#E1E4E8}html pre.shiki code .s9eBZ, html code.shiki .s9eBZ{--shiki-default:#22863A;--shiki-dark:#85E89D}html pre.shiki code .sScJk, html code.shiki .sScJk{--shiki-default:#6F42C1;--shiki-dark:#B392F0}html pre.shiki code .sZZnC, html code.shiki .sZZnC{--shiki-default:#032F62;--shiki-dark:#9ECBFF}html .default .shiki span {color: var(--shiki-default);background: var(--shiki-default-bg);font-style: var(--shiki-default-font-style);font-weight: var(--shiki-default-font-weight);text-decoration: var(--shiki-default-text-decoration);}html .shiki span {color: var(--shiki-default);background: var(--shiki-default-bg);font-style: var(--shiki-default-font-style);font-weight: var(--shiki-default-font-weight);text-decoration: var(--shiki-default-text-decoration);}html .dark .shiki span {color: var(--shiki-dark);background: var(--shiki-dark-bg);font-style: var(--shiki-dark-font-style);font-weight: var(--shiki-dark-font-weight);text-decoration: var(--shiki-dark-text-decoration);}html.dark .shiki span {color: var(--shiki-dark);background: var(--shiki-dark-bg);font-style: var(--shiki-dark-font-style);font-weight: var(--shiki-dark-font-weight);text-decoration: var(--shiki-dark-text-decoration);}html pre.shiki code .szBVR, html code.shiki .szBVR{--shiki-default:#D73A49;--shiki-dark:#F97583}html pre.shiki code .s4XuR, html code.shiki .s4XuR{--shiki-default:#E36209;--shiki-dark:#FFAB70}html pre.shiki code .sj4cs, html code.shiki .sj4cs{--shiki-default:#005CC5;--shiki-dark:#79B8FF}html pre.shiki code .sJ8bj, html code.shiki .sJ8bj{--shiki-default:#6A737D;--shiki-dark:#6A737D}",{"title":272,"searchDepth":273,"depth":273,"links":4491},[4492,4493,4494,4495,4496,4497,4498,4499,4500,4501,4502,4503,4504,4505,4506,4507,4508,4509,4510,4511,4512,4513,4520,4521,4522,4523,4524,4529,4530,4531,4541,4548,4558,4559,4560,4561],{"id":1147,"depth":273,"text":1148},{"id":1198,"depth":273,"text":1199},{"id":1231,"depth":273,"text":1232},{"id":1361,"depth":273,"text":1362},{"id":1425,"depth":273,"text":1426},{"id":1557,"depth":273,"text":1558},{"id":1702,"depth":273,"text":1703},{"id":1815,"depth":273,"text":1816},{"id":1886,"depth":273,"text":1887},{"id":1924,"depth":273,"text":1925},{"id":2047,"depth":273,"text":2048},{"id":2108,"depth":273,"text":2109},{"id":2194,"depth":273,"text":2195},{"id":2291,"depth":273,"text":2292},{"id":2365,"depth":273,"text":2366},{"id":2436,"depth":273,"text":2437},{"id":2495,"depth":273,"text":2496},{"id":2537,"depth":273,"text":2538},{"id":2712,"depth":273,"text":2713},{"id":2801,"depth":273,"text":2802},{"id":2860,"depth":273,"text":2861},{"id":2899,"depth":273,"text":2900,"children":4514},[4515,4516,4517,4518,4519],{"id":2906,"depth":35,"text":2909},{"id":2924,"depth":35,"text":2927},{"id":2939,"depth":35,"text":2942},{"id":2996,"depth":35,"text":2999},{"id":3030,"depth":35,"text":2776},{"id":3081,"depth":273,"text":3082},{"id":3164,"depth":273,"text":3165},{"id":3306,"depth":273,"text":3307},{"id":3378,"depth":273,"text":3379},{"id":3478,"depth":273,"text":3479,"children":4525},[4526,4527,4528],{"id":3485,"depth":35,"text":3486},{"id":3526,"depth":35,"text":3527},{"id":3570,"depth":35,"text":3571},{"id":3624,"depth":273,"text":3625},{"id":3707,"depth":273,"text":3708},{"id":3741,"depth":273,"text":3742,"children":4532},[4533,4534,4535,4536,4537,4538,4539,4540],{"id":3745,"depth":35,"text":3746},{"id":3758,"depth":35,"text":3758},{"id":3770,"depth":35,"text":3770},{"id":3782,"depth":35,"text":3782},{"id":3799,"depth":35,"text":3799},{"id":3815,"depth":35,"text":3815},{"id":3825,"depth":35,"text":3825},{"id":3842,"depth":35,"text":3842},{"id":3851,"depth":273,"text":3852,"children":4542},[4543,4544,4545,4546,4547],{"id":3855,"depth":35,"text":3856},{"id":3881,"depth":35,"text":3881},{"id":3916,"depth":35,"text":3917},{"id":4009,"depth":35,"text":4009},{"id":4102,"depth":35,"text":4102},{"id":4147,"depth":273,"text":4148,"children":4549},[4550,4551,4552,4553,4554,4555,4556,4557],{"id":4151,"depth":35,"text":4152},{"id":4178,"depth":35,"text":4178},{"id":4197,"depth":35,"text":4198},{"id":4212,"depth":35,"text":4212},{"id":4223,"depth":35,"text":4223},{"id":4232,"depth":35,"text":4233},{"id":4246,"depth":35,"text":4247},{"id":4269,"depth":35,"text":4270},{"id":4279,"depth":273,"text":4280},{"id":4311,"depth":273,"text":4312},{"id":4339,"depth":273,"text":4340},{"id":4426,"depth":273,"text":4426},"记录 Quilibra 个人主页 2026 年 7 月版本的技术选型、页面架构、内容系统、主题动画、双环境发布、HTTPS、测试体系和维护方法。",{},"\u002Fwriting\u002F2026-07-26-quilibra-complete-technical-handbook","2026-07-26T23:46:23+08:00",{"title":1116,"description":4562},"quilibra-complete-technical-handbook","writing\u002F2026-07-26-quilibra-complete-technical-handbook",[4570,4571,1270,4572,1318,1310,4573],"Nuxt","Vue","Nuxt Content","静态部署","ILFf8HTh9BFuZakheSV0lJmjpNenfU1fBRtcsKT1umE",{"id":4576,"title":4577,"body":4578,"category":4728,"date":281,"description":4729,"extension":283,"hidden":284,"meta":4730,"navigation":31,"path":4731,"publishedAt":4565,"seo":4732,"slug":4733,"stem":4734,"tags":4735,"updated":281,"__hash__":4740},"writing\u002Fwriting\u002F2026-07-26-trading-cognition-and-compound-reality.md","交易是为了赚钱还是验证认知：从量化门槛到复利现实",{"type":107,"value":4579,"toc":4717},[4580,4583,4587,4590,4593,4597,4600,4603,4606,4609,4612,4615,4618,4622,4625,4645,4648,4659,4662,4665,4679,4682,4685,4688,4691,4694,4698,4701,4704,4708,4711,4714],[115,4581,4582],{},"刷到一个清华博士的视频，说想赚钱就去量化。但这对于普通人的门槛是不是太高？就算跨过去了，市场容量真的够赚到钱吗？带着这些问题和 AI 聊了一圈，从量化门槛一路聊到\"你到底为什么做交易\"，把思路理了一遍。",[110,4584,4586],{"id":4585},"机构量化-vs-系统化交易","机构量化 vs 系统化交易",[115,4588,4589],{},"机构量化（幻方、九坤级别）的门槛确实很高：博士学历、海量 tick 数据、专用算力、上亿起步资金，而且赛道内卷、alpha 衰减快、容量有限。",[115,4591,4592],{},"但普通人能做的是\"系统化交易\"而非机构量化。20 日均线 + 严格止损 + 行业 ETF，这套逻辑本质就是量化思维。真正的问题不是门槛，是把博士生的参照系错误地套用在自己身上。这类建议对清北学生可操作，对大多数人是废话，因为它省略了关键前提：清北学生的起点已经过滤掉 90% 的竞争。",[110,4594,4596],{"id":4595},"只是为了赚钱可能是对自己的贬低","\"只是为了赚钱\"可能是对自己的贬低",[115,4598,4599],{},"我说做交易只是为了赚钱。但仔细想想，我对系统和机制本身一直有探究欲--止损逻辑、去中心化的交易所架构、自己项目的技术选型会追求优雅和性能。真正纯粹为钱的人不会纠结止损后策略逻辑对不对。",[115,4601,4602],{},"如果账户明天清零，还会不会继续研究这套系统？答案可能是会的。因为做交易挣不挣钱是对我认知是否正确的检验，猜对了奖励，猜错了亏钱。我在市场里的目的是验证认知，想知道自己是怎样的人。",[115,4604,4605],{},"核心驱动不是钱（AI说的 我持怀疑态度），是\"认知闭环\"--市场是不会说谎的裁判，钱只是计分板。",[110,4607,4608],{"id":4608},"一个需要警惕的特质",[115,4610,4611],{},"对\"是否正确\"极度敏感的人，容易把坚持系统等同于坚持自我，把止损等同于承认\"我是错的\"。如果哪天开始扛单不是因为信号而是无法接受\"我看错了\"，这个特质就从优势变成风险。",[115,4613,4614],{},"关键区分：把\"验证认知\"和\"验证自我价值\"分开。市场说这笔交易错了，不等于整个判断体系错了。我相信市场大部分时间是对的，应该是我往市场靠去检验自己，而不是市场错的时候坚持自己。但空仓时说这话容易，真正重仓浮亏 5%、系统说止损但脑子里说\"只是短期波动\"的那一刻，这套认知还在不在才是真考验。",[115,4616,4617],{},"止损系统的价值不只是风控，更是强制执行那些理性认同但情绪会动摇的信念。",[110,4619,4621],{"id":4620},"_10-万到几百万数学可行执行淘汰大多数人","10 万到几百万：数学可行，执行淘汰大多数人",[115,4623,4624],{},"市场容量直接排除--A 股日均成交额 8000 亿到 1.2 万亿，几十万到几百万对市场影响约等于零。真实难度分三段：",[184,4626,4627,4633,4639],{},[136,4628,4629,4632],{},[139,4630,4631],{},"10 万到 100 万","：拼策略稳定性和纪律一致性",[136,4634,4635,4638],{},[139,4636,4637],{},"100 万到 500 万","：拼复利需要时间，以及账户变大后心态是否变化（100 万亏 10% 是 10 万，感受和 10 万亏 10% 完全不同）",[136,4640,4641,4644],{},[139,4642,4643],{},"500 万以上","：拼市场风格切换时系统能否不变形",[115,4646,4647],{},"参照现有系统严格执行，年化 15%-30% 的历史区间成立。三种情景的复利推算：",[184,4649,4650,4653,4656],{},[136,4651,4652],{},"**保守 15%**：10 万到 100 万约 17 年，到 500 万约 39 年",[136,4654,4655],{},"**中性 20%**：10 万到 100 万约 13 年，到 500 万约 21 年",[136,4657,4658],{},"**乐观 25%**：10 万到 100 万约 11 年，到 500 万约 15 年",[115,4660,4661],{},"数学上可行，但\"10 年严格执行\"淘汰的人比策略本身多。年化均值背后有巨大的方差，最大风险不是策略本身，而是震荡市连续止损后大多数人会改变规则。",[110,4663,4664],{"id":4664},"人群中能走到哪",[184,4666,4667,4670,4673,4676],{},[136,4668,4669],{},"能长期跑赢通胀：前 20%",[136,4671,4672],{},"年化 15%+ 持续 5 年以上：前 5%",[136,4674,4675],{},"系统化策略 10 年不变形：前 2-3%",[136,4677,4678],{},"10 万做到 500 万：前 1-2%",[115,4680,4681],{},"这个门槛不是天才门槛，而是纪律和时间门槛。输给认知的少，输给自己的多。",[110,4683,4684],{"id":4684},"交易不是生活的全部",[115,4686,4687],{},"正常认真上班拿现金流，有自己的爱好爱好，偶尔玩玩游戏，继续交易探索市场和世界。每个部分各司其职：工作管现金流，运动游戏管切换放松，交易是认知战场。",[115,4689,4690],{},"这个心态本身是优势。很多人把交易当生活全部，盯盘过度、情绪放大、决策变差。有工作提供现金流、有运动游戏切换状态，交易在这个结构里心理压力更小，压力小反而更容易执行系统。",[115,4692,4693],{},"但有一个前提：执行密度。想清楚和做到之间有 gap，关键不是今天想通了，而是明天还在做。",[110,4695,4697],{"id":4696},"一个佩服的-up-主","一个佩服的 UP 主",[115,4699,4700],{},"关注过一个 UP 主，清华毕业三十多岁，提过资产千万级。他的操作和认知确实让人佩服：航运信息差套利没记错一天挣了20w，6月8日暴跌开中证 500 期权多单为国护盘，科技股暴跌后判断主线仍是科技股事后看来也是对的。他还分享打新、股东福利等内容。",[115,4702,4703],{},"他的宏观判断和产业理解思路值得学，具体操作和套利思路也有参考价值。没有实盘记录这点确实无法验证，但判断以及操作确实很牛逼。",[110,4705,4707],{"id":4706},"a-股打新现状","A 股打新现状",[115,4709,4710],{},"查了一下 A 股打新的数据：2024 年新股首日平均涨幅 253%，单签平均收益约 2 万元；2025 年至 6 月，45 只新股无一破发。但主板网上中签率仅 0.03% 左右，参与人数超 1100 万户，普通人中签概率极低，更像是彩票。网下机构打新年化约 4.5%，但门槛是亿级资金账户。包括今年以来的一些大肉签（比如沐曦股份 摩尔线程 最近的长鑫存储）上市当日涨幅都很夸张，随便中几签都是几万块。可惜科创板门槛高啊。（我什么时候才能有50w）",[110,4712,4713],{"id":4713},"止损永远是最重要的",[115,4715,4716],{},"按照均线止损太死板，套到很多个股上买点太高了。但不管用什么方式入场，止损永远是第一位的。买点可以慢慢校准，进场理由可以有很多种，但止损必须进场前定好，而且不能因为浮亏就移动它。止损一旦守不住，什么策略都白搭。在市场里还是先活下来比较重要。",{"title":272,"searchDepth":273,"depth":273,"links":4718},[4719,4720,4721,4722,4723,4724,4725,4726,4727],{"id":4585,"depth":273,"text":4586},{"id":4595,"depth":273,"text":4596},{"id":4608,"depth":273,"text":4608},{"id":4620,"depth":273,"text":4621},{"id":4664,"depth":273,"text":4664},{"id":4684,"depth":273,"text":4684},{"id":4696,"depth":273,"text":4697},{"id":4706,"depth":273,"text":4707},{"id":4713,"depth":273,"text":4713},"交易","从清华博士的建议想要赚钱就去做量化出发，梳理普通人做系统化交易的现实路径、人群比例和自我认知驱动。",{},"\u002Fwriting\u002F2026-07-26-trading-cognition-and-compound-reality",{"title":4577,"description":4729},"trading-cognition-and-compound-reality","writing\u002F2026-07-26-trading-cognition-and-compound-reality",[4728,4736,4737,4738,4739],"量化","认知","复利","系统化交易","ncRskTdQt3kCImby9qKSkM5aUBbcdCb-MjUngbbkfEs",{"id":4742,"title":4743,"body":4744,"category":4728,"date":281,"description":4920,"extension":283,"hidden":284,"meta":4921,"navigation":31,"path":4922,"publishedAt":4923,"seo":4924,"slug":4925,"stem":4926,"tags":4927,"updated":295,"__hash__":4934},"writing\u002Fwriting\u002F2026-07-26-btc-synthetic-futures-arbitrage.md","BTC期权合成期货套利：用定价偏差锁定无风险利润",{"type":107,"value":4745,"toc":4911},[4746,4749,4753,4756,4759,4763,4769,4777,4780,4783,4786,4792,4798,4801,4805,4808,4811,4815,4822,4828,4831,4834,4848,4853,4861,4867,4872,4883,4888,4898,4901,4905,4908],[115,4747,4748],{},"看到一套量化交易系统看板：年化 197%、极低回撤、多平仓少持仓。核心策略是利用 Deribit 期权市场合成的虚拟期货价格与 Binance 永续合约价格之间的定价偏差进行对冲套利。当时对着看板追问了 AI 几轮，才把这套合成期货套利的逻辑理清楚。这篇文章是给自己留的复盘笔记。",[110,4750,4752],{"id":4751},"deribit-是什么","Deribit 是什么",[115,4754,4755],{},"Deribit 是目前加密货币期权交易量最大的交易所，总部在巴拿马，2016 年上线。BTC 和 ETH 期权的主流流动性都在这里，行权价、到期日覆盖范围广，机构参与度高。它的期权以币本位结算（BTC 期权用 BTC 结算），也支持永续合约和交割期货。",[115,4757,4758],{},"正因为 Deribit 在期权市场的深度优势，它的期权定价成为合成期货策略的基础端。另一端选 Binance 永续合约，是因为 Binance 流动性最好、API 稳定、支持开空，适合做对冲腿。",[110,4760,4762],{"id":4761},"核心概念合成期货","核心概念：合成期货",[115,4764,4765,4766,779],{},"期权平价公式告诉我们：",[139,4767,4768],{},"买入 Call + 卖出 Put = 合成期货多头",[184,4770,4771,4774],{},[136,4772,4773],{},"买入 Call（看涨期权）：涨了赚，跌了不亏",[136,4775,4776],{},"卖出 Put（看跌期权）：涨了没事，跌了亏",[115,4778,4779],{},"两者组合后的盈亏轨迹，与直接持有 BTC 现货或期货完全一致——涨了赚、跌了亏。这就是\"合成期货\"。",[110,4781,4782],{"id":4782},"两种套利方向",[115,4784,4785],{},"关键在于：Deribit 期权合成的期货价格，和 Binance 永续合约的价格，经常不一致。",[115,4787,4788,4791],{},[139,4789,4790],{},"转换套利（Conversion \u002F SF+BS）","：当合成期货价格 > Binance 期货价格时，在 Deribit 卖出合成期货（卖出 Call + 买入 Put），同时在 Binance 买入永续合约。",[115,4793,4794,4797],{},[139,4795,4796],{},"反转套利（Reversal \u002F BF+SS）","：当合成期货价格 \u003C Binance 期货价格时，在 Deribit 买入合成期货（买入 Call + 卖出 Put），同时在 Binance 卖出永续合约。",[115,4799,4800],{},"本质上就是\"哪边便宜哪边买，哪边贵了哪边卖\"。",[110,4802,4804],{"id":4803},"对冲机制为什么涨跌都不怕","对冲机制：为什么涨跌都不怕",[115,4806,4807],{},"币安端永远做与 Deribit 完全相反的动作。Deribit 开合成多头，Binance 必开永续空头。BTC 暴涨暴跌时，两端的盈亏完全抵消，行情风险归零。",[115,4809,4810],{},"利润不来自方向判断，而是开仓瞬间两端的定价差。期权到期结算时，合成价格强制回归现货价，基差收敛完成无风险落袋。",[110,4812,4814],{"id":4813},"一个关键区分行权价-现价","一个关键区分：行权价 ≠ 现价",[115,4816,4817,4818,4821],{},"看板中的合约代码 ",[62,4819,4820],{},"BTC-10JUN26-62000","，62000 是行权价，不是当时的市场现价。当时 Binance 的标记价约为 62,991 美元。因为现价高于行权价，看涨期权呈浮盈状态，看跌期权的权利金已被吃满。",[115,4823,4824,4825,779],{},"比对套利机会时，标的不是行权价本身，而是",[139,4826,4827],{},"合成总成本 = 行权价 + 买Call权利金 - 卖Put权利金",[110,4829,4830],{"id":4830},"完整数值演练",[115,4832,4833],{},"设定场景：",[184,4835,4836,4839,4842],{},[136,4837,4838],{},"Binance 现价：$63,000",[136,4840,4841],{},"Deribit 行权价 $62,000 的期权：Call 权利金 $1,500，Put 权利金 $300",[136,4843,4844,4845],{},"合成空头总成本：$62,000 + $1,500 - $300 = ",[139,4846,4847],{},"$63,200",[115,4849,4850],{},[139,4851,4852],{},"开仓动作：",[184,4854,4855,4858],{},[136,4856,4857],{},"Binance 开多，$63,000",[136,4859,4860],{},"Deribit 合成空头（卖 Call + 买 Put），$63,200",[115,4862,4863,4864],{},"开仓瞬间锁定利润：$63,200 - $63,000 = ",[139,4865,4866],{},"$200",[115,4868,4869],{},[139,4870,4871],{},"极端行情 A - 暴涨至 $80,000：",[184,4873,4874,4877,4880],{},[136,4875,4876],{},"Binance 多头盈利：+$17,000",[136,4878,4879],{},"Deribit 合成空头亏损：-$16,800",[136,4881,4882],{},"合计：**+$200**",[115,4884,4885],{},[139,4886,4887],{},"极端行情 B - 暴跌至 $40,000：",[184,4889,4890,4893,4896],{},[136,4891,4892],{},"Binance 多头亏损：-$23,000",[136,4894,4895],{},"Deribit 合成空头盈利：+$23,200",[136,4897,4882],{},[115,4899,4900],{},"无论涨跌，总利润始终为开仓时锁定的 $200。到期自动结算收敛，整个过程是左手倒右手，利润在开仓那一下就已经确定了。",[110,4902,4904],{"id":4903},"模型不难难的是工程","模型不难，难的是工程",[115,4906,4907],{},"这套套利的交易系统作者说过一句话：套利模型倒是不难，难的是一些工程上的事情。",[115,4909,4910],{},"确实，逻辑上这就是一个低买高卖的价差捕捉，但实际跑起来要处理的问题远比模型复杂：两个交易所的 API 延迟差异、订单簿深度和滑点、手续费摩擦对利润空间的侵蚀、资金在两个平台之间的划转效率、期权到期时的自动结算对齐、极端行情下的风控（硬止损、Gamma 检查、行情降级）、以及资金容量上限——能套的价差就那么多，资金量大了反而吃不掉。这些工程细节才是决定系统能不能真正赚钱的关键。",{"title":272,"searchDepth":273,"depth":273,"links":4912},[4913,4914,4915,4916,4917,4918,4919],{"id":4751,"depth":273,"text":4752},{"id":4761,"depth":273,"text":4762},{"id":4782,"depth":273,"text":4782},{"id":4803,"depth":273,"text":4804},{"id":4813,"depth":273,"text":4814},{"id":4830,"depth":273,"text":4830},{"id":4903,"depth":273,"text":4904},"看到一套量化套利系统后追问AI，拆解Deribit合成期货与Binance永续合约之间的跨平台套利逻辑，含完整数值演练。",{},"\u002Fwriting\u002F2026-07-26-btc-synthetic-futures-arbitrage","2026-07-26T13:37:11+08:00",{"title":4743,"description":4920},"btc-synthetic-futures-arbitrage","writing\u002F2026-07-26-btc-synthetic-futures-arbitrage",[4928,4929,4930,4931,4932,4933],"BTC","期权","套利","合成期货","Deribit","Binance","o_rfqacyzAeRyNmp-ceWG6OvHBlEmjo5vFyZhRVC06A",{"id":104,"title":105,"body":4936,"category":280,"date":281,"description":282,"extension":283,"hidden":284,"meta":5047,"navigation":31,"path":286,"publishedAt":287,"seo":5048,"slug":289,"stem":290,"tags":5049,"updated":295,"__hash__":296},{"type":107,"value":4937,"toc":5040},[4938,4940,4942,4948,4950,4960,4962,4964,4974,4976,4978,4980,4982,4996,5000,5002,5004,5006,5018,5020,5022,5038],[110,4939,113],{"id":112},[115,4941,117],{},[115,4943,120,4944,124,4946,128],{},[62,4945,123],{},[62,4947,127],{},[115,4949,131],{},[133,4951,4952,4956],{},[136,4953,4954,142],{},[139,4955,141],{},[136,4957,4958,148],{},[139,4959,147],{},[110,4961,152],{"id":151},[115,4963,155],{},[133,4965,4966,4968,4970,4972],{},[136,4967,160],{},[136,4969,163],{},[136,4971,166],{},[136,4973,169],{},[115,4975,172],{},[110,4977,176],{"id":175},[115,4979,179],{},[115,4981,182],{},[184,4983,4984,4988,4992],{},[136,4985,4986,191],{},[139,4987,190],{},[136,4989,4990,197],{},[139,4991,196],{},[136,4993,4994,203],{},[139,4995,202],{},[115,4997,206,4998,210],{},[62,4999,209],{},[110,5001,213],{"id":213},[115,5003,216],{},[115,5005,219],{},[184,5007,5008,5012],{},[136,5009,5010,227],{},[139,5011,226],{},[136,5013,5014,233,5016,237],{},[139,5015,232],{},[62,5017,236],{},[110,5019,240],{"id":240},[115,5021,243],{},[184,5023,5024,5030,5034],{},[136,5025,5026,251,5028,255],{},[139,5027,250],{},[62,5029,254],{},[136,5031,5032,261],{},[139,5033,260],{},[136,5035,5036,267],{},[139,5037,266],{},[115,5039,270],{},{"title":272,"searchDepth":273,"depth":273,"links":5041},[5042,5043,5044,5045,5046],{"id":112,"depth":273,"text":113},{"id":151,"depth":273,"text":152},{"id":175,"depth":273,"text":176},{"id":213,"depth":273,"text":213},{"id":240,"depth":273,"text":240},{},{"title":105,"description":282},[292,293,294],1785768183757]