> ## Documentation Index
> Fetch the complete documentation index at: https://www.yuan111.asia/doc/llms.txt
> Use this file to discover all available pages before exploring further.

# 00 · 环境准备：从零搭起能跑 LangChain 的机器

> 搭建能跑 LangChain 的机器：Miniconda + learn-langchain 环境 + 按需安装的 langchain 集成包 + .env 密钥管理。

# 00 · 环境准备：从零搭起能跑 LangChain 的机器

> 这是整套教程的"第 0 篇"——在小林学任何概念之前，他得先有一台能跑代码的机器。 所有命令、版本号、依赖关系都是**本机实测**（Windows + Miniconda + Python 3.11.15）。 读完这篇，你会有：一个独立的 conda 环境、一堆装好的 langchain 包、一个 `.env` 密钥文件、一段能跑的三轮对话代码。

***

## 开头的现象

小林拿到公司发的新电脑，老板说："环境你自己配，配好跑通第一个例子给我看。"

小林打开终端，敲 `python`，弹出来的是 Windows 自带的 Python 3.13——他一查，这货连 pip 都没有，装包全靠 `py -m pip`，而且**全世界只有一个 Python**，装什么都装到这个全局环境里。

他想起同事老王说过的话："**Python 环境就像你的衣柜——你不分层，所有衣服堆一起，找一条裤子要翻三十分钟；你分层（conda 环境），每层放一季的衣服，换季直接把整层搬走。**"

小林决定：**装 Miniconda，给 LangChain 单独开一个"衣柜层"（conda 环境），叫 `learn-langchain`。**

> 想验证"为什么需要独立环境"吗？随便建个临时环境 `conda create -n test_env python=3.11 -y`，在里面 `pip install flask`，再回到主环境 `import flask`——报错。两个环境互不干扰，这就是"分层"。

***

## 第一幕：装 Miniconda + 初始化终端（三步走）

**下载安装包：别去官网，走清华源**（官网在国外，下载慢到你怀疑人生）。

清华源地址（选最新的 Windows 安装包）：

```text theme={null}
https://mirrors.tuna.tsinghua.edu.cn/anaconda/miniconda/
# → 页面里找 Miniconda3-latest-Windows-x86_64.exe 下载
#   （Windows 64 位就是它；其他系统在同一个目录找对应安装包）
```

下载好后双击安装，一路下一步。**安装时可以选择安装目录**——小林装到了 `D:\lib\miniconda3\`（不占 C 盘，也方便管理）。真正决定"装完好不好用"的，是安装界面里那个勾选框：

```text theme={null}
☑ Add Miniconda3 to my PATH environment variable
   （把 Miniconda 加进 PATH 环境变量）
```

**勾不勾？** 官网推荐不勾（避免 conda 跟系统 Python 的命令冲突）。但小林**勾了**——理由一句话：**他要能在任意终端（PowerShell/VS Code/PyCharm）直接敲 `conda`，而不是每次先开专用终端。** 风险可接受，收益是"随时随地可用"。

装完还没完——还要让 conda **初始化你的终端**：

```powershell theme={null}
# ① 让 conda 往 PowerShell 的启动配置（$PROFILE）里注入引导脚本
#    （以后每次开终端自动运行，conda activate 才能真正生效）
conda init

# ② 重启终端（已开着的终端不会重新加载配置）
#    重启后提示符前面出现 (base)，就是初始化成功的标志
```

```powershell theme={null}
(base) PS C:\Users\小林>
# ↑ 有 (base) = conda init 生效，接下来可以正常创建和激活环境了
```

**这一步为什么必须做？** 因为 `conda activate` 的原理是：conda 算出"激活要改哪些环境变量"，然后输出一串 PowerShell 赋值语句——**这串语句必须有 `conda init` 注入的引导脚本接住并执行**，环境才会真的切换。所以顺序是：**装 → 勾 PATH → `conda init powershell` → 重启终端（看到 `(base)`）→ 之后才能正常 `conda create` / `conda activate`。**

## 第二幕：配置 conda（先配源和路径，再建环境）

> 建环境之前，先花一分钟把 conda 配好——不然 `conda create` 会走默认官方源（可能很慢），环境也会建到默认目录。配好再建，事半功倍。

### 📍 先认识你的安装目录

**小林的 Miniconda 装在 `D:\lib\miniconda3\`**——这是安装时自己选的路径（不是默认的 C 盘用户目录）。

为什么特意提这个？因为后面所有配置（`envs_dirs`、`pkgs_dirs`）都围绕这个目录展开，你得先知道它长什么样：

```text theme={null}
D:\lib\miniconda3\              ← Miniconda 本体（安装目录）
├── envs\                       ← 所有 conda 环境都建在这里（learn-langchain 就在这）
├── pkgs\                       ← 下载的包缓存（装过的包都在）
├── Scripts\                    ← conda.exe 等可执行文件
├── Library\                    ← conda 自带的一些运行库
└── conda-meta\                 ← conda 自身的元数据
```

**如果你当初装到了默认位置**（C 盘用户目录），路径会不一样，但目录结构相同——记住你自己的 `envs` 和 `pkgs` 在哪就行。**判断方法**：打开你安装 Miniconda 的那个文件夹，看到 `envs` 和 `pkgs` 两个子目录，就认对了。

> 想验证安装目录吗？在任意终端敲 `conda info`，看 `base environment` 那一行——它直接告诉你 Miniconda 装在哪。小林的输出是 `base environment : D:\lib\miniconda3`。

### 🔧 你的真实配置文件长什么样（小林电脑实测）

conda 的配置都集中在一个文件：用户级 `.condarc`，在 `C:\Users\<你的用户名>\.condarc`。

这是小林的 `.condarc` 真实内容（国内源 + 路径）：

```yaml theme={null}
# C:\Users\15565\.condarc —— conda 全局配置（用户级）
channels:
  - https://mirrors.tuna.tsinghua.edu.cn/anaconda/pkgs/main    # 清华源
  - https://mirrors.tuna.tsinghua.edu.cn/anaconda/pkgs/pro/
  - https://mirrors.tuna.tsinghua.edu.cn/anaconda/pkgs/r/
  - https://mirrors.tuna.tsinghua.edu.cn/anaconda/pkgs/free/
  - https://mirrors.tuna.tsinghua.edu.cn/anaconda/cloud/conda-forge/
  - defaults                                            # 官方源兜底

show_channel_urls: true          # 装包时显示来源地址
channel_priority: strict         # 严格按 channels 顺序选源

envs_dirs:                       # 环境装在哪（⚠️ 建环境前配好）
  - D:\lib\miniconda3\envs       #   → 以后 create 的环境全在这

pkgs_dirs:                       # 下载的包缓存装在哪
  - D:\lib\miniconda3\pkgs       #   → 包缓存就是这里！
```

**每个配置项是干嘛的：**

| 配置项                        | 作用                   | 理解                                                               |
| -------------------------- | -------------------- | ---------------------------------------------------------------- |
| `channels`                 | 装包时去哪下载              | 国内源 = 下载快，不然从国外拉包能等死你                                            |
| `channel_priority: strict` | 严格按列表顺序选源            | 清华源有就用清华，没有才找 defaults                                           |
| `envs_dirs`                | **conda 环境创建到哪个文件夹** | `conda create` 出来的环境全在这：`D:\lib\miniconda3\envs\learn-langchain` |
| `pkgs_dirs`                | 下载的 .conda 包缓存放哪     | 装过的包有缓存，重装秒开                                                     |
| `show_channel_urls`        | 装包时显示来源              | 排错时知道包从哪来的                                                       |

**两种配法（选一种）：**

```powershell theme={null}
# 方式 A：直接编辑文件（推荐，看得见全貌）
notepad C:\Users\15565\.condarc
# → 把上面的内容粘贴进去保存

# 方式 B：命令行逐条配（适合只想改一两项）
conda config --add channels https://mirrors.tuna.tsinghua.edu.cn/anaconda/pkgs/main
conda config --set channel_priority strict
conda config --add envs_dirs D:\lib\miniconda3\envs
conda config --add pkgs_dirs D:\lib\miniconda3\pkgs
```

**配完验证（`conda info` 看关键几行）：**

```text theme={null}
user config file : C:\Users\15565\.condarc                      ← 配置在这
base environment : D:\lib\miniconda3                            ← base 总环境在这
package cache : D:\lib\miniconda3\pkgs                          ← 包缓存在这
envs directories : D:\lib\miniconda3\envs                       ← 以后环境都建这
channel URLs : https://mirrors.tuna.tsinghua.edu.cn/...         ← 清华源生效了
```

**⚠️ 关于 pip 源**：小林的 `pip config list` 是空的（没配 pip 源）——他只配了 conda 的清华源，pip 装包还是走官方源。如果 `pip install` 很慢，可以配 pip 国内源：

```powershell theme={null}
pip config set global.index-url https://pypi.tuna.tsinghua.edu.cn/simple
# → 会写进 C:\Users\<用户名>\pip\pip.ini
```

> 想验证配置生效吗？跑 `conda info`，看 `user config file` / `envs directories` / `package cache` / `channel URLs` 四行——都对上了，说明配置成功，接下来建环境就会又快又稳地建到指定目录。

***

## 第三幕：创建 learn-langchain 环境 + 装包

小林在任意终端里（重启过、带 `(base)` 前缀的终端），敲了三组命令，把环境建起来了：

```powershell theme={null}
# ① 创建独立环境，指定 Python 3.11（LangChain 1.x 支持 3.9~3.13，3.11 最稳）
conda create -n learn-langchain python=3.11 -y

# ② 激活环境（激活后终端提示符会从 (base) 变成 (learn-langchain)）
conda activate learn-langchain
# → 提示符变成：(learn-langchain) PS C:\...>
#    ↑ 环境名跟着变，这才叫"激活成功"

# ③ 验证：python 现在指向环境里的解释器
python --version
# → Python 3.11.15    （不是系统那个 3.13 了）

# ④ 看 python 到底在哪（确认指向环境，不是 base）
where.exe python
# → D:\lib\miniconda3\envs\learn-langchain\python.exe   ← 在环境里
```

然后装 langchain 生态包。小林一开始想 `pip install langchain` 一把梭，但老王拦住他："**langchain 主包是个'空壳'，真正干活的是各个集成包。你按需装，别一把梭。**"

小林按"需要什么装什么"的原则，装了这几类：

```powershell theme={null}
# ① 核心（必装）：langchain 主包 + 地基 langchain-core
pip install langchain

# ② 模型集成包（按你用的厂商装）
pip install langchain-anthropic     # 智谱（Anthropic 协议）
pip install langchain-deepseek      # DeepSeek 官方（OpenAI 协议）
pip install langchain-openai        # 通用 OpenAI 兼容（通义/Kimi 等）

# ③ 数据/向量（RAG 用）
pip install langchain-chroma        # Chroma 向量库
pip install langchain-huggingface   # HuggingFace embedding（本地 bge 模型）
pip install langchain-text-splitters  # 文本切分器

# ④ 配套
pip install python-dotenv           # 读 .env 文件
pip install sentence-transformers   # embedding 模型运行依赖
```

## 第四幕：langchain 包的"拆分哲学"——一个包变成十一个

小林看着 `pip list` 里那一排 langchain 包，突然意识到一个问题：**"为什么 langchain 不做一个大而全的包，而要拆成这么多？"**

他查了查 langchain 的历史，发现这是一个"血的教训换来的设计"：

```text theme={null}
LangChain 0.x 时代（老）：一个 langchain 大包，塞进所有东西
  ├── 模型、链、Agent、文档加载、向量库、记忆……全在一个包里
  ├── 装一个包 = 下载几十个依赖（哪怕你只用对话）
  └── 升级一个组件 = 整个包一起动（容易崩）

LangChain 1.x 时代（现在）：拆成"核心 + 按需集成包"
  ├── langchain-core（地基）：抽象基类、消息、Runnable 协议
  ├── langchain（主包）：create_agent 等高层功能
  ├── langchain-anthropic / langchain-openai / ...（厂商集成）
  ├── langchain-chroma / langchain-huggingface / ...（数据集成）
  └── langgraph（编排，langchain 主包自动带）
```

**拆分哲学一句话**：**核心（langchain-core）定义接口，集成包实现"怎么连具体服务"，主包组装高层功能。** 这样你只用智谱就只装 langchain-anthropic，不用为了一个对话拖上几十个用不上的依赖。

小林画了张依赖图（实测依赖关系）：

```text theme={null}
你 pip install langchain
        │ 自动带上
        ├─ langchain-core   ← 地基（抽象基类/消息/Runnable）
        └─ langgraph        ← 编排（状态图/checkpointer）

你 pip install langchain-anthropic
        │ 自动带上
        ├─ langchain-core   ← 也要地基
        └─ anthropic        ← 智谱/Claude 的官方 SDK

你 pip install langchain-deepseek
        │ 自动带上
        ├─ langchain-core
        ├─ langchain-openai ← 它继承 OpenAI 的 BaseChatOpenAI！
        └─ openai           ← OpenAI SDK

你 pip install langchain-chroma
        │ 自动带上
        ├─ langchain-core
        └─ chromadb         ← Chroma 官方库
```

**小林的顿悟**：所有包都依赖 `langchain-core`——它是整个生态的"地基"。而 `langchain-deepseek` 依赖 `langchain-openai`——说明 DeepSeek 底层就是 OpenAI 协议（第 01 篇他会验证这个）。**看一个包的依赖，就能猜出它"是什么协议、站在谁的肩膀上"。**

> 想验证依赖关系吗？`pip show langchain-deepseek` 看 Requires 一栏——会列出 `langchain-core` 和 `langchain-openai`。你装的每个包，`pip show` 都能看到它的"地基"。

***

## 第五幕：废弃的包——看到这些名字要警惕

小林在搜索教程时，频繁踩到一些"历史遗留"的包名。他专门整理了一张"避坑清单"：

```jsonc theme={null}
{
  "langchain_community": "⚠️ 已停止维护的社区包（0.4.2 是最后版本）。旧教程大量 from langchain_community.xxx import，现在能用但别再依赖",
  "langchain-classic": "⚠️ 1.x 把 0.x 的遗留功能（旧链、AgentExecutor、hub）搬进了这个'养老院'包。看到 from langchain.chains import 老代码，其实是它在工作",
  "langchain-experimental": "⚠️ 实验性功能，API 不稳定，别在生产用",
  "langchain-community.vectorstores": "⚠️ 旧向量库集成全在这，已停更；新项目用 langchain-chroma 这类官方集成包"
}
```

**小林的核心教训**：**2024 年之前的教程，90% 是旧的**。看到 `from langchain.llms import OpenAI`（老）、`from langchain_community.xxx import`（社区停更）——别照着抄，去查 `pip install langchain-xxx` 的官方集成包。**装包前先 `pip show` 看它是不是活跃维护。**

> 想验证"废弃包"吗？试着 `pip install langchain-experimental` 然后 `import` 它——能装上但会有 DeprecationWarning。再看看 `langchain_community` 的版本号停在 0.4.2 很久没动——这就是"停更"的信号。

***

## 第六幕：`.env`——密钥和模型配置不进代码

小林写代码时有个习惯：**API key 绝不写进 .py 文件**（会被 git 泄漏）。他建了一个 `.env` 文件（被 `.gitignore` 排除），放密钥和模型配置：

```env theme={null}
# .env —— API 密钥和模型配置（不要提交到 git！）

# ⚠️ 主配置：智谱 AI（Anthropic 兼容协议）
ZHIPU_API_KEY=你的智谱key
ZHIPU_BASE_URL=https://open.bigmodel.cn/api/anthropic
ZHIPU_MODEL=glm-4.7

# ⚠️ 备选配置：DeepSeek 官方（OpenAI 兼容协议）
DEEPSEEK_API_KEY=你的deepseek-key
DEEPSEEK_BASE_URL=https://api.deepseek.com
DEEPSEEK_MODEL=deepseek-v4-flash
```

代码里用 `python-dotenv` 读取，密钥永远不进代码：

```python theme={null}
from dotenv import load_dotenv
load_dotenv()   # 读 .env 到环境变量

import os
model = ChatAnthropic(
    model=os.environ.get("ZHIPU_MODEL", "glm-4.7"),          # 从环境变量读模型名
    base_url=os.environ.get("ZHIPU_BASE_URL", "https://open.bigmodel.cn/api/anthropic"),
    api_key=os.environ["ZHIPU_API_KEY"],                     # 密钥从环境变量读，绝不写死
    max_tokens=512,
)
```

**小林的配置哲学**：

* **模型名和地址也放 .env**——换模型只改 .env，不改代码（呼应第 01 篇"换供应商只改三行"）
* **`.gitignore` 里写 `.env`**——防止密钥进 git 仓库
* **`.env.example` 是公开模板**——放变量名和占位值，别人 clone 后照着填

> 想验证 .env 生效吗？`from dotenv import load_dotenv; load_dotenv(); import os; print(os.environ.get("ZHIPU_MODEL"))`——能打印出 `glm-4.7` 就说明读到了。把 .env 里的 `ZHIPU_MODEL=glm-4.7` 改成别的，重跑就生效（不用改代码）。

***

### 🔧 技术细节：这一章涉及的关键命令和配置

小林把环境准备阶段的所有"命令签名"整理成了一张速查卡（实测）：

**① conda 命令——环境管理**

```powershell theme={null}
conda create -n learn-langchain python=3.11 -y   # 建环境（-n 名字, -y 跳过确认）
conda activate learn-langchain                   # 激活环境（提示符变环境名）
conda deactivate                                 # 退出环境
conda env list                                   # 列出所有环境
conda info                                       # 环境信息（base location/envs dirs 等）
conda remove -n learn-langchain --all -y         # 删环境（--all 全删）
```

**② conda config——配置（写入 \~/.condarc）**

```powershell theme={null}
conda config --add channels https://mirrors.tuna.tsinghua.edu.cn/anaconda/pkgs/main
conda config --set channel_priority strict
conda config --add envs_dirs D:\lib\miniconda3\envs
conda config --add pkgs_dirs D:\lib\miniconda3\pkgs
conda config --show        # 查看当前全部配置
conda config --show envs_dirs   # 只看某一项
```

**③ conda init——终端初始化**

```powershell theme={null}
conda init powershell   # 注入引导脚本到 $PROFILE（bash/zsh/cmd 同理换名）
# 之后必须重启终端，提示符出现 (base) = 成功
```

**④ pip 命令——装包**

```powershell theme={null}
pip install langchain                # 装主包（自动带 langchain-core + langgraph）
pip install langchain-anthropic      # 智谱/Claude
pip install langchain-deepseek       # DeepSeek（自动带 langchain-openai）
pip list | findstr langchain         # 查看已装的 langchain 包（Windows）
pip show langchain-deepseek          # 看包的依赖（Requires 列）
pip config set global.index-url https://pypi.tuna.tsinghua.edu.cn/simple  # pip 国内源
```

**⑤ Python 侧验证（包源码位置）**

```python theme={null}
import langchain_core
print(langchain_core.__file__)
# → D:\lib\miniconda3\envs\learn-langchain\Lib\site-packages\langchain_core\__init__.py
# import 的每个包，源码都在这个目录下
```

> 想验证吗？`conda info` 看 base environment / envs directories / channel URLs 三行；`pip show langchain-deepseek` 看 Requires 列（能看出它依赖 langchain-openai）。全部对上了，环境就配好了。

## 第七幕：第一个可运行示例——原始函数调用版（不用 `|`）

小林想把"模板 → 模型 → 解析器"跑通，但他刻意**不用第 07 篇那种 `|` 管道**——他要先用最原始的函数调用看清楚每一步发生了什么，每个环节的数据长什么样。

他写了这段代码（也是 `narrative-course/code/00_env_setup.py`）：

```python theme={null}
"""起始篇示例：原始函数调用版（不用 | 管道）——三轮带历史对话，全量打印数据字典"""
import json, os, sys
sys.stdout.reconfigure(encoding="utf-8")

from dotenv import load_dotenv
load_dotenv()

# ---------- ① 提示词模板 ----------
from langchain_core.prompts import ChatPromptTemplate, MessagesPlaceholder
from langchain_core.messages import HumanMessage

prompt = ChatPromptTemplate.from_messages([
    ("system", "你是小林公司的智能助手，说话简洁。"),
    MessagesPlaceholder("history"),   # 历史槽位
    ("human", "{question}"),
])

# ---------- ② 具体厂商大语言模型实现 ----------
from langchain_anthropic import ChatAnthropic
model = ChatAnthropic(
    model=os.environ.get("ZHIPU_MODEL", "glm-4.7"),
    base_url=os.environ.get("ZHIPU_BASE_URL", "https://open.bigmodel.cn/api/anthropic"),
    api_key=os.environ["ZHIPU_API_KEY"],
    max_tokens=512,
)

# ---------- ③ 输出转换器 ----------
from langchain_core.output_parsers import StrOutputParser
parser = StrOutputParser()

def show(label, obj):
    """打印完整数据字典 JSON"""
    def to_jsonable(o):
        if hasattr(o, "model_dump"):
            return o.model_dump()
        if isinstance(o, dict):
            return {k: to_jsonable(v) for k, v in o.items()}
        if isinstance(o, (list, tuple)):
            return [to_jsonable(v) for v in o]
        return o
    print(f"\n── {label} ──")
    print(json.dumps(to_jsonable(obj), ensure_ascii=False, indent=2, default=str))

# ---------- 三轮带历史对话（原始函数调用，不用 |） ----------
history = []   # 历史消息列表（不含 system，system 由模板注入）
questions = ["我叫小林，是后端工程师", "我叫什么名字？", "我是做什么工作的？"]

for i, q in enumerate(questions, 1):
    print("=" * 60)
    print(f"第 {i} 轮：{q}")

    # ① 模板拼装 → PromptValue → 消息列表
    prompt_value = prompt.invoke({"history": history, "question": q})
    messages = prompt_value.to_messages()
    show(f"第 {i} 轮·给模型的输入消息列表（含 system + 历史 + 当前问题）", messages)

    # ② 大模型一次性调用 invoke()
    ai_msg = model.invoke(messages)
    show(f"第 {i} 轮·模型返回的 AIMessage", ai_msg)

    # ③ 输出转换器：AIMessage → 纯文本
    answer = parser.invoke(ai_msg)
    print(f"\n  转换后文本：{answer}")

    # 追加历史：用户问题 + 模型回答
    history.append(HumanMessage(content=q))
    history.append(ai_msg)
```

小林盯着运行输出，看到了他想要的**完整数据流**（每轮两个大 JSON）：

**第 1 轮·给模型的输入消息列表**（只有 system + 当前问题，因为 history 还是空的）：

```json theme={null}
[
  { "content": "你是小林公司的智能助手，说话简洁。", "type": "system", "id": null },
  { "content": "我叫小林，是后端工程师", "type": "human", "id": null }
]
```

**第 1 轮·模型返回的 AIMessage**（完整字段：content、response\_metadata、usage\_metadata、type、id、tool\_calls）：

```json theme={null}
{
  "content": "收到，小林工程师。请问有什么需要协助？",
  "response_metadata": { "id": "msg_xxx", "model": "glm-4.7", "stop_reason": "end_turn", "usage": { "input_tokens": 24, "output_tokens": 11 } },
  "usage_metadata": { "input_tokens": 24, "output_tokens": 11, "total_tokens": 35 },
  "type": "ai",
  "id": "lc_run--xxx",
  "tool_calls": [],
  "invalid_tool_calls": []
}
```

**第 2 轮·输入消息列表**（history 滚雪球了：system + 第1轮 human + 第1轮 ai + 当前问题）：

```json theme={null}
[
  { "content": "你是小林公司的智能助手，说话简洁。", "type": "system" },
  { "content": "我叫小林，是后端工程师", "type": "human" },
  { "content": "收到，小林工程师。请问有什么需要协助？", "type": "ai" },   ← 第1轮的回复进历史了
  { "content": "我叫什么名字？", "type": "human" }                          ← 当前问题
]
```

小林看着这三轮的输出，明白了这条"原始链路"的每个环节：

```text theme={null}
① 提示词模板 prompt.invoke({...})   →  PromptValue → .to_messages() → 消息列表
② 大模型 model.invoke(消息列表)      →  AIMessage（完整字段）
③ 输出转换器 parser.invoke(AIMessage) → 纯文本字符串
④ 历史：history.append(用户问题) + history.append(AIMessage)  → 滚雪球
```

**他发现了一个关键点**：`parser.invoke(ai_msg)` 接收的是 AIMessage，返回的是字符串——**`StrOutputParser` 就是"剥壳取 content"**。这跟他第 07 篇学的 `| parser` 是同一个东西，只是这次他亲眼看到了"壳"和"肉"。

> 想验证这个示例吗？把 `00_env_setup.py` 复制到你的项目目录（旁边要有 `.env`），`python 00_env_setup.py`——你会看到三轮对话，每轮完整打印：给模型的输入消息列表（JSON）+ 模型返回的 AIMessage（JSON）+ 转换后的文本。第 2 轮的输入里，第 1 轮的回复已经躺在历史里了。

***

## 边界——这套环境什么时候会坑你

小林用了一天，摸清了这套环境的边界：

**能干的**：独立环境隔离依赖、按需装集成包、.env 管密钥、原始函数调用看清数据流。

**会坑你的**：

* **忘记 `conda activate learn-langchain`**——你 `python` 可能还在 base 环境，import langchain 直接 ModuleNotFoundError。**终端提示符看到 `(learn-langchain)` 才算激活成功。**
* **装了 langchain 但没装 langchain-anthropic**——`from langchain_anthropic import ChatAnthropic` 报错。主包是空壳，集成包才干活。
* **`pip` 装到了错误环境**——如果没激活环境就 `pip install`，可能装进 base。**先 `conda activate`，再确认 `pip --version` 指向 learn-langchain。**
* **.env 没在代码运行目录**——`load_dotenv()` 默认读当前目录的 `.env`，代码和 .env 不在一起就读不到。**要么 .env 放代码同目录，要么 `load_dotenv("路径/.env")`。**
* **密钥写进代码/提交 git**——泄漏风险。**.env 必须被 .gitignore 排除。**

> 想验证"环境坑"吗？`conda activate` 一个不存在的环境试试，或者故意在 base 环境 `import langchain_anthropic`——报错信息会告诉你"没装对地方"。

***

## 结论（小林用一天换来的）

**环境 = 独立 conda 环境（learn-langchain，Python 3.11）+ 按需安装的 langchain 集成包 + .env 管密钥。** langchain 拆分哲学：`langchain-core` 定义接口（地基），集成包实现"怎么连具体服务"，`langchain` 主包组装高层功能——看一个包的依赖就能猜它是什么协议。**废弃包（langchain\_community/langchain-classic）能用但别依赖。** 第一个示例用原始函数调用（模板 → 模型 invoke → 解析器 → 历史滚雪球），不用 `|` 管道，为的是看清每一步的数据长什么样。

## 复现信号：什么时候你会想起这一章

1. **`import langchain` 报错**——先 `conda activate learn-langchain`，再 `pip list | findstr langchain` 查装没装。
2. **想改 conda 源/环境目录**——编辑 `C:\Users\<用户名>\.condarc`（channels/envs\_dirs/pkgs\_dirs），改完 `conda info` 验证。
3. **想看包源码**——去 `envs\learn-langchain\Lib\site-packages\langchain_xxx\` 翻。
4. **pip 下载慢**——`pip config set global.index-url https://pypi.tuna.tsinghua.edu.cn/simple`。
5. **`from langchain_anthropic import ...` 报错**——主包装了但集成包没装，`pip install langchain-anthropic`。
6. **想换模型厂商**——改 .env 的 ZHIPU\_\* / DEEPSEEK\_\* 配置，代码不用动。
7. **看到 `langchain_community` 的教程**——停更包，去查官方集成包。
8. **想看"数据流"长什么样**——用这篇的原始函数调用版，每步打印完整 JSON。

现在你的机器能跑 LangChain 了。小林要开始真正的冒险了——[翻到第 01 篇：第一次对话](/doc/doc/narrative-course/01-对话)。
