title: 记录 LLM 调用 sidebarTitle: 记录 LLM 调用


本指南说明在使用自定义模型或自定义输入/输出格式时,如何把 LLM 调用记录到 LangSmith。为了充分利用 LangSmith 的 LLM 跟踪能力,你应按照指定格式之一记录追踪数据。 LangSmith 为 LLM 跟踪提供以下优势:
  • 消息列表的丰富结构化渲染
  • 每次 LLM 调用、每次跟踪以及跨时间跟踪的令牌和成本跟踪
如果没有按照推荐格式记录 LLM 跟踪,数据仍会写入 LangSmith,但可能无法以预期方式处理或渲染。 如果你使用 LangChain OSS 调用语言模型,或使用 LangSmith 包装器(OpenAIAnthropic),这些调用会自动以正确格式记录追踪。
本页示例使用 traceable 装饰器/包装器记录模型运行(推荐的 Python 与 JS/TS 用法)。如果你直接使用 RunTreeAPI,思路完全相同。

消息格式

记录自定义模型或自定义输入/输出格式时,需要遵循 LangChain、OpenAI completions 或 Anthropic messages 其中之一。详情可参考 OpenAI Chat CompletionsAnthropic Messages 文档。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 兼容格式。 这些函数可在写入 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.
若实现自定义流式 chat_model,可以将增量输出汇总为非流式格式,目前仅 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"},
        ],
    )
)
extra.metadata 中缺少 ls_model_name,LangSmith 会按以下优先级使用其他字段估算 Token:
  1. metadata.ls_model_name
  2. inputs.model
  3. inputs.model_name
关于 metadata 字段的更多用法参见 添加元数据与标签

提供 Token 与成本信息

当提供 Token 统计数据时,LangSmith 会根据 模型定价表 自动计算成本。更多计算方式参考成本指南 许多模型会在响应中包含 Token 统计。可通过以下两种方式提供给 LangSmith:
  1. 在跟踪函数内提取使用情况,并写入运行的 usage_metadata
  2. 在函数输出中直接返回 usage_metadata
两种方式都应使用下列字段的子集:
请勿设置未列出的字段,也无需包含全部字段。
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 默认公式的模型(例如按调用计费或非线性 token 计费)。

设置运行元数据

可以在被跟踪函数内部修改当前运行的元数据,无需改变返回值。
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,手动指定 Token 与成本。
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)

首 Token 延迟

使用 traceable 或 SDK 包装器时,LangSmith 会为流式 LLM 运行自动填充首 Token 延迟。 若直接使用 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()

Connect these docs programmatically to Claude, VSCode, and more via MCP for real-time answers.