> ## Documentation Index
> Fetch the complete documentation index at: https://cndoc-langchain.site/llms.txt
> Use this file to discover all available pages before exploring further.

# 排查追踪嵌套问题

当使用 LangSmith SDK、LangGraph 和 LangChain 进行追踪时，追踪应自动传播正确的上下文，以便在父追踪中执行的代码会在 UI 的预期位置呈现。

如果你看到子运行进入了一个单独的追踪（并显示在顶层），这可能是由以下已知的“边缘情况”之一引起的。

## Python

以下概述了使用 Python 构建时导致追踪“分裂”的常见原因。

### 使用 asyncio 进行上下文传播

在 Python 版本 \< 3.11 中使用异步调用（尤其是流式处理）时，你可能会遇到追踪嵌套问题。这是因为 Python 的 `asyncio` 直到 3.11 版本才[完全支持传递上下文](https://docs.python.org/3/library/asyncio-task.html#asyncio.create_task)。

#### 原因

LangChain 和 LangSmith SDK 使用 [contextvars](https://docs.python.org/3/library/contextvars.html) 来隐式传播追踪信息。在 Python 3.11 及更高版本中，这可以无缝工作。然而，在早期版本（3.8、3.9、3.10）中，`asyncio` 任务缺乏适当的 `contextvar` 支持，这可能导致追踪断开。

#### 解决方法

1. **升级 Python 版本（推荐）** 如果可能，升级到 Python 3.11 或更高版本以实现自动上下文传播。

2. **手动上下文传播** 如果无法升级，你需要手动传播追踪上下文。方法因你的设置而异：

   a) **使用 LangGraph 或 LangChain** 将父级 `config` 传递给子调用：

   ```python theme={"theme":{"light":"catppuccin-latte","dark":"catppuccin-mocha"}}
   import asyncio
   from langchain_core.runnables import RunnableConfig, RunnableLambda

   @RunnableLambda
   async def my_child_runnable(
       inputs: str,
       # config 参数（在下面的 parent_runnable 中存在）是可选的
   ):
       yield "A"
       yield "response"

   @RunnableLambda
   async def parent_runnable(inputs: str, config: RunnableConfig):
       async for chunk in my_child_runnable.astream(inputs, config):
           yield chunk

   async def main():
       return [val async for val in parent_runnable.astream("call")]

   asyncio.run(main())
   ```

   b) **直接使用 LangSmith** 直接传递运行树：

   ```python theme={"theme":{"light":"catppuccin-latte","dark":"catppuccin-mocha"}}
   import asyncio
   import langsmith as ls

   @ls.traceable
   async def my_child_function(inputs: str):
       yield "A"
       yield "response"

   @ls.traceable
   async def parent_function(
       inputs: str,
       # 运行树可以由装饰器自动填充
       run_tree: ls.RunTree,
   ):
       async for chunk in my_child_function(inputs, langsmith_extra={"parent": run_tree}):
           yield chunk

   async def main():
       return [val async for val in parent_function("call")]

   asyncio.run(main())
   ```

   c) **将装饰代码与 LangGraph/LangChain 结合使用** 使用组合技术进行手动交接：

   ```python theme={"theme":{"light":"catppuccin-latte","dark":"catppuccin-mocha"}}
   import asyncio
   import langsmith as ls
   from langchain_core.runnables import RunnableConfig, RunnableLambda

   @RunnableLambda
   async def my_child_runnable(inputs: str):
       yield "A"
       yield "response"

   @ls.traceable
   async def my_child_function(inputs: str, run_tree: ls.RunTree):
       with ls.tracing_context(parent=run_tree):
           async for chunk in my_child_runnable.astream(inputs):
               yield chunk

   @RunnableLambda
   async def parent_runnable(inputs: str, config: RunnableConfig):
       # @traceable 装饰的函数在通过 "config" 传入时可以直接接受 RunnableConfig
       async for chunk in my_child_function(inputs, langsmith_extra={"config": config}):
           yield chunk

   @ls.traceable
   async def parent_function(inputs: str, run_tree: ls.RunTree):
       # 你可以手动设置追踪上下文
       with ls.tracing_context(parent=run_tree):
           async for chunk in parent_runnable.astream(inputs):
               yield chunk

   async def main():
       return [val async for val in parent_function("call")]

   asyncio.run(main())
   ```

### 使用 threading 进行上下文传播

通常，你会开始追踪，并希望在单个追踪内对子任务应用一些并行性。Python 标准库的 `ThreadPoolExecutor` 默认会破坏追踪。

#### 原因

Python 的 contextvars 在新线程中开始时是空的。以下是两种处理以保持追踪连续性的方法：

#### 解决方法

1. **使用 LangSmith 的 ContextThreadPoolExecutor**

   LangSmith 提供了一个 `ContextThreadPoolExecutor`，可以自动处理上下文传播：

   ```python theme={"theme":{"light":"catppuccin-latte","dark":"catppuccin-mocha"}}
   from langsmith.utils import ContextThreadPoolExecutor
   from langsmith import traceable

   @traceable
   def outer_func():
       with ContextThreadPoolExecutor() as executor:
           inputs = [1, 2]
           r = list(executor.map(inner_func, inputs))

   @traceable
   def inner_func(x):
       print(x)

   outer_func()
   ```

2. **手动提供父运行树**

   或者，你可以手动将父运行树传递给内部函数：

   ```python theme={"theme":{"light":"catppuccin-latte","dark":"catppuccin-mocha"}}
   from langsmith import traceable, get_current_run_tree
   from concurrent.futures import ThreadPoolExecutor

   @traceable
   def outer_func():
       rt = get_current_run_tree()
       with ThreadPoolExecutor() as executor:
           r = list(
               executor.map(
                   lambda x: inner_func(x, langsmith_extra={"parent": rt}), [1, 2]
               )
           )

   @traceable
   def inner_func(x):
       print(x)

   outer_func()
   ```

在这种方法中，我们使用 `get_current_run_tree()` 获取当前运行树，并使用 `langsmith_extra` 参数将其传递给内部函数。

这两种方法都确保内部函数调用即使在单独的线程中执行，也能正确地聚合在初始追踪堆栈下。

***

<div className="source-links">
  <Callout icon="terminal-2">
    [将这些文档连接](/use-these-docs)到 Claude、VSCode 等，通过 MCP 获取实时答案。
  </Callout>

  <Callout icon="edit">
    [在 GitHub 上编辑此页面](https://github.com/langchain-ai/docs/edit/main/src/langsmith/nest-traces.mdx) 或 [提交问题](https://github.com/langchain-ai/docs/issues/new/choose)。
  </Callout>
</div>
