我以为 LangChain 就是调用大模型,直到我写出第一条 Chain

刚开始学 LangChain 的时候,我心里其实有点不屑:不就是封装一下 OpenAI API 吗?我自己用 requests.post 也能调,为什么要多装一个框架?

直到我接到一个实际需求------从一条运维日志里提取出级别、服务名、错误类型、时间,我才意识到:调用 API 只是第一步,让模型按我想要的格式稳定输出,才是 LangChain 真正帮我解决的问题。

这篇文章记录我从"直接调 API"到"写出第一条 Chain"的过程。


一、我当时想干什么

我手里有一条这样的日志:

text 复制代码
2026-07-02 ERROR web_scrm 数据库连接超时 导致支付接口返回超时请及时处理

我需要把它变成结构化的数据,比如:

json 复制代码
{
  "level": "ERROR",
  "service": "web_scrm",
  "error_type": "数据库连接超时",
  "message": "导致支付接口返回超时请及时处理",
  "timestamp": "2026-07-02T00:00:00"
}

第一反应:让大模型帮我提取。我试了直接发一条请求,模型确实能返回 JSON,但格式每次都不太一样。有时候带解释,有时候缺字段,有时候 JSON 外面套了一层 markdown 代码块。

这让我意识到:光会调 API 不够,你还得约束输出。


二、我写的代码

LangChain 里的 Chain,简单说就是 prompt | model | parser 这三个东西串起来。

我的第一个 Chain 长这样:

python 复制代码
from pydantic import BaseModel, Field
from langchain_core.output_parsers import JsonOutputParser
from langchain_core.prompts import ChatPromptTemplate
from langchain_openai import ChatOpenAI


class LogInfo(BaseModel):
    """从日志中提取关键信息"""
    level: str = Field(description="日志级别:INFO、WARN、ERROR")
    service: str = Field(description="服务名")
    error_type: str = Field(description="错误类型")
    message: str = Field(description="错误信息")
    timestamp: datetime = Field(description="告警时间")


llm = ChatOpenAI(
    model="deepseek-chat",
    temperature=0,
    openai_api_key="你的 api key",
    openai_api_base="https://api.deepseek.com/v1",
)

parser = JsonOutputParser(pydantic_object=LogInfo)

prompt = ChatPromptTemplate.from_messages([
    ("system", "你是日志分析助手,只返回 JSON,不要任何解释。"),
    ("human", "请从这条日志中提取信息:{log}\n\n{format_instructions}"),
])

chain = prompt | llm | parser

result = chain.invoke({
    "log": "2026-07-02 ERROR web_scrm 数据库连接超时 导致支付接口返回超时请及时处理",
    "format_instructions": parser.get_format_instructions(),
})

print(result)

输出:

python 复制代码
{
  'level': 'ERROR',
  'service': 'web_scrm',
  'error_type': '数据库连接超时',
  'message': '导致支付接口返回超时请及时处理',
  'timestamp': datetime.datetime(2026, 7, 2, 0, 0)
}

看起来简单,但这里每一部分都有它的作用。


三、我遇到的坑

坑 1:temperature 没调,JSON 偶尔格式不对

我一开始写的代码:

python 复制代码
llm = ChatOpenAI(
    model="deepseek-chat",
    temperature=0.7,  # 默认值,问题出在这里
    openai_api_key="你的 api key",
    openai_api_base="https://api.deepseek.com/v1",
)

跑出来的输出有时候是这样:

text 复制代码
以下是根据日志提取的结构化信息:

```json
{
  "level": "ERROR",
  "service": "web_scrm",
  ...
}
javascript 复制代码
模型"自由发挥",在 JSON 前面加了一句解释,还套了 markdown 代码块。`JsonOutputParser` 解析时直接报错:

```text
OutputParserException: Failed to parse response

改成 temperature=0 后,输出稳定了。对于需要结构化输出的任务,temperature=0 几乎是标配。

坑 2:忘了把 format_instructions 传给 prompt

JsonOutputParser 会根据 Pydantic schema 自动生成一段格式说明,但这段说明不会自动塞进 prompt。

我当时犯的错:

python 复制代码
prompt = ChatPromptTemplate.from_messages([
    ("system", "你是日志分析助手,只返回 JSON,不要任何解释。"),
    ("human", "请从这条日志中提取信息:{log}"),  # 漏了 format_instructions
])

result = chain.invoke({
    "log": "2026-07-02 ERROR web_scrm 数据库连接超时...",
    # 这里虽然传了,但 prompt 里没有对应的占位符
    "format_instructions": parser.get_format_instructions(),
})

模型根本不知道我要哪些字段,输出完全是随机的:

json 复制代码
{
  "时间": "2026-07-02",
  "类型": "错误",
  "内容": "web_scrm 数据库连接超时"
}

正确的 prompt 必须显式留出位置:

python 复制代码
("human", "请从这条日志中提取信息:{log}\n\n{format_instructions}")

坑 3:以为 | 只是字符串拼接

我开始以为 prompt | llm | parser 和下面这种写法差不多:

python 复制代码
# 我最初的理解(错误的)
response = llm.invoke(prompt.invoke({"log": log}))
result = parser.invoke(response)

结果发现手动调的时候,类型转换、流式输出、错误追踪全要自己处理。prompt | llm | parser 这个写法叫 LCEL,它会自动处理这些事。

比如流式输出:

python 复制代码
for chunk in chain.stream({"log": log, "format_instructions": parser.get_format_instructions()}):
    print(chunk)

不用我自己拆响应里的 token。

坑 4:langchain_corelangchain 分不清

我一开始写的导入:

python 复制代码
from langchain.prompts import ChatPromptTemplate
from langchain.output_parsers import JsonOutputParser

运行没问题,但看文档的时候发现很多示例用的是 langchain_core.prompts。我一度以为这两个是同一个东西的不同写法。

后来才明白区别:

  • langchain_core:核心抽象,稳定、轻量,不依赖具体集成
  • langchain:高层封装,包含更多社区集成和便捷方法

写基础 Chain 的时候,优先从 langchain_core 导入。这样你的代码更稳定,后面换模型提供商时改动也更小。

改成:

python 复制代码
from langchain_core.prompts import ChatPromptTemplate
from langchain_core.output_parsers import JsonOutputParser

四、搞清楚后的结论

写出第一条 Chain 后,我对 LangChain 的理解变了。

它不是一个"更好用的 API 客户端",而是一个把 LLM 应用拆成标准积木的框架

flowchart LR A["Prompt告诉模型要做什么"] --> B["LLM模型思考"] B --> C["Parser解析成结构化输出"] C --> D["Result可直接用的 Python 对象"]

这三个积木各自负责:

组件 职责 对应我的代码
Prompt 构造给模型的输入 ChatPromptTemplate
LLM 执行推理 ChatOpenAI
Parser 把模型输出转成程序能用的格式 JsonOutputParser

如果只用 requests.post 调 API,这三件事你都得自己写。LangChain 把它们标准化了,所以后面加工具、加记忆、加 Agent 的时候,每一步都是可插拔的。


五、我的收获

LangChain 的价值不是封装 API,而是把"输入 → 思考 → 输出"拆成可组合的积木。

我原来的思路是:有一个需求,就写一段 prompt,调一次 API,再写一段正则解析结果。需求一多,代码就成了一团乱麻。

现在我的思路是:先定义 schema,再拼 Chain,最后把 Chain 当成一个可测试、可复用的单元。下一步 Agent 能调用的"工具",其实也就是这种 Chain 或函数。

这条 Chain 虽然小,但它是我后面写 Agent 的起点。


下一篇我会写 Tool------怎么把 query_elk_mcp 这种函数变成 Agent 能调用的工具。

相关推荐
大模型真好玩1 天前
LangChain DeepAgents 速通指南(十)—— DeepAgents Code 智能体服务核心源码解读
人工智能·langchain·agent
花千树_0102 天前
多工具调用只是开始:用 Regnexe 构建真正会反思的 Java Agent
langchain·agent
大模型真好玩6 天前
LangChain DeepAgents 速通指南(九)—— 生产级智能体框架 DeepAgents Code 源码导读
人工智能·langchain·agent
早点睡啊8 天前
精读 LangChain 官方文档(二)Model 篇:把模型调用升级成工程化推理接口
人工智能·langchain
星始流年10 天前
从 Tool 到 Skill——基于 LangChain 的服务端Skill实现
前端·langchain·agent
codedx10 天前
LangChain 和 LangGraph 构建的 Agent 项目模版
后端·langchain·agent
颜酱11 天前
LangGraph 入门指南
langchain
武子康12 天前
调查研究-186 LangChain 和 LangGraph 的区别:从快速构建 Agent 到生产级工作流编排
人工智能·langchain·llm
葫芦和十三15 天前
渐进发现|代码库不是文档库
langchain·agent·ai编程