<?xml version="1.0" encoding="utf-8" standalone="yes"?><rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom"><channel><title>缓存 on 小盒子的技术分享</title><link>https://xiaobox.github.io/tags/%E7%BC%93%E5%AD%98/</link><description>Recent content in 缓存 on 小盒子的技术分享</description><generator>Hugo -- gohugo.io</generator><language>zh-cn</language><lastBuildDate>Fri, 06 Mar 2026 03:44:58 +0000</lastBuildDate><atom:link href="https://xiaobox.github.io/tags/%E7%BC%93%E5%AD%98/index.xml" rel="self" type="application/rss+xml"/><item><title>昨天面试官问我：一个 Prompt 进入大模型后，内部到底发生了什么？</title><link>https://xiaobox.github.io/p/2026-03-06-zuo-tian-mian-shi-guan-wen-wo-yi-ge-prompt-jin-ru-da-mo-xing/</link><pubDate>Fri, 06 Mar 2026 03:44:58 +0000</pubDate><guid>https://xiaobox.github.io/p/2026-03-06-zuo-tian-mian-shi-guan-wen-wo-yi-ge-prompt-jin-ru-da-mo-xing/</guid><description>&lt;img src="https://pub-f29bf2b53160470c9a85250116509a24.r2.dev/post/2026-03-06-zuo-tian-mian-shi-guan-wen-wo-yi-ge-prompt-jin-ru-da-mo-xing/cover.jpg" alt="Featured image of post 昨天面试官问我：一个 Prompt 进入大模型后，内部到底发生了什么？" /&gt;&lt;p&gt;昨天面试时，面试官抛给我一道很典型的问题：&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;“描述一下一个请求 prompt 经过 LLM 直到返回结果，这中间的推理过程，越详细越好。”&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;这类题看起来开放，实际上很考验基本功。&lt;/p&gt;
&lt;p&gt;因为它不是在问你会不会背几个名词，而是在看你是否真的理解：&lt;/p&gt;
&lt;p&gt;●一个请求在系统里是怎么流动的&lt;/p&gt;
&lt;p&gt;●进入模型之后到底算了什么&lt;/p&gt;
&lt;p&gt;●为什么大模型是一个 token 一个 token 地往外生成&lt;/p&gt;
&lt;p&gt;●为什么会有 prefill、decode、KV cache、sampling 这些概念&lt;/p&gt;
&lt;p&gt;●为什么工程侧还要引入 batching、FlashAttention、continuous batching 之类的优化&lt;/p&gt;
&lt;p&gt;如果回答得太浅，就会变成泛泛而谈；如果一上来就扎进公式，又很容易失去结构。&lt;/p&gt;
&lt;p&gt;我后来复盘了一下，觉得这道题最好的答法，不是“想到哪说到哪”，而是按一条完整链路去讲：&lt;strong&gt;服务层怎么处理请求，LLM 内部怎么做前向计算，生成阶段又是如何一步步产出结果的&lt;/strong&gt;。 这也是 GPT-3 所代表的自回归语言模型在推理时的基本工作方式：它不会在一次请求里更新参数，而是在固定权重下做前向传播，并逐 token 预测后续内容&lt;/p&gt;
&lt;h2 id="一个高分回答最好先把整体框架立住"&gt;&lt;a href="#%e4%b8%80%e4%b8%aa%e9%ab%98%e5%88%86%e5%9b%9e%e7%ad%94%e6%9c%80%e5%a5%bd%e5%85%88%e6%8a%8a%e6%95%b4%e4%bd%93%e6%a1%86%e6%9e%b6%e7%ab%8b%e4%bd%8f" class="header-anchor"&gt;&lt;/a&gt;一个高分回答，最好先把整体框架立住
&lt;/h2&gt;&lt;p&gt;如果让我在面试里先用一句话概括，我会这样回答：&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;一个 prompt 从输入到输出，大体会经历 6 个阶段：请求封装、tokenization、推理调度、prefill、decode、结果反解码返回。其核心本质是：模型先并行“读懂”整段输入，建立上下文状态和 KV cache，然后再进入自回归生成循环，每次只预测下一个 token。&lt;/strong&gt; 这种“自回归 + 不做本次梯度更新”的推理方式，正是 GPT 类语言模型的基本范式；而 Transformer 则提供了它内部 attention 和前馈网络的计算骨架。&lt;/p&gt;
&lt;p&gt;&lt;img alt="图片" loading="lazy" sizes="(max-width: 767px) calc(100vw - 30px), (max-width: 1023px) 700px, (max-width: 1279px) 950px, 1232px" src="https://pub-f29bf2b53160470c9a85250116509a24.r2.dev/post/2026-03-06-zuo-tian-mian-shi-guan-wen-wo-yi-ge-prompt-jin-ru-da-mo-xing/001-6e34d687.png"&gt;&lt;/p&gt;
&lt;p&gt;这句话为什么重要？&lt;/p&gt;
&lt;p&gt;因为它先把&lt;strong&gt;系统层和模型层&lt;/strong&gt;分开了，也先把&lt;strong&gt;prefill和decode&lt;/strong&gt;分开了。很多人答这道题失分，不是因为不会，而是因为把所有层次混在一起，听起来就没有脉络。&lt;/p&gt;
&lt;h2 id="第一阶段用户输入的-prompt并不是模型真正看到的内容"&gt;&lt;a href="#%e7%ac%ac%e4%b8%80%e9%98%b6%e6%ae%b5%e7%94%a8%e6%88%b7%e8%be%93%e5%85%a5%e7%9a%84-prompt%e5%b9%b6%e4%b8%8d%e6%98%af%e6%a8%a1%e5%9e%8b%e7%9c%9f%e6%ad%a3%e7%9c%8b%e5%88%b0%e7%9a%84%e5%86%85%e5%ae%b9" class="header-anchor"&gt;&lt;/a&gt;第一阶段：用户输入的 Prompt，并不是模型真正看到的内容
&lt;/h2&gt;&lt;p&gt;我们在聊天框里看到的是自然语言，但模型真正接收到的，通常不是这段原始文本本身。&lt;/p&gt;
&lt;p&gt;在送入模型之前，服务层一般会先把 system、user、assistant 等多轮消息按固定模板组织起来，再补上一些特殊标记。随后，文本会经过 tokenizer，被切成 token 序列。像 OpenAI 开源的 tiktoken 就明确说明，它是一个用于模型的 BPE tokenizer。也就是说，对模型来说，文本首先会被变成一串离散的 token IDs，而不是“句子”本身。&lt;/p&gt;
&lt;p&gt;这一层很多人容易忽略，但它很关键。&lt;/p&gt;
&lt;p&gt;因为后面所有推理，都是建立在 token 序列上的。你输入的是一句中文、一段英文、还是一段代码，对模型来说，第一步都得先转换成 token IDs。&lt;/p&gt;
&lt;p&gt;&lt;img alt="图片" loading="lazy" sizes="(max-width: 767px) calc(100vw - 30px), (max-width: 1023px) 700px, (max-width: 1279px) 950px, 1232px" src="https://pub-f29bf2b53160470c9a85250116509a24.r2.dev/post/2026-03-06-zuo-tian-mian-shi-guan-wen-wo-yi-ge-prompt-jin-ru-da-mo-xing/002-48de7d31.png"&gt;&lt;/p&gt;
&lt;h2 id="第二阶段请求不会立刻进模型而是先进入推理服务和调度层"&gt;&lt;a href="#%e7%ac%ac%e4%ba%8c%e9%98%b6%e6%ae%b5%e8%af%b7%e6%b1%82%e4%b8%8d%e4%bc%9a%e7%ab%8b%e5%88%bb%e8%bf%9b%e6%a8%a1%e5%9e%8b%e8%80%8c%e6%98%af%e5%85%88%e8%bf%9b%e5%85%a5%e6%8e%a8%e7%90%86%e6%9c%8d%e5%8a%a1%e5%92%8c%e8%b0%83%e5%ba%a6%e5%b1%82" class="header-anchor"&gt;&lt;/a&gt;第二阶段：请求不会立刻进模型，而是先进入推理服务和调度层
&lt;/h2&gt;&lt;p&gt;在真实工程系统里，一个请求到达后，通常不会马上冲进 GPU 执行。&lt;/p&gt;
&lt;p&gt;它往往还要经过一层推理服务框架，比如 TGI、vLLM 这一类系统。它们会负责请求排队、动态 batching、缓存管理、流式返回等工作。Hugging Face 的 TGI 文档明确把 &lt;strong&gt;continuous batching、token streaming、Flash Attention、Paged Attention&lt;/strong&gt; 等列为核心特性；而 Transformers 的 continuous batching 文档也说明，这种动态调度的目的是提高 GPU 利用率、降低延迟，并允许请求在每一步动态加入和退出批次。&lt;/p&gt;
&lt;p&gt;所以，从系统视角看，链路通常是这样的：&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;用户输入 → prompt 模板展开 → tokenization → 请求调度 / batching → 送入模型&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;这一步的意义在于：&lt;/p&gt;
&lt;p&gt;模型推理不是单个请求的“裸跑”，而是和其他请求一起，由推理引擎统一组织和优化的。&lt;/p&gt;
&lt;p&gt;我们上一阶段说的 tokenization ，&lt;strong&gt;严格来说， 不属于 Transformer 前向推理本身，模型只接收 input_ids。但在现代推理服务里，tokenizer 往往和 serving 引擎绑定在一起，所以工程上看起来像是推理引擎在处理原始字符串。像 vLLM 就同时支持 text prompt 和 pre-tokenized prompt，两种模式都能跑。&lt;/strong&gt;&lt;/p&gt;

 &lt;blockquote&gt;
 &lt;p&gt;用户通常把原始字符串发给后端；后端中的推理服务通常持有 tokenizer，先把字符串编码成 token IDs，再交给模型执行 prefill/decode。只有在某些架构下，tokenization 才会提前在客户端或独立预处理层完成。&lt;/p&gt;

 &lt;/blockquote&gt;
&lt;h2 id="第三阶段进入模型后token-会先变成向量表示"&gt;&lt;a href="#%e7%ac%ac%e4%b8%89%e9%98%b6%e6%ae%b5%e8%bf%9b%e5%85%a5%e6%a8%a1%e5%9e%8b%e5%90%8etoken-%e4%bc%9a%e5%85%88%e5%8f%98%e6%88%90%e5%90%91%e9%87%8f%e8%a1%a8%e7%a4%ba" class="header-anchor"&gt;&lt;/a&gt;第三阶段：进入模型后，token 会先变成向量表示
&lt;/h2&gt;&lt;p&gt;真正进入 LLM 后，第一步不是“开始回答”，而是把 token IDs 映射成高维向量。&lt;/p&gt;
&lt;p&gt;这一步叫 &lt;strong&gt;embedding lookup&lt;/strong&gt;。每个 token 都会查一张巨大的 embedding 表，得到自己的向量表示。到这时，模型才真正进入连续空间的数值计算。Transformer 的基础论文《Attention Is All You Need》所定义的，就是这样一种基于 attention 的序列建模方式。&lt;/p&gt;
&lt;p&gt;不过只有 token 向量还不够，因为模型还得知道“谁在前、谁在后”。&lt;/p&gt;
&lt;p&gt;早期 Transformer 使用位置编码，后来很多大模型会用 &lt;strong&gt;RoPE&lt;/strong&gt;（Rotary Position Embedding）。RoPE 的核心价值，是把位置信息融入 attention 计算中，让模型在处理 token 时同时保留相对位置信息。&lt;/p&gt;
&lt;p&gt;&lt;img alt="图片" loading="lazy" sizes="(max-width: 767px) calc(100vw - 30px), (max-width: 1023px) 700px, (max-width: 1279px) 950px, 1232px" src="https://pub-f29bf2b53160470c9a85250116509a24.r2.dev/post/2026-03-06-zuo-tian-mian-shi-guan-wen-wo-yi-ge-prompt-jin-ru-da-mo-xing/003-100bf39a.png"&gt;&lt;/p&gt;
&lt;h2 id="第四阶段真正的推理核心发生在一层层-transformer-block-里"&gt;&lt;a href="#%e7%ac%ac%e5%9b%9b%e9%98%b6%e6%ae%b5%e7%9c%9f%e6%ad%a3%e7%9a%84%e6%8e%a8%e7%90%86%e6%a0%b8%e5%bf%83%e5%8f%91%e7%94%9f%e5%9c%a8%e4%b8%80%e5%b1%82%e5%b1%82-transformer-block-%e9%87%8c" class="header-anchor"&gt;&lt;/a&gt;第四阶段：真正的“推理核心”发生在一层层 Transformer Block 里
&lt;/h2&gt;&lt;p&gt;这是这道题最核心的部分。&lt;/p&gt;
&lt;p&gt;如果面试官说“越详细越好”，你就必须把 Transformer Block 讲清楚。&lt;/p&gt;
&lt;p&gt;一个典型的 decoder-only LLM，每一层大体都会做两件事：&lt;/p&gt;
&lt;p&gt;●&lt;strong&gt;第一，Self-Attention&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;●&lt;strong&gt;第二，FFN / MLP（前馈网络&lt;/strong&gt;）&lt;/p&gt;
&lt;p&gt;中间再配合残差连接和归一化。Transformer 论文给出的主体结构就是这样。&lt;/p&gt;
&lt;p&gt;你可以把它想成：&lt;/p&gt;
&lt;p&gt;●attention 负责“读群聊”&lt;/p&gt;
&lt;p&gt;●FFN 负责“自己想一想、整理一下”&lt;/p&gt;
&lt;h3 id="self-attention-在干什么"&gt;&lt;a href="#self-attention-%e5%9c%a8%e5%b9%b2%e4%bb%80%e4%b9%88" class="header-anchor"&gt;&lt;/a&gt;Self-Attention 在干什么？
&lt;/h3&gt;&lt;p&gt;可以把它理解成：&lt;strong&gt;当前位置的 token，要去看上下文里哪些 token 最相关。&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;模型会把当前隐藏状态投影成 Query、Key、Value 三组向量，然后通过 Query 和所有 Key 的相似度算出注意力权重，再对 Value 做加权求和。Transformer 论文把它定义为 &lt;strong&gt;Scaled Dot-Product Attention&lt;/strong&gt;。&lt;/p&gt;
&lt;p&gt;对于生成式语言模型，还有一个必须强调的点：causal mask。&lt;/p&gt;
&lt;p&gt;也就是当前位置只能看见自己和前面的 token，不能偷看未来。这一点决定了模型天然是自回归生成的：它永远只能基于已有上下文，去预测下一个 token。GPT-3 论文里所讨论的 few-shot/in-context learning，本质上也是建立在这种&lt;strong&gt;自回归&lt;/strong&gt;预测机制之上的。&lt;/p&gt;
&lt;p&gt;&lt;img alt="图片" loading="lazy" sizes="(max-width: 767px) calc(100vw - 30px), (max-width: 1023px) 700px, (max-width: 1279px) 950px, 1232px" src="https://pub-f29bf2b53160470c9a85250116509a24.r2.dev/post/2026-03-06-zuo-tian-mian-shi-guan-wen-wo-yi-ge-prompt-jin-ru-da-mo-xing/004-a52cdfd4.png"&gt;&lt;/p&gt;
&lt;p&gt;关于 Q、K、V，可以简单这样理解：&lt;/p&gt;
&lt;p&gt;Q = 我现在想找什么&lt;/p&gt;
&lt;p&gt;K = 每个词身上贴的“索引标签”&lt;/p&gt;
&lt;p&gt;V = 每个词真正携带、可被取走的信息。&lt;/p&gt;
&lt;p&gt;最通俗的比喻是“图书馆检索”：&lt;/p&gt;

 &lt;blockquote&gt;
 &lt;p&gt;你现在脑子里有一个问题，这就是 Q（Query）；书架上每本书卡片上的主题标签，是 K（Key）；书里真正的内容，是 V（Value）。系统先拿你的问题 Q 去和所有标签 K 比一比，看看“像不像、相关不相关”；相关度高的那些书，它们的内容 V 就会被更多地取出来，最后合成当前这一步该看的信息。Transformer 论文对 attention 的定义，本质上就是“一个 query 对一组 key-value 对做匹配，输出是 values 的加权和”。&lt;/p&gt;

 &lt;/blockquote&gt;
&lt;h3 id="ffn-又在干什么"&gt;&lt;a href="#ffn-%e5%8f%88%e5%9c%a8%e5%b9%b2%e4%bb%80%e4%b9%88" class="header-anchor"&gt;&lt;/a&gt;FFN 又在干什么？
&lt;/h3&gt;&lt;p&gt;如果说 attention 负责“从上下文搬运信息”，那么 FFN 更像是“对当前位置做进一步加工”。&lt;/p&gt;
&lt;p&gt;它不会跨位置交互，而是对每个 token 的表示单独做非线性变换，把特征进一步提纯和增强。Transformer 论文把它称为 position-wise feed-forward network。&lt;/p&gt;
&lt;p&gt;所以一个 Transformer Block 可以粗略理解成：&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;先决定我该关注上下文里的谁，再把取回来的信息做一轮更深的特征变换&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;&lt;img alt="图片" loading="lazy" sizes="(max-width: 767px) calc(100vw - 30px), (max-width: 1023px) 700px, (max-width: 1279px) 950px, 1232px" src="https://pub-f29bf2b53160470c9a85250116509a24.r2.dev/post/2026-03-06-zuo-tian-mian-shi-guan-wen-wo-yi-ge-prompt-jin-ru-da-mo-xing/005-ea012f28.png"&gt;&lt;/p&gt;
&lt;p&gt;注意在整个流程中，&lt;strong&gt;prefill 和 decode 阶段，都要做 self-attention 和 FFN。&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;但要分清楚：“都要做”不等于“做法完全一样”。&lt;/p&gt;
&lt;p&gt;●Prefill 把整段 prompt 一次性送进去。 这时每一层都会对这批 token 做 masked self-attention，然后再过 FFN。因为整段 prompt 一开始就都已知，所以这一步可以在单个请求内部并行处理很多 token。Hugging Face 对 prefill 的描述也是：prefill 会处理整段输入，并建立 KV cache。&lt;/p&gt;
&lt;p&gt;●Decode 开始一个 token 一个 token 往后生成。 这时每生成一个新 token，它仍然要在每一层里经过：一次 self-attention，一次 FFN&lt;/p&gt;
&lt;p&gt;decode 不是把旧 token 全部再跑一遍 attention 和 FFN。有了 KV cache 后，旧 token 的 K/V 会被缓存起来；新 token 到来时，只需要为这个新 token 计算当前层需要的表示，再和历史 K/V 做注意力计算，然后继续过 FFN。Hugging Face 官方缓存文档明确说了：后续生成时，只传入尚未处理的新 token，并把 key/value 写入和读取自 cache。&lt;/p&gt;

 &lt;blockquote&gt;
 &lt;p&gt;FFN 就是 Transformer 每层里、紧跟在 self-attention 后面的前馈网络，本质上是对每个 token 单独做的 MLP 加工。在标准 LLM 里，prefill 和 decode 两个阶段都要经过 self-attention 和 FFN；区别只是 prefill 处理整段已知 token，decode 只处理当前新 token，并复用历史 KV cache&lt;/p&gt;

 &lt;/blockquote&gt;
&lt;h2 id="第五阶段prefill先把整段-prompt-读完"&gt;&lt;a href="#%e7%ac%ac%e4%ba%94%e9%98%b6%e6%ae%b5prefill%e5%85%88%e6%8a%8a%e6%95%b4%e6%ae%b5-prompt-%e8%af%bb%e5%ae%8c" class="header-anchor"&gt;&lt;/a&gt;第五阶段：Prefill——先把整段 Prompt “读完”
&lt;/h2&gt;&lt;p&gt;很多人会误以为模型一进来就开始逐字生成。&lt;/p&gt;
&lt;p&gt;其实不是。生成前通常会先有一个很重要的阶段：&lt;strong&gt;Prefill&lt;/strong&gt;。&lt;/p&gt;
&lt;p&gt;Prefill 的意思是：&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;先把整段 prompt 一次性跑完整个前向过程。&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;在这个阶段，模型会为输入中的所有 token 计算各层隐藏状态，并且生成后面 decode 要用到的 KV cache。Hugging Face 的缓存文档明确指出，KV cache 会把注意力层中之前 token 产生的 key-value 对存下来，后续生成时直接复用，从而避免重复计算。&lt;/p&gt;
&lt;p&gt;Prefill 的一个重要特点是：&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;它通常可以高度并行。&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;因为整段输入已经完整给定了，GPU 能把很多矩阵操作一起做完。所以 prefill 更像“先整体读题”，吞吐通常更高。vLLM 文档也明确把 prefill 归类为更偏 compute-bound 的阶段&lt;/p&gt;
&lt;p&gt;你可以把 prefill 想象成一个正在考试的人，prefill 就是他正在读题，把题目先读到脑子里，填充好上下文，然后再开始做答（输出 token）&lt;/p&gt;
&lt;p&gt;&lt;img alt="图片" loading="lazy" sizes="(max-width: 767px) calc(100vw - 30px), (max-width: 1023px) 700px, (max-width: 1279px) 950px, 1232px" src="https://pub-f29bf2b53160470c9a85250116509a24.r2.dev/post/2026-03-06-zuo-tian-mian-shi-guan-wen-wo-yi-ge-prompt-jin-ru-da-mo-xing/006-a96fea1e.png"&gt;&lt;/p&gt;
&lt;h2 id="第六阶段kv-cache为什么不会每次都重算全文"&gt;&lt;a href="#%e7%ac%ac%e5%85%ad%e9%98%b6%e6%ae%b5kv-cache%e4%b8%ba%e4%bb%80%e4%b9%88%e4%b8%8d%e4%bc%9a%e6%af%8f%e6%ac%a1%e9%83%bd%e9%87%8d%e7%ae%97%e5%85%a8%e6%96%87" class="header-anchor"&gt;&lt;/a&gt;第六阶段：KV Cache——为什么不会每次都重算全文
&lt;/h2&gt;&lt;p&gt;这部分是面试里非常加分的点。&lt;/p&gt;
&lt;p&gt;因为它体现你不只懂“算法”，还懂“推理为什么能跑得起来”。&lt;/p&gt;
&lt;p&gt;如果没有 KV cache，那么每生成一个新 token，模型都要把整个历史上下文从头再算一遍，成本会非常高。&lt;/p&gt;
&lt;p&gt;而有了 KV cache 后，历史 token 在每层 attention 中算出的 K 和 V 都会被缓存起来。下一个时间步只需要为新 token 计算新的 Query、Key、Value，再用新的 Query 去和历史缓存里的 Key 做匹配即可。Hugging Face 的官方文档把这一点解释得很清楚：KV cache 的目标就是消除重复计算，加速自回归生成。&lt;/p&gt;
&lt;p&gt;一句话说明就是：&lt;/p&gt;
&lt;p&gt;●&lt;strong&gt;没有 KV cache，像每次都重读整篇文章&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;●&lt;strong&gt;有 KV cache，则像前文已经做好笔记，现在只补最后一句。&lt;/strong&gt;&lt;/p&gt;
&lt;h3 id="为什么-kv-cache-只缓存-k-和-v而不缓存-q"&gt;&lt;a href="#%e4%b8%ba%e4%bb%80%e4%b9%88-kv-cache-%e5%8f%aa%e7%bc%93%e5%ad%98-k-%e5%92%8c-v%e8%80%8c%e4%b8%8d%e7%bc%93%e5%ad%98-q" class="header-anchor"&gt;&lt;/a&gt;为什么 KV cache 只缓存 K 和 V，而不缓存 Q？
&lt;/h3&gt;&lt;p&gt;&lt;strong&gt;一个东西值不值得缓存，不看它“重不重要”，而看它“后面还会不会再次被用到”。&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;KV cache 只缓存 K 和 V，不缓存 Q，不是因为 Q 不重要，而是因为 Q “只在当前这一步有用一次”；而 K、V 会在后面每一步继续被反复用到。 这正是 Hugging Face 官方对缓存机制的解释：过去 token 的 K 和 V 可以缓存并复用，而在推理时，只需要“最后一个 token 的 query”来计算当前步的表示。&lt;/p&gt;
&lt;h2 id="第七阶段decode开始逐-token-生成答案"&gt;&lt;a href="#%e7%ac%ac%e4%b8%83%e9%98%b6%e6%ae%b5decode%e5%bc%80%e5%a7%8b%e9%80%90-token-%e7%94%9f%e6%88%90%e7%ad%94%e6%a1%88" class="header-anchor"&gt;&lt;/a&gt;第七阶段：Decode——开始逐 token 生成答案
&lt;/h2&gt;&lt;p&gt;当 prefill 完成后，模型已经“读懂”了整段输入。&lt;/p&gt;
&lt;p&gt;接下来，系统会取最后一个位置的隐藏状态，通过输出层映射成整个词表上的 logits，也就是“下一个 token 的打分”。随后再通过 softmax 和解码策略，决定下一个 token 输出什么。Transformer 的输出逻辑与 Hugging Face 的生成文档都说明了这一点。&lt;/p&gt;
&lt;p&gt;这里又有一个容易被问到的点：&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;下一个 token 是怎么选出来的？&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;并不是只有“选概率最大”这一种方式。常见解码策略包括 greedy、sampling、top-k、top-p 等。不同策略会影响文本的稳定性、多样性和创造性。Hugging Face 的生成策略文档对此有系统说明。&lt;/p&gt;
&lt;p&gt;然后，流程进入一个循环：&lt;/p&gt;
&lt;p&gt;●把刚生成的 token 接到上下文后面&lt;/p&gt;
&lt;p&gt;●复用 KV cache&lt;/p&gt;
&lt;p&gt;●只为这个新 token 跑一遍前向计算&lt;/p&gt;
&lt;p&gt;●再得到新的 logits&lt;/p&gt;
&lt;p&gt;●再生成下一个 token&lt;/p&gt;
&lt;p&gt;&lt;img alt="图片" loading="lazy" sizes="(max-width: 767px) calc(100vw - 30px), (max-width: 1023px) 700px, (max-width: 1279px) 950px, 1232px" src="https://pub-f29bf2b53160470c9a85250116509a24.r2.dev/post/2026-03-06-zuo-tian-mian-shi-guan-wen-wo-yi-ge-prompt-jin-ru-da-mo-xing/007-469317f3.png"&gt;&lt;/p&gt;
&lt;p&gt;这就是为什么你看到的大模型回答，总是一个 token 一个 token 流式地吐出来，而不是整段瞬间出现。&lt;/p&gt;
&lt;h2 id="为什么第一个字慢后面快"&gt;&lt;a href="#%e4%b8%ba%e4%bb%80%e4%b9%88%e7%ac%ac%e4%b8%80%e4%b8%aa%e5%ad%97%e6%85%a2%e5%90%8e%e9%9d%a2%e5%bf%ab" class="header-anchor"&gt;&lt;/a&gt;为什么“第一个字慢，后面快”？
&lt;/h2&gt;&lt;p&gt;这也是一个非常像面试 follow-up 的问题。&lt;/p&gt;
&lt;p&gt;很多候选人知道 prefill 和 decode，但解释不清为什么两者速度特征不同。&lt;/p&gt;
&lt;p&gt;vLLM 的优化文档明确提到，&lt;strong&gt;prefill 更偏 compute-bound，decode 更偏 memory-bound。&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;原因在于：prefill 可以把整段输入并行做大矩阵乘法，吃满 GPU 算力；而 decode 虽然每步只算一个 token，但它强依赖历史 KV cache，频繁访问显存，并且步骤之间有严格的顺序依赖。&lt;/p&gt;
&lt;p&gt;这也是为什么工程上会有很多针对推理性能的优化，比如：&lt;/p&gt;
&lt;p&gt;●FlashAttention：通过 IO-aware 的 attention 计算方式，减少显存读写&lt;/p&gt;
&lt;p&gt;●continuous batching：动态调整批次，减少 GPU 空转&lt;/p&gt;
&lt;p&gt;●chunked prefill / Paged Attention：改进长上下文和缓存管理效率&lt;/p&gt;
&lt;p&gt;要注意，这些技术优化的是执行效率，不是模型的“语义本质”。模型本质上做的事情仍然是：基于已有上下文，反复预测下一个 token&lt;/p&gt;
&lt;p&gt;我现在觉得，这道题最稳妥的回答方式，就是最后收束成一句话：&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;一个 LLM 请求的推理过程，本质上是：先把 prompt 模板化并 token 化，经由推理服务调度进入 GPU；模型通过 embedding 和多层 Transformer block 并行完成 prefill，建立上下文表示和 KV cache；随后进入 decode 循环，基于历史缓存逐 token 执行注意力、前馈网络和采样，直到生成结束，再把 token 序列反解码成文本返回。 这条链路同时体现了 Transformer 的计算机制、自回归生成范式，以及现代推理系统在 batching、缓存和 attention kernel 上的工程优化&lt;/strong&gt;&lt;/p&gt;
&lt;h2 id="看起来都是推理引擎的活儿啊"&gt;&lt;a href="#%e7%9c%8b%e8%b5%b7%e6%9d%a5%e9%83%bd%e6%98%af%e6%8e%a8%e7%90%86%e5%bc%95%e6%93%8e%e7%9a%84%e6%b4%bb%e5%84%bf%e5%95%8a" class="header-anchor"&gt;&lt;/a&gt;看起来都是推理引擎的活儿啊？
&lt;/h2&gt;&lt;p&gt;从整个流程上看，几乎都是推理引擎在负责，所以可以这么理解，但要再往前走半步：&lt;/p&gt;
&lt;p&gt;●从“流程编排”角度看，LLM 本体确实很被动；&lt;/p&gt;
&lt;p&gt;●从“核心计算与语义生成”角度看，LLM 才是全链路里最不可替代的部分。&lt;/p&gt;
&lt;p&gt;如果把整个链路拆开，职责大致是这样的：&lt;/p&gt;
&lt;p&gt;1.&lt;strong&gt;推理引擎 / serving 系统负责&lt;/strong&gt;：接 HTTP 请求、做 tokenization / 输入处理、调度 batching、管理 KV cache、协调 GPU worker、流式返回结果、做一部分采样与系统优化。vLLM 的官方文档甚至把这几层写得很直白：最少会有 1 个 API server 负责 HTTP、tokenization 和输入处理，1 个 engine core 负责 scheduler 和 KV cache 管理，再加上 N 个 GPU worker 负责执行模型前向计算。&lt;/p&gt;
&lt;p&gt;2.&lt;strong&gt;LLM 模型本体负责&lt;/strong&gt;：对 input_ids 做 embedding，经过多层 Transformer block 的 self-attention 和 feed-forward network，输出 logits，也就是“下一个 token 的分数分布”。Transformer 论文给出的核心结构就是 attention + FFN；Transformers 文档也明确说 causal language modeling 本质上是在左侧上下文条件下做 next-token prediction，而模型输出里的 logits 是对词表中每个 token 的预测分数。&lt;/p&gt;
&lt;p&gt;所以，**推理引擎决定“怎么高效地跑”，模型决定“到底生成什么”。**前者偏“编排与优化”，后者偏“语义计算与内容生成”&lt;/p&gt;
&lt;p&gt;&lt;img alt="图片" loading="lazy" sizes="(max-width: 767px) calc(100vw - 30px), (max-width: 1023px) 700px, (max-width: 1279px) 950px, 1232px" src="https://pub-f29bf2b53160470c9a85250116509a24.r2.dev/post/2026-03-06-zuo-tian-mian-shi-guan-wen-wo-yi-ge-prompt-jin-ru-da-mo-xing/008-d8a4beaa.png"&gt;&lt;/p&gt;</description></item><item><title>拒绝内卷！为什么我们应该抵制用 LeetCode 考查真实的工程师？</title><link>https://xiaobox.github.io/p/2026-03-04-ju-jue-nei-juan-wei-shen-me-wo-men-ying-gai-di-zhi-yong-leet/</link><pubDate>Wed, 04 Mar 2026 08:41:32 +0000</pubDate><guid>https://xiaobox.github.io/p/2026-03-04-ju-jue-nei-juan-wei-shen-me-wo-men-ying-gai-di-zhi-yong-leet/</guid><description>&lt;img src="https://pub-f29bf2b53160470c9a85250116509a24.r2.dev/post/2026-03-04-ju-jue-nei-juan-wei-shen-me-wo-men-ying-gai-di-zhi-yong-leet/cover.jpg" alt="Featured image of post 拒绝内卷！为什么我们应该抵制用 LeetCode 考查真实的工程师？" /&gt;&lt;h1 id="拒绝内卷为什么我们应该抵制用-leetcode-考查真实的工程师"&gt;&lt;a href="#%e6%8b%92%e7%bb%9d%e5%86%85%e5%8d%b7%e4%b8%ba%e4%bb%80%e4%b9%88%e6%88%91%e4%bb%ac%e5%ba%94%e8%af%a5%e6%8a%b5%e5%88%b6%e7%94%a8-leetcode-%e8%80%83%e6%9f%a5%e7%9c%9f%e5%ae%9e%e7%9a%84%e5%b7%a5%e7%a8%8b%e5%b8%88" class="header-anchor"&gt;&lt;/a&gt;拒绝内卷！为什么我们应该抵制用 LeetCode 考查真实的工程师？
&lt;/h1&gt;&lt;p&gt;如果你要招募一位主刀医生，你会让他当场默写《人体解剖学》的第一章吗？如果你要找一位米其林大厨，你会蒙住他的眼睛，让他比赛在一分钟内切出多少根标准厚度的土豆丝吗？&lt;/p&gt;
&lt;p&gt;显然不会。但在如今的软件工程招聘中，我们却在做着同样荒谬的事情：让那些在复杂的业务泥潭中摸爬滚打、主导过千万级并发系统、熟练操纵复杂云原生架构的资深工程师，站在白板前，徒手写出一个“翻转二叉树”或者“接雨水”的最佳时间复杂度解法。&lt;/p&gt;
&lt;p&gt;不知从何时起，“刷 LeetCode”已经从一种思维训练，演变成了一场病态的军备竞赛。是时候戳破这个泡沫了：&lt;strong&gt;LeetCode 根本选拔不出优秀的软件工程师，它正在毁掉我们的行业生态。&lt;/strong&gt;&lt;/p&gt;
&lt;hr&gt;
&lt;h3 id="一-真实的工程世界从来不是一道闭卷考试"&gt;&lt;a href="#%e4%b8%80-%e7%9c%9f%e5%ae%9e%e7%9a%84%e5%b7%a5%e7%a8%8b%e4%b8%96%e7%95%8c%e4%bb%8e%e6%9d%a5%e4%b8%8d%e6%98%af%e4%b8%80%e9%81%93%e9%97%ad%e5%8d%b7%e8%80%83%e8%af%95" class="header-anchor"&gt;&lt;/a&gt;一、 真实的工程世界，从来不是一道“闭卷考试”
&lt;/h3&gt;&lt;p&gt;让我们先来看看，一个现代软件工程师的真实一天是怎样度过的。&lt;/p&gt;
&lt;p&gt;你可能会花一整个上午，在一堆没有注释的“屎山”代码中追踪一个诡异的内存泄漏问题；你可能会在下午和产品经理反复拉扯，确定一个新功能在微服务架构下的 API 边界；你可能会在排查为什么 Kubernetes 集群里的 HPA（水平Pod自动扩缩容）没有按预期触发，或者研究 Istio 网关的流量路由策略。&lt;/p&gt;
&lt;p&gt;如果你身处最前沿的 AI 领域，你可能正在评估是用 LangGraph 还是 AutoGen 来构建多 Agent 协同流，或者在调试大模型 API 的 Top-p 采样参数，试图让生成的回答既准确又具有随机性。甚至，在业余时间，你可能在设计一款解决自己痛点的小工具——比如一个用来清理、分类和管理繁杂书签的浏览器插件。&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;这些工作有一个共同点：它们都是极其复杂的、高度依赖上下文的、开放性的问题。&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;而在真实的工作环境中，我们解决这些问题依靠的是什么？&lt;/p&gt;
&lt;p&gt;1.&lt;strong&gt;查阅文档与搜索能力：&lt;/strong&gt; 我们有 Google、有官方文档、有开源社区，甚至现在还有 AI 助手。&lt;/p&gt;
&lt;p&gt;2.&lt;strong&gt;调试与试错能力：&lt;/strong&gt; 我们通过打日志、单步调试、看监控指标来定位问题。&lt;/p&gt;
&lt;p&gt;3.&lt;strong&gt;架构视野与经验直觉：&lt;/strong&gt; 我们知道什么时候该用单例模式，什么时候该用工厂方法；我们知道在高并发下如何设计缓存策略，如何保证数据一致性。&lt;/p&gt;
&lt;p&gt;4.&lt;strong&gt;沟通与协作：&lt;/strong&gt; 我们需要阅读别人的代码，也需要让别人看懂我们的设计。&lt;/p&gt;
&lt;p&gt;反观 LeetCode 面试，它创造了一个极其不真实的&lt;strong&gt;无菌实验室环境&lt;/strong&gt;：&lt;/p&gt;
&lt;p&gt;●题目边界清晰，输入输出明确。&lt;/p&gt;
&lt;p&gt;●只有单一的“最优解”（通常是时间复杂度和空间复杂度的极限）。&lt;/p&gt;
&lt;p&gt;●不允许查阅文档，甚至不允许使用趁手的 IDE（有时只能在网页的纯文本框里写代码）。&lt;/p&gt;
&lt;p&gt;●偏离日常使用的技术栈（你可能用 Python 写了十几年业务，却要用 C++ 的思维去考虑指针和内存管理）。&lt;/p&gt;
&lt;p&gt;这就像是要求一个现代战争中的王牌飞行员，在面试时去比拼谁的射箭准头更好。它考察的不是“解决问题的能力”，而是“在极其受限条件下的默写能力”。&lt;/p&gt;
&lt;hr&gt;
&lt;h3 id="二-刷题面试正在惩罚真正有经验的老兵"&gt;&lt;a href="#%e4%ba%8c-%e5%88%b7%e9%a2%98%e9%9d%a2%e8%af%95%e6%ad%a3%e5%9c%a8%e6%83%a9%e7%bd%9a%e7%9c%9f%e6%ad%a3%e6%9c%89%e7%bb%8f%e9%aa%8c%e7%9a%84%e8%80%81%e5%85%b5" class="header-anchor"&gt;&lt;/a&gt;二、 刷题面试，正在惩罚真正有经验的“老兵”
&lt;/h3&gt;&lt;p&gt;在软件开发领域，经验是一笔巨大的财富。一个拥有 10 年、15 年工作经验的研发架构师，他最大的价值并不在于写代码的速度有多快，而在于他&lt;strong&gt;踩过足够多的坑&lt;/strong&gt;。&lt;/p&gt;
&lt;p&gt;资深工程师知道，一个系统最大的危机往往不是算法复杂度从 变成了 （很多时候硬件资源和缓存机制完全能弥补），而是：&lt;/p&gt;
&lt;p&gt;●数据库连接池配置不当导致的雪崩。&lt;/p&gt;
&lt;p&gt;●缺乏熔断降级机制导致的服务级联故障。&lt;/p&gt;
&lt;p&gt;●领域模型设计错误导致的后续需求无法扩展。&lt;/p&gt;
&lt;p&gt;●业务逻辑耦合过深导致的测试困难。&lt;/p&gt;
&lt;p&gt;然而，当这位资深架构师带着一身的实战本领走进面试房间时，等待他的却是一道“动态规划（DP）”的 Hard 题。&lt;/p&gt;
&lt;p&gt;这是一种极大的资源浪费。一个能在生产环境中稳稳掌控全局、能设计出高可用 AI 基础设施、能带领团队攻坚克难的资深人才，仅仅因为最近几个月忙于项目交付、或者忙于应对生活中的变故（比如寻找新机会、照顾家庭），没有抽出几百个小时去死记硬背算法题库，就被无情地贴上“技术不过关”的标签淘汰出局。&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;这种现象导致了一个极其荒谬的倒挂：&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;那些刚刚毕业、没有写过一行生产环境代码、不懂得什么是持续集成、不知道如何进行线上排障的学生，只要花三个月把 LeetCode 刷个滚瓜烂熟，就能在面试中大杀四方；而那些真正在一线扛过枪、打过仗，能够解决复杂工程灾难的老兵，却在白板前因为忘记了一个状态转移方程而涨红了脸。&lt;/p&gt;
&lt;p&gt;企业以为自己招到了“绝顶聪明”的天才，结果新人一入职，面对极其复杂的微服务依赖和一团乱麻的业务逻辑，立刻束手无策。因为真实的业务系统里，没有人会为你准备好整洁的 &lt;code&gt;ListNode&lt;/code&gt; 或者 &lt;code&gt;TreeNode&lt;/code&gt;。&lt;/p&gt;
&lt;hr&gt;
&lt;h3 id="三-算法题面试的本质一场低效的智商服从性测试"&gt;&lt;a href="#%e4%b8%89-%e7%ae%97%e6%b3%95%e9%a2%98%e9%9d%a2%e8%af%95%e7%9a%84%e6%9c%ac%e8%b4%a8%e4%b8%80%e5%9c%ba%e4%bd%8e%e6%95%88%e7%9a%84%e6%99%ba%e5%95%86%e6%9c%8d%e4%bb%8e%e6%80%a7%e6%b5%8b%e8%af%95" class="header-anchor"&gt;&lt;/a&gt;三、 算法题面试的本质：一场低效的“智商服从性测试”
&lt;/h3&gt;&lt;p&gt;为什么即便怨声载道，这么多公司依然痴迷于 LeetCode 面试？很多面试官会辩解说：“算法题能考察候选人的聪明程度和逻辑思维。”&lt;/p&gt;
&lt;p&gt;这其实是一个伪命题。&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;1. 算法题早就不测智商了，它只测“准备度”。&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;在互联网早期，用算法题面试确实能筛选出一些思维敏捷的人，因为那时没有题库。但现在，LeetCode 已经有上千道题，“面经”满天飞。面试不仅变成了开卷考试的闭卷化，更变成了一门应试产业。能解出 Hard 题，往往不意味着你绝顶聪明，只意味着你刷到过原题，或者你花了大把时间去背诵套路。这充其量是一场“服从性测试”——看候选人愿不愿意为了这份工作去吃毫无意义的苦。&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;2. 忽视了工程中最关键的“可维护性”。&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;在 LeetCode 的评价体系里，“代码跑得快”是唯一的真理。哪怕你的代码里全是 &lt;code&gt;i&lt;/code&gt;, &lt;code&gt;j&lt;/code&gt;, &lt;code&gt;k&lt;/code&gt;, &lt;code&gt;dp&lt;/code&gt;, &lt;code&gt;res&lt;/code&gt; 这种毫无语义的变量名，哪怕你的逻辑晦涩难懂如天书，只要能 AC（Accepted），你就是赢家。&lt;/p&gt;
&lt;p&gt;但在实际工程中，这种代码是灾难。好的工程师写出的代码是给人看的，其次才是给机器执行的。如果你的代码在生产环境中出了 Bug，同事半夜被叫醒排查，看到满屏追求极致技巧却毫无注释的“炫技代码”，他大概率会在心里把你骂上一万遍。LeetCode 培养出的“做题家”思维，与团队协作所需的工程素养往往是背道而驰的。&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;3. 面试官的“安全牌”与偷懒。&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;其实，很多面试官也根本不知道该怎么面试。对他们来说，从题库里随机抽一道题扔给候选人，是最省事、最没有风险的做法。如果你没写出来，那是你不行，面试官不需要承担招错人的责任。这种做法掩盖了面试官自身架构视野和识人能力的匮乏。要深入了解一个人的项目经验、技术深度和系统设计能力，需要面试官投入极大的精力和极高的技术水平去进行深度的技术探讨，而“考一道题”则轻易地把压力全抛给了候选人。&lt;/p&gt;
&lt;hr&gt;
&lt;h3 id="四-如何打破僵局回归工程本质的面试方法"&gt;&lt;a href="#%e5%9b%9b-%e5%a6%82%e4%bd%95%e6%89%93%e7%a0%b4%e5%83%b5%e5%b1%80%e5%9b%9e%e5%bd%92%e5%b7%a5%e7%a8%8b%e6%9c%ac%e8%b4%a8%e7%9a%84%e9%9d%a2%e8%af%95%e6%96%b9%e6%b3%95" class="header-anchor"&gt;&lt;/a&gt;四、 如何打破僵局：回归工程本质的面试方法
&lt;/h3&gt;&lt;p&gt;批判之后，我们需要建设。如果不考 LeetCode，我们该怎么筛选优秀的软件工程师？真正的面试，应该是一场对日常工作的高度模拟。&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;1. 结对编程 (Pair Programming)&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;不要让候选人在白板上写代码，给他一台配置好 IDE 的电脑。面试官准备一个真实但简化过的业务小项目，或者直接在公司的一个开源代码分支上，两人结对协作。&lt;/p&gt;
&lt;p&gt;●“我们现在有一个 Python 的服务端，用 FastAPI 写的，现在需要增加一个中间件来做简单的限流，你打算怎么做？”&lt;/p&gt;
&lt;p&gt;●允许候选人查阅文档，允许使用 Google。&lt;/p&gt;
&lt;p&gt;●观察他的编码习惯、他对框架的熟悉程度、他如何拆解问题，以及更重要的——他如何与你沟通和协作。&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;2. 代码审查 (Code Review)&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;给候选人一段存在各种“坑”的代码（可以是以前团队写出的真实烂代码，隐去敏感信息）。这段代码可能存在并发竞争、内存泄漏、或者设计模式的滥用。&lt;/p&gt;
&lt;p&gt;让候选人进行 Code Review。优秀的工程师能立刻嗅出代码中的“坏味道”，并提出合理的重构建议。这比让他默写快速排序要有效得多。&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;3. 深度系统设计与项目复盘&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;抛弃那些假大空的“如何设计一个推特”的八股文。让候选人深度讲解他简历中最自豪的一个项目。&lt;/p&gt;
&lt;p&gt;●“你在简历中提到主导了容器化改造，能画一下当时的 Kubernetes 架构图吗？”&lt;/p&gt;
&lt;p&gt;●“在使用 Ingress 和服务网格（比如 APISIX 或 Istio）时，你们遇到了什么性能瓶颈？是如何排查的？”&lt;/p&gt;
&lt;p&gt;●“你提到在做 AI 相关的研发，在整合底层大模型接口时，你们是如何处理长上下文带来的延迟问题和 token 消耗的？”&lt;/p&gt;
&lt;p&gt;通过深度的追问，直到触及他的知识边界。真正的行家，在谈论自己亲手一砖一瓦建起来的系统时，眼里是有光的，细节是经得起推敲的。&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;4. 聊聊他创造的“小玩意儿”&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;一个真正的工程师，往往是对技术充满热情的创造者。与其问算法，不如问问他平时都在折腾什么。如果他告诉你，他因为受不了浏览器书签太乱，正在自己设计开发一个管理书签的插件；或者他为了解某种新技术栈，自己搭了一个爬虫和数据展示网站。请让他展示一下！这种对痛点的敏锐察觉和动手解决问题的能力，是任何算法题都无法衡量出的核心特质。&lt;/p&gt;
&lt;hr&gt;
&lt;h3 id="五-结语放过工程师也放过企业自己"&gt;&lt;a href="#%e4%ba%94-%e7%bb%93%e8%af%ad%e6%94%be%e8%bf%87%e5%b7%a5%e7%a8%8b%e5%b8%88%e4%b9%9f%e6%94%be%e8%bf%87%e4%bc%81%e4%b8%9a%e8%87%aa%e5%b7%b1" class="header-anchor"&gt;&lt;/a&gt;五、 结语：放过工程师，也放过企业自己
&lt;/h3&gt;&lt;p&gt;技术招聘走到今天“无算法不面试”的地步，是整个行业的悲哀。它消耗了工程师们原本可以用来学习新框架、钻研底层原理、甚至陪伴家人的宝贵精力；它也让企业错失了大量踏实肯干、经验丰富的实战派人才。&lt;/p&gt;
&lt;p&gt;编程，是一门结合了逻辑、工程、设计甚至艺术的创造性活动。它不该被简化为一场机械的背诵比赛。&lt;/p&gt;
&lt;p&gt;作为面试官，下次当你准备掏出一道 LeetCode Hard 题时，不妨停下来问问自己：“这道题，真的能帮我找到那个能和我并肩作战、一起扛住双十一流量洪峰、一起在深夜排查诡异 Bug 的可靠队友吗？”&lt;/p&gt;
&lt;p&gt;如果不能，请放下那道该死的算法题，和候选人像真正的工程师一样，聊聊真实的架构，看看真实的代码。&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;把时间还给工程，把尊严还给工程师。&lt;/strong&gt;&lt;/p&gt;</description></item><item><title>一文讲透 GoF 的 23 种设计模式之单例</title><link>https://xiaobox.github.io/p/2026-02-25-yi-wen-jiang-tou-gof-de-23-zhong-she-ji-mo-shi-zhi-dan-li/</link><pubDate>Wed, 25 Feb 2026 10:13:49 +0000</pubDate><guid>https://xiaobox.github.io/p/2026-02-25-yi-wen-jiang-tou-gof-de-23-zhong-she-ji-mo-shi-zhi-dan-li/</guid><description>&lt;img src="https://pub-f29bf2b53160470c9a85250116509a24.r2.dev/post/2026-02-25-yi-wen-jiang-tou-gof-de-23-zhong-she-ji-mo-shi-zhi-dan-li/cover.jpg" alt="Featured image of post 一文讲透 GoF 的 23 种设计模式之单例" /&gt;&lt;h1 id="一文讲透-gof-的-23-种设计模式之单例"&gt;&lt;a href="#%e4%b8%80%e6%96%87%e8%ae%b2%e9%80%8f-gof-%e7%9a%84-23-%e7%a7%8d%e8%ae%be%e8%ae%a1%e6%a8%a1%e5%bc%8f%e4%b9%8b%e5%8d%95%e4%be%8b" class="header-anchor"&gt;&lt;/a&gt;一文讲透 GoF 的 23 种设计模式之单例
&lt;/h1&gt;&lt;p&gt;单例模式&amp;ndash;Singleton 是创建型模式&lt;/p&gt;
&lt;h2 id="定义"&gt;&lt;a href="#%e5%ae%9a%e4%b9%89" class="header-anchor"&gt;&lt;/a&gt;定义
&lt;/h2&gt;&lt;p&gt;&lt;strong&gt;确保一个类在一个 JVM 内只有一个实例，并提供全局访问点&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;&lt;img alt="图片" loading="lazy" sizes="(max-width: 767px) calc(100vw - 30px), (max-width: 1023px) 700px, (max-width: 1279px) 950px, 1232px" src="https://pub-f29bf2b53160470c9a85250116509a24.r2.dev/post/2026-02-25-yi-wen-jiang-tou-gof-de-23-zhong-she-ji-mo-shi-zhi-dan-li/001-d2d7932d.png"&gt;&lt;/p&gt;
&lt;h2 id="什么时候用"&gt;&lt;a href="#%e4%bb%80%e4%b9%88%e6%97%b6%e5%80%99%e7%94%a8" class="header-anchor"&gt;&lt;/a&gt;什么时候用?
&lt;/h2&gt;&lt;p&gt;●配置中心、缓存管理器、日志器（有时）&lt;/p&gt;
&lt;p&gt;●需要全局共享状态/资源&lt;/p&gt;
&lt;p&gt;对于 那些初始化很贵，重复创建又特别浪费资源的场景非常合适 。&lt;/p&gt;
&lt;h2 id="不要滥用"&gt;&lt;a href="#%e4%b8%8d%e8%a6%81%e6%bb%a5%e7%94%a8" class="header-anchor"&gt;&lt;/a&gt;不要滥用
&lt;/h2&gt;&lt;p&gt;单例本质是“全局变量 + 访问入口”，会增加耦合、影响测试&lt;/p&gt;
&lt;h2 id="实现方式"&gt;&lt;a href="#%e5%ae%9e%e7%8e%b0%e6%96%b9%e5%bc%8f" class="header-anchor"&gt;&lt;/a&gt;实现方式
&lt;/h2&gt;&lt;p&gt;以下为常见的 5 种实现方式对比。&lt;/p&gt;
&lt;table&gt;
 &lt;thead&gt;
 &lt;tr&gt;
 &lt;th&gt;实现方式&lt;/th&gt;
 &lt;th&gt;核心机制简述&lt;/th&gt;
 &lt;th&gt;并发安全性 (线程安全)&lt;/th&gt;
 &lt;th&gt;性能表现&lt;/th&gt;
 &lt;th&gt;核心易错点 / 致命缺陷&lt;/th&gt;
 &lt;th&gt;综合推荐度&lt;/th&gt;
 &lt;/tr&gt;
 &lt;/thead&gt;
 &lt;tbody&gt;
 &lt;tr&gt;
 &lt;td&gt;1. 饿汉式(Eager)&lt;/td&gt;
 &lt;td&gt;类加载时立即创建静态 final 实例。&lt;/td&gt;
 &lt;td&gt;安全(JVM类加载机制保证)&lt;/td&gt;
 &lt;td&gt;高 (运行时)获取实例无锁。但可能会拖慢系统启动速度，且如果不用会浪费内存。&lt;/td&gt;
 &lt;td&gt;低实现简单，不易出错。缺点是无法进行懒加载，且难以传递动态参数进行初始化。&lt;/td&gt;
 &lt;td&gt;⭐⭐⭐&lt;/td&gt;
 &lt;/tr&gt;
 &lt;tr&gt;
 &lt;td&gt;2. 懒汉式(同步方法)&lt;/td&gt;
 &lt;td&gt;在 getInstance 方法上加 synchronized 锁。&lt;/td&gt;
 &lt;td&gt;安全(粗粒度锁保证)&lt;/td&gt;
 &lt;td&gt;非常低每次调用 getInstance 都要发生线程竞争和锁获取，高并发下是严重的性能瓶颈。&lt;/td&gt;
 &lt;td&gt;低实现简单。主要的&amp;quot;错&amp;quot;是选择了这种低效的方案。&lt;/td&gt;
 &lt;td&gt;⭐&lt;/td&gt;
 &lt;/tr&gt;
 &lt;tr&gt;
 &lt;td&gt;3. 双重检查锁(DCL)&lt;/td&gt;
 &lt;td&gt;两次判空 + 同步代码块 + volatile 关键字。&lt;/td&gt;
 &lt;td&gt;安全 (有前提)必须在实例变量上加 volatile 禁止指令重排序。&lt;/td&gt;
 &lt;td&gt;高只在第一次初始化时加锁，后续调用无锁。实现了高性能的懒加载。&lt;/td&gt;
 &lt;td&gt;极高 (致命)最常见的错误是忘记加 volatile 关键字。这会导致多线程环境下，某个线程可能会拿到一个&amp;quot;半初始化&amp;quot;的对象，引发难以排查的 Bug。&lt;/td&gt;
 &lt;td&gt;⭐⭐⭐&lt;/td&gt;
 &lt;/tr&gt;
 &lt;tr&gt;
 &lt;td&gt;4. 静态内部类(Holder模式)&lt;/td&gt;
 &lt;td&gt;利用 JVM 加载外部类时不加载静态内部类的特性实现懒加载。&lt;/td&gt;
 &lt;td&gt;安全(JVM类加载机制保证)&lt;/td&gt;
 &lt;td&gt;高既实现了懒加载，又在获取实例时没有任何锁机制，性能优异。&lt;/td&gt;
 &lt;td&gt;低非常规整的写法。唯一需要注意的是要确保构造函数私有，防止外部意外实例化。&lt;/td&gt;
 &lt;td&gt;⭐⭐⭐⭐⭐ (手动实现首选)&lt;/td&gt;
 &lt;/tr&gt;
 &lt;tr&gt;
 &lt;td&gt;5. 枚举(Enum)&lt;/td&gt;
 &lt;td&gt;利用 Java 枚举类型的特殊语法和底层实现。&lt;/td&gt;
 &lt;td&gt;安全 (天然)(JVM 层面保障，防御反射和序列化攻击)&lt;/td&gt;
 &lt;td&gt;高类似于饿汉式，类加载时完成初始化，运行时无锁。&lt;/td&gt;
 &lt;td&gt;极低代码最简洁，几乎不可能写错。缺点是无法继承其他类，且在语义上用来做复杂业务对象时显得突兀。&lt;/td&gt;
 &lt;td&gt;⭐⭐⭐⭐⭐ (最安全简洁)&lt;/td&gt;
 &lt;/tr&gt;
 &lt;/tbody&gt;
&lt;/table&gt;
&lt;p&gt;重点说明两种实现方式：枚举和静态内部类。&lt;/p&gt;
&lt;h3 id="枚举"&gt;&lt;a href="#%e6%9e%9a%e4%b8%be" class="header-anchor"&gt;&lt;/a&gt;枚举
&lt;/h3&gt;&lt;p&gt;这是 Java 最简洁实现。Java 的 Enum 在语言层面有一些特殊保证（例如不会被克隆），这也是它常被用来实现单例的原因之一。&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-java" data-lang="java"&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt; 1&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="err"&gt;⚡&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;java片段public&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kd"&gt;enum&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;AppConfig&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt; 2&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;INSTANCE&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt; 3&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt; 4&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kd"&gt;private&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;String&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;env&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s"&gt;&amp;#34;prod&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt; 5&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt; 6&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kd"&gt;public&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;String&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nf"&gt;getEnv&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt; 7&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;return&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;env&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt; 8&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt; 9&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;10&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kd"&gt;public&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kt"&gt;void&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nf"&gt;setEnv&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;String&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;env&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;11&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="na"&gt;env&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;env&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;12&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;13&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;14&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kd"&gt;public&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kd"&gt;static&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kt"&gt;void&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nf"&gt;main&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;String&lt;/span&gt;&lt;span class="o"&gt;[]&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;args&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;15&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;AppConfig&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;c1&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;AppConfig&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="na"&gt;INSTANCE&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;16&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;AppConfig&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;c2&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;AppConfig&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="na"&gt;INSTANCE&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;17&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;18&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;c1&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="na"&gt;setEnv&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;&amp;#34;test&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;19&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;20&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;System&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="na"&gt;out&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="na"&gt;println&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;c1&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;==&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;c2&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="c1"&gt;// true&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;21&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;System&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="na"&gt;out&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="na"&gt;println&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;c2&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="na"&gt;getEnv&lt;/span&gt;&lt;span class="p"&gt;());&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="c1"&gt;// test&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;22&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;23&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;使用枚举（enum）来实现单例模式，被《Effective Java》的作者 Joshua Bloch 称为 &lt;strong&gt;“实现单例模式的最佳方法”&lt;/strong&gt;。&lt;/p&gt;
&lt;p&gt;它之所以备受推崇，是因为它用极其简洁的代码，完美解决了传统单例模式面临的线程安全、序列化破坏和反射破坏三大难题&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;原理一：利用 JVM 类加载机制保证“线程安全”&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;在传统的懒汉式单例中，为了保证多线程下只创建一个实例，我们需要写复杂的“双重检查锁（Double-Checked Locking）”并加上 volatile 关键字。&lt;/p&gt;
&lt;p&gt;而枚举怎么做的？&lt;/p&gt;
&lt;p&gt;当你定义 INSTANCE 时，编译器底层实际会把它转化为类似这样的代码：&lt;/p&gt;
&lt;p&gt;⚡ java片段&lt;code&gt;public static final AppConfig INSTANCE = new AppConfig();&lt;/code&gt;&lt;/p&gt;
&lt;p&gt;Java 虚拟机（JVM）在加载类的时候，会利用底层的类加载机制保证静态成员的初始化是绝对线程安全的。在这个类被加载到内存时，JVM 会自动实例化 INSTANCE 且只实例化一次，整个过程由 JVM 内部加锁保证同步，不需要你手动写任何并发控制代码。&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;原理二：天生防御“反射攻击”&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;传统的单例模式有一个致命弱点：恶意代码可以通过 Java 的反射机制（Reflection）把私有构造函数设置为可见（setAccessible(true)），从而强行 new 出新的实例，打破单例。&lt;/p&gt;
&lt;p&gt;而枚举怎么做的？&lt;/p&gt;
&lt;p&gt;Java 的反射 API 从源码级别就直接“封杀”了通过反射创建枚举实例的可能性。如果你去看 Constructor.newInstance() 的 Java 底层源码，会发现有一段明确的校验逻辑：&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-gdscript3" data-lang="gdscript3"&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;1&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="err"&gt;⚡&lt;/span&gt; &lt;span class="n"&gt;java片段if&lt;/span&gt; &lt;span class="p"&gt;((&lt;/span&gt;&lt;span class="n"&gt;clazz&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;getModifiers&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;&lt;/span&gt; &lt;span class="n"&gt;Modifier&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;ENUM&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;!=&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;2&lt;/span&gt;&lt;span class="cl"&gt; &lt;span class="n"&gt;throw&lt;/span&gt; &lt;span class="n"&gt;new&lt;/span&gt; &lt;span class="n"&gt;IllegalArgumentException&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;&amp;#34;Cannot reflectively create enum objects&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;也就是说，&lt;strong&gt;一旦 JVM 发现你要用反射去创建枚举类的对象，就会直接抛出异常&lt;/strong&gt;，从根本上杜绝了反射攻击。&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;原理三：天生防御“序列化破坏”&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;传统的单例对象如果实现了 Serializable 接口，在进行网络传输或持久化到磁盘再反序列化读取回来时，默认会重新分配内存，生成一个全新的对象。传统做法是必须手动写一个 readResolve() 方法来返回原实例。&lt;/p&gt;
&lt;p&gt;而枚举怎么做的？&lt;/p&gt;
&lt;p&gt;Java 规范对枚举的序列化有特殊的规定。枚举在序列化的时候，仅仅是将枚举常量的名称（name）输出到了结果中；在反序列化的时候，Java 会调用 java.lang.Enum.valueOf() 方法，通过名字去查找并返回内存中已经存在的那个常量对象。&lt;/p&gt;
&lt;p&gt;因此，无论你反序列化多少次，拿到的永远是内存里的同一个 INSTANCE 对象。&lt;/p&gt;
&lt;p&gt;总结来说：枚举单例的核心原理就是 &lt;strong&gt;直接利用 Java 语言底层的机制&lt;/strong&gt;：&lt;/p&gt;
&lt;p&gt;●用 JVM 类加载机制 搞定了线程安全。&lt;/p&gt;
&lt;p&gt;●用 反射 API 的硬编码拦截 搞定了反射破坏。&lt;/p&gt;
&lt;p&gt;●用 特殊的名称匹配机制 搞定了序列化破坏。&lt;/p&gt;
&lt;p&gt;在理论上，枚举单例确实是“最完美”的单例实现；但在实际的工程代码中，它的出场率确实不高。这并不是因为枚举本身有 bug，而是因为它在现代工程架构、面向对象设计理念以及测试友好度上，存在一些不可避免的局限性&lt;/p&gt;
&lt;p&gt;具体来说，有以下几个核心原因：&lt;/p&gt;
&lt;p&gt;1.&lt;strong&gt;现代框架（如 Spring）接管了单例的管理&lt;/strong&gt; 这是最根本的原因。在现代 Java 工程中（尤其是企业级开发），我们几乎不再手动编写任何单例模式了。 我们广泛使用 Spring/Spring Boot 这样的依赖注入（DI）框架。在 Spring 中，你只需要在一个普通的类上加上 @Service、@Component 或 @Configuration 注解，Spring 容器（IoC Container）就会默认将其作为一个单例来管理。框架不仅帮你保证了单例，还能帮你自动注入其他依赖（如数据库连接、其他服务），这比用枚举手写单例要强大、灵活得多。&lt;/p&gt;
&lt;p&gt;2.&lt;strong&gt;违反了“语义”和开发者的直觉&lt;/strong&gt; 代码不仅是给机器运行的，更是给人读的。 枚举的本来语义：代表一组固定的常量集合（如星期、颜色、订单状态）。单例的语义：通常是一个拥有复杂业务逻辑的管理类（如 UserManager、DatabaseConnectionPool）。&lt;/p&gt;
&lt;p&gt;如果把一个复杂的业务服务写成 enum，会让接手代码的其他开发者感到困惑，这违反了“最小惊讶原则（Principle of Least Astonishment）”。感觉就像是“为了用单例模式而强行用枚举”。&lt;/p&gt;
&lt;p&gt;3.&lt;strong&gt;面向对象特性的缺失（无法继承）&lt;/strong&gt; Java 规定，所有的枚举类都隐式继承了 java.lang.Enum。因为 Java 不支持多重继承，这意味着你的枚举单例不能再继承任何其他的父类。 如果你的架构需要 AppConfig 继承一个 BaseConfig 类来复用代码，枚举单例直接就做不到。 虽然枚举可以实现接口（implements Interface），但在需要共享基类代码的场景下，它的表现非常无力。&lt;/p&gt;
&lt;p&gt;4.&lt;strong&gt;传参初始化非常困难&lt;/strong&gt; 在工程实践中，单例对象在初始化时往往需要外部参数。比如，一个数据库连接池单例，在启动时需要读取配置文件里的 url 和 password。 普通的单例模式或 Spring 管理的 Bean，可以在运行时读取配置后，再进行初始化。 枚举常量的实例化是在类加载的最早期进行的，这个时候你很难把运行时的参数优雅地传递给枚举的构造函数。&lt;/p&gt;
&lt;p&gt;5.&lt;strong&gt;极难进行单元测试（Mock）&lt;/strong&gt; 在做单元测试时，我们经常需要把某些依赖的单例对象“Mock（模拟）”掉（比如使用 Mockito），以隔离测试环境。 普通类别的单例很容易被 Mock 框架替换。但是，枚举是静态的全局常量，它的生命周期和类加载器绑定。在测试中强行替换枚举实例极其困难，容易导致测试用例之间互相污染。&lt;/p&gt;
&lt;p&gt;在实际工程中：&lt;/p&gt;
&lt;p&gt;●如果你要写一个完全无状态、不需要继承、不依赖外部配置的纯工具类/简单配置类，用枚举单例确实不错。&lt;/p&gt;
&lt;p&gt;●但对于包含业务逻辑、需要依赖注入、需要被测试的类，交给 Spring 等框架去管理才是工业界的最佳实践。&lt;/p&gt;
&lt;h3 id="静态内部类"&gt;&lt;a href="#%e9%9d%99%e6%80%81%e5%86%85%e9%83%a8%e7%b1%bb" class="header-anchor"&gt;&lt;/a&gt;静态内部类
&lt;/h3&gt;&lt;p&gt;如果你不想用枚举，又想要一个&lt;strong&gt;既能延迟加载（懒汉式），又绝对线程安全，还能完美避开繁琐的加锁（synchronized）&lt;/strong&gt; 的单例，静态内部类是最佳选择。&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-java" data-lang="java"&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt; 1&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="err"&gt;⚡&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;java片段public&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kd"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;DatabaseConnectionPool&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt; 2&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt; 3&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="c1"&gt;// 1. 私有化构造函数，防止外部 new&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt; 4&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kd"&gt;private&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nf"&gt;DatabaseConnectionPool&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt; 5&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="c1"&gt;// 可选：在这里加上防御反射攻击的代码&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt; 6&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;if&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;SingletonHolder&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="na"&gt;INSTANCE&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;!=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kc"&gt;null&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt; 7&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;throw&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;new&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;RuntimeException&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;&amp;#34;不允许通过反射创建单例！&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt; 8&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt; 9&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;10&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;11&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="c1"&gt;// 2. 核心：定义一个私有的静态内部类&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;12&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="c1"&gt;// 这个类直到被调用时才会被 JVM 加载&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;13&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kd"&gt;private&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kd"&gt;static&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kd"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;SingletonHolder&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;14&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="c1"&gt;// 由 JVM 保证这里的实例化是绝对线程安全的&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;15&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kd"&gt;private&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kd"&gt;static&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kd"&gt;final&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;DatabaseConnectionPool&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;INSTANCE&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;new&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;DatabaseConnectionPool&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;16&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;17&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;18&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="c1"&gt;// 3. 提供全局访问点&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;19&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kd"&gt;public&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kd"&gt;static&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;DatabaseConnectionPool&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nf"&gt;getInstance&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;20&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="c1"&gt;// 只有在调用这里时，SingletonHolder 才会被加载，从而实例化 INSTANCE&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;21&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;return&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;SingletonHolder&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="na"&gt;INSTANCE&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;22&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;23&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;为什么它很巧妙？&lt;/p&gt;
&lt;p&gt;●懒加载（Lazy Loading）：当你加载 DatabaseConnectionPool 这个类时，内部类 SingletonHolder 并不会被立刻加载。只有当你真正调用 getInstance() 方法时，内部类才会被加载，对象才会被创建。这就节省了内存。&lt;/p&gt;
&lt;p&gt;●零并发负担：它没有使用任何 synchronized 或者 volatile 关键字。它完全将线程安全的控制权交给了 JVM 底层的类加载机制（JVM 在加载一个类时，会自动加锁保证全局唯一）。&lt;/p&gt;
&lt;h2 id="spring-是如何实现单例的"&gt;&lt;a href="#spring-%e6%98%af%e5%a6%82%e4%bd%95%e5%ae%9e%e7%8e%b0%e5%8d%95%e4%be%8b%e7%9a%84" class="header-anchor"&gt;&lt;/a&gt;Spring 是如何实现单例的？
&lt;/h2&gt;&lt;p&gt;Spring 里的单例（Singleton）和我们在《设计模式》书里学到的单例，在概念和实现思路上有很大的不同。&lt;/p&gt;
&lt;p&gt;●传统单例（GoF单例）：保证在一个 JVM（准确地说是类加载器）级别，某个类只有一个实例。类自己控制自己的实例化。&lt;/p&gt;
&lt;p&gt;●Spring 单例：保证在一个 Spring IoC 容器（ApplicationContext）内部，某个指定的 Bean 名称只有一个实例。它是由 Spring 框架来统一管理的。&lt;/p&gt;
&lt;p&gt;Spring 实现单例的核心原理可以概括为：&lt;strong&gt;单例注册表（Singleton Registry）&lt;/strong&gt;&lt;/p&gt;
&lt;h3 id="1-核心数据结构concurrenthashmap"&gt;&lt;a href="#1-%e6%a0%b8%e5%bf%83%e6%95%b0%e6%8d%ae%e7%bb%93%e6%9e%84concurrenthashmap" class="header-anchor"&gt;&lt;/a&gt;1. 核心数据结构：ConcurrentHashMap
&lt;/h3&gt;&lt;p&gt;如果你翻开 Spring 的底层源码（DefaultSingletonBeanRegistry 类），你会发现 Spring 管理单例的本质，就是一个大大的缓存 Map：&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-fallback" data-lang="fallback"&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;1&lt;/span&gt;&lt;span class="cl"&gt;⚡ java片段// Spring 源码中的 &amp;#34;一级缓存&amp;#34;，存放所有完全初始化好的单例 Bean
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;2&lt;/span&gt;&lt;span class="cl"&gt;private final Map&amp;lt;String, Object&amp;gt; singletonObjects = new ConcurrentHashMap&amp;lt;&amp;gt;(256);
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;Spring 的单例其实就是把创建好的对象塞进了一个线程安全的 ConcurrentHashMap 里。Key 是 Bean 的名字（通常是类名首字母小写），Value 就是这个类的实例对象。&lt;/p&gt;
&lt;h3 id="2-spring-创建单例的流程"&gt;&lt;a href="#2-spring-%e5%88%9b%e5%bb%ba%e5%8d%95%e4%be%8b%e7%9a%84%e6%b5%81%e7%a8%8b" class="header-anchor"&gt;&lt;/a&gt;2. Spring 创建单例的流程
&lt;/h3&gt;&lt;p&gt;当你在代码里注入一个单例（比如通过 @Autowired），或者调用 context.getBean(&amp;ldquo;myService&amp;rdquo;) 时，Spring 大致会经历以下步骤：&lt;/p&gt;
&lt;p&gt;1.查缓存：Spring 首先会去 singletonObjects 这个 Map 里查，看看有没有叫 &amp;ldquo;myService&amp;rdquo; 的对象。&lt;/p&gt;
&lt;p&gt;2.有则返回：如果 Map 里有，说明已经创建过了，直接把这个对象返回给你。这就是单例的体现。&lt;/p&gt;
&lt;p&gt;3.无则创建并加锁：如果 Map 里没有，Spring 就会准备创建它。为了保证在多线程环境下只有一个线程能去创建这个 Bean，Spring 会对这个 Bean 的名字进行加锁（通常是通过对全局单例集合的锁或者特定的互斥锁来实现同步）。&lt;/p&gt;
&lt;p&gt;4.实例化与初始化：Spring 通过反射调用构造函数把对象 new 出来，然后进行属性填充（依赖注入），再调用 @PostConstruct 等初始化方法。&lt;/p&gt;
&lt;p&gt;5.放入 Map 并返回：最后，把完全准备好的对象放进 singletonObjects 这个 ConcurrentHashMap 里，然后返回给你。以后所有对这个 Bean 的请求，都直接从 Map 里拿。&lt;/p&gt;
&lt;h3 id="3-补充循环依赖的杀手锏三级缓存"&gt;&lt;a href="#3-%e8%a1%a5%e5%85%85%e5%be%aa%e7%8e%af%e4%be%9d%e8%b5%96%e7%9a%84%e6%9d%80%e6%89%8b%e9%94%8f%e4%b8%89%e7%ba%a7%e7%bc%93%e5%ad%98" class="header-anchor"&gt;&lt;/a&gt;3. 补充：循环依赖的杀手锏“三级缓存”
&lt;/h3&gt;&lt;p&gt;Spring 在管理单例时，还要解决一个传统单例很难解决的问题——循环依赖（比如 A 依赖 B，B 又依赖 A）。&lt;/p&gt;
&lt;p&gt;为了解决这个问题，Spring 其实并没有只用一个 Map，而是用了三个 Map（传说中的三级缓存）：&lt;/p&gt;
&lt;p&gt;●一级缓存（singletonObjects）：存完整的、可用的单例对象。&lt;/p&gt;
&lt;p&gt;●二级缓存（earlySingletonObjects）：存半成品对象（刚 new 出来，但还没注入属性的对象），用于提前暴露自己，打破循环。&lt;/p&gt;
&lt;p&gt;●三级缓存（singletonFactories）：存对象工厂，用于在需要时生成代理对象（比如处理 AOP 切面）。&lt;/p&gt;
&lt;p&gt;&lt;img alt="图片" loading="lazy" sizes="(max-width: 767px) calc(100vw - 30px), (max-width: 1023px) 700px, (max-width: 1279px) 950px, 1232px" src="https://pub-f29bf2b53160470c9a85250116509a24.r2.dev/post/2026-02-25-yi-wen-jiang-tou-gof-de-23-zhong-she-ji-mo-shi-zhi-dan-li/002-5ae19680.svg"&gt;&lt;/p&gt;
&lt;p&gt;结合上面的图，核心过程如下：&lt;/p&gt;
&lt;p&gt;第一阶段：A 的创建与曝光&lt;/p&gt;
&lt;p&gt;1.调用 getBean(A)：Spring 容器开始创建 Bean A。&lt;/p&gt;
&lt;p&gt;2.实例化 A：调用构造函数，A 对象在内存中诞生，但属性（如 B）还是 null。&lt;/p&gt;
&lt;p&gt;3.暴露三级缓存：Spring 将 A 的工厂对象放入 三级缓存 (singletonFactories)。这是解决循环依赖的关键一步，意味着此时如果有其他对象引用 A，可以通过这个工厂拿到 A 的引用。&lt;/p&gt;
&lt;p&gt;第二阶段：A 填充属性，触发 B 的创建&lt;/p&gt;
&lt;p&gt;4.填充属性 B：A 发现自己依赖 B，于是暂停自己，转而去创建 B。&lt;/p&gt;
&lt;p&gt;第三阶段：B 的创建与获取 A&lt;/p&gt;
&lt;p&gt;5.实例化 B：B 对象诞生，属性（如 A）还是 null。&lt;/p&gt;
&lt;p&gt;6.暴露三级缓存：将 B 的工厂放入三级缓存。&lt;/p&gt;
&lt;p&gt;7.填充属性 A：B 发现自己依赖 A，于是尝试去缓存找 A。&lt;/p&gt;
&lt;p&gt;第四阶段：B 从缓存中找到 A (核心转折)&lt;/p&gt;
&lt;p&gt;8.查找缓存：&lt;/p&gt;
&lt;p&gt;●找一级缓存？没有（A 还没彻底完工）。&lt;/p&gt;
&lt;p&gt;●找二级缓存？没有（还没人提取过 A 的早期引用）。&lt;/p&gt;
&lt;p&gt;●找三级缓存？有了！&lt;/p&gt;
&lt;p&gt;9.升级缓存：&lt;/p&gt;
&lt;p&gt;●B 调用三级缓存中的工厂方法，拿到 A 的早期引用。&lt;/p&gt;
&lt;p&gt;●重点：如果 A 配置了 AOP（比如事务管理），这个工厂会提前生成 A 的代理对象。&lt;/p&gt;
&lt;p&gt;●将 A 的早期引用放入 二级缓存 (earlySingletonObjects)，并从三级缓存移除。&lt;/p&gt;
&lt;p&gt;10.B 完成：B 拿到了 A 的引用，完成属性填充和初始化，放入 一级缓存。&lt;/p&gt;
&lt;p&gt;第五阶段：A 完成&lt;/p&gt;
&lt;p&gt;11.A 获取 B：B 已经创建好了，A 顺利拿到 B 的引用。&lt;/p&gt;
&lt;p&gt;12.A 完成：A 完成属性填充和初始化，放入 一级缓存。&lt;/p&gt;</description></item><item><title>全程0人工写代码！干掉低级码农的不是大模型</title><link>https://xiaobox.github.io/p/2026-02-24-quan-cheng-0-ren-gong-xie-dai-ma-gan-diao-di-ji-ma-nong-de-b/</link><pubDate>Tue, 24 Feb 2026 03:46:39 +0000</pubDate><guid>https://xiaobox.github.io/p/2026-02-24-quan-cheng-0-ren-gong-xie-dai-ma-gan-diao-di-ji-ma-nong-de-b/</guid><description>&lt;img src="https://pub-f29bf2b53160470c9a85250116509a24.r2.dev/post/2026-02-24-quan-cheng-0-ren-gong-xie-dai-ma-gan-diao-di-ji-ma-nong-de-b/cover.jpg" alt="Featured image of post 全程0人工写代码！干掉低级码农的不是大模型" /&gt;&lt;h1 id="全程0人工写代码干掉低级码农的不是大模型"&gt;&lt;a href="#%e5%85%a8%e7%a8%8b0%e4%ba%ba%e5%b7%a5%e5%86%99%e4%bb%a3%e7%a0%81%e5%b9%b2%e6%8e%89%e4%bd%8e%e7%ba%a7%e7%a0%81%e5%86%9c%e7%9a%84%e4%b8%8d%e6%98%af%e5%a4%a7%e6%a8%a1%e5%9e%8b" class="header-anchor"&gt;&lt;/a&gt;全程0人工写代码！干掉低级码农的不是大模型
&lt;/h1&gt;&lt;p&gt;在当前全行业的 AI 辅助编程浪潮中，大多数工具仍停留在“交互式伴游”阶段，而支付巨头 Stripe 却打造了一套完全无人值守的端到端代码智能体——“小黄人”（Minions）&lt;/p&gt;
&lt;p&gt;小黄人是一个独立打工的“数字员工”。目前的惊人数据是：在 Stripe 内部，&lt;strong&gt;每周有超过 1300 个由小黄人完全生成的 Pull Requests（合并请求）被成功合并。这些代码在最终阶段会经过人类审查，但其中不包含任何人类编写的代码。&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;更具挑战的是，Stripe 的代码库高达数亿行，主要使用较冷门的带有 Sorbet 类型的 Ruby 语言，且包含大量 LLM 根本没见过的大型内部自研库。此外，这些代码每年要处理超过 1 万亿美元的支付量，合规与容错要求极高。&lt;/p&gt;
&lt;p&gt;Stripe 是如何让 LLM 驾驭如此庞大且复杂的企业级代码库的？核心答案在于极其强大的定制化工程脚手架。&lt;/p&gt;
&lt;p&gt;以下是小黄人能高效运转的四大核心技术拆解。&lt;/p&gt;
&lt;h3 id="1-极致标准化的预热沙盒devboxes"&gt;&lt;a href="#1-%e6%9e%81%e8%87%b4%e6%a0%87%e5%87%86%e5%8c%96%e7%9a%84%e9%a2%84%e7%83%ad%e6%b2%99%e7%9b%92devboxes" class="header-anchor"&gt;&lt;/a&gt;1 极致标准化的预热沙盒（Devboxes）
&lt;/h3&gt;&lt;p&gt;要让全自动 Agent 大规模并行工作，绝不能让它们跑在开发者杂乱的本地笔记本上。Stripe 的解法是直接复用为人类工程师打造的云端开发机（Devboxes）。&lt;/p&gt;
&lt;p&gt;●&lt;strong&gt;10 秒极速“热启动”&lt;/strong&gt;：这些 Devbox 是 AWS EC2 实例。Stripe 预先配置并预热了一个资源池，里面已经克隆好了巨大的 Git 仓库，预热了 Bazel 构建缓存和类型检查缓存，甚至启动了持续运行的代码生成服务。因此，只要 10 秒钟，小黄人就能拿到一台随时可以运行测试和修改代码的机器。&lt;/p&gt;
&lt;p&gt;●&lt;strong&gt;免弹窗的完全提权&lt;/strong&gt;：为了让小黄人在后台静默运行，它需要无缝执行各种 Shell 命令。因为 Devbox 运行在与生产资源和外部互联网隔离的 QA 环境中，爆炸半径被严格限制，所以系统敢于跳过人类权限确认弹窗，给予小黄人完整的执行自由。&lt;/p&gt;
&lt;p&gt;●&lt;strong&gt;解决并发冲突&lt;/strong&gt;：如果用本地环境，并发运行多个 Agent 需要处理复杂的 git worktrees（这在 Stripe 的庞大代码库中无法扩展）。而在云端，工程师可以轻易地同时为 6 个不同的任务启动 6 个分配了独立 Devbox 的小黄人，实现物理级别的完美隔离&lt;/p&gt;
&lt;h3 id="2--蓝图编排blueprints将大模型装进确定性的盒子里"&gt;&lt;a href="#2--%e8%93%9d%e5%9b%be%e7%bc%96%e6%8e%92blueprints%e5%b0%86%e5%a4%a7%e6%a8%a1%e5%9e%8b%e8%a3%85%e8%bf%9b%e7%a1%ae%e5%ae%9a%e6%80%a7%e7%9a%84%e7%9b%92%e5%ad%90%e9%87%8c" class="header-anchor"&gt;&lt;/a&gt;2 “蓝图”编排（Blueprints）：将大模型装进确定性的盒子里
&lt;/h3&gt;&lt;p&gt;常规的 Agent 往往采用开放的循环机制，任由 LLM 自己决定下一步调什么工具，这极易导致出错和浪费 Token。 Stripe 创造性地引入了**“蓝图”（Blueprints）**状态机机制。蓝图将整个工作流视为一张图，将 LLM 的创造力与确定性的系统代码交织在一起：&lt;/p&gt;
&lt;p&gt;●&lt;strong&gt;确定性节点 vs Agent 节点&lt;/strong&gt;：在蓝图中，像“实现具体任务”或“修复 CI 失败”是让 LLM 自由发挥的 Agent 节点；但是，像“运行配置好的 Linter”或“推送 Git 变更”则是完全不调用 LLM 的纯代码确定性节点。&lt;/p&gt;
&lt;p&gt;●&lt;strong&gt;底线兜底&lt;/strong&gt;：这意味着小黄人无法绕过代码格式化等硬性规范。把大模型“关进受控的盒子里”，不仅极大地节省了 Token，还从系统层面提高了整体可靠性。各团队甚至可以编写自定义的蓝图，来处理复杂的、LLM 辅助的代码库迁移任务&lt;/p&gt;
&lt;h3 id="3-极其克制的上下文投喂规则文件与-toolshed"&gt;&lt;a href="#3-%e6%9e%81%e5%85%b6%e5%85%8b%e5%88%b6%e7%9a%84%e4%b8%8a%e4%b8%8b%e6%96%87%e6%8a%95%e5%96%82%e8%a7%84%e5%88%99%e6%96%87%e4%bb%b6%e4%b8%8e-toolshed" class="header-anchor"&gt;&lt;/a&gt;3 极其克制的上下文投喂：规则文件与 Toolshed
&lt;/h3&gt;&lt;p&gt;面对上亿行代码，如果把所有全局规则都塞给大模型，上下文窗口瞬间就会被撑爆。&lt;/p&gt;
&lt;p&gt;●&lt;strong&gt;按目录生效的局部规则&lt;/strong&gt;：Stripe 几乎只使用作用于特定子目录或文件模式的规则文件。他们巧妙地复用了人类工程师为 Cursor 编写的规则格式。这样，工程师在日常开发中沉淀的最佳实践，小黄人（以及 Claude Code）在遍历文件系统时就能直接动态读取并学习。&lt;/p&gt;
&lt;p&gt;●&lt;strong&gt;MCP 工具棚（Toolshed）&lt;/strong&gt;：小黄人通过模型上下文协议（MCP）获取网络信息（工单、文档、代码搜索等）。Stripe 建立了一个包含近 500 个内部与 SaaS 工具的中央服务器 Toolshed。但为了防止 Agent 分心，系统每次只会为小黄人精心挑选一个“小巧而高度相关”&lt;/p&gt;
&lt;h3 id="4-反馈左移shifting-feedback-left极速纠错循环"&gt;&lt;a href="#4-%e5%8f%8d%e9%a6%88%e5%b7%a6%e7%a7%bbshifting-feedback-left%e6%9e%81%e9%80%9f%e7%ba%a0%e9%94%99%e5%be%aa%e7%8e%af" class="header-anchor"&gt;&lt;/a&gt;4 反馈左移（Shifting Feedback Left）：极速纠错循环
&lt;/h3&gt;&lt;p&gt;无人值守 Agent 成功的关键在于能否实现自我闭环修正。Stripe 为其构建了多层极速反馈循环：&lt;/p&gt;
&lt;p&gt;●&lt;strong&gt;5 秒内的本地验证&lt;/strong&gt;：在小黄人把代码推送到 CI 之前，Devbox 上的后台守护进程会通过启发式算法自动运行相关的 Linter 和类型检查。这个本地节点耗时不到 5 秒，让小黄人在本地极速完成语法纠错。&lt;/p&gt;
&lt;p&gt;●&lt;strong&gt;克制的 CI 测试轮数&lt;/strong&gt;：Stripe 的 CI 拥有超过 300 万个测试用例。推送到 CI 后，系统会运行相关测试，并自动应用已有的修复脚本（Autofixes）。如果还有未修复的错误，报错会发回给小黄人。但为了平衡算力成本、时间与边际收益，小黄人最多只被允许进行 1 到 2 次的 CI 循环试错。之后无论成败，都会将其移交给人类处理，防止其陷入昂贵的死循环&lt;/p&gt;
&lt;h2 id="给我的启示"&gt;&lt;a href="#%e7%bb%99%e6%88%91%e7%9a%84%e5%90%af%e7%a4%ba" class="header-anchor"&gt;&lt;/a&gt;给我的启示
&lt;/h2&gt;&lt;p&gt;基于 Stripe 公开的这些技术细节，我得出了以下几点关于 AI 研发提效的深刻感悟：&lt;/p&gt;
&lt;p&gt;1.“&lt;strong&gt;对人类工程师有益的基础设施，对 LLM 同样有益&lt;/strong&gt;” 这是 Stripe 整个小黄人项目最核心的哲学。Stripe 并没有为了做 AI Agent 去凭空造一套新基建，而是直接将 AI 接入了他们多年打磨的 Devbox 环境、Pre-push hooks 和自动化测试管线中。这给所有企业的启示是：AI Agent 的天花板，取决于你现有工程基础设施的底座。 如果你的人类工程师本地环境经常崩溃、缺乏单测覆盖率、文档陈旧，那么大模型也一样会在这些泥坑里寸步难行。过去在人类开发者体验（Developer Productivity）上的每一分投资，都会在 AI 时代转化为巨大的复利回报。&lt;/p&gt;
&lt;p&gt;2.&lt;strong&gt;放弃追求纯粹的“全能 Agent”&lt;/strong&gt;，用“蓝图”管控不确定性 目前业界过度迷恋让一个 Agent 自主解决所有问题。但 Stripe 的蓝图（Blueprints）设计极其务实：能用一行 Bash 脚本或 Linter 稳定解决的问题（如代码格式化、Git 提交流程），就绝对不让 LLM 消耗 Token 去“推理”。在企业级生产环境中，**混合架构（确定性代码逻辑 + 局部受控的 LLM 节点）**才是保证系统高可靠性（SLA）的唯一出路。&lt;/p&gt;
&lt;p&gt;3.&lt;strong&gt;工程师的日常工作流正在被重塑&lt;/strong&gt; ，在 Stripe，触发小黄人的方式极度符合人体工程学：工程师可以直接在 Slack 的讨论线程里@小黄人，或者在内部的“CI 间歇性失败（Flaky test）”工单中点击一个按钮启动它。我们可以预见，未来的高级工程师将越来越像一个“包工头”：他们在值班（On-call）时并行启动几十个小黄人去处理琐碎的 Bug，自己则专注于审查 PR、设计架构，以及维护和编写能够指导小黄人的局部规则（Cursor rules）。工程师不再逐行敲击代码，而是定义意图并管理基础设施。&lt;/p&gt;
&lt;p&gt;&lt;img alt="图片" loading="lazy" sizes="(max-width: 767px) calc(100vw - 30px), (max-width: 1023px) 700px, (max-width: 1279px) 950px, 1232px" src="https://pub-f29bf2b53160470c9a85250116509a24.r2.dev/post/2026-02-24-quan-cheng-0-ren-gong-xie-dai-ma-gan-diao-di-ji-ma-nong-de-b/001-a01d1a80.png"&gt;&lt;/p&gt;
&lt;h2 id="参考"&gt;&lt;a href="#%e5%8f%82%e8%80%83" class="header-anchor"&gt;&lt;/a&gt;参考
&lt;/h2&gt;&lt;p&gt;●https://stripe.dev/blog/minions-stripes-one-shot-end-to-end-coding-agents&lt;/p&gt;
&lt;p&gt;●https://stripe.dev/blog/minions-stripes-one-shot-end-to-end-coding-agents-part-2&lt;/p&gt;</description></item><item><title>提示词缓存:让 LLM 成本降 10 倍</title><link>https://xiaobox.github.io/p/2025-12-29-ti-shi-ci-huan-cun-rang-llm-cheng-ben-jiang-10-bei/</link><pubDate>Mon, 29 Dec 2025 05:37:52 +0000</pubDate><guid>https://xiaobox.github.io/p/2025-12-29-ti-shi-ci-huan-cun-rang-llm-cheng-ben-jiang-10-bei/</guid><description>&lt;img src="https://pub-f29bf2b53160470c9a85250116509a24.r2.dev/post/2025-12-29-ti-shi-ci-huan-cun-rang-llm-cheng-ben-jiang-10-bei/cover.jpg" alt="Featured image of post 提示词缓存:让 LLM 成本降 10 倍" /&gt;
 &lt;blockquote&gt;
 &lt;p&gt;“&lt;/p&gt;
&lt;p&gt;OpenAI 和 Anthropic 声称，缓存的输入 token 在成本上比常规输入 token 便宜 10 倍。&lt;/p&gt;

 &lt;/blockquote&gt;
&lt;p&gt;&lt;img alt="Image" loading="lazy" sizes="(max-width: 767px) calc(100vw - 30px), (max-width: 1023px) 700px, (max-width: 1279px) 950px, 1232px" src="https://pub-f29bf2b53160470c9a85250116509a24.r2.dev/post/2025-12-29-ti-shi-ci-huan-cun-rang-llm-cheng-ben-jiang-10-bei/001-3eded4f7.png"&gt;&lt;/p&gt;
&lt;h1 id="到底什么是-cached-token-"&gt;&lt;a href="#%e5%88%b0%e5%ba%95%e4%bb%80%e4%b9%88%e6%98%af-cached-token-" class="header-anchor"&gt;&lt;/a&gt;到底什么是 Cached Token ？
&lt;/h1&gt;&lt;p&gt;&lt;strong&gt;Cached Token 就是让 AI “记住” 它刚刚读过的长内容，不用每次都在脑子里从头重新算一遍，从而让回答变得极快且极便宜&lt;/strong&gt;。&lt;/p&gt;
&lt;p&gt;想象你正在参加一场开卷考试，考试内容是一本 500 页的历史书。&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;没有 Cache (传统模式)&lt;/strong&gt; ：&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;第一题： 你把书从第 1 页读到第 500 页，然后回答问题。&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;第二题： 你忘光了刚才读的内容，必须再次从第 1 页读到第 500 页，才能回答第二个问题。&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;后果： 每次回答都很慢，而且把你累得半死（消耗算力，费钱）。&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;有了 Cached Token (缓存模式)&lt;/strong&gt; ：&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;第一题： 你从第 1 页读到第 500 页，并把关键知识点和理解暂时存在脑子里（存入显存）。&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;第二题： 你直接调用脑子里的记忆，跳过阅读过程，立刻回答问题。&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;后果： 只有第一次慢，后面飞快，而且因为不用重复劳动，甚至可以给考官（用户）打个一折的优惠价&lt;/p&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;img alt="Image" loading="lazy" sizes="(max-width: 767px) calc(100vw - 30px), (max-width: 1023px) 700px, (max-width: 1279px) 950px, 1232px" src="https://pub-f29bf2b53160470c9a85250116509a24.r2.dev/post/2025-12-29-ti-shi-ci-huan-cun-rang-llm-cheng-ben-jiang-10-bei/002-6ca0bffb.png"&gt;&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;很多人会误以为 “缓存 = 把上次的回复存起来再发一遍”。不是的&lt;/strong&gt;。&lt;/p&gt;
&lt;p&gt;更准确地说，缓存的是模型在处理这段输入时产生的一些中间计算结果（常被称为 KV cache：attention 里的 K / V 矩阵）。所以即使 cached_tokens 很高，你也仍然可能得到不同的回答（因为采样、temperature 等发生在更后面）&lt;/p&gt;
&lt;h2 id="llm-架构"&gt;&lt;a href="#llm-%e6%9e%b6%e6%9e%84" class="header-anchor"&gt;&lt;/a&gt;LLM 架构
&lt;/h2&gt;&lt;p&gt;想要彻底弄明白 Cached Token，我们需要从原理上了解一下 LLM 架构。&lt;/p&gt;
&lt;p&gt;我们可以将大语言模型（LLM）的架构看作是一个巨大的数学函数：输入一串数字，输出一个数字。这个过程主要由以下四个核心部分组成：&lt;/p&gt;
&lt;p&gt;&lt;img alt="Image" loading="lazy" sizes="(max-width: 767px) calc(100vw - 30px), (max-width: 1023px) 700px, (max-width: 1279px) 950px, 1232px" src="https://pub-f29bf2b53160470c9a85250116509a24.r2.dev/post/2025-12-29-ti-shi-ci-huan-cun-rang-llm-cheng-ben-jiang-10-bei/003-604505c7.png"&gt;&lt;/p&gt;
&lt;h3 id="tokenizer-分词器--切词器"&gt;&lt;a href="#tokenizer-%e5%88%86%e8%af%8d%e5%99%a8--%e5%88%87%e8%af%8d%e5%99%a8" class="header-anchor"&gt;&lt;/a&gt;Tokenizer (分词器 / 切词器)
&lt;/h3&gt;&lt;p&gt;这是模型与人类语言交互的翻译官。&lt;/p&gt;
&lt;p&gt;LLM 无法直接理解文本（如中文或英文），它只能处理数字。Tokenizer 的作用是将你输入的提示词（Prompt）切分成一个个小的片段，称为 Token，并为每个 Token 分配一个唯一的整数 ID。&lt;/p&gt;
&lt;p&gt;比如输入 &amp;ldquo;Check out ngrok.ai&amp;rdquo;，Tokenizer 会将其切分为 [&amp;ldquo;Check&amp;rdquo;, &amp;ldquo;out&amp;rdquo;, &amp;ldquo;ng&amp;rdquo;, &amp;ldquo;rok&amp;rdquo;, &amp;ldquo;.ai&amp;rdquo;]，并转换为对应的数字序列 。&lt;/p&gt;
&lt;p&gt;&lt;img alt="Image" loading="lazy" sizes="(max-width: 767px) calc(100vw - 30px), (max-width: 1023px) 700px, (max-width: 1279px) 950px, 1232px" src="https://pub-f29bf2b53160470c9a85250116509a24.r2.dev/post/2025-12-29-ti-shi-ci-huan-cun-rang-llm-cheng-ben-jiang-10-bei/004-911d1b28.png"&gt;&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;注意&lt;/strong&gt;：不同的模型（如 GPT-5 和 Claude）使用不同的 Tokenizer 规则&lt;/p&gt;
&lt;h3 id="embedding-嵌入层"&gt;&lt;a href="#embedding-%e5%b5%8c%e5%85%a5%e5%b1%82" class="header-anchor"&gt;&lt;/a&gt;Embedding (嵌入层)
&lt;/h3&gt;&lt;p&gt;这是让数字拥有含义的一步。将 Tokenizer 生成的简单整数 ID 转换为 高维向量（即一长串数字数组）。这个过程就像查字典，每个 Token ID 对应一个固定的向量。&lt;/p&gt;
&lt;p&gt;下面是一个例子，可以看到将原始 token 进行 embedding 后是什么样子。&lt;/p&gt;
&lt;p&gt;&lt;img alt="Image" loading="lazy" sizes="(max-width: 767px) calc(100vw - 30px), (max-width: 1023px) 700px, (max-width: 1279px) 950px, 1232px" src="https://pub-f29bf2b53160470c9a85250116509a24.r2.dev/post/2025-12-29-ti-shi-ci-huan-cun-rang-llm-cheng-ben-jiang-10-bei/005-6e6071e9.png"&gt;&lt;/p&gt;
&lt;p&gt;&lt;img alt="Image" loading="lazy" sizes="(max-width: 767px) calc(100vw - 30px), (max-width: 1023px) 700px, (max-width: 1279px) 950px, 1232px" src="https://pub-f29bf2b53160470c9a85250116509a24.r2.dev/post/2025-12-29-ti-shi-ci-huan-cun-rang-llm-cheng-ben-jiang-10-bei/006-91838d99.png"&gt;&lt;/p&gt;
&lt;p&gt;Embedding 是可以有很多维度的，最大的模型甚至超过 10,000 维，上面的例子只显示了三维。&lt;strong&gt;维度越多，大语言模型对每个标记的表示就越复杂、越细致&lt;/strong&gt;。&lt;/p&gt;
&lt;p&gt;&lt;img alt="Image" loading="lazy" sizes="(max-width: 767px) calc(100vw - 30px), (max-width: 1023px) 700px, (max-width: 1279px) 950px, 1232px" src="https://pub-f29bf2b53160470c9a85250116509a24.r2.dev/post/2025-12-29-ti-shi-ci-huan-cun-rang-llm-cheng-ben-jiang-10-bei/007-08a0a8f3.png"&gt;&lt;/p&gt;
&lt;p&gt;这些向量代表了 Token 的 “语义位置”。在这个高维空间中，含义相似的词（如 “猫” 和 “狗”）在空间上的距离会更近。这一步还会把 Token 的 位置信息 编码进去，这样模型就能知道词语的先后顺序。&lt;/p&gt;
&lt;p&gt;如果你听说过 “余弦相似度”，那么恭喜你找对了方向。Embedding（嵌入） 和 Cosine Similarity（余弦相似度） 的关系可以理解为 “坐标” 与 “距离测量工具” 的关系。&lt;/p&gt;
&lt;p&gt;想象一个巨大的多维空间（就像一个无限大的图书馆）。Embedding 就是把每一个词、每一句话都变成这个空间里的一个 具体的坐标点，在这个空间里，意思相近的词（比如 “猫” 和 “小猫”），它们的坐标点会靠得很近；意思无关的词（比如 “猫” 和 “微波炉”），距离就会很远。&lt;strong&gt;Embedding 把文字变成了数学空间里的向量，而余弦相似度用来计算这些向量之间的 “语义距离”。&lt;/strong&gt;&lt;/p&gt;
&lt;h3 id="transformer-变换器--核心处理层"&gt;&lt;a href="#transformer-%e5%8f%98%e6%8d%a2%e5%99%a8--%e6%a0%b8%e5%bf%83%e5%a4%84%e7%90%86%e5%b1%82" class="header-anchor"&gt;&lt;/a&gt;Transformer (变换器 / 核心处理层)
&lt;/h3&gt;&lt;p&gt;这是 LLM 的大脑，负责理解和推理。&lt;/p&gt;
&lt;p&gt;它的主要工作是让输入序列中的每个 Token 相互 “交流”。模型会计算每个 Token 对其他 Token 的重要程度（即 “注意力权重”）。例如在句子 &amp;ldquo;Mary had a little lamb&amp;rdquo; 中，模型会计算出 &amp;ldquo;Mary&amp;rdquo; 对 &amp;ldquo;had&amp;rdquo; 的生成有多重要。这就是它的核心机制。&lt;/p&gt;
&lt;p&gt;到这里我知道你肯定会想到这篇开山之作**《Attention Is All You Need》**。没错，这篇论文作为开山之作，几乎全篇都在讨论 “Transformer”。该论文提出的 Transformer 架构，其主要职责就是接收 Embedding 层的输入（一堆数字向量），然后在这一层内部通过 Attention（注意力机制） 和 Feedforward（前馈网络） 对这些数据进行复杂的数学变换。关于论文这里不便展开，我们言归正传。&lt;/p&gt;
&lt;p&gt;在这一层，输入的 Embedding 会被转化为 &lt;strong&gt;Query (Q)、Key (K) 和 Value (V)&lt;/strong&gt; 三种形态。通过复杂的矩阵运算（Q 乘以 K 得到权重，再乘以 V），模型能够理解上下文的语境和词与词之间的关系。&lt;/p&gt;
&lt;p&gt;&lt;img alt="Image" loading="lazy" sizes="(max-width: 767px) calc(100vw - 30px), (max-width: 1023px) 700px, (max-width: 1279px) 950px, 1232px" src="https://pub-f29bf2b53160470c9a85250116509a24.r2.dev/post/2025-12-29-ti-shi-ci-huan-cun-rang-llm-cheng-ben-jiang-10-bei/008-582549f3.png"&gt;&lt;/p&gt;
&lt;p&gt;&lt;img alt="Image" loading="lazy" sizes="(max-width: 767px) calc(100vw - 30px), (max-width: 1023px) 700px, (max-width: 1279px) 950px, 1232px" src="https://pub-f29bf2b53160470c9a85250116509a24.r2.dev/post/2025-12-29-ti-shi-ci-huan-cun-rang-llm-cheng-ben-jiang-10-bei/009-b37c275b.png"&gt;&lt;/p&gt;
&lt;p&gt;&lt;img alt="Image" loading="lazy" sizes="(max-width: 767px) calc(100vw - 30px), (max-width: 1023px) 700px, (max-width: 1279px) 950px, 1232px" src="https://pub-f29bf2b53160470c9a85250116509a24.r2.dev/post/2025-12-29-ti-shi-ci-huan-cun-rang-llm-cheng-ben-jiang-10-bei/010-43603666.png"&gt;&lt;/p&gt;
&lt;p&gt;简单来说：&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;每个 token 会生成三组向量：&lt;strong&gt;Q (Query：我想找什么)、K (Key：我有什么线索)、V (Value：我的内容是什么)&lt;/strong&gt;&lt;/li&gt;
&lt;li&gt;通过计算 Q 和所有 K 的相似度，得到 “该关注谁” 的权重（softmax 归一化），再对 V 做加权求和，得到 “结合上下文后的新表示”。&lt;/li&gt;
&lt;li&gt;Multi-head 就是并行做多组注意力，让模型能同时学到多种关系（语法、指代、主题等）&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;img alt="Image" loading="lazy" sizes="(max-width: 767px) calc(100vw - 30px), (max-width: 1023px) 700px, (max-width: 1279px) 950px, 1232px" src="https://pub-f29bf2b53160470c9a85250116509a24.r2.dev/post/2025-12-29-ti-shi-ci-huan-cun-rang-llm-cheng-ben-jiang-10-bei/011-bf555d65.png"&gt;&lt;/p&gt;
&lt;p&gt;这个阶段是计算量最大的部分。为了加速，推理过程中会将计算过的 K 和 V 矩阵缓存起来（即 KV Cache），避免对之前的 Token 重复计算&lt;/p&gt;
&lt;h3 id="output-输出层"&gt;&lt;a href="#output-%e8%be%93%e5%87%ba%e5%b1%82" class="header-anchor"&gt;&lt;/a&gt;Output (输出层)
&lt;/h3&gt;&lt;p&gt;这是最终生成结果的一步。&lt;/p&gt;
&lt;p&gt;经过 Transformer 层层处理后，最后得到一个新的 Embedding。输出层会将其转化为概率分布，预测 下一个最可能出现的 Token。&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;LLM 是 “自回归” 的，这意味着它每次只生成一个 Token。生成的这个新 Token 会被加回到输入的末尾，整个流程（Tokenizer -&amp;gt; &amp;hellip; -&amp;gt; Output）再次循环，直到生成结束符（如）或达到长度限制&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;&lt;img alt="Image" loading="lazy" sizes="(max-width: 767px) calc(100vw - 30px), (max-width: 1023px) 700px, (max-width: 1279px) 950px, 1232px" src="https://pub-f29bf2b53160470c9a85250116509a24.r2.dev/post/2025-12-29-ti-shi-ci-huan-cun-rang-llm-cheng-ben-jiang-10-bei/012-215f95d4.gif"&gt;&lt;/p&gt;
&lt;h2 id="实现原理"&gt;&lt;a href="#%e5%ae%9e%e7%8e%b0%e5%8e%9f%e7%90%86" class="header-anchor"&gt;&lt;/a&gt;实现原理
&lt;/h2&gt;&lt;p&gt;了解了之前这些背景知道，我们就可以解释 Cached Token 的技术原理了。&lt;/p&gt;
&lt;p&gt;在 LLM（大语言模型）推理过程中，Cached Token 指的是对 KV Cache (Key-Value Cache) 的复用技术。&lt;/p&gt;
&lt;p&gt;Transformer 架构是自回归的。在生成回答（Decode 阶段）之前，模型必须先 “理解” 输入（Prefill 阶段）。这个 “理解” 过程涉及大量的矩阵运算，计算出每个 Token 的 Key 和 Value 向量（即注意力机制的中间状态）。对于长文本（如 RAG 场景中的大量文档），每次请求都重新计算这些 KV 向量是巨大的算力浪费，这就是 Cached Token 解决的问题。&lt;/p&gt;
&lt;p&gt;实现机制：&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;&lt;strong&gt;存储状态&lt;/strong&gt;： 当模型第一次处理前缀（Prefix，例如 System Prompt 或长文档）时，将计算好的 KV 向量驻留在 GPU 显存（VRAM）或层级存储中。&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;前缀匹配&lt;/strong&gt;： 当新的请求进来，如果开头部分（Prefix）与缓存中的 Token 完全一致，推理引擎（如 vLLM, SGLang）会直接加载已计算好的 KV 状态，跳过 Transformer 的前向计算过程。&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;PagedAttention&lt;/strong&gt;： 现代推理引擎（如 vLLM）使用类似操作系统内存分页的技术（PagedAttention）来管理这些缓存块，解决了显存碎片化问题，允许多个请求共享同一份物理显存中的 Prompt 数据&lt;/li&gt;
&lt;/ol&gt;
&lt;h1 id="想省钱要这样用"&gt;&lt;a href="#%e6%83%b3%e7%9c%81%e9%92%b1%e8%a6%81%e8%bf%99%e6%a0%b7%e7%94%a8" class="header-anchor"&gt;&lt;/a&gt;想省钱，要这样用
&lt;/h1&gt;&lt;p&gt;要在应用里稳定吃到 cached tokens（prompt caching），核心就三句话：&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;&lt;strong&gt;提示词要够长&lt;/strong&gt;（通常 ≥ 1024 tokens 才会开始命中）&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;前缀要 “完全一致”&lt;/strong&gt;（缓存按 “最长相同前缀” 命中，哪怕一个字符 / 空格不同都可能全失效）&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;把不变的放前面，把变化的放后面&lt;/strong&gt;（指令/工具/示例/长背景固定；用户问题、检索结果、时间戳等放末尾）&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;所以我们要从设计上进行些调整才能够 “省钱”：&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;设计 “可缓存的前缀结构”&lt;/strong&gt;，把 prompt 拆成两段（非常重要）：&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;可缓存前缀（Static Prefix）：system 指令、角色设定、规范、few-shot 示例、工具定义、长期不变的背景资料&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;动态尾部（Dynamic Tail）：用户输入、RAG 检索内容、实时数据、时间戳、request_id、实验开关等&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;多轮对话 / Agent 的注意事项&lt;/strong&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;消息数组要 “只追加，不改历史”：如果你为了省 tokens 把历史消息重排、压缩、或插入到中间，很可能导致前缀变了 → cache miss。&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;工具定义（tools）必须完全一致，顺序也要一致，否则工具部分也进不了缓存前缀&lt;/p&gt;
&lt;/li&gt;
&lt;/ul&gt;

 &lt;blockquote&gt;
 &lt;p&gt;“&lt;/p&gt;
&lt;p&gt;OpenAI Cookbook 直接建议： 静态内容放开头，可变内容放结尾；工具 / 图片也一样。&lt;/p&gt;

 &lt;/blockquote&gt;
&lt;h2 id="常见-踩坑清单"&gt;&lt;a href="#%e5%b8%b8%e8%a7%81-%e8%b8%a9%e5%9d%91%e6%b8%85%e5%8d%95" class="header-anchor"&gt;&lt;/a&gt;常见 “踩坑清单”
&lt;/h2&gt;&lt;ul&gt;
&lt;li&gt;把时间戳 / 随机 ID 放在 system 开头：每次都变，等于主动让缓存失效。&lt;/li&gt;
&lt;li&gt;JSON 序列化不稳定：同一份 tool schema 如果字段顺序、空格、换行变化，token 序列可能变 → miss（所以建议对 system/tools 做 “规范化输出”，并保持完全一致）&lt;/li&gt;
&lt;li&gt;指令在每次请求里微调一两个字：看似小改动，可能让前 1024 tokens 出现差异，直接从 “高命中” 变成 “全 miss”。Azure 文档 明确说 “前 1024 tokens 一个字符差异就会 miss”&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 id="缓存能活多久--怎么保持"&gt;&lt;a href="#%e7%bc%93%e5%ad%98%e8%83%bd%e6%b4%bb%e5%a4%9a%e4%b9%85--%e6%80%8e%e4%b9%88%e4%bf%9d%e6%8c%81" class="header-anchor"&gt;&lt;/a&gt;缓存能活多久 / 怎么保持
&lt;/h2&gt;&lt;p&gt;不同厂商策略不同，但你可以这么理解：缓存不是永久的，要么靠短时间内重复使用，要么使用更长的保留策略（如果提供）。&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Azure OpenAI：缓存通常在空闲 5–10 分钟清理，并且最晚 1 小时内会移除；还支持 prompt_cache_key 帮你影响路由提高命中，但同一前缀 + key 如果请求过猛（文档提到约 15 RPM 量级）可能溢出导致命中变差。&lt;/li&gt;
&lt;li&gt;OpenAI：提供 prompt_cache_retention（默认 in_memory，也可选 24h 做更长保留），并说明缓存的是 attention prefill 产生的 KV tensors，原始提示文本不以同样方式持久化。&lt;/li&gt;
&lt;li&gt;Anthropic Claude：通过在特定内容块上标注 cache_control 来启用 / 控制缓存（用法是显式的）。&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 id="落地建议"&gt;&lt;a href="#%e8%90%bd%e5%9c%b0%e5%bb%ba%e8%ae%ae" class="header-anchor"&gt;&lt;/a&gt;落地建议
&lt;/h2&gt;&lt;p&gt;&lt;strong&gt;给开发&lt;/strong&gt;：&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;把系统提示词拆成 STATIC_SYSTEM_PROMPT（长期不变）+ DYNAMIC_CONTEXT（每次变）&lt;/li&gt;
&lt;li&gt;所有请求都按固定模板拼：STATIC_SYSTEM_PROMPT + tools + (可选固定示例) + DYNAMIC_CONTEXT + user_question&lt;/li&gt;
&lt;li&gt;总结来说：把静态内容（System Prompt、Tools）置顶，动态内容（User Query、Time）置底；确保 JSON 序列化顺序固定；针对 Claude 需手动加标记；监控 “缓存命中率”（Cache Hit Rate）指标，确保不是在做负优化。&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;strong&gt;给产品&lt;/strong&gt;：&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;缓存能让长文档分析、多轮对话变得极快且便宜。设计功能时，尽量让用户基于一个 “固定的背景”（如上传一份文档后针对该文档多次提问），这最能利用缓存优势。&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 id="实际应用场景"&gt;&lt;a href="#%e5%ae%9e%e9%99%85%e5%ba%94%e7%94%a8%e5%9c%ba%e6%99%af" class="header-anchor"&gt;&lt;/a&gt;实际应用场景
&lt;/h2&gt;&lt;ul&gt;
&lt;li&gt;多轮对话 (Chatbot)： 用户和 AI 聊了 20 轮，第 21 轮时，前 20 轮的历史记录就是 “Cached Token”。不用每次都重算历史记录，响应更快。&lt;/li&gt;
&lt;li&gt;文档问答 (RAG)： 上传一本 PDF 法律合同。只要文件没变，第二个问题开始，AI 就不需要重新处理这份文件&lt;/li&gt;
&lt;li&gt;代码助手 (Coding Agent)： 将整个项目的代码库结构作为 Prompt 发送给 AI。这部分内容巨大且变动不频繁，非常适合缓存。&lt;/li&gt;
&lt;li&gt;角色扮演 / Agent： 复杂的 System Prompt（设定 AI 的性格、规则、工具定义）通常很长且固定，缓存后每次调用都极快&lt;/li&gt;
&lt;/ul&gt;</description></item><item><title>3 毛钱干大事？ 用了几天豆包编程模型，我来扒一扒字节这波操作</title><link>https://xiaobox.github.io/p/2025-11-17-3-mao-qian-gan-da-shi-yong-le-ji-tian-dou-bao-bian-cheng-mo/</link><pubDate>Mon, 17 Nov 2025 12:58:03 +0000</pubDate><guid>https://xiaobox.github.io/p/2025-11-17-3-mao-qian-gan-da-shi-yong-le-ji-tian-dou-bao-bian-cheng-mo/</guid><description>&lt;img src="https://pub-f29bf2b53160470c9a85250116509a24.r2.dev/post/2025-11-17-3-mao-qian-gan-da-shi-yong-le-ji-tian-dou-bao-bian-cheng-mo-/cover.jpg" alt="Featured image of post 3 毛钱干大事？ 用了几天豆包编程模型，我来扒一扒字节这波操作" /&gt;
 &lt;blockquote&gt;
 &lt;p&gt;“&lt;/p&gt;
&lt;p&gt;测评人： 小盒子（AI 架构仔，Agentic 编程方向，常年被 API 账单搞到头大） 测评时间： 2025 年 11 月 11 日发布后的一周&lt;/p&gt;

 &lt;/blockquote&gt;
&lt;p&gt;&lt;strong&gt;这波价格战，字节是真不想给同行活路了&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;说实话，我当时 &lt;strong&gt;凌晨 1:47&lt;/strong&gt; 在公司改那个傻 X 的 Kubernetes 配置。看到新闻推送，火山引擎出了个 &lt;strong&gt;豆包编程模型 Doubao-Seed-Code&lt;/strong&gt;，说性能 SOTA，但这不是重点。&lt;/p&gt;
&lt;p&gt;重点是价格。它宣称 综合成本能比业界平均水平低 62.7%，直接是 &lt;strong&gt;国内最低价&lt;/strong&gt;。我当时正在用 cc 搭配 k2，心想：都说最低价，质量怎么样呢？k2 测完了其实还是不如原装的 claude，所以 doubao-seed-code 如果真是质量高价格低的话，多一个选择也是蛮不错的。&lt;/p&gt;
&lt;p&gt;以前我们跑一次复杂的 Agentic 任务，特别是涉及多轮 Bug 修复和重构的，Claude Sonnet 4.5 那个账单，每个月看一次疼一次。&lt;/p&gt;
&lt;p&gt;我看官方资料里明晃晃地写着，做一个交互式英语学习网站，用 Doubao-Seed-Code 只需要 &lt;strong&gt;0.34 元左右&lt;/strong&gt;，用 Claude Sonnet 4.5 可是要大概 4 块多。 这差距，可以的～&lt;/p&gt;
&lt;p&gt;它这个 API 定价，输入 1.20 元/百万 Tokens，输出 8.00 元/百万 Tokens（0-32K 区间），配合那个 Cache 技术，还能再降 80% 的成本。我们现在正在做 Agent 自动化项目，以前成本受限，很多地方要做工程优化，要这样的话，感觉忽然就 &lt;strong&gt;经济可行&lt;/strong&gt; 了。&lt;/p&gt;
&lt;p&gt;&lt;img alt="Image" loading="lazy" sizes="(max-width: 767px) calc(100vw - 30px), (max-width: 1023px) 700px, (max-width: 1279px) 950px, 1232px" src="https://pub-f29bf2b53160470c9a85250116509a24.r2.dev/post/2025-11-17-3-mao-qian-gan-da-shi-yong-le-ji-tian-dou-bao-bian-cheng-mo-/001-ae46438e.png"&gt;&lt;/p&gt;
&lt;p&gt;&lt;img alt="Image" loading="lazy" sizes="(max-width: 767px) calc(100vw - 30px), (max-width: 1023px) 700px, (max-width: 1279px) 950px, 1232px" src="https://pub-f29bf2b53160470c9a85250116509a24.r2.dev/post/2025-11-17-3-mao-qian-gan-da-shi-yong-le-ji-tian-dou-bao-bian-cheng-mo-/002-3fa20132.png"&gt;&lt;/p&gt;
&lt;p&gt;我立马摸鱼时试了下，冲了它那个 &lt;strong&gt;9.9&lt;/strong&gt; 块首月 的 Coding Plan。一杯咖啡钱，买一个号称 SWE-Bench Verified 榜单上 SOTA 的模型（这个榜单是测 Agent 端到端解决问题的能力，很硬核的）。&lt;/p&gt;
&lt;h1 id="兼容-claude-code"&gt;&lt;a href="#%e5%85%bc%e5%ae%b9-claude-code" class="header-anchor"&gt;&lt;/a&gt;兼容 Claude Code
&lt;/h1&gt;&lt;p&gt;感觉最近这都成了编程模型的标配了哈。&lt;/p&gt;
&lt;p&gt;作为 Claude Code 用户，感觉接入不是很丝滑的。Doubao-Seed-Code &lt;strong&gt;原生兼容 Anthropic API&lt;/strong&gt; ，接入方法还是老套路，很简单：&lt;/p&gt;
&lt;h2 id="第一种方式"&gt;&lt;a href="#%e7%ac%ac%e4%b8%80%e7%a7%8d%e6%96%b9%e5%bc%8f" class="header-anchor"&gt;&lt;/a&gt;第一种方式
&lt;/h2&gt;&lt;p&gt;如果是短期测试，可以直接在终端中配置环境变量，在启动 Claude Code 前输入环境变量&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-gdscript3" data-lang="gdscript3"&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;1&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="k"&gt;export&lt;/span&gt; &lt;span class="n"&gt;ANTHROPIC_BASE_URL&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="n"&gt;https&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="o"&gt;//&lt;/span&gt;&lt;span class="n"&gt;ark&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;cn&lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="n"&gt;beijing&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;volces&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;com&lt;/span&gt;&lt;span class="o"&gt;/&lt;/span&gt;&lt;span class="n"&gt;api&lt;/span&gt;&lt;span class="o"&gt;/&lt;/span&gt;&lt;span class="n"&gt;compatible&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;2&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="k"&gt;export&lt;/span&gt; &lt;span class="n"&gt;ANTHROPIC_AUTH_TOKEN&lt;/span&gt;&lt;span class="o"&gt;=&amp;lt;&lt;/span&gt;&lt;span class="n"&gt;ARK&lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="n"&gt;API&lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="n"&gt;KEY&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;3&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="k"&gt;export&lt;/span&gt; &lt;span class="n"&gt;ANTHROPIC_MODEL&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="n"&gt;doubao&lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="nb"&gt;seed&lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="n"&gt;code&lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="n"&gt;preview&lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="n"&gt;latest&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;h2 id="第二种方式"&gt;&lt;a href="#%e7%ac%ac%e4%ba%8c%e7%a7%8d%e6%96%b9%e5%bc%8f" class="header-anchor"&gt;&lt;/a&gt;第二种方式
&lt;/h2&gt;&lt;p&gt;如果是长期使用，可以直接配置文件&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-json" data-lang="json"&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;1&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="err"&gt;open&lt;/span&gt; &lt;span class="err"&gt;-e&lt;/span&gt; &lt;span class="err"&gt;~/.claude/settings.json&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;2&lt;/span&gt;&lt;span class="cl"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;3&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="p"&gt;{&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;4&lt;/span&gt;&lt;span class="cl"&gt; &lt;span class="nt"&gt;&amp;#34;api_key&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;&amp;#34;xxxxxxx&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;5&lt;/span&gt;&lt;span class="cl"&gt; &lt;span class="nt"&gt;&amp;#34;api_url&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;&amp;#34;https://ark.cn-beijing.volces.com/api/compatible&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;6&lt;/span&gt;&lt;span class="cl"&gt; &lt;span class="nt"&gt;&amp;#34;model&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;&amp;#34;doubao-seed-code-preview-latest&amp;#34;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;7&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;说句提外话，最近这几家搞 code 模型的，就是明着抢 Claude 的客户，但我支持，哈哈。&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;切换零成本 + 价格低 60%+ 性能 SOTA 确实有点儿心动。&lt;/strong&gt;&lt;/p&gt;
&lt;h1 id="核心能力体验"&gt;&lt;a href="#%e6%a0%b8%e5%bf%83%e8%83%bd%e5%8a%9b%e4%bd%93%e9%aa%8c" class="header-anchor"&gt;&lt;/a&gt;核心能力体验
&lt;/h1&gt;
 &lt;blockquote&gt;
 &lt;p&gt;“&lt;/p&gt;
&lt;p&gt;长上下文和那个 VLM 才是真杀手锏&lt;/p&gt;

 &lt;/blockquote&gt;
&lt;p&gt;光便宜和兼容没用，代码写得烂，那也是浪费我时间。&lt;/p&gt;
&lt;p&gt;看了一下上下文，256K，还成，跟 K2 一样，感觉现在没个 256K 都不好拿出手。&lt;/p&gt;
&lt;p&gt;虽然 Claude 4.5 Sonnet 的上下文声称是 1M，但实际上只有 200K，而且还死贵。 256 好，还多 56K，哈哈&lt;/p&gt;
&lt;p&gt;别小看多出来的这点儿。我手头有个遗留项目，Python 写的，几百个文件，那叫一个乱。模型处理 Bug，有时候上下文 Token 一爆，它就变瞎子了，你得手动 RAG 喂它代码，有时候就差那么一两个文件，逼得我重开个 thread，前面都白费劲了。&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Doubao-Seed-Code 多出来的这 56K，意味着它能把 整个中等规模的项目结构和依赖 都装进 “脑子” 里&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;&lt;img alt="Image" loading="lazy" sizes="(max-width: 767px) calc(100vw - 30px), (max-width: 1023px) 700px, (max-width: 1279px) 950px, 1232px" src="https://pub-f29bf2b53160470c9a85250116509a24.r2.dev/post/2025-11-17-3-mao-qian-gan-da-shi-yong-le-ji-tian-dou-bao-bian-cheng-mo-/003-9eee4524.png"&gt;&lt;/p&gt;
&lt;p&gt;刚才我让它解决一个跨越十几个文件的逻辑 Bug，以前的模型得来回拉扯五六轮，这次它 &lt;strong&gt;一步到位&lt;/strong&gt; 定位到了问题。而且它不只是修复 Bug，它还会 &lt;strong&gt;优化结构&lt;/strong&gt;，提升代码的可读性和维护性。这才是 Agent 编程，不过客观地讲跟最贵的那位比还是有一定的差距。&lt;/p&gt;
&lt;h1 id="vlm前端仔的末日-还是福音"&gt;&lt;a href="#vlm%e5%89%8d%e7%ab%af%e4%bb%94%e7%9a%84%e6%9c%ab%e6%97%a5-%e8%bf%98%e6%98%af%e7%a6%8f%e9%9f%b3" class="header-anchor"&gt;&lt;/a&gt;VLM：前端仔的末日… 还是福音？
&lt;/h1&gt;&lt;p&gt;这个视觉理解（VLM）能力， &lt;strong&gt;国内首发&lt;/strong&gt;。&lt;/p&gt;
&lt;p&gt;这个功能并不新鲜，但国内首发，算是跟上了。我现在可以直接把 UI 稿截图，或者 手绘草稿 扔给它。然后它能给你生成对应的代码。&lt;/p&gt;
&lt;p&gt;我一开始以为它就是搞了个图转文字，再让 LLM 去生成代码，这种方法信息折损很大。结果 它这个是&lt;strong&gt;原生的 VLM 能力&lt;/strong&gt;，不是靠工具调用。最牛逼的是，它能 &lt;strong&gt;自己完成样式修复和 Bug 修复&lt;/strong&gt;。它生成一个页面，然后拿截图跟你原始的设计稿对比，发现哪里边距不对，哪里颜色溢出了，自己动手改&lt;/p&gt;
&lt;p&gt;我当时试了一个复杂的 Dashboard 界面，只给了一张截图，它生成的 React + Tailwind 代码还原度还是非常高的。前端兄弟估计已经麻木了，据我所知，他们自己也在用 vibe coding 干活，哈哈。&lt;/p&gt;
&lt;h1 id="聊聊技术底裤"&gt;&lt;a href="#%e8%81%8a%e8%81%8a%e6%8a%80%e6%9c%af%e5%ba%95%e8%a3%a4" class="header-anchor"&gt;&lt;/a&gt;聊聊技术底裤
&lt;/h1&gt;&lt;p&gt;Doubao-Seed-Code 的核心是 &lt;strong&gt;Seed-Coder&lt;/strong&gt; 家族，能 SOTA，说明字节在训练上砸了&lt;strong&gt;不少黑科技&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;官方资料里提了一堆很唬人的词儿, 小盒子来翻译翻译：&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;“大规模 Agent 强化学习训练系统”&lt;/strong&gt; ：他们好像是搞了一套巨大的 &lt;strong&gt;打怪升级系统&lt;/strong&gt;，专门用来训练代码 Agent。模型不是靠背书（预训练数据）学编程的，它是直接在 &lt;strong&gt;沙盒里&lt;/strong&gt; 跑代码&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;构建了覆盖 10 万容器镜像的训练数据集”&lt;/strong&gt;：为了让模型见过各种稀奇古怪的运行环境（比如 Python 3.7 + PyTorch 1.9 + CUDA 10.2），他们准备了 10 万个容器&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;“万级并发沙盒 session”&lt;/strong&gt;：几万个容器同时跑。让模型在里面不断试错，错了就 &lt;strong&gt;罚站（接收执行反馈）&lt;/strong&gt;。这样练出来的 Agent，解决问题的鲁棒性才强&lt;/p&gt;
&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;这套机制直接解释了为什么它能在 SWE-Bench Verified 这种需要端到端解决问题的测试里登顶。它不是一个静态的知识库，它是个会 &lt;strong&gt;思考、会动手、会自我修正&lt;/strong&gt; 的开发伙伴&lt;/p&gt;
&lt;p&gt;顺便提一句，这个 Seed-Coder 还有开源版本。开源的 Seed-Coder-8B-Reasoning 有 64K 上下文，虽然不如商业 API 的 256K 那么猛，但对于个人研究也够用了。&lt;/p&gt;
&lt;p&gt;&lt;img alt="Image" loading="lazy" sizes="(max-width: 767px) calc(100vw - 30px), (max-width: 1023px) 700px, (max-width: 1279px) 950px, 1232px" src="https://pub-f29bf2b53160470c9a85250116509a24.r2.dev/post/2025-11-17-3-mao-qian-gan-da-shi-yong-le-ji-tian-dou-bao-bian-cheng-mo-/004-1050e617.png"&gt;&lt;/p&gt;
&lt;h1 id="测试"&gt;&lt;a href="#%e6%b5%8b%e8%af%95" class="header-anchor"&gt;&lt;/a&gt;测试
&lt;/h1&gt;&lt;p&gt;这里我做了一个测试，目的是看它能不能真的理解 “Vibe Coding”（用户描述一个抽象的、高层的需求，让 Agent 去实现），特别是设计稿的还原和自我纠错能力&lt;/p&gt;
&lt;p&gt;找一个 UI 稿截图，越复杂越好。&lt;/p&gt;
&lt;p&gt;&lt;img alt="Image" loading="lazy" sizes="(max-width: 767px) calc(100vw - 30px), (max-width: 1023px) 700px, (max-width: 1279px) 950px, 1232px" src="https://pub-f29bf2b53160470c9a85250116509a24.r2.dev/post/2025-11-17-3-mao-qian-gan-da-shi-yong-le-ji-tian-dou-bao-bian-cheng-mo-/005-cfa337f6.png"&gt;&lt;/p&gt;
&lt;p&gt;&lt;img alt="Image" loading="lazy" sizes="(max-width: 767px) calc(100vw - 30px), (max-width: 1023px) 700px, (max-width: 1279px) 950px, 1232px" src="https://pub-f29bf2b53160470c9a85250116509a24.r2.dev/post/2025-11-17-3-mao-qian-gan-da-shi-yong-le-ji-tian-dou-bao-bian-cheng-mo-/006-b0aed37a.png"&gt;&lt;/p&gt;
&lt;p&gt;最终生成的效果如下：&lt;/p&gt;
&lt;p&gt;&lt;img alt="Image" loading="lazy" sizes="(max-width: 767px) calc(100vw - 30px), (max-width: 1023px) 700px, (max-width: 1279px) 950px, 1232px" src="https://pub-f29bf2b53160470c9a85250116509a24.r2.dev/post/2025-11-17-3-mao-qian-gan-da-shi-yong-le-ji-tian-dou-bao-bian-cheng-mo-/007-9d9ec23b.png"&gt;&lt;/p&gt;
&lt;h1 id="总结"&gt;&lt;a href="#%e6%80%bb%e7%bb%93" class="header-anchor"&gt;&lt;/a&gt;总结
&lt;/h1&gt;&lt;p&gt;无论最后你是否使用 doubao-seed-code 模型作为你的生产工具，我都推荐你试试，包括 k2 等其他模型，无它，AI 进化的速度很快，先上车！&lt;/p&gt;
&lt;p&gt;单纯就 doubao-seed-code 来说，我觉得也还可以：&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;价格摆在那儿，跑 100 次 Agentic 任务的成本，以前可能只能跑 30 次。&lt;/li&gt;
&lt;li&gt;VLM 是未来：前端开发效率的飞跃。&lt;/li&gt;
&lt;li&gt;256K 上下文：真正能处理企业级复杂重构任务的基础。&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;Doubao-Seed-Code 这波操作，是想把 AI 编程从 “昂贵的工具” 变成 “水、电、煤” 一样基础设施 。对于追求极致效率和成本控制的团队，值得一试。&lt;/p&gt;</description></item><item><title>LangChain：是银弹，还是 “技术债”？</title><link>https://xiaobox.github.io/p/2025-09-23-langchain-shi-yin-tan-hai-shi-ji-shu-zhai/</link><pubDate>Tue, 23 Sep 2025 05:46:17 +0000</pubDate><guid>https://xiaobox.github.io/p/2025-09-23-langchain-shi-yin-tan-hai-shi-ji-shu-zhai/</guid><description>&lt;img src="https://pub-f29bf2b53160470c9a85250116509a24.r2.dev/post/2025-09-23-langchain-shi-yin-tan-hai-shi-ji-shu-zhai/cover.jpg" alt="Featured image of post LangChain：是银弹，还是 “技术债”？" /&gt;&lt;h1 id="引言"&gt;&lt;a href="#%e5%bc%95%e8%a8%80" class="header-anchor"&gt;&lt;/a&gt;引言
&lt;/h1&gt;&lt;p&gt;当前，智能 Agent 的开发正面临两条截然不同的路径选择。一方面，高代码方式通过 SDK 和 API 编码提供灵活性，但带来了巨大的复杂性负担——开发者需要深入理解模型集成、工具调用、记忆管理和分布式协调等复杂概念，显著提高了开发门槛和维护成本。另一方面，像百炼，Dify、Coze 为代表的低代码平台以其出色的易用性迅速占领市场，通过可视化界面让用户能够快速构建 &amp;ldquo;Model+Prompt+MCP+RAG+Memory&amp;rdquo; 的标准 Agent 模式。&lt;/p&gt;
&lt;h1 id="高代码与低代码"&gt;&lt;a href="#%e9%ab%98%e4%bb%a3%e7%a0%81%e4%b8%8e%e4%bd%8e%e4%bb%a3%e7%a0%81" class="header-anchor"&gt;&lt;/a&gt;高代码与低代码
&lt;/h1&gt;&lt;h2 id="高代码"&gt;&lt;a href="#%e9%ab%98%e4%bb%a3%e7%a0%81" class="header-anchor"&gt;&lt;/a&gt;高代码
&lt;/h2&gt;&lt;p&gt;优势&lt;/p&gt;
&lt;p&gt;●控制粒度高：检索、重排、记忆淘汰策略、工具容错、并发/一致性都能精细掌控。&lt;/p&gt;
&lt;p&gt;●可移植/可替换：模型、向量库、存储、消息队列可按需换，避免深度锁定。&lt;/p&gt;
&lt;p&gt;●性能上限高：可针对热路径做缓存/批量化/并行/算力亲和等优化。&lt;/p&gt;
&lt;p&gt;●合规友好：易于纯内网/私有化落地，满足数据边界与审计需求。&lt;/p&gt;
&lt;p&gt;劣势&lt;/p&gt;
&lt;p&gt;●上手成本高：需要理解模型行为、工具协议、状态管理、分布式、测试/评测。&lt;/p&gt;
&lt;p&gt;●开发周期长：原型到生产的路径更长，对团队工程能力要求高。&lt;/p&gt;
&lt;p&gt;●维护复杂：提示/数据/评测/日志/灰度与回滚都要自己做治理。&lt;/p&gt;
&lt;p&gt;适用场景&lt;/p&gt;
&lt;p&gt;●对稳定性、性能、合规要求高的核心业务流程（客服、风控、运维、知识中枢）。&lt;/p&gt;
&lt;p&gt;●强定制：复杂工具链（多后端系统、定制检索策略、多段对话状态机）。&lt;/p&gt;
&lt;p&gt;●内网/私有化：外网受限、需与既有基建深度耦合（监控、鉴权、审计）。&lt;/p&gt;
&lt;h2 id="低代码"&gt;&lt;a href="#%e4%bd%8e%e4%bb%a3%e7%a0%81" class="header-anchor"&gt;&lt;/a&gt;低代码
&lt;/h2&gt;&lt;p&gt;优势&lt;/p&gt;
&lt;p&gt;●速度快：原型与迭代极快，业务同学也能参与搭建与验收。&lt;/p&gt;
&lt;p&gt;●门槛低：抽象好了调用、编排、上下文缓存、简单评测与发布。&lt;/p&gt;
&lt;p&gt;●运维成本低：平台内置监控/日志/版本管理（能力视平台而定）。&lt;/p&gt;
&lt;p&gt;劣势&lt;/p&gt;
&lt;p&gt;●可扩展性受限：复杂状态机、精细化检索/重排、跨域事务一致性等较难。&lt;/p&gt;
&lt;p&gt;●性能上限有限：难做深度批处理、算力亲和、跨服务并行等工程优化。&lt;/p&gt;
&lt;p&gt;●供应商/能力锁定：某些特性依赖平台实现，迁移成本较高。&lt;/p&gt;
&lt;p&gt;●私有化差异：部分平台更偏 SaaS；若需纯内网，要筛选支持私有化/离线模型的方案。&lt;/p&gt;
&lt;p&gt;适用场景&lt;/p&gt;
&lt;p&gt;●探索/验证期：快速做 PoC、AB 实验、用户调研与演示。&lt;/p&gt;
&lt;p&gt;●中轻量业务：知识问答、表单处理、运营活动、内部助理等非关键路径。&lt;/p&gt;
&lt;p&gt;●混合团队：产品/运营可直接改提示与流程，工程只需提供数据/工具接口。&lt;/p&gt;
&lt;h2 id="场景选型"&gt;&lt;a href="#%e5%9c%ba%e6%99%af%e9%80%89%e5%9e%8b" class="header-anchor"&gt;&lt;/a&gt;场景选型
&lt;/h2&gt;&lt;p&gt;高代码和低代码有各自的特点和适用场景，那我们该如何决策呢？下面是一个快速决策矩阵：&lt;/p&gt;
&lt;p&gt;&lt;img alt="图片" loading="lazy" sizes="(max-width: 767px) calc(100vw - 30px), (max-width: 1023px) 700px, (max-width: 1279px) 950px, 1232px" src="https://pub-f29bf2b53160470c9a85250116509a24.r2.dev/post/2025-09-23-langchain-shi-yin-tan-hai-shi-ji-shu-zhai/001-b6df3b08.png"&gt;&lt;/p&gt;
&lt;p&gt;总结来说：要“快试错”选低代码，要“硬落地”选高代码；两者并不对立，适合“原型低代码 + 核心高代码”的混合路线。&lt;/p&gt;
&lt;p&gt;具体来说：&lt;/p&gt;
&lt;p&gt;●2 周内交付可用原型、验证需求是否真实 → 低代码&lt;/p&gt;
&lt;p&gt;●承载 7×24 核心业务，SLA/审计/内网合规很硬 → 高代码&lt;/p&gt;
&lt;p&gt;●大量业务同学参与、频繁改提示与流程 → 低代码 +（必要时）接入自研工具&lt;/p&gt;
&lt;p&gt;●把 RAG/记忆/工具编排做成“组织级能力层” → 高代码（沉淀为平台/服务）&lt;/p&gt;
&lt;p&gt;●先做 Demo，再逐步把关键链路“工程化” → 低→高的混合迁移&lt;/p&gt;
&lt;p&gt;这里需要注意的是，要避免反模式：&lt;/p&gt;
&lt;p&gt;●把复杂状态机硬堆在低代码画布里，后期难以维护与回放。&lt;/p&gt;
&lt;p&gt;●过早全高代码，导致验证周期太长、需求未定先造轮子。&lt;/p&gt;
&lt;p&gt;●忽视提示/知识/评测的版本化与可回滚。&lt;/p&gt;
&lt;h2 id="混合实践"&gt;&lt;a href="#%e6%b7%b7%e5%90%88%e5%ae%9e%e8%b7%b5" class="header-anchor"&gt;&lt;/a&gt;混合实践
&lt;/h2&gt;&lt;p&gt;对于我们来说，现在正好处于一个 “混合迁移” 的阶段，我们即在使用低代码平台 Dify,也在某些具体的场景下感到了 Dify的不适。所以对于某些项目要进行必要的工程化迁移和改造，具体思路是：&lt;/p&gt;
&lt;p&gt;●前台用低代码（业务侧快速改动、AB/评测、需求验证）；&lt;/p&gt;
&lt;p&gt;●后台用高代码沉淀“能力层”（RAG 服务、工具/MCP、回溯评测、观测/追踪、策略引擎）。&lt;/p&gt;
&lt;p&gt;●平台只做编排与呈现，能力层提供稳定 API。&lt;/p&gt;
&lt;p&gt;●形成“能力可复用、前台可迭代、核心可控”的结构。&lt;/p&gt;
&lt;p&gt;一句话总结：低代码赢在速度，高代码赢在确定性；用低代码把事儿“做成”，再用高代码把事儿“做稳且做大”。&lt;/p&gt;
&lt;h1 id="langchain"&gt;&lt;a href="#langchain" class="header-anchor"&gt;&lt;/a&gt;LangChain
&lt;/h1&gt;&lt;h2 id="概念说明"&gt;&lt;a href="#%e6%a6%82%e5%bf%b5%e8%af%b4%e6%98%8e" class="header-anchor"&gt;&lt;/a&gt;概念说明
&lt;/h2&gt;&lt;p&gt;提到 LangChain 我们要先厘清一下概念，因为这里有两个概念：&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;第一&lt;/strong&gt;，LangChain Inc. 是一家总部位于美国旧金山的前沿人工智能技术公司。公司成立于 2022 年，由 Harrison Chase 和 Ankush Gola 共同创立，2023 年正式独立成立公司实体。 公司注册于 2023 年 1 月 31 日，总部地址位于加利福尼亚州旧金山市 Decatur 街 42 号。&lt;/p&gt;
&lt;p&gt;&lt;img alt="图片" loading="lazy" sizes="(max-width: 767px) calc(100vw - 30px), (max-width: 1023px) 700px, (max-width: 1279px) 950px, 1232px" src="https://pub-f29bf2b53160470c9a85250116509a24.r2.dev/post/2025-09-23-langchain-shi-yin-tan-hai-shi-ji-shu-zhai/002-475d5330.png"&gt;&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;第二&lt;/strong&gt;，LangChain 还是一个用来开发基于 LLM 的 AI 应用框架。&lt;/p&gt;
&lt;p&gt;&lt;img alt="图片" loading="lazy" sizes="(max-width: 767px) calc(100vw - 30px), (max-width: 1023px) 700px, (max-width: 1279px) 950px, 1232px" src="https://pub-f29bf2b53160470c9a85250116509a24.r2.dev/post/2025-09-23-langchain-shi-yin-tan-hai-shi-ji-shu-zhai/003-6dd54185.png"&gt;&lt;/p&gt;
&lt;p&gt;从 LangChain 公司官网和官方文档提供的产品架构图中可以看出，LangChain公司提供的主要产品有：&lt;/p&gt;
&lt;p&gt;●开发框架&lt;/p&gt;
&lt;p&gt;○LangChain（OSS-免费开源软件)&lt;/p&gt;
&lt;p&gt;○LangGraph（OSS-免费开源软件）&lt;/p&gt;
&lt;p&gt;●平台&lt;/p&gt;
&lt;p&gt;○LangSmith (COMMERCIAL-商业收费)&lt;/p&gt;
&lt;p&gt;○LangGraph Platform (COMMERCIAL-商业收费)&lt;/p&gt;
&lt;p&gt;在下文中如无特殊说明，LangChain 一律指代第二个概念，即开源的开发框架。&lt;/p&gt;
&lt;h2 id="大模型应用开发核心矛盾"&gt;&lt;a href="#%e5%a4%a7%e6%a8%a1%e5%9e%8b%e5%ba%94%e7%94%a8%e5%bc%80%e5%8f%91%e6%a0%b8%e5%bf%83%e7%9f%9b%e7%9b%be" class="header-anchor"&gt;&lt;/a&gt;大模型应用开发核心矛盾
&lt;/h2&gt;&lt;p&gt;当下的 LLM 本身如同一个 “博学但无手无脚的大脑”，它无法感知实时信息、无法操作外部工具、也无法与我们的私有数据交互。这个 “从 “模型能力” 到 “应用能力” 的鸿沟” 正是所有 LLM 应用开发者面临的首要难题。&lt;/p&gt;
&lt;p&gt;&lt;img alt="图片" loading="lazy" sizes="(max-width: 767px) calc(100vw - 30px), (max-width: 1023px) 700px, (max-width: 1279px) 950px, 1232px" src="https://pub-f29bf2b53160470c9a85250116509a24.r2.dev/post/2025-09-23-langchain-shi-yin-tan-hai-shi-ji-shu-zhai/004-879f94f6.png"&gt;&lt;/p&gt;
&lt;p&gt;LangChain 不是一个 “新发明”，而是一个 “高效的连接器和编排器”。它的战略价值在于，它是当前弥合 “模型能力” 与 “应用能力” 鸿沟的最成熟的工程化解决方案之一。&lt;/p&gt;
&lt;h2 id="框架介绍"&gt;&lt;a href="#%e6%a1%86%e6%9e%b6%e4%bb%8b%e7%bb%8d" class="header-anchor"&gt;&lt;/a&gt;框架介绍
&lt;/h2&gt;&lt;p&gt;LangChain 的核心思想是“链”，它将 LLM 应用程序的各个组件连接在一起，形成一个完整的工作流。这种模块化的方法可以将复杂的人工智能系统分解为可重用的部分。LangChain 提供了一系列工具和抽象，帮助开发人员将 LLM 与外部数据源（如数据库、API等）连接起来，从而创建功能更强大的应用程序。&lt;/p&gt;
&lt;p&gt;&lt;img alt="图片" loading="lazy" sizes="(max-width: 767px) calc(100vw - 30px), (max-width: 1023px) 700px, (max-width: 1279px) 950px, 1232px" src="https://pub-f29bf2b53160470c9a85250116509a24.r2.dev/post/2025-09-23-langchain-shi-yin-tan-hai-shi-ji-shu-zhai/005-e22fbc6c.png"&gt;&lt;/p&gt;
&lt;h3 id="langchain-能够解决的五类问题"&gt;&lt;a href="#langchain-%e8%83%bd%e5%a4%9f%e8%a7%a3%e5%86%b3%e7%9a%84%e4%ba%94%e7%b1%bb%e9%97%ae%e9%a2%98" class="header-anchor"&gt;&lt;/a&gt;LangChain 能够解决的五类问题
&lt;/h3&gt;&lt;p&gt;LangChain 能够解决五个核心领域（按复杂度递增）&lt;/p&gt;
&lt;p&gt;&lt;img alt="图片" loading="lazy" sizes="(max-width: 767px) calc(100vw - 30px), (max-width: 1023px) 700px, (max-width: 1279px) 950px, 1232px" src="https://pub-f29bf2b53160470c9a85250116509a24.r2.dev/post/2025-09-23-langchain-shi-yin-tan-hai-shi-ji-shu-zhai/006-677c7dd2.png"&gt;&lt;/p&gt;
&lt;h4 id="1-模型与提示i--o-层"&gt;&lt;a href="#1-%e6%a8%a1%e5%9e%8b%e4%b8%8e%e6%8f%90%e7%a4%bai--o-%e5%b1%82" class="header-anchor"&gt;&lt;/a&gt;1. 模型与提示（I / O 层）
&lt;/h4&gt;&lt;p&gt;&lt;strong&gt;要解决什么？&lt;/strong&gt; 稳定、可替换地调用任意 LLM，并拿到可解析、可复用的输出。&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;关键点&lt;/strong&gt;：BaseChatModel、ChatPromptTemplate、OutputParser、LCEL invoke/stream/batch。&lt;/p&gt;
&lt;p&gt;一般来说入门 LangChain 都是从第一层起步：prompt | llm | parser&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-python" data-lang="python"&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt; 1&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="err"&gt;⚡&lt;/span&gt; &lt;span class="n"&gt;python片段&lt;/span&gt;&lt;span class="c1"&gt;# pip install -U langchain langchain-openai&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt; 2&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="kn"&gt;from&lt;/span&gt; &lt;span class="nn"&gt;langchain_core.prompts&lt;/span&gt; &lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;ChatPromptTemplate&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt; 3&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="kn"&gt;from&lt;/span&gt; &lt;span class="nn"&gt;langchain_core.output_parsers&lt;/span&gt; &lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;StrOutputParser&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt; 4&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="kn"&gt;from&lt;/span&gt; &lt;span class="nn"&gt;langchain_openai&lt;/span&gt; &lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;ChatOpenAI&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt; 5&lt;/span&gt;&lt;span class="cl"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt; 6&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="n"&gt;prompt&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;ChatPromptTemplate&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;from_messages&lt;/span&gt;&lt;span class="p"&gt;([&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt; 7&lt;/span&gt;&lt;span class="cl"&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;&amp;#34;system&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s2"&gt;&amp;#34;你是精炼的中文助手。&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt; 8&lt;/span&gt;&lt;span class="cl"&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;&amp;#34;human&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s2"&gt;&amp;#34;用一句话解释：&lt;/span&gt;&lt;span class="si"&gt;{topic}&lt;/span&gt;&lt;span class="s2"&gt;&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt; 9&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="p"&gt;])&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;10&lt;/span&gt;&lt;span class="cl"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;11&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="n"&gt;chain&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;prompt&lt;/span&gt; &lt;span class="o"&gt;|&lt;/span&gt; &lt;span class="n"&gt;ChatOpenAI&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;model&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s2"&gt;&amp;#34;gpt-4o-mini&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;|&lt;/span&gt; &lt;span class="n"&gt;StrOutputParser&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;12&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="nb"&gt;print&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;chain&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;invoke&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;&lt;span class="s2"&gt;&amp;#34;topic&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;&amp;#34;LCEL 是什么？&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;}))&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;h4 id="2-链式编排流程层"&gt;&lt;a href="#2-%e9%93%be%e5%bc%8f%e7%bc%96%e6%8e%92%e6%b5%81%e7%a8%8b%e5%b1%82" class="header-anchor"&gt;&lt;/a&gt;2. 链式编排（流程层）
&lt;/h4&gt;&lt;p&gt;&lt;strong&gt;要解决什么？&lt;/strong&gt; 把多个步骤（清洗→生成→解析→后处理）按顺序 / 并行可靠执行。&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;关键点&lt;/strong&gt;：Runnable 统一协议、| 管道、并行 map、重试与超时、缓存&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;适用&lt;/strong&gt;：流程确定、依赖明确的任务（如格式转换、规则后处理、批处理）。&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-python" data-lang="python"&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt; 1&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="err"&gt;⚡&lt;/span&gt; &lt;span class="n"&gt;python片段&lt;/span&gt;&lt;span class="c1"&gt;# pip install -U langchain langchain-openai&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt; 2&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="kn"&gt;from&lt;/span&gt; &lt;span class="nn"&gt;langchain_core.runnables&lt;/span&gt; &lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;RunnableLambda&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt; 3&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="kn"&gt;from&lt;/span&gt; &lt;span class="nn"&gt;langchain_core.prompts&lt;/span&gt; &lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;ChatPromptTemplate&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt; 4&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="kn"&gt;from&lt;/span&gt; &lt;span class="nn"&gt;langchain_core.output_parsers&lt;/span&gt; &lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;StrOutputParser&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt; 5&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="kn"&gt;from&lt;/span&gt; &lt;span class="nn"&gt;langchain_openai&lt;/span&gt; &lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;ChatOpenAI&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt; 6&lt;/span&gt;&lt;span class="cl"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt; 7&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="n"&gt;pre&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;RunnableLambda&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;lambda&lt;/span&gt; &lt;span class="n"&gt;x&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="s2"&gt;&amp;#34;q&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;x&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s2"&gt;&amp;#34;q&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;strip&lt;/span&gt;&lt;span class="p"&gt;()[:&lt;/span&gt;&lt;span class="mi"&gt;200&lt;/span&gt;&lt;span class="p"&gt;]})&lt;/span&gt; &lt;span class="c1"&gt;# 预处理：清理&amp;amp;截断&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt; 8&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="n"&gt;post&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;RunnableLambda&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;lambda&lt;/span&gt; &lt;span class="n"&gt;s&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;s&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;rstrip&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;&amp;#34;。&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="s2"&gt;&amp;#34;。&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="c1"&gt;# 后处理：补全句号&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt; 9&lt;/span&gt;&lt;span class="cl"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;10&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="n"&gt;prompt&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;ChatPromptTemplate&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;from_messages&lt;/span&gt;&lt;span class="p"&gt;([&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;11&lt;/span&gt;&lt;span class="cl"&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;&amp;#34;system&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s2"&gt;&amp;#34;用简洁中文回答。&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;12&lt;/span&gt;&lt;span class="cl"&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;&amp;#34;human&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s2"&gt;&amp;#34;把这些要点合成一句话：&lt;/span&gt;&lt;span class="si"&gt;{q}&lt;/span&gt;&lt;span class="s2"&gt;&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;13&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="p"&gt;])&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;14&lt;/span&gt;&lt;span class="cl"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;15&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="n"&gt;chain&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;pre&lt;/span&gt; &lt;span class="o"&gt;|&lt;/span&gt; &lt;span class="n"&gt;prompt&lt;/span&gt; &lt;span class="o"&gt;|&lt;/span&gt; &lt;span class="n"&gt;ChatOpenAI&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;model&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s2"&gt;&amp;#34;gpt-4o-mini&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;temperature&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;|&lt;/span&gt; &lt;span class="n"&gt;StrOutputParser&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="o"&gt;|&lt;/span&gt; &lt;span class="n"&gt;post&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;16&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="nb"&gt;print&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;chain&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;invoke&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;&lt;span class="s2"&gt;&amp;#34;q&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;&amp;#34;LCEL, Runnable, invoke/batch/stream&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;}))&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;h4 id="3-检索增强生成-rag数据层"&gt;&lt;a href="#3-%e6%a3%80%e7%b4%a2%e5%a2%9e%e5%bc%ba%e7%94%9f%e6%88%90-rag%e6%95%b0%e6%8d%ae%e5%b1%82" class="header-anchor"&gt;&lt;/a&gt;3. 检索增强生成 RAG（数据层）
&lt;/h4&gt;&lt;p&gt;&lt;strong&gt;要解决什么？&lt;/strong&gt; 当模型 “知道的不够”，要从外部资料中取对内容。&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;关键点&lt;/strong&gt;：Loader/TextSplitter → Embeddings → VectorStore → Retriever（可带重排 / 压缩）。&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-python" data-lang="python"&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt; 1&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="err"&gt;⚡&lt;/span&gt; &lt;span class="n"&gt;python片段&lt;/span&gt;&lt;span class="c1"&gt;# pip install -U langchain langchain-openai langchain-community faiss-cpu&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt; 2&lt;/span&gt;&lt;span class="cl"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt; 3&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="kn"&gt;from&lt;/span&gt; &lt;span class="nn"&gt;langchain_openai&lt;/span&gt; &lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;ChatOpenAI&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;OpenAIEmbeddings&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt; 4&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="kn"&gt;from&lt;/span&gt; &lt;span class="nn"&gt;langchain_core.prompts&lt;/span&gt; &lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;ChatPromptTemplate&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt; 5&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="kn"&gt;from&lt;/span&gt; &lt;span class="nn"&gt;langchain_core.output_parsers&lt;/span&gt; &lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;StrOutputParser&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt; 6&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="kn"&gt;from&lt;/span&gt; &lt;span class="nn"&gt;langchain_core.runnables&lt;/span&gt; &lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;RunnablePassthrough&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;RunnableLambda&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt; 7&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="kn"&gt;from&lt;/span&gt; &lt;span class="nn"&gt;langchain_community.vectorstores&lt;/span&gt; &lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;FAISS&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt; 8&lt;/span&gt;&lt;span class="cl"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt; 9&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="c1"&gt;# 1) 准备示例知识（演示使用；实际替换为你的文档）&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;10&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="n"&gt;texts&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;11&lt;/span&gt;&lt;span class="cl"&gt; &lt;span class="s2"&gt;&amp;#34;LCEL 是 LangChain 的可组合执行协议，用 | 串联组件（prompt、llm、parser）。&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;12&lt;/span&gt;&lt;span class="cl"&gt; &lt;span class="s2"&gt;&amp;#34;RAG（检索增强生成）通过向量检索把外部资料接入模型，以降低幻觉并注入最新知识。&amp;#34;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;13&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="p"&gt;]&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;14&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="n"&gt;vs&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;FAISS&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;from_texts&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;texts&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;OpenAIEmbeddings&lt;/span&gt;&lt;span class="p"&gt;())&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;15&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="n"&gt;retriever&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;vs&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;as_retriever&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;k&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="mi"&gt;3&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;16&lt;/span&gt;&lt;span class="cl"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;17&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="c1"&gt;# 2) RAG Prompt（把检索到的资料塞进上下文）&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;18&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="n"&gt;prompt&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;ChatPromptTemplate&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;from_messages&lt;/span&gt;&lt;span class="p"&gt;([&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;19&lt;/span&gt;&lt;span class="cl"&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;&amp;#34;system&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s2"&gt;&amp;#34;你是知识助手，必须基于提供的资料回答。&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;20&lt;/span&gt;&lt;span class="cl"&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;&amp;#34;human&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s2"&gt;&amp;#34;问题：&lt;/span&gt;&lt;span class="si"&gt;{question}&lt;/span&gt;&lt;span class="se"&gt;\n\n&lt;/span&gt;&lt;span class="s2"&gt;资料：&lt;/span&gt;&lt;span class="se"&gt;\n&lt;/span&gt;&lt;span class="si"&gt;{context}&lt;/span&gt;&lt;span class="se"&gt;\n\n&lt;/span&gt;&lt;span class="s2"&gt;请用中文简洁作答，并在句末用[]引用关键词。&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;21&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="p"&gt;])&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;22&lt;/span&gt;&lt;span class="cl"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;23&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="c1"&gt;# 3) 组合：question → retriever → prompt → llm → parser&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;24&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="n"&gt;format_docs&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;RunnableLambda&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;lambda&lt;/span&gt; &lt;span class="n"&gt;docs&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;&amp;#34;&lt;/span&gt;&lt;span class="se"&gt;\n\n&lt;/span&gt;&lt;span class="s2"&gt;&amp;#34;&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;join&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;d&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;page_content&lt;/span&gt; &lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="n"&gt;d&lt;/span&gt; &lt;span class="ow"&gt;in&lt;/span&gt; &lt;span class="n"&gt;docs&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;25&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="n"&gt;chain&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;26&lt;/span&gt;&lt;span class="cl"&gt; &lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="s2"&gt;&amp;#34;context&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;retriever&lt;/span&gt; &lt;span class="o"&gt;|&lt;/span&gt; &lt;span class="n"&gt;format_docs&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s2"&gt;&amp;#34;question&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;RunnablePassthrough&lt;/span&gt;&lt;span class="p"&gt;()}&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;27&lt;/span&gt;&lt;span class="cl"&gt; &lt;span class="o"&gt;|&lt;/span&gt; &lt;span class="n"&gt;prompt&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;28&lt;/span&gt;&lt;span class="cl"&gt; &lt;span class="o"&gt;|&lt;/span&gt; &lt;span class="n"&gt;ChatOpenAI&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;model&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s2"&gt;&amp;#34;gpt-4o-mini&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;temperature&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;29&lt;/span&gt;&lt;span class="cl"&gt; &lt;span class="o"&gt;|&lt;/span&gt; &lt;span class="n"&gt;StrOutputParser&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;30&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;31&lt;/span&gt;&lt;span class="cl"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;32&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="nb"&gt;print&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;chain&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;invoke&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;&amp;#34;什么是 RAG？&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;h4 id="4-智能代理自主层"&gt;&lt;a href="#4-%e6%99%ba%e8%83%bd%e4%bb%a3%e7%90%86%e8%87%aa%e4%b8%bb%e5%b1%82" class="header-anchor"&gt;&lt;/a&gt;4. 智能代理（自主层）
&lt;/h4&gt;&lt;p&gt;&lt;strong&gt;要解决什么？&lt;/strong&gt; 目标不完全明确、步骤不固定，需要选择工具、反复试探（行动 - 观察 - 反思）。&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;关键点&lt;/strong&gt;：BaseTool/工具调用、函数调用、AgentExecutor（或用 LangGraph 做有状态策略）、记忆 / 护栏。&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-python" data-lang="python"&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt; 1&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="err"&gt;⚡&lt;/span&gt; &lt;span class="n"&gt;python片段&lt;/span&gt;&lt;span class="c1"&gt;# pip install -U langchain langchain-openai&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt; 2&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="kn"&gt;from&lt;/span&gt; &lt;span class="nn"&gt;langchain_openai&lt;/span&gt; &lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;ChatOpenAI&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt; 3&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="kn"&gt;from&lt;/span&gt; &lt;span class="nn"&gt;langchain_core.tools&lt;/span&gt; &lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;tool&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt; 4&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="kn"&gt;from&lt;/span&gt; &lt;span class="nn"&gt;langchain_core.messages&lt;/span&gt; &lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;SystemMessage&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;HumanMessage&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;ToolMessage&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt; 5&lt;/span&gt;&lt;span class="cl"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt; 6&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="c1"&gt;# 1) 定义一个可被模型调用的工具（OpenAI Tool Calling）&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt; 7&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="nd"&gt;@tool&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt; 8&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;multiply&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;a&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;int&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;b&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;int&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;-&amp;gt;&lt;/span&gt; &lt;span class="nb"&gt;int&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt; 9&lt;/span&gt;&lt;span class="cl"&gt; &lt;span class="s2"&gt;&amp;#34;精确乘法&amp;#34;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;10&lt;/span&gt;&lt;span class="cl"&gt; &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;a&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt; &lt;span class="n"&gt;b&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;11&lt;/span&gt;&lt;span class="cl"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;12&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="c1"&gt;# 2) 绑定工具，让模型自行决定是否调用&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;13&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="n"&gt;llm&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;ChatOpenAI&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;model&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s2"&gt;&amp;#34;gpt-4o-mini&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;temperature&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;bind_tools&lt;/span&gt;&lt;span class="p"&gt;([&lt;/span&gt;&lt;span class="n"&gt;multiply&lt;/span&gt;&lt;span class="p"&gt;])&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;14&lt;/span&gt;&lt;span class="cl"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;15&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="c1"&gt;# 3) 行动-观察-再思考（最小一次循环）&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;16&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="n"&gt;msgs&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;17&lt;/span&gt;&lt;span class="cl"&gt; &lt;span class="n"&gt;SystemMessage&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;&amp;#34;你是严谨助手，涉及计算必须调用工具，不要心算。&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;18&lt;/span&gt;&lt;span class="cl"&gt; &lt;span class="n"&gt;HumanMessage&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;&amp;#34;先算 12×34，再把结果乘以 2，给出最终数值即可。&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;19&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="p"&gt;]&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;20&lt;/span&gt;&lt;span class="cl"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;21&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="n"&gt;ai&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;llm&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;invoke&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;msgs&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="c1"&gt;# 行动：模型决定要不要调用工具&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;22&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="n"&gt;msgs&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;append&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;ai&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;23&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="n"&gt;tc&lt;/span&gt; &lt;span class="ow"&gt;in&lt;/span&gt; &lt;span class="n"&gt;ai&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;tool_calls&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="c1"&gt;# 观察：执行工具并把结果回传给模型&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;24&lt;/span&gt;&lt;span class="cl"&gt; &lt;span class="n"&gt;out&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;multiply&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;invoke&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;tc&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s2"&gt;&amp;#34;args&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;])&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;25&lt;/span&gt;&lt;span class="cl"&gt; &lt;span class="n"&gt;msgs&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;append&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;ToolMessage&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nb"&gt;str&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;out&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt; &lt;span class="n"&gt;tool_call_id&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="n"&gt;tc&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s2"&gt;&amp;#34;id&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;]))&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;26&lt;/span&gt;&lt;span class="cl"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;27&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="n"&gt;final&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;llm&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;invoke&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;msgs&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="c1"&gt;# 再思考：基于工具结果给最终答案&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;28&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="nb"&gt;print&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;final&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;content&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;h4 id="5-评估与观测质量层"&gt;&lt;a href="#5-%e8%af%84%e4%bc%b0%e4%b8%8e%e8%a7%82%e6%b5%8b%e8%b4%a8%e9%87%8f%e5%b1%82" class="header-anchor"&gt;&lt;/a&gt;5. 评估与观测（质量层）
&lt;/h4&gt;&lt;p&gt;&lt;strong&gt;要解决什么？&lt;/strong&gt; 度量 “是否正确/有用/鲁棒”，以及在真实流量中看得见链路与瓶颈。&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;关键点&lt;/strong&gt;：基准指标（EM/F1/检索命中率）、LLM 判分、回放/对比、LangSmith（或自建追踪）。&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-python" data-lang="python"&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt; 1&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="err"&gt;⚡&lt;/span&gt; &lt;span class="n"&gt;python片段&lt;/span&gt;&lt;span class="c1"&gt;# pip install -U langchain langchain-openai&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt; 2&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="kn"&gt;from&lt;/span&gt; &lt;span class="nn"&gt;langchain_openai&lt;/span&gt; &lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;ChatOpenAI&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt; 3&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="kn"&gt;from&lt;/span&gt; &lt;span class="nn"&gt;langchain_core.prompts&lt;/span&gt; &lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;ChatPromptTemplate&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt; 4&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="kn"&gt;from&lt;/span&gt; &lt;span class="nn"&gt;langchain_core.output_parsers&lt;/span&gt; &lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;StrOutputParser&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt; 5&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="kn"&gt;from&lt;/span&gt; &lt;span class="nn"&gt;langchain.evaluation&lt;/span&gt; &lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;load_evaluator&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt; 6&lt;/span&gt;&lt;span class="cl"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt; 7&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="c1"&gt;# 被评对象：最小 QA 链（LCEL）&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt; 8&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="n"&gt;qa&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;ChatPromptTemplate&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;from_template&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;&amp;#34;用一句话回答：&lt;/span&gt;&lt;span class="si"&gt;{q}&lt;/span&gt;&lt;span class="s2"&gt;&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt; 9&lt;/span&gt;&lt;span class="cl"&gt; &lt;span class="o"&gt;|&lt;/span&gt; &lt;span class="n"&gt;ChatOpenAI&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;model&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s2"&gt;&amp;#34;gpt-4o-mini&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;temperature&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;10&lt;/span&gt;&lt;span class="cl"&gt; &lt;span class="o"&gt;|&lt;/span&gt; &lt;span class="n"&gt;StrOutputParser&lt;/span&gt;&lt;span class="p"&gt;())&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;11&lt;/span&gt;&lt;span class="cl"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;12&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="n"&gt;q&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;&amp;#34;LCEL 是什么？&amp;#34;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;13&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="n"&gt;ref&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;&amp;#34;LCEL 是 LangChain 的统一执行协议，用 | 将组件串联成可组合管道。&amp;#34;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;14&lt;/span&gt;&lt;span class="cl"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;15&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="n"&gt;pred&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;qa&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;invoke&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;&lt;span class="s2"&gt;&amp;#34;q&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;q&lt;/span&gt;&lt;span class="p"&gt;})&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;16&lt;/span&gt;&lt;span class="cl"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;17&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="c1"&gt;# LangChain 自带评估器：按“准确&amp;amp;简洁”两条标准打分+给理由&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;18&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="n"&gt;evaluator&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;load_evaluator&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;19&lt;/span&gt;&lt;span class="cl"&gt; &lt;span class="s2"&gt;&amp;#34;labeled_criteria&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;20&lt;/span&gt;&lt;span class="cl"&gt; &lt;span class="n"&gt;llm&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="n"&gt;ChatOpenAI&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;model&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s2"&gt;&amp;#34;gpt-4o-mini&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;temperature&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;21&lt;/span&gt;&lt;span class="cl"&gt; &lt;span class="n"&gt;criteria&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="s2"&gt;&amp;#34;accuracy&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;&amp;#34;是否与参考一致且不捏造&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s2"&gt;&amp;#34;conciseness&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;&amp;#34;是否一句话且清晰&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;22&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;23&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="n"&gt;grade&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;evaluator&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;evaluate_strings&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nb"&gt;input&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="n"&gt;q&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;prediction&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="n"&gt;pred&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;reference&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="n"&gt;ref&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;24&lt;/span&gt;&lt;span class="cl"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;25&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="nb"&gt;print&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;&amp;#34;答案：&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;pred&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;26&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="nb"&gt;print&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;&amp;#34;评分：&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;grade&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;get&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;&amp;#34;score&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt; &lt;span class="s2"&gt;&amp;#34;理由：&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;grade&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;get&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;&amp;#34;reason&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;总结如下：&lt;/p&gt;
&lt;p&gt;&lt;img alt="图片" loading="lazy" sizes="(max-width: 767px) calc(100vw - 30px), (max-width: 1023px) 700px, (max-width: 1279px) 950px, 1232px" src="https://pub-f29bf2b53160470c9a85250116509a24.r2.dev/post/2025-09-23-langchain-shi-yin-tan-hai-shi-ji-shu-zhai/007-736d8f55.png"&gt;&lt;/p&gt;
&lt;h3 id="架构图"&gt;&lt;a href="#%e6%9e%b6%e6%9e%84%e5%9b%be" class="header-anchor"&gt;&lt;/a&gt;架构图
&lt;/h3&gt;&lt;p&gt;LangChain 是一个以组合性为核心哲学的大语言模型应用开发框架&lt;/p&gt;
&lt;p&gt;&lt;img alt="Image" loading="lazy" sizes="(max-width: 767px) calc(100vw - 30px), (max-width: 1023px) 700px, (max-width: 1279px) 950px, 1232px" src="https://pub-f29bf2b53160470c9a85250116509a24.r2.dev/post/2025-09-23-langchain-shi-yin-tan-hai-shi-ji-shu-zhai/008-1b4ddddf.jpg"&gt;&lt;/p&gt;
&lt;p&gt;其设计理念是 “通过组合性构建LLM应用”，具体来说：&lt;/p&gt;
&lt;p&gt;1.可组合性 (Composability)：所有组件都是 Runnable，可以像乐高积木一样组合；使用 LCEL（LangChain Expression Language）轻松构建复杂流程&lt;/p&gt;
&lt;p&gt;2.标准化接口 (Standardization)：统一的输入输出接口；一致的同步/异步/批处理/流式处理方法&lt;/p&gt;
&lt;p&gt;3.可扩展性 (Extensibility)：通过继承基类轻松添加新实现；插件化架构，易于集成第三方服务&lt;/p&gt;
&lt;p&gt;4.类型安全 (Type Safety)：使用泛型和类型提示；编译时类型检查，减少运行时错误&lt;/p&gt;
&lt;h3 id="架构层次"&gt;&lt;a href="#%e6%9e%b6%e6%9e%84%e5%b1%82%e6%ac%a1" class="header-anchor"&gt;&lt;/a&gt;架构层次
&lt;/h3&gt;&lt;p&gt;LangChain 采用严格的分层架构，从底层的核心抽象到上层的应用组件，确保了良好的模块化和可扩展性。&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-fallback" data-lang="fallback"&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt; 1&lt;/span&gt;&lt;span class="cl"&gt;⚡ text片段LangChain
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt; 2&lt;/span&gt;&lt;span class="cl"&gt;├── langchain-core/ # 核心抽象层
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt; 3&lt;/span&gt;&lt;span class="cl"&gt;│ ├── language_models/ # 基础模型抽象
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt; 4&lt;/span&gt;&lt;span class="cl"&gt;│ ├── runnables/ # LCEL 核心
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt; 5&lt;/span&gt;&lt;span class="cl"&gt;│ ├── prompts/ # 提示抽象
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt; 6&lt;/span&gt;&lt;span class="cl"&gt;│ └── ...
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt; 7&lt;/span&gt;&lt;span class="cl"&gt;│
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt; 8&lt;/span&gt;&lt;span class="cl"&gt;├── langchain/ # 主实现层
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt; 9&lt;/span&gt;&lt;span class="cl"&gt;│ ├── llms/ # LLM 实现
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;10&lt;/span&gt;&lt;span class="cl"&gt;│ ├── chat_models/ # Chat 实现
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;11&lt;/span&gt;&lt;span class="cl"&gt;│ ├── chains/ # 链实现
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;12&lt;/span&gt;&lt;span class="cl"&gt;│ └── ...
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;13&lt;/span&gt;&lt;span class="cl"&gt;│
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;14&lt;/span&gt;&lt;span class="cl"&gt;└── langchain-community/ # 社区集成
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;15&lt;/span&gt;&lt;span class="cl"&gt; └── partners/ # 第三方集成
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;16&lt;/span&gt;&lt;span class="cl"&gt; ├── openai/
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;17&lt;/span&gt;&lt;span class="cl"&gt; ├── anthropic/
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;18&lt;/span&gt;&lt;span class="cl"&gt; └── ...
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;h3 id="模块结构"&gt;&lt;a href="#%e6%a8%a1%e5%9d%97%e7%bb%93%e6%9e%84" class="header-anchor"&gt;&lt;/a&gt;模块结构
&lt;/h3&gt;&lt;p&gt;&lt;img alt="图片" loading="lazy" sizes="(max-width: 767px) calc(100vw - 30px), (max-width: 1023px) 700px, (max-width: 1279px) 950px, 1232px" src="https://pub-f29bf2b53160470c9a85250116509a24.r2.dev/post/2025-09-23-langchain-shi-yin-tan-hai-shi-ji-shu-zhai/009-2bd9f481.png"&gt;&lt;/p&gt;
&lt;p&gt;LangChain 主要包含以下模块：&lt;/p&gt;
&lt;p&gt;1.核心语言模型模块&lt;/p&gt;
&lt;p&gt;○llms/ - 传统 LLM（85+ 个实现）&lt;/p&gt;
&lt;p&gt;○chat_models/ - 对话模型（35+ 个实现）&lt;/p&gt;
&lt;p&gt;○embeddings/ - 嵌入模型（51+ 个实现）&lt;/p&gt;
&lt;p&gt;2.输入输出模块&lt;/p&gt;
&lt;p&gt;○prompts/ - 提示模板&lt;/p&gt;
&lt;p&gt;○output_parsers/ - 输出解析器（23+ 种）&lt;/p&gt;
&lt;p&gt;○prompt_values/ - 提示值处理&lt;/p&gt;
&lt;p&gt;3.数据处理模块&lt;/p&gt;
&lt;p&gt;○document_loaders/ - 文档加载器（166+ 种）&lt;/p&gt;
&lt;p&gt;○document_transformers/ - 文档转换器&lt;/p&gt;
&lt;p&gt;○text_splitter.py - 文本分割&lt;/p&gt;
&lt;p&gt;○indexes/ - 索引管理&lt;/p&gt;
&lt;p&gt;4.存储与检索模块&lt;/p&gt;
&lt;p&gt;○vectorstores/ - 向量数据库（76+ 种）&lt;/p&gt;
&lt;p&gt;○retrievers/ - 检索器（78+ 种）&lt;/p&gt;
&lt;p&gt;○memory/ - 记忆管理（39+ 种）&lt;/p&gt;
&lt;p&gt;○storage/ - 存储抽象&lt;/p&gt;
&lt;p&gt;○docstore/ - 文档存储&lt;/p&gt;
&lt;p&gt;5.链与编排模块&lt;/p&gt;
&lt;p&gt;○chains/ - 各种链（144+ 个文件）&lt;/p&gt;
&lt;p&gt;○runnables/ - 可运行组件&lt;/p&gt;
&lt;p&gt;○agents/ - 智能体（146+ 个文件）&lt;/p&gt;
&lt;p&gt;6.工具与集成模块&lt;/p&gt;
&lt;p&gt;○tools/ - 工具集（186+ 种）&lt;/p&gt;
&lt;p&gt;○agent_toolkits/ - 工具包&lt;/p&gt;
&lt;p&gt;○utilities/ - 实用工具（59+ 个）&lt;/p&gt;
&lt;p&gt;○utils/ - 辅助函数&lt;/p&gt;
&lt;p&gt;7.回调与监控模块&lt;/p&gt;
&lt;p&gt;○callbacks/ - 回调处理器（46+ 种）&lt;/p&gt;
&lt;p&gt;○tracers/ - 追踪器&lt;/p&gt;
&lt;p&gt;○evaluation/ - 评估工具（32+ 个）&lt;/p&gt;
&lt;p&gt;8.特殊功能模块&lt;/p&gt;
&lt;p&gt;○chat_loaders/ - 聊天记录加载&lt;/p&gt;
&lt;p&gt;○graphs/ - 图处理&lt;/p&gt;
&lt;p&gt;○sql_database.py - SQL 数据库支持&lt;/p&gt;
&lt;p&gt;○cache.py - 缓存管理&lt;/p&gt;
&lt;h3 id="runnable-抽象"&gt;&lt;a href="#runnable-%e6%8a%bd%e8%b1%a1" class="header-anchor"&gt;&lt;/a&gt;Runnable 抽象
&lt;/h3&gt;&lt;p&gt;LangChain 的架构精髓在于 Runnable 接口 —— 一个&amp;quot;可以被调用、批处理、流化、转换和组合的工作单元&amp;quot; 。这个抽象提供了六种核心执行模式：&lt;/p&gt;
&lt;p&gt;●invoke/ainvoke: 单次同步/异步执行&lt;/p&gt;
&lt;p&gt;●batch/abatch: 并行同步/异步批处理执行&lt;/p&gt;
&lt;p&gt;●stream/astream: 同步/异步流式输出执行&lt;/p&gt;
&lt;p&gt;&lt;img alt="图片" loading="lazy" sizes="(max-width: 767px) calc(100vw - 30px), (max-width: 1023px) 700px, (max-width: 1279px) 950px, 1232px" src="https://pub-f29bf2b53160470c9a85250116509a24.r2.dev/post/2025-09-23-langchain-shi-yin-tan-hai-shi-ji-shu-zhai/010-de897147.png"&gt;&lt;/p&gt;
&lt;p&gt;所有组件都实现 Runnable 接口，从检索器到代理系统 ，确保了组件间的无缝互操作性，使得 LangChain 组件具有极高的可组合性。&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-fallback" data-lang="fallback"&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;1&lt;/span&gt;&lt;span class="cl"&gt;⚡ 代码片段# 示例：链式组合
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;2&lt;/span&gt;&lt;span class="cl"&gt;chain = prompt | model | parser
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;h3 id="lcel声明式表达语言"&gt;&lt;a href="#lcel%e5%a3%b0%e6%98%8e%e5%bc%8f%e8%a1%a8%e8%be%be%e8%af%ad%e8%a8%80" class="header-anchor"&gt;&lt;/a&gt;LCEL：声明式表达语言
&lt;/h3&gt;&lt;p&gt;LangChain Expression Language (LCEL) 是框架的&amp;quot;声明式方法构建生产级程序&amp;quot; 。通过管道操作符（|）实现组件的优雅组合，天然支持异步、批处理和流式操作，这使得基于LCEL的程序能够更好地扩展以处理更高的并发负载。&lt;/p&gt;
&lt;p&gt;LCEL 不仅仅是语法糖。它是一种声明式的编程范式。开发者只需声明 “数据如何流动”，而框架负责处理底层的执行、流式传输、并行化和日志记录。&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-python" data-lang="python"&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt; 1&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="err"&gt;⚡&lt;/span&gt; &lt;span class="n"&gt;python片段&lt;/span&gt;&lt;span class="c1"&gt;# pip install -U langchain langchain-openai&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt; 2&lt;/span&gt;&lt;span class="cl"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt; 3&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="kn"&gt;from&lt;/span&gt; &lt;span class="nn"&gt;langchain_core.prompts&lt;/span&gt; &lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;ChatPromptTemplate&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt; 4&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="kn"&gt;from&lt;/span&gt; &lt;span class="nn"&gt;langchain_openai&lt;/span&gt; &lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;ChatOpenAI&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt; 5&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="kn"&gt;from&lt;/span&gt; &lt;span class="nn"&gt;langchain_core.output_parsers&lt;/span&gt; &lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;StrOutputParser&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt; 6&lt;/span&gt;&lt;span class="cl"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt; 7&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="c1"&gt;# 1) Prompt：定义输入模板&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt; 8&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="n"&gt;prompt&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;ChatPromptTemplate&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;from_messages&lt;/span&gt;&lt;span class="p"&gt;([&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt; 9&lt;/span&gt;&lt;span class="cl"&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;&amp;#34;system&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s2"&gt;&amp;#34;你是精炼、可靠的中文助手。&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;10&lt;/span&gt;&lt;span class="cl"&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;&amp;#34;human&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s2"&gt;&amp;#34;用一句话回答：&lt;/span&gt;&lt;span class="si"&gt;{question}&lt;/span&gt;&lt;span class="s2"&gt;&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;11&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="p"&gt;])&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;12&lt;/span&gt;&lt;span class="cl"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;13&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="c1"&gt;# 2) LLM：任意 OpenAI 兼容服务均可（公有云/企业网关/vLLM 等）&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;14&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="n"&gt;llm&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;ChatOpenAI&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;15&lt;/span&gt;&lt;span class="cl"&gt; &lt;span class="n"&gt;model&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s2"&gt;&amp;#34;gpt-4o-mini&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="c1"&gt;# 换成你的模型名即可&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;16&lt;/span&gt;&lt;span class="cl"&gt; &lt;span class="n"&gt;api_key&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s2"&gt;&amp;#34;YOUR_KEY&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="c1"&gt;# 也可用环境变量 OPENAI_API_KEY&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;17&lt;/span&gt;&lt;span class="cl"&gt; &lt;span class="n"&gt;base_url&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s2"&gt;&amp;#34;http://localhost:8000/v1&amp;#34;&lt;/span&gt; &lt;span class="c1"&gt;# 选填：本地或内网的 OpenAI 兼容地址&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;18&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;19&lt;/span&gt;&lt;span class="cl"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;20&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="c1"&gt;# 3) Parser：把模型返回的消息对象转成纯字符串&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;21&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="n"&gt;parser&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;StrOutputParser&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;22&lt;/span&gt;&lt;span class="cl"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;23&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="c1"&gt;# LCEL：像搭乐高一样用“|”把组件串起来&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;24&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="n"&gt;chain&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;prompt&lt;/span&gt; &lt;span class="o"&gt;|&lt;/span&gt; &lt;span class="n"&gt;llm&lt;/span&gt; &lt;span class="o"&gt;|&lt;/span&gt; &lt;span class="n"&gt;parser&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;25&lt;/span&gt;&lt;span class="cl"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;26&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="c1"&gt;# ——最简单的单次调用（演示用）——&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;27&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="nb"&gt;print&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;chain&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;invoke&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;&lt;span class="s2"&gt;&amp;#34;question&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;&amp;#34;LCEL 是什么？&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;}))&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;28&lt;/span&gt;&lt;span class="cl"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;29&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="c1"&gt;# ——可选：流式演示（边生成边打印）——&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;30&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="n"&gt;chunk&lt;/span&gt; &lt;span class="ow"&gt;in&lt;/span&gt; &lt;span class="n"&gt;chain&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;stream&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;&lt;span class="s2"&gt;&amp;#34;question&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;&amp;#34;给出一条使用 LCEL 的建议&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;}):&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;31&lt;/span&gt;&lt;span class="cl"&gt; &lt;span class="nb"&gt;print&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;chunk&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;end&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s2"&gt;&amp;#34;&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;32&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="nb"&gt;print&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;33&lt;/span&gt;&lt;span class="cl"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;34&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="c1"&gt;# ——可选：批量演示（一次处理多条）——&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;35&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="nb"&gt;print&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;chain&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;batch&lt;/span&gt;&lt;span class="p"&gt;([&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;36&lt;/span&gt;&lt;span class="cl"&gt; &lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="s2"&gt;&amp;#34;question&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;&amp;#34;一句话解释 LangChain&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;},&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;37&lt;/span&gt;&lt;span class="cl"&gt; &lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="s2"&gt;&amp;#34;question&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;&amp;#34;一句话解释 LCEL 的优势&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;38&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="p"&gt;]))&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;h3 id="schema-与类型系统"&gt;&lt;a href="#schema-%e4%b8%8e%e7%b1%bb%e5%9e%8b%e7%b3%bb%e7%bb%9f" class="header-anchor"&gt;&lt;/a&gt;Schema 与类型系统
&lt;/h3&gt;&lt;p&gt;LangChain 的类型系统建立在 Python 的类型提示和 Pydantic模型之上，提供了一套完整的类型定义来支持 LLM 应用开发。LangChain 的类型系统具有以下特点：&lt;/p&gt;
&lt;p&gt;1.强类型: 基于 Python 类型提示和 Pydantic，提供编译时和运行时类型检查&lt;/p&gt;
&lt;p&gt;2.可组合: 通过 Runnable 接口实现组件的灵活组合&lt;/p&gt;
&lt;p&gt;3.可序列化: 所有核心类型都继承自 Serializable&lt;/p&gt;
&lt;p&gt;4.灵活性: 支持多种 Schema 定义方式（Pydantic、TypedDict、JSON Schema）&lt;/p&gt;
&lt;p&gt;5.流式支持: 原生支持同步、异步、批处理和流式处理&lt;/p&gt;
&lt;p&gt;6.标准化: 统一的输入输出类型定义，便于组件互操作&lt;/p&gt;

 &lt;blockquote&gt;
 &lt;p&gt;Pydantic 由 Samuel Colvin 创建，核心思想是：“使用类型注解定义数据模型，Pydantic 自动帮你验证和转换数据。” 它基于 Python 3.6+ 的类型提示系统（如 str, int, List, Optional 等），通过定义继承自 BaseModel 的类，来描述期望的数据结构。&lt;/p&gt;

 &lt;/blockquote&gt;
&lt;p&gt;想象一下，你正在指挥一个非常聪明但有点 “随心所欲” 的机器人。如果你只是模糊地说 “给我找点关于猫的资料”，它可能会给你一篇科学论文，一张猫的图片，或者一段猫叫的音频。这太不可预测了。LangChain 的 Schema 和类型系统，就像是给这个机器人的一套精确的 “指令图纸” 和 “数据表格”。它让你能够用一种机器人能精确理解的方式下达指令，并要求它以你想要的、规整的格式返回结果。下面我们通过几个场景和代码例子，来看看这些 “图纸” 和 “表格” 是怎么工作的。&lt;/p&gt;
&lt;h4 id="场景-1-从简单的闲聊到有角色区分的对话"&gt;&lt;a href="#%e5%9c%ba%e6%99%af-1-%e4%bb%8e%e7%ae%80%e5%8d%95%e7%9a%84%e9%97%b2%e8%81%8a%e5%88%b0%e6%9c%89%e8%a7%92%e8%89%b2%e5%8c%ba%e5%88%86%e7%9a%84%e5%af%b9%e8%af%9d" class="header-anchor"&gt;&lt;/a&gt;场景 1: 从简单的闲聊到有角色区分的对话
&lt;/h4&gt;&lt;p&gt;一开始，我们和 AI 的交互很简单，就是 “一问一答”。&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;最基础的类型: Text (字符串): 这就是最原始的交互方式。&lt;/strong&gt;&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-fallback" data-lang="fallback"&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;1&lt;/span&gt;&lt;span class="cl"&gt;⚡ 代码片段# 这其实就是最基础的文本 Schema
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;2&lt;/span&gt;&lt;span class="cl"&gt;my_question = &amp;#34;你好，你叫什么名字？&amp;#34;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;3&lt;/span&gt;&lt;span class="cl"&gt;ai_response = &amp;#34;我是AI助手。&amp;#34;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;但这很快就不够用了。在一个持续的对话中，AI 需要知道哪句话是谁说的，才能更好地理解上下文。&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;进阶类型: ChatMessage&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;ChatMessage 就是为了解决这个问题而生的 “对话表格”。它规定了每条消息都应该有 role (角色) 和 content (内容) 两列。主要角色有：&lt;/p&gt;
&lt;p&gt;○SystemMessage: 系统指令。给 AI 设定一个 “人设” 或总体的行为准则。&lt;/p&gt;
&lt;p&gt;○HumanMessage: 你的话。&lt;/p&gt;
&lt;p&gt;○AIMessage: AI 的话。&lt;/p&gt;
&lt;p&gt;每个元素都有明确的role 和content。 这让 AI 不再混乱，能够更好地进行多轮对话。&lt;/p&gt;
&lt;h4 id="场景-2-我需要-ai-给我一个结构化的数据而不是一段话"&gt;&lt;a href="#%e5%9c%ba%e6%99%af-2-%e6%88%91%e9%9c%80%e8%a6%81-ai-%e7%bb%99%e6%88%91%e4%b8%80%e4%b8%aa%e7%bb%93%e6%9e%84%e5%8c%96%e7%9a%84%e6%95%b0%e6%8d%ae%e8%80%8c%e4%b8%8d%e6%98%af%e4%b8%80%e6%ae%b5%e8%af%9d" class="header-anchor"&gt;&lt;/a&gt;场景 2: 我需要 AI 给我一个结构化的数据，而不是一段话
&lt;/h4&gt;&lt;p&gt;假设你想让 AI 帮你生成用户信息，并存入数据库。如果你只对它说 “生成一个用户，名叫张三，25 岁，邮箱是 &lt;a class="link" href="mailto:zhangsan@email.com" &gt;zhangsan@email.com&lt;/a&gt;”，它可能会返回：&lt;/p&gt;
&lt;p&gt;●&amp;quot; 好的，用户信息如下：姓名：张三，年龄：25，邮箱：zhangsan@email.com&amp;quot;&lt;/p&gt;
&lt;p&gt;●&amp;quot; 张三，25 岁，邮箱 &lt;a class="link" href="mailto:zhangsan@email.com" &gt;zhangsan@email.com&lt;/a&gt;&amp;quot;&lt;/p&gt;
&lt;p&gt;●&amp;quot; 这是一个名叫张三的用户，他今年 25 岁了，你可以通过 &lt;a class="link" href="mailto:zhangsan@email.com" &gt;zhangsan@email.com&lt;/a&gt; 联系到他。&amp;quot;&lt;/p&gt;
&lt;p&gt;这些都是字符串，程序很难处理！我们需要的是一个干净的、可以直接用的 JSON 对象。这时，我们就要给 AI 一张 “图纸”，告诉它我们想要的输出格式。在 LangChain 中，最常用的 “绘图工具” 就是Pydantic 库。&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-python" data-lang="python"&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt; 1&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="err"&gt;⚡&lt;/span&gt; &lt;span class="n"&gt;python片段&lt;/span&gt;&lt;span class="c1"&gt;# 伪代码，演示核心逻辑&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt; 2&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="kn"&gt;from&lt;/span&gt; &lt;span class="nn"&gt;langchain_core.pydantic_v1&lt;/span&gt; &lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;BaseModel&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;Field&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt; 3&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="kn"&gt;from&lt;/span&gt; &lt;span class="nn"&gt;langchain_core.output_parsers&lt;/span&gt; &lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;JsonOutputParser&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt; 4&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="kn"&gt;from&lt;/span&gt; &lt;span class="nn"&gt;langchain_openai&lt;/span&gt; &lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;ChatOpenAI&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt; 5&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="kn"&gt;from&lt;/span&gt; &lt;span class="nn"&gt;langchain_core.prompts&lt;/span&gt; &lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;PromptTemplate&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt; 6&lt;/span&gt;&lt;span class="cl"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt; 7&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="c1"&gt;# 1. 用 Pydantic 画一张“图纸”，定义你想要的输出结构&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt; 8&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="n"&gt;classUserProfile&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;BaseModel&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt; 9&lt;/span&gt;&lt;span class="cl"&gt; &lt;span class="n"&gt;name&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;str&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;Field&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;description&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s2"&gt;&amp;#34;用户的全名&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;10&lt;/span&gt;&lt;span class="cl"&gt; &lt;span class="n"&gt;age&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;int&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;Field&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;description&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s2"&gt;&amp;#34;用户的年龄&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;11&lt;/span&gt;&lt;span class="cl"&gt; &lt;span class="n"&gt;email&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;str&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;Field&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;description&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s2"&gt;&amp;#34;用户的电子邮件地址&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;12&lt;/span&gt;&lt;span class="cl"&gt; &lt;span class="n"&gt;is_active&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;bool&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;Field&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;description&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s2"&gt;&amp;#34;用户账户是否活跃&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;13&lt;/span&gt;&lt;span class="cl"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;14&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="c1"&gt;# 2. 创建一个输出解析器，告诉它要用哪张“图纸”&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;15&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="n"&gt;parser&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;JsonOutputParser&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;pydantic_object&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="n"&gt;UserProfile&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;16&lt;/span&gt;&lt;span class="cl"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;17&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="c1"&gt;# 3. 在提示中，告诉AI要按照“图纸”的格式来回答&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;18&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="n"&gt;prompt&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;PromptTemplate&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;19&lt;/span&gt;&lt;span class="cl"&gt; &lt;span class="n"&gt;template&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s2"&gt;&amp;#34;&amp;#34;&amp;#34;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;20&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="s2"&gt; 根据下面的用户信息，生成一个JSON对象。
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;21&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="s2"&gt; 用户信息：&lt;/span&gt;&lt;span class="si"&gt;{user_info}&lt;/span&gt;&lt;span class="s2"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;22&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="s2"&gt; &lt;/span&gt;&lt;span class="si"&gt;{format_instructions}&lt;/span&gt;&lt;span class="s2"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;23&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="s2"&gt; &amp;#34;&amp;#34;&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;24&lt;/span&gt;&lt;span class="cl"&gt; &lt;span class="n"&gt;input_variables&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s2"&gt;&amp;#34;user_info&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;25&lt;/span&gt;&lt;span class="cl"&gt; &lt;span class="c1"&gt;# 把“图纸”的说明书（格式指令）插入到提示中&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;26&lt;/span&gt;&lt;span class="cl"&gt; &lt;span class="n"&gt;partial_variables&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="s2"&gt;&amp;#34;format_instructions&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;parser&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;get_format_instructions&lt;/span&gt;&lt;span class="p"&gt;()}&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;27&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;28&lt;/span&gt;&lt;span class="cl"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;29&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="c1"&gt;# 4. 创建模型并链接所有部分&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;30&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="c1"&gt;# model = ChatOpenAI(temperature=0)&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;31&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="c1"&gt;# chain = prompt | model | parser&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;32&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="c1"&gt;#&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;33&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="c1"&gt;# response = chain.invoke({&amp;#34;user_info&amp;#34;: &amp;#34;创建一个用户，名叫李四，30岁，邮箱是 lisi@email.com，账户是活跃的。&amp;#34;})&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;34&lt;/span&gt;&lt;span class="cl"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;35&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="c1"&gt;# 期望的 response 会是一个干净的 Python 字典，而不是字符串&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;36&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="c1"&gt;# print(response)&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;37&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="c1"&gt;#&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;38&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="c1"&gt;# 输出:&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;39&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="c1"&gt;# {&amp;#39;name&amp;#39;: &amp;#39;李四&amp;#39;, &amp;#39;age&amp;#39;: 30, &amp;#39;email&amp;#39;: &amp;#39;lisi@email.com&amp;#39;, &amp;#39;is_active&amp;#39;: True}&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;看到妙处了吗？通过定义UserProfile 这个 Schema，我们强制 AI 的输出符合我们预设的结构，让它的输出变得 100% 可预测和可用。&lt;/p&gt;
&lt;h4 id="场景-3-让-ai-使用我们定义的工具"&gt;&lt;a href="#%e5%9c%ba%e6%99%af-3-%e8%ae%a9-ai-%e4%bd%bf%e7%94%a8%e6%88%91%e4%bb%ac%e5%ae%9a%e4%b9%89%e7%9a%84%e5%b7%a5%e5%85%b7" class="header-anchor"&gt;&lt;/a&gt;场景 3: 让 AI 使用我们定义的工具
&lt;/h4&gt;&lt;p&gt;假设你想让 AI 能够查询天气。AI 本身是不知道今天天气的，但你可以提供一个查询天气的函数（工具）给它。但是，AI 怎么知道这个函数是干嘛的？需要哪些参数？&lt;/p&gt;
&lt;p&gt;LangChain 的 @tool 装饰器可以自动读取你函数的 “类型提示”(Type Hinting) 和文档字符串 (docstring)，并把它们变成一份 AI 能看懂的 “工具说明书”。&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-gdscript3" data-lang="gdscript3"&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt; 1&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="err"&gt;⚡&lt;/span&gt; &lt;span class="n"&gt;python片段&lt;/span&gt;&lt;span class="c1"&gt;# 伪代码，演示核心逻辑&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt; 2&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="n"&gt;from&lt;/span&gt; &lt;span class="n"&gt;langchain_core&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;tools&lt;/span&gt; &lt;span class="n"&gt;import&lt;/span&gt; &lt;span class="k"&gt;tool&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt; 3&lt;/span&gt;&lt;span class="cl"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt; 4&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="err"&gt;@&lt;/span&gt;&lt;span class="k"&gt;tool&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt; 5&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;search_weather&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;city&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;str&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;unit&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;str&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;&amp;#34;celsius&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;-&amp;gt;&lt;/span&gt; &lt;span class="nb"&gt;str&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt; 6&lt;/span&gt;&lt;span class="cl"&gt; &lt;span class="s2"&gt;&amp;#34;&amp;#34;&amp;#34;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt; 7&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="s2"&gt; 根据城市名称查询实时天气。
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt; 8&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="s2"&gt; :param city: 城市的名字，例如：北京
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt; 9&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="s2"&gt; :param unit: 温度单位，可以是 &amp;#39;celsius&amp;#39; (摄氏度) 或 &amp;#39;fahrenheit&amp;#39; (华氏度)
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;10&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="s2"&gt; &amp;#34;&amp;#34;&amp;#34;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;11&lt;/span&gt;&lt;span class="cl"&gt; &lt;span class="c1"&gt;# 这里的代码会真实地去调用天气API&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;12&lt;/span&gt;&lt;span class="cl"&gt; &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;city&lt;/span&gt; &lt;span class="o"&gt;==&lt;/span&gt; &lt;span class="s2"&gt;&amp;#34;北京&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;13&lt;/span&gt;&lt;span class="cl"&gt; &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;f&lt;/span&gt;&lt;span class="s2"&gt;&amp;#34;北京现在的天气是 25°{unit}&amp;#34;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;14&lt;/span&gt;&lt;span class="cl"&gt; &lt;span class="k"&gt;elif&lt;/span&gt; &lt;span class="n"&gt;city&lt;/span&gt; &lt;span class="o"&gt;==&lt;/span&gt; &lt;span class="s2"&gt;&amp;#34;上海&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;15&lt;/span&gt;&lt;span class="cl"&gt; &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;f&lt;/span&gt;&lt;span class="s2"&gt;&amp;#34;上海现在的天气是 28°{unit}&amp;#34;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;16&lt;/span&gt;&lt;span class="cl"&gt; &lt;span class="k"&gt;else&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;17&lt;/span&gt;&lt;span class="cl"&gt; &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;f&lt;/span&gt;&lt;span class="s2"&gt;&amp;#34;抱歉，我查询不到 {city} 的天气。&amp;#34;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;18&lt;/span&gt;&lt;span class="cl"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;19&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="c1"&gt;# 当你把这个工具提供给一个支持工具调用的AI模型时，&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;20&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="c1"&gt;# LangChain会自动生成类似这样的“说明书”给AI看：&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;21&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="c1"&gt;# Tool Name: search_weather&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;22&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="c1"&gt;# Tool Description: 根据城市名称查询实时天气。&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;23&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="c1"&gt;# Tool Arguments:&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;24&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="c1"&gt;# - name: city, type: string, description: 城市的名字，例如：北京&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;25&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="c1"&gt;# - name: unit, type: string, description: 温度单位，可以是 &amp;#39;celsius&amp;#39; (摄氏度) 或 &amp;#39;fahrenheit&amp;#39; (华氏度)&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;26&lt;/span&gt;&lt;span class="cl"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;27&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="c1"&gt;# 当你问AI：“北京今天天气怎么样？”&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;28&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="c1"&gt;# AI会分析你的问题，发现需要查询天气，然后查看它手上的“工具说明书”。&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;29&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="c1"&gt;# 它会找到 search_weather 工具，并自动生成调用参数：{&amp;#34;city&amp;#34;: &amp;#34;北京&amp;#34;, &amp;#34;unit&amp;#34;: &amp;#34;celsius&amp;#34;}&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;30&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="c1"&gt;# 然后执行函数，得到结果，最后把结果用自然语言告诉你。&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;这里的 city: str 和 unit: str 就是 Schema 的一部分，它明确规定了工具需要什么类型的输入。文档字符串 &amp;ldquo;&amp;rdquo;&amp;quot;&amp;hellip;&amp;quot;&amp;quot;&amp;quot; 则成了 AI 理解工具功能的关键。&lt;/p&gt;
&lt;h3 id="核心抽象组件"&gt;&lt;a href="#%e6%a0%b8%e5%bf%83%e6%8a%bd%e8%b1%a1%e7%bb%84%e4%bb%b6" class="header-anchor"&gt;&lt;/a&gt;核心抽象组件
&lt;/h3&gt;&lt;p&gt;LangChain 的架构围绕以下几个基本抽象组件构建，这些抽象组件共同构成了 LangChain 的核心架构，让开发者能够快速构建复杂的 LLM 应用。每个组件都有明确的职责，通过 Runnable 接口相互连接，形成了一个强大而灵活的框架。&lt;/p&gt;
&lt;h4 id="language-models-语言模型"&gt;&lt;a href="#language-models-%e8%af%ad%e8%a8%80%e6%a8%a1%e5%9e%8b" class="header-anchor"&gt;&lt;/a&gt;Language Models (语言模型)
&lt;/h4&gt;&lt;p&gt;提供文本生成、对话、推理等核心 AI 能力&lt;/p&gt;
&lt;p&gt;●BaseLanguageModel: 所有语言模型的基类,它继承自 RunnableSerializable，并定义了语言模型交互的通用接口 。它通过其 generate_prompt() 方法接受 PromptValue 对象 。&lt;/p&gt;
&lt;p&gt;●BaseChatModel: 对话模型（GPT-4、Claude）- 处理消息序列&lt;/p&gt;
&lt;p&gt;●BaseLLM: 文本生成模型 - 处理字符串输入输出&lt;/p&gt;
&lt;h4 id="prompts-提示模板"&gt;&lt;a href="#prompts-%e6%8f%90%e7%a4%ba%e6%a8%a1%e6%9d%bf" class="header-anchor"&gt;&lt;/a&gt;Prompts (提示模板)
&lt;/h4&gt;&lt;p&gt;输入构造层，支持变量替换、少样本示例、消息格式化&lt;/p&gt;
&lt;p&gt;●BasePromptTemplate: 动态构造模型输入&lt;/p&gt;
&lt;p&gt;●ChatPromptTemplate: 构造对话消息序列&lt;/p&gt;
&lt;p&gt;●PromptTemplate: 构造文本提示&lt;/p&gt;
&lt;h4 id="messages-消息"&gt;&lt;a href="#messages-%e6%b6%88%e6%81%af" class="header-anchor"&gt;&lt;/a&gt;Messages (消息)
&lt;/h4&gt;&lt;p&gt;对话交互的基本单元&lt;/p&gt;
&lt;p&gt;●BaseMessage: 所有消息的基类&lt;/p&gt;
&lt;p&gt;●HumanMessage: 用户消息&lt;/p&gt;
&lt;p&gt;●AIMessage: AI 回复&lt;/p&gt;
&lt;p&gt;●SystemMessage: 系统指令&lt;/p&gt;
&lt;p&gt;●ToolMessage: 工具调用结果&lt;/p&gt;
&lt;h4 id="documents-文档"&gt;&lt;a href="#documents-%e6%96%87%e6%a1%a3" class="header-anchor"&gt;&lt;/a&gt;Documents (文档)
&lt;/h4&gt;&lt;p&gt;知识存储的基本单元，是 RAG（检索增强生成）的基础数据结构&lt;/p&gt;
&lt;p&gt;●Document: 包含 page_content（内容）和 metadata（元数据）&lt;/p&gt;
&lt;p&gt;●用于表示任何文本数据：网页、PDF、数据库记录等&lt;/p&gt;
&lt;h4 id="retrievers-检索器"&gt;&lt;a href="#retrievers-%e6%a3%80%e7%b4%a2%e5%99%a8" class="header-anchor"&gt;&lt;/a&gt;Retrievers (检索器)
&lt;/h4&gt;&lt;p&gt;知识检索层，RAG 架构的核心组件&lt;/p&gt;
&lt;p&gt;●BaseRetriever: 是文档检索系统的抽象基类，它实现了 Runnable 接口以实现可组合性。它定义了基于查询检索相关文档的标准接口。&lt;/p&gt;
&lt;p&gt;●连接向量数据库、搜索引擎、数据库等&lt;/p&gt;
&lt;h4 id="vector-stores-向量存储"&gt;&lt;a href="#vector-stores-%e5%90%91%e9%87%8f%e5%ad%98%e5%82%a8" class="header-anchor"&gt;&lt;/a&gt;Vector Stores (向量存储)
&lt;/h4&gt;&lt;p&gt;语义搜索的基础设施&lt;/p&gt;
&lt;p&gt;●VectorStore: 向量数据库的抽象接口&lt;/p&gt;
&lt;p&gt;●存储和检索文档的向量表示&lt;/p&gt;
&lt;p&gt;●支持相似度搜索、混合搜索等&lt;/p&gt;
&lt;h4 id="embeddings-嵌入"&gt;&lt;a href="#embeddings-%e5%b5%8c%e5%85%a5" class="header-anchor"&gt;&lt;/a&gt;Embeddings (嵌入)
&lt;/h4&gt;&lt;p&gt;文本向量化，提供了嵌入模型的抽象接口，定义了将文本转换为向量表示的方法。它要求实现 embed&lt;em&gt;documents() 和 embed&lt;/em&gt;query() 方法。是语义搜索和相似度计算的基础。&lt;/p&gt;
&lt;p&gt;●Embeddings: 将文本转换为向量表示&lt;/p&gt;
&lt;p&gt;●支持各种嵌入模型（OpenAI、Hugging Face 等）&lt;/p&gt;
&lt;h4 id="output-parsers-输出解析器"&gt;&lt;a href="#output-parsers-%e8%be%93%e5%87%ba%e8%a7%a3%e6%9e%90%e5%99%a8" class="header-anchor"&gt;&lt;/a&gt;Output Parsers (输出解析器)
&lt;/h4&gt;&lt;p&gt;结构化输出，确保模型输出符合预期格式&lt;/p&gt;
&lt;p&gt;●BaseOutputParser: 解析模型输出为结构化数据&lt;/p&gt;
&lt;p&gt;●PydanticOutputParser: 解析为 Pydantic 模型&lt;/p&gt;
&lt;p&gt;●JsonOutputParser: 解析为 JSON&lt;/p&gt;
&lt;h4 id="tools-工具"&gt;&lt;a href="#tools-%e5%b7%a5%e5%85%b7" class="header-anchor"&gt;&lt;/a&gt;Tools (工具)
&lt;/h4&gt;&lt;p&gt;BaseTool 是供智能体（Agents）使用的工具（Tools）的抽象基础，继承自 RunnableSerializable，并为与外部系统交互提供了标准化接口 。是 Function Calling 和 Agent 的基础。&lt;/p&gt;
&lt;p&gt;●BaseTool: 定义模型可调用的外部功能&lt;/p&gt;
&lt;p&gt;●让模型能执行计算、查询数据库、调用 API 等&lt;/p&gt;
&lt;h4 id="callbacks-回调"&gt;&lt;a href="#callbacks-%e5%9b%9e%e8%b0%83" class="header-anchor"&gt;&lt;/a&gt;Callbacks (回调)
&lt;/h4&gt;&lt;p&gt;观察和控制执行流程，提供执行过程的可见性&lt;/p&gt;
&lt;p&gt;●BaseCallbackHandler: 监听和响应执行事件&lt;/p&gt;
&lt;p&gt;●用于日志记录、调试、监控、流式输出等&lt;/p&gt;
&lt;h4 id="memorycache-记忆缓存"&gt;&lt;a href="#memorycache-%e8%ae%b0%e5%bf%86%e7%bc%93%e5%ad%98" class="header-anchor"&gt;&lt;/a&gt;Memory/Cache (记忆/缓存)
&lt;/h4&gt;&lt;p&gt;状态管理，对话历史管理、会话状态保持&lt;/p&gt;
&lt;p&gt;●BaseCache: 缓存 LLM 响应，避免重复调用&lt;/p&gt;
&lt;p&gt;●BaseStore: 键值存储抽象&lt;/p&gt;
&lt;h4 id="对照表"&gt;&lt;a href="#%e5%af%b9%e7%85%a7%e8%a1%a8" class="header-anchor"&gt;&lt;/a&gt;对照表
&lt;/h4&gt;&lt;p&gt;这里是一个快速对照表，来将上文的 LangChain 模块与核心抽象组件之间做个对应。&lt;/p&gt;

 &lt;blockquote&gt;
 &lt;p&gt;模块就像房间（客厅/厨房/卧室），抽象就像插座与标准接口（任意电器都能插上电并协同工作）。&lt;/p&gt;

 &lt;/blockquote&gt;
&lt;p&gt;●主要模块 = 按 “职能分区” 的功能板块（编排、模型、检索、Agent、记忆、部署等），回答 “系统里有哪些能力”。&lt;/p&gt;
&lt;p&gt;●核心抽象 = 跨模块通用的接口 / 基类（如 Runnable、BaseChatModel、BaseRetriever…），回答 “各模块如何被替换与拼装”。&lt;/p&gt;
&lt;p&gt;&lt;img alt="图片" loading="lazy" sizes="(max-width: 767px) calc(100vw - 30px), (max-width: 1023px) 700px, (max-width: 1279px) 950px, 1232px" src="https://pub-f29bf2b53160470c9a85250116509a24.r2.dev/post/2025-09-23-langchain-shi-yin-tan-hai-shi-ji-shu-zhai/011-934b9b80.png"&gt;&lt;/p&gt;
&lt;h3 id="agent"&gt;&lt;a href="#agent" class="header-anchor"&gt;&lt;/a&gt;Agent
&lt;/h3&gt;&lt;p&gt;LangChain 框架完全可以构建 Agent，并且这是它自诞生以来最核心、最吸引人的功能之一。经典的 Agent（如 ReAct 范式）通过 AgentExecutor 实现一个 “思考 -&amp;gt; 行动 -&amp;gt; 观察” 的循环。LLM 在这个循环中扮演决策者，决定下一步调用哪个工具（Tool）。对于绝大多数单 Agent 任务，LangChain 的原生 Agent 完全够用。&lt;/p&gt;
&lt;p&gt;&lt;img alt="图片" loading="lazy" sizes="(max-width: 767px) calc(100vw - 30px), (max-width: 1023px) 700px, (max-width: 1279px) 950px, 1232px" src="https://pub-f29bf2b53160470c9a85250116509a24.r2.dev/post/2025-09-23-langchain-shi-yin-tan-hai-shi-ji-shu-zhai/012-a42c3765.png"&gt;&lt;/p&gt;
&lt;p&gt;但是，当 Agent 的逻辑变得极其复杂，例如：&lt;/p&gt;
&lt;p&gt;1.需要循环和分支： 当流程不是线性，而是需要在多个步骤之间来回跳转。&lt;/p&gt;
&lt;p&gt;2.需要多 Agent 协作： 例如，一个 “分析师 Agent” 生成报告，交给 “代码生成 Agent” 编写代码，再由 “测试 Agent” 进行验证，如果测试失败，流程需要返回给 “分析师 Agent” 重新分析。&lt;/p&gt;
&lt;p&gt;3.需要持久化的状态管理： 在复杂的交互中，需要精确控制每一步的状态。&lt;/p&gt;
&lt;p&gt;这时 LangGraph 框架应运而生。它将 Agent 的工作流显式地定义为一个 状态图 (State Graph)。每个节点是一个工作单元（一个 LLM 调用或一个工具调用），每条边是状态的转移。它不是取代了 LangChain Agent，而是为构建更强大、更可控的 “状态化 Agent 系统” 提供了新的范式。&lt;/p&gt;
&lt;p&gt;&lt;img alt="图片" loading="lazy" sizes="(max-width: 767px) calc(100vw - 30px), (max-width: 1023px) 700px, (max-width: 1279px) 950px, 1232px" src="https://pub-f29bf2b53160470c9a85250116509a24.r2.dev/post/2025-09-23-langchain-shi-yin-tan-hai-shi-ji-shu-zhai/013-f494e8e1.png"&gt;&lt;/p&gt;
&lt;h3 id="langgraph"&gt;&lt;a href="#langgraph" class="header-anchor"&gt;&lt;/a&gt;LangGraph
&lt;/h3&gt;&lt;p&gt;&lt;img alt="图片" loading="lazy" sizes="(max-width: 767px) calc(100vw - 30px), (max-width: 1023px) 700px, (max-width: 1279px) 950px, 1232px" src="https://pub-f29bf2b53160470c9a85250116509a24.r2.dev/post/2025-09-23-langchain-shi-yin-tan-hai-shi-ji-shu-zhai/014-5f5786ef.png"&gt;&lt;/p&gt;

 &lt;blockquote&gt;
 &lt;p&gt;LangGraph 可以独立使用，但它也可以无缝集成到任何 LangChain 产品中。&lt;/p&gt;

 &lt;/blockquote&gt;
&lt;p&gt;LangGraph 提供了比 LangChain 更底层、更灵活的控制能力，特别适合需要状态管理、人机协作和复杂流程编排的场景。而 LangChain 则更适合快速原型开发和简单的链式处理任务。两者可以协同使用：LangChain 的组件可以作为 LangGraph 的节点，但 LangGraph 也可以完全独立于 LangChain 使用。&lt;/p&gt;
&lt;p&gt;如果不用 LangGraph 开发，选择其他框架，推荐使用 CrewAI。&lt;/p&gt;
&lt;h4 id="langgraph-架构"&gt;&lt;a href="#langgraph-%e6%9e%b6%e6%9e%84" class="header-anchor"&gt;&lt;/a&gt;LangGraph 架构
&lt;/h4&gt;&lt;p&gt;&lt;img alt="图片" loading="lazy" sizes="(max-width: 767px) calc(100vw - 30px), (max-width: 1023px) 700px, (max-width: 1279px) 950px, 1232px" src="https://pub-f29bf2b53160470c9a85250116509a24.r2.dev/post/2025-09-23-langchain-shi-yin-tan-hai-shi-ji-shu-zhai/015-8a05667b.png"&gt;&lt;/p&gt;
&lt;p&gt;LangGraph 的执行流程遵循以下算法：&lt;/p&gt;
&lt;p&gt;&lt;img alt="mermaid diagram" loading="lazy" sizes="(max-width: 767px) calc(100vw - 30px), (max-width: 1023px) 700px, (max-width: 1279px) 950px, 1232px" src="https://pub-f29bf2b53160470c9a85250116509a24.r2.dev/post/2025-09-23-langchain-shi-yin-tan-hai-shi-ji-shu-zhai/016-0df3480e.png"&gt;&lt;/p&gt;
&lt;p&gt;LangChain 与 LangGraph 适用场景对比：&lt;/p&gt;
&lt;p&gt;&lt;img alt="mermaid diagram" loading="lazy" sizes="(max-width: 767px) calc(100vw - 30px), (max-width: 1023px) 700px, (max-width: 1279px) 950px, 1232px" src="https://pub-f29bf2b53160470c9a85250116509a24.r2.dev/post/2025-09-23-langchain-shi-yin-tan-hai-shi-ji-shu-zhai/017-346dca23.png"&gt;&lt;/p&gt;
&lt;p&gt;LangChain 与 LangGraph 代码对比&lt;/p&gt;
&lt;p&gt;LangChain（使用 AgentExecutor）:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-python" data-lang="python"&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt; 1&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="err"&gt;⚡&lt;/span&gt; &lt;span class="n"&gt;python片段&lt;/span&gt;&lt;span class="c1"&gt;# pip install -U langchain langchain-openai&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt; 2&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="nn"&gt;os&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt; 3&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="kn"&gt;from&lt;/span&gt; &lt;span class="nn"&gt;datetime&lt;/span&gt; &lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;datetime&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt; 4&lt;/span&gt;&lt;span class="cl"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt; 5&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="kn"&gt;from&lt;/span&gt; &lt;span class="nn"&gt;langchain_openai&lt;/span&gt; &lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;ChatOpenAI&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt; 6&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="kn"&gt;from&lt;/span&gt; &lt;span class="nn"&gt;langchain_core.tools&lt;/span&gt; &lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;tool&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt; 7&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="kn"&gt;from&lt;/span&gt; &lt;span class="nn"&gt;langchain_core.prompts&lt;/span&gt; &lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;ChatPromptTemplate&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt; 8&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="kn"&gt;from&lt;/span&gt; &lt;span class="nn"&gt;langchain.agents&lt;/span&gt; &lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;create_tool_calling_agent&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;AgentExecutor&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt; 9&lt;/span&gt;&lt;span class="cl"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;10&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="c1"&gt;# ========== Tools ==========&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;11&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="nd"&gt;@tool&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;12&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;multiply&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;a&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;float&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;b&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;float&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;-&amp;gt;&lt;/span&gt; &lt;span class="nb"&gt;float&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;13&lt;/span&gt;&lt;span class="cl"&gt; &lt;span class="s2"&gt;&amp;#34;&amp;#34;&amp;#34;Return a*b.&amp;#34;&amp;#34;&amp;#34;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;14&lt;/span&gt;&lt;span class="cl"&gt; &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;a&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt; &lt;span class="n"&gt;b&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;15&lt;/span&gt;&lt;span class="cl"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;16&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="nd"&gt;@tool&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;17&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;get_time&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;fmt&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;str&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;&amp;#34;%Y-%m-&lt;/span&gt;&lt;span class="si"&gt;%d&lt;/span&gt;&lt;span class="s2"&gt; %H:%M:%S&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;-&amp;gt;&lt;/span&gt; &lt;span class="nb"&gt;str&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;18&lt;/span&gt;&lt;span class="cl"&gt; &lt;span class="s2"&gt;&amp;#34;&amp;#34;&amp;#34;Return current local time formatted by fmt.&amp;#34;&amp;#34;&amp;#34;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;19&lt;/span&gt;&lt;span class="cl"&gt; &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;datetime&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;now&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;strftime&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;fmt&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;20&lt;/span&gt;&lt;span class="cl"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;21&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="n"&gt;tools&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;multiply&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;get_time&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;22&lt;/span&gt;&lt;span class="cl"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;23&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="c1"&gt;# ========== LLM ==========&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;24&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="n"&gt;llm&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;ChatOpenAI&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;model&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s2"&gt;&amp;#34;gpt-4o-mini&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;temperature&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;25&lt;/span&gt;&lt;span class="cl"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;26&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="c1"&gt;# ========== Agent ==========&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;27&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="n"&gt;prompt&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;ChatPromptTemplate&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;from_messages&lt;/span&gt;&lt;span class="p"&gt;([&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;28&lt;/span&gt;&lt;span class="cl"&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;&amp;#34;system&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s2"&gt;&amp;#34;You are a concise tool-using agent. Use the tools when helpful. Reply in Chinese.&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;29&lt;/span&gt;&lt;span class="cl"&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;&amp;#34;human&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s2"&gt;&amp;#34;&lt;/span&gt;&lt;span class="si"&gt;{input}&lt;/span&gt;&lt;span class="s2"&gt;&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;30&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="p"&gt;])&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;31&lt;/span&gt;&lt;span class="cl"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;32&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="n"&gt;agent&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;create_tool_calling_agent&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;llm&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;tools&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;prompt&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;33&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="n"&gt;executor&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;AgentExecutor&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;agent&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="n"&gt;agent&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;tools&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="n"&gt;tools&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;verbose&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="kc"&gt;True&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;34&lt;/span&gt;&lt;span class="cl"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;35&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="c1"&gt;# ========== Demo ==========&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;36&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="n"&gt;res&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;executor&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;invoke&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;37&lt;/span&gt;&lt;span class="cl"&gt; &lt;span class="s2"&gt;&amp;#34;input&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;&amp;#34;先用 multiply 计算 7×12，再调用 get_time 给出当前时间。答案只要一行。&amp;#34;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;38&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="p"&gt;})&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;39&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="nb"&gt;print&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;&amp;#34;Final:&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;res&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s2"&gt;&amp;#34;output&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;])&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;我来画出详细的流程图，展示这个 LangChain Agent 的执行过程：&lt;/p&gt;
&lt;p&gt;&lt;img alt="mermaid diagram" loading="lazy" sizes="(max-width: 767px) calc(100vw - 30px), (max-width: 1023px) 700px, (max-width: 1279px) 950px, 1232px" src="https://pub-f29bf2b53160470c9a85250116509a24.r2.dev/post/2025-09-23-langchain-shi-yin-tan-hai-shi-ji-shu-zhai/018-03e5067a.png"&gt;&lt;/p&gt;
&lt;p&gt;LangGraph 代码：&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-python" data-lang="python"&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt; 1&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="err"&gt;⚡&lt;/span&gt; &lt;span class="n"&gt;python片段&lt;/span&gt;&lt;span class="c1"&gt;# pip install -U langgraph langchain langchain-openai&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt; 2&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="kn"&gt;from&lt;/span&gt; &lt;span class="nn"&gt;datetime&lt;/span&gt; &lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;datetime&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt; 3&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="kn"&gt;from&lt;/span&gt; &lt;span class="nn"&gt;typing&lt;/span&gt; &lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;Sequence&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt; 4&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="kn"&gt;from&lt;/span&gt; &lt;span class="nn"&gt;typing_extensions&lt;/span&gt; &lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;TypedDict&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt; 5&lt;/span&gt;&lt;span class="cl"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt; 6&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="kn"&gt;from&lt;/span&gt; &lt;span class="nn"&gt;langchain_core.tools&lt;/span&gt; &lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;tool&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt; 7&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="kn"&gt;from&lt;/span&gt; &lt;span class="nn"&gt;langchain_core.messages&lt;/span&gt; &lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;AnyMessage&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;HumanMessage&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt; 8&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="kn"&gt;from&lt;/span&gt; &lt;span class="nn"&gt;langchain_openai&lt;/span&gt; &lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;ChatOpenAI&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt; 9&lt;/span&gt;&lt;span class="cl"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;10&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="kn"&gt;from&lt;/span&gt; &lt;span class="nn"&gt;langgraph.graph&lt;/span&gt; &lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;StateGraph&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;END&lt;/span&gt; &lt;span class="c1"&gt;# END 表示图的终点&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;11&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="kn"&gt;from&lt;/span&gt; &lt;span class="nn"&gt;langgraph.prebuilt&lt;/span&gt; &lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;ToolNode&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;tools_condition&lt;/span&gt; &lt;span class="c1"&gt;# 预置的“工具节点”和“是否需要走工具”的路由函数&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;12&lt;/span&gt;&lt;span class="cl"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;13&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="c1"&gt;# ===== 工具定义：用 @tool 自动生成 JSON Schema，便于模型函数调用 =====&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;14&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="nd"&gt;@tool&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;15&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;multiply&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;a&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;float&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;b&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;float&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;-&amp;gt;&lt;/span&gt; &lt;span class="nb"&gt;float&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="c1"&gt;# 定义乘法工具&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;16&lt;/span&gt;&lt;span class="cl"&gt; &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;a&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt; &lt;span class="n"&gt;b&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;17&lt;/span&gt;&lt;span class="cl"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;18&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="nd"&gt;@tool&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;19&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;get_time&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;fmt&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;str&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;&amp;#34;%Y-%m-&lt;/span&gt;&lt;span class="si"&gt;%d&lt;/span&gt;&lt;span class="s2"&gt; %H:%M:%S&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;-&amp;gt;&lt;/span&gt; &lt;span class="nb"&gt;str&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="c1"&gt;# 定义获取时间工具&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;20&lt;/span&gt;&lt;span class="cl"&gt; &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;datetime&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;now&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;strftime&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;fmt&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;21&lt;/span&gt;&lt;span class="cl"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;22&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="n"&gt;tools&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;multiply&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;get_time&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="c1"&gt;# 工具列表（给模型/工具节点用）&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;23&lt;/span&gt;&lt;span class="cl"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;24&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="c1"&gt;# ===== 绑定工具到模型：让 LLM 知道有哪些可调用的函数（工具） =====&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;25&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="n"&gt;llm&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;ChatOpenAI&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;model&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s2"&gt;&amp;#34;gpt-4o-mini&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;temperature&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;bind_tools&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;tools&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;26&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="c1"&gt;# bind_tools(...) 的效果：&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;27&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="c1"&gt;# 1) 把 tools 的参数签名/描述转成 JSON Schema 给模型；&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;28&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="c1"&gt;# 2) 让模型在需要时产生 tool_calls（函数调用）结构，而不是直接“胡说答案”。&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;29&lt;/span&gt;&lt;span class="cl"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;30&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="c1"&gt;# ===== 定义“状态”的数据结构：LangGraph 的节点之间传的就是这个 State =====&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;31&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="n"&gt;classS&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;TypedDict&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;32&lt;/span&gt;&lt;span class="cl"&gt; &lt;span class="n"&gt;messages&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;Sequence&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;AnyMessage&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="c1"&gt;# 一串消息（人类/AI/工具消息），作为“对话上下文”&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;33&lt;/span&gt;&lt;span class="cl"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;34&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="c1"&gt;# ===== 定义一个“模型节点”：输入 State，调用 LLM，输出一条 AI 消息 =====&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;35&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;agent_node&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;state&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;S&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;36&lt;/span&gt;&lt;span class="cl"&gt; &lt;span class="c1"&gt;# 关键：把现有 messages（含用户、人类消息、工具结果等）喂给 LLM&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;37&lt;/span&gt;&lt;span class="cl"&gt; &lt;span class="n"&gt;ai_msg&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;llm&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;invoke&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;state&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s2"&gt;&amp;#34;messages&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;])&lt;/span&gt; &lt;span class="c1"&gt;# 可能返回带 tool_calls 的 AIMessage&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;38&lt;/span&gt;&lt;span class="cl"&gt; &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="s2"&gt;&amp;#34;messages&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;ai_msg&lt;/span&gt;&lt;span class="p"&gt;]}&lt;/span&gt; &lt;span class="c1"&gt;# LangGraph 会把这条消息合并到全局 state&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;39&lt;/span&gt;&lt;span class="cl"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;40&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="c1"&gt;# ===== 搭建“有向图”：节点 + 边，决定执行路径 =====&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;41&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="n"&gt;g&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;StateGraph&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;S&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="c1"&gt;# 新建一个“状态图”，S 是状态类型（结构）&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;42&lt;/span&gt;&lt;span class="cl"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;43&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="c1"&gt;# ——① 注册节点（起名叫 &amp;#34;agent&amp;#34; 和 &amp;#34;tools&amp;#34;）——&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;44&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="n"&gt;g&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;add_node&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;&amp;#34;agent&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;agent_node&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="c1"&gt;# 把上面的函数包装成一个图节点&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;45&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="n"&gt;g&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;add_node&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;&amp;#34;tools&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;ToolNode&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;tools&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt; &lt;span class="c1"&gt;# 预置的工具节点：会读取 AI 的 tool_calls 并执行&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;46&lt;/span&gt;&lt;span class="cl"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;47&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="c1"&gt;# ——② 设置“入口节点”：从哪个节点开始跑——&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;48&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="n"&gt;g&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;set_entry_point&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;&amp;#34;agent&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="c1"&gt;# ★ 从 &amp;#34;agent&amp;#34; 开始（也就是先问一次模型）&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;49&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="c1"&gt;# 解释：这句相当于“第一步进图先走哪个节点”。如果不设，编译时会报错或不知道从哪开始。&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;50&lt;/span&gt;&lt;span class="cl"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;51&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="c1"&gt;# ——③ 配置“条件边”：根据模型输出决定接下来走哪条边——&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;52&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="n"&gt;g&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;add_conditional_edges&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;53&lt;/span&gt;&lt;span class="cl"&gt; &lt;span class="s2"&gt;&amp;#34;agent&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="c1"&gt;# 从 &amp;#34;agent&amp;#34; 节点出来时&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;54&lt;/span&gt;&lt;span class="cl"&gt; &lt;span class="n"&gt;tools_condition&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="c1"&gt;# 用预置的判断函数：看 AIMessage 是否包含 tool_calls&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;55&lt;/span&gt;&lt;span class="cl"&gt; &lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="s2"&gt;&amp;#34;tools&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;&amp;#34;tools&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s2"&gt;&amp;#34;end&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;END&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="c1"&gt;# 如果需要工具→去 &amp;#34;tools&amp;#34;；否则→直接结束&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;56&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;57&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="c1"&gt;# 解释：tools_condition 会检查最新一条 AI 消息。&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;58&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="c1"&gt;# - 若模型产生了函数调用（tool_calls），返回路由键 &amp;#34;tools&amp;#34;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;59&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="c1"&gt;# - 若没有需要的工具调用，返回路由键 &amp;#34;end&amp;#34;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;60&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="c1"&gt;# 这行把路由键映射为真正的边：“tools”→去 tools 节点，“end”→走到 END（图的终点）。&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;61&lt;/span&gt;&lt;span class="cl"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;62&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="c1"&gt;# ——④ 把工具节点执行完后的边接回“agent”，形成闭环——&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;63&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="n"&gt;g&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;add_edge&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;&amp;#34;tools&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s2"&gt;&amp;#34;agent&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;64&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="c1"&gt;# 解释：当 tools 节点执行完所有函数调用，会把结果（ToolMessage）追加到 state.messages，&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;65&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="c1"&gt;# 然后回到 &amp;#34;agent&amp;#34; 再问一次模型。直到模型不再发起新的 tool_calls。&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;66&lt;/span&gt;&lt;span class="cl"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;67&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="c1"&gt;# ——⑤ 编译成可运行的“应用”对象——&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;68&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="n"&gt;app&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;g&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;compile&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;69&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="c1"&gt;# 解释：compile() 会把上面定义的节点/边/合并策略等打包成可执行的图（可 invoke/stream）。&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;70&lt;/span&gt;&lt;span class="cl"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;71&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="c1"&gt;# ===== 运行：给一条人类消息，按图的“入口节点”开始执行 =====&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;72&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="n"&gt;final&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;app&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;invoke&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;73&lt;/span&gt;&lt;span class="cl"&gt; &lt;span class="s2"&gt;&amp;#34;messages&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;HumanMessage&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;content&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s2"&gt;&amp;#34;先用 multiply 计算 7×12，再调用 get_time 报当前时间。只要一行中文。&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;)]&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;74&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="p"&gt;})&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;75&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="c1"&gt;# 解释：invoke 会：&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;76&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="c1"&gt;# step1: 进入入口 &amp;#34;agent&amp;#34; → LLM 读到 HumanMessage，判断要不要调用工具；&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;77&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="c1"&gt;# step2: 如果需要工具 → 路由到 &amp;#34;tools&amp;#34; 执行（得到 ToolMessage）→ 回到 &amp;#34;agent&amp;#34;；&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;78&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="c1"&gt;# step3: 重复 step1~2，直到不需要工具 → 按条件边路由到 END → 返回最终状态。&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;79&lt;/span&gt;&lt;span class="cl"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;80&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="nb"&gt;print&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;final&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s2"&gt;&amp;#34;messages&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;][&lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;content&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="c1"&gt;# 打印最后一条 AI 的自然语言回复&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;LangGraph 详细执行流程：&lt;/p&gt;
&lt;p&gt;&lt;img alt="mermaid diagram" loading="lazy" sizes="(max-width: 767px) calc(100vw - 30px), (max-width: 1023px) 700px, (max-width: 1279px) 950px, 1232px" src="https://pub-f29bf2b53160470c9a85250116509a24.r2.dev/post/2025-09-23-langchain-shi-yin-tan-hai-shi-ji-shu-zhai/019-ad88b6b3.png"&gt;&lt;/p&gt;
&lt;h2 id="竞品"&gt;&lt;a href="#%e7%ab%9e%e5%93%81" class="header-anchor"&gt;&lt;/a&gt;竞品
&lt;/h2&gt;&lt;p&gt;如果不使用 LangChain 开发 AI/LLM 应用，以下是主要的替代框架选择：&lt;/p&gt;
&lt;p&gt;以下是按要求整理的表格内容：&lt;/p&gt;
&lt;table&gt;
 &lt;thead&gt;
 &lt;tr&gt;
 &lt;th&gt;框架&lt;/th&gt;
 &lt;th&gt;定位/标签&lt;/th&gt;
 &lt;th&gt;语言/平台&lt;/th&gt;
 &lt;th&gt;核心优势关键词&lt;/th&gt;
 &lt;th&gt;典型场景&lt;/th&gt;
 &lt;th&gt;🔎RAG&lt;/th&gt;
 &lt;th&gt;👥Agent&lt;/th&gt;
 &lt;th&gt;🏢企业&lt;/th&gt;
 &lt;th&gt;🎯输出控制&lt;/th&gt;
 &lt;th&gt;🪶轻量&lt;/th&gt;
 &lt;/tr&gt;
 &lt;/thead&gt;
 &lt;tbody&gt;
 &lt;tr&gt;
 &lt;td&gt;&lt;strong&gt;LlamaIndex&lt;/strong&gt;&lt;/td&gt;
 &lt;td&gt;RAG 与数据检索专家&lt;/td&gt;
 &lt;td&gt;Py/TS&lt;/td&gt;
 &lt;td&gt;多索引策略、查询路由、强检索、结构/非结构数据&lt;/td&gt;
 &lt;td&gt;知识库问答、文档分析、RAG&lt;/td&gt;
 &lt;td&gt;★★★★★&lt;/td&gt;
 &lt;td&gt;★★☆☆☆&lt;/td&gt;
 &lt;td&gt;★★★☆☆&lt;/td&gt;
 &lt;td&gt;★★☆☆☆&lt;/td&gt;
 &lt;td&gt;★★★☆☆&lt;/td&gt;
 &lt;/tr&gt;
 &lt;tr&gt;
 &lt;td&gt;&lt;strong&gt;CrewAI&lt;/strong&gt;&lt;/td&gt;
 &lt;td&gt;多智能体团队协作&lt;/td&gt;
 &lt;td&gt;Py&lt;/td&gt;
 &lt;td&gt;角色/任务、顺序/并行/层级协作、社区活跃&lt;/td&gt;
 &lt;td&gt;多智能体系统、内容流水线、任务分解&lt;/td&gt;
 &lt;td&gt;★★☆☆☆&lt;/td&gt;
 &lt;td&gt;★★★★★&lt;/td&gt;
 &lt;td&gt;★★★☆☆&lt;/td&gt;
 &lt;td&gt;★★☆☆☆&lt;/td&gt;
 &lt;td&gt;★★★☆☆&lt;/td&gt;
 &lt;/tr&gt;
 &lt;tr&gt;
 &lt;td&gt;&lt;strong&gt;Semantic Kernel&lt;/strong&gt;&lt;/td&gt;
 &lt;td&gt;企业级 AI 编排&lt;/td&gt;
 &lt;td&gt;C#/Py/Java&lt;/td&gt;
 &lt;td&gt;技能/规划器、Azure 深度集成、微软背书&lt;/td&gt;
 &lt;td&gt;企业应用、复杂规划、微软技术栈&lt;/td&gt;
 &lt;td&gt;★★★☆☆&lt;/td&gt;
 &lt;td&gt;★★★☆☆&lt;/td&gt;
 &lt;td&gt;★★★★★&lt;/td&gt;
 &lt;td&gt;★★☆☆☆&lt;/td&gt;
 &lt;td&gt;★★☆☆☆&lt;/td&gt;
 &lt;/tr&gt;
 &lt;tr&gt;
 &lt;td&gt;&lt;strong&gt;Haystack&lt;/strong&gt;&lt;/td&gt;
 &lt;td&gt;NLP/搜索系统专家&lt;/td&gt;
 &lt;td&gt;Py&lt;/td&gt;
 &lt;td&gt;Pipeline 清晰、文档处理强、评测完善、重隐私&lt;/td&gt;
 &lt;td&gt;QA 系统、语义搜索、信息抽取&lt;/td&gt;
 &lt;td&gt;★★★★☆&lt;/td&gt;
 &lt;td&gt;★★☆☆☆&lt;/td&gt;
 &lt;td&gt;★★★★☆&lt;/td&gt;
 &lt;td&gt;★★☆☆☆&lt;/td&gt;
 &lt;td&gt;★★★☆☆&lt;/td&gt;
 &lt;/tr&gt;
 &lt;tr&gt;
 &lt;td&gt;&lt;strong&gt;AutoGen&lt;/strong&gt;&lt;/td&gt;
 &lt;td&gt;自动化对话与代码生成&lt;/td&gt;
 &lt;td&gt;Py&lt;/td&gt;
 &lt;td&gt;代码生成/执行、多 Agent 对话、自动化流程、人机协作&lt;/td&gt;
 &lt;td&gt;代码生成、数据分析自动化、技术任务&lt;/td&gt;
 &lt;td&gt;★★☆☆☆&lt;/td&gt;
 &lt;td&gt;★★★★☆&lt;/td&gt;
 &lt;td&gt;★★★☆☆&lt;/td&gt;
 &lt;td&gt;★★☆☆☆&lt;/td&gt;
 &lt;td&gt;★★★☆☆&lt;/td&gt;
 &lt;/tr&gt;
 &lt;tr&gt;
 &lt;td&gt;&lt;strong&gt;Guidance&lt;/strong&gt;&lt;/td&gt;
 &lt;td&gt;精确输出控制&lt;/td&gt;
 &lt;td&gt;Py&lt;/td&gt;
 &lt;td&gt;模板语言、强格式约束、轻量高效&lt;/td&gt;
 &lt;td&gt;结构化输出、格式化生成、提示工程&lt;/td&gt;
 &lt;td&gt;★☆☆☆☆&lt;/td&gt;
 &lt;td&gt;★☆☆☆☆&lt;/td&gt;
 &lt;td&gt;★★★☆☆&lt;/td&gt;
 &lt;td&gt;★★★★★&lt;/td&gt;
 &lt;td&gt;★★★★☆&lt;/td&gt;
 &lt;/tr&gt;
 &lt;/tbody&gt;
&lt;/table&gt;
&lt;p&gt;还有一些新兴 / 特色框架：&lt;/p&gt;
&lt;p&gt;以下是按要求整理的表格内容：&lt;/p&gt;
&lt;table&gt;
 &lt;thead&gt;
 &lt;tr&gt;
 &lt;th&gt;框架&lt;/th&gt;
 &lt;th&gt;定位/标签&lt;/th&gt;
 &lt;th&gt;语言/平台&lt;/th&gt;
 &lt;th&gt;核心优势关键词&lt;/th&gt;
 &lt;th&gt;典型场景&lt;/th&gt;
 &lt;th&gt;🔎RAG&lt;/th&gt;
 &lt;th&gt;👥Agent&lt;/th&gt;
 &lt;th&gt;🏢企业&lt;/th&gt;
 &lt;th&gt;🎯输出控制&lt;/th&gt;
 &lt;th&gt;🪶轻量&lt;/th&gt;
 &lt;/tr&gt;
 &lt;/thead&gt;
 &lt;tbody&gt;
 &lt;tr&gt;
 &lt;td&gt;&lt;strong&gt;LiteLLM&lt;/strong&gt;&lt;/td&gt;
 &lt;td&gt;统一 LLM API&lt;/td&gt;
 &lt;td&gt;Py&lt;/td&gt;
 &lt;td&gt;100+ 模型统一接口、超轻量、零依赖&lt;/td&gt;
 &lt;td&gt;简单 LLM 调用、多模型切换、原型&lt;/td&gt;
 &lt;td&gt;★☆☆☆☆&lt;/td&gt;
 &lt;td&gt;★☆☆☆☆&lt;/td&gt;
 &lt;td&gt;★★★☆☆&lt;/td&gt;
 &lt;td&gt;★★☆☆☆&lt;/td&gt;
 &lt;td&gt;★★★★★&lt;/td&gt;
 &lt;/tr&gt;
 &lt;tr&gt;
 &lt;td&gt;&lt;strong&gt;DSPy&lt;/strong&gt;&lt;/td&gt;
 &lt;td&gt;声明式/自动提示优化&lt;/td&gt;
 &lt;td&gt;Py&lt;/td&gt;
 &lt;td&gt;可学习模块、自动调参、学术完善&lt;/td&gt;
 &lt;td&gt;指标导向优化、研究/实验&lt;/td&gt;
 &lt;td&gt;★★★☆☆&lt;/td&gt;
 &lt;td&gt;★★☆☆☆&lt;/td&gt;
 &lt;td&gt;★★☆☆☆&lt;/td&gt;
 &lt;td&gt;★★★☆☆&lt;/td&gt;
 &lt;td&gt;★★★☆☆&lt;/td&gt;
 &lt;/tr&gt;
 &lt;tr&gt;
 &lt;td&gt;&lt;strong&gt;txtai&lt;/strong&gt;&lt;/td&gt;
 &lt;td&gt;轻量 Transformers 框架&lt;/td&gt;
 &lt;td&gt;Py&lt;/td&gt;
 &lt;td&gt;HF 生态、内置向量库、性能好&lt;/td&gt;
 &lt;td&gt;轻量语义搜索、嵌入式应用&lt;/td&gt;
 &lt;td&gt;★★★☆☆&lt;/td&gt;
 &lt;td&gt;★☆☆☆☆&lt;/td&gt;
 &lt;td&gt;★★☆☆☆&lt;/td&gt;
 &lt;td&gt;★★☆☆☆&lt;/td&gt;
 &lt;td&gt;★★★★☆&lt;/td&gt;
 &lt;/tr&gt;
 &lt;tr&gt;
 &lt;td&gt;&lt;strong&gt;MetaGPT&lt;/strong&gt;&lt;/td&gt;
 &lt;td&gt;“AI 软件公司”模拟&lt;/td&gt;
 &lt;td&gt;Py&lt;/td&gt;
 &lt;td&gt;角色分工、项目级生成、代码产出&lt;/td&gt;
 &lt;td&gt;自动化软件开发、代码生成&lt;/td&gt;
 &lt;td&gt;★☆☆☆☆&lt;/td&gt;
 &lt;td&gt;★★★★☆&lt;/td&gt;
 &lt;td&gt;★★☆☆☆&lt;/td&gt;
 &lt;td&gt;★★☆☆☆&lt;/td&gt;
 &lt;td&gt;★★☆☆☆&lt;/td&gt;
 &lt;/tr&gt;
 &lt;/tbody&gt;
&lt;/table&gt;
&lt;p&gt;在实际开发中，如不用 LangChain， 选型范围会缩小到最流行的几个竞品，比如：&lt;/p&gt;
&lt;p&gt;●🦙 LlamaIndex：原名 GPT-Index，是一个开源的数据框架，专门用于构建大型语言模型 (LLM) 应用。它主要解决了如何将 LLM 与外部数据有效连接的问题，使开发者能够创建更强大的知识密集型应用。&lt;/p&gt;
&lt;p&gt;●🚢 CrewAI：多智能体团队协作框架，CrewAI 是一个轻量、快速的 Python 框架，完全从零构建，与 LangChain 或其他代理框架完全无关。它为开发者提供高级别的简洁性和精确的底层控制，非常适合创建适用于任何场景的自主 AI 代理。&lt;/p&gt;
&lt;p&gt;●🔧 Haystack：一个端到端的大型语言模型（LLM）框架，它允许你构建由 LLM、Transformer 模型、向量搜索等功能驱动的应用程序。&lt;/p&gt;
&lt;p&gt;●🚀 AutoGen：用于创建能够自主行动或与人类协作的多智能体AI应用的框架&lt;/p&gt;
&lt;p&gt;需要注意的是：这些框架不是互斥的，可以组合使用（如 LlamaIndex + CrewAI）&lt;/p&gt;
&lt;h2 id="推荐学习路径"&gt;&lt;a href="#%e6%8e%a8%e8%8d%90%e5%ad%a6%e4%b9%a0%e8%b7%af%e5%be%84" class="header-anchor"&gt;&lt;/a&gt;推荐学习路径
&lt;/h2&gt;&lt;h3 id="掌握灵魂-lcel-langchain-expression-language"&gt;&lt;a href="#%e6%8e%8c%e6%8f%a1%e7%81%b5%e9%ad%82-lcel-langchain-expression-language" class="header-anchor"&gt;&lt;/a&gt;掌握灵魂 ——LCEL (LangChain Expression Language)
&lt;/h3&gt;&lt;p&gt;这是当前 LangChain 的绝对核心，也是最佳实践的开端。忘记很多旧的、高阶的 Chain 对象，从 LCEL 开始。&lt;/p&gt;
&lt;p&gt;实践: 构建你第一个，也是最重要的 LCEL 链：&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-python" data-lang="python"&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt; 1&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="err"&gt;⚡&lt;/span&gt; &lt;span class="n"&gt;python片段from&lt;/span&gt; &lt;span class="n"&gt;langchain_core&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;prompts&lt;/span&gt; &lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="nn"&gt;ChatPromptTemplate&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt; 2&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="kn"&gt;from&lt;/span&gt; &lt;span class="nn"&gt;langchain_openai&lt;/span&gt; &lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;ChatOpenAI&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt; 3&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="kn"&gt;from&lt;/span&gt; &lt;span class="nn"&gt;langchain_core.output_parsers&lt;/span&gt; &lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;StrOutputParser&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt; 4&lt;/span&gt;&lt;span class="cl"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt; 5&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="c1"&gt;# 1. 定义提示模板 (Prompt)&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt; 6&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="n"&gt;prompt&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;ChatPromptTemplate&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;from_template&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;&amp;#34;请给我讲一个关于&lt;/span&gt;&lt;span class="si"&gt;{topic}&lt;/span&gt;&lt;span class="s2"&gt;的笑话。&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt; 7&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="c1"&gt;# 2. 初始化模型 (Model)&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt; 8&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="n"&gt;model&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;ChatOpenAI&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt; 9&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="c1"&gt;# 3. 定义输出解析器 (Parser)&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;10&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="n"&gt;output_parser&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;StrOutputParser&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;11&lt;/span&gt;&lt;span class="cl"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;12&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="c1"&gt;# 4. 使用管道符 | 链接起来&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;13&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="n"&gt;chain&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;prompt&lt;/span&gt; &lt;span class="o"&gt;|&lt;/span&gt; &lt;span class="n"&gt;model&lt;/span&gt; &lt;span class="o"&gt;|&lt;/span&gt; &lt;span class="n"&gt;output_parser&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;14&lt;/span&gt;&lt;span class="cl"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;15&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="c1"&gt;# 5. 执行链&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;16&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="n"&gt;result&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;chain&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;invoke&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;&lt;span class="s2"&gt;&amp;#34;topic&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;&amp;#34;程序员&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;})&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;17&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="nb"&gt;print&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;result&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;这个 | 管道符就是 “数据流” 的体现。invoke 的输入字典首先流入 prompt 变成一个完整的提示，然后流入 model 得到模型的响应，最后流入 output_parser 提取出字符串结果。&lt;/p&gt;
&lt;h3 id="构建最核心的应用-rag"&gt;&lt;a href="#%e6%9e%84%e5%bb%ba%e6%9c%80%e6%a0%b8%e5%bf%83%e7%9a%84%e5%ba%94%e7%94%a8-rag" class="header-anchor"&gt;&lt;/a&gt;构建最核心的应用 ——RAG
&lt;/h3&gt;&lt;p&gt;理解了 LCEL 后，构建一个基础的 RAG 应用来贯穿大部分核心组件。&lt;/p&gt;
&lt;p&gt;实践:&lt;/p&gt;
&lt;p&gt;1.加载 (Load): 使用 TextLoader 或 PyPDFLoader 加载一个本地文件。&lt;/p&gt;
&lt;p&gt;2.分割 (Split): 使用 RecursiveCharacterTextSplitter 将文档分割成块 (Chunks)。&lt;/p&gt;
&lt;p&gt;3.存储 (Store): 使用 OpenAIEmbeddings 创建嵌入，并使用 FAISS 或 Chroma 等向量数据库进行存储。&lt;/p&gt;
&lt;p&gt;4.检索 (Retrieve): 从向量数据库创建一个 retriever 对象。&lt;/p&gt;
&lt;p&gt;5.生成 (Generate): 使用 LCEL 将检索到的内容、用户问题和模型调用组合成一个完整的 RAG 链。这比使用旧的 RetrievalQA 链更能让你理解内部原理。&lt;/p&gt;
&lt;p&gt;&lt;img alt="图片" loading="lazy" sizes="(max-width: 767px) calc(100vw - 30px), (max-width: 1023px) 700px, (max-width: 1279px) 950px, 1232px" src="https://pub-f29bf2b53160470c9a85250116509a24.r2.dev/post/2025-09-23-langchain-shi-yin-tan-hai-shi-ji-shu-zhai/020-f489b51e.png"&gt;&lt;/p&gt;
&lt;h3 id="进入高阶-agents"&gt;&lt;a href="#%e8%bf%9b%e5%85%a5%e9%ab%98%e9%98%b6-agents" class="header-anchor"&gt;&lt;/a&gt;进入高阶 ——Agents
&lt;/h3&gt;&lt;p&gt;当你的应用需要与外部世界交互（如调用 API、查询数据库）时，才需要 Agent。&lt;/p&gt;
&lt;p&gt;实践:&lt;/p&gt;
&lt;p&gt;1.定义一两个简单的 Tool，例如一个用于数学计算的工具，一个用于获取当前日期的工具。&lt;/p&gt;
&lt;p&gt;2.选择一个 Agent 类型 (如 create&lt;em&gt;openai&lt;/em&gt;tools_agent)。&lt;/p&gt;
&lt;p&gt;3.使用 AgentExecutor 来运行它，观察它如何根据你的问题选择工具、执行并返回结果。&lt;/p&gt;
&lt;h2 id="常见陷阱与挑战"&gt;&lt;a href="#%e5%b8%b8%e8%a7%81%e9%99%b7%e9%98%b1%e4%b8%8e%e6%8c%91%e6%88%98" class="header-anchor"&gt;&lt;/a&gt;常见陷阱与挑战
&lt;/h2&gt;&lt;h3 id="抽象泄漏-leaky-abstraction"&gt;&lt;a href="#%e6%8a%bd%e8%b1%a1%e6%b3%84%e6%bc%8f-leaky-abstraction" class="header-anchor"&gt;&lt;/a&gt;抽象泄漏 (Leaky Abstraction)
&lt;/h3&gt;&lt;p&gt;定义: 一个 “抽象” 旨在隐藏底层实现的复杂性。当这个抽象无法完全隐藏底层细节，导致你必须理解底层是如何工作的才能正确地使用它或排查问题时，就发生了 “抽象泄漏”。&lt;/p&gt;
&lt;p&gt;LangChain 中的具体例子 (以旧版的 RetrievalQA 链为例):&lt;/p&gt;
&lt;p&gt;●美好的抽象: RetrievalQA 链看起来很简单，你只需要给它一个 llm 和一个 retriever，它就能帮你完成 RAG。你期望它能 “神奇地” 工作。&lt;/p&gt;
&lt;p&gt;●泄漏的现实:&lt;/p&gt;
&lt;p&gt;a.Prompt 在哪里？ 你发现问答效果不好。为什么？因为 RetrievalQA 内部使用了一个默认的、隐藏的 Prompt 模板 (类似 &amp;ldquo;Use the following pieces of context to answer the user&amp;rsquo;s question&amp;hellip;&amp;quot;)。这个默认模板可能不适合你的模型（比如某些中文模型），或者不符合你的业务场景。&lt;/p&gt;
&lt;p&gt;b.文档如何组合？ 当 retriever 返回了 4 个文档块 (Chunks) 时，这些文档是如何被塞进最终的 Prompt 里的？是简单拼接吗？如果超过了模型的上下文窗口怎么办？RetrievalQA 有一个 chain&lt;em&gt;type 参数（如 stuff, map&lt;/em&gt;reduce）来控制这个行为。&lt;/p&gt;
&lt;p&gt;问题的根源: 为了解决上述问题，你被迫去阅读 LangChain 的源码，去理解 RetrievalQA 内部隐藏的 Prompt 和文档组合逻辑。这时，RetrievalQA 这个本应让你省心的 “高级抽象”，反而成了你理解和调试的障碍。抽象 “泄漏” 了底层的实现细节。&lt;/p&gt;
&lt;p&gt;如何应对？&lt;/p&gt;
&lt;p&gt;拥抱 LCEL: LCEL 在很大程度上解决了这个问题。通过 prompt | model | parser 的方式， Prompt 是显式的，数据流是清晰的。你可以完全控制每一个环节，没有 “魔法” 和隐藏的逻辑。这是一种更 “白盒” 的构建方式，虽然初看起来代码多了一点，但可控性和可调试性大大增强。当然还有一种方案就是 “取其精华，去其糟粕”，LangChain 框架引入后，只用必要的部分组件，其余需要灵活处理的部分，全手写。&lt;/p&gt;
&lt;h3 id="调试的-黑盒感"&gt;&lt;a href="#%e8%b0%83%e8%af%95%e7%9a%84-%e9%bb%91%e7%9b%92%e6%84%9f" class="header-anchor"&gt;&lt;/a&gt;调试的 “黑盒感”
&lt;/h3&gt;&lt;p&gt;问题: 链的最终输出不符合预期，但中间过程完全不可见，不知道是 Prompt 错了、检索出的文档错了，还是模型理解错了。&lt;/p&gt;
&lt;p&gt;解决方案: LangSmith。这是解决此问题的标准答案。设置环境变量 LANGCHAIN&lt;em&gt;TRACING&lt;/em&gt;V2=&amp;ldquo;true&amp;rdquo; 即可开始追踪。用不了 LangSmith 的话，也可以用开源的 langfuse 替代。&lt;/p&gt;
&lt;h3 id="对简单任务过度设计"&gt;&lt;a href="#%e5%af%b9%e7%ae%80%e5%8d%95%e4%bb%bb%e5%8a%a1%e8%bf%87%e5%ba%a6%e8%ae%be%e8%ae%a1" class="header-anchor"&gt;&lt;/a&gt;对简单任务过度设计
&lt;/h3&gt;&lt;p&gt;问题: 你的任务只是需要根据一个模板调用一次 OpenAI API。这种情况下，引入 LangChain 的 LLMChain 相比直接使用 openai 库的 client.chat.completions.create()，增加了不必要的复杂性。&lt;/p&gt;
&lt;p&gt;解决方案: 保持务实。如果你的应用逻辑非常简单，就是一个单一的 LLM 调用，那么直接使用原生 SDK 可能更清晰、更轻量。当且仅当你需要编排多个步骤（如 RAG）、管理记忆、或使用 Agents 时，LangChain 的价值才能最大化。&lt;/p&gt;
&lt;h2 id="langchain-的版本演进"&gt;&lt;a href="#langchain-%e7%9a%84%e7%89%88%e6%9c%ac%e6%bc%94%e8%bf%9b" class="header-anchor"&gt;&lt;/a&gt;LangChain 的版本演进
&lt;/h2&gt;&lt;p&gt;&lt;img alt="mermaid diagram" loading="lazy" sizes="(max-width: 767px) calc(100vw - 30px), (max-width: 1023px) 700px, (max-width: 1279px) 950px, 1232px" src="https://pub-f29bf2b53160470c9a85250116509a24.r2.dev/post/2025-09-23-langchain-shi-yin-tan-hai-shi-ji-shu-zhai/021-327dd488.png"&gt;&lt;/p&gt;
&lt;h3 id="v10-alpha和-v0x-的关键差异"&gt;&lt;a href="#v10-alpha%e5%92%8c-v0x-%e7%9a%84%e5%85%b3%e9%94%ae%e5%b7%ae%e5%bc%82" class="header-anchor"&gt;&lt;/a&gt;v1.0 alpha：和 v0.x 的关键差异
&lt;/h3&gt;&lt;p&gt;v1.0 alpha（2025-09）是 LangChain 的一次 “面向长期” 的大改版&lt;/p&gt;
&lt;p&gt;核心变化&lt;/p&gt;
&lt;p&gt;●消息模型统一：新增 .content_blocks（标准化的 “内容块” 视图），把不同厂商的 “推理、引用、服务端工具调用、多模态” 等表示成 同一种类型，减少 provider 差异带来的胶水代码；对旧 .content 后向兼容。&lt;/p&gt;
&lt;p&gt;●Agent 重心调整：create_agent() 成为默认入口，底层基于 LangGraph 的图式运行时（持久化、流式、人审 / 中断、错误处理更规范）。&lt;/p&gt;
&lt;p&gt;●包面积极度收敛：langchain 更聚焦 “标准接口 + 预置 Agent / 链”；历史面迁到 langchain-legacy，便于兼容老代码再慢慢重构。&lt;/p&gt;
&lt;p&gt;●默认行为与平台要求：&lt;/p&gt;
&lt;p&gt;○Python 需 ≥3.10；Chat 模型返回类型固定为 AIMessage；OpenAI Responses API 的默认输出版本调整（可用 LC&lt;em&gt;OUTPUT&lt;/em&gt;VERSION=v0 退回）；Anthropic max_tokens 默认值上调。&lt;/p&gt;
&lt;p&gt;○JS / TS 侧：核心原语（createAgent、ToolNode、tool、消息类型）直接从 langchain 导出；Node.js 需 ≥20；大量老子路径导出清理。&lt;/p&gt;
&lt;h4 id="三个常见场景"&gt;&lt;a href="#%e4%b8%89%e4%b8%aa%e5%b8%b8%e8%a7%81%e5%9c%ba%e6%99%af" class="header-anchor"&gt;&lt;/a&gt;三个常见场景
&lt;/h4&gt;&lt;p&gt;1.“会用工具、可结构化返回” 的 Agent&lt;/p&gt;
&lt;p&gt;v0.x（典型写法）：构建 Agent + Executor，再自己管结构化解析 / 二次调用&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-python" data-lang="python"&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt; 1&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="err"&gt;⚡&lt;/span&gt; &lt;span class="n"&gt;python片段&lt;/span&gt;&lt;span class="c1"&gt;# 旧式（示意）：AgentExecutor + 自行处理结构化输出&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt; 2&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="kn"&gt;from&lt;/span&gt; &lt;span class="nn"&gt;langchain_openai&lt;/span&gt; &lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;ChatOpenAI&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt; 3&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="kn"&gt;from&lt;/span&gt; &lt;span class="nn"&gt;langchain.agents&lt;/span&gt; &lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;AgentExecutor&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;initialize_agent&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt; 4&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="kn"&gt;from&lt;/span&gt; &lt;span class="nn"&gt;pydantic&lt;/span&gt; &lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;BaseModel&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt; 5&lt;/span&gt;&lt;span class="cl"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt; 6&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="k"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;Weather&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;BaseModel&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt; 7&lt;/span&gt;&lt;span class="cl"&gt; &lt;span class="n"&gt;temperature&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;float&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt; 8&lt;/span&gt;&lt;span class="cl"&gt; &lt;span class="n"&gt;condition&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;str&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt; 9&lt;/span&gt;&lt;span class="cl"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;10&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="n"&gt;llm&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;ChatOpenAI&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;model&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s2"&gt;&amp;#34;gpt-4o-mini&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;11&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="n"&gt;tools&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;get_weather&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;12&lt;/span&gt;&lt;span class="cl"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;13&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="n"&gt;agent&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;initialize_agent&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;tools&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;llm&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;agent&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s2"&gt;&amp;#34;zero-shot-react-description&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;14&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="n"&gt;result&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;agent&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;run&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;&amp;#34;What&amp;#39;s the weather in SF?&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;15&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="c1"&gt;# 再用正则/二次调用或手写解析把 result 转成 Weather(...)&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;v1.0 alpha（新写法）：一个入口 create_agent，在主循环里直接产出结构化结果（避免多一次 LLM 调用、少走弯路）&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-python" data-lang="python"&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt; 1&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="err"&gt;⚡&lt;/span&gt; &lt;span class="n"&gt;python片段from&lt;/span&gt; &lt;span class="n"&gt;langchain&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;agents&lt;/span&gt; &lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="nn"&gt;create_agent&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt; 2&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="kn"&gt;from&lt;/span&gt; &lt;span class="nn"&gt;pydantic&lt;/span&gt; &lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;BaseModel&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt; 3&lt;/span&gt;&lt;span class="cl"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt; 4&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="k"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;Weather&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;BaseModel&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt; 5&lt;/span&gt;&lt;span class="cl"&gt; &lt;span class="n"&gt;temperature&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;float&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt; 6&lt;/span&gt;&lt;span class="cl"&gt; &lt;span class="n"&gt;condition&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;str&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt; 7&lt;/span&gt;&lt;span class="cl"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt; 8&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="n"&gt;agent&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;create_agent&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt; 9&lt;/span&gt;&lt;span class="cl"&gt; &lt;span class="s2"&gt;&amp;#34;openai:gpt-4o-mini&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="c1"&gt;# 也可传入已实例化 model&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;10&lt;/span&gt;&lt;span class="cl"&gt; &lt;span class="n"&gt;tools&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;get_weather&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;11&lt;/span&gt;&lt;span class="cl"&gt; &lt;span class="n"&gt;response_format&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="n"&gt;Weather&lt;/span&gt; &lt;span class="c1"&gt;# 结构化输出内建&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;12&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;13&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="n"&gt;out&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;agent&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;invoke&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;&lt;span class="s2"&gt;&amp;#34;messages&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;[{&lt;/span&gt;&lt;span class="s2"&gt;&amp;#34;role&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;&amp;#34;user&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s2"&gt;&amp;#34;content&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;&amp;#34;Weather in SF?&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;}]})&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;14&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="nb"&gt;print&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;out&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s2"&gt;&amp;#34;structured_response&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;])&lt;/span&gt; &lt;span class="c1"&gt;# -&amp;gt; Weather(...)&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;2.“跨厂商、多模态/推理/引用” 的消息处理&lt;/p&gt;
&lt;p&gt;v0.x：不同厂商字段名不同（如 reasoning、thinking、citations、server tool 等），常见一堆 if provider == &amp;hellip; 的分支。&lt;/p&gt;
&lt;p&gt;v1.0 alpha：直接读 .content_blocks（统一、强类型），必要时再序列化回标准块。&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-fallback" data-lang="fallback"&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;1&lt;/span&gt;&lt;span class="cl"&gt;⚡ python片段from langchain_core.messages import AIMessage
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;2&lt;/span&gt;&lt;span class="cl"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;3&lt;/span&gt;&lt;span class="cl"&gt;msg = some_llm.invoke(&amp;#34;Explain with sources &amp;amp; brief reasoning&amp;#34;)
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;4&lt;/span&gt;&lt;span class="cl"&gt;# v1 统一读取：
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;5&lt;/span&gt;&lt;span class="cl"&gt;for block in msg.content_blocks:
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;6&lt;/span&gt;&lt;span class="cl"&gt; if block[&amp;#34;type&amp;#34;] == &amp;#34;reasoning&amp;#34;:
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;7&lt;/span&gt;&lt;span class="cl"&gt; use_reasoning(block[&amp;#34;reasoning&amp;#34;])
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;8&lt;/span&gt;&lt;span class="cl"&gt; if block[&amp;#34;type&amp;#34;] == &amp;#34;text&amp;#34; and &amp;#34;annotations&amp;#34; in block:
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;9&lt;/span&gt;&lt;span class="cl"&gt; use_citations(block[&amp;#34;annotations&amp;#34;])
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;3.“工具错误/人审/长对话摘要” 的生产级控制&lt;/p&gt;
&lt;p&gt;v0.x：这些能力多靠自写回调或外层控制流拼起来。&lt;/p&gt;
&lt;p&gt;v1.0 alpha：内置 Middleware 三钩子（before&lt;em&gt;model / modify&lt;/em&gt;model&lt;em&gt;request / after&lt;/em&gt;model）+ 现成中间件（摘要、人审、Anthropic Prompt Caching），还能 “跳转 / 中断”。&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-gdscript3" data-lang="gdscript3"&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt; 1&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="err"&gt;⚡&lt;/span&gt; &lt;span class="n"&gt;python片段from&lt;/span&gt; &lt;span class="n"&gt;langchain&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;agents&lt;/span&gt; &lt;span class="n"&gt;import&lt;/span&gt; &lt;span class="n"&gt;create_agent&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt; 2&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="n"&gt;from&lt;/span&gt; &lt;span class="n"&gt;langchain&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;agents&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;middleware&lt;/span&gt; &lt;span class="n"&gt;import&lt;/span&gt; &lt;span class="n"&gt;SummarizationMiddleware&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;HumanInTheLoopMiddleware&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt; 3&lt;/span&gt;&lt;span class="cl"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt; 4&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="n"&gt;agent&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;create_agent&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt; 5&lt;/span&gt;&lt;span class="cl"&gt; &lt;span class="s2"&gt;&amp;#34;openai:gpt-4o&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt; 6&lt;/span&gt;&lt;span class="cl"&gt; &lt;span class="n"&gt;tools&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="o"&gt;...&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt; 7&lt;/span&gt;&lt;span class="cl"&gt; &lt;span class="n"&gt;middleware&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt; 8&lt;/span&gt;&lt;span class="cl"&gt; &lt;span class="n"&gt;SummarizationMiddleware&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;max_tokens_before_summary&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="mi"&gt;4000&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt; 9&lt;/span&gt;&lt;span class="cl"&gt; &lt;span class="n"&gt;HumanInTheLoopMiddleware&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;tool_configs&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="s2"&gt;&amp;#34;write_file&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="s2"&gt;&amp;#34;allow_approve&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;True&lt;/span&gt;&lt;span class="p"&gt;}})&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;10&lt;/span&gt;&lt;span class="cl"&gt; &lt;span class="p"&gt;],&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;11&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;h1 id="langsmith--"&gt;&lt;a href="#langsmith--" class="header-anchor"&gt;&lt;/a&gt;LangSmith 🦜️⚒️
&lt;/h1&gt;&lt;p&gt;●解决 LLM 应用开发中最头疼的调试、追踪和评估问题，这是其商业化的核心（收费）。一个应用越复杂，就越离不开 LangSmith，形成用户粘性。&lt;/p&gt;
&lt;p&gt;●LangSmith = LLM 应用的 “一体化可观测 + 评测平台”：用来给 Agent / RAG / 多模态应用做 全链路追踪（Tracing）、 离 / 在线评测（Evals）、 监控告警、 成本与时延看板、Prompt 协作等；既可配合 LangChain / LangGraph，也支持非 LangChain 项目、OTEL 接入。&lt;/p&gt;
&lt;p&gt;可以开源免费的 langfuse 替代 LangSmith&lt;/p&gt;
&lt;h1 id="langserve-"&gt;&lt;a href="#langserve-" class="header-anchor"&gt;&lt;/a&gt;LangServe 🦜️🏓
&lt;/h1&gt;&lt;p&gt;●打通 “最后一公里”，让开发者能一键将用 LangChain 构建的应用 API 化&lt;/p&gt;
&lt;p&gt;●LangServe 可以把 LangChain 的 Runnable/Chain 一键暴露成 REST API 的开源库，基于 FastAPI + Pydantic，自带 /invoke、/batch、/stream、/stream&lt;em&gt;log、/stream&lt;/em&gt;events 等端点、自动推断 I / O Schema、内置 Playground，并可把追踪接到 LangSmith&lt;/p&gt;
&lt;p&gt;收费吗？&lt;/p&gt;
&lt;p&gt;库本身不收费。但其许可证限制： 不得把 LangServe 作为托管 / 托管式服务提供给第三方（SaaS）。也就是说，你可以用它部署自己的应用，但不能把 “LangServe 平台” 卖给其他公司用。另外，LangChain 官方更推荐新项目用 LangGraph Platform（LangServe 只接受社区 bug 修，不再收新特性）。如果要 “托管式平台”，那是另一条产品线（付费）&lt;/p&gt;
&lt;h1 id="langchain-的使用争议"&gt;&lt;a href="#langchain-%e7%9a%84%e4%bd%bf%e7%94%a8%e4%ba%89%e8%ae%ae" class="header-anchor"&gt;&lt;/a&gt;LangChain 的使用争议
&lt;/h1&gt;&lt;p&gt;关于 LangChain 最开始的时候是一片盛赞，几乎全是正面评价，但随着开发者使用的深入，不断有负面评价出现。&lt;/p&gt;
&lt;p&gt;LangChain 在使用上的主要争议，或者说让开发者后来“抛弃” 它的主要原因有：&lt;/p&gt;
&lt;p&gt;1.API 频繁改、文档滞后，维护成本高&lt;/p&gt;
&lt;p&gt;○2023–2025 年间最常见吐槽：接口 / 导入路径经常变、语义化版本执行不严、文档跟不上，导致线上项目要反复改代码与迁移；社区讨论与帖子里这一点重复被提及。官方后来在 0.2 才引入 “版本化文档”以缓解这个痛点。Reddit&lt;/p&gt;
&lt;p&gt;2.抽象层过重/“抽象泄漏”&lt;/p&gt;
&lt;p&gt;○本来想简化 LLM 应用，但 层层抽象（Chains/Agents/Memory/Tools…） 让简单事变复杂；当跨供应商（OpenAI/Anthropic）或多模态/工具调用时，还是要理解底层差异并写分支， 抽象并未完全 “挡住细节”，很多人因此更倾向 “直接调 API / 自己拼装”。&lt;/p&gt;
&lt;p&gt;3.性能 / 成本开销与 “隐形调用”&lt;/p&gt;
&lt;p&gt;○经验帖里常见：token 使用低效、默认批量/重试/校验带来额外请求与延迟；默认设置偏原型友好，不一定适合生产（缓存、批处理、上下文裁剪都要自己细调）。DEV Community&lt;/p&gt;
&lt;p&gt;4.调试与可观测性难（不用配套工具时）&lt;/p&gt;
&lt;p&gt;○没接追踪时，多层封装中的报错 / 耗时难以定位，“像在雾里调试”；不少团队因此上了自家的可观测或放弃高阶封装。&lt;/p&gt;
&lt;p&gt;5.学习曲线与 “过度框架化”&lt;/p&gt;
&lt;p&gt;○许多工程团队反映：学习成本与收益不匹配，做简单的 RAG / 工作流时，直接 SDK + 少量自写代码更快可控；把 LangChain 当工具库（utility）用反而更顺。&lt;/p&gt;
&lt;p&gt;6.生态分拆、选择困难与 “框架漂移”&lt;/p&gt;
&lt;p&gt;○LangChain/ LangGraph/ LangServe/ LangSmith 产品边界不断演进；多数对比文章建议：流程化任务用 LangChain，复杂状态 / 多轮 agent 用 LangGraph—— 很多团队索性选更贴合场景的替代或 “手写”。DEV Community&lt;/p&gt;
&lt;p&gt;上面这些点，不是说 LangChain “不能用”，而是在规模化、稳定性要求高的团队里，可预期性 / 可控性往往比 “快速堆组件” 更重要。&lt;/p&gt;
&lt;h2 id="负面评价"&gt;&lt;a href="#%e8%b4%9f%e9%9d%a2%e8%af%84%e4%bb%b7" class="header-anchor"&gt;&lt;/a&gt;负面评价
&lt;/h2&gt;&lt;p&gt;这里我引用一些具体的负面评价，通过开发者真实的体验和文章来了解一下 LangChain 的问题。&lt;/p&gt;
&lt;h3 id="为什么我们不再使用-langchain-来构建我们的ai代理"&gt;&lt;a href="#%e4%b8%ba%e4%bb%80%e4%b9%88%e6%88%91%e4%bb%ac%e4%b8%8d%e5%86%8d%e4%bd%bf%e7%94%a8-langchain-%e6%9d%a5%e6%9e%84%e5%bb%ba%e6%88%91%e4%bb%ac%e7%9a%84ai%e4%bb%a3%e7%90%86" class="header-anchor"&gt;&lt;/a&gt;为什么我们不再使用 langchain 来构建我们的AI代理
&lt;/h3&gt;&lt;p&gt;●来源： &lt;a class="link" href="https://octomind.dev/blog/why-we-no-longer-use-langchain-for-building-our-ai-agents" target="_blank" rel="noopener"
 &gt;https://octomind.dev/blog/why-we-no-longer-use-langchain-for-building-our-ai-agents&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;●结论：作者最初喜欢 LangChain 因为其丰富组件和易用性，但后来因其抽象复杂、灵活性差而不推荐。&lt;/p&gt;
&lt;h4 id="初期为何喜欢使用-langchain"&gt;&lt;a href="#%e5%88%9d%e6%9c%9f%e4%b8%ba%e4%bd%95%e5%96%9c%e6%ac%a2%e4%bd%bf%e7%94%a8-langchain" class="header-anchor"&gt;&lt;/a&gt;初期为何喜欢使用 LangChain
&lt;/h4&gt;&lt;p&gt;作者在项目初期选择 LangChain，主要因为它具备以下优点：&lt;/p&gt;
&lt;p&gt;●丰富的工具和组件：LangChain 提供了大量现成的模块，能快速搭建 LLM 应用。&lt;/p&gt;
&lt;p&gt;●易于集成：框架承诺“让开发者一个下午就能从想法变成可运行代码”，适合原型开发和快速试错。&lt;/p&gt;
&lt;p&gt;●人气高、社区活跃：2023 年 LangChain 热度很高，生态完善，容易找到资料和支持。&lt;/p&gt;
&lt;p&gt;这些特性让作者在项目早期能专注于业务逻辑，而不用过多关心底层实现细节。&lt;/p&gt;
&lt;h4 id="后期为何不再推荐-langchain"&gt;&lt;a href="#%e5%90%8e%e6%9c%9f%e4%b8%ba%e4%bd%95%e4%b8%8d%e5%86%8d%e6%8e%a8%e8%8d%90-langchain" class="header-anchor"&gt;&lt;/a&gt;后期为何不再推荐 LangChain
&lt;/h4&gt;&lt;p&gt;随着项目复杂度提升，作者逐渐发现 LangChain 带来的问题：&lt;/p&gt;
&lt;p&gt;1.抽象层级过多，代码复杂&lt;/p&gt;
&lt;p&gt;○LangChain 引入了大量抽象（如 Prompt 模板、输出解析器、链等），让简单任务变得繁琐。&lt;/p&gt;
&lt;p&gt;○代码难以理解和维护，调试时需要花大量时间研究框架内部逻辑。&lt;/p&gt;
&lt;p&gt;2.灵活性不足，难以定制&lt;/p&gt;
&lt;p&gt;○框架对底层细节封装过度，开发者很难根据实际需求修改或扩展功能。&lt;/p&gt;
&lt;p&gt;○当需求超出 LangChain 设计假设时，反而成为限制，必须“将需求转化为适合 LangChain 的方案”，而不是直接实现业务逻辑。&lt;/p&gt;
&lt;p&gt;3.适应快速变化的 AI 领域能力有限&lt;/p&gt;
&lt;p&gt;○AI 和 LLM 领域变化极快，LangChain 的抽象和设计难以跟上新技术和新需求。&lt;/p&gt;
&lt;p&gt;○框架的“嵌套抽象”导致开发者需要理解庞大的堆栈和内部机制，增加了认知负担。&lt;/p&gt;
&lt;p&gt;4.团队协作和维护成本高&lt;/p&gt;
&lt;p&gt;○团队成员需要花大量时间理解和调试 LangChain，而不是专注于功能开发。&lt;/p&gt;
&lt;p&gt;○框架的复杂性让代码维护变得困难，影响开发效率。&lt;/p&gt;
&lt;h4 id="反思与建议"&gt;&lt;a href="#%e5%8f%8d%e6%80%9d%e4%b8%8e%e5%bb%ba%e8%ae%ae" class="header-anchor"&gt;&lt;/a&gt;反思与建议
&lt;/h4&gt;&lt;p&gt;作者认为，虽然 LangChain 在原型阶段有用，但长期来看，直接用基础库（如 OpenAI API）开发更简单、灵活。大多数 LLM 应用只需少量核心组件，无需复杂框架。对于 AI Agent 等复杂场景，建议在 Agent 模式成熟前保持简单，避免过度依赖抽象。&lt;/p&gt;
&lt;p&gt;“一旦我们删除了它，我们就不再需要将我们的需求转化为适合 LangChain 的解决方案。我们只需编写代码即可。”&lt;/p&gt;
&lt;p&gt;作者的经历体现了技术选型中“早期便利 vs. 长期灵活性”的权衡，也反映了 AI 应用开发领域对“抽象与简单”的持续思考。&lt;/p&gt;
&lt;h3 id="2025-年了langchain-还是个无底洞"&gt;&lt;a href="#2025-%e5%b9%b4%e4%ba%86langchain-%e8%bf%98%e6%98%af%e4%b8%aa%e6%97%a0%e5%ba%95%e6%b4%9e" class="header-anchor"&gt;&lt;/a&gt;2025 年了，LangChain 还是个无底洞。
&lt;/h3&gt;&lt;p&gt;&lt;a class="link" href="https://www.reddit.com/r/LocalLLaMA/comments/1iudao8/langchain" target="_blank" rel="noopener"
 &gt;https://www.reddit.com/r/LocalLLaMA/comments/1iudao8/langchain&lt;/a&gt;&lt;em&gt;is&lt;/em&gt;still&lt;em&gt;a&lt;/em&gt;rabbit&lt;em&gt;hole&lt;/em&gt;in_2025/?tl=zh-hans&lt;/p&gt;
&lt;p&gt;&lt;img alt="图片" loading="lazy" sizes="(max-width: 767px) calc(100vw - 30px), (max-width: 1023px) 700px, (max-width: 1279px) 950px, 1232px" src="https://pub-f29bf2b53160470c9a85250116509a24.r2.dev/post/2025-09-23-langchain-shi-yin-tan-hai-shi-ji-shu-zhai/022-d6aace21.png"&gt;&lt;/p&gt;
&lt;h3 id="给研发团队的建议"&gt;&lt;a href="#%e7%bb%99%e7%a0%94%e5%8f%91%e5%9b%a2%e9%98%9f%e7%9a%84%e5%bb%ba%e8%ae%ae" class="header-anchor"&gt;&lt;/a&gt;给研发团队的建议
&lt;/h3&gt;&lt;p&gt;●“轻 LangChain”：保留少量 LCEL/Runnable 原语（或只用部分解析 / 工具封装），核心链路直接用供应商 SDK 实现，减少抽象层。&lt;/p&gt;
&lt;p&gt;●“换轨到 LangGraph”：涉及复杂状态/长对话/人审/错误恢复的 agent，改用图式运行时（LangGraph / 其他工作流框架）。&lt;/p&gt;
&lt;p&gt;●“专用 RAG 框架 / 自研”：检索、重排、结构化输出走 LlamaIndex / 自研管线&lt;/p&gt;
&lt;p&gt;总之，手写代码是一条自主可控的道路，而梭哈 LangChain 可能会是一条不归路。&lt;/p&gt;</description></item><item><title>从初始化一个现代 python 项目中学习到的东西</title><link>https://xiaobox.github.io/p/2025-04-27-cong-chu-shi-hua-yi-ge-xian-dai-python-xiang-mu-zhong-xue-xi/</link><pubDate>Sun, 27 Apr 2025 06:57:05 +0000</pubDate><guid>https://xiaobox.github.io/p/2025-04-27-cong-chu-shi-hua-yi-ge-xian-dai-python-xiang-mu-zhong-xue-xi/</guid><description>&lt;img src="https://pub-f29bf2b53160470c9a85250116509a24.r2.dev/post/2025-04-27-cong-chu-shi-hua-yi-ge-xian-dai-python-xiang-mu-zhong-xue-xi/cover.jpg" alt="Featured image of post 从初始化一个现代 python 项目中学习到的东西" /&gt;&lt;h2 id="uv"&gt;&lt;a href="#uv" class="header-anchor"&gt;&lt;/a&gt;uv
&lt;/h2&gt;&lt;p&gt;我准备用 uv 初始化一个 python 项目&lt;/p&gt;
&lt;h3 id="环境"&gt;&lt;a href="#%e7%8e%af%e5%a2%83" class="header-anchor"&gt;&lt;/a&gt;环境
&lt;/h3&gt;&lt;p&gt;我用的是苹果笔记本 MacBookPro ，具体的操作系统及硬件参数如下：&lt;/p&gt;
&lt;p&gt;&lt;img alt="Image" loading="lazy" sizes="(max-width: 767px) calc(100vw - 30px), (max-width: 1023px) 700px, (max-width: 1279px) 950px, 1232px" src="https://pub-f29bf2b53160470c9a85250116509a24.r2.dev/post/2025-04-27-cong-chu-shi-hua-yi-ge-xian-dai-python-xiang-mu-zhong-xue-xi/001-7b50b558.png"&gt;&lt;/p&gt;
&lt;h3 id="uv-的介绍与安装"&gt;&lt;a href="#uv-%e7%9a%84%e4%bb%8b%e7%bb%8d%e4%b8%8e%e5%ae%89%e8%a3%85" class="header-anchor"&gt;&lt;/a&gt;uv 的介绍与安装
&lt;/h3&gt;
 &lt;blockquote&gt;
 &lt;p&gt;“&lt;/p&gt;
&lt;p&gt;uv 是一个使用 Rust 编写的工具，可以用来替代 pip、pipenv、pipx、poetry、virtualenv 等工具的使用，甚至还可以用来管理系统中所安装的 Python 发行版。uv 借鉴了很多现代语言中对于项目依赖的管理方式，项目中对于依赖的管理要远远优于使用 pip 和requirements.txt的方式。&lt;/p&gt;

 &lt;/blockquote&gt;
&lt;p&gt;我之前用过 pip 、pipx 等工具，发现 uv 确实要快不少。具体有多快呢？ github 上有个图：&lt;/p&gt;
&lt;p&gt;&lt;img alt="Image" loading="lazy" sizes="(max-width: 767px) calc(100vw - 30px), (max-width: 1023px) 700px, (max-width: 1279px) 950px, 1232px" src="https://pub-f29bf2b53160470c9a85250116509a24.r2.dev/post/2025-04-27-cong-chu-shi-hua-yi-ge-xian-dai-python-xiang-mu-zhong-xue-xi/002-23c27ef6.png"&gt;&lt;/p&gt;
&lt;p&gt;🚀速度比传统 pip 快 10-100倍。&lt;/p&gt;
&lt;p&gt;根据官网的介绍，uv 主要支持以下功能：&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;支持版本锁定的项目依赖管理。&lt;/li&gt;
&lt;li&gt;支持直接运行 Python 脚本。&lt;/li&gt;
&lt;li&gt;支持对系统中安装的 Python 进行管理，支持多版本 Python 共存。&lt;/li&gt;
&lt;li&gt;支持 Python 包的发布和安装。&lt;/li&gt;
&lt;li&gt;支持兼容 pip 的应用接口。&lt;/li&gt;
&lt;li&gt;支持 Cargo 模式的项目工作区管理。&lt;/li&gt;
&lt;li&gt;更优化的全局支持库缓存。&lt;/li&gt;
&lt;li&gt;运行无需 Rust 或者 Python 支持。&lt;/li&gt;
&lt;li&gt;支持 Windows、macOS 和 Linux 系统&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;uv 对多 python 版本和环境的管理很不错，这样你就可以一个项目指定一个特定的 Python 版本，放心使用，想怎么折腾怎么折腾，不会影响全局。&lt;/p&gt;
&lt;p&gt;最近比较火的 MCP 很多也是用 uv 运行的，因为用 uv 命令可以直接运行 python 脚本。&lt;/p&gt;
&lt;p&gt;uv 的安装非常简单：&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-bash" data-lang="bash"&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;1&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="c1"&gt;# macOS和Linux&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;2&lt;/span&gt;&lt;span class="cl"&gt;curl -LsSf https://astral.sh/uv/install.sh &lt;span class="p"&gt;|&lt;/span&gt; sh
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;3&lt;/span&gt;&lt;span class="cl"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;4&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="c1"&gt;# Windows PowerShell&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;5&lt;/span&gt;&lt;span class="cl"&gt;powershell -ExecutionPolicy ByPass -c &lt;span class="s2"&gt;&amp;#34;irm https://astral.sh/uv/install.ps1 | iex&amp;#34;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;h3 id="uv-对-python-的环境管理"&gt;&lt;a href="#uv-%e5%af%b9-python-%e7%9a%84%e7%8e%af%e5%a2%83%e7%ae%a1%e7%90%86" class="header-anchor"&gt;&lt;/a&gt;uv 对 Python 的环境管理
&lt;/h3&gt;&lt;p&gt;首先用 uv 管理一下我们本机安装的 Python 环境。即到底安装了几个、哪些版本的 python。&lt;/p&gt;
&lt;p&gt;可以用 &lt;code&gt;uv python list&lt;/code&gt; 查看，像这样：&lt;/p&gt;
&lt;p&gt;&lt;img alt="Image" loading="lazy" sizes="(max-width: 767px) calc(100vw - 30px), (max-width: 1023px) 700px, (max-width: 1279px) 950px, 1232px" src="https://pub-f29bf2b53160470c9a85250116509a24.r2.dev/post/2025-04-27-cong-chu-shi-hua-yi-ge-xian-dai-python-xiang-mu-zhong-xue-xi/003-cd779d4c.png"&gt;&lt;/p&gt;
&lt;p&gt;可以看到我已经安装了多个版本的 python。 在后面建项目的时候，我选用 3.13 这个版本。当然你也可以根据你的情况下载新的需要使用的版本。这里给出一组相关命令：&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-fallback" data-lang="fallback"&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;1&lt;/span&gt;&lt;span class="cl"&gt;uv python install，安装指定版本的 Python。
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;2&lt;/span&gt;&lt;span class="cl"&gt;uv python list，列出系统中当前已经安装的 Python 版本。
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;3&lt;/span&gt;&lt;span class="cl"&gt;uv python find，查找一个已经安装的 Python 版本。
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;4&lt;/span&gt;&lt;span class="cl"&gt;uv python pin，固定当前项目使用指定的 Python 版本。
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;5&lt;/span&gt;&lt;span class="cl"&gt;uv python uninstall，卸载指定版本的 Python。
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;比如我要安装 3.12 这个版本，我就可以这样：&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-fallback" data-lang="fallback"&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;1&lt;/span&gt;&lt;span class="cl"&gt;uv python install 3.13
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;装好了不想要了，就可以这样卸载掉它：&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-fallback" data-lang="fallback"&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;1&lt;/span&gt;&lt;span class="cl"&gt;uv python uninstall 3.13 
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;h3 id="uv-进行项目管理"&gt;&lt;a href="#uv-%e8%bf%9b%e8%a1%8c%e9%a1%b9%e7%9b%ae%e7%ae%a1%e7%90%86" class="header-anchor"&gt;&lt;/a&gt;uv 进行项目管理
&lt;/h3&gt;&lt;p&gt;python 的环境有了以后，我们就可以新建项目了，建项目的时候也要用 uv 来进行初始化。&lt;/p&gt;

 &lt;blockquote&gt;
 &lt;p&gt;“&lt;/p&gt;
&lt;p&gt;uv 的项目管理功能更多的借鉴了 Rust 中 Cargo 工具的项目管理理念。但主要区别是 uv 是通过项目目录中的pyproject.toml文件来完成项目管理的。&lt;/p&gt;

 &lt;/blockquote&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-fallback" data-lang="fallback"&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;1&lt;/span&gt;&lt;span class="cl"&gt;uv init myproject
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;初始化后会生成以下几个文件 ：&lt;/p&gt;
&lt;p&gt;&lt;img alt="Image" loading="lazy" sizes="(max-width: 767px) calc(100vw - 30px), (max-width: 1023px) 700px, (max-width: 1279px) 950px, 1232px" src="https://pub-f29bf2b53160470c9a85250116509a24.r2.dev/post/2025-04-27-cong-chu-shi-hua-yi-ge-xian-dai-python-xiang-mu-zhong-xue-xi/004-32d38507.png"&gt;&lt;/p&gt;
&lt;p&gt;虽然 uv init myproject 会帮你创建项目目录和 pyproject.toml，但默认 不会自动创建虚拟环境（env），所以我们需要手动创建。&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-fallback" data-lang="fallback"&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;1&lt;/span&gt;&lt;span class="cl"&gt;# 手动创建虚拟环境
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;2&lt;/span&gt;&lt;span class="cl"&gt;uv venv --python 3.13
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;3&lt;/span&gt;&lt;span class="cl"&gt;# 激活虚拟环境
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;4&lt;/span&gt;&lt;span class="cl"&gt;source .venv/bin/activate
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;虚拟环境激活后，项目中会多一个.venv 文件夹。&lt;/p&gt;
&lt;p&gt;&lt;img alt="Image" loading="lazy" sizes="(max-width: 767px) calc(100vw - 30px), (max-width: 1023px) 700px, (max-width: 1279px) 950px, 1232px" src="https://pub-f29bf2b53160470c9a85250116509a24.r2.dev/post/2025-04-27-cong-chu-shi-hua-yi-ge-xian-dai-python-xiang-mu-zhong-xue-xi/005-3a39005f.png"&gt;&lt;/p&gt;
&lt;p&gt;接下来我们要自己创建一下源码目录和测试目录：&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-fallback" data-lang="fallback"&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;1&lt;/span&gt;&lt;span class="cl"&gt;mkdir -p src tests
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;到这里工程的相关目录我们就先到此为止，基本上创建完了，然后我们来编辑&lt;/p&gt;
&lt;p&gt;pyproject.toml 配置文件。&lt;/p&gt;
&lt;h3 id="toml-配置文件"&gt;&lt;a href="#toml-%e9%85%8d%e7%bd%ae%e6%96%87%e4%bb%b6" class="header-anchor"&gt;&lt;/a&gt;toml 配置文件
&lt;/h3&gt;&lt;p&gt;我们先介绍一下 toml 文件，可能有些朋友不怎么了解它，比如搞 java 开发的。&lt;/p&gt;
&lt;p&gt;TOML（Tom&amp;rsquo;s Obvious, Minimal Language）是一种配置文件格式，设计目标是&lt;strong&gt;易读、易写、易于解析&lt;/strong&gt;，非常适合作为程序的配置语言，尤其是在现代的跨平台开发中被广泛采用。&lt;/p&gt;
&lt;p&gt;你看这名字是不是觉得肯定跟 Tom 大哥有关系？&lt;/p&gt;
&lt;p&gt;对，因为 TOML 由 GitHub 联合创始人 Tom Preston-Werner 在 2013 年发起，用以替代 JSON、INI 等配置格式在可读性和灵活性上的不足。&lt;/p&gt;
&lt;p&gt;不过吧，后来这大哥（和她媳妇）不在 GitHub 干了，因为他们的一些不光彩的行为。具体是什么就不多说了，想八卦一下的可以去查查。&lt;/p&gt;
&lt;p&gt;toml 配置文件用途广泛，常用于以下场景：&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;应用程序运行时配置&lt;/li&gt;
&lt;li&gt;包管理工具（如 Python 的 pyproject.toml、Rust 的 Cargo.toml）&lt;/li&gt;
&lt;li&gt;构建工具配置（如 poetry.toml, uv.toml）&lt;/li&gt;
&lt;li&gt;数据库或服务连接信息等环境参数配置&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;举个例子吧：&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-fallback" data-lang="fallback"&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt; 1&lt;/span&gt;&lt;span class="cl"&gt;# 数据库配置
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt; 2&lt;/span&gt;&lt;span class="cl"&gt;[database]
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt; 3&lt;/span&gt;&lt;span class="cl"&gt;server = &amp;#34;192.168.1.1&amp;#34;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt; 4&lt;/span&gt;&lt;span class="cl"&gt;ports = [ 8001, 8001, 8002 ]
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt; 5&lt;/span&gt;&lt;span class="cl"&gt;enabled = true
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt; 6&lt;/span&gt;&lt;span class="cl"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt; 7&lt;/span&gt;&lt;span class="cl"&gt;# 应用信息
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt; 8&lt;/span&gt;&lt;span class="cl"&gt;[app]
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt; 9&lt;/span&gt;&lt;span class="cl"&gt;name = &amp;#34;MyApp&amp;#34;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;10&lt;/span&gt;&lt;span class="cl"&gt;version = &amp;#34;1.0.0&amp;#34;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;11&lt;/span&gt;&lt;span class="cl"&gt;release_date = 2025-04-25T12:00:00Z
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;TOML 的特点可以总结为：&lt;/p&gt;

 &lt;blockquote&gt;
 &lt;p&gt;“&lt;/p&gt;
&lt;p&gt;“比 JSON 更适合人读，比 YAML 更适合程序解析。”&lt;/p&gt;

 &lt;/blockquote&gt;
&lt;p&gt;它已经成为现代软件开发中最流行的配置文件格式之一，特别是在需要 &lt;strong&gt;清晰结构 + 丰富类型 + 可维护性&lt;/strong&gt; 的场景中表现出色。&lt;/p&gt;
&lt;p&gt;常见语言的支持情况：&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Python：tomli / toml / pytoml / tomllib（Python 3.11 原生支持）&lt;/li&gt;
&lt;li&gt;Rust：官方包管理工具 Cargo 就使用 TOML 格式的 Cargo.toml&lt;/li&gt;
&lt;li&gt;Go：支持 BurntSushi/toml 库&lt;/li&gt;
&lt;li&gt;Node.js：支持 @iarna/toml 等多个库&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;常见用途：&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Python 包管理：pyproject.toml（PEP 518 标准）&lt;/li&gt;
&lt;li&gt;Rust 项目管理：Cargo.toml&lt;/li&gt;
&lt;li&gt;Web 项目配置：netlify.toml&lt;/li&gt;
&lt;li&gt;DevOps 工具：例如 uv 的配置也是用 toml 文件&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;TOML 与其他格式的对比：&lt;/p&gt;
&lt;table&gt;
 &lt;thead&gt;
 &lt;tr&gt;
 &lt;th&gt;特性&lt;/th&gt;
 &lt;th&gt;TOML&lt;/th&gt;
 &lt;th&gt;JSON&lt;/th&gt;
 &lt;th&gt;YAML&lt;/th&gt;
 &lt;th&gt;INI&lt;/th&gt;
 &lt;/tr&gt;
 &lt;/thead&gt;
 &lt;tbody&gt;
 &lt;tr&gt;
 &lt;td&gt;可读性&lt;/td&gt;
 &lt;td&gt;✅ 高&lt;/td&gt;
 &lt;td&gt;中&lt;/td&gt;
 &lt;td&gt;中高（但复杂）&lt;/td&gt;
 &lt;td&gt;中&lt;/td&gt;
 &lt;/tr&gt;
 &lt;tr&gt;
 &lt;td&gt;注释支持&lt;/td&gt;
 &lt;td&gt;✅ 支持&lt;/td&gt;
 &lt;td&gt;❌ 不支持&lt;/td&gt;
 &lt;td&gt;✅ 支持&lt;/td&gt;
 &lt;td&gt;✅ 支持&lt;/td&gt;
 &lt;/tr&gt;
 &lt;tr&gt;
 &lt;td&gt;数据类型支持&lt;/td&gt;
 &lt;td&gt;✅ 多&lt;/td&gt;
 &lt;td&gt;✅ 多&lt;/td&gt;
 &lt;td&gt;✅ 多&lt;/td&gt;
 &lt;td&gt;❌ 有限&lt;/td&gt;
 &lt;/tr&gt;
 &lt;tr&gt;
 &lt;td&gt;库支持&lt;/td&gt;
 &lt;td&gt;✅ 常见语言皆支持&lt;/td&gt;
 &lt;td&gt;✅ 全面&lt;/td&gt;
 &lt;td&gt;✅ 全面&lt;/td&gt;
 &lt;td&gt;✅ 较好&lt;/td&gt;
 &lt;/tr&gt;
 &lt;tr&gt;
 &lt;td&gt;学习曲线&lt;/td&gt;
 &lt;td&gt;✅ 低&lt;/td&gt;
 &lt;td&gt;✅ 低&lt;/td&gt;
 &lt;td&gt;❌ 偏高&lt;/td&gt;
 &lt;td&gt;✅ 极低&lt;/td&gt;
 &lt;/tr&gt;
 &lt;/tbody&gt;
&lt;/table&gt;
&lt;p&gt;你看，TOML 作为配置文件感觉很不错对吧。&lt;/p&gt;
&lt;p&gt;我们关于 TOML 的介绍就到此为止，现在来说一下我们这个初始化的新项目中的 pyproject.toml 文件要写成什么样。&lt;/p&gt;
&lt;p&gt;就这样：&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-gdscript3" data-lang="gdscript3"&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt; 1&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;build&lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="n"&gt;system&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt; 2&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="n"&gt;requires&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s2"&gt;&amp;#34;hatchling&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt; 3&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="n"&gt;build&lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="n"&gt;backend&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;&amp;#34;hatchling.build&amp;#34;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt; 4&lt;/span&gt;&lt;span class="cl"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt; 5&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;project&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt; 6&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="n"&gt;name&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;&amp;#34;myproject&amp;#34;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt; 7&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="n"&gt;version&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;&amp;#34;0.1.0&amp;#34;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt; 8&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="n"&gt;description&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;&amp;#34;一个基于Python 3.13.3的项目&amp;#34;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt; 9&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="n"&gt;readme&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;&amp;#34;README.md&amp;#34;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;10&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="n"&gt;requires&lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="n"&gt;python&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;&amp;#34;&amp;gt;=3.13&amp;#34;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;11&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="n"&gt;authors&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;12&lt;/span&gt;&lt;span class="cl"&gt; &lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="n"&gt;name&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;&amp;#34;xiaobox&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;email&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;&amp;#34;xiaobox@gmail.com&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;13&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="p"&gt;]&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;14&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="n"&gt;dependencies&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;15&lt;/span&gt;&lt;span class="cl"&gt; &lt;span class="s2"&gt;&amp;#34;pytest&amp;gt;=7.4.3&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;16&lt;/span&gt;&lt;span class="cl"&gt; &lt;span class="s2"&gt;&amp;#34;fastapi&amp;gt;=0.110.0&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;17&lt;/span&gt;&lt;span class="cl"&gt; &lt;span class="s2"&gt;&amp;#34;uvicorn&amp;gt;=0.27.0&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;18&lt;/span&gt;&lt;span class="cl"&gt; &lt;span class="s2"&gt;&amp;#34;httpx&amp;gt;=0.27.0&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;19&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="p"&gt;]&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;20&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="n"&gt;classifiers&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;21&lt;/span&gt;&lt;span class="cl"&gt; &lt;span class="s2"&gt;&amp;#34;Programming Language :: Python :: 3.13&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;22&lt;/span&gt;&lt;span class="cl"&gt; &lt;span class="s2"&gt;&amp;#34;License :: OSI Approved :: MIT License&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;23&lt;/span&gt;&lt;span class="cl"&gt; &lt;span class="s2"&gt;&amp;#34;Operating System :: OS Independent&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;24&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="p"&gt;]&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;25&lt;/span&gt;&lt;span class="cl"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;26&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;project&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;scripts&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;27&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="n"&gt;myproject&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;&amp;#34;src.main:main&amp;#34;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;28&lt;/span&gt;&lt;span class="cl"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;29&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;project&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;urls&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;30&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="s2"&gt;&amp;#34;Homepage&amp;#34;&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;&amp;#34;https://github.com/yourusername/myproject&amp;#34;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;31&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="s2"&gt;&amp;#34;Bug Tracker&amp;#34;&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;&amp;#34;https://github.com/yourusername/myproject/issues&amp;#34;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;32&lt;/span&gt;&lt;span class="cl"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;33&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;project&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;optional&lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="n"&gt;dependencies&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;34&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="n"&gt;dev&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;35&lt;/span&gt;&lt;span class="cl"&gt; &lt;span class="s2"&gt;&amp;#34;black&amp;gt;=23.1.0&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;36&lt;/span&gt;&lt;span class="cl"&gt; &lt;span class="s2"&gt;&amp;#34;isort&amp;gt;=5.12.0&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;37&lt;/span&gt;&lt;span class="cl"&gt; &lt;span class="s2"&gt;&amp;#34;mypy&amp;gt;=1.5.1&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;38&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="p"&gt;]&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;39&lt;/span&gt;&lt;span class="cl"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;40&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="k"&gt;tool&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;pytest&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;41&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="n"&gt;testpaths&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s2"&gt;&amp;#34;tests&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;42&lt;/span&gt;&lt;span class="cl"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;43&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="k"&gt;tool&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;black&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;44&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="n"&gt;line&lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="n"&gt;length&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;88&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;45&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="n"&gt;target&lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="n"&gt;version&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s2"&gt;&amp;#34;py313&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;46&lt;/span&gt;&lt;span class="cl"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;47&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="k"&gt;tool&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;isort&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;48&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="n"&gt;profile&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;&amp;#34;black&amp;#34;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;49&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="n"&gt;line_length&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;88&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;50&lt;/span&gt;&lt;span class="cl"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;51&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="k"&gt;tool&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;hatch&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;build&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;targets&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;wheel&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;52&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="n"&gt;packages&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s2"&gt;&amp;#34;src&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; 
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;别小看了这个文件，它可是一个使用了 Hatch 构建工具、遵循 PEP 621 和现代 Python 项目结构规范的项目配置，涵盖了运行依赖、开发依赖、CLI 脚本、格式化工具配置、测试路径和打包目标，非常完整规范。&lt;/p&gt;
&lt;p&gt;所以我们得逐行解释一下这个重要的文件。&lt;/p&gt;
&lt;h3 id="toml-配置文件的逐行解释"&gt;&lt;a href="#toml-%e9%85%8d%e7%bd%ae%e6%96%87%e4%bb%b6%e7%9a%84%e9%80%90%e8%a1%8c%e8%a7%a3%e9%87%8a" class="header-anchor"&gt;&lt;/a&gt;toml 配置文件的逐行解释
&lt;/h3&gt;&lt;p&gt;我们上面的配置文件是一个标准的 Python 项目使用 &lt;code&gt;pyproject.toml&lt;/code&gt; 来管理构建系统、依赖、工具配置的典型示例。下面我们来拆解和解释一下。&lt;/p&gt;
&lt;hr&gt;
&lt;p&gt;✅ &lt;code&gt;[build-system]&lt;/code&gt;：构建系统配置（PEP 517 标准）&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-fallback" data-lang="fallback"&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;1&lt;/span&gt;&lt;span class="cl"&gt;[build-system]
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;2&lt;/span&gt;&lt;span class="cl"&gt;requires = [&amp;#34;hatchling&amp;#34;]
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;3&lt;/span&gt;&lt;span class="cl"&gt;build-backend = &amp;#34;hatchling.build&amp;#34;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;ul&gt;
&lt;li&gt;&lt;code&gt;requires&lt;/code&gt;：构建该项目所需的构建工具，这里是 &lt;code&gt;hatchling&lt;/code&gt;，必须先安装。&lt;/li&gt;
&lt;li&gt;&lt;code&gt;build-backend&lt;/code&gt;：指定用哪个构建后端来执行打包任务，这里是 &lt;code&gt;hatchling.build&lt;/code&gt;。&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;hatchling 有点儿类似 java 中的 Maven 或 Gradle，都是用来执行自动化构建流程的。&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Maven 是把 java 代码编译、构建成 jar 包，方便管理依赖、分发、版本控制&lt;/li&gt;
&lt;li&gt;hatchling 是把 python 代码构建成 Wheel（.whl 文件）或 Source Distribution（.tar.gz 或 .zip 文件），也是为了做依赖管理、分发和版本控制。&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;总结来说：&lt;strong&gt;Python 的构建是将代码和依赖打包成 .whl 或 .tar.gz，类似于 Java 打包成 .jar。核心目的是简化分发、确保环境一致性、自动化依赖管理。&lt;/strong&gt;&lt;/p&gt;
&lt;hr&gt;
&lt;p&gt;✅ &lt;code&gt;[project]&lt;/code&gt;：项目的核心元信息（PEP 621 标准）&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-fallback" data-lang="fallback"&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;1&lt;/span&gt;&lt;span class="cl"&gt;[project]
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;2&lt;/span&gt;&lt;span class="cl"&gt;name = &amp;#34;myproject&amp;#34;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;ul&gt;
&lt;li&gt;项目名称，最终发布到 PyPI 时会用这个名字。&lt;/li&gt;
&lt;/ul&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-fallback" data-lang="fallback"&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;1&lt;/span&gt;&lt;span class="cl"&gt;version = &amp;#34;0.1.0&amp;#34;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;ul&gt;
&lt;li&gt;当前版本号。&lt;/li&gt;
&lt;/ul&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-fallback" data-lang="fallback"&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;1&lt;/span&gt;&lt;span class="cl"&gt;description = &amp;#34;一个基于Python 3.13.3的项目&amp;#34;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;ul&gt;
&lt;li&gt;简短的项目说明。&lt;/li&gt;
&lt;/ul&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-fallback" data-lang="fallback"&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;1&lt;/span&gt;&lt;span class="cl"&gt;readme = &amp;#34;README.md&amp;#34;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;ul&gt;
&lt;li&gt;指定项目的 README 文件，将作为 PyPI 上项目首页的介绍内容。&lt;/li&gt;
&lt;/ul&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-fallback" data-lang="fallback"&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;1&lt;/span&gt;&lt;span class="cl"&gt;requires-python = &amp;#34;&amp;gt;=3.13&amp;#34;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;ul&gt;
&lt;li&gt;要求的 Python 版本最低为 3.13。&lt;/li&gt;
&lt;/ul&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-fallback" data-lang="fallback"&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;1&lt;/span&gt;&lt;span class="cl"&gt;authors = [
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;2&lt;/span&gt;&lt;span class="cl"&gt; {name = &amp;#34;xiaobox&amp;#34;, email = &amp;#34;xiaobox@gmail.com&amp;#34;}
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;3&lt;/span&gt;&lt;span class="cl"&gt;]
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;ul&gt;
&lt;li&gt;作者信息，支持多个，用列表表示。&lt;/li&gt;
&lt;/ul&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-fallback" data-lang="fallback"&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;1&lt;/span&gt;&lt;span class="cl"&gt;dependencies = [
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;2&lt;/span&gt;&lt;span class="cl"&gt; &amp;#34;pytest&amp;gt;=7.4.3&amp;#34;,
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;3&lt;/span&gt;&lt;span class="cl"&gt; &amp;#34;fastapi&amp;gt;=0.110.0&amp;#34;,
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;4&lt;/span&gt;&lt;span class="cl"&gt; &amp;#34;uvicorn&amp;gt;=0.27.0&amp;#34;,
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;5&lt;/span&gt;&lt;span class="cl"&gt; &amp;#34;httpx&amp;gt;=0.27.0&amp;#34;,
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;6&lt;/span&gt;&lt;span class="cl"&gt;]
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;ul&gt;
&lt;li&gt;项目的运行时依赖库，在安装时会自动安装这些包。这里我加入了 pytest、&lt;strong&gt;fastapi&lt;/strong&gt; 的依赖，因为我想把这个项目作为一个 api 服务提供出去。&lt;/li&gt;
&lt;/ul&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-fallback" data-lang="fallback"&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;1&lt;/span&gt;&lt;span class="cl"&gt;classifiers = [
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;2&lt;/span&gt;&lt;span class="cl"&gt; &amp;#34;Programming Language :: Python :: 3.13&amp;#34;,
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;3&lt;/span&gt;&lt;span class="cl"&gt; &amp;#34;License :: OSI Approved :: MIT License&amp;#34;,
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;4&lt;/span&gt;&lt;span class="cl"&gt; &amp;#34;Operating System :: OS Independent&amp;#34;,
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;5&lt;/span&gt;&lt;span class="cl"&gt;]
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;ul&gt;
&lt;li&gt;用于 PyPI 分类（帮助搜索和筛选）。&lt;/li&gt;
&lt;/ul&gt;
&lt;hr&gt;
&lt;h3 id="-projectscripts定义可执行命令如-cli"&gt;&lt;a href="#-projectscripts%e5%ae%9a%e4%b9%89%e5%8f%af%e6%89%a7%e8%a1%8c%e5%91%bd%e4%bb%a4%e5%a6%82-cli" class="header-anchor"&gt;&lt;/a&gt;✅ &lt;code&gt;[project.scripts]&lt;/code&gt;：定义可执行命令（如 CLI）
&lt;/h3&gt;&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-fallback" data-lang="fallback"&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;1&lt;/span&gt;&lt;span class="cl"&gt;[project.scripts]
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;2&lt;/span&gt;&lt;span class="cl"&gt;myproject = &amp;#34;src.main:main&amp;#34;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;ul&gt;
&lt;li&gt;安装后运行 &lt;code&gt;myproject&lt;/code&gt; 命令会调用 &lt;code&gt;src/main.py&lt;/code&gt; 中的 &lt;code&gt;main()&lt;/code&gt; 函数。（我们需要提前把之前的 main.py 文件要先移动到 /src 目录下）&lt;/li&gt;
&lt;/ul&gt;
&lt;hr&gt;
&lt;h3 id="-projecturls项目的相关链接非必须"&gt;&lt;a href="#-projecturls%e9%a1%b9%e7%9b%ae%e7%9a%84%e7%9b%b8%e5%85%b3%e9%93%be%e6%8e%a5%e9%9d%9e%e5%bf%85%e9%a1%bb" class="header-anchor"&gt;&lt;/a&gt;✅ &lt;code&gt;[project.urls]&lt;/code&gt;：项目的相关链接（非必须）
&lt;/h3&gt;&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-fallback" data-lang="fallback"&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;1&lt;/span&gt;&lt;span class="cl"&gt;[project.urls]
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;2&lt;/span&gt;&lt;span class="cl"&gt;&amp;#34;Homepage&amp;#34; = &amp;#34;https://github.com/yourusername/myproject&amp;#34;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;3&lt;/span&gt;&lt;span class="cl"&gt;&amp;#34;Bug Tracker&amp;#34; = &amp;#34;https://github.com/yourusername/myproject/issues&amp;#34;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;ul&gt;
&lt;li&gt;为项目指定一些有用的链接，如主页、问题反馈页等。&lt;/li&gt;
&lt;/ul&gt;
&lt;hr&gt;
&lt;h3 id="-projectoptional-dependencies可选依赖比如开发环境"&gt;&lt;a href="#-projectoptional-dependencies%e5%8f%af%e9%80%89%e4%be%9d%e8%b5%96%e6%af%94%e5%a6%82%e5%bc%80%e5%8f%91%e7%8e%af%e5%a2%83" class="header-anchor"&gt;&lt;/a&gt;✅ &lt;code&gt;[project.optional-dependencies]&lt;/code&gt;：可选依赖（比如开发环境）
&lt;/h3&gt;&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-fallback" data-lang="fallback"&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;1&lt;/span&gt;&lt;span class="cl"&gt;[project.optional-dependencies]
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;2&lt;/span&gt;&lt;span class="cl"&gt;dev = [
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;3&lt;/span&gt;&lt;span class="cl"&gt; &amp;#34;black&amp;gt;=23.1.0&amp;#34;,
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;4&lt;/span&gt;&lt;span class="cl"&gt; &amp;#34;isort&amp;gt;=5.12.0&amp;#34;,
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;5&lt;/span&gt;&lt;span class="cl"&gt; &amp;#34;mypy&amp;gt;=1.5.1&amp;#34;,
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;6&lt;/span&gt;&lt;span class="cl"&gt;]
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;我们为开发环境安装了三个库：black、isort 和 mypy&lt;/p&gt;
&lt;p&gt;介绍一下这三个工具&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;
&lt;p&gt;black：是一个 Python 代码格式化工具。自动把你的 Python 代码排版成统一风格，比如：缩进、换行、空格都按标准格式处理，让你的 Python 代码看起来更整齐、统一，无需自己动手排版。&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;isort：是一个 Python 导入（import）语句自动排序工具。自动整理文件顶部的 import 语句，比如按字母顺序排列，分组标准库、第三方库、自定义模块，保持导入部分有序且规范。&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;mypy：是一个 Python 静态类型检查工具。检查你的代码里的类型注解（type hints）是不是正确，比如函数参数和返回值类型对不对，帮你在写代码时发现类型出错的地方，提前避免 bug。&lt;/p&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;hr&gt;
&lt;h3 id="-toolpytestpytest-配置"&gt;&lt;a href="#-toolpytestpytest-%e9%85%8d%e7%bd%ae" class="header-anchor"&gt;&lt;/a&gt;✅ &lt;code&gt;[tool.pytest]&lt;/code&gt;：Pytest 配置
&lt;/h3&gt;&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-gdscript3" data-lang="gdscript3"&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;1&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="k"&gt;tool&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;pytest&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;2&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="n"&gt;testpaths&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s2"&gt;&amp;#34;tests&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;ul&gt;
&lt;li&gt;指定测试用例所在路径，&lt;code&gt;pytest&lt;/code&gt; 会从 &lt;code&gt;tests/&lt;/code&gt; 目录开始查找测试文件。&lt;/li&gt;
&lt;/ul&gt;
&lt;hr&gt;
&lt;h3 id="-toolblack代码格式化工具-black-的配置"&gt;&lt;a href="#-toolblack%e4%bb%a3%e7%a0%81%e6%a0%bc%e5%bc%8f%e5%8c%96%e5%b7%a5%e5%85%b7-black-%e7%9a%84%e9%85%8d%e7%bd%ae" class="header-anchor"&gt;&lt;/a&gt;✅ &lt;code&gt;[tool.black]&lt;/code&gt;：代码格式化工具 Black 的配置
&lt;/h3&gt;&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-gdscript3" data-lang="gdscript3"&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;1&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="k"&gt;tool&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;black&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;2&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="n"&gt;line&lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="n"&gt;length&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;88&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;3&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="n"&gt;target&lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="n"&gt;version&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s2"&gt;&amp;#34;py313&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;ul&gt;
&lt;li&gt;设置代码的行最大长度为 88（默认值），目标 Python 版本是 3.13。&lt;/li&gt;
&lt;/ul&gt;
&lt;hr&gt;
&lt;h3 id="-toolisortimport-排序工具-isort-的配置"&gt;&lt;a href="#-toolisortimport-%e6%8e%92%e5%ba%8f%e5%b7%a5%e5%85%b7-isort-%e7%9a%84%e9%85%8d%e7%bd%ae" class="header-anchor"&gt;&lt;/a&gt;✅ &lt;code&gt;[tool.isort]&lt;/code&gt;：import 排序工具 isort 的配置
&lt;/h3&gt;&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-gdscript3" data-lang="gdscript3"&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;1&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="k"&gt;tool&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;isort&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;2&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="n"&gt;profile&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;&amp;#34;black&amp;#34;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;3&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="n"&gt;line_length&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;88&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;ul&gt;
&lt;li&gt;使用 &lt;code&gt;black&lt;/code&gt; 的风格对 import 排序。&lt;/li&gt;
&lt;li&gt;设置行长度为 88，与 black 保持一致。&lt;/li&gt;
&lt;/ul&gt;
&lt;hr&gt;
&lt;h3 id="-toolhatchbuildtargetswheelhatchling-打包配置"&gt;&lt;a href="#-toolhatchbuildtargetswheelhatchling-%e6%89%93%e5%8c%85%e9%85%8d%e7%bd%ae" class="header-anchor"&gt;&lt;/a&gt;✅ &lt;code&gt;[tool.hatch.build.targets.wheel]&lt;/code&gt;：Hatchling 打包配置
&lt;/h3&gt;&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-gdscript3" data-lang="gdscript3"&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;1&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="k"&gt;tool&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;hatch&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;build&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;targets&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;wheel&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;2&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="n"&gt;packages&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s2"&gt;&amp;#34;src&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;ul&gt;
&lt;li&gt;指定打包时要包含的代码目录为 &lt;code&gt;src&lt;/code&gt;。&lt;/li&gt;
&lt;/ul&gt;
&lt;hr&gt;
&lt;p&gt;用一句话总结下这个 &lt;code&gt;pyproject.toml&lt;/code&gt; 配置文件 ：&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;“这是一个使用 Hatch 构建工具、遵循 PEP 621 和现代 Python 项目结构规范的项目配置，涵盖了运行依赖、开发依赖、CLI 脚本、格式化工具配置、测试路径和打包目标，非常完整规范。”&lt;/strong&gt;&lt;/p&gt;
&lt;h3 id="安装和更新依赖"&gt;&lt;a href="#%e5%ae%89%e8%a3%85%e5%92%8c%e6%9b%b4%e6%96%b0%e4%be%9d%e8%b5%96" class="header-anchor"&gt;&lt;/a&gt;安装和更新依赖
&lt;/h3&gt;&lt;p&gt;上面这个文件编辑完成后，我们就可以 安装项目和开发依赖了：&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-fallback" data-lang="fallback"&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;1&lt;/span&gt;&lt;span class="cl"&gt;uv pip install -e &amp;#34;.[dev]&amp;#34;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;如果后面你更新了 &lt;code&gt;pyproject.toml&lt;/code&gt; 文件可以执行以下命令来 “手动刷新” 一个依赖库：&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-fallback" data-lang="fallback"&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;1&lt;/span&gt;&lt;span class="cl"&gt;uv sync --extra dev 
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;加入 &lt;code&gt;--extra dev&lt;/code&gt; 参数是因为 &lt;code&gt;uv sync&lt;/code&gt; 默认只安装 [project.dependencies] 中列出的正式依赖。&lt;/p&gt;
&lt;p&gt;不会自动安装 [project.optional-dependencies]（比如 dev 里面的 black、isort、mypy）&lt;/p&gt;
&lt;p&gt;&lt;code&gt;uv sync --extra dev&lt;/code&gt; 的意思是：除了正式依赖，还要把 [project.optional-dependencies.dev] 里的东西也同步上&lt;/p&gt;
&lt;h3 id="uvlock"&gt;&lt;a href="#uvlock" class="header-anchor"&gt;&lt;/a&gt;uv.lock
&lt;/h3&gt;&lt;p&gt;当执行完 &lt;code&gt;uv sync --extra dev&lt;/code&gt; ，安装好依赖好， uv 会在项目根路径生成一个 &lt;code&gt;uv.lock&lt;/code&gt; 文件 。uv.lock 是 锁定依赖版本 的文件。&lt;/p&gt;
&lt;p&gt;它的作用是：把 pyproject.toml 里描述的依赖（比如 &amp;ldquo;fastapi&amp;gt;=0.110.0&amp;rdquo; 这样比较宽松的范围），具体锁定成明确、唯一的版本（比如 &amp;ldquo;fastapi==0.110.1&amp;rdquo;）。&lt;/p&gt;
&lt;p&gt;这样，每次安装时，不管谁来安装（你自己、你的同事、你的服务器），大家安装的依赖版本都是一模一样的，不会因为小版本不同导致奇怪的 bug。&lt;/p&gt;
&lt;p&gt;uv.lock 是自动生成、自动管理的。不需手动编辑。&lt;/p&gt;
&lt;h2 id="其他"&gt;&lt;a href="#%e5%85%b6%e4%bb%96" class="header-anchor"&gt;&lt;/a&gt;其他
&lt;/h2&gt;&lt;p&gt;其他的，如 fastapi 相关的、 打 docker 镜像部署什么的相对本文主题超纲了，就不在本文中过多描述了。&lt;/p&gt;
&lt;h2 id="总结"&gt;&lt;a href="#%e6%80%bb%e7%bb%93" class="header-anchor"&gt;&lt;/a&gt;总结
&lt;/h2&gt;&lt;p&gt;本文我们分享了用 uv 初始化和管理 Python 项目的完整流程。&lt;/p&gt;
&lt;p&gt;从安装 uv 开始，我介绍了它为什么比传统工具（pip、pipx、poetry 等）更快更好用，以及 uv 在多 Python 版本管理、依赖锁定、项目初始化方面带来的便利。&lt;/p&gt;
&lt;p&gt;随后，详细讲了如何用 uv 管理本地 Python 环境、新建项目、创建虚拟环境、编辑 pyproject.toml 配置，并逐步解释了各个配置项的作用&lt;/p&gt;
&lt;p&gt;整体来看，uv 提供了一套现代、规范、高效的 Python 项目管理方案，非常适合用来打基础，后续无论是开发 API、打包 Docker 镜像，还是部署上线，都能有条不紊地进行。&lt;/p&gt;
&lt;p&gt;同时我们通过在项目创建的过程中看到各语言（java、nodejs&amp;hellip;）都相通或类似的工程 “最佳实践”，真是应了那句话：“大道至简，真理趋同”&lt;/p&gt;</description></item><item><title>数据库选型终极指南：从数据类型到应用场景，一篇就够了</title><link>https://xiaobox.github.io/p/2025-03-14-shu-ju-ku-xuan-xing-zhong-ji-zhi-nan-cong-shu-ju-lei-xing-da/</link><pubDate>Fri, 14 Mar 2025 09:19:52 +0000</pubDate><guid>https://xiaobox.github.io/p/2025-03-14-shu-ju-ku-xuan-xing-zhong-ji-zhi-nan-cong-shu-ju-lei-xing-da/</guid><description>&lt;img src="https://pub-f29bf2b53160470c9a85250116509a24.r2.dev/post/2025-03-14-shu-ju-ku-xuan-xing-zhong-ji-zhi-nan-cong-shu-ju-lei-xing-da/cover.jpg" alt="Featured image of post 数据库选型终极指南：从数据类型到应用场景，一篇就够了" /&gt;&lt;h2 id="引言"&gt;&lt;a href="#%e5%bc%95%e8%a8%80" class="header-anchor"&gt;&lt;/a&gt;引言
&lt;/h2&gt;&lt;p&gt;在当今的数字化时代，数据已成为企业和组织的核心资产。无论是金融交易记录、社交媒体互动、物联网传感器数据，还是企业内部的业务流程信息，都需要通过数据库进行存储、管理和分析。然而，面对市场上数十种主流的数据库技术（如 MySQL、MongoDB、Elasticsearch、HBase、Hive等），如何选择适合自身业务需求的数据库系统，成为许多技术决策者面临的难题。本文将深入探讨数据库的核心分类、技术特性、应用场景以及选择策略，帮助读者构建系统化的选型框架。&lt;/p&gt;
&lt;hr&gt;
&lt;h2 id="数据库的分类"&gt;&lt;a href="#%e6%95%b0%e6%8d%ae%e5%ba%93%e7%9a%84%e5%88%86%e7%b1%bb" class="header-anchor"&gt;&lt;/a&gt;数据库的分类
&lt;/h2&gt;&lt;p&gt;在进行数据库的选择前，你需要至少知道它的&lt;strong&gt;分类&lt;/strong&gt;。&lt;/p&gt;
&lt;p&gt;在数据库技术的演进过程中，数据存储模型和应用需求的多样性催生了不同类型的数据库系统。这些系统根据其核心设计理念、数据组织方式以及适用场景的差异，形成了多个分类。&lt;/p&gt;
&lt;h3 id="关系型数据库rdbms结构化数据的基石"&gt;&lt;a href="#%e5%85%b3%e7%b3%bb%e5%9e%8b%e6%95%b0%e6%8d%ae%e5%ba%93rdbms%e7%bb%93%e6%9e%84%e5%8c%96%e6%95%b0%e6%8d%ae%e7%9a%84%e5%9f%ba%e7%9f%b3" class="header-anchor"&gt;&lt;/a&gt;关系型数据库（RDBMS）：结构化数据的基石
&lt;/h3&gt;&lt;p&gt;&lt;strong&gt;关系型数据库的根基是关系代数和集合论&lt;/strong&gt;，通过二维表（Table）组织数据。每个表由行（记录）和列（字段）构成，通过主键（Primary Key）唯一标识记录，外键（Foreign Key）实现表间的关联。其核心优势在于ACID事务支持，即原子性（Atomicity）、一致性（Consistency）、隔离性（Isolation）、持久性（Durability），适用于对数据一致性要求极高的场景（如金融交易）&lt;/p&gt;
&lt;p&gt;适用场景：&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;需要强一致性的业务系统（银行核心系统、ERP）。&lt;/li&gt;
&lt;li&gt;多表关联查询频繁的OLTP（联机事务处理）场景（电商订单管理）&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;局限性：&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;表结构预定义，修改成本高（如新增字段需 ALTER TABLE）。&lt;/li&gt;
&lt;li&gt;水平扩展困难，分库分表复杂度高（需处理分布式事务和跨分片查询）。&lt;/li&gt;
&lt;li&gt;不适合存储半结构化数据（如JSON文档、嵌套数组）。&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;代表数据库：&lt;code&gt;MySQL&lt;/code&gt;、&lt;code&gt;PostgreSQL&lt;/code&gt;、&lt;code&gt;Oracle&lt;/code&gt;、&lt;code&gt;SQL Server&lt;/code&gt;&lt;/p&gt;
&lt;h3 id="nosql-数据库灵活性与扩展性的革命"&gt;&lt;a href="#nosql-%e6%95%b0%e6%8d%ae%e5%ba%93%e7%81%b5%e6%b4%bb%e6%80%a7%e4%b8%8e%e6%89%a9%e5%b1%95%e6%80%a7%e7%9a%84%e9%9d%a9%e5%91%bd" class="header-anchor"&gt;&lt;/a&gt;NoSQL 数据库：灵活性与扩展性的革命
&lt;/h3&gt;&lt;p&gt;NoSQL（Not Only SQL）的诞生是为了解决关系型数据库在扩展性、灵活性和高性能场景下的不足。根据数据模型的差异，NoSQL 可进一步细分为四类：&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;1. 文档型数据库（Document Database）&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;数据模型：以文档为基本单元，通常采用JSON或BSON格式存储，支持嵌套结构和动态字段&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-json" data-lang="json"&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;1&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="p"&gt;{&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;2&lt;/span&gt;&lt;span class="cl"&gt; &lt;span class="nt"&gt;&amp;#34;user_id&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;101&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;3&lt;/span&gt;&lt;span class="cl"&gt; &lt;span class="nt"&gt;&amp;#34;name&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;&amp;#34;张三&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;4&lt;/span&gt;&lt;span class="cl"&gt; &lt;span class="nt"&gt;&amp;#34;orders&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;5&lt;/span&gt;&lt;span class="cl"&gt; &lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="nt"&gt;&amp;#34;order_id&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;2001&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nt"&gt;&amp;#34;amount&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mf"&gt;150.0&lt;/span&gt;&lt;span class="p"&gt;},&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;6&lt;/span&gt;&lt;span class="cl"&gt; &lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="nt"&gt;&amp;#34;order_id&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;2002&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nt"&gt;&amp;#34;amount&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mf"&gt;300.0&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;7&lt;/span&gt;&lt;span class="cl"&gt; &lt;span class="p"&gt;]&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;8&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;查询能力：支持基于文档属性的查询，部分数据库（如MongoDB）提供类SQL的聚合管道（Aggregation Pipeline）和索引优化。&lt;/p&gt;
&lt;p&gt;适用场景：&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;内容管理系统（CMS）中文章的多版本存储。&lt;/li&gt;
&lt;li&gt;用户配置文件的动态字段管理（如社交平台用户的个性化标签）。&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;局限性：跨文档事务支持较弱（MongoDB 4.0后支持多文档事务，但性能损耗较大）。&lt;/p&gt;
&lt;p&gt;代表数据库：&lt;code&gt;MongoDB&lt;/code&gt;、&lt;code&gt;Couchbase&lt;/code&gt;&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;2. 键值型数据库（Key-Value Store）&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;数据模型：最简单的 NoSQL 模型，数据以键值对（Key-Value）形式存储，Value可以是任意二进制数据。&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-fallback" data-lang="fallback"&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;1&lt;/span&gt;&lt;span class="cl"&gt;Key: &amp;#34;user:101:profile&amp;#34;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;2&lt;/span&gt;&lt;span class="cl"&gt;Value: &amp;#34;{&amp;#39;name&amp;#39;: &amp;#39;李四&amp;#39;, &amp;#39;last_login&amp;#39;: &amp;#39;2023-10-01&amp;#39;}&amp;#34;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;高性能特性：通过哈希表实现O(1)时间复杂度的读写操作，适合缓存和高并发场景。&lt;/p&gt;
&lt;p&gt;适用场景：&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;会话存储（Session Storage）：快速存取用户登录状态。&lt;/li&gt;
&lt;li&gt;分布式缓存（如Redis缓存热门商品信息）。&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;局限性：缺乏复杂查询能力（仅能通过Key检索），需业务层处理数据关联逻辑。&lt;/p&gt;
&lt;p&gt;代表数据库：&lt;code&gt;Redis&lt;/code&gt;、&lt;code&gt;Memcached&lt;/code&gt;、&lt;code&gt;Amazon DynamoDB&lt;/code&gt;&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;3. 列族数据库（Wide-Column Store）&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;数据模型：数据按列族（Column Family）组织，每行可动态添加列，适合稀疏矩阵存储。&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-fallback" data-lang="fallback"&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;1&lt;/span&gt;&lt;span class="cl"&gt;Row Key: &amp;#34;device_001&amp;#34;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;2&lt;/span&gt;&lt;span class="cl"&gt;Columns: 
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;3&lt;/span&gt;&lt;span class="cl"&gt; &amp;#34;metrics:temperature&amp;#34; -&amp;gt; 25.5
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;4&lt;/span&gt;&lt;span class="cl"&gt; &amp;#34;metrics:humidity&amp;#34; -&amp;gt; 60%
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;5&lt;/span&gt;&lt;span class="cl"&gt; &amp;#34;location:city&amp;#34; -&amp;gt; &amp;#34;北京&amp;#34; 
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;存储优势：基于LSM树（Log-Structured Merge Tree）的存储引擎，优化高吞吐写入（如日志、传感器数据）。&lt;/p&gt;
&lt;p&gt;适用场景：&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;时间序列数据（物联网设备监控）。&lt;/li&gt;
&lt;li&gt;海量数据的随机读写（如HBase存储网页爬虫数据）。&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;局限性：复杂查询需依赖Row Key设计，二级索引支持有限。&lt;/p&gt;
&lt;p&gt;代表数据库：&lt;code&gt;Apache HBase&lt;/code&gt;、&lt;code&gt;Cassandra&lt;/code&gt;、&lt;code&gt;Google Bigtable&lt;/code&gt;&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;4. 图数据库（Graph Database）&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;数据模型：以图论为基础，通过节点（Node）、边（Edge）、属性（Property）表示实体及其关系。&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-fallback" data-lang="fallback"&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;1&lt;/span&gt;&lt;span class="cl"&gt;Node: User(id=101, name=&amp;#34;王五&amp;#34;)
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;2&lt;/span&gt;&lt;span class="cl"&gt;Edge: User101 -[FRIEND]-&amp;gt; User102 (since=2020)
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;查询优势：专为关系查询优化，可高效遍历多跳关系（如社交网络的六度分隔理论）。&lt;/p&gt;
&lt;p&gt;适用场景：&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;社交网络中的好友推荐。&lt;/li&gt;
&lt;li&gt;欺诈检测（识别异常交易环路）。&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;局限性：非关系场景下性能无明显优势，学习曲线陡峭。&lt;/p&gt;
&lt;p&gt;代表数据库：&lt;code&gt;Neo4j&lt;/code&gt;、&lt;code&gt;Amazon Neptune&lt;/code&gt;&lt;/p&gt;
&lt;h3 id="大数据生态数据库分布式与批量处理的支柱"&gt;&lt;a href="#%e5%a4%a7%e6%95%b0%e6%8d%ae%e7%94%9f%e6%80%81%e6%95%b0%e6%8d%ae%e5%ba%93%e5%88%86%e5%b8%83%e5%bc%8f%e4%b8%8e%e6%89%b9%e9%87%8f%e5%a4%84%e7%90%86%e7%9a%84%e6%94%af%e6%9f%b1" class="header-anchor"&gt;&lt;/a&gt;大数据生态数据库：分布式与批量处理的支柱
&lt;/h3&gt;&lt;p&gt;&lt;strong&gt;1. 分布式列式存储（HBase）&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;技术架构：基于HDFS的分布式存储，通过Region分片实现水平扩展，ZooKeeper协调元数据。&lt;/p&gt;
&lt;p&gt;核心能力：&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;随机实时读写（毫秒级延迟）。&lt;/li&gt;
&lt;li&gt;稀疏数据的高效存储（空值不占空间）。&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;适用场景：实时查询TB级数据（如电信通话记录检索）。&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;2. 数据仓库（Hive）&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;技术原理：将结构化数据映射为HDFS文件，通过 HiveQL（类SQL）转换为MapReduce或Tez任务。&lt;/p&gt;
&lt;p&gt;核心能力：&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;离线批量处理（小时级延迟）。&lt;/li&gt;
&lt;li&gt;复杂ETL流程（数据清洗、转换）。&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;适用场景：历史数据报表生成（如零售业月度销售分析）。&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;3. 实时数仓（ClickHouse、Doris）&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;技术突破：向量化执行引擎、列式存储、预聚合，实现亚秒级响应。&lt;/p&gt;
&lt;p&gt;适用场景：交互式OLAP分析（如广告投放效果实时看板）。&lt;/p&gt;
&lt;p&gt;&lt;img alt="Image" loading="lazy" sizes="(max-width: 767px) calc(100vw - 30px), (max-width: 1023px) 700px, (max-width: 1279px) 950px, 1232px" src="https://pub-f29bf2b53160470c9a85250116509a24.r2.dev/post/2025-03-14-shu-ju-ku-xuan-xing-zhong-ji-zhi-nan-cong-shu-ju-lei-xing-da/001-609ddd79.png"&gt;&lt;/p&gt;
&lt;h3 id="总结"&gt;&lt;a href="#%e6%80%bb%e7%bb%93" class="header-anchor"&gt;&lt;/a&gt;总结
&lt;/h3&gt;&lt;p&gt;我们做一个整体的对比&lt;/p&gt;
&lt;p&gt;&lt;img alt="Image" loading="lazy" sizes="(max-width: 767px) calc(100vw - 30px), (max-width: 1023px) 700px, (max-width: 1279px) 950px, 1232px" src="https://pub-f29bf2b53160470c9a85250116509a24.r2.dev/post/2025-03-14-shu-ju-ku-xuan-xing-zhong-ji-zhi-nan-cong-shu-ju-lei-xing-da/002-4df3e69a.png"&gt;&lt;/p&gt;
&lt;p&gt;随着技术发展，数据库的界限逐渐模糊。例如：&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;多模型数据库：如PostgreSQL通过扩展支持JSONB（文档模型）和Citus（分布式能力）。&lt;/li&gt;
&lt;li&gt;&lt;code&gt;HTAP&lt;/code&gt;(Hybrid Transactional/Analytical Processing)数据库：TiDB、Oracle Exadata支持OLTP与OLAP混合负载。&lt;/li&gt;
&lt;li&gt;AI驱动数据库：利用机器学习优化查询计划（如Google AlloyDB）。&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;随着 AI 技术的兴起，&lt;code&gt;向量数据库&lt;/code&gt;也是非常热门的一类数据库。数据库的分类也并非绝对的技术壁垒，而是反映了不同场景下的核心矛盾权衡：&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;结构化 vs 灵活性&lt;/strong&gt;：关系型牺牲灵活性换取严格约束，文档型反之。&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;一致性 vs 扩展性&lt;/strong&gt;：CP系统（如ZooKeeper）优先保障一致性，AP系统（如Cassandra）优先保障可用性。&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;实时性 vs 吞吐量&lt;/strong&gt;：HBase优化单点查询延迟，Hive优化批量吞吐量。&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;理解这些分类背后的哲学，才能避免“技术选型中的锤子效应”（手里只有一把锤子，看所有问题都是钉子），从而在复杂业务场景中构建合理的数据存储架构。&lt;/p&gt;
&lt;hr&gt;
&lt;h2 id="数据类型"&gt;&lt;a href="#%e6%95%b0%e6%8d%ae%e7%b1%bb%e5%9e%8b" class="header-anchor"&gt;&lt;/a&gt;数据类型
&lt;/h2&gt;&lt;p&gt;在进行数据库的选择前，你要处理的数据类型是你必须要明确的。&lt;/p&gt;
&lt;p&gt;结构化、半结构化和非结构化数据在存储、查询和处理方式上存在本质差异，直接影响了技术选型的路径。&lt;/p&gt;
&lt;p&gt;在数据管理的实践中，数据类型是决定数据库选型的关键因素之一。结构化、半结构化和非结构化数据在存储、查询和处理方式上存在本质差异，直接影响了技术选型的路径。以下从数据特征、处理需求到典型数据库选择展开系统性分析。&lt;/p&gt;
&lt;h3 id="结构化数据秩序与约束的领域"&gt;&lt;a href="#%e7%bb%93%e6%9e%84%e5%8c%96%e6%95%b0%e6%8d%ae%e7%a7%a9%e5%ba%8f%e4%b8%8e%e7%ba%a6%e6%9d%9f%e7%9a%84%e9%a2%86%e5%9f%9f" class="header-anchor"&gt;&lt;/a&gt;结构化数据：秩序与约束的领域
&lt;/h3&gt;&lt;p&gt;&lt;strong&gt;1. 核心特征&lt;/strong&gt;&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;严格模式（Schema）：数据字段预先定义，类型明确（如整数、日期、枚举值）。&lt;/li&gt;
&lt;li&gt;二维表结构：数据以行和列的形式组织，遵循第一范式（1NF）到第三范式（3NF）的规范。&lt;/li&gt;
&lt;li&gt;强关联性：通过外键建立表间关系，支持JOIN操作实现跨表查询。&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;示例：&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;银行账户表：&lt;code&gt;账户ID (主键) | 户主姓名 | 余额 | 开户日期&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;电商订单表：&lt;code&gt;订单ID | 用户ID (外键) | 商品ID (外键) | 订单金额 | 支付状态&lt;/code&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;strong&gt;2. 数据库选择&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;首选：关系型数据库（RDBMS）。它的选型逻辑：&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;事务完整性：需要ACID保障的场景（如转账操作）。&lt;/li&gt;
&lt;li&gt;复杂查询：涉及多表关联、聚合计算（如财务报表生成）。&lt;/li&gt;
&lt;li&gt;数据一致性：字段之间存在强约束（如库存数量不能为负值）。&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;其中代表方案有：&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;MySQL/PostgreSQL：适用于中小规模OLTP系统。&lt;/li&gt;
&lt;li&gt;Oracle：企业级高并发、高可靠性需求（如金融核心系统）。&lt;/li&gt;
&lt;li&gt;TiDB：分布式架构下仍需强一致性的场景（如跨境支付平台）。&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;strong&gt;3. 反模式案例&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;错误尝试：将用户行为日志（半结构化JSON）存入MySQL。这样做的问题是：&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;需要为动态字段创建稀疏列，导致存储空间浪费。&lt;/li&gt;
&lt;li&gt;频繁ALTER TABLE修改表结构，引发锁表风险。&lt;/li&gt;
&lt;li&gt;查询嵌套字段需解析JSON字符串，性能低下。&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 id="半结构化数据灵活性与动态性的平衡"&gt;&lt;a href="#%e5%8d%8a%e7%bb%93%e6%9e%84%e5%8c%96%e6%95%b0%e6%8d%ae%e7%81%b5%e6%b4%bb%e6%80%a7%e4%b8%8e%e5%8a%a8%e6%80%81%e6%80%a7%e7%9a%84%e5%b9%b3%e8%a1%a1" class="header-anchor"&gt;&lt;/a&gt;半结构化数据：灵活性与动态性的平衡
&lt;/h3&gt;&lt;p&gt;&lt;strong&gt;1. 核心特征&lt;/strong&gt;&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;松散模式：字段可动态增减，数据类型允许一定灵活性。&lt;/li&gt;
&lt;li&gt;层次化结构：数据以树形或网状形式组织（如JSON、XML）。&lt;/li&gt;
&lt;li&gt;自描述性：数据本身携带元信息（如字段名称、嵌套关系）。&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;示例：用户配置文件&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-json" data-lang="json"&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt; 1&lt;/span&gt;&lt;span class="cl"&gt; &lt;span class="p"&gt;{&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt; 2&lt;/span&gt;&lt;span class="cl"&gt; &lt;span class="nt"&gt;&amp;#34;user_id&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;1001&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt; 3&lt;/span&gt;&lt;span class="cl"&gt; &lt;span class="nt"&gt;&amp;#34;preferences&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt; 4&lt;/span&gt;&lt;span class="cl"&gt; &lt;span class="nt"&gt;&amp;#34;theme&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;&amp;#34;dark&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt; 5&lt;/span&gt;&lt;span class="cl"&gt; &lt;span class="nt"&gt;&amp;#34;notifications&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt; 6&lt;/span&gt;&lt;span class="cl"&gt; &lt;span class="nt"&gt;&amp;#34;email&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kc"&gt;true&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt; 7&lt;/span&gt;&lt;span class="cl"&gt; &lt;span class="nt"&gt;&amp;#34;sms&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kc"&gt;false&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt; 8&lt;/span&gt;&lt;span class="cl"&gt; &lt;span class="p"&gt;}&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt; 9&lt;/span&gt;&lt;span class="cl"&gt; &lt;span class="p"&gt;},&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;10&lt;/span&gt;&lt;span class="cl"&gt; &lt;span class="nt"&gt;&amp;#34;last_activity&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;11&lt;/span&gt;&lt;span class="cl"&gt; &lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="nt"&gt;&amp;#34;type&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;&amp;#34;login&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nt"&gt;&amp;#34;timestamp&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;&amp;#34;2023-10-05T08:30:00Z&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;},&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;12&lt;/span&gt;&lt;span class="cl"&gt; &lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="nt"&gt;&amp;#34;type&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;&amp;#34;purchase&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nt"&gt;&amp;#34;item_id&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;&amp;#34;SKU123&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;13&lt;/span&gt;&lt;span class="cl"&gt; &lt;span class="p"&gt;]&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;14&lt;/span&gt;&lt;span class="cl"&gt; &lt;span class="p"&gt;}&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;设备传感器元数据：&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-html" data-lang="html"&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;1&lt;/span&gt;&lt;span class="cl"&gt; &lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nt"&gt;device&lt;/span&gt; &lt;span class="na"&gt;id&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s"&gt;&amp;#34;D001&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;2&lt;/span&gt;&lt;span class="cl"&gt; &lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nt"&gt;location&lt;/span&gt; &lt;span class="na"&gt;lat&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s"&gt;&amp;#34;39.9042&amp;#34;&lt;/span&gt; &lt;span class="na"&gt;lon&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s"&gt;&amp;#34;116.4074&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;/&amp;gt;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;3&lt;/span&gt;&lt;span class="cl"&gt; &lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nt"&gt;sensors&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;4&lt;/span&gt;&lt;span class="cl"&gt; &lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nt"&gt;sensor&lt;/span&gt; &lt;span class="na"&gt;type&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s"&gt;&amp;#34;temperature&amp;#34;&lt;/span&gt; &lt;span class="na"&gt;unit&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s"&gt;&amp;#34;°C&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;/&amp;gt;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;5&lt;/span&gt;&lt;span class="cl"&gt; &lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nt"&gt;sensor&lt;/span&gt; &lt;span class="na"&gt;type&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s"&gt;&amp;#34;humidity&amp;#34;&lt;/span&gt; &lt;span class="na"&gt;unit&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s"&gt;&amp;#34;%&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;/&amp;gt;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;6&lt;/span&gt;&lt;span class="cl"&gt; &lt;span class="p"&gt;&amp;lt;/&lt;/span&gt;&lt;span class="nt"&gt;sensors&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;7&lt;/span&gt;&lt;span class="cl"&gt; &lt;span class="p"&gt;&amp;lt;/&lt;/span&gt;&lt;span class="nt"&gt;device&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;&lt;strong&gt;2. 数据库选择&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;首选技术：文档型数据库、宽列数据库。它的选型逻辑：&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;动态模式支持：无需预定义字段，适应业务快速迭代。&lt;/li&gt;
&lt;li&gt;嵌套查询效率：直接存储层次化数据，避免关联表拆分。&lt;/li&gt;
&lt;li&gt;局部更新能力：修改文档部分字段不影响整体结构。&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;代表方案：&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;
&lt;p&gt;MongoDB：&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;适用场景：CMS内容管理、物联网设备元数据存储。&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;优势：BSON二进制存储、聚合管道、地理位置索引。&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;限制：事务跨文档操作成本高（需4.0+版本）。&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;Cassandra：&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;适用场景：时间序列数据（如日志事件流）。&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;优势：高写入吞吐、多数据中心复制。&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;限制：查询必须指定分区键，二级索引效率低。&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;Elasticsearch：&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;适用场景：日志分析、全文检索（如电商商品搜索）。&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;优势：倒排索引、近实时搜索、分词器定制。&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;限制：写入吞吐受分片数限制，不支持事务。&lt;/p&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;strong&gt;3. 混合架构实践&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;典型组合：MySQL + MongoDB + Elasticsearch。 数据流示例：&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;用户注册信息（结构化）存入MySQL。&lt;/li&gt;
&lt;li&gt;用户行为轨迹（半结构化JSON）写入MongoDB。&lt;/li&gt;
&lt;li&gt;关键字段（如用户ID、行为类型）同步到Elasticsearch供快速检索。&lt;/li&gt;
&lt;/ol&gt;
&lt;h3 id="非结构化数据海量与多元化的挑战"&gt;&lt;a href="#%e9%9d%9e%e7%bb%93%e6%9e%84%e5%8c%96%e6%95%b0%e6%8d%ae%e6%b5%b7%e9%87%8f%e4%b8%8e%e5%a4%9a%e5%85%83%e5%8c%96%e7%9a%84%e6%8c%91%e6%88%98" class="header-anchor"&gt;&lt;/a&gt;非结构化数据：海量与多元化的挑战
&lt;/h3&gt;&lt;p&gt;&lt;strong&gt;1. 核心特征&lt;/strong&gt;&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;无固定模式：数据格式不遵循预定义结构。&lt;/li&gt;
&lt;li&gt;大文件倾向：单个数据单元体积大（如视频、图片）。&lt;/li&gt;
&lt;li&gt;内容多样性：文本、图像、音频、二进制文件等。&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;示例：&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;媒体文件：监控摄像头的1080P视频流（MP4格式）。&lt;/li&gt;
&lt;li&gt;办公文档：PDF合同、Word报告。&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;strong&gt;2. 数据库选择&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;核心矛盾：非结构化数据的管理重点不是“查询”，而是“存储与访问”。它的选型逻辑：&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;存储效率：需支持大文件分块存储（如HDFS的128MB块）。&lt;/li&gt;
&lt;li&gt;元数据管理：通过附加结构化信息实现快速检索。&lt;/li&gt;
&lt;li&gt;访问接口：提供HTTP API或对象存储接口（如S3兼容）。&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;代表方案：&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;对象存储：&lt;/li&gt;
&lt;/ol&gt;
&lt;ul&gt;
&lt;li&gt;Amazon S3/阿里云OSS：存储图片、视频等静态资源。&lt;/li&gt;
&lt;li&gt;MinIO：自建私有化对象存储方案。&lt;/li&gt;
&lt;/ul&gt;
&lt;ol start="3"&gt;
&lt;li&gt;分布式文件系统：&lt;/li&gt;
&lt;/ol&gt;
&lt;ul&gt;
&lt;li&gt;HDFS：用于Hadoop生态的原始文件存储。&lt;/li&gt;
&lt;li&gt;Ceph：统一存储池支持块、文件、对象接口。&lt;/li&gt;
&lt;/ul&gt;
&lt;ol start="5"&gt;
&lt;li&gt;专用数据库扩展：&lt;/li&gt;
&lt;/ol&gt;
&lt;ul&gt;
&lt;li&gt;MongoDB GridFS：将大文件分块存储为文档。&lt;/li&gt;
&lt;li&gt;PostgreSQL大对象（LOB）：通过TOAST机制存储二进制数据。&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;strong&gt;3. 元数据关联策略&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;典型架构是：对象存储 + 关系型数据库。分两步：&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;数据流：&lt;/li&gt;
&lt;/ol&gt;
&lt;ul&gt;
&lt;li&gt;上传视频文件到S3，获得存储路径&lt;code&gt;s3://bucket/video_001.mp4&lt;/code&gt;。&lt;/li&gt;
&lt;li&gt;在MySQL中创建记录：&lt;/li&gt;
&lt;/ul&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-sql" data-lang="sql"&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;1&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="k"&gt;INSERT&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;INTO&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;media_files&lt;/span&gt;&lt;span class="w"&gt; 
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;2&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;id&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;s3_path&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;uploader_id&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;duration&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;resolution&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;3&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="k"&gt;VALUES&lt;/span&gt;&lt;span class="w"&gt; 
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;4&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;1001&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s1"&gt;&amp;#39;s3://bucket/video_001.mp4&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;501&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;120&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s1"&gt;&amp;#39;1920x1080&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;ol&gt;
&lt;li&gt;查询过程：&lt;/li&gt;
&lt;/ol&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-sql" data-lang="sql"&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;1&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="c1"&gt;-- 查找用户501上传的高清视频
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;2&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="k"&gt;SELECT&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;s3_path&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;FROM&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;media_files&lt;/span&gt;&lt;span class="w"&gt; 
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;3&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="k"&gt;WHERE&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;uploader_id&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;501&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;AND&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;resolution&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s1"&gt;&amp;#39;1920x1080&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;h3 id="总结-1"&gt;&lt;a href="#%e6%80%bb%e7%bb%93-1" class="header-anchor"&gt;&lt;/a&gt;总结
&lt;/h3&gt;&lt;p&gt;总结一下不同数据类型的特点&lt;/p&gt;
&lt;p&gt;&lt;img alt="Image" loading="lazy" sizes="(max-width: 767px) calc(100vw - 30px), (max-width: 1023px) 700px, (max-width: 1279px) 950px, 1232px" src="https://pub-f29bf2b53160470c9a85250116509a24.r2.dev/post/2025-03-14-shu-ju-ku-xuan-xing-zhong-ji-zhi-nan-cong-shu-ju-lei-xing-da/003-ff369010.png"&gt;&lt;/p&gt;
&lt;p&gt;总结来说：&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;结构化数据是商业规则的数字化体现，适合通过关系型数据库实现精准控制。&lt;/li&gt;
&lt;li&gt;半结构化数据反映了现实世界的复杂关联，文档型或宽列数据库提供必要的灵活性。&lt;/li&gt;
&lt;li&gt;非结构化数据代表信息的原始形态，需通过对象存储与元数据管理实现规模化处理。&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;说了这么多，虽然对于数据是什么类型有了比较清楚的定义和区分，但是数据到底是结构化的还是非结构化的，其实&lt;strong&gt;主要是看 “数据的组织方式”和“处理方式”&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;这里举个例子，比如 &lt;code&gt;用户评论&lt;/code&gt;&lt;/p&gt;
&lt;p&gt;如果我们只是想简单的读写用户评论，可以把它用关系型数据库存储，当作一个表中的一个字段:&lt;/p&gt;

 &lt;blockquote&gt;
 &lt;p&gt;在评论内容（CommentContent）这个字段中，我们可以存储用户的评论文本。对于包含的表情、图片等多媒体元素，也有一些常见的处理方法。例如，把表情转换为编码存储，而图片可以存储在文件服务器上，并在数据库中保存链接地址。&lt;/p&gt;

 &lt;/blockquote&gt;
&lt;p&gt;如果把用户评论当成非结构化数据，那么它的&lt;strong&gt;处理方式&lt;/strong&gt;就会更加复杂。&lt;/p&gt;
&lt;p&gt;用户评论的内容通常是文本信息，但其实不容易进行有效的结构化处理。评论的长度、格式、语言等都可能差异很大，甚至某些评论可能包含表情符号或者图片等多媒体元素。这些元素都无法通过预定义的数据模型进行有效地分类和组织，因此我们将其当做非结构化数据来处理。&amp;ndash;这里主要是指数据的&lt;strong&gt;组织方式&lt;/strong&gt;。&lt;/p&gt;
&lt;p&gt;以下是一些具体的例子：&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;评论情感分析：通过对用户评论的文本内容分析，我们可以识别出评论者的情绪态度，比如正面的、负面的，或者中性的。这对于公司来说是非常重要的，可以了解产品或者服务在消费者中的口碑和接受程度。&lt;/li&gt;
&lt;li&gt;评论分类：我们还可以将评论分到不同的类别。可以根据情绪分为好评、中评、差评。同时，还可以按照评论的内容将其分为产品评价，客服评价等类别。&lt;/li&gt;
&lt;li&gt;评论的全文搜索：对于用户评论这种非结构化数据的全文搜索，可以帮助我们即时搜索到关于某一产品或者某一特定主题的所有相关评论。&lt;/li&gt;
&lt;li&gt;主题模型：主题模型可以帮助我们从大量的评论中提炼出几个主要的话题，帮助公司了解消费者最关心的问题有哪些。&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;具体实现架构如下：&lt;/p&gt;
&lt;p&gt;&lt;img alt="Image" loading="lazy" sizes="(max-width: 767px) calc(100vw - 30px), (max-width: 1023px) 700px, (max-width: 1279px) 950px, 1232px" src="https://pub-f29bf2b53160470c9a85250116509a24.r2.dev/post/2025-03-14-shu-ju-ku-xuan-xing-zhong-ji-zhi-nan-cong-shu-ju-lei-xing-da/004-560fe49f.png"&gt;&lt;/p&gt;
&lt;p&gt;用户评论的存储与分析系统需结合多种技术实现高效处理。在存储层设计中，推荐采用混合存储架构以满足非结构化数据的持久化需求。核心存储使用MongoDB文档数据库保存完整的评论内容（如文本、表情编码、图片链接等），其灵活的JSON结构支持动态字段扩展，例如可包含用户设备信息、地理位置等元数据。同时，MongoDB的水平扩展能力和聚合查询功能可有效支持大规模数据管理。对于评论中的图片、视频等二进制文件，则通过对象存储（如Amazon S3或阿里云OSS）存储，结合预签名URL实现安全访问，避免数据库性能损耗。辅助索引层采用Elasticsearch同步关键字段，通过倒排索引和中文分词技术（如IK分词）实现秒级全文检索，并支持模糊搜索与高亮显示。&lt;/p&gt;
&lt;p&gt;在场景化应用中，情感分析可通过多种技术实现：对于中文评论，SnowNLP或Hugging Face的BERT模型能精准识别情感倾向，例如通过预训练模型对“电池续航太差”等文本输出负面标签及置信度评分。评论分类则结合监督学习（如SVM、BERT）与无监督方法（如K-Means聚类），通过FastAPI构建实时分类服务或使用Spark进行批量处理。全文搜索功能由Elasticsearch支撑，通过MongoDB Connector实现实时数据同步，支持用户快速定位包含特定关键词的评论内容。主题模型则利用LDA、BERTopic等算法从海量评论中提取高频主题（如“屏幕质量”“物流服务”），并通过WordCloud等工具可视化呈现，帮助业务方洞察用户关注焦点。整个架构通过混合存储与多技术协同，在保证性能的同时实现成本优化。&lt;/p&gt;
&lt;hr&gt;
&lt;h2 id="应用场景"&gt;&lt;a href="#%e5%ba%94%e7%94%a8%e5%9c%ba%e6%99%af" class="header-anchor"&gt;&lt;/a&gt;应用场景
&lt;/h2&gt;&lt;p&gt;数据库选型的核心是：理解业务数据的生命周期，把握各类数据库的能力边界，在架构灵活性与技术可控性之间寻找最佳平衡点。任何脱离具体业务场景的数据库对比都是无效的，优秀的架构设计应当像精密钟表般，让每个齿轮（数据库）在最适合的位置发挥最大效能。&lt;/p&gt;
&lt;p&gt;结合典型应用场景，什么场景应该用什么数据库呢？其实在一个业务场景下需要多种类数据库结合使用，总结如下：&lt;/p&gt;
&lt;p&gt;&lt;img alt="Image" loading="lazy" sizes="(max-width: 767px) calc(100vw - 30px), (max-width: 1023px) 700px, (max-width: 1279px) 950px, 1232px" src="https://pub-f29bf2b53160470c9a85250116509a24.r2.dev/post/2025-03-14-shu-ju-ku-xuan-xing-zhong-ji-zhi-nan-cong-shu-ju-lei-xing-da/005-00b9fa8f.png"&gt;&lt;/p&gt;
&lt;p&gt;我们以单个数据库为维度再分别讨论一下：&lt;/p&gt;
&lt;h3 id="关系型mysql"&gt;&lt;a href="#%e5%85%b3%e7%b3%bb%e5%9e%8bmysql" class="header-anchor"&gt;&lt;/a&gt;关系型:MySQL
&lt;/h3&gt;&lt;p&gt;&lt;img alt="Image" loading="lazy" sizes="(max-width: 767px) calc(100vw - 30px), (max-width: 1023px) 700px, (max-width: 1279px) 950px, 1232px" src="https://pub-f29bf2b53160470c9a85250116509a24.r2.dev/post/2025-03-14-shu-ju-ku-xuan-xing-zhong-ji-zhi-nan-cong-shu-ju-lei-xing-da/006-22a3aff1.png"&gt;&lt;/p&gt;
&lt;p&gt;MySQL：高并发事务系统（如电商订单处理）&lt;/p&gt;
&lt;p&gt;核心场景：电商平台的订单系统，需要保证每笔交易的原子性（如扣减库存、生成订单、支付记录必须同时成功或回滚）。&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;为什么选择MySQL&lt;/strong&gt;：&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;ACID事务支持：通过InnoDB引擎实现强一致性，确保订单状态的准确性。&lt;/li&gt;
&lt;li&gt;复杂查询能力：支持多表JOIN（如查询用户历史订单及商品详情）。&lt;/li&gt;
&lt;li&gt;成熟生态：主从复制、分库分表工具（如ShardingSphere）支持高可用和扩展。&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;strong&gt;对比其他数据库&lt;/strong&gt;：&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;MongoDB&lt;/strong&gt;：不支持跨文档事务（早期版本），不适合强一致性场景。&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Redis&lt;/strong&gt;：内存数据库，无法持久化复杂事务逻辑。&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;strong&gt;示例&lt;/strong&gt;：每秒处理10万笔订单的电商平台，通过MySQL分库分表（按用户ID哈希）实现横向扩展。&lt;/p&gt;
&lt;h3 id="搜索引擎es"&gt;&lt;a href="#%e6%90%9c%e7%b4%a2%e5%bc%95%e6%93%8ees" class="header-anchor"&gt;&lt;/a&gt;搜索引擎：ES
&lt;/h3&gt;&lt;p&gt;&lt;img alt="Image" loading="lazy" sizes="(max-width: 767px) calc(100vw - 30px), (max-width: 1023px) 700px, (max-width: 1279px) 950px, 1232px" src="https://pub-f29bf2b53160470c9a85250116509a24.r2.dev/post/2025-03-14-shu-ju-ku-xuan-xing-zhong-ji-zhi-nan-cong-shu-ju-lei-xing-da/007-559c2219.png"&gt;&lt;/p&gt;
&lt;p&gt;Elasticsearch：实时商品搜索与日志分析&lt;/p&gt;
&lt;p&gt;核心场景：电商平台商品搜索，用户输入关键词（如“防水运动鞋”）后毫秒级返回结果。&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;为什么选择Elasticsearch&lt;/strong&gt;：&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;倒排索引&lt;/strong&gt;：快速匹配关键词，支持分词、同义词扩展、模糊查询。&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;聚合分析&lt;/strong&gt;：统计商品类目的平均评分、价格区间分布。&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;近实时（NRT）&lt;/strong&gt;：新上架商品1秒内可被搜索。&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;strong&gt;对比其他数据库&lt;/strong&gt;：&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;MySQL&lt;/strong&gt;：全文索引性能差，无法支持高并发搜索。&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;MongoDB&lt;/strong&gt;：文本搜索功能简单，缺乏分词器和相关性排序。&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;strong&gt;示例&lt;/strong&gt;：某跨境电商平台，每日处理1亿次搜索请求，通过ES集群（分片+副本）实现99.9%的查询响应时间&amp;lt;50ms。&lt;/p&gt;
&lt;h3 id="文档型mongodb"&gt;&lt;a href="#%e6%96%87%e6%a1%a3%e5%9e%8bmongodb" class="header-anchor"&gt;&lt;/a&gt;文档型：MongoDB
&lt;/h3&gt;&lt;p&gt;&lt;img alt="Image" loading="lazy" sizes="(max-width: 767px) calc(100vw - 30px), (max-width: 1023px) 700px, (max-width: 1279px) 950px, 1232px" src="https://pub-f29bf2b53160470c9a85250116509a24.r2.dev/post/2025-03-14-shu-ju-ku-xuan-xing-zhong-ji-zhi-nan-cong-shu-ju-lei-xing-da/008-6afc9d95.png"&gt;&lt;/p&gt;
&lt;p&gt;MongoDB：内容管理系统（CMS）与动态配置存储**&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;核心场景&lt;/strong&gt;：新闻发布平台的文章存储，每篇文章包含标题、正文、多级评论、动态标签。&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;为什么选择MongoDB&lt;/strong&gt;：&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;灵活文档模型&lt;/strong&gt;：存储嵌套结构的JSON数据（如评论树形结构）。&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;水平扩展&lt;/strong&gt;：通过Sharding自动分配数据到多个分片。&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;局部更新&lt;/strong&gt;：修改文章某个字段无需重写整个文档。&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;strong&gt;对比其他数据库&lt;/strong&gt;：&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;MySQL&lt;/strong&gt;：需要拆分为多张表（文章表、评论表），JOIN查询效率低。&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;HBase&lt;/strong&gt;：适合结构化扫描，不适合嵌套数据查询。&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;strong&gt;示例&lt;/strong&gt;：某媒体平台存储1000万篇文章，每篇文章包含动态标签（如“科技, 2023趋势”），通过MongoDB的文档结构直接存储。&lt;/p&gt;
&lt;h3 id="键值存储redis"&gt;&lt;a href="#%e9%94%ae%e5%80%bc%e5%ad%98%e5%82%a8redis" class="header-anchor"&gt;&lt;/a&gt;键值存储：Redis
&lt;/h3&gt;&lt;p&gt;&lt;img alt="Image" loading="lazy" sizes="(max-width: 767px) calc(100vw - 30px), (max-width: 1023px) 700px, (max-width: 1279px) 950px, 1232px" src="https://pub-f29bf2b53160470c9a85250116509a24.r2.dev/post/2025-03-14-shu-ju-ku-xuan-xing-zhong-ji-zhi-nan-cong-shu-ju-lei-xing-da/009-116b15b9.png"&gt;&lt;/p&gt;
&lt;p&gt;Redis：高频访问缓存与会话管理&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;核心场景&lt;/strong&gt;：社交平台的热门帖子缓存，用户访问时优先从缓存读取，减少数据库压力。&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;为什么选择Redis&lt;/strong&gt;：&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;内存存储&lt;/strong&gt;：读写延迟&amp;lt;1ms，支持每秒百万级操作。&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;数据结构丰富&lt;/strong&gt;：使用Sorted Set存储热门帖子排行榜，Hash存储用户会话信息。&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;持久化可选&lt;/strong&gt;：RDB快照或AOF日志保障数据安全。&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;strong&gt;对比其他数据库&lt;/strong&gt;：&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;MySQL&lt;/strong&gt;：磁盘存储，无法满足毫秒级响应。&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;MongoDB&lt;/strong&gt;：内存占用高，不适合纯缓存场景。&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;strong&gt;示例&lt;/strong&gt;：某论坛每日活跃用户500万，通过Redis缓存前1000热门帖子，命中率90%，数据库负载下降70%。&lt;/p&gt;
&lt;h3 id="宽列存储hbasecassandra"&gt;&lt;a href="#%e5%ae%bd%e5%88%97%e5%ad%98%e5%82%a8hbasecassandra" class="header-anchor"&gt;&lt;/a&gt;宽列存储：HBase、Cassandra
&lt;/h3&gt;&lt;p&gt;&lt;img alt="Image" loading="lazy" sizes="(max-width: 767px) calc(100vw - 30px), (max-width: 1023px) 700px, (max-width: 1279px) 950px, 1232px" src="https://pub-f29bf2b53160470c9a85250116509a24.r2.dev/post/2025-03-14-shu-ju-ku-xuan-xing-zhong-ji-zhi-nan-cong-shu-ju-lei-xing-da/010-9e144a87.png"&gt;&lt;/p&gt;
&lt;p&gt;HBase：海量时序数据存储（如物联网设备监控）&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;核心场景&lt;/strong&gt;：电力公司存储智能电表每秒采集的电流、电压数据。&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;为什么选择HBase&lt;/strong&gt;：&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;列族存储&lt;/strong&gt;：按列压缩时序数据，节省存储空间。&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;随机读写&lt;/strong&gt;：按设备ID+时间戳快速查询某时刻数据。&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;HDFS集成&lt;/strong&gt;：数据自动下沉至HDFS实现低成本归档。&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;strong&gt;对比其他数据库&lt;/strong&gt;：&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;Cassandra&lt;/strong&gt;：适合跨数据中心写入，但单点查询性能不如HBase。&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;MySQL&lt;/strong&gt;：无法支持每秒百万级数据写入。&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;strong&gt;示例&lt;/strong&gt;：某物联网平台每日新增1TB传感器数据，通过HBase的RowKey设计（设备ID+时间戳）实现毫秒级查询。&lt;/p&gt;
&lt;p&gt;&lt;img alt="Image" loading="lazy" sizes="(max-width: 767px) calc(100vw - 30px), (max-width: 1023px) 700px, (max-width: 1279px) 950px, 1232px" src="https://pub-f29bf2b53160470c9a85250116509a24.r2.dev/post/2025-03-14-shu-ju-ku-xuan-xing-zhong-ji-zhi-nan-cong-shu-ju-lei-xing-da/011-87d2288b.png"&gt;&lt;/p&gt;
&lt;p&gt;Cassandra：多数据中心日志同步（如全球化应用）&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;核心场景&lt;/strong&gt;：跨国社交应用的聊天日志存储，要求数据在欧美亚三地就近写入且最终一致。&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;为什么选择Cassandra&lt;/strong&gt;：&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;多活架构&lt;/strong&gt;：数据自动复制到多个数据中心，写入本地即成功。&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;高吞吐写入&lt;/strong&gt;：LSM树引擎支持每秒百万级写入。&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;无单点故障&lt;/strong&gt;：去中心化架构避免主从瓶颈。&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;strong&gt;对比其他数据库&lt;/strong&gt;：&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;HBase&lt;/strong&gt;：依赖HDFS和ZooKeeper，扩展性受限。&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;MySQL&lt;/strong&gt;：主从复制跨地域延迟高。&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;strong&gt;示例&lt;/strong&gt;：某IM应用每日处理50亿条消息，通过Cassandra实现三地数据中心写入延迟&amp;lt;10ms。&lt;/p&gt;
&lt;h3 id="数据仓库hive"&gt;&lt;a href="#%e6%95%b0%e6%8d%ae%e4%bb%93%e5%ba%93hive" class="header-anchor"&gt;&lt;/a&gt;数据仓库：Hive
&lt;/h3&gt;&lt;p&gt;&lt;img alt="Image" loading="lazy" sizes="(max-width: 767px) calc(100vw - 30px), (max-width: 1023px) 700px, (max-width: 1279px) 950px, 1232px" src="https://pub-f29bf2b53160470c9a85250116509a24.r2.dev/post/2025-03-14-shu-ju-ku-xuan-xing-zhong-ji-zhi-nan-cong-shu-ju-lei-xing-da/012-a845c0c5.png"&gt;&lt;/p&gt;
&lt;p&gt;Hive：离线数据仓库与ETL批处理&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;核心场景&lt;/strong&gt;：零售企业每月销售数据的批量清洗与报表生成。&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;为什么选择Hive&lt;/strong&gt;：&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;SQL兼容&lt;/strong&gt;：通过HiveQL实现类SQL查询，降低学习成本。&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;海量数据批处理&lt;/strong&gt;：基于MapReduce或Tez引擎处理TB级数据。&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;低成本存储&lt;/strong&gt;：数据存储在HDFS，支持压缩格式（ORC、Parquet）。&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;strong&gt;对比其他数据库&lt;/strong&gt;：&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;ClickHouse&lt;/strong&gt;：适合实时分析，但存储成本高。&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;MySQL&lt;/strong&gt;：无法处理PB级数据。&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;strong&gt;示例&lt;/strong&gt;：某电商每月分析10TB历史订单数据，通过Hive生成“年度区域销售趋势”报表，耗时2小时。&lt;/p&gt;
&lt;h3 id="列式存储clickhouse"&gt;&lt;a href="#%e5%88%97%e5%bc%8f%e5%ad%98%e5%82%a8clickhouse" class="header-anchor"&gt;&lt;/a&gt;列式存储：ClickHouse
&lt;/h3&gt;&lt;p&gt;&lt;img alt="Image" loading="lazy" sizes="(max-width: 767px) calc(100vw - 30px), (max-width: 1023px) 700px, (max-width: 1279px) 950px, 1232px" src="https://pub-f29bf2b53160470c9a85250116509a24.r2.dev/post/2025-03-14-shu-ju-ku-xuan-xing-zhong-ji-zhi-nan-cong-shu-ju-lei-xing-da/013-c7953de3.png"&gt;&lt;/p&gt;
&lt;p&gt;ClickHouse：实时OLAP与用户行为分析&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;核心场景&lt;/strong&gt;：广告平台的实时点击流分析，每日处理千亿级事件，生成实时报表。&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;为什么选择ClickHouse&lt;/strong&gt;：&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;列式存储&lt;/strong&gt;：压缩率高，适合聚合计算（如SUM、COUNT）。&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;向量化执行&lt;/strong&gt;：利用CPU SIMD指令加速查询。&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;实时写入&lt;/strong&gt;：支持Kafka直接导入数据，延迟低至秒级。&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;strong&gt;对比其他数据库&lt;/strong&gt;：&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;Hive&lt;/strong&gt;：批处理模式，查询延迟分钟级。&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;MySQL&lt;/strong&gt;：无法支撑海量数据聚合。&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;strong&gt;示例&lt;/strong&gt;：某广告平台分析每日200亿次点击事件，通过ClickHouse集群实现“过去1小时各渠道转化率”秒级响应。&lt;/p&gt;
&lt;h3 id="图数据库neo4j"&gt;&lt;a href="#%e5%9b%be%e6%95%b0%e6%8d%ae%e5%ba%93neo4j" class="header-anchor"&gt;&lt;/a&gt;图数据库：Neo4j
&lt;/h3&gt;&lt;p&gt;&lt;img alt="Image" loading="lazy" sizes="(max-width: 767px) calc(100vw - 30px), (max-width: 1023px) 700px, (max-width: 1279px) 950px, 1232px" src="https://pub-f29bf2b53160470c9a85250116509a24.r2.dev/post/2025-03-14-shu-ju-ku-xuan-xing-zhong-ji-zhi-nan-cong-shu-ju-lei-xing-da/014-d3af3ea6.png"&gt;&lt;/p&gt;
&lt;p&gt;Neo4j：社交网络关系挖掘（如好友推荐）&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;核心场景&lt;/strong&gt;：社交平台的“六度关系”分析，计算用户A到用户B的最短路径。&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;为什么选择Neo4j&lt;/strong&gt;：&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;图遍历优化&lt;/strong&gt;：通过原生图存储引擎高效遍历多跳关系。&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Cypher查询语言&lt;/strong&gt;：直观表达复杂关系模式（如查找共同好友）。&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;实时更新&lt;/strong&gt;：支持动态添加节点和边。&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;strong&gt;对比其他数据库&lt;/strong&gt;：&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;MySQL&lt;/strong&gt;：需递归JOIN，性能随跳数指数级下降。&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;MongoDB&lt;/strong&gt;：无法直接表达关系网络。&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;strong&gt;示例&lt;/strong&gt;：某社交平台分析10亿用户关系，Neo4j可在毫秒级返回“用户A的三度人脉中可能认识的人”。&lt;/p&gt;
&lt;h3 id="总结-2"&gt;&lt;a href="#%e6%80%bb%e7%bb%93-2" class="header-anchor"&gt;&lt;/a&gt;总结
&lt;/h3&gt;&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;事务强一致&lt;/strong&gt; → MySQL&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;实时搜索&lt;/strong&gt; → Elasticsearch&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;动态文档&lt;/strong&gt; → MongoDB&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;高频缓存&lt;/strong&gt; → Redis&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;实时OLAP&lt;/strong&gt; → ClickHouse&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;时序海量存储&lt;/strong&gt; → HBase&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;全球化写入&lt;/strong&gt; → Cassandra&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;关系网络&lt;/strong&gt; → Neo4j&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;离线批处理&lt;/strong&gt; → Hive&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;img alt="Image" loading="lazy" sizes="(max-width: 767px) calc(100vw - 30px), (max-width: 1023px) 700px, (max-width: 1279px) 950px, 1232px" src="https://pub-f29bf2b53160470c9a85250116509a24.r2.dev/post/2025-03-14-shu-ju-ku-xuan-xing-zhong-ji-zhi-nan-cong-shu-ju-lei-xing-da/015-95068fb0.png"&gt;&lt;/p&gt;
&lt;hr&gt;
&lt;h2 id="最后总结"&gt;&lt;a href="#%e6%9c%80%e5%90%8e%e6%80%bb%e7%bb%93" class="header-anchor"&gt;&lt;/a&gt;最后总结
&lt;/h2&gt;&lt;p&gt;**数据模型的本质差异是选型的第一道分水岭。**关系型数据库（如MySQL、PostgreSQL）建立在严格的二维表结构之上，通过外键约束和范式理论保障数据完整性。这种结构特别适合需要复杂关联查询的财务系统、ERP等业务场景。例如银行转账操作需要严格遵循ACID事务原则，MySQL的InnoDB引擎通过行级锁和MVCC机制实现事务隔离，配合主从复制架构可以满足多数金融级需求。但在物联网设备日志存储场景下，每天千万级的写入请求会导致关系型数据库的索引维护成本急剧上升，此时文档型数据库MongoDB的BSON自由格式和分片集群优势便显现出来。MongoDB的写操作默认不等待磁盘确认，通过内存映射文件实现高速写入，特别适合内容管理系统或实时分析场景中半结构化数据的快速摄入。&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;分布式架构的CAP权衡直接影响系统可用性。&lt;/strong&gt; Elasticsearch作为分布式搜索引擎，其倒排索引结构对文本检索的优化已达到毫秒级响应，在电商商品搜索、日志分析等场景具有不可替代性。但ES的强一致性模型可能导致集群脑裂风险，需要结合zen discovery机制进行节点状态管理。相比之下，HBase作为Hadoop生态的列式存储，通过RegionServer的水平扩展和LSM树的写入优化，能够承载PB级数据量的实时读写。某智慧城市项目曾使用HBase存储数十亿条交通卡口数据，利用其行键有序分布特性实现车辆轨迹的快速回溯，这是传统关系型数据库难以企及的吞吐能力。&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;计算与存储的分离趋势重构了数据分析范式。&lt;/strong&gt; Hive建立在HDFS之上的元数据管理机制，通过类SQL语法实现大数据集的离线分析，其分区表和桶表的设计显著提升了TB级数据查询效率。某电商平台的历史订单分析采用Hive进行月度销售统计，配合Tez执行引擎将任务耗时从小时级压缩到分钟级。但Hive的高延迟特性使其不适合实时查询场景，这正是ClickHouse等OLAP数据库的突破方向。需要特别注意的是，数据湖架构的兴起使得Delta Lake、Hudi等解决方案开始融合事务管理和批流一体处理，这对传统数仓选型提出了新的挑战。&lt;/p&gt;
&lt;p&gt;**事务完整性与系统弹性的平衡艺术。**当业务需要跨数据库操作时，如电商订单系统同时涉及MySQL库存扣减和MongoDB订单日志记录，分布式事务管理就成为关键挑战。Saga模式通过补偿机制实现最终一致性，而Seata框架的AT模式能在业务侵入性较低的情况下保障事务边界。但在高并发场景下，这类方案的性能损耗可能达到20%-30%，这就需要架构师在一致性级别和系统吞吐之间做出权衡。例如社交平台的点赞功能更适合使用Redis的原子计数器，完全放弃强一致性以换取百万级QPS的处理能力。&lt;/p&gt;
&lt;p&gt;**硬件成本与运维复杂度的隐藏成本。**云原生时代，AWS Aurora通过计算存储分离架构实现了MySQL兼容数据库的自动扩缩容，其存储层可自动扩展到128TB，这种托管服务显著降低了运维负担。但对于需要定制化优化的场景，如金融行业的风控模型计算，仍需要基于物理机部署的Oracle RAC集群来保障IOPS性能。开源方案的隐性成本同样不容忽视，Elasticsearch集群的JVM堆内存配置直接影响索引性能，不当的分片设置可能导致磁盘空间浪费，这需要运维团队积累足够的调优经验。&lt;/p&gt;
&lt;p&gt;在具体选型实践中，建议采用四维评估法：首先明确数据结构化程度（结构化、半结构化、非结构化），其次分析读写比例和并发量级，再次确定一致性要求（强一致、最终一致），最后考量扩展性和生态集成需求。例如智能穿戴设备数据采集场景，设备标识符作为MongoDB文档的天然主键，时间序列数据采用嵌套文档存储，既避免了关系型数据库的表关联开销，又利用TTL索引实现自动过期清理。而在用户画像分析场景，HBase 的宽表结构可以存储数千个用户标签，配合Phoenix的SQL层实现灵活查询，这种架构组合充分发挥了列式存储的高压缩比优势。&lt;/p&gt;
&lt;p&gt;最后我们用一个简单的流程图来说明一下这个选型过程：&lt;/p&gt;
&lt;p&gt;&lt;img alt="Image" loading="lazy" sizes="(max-width: 767px) calc(100vw - 30px), (max-width: 1023px) 700px, (max-width: 1279px) 950px, 1232px" src="https://pub-f29bf2b53160470c9a85250116509a24.r2.dev/post/2025-03-14-shu-ju-ku-xuan-xing-zhong-ji-zhi-nan-cong-shu-ju-lei-xing-da/016-d55b1bf4.png"&gt;&lt;/p&gt;</description></item><item><title>REST 与 gRPC 的详细比较</title><link>https://xiaobox.github.io/p/2024-09-04-rest-yu-grpc-de-xiang-xi-bi-jiao/</link><pubDate>Wed, 04 Sep 2024 02:40:44 +0000</pubDate><guid>https://xiaobox.github.io/p/2024-09-04-rest-yu-grpc-de-xiang-xi-bi-jiao/</guid><description>&lt;img src="https://pub-f29bf2b53160470c9a85250116509a24.r2.dev/post/2024-09-04-rest-yu-grpc-de-xiang-xi-bi-jiao/cover.jpg" alt="Featured image of post REST 与 gRPC 的详细比较" /&gt;&lt;p&gt;在很长一段时间里，REST 是构建 API 的唯一“标准”。它在某种程度上取代了 SOAP，后者因为“太多的 XML”而变得混乱不堪。&lt;/p&gt;
&lt;p&gt;但近年来，新的选择出现了。2015 年，Facebook 向公众发布了 GraphQL，2016 年，谷歌紧随其后，发布了 gRPC。在这篇文章中，我们将重点关注后者，并将其与仍然广泛使用的 REST 进行比较。&lt;/p&gt;
&lt;p&gt;&lt;img alt="Image" loading="lazy" sizes="(max-width: 767px) calc(100vw - 30px), (max-width: 1023px) 700px, (max-width: 1279px) 950px, 1232px" src="https://pub-f29bf2b53160470c9a85250116509a24.r2.dev/post/2024-09-04-rest-yu-grpc-de-xiang-xi-bi-jiao/001-45a56366.jpg"&gt;&lt;/p&gt;
&lt;h2 id="概述"&gt;&lt;a href="#%e6%a6%82%e8%bf%b0" class="header-anchor"&gt;&lt;/a&gt;概述
&lt;/h2&gt;&lt;p&gt;下表将为您提供讨论要点的概览，并展示了 REST 和 gRPC 真正闪耀的地方。&lt;/p&gt;
&lt;table&gt;
 &lt;thead&gt;
 &lt;tr&gt;
 &lt;th&gt;主题&lt;/th&gt;
 &lt;th&gt;REST&lt;/th&gt;
 &lt;th&gt;gRPC&lt;/th&gt;
 &lt;/tr&gt;
 &lt;/thead&gt;
 &lt;tbody&gt;
 &lt;tr&gt;
 &lt;td&gt;标准化&lt;/td&gt;
 &lt;td&gt;无标准&lt;/td&gt;
 &lt;td&gt;定义明确&lt;/td&gt;
 &lt;/tr&gt;
 &lt;tr&gt;
 &lt;td&gt;范式&lt;/td&gt;
 &lt;td&gt;基于资源&lt;/td&gt;
 &lt;td&gt;RPC&lt;/td&gt;
 &lt;/tr&gt;
 &lt;tr&gt;
 &lt;td&gt;服务模式&lt;/td&gt;
 &lt;td&gt;仅单向&lt;/td&gt;
 &lt;td&gt;单向、客户端流、服务器流和双向流&lt;/td&gt;
 &lt;/tr&gt;
 &lt;tr&gt;
 &lt;td&gt;要求&lt;/td&gt;
 &lt;td&gt;任何 HTTP 版本，JSON 解析器&lt;/td&gt;
 &lt;td&gt;HTTP/2，gRPC 语言实现&lt;/td&gt;
 &lt;/tr&gt;
 &lt;tr&gt;
 &lt;td&gt;API 设计&lt;/td&gt;
 &lt;td&gt;代码优先&lt;/td&gt;
 &lt;td&gt;设计优先&lt;/td&gt;
 &lt;/tr&gt;
 &lt;tr&gt;
 &lt;td&gt;默认数据格式&lt;/td&gt;
 &lt;td&gt;JSON&lt;/td&gt;
 &lt;td&gt;Protobuf&lt;/td&gt;
 &lt;/tr&gt;
 &lt;tr&gt;
 &lt;td&gt;浏览器支持&lt;/td&gt;
 &lt;td&gt;原生&lt;/td&gt;
 &lt;td&gt;gRPC Web，通过变通方法&lt;/td&gt;
 &lt;/tr&gt;
 &lt;tr&gt;
 &lt;td&gt;工具&lt;/td&gt;
 &lt;td&gt;更成熟的工具&lt;/td&gt;
 &lt;td&gt;语言支持各异，部分有出色的实现&lt;/td&gt;
 &lt;/tr&gt;
 &lt;/tbody&gt;
&lt;/table&gt;
&lt;h2 id="标准化"&gt;&lt;a href="#%e6%a0%87%e5%87%86%e5%8c%96" class="header-anchor"&gt;&lt;/a&gt;标准化
&lt;/h2&gt;&lt;p&gt;REST 的一个缺点是缺乏标准化。REST 更像是一种范式，而不是 API 标准，许多人对它的理解各不相同。对大多数人来说，“REST API”一词用于指代基于 HTTP 的 JSON API。对其他人来说，REST 可以与某些规范如 HATEOAS 或 JSON:API 互换使用。但即使使用 XML 而不是 JSON，API 仍然可以是 RESTful 的，尽管这一点并不广为人知。REST 这个术语甚至不与 HTTP 绑定。这在处理 REST API 时可能导致很多混淆。例如，消费者可能会自动期望某些 REST API 端点具有幂等性或可缓存性，尽管这些并没有明确定义。&lt;/p&gt;
&lt;p&gt;相比之下，gRPC 定义明确。例如，gRPC 在 HTTP/2 上的实现非常详细。&lt;/p&gt;
&lt;h2 id="根本差异"&gt;&lt;a href="#%e6%a0%b9%e6%9c%ac%e5%b7%ae%e5%bc%82" class="header-anchor"&gt;&lt;/a&gt;根本差异
&lt;/h2&gt;&lt;p&gt;REST 和 gRPC 的范式不同。&lt;/p&gt;
&lt;p&gt;在 REST 中，一切都围绕资源展开，资源可以被检索和操作。如果我们以书籍为例，REST API 通常会提供以下端点：&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;code&gt;GET /books&lt;/code&gt;（获取所有书籍，很可能带有用于过滤和分页结果的参数）&lt;/li&gt;
&lt;li&gt;&lt;code&gt;GET /books/{id}&lt;/code&gt;（获取特定书籍）&lt;/li&gt;
&lt;li&gt;&lt;code&gt;POST /books&lt;/code&gt;（创建书籍）&lt;/li&gt;
&lt;li&gt;&lt;code&gt;DELETE /books/{id}&lt;/code&gt;（删除书籍）&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;大多数基于 HTTP 的 REST API 都遵循这种模式。虽然这种方式运作良好，但在某些情况下，作为 REST API 表示起来比较困难。例如，如果我想一次性创建多本书籍，而不想为每本书重复调用&lt;code&gt;POST /books&lt;/code&gt;（出于性能、幂等性或其他原因），我该怎么办？我创建一个&lt;code&gt;POST /books/batch&lt;/code&gt;端点吗？这还是“RESTful”的吗？虽然技术上容易解决，但它经常在开发者之间引发长时间的讨论。&lt;/p&gt;
&lt;p&gt;另一方面，gRPC 是一个 RPC 框架。它围绕服务方法展开。如果我们以书籍 API 为例，使用 gRPC，我们会创建一个&lt;code&gt;BookService&lt;/code&gt;，包含以下方法：&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;code&gt;GetBooks()&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;GetBook()&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;CreateBook()&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;DeleteBook()&lt;/code&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;我们可以随意命名这些方法，并需要任何我们需要的参数。如果我们现在想添加一个创建多本书籍的方法，没有什么可以阻止我们添加一个&lt;code&gt;CreateBooks()&lt;/code&gt;方法。gRPC 在设计 API 时提供了更多的“自由”，因为（自我施加的）限制更少。&lt;/p&gt;
&lt;h3 id="服务模式"&gt;&lt;a href="#%e6%9c%8d%e5%8a%a1%e6%a8%a1%e5%bc%8f" class="header-anchor"&gt;&lt;/a&gt;服务模式
&lt;/h3&gt;&lt;p&gt;gRPC 支持四种服务方法：&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;单向：&lt;/strong&gt; 发送单个请求，接收单个响应&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;服务器流：&lt;/strong&gt; 发送单个请求，接收多个响应&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;客户端流：&lt;/strong&gt; 发送多个请求，接收单个响应&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;双向流：&lt;/strong&gt; 发送多个请求，接收多个响应&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;与仅支持单向请求的 REST 相比，这是 gRPC 的一个非常大的优势。在 REST API 中支持其他服务模式将需要使用不同的协议，如服务器发送事件或 WebSockets，这并不完全是“RESTful”的。&lt;/p&gt;
&lt;h3 id="要求"&gt;&lt;a href="#%e8%a6%81%e6%b1%82" class="header-anchor"&gt;&lt;/a&gt;要求
&lt;/h3&gt;&lt;p&gt;REST API 通常“只要工作”就可以与任何类型的 HTTP 版本一起使用。只要编程语言具有 HTTP 客户端和 JSON 解析库，消费 REST API 就变得轻而易举。&lt;/p&gt;
&lt;p&gt;gRPC 明确需要 HTTP/2 支持，否则它将无法工作。近年来，这已不再是一个问题，因为大多数代理和框架都增加了对 HTTP/2 的支持。&lt;/p&gt;
&lt;p&gt;由于 gRPC 需要代码生成（用于创建客户端或服务器存根），因此只支持一组编程语言。&lt;/p&gt;
&lt;h2 id="api-设计"&gt;&lt;a href="#api-%e8%ae%be%e8%ae%a1" class="header-anchor"&gt;&lt;/a&gt;API 设计
&lt;/h2&gt;&lt;p&gt;REST API 通常是其实现的结果，被称为“代码优先”。虽然可以先设计 OpenAPI，然后生成服务器存根，但这不是许多开发者采取的方法。OpenAPI 定义更有可能从 API 实现中生成，如果有 OpenAPI 定义的话。因此，API 定义与实现紧密耦合。错误的模型/类的更改可能导致 API 的意外破坏性更改。&lt;/p&gt;
&lt;p&gt;gRPC 采用不同的方法，其中 API 必须在实现之前定义（被称为“设计优先”）。然后从这个 API 定义生成客户端和服务器存根。这需要一些预先思考，因为不能直接跳入实现 API。&lt;/p&gt;
&lt;p&gt;两种方法都有其优缺点。通常的 REST API 方法允许更快的迭代，因为服务器始终是真实的来源。使用 gRPC，可能很烦人，必须首先更改 API 定义，然后才能调整实现。然而，它通过明确定义 API 带来了一些安全优势。&lt;/p&gt;
&lt;p&gt;&lt;img alt="Image" loading="lazy" sizes="(max-width: 767px) calc(100vw - 30px), (max-width: 1023px) 700px, (max-width: 1279px) 950px, 1232px" src="https://pub-f29bf2b53160470c9a85250116509a24.r2.dev/post/2024-09-04-rest-yu-grpc-de-xiang-xi-bi-jiao/002-c4430419.jpg"&gt;&lt;/p&gt;
&lt;h2 id="数据格式"&gt;&lt;a href="#%e6%95%b0%e6%8d%ae%e6%a0%bc%e5%bc%8f" class="header-anchor"&gt;&lt;/a&gt;数据格式
&lt;/h2&gt;&lt;p&gt;REST 和 gRPC 都可以使用不同的格式传输数据。大多数 REST API 使用 JSON，而 gRPC 默认使用 Protocol Buffers（Protobuf），因此我们将比较这两种。&lt;/p&gt;
&lt;p&gt;JSON 对数据类型的支持有限，也有一些怪癖（例如，大数字需要作为字符串表示）。它是一种文本格式，人类可读。字段名被序列化，这占用了一些空间。在某些编程语言中，这也需要使用反射来反序列化 JSON 消息，这相当慢。&lt;/p&gt;
&lt;p&gt;如上所述，gRPC API 及其相应的消息类型首先被定义为 Protocol Buffers。每个消息都是强类型的，可能包含有用的注释，并且有许多其他有趣的特性。对于支持的编程语言列表，可以自动生成（反）序列化消息的代码。由于它是一种二进制格式，并且不序列化字段名，它比等效的 JSON 消息占用的空间更少。这确实有一个缺点，即它不再是人类可读的，需要 Protobuf 定义来反序列化消息，这可能会妨碍开发体验。&lt;/p&gt;
&lt;p&gt;以下 JSON 示例大约占用 66 字节（去除空格）。&lt;/p&gt;
&lt;p&gt;&lt;code&gt;{ &amp;quot;persons&amp;quot;: [ { &amp;quot;name&amp;quot;: &amp;quot;Max&amp;quot;, &amp;quot;age&amp;quot;: 23 }, { &amp;quot;name&amp;quot;: &amp;quot;Mike&amp;quot;, &amp;quot;age&amp;quot;: 52 } ] } &lt;/code&gt;&lt;/p&gt;
&lt;p&gt;等效的序列化 protobuf 消息只会使用 19 字节。&lt;/p&gt;
&lt;p&gt;&lt;code&gt;0x0A070A034D617810170A080A0448616E731034 &lt;/code&gt;&lt;/p&gt;
&lt;h3 id="大消息"&gt;&lt;a href="#%e5%a4%a7%e6%b6%88%e6%81%af" class="header-anchor"&gt;&lt;/a&gt;大消息
&lt;/h3&gt;&lt;p&gt;Protobuf 旨在在内存中序列化和反序列化消息。因此，不建议使用 Protobuf/gRPC 传输大消息。大多数 gRPC 实现对单个消息设置了默认的 4MB 限制。&lt;/p&gt;
&lt;p&gt;使用 REST API 处理大数据大小（如文件上传）相对直接。接收到的文件可以作为流处理，使用很少的内存。这在 gRPC 中并非不可能，但需要更多的手动努力。文件需要在发送方分成几个部分。然后每个部分作为单独的消息通过客户端流方法发送到服务器。服务器接收每个部分，并可以从中构建数据流，从而实现与 REST API 类似的行为，尽管需要更多的努力。&lt;/p&gt;
&lt;h2 id="浏览器兼容性"&gt;&lt;a href="#%e6%b5%8f%e8%a7%88%e5%99%a8%e5%85%bc%e5%ae%b9%e6%80%a7" class="header-anchor"&gt;&lt;/a&gt;浏览器兼容性
&lt;/h2&gt;&lt;p&gt;这是 REST 真正闪耀的地方。它被 Web 浏览器原生支持，使得从 Web 应用程序消费 REST API 变得毫不费力。&lt;/p&gt;
&lt;p&gt;gRPC 并不直接被浏览器支持，因为它需要明确的 HTTP/2 支持和访问某些 HTTP/2 特性，而 Web 浏览器并不提供。作为变通方法，可以使用 gRPC Web。它是 gRPC 协议的轻微变体，使其可以被 Web 浏览器消费。对于某些编程语言，gRPC Web 支持已经包含在框架中。对于其他语言，需要一个代理来将 gRPC 流量转换为 gRPC Web 流量，反之亦然。与不需要依赖的 REST API 相比，从 Web 消费 gRPC API 更加繁琐。&lt;/p&gt;
&lt;p&gt;一个变通方法是使用 JSON 转码，它允许开发人员将 gRPC API 作为 REST API 公开。&lt;/p&gt;
&lt;p&gt;gRPC 和 REST 工具在编程语言和框架之间的差异很大。在某些情况下，gRPC 感觉更“原生”，而在其他情况下，REST 工具更加先进。&lt;/p&gt;
&lt;p&gt;适当的 gRPC 语言支持非常重要，因为它需要工具来生成客户端和服务器存根。对于不支持的编程语言，你将无计可施。REST API 的客户端总是可以手动创建的，但这可能需要一些努力。虽然存在从 OpenAPI 定义创建 REST 客户端的工具，但与 gRPC 等效工具相比，它们的开发体验通常较差。&lt;/p&gt;
&lt;p&gt;由于 REST API 已经存在了很长时间，因此存在更多帮助构建、测试和部署 REST API 的工具。它们的功能通常比 gRPC 工具更先进。&lt;/p&gt;
&lt;h2 id="结论"&gt;&lt;a href="#%e7%bb%93%e8%ae%ba" class="header-anchor"&gt;&lt;/a&gt;结论
&lt;/h2&gt;&lt;p&gt;REST 和 gRPC 都有其优点和缺点。&lt;/p&gt;
&lt;p&gt;从 Web 应用程序消费 REST API 通常更容易。 REST 也更广泛地被使用，对于某些开发者来说，可能更简单，因为他们可能不了解 gRPC。&lt;/p&gt;
&lt;p&gt;在我看来，gRPC 在服务器到服务器通信（例如，微服务之间）中肯定有优势。 能够共享确切的 API 定义，并在多种编程语言中创建 API 客户端是一个巨大的胜利。&lt;/p&gt;</description></item></channel></rss>