跳到主要内容
本指南将介绍在使用自定义模型或自定义输入/输出格式时,如何将 LLM 调用记录到 LangSmith。为了充分利用 LangSmith 的 LLM 追踪处理功能,您应该以指定的格式之一记录 LLM 追踪。 LangSmith 为 LLM 追踪提供以下优势:
  • 消息列表的丰富、结构化渲染
  • 每个 LLM 调用、每个追踪以及随时间变化的追踪的令牌和成本跟踪
如果您没有以建议的格式记录 LLM 追踪,您仍然可以将数据记录到 LangSmith,但可能无法以预期的方式处理或渲染。 如果您使用 LangChain OSS 调用语言模型或 LangSmith 包装器(OpenAIAnthropic),这些方法将自动以正确的格式记录追踪。
本页上的示例使用 traceable 装饰器/包装器来记录模型运行(这是 Python 和 JS/TS 的推荐方法)。但是,如果您直接使用 RunTreeAPI,同样的原理也适用。

消息格式

追踪自定义模型或自定义输入/输出格式时,它必须遵循 LangChain 格式、OpenAI 完成格式或 Anthropic 消息格式。有关更多详细信息,请参阅 OpenAI 聊天完成Anthropic 消息 文档。LangChain 格式是

示例

 inputs = {
  "messages": [
    {
      "role": "user",
      "content": [
        {
          "type": "text",
          "text": "Hi, can you tell me the capital of France?"
        }
      ]
    }
  ]
}

outputs = {
  "messages": [
    {
      "role": "assistant",
      "content": [
        {
          "type": "text",
          "text": "The capital of France is Paris."
        },
        {
          "type": "reasoning",
          "text": "The user is asking about..."
        }
      ]
    }
  ]
}

将自定义 I/O 格式转换为 LangSmith 兼容格式

如果您使用自定义输入或输出格式,可以使用 @traceable 装饰器 (Python) 或 traceable 函数 (TS) 上的 process_inputs/processInputsprocess_outputs/processOutputs 函数将其转换为 LangSmith 兼容格式。 process_inputs/processInputsprocess_outputs/processOutputs 接受函数,这些函数允许您在将特定追踪的输入和输出记录到 LangSmith 之前对其进行转换。它们可以访问追踪的输入和输出,并可以返回一个包含已处理数据的新字典。 以下是如何使用 process_inputsprocess_outputs 将自定义 I/O 格式转换为 LangSmith 兼容格式的样板示例:

在追踪中识别自定义模型

使用自定义模型时,建议同时提供以下 metadata 字段,以便在查看追踪和进行过滤时识别模型。
  • ls_provider: 模型的提供商,例如“openai”、“anthropic”等。
  • ls_model_name: 模型的名称,例如“gpt-4o-mini”、“claude-3-opus-20240229”等。
from langsmith import traceable

inputs = [
    {"role": "system", "content": "You are a helpful assistant."},
    {"role": "user", "content": "I'd like to book a table for two."},
]
output = {
    "choices": [
        {
            "message": {
                "role": "assistant",
                "content": "Sure, what time would you like to book the table for?"
            }
        }
    ]
}

@traceable(
    run_type="llm",
    metadata={"ls_provider": "my_provider", "ls_model_name": "my_model"}
)
def chat_model(messages: list):
    return output

chat_model(inputs)
此代码将记录以下追踪
LangSmith UI showing an LLM call trace called ChatOpenAI with a system and human input followed by an AI Output.
如果您实现自定义流式聊天模型,可以将输出“简化”为与非流式版本相同的格式。目前仅在 Python 中支持此功能。
def _reduce_chunks(chunks: list):
    all_text = "".join([chunk["choices"][0]["message"]["content"] for chunk in chunks])
    return {"choices": [{"message": {"content": all_text, "role": "assistant"}}]}

@traceable(
    run_type="llm",
    reduce_fn=_reduce_chunks,
    metadata={"ls_provider": "my_provider", "ls_model_name": "my_model"}
)
def my_streaming_chat_model(messages: list):
    for chunk in ["Hello, " + messages[1]["content"]]:
        yield {
            "choices": [
                {
                    "message": {
                        "content": chunk,
                        "role": "assistant",
                    }
                }
            ]
        }

list(
    my_streaming_chat_model(
        [
            {"role": "system", "content": "You are a helpful assistant. Please greet the user."},
            {"role": "user", "content": "polly the parrot"},
        ],
    )
)
如果 ls_model_name 不存在于 extra.metadata 中,则可能会使用 extra.metadata 中的其他字段来估计令牌计数。以下字段按优先级顺序使用
  1. metadata.ls_model_name
  2. inputs.model
  3. inputs.model_name
要了解有关如何使用 metadata 字段的更多信息,请参阅 添加元数据和标签 指南。

提供令牌和成本信息

当提供令牌计数时,LangSmith 通过使用模型定价表自动计算成本。要了解 LangSmith 如何计算基于令牌的成本,请参阅本指南 许多模型将令牌计数作为响应的一部分。您可以通过以下两种方式之一向 LangSmith 提供令牌计数:
  1. 在您的追踪函数中提取用法并设置运行元数据上的 usage_metadata 字段。
  2. 在您的追踪函数输出中返回一个 usage_metadata 字段。
在这两种情况下,您发送的 usage metadata 应包含以下 LangSmith 识别字段的子集
您不能设置除以下列出的字段之外的任何字段。您不需要包含所有字段。
class UsageMetadata(TypedDict, total=False):
    input_tokens: int
    """The number of tokens used for the prompt."""
    output_tokens: int
    """The number of tokens generated as output."""
    total_tokens: int
    """The total number of tokens used."""
    input_token_details: dict[str, float]
    """The details of the input tokens."""
    output_token_details: dict[str, float]
    """The details of the output tokens."""
    input_cost: float
    """The cost of the input tokens."""
    output_cost: float
    """The cost of the output tokens."""
    total_cost: float
    """The total cost of the tokens."""
    input_cost_details: dict[str, float]
    """The cost details of the input tokens."""
    output_cost_details: dict[str, float]
    """The cost details of the output tokens."""
请注意,用法数据还可以包括成本信息,以防您不想依赖 LangSmith 基于令牌的成本计算公式。这对于定价不按令牌类型线性变化的模型非常有用。

设置运行元数据

您可以在追踪函数中使用用法信息修改当前运行的元数据。这种方法的优点是您无需更改追踪函数的运行时输出。这是一个示例
需要 langsmith>=0.3.43 (Python) 和 langsmith>=0.3.30 (JS/TS)。
from langsmith import traceable, get_current_run_tree

inputs = [
    {"role": "system", "content": "You are a helpful assistant."},
    {"role": "user", "content": "I'd like to book a table for two."},
]

@traceable(
    run_type="llm",
    metadata={"ls_provider": "my_provider", "ls_model_name": "my_model"}
)
def chat_model(messages: list):
    llm_output = {
        "choices": [
            {
                "message": {
                    "role": "assistant",
                    "content": "Sure, what time would you like to book the table for?"
                }
            }
        ],
        "usage_metadata": {
            "input_tokens": 27,
            "output_tokens": 13,
            "total_tokens": 40,
            "input_token_details": {"cache_read": 10},
            # If you wanted to specify costs:
            # "input_cost": 1.1e-6,
            # "input_cost_details": {"cache_read": 2.3e-7},
            # "output_cost": 5.0e-6,
        },
    }
    run = get_current_run_tree()
    run.set(usage_metadata=llm_output["usage_metadata"])
    return llm_output["choices"][0]["message"]

chat_model(inputs)

设置运行输出

您可以向函数的响应添加一个 usage_metadata 键来设置手动令牌计数和成本。
from langsmith import traceable

inputs = [
    {"role": "system", "content": "You are a helpful assistant."},
    {"role": "user", "content": "I'd like to book a table for two."},
]
output = {
    "choices": [
        {
            "message": {
                "role": "assistant",
                "content": "Sure, what time would you like to book the table for?"
            }
        }
    ],
    "usage_metadata": {
        "input_tokens": 27,
        "output_tokens": 13,
        "total_tokens": 40,
        "input_token_details": {"cache_read": 10},
        # If you wanted to specify costs:
        # "input_cost": 1.1e-6,
        # "input_cost_details": {"cache_read": 2.3e-7},
        # "output_cost": 5.0e-6,
    },
}

@traceable(
    run_type="llm",
    metadata={"ls_provider": "my_provider", "ls_model_name": "my_model"}
)
def chat_model(messages: list):
    return output

chat_model(inputs)

首个令牌时间 (Time-to-first-token)

如果您正在使用 traceable 或我们的某个 SDK 包装器,LangSmith 将自动填充流式 LLM 运行的首次令牌时间。但是,如果您直接使用 RunTree API,则需要向运行树添加一个 new_token 事件,才能正确填充首次令牌时间。 以下是一个示例:
from langsmith.run_trees import RunTree
run_tree = RunTree(
    name="CustomChatModel",
    run_type="llm",
    inputs={ ... }
)
run_tree.post()
llm_stream = ...
first_token = None
for token in llm_stream:
    if first_token is None:
      first_token = token
      run_tree.add_event({
        "name": "new_token"
      })
run_tree.end(outputs={ ... })
run_tree.patch()

以编程方式连接这些文档到 Claude、VSCode 等,通过 MCP 获取实时答案。
© . This site is unofficial and not affiliated with LangChain, Inc.