Skip to content
LangSmith 文档版本 跟随上游 源快照 a7419e09 更新于 2026-10-18

记录 LLM 调用

当你直接在 LangChain 或 LangSmith 支持的集成之外调用 LLM 时,需要提供特定元数据,以便 LangSmith 显示 token 计数、计算成本,并让你以正确的提供程序和模型打开运行,并在 Playground 中查看。

完全可用的 LLM 追踪有四项要求:

要求要做什么启用的功能
1. 设置 run_type="llm"将 run_type="llm" 传给 @traceableLLM 专用渲染、token/成本显示
2. 格式化输入/输出使用 OpenAI、Anthropic 或 LangChain 消息格式结构化消息渲染、Playground 支持
3. 设置 ls_provider 和 ls_model_name在 metadata 中传入两者成本跟踪、Playground 模型选择
4. 提供 token 计数在运行上设置 usage_metadatatoken 计数和成本计算

NOTE

如果你使用 LangChain OSS、OpenAI 包装器或 Anthropic 包装器,这些细节会自动处理。

本页示例使用 traceable 装饰器/包装器(Python 和 JS/TS 的推荐方式)。如果你直接使用 RunTree 或 API,也适用相同的要求。

消息格式

追踪自定义模型或自定义输入/输出格式时,它必须遵循 LangChain 格式、OpenAI completions 格式或 Anthropic messages 格式。有关更多细节,请参阅 OpenAI Chat Completions 或 Anthropic Messages 文档。LangChain 格式如下:

LangChain format

messagesarray必填

包含对话内容的消息列表。

rolestring必填

标识消息类型。以下之一:system | reasoning | user | assistant | tool

contentarray必填

消息内容。类型化字典的列表。

内容选项

typestring必填

以下之一:text | image | file | audio | video | tool_call | server_tool_call | server_tool_result。

text

typeliteral('text')必填

textstring必填

文本内容。

annotationsobject[]

文本的注解列表

extrasobject

其他提供程序特定数据。

reasoning

typeliteral('reasoning')必填

textstring必填

文本内容。

extrasobject

其他提供程序特定数据。

image

typeliteral('image')必填

urlstring

指向图像位置的 URL。

base64string必填

Base64 编码的图像数据。

idstring

指向外部存储图像(例如提供程序的文件系统或存储桶中)的引用 ID。

mime_typestring

图像 MIME 类型(例如 image/jpeg、image/png)。

文件(例如 PDF)

typeliteral('file')必填

urlstring

指向文件的 URL。

base64string必填

Base64 编码的文件数据。

idstring

指向外部存储文件(例如提供程序的文件系统或存储桶中)的引用 ID。

mime_typestring

文件 MIME 类型(例如 application/pdf)。

audio

typeliteral('audio')必填

urlstring

指向音频文件的 URL。

base64string必填

Base64 编码的音频数据。

idstring

指向外部存储音频文件(例如提供程序的文件系统或存储桶中)的引用 ID。

mime_typestring

音频 MIME 类型(例如 audio/mpeg、audio/wav)。

video

typeliteral('video')必填

urlstring

指向视频文件的 URL。

base64string必填

Base64 编码的视频数据。

idstring

指向外部存储视频文件(例如提供程序的文件系统或存储桶中)的引用 ID。

mime_typestring

视频 MIME 类型(例如 video/mp4、video/webm)。

tool_call

typeliteral('tool_call')必填

namestring

argsobject必填

要传递给工具的参数。

idstring

此工具调用的唯一标识符。

server_tool_call

typeliteral('server_tool_call')必填

idstring必填

此工具调用的唯一标识符。

namestring必填

要调用的工具名称。

argsobject必填

要传递给工具的参数。

server_tool_result

typeliteral('server_tool_result')必填

tool_call_idstring必填

对应服务器工具调用的标识符。

idstring

此工具调用的唯一标识符。

statusstring必填

服务器端工具的执行状态。以下之一:success | error。

output

已执行工具的输出。

tool_call_idstring

必须与先前 assistant 消息的 tool_calls[i] 条目中的 id 匹配。仅当 role 为 tool 时有效。

usage_metadataobject

使用此字段随模型输出发送 token 计数和/或成本。详情请参阅提供 token 和成本信息。

python
 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..."
        }
      ]
    }
  ]
}
python
input = {
  "messages": [
    {
      "role": "user",
      "content": [
        {
          "type": "text",
          "text": "What's the weather in San Francisco?"
        }
      ]
    }
  ]
}

outputs = {
  "messages": [
    {
      "role": "assistant",
      "content": [{"type": "tool_call", "name": "get_weather", "args": {"city": "San Francisco"}, "id": "call_1"}],
    },
    {
      "role": "tool",
      "tool_call_id": "call_1",
      "content": [
        {
          "type": "text",
          "text": "{\"temperature\": \"18°C\", \"condition\": \"Sunny\"}"
        }
      ]
    },
    {
      "role": "assistant",
      "content": [
        {
          "type": "text",
          "text": "The weather in San Francisco is 18°C and sunny."
        }
      ]
    }
  ]
}
python
inputs = {
  "messages": [
    {
      "role": "user",
      "content": [
        {
          "type": "text",
          "text": "What breed is this dog?"
        },
        {
          "type": "image",
          "url": "https://fastly.picsum.photos/id/237/200/300.jpg?hmac=TmmQSbShHz9CdQm0NkEjx1Dyh_Y984R9LpNrpvH2D_U",
          # alternative to a url, you can provide a base64 encoded image
          # "base64": "<base64 encoded image>",
          "mime_type": "image/jpeg",
        }
      ]
    }
  ]
}

outputs = {
  "messages": [
    {
      "role": "assistant",
      "content": [
        {
          "type": "text",
          "text": "This looks like a Black Labrador."
        }
      ]
    }
  ]
}
python
input = {
  "messages": [
    {
      "role": "user",
      "content": [
        {
          "type": "text",
          "text": "What is the price of AAPL?"
        }
      ]
    }
  ]
}

output = {
  "messages": [
    {
      "role": "assistant",
      "content": [
        {
          "type": "server_tool_call",
          "name": "web_search",
          "args": {
            "query": "price of AAPL",
            "type": "search"
          },
          "id": "call_1"
        },
        {
          "type": "server_tool_result",
          "tool_call_id": "call_1",
          "status": "success"
        },
        {
          "type": "text",
          "text": "The price of AAPL is $150.00"
        }
      ]
    }
  ]
}

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

如果你使用自定义输入或输出格式,可以使用 @traceable 装饰器(Python)或 traceable 函数(TS)上的 process_inputs/processInputs 和 process_outputs/processOutputs 函数,将其转换为 LangSmith 兼容格式。

process_inputs/processInputs 和 process_outputs/processOutputs 接受函数,允许你在特定追踪的输入和输出记录到 LangSmith 之前对其进行转换。这些函数可以访问追踪的输入和输出,并可以返回包含处理后数据的新字典。

以下是一个样板示例,展示如何使用 process_inputs 和 process_outputs 将自定义 I/O 格式转换为 LangSmith 兼容格式:

python
class OriginalInputs(BaseModel):
    """Your app's custom request shape"""

class OriginalOutputs(BaseModel):
    """Your app's custom response shape."""

class LangSmithInputs(BaseModel):
    """The input format LangSmith expects."""

class LangSmithOutputs(BaseModel):
    """The output format LangSmith expects."""

def process_inputs(inputs: dict) -> dict:
    """Dict -> OriginalInputs -> LangSmithInputs -> dict"""

def process_outputs(output: Any) -> dict:
    """OriginalOutputs -> LangSmithOutputs -> dict"""

@traceable(run_type="llm", process_inputs=process_inputs, process_outputs=process_outputs)
def chat_model(inputs: dict) -> dict:
    """
    Your app's model call. Keeps your custom I/O shape.
    The decorators call process_* to log LangSmith-compatible format.
    """

在追踪中标识自定义模型

使用自定义模型时,建议还提供以下 metadata 字段,以便在查看追踪和筛选时标识模型。

  • ls_provider:模型提供程序,例如 "openai"、"anthropic"。
  • ls_model_name:模型名称,例如 "gpt-5.4-mini"、"claude-opus-4-8"。
python
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)
typescript
import { traceable } from "langsmith/traceable";

const messages = [
    { role: "system", content: "You are a helpful assistant." },
    { role: "user", content: "I'd like to book a table for two." }
];
const 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,
    },
};

// Can also use one of:
// const output = {
//     message: {
//         role: "assistant",
//         content: "Sure, what time would you like to book the table for?"
//     }
// };
//
// const output = {
//     role: "assistant",
//     content: "Sure, what time would you like to book the table for?"
// };
//
// const output = ["assistant", "Sure, what time would you like to book the table for?"];

const chatModel = traceable(
    async ({ messages }: { messages: { role: string; content: string }[] }) => {
        return output;
    },
    {
        run_type: "llm",
        name: "chat_model",
        metadata: {
            ls_provider: "my_provider",
            ls_model_name: "my_model"
        }
    }
);

await chatModel({ messages });

如果你实现自定义流式 chat_model,可以将输出“归约”为非流式版本相同的格式。这仅在 Python 中受支持:

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": "assistant"},
        ],
    )
)

TIP

对于 LangSmith 识别模型并计算自定义 LLM 追踪的成本,在 metadata 中设置 ls_model_name 是必需的。缺少它时,token 计数可能仍会被记录,但不会估算成本。

要详细了解如何使用 metadata 字段,请参阅添加元数据和标签指南。要自定义自定义 agent 运行在 Trajectory 视图中的显示方式,请参阅自定义 Trajectory 视图。

提供 token 和成本信息

token 计数支持成本计算,LangSmith 会在 Tracing Projects UI 中显示这些信息。有两种方式可以提供它们:

  • 在运行树上设置 usage_metadata:在你的函数内调用 get_current_run_tree() / getCurrentRunTree(),然后设置 usage_metadata 字段,该函数通过 @traceable 包装。这不会更改函数的返回值。
  • 在输出中返回 usage_metadata:将 usage_metadata 作为函数返回字典中的顶级键。

支持的 usage_metadata 字段

字段类型描述
input_tokensint输入/提示词 token 总数
output_tokensint输出/补全 token 总数
total_tokensint输入 + 输出之和(可选,可推断)
input_token_detailsobject细分:cache_read、cache_creation、cache_read_over_200k、ephemeral_5m_input_tokens、ephemeral_1h_input_tokens、audio、text、image
output_token_detailsobject细分:reasoning、audio、text、image

要直接发送成本(用于非线性定价),还可以包含 input_cost、output_cost 和 total_cost 字段。有关配置模型定价和在 UI 中查看成本的详细信息,请参阅成本跟踪页面。

首 token 时间

如果你使用 traceable 或某个 SDK 包装器,LangSmith 会自动为流式 LLM 运行填充首 token 时间。但是,如果你直接使用 RunTree API,则需要向运行树添加 new_token 事件,以便正确填充首 token 时间。

以下是一个示例:

python
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()
typescript
import { RunTree } from "langsmith";
const runTree = new RunTree({
    name: "CustomChatModel",
    run_type: "llm",
    inputs: { ... },
});
await runTree.postRun();
const llmStream = ...;
let firstToken;
for (const token of llmStream) {
    if (firstToken == null) {
        firstToken = token;
        runTree.addEvent({ name: "new_token" });
    }
}
await runTree.end({
    outputs: { ... },
});
await runTree.patchRun();

相关


BytesRouter 是社区翻译站点,所有译文仅供参考,一切以官方英文原文为准。