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

成本追踪

大规模构建智能体会带来不可忽视且难以追踪的按用量计费成本。LangSmith 会自动记录主要服务商的 LLM token 用量和成本,同时也允许你为任何其他组件提交自定义成本数据。

这为你提供了一个覆盖整个应用的统一成本视图,便于监控、理解和排查你的支出。

NOTE

要为评估器运行设置 LLM 成本上限,请参阅追踪并限制评估器支出。评估器支出追踪与限额使用在模型定价下配置的按模型定价。

在 LangSmith 界面中查看成本

在 LangSmith 界面 中,你可以通过三种方式查看用量和支出:单条追踪内的明细、项目统计中的聚合指标,以及仪表盘。

Token 与成本明细

界面将 token 用量和成本划分为三类:

  • Input:发送给模型的提示词中的 token。子类型包括:缓存读取、文本 token、图像 token 等。
  • Output:模型响应中生成的 token。子类型包括:推理 token、文本 token、图像 token 等。
  • Other:来自工具调用、检索步骤或任何自定义运行的成本。

在界面中将鼠标悬停在成本区块上即可查看详细明细。在可用情况下,每个区块还会按子类型进一步分类。

成本提示成本提示

你可以在 LangSmith 界面的各处查看这些明细:

在追踪树中

追踪树展示了 token 用量和成本最详细的视图(针对单条追踪)。它显示整条追踪的总用量、每个父运行的聚合值,以及每个子运行的 token 和成本明细。

在追踪项目中打开任意运行即可查看其追踪树。

NOTE

跨线程追踪成本时,请确保所有子运行都包含线程元数据(session_id 或 thread_id)。如果子运行缺少线程元数据,这些运行的 token 计数和成本将不会被计入线程级别的聚合。有关设置线程元数据的详细信息,请参阅配置线程。

在项目统计中

项目统计面板显示项目中所有追踪的 token 总用量和总成本。

在仪表盘中

仪表盘可帮助你探索成本和 token 用量随时间变化的趋势。追踪项目的预置仪表盘会显示总成本,以及按输入和输出 token 划分的成本明细。

你也可以在自定义仪表盘中配置自定义成本追踪图表。

成本追踪

你可以通过两种方式追踪成本:

  1. 自动:根据 LLM 调用的 token 计数和模型价格推导得出。
  2. 手动:直接在任意运行上指定,包括非 LLM 类型。
方式运行类型:LLM运行类型:其他
自动不适用。
手动如果 LLM 调用成本是非线性的(例如遵循自定义成本函数)为任意运行类型发送成本,例如工具调用、检索步骤

LLM 调用:基于 token 计数自动追踪成本

要根据 token 用量自动计算成本,你需要提供 token 计数、模型与服务商以及模型价格。

NOTE

如果你使用 LangChain 调用 LLM、对 OpenAI 或 Anthropic(或 OpenAI 兼容模型)使用 @traceable,或者使用 LangSmith 提供的 OpenAI 或 Anthropic 封装,可跳过本节。

  1. 发送 token 计数。许多模型会在响应中返回 token 计数。你必须提取该信息,并通过以下任一方法将其包含在你的运行中:

    • 在运行的元数据上设置 usage_metadata 字段。这种方式的优点是无需更改被追踪函数的运行时输出:
 ```python [Python]
 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):
     # Imagine this is the real model output format your application expects
     assistant_message = {
         "role": "assistant",
         "content": "Sure, what time would you like to book the table for?"
     }

     # Token usage you compute or receive from the provider
     token_usage = {
         "input_tokens": 27,
         "output_tokens": 13,
         "total_tokens": 40,
         "input_token_details": {"cache_read": 10}
     }

     # Attach token usage to the LangSmith run
     run = get_current_run_tree()
     run.set(usage_metadata=token_usage)

     return assistant_message

 chat_model(inputs)
 ```

 ```typescript [TypeScript]
 import { traceable, getCurrentRunTree } from "langsmith/traceable";

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

 const chatModel = traceable(
 async ({ messages }) => {
     // The output your application expects
     const assistantMessage = {
     role: "assistant",
     content: "Sure, what time would you like to book the table for?",
     };

     // Token usage you compute or receive from the provider
     const tokenUsage = {
     input_tokens: 27,
     output_tokens: 13,
     total_tokens: 40,
     input_token_details: { cache_read: 10 },
     };

     // Attach usage to the LangSmith run
     const runTree = getCurrentRunTree();
     runTree.metadata.usage_metadata = tokenUsage;

     return assistantMessage;
 },
 {
     run_type: "llm",
     name: "chat_model",
     metadata: {
     ls_provider: "my_provider",
     ls_model_name: "my_model",
     },
 }
 );

 await chatModel({ messages: inputs });
 ```

 ```java [Java]
 import com.langchain.smith.client.LangsmithClient;
 import com.langchain.smith.client.okhttp.LangsmithOkHttpClient;
 import com.langchain.smith.tracing.RunTree;
 import com.langchain.smith.tracing.RunType;
 import com.langchain.smith.tracing.TraceConfig;
 import com.langchain.smith.tracing.Tracing;
 import java.util.Arrays;
 import java.util.HashMap;
 import java.util.List;
 import java.util.Map;
 import java.util.concurrent.ExecutorService;
 import java.util.concurrent.Executors;
 import java.util.concurrent.TimeUnit;
 import java.util.function.Function;

 class CostTrackingUsageMetadataRun {
   public static void main(String[] args) throws InterruptedException {
     if (System.getenv("LANGSMITH_API_KEY") == null
         || System.getenv("LANGSMITH_API_KEY").isBlank()) {
       System.out.println(
           "[cost-tracking-usage-metadata-run] Skipping (LANGSMITH_API_KEY is not set).");
       return;
     }

     LangsmithClient langsmith = LangsmithOkHttpClient.fromEnv();
     ExecutorService executor = Executors.newSingleThreadExecutor();

     try {
       List<Map<String, String>> inputs =
           Arrays.asList(
               message("system", "You are a helpful assistant."),
               message("user", "I'd like to book a table for two."));

       Map<String, Object> metadata = new HashMap<>();
       metadata.put("ls_provider", "my_provider");
       metadata.put("ls_model_name", "my_model");

       Function<List<Map<String, String>>, Map<String, String>> chatModel =
           Tracing.traceFunction(
               messages -> {
                 Map<String, String> assistantMessage =
                     message(
                         "assistant",
                         "Sure, what time would you like to book the table for?");

                 Map<String, Object> inputTokenDetails = new HashMap<>();
                 inputTokenDetails.put("cache_read", 10);

                 Map<String, Object> tokenUsage = new HashMap<>();
                 tokenUsage.put("input_tokens", 27);
                 tokenUsage.put("output_tokens", 13);
                 tokenUsage.put("total_tokens", 40);
                 tokenUsage.put("input_token_details", inputTokenDetails);

                 RunTree run = Tracing.getCurrentRunTree();
                 if (run != null) {
                   run.getMetadata().put("usage_metadata", tokenUsage);
                 }

                 return assistantMessage;
               },
               TraceConfig.builder()
                   .name("chat_model")
                   .runType(RunType.LLM)
                   .client(langsmith)
                   .executor(executor)
                   .metadata(metadata)
                   .build());

       chatModel.apply(inputs);
     } finally {
       executor.shutdown();
       if (!executor.awaitTermination(10, TimeUnit.SECONDS)) {
         throw new IllegalStateException("Timed out waiting for LangSmith traces to submit");
       }
     }
   }

   private static Map<String, String> message(String role, String content) {
     Map<String, String> message = new HashMap<>();
     message.put("role", role);
     message.put("content", content);
     return message;
   }
 }
 ```

 ```kotlin [Kotlin]
 import com.langchain.smith.client.okhttp.LangsmithOkHttpClient
 import com.langchain.smith.tracing.RunType
 import com.langchain.smith.tracing.TraceConfig
 import com.langchain.smith.tracing.getCurrentRunTree
 import com.langchain.smith.tracing.traceable
 import java.util.concurrent.Executors
 import java.util.concurrent.TimeUnit

 val langsmith = LangsmithOkHttpClient.fromEnv()
 val executor = Executors.newSingleThreadExecutor()

 fun message(role: String, content: String) = mapOf("role" to role, "content" to content)

 try {
     val inputs =
         listOf(
             message("system", "You are a helpful assistant."),
             message("user", "I'd like to book a table for two."),
         )

     val chatModel =
         traceable(
             { _: List<Map<String, String>> ->
                 val assistantMessage =
                     message(
                         "assistant",
                         "Sure, what time would you like to book the table for?",
                     )
                 val tokenUsage =
                     mapOf(
                         "input_tokens" to 27,
                         "output_tokens" to 13,
                         "total_tokens" to 40,
                         "input_token_details" to mapOf("cache_read" to 10),
                     )
                 getCurrentRunTree()?.metadata?.put("usage_metadata", tokenUsage)
                 assistantMessage
             },
             TraceConfig.builder()
                 .name("chat_model")
                 .runType(RunType.LLM)
                 .client(langsmith)
                 .executor(executor)
                 .metadata(
                     mapOf(
                         "ls_provider" to "my_provider",
                         "ls_model_name" to "my_model",
                     ),
                 )
                 .build(),
         )

     chatModel(inputs)
 } finally {
     executor.shutdown()
     check(executor.awaitTermination(10, TimeUnit.SECONDS)) {
         "Timed out waiting for LangSmith traces to submit"
     }
 }
 ```
 Java 和 Kotlin 示例使用了专用的 executor。关闭该 executor 并等待其终止,可确保后台追踪提交在进程退出之前完成。
  • 在被追踪函数的输出中返回 usage_metadata 字段。直接将 usage_metadata 键包含在被追踪函数返回的对象中。LangSmith 会从输出中提取它:
 ```python [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?"
             }
         }
     ],
     "usage_metadata": {
         "input_tokens": 27,
         "output_tokens": 13,
         "total_tokens": 40,
         "input_token_details": {"cache_read": 10}
     },
 }

 @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 [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,
     },
 };

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

 await chatModel({ messages });
 ```

 ```java [Java]
 import com.langchain.smith.client.LangsmithClient;
 import com.langchain.smith.client.okhttp.LangsmithOkHttpClient;
 import com.langchain.smith.tracing.RunType;
 import com.langchain.smith.tracing.TraceConfig;
 import com.langchain.smith.tracing.Tracing;
 import java.util.Arrays;
 import java.util.HashMap;
 import java.util.List;
 import java.util.Map;
 import java.util.concurrent.ExecutorService;
 import java.util.concurrent.Executors;
 import java.util.concurrent.TimeUnit;
 import java.util.function.Function;

 class CostTrackingUsageMetadataOutput {
   public static void main(String[] args) throws InterruptedException {
     if (System.getenv("LANGSMITH_API_KEY") == null
         || System.getenv("LANGSMITH_API_KEY").isBlank()) {
       System.out.println(
           "[cost-tracking-usage-metadata-output] Skipping (LANGSMITH_API_KEY is not set).");
       return;
     }

     LangsmithClient langsmith = LangsmithOkHttpClient.fromEnv();
     ExecutorService executor = Executors.newSingleThreadExecutor();

     try {
       List<Map<String, String>> messages =
           Arrays.asList(
               message("system", "You are a helpful assistant."),
               message("user", "I'd like to book a table for two."));

       Map<String, Object> metadata = new HashMap<>();
       metadata.put("ls_provider", "my_provider");
       metadata.put("ls_model_name", "my_model");

       Function<List<Map<String, String>>, Map<String, Object>> chatModel =
           Tracing.traceFunction(
               inputMessages -> output(),
               TraceConfig.builder()
                   .name("chat_model")
                   .runType(RunType.LLM)
                   .client(langsmith)
                   .executor(executor)
                   .metadata(metadata)
                   .build());

       chatModel.apply(messages);
     } finally {
       executor.shutdown();
       if (!executor.awaitTermination(10, TimeUnit.SECONDS)) {
         throw new IllegalStateException("Timed out waiting for LangSmith traces to submit");
       }
     }
   }

   private static Map<String, Object> output() {
     Map<String, Object> output = new HashMap<>();
     Map<String, Object> choice = new HashMap<>();
     choice.put(
         "message",
         message("assistant", "Sure, what time would you like to book the table for?"));
     output.put("choices", Arrays.asList(choice));

     Map<String, Object> inputTokenDetails = new HashMap<>();
     inputTokenDetails.put("cache_read", 10);

     Map<String, Object> usageMetadata = new HashMap<>();
     usageMetadata.put("input_tokens", 27);
     usageMetadata.put("output_tokens", 13);
     usageMetadata.put("total_tokens", 40);
     usageMetadata.put("input_token_details", inputTokenDetails);
     output.put("usage_metadata", usageMetadata);
     return output;
   }

   private static Map<String, String> message(String role, String content) {
     Map<String, String> message = new HashMap<>();
     message.put("role", role);
     message.put("content", content);
     return message;
   }
 }
 ```

 ```kotlin [Kotlin]
 import com.langchain.smith.client.okhttp.LangsmithOkHttpClient
 import com.langchain.smith.tracing.RunType
 import com.langchain.smith.tracing.TraceConfig
 import com.langchain.smith.tracing.traceable
 import java.util.concurrent.Executors
 import java.util.concurrent.TimeUnit

 val langsmith = LangsmithOkHttpClient.fromEnv()
 val executor = Executors.newSingleThreadExecutor()

 fun message(role: String, content: String) = mapOf("role" to role, "content" to content)

 val output =
     mapOf(
         "choices" to
             listOf(
                 mapOf(
                     "message" to
                         message(
                             "assistant",
                             "Sure, what time would you like to book the table for?",
                         ),
                 ),
             ),
         "usage_metadata" to
             mapOf(
                 "input_tokens" to 27,
                 "output_tokens" to 13,
                 "total_tokens" to 40,
                 "input_token_details" to mapOf("cache_read" to 10),
             ),
     )

 try {
     val messages =
         listOf(
             message("system", "You are a helpful assistant."),
             message("user", "I'd like to book a table for two."),
         )

     val chatModel =
         traceable(
             { _: List<Map<String, String>> -> output },
             TraceConfig.builder()
                 .name("chat_model")
                 .runType(RunType.LLM)
                 .client(langsmith)
                 .executor(executor)
                 .metadata(
                     mapOf(
                         "ls_provider" to "my_provider",
                         "ls_model_name" to "my_model",
                     ),
                 )
                 .build(),
         )

     chatModel(messages)
 } finally {
     executor.shutdown()
     check(executor.awaitTermination(10, TimeUnit.SECONDS)) {
         "Timed out waiting for LangSmith traces to submit"
     }
 }
 ```

无论采用哪种方式,用量元数据都应包含以下 LangSmith 可识别字段的子集:

用量元数据 schema 与成本计算

以下 usage_metadata 字典中的字段会被 LangSmith 识别。你可以直接查看完整的 Python 类型 或 TypeScript 接口。

input_tokensnumber

模型输入中使用的 token 数。所有输入 token 类型之和。

output_tokensnumber

模型响应中使用的 token 数。所有输出 token 类型之和。

total_tokensnumber

输入和输出中使用的 token 数。可选,可以推断。input_tokens + output_tokens 之和。

input_token_detailsobject

输入 token 类型的明细。键为 token 类型字符串,值为计数。示例 {"cache_read": 5}。

已知字段包括:audio、text、image、cache_read、cache_creation、cache_read_over_200k(Gemini)、ephemeral_5m_input_tokens、ephemeral_1h_input_tokens(Anthropic 临时缓存层级)。根据模型或提供商的不同,还可能有其他字段。

output_token_detailsobject

输出 token 类型的明细。键为 token 类型字符串,值为计数。示例 {"reasoning": 5}。

已知字段包括:audio、text、image、reasoning。根据模型或提供商的不同,还可能有其他字段。

input_costnumber

输入 token 的成本。

output_costnumber

输出 token 的成本。

total_costnumber

token 成本。可选,可以推断。input_cost + output_cost 之和。

input_cost_detailsobject

输入成本的明细。键为 token 类型字符串,值为成本金额。

output_cost_detailsobject

输出成本的明细。键为 token 类型字符串,值为成本金额。

成本计算

run 的成本按 token 类型从最具体到最粗略贪心计算。假设你将每 1M 输入 token 的价格设为 $2,其中 cache_read 输入 token 的明细价格为每 1M $1,输出 token 为每 1M $3。如果你上传了以下用量元数据:

python
{
"input_tokens": 20,
"input_token_details": {"cache_read": 5},
"output_tokens": 10,
"total_tokens": 30,
}

那么,token 成本将按如下方式计算:

python
# Notice that LangSmith computes the cache_read cost and then for any
# remaining input_tokens, the default input price is applied.
input_cost = 5 * 1e-6 + (20 - 5) * 2e-6  # 3.5e-5
output_cost = 10 * 3e-6  # 3e-5
total_cost = input_cost + output_cost  # 6.5e-5
  1. 指定模型名称。使用自定义模型时,需要在 run 的元数据 中指定以下字段,以便将 token 计数与成本关联起来。在查看追踪和过滤时,提供这些元数据字段也有助于识别模型。

    • ls_provider:模型提供商,例如 “openai”、“anthropic”
    • ls_model_name:模型名称,例如 “gpt-5.4-mini”、“claude-opus-4-8”
  2. 设置模型价格。LangSmith 使用其模型价格表将模型名称映射为每 token 价格,以根据 token 计数计算成本。

NOTE

该表包含大多数 OpenAI、Anthropic 和 Gemini 模型的价格信息。如果你有自定义价格,可以为默认模型新建价格条目或覆盖其价格。

对于不同 token 类型有不同价格的模型(例如多模态或缓存 token),你可以按 token 类型分别指定价格明细。将鼠标悬停在 Input price 和 Output price 条目旁的 ... 上,即可查看按 token 类型划分的价格明细。

NOTE

LangSmith 不会把模型价格表的更新反映到已经记录的追踪成本中。不支持回溯补算模型价格变更。

新建或修改现有模型价格条目

要修改默认模型价格,请创建一个与默认条目具有相同 model、provider 和 match pattern 的新条目。

要在模型价格表中新建条目,请点击右上角的 + Model 按钮。

在这里,你可以指定以下字段:

  • Model Name:模型的可读名称。
  • Input Price:每 1M 输入 token 的成本。该数值乘以提示词中的 token 数来计算提示词成本。
  • Input Price Breakdown(可选):每种不同输入 token 类型的价格明细,例如 cache_read、video、audio。
  • Output Price:每 1M 输出 token 的成本。该数值乘以补全中的 token 数来计算补全成本。
  • Output Price Breakdown(可选):每种不同输出 token 类型的价格明细,例如 reasoning、image 等。
  • Model Activation Date(可选):价格生效的日期。只有此日期之后的 run 才会应用该模型价格。
  • Match Pattern:用于匹配模型名称的正则表达式。它用于匹配 run 元数据中 ls_model_name 的值。
  • Provider(可选):模型提供商。如果指定,将与 run 元数据中的 ls_provider 进行匹配。

设置好模型价格表后,LangSmith 会根据 LLM 调用中提供的 token 计数,自动计算并汇总追踪中基于 token 的成本。

LLM 调用:直接发送成本

Gemini 2.5 Pro Preview 和 Gemini 2.5 Pro 使用阶梯式成本函数,LangSmith 默认支持。对于其他具有非线性定价的模型,请在客户端计算成本,并按下述代码将其作为 usage_metadata 发送:

python
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": {
            # Specify cost (in dollars) for the inputs and outputs
            "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)
typescript
import { traceable, getCurrentRunTree } 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 chatModel = traceable(
  async (messages: { role: string; content: string }[]) => {
    const llmOutput = {
      choices: [
        {
          message: {
            role: "assistant",
            content: "Sure, what time would you like to book the table for?",
          },
        },
      ],
      // Specify cost (in dollars) for the inputs and outputs
      usage_metadata: {
        input_cost: 1.1e-6,
        input_cost_details: { cache_read: 2.3e-7 },
        output_cost: 5.0e-6,
      },
    };

    // Attach usage metadata to the run
    const runTree = getCurrentRunTree();
    runTree.metadata.usage_metadata = llmOutput.usage_metadata;

    // Return only the assistant message
    return llmOutput.choices[0].message;
  },
  {
    run_type: "llm",
    name: "chat_model",
    metadata: {
      ls_provider: "my_provider",
      ls_model_name: "my_model",
    },
  }
);

await chatModel(messages);
java
import com.langchain.smith.client.LangsmithClient;
import com.langchain.smith.client.okhttp.LangsmithOkHttpClient;
import com.langchain.smith.tracing.RunTree;
import com.langchain.smith.tracing.RunType;
import com.langchain.smith.tracing.TraceConfig;
import com.langchain.smith.tracing.Tracing;
import java.util.Arrays;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.TimeUnit;
import java.util.function.Function;

class CostTrackingLlmCostDirect {
  public static void main(String[] args) throws InterruptedException {
    LangsmithClient langsmith = LangsmithOkHttpClient.fromEnv();
    ExecutorService executor = Executors.newSingleThreadExecutor();

    try {
      List<Map<String, String>> messages =
          Arrays.asList(
              message("system", "You are a helpful assistant."),
              message("user", "I'd like to book a table for two."));

      Map<String, Object> metadata = new HashMap<>();
      metadata.put("ls_provider", "my_provider");
      metadata.put("ls_model_name", "my_model");

      Function<List<Map<String, String>>, Map<String, String>> chatModel =
          Tracing.traceFunction(
              inputMessages -> {
                Map<String, Object> inputCostDetails = new HashMap<>();
                inputCostDetails.put("cache_read", 2.3e-7);

                Map<String, Object> usageMetadata = new HashMap<>();
                usageMetadata.put("input_cost", 1.1e-6);
                usageMetadata.put("input_cost_details", inputCostDetails);
                usageMetadata.put("output_cost", 5.0e-6);

                RunTree run = Tracing.getCurrentRunTree();
                if (run != null) {
                  run.getMetadata().put("usage_metadata", usageMetadata);
                }

                return message(
                    "assistant", "Sure, what time would you like to book the table for?");
              },
              TraceConfig.builder()
                  .name("chat_model")
                  .runType(RunType.LLM)
                  .client(langsmith)
                  .executor(executor)
                  .metadata(metadata)
                  .build());

      chatModel.apply(messages);
    } finally {
      executor.shutdown();
      if (!executor.awaitTermination(10, TimeUnit.SECONDS)) {
        throw new IllegalStateException("Timed out waiting for LangSmith traces to submit");
      }
    }
  }

  private static Map<String, String> message(String role, String content) {
    Map<String, String> message = new HashMap<>();
    message.put("role", role);
    message.put("content", content);
    return message;
  }
}
kotlin
import com.langchain.smith.client.okhttp.LangsmithOkHttpClient
import com.langchain.smith.tracing.RunType
import com.langchain.smith.tracing.TraceConfig
import com.langchain.smith.tracing.getCurrentRunTree
import com.langchain.smith.tracing.traceable
import java.util.concurrent.Executors
import java.util.concurrent.TimeUnit

val langsmith = LangsmithOkHttpClient.fromEnv()
val executor = Executors.newSingleThreadExecutor()

fun message(role: String, content: String) = mapOf("role" to role, "content" to content)

try {
    val messages =
        listOf(
            message("system", "You are a helpful assistant."),
            message("user", "I'd like to book a table for two."),
        )

    val chatModel =
        traceable(
            { _: List<Map<String, String>> ->
                val usageMetadata =
                    mapOf(
                        "input_cost" to 1.1e-6,
                        "input_cost_details" to mapOf("cache_read" to 2.3e-7),
                        "output_cost" to 5.0e-6,
                    )
                getCurrentRunTree()?.metadata?.put("usage_metadata", usageMetadata)
                message(
                    "assistant",
                    "Sure, what time would you like to book the table for?",
                )
            },
            TraceConfig.builder()
                .name("chat_model")
                .runType(RunType.LLM)
                .client(langsmith)
                .executor(executor)
                .metadata(
                    mapOf(
                        "ls_provider" to "my_provider",
                        "ls_model_name" to "my_model",
                    ),
                )
                .build(),
        )

    chatModel(messages)
} finally {
    executor.shutdown()
    check(executor.awaitTermination(10, TimeUnit.SECONDS)) {
        "Timed out waiting for LangSmith traces to submit"
    }
}

View example trace

打开本示例的一个公开 LangSmith 运行记录。

其他 run:发送成本

你也可以为任何非 LLM 的 run(例如工具调用)发送成本信息。在 run 的 usage_metadata 中以 total_cost 字段指定成本:

python
from langsmith import traceable, get_current_run_tree

# Example tool: get_weather
@traceable(run_type="tool", name="get_weather")
def get_weather(city: str):
    # Your tool logic goes here
    result = {
        "temperature_f": 68,
        "condition": "sunny",
        "city": city,
    }

    # Cost for this tool call (computed however you like)
    tool_cost = 0.0015

    # Attach usage metadata to the LangSmith run
    run = get_current_run_tree()
    run.set(usage_metadata={"total_cost": tool_cost})

    # Return only the actual tool result (no usage info)
    return result

tool_response = get_weather("San Francisco")
typescript
import { traceable, getCurrentRunTree } from "langsmith/traceable";

// Example tool: get_weather
const getWeather = traceable(
  async ({ city }) => {
    // Your tool logic goes here
    const result = {
      temperature_f: 68,
      condition: "sunny",
      city,
    };

    // Cost for this tool call (computed however you like)
    const toolCost = 0.0015;

    // Attach usage metadata to the LangSmith run
    const runTree = getCurrentRunTree();
    runTree.metadata.usage_metadata = {
      total_cost: toolCost,
    };

    // Return only the actual tool result (no usage info)
    return result;
  },
  {
    run_type: "tool",
    name: "get_weather",
  }
);

const toolResponse = await getWeather({ city: "San Francisco" });
java
import com.langchain.smith.client.LangsmithClient;
import com.langchain.smith.client.okhttp.LangsmithOkHttpClient;
import com.langchain.smith.tracing.RunTree;
import com.langchain.smith.tracing.RunType;
import com.langchain.smith.tracing.TraceConfig;
import com.langchain.smith.tracing.Tracing;
import java.util.HashMap;
import java.util.Map;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.TimeUnit;
import java.util.function.Function;

class CostTrackingToolCostRun {
  public static void main(String[] args) throws InterruptedException {
    LangsmithClient langsmith = LangsmithOkHttpClient.fromEnv();
    ExecutorService executor = Executors.newSingleThreadExecutor();

    try {
      Function<String, Map<String, Object>> getWeather =
          Tracing.traceFunction(
              city -> {
                Map<String, Object> result = new HashMap<>();
                result.put("temperature_f", 68);
                result.put("condition", "sunny");
                result.put("city", city);

                RunTree run = Tracing.getCurrentRunTree();
                if (run != null) {
                  Map<String, Object> usageMetadata = new HashMap<>();
                  usageMetadata.put("total_cost", 0.0015);
                  run.getMetadata().put("usage_metadata", usageMetadata);
                }

                return result;
              },
              TraceConfig.builder()
                  .name("get_weather")
                  .runType(RunType.TOOL)
                  .client(langsmith)
                  .executor(executor)
                  .build());

      Map<String, Object> toolResponse = getWeather.apply("San Francisco");
    } finally {
      executor.shutdown();
      if (!executor.awaitTermination(10, TimeUnit.SECONDS)) {
        throw new IllegalStateException("Timed out waiting for LangSmith traces to submit");
      }
    }
  }
}
kotlin
import com.langchain.smith.client.okhttp.LangsmithOkHttpClient
import com.langchain.smith.tracing.RunType
import com.langchain.smith.tracing.TraceConfig
import com.langchain.smith.tracing.getCurrentRunTree
import com.langchain.smith.tracing.traceable
import java.util.concurrent.Executors
import java.util.concurrent.TimeUnit

val langsmith = LangsmithOkHttpClient.fromEnv()
val executor = Executors.newSingleThreadExecutor()

try {
    val getWeather =
        traceable(
            { city: String ->
                val result =
                    mapOf(
                        "temperature_f" to 68,
                        "condition" to "sunny",
                        "city" to city,
                    )
                getCurrentRunTree()
                    ?.metadata
                    ?.put("usage_metadata", mapOf("total_cost" to 0.0015))
                result
            },
            TraceConfig.builder()
                .name("get_weather")
                .runType(RunType.TOOL)
                .client(langsmith)
                .executor(executor)
                .build(),
        )

    val toolResponse = getWeather("San Francisco")
} finally {
    executor.shutdown()
    check(executor.awaitTermination(10, TimeUnit.SECONDS)) {
        "Timed out waiting for LangSmith traces to submit"
    }
}

或者,在追踪函数的返回值中直接包含 usage_metadata:

python
from langsmith import traceable

# Example tool: get_weather
@traceable(run_type="tool", name="get_weather")
def get_weather(city: str):
    # Your tool logic goes here
    result = {
        "temperature_f": 68,
        "condition": "sunny",
        "city": city,
    }

    # Attach tool call costs here
    return {
        **result,
        "usage_metadata": {
            "total_cost": 0.0015,   # <-- cost for this tool call
        },
    }

tool_response = get_weather("San Francisco")
typescript
import { traceable } from "langsmith/traceable";

// Example tool: get_weather
const getWeather = traceable(
  async ({ city }) => {
    // Your tool logic goes here
    const result = {
      temperature_f: 68,
      condition: "sunny",
      city,
    };

    // Attach tool call costs here
    return {
      ...result,
      usage_metadata: {
        total_cost: 0.0015,  // <-- cost for this tool call
      },
    };
  },
  {
    run_type: "tool",
    name: "get_weather",
  }
);

const toolResponse = await getWeather({ city: "San Francisco" });
java
import com.langchain.smith.client.LangsmithClient;
import com.langchain.smith.client.okhttp.LangsmithOkHttpClient;
import com.langchain.smith.tracing.RunType;
import com.langchain.smith.tracing.TraceConfig;
import com.langchain.smith.tracing.Tracing;
import java.util.HashMap;
import java.util.Map;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.TimeUnit;
import java.util.function.Function;

class CostTrackingToolCostOutput {
  public static void main(String[] args) throws InterruptedException {
    if (System.getenv("LANGSMITH_API_KEY") == null
        || System.getenv("LANGSMITH_API_KEY").isBlank()) {
      System.out.println(
          "[cost-tracking-tool-cost-output] Skipping (LANGSMITH_API_KEY is not set).");
      return;
    }

    LangsmithClient langsmith = LangsmithOkHttpClient.fromEnv();
    ExecutorService executor = Executors.newSingleThreadExecutor();

    try {
      Function<String, Map<String, Object>> getWeather =
          Tracing.traceFunction(
              city -> {
                Map<String, Object> result = new HashMap<>();
                result.put("temperature_f", 68);
                result.put("condition", "sunny");
                result.put("city", city);

                Map<String, Object> usageMetadata = new HashMap<>();
                usageMetadata.put("total_cost", 0.0015);
                result.put("usage_metadata", usageMetadata);
                return result;
              },
              TraceConfig.builder()
                  .name("get_weather")
                  .runType(RunType.TOOL)
                  .client(langsmith)
                  .executor(executor)
                  .build());

      Map<String, Object> toolResponse = getWeather.apply("San Francisco");
    } finally {
      executor.shutdown();
      if (!executor.awaitTermination(10, TimeUnit.SECONDS)) {
        throw new IllegalStateException("Timed out waiting for LangSmith traces to submit");
      }
    }
  }
}
kotlin
import com.langchain.smith.client.okhttp.LangsmithOkHttpClient
import com.langchain.smith.tracing.RunType
import com.langchain.smith.tracing.TraceConfig
import com.langchain.smith.tracing.traceable
import java.util.concurrent.Executors
import java.util.concurrent.TimeUnit

val langsmith = LangsmithOkHttpClient.fromEnv()
val executor = Executors.newSingleThreadExecutor()

try {
    val getWeather =
        traceable(
            { city: String ->
                mapOf(
                    "temperature_f" to 68,
                    "condition" to "sunny",
                    "city" to city,
                    "usage_metadata" to mapOf("total_cost" to 0.0015),
                )
            },
            TraceConfig.builder()
                .name("get_weather")
                .runType(RunType.TOOL)
                .client(langsmith)
                .executor(executor)
                .build(),
        )

    val toolResponse = getWeather("San Francisco")
} finally {
    executor.shutdown()
    check(executor.awaitTermination(10, TimeUnit.SECONDS)) {
        "Timed out waiting for LangSmith traces to submit"
    }
}

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