<?xml version="1.0" encoding="UTF-8"?><rss version="2.0" xmlns:content="http://purl.org/rss/1.0/modules/content/"><channel><title>Saurlax&apos;s Blog</title><description>A blog about web development, programming, and more.</description><link>https://saurlax.com/</link><item><title>代码如诗</title><link>https://saurlax.com/blog/code-like-poetry/</link><guid isPermaLink="true">https://saurlax.com/blog/code-like-poetry/</guid><description>少年时读散文，只见辞藻如繁花，  
却不懂那字里行间，藏着未说出口的牵挂。  
为赋新词，强说愁绪，  
像初学写作者，只描摹事物的表皮，  
却不知每一篇佳作，都有明线与暗线交织，  
情感如暗流，...</description><pubDate>Wed, 08 Apr 2026 09:37:21 GMT</pubDate><content:encoded>&lt;p&gt;少年时读散文，只见辞藻如繁花，&lt;br /&gt;
却不懂那字里行间，藏着未说出口的牵挂。&lt;br /&gt;
为赋新词，强说愁绪，&lt;br /&gt;
像初学写作者，只描摹事物的表皮，&lt;br /&gt;
却不知每一篇佳作，都有明线与暗线交织，&lt;br /&gt;
情感如暗流，在背景与经历中悄然涌动。&lt;/p&gt;
&lt;p&gt;我们赏析，我们揣摩，&lt;br /&gt;
结合作者的生平，去读懂那未言的痛与爱。&lt;br /&gt;
因为真正的表达，从不在表面张扬，&lt;br /&gt;
而在隐晦处，让意义自然流淌。&lt;/p&gt;
&lt;p&gt;如今我写代码，亦如写诗，&lt;br /&gt;
不愿堆砌冗余的配置，如死板的注释，&lt;br /&gt;
不愿在繁琐的教程里，重复千篇一律的样板。&lt;br /&gt;
我渴望类型如溪流，在模块间自由穿行，&lt;br /&gt;
像 &lt;a href=&quot;https://nuxt.com/&quot;&gt;Nuxt&lt;/a&gt; 于 &lt;a href=&quot;https://nitro.build/&quot;&gt;Nitro&lt;/a&gt;，InternalApi 悄然传递，&lt;br /&gt;
像 &lt;a href=&quot;https://orm.drizzle.team/&quot;&gt;Drizzle ORM&lt;/a&gt;，All in TypeScript，一切尽在定义之中。&lt;/p&gt;
&lt;p&gt;我不写 &lt;code&gt;interface User { id: number; name: string }&lt;/code&gt; 然后到处导入，&lt;br /&gt;
我让类型流动，&lt;br /&gt;
让 infer 从数据库的 schema 中，&lt;br /&gt;
推演出前端的表单校验，&lt;br /&gt;
让 ReturnType 成为组件的契约，&lt;br /&gt;
无需重复声明，却处处严谨。&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-ts&quot;&gt;// 类型如诗，不言自明
const user = await db.query.users.findFirst({ where: { id } });
// user 的类型，已在 query 中悄然生成
// 无需再写 interface，无需再导 import
// 一切如呼吸般自然
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;代码如诗，不在繁复，而在留白，&lt;br /&gt;
不在重复，而在推导，&lt;br /&gt;
不在死板，而在流动。&lt;br /&gt;
当类型在函数间穿行，如情感在段落间蔓延，&lt;br /&gt;
当逻辑如韵律，自然溢出注释之外，&lt;br /&gt;
那一刻，代码不再是冰冷的指令，&lt;br /&gt;
而是思想的低语，是架构的抒情。&lt;/p&gt;
&lt;p&gt;我们不再为赋新码强写接口，&lt;br /&gt;
而是让类型如诗行，在静默中诉说一切。&lt;/p&gt;
&lt;p&gt;诗如代码，不在华丽，而在精准，&lt;br /&gt;
不在堆砌，而在结构，&lt;br /&gt;
不在直白，而在隐喻。&lt;br /&gt;
当诗行的节奏，如函数的调用链般清晰，&lt;br /&gt;
当情感的递进，如类型的继承般自然，&lt;br /&gt;
当意象的跳跃，如异步的 Promise 般优雅，&lt;br /&gt;
那一刻，诗不再是文字的堆砌，&lt;br /&gt;
而是逻辑的绽放，是情感的算法。&lt;/p&gt;
&lt;p&gt;我们不再为赋新诗强说愁，&lt;br /&gt;
而是让情感如类型，在字里行间自动推导，&lt;br /&gt;
在留白处，完成最精准的表达。&lt;/p&gt;</content:encoded></item><item><title>爱情心理学</title><link>https://saurlax.com/blog/psychology-of-love/</link><guid isPermaLink="true">https://saurlax.com/blog/psychology-of-love/</guid><description>爱情是人类最复杂的情感之一，它涉及生理、心理、社会等多个层面。理解爱情的心理学，可以帮助我们更好地认识自己和他人，建立更健康的关系。

## 吸引法则

### 看脸的世界？

外貌在吸引的起点阶段非...</description><pubDate>Mon, 06 Apr 2026 11:13:29 GMT</pubDate><content:encoded>&lt;p&gt;爱情是人类最复杂的情感之一，它涉及生理、心理、社会等多个层面。理解爱情的心理学，可以帮助我们更好地认识自己和他人，建立更健康的关系。&lt;/p&gt;
&lt;h2&gt;吸引法则&lt;/h2&gt;
&lt;h3&gt;看脸的世界？&lt;/h3&gt;
&lt;p&gt;外貌在吸引的起点阶段非常重要。它先决定注意力如何被分配，再决定后续判断是否偏向正面，因此很容易触发光环效应。一个外表吸引的人，常会被自动赋予能力强、性格好、值得信赖等评价，这种判断并不完全来自事实，而来自认知捷径。&lt;/p&gt;
&lt;p&gt;外貌的作用是入口作用，不是结局作用。它可以提高接触概率、提高初始容忍度，也可以增强约会机会，但它不能替代沟通、责任与稳定性。真正进入长期相处后，决定关系质量的，是生活协同能力、情绪调节能力和承诺能力。&lt;/p&gt;
&lt;p&gt;美貌带来的还有一种隐蔽效果，就是被提前填满。一个人因为外表而被持续抬高期待时，后面哪怕只是普通表现，也容易被理解成落差。于是美貌既能带来红利，也会制造额外压力。越早被看见的人，越早被审视；越容易被注意的人，也越容易被误读。&lt;/p&gt;
&lt;p&gt;颜值并不是单纯的审美问题，而是社会资源分配的问题。它会影响你是否更容易被邀请、是否更容易获得善意、是否更容易被给予第二次机会。也正因为如此，外貌并非可有可无，而是一个会真实改变社交路径的变量。只是这种变量的影响范围有限，它能打开门，却很难替你走完整条路。&lt;/p&gt;
&lt;p&gt;外貌还有一个很现实的时间维度。年轻时，颜值常常被直接看见；进入更长的关系周期后，气质、谈吐、稳定性和生活管理能力会逐步抬升权重。也就是说，外貌的优势并不会自动消失，但它会被其他能力重新排序。一个只会凭外貌获得起点的人，通常很难在长期关系里继续保持优势。&lt;/p&gt;
&lt;p&gt;这也解释了为什么“红利”和“代价”总是会同时出现。一个人如果长期享受外貌带来的便利，就很容易对关系中的互动成本估计不足，以为别人靠近只是因为喜欢自己，忽略了外表本身也在替关系制造门槛。真正成熟的理解，是知道外貌能带来开局，但不能替代经营。&lt;/p&gt;
&lt;h3&gt;只有接近，才能喜欢&lt;/h3&gt;
&lt;p&gt;喜欢常常从频繁接触中生成。接近减少陌生感，提高熟悉度，也降低判断成本。人会在反复互动中逐步形成心理舒适区，而舒适感本身就是好感增长的前提。&lt;/p&gt;
&lt;p&gt;但接近并不等于喜欢。若互动质量低、边界感差、相处体验紧张，高频接触只会放大反感。真正有效的接近，应当是低压力、高可预测、能够持续获得正反馈的接近。&lt;/p&gt;
&lt;p&gt;近水楼台先得月、远亲不如近邻这类俗语，说的不是空间本身，而是空间所带来的互动机会。距离越近，交换越频繁，越容易产生信息累积和情绪累积。熟悉感的形成并不神秘，它本质上就是重复暴露和低成本回应不断叠加的结果。&lt;/p&gt;
&lt;p&gt;但接近也有边界。一个人如果总是越界出现、频繁打扰、在对方没有准备时强行推进，接近就会从机会变成压力。爱情里常见的误区，就是把常见面误认成会喜欢，把能接触误认成能靠近。真正的接近不是黏住对方，而是让对方愿意继续留在互动里。&lt;/p&gt;
&lt;p&gt;可以把接近理解成三层：&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;物理接近，先让彼此有机会被看见。&lt;/li&gt;
&lt;li&gt;心理接近，再让彼此愿意交换信息。&lt;/li&gt;
&lt;li&gt;关系接近，最后让彼此形成稳定期待。&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;前两层可以制造好感，第三层才决定关系是否真的开始。&lt;/p&gt;
&lt;p&gt;接近的节奏也很重要。太快会让对方没有准备，太慢又会让关系迟迟无法形成。搭讪并不是追求技巧花样，而是判断时机和回应。你能不能看懂对方有没有继续聊天的意愿，能不能在对方回应冷淡时及时后退，这些其实比话术更重要。&lt;/p&gt;
&lt;p&gt;换句话说，接近不是把对方拉进来，而是试探彼此是否能在同一节奏上继续走。很多失败并不在于不够努力，而在于把一次性的主动当成了持续性的资格。&lt;/p&gt;
&lt;h3&gt;喜欢和自己相像的人&lt;/h3&gt;
&lt;p&gt;相似性是长期吸引的重要基础。背景相似、价值观相近、表达方式相通、生活节奏一致，都会减少解释成本与协调成本。两个人越相似，就越容易共享对世界的理解，也越容易形成稳定协同。&lt;/p&gt;
&lt;p&gt;互补可以带来新鲜感，却很难单独支撑长期关系。长期关系需要的不是刺激，而是可持续的协商。相像的人更容易相处，也更容易在日常中形成稳定的默契。&lt;/p&gt;
&lt;p&gt;相像的作用，尤其体现在日常琐事上。真正让关系消耗的，往往不是一次大争执，而是无数次小地方的你怎么会这样想。背景接近的人，更容易在这些地方少一点解释，多一点默认；少一点防守，多一点合作。&lt;/p&gt;
&lt;p&gt;不过，相像不是完全一样。完全一样会失去互相补充的空间，完全不同又会增加理解成本。更合理的状态，是核心价值相近，细节风格保留差异。这样既能共享方向，也能保留弹性。&lt;/p&gt;
&lt;h3&gt;喜欢得不到的人&lt;/h3&gt;
&lt;p&gt;阻碍会放大对象的心理价值。越难得到，越容易被想象得更好，越容易激发占有欲和追逐欲。这种心理并不说明对象一定更好，而说明稀缺性会提高主观价值。&lt;/p&gt;
&lt;p&gt;这种关系常常夹杂着逆反心理。人有时不是更喜欢对象本身，而是更喜欢“克服阻碍之后得到”的感觉。理解这一点，能够帮助区分真实吸引和被阻碍激活的冲动。&lt;/p&gt;
&lt;p&gt;所谓红玫瑰与白玫瑰，指的是人在选择中对“已得到”和“未得到”不断重估：得到的那一方容易被日常化，未得到的那一方容易被理想化。对象一旦难以到手，想象就会自动补位，现实的轮廓反而变得模糊。于是人追逐的，有时不是某个具体的人，而是如果我得到了，那就证明我很厉害的自我想象。&lt;/p&gt;
&lt;p&gt;这种喜欢还常常和不服输有关。越是被拒绝，越想证明自己；越是被忽略，越想夺回主动权。它不是爱情最稳固的根基，却是很多追逐行为最真实的动力。识别这一点，可以避免把征服欲当成深情。&lt;/p&gt;
&lt;h2&gt;亲密关系&lt;/h2&gt;
&lt;h3&gt;恋爱的感觉&lt;/h3&gt;
&lt;p&gt;恋爱的感觉并不是单纯情绪，而是多巴胺、生理唤醒、气味偏好和认知期待共同构成的体验。它会带来兴奋、理想化和风险低估，也会让人倾向于把短期强度误认为长期匹配。&lt;/p&gt;
&lt;p&gt;感觉适合作为起点，却不适合作为结论。真正稳定的关系，不是让人最上头的关系，而是热度下降后仍然愿意投入的关系。热烈是一种现象，长期相处才是判断标准。&lt;/p&gt;
&lt;p&gt;要有感觉才行这句话听起来朴素，实际上包含了大量未说出口的判断。感觉来自线索整合：外表、气味、说话方式、回应速度、对方是否让自己放松。很多时候，人说自己没感觉，并不是完全没有原因，而是身体和认知没有同时被激活。&lt;/p&gt;
&lt;p&gt;但感觉有很强的误导性。它擅长判断我愿不愿意靠近，不擅长判断我们能不能过日子。短期感觉强，不代表长期协同强；短期没感觉，也不代表关系没有发展空间。把感觉从决策中完全剔除不现实，但把感觉当成唯一标准，会让关系很容易在热闹和失落之间反复摆动。&lt;/p&gt;
&lt;h3&gt;情为何物&lt;/h3&gt;
&lt;p&gt;爱情可以理解为一个结构，而不是一个瞬间。亲密对应理解，激情对应动力，承诺对应方向。三者共同存在，爱情才比较完整；若缺失其中一项，关系就会失衡。&lt;/p&gt;
&lt;p&gt;爱情结构可以帮助识别关系状态。很多关系不是突然坏掉，而是某一项长期缺位后，整体慢慢失衡。爱情不是一个单一感受，而是一种持续组织关系的方式。&lt;/p&gt;
&lt;p&gt;亲密不是简单的聊天很多，而是彼此愿意让对方进入自己的内心世界。它体现为理解、支持、分享、尊重与稳定沟通，也体现为愿意把对方的幸福纳入自己的考虑。亲密像关系的土壤，决定这段关系能不能扎根。&lt;/p&gt;
&lt;p&gt;激情也不是只指身体上的吸引，它更像一种推动关系前进的能量。它包含生理唤醒和情感冲动，比如心跳加速、紧张兴奋、强烈想念、占有倾向和理想化想象。激情像火花，能快速点燃关系，也最容易在没有其他支撑时迅速回落。&lt;/p&gt;
&lt;p&gt;承诺不是一句口头保证，而是愿意在不确定中继续承担。它既有短期层面，例如愿意维系当前关系、对关系负责，也有长期层面，例如愿意规划共同未来、在压力和诱惑中保持忠诚。承诺像护城河，决定关系能否跨越波动并持续下去。&lt;/p&gt;
&lt;p&gt;三者组合起来，才构成比较完整的爱。激情提供起点和动力，亲密提供深度和连接，承诺提供稳定和方向。只有其中一项很强，关系都可能显得偏科；三项越平衡，关系越有韧性。&lt;/p&gt;
&lt;p&gt;很多关系的问题，恰恰来自把三者混为一谈。有人以为激情高就等于爱很深，结果关系一降温就怀疑感情；有人以为承诺稳就一定等于真爱，结果相处多年却缺少温度；还有人把亲密误当成安全，结果只剩依赖，没有方向。理解三元结构，是为了让关系诊断更准确。&lt;/p&gt;
&lt;p&gt;| 成分 | 核心含义         | 缺失后的表现       |
| ---- | ---------------- | ------------------ |
| 亲密 | 理解、信任、接纳 | 关系疏离、缺少沟通 |
| 激情 | 吸引、动机、热度 | 关系平淡、推进缓慢 |
| 承诺 | 维持、承担、方向 | 关系脆弱、容易动摇 |&lt;/p&gt;
&lt;h3&gt;爱情的类型&lt;/h3&gt;
&lt;p&gt;爱情类型的差异，本质上是三要素比例不同。根据激情、亲密、承诺的有无与强弱，可以形成七种常见类型。类型不是给人贴标签，而是帮助判断关系当前处在哪个阶段、缺了哪一块。&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;喜欢式爱情：只有亲密，没有明显激情和承诺。双方彼此理解、相处舒适，但关系尚未进入明确的伴侣方向。&lt;/li&gt;
&lt;li&gt;迷恋式爱情：只有激情，没有稳固亲密和承诺。关系往往来得快、强度高，也可能因为基础薄弱而快速降温。&lt;/li&gt;
&lt;li&gt;空洞式爱情：只有承诺，缺少亲密与激情。关系可能出于责任、习惯或现实结构而维持，情感体验却逐渐变薄。&lt;/li&gt;
&lt;li&gt;浪漫式爱情：亲密加激情，承诺不足。双方有连接、有心动，但未来方向和稳定性还不够清晰。&lt;/li&gt;
&lt;li&gt;愚蠢式爱情：激情加承诺，亲密不足。关系推进很快，甚至很快绑定长期安排，但彼此真实了解不够，后续磨合压力较大。&lt;/li&gt;
&lt;li&gt;伴侣式爱情：亲密加承诺，激情减弱。关系稳定、可靠、合作性高，常见于长期关系的成熟阶段。&lt;/li&gt;
&lt;li&gt;完美爱情：激情、亲密、承诺三者兼具。它不是永恒高点，而是一种需要持续维护的动态平衡。&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;理解类型的价值，在于判断关系的走向。激情仍在但承诺不足，关系容易热闹而脆弱；亲密和承诺在而激情退潮，关系容易进入平稳但缺少活力的状态。识别变化，比执着于命名更重要。&lt;/p&gt;
&lt;p&gt;爱情像故事，每个人的故事类型不一样。有些关系从强烈吸引开始，再慢慢补足理解；有些关系从朋友走向伴侣，起步慢却更稳；也有些关系在开始时很完整，后来因为某一项长期缺位而慢慢变形。类型的意义，不是给关系贴死标签，而是看清它现在缺哪一块。&lt;/p&gt;
&lt;p&gt;关系在不同阶段改变形态，这一点很容易被忽视。刚开始在意的是激情，之后在意的是默契，再往后在意的是承诺和协作。很多人会在关系进入平稳期时误以为感情变淡，其实只是关系从“追求”转入“经营”。如果还用追求期的标准去看经营期，就会不断误判。&lt;/p&gt;
&lt;p&gt;因此，理解爱情类型不是为了分类人，而是为了避免把某一阶段的关系当成唯一标准。激情少一点并不等于不爱，安稳多一点也不等于无聊。关键在于，这段关系有没有继续长出新的能力。&lt;/p&gt;
&lt;h3&gt;安全感&lt;/h3&gt;
&lt;p&gt;安全感来源于关系的可预测性，而不是短期安慰。稳定回应、边界清楚、行动一致，都会让人感到可靠；反复试探、频繁确认、要求解释，则常常说明内在不安。&lt;/p&gt;
&lt;p&gt;真正的安全感不是控制对方，而是让彼此都能感到可依赖。短期控制或许能缓解焦虑，但长期会侵蚀信任。安全感的建立依赖一致性，而不是压力。&lt;/p&gt;
&lt;p&gt;安全感在关系里经常被误解成对方给我很多承诺，我就安全了。但承诺如果没有行为配合，只会变成语言上的安抚。更关键的是依恋和爱的信任：你是否知道对方大致会怎么回应你，是否知道冲突后能不能回到联系，是否知道对方在重要时刻会不会站在你这一边。&lt;/p&gt;
&lt;p&gt;所以，安全感不是一瞬间被说服，而是一次次互动之后形成的预期。它的建立靠的是可重复的稳定，而不是某次特别大的浪漫。关系里越不稳定的人，越容易反复追问你还爱不爱我；越稳定的人，越容易把爱落实成日常一致性。&lt;/p&gt;
&lt;p&gt;安全感还和依恋经验有关。一个人在原生关系里如果经常经历忽冷忽热、承诺失效或回应不一致，进入亲密关系后就更容易对细小变化高度敏感。这样的人不是故意多疑，而是过去的经验让他更难相信稳定会持续。理解这一点，能减少把安全感问题简单归结为“你想太多”。&lt;/p&gt;
&lt;p&gt;关系稳定比口头保证更能安顿人。说“我在”很容易，难的是在冲突、压力、忙碌和误解里仍然保持基本一致。安全感最怕的不是暂时忙，而是长期不一致。&lt;/p&gt;
&lt;p&gt;还有一个非常关键的分析框架：安全感可以拆成两个维度来看。&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;焦虑维度：核心是“怕失去”，常见体验是担心被抛弃、担心自己不够重要。&lt;/li&gt;
&lt;li&gt;回避维度：核心是“怕靠近”，常见体验是回避亲密接触、回避依赖与暴露脆弱。&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;当这两个维度交叉时，会形成四种典型的依恋四象限：&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;安全型（低焦虑、低回避）：这类人可以比较安心地建立亲密关系，既能依赖也能独立，不会长期陷在“对方会不会不要我”的恐惧里，也更容易相信自己值得被爱。&lt;/li&gt;
&lt;li&gt;痴迷型（高焦虑、低回避）：这类人很渴望亲密，但又高度担心失去，常处于“又想靠近、又怕被丢下”的紧绷状态。为了获得关注和爱，往往会做很多努力，关系中容易出现反复确认和情绪拉扯。&lt;/li&gt;
&lt;li&gt;疏离型（低焦虑、高回避）：这类人强调独立、回避亲密，较难信任并依赖对方。关系推进到需要更深连接时，容易先退一步，也容易让伴侣感到被推开，进而诱发伴侣焦虑。&lt;/li&gt;
&lt;li&gt;恐惧型（高焦虑、高回避）：这类人内心期待亲密，但同时强烈害怕受伤，常见表现是犹豫与抗拒并存。既想依赖、又想抽离；既怕被拒绝，也怕自己离不开对方，因此在靠近和回避之间反复摇摆。&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;四象限的意义不是给人贴死标签，而是帮助识别自己在关系中的惯性反应。一个人的状态可以随着后天关系体验和自我调整而改变，方向通常是从高焦虑或高回避，逐步走向更安全的连接方式。&lt;/p&gt;
&lt;h2&gt;爱的表达&lt;/h2&gt;
&lt;h3&gt;爱要怎么说出口&lt;/h3&gt;
&lt;p&gt;爱的表达不是一次性倾倒情绪，而是循序推进的关系行为。搭讪负责打开入口，自我表露负责建立理解，表白负责确认方向。三者各有功能，不能混为一谈。&lt;/p&gt;
&lt;p&gt;表达的关键在于时机、分寸和诚意。太早容易冒进，太晚容易错过，太满容易压迫，太空又会失去真实感。真正有效的表达，是让对方感到可接近、可回应、可继续互动。&lt;/p&gt;
&lt;p&gt;搭讪不是套路秀，也不是我一定要让你立刻喜欢我。它更像关系的开门动作，目标只是让陌生人从完全陌生，转向愿意继续说话。自我表露则是关系升温的关键，信息从表层进入较深层，彼此才会开始把对方当成真实的人，而不是抽象对象。表白则是对已有互动的定向确认，它适合在一定理解基础上发生，而不是拿来替代前面的接触过程。&lt;/p&gt;
&lt;p&gt;这三步之间最常见的错误，是把表白当成起点，把表达当成终点。实际上，表白只是一个确认信号，不是关系本身。没有前面持续的互动积累，表白只会显得突兀；没有后面的继续经营，表白也不会自动变成稳定关系。&lt;/p&gt;
&lt;p&gt;更细一点看，自我表露其实还有层次。浅层表露是生活信息，中层表露是偏好和观点，深层表露才是脆弱、担心、需求和真正的在意。很多关系只能走到浅层，因为双方都担心太快暴露自己会失去优势。但如果始终停留在浅层，关系就很难真正变深。&lt;/p&gt;
&lt;p&gt;所以，表达的成熟不是一口气说完，而是知道什么可以逐步说、什么需要在稳定互动后再说。能否承受对方暂时不完全理解自己，也是表达能力的一部分。&lt;/p&gt;
&lt;h3&gt;藏不住的爱&lt;/h3&gt;
&lt;p&gt;非语言信号往往比语言更早暴露关系状态。眼神、表情、姿态、停顿、身体距离，都会传递出接近、回避、紧张或信任。一个人说得很好，但身体在后撤，关系就已经显示出紧张。&lt;/p&gt;
&lt;p&gt;非语言交流之所以重要，是因为它更接近真实情绪。若语言表示亲近、身体却持续拒斥，关系就会停留在表面。真正的亲密，往往体现为身体和语言的一致。&lt;/p&gt;
&lt;p&gt;所谓藏不住的爱，本质上就是在提醒人，非语言信号常常比措辞更诚实。眼神是否停留、说话时是否愿意靠近、身体是否朝向对方、回应时是否及时，这些都在悄悄暴露态度。很多关系里，语言还在坚持，身体已经先撤退了。&lt;/p&gt;
&lt;p&gt;非语言交流还有一个重要作用，就是调节距离感。一个让人感到舒服的人，不一定说得最多，但往往在节奏、姿态和空间感上更能让人放松。反过来，如果一个人的非语言信息总是紧张、僵硬、逃避，就算话说得再好，也很难让关系自然推进。&lt;/p&gt;
&lt;h3&gt;获取爱的3种兵器&lt;/h3&gt;
&lt;p&gt;关系中常见的三种低效策略，是用自恋吸引注意、用回避制造神秘、用情绪制造牵引力。它们能够短期获得回应，却会让关系变得消耗、摇摆和不稳定。&lt;/p&gt;
&lt;p&gt;更成熟的方式，是把情绪转化为需求，把指责转化为边界，把争夺转化为协商。关系不是争夺谁更重要，而是让双方都能更安稳地被看见。&lt;/p&gt;
&lt;p&gt;这部分可以直接理解成错误方法和建设方法的对照。错误方法往往靠刺激维持存在感，建设方法则靠清晰和回应维持信任。前者像在拉扯，后者像在搭桥。前者制造的是被动反应，后者建立的才是主动合作。&lt;/p&gt;
&lt;p&gt;如果把情感关系看成一个系统，那么自恋、回避、情绪这三种兵器，本质上都在操控注意力，却没有真正增加理解。真正有用的表达，不是让对方反复猜，而是让对方逐渐知道：我想要什么，我在意什么，我愿意怎样相处。&lt;/p&gt;
&lt;p&gt;高质量表达还有一个核心，就是把评价句改成描述句。与其说“你总是不在乎我”，不如说“你这周三次临时改计划，我会感到被放在后面”。前者容易引发防御，后者更接近协商。把争夺转化为协商，其实就是把情绪翻译成对方能接住的话。&lt;/p&gt;
&lt;p&gt;同样重要的是，表达不是只会说，更要会听。很多关系之所以卡住，不是因为没人表达，而是表达以后没人真正接住。接住不等于立刻同意，而是先确认对方的意思，再决定怎么回应。&lt;/p&gt;
&lt;h3&gt;友谊和爱情&lt;/h3&gt;
&lt;p&gt;友谊是爱情的重要基础。朋友之间的理解、信任和共享经验，为亲密关系提供了稳定底座。爱情并不是把友谊推翻，而是在友谊之上增加激情和承诺。&lt;/p&gt;
&lt;p&gt;但友谊和爱情不能简单混同。边界不清会让关系变复杂，一方若期待升级，另一方若仍停留在朋友位置，就会出现误读与失衡。成熟关系的前提，是边界可说、可见、可协商。&lt;/p&gt;
&lt;p&gt;友谊之所以重要，是因为它提供了低风险的相互了解。朋友关系允许试错、允许慢慢认识、允许在不急着定义的情况下观察对方的稳定性。这种底座一旦存在，爱情更容易建立在真实理解之上，而不是建立在幻想之上。&lt;/p&gt;
&lt;p&gt;但是，友谊转爱情也不是自然必然发生的。两个人如果长期停留在朋友框架里，就会形成固定期待，突然升级会带来认知冲击。更现实的做法，是在关系变化前先把信号说清，把期待说清，把边界说清。可见、可谈、可调整，比偷偷升级更可靠。&lt;/p&gt;
&lt;p&gt;友谊还能提供一个非常重要的校验功能：看对方在没有浪漫目的时是不是也值得相处。一个人如果只在想追求你时才表现得体，一旦回到普通关系就迅速变样，那么这种人不适合被简单理解为可靠。友谊阶段的观察，能帮助人把“会追”与“会处”区分开来。&lt;/p&gt;
&lt;p&gt;从这个角度看，朋友升恋人并不是升级而已，而是关系性质的重写。它会带来新的义务、期待和边界，原来的轻松感会被更明确的责任感替代。能不能接受这种变化，本身就是一项关系能力。&lt;/p&gt;
&lt;h2&gt;独处与孤独&lt;/h2&gt;
&lt;h3&gt;亲密与孤独&lt;/h3&gt;
&lt;p&gt;孤独并不只发生在独处时，也会发生在关系之中。即使拥有伴侣，只要长期得不到理解、回应和承接，依然会感到孤独。这样的孤独更深，因为它发生在本应最亲近的地方。&lt;/p&gt;
&lt;p&gt;亲密关系真正提供的，不是陪伴的存在感，而是情绪上的可达性。被看见、被理解、被接住，才是关系缓解孤独的关键。没有回应的相处，再近也会变远。&lt;/p&gt;
&lt;p&gt;这也是为什么孤独和亲密必须放在一起理解。很多人以为恋爱能自动消除孤独，结果发现即使在关系里，孤独仍旧存在。原因在于，孤独不是没人陪，而是没人懂。所以，独处未必痛苦，失联才更难受；单身未必孤独，关系里的失联才更容易让人感到空心。&lt;/p&gt;
&lt;p&gt;孤独还会影响择偶标准。人在孤独状态下更容易把“有人要我”当成主要目标，从而降低辨别标准。短期看，孤独会让接近变快；长期看，它会让关系质量下降。所以把孤独和恋爱动机放在一起理解，是有逻辑的：很多关系不是因为爱而开始，而是因为太孤独而匆忙开始。&lt;/p&gt;
&lt;p&gt;真正值得警惕的，不是一个人感到孤独，而是一个人习惯用关系麻醉孤独。那样的关系很容易在情绪稍微稳定后失去支撑，因为它从一开始就把对方当成止痛药，而不是一个独立的人。&lt;/p&gt;
&lt;p&gt;孤独还会改变一个人的关系感知。越孤独的人，越容易对一点点回应产生放大反应；越孤独的人，也越容易把沉默理解成拒绝，把冷淡理解成抛弃。理解这一点，有助于解释很多关系中的过度敏感。&lt;/p&gt;
&lt;h3&gt;恋爱动机&lt;/h3&gt;
&lt;p&gt;恋爱并不只是为了浪漫，它还承担发展任务。人会因为归属、身份确认、情感练习和自我成长等任务进入关系。人在不同发展阶段进入关系，往往带着不同的心理需求。&lt;/p&gt;
&lt;p&gt;更准确的理解方式，是追问这段关系满足了什么、训练了什么、推动了什么。恋爱不是一个单点事件，而是一个关系学习过程。&lt;/p&gt;
&lt;p&gt;大学阶段尤其如此。进入恋爱，有的人是为了摆脱孤独，有的人是为了确认自己被需要，有的人是为了学习亲密，有的人则是想在关系中找到未来生活的练习场。动机本身没有绝对高低，但动机若不清楚，关系很容易被误用。&lt;/p&gt;
&lt;p&gt;动机不清最常见的后果，是把关系里所有问题都解释成“我是不是不够好”，或者“对方是不是不够爱”。实际上，很多问题只是动机错位：一方想要陪伴，另一方想要成长；一方想要稳定，另一方只是想试试看。动机错位后，双方很容易都觉得自己委屈。&lt;/p&gt;
&lt;p&gt;所以，恋爱动机不是拿来评判，而是拿来对齐。把动机尽早说出来，反而更容易筛掉不合适的人。&lt;/p&gt;
&lt;p&gt;恋爱为哪般这个问题的关键，就是别把所有进入关系的冲动都当成同一种爱。有人需要陪伴，有人需要认同，有人需要成长，有人只是想逃离空窗期。动机不同，关系期待也不同，后续的失望点自然不一样。&lt;/p&gt;
&lt;h3&gt;羞怯&lt;/h3&gt;
&lt;p&gt;羞怯不是能力缺陷，而是一种在社交场景中的自我收缩。害羞的人往往过度在意评价，低估自己的表达能力，因此容易在关键时刻沉默或退缩。羞怯会影响表达，也会影响关系推进。&lt;/p&gt;
&lt;p&gt;改善羞怯的关键，不是强行装作外向，而是通过低压力场景反复练习接近、表达和反馈。把目标从“表现完美”改成“完成互动”，往往更有助于突破。&lt;/p&gt;
&lt;p&gt;羞怯的两面性很强。一方面，它让人显得谨慎、温和、可亲近；另一方面，它也会让人在关键时刻错过机会。害羞在亲缘场景里反而会被放大，其实说明羞怯本身会在他人眼中被解释成某种信号：可爱、矜持、拘谨，甚至是拒绝。可见，同样的行为，在不同语境里会产生不同后果。&lt;/p&gt;
&lt;p&gt;因此，改善羞怯不是消灭羞怯，而是让羞怯不再卡住关系的推进。先从低风险互动做起，比强迫自己一次性改变更现实。只要互动能持续，羞怯就有机会慢慢退后。&lt;/p&gt;
&lt;h3&gt;恋爱前功课&lt;/h3&gt;
&lt;p&gt;进入关系之前，先要明确自己要什么、能给什么、能承受什么。很多失败并非因为没有机会，而是因为准备不足：自我认知不清、情绪稳定不足、边界意识不足、拒绝承受力不足。&lt;/p&gt;
&lt;p&gt;恋爱前的准备，不是让自己变成完美的人，而是让自己进入关系时更清楚、更稳妥、更能承担后果。准备越充分，关系越不容易在起点就失衡。&lt;/p&gt;
&lt;p&gt;恋爱前功课的意义，在于把凭感觉前面的基础铺好。你至少要知道自己在关系里容易被什么触发、容易在哪里退缩、容易在什么地方过度投入。否则，进入关系后，一点点风吹草动都会被放大成重大危机。&lt;/p&gt;
&lt;p&gt;这类准备也包括对拒绝的承受。表白或接近并不一定成功，提前意识到这一点，能减少把失败理解成我不行的倾向。把表白与拒绝并置，实际上就在提示：关系的入口从来不是单向决定，它需要双方一起把门打开。&lt;/p&gt;
&lt;h2&gt;择偶秘籍&lt;/h2&gt;
&lt;h3&gt;脱单指导手册&lt;/h3&gt;
&lt;p&gt;脱单不是口号，也不是单纯等待。它需要接触、表达、判断和持续投入。单身本身不是问题，问题在于个体是否具备进入关系的能力，以及是否愿意为关系付出行动。&lt;/p&gt;
&lt;p&gt;真正有效的脱单，不是追求短期数量，而是提高关系入口的质量。接触机会、表达质量、边界判断、持续投入，这四项共同决定能否从单身走向稳定关系。&lt;/p&gt;
&lt;p&gt;把单身看成一个社会问题，不是为了制造压力，而是为了提醒人：长期单身有时不是外部条件太差，而是内部准备不够。有人不敢开始，有人不会表达，有人一遇到拒绝就撤退，有人接近了却不懂维持，这些都比没有合适的人更常见。&lt;/p&gt;
&lt;p&gt;这类问题还有一个特点，就是会互相强化。不会表达的人更难获得回应，越来越不敢开始；一再被拒绝的人更容易害怕主动，最后就把单身解释成命运。所谓脱单手册，实际上是在打破这种自我循环。&lt;/p&gt;
&lt;p&gt;脱单不是制造焦虑，而是让人把关系能力当回事。你不是只能等待合适的人出现，你也要让自己成为更容易被关系接住的人。&lt;/p&gt;
&lt;p&gt;所以脱单并不等于急着找对象，它更像把自己从被动状态调成主动状态。主动不是追着别人跑，而是能在合适的时候接触、在合适的时候表达、在不合适的时候止损。&lt;/p&gt;
&lt;h3&gt;择偶实用宝典&lt;/h3&gt;
&lt;p&gt;择偶标准表面上是偏好，深层其实是价值排序。有人看重外貌，有人看重稳定，有人看重成长性，但真正关键的是这些标准是否与长期生活目标一致。择偶不是挑选最耀眼的对象，而是挑选最适合共同生活的人。&lt;/p&gt;
&lt;p&gt;“足够好的伴侣”比“完美的伴侣”更真实。能否长期合作、是否具备责任感、是否可共同成长，往往比单项优势更重要。择偶若只看单一亮点，关系往往经不起现实检验。&lt;/p&gt;
&lt;p&gt;讨论理想伴侣是谁，其实是在逼人承认一个现实：择偶永远不是单项比较，而是整体比较。一个人可能很有吸引力，但不一定适合你的生活；也可能不是最耀眼，但在长期协作里非常稳。选伴侣就像选长期合作者，最重要的不是一项漂亮指标，而是综合可持续性。&lt;/p&gt;
&lt;p&gt;这里还有一个很容易被忽略的点，就是择偶标准会随着人生阶段改变。学生时期更容易看重感觉和新鲜感，进入更稳定的生活后，会越来越重视责任、情绪管理和生活协同。标准变化不是善变，而是需求变了。理解自己的阶段位置，比死守一套标准更重要。&lt;/p&gt;
&lt;p&gt;有些人会把择偶标准讲得很高，但真正在关系里又会因为孤独、焦虑或自尊脆弱而不断让步。标准和行为不一致时，最容易产生后悔。做足够好的伴侣，本质上也要先做足够清楚的选择者。&lt;/p&gt;
&lt;p&gt;理想化择偶常常会忽略几个关键问题：对方是否愿意承担责任、是否能面对冲突、是否愿意为共同生活做出调整、是否在关键时刻可靠。这些问题在热恋里不显眼，在真正生活化之后会变得很关键。&lt;/p&gt;
&lt;p&gt;自尊在择偶中起决定性作用。自尊越稳，越能避免讨好和防御；自尊越弱，越容易把对方的反应当作自我价值的裁判。低自尊会让关系中的每次波动都被放大成“我不值得”。&lt;/p&gt;
&lt;p&gt;补充性的择偶视角还指出，关系中可能存在“供养者”和“情人”的角色错位。一方在资源上被依赖，在情感上却未被真正认同，这种结构短期可维持，长期容易进入抱怨、贬低与冷却。理解这一点，有助于识别关系中的结构性失衡。&lt;/p&gt;
&lt;p&gt;自尊的问题之所以和择偶绑得这么紧，是因为很多人不是在选择伴侣，而是在借伴侣确认自己。自尊稳定的人，更容易把择偶当作匹配问题；自尊脆弱的人，则容易把择偶变成被评价、被审判、被证明的过程。后者在关系里会特别辛苦，因为关系的每次波动都像在审判自己。&lt;/p&gt;
&lt;p&gt;自尊还会影响一个人能不能接受不完美。自尊稳定的人，能够承认“这个人有优点也有缺点，但整体上适合我”；自尊脆弱的人，常常希望对方不断证明自己值得爱。这样的差异会直接影响婚恋中的压力大小。&lt;/p&gt;
&lt;p&gt;把爱和自尊联系起来，并不是附会，而是指出一个基本事实：一个人越能稳稳地站在自己这边，就越不容易在关系里把自己交出去任人裁决。&lt;/p&gt;
&lt;p&gt;供养者和情人的错位，也是很值得警惕的关系结构。一个人可能在物质上持续投入，却在情感上得不到肯定；也可能在情感上不断付出，却没有被当作平等伙伴。这样的关系短期看似正常，长期却容易失衡。&lt;/p&gt;
&lt;h2&gt;爱的B面&lt;/h2&gt;
&lt;h3&gt;嫉妒&lt;/h3&gt;
&lt;p&gt;嫉妒是边界被触动后的心理反应，它提示关系中存在不安、竞争、失衡或威胁。嫉妒本身不是问题，关键是它之后如何被处理。若转化为监控和报复，关系会被进一步破坏；若转化为需求和边界，嫉妒反而可能成为修复入口。&lt;/p&gt;
&lt;p&gt;面对嫉妒时，更有建设性的做法是识别触发点、说清需求、明确边界，而不是陷入猜测和审讯。嫉妒若被解释为“你不可信”，就会滑向控制；若被解释为“我需要更稳定的回应”，就更容易进入协商。&lt;/p&gt;
&lt;p&gt;嫉妒像一块烧红的铁，意思不是说嫉妒一定坏，而是说它会先灼伤持有者自己。嫉妒的痛苦，常常来自“别人拥有我没有的东西”这件事被放大成“我不如别人”。所以嫉妒最先伤到的往往不是对方，而是自己的自尊。&lt;/p&gt;
&lt;p&gt;嫉妒并不只会因为第三者出现。对方的时间分配、注意力分配、情绪投入、未来计划，只要和自己心里的期待不一致，都可能触发嫉妒。它本质上是在提醒人：我害怕失去独占性，或者我害怕自己不够重要。&lt;/p&gt;
&lt;p&gt;所以，处理嫉妒不能只盯着事件，还要处理背后的比较机制。否则嫉妒会一直被新的外部刺激重新点燃。&lt;/p&gt;
&lt;p&gt;嫉妒还跟关系安全程度有关。越不确定关系的人，越容易把外界刺激当成威胁；越稳定的人，越能把它视为可讨论的问题。换句话说，嫉妒的强弱，往往不仅说明关系里发生了什么，也说明内在安全感如何。&lt;/p&gt;
&lt;h3&gt;欺骗&lt;/h3&gt;
&lt;p&gt;欺骗不能只按一次谎言来判断，而要看是否形成模式。判断的重点是叙述是否一致、行为是否统一、语言和非语言是否匹配。持续的不一致，才是关系里真正值得警惕的信号。&lt;/p&gt;
&lt;p&gt;说谎常常出于回避冲突、维持形象或获得便利，但无论动机为何，长期欺骗都会侵蚀信任。信任一旦受损，恢复成本往往远高于维持成本。越是亲密的关系，越需要真实，因为隐瞒在亲密关系里通常会被放大。&lt;/p&gt;
&lt;p&gt;欺骗之所以难处理，是因为它不是单一行为，而是一个关系信号系统。一次谎言可能只是习惯性回避，持续谎言就会演变成结构性不信任。强调大学生平均每天也会说谎，并不是为了夸张，而是提醒人：说谎不是少数人的恶习，而是人人都会用的工具。区别只在于，它被用来保护关系，还是破坏关系。&lt;/p&gt;
&lt;p&gt;说谎背后还有一种常见心理，就是怕麻烦。许多人并不是想恶意伤害对方，而是觉得讲真话太麻烦、太尴尬、太容易引发冲突。问题在于，关系里的麻烦往往会被延后放大。短期为了省事，长期却付出更大的信任成本。&lt;/p&gt;
&lt;p&gt;所以，判断欺骗时不能只问“有没有说谎”，还要问“这段关系是否允许真话存在”。如果一段关系连真实表达都难，这段关系本身就已经处在不健康区间。&lt;/p&gt;
&lt;p&gt;辨别欺骗时，最重要的不是追着细节不放，而是看整体一致性。说法变了、时间线乱了、表情和内容不一致、前后解释互相打架，这些才是值得警惕的地方。单次记忆偏差和模式性撒谎之间，应该区分开来。&lt;/p&gt;
&lt;h3&gt;背叛&lt;/h3&gt;
&lt;p&gt;背叛之所以强烈，是因为它不只破坏关系，也破坏对他人的基本信赖。被背叛的人常会经历痛苦、羞耻、愤怒和自我怀疑，这些反应都很正常，因为背叛会改变一个人对关系的预期。&lt;/p&gt;
&lt;p&gt;应对背叛有两条路径：修复和退出。修复需要诚实、承担和一致行动；退出需要切断重复伤害并重建生活秩序。关键不是哪条路更体面，而是哪条路更符合现实条件。&lt;/p&gt;
&lt;p&gt;背叛的可怕，不只是事件本身，而是它让人重新理解亲密的代价。一个人如果在最信任的位置上被伤害，之后会更难相信承诺。这就是为什么背叛往往会留下长尾效应。它不是事情结束就结束，而是会持续影响后续关系中的安全感。&lt;/p&gt;
&lt;p&gt;修复背叛的时候，最怕的是只有道歉没有变化，或者只有原谅没有边界。真正的修复必须把事件说清、责任说清、后续行动说清。否则所谓原谅，往往只是把伤口压下去，过一阵子又会被重新触发。&lt;/p&gt;
&lt;p&gt;退出也不是失败。对于那些反复伤害、没有承担、没有修复意愿的关系，退出反而是保护自己。修复不应被神圣化，有些关系更适合终止。&lt;/p&gt;
&lt;p&gt;把背叛放在个体差异、两面性、应对三个层次来看，也说明背叛不是单点判断。不同的人对背叛敏感度不同，不同关系对背叛的承受力也不同。有人会把一次失误视为可修复，有人则会把它视为关系根本破裂。关键是双方能不能对破裂的程度达成现实判断。&lt;/p&gt;
&lt;h2&gt;婚姻的秘密&lt;/h2&gt;
&lt;h3&gt;婚姻中社会交换&lt;/h3&gt;
&lt;p&gt;婚姻并不只是情感，还包含交换。人会自然评估投入与回报，比较现实体验和心理预期之间的差距。比较水平决定满意度，替代选择决定安全感，这也是婚姻稳定度变化的核心逻辑。&lt;/p&gt;
&lt;p&gt;这并不意味着婚姻功利，而是说明婚姻需要现实支持。只有情感没有结构，关系容易空转；只有结构没有情感，关系容易僵化。成熟的婚姻，必须同时具备情感与协同能力。&lt;/p&gt;
&lt;p&gt;把婚姻解释成一种社会交换，并不是把关系贬低成买卖，而是承认：长期关系本来就需要评估成本和收益。这里的收益不只是钱，也包括陪伴、支持、尊重、共同成长。成本也不只是金钱，还包括时间、情绪、机会和妥协。&lt;/p&gt;
&lt;p&gt;这套理论的价值，在于它让人不再把婚姻想成纯粹浪漫，而是想成一个要持续运营的系统。系统里如果一直只有支出没有回报，满意度自然会下降；如果一直只有回报没有责任，关系也未必能持续。现实中的婚姻，恰恰是情感交换和现实交换共同发生的地方。&lt;/p&gt;
&lt;p&gt;比较水平高的人，会对婚姻有更高的期待；替代选择多的人，会更容易对现有婚姻不满。两者都不是错，但它们会改变关系体验。识别这一点，可以帮助人理解“为什么同一段婚姻，前后感受会不同”。&lt;/p&gt;
&lt;p&gt;比较水平说的是，一个人拿当下婚姻体验去对照自己心里的期待标准；替代选择说的是，如果还有别的可能，现有婚姻的吸引力就会被重新计算。理解这两个概念，可以解释为什么同样的婚姻，在不同阶段、不同环境中会有不同满意度。&lt;/p&gt;
&lt;h3&gt;不吵了，OK?&lt;/h3&gt;
&lt;p&gt;冲突并不说明关系失败，失败的是不懂如何冲突。真正危险的不是意见不同，而是轻蔑、羞辱、翻旧账、冷处理和持续否定。冲突若演变成权力斗争，关系就会进入消耗循环。&lt;/p&gt;
&lt;p&gt;更成熟的处理方式，是把争吵转化成问题处理。先说事实，再说感受，再说需求，最后才进入协商。顺序一旦错乱，冲突就容易失控。冲突不是要压下去，而是要被组织起来。&lt;/p&gt;
&lt;p&gt;冲突处理需要很实际：冲突本身很普遍，关键在于它是否被正确地处理。很多伴侣并不是没有冲突，而是不会把冲突说成问题，只会把问题说成攻击。于是原本可以谈判的事情，变成了彼此互相证明“你不行”。&lt;/p&gt;
&lt;p&gt;冲突处理得好，反而会让关系更稳。因为双方会在冲突里学会对方真正介意什么、害怕什么、在乎什么。关系不是靠完全无摩擦维持的，而是靠摩擦后还能回到合作维持的。也就是说，冲突不是关系的敌人，处理不好冲突才是。&lt;/p&gt;
&lt;p&gt;所以，吵架时最重要的不是把话说尽，而是别把对方说死。说问题，不说人格；说行为，不说本质；说现在，不无限上纲，这些都能让冲突保留修复空间。&lt;/p&gt;
&lt;p&gt;因此，冲突处理的重点不是谁赢谁输，而是双方能否继续保持合作关系。能说事实的人，比能吵赢的人更接近解决问题；能说明感受的人，比能压住情绪的人更有修复可能。&lt;/p&gt;
&lt;h3&gt;亲密敌人&lt;/h3&gt;
&lt;p&gt;危险关系常见于长期失衡：索取与给予不对等，情感支持不足，沟通系统长期停摆。若再叠加固定型思维，双方就更容易把问题归结为“你就是这样的人”，而不是互动方式出了问题。&lt;/p&gt;
&lt;p&gt;成长型思维的价值在于，它允许关系变化，也允许双方调整互动方式。不是所有关系都能修复，但能否修复，往往先取决于是否存在修复的认知框架。&lt;/p&gt;
&lt;p&gt;危险关系之所以被称为亲密敌人，就是因为外表还在亲近，内部却已经变成对抗。最危险的并不是争执，而是争执背后长期积累的失望、耗竭和不对等。“给予与索取不平衡”往往就是很多关系走向破裂的起点。&lt;/p&gt;
&lt;p&gt;危险关系里常见的一种变化，是一开始靠强烈吸引维持，后来靠惯性维持，再后来只剩下纠缠。这个过程之所以难被及时识别，是因为外表上关系还没有断，但内部已经没有真正的互惠。等到双方都觉得累了，往往已经拖了很久。&lt;/p&gt;
&lt;p&gt;成长型思维在这里尤其重要。它不保证修复成功，但至少让关系有机会从“你就是这样”回到“我们哪里出了问题”。这个回到问题本身的动作，就是修复的起点。&lt;/p&gt;
&lt;p&gt;固定型思维会让人把关系问题人格化，成长型思维则会把问题过程化。前者会说“你就是这样的人”，后者会问“我们是不是形成了不好的互动模式”。两者的差别非常大，因为前者会冻结关系，后者还保留调整空间。&lt;/p&gt;
&lt;h2&gt;性&lt;/h2&gt;
&lt;h3&gt;不可不提性&lt;/h3&gt;
&lt;p&gt;性不是关系的全部，但它与亲密、信任、安全感和身体认同密切相关。性可以加强连接，也可以暴露差异。关键不在欲望是否存在，而在于双方如何理解欲望、表达欲望和协商欲望。&lt;/p&gt;
&lt;p&gt;在亲密关系里，性更像一种关系语言。理解彼此节奏、边界和感受，往往比单纯的技巧更重要。性如果缺少尊重和协商，就容易变成误解来源。&lt;/p&gt;
&lt;p&gt;讨论性时，不能把它单独孤立出来，而要把它放进浪漫关系的结构里。意思很明确：性的体验不是脱离关系存在的，它总会受到信任程度、沟通质量、角色期待和文化观念的影响。有人把性当成证明爱，有人把性当成压力来源，有人把性当成关系协调的一部分，这些理解都会影响实际体验。&lt;/p&gt;
&lt;p&gt;性还和生命周期密切相关。不同年龄阶段、不同身体状态、不同压力水平下，人对性的需求和体验都会变化。把性理解成固定不变的冲动，本来就是一种误区。现实里更常见的，是随着关系发展、身体变化和生活压力，性逐渐需要重新协商。&lt;/p&gt;
&lt;p&gt;这也意味着，性问题里往往不是“有没有”，而是“怎么一起处理差异”。一方的节奏、另一方的期待、一方的疲惫、另一方的需要，这些都需要被看见。只有看见差异，才谈得上协调。&lt;/p&gt;
&lt;p&gt;所以，性议题里最需要被看见的，不是技术细节，而是关系协商能力。节奏不一致、期待不一致、边界不一致，都会让性变得困难。能不能尊重节奏、能不能接受差异、能不能把感受说清，常常比任何技巧都重要。&lt;/p&gt;
&lt;h3&gt;性别角色和性别认同&lt;/h3&gt;
&lt;p&gt;性别意识决定一个人如何看待自己，性别角色决定一个人如何在关系中行动。很多关系摩擦，不是因为谁对谁错，而是因为双方对“应该如何表现”有不同预期。角色预期越僵硬，关系越容易变成任务分配和身份冲突。&lt;/p&gt;
&lt;p&gt;双性气质的价值在于，它让个体在表达上更有弹性，也更容易适应关系中的不同情境。真正成熟的关系，不依赖刻板角色，而依赖灵活协商。&lt;/p&gt;
&lt;p&gt;性别教育比性教育更重要，不是把性教育降级，而是说很多性的困扰其实来自性别角色的僵化。一个人如果从小就被灌输“你应该怎样像个男人”“你应该怎样像个女人”，进入亲密关系后，就很容易把这些期待带进互动里。于是，表达、拒绝、主动、照顾都会被角色脚本绑住。&lt;/p&gt;
&lt;p&gt;性别角色一旦太僵硬，很多本来可以协商的问题就会变成身份问题。比如谁该先开口、谁该更主动、谁该更能忍、谁该负责安慰，都会被看成“应该”。而一旦进入应该模式，关系就会少了沟通，多了压迫。&lt;/p&gt;
&lt;p&gt;双性气质的意义就在这里。它不是让人失去性别感，而是让人不被单一脚本控制。能主动，也能等待；能坚定，也能柔和，这种灵活性在亲密关系里非常重要。&lt;/p&gt;
&lt;p&gt;双性气质的价值，恰恰在于让人拥有更多表达方式。能果断，也能温柔；能承担，也能求助；能主动，也能倾听。这种灵活性比单一标签更适合真实关系，因为关系本来就不是单一角色的对撞，而是多种能力的协作。&lt;/p&gt;
&lt;h3&gt;性取向和性少数&lt;/h3&gt;
&lt;p&gt;性态度受文化、教育、代际和经验影响，不存在绝对统一的标准。重要的是把尊重、同意、责任和边界作为底线，而不是把差异变成道德化评价。性少数群体议题的核心，不在标签本身，而在如何在差异中保持尊重。&lt;/p&gt;
&lt;p&gt;成熟的亲密关系不依赖同质化，而依赖对差异的处理能力。真正稳定的关系，往往不是双方完全相同，而是双方都能承认差异、处理差异并尊重差异。&lt;/p&gt;
&lt;p&gt;性态度这个问题，关键是先看见自己，再理解他人。你怎么看婚前性、婚外性，你如何理解性少数，这些问题背后都是一个底层要求：不要让未经思考的偏见替代理解。性少数不等于异常，差异也不等于问题，真正需要判断的是是否伤害他人、是否尊重边界、是否建立在同意基础上。&lt;/p&gt;
&lt;p&gt;性态度还会影响一个人面对伴侣时的安全感。若一个人把性理解得非常道德化，就容易在关系中把差异转成评判；若一个人完全没有边界意识，又容易在关系中忽略对方感受。比较成熟的态度，是既不放任，也不恐惧，而是在尊重前提下讨论差异。&lt;/p&gt;
&lt;p&gt;东方与西方、保守与开放这些对照，其实都在提醒：性态度没有单一正确答案，但有明确底线，那就是尊重、同意和责任。&lt;/p&gt;</content:encoded></item><item><title>大黑山记</title><link>https://saurlax.com/blog/mount-daheishan/</link><guid isPermaLink="true">https://saurlax.com/blog/mount-daheishan/</guid><description>![](image.jpg)

山脚的风，还带着几分料峭的寒意，像极了那些揣测不定的心思。抬头望去，大黑山的轮廓在薄雾中显得有些模糊，石阶蜿蜒向上，没入一片苍茫的林海。我深吸一口气，迈开了步子。

起...</description><pubDate>Thu, 26 Mar 2026 10:47:57 GMT</pubDate><content:encoded>&lt;p&gt;&lt;img src=&quot;image.jpg&quot; alt=&quot;&quot; /&gt;&lt;/p&gt;
&lt;p&gt;山脚的风，还带着几分料峭的寒意，像极了那些揣测不定的心思。抬头望去，大黑山的轮廓在薄雾中显得有些模糊，石阶蜿蜒向上，没入一片苍茫的林海。我深吸一口气，迈开了步子。&lt;/p&gt;
&lt;p&gt;起初的路，走得有些沉闷。石阶冰冷而坚硬，每一步都踏得小心翼翼。路旁的树木大多还光秃着枝桠，偶尔有几株倔强的杜鹃，也只是在枝头缀着几个紧闭的花苞，像一个个沉默的谜。我时常会停下脚步，怀疑自己是否走错了方向，这漫长的攀登，究竟会通向一个怎样的结果？山风穿过林间，发出呜呜的声响，像是在诉说着我的踌躇与不安。&lt;/p&gt;
&lt;p&gt;越往上走，山路愈发崎岖。怪石嶙峋，盘根错节，仿佛在刻意设置障碍。我的呼吸变得急促，额头也渗出了细密的汗珠。这多像是一场没有把握的追逐，你付出了全部的努力，却只能听到自己孤单的回响。那些主动发出的讯号，如同投入深谷的石子，听不见半点回响，让人不禁怀疑，这一切是否只是自己的一厢情愿。山间的雾气时浓时淡，眼前的路也随之忽明忽暗，我的心绪也在这希望与失望的交织中，起伏不定。&lt;/p&gt;
&lt;p&gt;就在我感到有些疲惫，几乎要放弃的时候，转过一个山坳，眼前的景象豁然开朗。一片向阳的山坡上，几株早开的杜鹃，正热烈地绽放着。那是一种近乎于燃烧的色彩，在料峭的春风中，显得格外醒目。它们没有连成一片花海，只是零星地散布着，却足以点亮整座山的沉寂。我走近其中一株，花瓣上还挂着晶莹的露珠，在微弱的阳光下闪烁着光芒，像是一颗终于被捂热的心。&lt;/p&gt;
&lt;p&gt;那一刻，所有的疲惫与迷茫都烟消云散。我忽然明白，攀登的意义，或许并不在于征服这座山，而在于这个过程本身。那些崎岖与寒冷，那些等待与期盼，都是为了这一刻的相遇。山风依旧清冷，但吹在脸上，却带着一丝不易察觉的温柔。我站在山坡上，望着远方，心中充满了前所未有的笃定与喜悦。春天，终究是来了。&lt;/p&gt;</content:encoded></item><item><title>在 Nuxt 中使用 AI 编程</title><link>https://saurlax.com/blog/nuxt-ai-coding/</link><guid isPermaLink="true">https://saurlax.com/blog/nuxt-ai-coding/</guid><description>[Nuxt](https://nuxt.com/) 是一个基于 Vue.js 的全栈框架，可以帮助我们快速构建现代化的 Web 应用。它通过 Modules 提供了丰富的功能扩展，让我们能够非常方便地...</description><pubDate>Wed, 25 Mar 2026 12:07:00 GMT</pubDate><content:encoded>&lt;p&gt;&lt;a href=&quot;https://nuxt.com/&quot;&gt;Nuxt&lt;/a&gt; 是一个基于 Vue.js 的全栈框架，可以帮助我们快速构建现代化的 Web 应用。它通过 Modules 提供了丰富的功能扩展，让我们能够非常方便地集成开箱即用的数据库、认证、API 等功能。这实际上有点类似 Java 企业级开发中常见的 Spring Boot，都是通过模块化的方式来扩展能力。但使用 Nuxt 显然是一个更轻量、更现代的方案。&lt;/p&gt;
&lt;p&gt;相比 Next.js 等其他框架，Nuxt 对 TypeScript 的强力支持和开箱即用的体验是一个非常大的优势。Nuxt 通过 &lt;a href=&quot;https://nitro.build/&quot;&gt;Nitro&lt;/a&gt; 提供的 InternalApi 类型，可以自动生成后端 API 的类型定义，然后在前端通过 &lt;code&gt;$fetch&lt;/code&gt; 和 &lt;code&gt;useFetch&lt;/code&gt; 直接获取对应类型。这样一来，后端的任何修改都能立刻反馈到前端的类型检查和报错中。&lt;/p&gt;
&lt;h2&gt;Nuxt Modules&lt;/h2&gt;
&lt;p&gt;我写项目通常会使用以下几个模块：&lt;/p&gt;
&lt;h3&gt;&lt;a href=&quot;https://hub.nuxt.com/&quot;&gt;NuxtHub&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;这是其中最重要的模块之一。NuxtHub 提供了开箱即用的数据库、KV、Storage、Cache 集成方案。只需要简单地在 &lt;code&gt;nuxt.config.ts&lt;/code&gt; 中配置&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-ts&quot;&gt;export default defineNuxtConfig({
  hub: {
    db: &amp;quot;postgresql&amp;quot;,
    blob: true,
    // ...
  },
});
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;就可以非常快地连接到数据库、对象存储等服务。对于数据库来说，NuxtHub 内置了对 PostgreSQL、MySQL、SQLite 等数据库的支持，并且提供了统一的 API 来进行数据操作，非常方便。此外，这些配置都通过环境变量管理。&lt;/p&gt;
&lt;p&gt;以 PostgreSQL 为例，你只需要设置 &lt;code&gt;DATABASE_URL&lt;/code&gt; 环境变量，NuxtHub 就会自动连接到数据库，并提供一个 &lt;code&gt;db&lt;/code&gt; 对象来进行数据操作。此外，NuxtHub 默认集成的是 &lt;a href=&quot;https://orm.drizzle.team/&quot;&gt;Drizzle ORM&lt;/a&gt;，这是一个完全基于 TypeScript 的 ORM，提供了极强的类型安全和开发体验。通过 Drizzle ORM，你可以非常方便地定义数据模型、进行数据库迁移以及执行查询操作，并且所有操作都会有完整的类型提示，极大地提升开发效率和代码质量。NuxtHub 已经默认帮你配置好了 Drizzle ORM，你只需要直接在 &lt;code&gt;server/db/schema.ts&lt;/code&gt; 中定义数据模型：&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-ts&quot;&gt;import { pgTable, serial, text } from &amp;quot;drizzle-orm/pg-core&amp;quot;;

export const user = pgTable(&amp;quot;user&amp;quot;, {
  id: serial(&amp;quot;id&amp;quot;).primaryKey(),
  name: text(&amp;quot;name&amp;quot;).notNull(),
});
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;然后运行 &lt;code&gt;npx nuxt db generate&lt;/code&gt; 就可以自动生成数据库迁移文件了，非常方便。&lt;/p&gt;
&lt;p&gt;为什么说 NuxtHub + Drizzle ORM 非常适合 AI 编程？在前文 &lt;a href=&quot;/blog/harness-engineering&quot;&gt;Harness Engineering&lt;/a&gt; 里我们提到，对于现在的大模型来说，模型能力已经很强，真正稀缺的是一个能让 AI 发挥能力的工程环境。使用 Drizzle ORM 时，类型会严格按照“数据库 → 后端 → 前端”的顺序流动。换句话说，我们真正实现了全链路的类型安全：从数据库 schema 定义开始，类型信息会自动流向后端 API 和前端调用，从而最大程度利用 TypeScript 的类型系统来提升开发效率和代码质量。这样一来，AI 在编写代码时，也能直接利用这些类型信息进行补全、推断和错误检查。&lt;/p&gt;
&lt;p&gt;另外还想提一嘴，NuxtHub 在 build 阶段会尝试自动迁移数据库，所以通常不需要额外的迁移步骤。代码推送后，它会自动帮我们执行迁移。&lt;/p&gt;
&lt;p&gt;还有就是，Drizzle ORM 的 &lt;a href=&quot;https://orm.drizzle.team/docs/rqb-v2&quot;&gt;Query API&lt;/a&gt; 非常好用，在 schema 中定义好关系后可以直接通过链式调用来进行复杂的查询操作。&lt;/p&gt;
&lt;h3&gt;&lt;a href=&quot;https://github.com/Atinux/nuxt-auth-utils&quot;&gt;nuxt-auth-utils&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;这是一个简单的认证模块，提供了开箱即用的认证方案。它支持多种认证方式，包括基于 JWT 的认证、基于 Session 的认证等，并且提供了统一的 API 来进行认证操作，非常方便。通过这个模块，我们可以快速实现用户注册、登录、权限管理等功能。&lt;/p&gt;
&lt;p&gt;亮点是你可以编写一个 &lt;code&gt;shared/types/auth.d.ts&lt;/code&gt; 文件：&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-ts&quot;&gt;declare module &amp;quot;#auth-utils&amp;quot; {
  interface User {
    // Add your own fields
  }

  interface UserSession {
    // Add your own fields
  }

  interface SecureSessionData {
    // Add your own fields
  }
}

export {};
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;这样当你在前端读取用户数据 &lt;code&gt;const { user } = useUserSession()&lt;/code&gt; 时，你可以直接获得对应存储在 JWT 中的具体数据类型。&lt;/p&gt;
&lt;h3&gt;&lt;a href=&quot;https://i18n.nuxtjs.org/&quot;&gt;@nuxtjs/i18n&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;这是一个 i18n 国际化框架，提供了开箱即用的国际化方案。它支持多种语言翻译，并且提供了统一的 API 来进行国际化操作。读者可能会问，我们做的大部分项目可能都是单语言的，用这个模块有什么用呢？&lt;/p&gt;
&lt;p&gt;这里其实有一个非常有意思的用法。前面提到 NuxtHub + Drizzle ORM 的时候，我们不是说要让类型定义只存在一处，然后流动起来吗？对于一些预定义的键值，特别是数据库 enum，这个思路非常有用。以下面的数据库 schema 为例：&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-ts&quot;&gt;import { pgEnum, pgTable, serial } from &amp;quot;drizzle-orm/pg-core&amp;quot;;

export const userRole = pgEnum(&amp;quot;user_role&amp;quot;, [&amp;quot;admin&amp;quot;, &amp;quot;editor&amp;quot;, &amp;quot;viewer&amp;quot;]);
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;然后我们可以先创建一个 &lt;code&gt;shared/types/db.ts&lt;/code&gt; 导出数据库类型给前端：&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-ts&quot;&gt;import { userRole } from &amp;quot;@nuxthub/db/schema&amp;quot;;
// 注意这里一定要从 &amp;quot;@nuxthub/db/schema&amp;quot; 里导入，直接导入 &amp;quot;@nuxthub/db&amp;quot; 会导致前端把整个 Drizzle ORM 都打包进来

export const userRoleEnum = userRole.enumValues;

export type UserRole = (typeof userRoleEnum)[number];
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;之后创建 &lt;code&gt;i18n/locales/en.json&lt;/code&gt; 来定义前端的显示文本：&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-json&quot;&gt;{
  &amp;quot;userRole&amp;quot;: {
    &amp;quot;admin&amp;quot;: &amp;quot;Administrator&amp;quot;,
    &amp;quot;editor&amp;quot;: &amp;quot;Editor&amp;quot;,
    &amp;quot;viewer&amp;quot;: &amp;quot;Viewer&amp;quot;
  }
}
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;这样我们就可以在前端的选择器和展示位置直接使用这个 &lt;code&gt;UserRole&lt;/code&gt; 类型：&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-vue&quot;&gt;&amp;lt;script setup lang=&amp;quot;ts&amp;quot;&amp;gt;
const items = userRoleEnum.map((value) =&amp;gt; ({
  value,
  label: t(`userRole.${value}`),
}));
&amp;lt;/script&amp;gt;

&amp;lt;template&amp;gt;
  &amp;lt;Select :items=&amp;quot;items&amp;quot; /&amp;gt;
&amp;lt;/template&amp;gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;通过这种方式，我们就实现了数据库 enum 类型只定义一次，再通过类型流动和国际化文本在前端复用。&lt;/p&gt;
&lt;h3&gt;&lt;a href=&quot;https://ui.nuxt.com/&quot;&gt;@nuxt/ui&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;Nuxt 官方的 UI 组件库，提供了开箱即用的 UI 组件。它基于 Tailwind CSS 构建，包含丰富的组件和工具类，可以帮助我们快速构建美观的用户界面。&lt;/p&gt;
&lt;p&gt;笔者本人其实不太喜欢 shadcn/ui 那种完全无样式的组件库。它虽然提供了极大的灵活性，但也需要自己编写大量样式代码才能实现完整界面。而 @nuxt/ui 提供了一个很好的平衡点：既有开箱即用的美观组件，也允许我们通过 Tailwind CSS 进行高度定制，非常适合在 AI 编程场景里快速构建界面。&lt;/p&gt;
&lt;h3&gt;其他&lt;/h3&gt;
&lt;p&gt;除此以外，还有一些在 Vue 生态里本来就很流行的模块，不过我暂时没有探索出特别有意思的用法，这里就不展开了，比如：&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href=&quot;https://vueuse.org/&quot;&gt;@vueuse/nuxt&lt;/a&gt;：提供了大量的实用工具函数和组合式 API，可以帮助我们更方便地进行状态管理、事件监听、动画等操作。&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://image.nuxt.com/&quot;&gt;@nuxt/image&lt;/a&gt;：提供了开箱即用的图片优化方案，支持多种图片格式和优化策略，可以帮助我们快速实现高性能的图片加载。&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://gugustinette.github.io/nuxt-maplibre/&quot;&gt;nuxt-maplibre&lt;/a&gt;：MapLibre 本身是一个非常不错的开源地图解决方案，nuxt-maplibre 则是它在 Nuxt 中的集成模块，提供了开箱即用的地图组件和工具。&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://content.nuxt.com/&quot;&gt;NuxtContent&lt;/a&gt;：主要是用来做博客的，提供了基于 Markdown 的内容管理方案。也可以写自己的数据源来从文本或代码中直接生成奇妙的页面（还在探索中）。&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://github.com/nuxt-content/mdc&quot;&gt;@nuxtjs/mdc&lt;/a&gt;：渲染 Markdown 内容的模块。mdc 的 c 是 Component 的意思，可以直接在 Markdown 中调用 Vue 组件，类似 mdx 的用法，非常适合我们在写一些文档或者博客的时候直接使用 Vue 组件来展示一些动态的内容。&lt;/li&gt;
&lt;/ul&gt;
&lt;h2&gt;Nitro&lt;/h2&gt;
&lt;p&gt;Nitro 本身也有一些比较有意思的功能。比如需要计划任务时，可以直接使用 &lt;a href=&quot;https://nitro.build/docs/tasks#scheduled-tasks&quot;&gt;Scheduled Tasks&lt;/a&gt; 来实现，不需要额外引入第三方任务调度库。&lt;/p&gt;
&lt;p&gt;此外还有一个我以前研究的 &lt;a href=&quot;/blog/nuxt-memory-based-message-broadcast&quot;&gt;基于内存的消息广播&lt;/a&gt; 功能。不过这个方案其实挺鸡肋，因为它只能在单实例中使用；如果要在分布式环境中使用，还是需要引入第三方消息队列或者 pub/sub 方案。NuxtHub 目前的 KV 只能存储，不能监听变化，也没有 Queue 功能，这点还是挺可惜的。&lt;/p&gt;
&lt;h2&gt;AGENTS.md&lt;/h2&gt;
&lt;p&gt;虽说这篇博客的名字是《在 Nuxt 中使用 AI 编程》，但其实我并没有在这篇博客里介绍太多关于 AI 编程的内容。因为我觉得 AI 编程的核心其实是一个工程环境，而不是 AI 模型本身。我们需要一个能够让 AI 发挥能力的工程环境，这个环境需要具备良好的开发体验、强大的功能扩展能力和高效的类型安全机制。而 Nuxt + NuxtHub + Drizzle ORM 就是这样一个非常适合 AI 编程的工程环境。最后，我们需要一个 AGENTS.md 来告诉 AI 如何在这个环境里编程，如何使用这些工具和模块来实现各种功能。以下是一些我觉得比较重要的点：&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-md&quot;&gt;This project is built with nuxt4 + nuxthub + @nuxt/ui + @nuxtjs/i18n + nuxt-auth-utils, please refer to Context7 for their documentation.

- Most of the files from `vue`, `shared`, `utils`, `types` and nuxt modules are auto-imported by nuxt, you can directly use them in your code. DO NOT import them by yourself.

## Frontend

- Prioritize using components from Nuxt UI, and avoid writing too much tailwindcss yourself if not necessary.
- For error messages in Toast, add `e.data?.message || e.message` in the description to display the specific error information.

## Backend

- Authentication should use `await getUserSession(event)` and `await requireUserSession(event)` from nuxt-auth-utils.
- The backend API should be as simple as possible. Do not arbitrarily customize the Type. If there is no need, do not perform too much validation. Please use Zod to validate the request body. Don&apos;t define the zod schema, directly use `z.object({ ... }).parse(body)` to validate the request body.
- Database operations use the Drizzle ORM provided by NuxtHub. The database schema is defined in `server/db/schema.ts`. Use `import { db, schema } from &amp;quot;@nuxthub/db&amp;quot;` to access it.
- The backend directly returns the result returned by the database. For example, `return db.select().from(users).where(eq(users.id, userId))`.
- When there are multiple sub-paths under a certain path, directories should be created first, and then the `index.xxx.ts` should be used.
&lt;/code&gt;&lt;/pre&gt;</content:encoded></item><item><title>使用 Husky 进行 Git 提交规范检查</title><link>https://saurlax.com/blog/husky/</link><guid isPermaLink="true">https://saurlax.com/blog/husky/</guid><description>Husky 是一个 Git hooks 工具，可以帮助我们在提交代码之前运行一些脚本来检查代码质量、规范提交信息等。

```bash
# use npm
npm install --save-dev...</description><pubDate>Wed, 25 Mar 2026 10:03:50 GMT</pubDate><content:encoded>&lt;p&gt;Husky 是一个 Git hooks 工具，可以帮助我们在提交代码之前运行一些脚本来检查代码质量、规范提交信息等。&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-bash&quot;&gt;# use npm
npm install --save-dev husky
npx husky install

# use pnpm
pnpm add --save-dev husky
pnpm exec husky init
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;默认情况下 &lt;code&gt;.husky/pre-commit&lt;/code&gt; 里会自带一行 &lt;code&gt;npm test&lt;/code&gt; 或者 &lt;code&gt;pnpm test&lt;/code&gt;，如果你不需要在提交前运行测试，可以删除这行。&lt;/p&gt;
&lt;p&gt;可用的钩子列表可用参考 &lt;a href=&quot;https://git-scm.com/docs/githooks&quot;&gt;Git 文档&lt;/a&gt;，我们常用的主要有：&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;code&gt;pre-commit&lt;/code&gt;：在提交之前运行，可以用来检查代码质量、运行测试等。&lt;/li&gt;
&lt;li&gt;&lt;code&gt;commit-msg&lt;/code&gt;：在提交信息编辑完成后运行，可以用来检查提交信息的规范性。&lt;/li&gt;
&lt;/ul&gt;
&lt;h2&gt;commitlint&lt;/h2&gt;
&lt;p&gt;如果需要对提交信息进行规范检查，可以安装 &lt;code&gt;commitlint&lt;/code&gt;：&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-bash&quot;&gt;# use npm
npm install -D @commitlint/cli @commitlint/config-conventional
echo &amp;quot;export default { extends: [&apos;@commitlint/config-conventional&apos;] };&amp;quot; &amp;gt; commitlint.config.js
echo &amp;quot;npx --no -- commitlint --edit \$1&amp;quot; &amp;gt; .husky/commit-msg

# use pnpm
pnpm add -D @commitlint/cli @commitlint/config-conventional
echo &amp;quot;export default { extends: [&apos;@commitlint/config-conventional&apos;] };&amp;quot; &amp;gt; commitlint.config.js
echo &amp;quot;pnpm dlx commitlint --edit \$1&amp;quot; &amp;gt; .husky/commit-msg
&lt;/code&gt;&lt;/pre&gt;
&lt;h2&gt;常见问题&lt;/h2&gt;
&lt;h3&gt;Please add rules to your &lt;code&gt;commitlint.config.js&lt;/code&gt;&lt;/h3&gt;
&lt;p&gt;如果明明已经添加了 &lt;code&gt;commitlint.config.js&lt;/code&gt;，但在提交时仍然提示 &lt;code&gt;Please add rules to your commitlint.config.js&lt;/code&gt;，可能是因为 &lt;code&gt;commitlint&lt;/code&gt; 没有正确加载配置文件，通常是模块解析的问题。可以尝试以下解决方案：&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;将 &lt;code&gt;commitlint.config.js&lt;/code&gt; 的内容改为 CommonJS 模块格式：
&lt;pre&gt;&lt;code class=&quot;language-js&quot;&gt;module.exports = { extends: [&amp;quot;@commitlint/config-conventional&amp;quot;] };
&lt;/code&gt;&lt;/pre&gt;&lt;/li&gt;
&lt;li&gt;确保 &lt;code&gt;package.json&lt;/code&gt; 中设置了 &lt;code&gt;&amp;quot;type&amp;quot;: &amp;quot;module&amp;quot;&lt;/code&gt;。&lt;/li&gt;
&lt;/ul&gt;
&lt;h2&gt;参考文档&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;https://typicode.github.io/husky/&lt;/li&gt;
&lt;li&gt;https://commitlint.js.org/guides/getting-started.html&lt;/li&gt;
&lt;/ul&gt;</content:encoded></item><item><title>Harness Engineering</title><link>https://saurlax.com/blog/harness-engineering/</link><guid isPermaLink="true">https://saurlax.com/blog/harness-engineering/</guid><description>2026 年 2 月，OpenAI 的工程博客发布了一篇题为 [Harness Engineering](https://openai.com/index/harness-engineering/) ...</description><pubDate>Wed, 11 Mar 2026 14:45:58 GMT</pubDate><content:encoded>&lt;p&gt;2026 年 2 月，OpenAI 的工程博客发布了一篇题为 &lt;a href=&quot;https://openai.com/index/harness-engineering/&quot;&gt;Harness Engineering&lt;/a&gt; 的文章，描述了他们用三名工程师在五个月内交付一百万行代码的经历——其中没有一行是人工编写的。这篇文章给出了一个概念：在 Agent 优先的开发模式下，工程师的职责不再是写代码，而是建设让 Agent 能够有效工作的环境，也就是 &lt;strong&gt;Harness&lt;/strong&gt;。&lt;/p&gt;
&lt;p&gt;“Harness”原本是“挽具”的意思，用来控制和引导力量的方向。放在软件工程语境里，它指的是支撑 Agent 工作的完整基础设施——包括文档、约束、观测工具、反馈回路，以及把所有这些组织起来的方法。Harness Engineering 就是专门设计、构建和维护这套基础设施的工程方法。&lt;/p&gt;
&lt;h2&gt;为什么需要&lt;/h2&gt;
&lt;p&gt;Agent 失败的原因，通常不是模型能力不够，而是环境规范不够明确。&lt;/p&gt;
&lt;p&gt;拿 Codex 的实践举例：早期进展比预期慢，原因不是模型出了问题，而是“环境的规范不够明确”。Agent 缺乏完成高级目标所需的工具、抽象层和内部结构，所以无法取得进展。碰到问题时，解决方案不该是“再提示一次”，而是追问——究竟还差什么，怎么让这个能力对 Agent 来说既清晰又可强制执行。&lt;/p&gt;
&lt;p&gt;换句话说，Agent 在运行时只能访问它上下文里已有的内容。存在 Slack 记录里的决策、存在人脑里的经验，对 Agent 来说一律不存在。从这个角度看，一个没有 Harness 的 Agent 系统，就像一个刚入职的工程师，既没有代码规范，也没有文档，甚至连项目结构都没人介绍——能产出什么，完全靠运气。&lt;/p&gt;
&lt;p&gt;更深层的问题是，随着 Agent 大量生成代码，代码库会出现漂移。Agent 会复制已有的模式，包括那些不理想的模式，错误会以指数速度扩散。没有持续的清理机制，技术债务会快速积累到不可控的程度。&lt;/p&gt;
&lt;h2&gt;核心构成&lt;/h2&gt;
&lt;h3&gt;代码仓库即记录系统&lt;/h3&gt;
&lt;p&gt;Harness Engineering 的核心信念之一：&lt;strong&gt;Agent 的知识边界就是代码仓库的边界&lt;/strong&gt;。&lt;/p&gt;
&lt;p&gt;OpenAI 的实践是把代码库当作“记录系统”（system of record）来运营。所有设计决策、架构约定、执行计划、质量标准，都以 Markdown 的形式版本化入库。一份典型的知识库结构大致是：&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-text&quot;&gt;docs/
├── design-docs/       # 设计文档（含验证状态）
├── exec-plans/        # 执行计划（活跃/已完成/技术债）
├── product-specs/     # 产品规格
├── references/        # 外部参考（llms.txt 格式）
├── generated/         # 自动生成内容
├── DESIGN.md
├── ARCHITECTURE.md
├── SECURITY.md
└── QUALITY_SCORE.md
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;重要的是&lt;strong&gt;渐进式披露&lt;/strong&gt;原则：Agent 从一个小而稳定的切入点开始，被引导去下一步查什么，而不是一开始就被淹没。&lt;code&gt;AGENTS.md&lt;/code&gt; 扮演的是目录角色，大约 100 行，告诉 Agent 去哪里找更深层的信息，而不是把所有规则都塞在里面变成百科全书。&lt;/p&gt;
&lt;p&gt;一个巨大的 &lt;code&gt;AGENTS.md&lt;/code&gt; 会失效，原因是多方面的：上下文是稀缺资源，一份庞杂的指令文件会挤掉任务本身；当一切都“重要”，Agent 最终靠本地模式匹配而不是主动导航；这份文件也会快速腐烂，陈旧规则变成干扰，且几乎无法做覆盖率和新鲜度检验。&lt;/p&gt;
&lt;h3&gt;Agent 可读性&lt;/h3&gt;
&lt;p&gt;与代码对人类工程师的可读性类似，Harness Engineering 强调代码库对 Agent 的可读性，简称 &lt;strong&gt;Agent Readability&lt;/strong&gt;。&lt;/p&gt;
&lt;p&gt;这要求工程师主动把知识“外化”到代码库里——那次在 Slack 上讨论并达成共识的架构决策，如果不写进 &lt;code&gt;docs/&lt;/code&gt;，对 Agent 来说就不存在。产品原则、工程规范、甚至团队的表情符号偏好，都可以是上下文的一部分。&lt;/p&gt;
&lt;p&gt;为了让应用程序本身也对 Agent 可读，OpenAI 做了以下几件事：&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;允许应用根据 &lt;code&gt;git worktree&lt;/code&gt; 分支启动独立实例，Codex 可以为每个任务启动并驱动一个应用实例；&lt;/li&gt;
&lt;li&gt;把 Chrome DevTools 协议接入 Agent 运行时，让 Agent 能直接进行 DOM 快照、截图和导航；&lt;/li&gt;
&lt;li&gt;为每个工作树提供临时可观测性堆栈（Victoria Logs、Metrics、Traces），Agent 可以用 LogQL 查日志、用 PromQL 查指标、用 TraceQL 查链路追踪。&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;这样，“确保服务启动在 800ms 以内”或“这四个关键用户路径的任何 Span 不得超过两秒”这类提示才变得可操作。&lt;/p&gt;
&lt;h3&gt;架构约束&lt;/h3&gt;
&lt;p&gt;Agent 在有严格边界和可预测结构的环境中工作最高效。Harness Engineering 把架构约束视为早期先决条件，而不是等到团队扩大才引入。&lt;/p&gt;
&lt;p&gt;一个典型的约束体系包含两层：&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;分层架构&lt;/strong&gt;：每个业务域内，代码只能沿固定方向依赖（如 Types → Config → Repo → Service → Runtime → UI）。横切关注点（认证、遥测、功能标志）通过单一显式接口 Providers 进入，其他任何路径都不允许。&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;机械强制执行&lt;/strong&gt;：这些规则不靠人工 Review，而通过自定义 Linter 和结构测试强制执行。Lint 的错误信息会在 Agent 上下文中注入修复指令，这样 Agent 不只是知道出了问题，还能直接得到怎么修的提示。&lt;/p&gt;
&lt;p&gt;除了硬约束，还有一组“品味不变式”（taste invariants）作为更主观的机械规则：结构化日志、命名约定、文件大小限制、特定平台的可靠性要求。此类规则一旦编码，就会立即应用到所有地方——在 Agent 驱动的代码库里，它们是倍增器，而不是束缚。&lt;/p&gt;
&lt;h3&gt;熵管理&lt;/h3&gt;
&lt;p&gt;完全由 Agent 生成的代码库会不断产生熵。Agent 会放大已有模式，包括不良模式，漂移是不可避免的。&lt;/p&gt;
&lt;p&gt;早期，OpenAI 团队每周五花 20% 的时间手动清理“AI 残渣”，这不具备可扩展性。解决方案是把清理过程自动化——制定一组&lt;strong&gt;黄金原则&lt;/strong&gt;（golden principles），把主观的代码库品味规则编码成机械可检查的规则，再跑周期性的后台 Agent 任务：&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;扫描代码漂移，识别不再符合约束的模式；&lt;/li&gt;
&lt;li&gt;更新质量评分；&lt;/li&gt;
&lt;li&gt;发起针对性重构的 Pull Request，大多数可以在一分钟内完成审查并自动合并。&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;还有专门的 &lt;code&gt;doc-gardening&lt;/code&gt; Agent 定期扫描那些不再反映真实代码行为的文档，自动发起修复 PR，阻止文档腐烂。&lt;/p&gt;
&lt;p&gt;这套机制的本质类似垃圾回收：持续以小代价还技术债，而不是让债务累积到必须大规模人工介入。&lt;/p&gt;
&lt;h2&gt;如何实践&lt;/h2&gt;
&lt;h3&gt;知识入库&lt;/h3&gt;
&lt;p&gt;把所有对 Agent 有价值的知识转化为代码库内可版本化的工件：&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;把架构决策写成设计文档（ADR），而不是只留在会议记录里；&lt;/li&gt;
&lt;li&gt;外部库的文档用 &lt;code&gt;llms.txt&lt;/code&gt; 格式存入 &lt;code&gt;docs/references/&lt;/code&gt;，这样 Agent 不依赖外部检索也能获取；&lt;/li&gt;
&lt;li&gt;任务计划以执行计划（exec plans）的形式提交入库，进度和决策日志也随之版本化，Agent 可以在不依赖外部上下文的情况下独立运转。&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;专职 Linter 和 CI 作业验证知识库的更新状况、是否有交叉链接且结构正确，防止文档与代码渐渐脱节。&lt;/p&gt;
&lt;h3&gt;构建反馈回路&lt;/h3&gt;
&lt;p&gt;判断 Agent 做得好不好，不能只看 Pull Request 数量，还要看应用是否实际正常运行。有效的反馈回路：&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;让 Agent 能直接复现 Bug、验证修复，并录制演示视频；&lt;/li&gt;
&lt;li&gt;让 CI 失败、Lint 错误、测试失败对 Agent 可读，不只是报错码，还要附带如何修复；&lt;/li&gt;
&lt;li&gt;用 Agent 对 Agent 的方式做代码审查，减少对人工注意力的依赖。&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;当 Agent 卡住时，把原因视为信号：缺少什么工具、缺少哪些约束、还有什么知识没入库，然后由 Agent 自己完成修复。&lt;/p&gt;
&lt;h3&gt;吞吐量对合并策略的影响&lt;/h3&gt;
&lt;p&gt;在传统低吞吐量环境中，PR 阻塞是常态，代价是可以接受的等待。在 Agent 驱动的高吞吐量环境下，这套逻辑会反转：纠错成本低，等待成本高。偶发测试失败通过后续重跑解决，而不是无限期阻塞进度。&lt;/p&gt;
&lt;p&gt;这不是降低质量标准，而是基于经济学做出的权衡——在一个 Agent 每天能产出数十个 PR 的系统里，人工把守每个门控会成为最大的瓶颈。&lt;/p&gt;
&lt;h3&gt;Codex 与 App Server&lt;/h3&gt;
&lt;p&gt;OpenAI 在实践中使用 &lt;a href=&quot;https://github.com/openai/codex&quot;&gt;Codex&lt;/a&gt; 作为核心 Agent，底层由 &lt;strong&gt;Codex App Server&lt;/strong&gt; 驱动。App Server 是 Codex 的运行框架，通过 JSON-RPC over stdio 向不同客户端（CLI、VS Code、浏览器端）暴露同一套 Agent 循环能力。&lt;/p&gt;
&lt;p&gt;App Server 中有三个核心对话原语：&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;Item&lt;/strong&gt;：输入/输出的基本单位，每个 Item 都有 &lt;code&gt;started&lt;/code&gt; → &lt;code&gt;delta&lt;/code&gt; → &lt;code&gt;completed&lt;/code&gt; 的生命周期；&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Turn&lt;/strong&gt;：由用户输入发起的 Agent 工作单位，包含若干 Item；&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Thread&lt;/strong&gt;：用户与 Agent 的持久会话容器，可创建、恢复、派生和归档。&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;对于想要把 Agent 集成进自己工具链的团队，Codex 提供了几个入口：&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;code&gt;codex app-server&lt;/code&gt;：完整运行框架，支持富事件流，适合构建完整 IDE 集成；&lt;/li&gt;
&lt;li&gt;&lt;code&gt;codex mcp-server&lt;/code&gt;：作为 MCP Server 对外暴露，适合现有 MCP 工作流；&lt;/li&gt;
&lt;li&gt;&lt;code&gt;codex exec&lt;/code&gt;：轻量 CLI 模式，适合 CI 场景的一次性执行；&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://developers.openai.com/codex/sdk/&quot;&gt;Codex SDK&lt;/a&gt;：TypeScript 库，适合在服务端应用中以编程方式控制 Agent。&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;整体而言，Harness Engineering 是 Agent 优先开发模式下工程师角色的重新定义。不再是“写代码”，而是“建结构”——让 Agent 能可靠地在这个结构里工作，把人类的判断力以可执行的约束形式持续注入系统。&lt;/p&gt;</content:encoded></item><item><title>Agent Skills</title><link>https://saurlax.com/blog/agent-skills/</link><guid isPermaLink="true">https://saurlax.com/blog/agent-skills/</guid><description>[Agent Skills](https://agentskills.io) 可以理解成“给 Agent 安装可复用能力包”的机制。它把一类任务的方法沉淀成目录，里面放规则、脚本和参考资料，让 Age...</description><pubDate>Fri, 06 Mar 2026 12:29:55 GMT</pubDate><content:encoded>&lt;p&gt;&lt;a href=&quot;https://agentskills.io&quot;&gt;Agent Skills&lt;/a&gt; 可以理解成“给 Agent 安装可复用能力包”的机制。它把一类任务的方法沉淀成目录，里面放规则、脚本和参考资料，让 Agent 在遇到相似任务时不必每次都从零推理。&lt;/p&gt;
&lt;p&gt;这套格式最早由 &lt;a href=&quot;https://www.anthropic.com/news/skills&quot;&gt;Anthropic&lt;/a&gt; 对外发布并产品化，随后以开放标准形式推进，GitHub Copilot、Codex 等生态也逐步兼容同类结构。&lt;/p&gt;
&lt;h2&gt;组成结构&lt;/h2&gt;
&lt;p&gt;最小可用单元是 &lt;code&gt;SKILL.md&lt;/code&gt;，其余目录按需添加：&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-text&quot;&gt;my-skill/
├─ SKILL.md
├─ scripts/
├─ references/
├─ assets/
└─ agents/
   └─ openai.yaml
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;在 &lt;a href=&quot;https://developers.openai.com/codex/skills&quot;&gt;OpenAI Codex Skills 文档&lt;/a&gt; 和 &lt;a href=&quot;https://agentskills.io/specification&quot;&gt;Agent Skills 规范&lt;/a&gt; 里，这几个点很关键：&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;code&gt;SKILL.md&lt;/code&gt; 至少要有 &lt;code&gt;name&lt;/code&gt; 和 &lt;code&gt;description&lt;/code&gt;；&lt;/li&gt;
&lt;li&gt;&lt;code&gt;description&lt;/code&gt; 是触发边界，不是装饰文本；&lt;/li&gt;
&lt;li&gt;触发后再加载完整说明，资源文件按需读取；&lt;/li&gt;
&lt;li&gt;结构拆分越清楚，技能越容易跨工具迁移。&lt;/li&gt;
&lt;/ul&gt;
&lt;h2&gt;Skills 与 Context7&lt;/h2&gt;
&lt;p&gt;&lt;a href=&quot;https://context7.com/context7&quot;&gt;Context7&lt;/a&gt; 是以 MCP Server 提供“最新文档检索与注入”。它解决的是“查什么文档”，Agent Skills 解决的是“按什么流程做事”。&lt;/p&gt;
&lt;p&gt;放在同一个项目里，职责差异通常是：&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Skills：流程、规则、脚本、组织经验；&lt;/li&gt;
&lt;li&gt;Context7：外部库文档的实时检索与补全；&lt;/li&gt;
&lt;li&gt;MCP：Context7 这类外部能力的接入协议层。&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;只用 Skills，容易出现流程正确但 API 过期；只用 Context7，容易出现文档新但执行无约束。两者组合更稳。&lt;/p&gt;
&lt;h2&gt;示例&lt;/h2&gt;
&lt;h3&gt;npx skills&lt;/h3&gt;
&lt;p&gt;&lt;a href=&quot;https://skills.sh/docs/cli&quot;&gt;skills CLI&lt;/a&gt; 是 Node.js 生态里的命令行工具。&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-bash&quot;&gt;# 从 GitHub 安装一个技能集合
npx skills add vercel-labs/agent-skills

# 指定安装到某类 agent
npx skills add vercel-labs/agent-skills --agent codex

# 从本地目录安装
npx skills add ./my-local-skills
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;目前很多 IDE 也直接集成了 Skills 管理的功能，一般存储在用户的 &lt;code&gt;~/.agents/skills&lt;/code&gt; 目录。&lt;/p&gt;
&lt;h3&gt;skill-creator&lt;/h3&gt;
&lt;p&gt;&lt;a href=&quot;https://developers.openai.com/codex/skills&quot;&gt;skill-creator&lt;/a&gt; 是 Codex 里最常用的创建入口，适合从零创建新 skill。&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-bash&quot;&gt;$ skill-creator
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;进入后通常会让你明确技能名、触发描述、是否需要脚本目录、要不要参考文档目录。&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;先定技能边界，例如 &lt;code&gt;log-archive&lt;/code&gt; 只做日志归档与清单输出；&lt;/li&gt;
&lt;li&gt;把触发条件写进 &lt;code&gt;description&lt;/code&gt;，避免写成过泛的“处理文件”；&lt;/li&gt;
&lt;li&gt;把稳定动作放进 &lt;code&gt;scripts/&lt;/code&gt;，例如 &lt;code&gt;scripts/archive_logs.py&lt;/code&gt;；&lt;/li&gt;
&lt;li&gt;把背景资料放进 &lt;code&gt;references/&lt;/code&gt;，不要把长文档全塞到 &lt;code&gt;SKILL.md&lt;/code&gt;。&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;一个精简 &lt;code&gt;SKILL.md&lt;/code&gt; 可以是：&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-markdown&quot;&gt;---
name: log-archive
description: 归档项目日志并输出清单，适用于日志压缩、按日期分桶、校验结果生成。
---

接收日志目录和归档目录。
检查目录存在性并统计文件数。
按日期分桶压缩日志并输出文件名、大小、校验和。
失败时返回错误原因和可重试建议。
&lt;/code&gt;&lt;/pre&gt;</content:encoded></item><item><title>Agent 协议：MCP、A2A、AG-UI</title><link>https://saurlax.com/blog/agent-protocol/</link><guid isPermaLink="true">https://saurlax.com/blog/agent-protocol/</guid><description>如果你正在做 Agent 应用，几乎一定会遇到这三个问题：

- Agent 怎么安全、标准地接外部工具和数据；
- Agent 怎么和别的 Agent 协作；
- Agent 怎么和用户界面稳定地实...</description><pubDate>Fri, 06 Mar 2026 11:47:27 GMT</pubDate><content:encoded>&lt;p&gt;如果你正在做 Agent 应用，几乎一定会遇到这三个问题：&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Agent 怎么安全、标准地接外部工具和数据；&lt;/li&gt;
&lt;li&gt;Agent 怎么和别的 Agent 协作；&lt;/li&gt;
&lt;li&gt;Agent 怎么和用户界面稳定地实时交互。&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;MCP、A2A、AG-UI 对应的正好是这三个层面。很多团队早期会把它们混在一起理解，最后要么“全都自己封装一层”，要么把一个协议硬塞进不适合的场景，系统复杂度暴涨，维护成本也很快失控。&lt;/p&gt;
&lt;p&gt;这篇文章把三者拆开讲清楚：&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;MCP 解决 Agent 与工具/上下文系统的连接问题；&lt;/li&gt;
&lt;li&gt;A2A 解决 Agent 与 Agent 的跨系统协作问题；&lt;/li&gt;
&lt;li&gt;AG-UI 解决 Agent 与用户界面的事件化交互问题。&lt;/li&gt;
&lt;/ul&gt;
&lt;h2&gt;MCP&lt;/h2&gt;
&lt;p&gt;&lt;a href=&quot;https://modelcontextprotocol.io&quot;&gt;MCP&lt;/a&gt; 全称是 &lt;strong&gt;Model Context Protocol&lt;/strong&gt;，即&lt;strong&gt;模型上下文协议&lt;/strong&gt;。&lt;/p&gt;
&lt;p&gt;它的核心目标很直白：给“LLM 应用（Host）和外部能力（Server）”之间定义一套统一的、可协商能力的协议层。这样应用不用为每个工具系统都单独写一套适配通信协议，也不需要把每个外部系统都改造成同一种 SDK 形态。&lt;/p&gt;
&lt;p&gt;官方对 MCP 的定位是“上下文交换协议”，它不规定你的 Agent 如何推理，不规定你必须用哪家模型，也不规定你应用层怎么设计工作流。它只把“连接、发现、调用、通知、协商”这层标准化。&lt;/p&gt;
&lt;p&gt;如果把一个完整 Agent 系统拆成三层：&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;上层是用户交互；&lt;/li&gt;
&lt;li&gt;中层是 Agent 编排与决策；&lt;/li&gt;
&lt;li&gt;下层是工具、数据、执行环境。&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;MCP 很明确地工作在中下层边界，重点是把“可供 Agent 使用的能力”暴露成统一协议对象。&lt;/p&gt;
&lt;p&gt;它本质上是“能力总线”，不是“业务编排框架”。&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-mermaid&quot;&gt;flowchart LR
    U[用户] --&amp;gt; H[MCP Host\n如 Claude Desktop / IDE Agent]
    H --&amp;gt; C[MCP Client]
    C &amp;lt;--&amp;gt;|JSON-RPC 2.0| S1[MCP Server A]
    C &amp;lt;--&amp;gt;|JSON-RPC 2.0| S2[MCP Server B]

    S1 --&amp;gt; T[Tools]
    S1 --&amp;gt; R[Resources]
    S1 --&amp;gt; P[Prompts]

    S2 --&amp;gt; T2[Tools]
    S2 --&amp;gt; R2[Resources]

    subgraph Transport
      ST[STDIO]
      SH[Streamable HTTP\nPOST/GET + 可选 SSE]
    end

    C -.基于传输层.- ST
    C -.基于传输层.- SH
&lt;/code&gt;&lt;/pre&gt;
&lt;h3&gt;协议组成&lt;/h3&gt;
&lt;p&gt;MCP 的消息格式基于 JSON-RPC 2.0。&lt;/p&gt;
&lt;p&gt;网络层面：&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;本地模式用标准输入输出流；&lt;/li&gt;
&lt;li&gt;远端模式用 HTTP（POST/GET），可选 SSE 做流式返回。&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;你可以把它理解为：&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;语义标准统一在 JSON-RPC；&lt;/li&gt;
&lt;li&gt;传输通道按部署场景替换。&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;MCP 可以按两层来理解：&lt;/p&gt;
&lt;h4&gt;数据层&lt;/h4&gt;
&lt;p&gt;数据层基于 JSON-RPC 2.0，负责消息语义，主要包含：&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;生命周期管理：&lt;code&gt;initialize&lt;/code&gt;、能力协商、初始化完成通知等；&lt;/li&gt;
&lt;li&gt;Server 侧能力：&lt;code&gt;tools&lt;/code&gt;、&lt;code&gt;resources&lt;/code&gt;、&lt;code&gt;prompts&lt;/code&gt;；&lt;/li&gt;
&lt;li&gt;Client 侧能力：&lt;code&gt;sampling&lt;/code&gt;、&lt;code&gt;elicitation&lt;/code&gt;、&lt;code&gt;logging&lt;/code&gt; 等；&lt;/li&gt;
&lt;li&gt;通知机制：列表变化、进度、状态更新。&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;这个分层的价值在于：你不用关心底层到底是本地进程管道还是网络连接，方法语义是一致的。&lt;/p&gt;
&lt;h4&gt;传输层&lt;/h4&gt;
&lt;p&gt;截至当前稳定规范，官方标准传输是两种：&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;code&gt;stdio&lt;/code&gt;：本地子进程通信，适合本机工具；&lt;/li&gt;
&lt;li&gt;&lt;code&gt;Streamable HTTP&lt;/code&gt;：HTTP POST/GET，支持返回 JSON 或 &lt;code&gt;text/event-stream&lt;/code&gt; 流。&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;规范明确指出：&lt;code&gt;Streamable HTTP&lt;/code&gt; 取代了更早版本中的 HTTP+SSE 传输定义，语义更清晰，也更适合做远端部署和可恢复流。&lt;/p&gt;
&lt;h3&gt;核心原语&lt;/h3&gt;
&lt;p&gt;MCP 最关键的三类 Server 原语：&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;code&gt;Tools&lt;/code&gt;：可执行动作，模型可调用；&lt;/li&gt;
&lt;li&gt;&lt;code&gt;Resources&lt;/code&gt;：上下文数据源，供应用和模型读取；&lt;/li&gt;
&lt;li&gt;&lt;code&gt;Prompts&lt;/code&gt;：可复用提示模板，常由用户触发。&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;官方还给了一个很好用的控制层级：&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Prompts 偏用户控制；&lt;/li&gt;
&lt;li&gt;Resources 偏应用控制；&lt;/li&gt;
&lt;li&gt;Tools 偏模型控制。&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;这个区分非常实用，能避免“什么都做成 Tool”的反模式。&lt;/p&gt;
&lt;h4&gt;发送方与接收方是谁&lt;/h4&gt;
&lt;p&gt;MCP 的参与者有三个角色：&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;code&gt;MCP Host&lt;/code&gt;：比如桌面 AI 客户端、IDE 插件容器；&lt;/li&gt;
&lt;li&gt;&lt;code&gt;MCP Client&lt;/code&gt;：Host 内部创建的连接组件；&lt;/li&gt;
&lt;li&gt;&lt;code&gt;MCP Server&lt;/code&gt;：真正提供能力的一端。&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;消息方向并不是单向。&lt;/p&gt;
&lt;p&gt;常见方向：&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Client -&amp;gt; Server：&lt;code&gt;tools/list&lt;/code&gt;、&lt;code&gt;tools/call&lt;/code&gt;、&lt;code&gt;resources/read&lt;/code&gt;；&lt;/li&gt;
&lt;li&gt;Server -&amp;gt; Client：通知、请求采样、请求用户补充信息。&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;因此它不是传统“纯后端 API”模型，而是可双向交互的协议会话。&lt;/p&gt;
&lt;h3&gt;示例&lt;/h3&gt;
&lt;p&gt;官方教程里最常见的是 Python &lt;code&gt;FastMCP&lt;/code&gt;，非常适合从零起步。我们以此做一个最小 Weather Server，暴露两个工具：&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;code&gt;get_alerts&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;get_forecast&lt;/code&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;pre&gt;&lt;code class=&quot;language-bash&quot;&gt;uv init weather
cd weather
uv venv
uv add &amp;quot;mcp[cli]&amp;quot; httpx
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;然后编写以下脚本：&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-python&quot;&gt;from typing import Any
import httpx
from mcp.server.fastmcp import FastMCP

mcp = FastMCP(&amp;quot;weather&amp;quot;)
NWS_API_BASE = &amp;quot;https://api.weather.gov&amp;quot;
USER_AGENT = &amp;quot;weather-app/1.0&amp;quot;

async def make_nws_request(url: str) -&amp;gt; dict[str, Any] | None:
    headers = {&amp;quot;User-Agent&amp;quot;: USER_AGENT, &amp;quot;Accept&amp;quot;: &amp;quot;application/geo+json&amp;quot;}
    async with httpx.AsyncClient() as client:
        try:
            resp = await client.get(url, headers=headers, timeout=30.0)
            resp.raise_for_status()
            return resp.json()
        except Exception:
            return None

@mcp.tool()
async def get_alerts(state: str) -&amp;gt; str:
    url = f&amp;quot;{NWS_API_BASE}/alerts/active/area/{state}&amp;quot;
    data = await make_nws_request(url)
    if not data or &amp;quot;features&amp;quot; not in data:
        return &amp;quot;Unable to fetch alerts or no alerts found.&amp;quot;
    return &amp;quot;No active alerts for this state.&amp;quot; if not data[&amp;quot;features&amp;quot;] else &amp;quot;Fetched&amp;quot;

def main():
    mcp.run(transport=&amp;quot;stdio&amp;quot;)

if __name__ == &amp;quot;__main__&amp;quot;:
    main()
&lt;/code&gt;&lt;/pre&gt;
&lt;h2&gt;A2A&lt;/h2&gt;
&lt;p&gt;&lt;a href=&quot;https://a2a-protocol.org&quot;&gt;A2A&lt;/a&gt; 的全称是 &lt;strong&gt;Agent2Agent Protocol&lt;/strong&gt;，常见中文是 &lt;strong&gt;智能体到智能体协议&lt;/strong&gt;。&lt;/p&gt;
&lt;p&gt;它的定位是让不同组织、不同框架、不同部署环境里的 Agent 可以标准化协作，避免一对一私有协议集成。&lt;/p&gt;
&lt;p&gt;A2A 关心的是“Agent 间任务协作”，不是“Agent 调单个工具”。&lt;/p&gt;
&lt;p&gt;A2A 想解决的问题是：&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Agent 不该被迫“伪装成工具”才能协作；&lt;/li&gt;
&lt;li&gt;跨框架多 Agent 场景不该每次都重做通信层；&lt;/li&gt;
&lt;li&gt;长任务、流式、异步通知应该是协议内建能力。&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;它强调“不暴露内部实现细节的协作”，也就是 opaque execution。你知道对方能力和协议入口，但不需要知道对方内部 memory、toolchain、模型编排细节。&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-mermaid&quot;&gt;sequenceDiagram
    participant Client as A2A Client（客户端 Agent）
    participant Server as A2A Server（远端 Agent）
    participant Auth as Auth Server

    Client-&amp;gt;&amp;gt;Server: GET /.well-known/agent-card.json
    Server--&amp;gt;&amp;gt;Client: Agent Card（能力、技能、认证要求）

    Client-&amp;gt;&amp;gt;Auth: 按 Agent Card 的安全方案取凭证
    Auth--&amp;gt;&amp;gt;Client: Token / Credential

    Client-&amp;gt;&amp;gt;Server: SendMessage（JSON-RPC over HTTP）
    alt 即时结果
        Server--&amp;gt;&amp;gt;Client: Message
    else 长任务
        Server--&amp;gt;&amp;gt;Client: Task（submitted / working ...）
        Server--&amp;gt;&amp;gt;Client: SSE 推送 TaskStatusUpdate / ArtifactUpdate
        opt 超长任务
            Server--&amp;gt;&amp;gt;Client: Webhook Push Notification
        end
    end
&lt;/code&gt;&lt;/pre&gt;
&lt;h3&gt;协议组成&lt;/h3&gt;
&lt;p&gt;A2A 的核心绑定里最常见的是：&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;code&gt;JSON-RPC 2.0 over HTTP(S)&lt;/code&gt;；&lt;/li&gt;
&lt;li&gt;&lt;code&gt;SSE&lt;/code&gt; 用于流式事件；&lt;/li&gt;
&lt;li&gt;规范还定义了 &lt;code&gt;gRPC&lt;/code&gt; 和 &lt;code&gt;HTTP+JSON/REST&lt;/code&gt; 绑定。&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;对大多数团队来说，JSON-RPC over HTTPS + SSE 是第一落地点，迁移成本最低。&lt;/p&gt;
&lt;h4&gt;核心对象&lt;/h4&gt;
&lt;p&gt;A2A 的模型对象非常完整，工程上重点记住这几类：&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;code&gt;Agent Card&lt;/code&gt;：Agent 的“能力名片”，含 endpoint、skills、认证方式；&lt;/li&gt;
&lt;li&gt;&lt;code&gt;Task&lt;/code&gt;：有生命周期的工作单元；&lt;/li&gt;
&lt;li&gt;&lt;code&gt;Message&lt;/code&gt;：一次通信回合，带 &lt;code&gt;role&lt;/code&gt;；&lt;/li&gt;
&lt;li&gt;&lt;code&gt;Part&lt;/code&gt;：Message/Artifact 的最小内容单元；&lt;/li&gt;
&lt;li&gt;&lt;code&gt;Artifact&lt;/code&gt;：任务产出物，可以增量更新。&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;这套对象组合的意义是：&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;可以先协商能力，再发任务；&lt;/li&gt;
&lt;li&gt;可以即时回复，也可以切成长任务；&lt;/li&gt;
&lt;li&gt;可以在任务执行中持续流式更新结果；&lt;/li&gt;
&lt;li&gt;可以把结果沉淀成结构化产物，而不是只吐一段文本。&lt;/li&gt;
&lt;/ul&gt;
&lt;h4&gt;发送方与接收方&lt;/h4&gt;
&lt;p&gt;在 A2A 里：&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;发送请求的一般是 &lt;code&gt;A2A Client&lt;/code&gt;；&lt;/li&gt;
&lt;li&gt;处理请求的一般是 &lt;code&gt;A2A Server&lt;/code&gt;（Remote Agent）。&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Role 语义也在协议里有定义：&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;code&gt;ROLE_USER&lt;/code&gt;：客户端到服务端的消息；&lt;/li&gt;
&lt;li&gt;&lt;code&gt;ROLE_AGENT&lt;/code&gt;：服务端到客户端的消息。&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;如果任务很长，Server 还会作为主动发送方，通过 SSE 或 Webhook 把状态变化发给 Client。&lt;/p&gt;
&lt;h4&gt;Agent Card&lt;/h4&gt;
&lt;p&gt;A2A 里最容易被低估的对象就是 Agent Card。&lt;/p&gt;
&lt;p&gt;它承担了几个关键职责：&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;发现：客户端知道“这个 Agent 是谁、会什么”；&lt;/li&gt;
&lt;li&gt;连接：知道该请求哪个 endpoint；&lt;/li&gt;
&lt;li&gt;安全：知道该用哪种认证方案；&lt;/li&gt;
&lt;li&gt;协议选择：知道支持哪些 transport / binding；&lt;/li&gt;
&lt;li&gt;扩展声明：知道是否需要扩展能力。&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;规范里还给了 well-known 地址约定：&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;code&gt;https://{server_domain}/.well-known/agent-card.json&lt;/code&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;这让大规模注册发现和自动化编排更可行。&lt;/p&gt;
&lt;h4&gt;Task 生命周期&lt;/h4&gt;
&lt;p&gt;A2A 的工程价值，很大一部分来自它把任务生命周期协议化了。&lt;/p&gt;
&lt;p&gt;一个任务不是只有“成功/失败”两种状态，而是可以经历：&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;submitted&lt;/li&gt;
&lt;li&gt;working&lt;/li&gt;
&lt;li&gt;input-required&lt;/li&gt;
&lt;li&gt;auth-required&lt;/li&gt;
&lt;li&gt;completed / failed / canceled / rejected&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;这会直接改善两类体验：&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;产品体验：用户能看到任务进度，不是一直转圈；&lt;/li&gt;
&lt;li&gt;系统体验：调度器可以根据状态做超时、重试、分派。&lt;/li&gt;
&lt;/ul&gt;
&lt;h3&gt;示例&lt;/h3&gt;
&lt;p&gt;这里我们使用官方的 &lt;code&gt;@a2a-js/sdk&lt;/code&gt; 包。&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-bash&quot;&gt;npm install @a2a-js/sdk express
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;然后编写核心代码：&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-ts&quot;&gt;import express from &amp;quot;express&amp;quot;;
import { v4 as uuidv4 } from &amp;quot;uuid&amp;quot;;
import type { AgentCard, Message } from &amp;quot;@a2a-js/sdk&amp;quot;;
import {
  AgentExecutor,
  DefaultRequestHandler,
  InMemoryTaskStore,
  RequestContext,
  ExecutionEventBus,
} from &amp;quot;@a2a-js/sdk/server&amp;quot;;
import {
  AGENT_CARD_PATH,
  agentCardHandler,
  jsonRpcHandler,
  UserBuilder,
} from &amp;quot;@a2a-js/sdk/server/express&amp;quot;;

const card: AgentCard = {
  name: &amp;quot;Hello Agent&amp;quot;,
  description: &amp;quot;A simple A2A hello server&amp;quot;,
  protocolVersion: &amp;quot;0.3.0&amp;quot;,
  version: &amp;quot;0.1.0&amp;quot;,
  url: &amp;quot;http://localhost:4000/a2a/jsonrpc&amp;quot;,
  skills: [
    { id: &amp;quot;chat&amp;quot;, name: &amp;quot;Chat&amp;quot;, description: &amp;quot;Say hello&amp;quot;, tags: [&amp;quot;chat&amp;quot;] },
  ],
  capabilities: { pushNotifications: false },
  defaultInputModes: [&amp;quot;text&amp;quot;],
  defaultOutputModes: [&amp;quot;text&amp;quot;],
};

class HelloExecutor implements AgentExecutor {
  async execute(ctx: RequestContext, bus: ExecutionEventBus): Promise&amp;lt;void&amp;gt; {
    const msg: Message = {
      kind: &amp;quot;message&amp;quot;,
      messageId: uuidv4(),
      role: &amp;quot;agent&amp;quot;,
      parts: [
        { kind: &amp;quot;text&amp;quot;, text: `Hello from A2A, context=${ctx.contextId}` },
      ],
      contextId: ctx.contextId,
    };
    bus.publish(msg);
    bus.finished();
  }
  cancelTask = async (): Promise&amp;lt;void&amp;gt; =&amp;gt; {};
}

const handler = new DefaultRequestHandler(
  card,
  new InMemoryTaskStore(),
  new HelloExecutor(),
);
const app = express();

app.use(
  `/${AGENT_CARD_PATH}`,
  agentCardHandler({ agentCardProvider: handler }),
);
app.use(
  &amp;quot;/a2a/jsonrpc&amp;quot;,
  jsonRpcHandler({
    requestHandler: handler,
    userBuilder: UserBuilder.noAuthentication,
  }),
);

app.listen(4000);
&lt;/code&gt;&lt;/pre&gt;
&lt;h2&gt;AG-UI&lt;/h2&gt;
&lt;p&gt;&lt;a href=&quot;https://ag-ui.com&quot;&gt;AG-UI&lt;/a&gt; 的全称是 &lt;strong&gt;Agent-User Interaction Protocol&lt;/strong&gt;，中文可以叫 &lt;strong&gt;智能体-用户交互协议&lt;/strong&gt;。&lt;/p&gt;
&lt;p&gt;它的关注点和 MCP、A2A 不同：它解决的是“用户界面如何稳定地消费 Agent 执行过程中的事件流”，并允许用户输入、工具反馈、状态变化在同一条交互通道里双向流动。&lt;/p&gt;
&lt;p&gt;AG-UI 主要在“前端应用 &amp;lt;-&amp;gt; Agent 后端”之间工作。&lt;/p&gt;
&lt;p&gt;它不是 LLM 推理协议，不是工具调用协议，也不是跨 Agent 协议。它是“交互编排协议”：&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;把长时间运行的 agent run 拆成可消费事件；&lt;/li&gt;
&lt;li&gt;把工具调用、状态变更、文本流、活动进度统一事件化；&lt;/li&gt;
&lt;li&gt;让前端用一致方式渲染和反馈。&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;这件事看起来简单，实际上是 Agent 产品化最容易崩的地方。没有协议化事件层，UI 往往会陷入“几十个 websocket/HTTP 回调拼起来”的不可维护状态。&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-mermaid&quot;&gt;flowchart LR
    UI[用户界面\nWeb/App] &amp;lt;--&amp;gt; Client[AG-UI Client\n如 HttpAgent]
    Client &amp;lt;--&amp;gt; Backend[Agent Backend\nLangGraph / CrewAI / ADK 等]

    Backend --&amp;gt; E1[Lifecycle Events]
    Backend --&amp;gt; E2[Text Events]
    Backend --&amp;gt; E3[Tool Events]
    Backend --&amp;gt; E4[State Events]
    Backend --&amp;gt; E5[Custom/Raw Events]

    subgraph Transport
      T1[HTTP SSE]
      T2[HTTP Binary]
      T3[WebSocket / Webhook]
    end

    Client -.传输无关.- T1
    Client -.传输无关.- T2
    Client -.可扩展.- T3
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;AG-UI 官方文档给的边界非常实用：&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;MCP：Agent &amp;lt;-&amp;gt; 工具与数据；&lt;/li&gt;
&lt;li&gt;A2A：Agent &amp;lt;-&amp;gt; Agent；&lt;/li&gt;
&lt;li&gt;AG-UI：Agent &amp;lt;-&amp;gt; 用户应用。&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;在真实系统里，一个 Agent 经常会三者同时使用：&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;前端与 Agent run 用 AG-UI；&lt;/li&gt;
&lt;li&gt;Agent 内部访问工具用 MCP；&lt;/li&gt;
&lt;li&gt;Agent 需要委派其它 Agent 用 A2A。&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;边界清晰之后，代码层的分层会稳定很多。&lt;/p&gt;
&lt;h3&gt;协议组成&lt;/h3&gt;
&lt;p&gt;AG-UI 明确是 transport-agnostic（传输无关）的。&lt;/p&gt;
&lt;p&gt;官方常见形态：&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;HTTP SSE：易调试、兼容面广；&lt;/li&gt;
&lt;li&gt;HTTP Binary：更高性能与更低带宽；&lt;/li&gt;
&lt;li&gt;也支持 WebSocket、webhook 等模式。&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;它的关键不是“只能用某一种传输”，而是“事件语义保持一致”。&lt;/p&gt;
&lt;h4&gt;事件模型&lt;/h4&gt;
&lt;p&gt;AG-UI 是典型事件协议，核心结构是 &lt;code&gt;BaseEvent&lt;/code&gt; 与事件类型集合。&lt;/p&gt;
&lt;p&gt;常见事件类别：&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;生命周期事件：&lt;code&gt;RUN_STARTED&lt;/code&gt;、&lt;code&gt;RUN_FINISHED&lt;/code&gt;、&lt;code&gt;RUN_ERROR&lt;/code&gt;、&lt;code&gt;STEP_STARTED&lt;/code&gt;、&lt;code&gt;STEP_FINISHED&lt;/code&gt;；&lt;/li&gt;
&lt;li&gt;文本事件：&lt;code&gt;TEXT_MESSAGE_START&lt;/code&gt;、&lt;code&gt;TEXT_MESSAGE_CONTENT&lt;/code&gt;、&lt;code&gt;TEXT_MESSAGE_END&lt;/code&gt;；&lt;/li&gt;
&lt;li&gt;工具事件：&lt;code&gt;TOOL_CALL_START&lt;/code&gt;、&lt;code&gt;TOOL_CALL_ARGS&lt;/code&gt;、&lt;code&gt;TOOL_CALL_END&lt;/code&gt;；&lt;/li&gt;
&lt;li&gt;状态事件：&lt;code&gt;STATE_SNAPSHOT&lt;/code&gt;、&lt;code&gt;STATE_DELTA&lt;/code&gt;、&lt;code&gt;MESSAGES_SNAPSHOT&lt;/code&gt;；&lt;/li&gt;
&lt;li&gt;活动事件：&lt;code&gt;ACTIVITY_SNAPSHOT&lt;/code&gt;、&lt;code&gt;ACTIVITY_DELTA&lt;/code&gt;；&lt;/li&gt;
&lt;li&gt;特殊事件：&lt;code&gt;RAW&lt;/code&gt;、&lt;code&gt;CUSTOM&lt;/code&gt;。&lt;/li&gt;
&lt;/ul&gt;
&lt;h4&gt;发送方与接收方&lt;/h4&gt;
&lt;p&gt;AG-UI 的发送方与接收方会在一个 run 生命周期内反复切换：&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;后端 Agent -&amp;gt; 前端：连续发事件流；&lt;/li&gt;
&lt;li&gt;前端 -&amp;gt; 后端：发送用户输入、交互动作、上下文补充、控制指令。&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;这意味着 AG-UI 天生是双向交互模型，而不是“前端拉一次结果”的静态接口。&lt;/p&gt;
&lt;h3&gt;示例&lt;/h3&gt;
&lt;p&gt;如果你要快速做一个可演示、可迭代的前端 Agent 应用，AG-UI 官方推荐路径是先用 CLI 脚手架。&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-bash&quot;&gt;npx create-ag-ui-app@latest
npm run dev
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;脚手架会生成一套可运行的客户端与服务端组合，包含 AG-UI 事件流处理。默认示例里可以直接访问本地页面验证交互。&lt;/p&gt;
&lt;p&gt;基于 &lt;code&gt;HttpAgent&lt;/code&gt; 思路，可以抽象成：&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-ts&quot;&gt;import { HttpAgent, EventType } from &amp;quot;@ag-ui/client&amp;quot;;

const agent = new HttpAgent({
  url: &amp;quot;http://localhost:8787/agent&amp;quot;,
  agentId: &amp;quot;demo-agent&amp;quot;,
  threadId: &amp;quot;thread-001&amp;quot;,
});

agent
  .runAgent({
    tools: [],
    context: [],
  })
  .subscribe({
    next: (event) =&amp;gt; {
      if (event.type === EventType.TEXT_MESSAGE_CONTENT) {
        // 把文本增量写入 UI
      }
      if (event.type === EventType.STATE_DELTA) {
        // 应用状态补丁
      }
    },
  });
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;AG-UI 后端适配通常只做三件事：&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;把框架原生回调翻译成 AG-UI 事件；&lt;/li&gt;
&lt;li&gt;保证事件顺序和 run/step 边界；&lt;/li&gt;
&lt;li&gt;把用户输入与控制信号映射回框架执行层。&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;这也是为什么它适合接在 LangGraph、CrewAI、ADK、Mastra 等不同框架前面，做统一交互层。&lt;/p&gt;
&lt;p&gt;AG-UI 的 &lt;code&gt;STATE_SNAPSHOT&lt;/code&gt; + &lt;code&gt;STATE_DELTA&lt;/code&gt; 组合值得重点用好：&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;首屏或重连先发 Snapshot；&lt;/li&gt;
&lt;li&gt;持续交互阶段只发 Delta；&lt;/li&gt;
&lt;li&gt;Delta 采用可回放的补丁语义；&lt;/li&gt;
&lt;li&gt;前端维护本地状态机，不把 UI 逻辑塞进后端 prompt。&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;这样可以显著降低前端抖动、重绘和状态错乱问题。&lt;/p&gt;</content:encoded></item><item><title>Spec Coding</title><link>https://saurlax.com/blog/spec-coding/</link><guid isPermaLink="true">https://saurlax.com/blog/spec-coding/</guid><description>Spec Coding 的核心不是“让 AI 多写代码”，而是“让规格主导代码”。需求、边界、验收、非功能要求先被写成可执行的 spec，随后实现、测试、评审、发布都围绕同一份规格推进。很多团队在引入...</description><pubDate>Wed, 04 Mar 2026 09:40:00 GMT</pubDate><content:encoded>&lt;p&gt;Spec Coding 的核心不是“让 AI 多写代码”，而是“让规格主导代码”。需求、边界、验收、非功能要求先被写成可执行的 spec，随后实现、测试、评审、发布都围绕同一份规格推进。很多团队在引入 AI 之后才真正感受到这一点：模型能力决定产出速度，规格质量决定产出上限。&lt;/p&gt;
&lt;h2&gt;起源与发展&lt;/h2&gt;
&lt;p&gt;Spec Coding 在术语上是新的，在思想上并不新。软件工程早就有一条稳定主线：把隐含需求外化为可验证约束，让实现行为绑定约束，而不是绑定个人理解。&lt;/p&gt;
&lt;p&gt;这条主线在不同分支里一直存在。需求工程强调可验证需求，关注“需求是否能被证明已经满足”。契约式设计强调前置条件、后置条件、不变量，关注“行为是否违背约束”。测试驱动强调可执行反馈，关注“实现是否持续满足行为定义”。早在 2004 年 &lt;a href=&quot;https://pure.york.ac.uk/portal/en/publications/agile-specification-driven-development&quot;&gt;Agile Specification-Driven Development&lt;/a&gt; 一文就已提出测试与契约是互补规格，规格不应停留在前期文档，而应贯穿迭代。&lt;/p&gt;
&lt;p&gt;AI 编程让这些老问题再次显形。Vibe Coding 能快速推进探索，但常见副作用也很稳定：需求漂移、边界遗漏、决策散落在聊天记录、多人协作时上下文失真。Spec Coding 的价值在这里变得具体：把“对话上下文”转换为“工程上下文”。&lt;/p&gt;
&lt;p&gt;从问题空间和解空间看也更容易理解。问题空间回答“要解决什么”，包括业务目标、规则、风险、合规和不变量。解空间回答“怎么实现”，包括架构、模块、接口、数据和部署。项目失控往往不是实现能力不足，而是问题空间表达不完整，导致解空间被迫猜测。Spec 的作用就是把问题空间结构化，再把结构化约束映射到可执行任务和可验证结果。&lt;/p&gt;
&lt;h2&gt;规格驱动开发&lt;/h2&gt;
&lt;p&gt;规格驱动开发，即 Specification-Driven Development。它不是额外负担，而是一种交付组织方式：规格先行，设计和开发围绕规格展开，测试和发布对规格负责。&lt;/p&gt;
&lt;p&gt;一份可用 spec 至少覆盖四类信息：&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;目标：要解决什么业务问题，成功标准是什么。&lt;/li&gt;
&lt;li&gt;边界：In/Out 范围，异常路径，依赖和前提条件。&lt;/li&gt;
&lt;li&gt;验收：可执行、可测量、可回归的验收条目。&lt;/li&gt;
&lt;li&gt;约束：性能、安全、审计、兼容、成本、时效。&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;这四类信息并不是写作模板，它们对应软件工程产物。目标对应需求项，边界对应设计决策，验收对应测试计划，约束对应质量门禁。规格越完整，后续分歧越少，返工越可控。&lt;/p&gt;
&lt;p&gt;Spec Coding、DDD（Domain-Driven Design，领域驱动设计）、TDD（Test-Driven Development，测试驱动开发） 的关系可以分层看。DDD 负责业务语义建模，解决“系统该如何表达真实业务”。SDD 负责交付编排，解决“业务约束如何变成可执行任务”。TDD 负责实现校验，解决“代码行为如何持续正确”。三者协同后，模型、实现、验证形成闭环。&lt;/p&gt;
&lt;p&gt;如果只有 DDD，没有 SDD，模型容易停在设计图上。如果只有 SDD，没有 TDD，规格难以持续验证。如果只有 TDD，没有 DDD，测试可以通过，但业务语义可能偏离。实践里最稳定的做法是：DDD 决定语义结构，SDD 决定执行结构，TDD 决定反馈结构。&lt;/p&gt;
&lt;p&gt;Spec Coding 与 Vibe Coding 也可放在同一框架对比：&lt;/p&gt;
&lt;p&gt;| 维度       | Spec Coding                  | Vibe Coding                    |
| ---------- | ---------------------------- | ------------------------------ |
| 控制面     | 规格文本                     | 对话上下文                     |
| 起手方式   | 先定义目标与验收，再实现     | 先生成结果，再迭代修正         |
| 需求管理   | 显式边界，变更可追踪         | 隐式边界，变更易漂移           |
| 协作成本   | 前期高，后期低               | 前期低，后期高                 |
| 质量稳定性 | 高，适合长期维护             | 波动大，依赖个人把控           |
| 典型场景   | 正式功能、多人协作、上线交付 | 原型探索、一次性脚本、灵感试验 |&lt;/p&gt;
&lt;p&gt;这不是二选一关系。许多团队采用“先 Vibe，后 Spec”的节奏：用 Vibe Coding 迅速探索方向，再把共识固化为 spec，切换到 Spec Coding 完成工程交付。&lt;/p&gt;
&lt;h2&gt;具体实践&lt;/h2&gt;
&lt;p&gt;把 Spec Coding 放进软件工程流水线，通常会形成一条稳定链路：需求阶段写 spec 草案，设计阶段映射架构与接口，实现阶段按 spec 拆任务，测试阶段按验收条目生成自动化验证，发布阶段按 spec 设门禁，维护阶段坚持“先改 spec，再改代码和测试”。&lt;/p&gt;
&lt;p&gt;这条链路的价值在于可追踪性。每次变更都能回答三个问题：改了什么，为什么改，影响了哪些行为。团队协作越复杂，这个能力越关键。&lt;/p&gt;
&lt;p&gt;落地时不依赖某个 IDE，关键在仓库组织。一个常见结构如下：&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-text&quot;&gt;.
├─ AGENTS.md
├─ specs/
│  ├─ README.md
│  ├─ order-refund.md
│  └─ order-refund.changelog.md
├─ tasks/
│  └─ order-refund.tasks.md
└─ tests/
   └─ refund.apply.spec.ts
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;code&gt;AGENTS.md&lt;/code&gt; 管全局规则，&lt;code&gt;specs/&lt;/code&gt; 管需求与边界，&lt;code&gt;tasks/&lt;/code&gt; 管执行拆解，&lt;code&gt;tests/&lt;/code&gt; 管行为验证。这个分层能把讨论沉淀成资产，避免知识只留在聊天上下文。&lt;/p&gt;
&lt;p&gt;&lt;code&gt;AGENTS.md&lt;/code&gt; 的规则建议同时覆盖代码和流程：&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-markdown&quot;&gt;## 代码

- 默认使用 TypeScript。
- 写入操作必须经由 service 层。
- 外部接口失败必须可重试或可补偿。

## 流程

- 任何功能变更先修改 spec，再修改代码。
- PR 说明必须引用 spec 和验收条目。
- 未覆盖验收测试的代码不得合并。
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;单个功能的 spec 可以保持简洁，但必须可执行：&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-markdown&quot;&gt;# order-refund

## 目标

支持支付后 7 天内全额退款，退款状态全程可追踪。

## 范围

- In：退款申请、审核、状态通知。
- Out：财务对账、跨渠道清算。

## 规则

- 退款金额不得超过实付金额。
- 审核动作必须记录操作者和时间。
- 超时 10 秒返回可重试错误。

## 验收

- 7 天内订单可提交退款申请。
- 超过 7 天返回 `REFUND_WINDOW_EXPIRED`。
- 审核通过后订单状态变更为 `refunded` 并写入审计日志。
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;随后把 spec 映射到任务清单和测试清单：&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-markdown&quot;&gt;# order-refund tasks

- [ ] API：新增 `POST /refund/apply`，对应验收 1、2。
- [ ] Domain：新增退款资格校验，覆盖 7 天窗口规则。
- [ ] Infra：新增审计日志写入，覆盖验收 3。
- [ ] Test：新增集成测试 `refund.apply.spec.ts`，覆盖验收 1、2、3。
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;这类“规格条目 -&amp;gt; 任务条目 -&amp;gt; 测试条目”的映射关系，就是 Spec Coding 的可执行形态。&lt;/p&gt;
&lt;p&gt;实践里最常见的问题也集中在这里。第一类是把 spec 写成愿景文案，缺少边界和验收，文本好看但不可执行。第二类是把 spec 当冻结文档，不做变更管理，最终代码和 spec 分叉。第三类是 spec 不接入测试和发布门禁，规格没有约束力，只是参考意见。要避免这些问题，关键不是写更长的 spec，而是让 spec 进入执行、测试、评审、发布四个环节。&lt;/p&gt;
&lt;h2&gt;GitHub Spec Kit&lt;/h2&gt;
&lt;p&gt;GitHub Spec Kit 把这条链路做成可直接启用的工程模板和命令集，适合团队把“规格先行”固定成统一流程。&lt;/p&gt;
&lt;p&gt;初始化可以用一次性命令，也可以持久安装后反复使用：&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-bash&quot;&gt;uv tool install specify-cli --from git+https://github.com/github/spec-kit.git
specify init --here --ai copilot
specify check
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;初始化完成后，仓库里会出现一组与代理工具相关的命令目录，以及 &lt;code&gt;.specify/&lt;/code&gt; 下的规格产物。对工程协作影响最大的点有两个：&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;规格、计划、任务会按固定路径沉淀，讨论内容不再散落在聊天记录里。&lt;/li&gt;
&lt;li&gt;同一项目可切换不同 agent，但保持同一套 spec 文件和推进节奏。&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;结合日常开发，比较稳的做法是把 Spec Kit 嵌进现有 PR 流程。&lt;/p&gt;
&lt;h3&gt;新功能流程&lt;/h3&gt;
&lt;p&gt;下面这组命令可以直接作为一个完整起手流程：&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-bash&quot;&gt;git switch -c feat/refund-window
/speckit.specify &amp;quot;支持退款窗口从 7 天调整为 15 天，并增加风控二次校验&amp;quot;
/speckit.plan &amp;quot;技术栈保持不变，优先复用现有退款服务与审计链路&amp;quot;
/speckit.tasks
specify check
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;这一步里每个产物要写的重点可以压缩成四行：&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;code&gt;spec.md&lt;/code&gt;：写清目标、范围、验收、非功能约束，尤其是边界条件和失败路径。&lt;/li&gt;
&lt;li&gt;&lt;code&gt;plan.md&lt;/code&gt;：写实现方案与取舍，明确改哪些模块、接口是否变化、数据是否迁移、风险怎么控。&lt;/li&gt;
&lt;li&gt;&lt;code&gt;tasks.md&lt;/code&gt;：拆成可执行任务并标注依赖关系，每条任务最好能对应一个可验证结果。&lt;/li&gt;
&lt;li&gt;PR 描述：引用 spec 条目和任务编号，评审结论按验收条目逐条对照。&lt;/li&gt;
&lt;/ul&gt;
&lt;h3&gt;增量更新流程&lt;/h3&gt;
&lt;p&gt;功能做了一半出现需求变更时，不建议直接改代码，先回到规格链路：&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-bash&quot;&gt;/speckit.specify &amp;quot;在已有退款功能上新增 VIP 用户 30 天窗口，普通用户维持 15 天&amp;quot;
/speckit.plan &amp;quot;补充分层鉴权策略与灰度发布计划，其他模块不动&amp;quot;
/speckit.tasks
specify check
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;这个更新流程对应的是“先改规范，再改实现”：&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;更新 &lt;code&gt;spec.md&lt;/code&gt;：新增变更背景，补充被影响的规则和验收条目。&lt;/li&gt;
&lt;li&gt;更新 &lt;code&gt;plan.md&lt;/code&gt;：只改受影响模块，标注不改动范围，避免计划膨胀。&lt;/li&gt;
&lt;li&gt;更新 &lt;code&gt;tasks.md&lt;/code&gt;：删除失效任务，新增变更任务，给任务补上验收映射。&lt;/li&gt;
&lt;li&gt;更新测试：按新验收补测试或调断言，保证 CI 对齐最新 spec。&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Spec Kit 也适合增量接入旧项目。不要一上来全量改造，优先挑一个高频变更模块做试点，把 &lt;code&gt;spec -&amp;gt; plan -&amp;gt; tasks -&amp;gt; test&lt;/code&gt; 链路跑通，再逐步推广到其他模块。这样能避免模板引入成本过高，也能快速验证团队是否真正按规格协作。&lt;/p&gt;</content:encoded></item><item><title>在海光 BW 加速卡上部署 vLLM 服务</title><link>https://saurlax.com/blog/vllm-on-hygon-bw/</link><guid isPermaLink="true">https://saurlax.com/blog/vllm-on-hygon-bw/</guid><description>最近在超算互联网平台上白嫖了一块海光 BW 加速卡，尝试了一下在上面部署 vLLM 服务。

海光 BW 是基于海光 DCU 的国产 AI 加速卡，由成都海光集成电路设计有限公司研发。它兼容 ROCm...</description><pubDate>Tue, 03 Mar 2026 15:44:57 GMT</pubDate><content:encoded>&lt;p&gt;最近在超算互联网平台上白嫖了一块海光 BW 加速卡，尝试了一下在上面部署 vLLM 服务。&lt;/p&gt;
&lt;p&gt;海光 BW 是基于海光 DCU 的国产 AI 加速卡，由成都海光集成电路设计有限公司研发。它兼容 ROCm 软件生态，支持主流深度学习框架，在大模型推理、科学计算等场景中可作为 NVIDIA CUDA GPU 的国产替代方案。&lt;/p&gt;
&lt;p&gt;使用的服务器：&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-bash&quot;&gt;root@crdnotebook-2028865973678886914-accol683th-15971:~# fastfetch --logo none
-----------------------------------------------------
OS: Ubuntu 22.04.5 LTS (Jammy Jellyfish) x86_64
Host: CY62-T58
Kernel: Linux 5.10.134-17.1.3.sga8.x86_64
Uptime: 20 hours, 17 mins
Packages: 1070 (dpkg)
Shell: bash 5.1.16
Theme: Yaru [GTK3]
Icons: Yaru [GTK3]
Cursor: Adwaita
Terminal: /dev/pts/0 8.9p1 Ubuntu-3ubuntu0.13
CPU: 2 x Hygon C86 (128) @ 2.50 GHz
GPU: ASPEED Technology, Inc. ASPEED Graphics Family
Memory: 38.27 GiB / 503.19 GiB (8%)
Swap: Disabled
Disk (/): 335.82 GiB / 436.09 GiB (77%) - overlay
Local IP (eth0): 172.20.129.106/32
Locale: C
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;加速卡信息：&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-bash&quot;&gt;root@crdnotebook-2028865973678886914-accol683th-15971:~# rocm-smi --showproductname --showmeminfo vram

================================= System Management Interface ==================================
================================================================================================
HCU[0]          : Card Series:           BW
HCU[0]          : Card Vendor:           C-3000 IC Design Co., Ltd.
================================================================================================
================================================================================================
HCU[0]          : vram Total Memory (MiB): 65520
HCU[0]          : vram Total Used Memory (MiB): 57766
================================================================================================
======================================== End of SMI Log ========================================
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;这里我们直接选用预装了 PyTorch 2.4.1 + DTK 25.04 + Python 3.10 的镜像。对应的模型文件可以直接在平台上下载，路径为 &lt;code&gt;/root/private_data/SothisAI/model/Aihub/*&lt;/code&gt;。在这里我们使用的是 &lt;code&gt;Qwen3-0.6B&lt;/code&gt; 模型。&lt;/p&gt;
&lt;p&gt;然后启动 vLLM 服务：&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-bash&quot;&gt;vllm serve /root/private_data/SothisAI/model/Aihub/Qwen3-0.6B/main/Qwen3-0.6B --served-model-name Qwen3-0.6B --tensor-parallel-size 1 --max-model-len 4096 --dtype bfloat16 --enforce-eager
...
INFO 03-04 01:10:16 [loader.py:460] Loading weights took 3.35 seconds
INFO 03-04 01:10:16 [model_runner.py:1155] Model loading took 1.1201 GiB and 3.505950 seconds
INFO 03-04 01:10:21 [worker.py:287] Memory profiling takes 5.16 seconds
INFO 03-04 01:10:21 [worker.py:287] the current vLLM instance can use total_gpu_memory (63.98GiB) x gpu_memory_utilization (0.90) = 57.59GiB
INFO 03-04 01:10:21 [worker.py:287] model weights take 1.12GiB; non_torch_memory takes 0.42GiB; PyTorch activation peak memory takes 1.39GiB; the rest of the memory reserved for KV Cache is 54.66GiB.
INFO 03-04 01:10:22 [executor_base.py:112] # rocm blocks: 31981, # CPU blocks: 2340
INFO 03-04 01:10:22 [executor_base.py:117] Maximum concurrency for 4096 tokens per request: 124.93x
INFO 03-04 01:10:27 [llm_engine.py:448] init engine (profile, create kv cache, warmup model) took 11.10 seconds
WARNING 03-04 01:10:28 [config.py:1241] Default sampling parameters have been overridden by the model&apos;s Hugging Face generation config recommended from the model creator. If this is not intended, please relaunch vLLM instance with `--generation-config vllm`.
INFO 03-04 01:10:28 [serving_chat.py:118] Using default chat sampling params from model: {&apos;temperature&apos;: 0.6, &apos;top_k&apos;: 20, &apos;top_p&apos;: 0.95}
INFO 03-04 01:10:28 [serving_completion.py:61] Using default completion sampling params from model: {&apos;temperature&apos;: 0.6, &apos;top_k&apos;: 20, &apos;top_p&apos;: 0.95}
INFO 03-04 01:10:28 [api_server.py:1090] Starting vLLM API server on http://0.0.0.0:8020
...
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;关于如何安装 Open WebUI 可参考 &lt;a href=&quot;https://docs.openwebui.com/getting-started/quick-start/&quot;&gt;Quick Start | Open WebUI&lt;/a&gt;。&lt;/p&gt;
&lt;p&gt;之后启动 Open WebUI：&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-bash&quot;&gt;open-webui serve
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;在管理员面板中设置外部连接 &lt;code&gt;OpenAI 接口&lt;/code&gt;，地址为 &lt;code&gt;http://localhost:8000/v1&lt;/code&gt;，之后就可以使用了。&lt;/p&gt;
&lt;p&gt;指标：Avg prompt throughput: 75.1 tokens/s, Avg generation throughput: 39.0 tokens/s, Running: 1 reqs, Swapped: 0 reqs, Pending: 0 reqs, GPU KV cache usage: 0.1%, CPU KV cache usage: 0.0%.&lt;/p&gt;</content:encoded></item><item><title>JS 奇技淫巧</title><link>https://saurlax.com/blog/js-tricks/</link><guid isPermaLink="true">https://saurlax.com/blog/js-tricks/</guid><description>倒序遍历

```js
for (let i = arr.length - 1; i--) {
  console.log(arr[i]);
}
```

小数取整

```js
~~3.14; //...</description><pubDate>Wed, 25 Feb 2026 06:49:01 GMT</pubDate><content:encoded>&lt;p&gt;倒序遍历&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-js&quot;&gt;for (let i = arr.length - 1; i--) {
  console.log(arr[i]);
}
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;小数取整&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-js&quot;&gt;~~3.14; // 3
3.14 | 0; // 3
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;变量交换&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-js&quot;&gt;[a, b] = [b, a];
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;布尔化&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-js&quot;&gt;Boolean(value);
!!value;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;数字转换&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-js&quot;&gt;Number(&amp;quot;123&amp;quot;); // 123
parseInt(&amp;quot;123&amp;quot;); // 123
+&amp;quot;123&amp;quot;; // 123
&amp;quot;123&amp;quot; - 0; // 123
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;| 特性       | Number()                                           | parseInt()                                             |
| ---------- | -------------------------------------------------- | ------------------------------------------------------ |
| 解析方式   | 整体转换：尝试把整个字符串当成一个合法的数字字面量 | 逐字符读取，遇到非数字就停（默认十进制，或可指定基数） |
| 空格处理   | 会忽略前后空格                                     | 忽略前导空格，但遇到空格即结束                         |
| 非数字字符 | 有任意非数值字符则返回 NaN                         | 读取到第一个非数字时停止并返回当前值                   |
| 前缀识别   | 识别 0x…、0b…、0o… 等                              | 支持 0x 前缀或通过 radix 指定进制                      |&lt;/p&gt;
&lt;p&gt;默认值&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-js&quot;&gt;foo = foo || &amp;quot;default&amp;quot;;
bar ??= &amp;quot;fallback&amp;quot;;
baz ||= 42;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;可空链与短路调用&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-js&quot;&gt;obj?.method?.();
callback &amp;amp;&amp;amp; callback();
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;逗号运算符返回最后一个结果&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-js&quot;&gt;let x = ((a = 1), (b = 2), a + b); // 3
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;模版字符串原始文本&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-js&quot;&gt;const raw = String.raw`Line1\nLine2`;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;箭头函数隐式返回逻辑结果&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-js&quot;&gt;const isPositive = (n) =&amp;gt; n &amp;gt; 0 &amp;amp;&amp;amp; &amp;quot;ok&amp;quot;;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;快速复制数组&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-js&quot;&gt;const copy = [...arr];
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;给对象添加属性并返回对象&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-js&quot;&gt;const withId = ((o) =&amp;gt; ((o.id = Date.now()), o))({});
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;用位运算快速取整&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-js&quot;&gt;Math.floor(x) === x ? x : x | 0;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;使用 map 转换 null/undefined&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-js&quot;&gt;const clean = arr.map((v) =&amp;gt; v ?? &amp;quot;&amp;quot;);
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;使用双重位非清除符号 (tilde)&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-js&quot;&gt;// 如果数组长度不是整数，则返回-1
const idx = ~arr.indexOf(x); // ≠ -1 表示存在
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;立即执行箭头函数&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-js&quot;&gt;(() =&amp;gt; {
  console.log(&amp;quot;IIFE&amp;quot;);
})();
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;函数返回对象并立即解构&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-js&quot;&gt;const { a: first } = ((x) =&amp;gt; ({ a: x, b: x * 2 }))(5);
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;逻辑运算结果赋值&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-js&quot;&gt;const result = value &amp;amp;&amp;amp; value.prop;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;创建指定长度填充空值再 map&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-js&quot;&gt;const zeros = Array(5)
  .fill()
  .map((_, i) =&amp;gt; i);
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;取余正值 hack&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-js&quot;&gt;const mod = ((n % m) + m) % m;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;用字典快速 switch&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-js&quot;&gt;const actions = { a: () =&amp;gt; 1, b: () =&amp;gt; 2 };
const res = (actions[key] || (() =&amp;gt; 0))();
&lt;/code&gt;&lt;/pre&gt;</content:encoded></item><item><title>通过 GitHub 导入 SSH keys</title><link>https://saurlax.com/blog/import-ssh-keys-via-github/</link><guid isPermaLink="true">https://saurlax.com/blog/import-ssh-keys-via-github/</guid><description>```bash
curl https://github.com/&lt;username&gt;.keys &gt;&gt; ~/.ssh/authorized_keys
```

将 `&lt;username&gt;` 替换为你的 ...</description><pubDate>Mon, 16 Feb 2026 04:13:31 GMT</pubDate><content:encoded>&lt;pre&gt;&lt;code class=&quot;language-bash&quot;&gt;curl https://github.com/&amp;lt;username&amp;gt;.keys &amp;gt;&amp;gt; ~/.ssh/authorized_keys
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;将 &lt;code&gt;&amp;lt;username&amp;gt;&lt;/code&gt; 替换为你的 GitHub 用户名，就可以将你在 GitHub 上添加的 SSH keys 导入到服务器的 &lt;code&gt;authorized_keys&lt;/code&gt; 文件中，从而允许你使用这些 SSH keys 进行远程登录。&lt;/p&gt;</content:encoded></item></channel></rss>