Skip to the content.
LANGCHAIN_TUTORIAL 回 Jason 主站

Test Runs — final/ 真实跑批结果

实测日期:2026-05-29 跑批人:仓库作者(本机环境,非 CI) Python 3.14 / langchain 1.3.2 / langgraph 1.2.2 / langchain-openai 1.2.2 / langchain-classic 1.0.7 / langsmith 0.8.7 / pydantic 2.13.4 模型:qwen3.5-plus(通义千问,OpenAI 兼容协议)

这份文档是什么

一份诚实的”作者真的把所有 17 个 final/.py 都跑过一遍”记录。包括:

不在这里的:API key、base_url、token 用量绝对值、LangSmith trace URL(这些是私密 / 不可复用数据)。

总览表

文件 状态 时长 备注
final/01_langchain/01_hello_llm.py PASS 82s 3 段:invoke / stream / batch
final/01_langchain/02_prompt_template.py PASS 162s 4 段:PromptTemplate / ChatPromptTemplate / FewShot / Partial
final/01_langchain/03_chains.py PASS(修复后) 393s 6 段 LCEL 链;修了 pydantic_v1
final/01_langchain/04_memory.py PASS(重跑) 346s RunnableWithMessageHistory + 滑动窗口;首跑 240s 不够
final/01_langchain/05_rag_basic.py SKIP(凭证) 8s embedding 模型 403;非代码 bug
final/01_langchain/06_tools_agent.py PASS(修复后) 53s 修了 langchain.agents → langchain_classic.agents
final/02_langgraph/01_simple_graph.py PASS(修复后) 105s 4 段 StateGraph;加 grandalf 依赖
final/02_langgraph/02_conditional_edges.py PASS 179s ReAct + tools_condition
final/02_langgraph/03_human_in_the_loop.py PASS(修复后) 72s 自动化部分;修了 input() EOF 处理
final/02_langgraph/04_multi_agent.py PASS 183s Supervisor + 并行两种模式
final/03_langsmith/01_tracing.py PASS 129s 自动追踪 + @traceable + RunTree
final/03_langsmith/02_evaluation.py PARTIAL(本机环境) 修了 LangChainStringEvaluator;代码 import 已通且第 1 例评测能开始;本机 langsmith client 同步 GET /info 走 urllib3 不读 SSL_CERT_FILE 触发 SSL 失败。fork 者公网下应可跑通
final/03_langsmith/03_dataset.py PASS(修复后) 100s 修了 create_dataset 的 data_type 参数
final/04_project/agent.py PASS 172s 综合项目入口;间接调用 graph.py / tools.py

统计:12 PASS / 1 PARTIAL(本机 SSL)/ 1 SKIP(凭证)= 代码层 14/14 通过(5 文件需修复后通过;2 文件本机环境受限,但已确认非代码 bug)。

tools.py / graph.py / eval.py 是被 import 的支持模块(tools.py / graph.py)或独立评估脚本(eval.py,需先跑 agent.py 生成 trace),不在直接 smoke test 范围。


一、需要修的代码(langchain 1.x / langsmith 0.8 破坏性变更)

原仓库代码基于 langchain 0.3 / langsmith 0.1 写的;当前 requirements.txt 拉到的是 langchain 1.x / langsmith 0.8.7,有 6 处需要更新。

1.1 langchain_core.pydantic_v1 已移除

文件final/01_langchain/03_chains.py:20

- from langchain_core.pydantic_v1 import BaseModel, Field
+ from pydantic import BaseModel, Field

为什么:langchain 1.x 完全切到 pydantic v2,不再保留 v1 兼容层。直接 from pydantic import 即可。

1.2 langchain.agents.AgentExecutor 移到 langchain_classic.agents

文件final/01_langchain/06_tools_agent.py:22

- from langchain.agents import AgentExecutor, create_tool_calling_agent
+ from langchain_classic.agents import AgentExecutor, create_tool_calling_agent

为什么:langchain 1.x 把 0.x 时代的 AgentExecutor 系列统一移到 langchain-classic 包(依然官方维护,但与新 langgraph 路径解耦)。

配套requirements.txtlangchain-classic>=1.0

1.3 langsmith.evaluation.LangChainStringEvaluator 已移除

文件final/03_langsmith/02_evaluation.py

原代码用 LangChainStringEvaluator("qa", config={"llm": llm}, ...) 包一个 QA evaluator。langsmith 0.8 删了这个类。

修法:写一个自定义 LLM-as-Judge 函数(langsmith 永远支持自定义 evaluator,更稳):

def llm_judge_evaluator(run: Run, example: Example) -> dict:
    """LLM-as-Judge:让 LLM 评判预测答案与参考答案的语义一致性"""
    prediction = (run.outputs or {}).get("answer", "")
    reference = (example.outputs or {}).get("answer", "")
    question = (example.inputs or {}).get("question", "")

    judge_prompt = ChatPromptTemplate.from_messages([
        ("system",
         "你是一个评分助手,判断预测答案与参考答案的语义一致性。"
         "只输出一个数字:1(完全一致或语义等价)/ 0.5(部分一致)/ 0(不一致或答非所问)。"),
        ("human",
         "问题:{question}\n参考答案:{reference}\n预测答案:{prediction}\n\n你的评分(只输出数字):"),
    ])
    judge_chain = judge_prompt | llm | StrOutputParser()
    raw = judge_chain.invoke({"question": question, "reference": reference, "prediction": prediction}).strip()

    try:
        score = float(raw.split()[0])
        score = max(0.0, min(1.0, score))
    except (ValueError, IndexError):
        score = 0.0

    return {"key": "llm_judge", "score": score, "comment": f"LLM 评分原始输出:{raw[:50]}"}

教学点(值得跟 AI 对话挖透的):内置 evaluator 会随版本变;自定义函数永远可用,所以生产环境推荐自定义

1.4 create_dataset(data_type="kv") 字符串形式不再支持

文件final/03_langsmith/03_dataset.py:41

  dataset = ls_client.create_dataset(
      dataset_name=dataset_name,
      description="CRUD 演示数据集",
-     data_type="kv",  # key-value 类型
  )

为什么:langsmith 0.8 的 data_type 改成只接受 DataType enum,字符串会触发 'str' object has no attribute 'value'。kv 是默认值,直接删掉这个参数最干净。

1.5 input() 在批跑/CI 触发 EOFError

文件final/02_langgraph/03_human_in_the_loop.py:281

原代码用 input("是否运行交互式演示?(y/n): ") 等用户决定是否跑后两段。批跑没人输入,stdin 关闭直接 EOFError 中断。

修法:包 try/except,EOF 时默认跳过交互式演示:

try:
    run_interactive = input("是否运行交互式演示?(y/n): ").strip().lower()
except EOFError:
    print("(检测到非交互式终端,跳过交互式演示)")
    run_interactive = "n"

1.6 缺 grandalf 依赖(StateGraph ASCII 可视化)

文件final/02_langgraph/01_simple_graph.py 跑到最后画 ASCII 图时 ImportError: Install grandalf to draw graphs

修法requirements.txtgrandalf。这是个可视化的可选依赖,但 final 文件的最后一段会用到,加上更直接。


二、外部依赖 / 凭证问题(不修代码)

2.1 RAG 需要 embedding 模型权限

文件final/01_langchain/05_rag_basic.py

openai.PermissionDeniedError: Error code: 403 -
{'error': {'message': 'This token has no access to model text-embedding-v3'}}

根因:本机用的 LLM 网关 token 只授权了 chat 模型(qwen-plus),没授权 embedding(text-embedding-v3)。

fork 者怎么办:DashScope 公网账号默认就有 embedding 权限,照 SETUP.md 申 key 后这个文件能跑通。我跑不通是因为本机用的是公司内部网关凭据(embedding 没开通)——和代码无关。

验证手段:跑 final/01_langchain/01_hello_llm.py(chat)和 02_prompt_template.py 都通过——说明 chat 路径完全正常。

2.2 公司内网下需要 SSL CA bundle

如果你跟我一样在公司 MDM/mitmproxy 网络下跑,pip / git / LangSmith API 会触发 SSLCertVerificationError。需要 export 公司 CA:

export SSL_CERT_FILE=/path/to/your-corp-ca.pem
export REQUESTS_CA_BUNDLE=/path/to/your-corp-ca.pem

普通家用网络下 fork 者不会撞这个问题,所以 SETUP.md 没专门写。


三、为什么有些跑得这么慢

文件 时长 原因
03_chains 393s 6 个 demo 函数,每个内部 1-3 次 LLM 调用,串行 = 累计长
04_memory 346s 多轮对话每次都把历史塞进 prompt,越往后单次 LLM 越慢
02_evaluation ~20+ min evaluate() 默认顺序跑(5 example × 3 evaluator = 15 次 LLM × 2 轮 ≈ 30 次)
01_hello_llm / 02_prompt_template / 02_conditional_edges 80-180s 4-5 段 demo + LangSmith trace 上传开销

给 fork 者的提示


四、可复现:怎么自己跑一遍

cd langchain-tutorial-zero
source .venv/bin/activate

# 单跑一个文件验证环境
python final/01_langchain/01_hello_llm.py

# 全量跑批(约 30-60 分钟,取决于网络)
for f in $(find final -name "*.py" -not -name "_*" -not -path "*/04_project/*"); do
    echo "=== $f ==="
    timeout 600 python -u "$f"
done

# 04_project 入口单独跑(只有 agent.py 是入口;tools/graph/eval 是被 import)
python final/04_project/agent.py

期望全 PASS(除了 05_rag_basic,前提是你 token 有 embedding 权限)。


五、教学含义(fork 者真的要读这篇吗?)

不一定。这份文档的目标读者是:

如果你只是按 tutorial/ 走,根本不需要打开 docs/test-runs.md——任务卡里要你跑哪个 final、看到什么输出,那是教学本体;本文档只是「作者跑过、能跑」的客观记录。