<?xml version="1.0" encoding="UTF-8"?><rss xmlns:atom="http://www.w3.org/2005/Atom" version="2.0" xmlns:content="http://purl.org/rss/1.0/modules/content/">
  <channel>
    <title>xancel的自留地</title>
    <link>https://xancel.top/</link>
    <description><![CDATA[分享技术与生活]]></description>
    <language>zh-CN</language>
    <managingEditor>xancelzc@gmail.com (xancel)</managingEditor>
    <pubDate>Fri, 12 Jun 2026 15:41:14 +0000</pubDate>
    <lastBuildDate>Fri, 12 Jun 2026 15:41:14 +0000</lastBuildDate>
    <generator>grtblog v2.0.4</generator>
    <image>
      <url>https://tlias-test-1347803077.cos.ap-nanjing.myqcloud.com/%E7%BD%91%E7%AB%99%E5%9B%BE%E6%A0%87.png</url>
      <title>xancel的自留地</title>
      <link>https://xancel.top/</link>
    </image>
    <atom:link href="https://xancel.top/feed" rel="self" type="application/rss+xml"/><item>
      <title>近期一些文章摘录（Agent、产品、记忆）</title>
      <link>https://xancel.top/posts/agent-native-dev-framework</link>
      <description><![CDATA[<blockquote><p>该内容由 RSS 渲染生成，最佳阅读体验请前往：<a href="https://xancel.top/posts/agent-native-dev-framework">https://xancel.top/posts/agent-native-dev-framework</a></p></blockquote><h1>Agent-Native 开发框架设计</h1>
<blockquote>
<p>原文：<a href="https://mp.weixin.qq.com/s/1htT_mikVg-8qn1sKWx2Fg">Agent-Native 开发框架设计</a></p>
</blockquote>
<ul>
<li>第一层：模块化能力
<ul>
<li>登录，支付，存储，多语言，SEO，后台这些通用功能如何做成一个低耦合可插拔的模块</li>
</ul>
</li>
<li>第二层：流程化工程
<ul>
<li>初始化项目，加模块，页面，CI/CD，部署等这些开发动作如何抽象成标准的流程（skills）</li>
</ul>
</li>
<li>第三层：Agent工程
<ul>
<li>tools，skills，context，memory，workflow怎么设计，怎么运转</li>
</ul>
</li>
<li>第四层：平台化视角
<ul>
<li>如何让别人和agent都能够更高效的开发</li>
</ul>
</li>
</ul>
<hr>
<h1>离技术越近，离商业越远</h1>
<blockquote>
<p>原文：<a href="https://mp.weixin.qq.com/s/6Kh8u8Ts9NsHFpTfVM3Dhw">离技术越近，离商业越远</a></p>
</blockquote>
<p>核心观点：在AI时代，怎么做出来不稀缺，怎么卖出去更稀缺</p>
<ul>
<li>AI的真正价值不是在于帮你多做，而是在于帮你快速证伪</li>
<li>对赚钱闭环来说，技术不是起点，对高质量职业发展来说，技术依然是护城河</li>
<li>AI 时代真正稀缺的，不再只是把功能做出来，而是知道什么值得做，以及怎样低成本验证它。</li>
</ul>
<hr>
<h1>与 AI 一起做产品的六条原则</h1>
<blockquote>
<p>原文：<a href="https://mp.weixin.qq.com/s/Q8j4611EjzdAN5rMFjAeoQ">与 AI 一起做产品的六条原则</a></p>
</blockquote>
<p>AI时代产品设计，如何做好人和AI的分工边界</p>
<ol>
<li>
<p>AI 是放大器，人的判断依旧是核心</p>
</li>
<li>
<p>AI 擅长做加法，人要做减法</p>
<blockquote>
<p>如果页面只能留一个按钮，是什么？</p>
</blockquote>
</li>
<li>
<p>AI 擅长平均值，人要提供独特的品味</p>
<ul>
<li>且要把品味沉淀成可复用的 context（skill，文档）</li>
</ul>
</li>
<li>
<p>AI 擅长表面功夫，只有人才能共情</p>
<ul>
<li>很炫酷的动画，页面，但也只是炫酷，无法打动人</li>
</ul>
</li>
<li>
<p>AI 可以无限生成，但人要保证一致性</p>
<ul>
<li>同一个按钮出现三次，要把用户适应的心智成本降到最低</li>
</ul>
</li>
</ol>
<table>
<thead>
<tr>
<th>传统时代的 PM 要求</th>
<th>AI 时代的 PM 要求</th>
</tr>
</thead>
<tbody>
<tr>
<td>需求分析、原型设计、项目管理、数据分析</td>
<td>判断力、品味、共情力、一致性管理、提示词/skill 工程能力</td>
</tr>
</tbody>
</table>
<hr>
<h1>ClaudeCode/Codex 节省80%Token</h1>
<ul>
<li>总费用 = input token（占60-90%）+ output token（占10-30%）</li>
<li>最大黑洞：项目文件自动读取</li>
</ul>
<p>思路：</p>
<pre><code>减少无效文件读取 → 压缩上下文 → 精简指令 → 选对模型
     ↓                 ↓              ↓            ↓
   省 60%           省 50%          省 40%       省 30%
</code></pre>
<p><strong>1. .claudeignore</strong></p>
<pre><code># .claudeignore
node_modules/
dist/
build/
.next/
__pycache__/
*.lock
package-lock.json
*.log
.git/
.idea/
.vscode/
*.png
*.jpg
*.svg
coverage/
</code></pre>
<p><strong>2. 善用 /compact 和 /memory</strong></p>
<p><strong>3. 善用Plan Mode 弄清需求</strong></p>
<hr>
<h1>记忆管理</h1>
<blockquote>
<p>参考：<a href="https://github.com/Tencent/TencentDB-Agent-Memory">GitHub - TencentDB-Agent-Memory</a> | <a href="https://mp.weixin.qq.com/s/QREQpeep9zn7ttJAh5I-bQ">我用了这个工具，Token消耗量降了60%</a></p>
</blockquote>
<p>模拟人脑的分层记忆：</p>
<pre><code>├─ 短期记忆：任务地图（Mermaid）
│  └─ 只保留结构，细节存到外部文件
├─ 长期记忆：分层沉淀
│  ├─ L0：原始对话（兜底）
│  ├─ L1：事件/偏好/约束（原子记忆）
│  ├─ L2：场景聚合（同一话题）
│  └─ L3：用户画像（稳定偏好）
└─ 检索机制：需要时精准召回
</code></pre>
<p>两个核心设计：</p>
<p><strong>1. 短期记忆：符号化压缩（解决长任务问题）</strong></p>
<pre><code>任务：调研 AI Infra 领域的最新进展

AI 的工作流程：
1. 搜索 &quot;AI Infra 2024&quot;，原文存到 file_001.txt
   ├─ 上下文只保留：&quot;搜索 AI Infra，发现 3 个关键趋势&quot;
   └─ Mermaid 任务地图：[搜索] → [发现趋势]

2. 读文章1，原文存到 file_002.txt
   ├─ 上下文只保留：&quot;趋势 1：多模态向量检索&quot;
   └─ 任务地图：[搜索] → [发现趋势] → [深挖趋势1]

3. 需要回顾文章1？→ 去 file_002.txt 读取原文
   └─ 而不是一直占据上下文

优势：
✓ 上下文始终保持轻量（只有任务地图）
✓ AI 能看到完整任务结构（不会迷路）
✓ 需要细节时可以随时找回原文（100% 可溯源）
</code></pre>
<p><strong>2. 长期记忆：分层沉淀（解决跨会话问题）</strong></p>
<pre><code>第 1 天对话：
你：&quot;我在做简历优化工具，目标用户是大学生。
     核心功能：10 秒诊断简历问题，
     不做编辑器，只做诊断&quot;

Agent Memory 自动沉淀：
├─ L1（原子记忆）：
│  ├─ 事实：&quot;项目是简历优化工具&quot;
│  ├─ 事实：&quot;目标用户是大学生&quot;
│  ├─ 约束：&quot;只做诊断，不做编辑&quot;
│  └─ 偏好：&quot;强调 10 秒快速&quot;
├─ L2（场景记忆）：
│  └─ 场景：&quot;简历优化项目&quot;
│      ├─ 关联记忆：上述 4 条
│      └─ 时间线：2026-06-12 第 1 次讨论
└─ L3（用户画像）：
    └─ &quot;用户偏好做减法、强调速度、注重用户体验&quot;

第 2 天对话：
你：&quot;继续优化简历诊断功能&quot;

AI 自动召回：
├─ 检索到场景：&quot;简历优化项目&quot;
├─ 加载 L1 记忆：项目背景、核心约束
└─ 直接回答：&quot;好的，我们继续优化诊断功能。
    根据之前的讨论，核心是 10 秒内给出 3 条最致命问题...&quot;

优势：
✓ 不用重复交代背景（省时间）
✓ 不用手动粘贴历史（省 Token）
✓ AI 能记住你的偏好和约束（保持一致性）
✓ 所有记忆可追溯到原始对话（100% 可验证）
</code></pre>]]></description>
      <author>xancel</author>
      <guid>article-10</guid>
      <pubDate>Fri, 12 Jun 2026 15:41:14 +0000</pubDate>
    </item>
    <item>
      <title>模型之外的关键：Harness工程体系</title>
      <link>https://xancel.top/posts/harness-engineering-guide</link>
      <description><![CDATA[<blockquote><p>该内容由 RSS 渲染生成，最佳阅读体验请前往：<a href="https://xancel.top/posts/harness-engineering-guide">https://xancel.top/posts/harness-engineering-guide</a></p></blockquote><p>::: link-card href=&quot;https://mp.weixin.qq.com/s/Pbhyg8PPHNzhWbMLI732eA&quot; title=&quot;Harness 完全指南：harness 是一切工作的核心（1.3万字）&quot; desc=&quot;本文内容摘取与李自然老师的文章，写的很好，推荐查看原文&quot; newtab=&quot;true&quot;</p>
<p>:::</p>
<h2>1. harness 是什么？</h2>
<p>Harness是模型之外的一整套运行环境。它决定AI能看到什么、能做什么、不能做什么、做完后如何验证、出错后如何修正、什么时候交还给人。</p>
<p><strong>核心公式</strong>：Agent = Model + Harness</p>
<ul>
<li><strong>Prompt</strong> 是你对AI说什么</li>
<li><strong>Context</strong> 是AI当下看见什么</li>
<li><strong>Harness</strong> 是AI被放进了一个什么样的行动系统</li>
</ul>
<p>模型是大脑。Harness是大脑之外的一切。</p>
<blockquote>
<p>同一个模型，放在不同harness里，可以是两个不同的智能体。
LangChain实验证明：同一个模型不变，只调整模型外面的运行环境，编码智能体在Terminal Bench 2.0上的得分从52.8跳到66.5——<strong>13分的差距，完全来自harness那一项</strong>。</p>
</blockquote>
<hr>
<h2>2. harness 解决了什么问题？如何解决？</h2>
<p><strong>问题</strong>：AI开始行动后，面对的不再是一个聊天框，而是一个带有不确定性的执行系统。</p>
<p><strong>核心目标</strong>：让AI第一次更可能做对；做错后更快知道自己错了。</p>
<p><strong>解决方式</strong>：</p>
<table>
<thead>
<tr>
<th>阶段</th>
<th>手段</th>
<th>具体内容</th>
</tr>
</thead>
<tbody>
<tr>
<td>行动前引导</td>
<td>前馈</td>
<td>AGENTS.md规则、目录结构、类型系统、权限设置——告诉AI该往哪走</td>
</tr>
<tr>
<td>行动后检查</td>
<td>反馈</td>
<td>测试、CI、linter、运行日志、代码review——告诉AI刚才那步有没有偏</td>
</tr>
</tbody>
</table>
<p><strong>本质</strong>：把非确定性的模型，放进一个尽量可控、可观测、可复盘的系统。</p>
<hr>
<h2>3. harness 的思想是什么？</h2>
<h3>核心理念</h3>
<ul>
<li>
<p><strong>围栏是给跑得快的马用的。跑不起来的马，不需要围栏。</strong></p>
<ul>
<li>早期模型跑不远、跑不稳，不需要harness。当模型能连续跑几小时、跨多个上下文窗口时，能犯的错误也变多了，这时harness才真正必要。</li>
</ul>
</li>
<li>
<p><strong>能用代码强制的，就不要写成「请记得」。</strong></p>
<ul>
<li>Hook会执行，自然语言会被忘。确定性骨架兜住的地方越多，留给模型自由发挥的空间就越精准。</li>
</ul>
</li>
<li>
<p><strong>补丁会消失。基础设施不会。</strong></p>
<ul>
<li>模型变强会吃掉「为弥补模型能力不足而存在的壳」（提示词注入防御、静态命令校验），但反馈层、治理层、上下文层不会消失，只会变得更值钱。</li>
</ul>
</li>
<li>
<p><strong>自由度只留给真正需要推理的地方。其他地方交给确定性系统。</strong></p>
<ul>
<li>智能体节点负责需要判断和生成的部分；确定性节点负责检查、提交、测试、拦截。</li>
</ul>
</li>
</ul>
<h3>底层逻辑</h3>
<blockquote>
<p>这是1948年维纳提出的控制论的核心——前馈给方向，反馈看偏差。AI没有让「控制」这件事过时，只是把它的对象从机器换成了一个比机器更不确定、行动空间更大的系统。</p>
</blockquote>
<hr>
<h2>4. harness 的构成是什么？</h2>
<p>Harness由六层构成：</p>
<h3>层级总览</h3>
<table>
<thead>
<tr>
<th>层级</th>
<th>名称</th>
<th>核心问题</th>
</tr>
</thead>
<tbody>
<tr>
<td>第1层</td>
<td>指令层</td>
<td>给AI一张地图，不是一本手册</td>
</tr>
<tr>
<td>第2层</td>
<td>上下文层</td>
<td>AI只能理解他看得见的东西</td>
</tr>
<tr>
<td>第3层</td>
<td>工具层</td>
<td>AI的手越多，越需要设计</td>
</tr>
<tr>
<td>第4层</td>
<td>边界层</td>
<td>先设计爆炸半径</td>
</tr>
<tr>
<td>第5层</td>
<td>反馈层</td>
<td>AI需要感受到现实</td>
</tr>
<tr>
<td>第6层</td>
<td>治理层</td>
<td>谁来管这些AI</td>
</tr>
</tbody>
</table>
<h3>各层详解</h3>
<p><strong>第1层：指令层</strong></p>
<ul>
<li>包括：系统提示词、项目规则文件、AGENTS.md、CLAUDE.md、skills</li>
<li>原则：每一条规则都对应一次智能体真实犯过的错，不是「最佳实践大全」，更像踩坑记录</li>
<li>OpenAI风格：告诉AI「应该做什么」</li>
<li>Anthropic风格：告诉AI「不能做什么」（更推荐——说清红线，剩下的留给模型发挥）</li>
</ul>
<p><strong>第2层：上下文层</strong></p>
<ul>
<li>问题：每次模型调用前，要决定塞哪些信息进上下文窗口</li>
<li>挑战：信息太少AI不知道情况，信息太多AI被噪音淹没</li>
<li>解法：结构化工作状态（如<code>claude-progress.txt</code>），让新会话像接班的工程师一样先读交接记录</li>
<li>子智能体的核心价值：隔离噪音——只把干净摘要交回主智能体</li>
<li>核心原则：<strong>不在上下文里的东西，对AI来说就不存在；不该进上下文的噪音，也会让AI做错判断</strong></li>
</ul>
<p><strong>第3层：工具层</strong></p>
<ul>
<li>包括：文件读写、命令行、Git、浏览器、数据库、内部API、MCP服务器等</li>
<li>反直觉：工具越多，系统提示越长，智能体越容易分心</li>
<li>解法：根据任务给AI一组相关工具，不是「以防万一」的全家桶</li>
<li>核心原则：<strong>工具不是越多越好，而是越清楚越好</strong></li>
</ul>
<p><strong>第4层：边界层</strong></p>
<ul>
<li>包括：权限、沙盒、网络隔离、文件系统隔离、密钥管理、审批规则</li>
<li>设计：拒绝优先（故障即安全）——命中拒绝规则就直接停下，不被其他允许规则覆盖</li>
<li>核心原则：<strong>爆炸半径要在事故发生前设计好，不要等事故发生后再补救</strong></li>
<li>反直觉：边界设计得越清楚，人类越敢把更多事情交给AI</li>
</ul>
<p><strong>第5层：反馈层</strong></p>
<ul>
<li>包括：测试、类型检查、linter、CI、运行日志、代码review、另一个模型的评估</li>
<li>发现：错误消息应该写得像给智能体看的修复提示（不只是「类型不匹配」，要说「应该用 <code>string[]</code>，不是 <code>string</code>」）</li>
<li>核心原则：<strong>代码质量基础设施，在智能体时代不是锦上添花。它是AI的传感器。</strong></li>
<li>没有反馈，AI只能猜。有反馈，他才能修正。</li>
</ul>
<p><strong>第6层：治理层</strong></p>
<ul>
<li>包括：成本监控、运行日志、审计记录、人工升级、失败复盘、组织级模板、回归评估</li>
<li>核心问题：
<ul>
<li>每个任务最多允许重试几次？</li>
<li>什么情况必须交还给人？</li>
<li>哪些工具调用要记录？</li>
<li>哪些权限需要审批？</li>
<li>一次harness改动之后，如何确认没有造成退化？</li>
</ul>
</li>
<li>核心原则：<strong>harness不是一个人的prompt文件。它会慢慢变成组织资产。</strong></li>
</ul>
<hr>
<h2>5. 如何构建自己的 harness？</h2>
<h3>最小 harness：四件套</h3>
<pre><code>project-root/
├── AGENTS.md / CLAUDE.md  规则
├── init.sh                启动入口
├── feature_list.json      任务状态
├── progress.md            进度日志
└── src/                   代码
</code></pre>
<table>
<thead>
<tr>
<th>文件</th>
<th>职责</th>
</tr>
</thead>
<tbody>
<tr>
<td>AGENTS.md/CLAUDE.md</td>
<td>管规则——技术栈、必须跑的命令、不能碰的地方、完成标准</td>
</tr>
<tr>
<td>init.sh</td>
<td>管开工前环境检查——安装依赖、检查环境、跑基础验证、确认项目能启动</td>
</tr>
<tr>
<td>feature_list.json</td>
<td>管任务状态——状态机：not_started/in_progress/blocked/passing，同一时间只有一个任务处于in_progress</td>
</tr>
<tr>
<td>progress.md</td>
<td>管跨会话记忆——上一轮做了什么、哪些检查通过、哪里还没验证、下轮从哪里继续</td>
</tr>
</tbody>
</table>
<p><strong>核心原则</strong>：</p>
<ul>
<li>规则文件控制在50-80行以内，不要一上来写成200行</li>
<li>一条规则最好对应一次真实失败</li>
<li>不要让AI在坏环境里工作</li>
<li>每次会话结束前做干净收尾，不要让下一轮AI替上一轮收拾残局</li>
</ul>
<h3>三层成熟度</h3>
<p><strong>Level 1：个人级</strong></p>
<ul>
<li>适合独立开发者</li>
<li>核心：四件套 + 常用命令 + 最小反馈循环 + 失败日志 + 干净收尾</li>
<li>目标：让AI少犯重复错误，每次会话都能稳定接上</li>
</ul>
<p><strong>Level 2：团队级</strong></p>
<ul>
<li>适合3到20人团队</li>
<li>核心：共享规则 + 统一目录结构 + CI必跑项 + PR模板 + 共享脚本 + 共享子智能体</li>
<li>原则：把团队共识变成AI也能执行的工程环境。以前靠口头约定和代码review传递的东西，现在要尽量变成规则、脚本、模板和测试</li>
</ul>
<p><strong>Level 3：组织级</strong></p>
<ul>
<li>适合大公司或生产级智能体</li>
<li>核心：工具注册中心 + 权限系统 + 沙盒环境 + 运行日志 + 成本监控 + 人工升级 + 回归评估</li>
<li>目标：让智能体成为组织基础设施的一部分</li>
</ul>
<h3>验证方法：对照实验</h3>
<p>做两轮对比：</p>
<ol>
<li><strong>第一轮</strong>：只给普通prompt，不给规则文件、不跑初始化、不准备功能状态表。记录AI花了多久、漏了什么、最后你花了多少时间收拾。</li>
<li><strong>第二轮</strong>：给最小四件套，按固定会话流程工作。</li>
</ol>
<p>比较指标：</p>
<ul>
<li>是否只做了一个功能，没有乱扩范围？</li>
<li>是否跑了必要验证？</li>
<li>是否留下了清楚的进度记录？</li>
<li>是否减少了重复解释和返工？</li>
<li>下一轮会话能不能直接接上？</li>
</ul>
<h3>最终原则</h3>
<blockquote>
<p><strong>Harness不是设计出来的，是迭代出来的。从简单开始，让AI跑，看他在哪里摔，再在那里加护栏。每一次真实失败，都是下一版harness的材料。</strong></p>
</blockquote>
<hr>
<h2>6. 为什么需要 harness？</h2>
<h3>直接原因</h3>
<p>当模型能连续跑几小时、跨多个上下文窗口接力完成任务时——他能创造的价值变大了，能犯的错误也变多了。</p>
<p>真正的问题不再是「让他更聪明」，而是：</p>
<ul>
<li>他应该看见什么？</li>
<li>他能调用什么工具？</li>
<li>他不能碰什么？</li>
<li>他做完之后怎么知道自己错没错？</li>
<li>他连续失败几次之后，什么时候该交还给人？</li>
</ul>
<p>这些问题加在一起，就是harness engineering。</p>
<h3>深层原因</h3>
<table>
<thead>
<tr>
<th>视角</th>
<th>结论</th>
</tr>
</thead>
<tbody>
<tr>
<td>个人</td>
<td>会写代码正在从核心技能，变成基础素养。下一个时代需要的能力，是为AI设计能稳定执行的环境</td>
</tr>
<tr>
<td>团队</td>
<td>智能体吃掉的是这个组织已经建好的工程基础设施。没有测试、清楚文档、稳定CI、标准化环境，买再多AI账号也很难跑出效果</td>
</tr>
<tr>
<td>行业</td>
<td>模型这一层会越来越普及。但模型外面的工程基础设施，不会自动普及。这会让组织之间的差距变大</td>
</tr>
</tbody>
</table>
<h3>核心判断</h3>
<blockquote>
<p><strong>Harness将是一切工作的核心。</strong></p>
<ul>
<li>工程化成熟的团队，AI会放大它的能力</li>
<li>工程化混乱的团队，AI也会放大它的混乱</li>
</ul>
</blockquote>
<h3>补充视角：Cherny vs Osmani 的争论</h3>
<table>
<thead>
<tr>
<th>人物</th>
<th>观点</th>
</tr>
</thead>
<tbody>
<tr>
<td>Boris Cherny (Anthropic Claude Code负责人)</td>
<td>harness会消失——模型变强后，提示词注入防御、静态命令校验等「弥补模型缺陷的壳」会被吸收</td>
</tr>
<tr>
<td>Addy Osmani (Google Chrome团队)</td>
<td>摩擦没有消失，只是搬家了——从写代码搬到了review和verify。高AI渗透团队PR合并量翻了98%，但review时间膨胀了91%</td>
</tr>
</tbody>
</table>
<p><strong>最终结论</strong>：补丁层会被模型吸收。基础层会变得更重要。Harness不会消失，它在演化。</p>]]></description>
      <author>xancel</author>
      <guid>article-9</guid>
      <pubDate>Tue, 19 May 2026 08:05:40 +0000</pubDate>
    </item>
    <item>
      <title>最近真的蛮糟糕的，好像有一种失去了对生活的掌控感，倒不是有外界对我的打破，而是我...</title>
      <link>https://xancel.top/thinkings#thinking-13</link>
      <description><![CDATA[<blockquote><p>该内容由 RSS 渲染生成，最佳阅读体验请前往：<a href="https://xancel.top/thinkings#thinking-13">https://xancel.top/thinkings#thinking-13</a></p></blockquote><p>最近真的蛮糟糕的，好像有一种失去了对生活的掌控感，倒不是有外界对我的打破，而是我总是情不自禁的进入到一种失去秩序的循环中，日复一日，我好像真的需要开始练习冥想了。</p>]]></description>
      <author>xancel</author>
      <guid>thinking-13</guid>
      <pubDate>Sun, 17 May 2026 11:52:02 +0000</pubDate>
    </item>
    <item>
      <title>ClaudeCode Desktop安装与 CC Switch 模型切换教程</title>
      <link>https://xancel.top/posts/claude-desktop-install-guide</link>
      <description><![CDATA[<blockquote><p>该内容由 RSS 渲染生成，最佳阅读体验请前往：<a href="https://xancel.top/posts/claude-desktop-install-guide">https://xancel.top/posts/claude-desktop-install-guide</a></p></blockquote><h2>1.前置准备安装包</h2>
<p>国内如果非指纹浏览器非严格网络环境很难在官网下载安装包，我这里上传到网盘里了，可以直接下载：
链接：https://pan.quark.cn/s/6bde2ce50085?pwd=Z2BP
提取码：Z2BP</p>
<h2>2.安装配置（环境最好是全局+TUN模式）</h2>
<p>安装完之后</p>
<ol>
<li>双击打开，不要登录，如图所示，进入开发者模式
<img src="/uploads/pictures/2026-05-14-15:48:58-3e.png" alt="image.png"></li>
<li>Developer → Configure third-party inference
<img src="/uploads/pictures/2026-05-14-15:49:54-bd.png" alt="image.png"></li>
<li>配置中转地址和 key，以DeepSeek为例：选择 Gateway，填写Gateway base URL为：https://api.deepseek.com/anthropic ，Gateway API key 填写你的 DeepSeek API key，模型根据你的选择来，可以选择DeepSeek-V4-Flash
<img src="/uploads/pictures/2026-05-14-15:52:36-3f.png" alt="image.png"></li>
</ol>
<h2>3.使用CC Switch任意切换模型</h2>
<p>其实上面就可以愉快使用了，但是为了更方便的，我们使用一下CC Switch来随意切换提供商和模型配置</p>
<h3>3.1 CC Switch设置中开启路由，如图</h3>
<p><img src="/uploads/pictures/2026-05-14-16:04:35-ad.png" alt="image.png"></p>
<h3>3.2配置本地url和key</h3>
<p>进入之前进入过的第三方提供商设置，重新填写一下url和key。</p>
<p>url为CC Switch中显示的服务地址，复制即可，例如我的是：http://127.0.0.1:15721</p>
<p>key填写PROXY_MANAGED即可</p>
<p><img src="/uploads/pictures/2026-05-14-16:05:58-90.png" alt="image.png"></p>
<h3>3.3 添加映射模型名称，为了设置思考强度</h3>
<p>往下滚动翻阅，找到 IDENTITY &amp; MODELS 的 Model llst 部分
添加并设置三个模型：</p>
<p>claude-sonnet-4-6<br>
claude-haiku-4-5<br>
claude-opus-4-7</p>
<p><img src="/uploads/pictures/2026-05-14-16:11:28-97.png" alt="image.png">
改完后保存，选择 Apply locally，等待重启即可</p>]]></description>
      <author>xancel</author>
      <guid>article-8</guid>
      <pubDate>Thu, 14 May 2026 16:13:45 +0000</pubDate>
    </item>
    <item>
      <title>开往</title>
      <link>https://xancel.top/go</link>
      <description><![CDATA[<blockquote><p>该内容由 RSS 渲染生成，最佳阅读体验请前往：<a href="https://xancel.top/go">https://xancel.top/go</a></p></blockquote><p><a href="https://www.travellings.cn/go.html">https://www.travellings.cn/go.html</a></p>]]></description>
      <guid>page-10</guid>
      <pubDate>Wed, 15 Apr 2026 12:01:27 +0000</pubDate>
    </item>
    <item>
      <title>完美的日子</title>
      <link>https://xancel.top/moments/2026/04/11/perfect-days</link>
      <description><![CDATA[<blockquote><p>该内容由 RSS 渲染生成，最佳阅读体验请前往：<a href="https://xancel.top/moments/2026/04/11/perfect-days">https://xancel.top/moments/2026/04/11/perfect-days</a></p></blockquote><p>2026.04.11</p>
<p>这是一部好好电影，值得看很多次，因为一次感觉有些意犹未尽，好像品出来些什么，但没有“成品”。</p>
<p>首先，这部电影很对我的审美，每一帧都很清新，整洁，有条不紊，其要表达的内涵我也能get到一点，但想要具体用语言描述清楚，也许我还得再看几遍，也许我还得再在生活中历练几段。</p>
<p>所以就我感悟到一些碎片，支离破碎的写一些，希望日后能将其拼凑完整。</p>
<p>1.主角对待生活的态度是我很喜欢，并且心底里隐约向往的，电影里提到的一句“以后是以后，现在是现在”，这句话我认为想表达的就是“活在当下”，也让我联想到《一句顶一万句》里面的“日子是过以后，不是过从前”。</p>
<p>2.主角是享受工作的吗？答案我认为是否。打扫厕所这件事对主角来说其实只是维持生活秩序的手段，从影片中可以看得出来，主角其实是不图钱不图名甚至不图感情（让其自然发生），这很接近我的“本我”，工作是生活中附带的一笔，它像是缝补衣服的麻线，让一些缺口规整起来。</p>
<p>3.那主角在体验或者说追求的是什么？从剧情中他的姐/妹可以看得出来他本来的物质条件是很不错的，住在乡下，独居，做厕所清洁工，这一切必然是他自己的选择。在这些选择背后，我认为主角追求的是“心灵的自由与平静”，他有善良的内心和善于发现美的眼睛，在生活中他凭借这两点就足以获得自己的追求。</p>
<p>无论今天有什么样的情绪波动，发生了什么事，总是按时睡前看书，到点睡觉，准时起床（加班除外哈哈哈），能做到这一点真的难得，主角不是圣人，他会哭，会笑，或者一边哭一边笑；也许他有自己的遗憾或者烦恼，也许他有自己内心的挣扎或者彷徨，但是这一切都不会阻碍：明早我要给我的小树浇水，出门前要买一罐咖啡，路上要听我喜欢的磁带，别忘了和陌生人的井字格游戏，别忘了给大树朋友拍照，别忘了晚上去喝一杯。</p>
<p>最后，生活充满变数，充满意外，但是也充满日升日落，晴天雨天，可能不确定中的一些确定能够让我们更好的面对这一切，加油。</p>]]></description>
      <author>xancel</author>
      <guid>moment-3</guid>
      <pubDate>Sat, 11 Apr 2026 16:13:44 +0000</pubDate>
    </item>
    <item>
      <title>稳住心态，保证睡眠，坚持锻炼，加油</title>
      <link>https://xancel.top/thinkings#thinking-12</link>
      <description><![CDATA[<blockquote><p>该内容由 RSS 渲染生成，最佳阅读体验请前往：<a href="https://xancel.top/thinkings#thinking-12">https://xancel.top/thinkings#thinking-12</a></p></blockquote><p>稳住心态，保证睡眠，坚持锻炼，加油</p>]]></description>
      <author>xancel</author>
      <guid>thinking-12</guid>
      <pubDate>Wed, 01 Apr 2026 12:50:36 +0000</pubDate>
    </item>
    <item>
      <title>要把决定做对，而不是做对的决定</title>
      <link>https://xancel.top/thinkings#thinking-11</link>
      <description><![CDATA[<blockquote><p>该内容由 RSS 渲染生成，最佳阅读体验请前往：<a href="https://xancel.top/thinkings#thinking-11">https://xancel.top/thinkings#thinking-11</a></p></blockquote><p>要把决定做对，而不是做对的决定</p>]]></description>
      <author>xancel</author>
      <guid>thinking-11</guid>
      <pubDate>Sun, 29 Mar 2026 08:12:30 +0000</pubDate>
    </item>
    <item>
      <title>平常心</title>
      <link>https://xancel.top/thinkings#thinking-10</link>
      <description><![CDATA[<blockquote><p>该内容由 RSS 渲染生成，最佳阅读体验请前往：<a href="https://xancel.top/thinkings#thinking-10">https://xancel.top/thinkings#thinking-10</a></p></blockquote><p>平常心</p>]]></description>
      <author>xancel</author>
      <guid>thinking-10</guid>
      <pubDate>Wed, 25 Mar 2026 12:08:47 +0000</pubDate>
    </item>
    <item>
      <title>Spring MVC，我是怎么理解的</title>
      <link>https://xancel.top/posts/spring-mvc</link>
      <description><![CDATA[<blockquote><p>该内容由 RSS 渲染生成，最佳阅读体验请前往：<a href="https://xancel.top/posts/spring-mvc">https://xancel.top/posts/spring-mvc</a></p></blockquote><p>我一直感觉SpringMVC的执行过程有些抽象，有些概念有点绕，于是我想着先把很多概念了解清楚，最后再把流程给捋清楚，尝试之后对我的理解还是很有效的，这篇文章经过我的整理输出后由AI润色和修正了格式等内容</p>
<h2>1. 什么是 Spring MVC</h2>
<p>Spring MVC 是 Spring 体系中的 Web MVC 框架，用来帮助我们基于 Servlet 快速开发 Web 应用。</p>
<p>这里的 MVC 指的是：</p>
<ul>
<li><code>Model</code>：模型数据，或者说业务数据</li>
<li><code>View</code>：视图，负责展示页面</li>
<li><code>Controller</code>：控制器，负责接收请求、组织调用、返回结果</li>
</ul>
<p>它的核心思想是把请求接入、参数处理、业务调用、结果响应这些职责拆开，让代码结构更清晰。</p>
<p>需要注意一点：<code>Controller</code> 负责的是请求入口和流程协调，真正的业务逻辑通常应该放在 <code>Service</code> 层，而不是全部堆在 <code>Controller</code> 里。</p>
<h2>2. 什么是 Servlet</h2>
<p>Servlet 本质上是 Java Web 的一套规范，用来定义服务器如何接收 HTTP 请求、如何处理请求、如何返回 HTTP 响应。</p>
<p>像 Tomcat、Jetty 这样的 Web 容器会实现这套规范，并负责：</p>
<ul>
<li>加载 Servlet</li>
<li>管理 Servlet 生命周期</li>
<li>在请求到来时调用对应的 Servlet</li>
</ul>
<p>可以把它理解成：Servlet 提供了 Java Web 最基础的请求处理能力，而 Spring MVC 是构建在这套能力之上的更高层封装。</p>
<h2>3. Spring MVC 和 Servlet 是什么关系</h2>
<p>Spring MVC 是建立在 Servlet 规范之上的。</p>
<p>它的核心入口是 <code>DispatcherServlet</code>。<code>DispatcherServlet</code> 本身就是一个 Servlet，所以它可以像普通 Servlet 一样被 Web 容器加载和调用。</p>
<p>也就是说：</p>
<ul>
<li>Web 容器负责把请求交给 <code>DispatcherServlet</code></li>
<li><code>DispatcherServlet</code> 负责在 Spring MVC 内部完成后续流程</li>
</ul>
<p>后续这些流程包括：</p>
<ul>
<li>路由分发</li>
<li>查找处理器</li>
<li>参数绑定</li>
<li>调用控制器方法</li>
<li>处理返回值</li>
<li>视图解析或响应体输出</li>
</ul>
<h2>4. Spring MVC 的几个核心组件</h2>
<h3>4.1 DispatcherServlet 是什么</h3>
<p><code>DispatcherServlet</code> 是 Spring MVC 的核心调度器，也是整个请求处理流程的统一入口。</p>
<p>浏览器发来的请求，通常会先到它这里，再由它决定后续交给谁处理。</p>
<p>它的职责可以概括为：</p>
<ul>
<li>接收请求</li>
<li>分发请求</li>
<li>协调各个组件一起工作</li>
<li>最终生成响应</li>
</ul>
<h3>4.2 HandlerMapping 是什么</h3>
<p><code>HandlerMapping</code> 可以理解成“请求路径到处理器的映射表”。</p>
<p>当请求到来时，它会根据请求信息去找到对应的处理器。匹配条件通常包括：</p>
<ul>
<li>URL</li>
<li>HTTP 请求方法</li>
<li>其他请求条件</li>
</ul>
<p>在注解驱动开发里，Spring 启动时会解析诸如 <code>@RequestMapping</code>、<code>@GetMapping</code>、<code>@PostMapping</code> 这类映射信息，并建立对应关系。</p>
<h3>4.3 Controller 和处理器是什么关系</h3>
<p>在日常开发里，我们会写很多 <code>@Controller</code> 或 <code>@RestController</code> 类。</p>
<p>对于注解驱动的 Spring MVC 来说，真正执行请求处理逻辑的，通常是 <code>Controller</code> 中某一个具体的方法。这个方法通常就可以理解为当前请求对应的处理器。</p>
<p>也就是说：</p>
<ul>
<li><code>Controller</code> 是处理请求的载体</li>
<li>具体的处理器，通常是其中某个带映射注解的方法</li>
</ul>
<p>所以 <code>HandlerMapping</code> 最终定位到的，通常不是整个类，而是类中的某个方法。</p>
<h3>4.4 HandlerAdapter 是什么</h3>
<p><code>HandlerAdapter</code> 可以理解成“处理器执行适配器”。</p>
<p><code>DispatcherServlet</code> 找到处理器之后，并不是自己直接用反射去调用，而是交给合适的 <code>HandlerAdapter</code> 来执行。</p>
<p>它主要负责：</p>
<ul>
<li>参数解析与绑定</li>
<li>类型转换</li>
<li>调用目标方法</li>
<li>处理方法返回值</li>
</ul>
<p>在基于注解的 Spring MVC 中，最常见的是 <code>RequestMappingHandlerAdapter</code>。</p>
<h3>4.5 ViewResolver 是什么</h3>
<p><code>ViewResolver</code> 是视图解析器。</p>
<p>当控制器返回的是一个“视图名”时，Spring MVC 会通过 <code>ViewResolver</code> 把这个名字解析成真正的视图对象，比如 JSP 或 Thymeleaf 模板。</p>
<h3>4.6 HttpMessageConverter 是什么</h3>
<p><code>HttpMessageConverter</code> 是消息转换器。</p>
<p>当前后端分离接口返回的是对象、JSON、XML 等响应体数据时，Spring MVC 会通过它把 Java 对象转换成 HTTP 响应内容。</p>
<p>最常见的场景就是把 Java 对象转换为 JSON。</p>
<h2>5. Controller 执行完之后，会返回什么</h2>
<p>这部分要分成两种场景来看。</p>
<h3>5.1 传统服务端页面渲染</h3>
<p>如果是传统 Web 项目，控制器方法通常可能返回：</p>
<ul>
<li>视图名，比如 <code>&quot;userList&quot;</code></li>
<li><code>ModelAndView</code></li>
</ul>
<p>这时 Spring MVC 的处理思路是：</p>
<ol>
<li>控制器方法执行完成</li>
<li>得到视图相关结果</li>
<li>交给 <code>ViewResolver</code> 解析视图</li>
<li>由视图技术渲染页面</li>
<li>最终把 HTML 返回给浏览器</li>
</ol>
<h3>5.2 前后端分离接口返回</h3>
<p>如果是前后端分离项目，控制器方法通常返回：</p>
<ul>
<li>一个普通对象</li>
<li><code>ResponseEntity</code></li>
<li>标注了 <code>@ResponseBody</code> 的返回结果</li>
</ul>
<p>如果类上使用了 <code>@RestController</code>，本质上也等价于默认带有 <code>@ResponseBody</code> 语义。</p>
<p>这时流程就不是“返回视图”，而是“直接返回数据”。</p>
<p>更准确地说，这类返回值会由 Spring MVC 的返回值处理机制配合 <code>HttpMessageConverter</code> 写入响应体，而不是再走视图解析。</p>
<p>所以这里不要简单理解成“HandlerAdapter 返回了 <code>null</code>”，更准确的理解应该是：</p>
<ul>
<li>控制器方法返回的是数据</li>
<li>Spring MVC 把这些数据写进 HTTP 响应体</li>
<li><code>DispatcherServlet</code> 不再进行视图渲染</li>
</ul>
<h2>6. 一次请求在 Spring MVC 中是怎么流转的</h2>
<p>把前面的内容串起来，一次典型请求大致会经过下面这些步骤。</p>
<ol>
<li>浏览器发起 HTTP 请求。</li>
<li>Web 容器接收到请求后，把请求交给 <code>DispatcherServlet</code>。</li>
<li><code>DispatcherServlet</code> 调用 <code>HandlerMapping</code> 查找当前请求对应的处理器。</li>
<li>找到处理器后，<code>DispatcherServlet</code> 再交给合适的 <code>HandlerAdapter</code> 去执行。</li>
<li><code>HandlerAdapter</code> 完成参数绑定、类型转换，然后调用目标控制器方法。</li>
<li>控制器方法执行业务流程，并返回结果。</li>
<li>如果返回的是视图名或 <code>ModelAndView</code>，则进入视图解析和页面渲染流程。</li>
<li>如果返回的是对象或 <code>ResponseEntity</code>，则通过 <code>HttpMessageConverter</code> 直接写入响应体。</li>
<li>最终响应返回给客户端。</li>
</ol>
<p>为了先把主线讲清楚，这里暂时没有展开拦截器、异常处理器、文件上传解析器这些扩展机制，但它们本质上也都是围绕这条主流程工作的。</p>
<h2>7. 几个容易混淆的点</h2>
<h3>7.1 Controller 不等于业务逻辑</h3>
<p><code>Controller</code> 的主要职责是接收请求、做参数承接、组织调用和返回结果。</p>
<p>真正的业务逻辑通常应该放在 <code>Service</code> 层。这样分层才清晰，也更容易维护。</p>
<h3>7.2 DispatcherServlet 不是“一个类包办一切”</h3>
<p>虽然 <code>DispatcherServlet</code> 是统一入口，但它并不是自己完成所有工作。</p>
<p>它更像一个总调度器，会把不同阶段的工作分别交给：</p>
<ul>
<li><code>HandlerMapping</code></li>
<li><code>HandlerAdapter</code></li>
<li><code>ViewResolver</code></li>
<li><code>HttpMessageConverter</code></li>
</ul>
<h3>7.3 前后端分离场景下，通常不会再走视图解析</h3>
<p>如果控制器返回的是 JSON 数据，那么重点就不再是 <code>ViewResolver</code>，而是返回值处理机制和 <code>HttpMessageConverter</code>。</p>
<p>也就是说，前后端分离项目和传统服务端页面渲染项目，后半段流程是不一样的。</p>]]></description>
      <author>xancel</author>
      <guid>article-7</guid>
      <pubDate>Tue, 24 Mar 2026 13:02:26 +0000</pubDate>
    </item>
    <item>
      <title>AI时代，如何放大凸显自己的长处，变得更加重要</title>
      <link>https://xancel.top/thinkings#thinking-9</link>
      <description><![CDATA[<blockquote><p>该内容由 RSS 渲染生成，最佳阅读体验请前往：<a href="https://xancel.top/thinkings#thinking-9">https://xancel.top/thinkings#thinking-9</a></p></blockquote><p>AI时代，如何放大凸显自己的长处，变得更加重要</p>]]></description>
      <author>xancel</author>
      <guid>thinking-9</guid>
      <pubDate>Fri, 20 Mar 2026 10:48:06 +0000</pubDate>
    </item>
    <item>
      <title>有时候感觉有那种摇滚精神，反叛精神是十分十分可贵的，那种燥的气息仿佛是生命力的外...</title>
      <link>https://xancel.top/thinkings#thinking-8</link>
      <description><![CDATA[<blockquote><p>该内容由 RSS 渲染生成，最佳阅读体验请前往：<a href="https://xancel.top/thinkings#thinking-8">https://xancel.top/thinkings#thinking-8</a></p></blockquote><p>有时候感觉有那种摇滚精神，反叛精神是十分十分可贵的，那种燥的气息仿佛是生命力的外显，怀念，昔日我曾风华正茂，如今我正苍老。</p>]]></description>
      <author>xancel</author>
      <guid>thinking-8</guid>
      <pubDate>Thu, 19 Mar 2026 16:40:12 +0000</pubDate>
    </item>
    <item>
      <title>AI 时代说个小暴论，越是在这种鼓吹人人都可以创造的时代，越要静下心来做一些长期...</title>
      <link>https://xancel.top/thinkings#thinking-6</link>
      <description><![CDATA[<blockquote><p>该内容由 RSS 渲染生成，最佳阅读体验请前往：<a href="https://xancel.top/thinkings#thinking-6">https://xancel.top/thinkings#thinking-6</a></p></blockquote><p>AI 时代说个小暴论，越是在这种鼓吹人人都可以创造的时代，越要静下心来做一些长期主义的事情。LLM 是一把锋利的刀，你买了这把刀，别人也可以买，这并不能带来什么本质性的差距。所以还是要沉下来，做一些长期主义、有积累的事情。</p>
<!-- raw HTML omitted -->]]></description>
      <author>xancel</author>
      <guid>thinking-6</guid>
      <pubDate>Tue, 17 Mar 2026 15:46:53 +0000</pubDate>
    </item>
    <item>
      <title>死亡是每个人必修的课题&#xA; &lt;p align=&#34;right&#34;&gt;—— 读《当呼吸化为...</title>
      <link>https://xancel.top/thinkings#thinking-5</link>
      <description><![CDATA[<blockquote><p>该内容由 RSS 渲染生成，最佳阅读体验请前往：<a href="https://xancel.top/thinkings#thinking-5">https://xancel.top/thinkings#thinking-5</a></p></blockquote><p>死亡是每个人必修的课题</p>
<!-- raw HTML omitted -->]]></description>
      <author>xancel</author>
      <guid>thinking-5</guid>
      <pubDate>Tue, 17 Mar 2026 12:31:36 +0000</pubDate>
    </item>
    <item>
      <title>Elasticsearch 入门</title>
      <link>https://xancel.top/posts/elasticsearch-guide</link>
      <description><![CDATA[<blockquote><p>该内容由 RSS 渲染生成，最佳阅读体验请前往：<a href="https://xancel.top/posts/elasticsearch-guide">https://xancel.top/posts/elasticsearch-guide</a></p></blockquote><h1>1.基础学习</h1>
<h2>一、 什么是 Elasticsearch？</h2>
<p><strong>Elasticsearch（简称 ES）</strong> 是一个基于 Lucene 的开源、分布式、RESTful 搜索引擎。它不仅是一个数据库，更是为了解决传统关系型数据库（如 MySQL）在<strong>全文检索</strong>、<strong>海量数据搜索</strong>和<strong>实时分析</strong>方面的不足而设计的。</p>
<h3>为什么不用 MySQL 的 LIKE 查询？</h3>
<ul>
<li><strong>性能差</strong>：<code>LIKE '%关键词%'</code> 无法利用索引，数据量大时查询极慢。</li>
<li><strong>结果不精准</strong>：无法根据相关性进行排序（比如“最匹配”的内容排在前面）。</li>
<li><strong>缺乏分词能力</strong>：难以处理复杂的自然语言搜索。</li>
</ul>
<hr>
<h2>二、 核心概念与实战操作</h2>
<h3>1. 核心术语对照</h3>
<table>
<thead>
<tr>
<th>ES 概念</th>
<th>MySQL 对应概念</th>
<th>说明</th>
</tr>
</thead>
<tbody>
<tr>
<td><strong>Index (索引)</strong></td>
<td>Table (表)</td>
<td>存储数据的容器。</td>
</tr>
<tr>
<td><strong>Mapping (映射)</strong></td>
<td>Schema (表结构)</td>
<td>定义字段类型、分词规则等。</td>
</tr>
<tr>
<td><strong>Document (文档)</strong></td>
<td>Row (行)</td>
<td>以 JSON 格式存储的具体数据。</td>
</tr>
</tbody>
</table>
<h3>2. 基础操作（DSL 语法）</h3>
<p>ES 使用 JSON 格式的 <strong>DSL</strong>（领域特定语言）进行交互，建议配合可视化工具 <strong>Kibana</strong> 使用。</p>
<ul>
<li>
<p><strong>创建索引</strong>：需指定字段类型，也就是映射mapping。<code>text</code> 用于全文检索（会分词），<code>keyword</code> 用于精确匹配（不分词）。
<!-- raw HTML omitted --></p>
</li>
<li>
<p><strong>插入数据</strong>：直接发送 POST 请求提交 JSON 文档。</p>
</li>
</ul>
<!-- raw HTML omitted -->
<ul>
<li><strong>搜索数据</strong>：使用 <code>match</code> 进行全文搜索，ES 会自动分词并计算匹配度。</li>
</ul>
<!-- raw HTML omitted -->
<h3>3. Java 客户端开发</h3>
<p>在生产中，通常使用 <strong>Spring Data Elasticsearch</strong>。它允许开发者通过定义接口（继承 <code>ElasticsearchRepository</code>）并使用注解（如 <code>@Document</code>）来操作 ES，就像使用 MyBatis-Plus 一样简单。后文会有代码示例</p>
<hr>
<h2>三、 ES 为什么这么快？（核心特性）</h2>
<h3>1. 倒排索引 (Inverted Index)</h3>
<p>这是 ES 的灵魂。它不直接存整行数据，而是记录<strong>单词 -&gt; 文档 ID 列表</strong>的映射。搜索时，系统先对搜索词进行分词，然后通过倒排索引直接定位包含这些词的文档，无需全表扫描。</p>
<h3>2. 分词器 (Analyzer)</h3>
<ul>
<li><strong>标准分词器</strong>：对中文支持较差（按字拆分）。</li>
<li><strong>IK 分词器</strong>：中文搜索必备，提供 <code>ik_smart</code>（智能切分）和 <code>ik_max_word</code>（最细粒度切分）模式。
<!-- raw HTML omitted --></li>
</ul>
<h3>3. 相关性评分</h3>
<p>ES 默认采用 <strong>BM25 算法</strong>，通过<strong>词频（TF）</strong>、<strong>逆文档频率（IDF）<strong><strong>和</strong></strong>文档长度</strong>来计算分数（_score），确保用户最想要的结果排在最前面。
<!-- raw HTML omitted --></p>
<hr>
<h2>四、 生产环境的挑战与方案</h2>
<h3>1. 数据同步</h3>
<p>由于 ES 和 MySQL 是独立系统，需要解决数据一致性问题：</p>
<ul>
<li><strong>定时任务</strong>：适合实时性要求不高的场景。</li>
<li><strong>双写</strong>：代码层面同时写入两端，但需处理写入失败。</li>
<li><strong>Canal 监听</strong>：实时监听 MySQL 的 Binlog 变更并同步，延迟低。</li>
</ul>
<h3>2. 高可用与扩展</h3>
<ul>
<li><strong>集群部署</strong>：通过多节点（主节点、数据节点）保证服务不宕机。</li>
<li><strong>分片 (Shard)</strong>：将大索引拆分存放在不同节点，提升存储上限和并发能力。</li>
<li><strong>副本 (Replica)</strong>：分片的备份，防止节点损坏导致数据丢失。</li>
</ul>
<h3>3. 常见进阶问题</h3>
<ul>
<li><strong>深度分页</strong>：ES 默认限制查询前 10,000 条。深层次翻页建议使用 <code>search_after</code>。</li>
<li><strong>ELK 生态</strong>：Elasticsearch (存储) + Logstash (收集) + Kibana (展示)，是目前主流的日志分析方案。</li>
</ul>
<hr>
<p><strong>建议</strong>：学习 ES 最好的方式是动手实践，先在本地搭建一个单机版环境，尝试通过 Kibana 练习各种 DSL 查询。</p>
<h1>2.代码实现</h1>
<p>我们通常会选择在 Spring Boot 项目中集成 <strong>Spring Data Elasticsearch</strong>。这是目前 Java 后端最主流、也是最高效的开发方式。</p>
<h2>1. 环境准备与配置</h2>
<p>首先，在项目的 <code>pom.xml</code> 中引入依赖：</p>
<pre><code class="language-xml">&lt;dependency&gt;
    &lt;groupId&gt;org.springframework.boot&lt;/groupId&gt;
    &lt;artifactId&gt;spring-boot-starter-data-elasticsearch&lt;/artifactId&gt;
&lt;/dependency&gt;

</code></pre>
<p>在 <code>application.yml</code> 中配置 ES 服务器地址：</p>
<pre><code class="language-yaml">spring:
  elasticsearch:
    uris: http://localhost:9200

</code></pre>
<hr>
<h2>2. 定义实体类（Mapping 映射）</h2>
<p>通过注解将 Java 对象映射为 ES 的索引结构。<code>text</code> 类型用于搜索，<code>keyword</code> 用于精确过滤。</p>
<pre><code class="language-java">@Data
@Document(indexName = &quot;article&quot;) // 指定索引名称
public class Article {
    @Id
    private Long id;

    // type = Text 表示支持全文检索，analyzer 指定分词器
    @Field(type = FieldType.Text, analyzer = &quot;ik_max_word&quot;, searchAnalyzer = &quot;ik_smart&quot;)
    private String title;

    @Field(type = FieldType.Text, analyzer = &quot;ik_max_word&quot;)
    private String content;

    // type = Keyword 表示不分词，直接匹配
    @Field(type = FieldType.Keyword)
    private String category;

    @Field(type = FieldType.Date, format = DateFormat.basic_date_time)
    private LocalDateTime createTime;
}

</code></pre>
<hr>
<h2>3. 编写 Repository 接口</h2>
<p>这是 Spring Data 的精髓，你只需要定义方法名，框架会自动帮你生成 DSL 查询语句。</p>
<pre><code class="language-java">public interface ArticleRepository extends ElasticsearchRepository&lt;Article, Long&gt; {
    
    // 自动解析：根据标题全文检索
    List&lt;Article&gt; findByTitle(String title);
    
    // 自动解析：根据分类精确匹配
    List&lt;Article&gt; findByCategory(String category);
    
    // 复合查询：标题匹配且分类一致
    List&lt;Article&gt; findByTitleAndCategory(String title, String category);
}

</code></pre>
<hr>
<h2>4. 高级搜索：使用 ElasticsearchTemplate</h2>
<p>如果 Repository 的方法名无法满足复杂的业务逻辑（如高亮、聚合、复杂的布尔过滤），可以使用 <code>ElasticsearchTemplate</code>。</p>
<h3>示例：带高亮和过滤的复合搜索</h3>
<pre><code class="language-java">@Autowired
private ElasticsearchRestTemplate elasticsearchRestTemplate;

public void complexSearch(String keyword) {
    // 1. 构建查询条件（类似 SQL 的 WHERE）
    NativeSearchQuery searchQuery = new NativeSearchQueryBuilder()
            .withQuery(QueryBuilders.boolQuery()
                    .must(QueryBuilders.matchQuery(&quot;title&quot;, keyword)) // 必须匹配标题
                    .should(QueryBuilders.matchQuery(&quot;content&quot;, keyword))) // 内容匹配可加分
            .withHighlightFields(new HighlightBuilder.Field(&quot;title&quot;)) // 设置高亮字段
            .withPageable(PageRequest.of(0, 10)) // 分页
            .build();

    // 2. 执行搜索
    SearchHits&lt;Article&gt; searchHits = elasticsearchRestTemplate.search(searchQuery, Article.class);

    // 3. 处理高亮结果
    searchHits.getSearchHits().forEach(hit -&gt; {
        Article article = hit.getContent();
        List&lt;String&gt; highlightTitle = hit.getHighlightField(&quot;title&quot;);
        if (!highlightTitle.isEmpty()) {
            article.setTitle(highlightTitle.get(0)); // 替换为带 &lt;em&gt; 标签的内容
        }
        System.out.println(&quot;搜索结果: &quot; + article);
    });
}

</code></pre>
<hr>
<h2>5. 核心逻辑总结</h2>
<p>在实际开发中，代码流程通常是：</p>
<ol>
<li><strong>MySQL 写入</strong>：文章发布成功。</li>
<li><strong>触发同步</strong>：通过 <code>articleRepository.save(article)</code> 同步到 ES。</li>
<li><strong>用户搜索</strong>：调用自定义的 <code>search</code> 方法，获取经过分词和评分排序后的结果。</li>
</ol>
<blockquote>
<p>注意：<br>
在本地调试时，请确保已经安装了 <strong>IK 分词器</strong> 插件，否则代码中的 <code>analyzer = &quot;ik_max_word&quot;</code> 会导致 ES 报错。</p>
</blockquote>]]></description>
      <author>xancel</author>
      <guid>article-6</guid>
      <pubDate>Mon, 16 Mar 2026 12:42:09 +0000</pubDate>
    </item>
    <item>
      <title></title>
      <link>https://xancel.top/message</link>
      <description><![CDATA[<blockquote><p>该内容由 RSS 渲染生成，最佳阅读体验请前往：<a href="https://xancel.top/message">https://xancel.top/message</a></p></blockquote>]]></description>
      <guid>page-9</guid>
      <pubDate>Mon, 16 Mar 2026 08:37:32 +0000</pubDate>
    </item>
    <item>
      <title>关于我</title>
      <link>https://xancel.top/about</link>
      <description><![CDATA[<blockquote><p>该内容由 RSS 渲染生成，最佳阅读体验请前往：<a href="https://xancel.top/about">https://xancel.top/about</a></p></blockquote><p>一个正在成长的大三学生</p>]]></description>
      <guid>page-8</guid>
      <pubDate>Mon, 16 Mar 2026 08:35:36 +0000</pubDate>
    </item>
    <item>
      <title>如果思考的页面可以插入图片就好了</title>
      <link>https://xancel.top/thinkings#thinking-4</link>
      <description><![CDATA[<blockquote><p>该内容由 RSS 渲染生成，最佳阅读体验请前往：<a href="https://xancel.top/thinkings#thinking-4">https://xancel.top/thinkings#thinking-4</a></p></blockquote><p>如果思考的页面可以插入图片就好了</p>]]></description>
      <author>xancel</author>
      <guid>thinking-4</guid>
      <pubDate>Sun, 15 Mar 2026 16:04:16 +0000</pubDate>
    </item>
    <item>
      <title>深入理解MySQL MVCC</title>
      <link>https://xancel.top/posts/mysql-mvcc</link>
      <description><![CDATA[<blockquote><p>该内容由 RSS 渲染生成，最佳阅读体验请前往：<a href="https://xancel.top/posts/mysql-mvcc">https://xancel.top/posts/mysql-mvcc</a></p></blockquote><h2>首先，什么是 MVCC</h2>
<p>MVCC 的中文是<strong>多版本并发控制</strong>，它是 InnoDB 在处理普通 <code>SELECT</code> 这类<strong>快照读</strong>时实现一致性读的核心机制，主要服务于<strong>读已提交（RC）</strong> 和 <strong>可重复读（RR）</strong> 这两个隔离级别。</p>
<p>并发事务里常见的冲突主要有两类：</p>
<ul>
<li>读写冲突：事务 A 在读一行数据，事务 B 同时修改这行数据</li>
<li>写写冲突：两个事务同时修改同一行数据</li>
</ul>
<p>写写冲突仍然需要依赖锁来保证串行化；MVCC 主要解决的是<strong>读写并发</strong>时，如何让读操作尽量不阻塞写操作。</p>
<p>需要注意，MVCC 主要用于<strong>快照读</strong>，不适用于所有读操作。像 <code>SELECT ... FOR UPDATE</code>、<code>SELECT ... FOR SHARE</code>、<code>UPDATE</code>、<code>DELETE</code> 这类<strong>当前读</strong>，仍然要结合锁来工作。</p>
<h2>MVCC 的三个核心点</h2>
<ol>
<li>
<p><strong>隐藏字段</strong></p>
<ul>
<li><code>trx_id</code>：最后一次修改该记录版本的事务 ID</li>
<li><code>roll_pointer</code>：指向该记录上一个历史版本的 undo log 指针</li>
</ul>
</li>
<li>
<p><strong>undo log 版本链</strong></p>
<p>一行记录每次被修改时，旧版本不会立刻消失，而是被写入 undo log，再通过 <code>roll_pointer</code> 串起来，形成一条从新到旧的版本链。</p>
</li>
<li>
<p><strong>Read View</strong></p>
<p>Read View 可以理解为事务做快照读时拿到的一份“可见性规则”。事务会基于这份规则，从版本链上判断哪个版本对自己可见。</p>
</li>
</ol>
<h2>Read View 中几个关键字段</h2>
<ul>
<li><code>m_ids</code>：创建 Read View 时，系统中活跃的读写事务 ID 集合</li>
<li><code>min_trx_id</code>：<code>m_ids</code> 中最小的事务 ID</li>
<li><code>max_trx_id</code>：创建 Read View 时，系统将要分配给下一个事务的 ID</li>
<li><code>creator_trx_id</code>：创建这个 Read View 的事务 ID</li>
</ul>
<p>这里要特别注意：</p>
<ul>
<li>记录上的 <code>trx_id</code>，表示“<strong>这个版本</strong>是由哪个事务生成的”</li>
<li><code>creator_trx_id</code>，表示“<strong>当前正在读取数据的事务</strong>是谁”</li>
</ul>
<p><strong>这两个不是同一个概念。</strong></p>
<h2>可见性判断算法</h2>
<p>当事务进行快照读时，会从版本链的最新版本开始判断可见性。这里参与判断的 <code>trx_id</code>，指的是“<strong>当前正在检查的那个记录版本的事务 ID</strong>”，不是当前读请求所属事务的 ID。</p>
<p>判断规则可以概括为：</p>
<ol>
<li>
<p>如果版本的 <code>trx_id == creator_trx_id</code>，<strong>该版本可见</strong></p>
<p>原因：这是当前事务自己生成的版本，当前事务当然可以看到。</p>
</li>
<li>
<p>如果版本的 <code>trx_id &lt; min_trx_id</code>，<strong>该版本可见</strong></p>
<p>原因：说明生成这个版本的事务，在 Read View 创建前就已经提交了。</p>
</li>
<li>
<p>如果版本的 <code>trx_id &gt;= max_trx_id</code>，<strong>该版本不可见</strong></p>
<p>原因：说明这个版本对应的事务，是在 Read View 创建之后才开始的。</p>
</li>
<li>
<p>如果 <code>min_trx_id &lt;= trx_id &lt; max_trx_id</code></p>
<ul>
<li><code>trx_id</code> 在 <code>m_ids</code> 中：<strong>不可见</strong>，说明该事务在创建 Read View 时仍然活跃，尚未提交</li>
<li><code>trx_id</code> 不在 <code>m_ids</code> 中：<strong>可见</strong>，说明该事务在创建 Read View 前已经提交</li>
</ul>
</li>
</ol>
<p>如果某个版本不可见，就顺着版本链继续找更老的版本，直到找到<strong>第一个可见版本</strong>为止。</p>
<h2>RC 和 RR 的本质区别</h2>
<ol>
<li>
<p><strong>RC（读已提交）</strong></p>
<p>每次执行快照读时，都会创建新的 Read View，所以同一个事务里两次普通 <code>SELECT</code>，可能看到不同的已提交结果，这就是不可重复读产生的原因。</p>
</li>
<li>
<p><strong>RR（可重复读）</strong></p>
<p>默认情况下，事务中的第一次快照读会创建 Read View，后续快照读复用同一个 Read View，因此同一个事务里的多次普通 <code>SELECT</code> 通常会看到同一份快照。</p>
</li>
</ol>
<p>这里的表述要稍微严谨一点：</p>
<ul>
<li>RR 下“保持一致”的是<strong>普通快照读</strong>看到的历史快照</li>
<li>如果当前事务自己更新了数据，那么它仍然可以看到自己更新后的结果</li>
<li>如果执行的是<strong>当前读</strong>，也不走这套快照可见性逻辑</li>
</ul>
<p>另外，InnoDB 中 RR 并不是“事务一启动就一定创建 Read View”。通常是<strong>第一次执行快照读时</strong>才创建；如果显式使用了 <code>START TRANSACTION WITH CONSISTENT SNAPSHOT</code>，则会在事务开始时创建一致性视图。</p>
<h2>举例说明版本选择过程</h2>
<p>假设有三个事务并发执行：</p>
<ul>
<li>事务 101：较早启动，修改了某行数据，但还没有提交</li>
<li>事务 102：稍后修改了同一行数据，并且已经提交</li>
<li>事务 103：当前事务，正在执行一次快照读</li>
</ul>
<p>假设这行数据原本的最后已提交版本是 <code>trx_id=100</code>。</p>
<p>之后：</p>
<ul>
<li>事务 102 修改该行，生成一个新版本，<code>trx_id=102</code></li>
<li>事务 101 又基于更新后的数据生成更“新”的版本，<code>trx_id=101</code>，但它尚未提交</li>
</ul>
<p>于是版本链可以表示为：</p>
<p><code>(101) -&gt; (102) -&gt; (100)</code></p>
<p>其中 <code>(101)</code> 是当前记录上最新的版本。</p>
<p>现在事务 103 在执行<strong>第一次快照读</strong>时创建 Read View。此时：</p>
<ul>
<li>活跃事务只有 101，所以 <code>m_ids = {101}</code></li>
<li><code>min_trx_id = 101</code></li>
<li><code>max_trx_id = 104</code>，表示下一个将被分配的事务 ID 是 104</li>
<li><code>creator_trx_id = 103</code></li>
</ul>
<p>事务 103 读取这行数据时，会这样判断：</p>
<ol>
<li>
<p><strong>先看最新版本 <code>(101)</code></strong></p>
<p><code>trx_id=101</code> 落在 <code>min_trx_id &lt;= trx_id &lt; max_trx_id</code> 区间内，并且 101 在 <code>m_ids</code> 中，说明该版本对应的事务在创建 Read View 时仍然活跃，因此不可见。</p>
</li>
<li>
<p><strong>顺着版本链看下一个版本 <code>(102)</code></strong></p>
<p><code>trx_id=102</code> 也还是落在<code>min_trx_id &lt;= trx_id &lt; max_trx_id</code> 区间内，但 102 不在 <code>m_ids</code> 中，说明它在 Read View 创建前已经提交，因此这个版本可见。</p>
</li>
<li>
<p><strong>返回版本 <code>(102)</code> 的数据</strong></p>
</li>
</ol>
<p>所以，事务 103 读到的是<strong>事务 102 提交后的值</strong>，而不是事务 101 尚未提交的值。</p>
<p>如果隔离级别是 RC，那么事务 103 每次执行快照读都会重新创建 Read View：</p>
<ul>
<li>只要事务 101 还没提交，新的 Read View 中仍然会把 101 视为活跃事务，因此看不到版本 101</li>
<li>一旦事务 101 提交，下一次快照读重新生成 Read View 时，101 不再出现在活跃事务列表中，此时就可能读到版本 101
::: link-card href=&quot;https://www.bilibili.com/video/BV1Yc411N7tK&quot; title=&quot;10分钟带你深刻理解MySQL中的MVCC机制&quot; desc=&quot;参考引用&quot; newtab=&quot;true&quot;</li>
</ul>
<p>:::
::: link-card href=&quot;https://www.bilibili.com/video/BV1Kr4y1i7ru&quot; title=&quot;MySQL数据库入门到精通&quot; desc=&quot;参考引用&quot; newtab=&quot;true&quot;</p>
<p>:::</p>]]></description>
      <author>xancel</author>
      <guid>article-5</guid>
      <pubDate>Sun, 15 Mar 2026 15:53:17 +0000</pubDate>
    </item>
    <item>
      <title>Life, the Universe and Everything.</title>
      <link>https://xancel.top/thinkings#thinking-2</link>
      <description><![CDATA[<blockquote><p>该内容由 RSS 渲染生成，最佳阅读体验请前往：<a href="https://xancel.top/thinkings#thinking-2">https://xancel.top/thinkings#thinking-2</a></p></blockquote><p>Life, the Universe and Everything.</p>]]></description>
      <author>xancel</author>
      <guid>thinking-2</guid>
      <pubDate>Sun, 15 Mar 2026 07:30:00 +0000</pubDate>
    </item>
  </channel>
</rss>