LangChain v1Note 12
第 6 章:Runnable 观测与生产边界
学习目标
学习目标
区分模型配置、运行追踪配置和业务 context,学会在 LCEL 链上使用 tags、metadata 与 astream_events。
核心机制
with_config 返回带默认运行配置的新 Runnable:
chain = (prompt | model | StrOutputParser()).with_config({
"tags": ["learning", "rag"],
"metadata": {"lesson": "06"},
})
tags 和 metadata 会沿子 Runnable 传播,适合 LangSmith 过滤、运行归类和调试。不要放 API Key、JWT、完整用户信息或文档正文。astream_events(..., version="v2") 返回 prompt、model、parser 等子步骤的事件,可观察链的层级与耗时。
| 数据 | 位置 |
|---|---|
| 模型名称、max tokens | 模型构造参数或 with_config({"configurable": ...}) |
| tags、metadata、callbacks | Runnable config 顶层 |
| 用户身份、密钥、权限 | Runtime.context 或受控服务端 |
| 消息、检索结果、回答 | graph state 或 Runnable 输入输出 |
当前项目的 langsmith:nostream 是标签约定,不是 Python async 开关。主图的 astream_events 在第 7 章讲的是图事件;本章讲的是 prompt/model/parser 组成的 Runnable 链事件。
最小真实验证
运行 06_runnable_observability.py:
uv run python docs/langchain/examples/06_runnable_observability.py
它执行一次链并输出启动事件顺序。启用 LangSmith 环境变量时,同一运行可在 trace 中按 tag 查找。
常见误区
- metadata 不是安全存储,也可能被追踪后端持久化。
- 事件流不等于最终文本流:事件包含生命周期和嵌套 Runnable,UI 文本输出通常用
astream。 max_concurrency控制 Runnable batch 并发,不等于项目的max_concurrent_research_units业务上限。
相关资源
查看示例代码:docs/langchain/examples/06_runnable_observability.py
"""Chapter 6: stream a Runnable chain and inspect its event hierarchy.""" import asyncio from dotenv import load_dotenv from langchain.chat_models import init_chat_model from langchain_core.output_parsers import StrOutputParser from langchain_core.prompts import ChatPromptTemplate from open_deep_research.configuration import Configuration load_dotenv() async def main(): settings = Configuration.from_env() chain = ( ChatPromptTemplate.from_template("用一句中文解释:{question}") | init_chat_model(model=settings.research_model, max_tokens=100) | StrOutputParser() ).with_config( { "tags": ["learning", "langchain-runnable-events"], "metadata": {"lesson": "06"}, } ) event_names = [] async for event in chain.astream_events( {"question": "RunnableConfig 中 tags 的作用"}, version="v2", ): if event["event"].endswith("_start"): event_names.append(event["name"]) print("启动事件: " + " -> ".join(event_names)) if __name__ == "__main__": asyncio.run(main())