<?xml version="1.0" encoding="utf-8" standalone="yes"?><rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom"><channel><title>Agent 工程实战 on 扎塔-Zata</title><link>https://www.zata.cc/tags/agent-%E5%B7%A5%E7%A8%8B%E5%AE%9E%E6%88%98/</link><description>Recent content in Agent 工程实战 on 扎塔-Zata</description><generator>Hugo -- gohugo.io</generator><language>zh-cn</language><copyright>Example Person</copyright><lastBuildDate>Fri, 24 Jul 2026 14:02:03 +0800</lastBuildDate><atom:link href="https://www.zata.cc/tags/agent-%E5%B7%A5%E7%A8%8B%E5%AE%9E%E6%88%98/index.xml" rel="self" type="application/rss+xml"/><item><title>数据库初始化与迁移：从创建那一刻就要钉死的三件事</title><link>https://www.zata.cc/p/%E6%95%B0%E6%8D%AE%E5%BA%93%E5%88%9D%E5%A7%8B%E5%8C%96%E4%B8%8E%E8%BF%81%E7%A7%BB%E4%BB%8E%E5%88%9B%E5%BB%BA%E9%82%A3%E4%B8%80%E5%88%BB%E5%B0%B1%E8%A6%81%E9%92%89%E6%AD%BB%E7%9A%84%E4%B8%89%E4%BB%B6%E4%BA%8B/index.md/</link><pubDate>Thu, 23 Jul 2026 11:21:08 +0800</pubDate><guid>https://www.zata.cc/p/%E6%95%B0%E6%8D%AE%E5%BA%93%E5%88%9D%E5%A7%8B%E5%8C%96%E4%B8%8E%E8%BF%81%E7%A7%BB%E4%BB%8E%E5%88%9B%E5%BB%BA%E9%82%A3%E4%B8%80%E5%88%BB%E5%B0%B1%E8%A6%81%E9%92%89%E6%AD%BB%E7%9A%84%E4%B8%89%E4%BB%B6%E4%BA%8B/index.md/</guid><description>&lt;img src="https://www.zata.cc/p/%E6%95%B0%E6%8D%AE%E5%BA%93%E5%88%9D%E5%A7%8B%E5%8C%96%E4%B8%8E%E8%BF%81%E7%A7%BB%E4%BB%8E%E5%88%9B%E5%BB%BA%E9%82%A3%E4%B8%80%E5%88%BB%E5%B0%B1%E8%A6%81%E9%92%89%E6%AD%BB%E7%9A%84%E4%B8%89%E4%BB%B6%E4%BA%8B/index.md/images/index/index.svg" alt="Featured image of post 数据库初始化与迁移：从创建那一刻就要钉死的三件事" />&lt;blockquote>
&lt;p>&lt;strong>一句话：版本 + 字符集 + 排序规则，三件事必须在 &lt;code>CREATE&lt;/code> 那一刻写死。&lt;/strong> 之后任何跨环境、迁移、同步都建立在这三件事一致的基础上。&lt;/p>
&lt;/blockquote>
&lt;p>&lt;img src="https://www.zata.cc/p/%E6%95%B0%E6%8D%AE%E5%BA%93%E5%88%9D%E5%A7%8B%E5%8C%96%E4%B8%8E%E8%BF%81%E7%A7%BB%E4%BB%8E%E5%88%9B%E5%BB%BA%E9%82%A3%E4%B8%80%E5%88%BB%E5%B0%B1%E8%A6%81%E9%92%89%E6%AD%BB%E7%9A%84%E4%B8%89%E4%BB%B6%E4%BA%8B/index.md/images/index/index.svg"
loading="lazy"
alt="封面图：迁移时 90% 报错来自三个不一致"
>&lt;/p>
&lt;h2 id="一三件事在四个数据库里的标准值">一、三件事在四个数据库里的标准值
&lt;/h2>&lt;table>
&lt;thead>
&lt;tr>
&lt;th>数据库&lt;/th>
&lt;th>版本&lt;/th>
&lt;th>字符集&lt;/th>
&lt;th>排序规则&lt;/th>
&lt;th>改的代价&lt;/th>
&lt;/tr>
&lt;/thead>
&lt;tbody>
&lt;tr>
&lt;td>MySQL 5.7&lt;/td>
&lt;td>&lt;code>5.7.x&lt;/code> LTS&lt;/td>
&lt;td>&lt;code>utf8mb4&lt;/code>&lt;/td>
&lt;td>&lt;code>utf8mb4_general_ci&lt;/code>&lt;/td>
&lt;td>单 ALTER&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>MySQL 8.0&lt;/td>
&lt;td>&lt;code>8.0.x&lt;/code> / &lt;code>8.4.x&lt;/code>&lt;/td>
&lt;td>&lt;code>utf8mb4&lt;/code>&lt;/td>
&lt;td>&lt;code>utf8mb4_0900_ai_ci&lt;/code>&lt;/td>
&lt;td>单 ALTER&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>PostgreSQL&lt;/td>
&lt;td>&lt;code>15.x&lt;/code> / &lt;code>16.x&lt;/code>&lt;/td>
&lt;td>&lt;code>UTF8&lt;/code> (client)&lt;/td>
&lt;td>ICU 默认&lt;/td>
&lt;td>&lt;strong>库级改不了&lt;/strong>&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>SQL Server&lt;/td>
&lt;td>&lt;code>2019&lt;/code> / &lt;code>2022&lt;/code>&lt;/td>
&lt;td>见 collation&lt;/td>
&lt;td>&lt;code>SQL_Latin1_General_CP1_CI_AS&lt;/code>&lt;/td>
&lt;td>可改，需重建索引&lt;/td>
&lt;/tr>
&lt;/tbody>
&lt;/table>
&lt;blockquote>
&lt;p>永远显式写。&lt;strong>不能依赖&amp;quot;实例默认值&amp;quot;&lt;/strong>——开发/测试/预发/线上四个环境经常不一样。&lt;/p>
&lt;/blockquote>
&lt;hr>
&lt;h2 id="二emoji-演示utf8--utf8mb4">二、emoji 演示：&lt;code>utf8&lt;/code> ≠ &lt;code>utf8mb4&lt;/code>
&lt;/h2>&lt;div class="highlight">&lt;pre tabindex="0" class="chroma">&lt;code class="language-fallback" data-lang="fallback">&lt;span class="line">&lt;span class="cl">INSERT INTO users (name) VALUES (&amp;#39;😀&amp;#39;); -- 字符: 4 字节
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;table>
&lt;thead>
&lt;tr>
&lt;th>字符集&lt;/th>
&lt;th>字节数上限&lt;/th>
&lt;th>结果&lt;/th>
&lt;/tr>
&lt;/thead>
&lt;tbody>
&lt;tr>
&lt;td>&lt;code>latin1&lt;/code>&lt;/td>
&lt;td>1 字节&lt;/td>
&lt;td>❌ 中文都存不了&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>&lt;code>utf8&lt;/code>&lt;/td>
&lt;td>&lt;strong>3 字节&lt;/strong> ⚠️&lt;/td>
&lt;td>❌ emoji 报错 &lt;code>ERROR 1366&lt;/code>&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>&lt;code>utf8mb4&lt;/code>&lt;/td>
&lt;td>4 字节&lt;/td>
&lt;td>✅&lt;/td>
&lt;/tr>
&lt;/tbody>
&lt;/table>
&lt;blockquote>
&lt;p>MySQL 的 &lt;code>utf8&lt;/code> 是历史包袱，&lt;strong>只支持 3 字节&lt;/strong>。JDBC / ORM 里写 &lt;code>utf8&lt;/code> 的，emoji 入库一律失败。&lt;strong>永远写 &lt;code>utf8mb4&lt;/code>&lt;/strong>。&lt;/p>
&lt;/blockquote>
&lt;hr>
&lt;h2 id="三四套初始化模板直接复制">三、四套初始化模板（直接复制）
&lt;/h2>&lt;h3 id="mysql-57">MySQL 5.7
&lt;/h3>&lt;div class="highlight">&lt;pre tabindex="0" class="chroma">&lt;code class="language-sql" data-lang="sql">&lt;span class="line">&lt;span class="cl">&lt;span class="k">CREATE&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="k">DATABASE&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">my_app&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="k">DEFAULT&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="nb">CHARACTER&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="k">SET&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">utf8mb4&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="k">DEFAULT&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="k">COLLATE&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">utf8mb4_general_ci&lt;/span>&lt;span class="p">;&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w">&lt;/span>&lt;span class="k">CREATE&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="k">TABLE&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">users&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="n">id&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="nb">BIGINT&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">UNSIGNED&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="k">NOT&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="k">NULL&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">AUTO_INCREMENT&lt;/span>&lt;span class="p">,&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="n">name&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="nb">VARCHAR&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="mi">64&lt;/span>&lt;span class="p">)&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="k">NOT&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="k">NULL&lt;/span>&lt;span class="p">,&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="n">email&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="nb">VARCHAR&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="mi">128&lt;/span>&lt;span class="p">)&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="k">NOT&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="k">NULL&lt;/span>&lt;span class="p">,&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="n">created_at&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">DATETIME&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="mi">3&lt;/span>&lt;span class="p">)&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="k">NOT&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="k">NULL&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="k">DEFAULT&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="k">CURRENT_TIMESTAMP&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="mi">3&lt;/span>&lt;span class="p">),&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="k">PRIMARY&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="k">KEY&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">id&lt;/span>&lt;span class="p">),&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="k">UNIQUE&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="k">KEY&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">uk_email&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">email&lt;/span>&lt;span class="p">)&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w">&lt;/span>&lt;span class="p">)&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">ENGINE&lt;/span>&lt;span class="o">=&lt;/span>&lt;span class="n">InnoDB&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="k">DEFAULT&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">CHARSET&lt;/span>&lt;span class="o">=&lt;/span>&lt;span class="n">utf8mb4&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="k">COLLATE&lt;/span>&lt;span class="o">=&lt;/span>&lt;span class="n">utf8mb4_general_ci&lt;/span>&lt;span class="p">;&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;h3 id="mysql-80">MySQL 8.0
&lt;/h3>&lt;div class="highlight">&lt;pre tabindex="0" class="chroma">&lt;code class="language-sql" data-lang="sql">&lt;span class="line">&lt;span class="cl">&lt;span class="k">CREATE&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="k">DATABASE&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">my_app&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="k">DEFAULT&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="nb">CHARACTER&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="k">SET&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">utf8mb4&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="k">DEFAULT&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="k">COLLATE&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">utf8mb4_0900_ai_ci&lt;/span>&lt;span class="p">;&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w">&lt;/span>&lt;span class="k">CREATE&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="k">TABLE&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">users&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="n">id&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="nb">BIGINT&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">UNSIGNED&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="k">NOT&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="k">NULL&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">AUTO_INCREMENT&lt;/span>&lt;span class="p">,&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="n">name&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="nb">VARCHAR&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="mi">64&lt;/span>&lt;span class="p">)&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="k">NOT&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="k">NULL&lt;/span>&lt;span class="p">,&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="n">email&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="nb">VARCHAR&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="mi">128&lt;/span>&lt;span class="p">)&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="k">NOT&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="k">NULL&lt;/span>&lt;span class="p">,&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="n">payload&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">JSON&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="k">DEFAULT&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="k">NULL&lt;/span>&lt;span class="p">,&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="n">created_at&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">DATETIME&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="mi">3&lt;/span>&lt;span class="p">)&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="k">NOT&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="k">NULL&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="k">DEFAULT&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="k">CURRENT_TIMESTAMP&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="mi">3&lt;/span>&lt;span class="p">),&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="k">PRIMARY&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="k">KEY&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">id&lt;/span>&lt;span class="p">),&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="k">UNIQUE&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="k">KEY&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">uk_email&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">email&lt;/span>&lt;span class="p">),&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="c1">-- 8.0 才支持的表达式索引
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1">&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="k">KEY&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">idx_payload_user_id&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="p">((&lt;/span>&lt;span class="k">CAST&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">payload&lt;/span>&lt;span class="o">-&amp;gt;&amp;gt;&lt;/span>&lt;span class="s1">&amp;#39;$.userId&amp;#39;&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="k">AS&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="nb">CHAR&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="mi">64&lt;/span>&lt;span class="p">))))&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w">&lt;/span>&lt;span class="p">)&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">ENGINE&lt;/span>&lt;span class="o">=&lt;/span>&lt;span class="n">InnoDB&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="k">DEFAULT&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">CHARSET&lt;/span>&lt;span class="o">=&lt;/span>&lt;span class="n">utf8mb4&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="k">COLLATE&lt;/span>&lt;span class="o">=&lt;/span>&lt;span class="n">utf8mb4_0900_ai_ci&lt;/span>&lt;span class="p">;&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;h3 id="postgresql">PostgreSQL
&lt;/h3>&lt;div class="highlight">&lt;pre tabindex="0" class="chroma">&lt;code class="language-sql" data-lang="sql">&lt;span class="line">&lt;span class="cl">&lt;span class="c1">-- 库级字符集在 CREATE DATABASE 时定型,后续改不动!
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1">&lt;/span>&lt;span class="k">CREATE&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="k">DATABASE&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">my_app&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="k">WITH&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="k">ENCODING&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="s1">&amp;#39;UTF8&amp;#39;&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="n">LC_COLLATE&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="o">=&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="s1">&amp;#39;en_US.UTF8&amp;#39;&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="n">LC_CTYPE&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="o">=&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="s1">&amp;#39;en_US.UTF8&amp;#39;&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="k">TEMPLATE&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="o">=&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">template0&lt;/span>&lt;span class="p">;&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w">&lt;/span>&lt;span class="k">CREATE&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="k">TABLE&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">users&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="n">id&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">BIGSERIAL&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="k">PRIMARY&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="k">KEY&lt;/span>&lt;span class="p">,&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="n">name&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="nb">TEXT&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="k">NOT&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="k">NULL&lt;/span>&lt;span class="p">,&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="n">email&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="nb">TEXT&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="k">NOT&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="k">NULL&lt;/span>&lt;span class="p">,&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="n">payload&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">JSONB&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="k">DEFAULT&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="k">NULL&lt;/span>&lt;span class="p">,&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="n">created_at&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">TIMESTAMPTZ&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="k">NOT&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="k">NULL&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="k">DEFAULT&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">NOW&lt;/span>&lt;span class="p">(),&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="k">UNIQUE&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">email&lt;/span>&lt;span class="p">)&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w">&lt;/span>&lt;span class="p">);&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w">&lt;/span>&lt;span class="k">CREATE&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="k">INDEX&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">idx_payload_user_id&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="k">ON&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">users&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="p">((&lt;/span>&lt;span class="n">payload&lt;/span>&lt;span class="o">-&amp;gt;&amp;gt;&lt;/span>&lt;span class="s1">&amp;#39;userId&amp;#39;&lt;/span>&lt;span class="p">));&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;blockquote>
&lt;p>PG 的字符集分两层：库级 &lt;code>ENCODING&lt;/code> + 连接级 &lt;code>client_encoding&lt;/code>。JDBC 必须显式：&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" class="chroma">&lt;code class="language-text" data-lang="text">&lt;span class="line">&lt;span class="cl">jdbc:postgresql://host:5432/my_app?charSet=UTF8
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;/blockquote>
&lt;h3 id="sql-server">SQL Server
&lt;/h3>&lt;div class="highlight">&lt;pre tabindex="0" class="chroma">&lt;code class="language-sql" data-lang="sql">&lt;span class="line">&lt;span class="cl">&lt;span class="c1">-- 实例默认 collation 不一定是这个,必须显式写
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1">&lt;/span>&lt;span class="k">CREATE&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="k">DATABASE&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">my_app&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="k">COLLATE&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">SQL_Latin1_General_CP1_CI_AS&lt;/span>&lt;span class="p">;&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w">&lt;/span>&lt;span class="k">GO&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w">&lt;/span>&lt;span class="k">CREATE&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="k">TABLE&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">users&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="n">id&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="nb">BIGINT&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="k">IDENTITY&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="mi">1&lt;/span>&lt;span class="p">,&lt;/span>&lt;span class="mi">1&lt;/span>&lt;span class="p">)&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="k">PRIMARY&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="k">KEY&lt;/span>&lt;span class="p">,&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="n">name&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="nb">VARCHAR&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="mi">64&lt;/span>&lt;span class="p">)&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="k">COLLATE&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">SQL_Latin1_General_CP1_CI_AS&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="k">NOT&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="k">NULL&lt;/span>&lt;span class="p">,&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="n">email&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="nb">VARCHAR&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="mi">128&lt;/span>&lt;span class="p">)&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="k">COLLATE&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">SQL_Latin1_General_CP1_CI_AS&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="k">NOT&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="k">NULL&lt;/span>&lt;span class="p">,&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="n">payload&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">NVARCHAR&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="k">MAX&lt;/span>&lt;span class="p">)&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="k">DEFAULT&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="k">NULL&lt;/span>&lt;span class="p">,&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="n">created_at&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">DATETIME2&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="mi">3&lt;/span>&lt;span class="p">)&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="k">NOT&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="k">NULL&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="k">DEFAULT&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">SYSUTCDATETIME&lt;/span>&lt;span class="p">(),&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="k">UNIQUE&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">email&lt;/span>&lt;span class="p">)&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w">&lt;/span>&lt;span class="p">);&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w">&lt;/span>&lt;span class="k">GO&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;hr>
&lt;h2 id="四迁移报错-4-种症状--原因--修复">四、迁移报错 4 种「症状 → 原因 → 修复」
&lt;/h2>&lt;h3 id="-症状-1emoji--中文入库乱码或报错">🔴 症状 1：emoji / 中文入库乱码或报错
&lt;/h3>&lt;div class="highlight">&lt;pre tabindex="0" class="chroma">&lt;code class="language-sql" data-lang="sql">&lt;span class="line">&lt;span class="cl">&lt;span class="c1">-- 报错信息
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1">&lt;/span>&lt;span class="n">ERROR&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="mi">1366&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">HY000&lt;/span>&lt;span class="p">):&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">Incorrect&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">string&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">value&lt;/span>&lt;span class="p">:&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="s1">&amp;#39;\xF0\x9F\x98\x80&amp;#39;&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="k">for&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="k">column&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="s1">&amp;#39;name&amp;#39;&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;table>
&lt;thead>
&lt;tr>
&lt;th>项&lt;/th>
&lt;th>内容&lt;/th>
&lt;/tr>
&lt;/thead>
&lt;tbody>
&lt;tr>
&lt;td>&lt;strong>原因&lt;/strong>&lt;/td>
&lt;td>表字符集不是 &lt;code>utf8mb4&lt;/code>&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>&lt;strong>修复&lt;/strong>&lt;/td>
&lt;td>&lt;code>ALTER TABLE users CONVERT TO CHARACTER SET utf8mb4 COLLATE utf8mb4_0900_ai_ci;&lt;/code>&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>&lt;strong>预防&lt;/strong>&lt;/td>
&lt;td>JDBC 连接串 &lt;code>characterEncoding=utf8&lt;/code>、表级显式 &lt;code>CHARSET=utf8mb4&lt;/code>&lt;/td>
&lt;/tr>
&lt;/tbody>
&lt;/table>
&lt;h3 id="-症状-2order-by-name-顺序变了--where-col-命中行数变化">🔴 症状 2：&lt;code>ORDER BY name&lt;/code> 顺序变了 / &lt;code>WHERE col='...'&lt;/code> 命中行数变化
&lt;/h3>&lt;table>
&lt;thead>
&lt;tr>
&lt;th>项&lt;/th>
&lt;th>内容&lt;/th>
&lt;/tr>
&lt;/thead>
&lt;tbody>
&lt;tr>
&lt;td>&lt;strong>原因&lt;/strong>&lt;/td>
&lt;td>排序规则从 &lt;code>general_ci&lt;/code> → &lt;code>0900_ai_ci&lt;/code>，UCA 9.0 比较更激进&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>&lt;strong>修复&lt;/strong>&lt;/td>
&lt;td>&lt;code>ORDER BY name COLLATE utf8mb4_bin&lt;/code> 锁定；或应用层排序&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>&lt;strong>预防&lt;/strong>&lt;/td>
&lt;td>&lt;strong>业务层不依赖 DB 排序结果做正确性判断&lt;/strong>&lt;/td>
&lt;/tr>
&lt;/tbody>
&lt;/table>
&lt;h3 id="-症状-3insert-截断字符串--整个应用雪崩">🔴 症状 3：&lt;code>INSERT&lt;/code> 截断字符串 → 整个应用雪崩
&lt;/h3>&lt;table>
&lt;thead>
&lt;tr>
&lt;th>项&lt;/th>
&lt;th>内容&lt;/th>
&lt;/tr>
&lt;/thead>
&lt;tbody>
&lt;tr>
&lt;td>&lt;strong>原因&lt;/strong>&lt;/td>
&lt;td>8.0 默认 &lt;code>STRICT_TRANS_TABLES&lt;/code>，截断从 warning 变 error&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>&lt;strong>修复&lt;/strong>&lt;/td>
&lt;td>灰度期间 &lt;code>SET GLOBAL sql_mode = 'STRICT_TRANS_TABLES,NO_ENGINE_SUBSTITUTION,...';&lt;/code>&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>&lt;strong>预防&lt;/strong>&lt;/td>
&lt;td>上线前用真实流量回放，发现 silent-truncate 全部修掉&lt;/td>
&lt;/tr>
&lt;/tbody>
&lt;/table>
&lt;h3 id="-症状-4本地能连ci线上连不上">🔴 症状 4：本地能连，CI/线上连不上
&lt;/h3>&lt;table>
&lt;thead>
&lt;tr>
&lt;th>项&lt;/th>
&lt;th>内容&lt;/th>
&lt;/tr>
&lt;/thead>
&lt;tbody>
&lt;tr>
&lt;td>&lt;strong>原因&lt;/strong>&lt;/td>
&lt;td>5.7 &lt;code>mysql_native_password&lt;/code> → 8.0 &lt;code>caching_sha2_password&lt;/code>&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>&lt;strong>修复&lt;/strong>&lt;/td>
&lt;td>升级 JDBC/ORM 驱动；禁掉 &lt;code>mysql_native_password&lt;/code>&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>&lt;strong>临时方案&lt;/strong>&lt;/td>
&lt;td>&lt;code>ALTER USER 'app'@'%' IDENTIFIED WITH mysql_native_password BY 'xxx';&lt;/code>&lt;/td>
&lt;/tr>
&lt;/tbody>
&lt;/table>
&lt;hr>
&lt;h2 id="五其他数据库的独有坑速查">五、其他数据库的独有坑（速查）
&lt;/h2>&lt;table>
&lt;thead>
&lt;tr>
&lt;th>数据库&lt;/th>
&lt;th>坑&lt;/th>
&lt;th>一句话修复&lt;/th>
&lt;/tr>
&lt;/thead>
&lt;tbody>
&lt;tr>
&lt;td>&lt;strong>PG&lt;/strong>&lt;/td>
&lt;td>字符串默认大小写敏感&lt;/td>
&lt;td>&lt;code>LOWER(name) = LOWER(?)&lt;/code> 或用 &lt;code>citext&lt;/code>&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>&lt;strong>PG&lt;/strong>&lt;/td>
&lt;td>10+ 隐式类型转换收紧&lt;/td>
&lt;td>所有比较显式 &lt;code>CAST&lt;/code>&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>&lt;strong>PG&lt;/strong>&lt;/td>
&lt;td>&lt;code>BIGSERIAL ≠ AUTO_INCREMENT&lt;/code>&lt;/td>
&lt;td>跨库迁移重置序列或改 UUID&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>&lt;strong>SQL Server&lt;/strong>&lt;/td>
&lt;td>collation 在 3 个层级设置&lt;/td>
&lt;td>列级 &lt;code>COLLATE ...&lt;/code> 钉死&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>&lt;strong>SQL Server&lt;/strong>&lt;/td>
&lt;td>&lt;code>DATETIME&lt;/code> vs &lt;code>DATETIME2&lt;/code>&lt;/td>
&lt;td>迁移时零日期映射为 &lt;code>NULL&lt;/code>&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>&lt;strong>SQL Server&lt;/strong>&lt;/td>
&lt;td>&lt;code>OFFSET&lt;/code> 在大表慢&lt;/td>
&lt;td>永远用 keyset 分页（&lt;code>WHERE id &amp;gt; last_id&lt;/code>）&lt;/td>
&lt;/tr>
&lt;/tbody>
&lt;/table>
&lt;hr>
&lt;h2 id="六迁移前-5-分钟-checklist直接跑">六、迁移前 5 分钟 checklist（直接跑）
&lt;/h2>&lt;h3 id="mysql">MySQL
&lt;/h3>&lt;div class="highlight">&lt;pre tabindex="0" class="chroma">&lt;code class="language-sql" data-lang="sql">&lt;span class="line">&lt;span class="cl">&lt;span class="c1">-- 1. 实例信息
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1">&lt;/span>&lt;span class="k">SELECT&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="o">@@&lt;/span>&lt;span class="k">version&lt;/span>&lt;span class="p">,&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="o">@@&lt;/span>&lt;span class="n">character_set_server&lt;/span>&lt;span class="p">,&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="o">@@&lt;/span>&lt;span class="n">collation_server&lt;/span>&lt;span class="p">,&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="o">@@&lt;/span>&lt;span class="n">sql_mode&lt;/span>&lt;span class="p">;&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w">&lt;/span>&lt;span class="c1">-- 2. 找出非 utf8mb4 的表
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1">&lt;/span>&lt;span class="k">SELECT&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">table_schema&lt;/span>&lt;span class="p">,&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="k">table_name&lt;/span>&lt;span class="p">,&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">table_collation&lt;/span>&lt;span class="p">,&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">CCSA&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="k">character_set_name&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w">&lt;/span>&lt;span class="k">FROM&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">information_schema&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="n">tables&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">T&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w">&lt;/span>&lt;span class="k">JOIN&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">information_schema&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="n">collation_character_set_applicability&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">CCSA&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="k">ON&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">CCSA&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="k">collation_name&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="o">=&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">T&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="n">table_collation&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w">&lt;/span>&lt;span class="k">WHERE&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">table_schema&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="k">NOT&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="k">IN&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="s1">&amp;#39;mysql&amp;#39;&lt;/span>&lt;span class="p">,&lt;/span>&lt;span class="s1">&amp;#39;sys&amp;#39;&lt;/span>&lt;span class="p">,&lt;/span>&lt;span class="s1">&amp;#39;information_schema&amp;#39;&lt;/span>&lt;span class="p">,&lt;/span>&lt;span class="s1">&amp;#39;performance_schema&amp;#39;&lt;/span>&lt;span class="p">)&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="k">AND&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">CCSA&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="k">character_set_name&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="o">!=&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="s1">&amp;#39;utf8mb4&amp;#39;&lt;/span>&lt;span class="p">;&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w">&lt;/span>&lt;span class="c1">-- 3. 找出表达式索引 (8.0+ 特性)
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1">&lt;/span>&lt;span class="k">SELECT&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="o">*&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="k">FROM&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">information_schema&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="k">statistics&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w">&lt;/span>&lt;span class="k">WHERE&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">expression&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="k">IS&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="k">NOT&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="k">NULL&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="k">AND&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">table_schema&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="k">NOT&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="k">IN&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="s1">&amp;#39;mysql&amp;#39;&lt;/span>&lt;span class="p">,&lt;/span>&lt;span class="s1">&amp;#39;sys&amp;#39;&lt;/span>&lt;span class="p">,&lt;/span>&lt;span class="s1">&amp;#39;information_schema&amp;#39;&lt;/span>&lt;span class="p">,&lt;/span>&lt;span class="s1">&amp;#39;performance_schema&amp;#39;&lt;/span>&lt;span class="p">);&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w">&lt;/span>&lt;span class="c1">-- 4. 找出 mysql_native_password 账号
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1">&lt;/span>&lt;span class="k">SELECT&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="k">user&lt;/span>&lt;span class="p">,&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="k">host&lt;/span>&lt;span class="p">,&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">plugin&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="k">FROM&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">mysql&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="k">user&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="k">WHERE&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">plugin&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="o">=&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="s1">&amp;#39;mysql_native_password&amp;#39;&lt;/span>&lt;span class="p">;&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;h3 id="postgresql-1">PostgreSQL
&lt;/h3>&lt;div class="highlight">&lt;pre tabindex="0" class="chroma">&lt;code class="language-sql" data-lang="sql">&lt;span class="line">&lt;span class="cl">&lt;span class="k">SELECT&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="k">version&lt;/span>&lt;span class="p">();&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w">&lt;/span>&lt;span class="k">SELECT&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">datname&lt;/span>&lt;span class="p">,&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">datcollate&lt;/span>&lt;span class="p">,&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">datctype&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w">&lt;/span>&lt;span class="k">FROM&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">pg_database&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="k">WHERE&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">datistemplate&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="o">=&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="k">false&lt;/span>&lt;span class="p">;&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w">&lt;/span>&lt;span class="k">SELECT&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="o">*&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="k">FROM&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">users&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="k">WHERE&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="k">length&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">name&lt;/span>&lt;span class="p">)&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="o">=&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="mi">0&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="k">OR&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">name&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="k">IS&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="k">NULL&lt;/span>&lt;span class="p">;&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;h3 id="sql-server-1">SQL Server
&lt;/h3>&lt;div class="highlight">&lt;pre tabindex="0" class="chroma">&lt;code class="language-sql" data-lang="sql">&lt;span class="line">&lt;span class="cl">&lt;span class="k">SELECT&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">SERVERPROPERTY&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="s1">&amp;#39;Collation&amp;#39;&lt;/span>&lt;span class="p">)&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="k">AS&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">server_collation&lt;/span>&lt;span class="p">;&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w">&lt;/span>&lt;span class="k">SELECT&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">name&lt;/span>&lt;span class="p">,&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="k">collation_name&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="k">FROM&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">sys&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="n">databases&lt;/span>&lt;span class="p">;&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w">&lt;/span>&lt;span class="k">SELECT&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="o">*&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="k">FROM&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">dbo&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="n">users&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="k">WHERE&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">created_at&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="o">&amp;lt;&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="s1">&amp;#39;1900-01-01&amp;#39;&lt;/span>&lt;span class="p">;&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;hr>
&lt;h2 id="七迁移流程七步走">七、迁移流程（七步走）
&lt;/h2>&lt;div class="highlight">&lt;pre tabindex="0" class="chroma">&lt;code class="language-fallback" data-lang="fallback">&lt;span class="line">&lt;span class="cl">1. 备份 (mysqldump --single-transaction --hex-blob)
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> ↓
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">2. 准备目标实例 (兼容 sql_mode)
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> ↓
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">3. 导入备份 (不要用 source,用 mysql ... &amp;lt; backup.sql)
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> ↓
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">4. 跑上面的 checklist SQL
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> ↓
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">5. CONVERT 字符集 + 验证表达式索引兼容
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> ↓
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">6. 灰度切流量 5% → 20% → 100%
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> ↓
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">7. 完全切完后再升级 ORM / 驱动
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;blockquote>
&lt;p>&lt;strong>回滚三件套&lt;/strong>：&lt;/p>
&lt;ol>
&lt;li>保留 7 天以上源端快照&lt;/li>
&lt;li>迁移脚本&lt;strong>幂等&lt;/strong>（跑两遍不报错也不丢数据）&lt;/li>
&lt;li>DNS / 负载均衡能&lt;strong>秒级切回&lt;/strong>&lt;/li>
&lt;/ol>
&lt;/blockquote>
&lt;hr>
&lt;h2 id="八agent-系统特别提醒">八、Agent 系统特别提醒
&lt;/h2>&lt;table>
&lt;thead>
&lt;tr>
&lt;th>#&lt;/th>
&lt;th>提醒&lt;/th>
&lt;th>一句话&lt;/th>
&lt;/tr>
&lt;/thead>
&lt;tbody>
&lt;tr>
&lt;td>1&lt;/td>
&lt;td>&lt;strong>向量存储&lt;/strong>&lt;/td>
&lt;td>别用原生 PG/MySQL 向量扩展做主索引,规模上来性能不行&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>2&lt;/td>
&lt;td>&lt;strong>对话历史&lt;/strong>&lt;/td>
&lt;td>高频追加→单独 &lt;code>messages&lt;/code> 表+定期归档&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>3&lt;/td>
&lt;td>&lt;strong>状态机持久化&lt;/strong> (LangGraph checkpoint)&lt;/td>
&lt;td>高频 UPDATE 同一行 → &lt;code>SKIP LOCKED&lt;/code> + 多分片&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>4&lt;/td>
&lt;td>&lt;strong>审计 / Trace 表&lt;/strong>&lt;/td>
&lt;td>可能比业务表大 10 倍 → 单独 schema + TTL + 列存&lt;/td>
&lt;/tr>
&lt;/tbody>
&lt;/table>
&lt;hr>
&lt;h2 id="速查表">速查表
&lt;/h2>&lt;div class="highlight">&lt;pre tabindex="0" class="chroma">&lt;code class="language-fallback" data-lang="fallback">&lt;span class="line">&lt;span class="cl">┌────────────────────────────────────────────┐
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">│ 创建数据库那一刻要钉死的三件事: │
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">│ │
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">│ VERSION → 5.7 / 8.0 / PG15 / 2019 │
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">│ CHARSET → utf8mb4 (永远,别用 utf8) │
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">│ COLLATION → utf8mb4_0900_ai_ci (8.0) │
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">│ │
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">│ 任何&amp;#34;看起来能跑&amp;#34;的 SQL 上生产前必查: │
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">│ │
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">│ EXPLAIN + CHARSET + COLLATION + TIMEOUT │
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">└────────────────────────────────────────────┘
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;hr>
&lt;p>下一篇：&lt;strong>Tool Calling 工程化&lt;/strong>——从 schema 设计到 Human-in-the-loop 的具体落地。&lt;/p>
&lt;h2 id="参考">参考
&lt;/h2>&lt;ul>
&lt;li>&lt;a class="link" href="https://dev.mysql.com/doc/refman/8.0/en/charset.html" target="_blank" rel="noopener"
>MySQL 8.0 Charset &amp;amp; Collation&lt;/a>&lt;/li>
&lt;li>&lt;a class="link" href="https://www.postgresql.org/docs/current/multibyte.html" target="_blank" rel="noopener"
>PostgreSQL: Character Set Support&lt;/a>&lt;/li>
&lt;li>&lt;a class="link" href="https://www.postgresql.org/docs/current/sql-createdatabase.html" target="_blank" rel="noopener"
>PostgreSQL: CREATE DATABASE&lt;/a>&lt;/li>
&lt;li>&lt;a class="link" href="https://learn.microsoft.com/en-us/sql/relational-databases/collations/collation-and-unicode-support" target="_blank" rel="noopener"
>SQL Server: Collation and Unicode Support&lt;/a>&lt;/li>
&lt;li>&lt;a class="link" href="https://pgloader.io/" target="_blank" rel="noopener"
>pgloader: MySQL → PostgreSQL&lt;/a>&lt;/li>
&lt;/ul></description></item><item><title>Agent 工程实战开篇：从 Demo 到生产还有多远</title><link>https://www.zata.cc/p/agent-%E5%B7%A5%E7%A8%8B%E5%AE%9E%E6%88%98%E5%BC%80%E7%AF%87%E4%BB%8E-demo-%E5%88%B0%E7%94%9F%E4%BA%A7%E8%BF%98%E6%9C%89%E5%A4%9A%E8%BF%9C/index.md/</link><pubDate>Thu, 23 Jul 2026 10:31:54 +0800</pubDate><guid>https://www.zata.cc/p/agent-%E5%B7%A5%E7%A8%8B%E5%AE%9E%E6%88%98%E5%BC%80%E7%AF%87%E4%BB%8E-demo-%E5%88%B0%E7%94%9F%E4%BA%A7%E8%BF%98%E6%9C%89%E5%A4%9A%E8%BF%9C/index.md/</guid><description>&lt;img src="https://www.zata.cc/p/agent-%E5%B7%A5%E7%A8%8B%E5%AE%9E%E6%88%98%E5%BC%80%E7%AF%87%E4%BB%8E-demo-%E5%88%B0%E7%94%9F%E4%BA%A7%E8%BF%98%E6%9C%89%E5%A4%9A%E8%BF%9C/index.md/images/index/index.svg" alt="Featured image of post Agent 工程实战开篇：从 Demo 到生产还有多远" />&lt;blockquote>
&lt;p>这是 Agent 工程实战系列的第一篇。我会把在生产环境里踩过的坑、验证过的方案，按主题整理成可复用的工程手册。&lt;/p>
&lt;/blockquote>
&lt;h2 id="一句话定义-agent-工程">一句话定义 Agent 工程
&lt;/h2>&lt;p>&lt;strong>Agent 工程 = 把一个在本地能跑 80% 的 LLM 循环，变成一个在真实流量下 99% 可用、可观测、可评估、可控成本的服务。&lt;/strong>&lt;/p>
&lt;p>把这句话拆开，至少有五件事要做：&lt;/p>
&lt;ol>
&lt;li>&lt;strong>可控&lt;/strong> — 输出可控、行为可控、失败可控&lt;/li>
&lt;li>&lt;strong>可观测&lt;/strong> — 每一步都留下 trace，事后能复盘&lt;/li>
&lt;li>&lt;strong>可评估&lt;/strong> — 不是&amp;quot;看着还不错&amp;quot;，是有数据证明它确实不错&lt;/li>
&lt;li>&lt;strong>可扩展&lt;/strong> — 用户从 10 个到 10 万个，延迟和成本不会爆&lt;/li>
&lt;li>&lt;strong>可治理&lt;/strong> — 安全、合规、可解释、可下线&lt;/li>
&lt;/ol>
&lt;p>下面聊聊从 Demo 走到生产最容易翻车的几个点。&lt;/p>
&lt;h2 id="1-别再把-prompt-当成代码">1. 别再把 Prompt 当成&amp;quot;代码&amp;quot;
&lt;/h2>&lt;p>Demo 阶段你可以在 &lt;code>system prompt&lt;/code> 里写一段 200 字的话，模型听话；但到了生产：&lt;/p>
&lt;ul>
&lt;li>不同模型的 instruction-following 能力差异巨大&lt;/li>
&lt;li>用户输入会污染 prompt 结构（prompt injection）&lt;/li>
&lt;li>Prompt 一改，行为就会漂移，且没人能精确描述改了什么&lt;/li>
&lt;/ul>
&lt;p>&lt;strong>工程化做法：&lt;/strong>&lt;/p>
&lt;ul>
&lt;li>Prompt 模板独立仓库管理，版本化（每条上线记录到变更日志）&lt;/li>
&lt;li>把&amp;quot;工具说明&amp;quot;、&amp;ldquo;角色设定&amp;rdquo;、&amp;ldquo;业务规则&amp;rdquo;、&amp;ldquo;用户输入&amp;quot;在拼接时显式分段，而不是塞进同一个 f-string&lt;/li>
&lt;li>关键 prompt 用 &lt;strong>LLM 评审 / 回归集&lt;/strong> 守门，而不是靠人肉感觉&lt;/li>
&lt;/ul>
&lt;h2 id="2-tool-calling-不是-if-name--">2. Tool Calling 不是 &lt;code>if name == &amp;quot;...&amp;quot;:&lt;/code>
&lt;/h2>&lt;p>很多 Demo 是这样的：&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" class="chroma">&lt;code class="language-python" data-lang="python">&lt;span class="line">&lt;span class="cl">&lt;span class="k">if&lt;/span> &lt;span class="s2">&amp;#34;天气&amp;#34;&lt;/span> &lt;span class="ow">in&lt;/span> &lt;span class="n">user_input&lt;/span>&lt;span class="p">:&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="k">return&lt;/span> &lt;span class="n">get_weather&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="o">...&lt;/span>&lt;span class="p">)&lt;/span>
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;p>这种&amp;quot;伪 function calling&amp;quot;看起来工作，但生产里会立刻暴露问题：&lt;/p>
&lt;ul>
&lt;li>用户可以构造输入绕过规则&lt;/li>
&lt;li>没有 schema 校验，模型幻觉出来的参数会直接打到下游&lt;/li>
&lt;li>没有权限隔离，所有工具对所有 prompt 开放&lt;/li>
&lt;/ul>
&lt;p>&lt;strong>工程化做法：&lt;/strong>&lt;/p>
&lt;ul>
&lt;li>用真正的 tool/function schema（OpenAI / Anthropic 风格），让模型输出结构化参数&lt;/li>
&lt;li>用 &lt;strong>Pydantic / Zod&lt;/strong> 在边界做参数校验&lt;/li>
&lt;li>工具分级别：&lt;strong>只读工具&lt;/strong> / &lt;strong>写工具&lt;/strong> / &lt;strong>外部副作用工具&lt;/strong>，按风险分桶调用&lt;/li>
&lt;li>高风险工具强制 &lt;strong>Human-in-the-loop&lt;/strong> 确认&lt;/li>
&lt;/ul>
&lt;h2 id="3-agent-loop-必须有上限">3. Agent Loop 必须有上限
&lt;/h2>&lt;p>最经典的 bug：&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" class="chroma">&lt;code class="language-fallback" data-lang="fallback">&lt;span class="line">&lt;span class="cl">用户问了一个含糊的问题 →
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">Agent 没拿到足够信息 →
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">Agent 又去调用工具 →
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">工具又返回模糊结果 →
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">Agent 再调工具 →
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">... → token 烧光 / 循环 100 次 / 超时
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;p>&lt;strong>工程化做法：&lt;/strong>&lt;/p>
&lt;ul>
&lt;li>显式设置 &lt;strong>max_steps&lt;/strong>（建议 ≤ 10）&lt;/li>
&lt;li>区分&amp;quot;成功终止&amp;rdquo;、&amp;ldquo;最大步数终止&amp;rdquo;、&amp;ldquo;循环检测终止&amp;rdquo;&lt;/li>
&lt;li>检测到重复 state（同一组工具调用两次以上）→ 主动 break 并 fallback 到&amp;quot;我不知道&amp;quot;&lt;/li>
&lt;li>关键决策点强制 &lt;strong>reflection / self-critique&lt;/strong>，但要限制其调用次数&lt;/li>
&lt;/ul>
&lt;h2 id="4-可观测性先有-trace再谈优化">4. 可观测性：先有 Trace，再谈优化
&lt;/h2>&lt;p>没有 trace 的 Agent 优化就是&amp;quot;蒙眼调参&amp;quot;。生产里至少要有：&lt;/p>
&lt;ul>
&lt;li>&lt;strong>Span&lt;/strong>：每一次 LLM 调用、工具调用、记忆读写的输入 / 输出 / 耗时 / token&lt;/li>
&lt;li>&lt;strong>Trace ID&lt;/strong>：贯穿一次完整任务的所有 span&lt;/li>
&lt;li>&lt;strong>成本字段&lt;/strong>：每次调用的 input/output token 与单价&lt;/li>
&lt;li>&lt;strong>失败分类&lt;/strong>：timeout / 工具异常 / 解析失败 / 内容安全拦截&lt;/li>
&lt;/ul>
&lt;p>推荐起步就用 &lt;a class="link" href="https://docs.smith.langchain.com/" target="_blank" rel="noopener"
>LangSmith&lt;/a>、&lt;a class="link" href="https://langfuse.com/" target="_blank" rel="noopener"
>Langfuse&lt;/a>、&lt;a class="link" href="https://phoenix.arize.com/" target="_blank" rel="noopener"
>Arize Phoenix&lt;/a> 这类平台之一，自建成本不低。&lt;/p>
&lt;h2 id="5-评估不是上线后再想的事">5. 评估不是上线后再想的事
&lt;/h2>&lt;p>生产里最容易回答不出的问题：&amp;ldquo;这个版本比上个版本到底好了多少？&amp;rdquo;&lt;/p>
&lt;p>&lt;strong>最小可用的评估体系：&lt;/strong>&lt;/p>
&lt;ul>
&lt;li>&lt;strong>离线评测集&lt;/strong>：50~200 个有标准答案或参考回答的 case，回归门禁&lt;/li>
&lt;li>&lt;strong>LLM-as-judge&lt;/strong>：用一个更强的模型给输出打分，但要注意：评分模型本身也会偏&lt;/li>
&lt;li>&lt;strong>线上指标&lt;/strong>：任务成功率、人工接管率、单任务成本、p95 延迟&lt;/li>
&lt;li>&lt;strong>红队 / 注入集&lt;/strong>：专门挑 prompt injection 与越权场景，定期跑&lt;/li>
&lt;/ul>
&lt;h2 id="6-成本治理模型路由--缓存--流式">6. 成本治理：模型路由 + 缓存 + 流式
&lt;/h2>&lt;p>Agent 系统烧钱的速度比传统服务快得多，因为：&lt;/p>
&lt;ul>
&lt;li>多轮 LLM 调用 × 长上下文 × 复杂工具链&lt;/li>
&lt;li>&amp;ldquo;小问题用大模型&amp;quot;是常态浪费&lt;/li>
&lt;/ul>
&lt;p>&lt;strong>工程化做法：&lt;/strong>&lt;/p>
&lt;ul>
&lt;li>&lt;strong>模型路由&lt;/strong>：简单任务走小模型（如分类、提取），复杂任务才走大模型&lt;/li>
&lt;li>&lt;strong>语义缓存&lt;/strong>：相似 query 直接返回上次结果，注意要带 cache key 的失效策略&lt;/li>
&lt;li>&lt;strong>流式输出&lt;/strong>：长生成场景下，TTFT 比总时长更重要&lt;/li>
&lt;li>&lt;strong>预算熔断&lt;/strong>：单任务 / 单用户 / 单租户的 token 上限，超限自动降级&lt;/li>
&lt;/ul>
&lt;h2 id="7-安全默认假设你的-prompt-会被攻破">7. 安全：默认假设你的 Prompt 会被攻破
&lt;/h2>&lt;ul>
&lt;li>&lt;strong>不要&lt;/strong> 把系统提示视为秘密，它一定会被泄露&lt;/li>
&lt;li>&lt;strong>不要&lt;/strong> 让 LLM 决定要不要执行高风险工具（删库、转账、发邮件），必须硬规则守门&lt;/li>
&lt;li>&lt;strong>必须&lt;/strong> 对工具返回值做清洗——它也是用户可控输入&lt;/li>
&lt;li>&lt;strong>必须&lt;/strong> 审计日志：谁、什么时候、通过哪个 agent、调了哪个工具、传了什么参数&lt;/li>
&lt;/ul>
&lt;h2 id="这个系列接下来会写什么">这个系列接下来会写什么？
&lt;/h2>&lt;p>按上面七个主题，每个都会展开成一篇实战文章，配可运行示例：&lt;/p>
&lt;ol>
&lt;li>Prompt 工程化：从 f-string 到模板系统&lt;/li>
&lt;li>Tool Calling 工程化：schema、权限、Human-in-the-loop&lt;/li>
&lt;li>Agent Loop 设计：状态机视角的 ReAct&lt;/li>
&lt;li>可观测性实战：Langfuse / LangSmith 接入&lt;/li>
&lt;li>评估体系搭建：离线集 + LLM-judge + 线上指标&lt;/li>
&lt;li>成本与性能：模型路由、缓存、流式&lt;/li>
&lt;li>安全与合规：Prompt injection 防护与审计&lt;/li>
&lt;/ol>
&lt;p>如果你也在做 Agent，欢迎把你踩过的坑写在评论区——下一篇我会挑留言里出现最多的那个话题。&lt;/p>
&lt;h2 id="参考">参考
&lt;/h2>&lt;ul>
&lt;li>&lt;a class="link" href="https://www.anthropic.com/engineering/building-effective-agents" target="_blank" rel="noopener"
>Anthropic: Building effective agents&lt;/a>&lt;/li>
&lt;li>&lt;a class="link" href="https://platform.openai.com/docs/guides/agents" target="_blank" rel="noopener"
>OpenAI: A practical guide to building agents&lt;/a>&lt;/li>
&lt;li>&lt;a class="link" href="https://docs.smith.langchain.com/" target="_blank" rel="noopener"
>LangSmith Documentation&lt;/a>&lt;/li>
&lt;li>&lt;a class="link" href="https://langfuse.com/" target="_blank" rel="noopener"
>Langfuse: Open Source LLM Engineering&lt;/a>&lt;/li>
&lt;/ul></description></item></channel></rss>