16 · langchain_core 全地图:那个”地基包”里到底装了什么
开头的现象
老王问小林:“你能一口气说出 langchain_core 里有哪些模块吗?” 小林掰着手指头数:“messages、prompts、runnables……呃,还有……” 数到第五个就卡住了。 他打开D:\lib\miniconda3\envs\learn-langchain\Lib\site-packages\langchain_core\ 目录,数了数——29 个模块。 小林倒吸一口凉气:“我学了两个月,只用了里面的五六个。”
他决定用一天时间把整个包翻一遍,搞清楚:每个模块是干嘛的?哪些我天天用?哪些我这辈子可能都用不上?用不上的,为什么用不上?
第一幕:小林先搞懂了 langchain_core 的”地位”——它是协议层
小林翻到langchain_core/__init__.py,看到版本号 1.5.1。他又看了看自己装的包,恍然大悟:
ChatAnthropic 住在 langchain_anthropic,但它的”爹” BaseChatModel 住在 langchain_core.language_models。他天天 import 的是砖头,但砖头长什么样,是地基说了算。
他还把 29 个模块分成了 5 大家族,画了一张图:
想验证”地位”吗?在 Python 里from langchain_core.language_models import BaseChatModel,然后issubclass(ChatAnthropic, BaseChatModel)——返回 True。你的 ChatAnthropic 就是 BaseChatModel 的孩子。
第二幕:小林逐个拜访——他标记了”天天用”的模块
小林从第①家族开始,一个一个拜访。他发现自己天天用的,集中在几个模块: messages(57 个导出)——他的老朋友。 第 02、08、10 篇的 AIMessage、tool_calls、usage_metadata 全在这。他扫了一眼导出列表,看到了BaseMessage(爹)、HumanMessage/SystemMessage/AIMessage/ToolMessage(四个孩子)、ToolCall(工具调用结构)、trim_messages(第 4 篇说的剪枝)。“我天天用,不用复习。”
prompts(21 个导出)——刚吵完架的那个。 ChatPromptTemplate、MessagesPlaceholder、PromptTemplate——第 3 篇刚说过。
language_models(19 个导出)——爹在这。 BaseChatModel(invoke/stream/bind_tools/with_structured_output 全定义在这)、BaseLLM(旧式文本模型)、还有一堆 FakeListChatModel 假模型。小林看到假模型,眼睛一亮:“测试用!不用真调 API 就能模拟模型回复!”
embeddings(3 个导出)——第 8 篇的接口。 Embeddings 基类:embed_query/embed_documents。他的 HuggingFaceEmbeddings 就是实现这个接口的。
documents(3 个导出)——第 7 篇的盒子。 Document(page_content + metadata)、BaseDocumentTransformer(切分器的爹)、BaseDocumentCompressor(压缩器的爹)。
retrievers(1 个类)——第 9 篇的检索器。 BaseRetriever,invoke(query) 返回 Document 列表。
vectorstores(4 个导出)——第 8 篇的库。 VectorStore(add_texts/similarity_search/as_retriever)、VectorStoreRetriever、还有 InMemoryVectorStore——小林看到这个乐了:“内存版向量库?测试不用装 Chroma 了!”
runnables(29 个导出)——第 9 篇的流水线。 Runnable(invoke/stream/|/batch)、RunnablePassthrough、RunnableLambda、RunnableWithMessageHistory(官方废弃,别学——1.3.3 标记 deprecated,2.0 移除)。小林看到它旁边的 deprecated 标记,心里暗暗庆幸自己没在它身上浪费时间。
tools(19 个导出)——第 10 篇的 @tool。 BaseTool、tool 装饰器、ToolException、create_retriever_tool。
小林画了张表,标出他的使用频率:
想验证”天天用”吗?翻你自己的.py文件——01_first_chat.py用了 language_models(间接)、messages;09_rag.py用了 prompts/runnables/output_parsers/documents/retrievers/vectorstores。你早就在用了,只是没意识到它们住在哪个包里。
第三幕:小林拜访了”知道就行”的模块——以及为什么用不上
小林接着拜访剩下的模块,每个都问一句”我用得上吗”: callbacks(34 个导出)——“进阶才用,但很有用。” 监听模型/链/工具的生命周期:on_llm_start、on_llm_end、on_tool_start……小林心想:“做生产应用想’看到每次调用花了多少 token、耗时多久’,就写个 BaseCallbackHandler 子类。LangSmith 追踪底层就是它。”
chat_history(普通模块)——“记忆的存储抽象。” BaseChatMessageHistory、InMemoryChatMessageHistory。小林想起第 10 篇:“这就是我手写 session 历史的’正规军’。LangGraph 的 checkpointer 是它的’进化版’。”
exceptions(7 个导出)——“报错时认类型用。” LangChainException(所有异常的爹)、OutputParserException(解析失败)。小林点点头:“报错先看异常类型,LangChainException 是总爹,catch 它不会漏。”
outputs(7 个导出)——“内部结构,日常碰不到。” LLMResult、ChatResult。小林解释给自己听:“model.invoke() 直接给你 AIMessage,碰不到这些。它们是 generate() 批量接口和回调里的原始结构。”
load(6 个导出)——“序列化,配置管理才用。” dumps/loads。小林心想:“把 prompt 存成 JSON 文件,下次加载——生产配置管理才需要。”
stores(普通模块)——“通用 KV 存储。” BaseStore、InMemoryStore。小林:“比 chat_history 更底层——存任何键值数据,跨会话共享。Agent 的记忆库用它。课程用不上。”
indexing(8 个导出)——“增量索引,生产文档更新才用。” index()、RecordManager。小林:“第 8 篇我每次 shutil.rmtree 重建库;生产环境文档会更新,才需要只处理新文档。课程用不上。”
rate_limiters(2 个导出)——“API 限速,批量调用才用。” BaseRateLimiter、InMemoryRateLimiter。小林:“个人学习调 API 量小,用不上;生产批量调用才要防 429。”
example_selectors(5 个导出)——“few-shot 选例子。” SemanticSimilarityExampleSelector。小林:“给模型几个例子再提问(few-shot)时,自动挑最相关的例子。做分类/抽取任务才有用。”
cross_encoders(2 个导出)——“重排序,RAG 进阶才用。” BaseCrossEncoder。小林:“检索后再精排(Reranker),生产级 RAG 才用。我的 2-Step RAG 用向量相似度够了。”
caches(7 个导出)、chat_loaders/chat_sessions、structured_query、agents、utils、prompt_values、document_loaders…… 小林一个个扫过,大多数是”知道存在即可”。
他特别注意到两个”容易踩雷”的:
agents(普通模块)——“历史的包袱。” 里面是 AgentAction/AgentStep/AgentFinish。小林查了查,发现这是 LangChain 0.x 时代 AgentExecutor 的数据结构,现在他用 create_agent(走 LangGraph)根本不碰。“看到旧教程里 AgentExecutor/AgentAction 就明白是历史包袱。”
还有——根本没有 structured_output 模块! 小林之前找过,翻遍整个包都没有。他查了查,明白了:“结构化输出不是独立模块,是 BaseChatModel.with_structured_output() 方法 + output_parsers 里的 Pydantic 系列实现的。” 他差点因为这个跑去装不存在的包。
想验证”没有 structured_output 模块”吗?试试from langchain_core.structured_output import ...——直接 ImportError。结构化输出在BaseChatModel.with_structured_output()里。
第四幕:小林给 29 个模块排了”优先级”
拜访完所有模块,小林画了一张”最终地图”:你 7 章学的”组件”(ChatModel/Document/Embeddings/VectorStore/Retriever/Memory/Tools/Agent)——抽象基类全在 langchain_core;具体实现在各集成包(ChatAnthropic/Chroma/HuggingFaceEmbeddings);编排在 langgraph。langchain_core = 协议层,集成包 = 实现层,langgraph = 编排层。三层各司其职。
想验证”三层各司其职”吗?from langchain_core.language_models import BaseChatModel(协议层)→from langchain_anthropic import ChatAnthropic(实现层)→from langgraph.graph import StateGraph(编排层)。三个 import,三层架构。
🔧 技术细节:langchain_core 关键类签名速查(本章全景)
小林把”天天用”的模块里的核心类签名整理成了一张速查表(实测签名): ① messages——消息与工具结构想验证吗?from langchain_core.language_models import BaseChatModel; isinstance(ChatAnthropic(...), BaseChatModel)返回 True——证明这些签名就是”统一插口”的物理定义。help(ChatPromptTemplate.from_messages)能看到完整签名。
结论(小林用一天换来的)
langchain_core 是 LangChain 的协议层(地基):29 个模块,5 大家族。 你真正天天用的只有 10 个(messages/prompts/language_models/embeddings/documents/retrievers/vectorstores/runnables/tools/output_parsers),其余大多是”知道存在即可”——用不上的原因是”你的场景没到那一步”(生产要观测才用 callbacks、文档会更新才用 indexing、批量调用才用 rate_limiters、RAG 进阶才用 cross_encoders)。没有structured_output 模块(结构化输出在方法里)。agents 模块是 0.x 历史包袱。 记住”协议层/实现层/编排层”三层架构,你就抓住了整个 LangChain 的骨架。
复现信号:什么时候你会想起这一章
from langchain_core.structured_output import ...报错——没这个模块,用with_structured_output()。from langchain_core.agents import AgentExecutor报错——agents 只有数据结构,AgentExecutor 是 langchain-classic 遗留。- 想给模型加”观测”(token/耗时/日志)——写
BaseCallbackHandler子类(callbacks 模块)。 - 报错搞不清类型——catch
LangChainException(exceptions 模块的爹)。 - 想知道”这个类是哪个包定义的”——抽象在 langchain_core,实现在集成包。