Skip to main content
当您直接调用 LLM,而不通过 LangChain 或支持的集成时,需要提供特定的元数据,以便 LangSmith 能够显示令牌计数、计算成本,并让您在Playground中使用正确的提供商和模型打开运行记录。 一个功能完整的 LLM 跟踪需要满足四个要求:
要求操作实现功能
1. 设置 run_type="llm"@traceable 传递 run_type="llm"LLM 专用渲染、令牌/成本显示
2. 格式化输入/输出使用 OpenAI、Anthropic 或 LangChain 消息格式结构化消息渲染、Playground 支持
3. 设置 ls_providerls_model_namemetadata 中同时传递两者成本跟踪、Playground 模型选择
4. 提供令牌计数在运行记录上设置 usage_metadata令牌计数和成本计算
如果您使用 LangChain OSS、OpenAI 包装器Anthropic 包装器,这些细节会自动处理。本页示例使用 traceable 装饰器/包装器(Python 和 JS/TS 的推荐方法)。如果您直接使用 RunTreeAPI,同样适用这些要求。

消息格式

当跟踪自定义模型或自定义输入/输出格式时,必须遵循 LangChain 格式、OpenAI 补全格式或 Anthropic 消息格式。更多详情,请参考 OpenAI 聊天补全Anthropic 消息 文档。LangChain 格式如下:
 inputs = {
  "messages": [
    {
      "role": "user",
      "content": [
        {
          "type": "text",
          "text": "你好,能告诉我法国的首都是哪里吗?"
        }
      ]
    }
  ]
}

outputs = {
  "messages": [
    {
      "role": "assistant",
      "content": [
        {
          "type": "text",
          "text": "法国的首都是巴黎。"
        },
        {
          "type": "reasoning",
          "text": "用户正在询问..."
        }
      ]
    }
  ]
}

将自定义 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-4.1-mini”、“claude-3-opus-20240229” 等。
from langsmith import traceable

inputs = [
    {"role": "system", "content": "你是一个有用的助手。"},
    {"role": "user", "content": "我想预订一张两人桌。"},
]
output = {
    "choices": [
        {
            "message": {
                "role": "assistant",
                "content": "好的,您想预订什么时间?"
            }
        }
    ]
}

@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 显示一个名为 ChatOpenAI 的 LLM 调用跟踪,包含系统和人机输入,后跟 AI 输出。
如果您实现自定义的流式聊天模型,可以将输出“归约”为非流式版本相同的格式。目前仅 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": "你是一个有用的助手。请向用户问好。"},
            {"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 字段的信息,请参阅 添加元数据和标签 指南。

提供令牌和成本信息

令牌计数支持成本计算,并显示在跟踪 UI 中。有两种提供方式:
  • 在运行树上设置 usage_metadata:在您的 @traceable 函数内部调用 get_current_run_tree() / getCurrentRunTree() 并设置 usage_metadata 字段。这不会改变您函数的返回值。
  • 在输出中返回 usage_metadata:在函数返回的字典中包含 usage_metadata 作为顶级键。

支持的 usage_metadata 字段

字段类型描述
input_tokensint总输入/提示令牌数
output_tokensint总输出/补全令牌数
total_tokensint输入 + 输出的总和(可选,可以推断)
input_token_detailsobject细分:cache_readcache_creationaudiotextimage
output_token_detailsobject细分:reasoningaudiotextimage
要直接发送成本(用于非线性定价),您还可以包含 input_costoutput_costtotal_cost 字段。有关配置模型定价和在 UI 中查看成本的详细信息,请参阅 成本跟踪

首令牌时间

如果您使用 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()