以编程方式使用文档

许多 LLM 应用程序具有类似聊天机器人的界面,用户和 LLM 应用程序之间进行多轮对话。为了跟踪这些对话,您可以使用 线程 在 LangSmith 中。

将跟踪分组到线程

要将跟踪关联到同一个线程,您需要传入一个特殊的 metadata 键,其值是该线程的唯一标识符。键名应为以下之一:

  • - session_id
  • - thread_id

该值可以是任意字符串,但我们建议使用 **UUID v7** 作为线程 ID。

LangSmith SDK 导出了一个 uuid7 辅助函数(Python v0.4.43+,JS v0.3.80+):

  • Python: from langsmith import uuid7
  • JS/TS: import { uuid7 } from 'langsmith'

有关说明,请参阅 向跟踪添加元数据和标签.

示例

此示例演示如何使用结构化消息格式记录和检索对话历史,以维护长时间运行的聊天。

该示例设置了一个 THREAD_ID 并通过 metadata 将其传递给跟踪包装器,将该会话中的每个运行链接到 LangSmith 中的同一线程。对话历史在轮次之间本地持久化——在生产环境中,将基于文件或内存的存储替换为数据库或缓存。 get_chat_history 标志控制管道是继续现有线程还是启动新线程:

from dotenv import load_dotenv

# Load environment variables from .env file
load_dotenv()

from langsmith import traceable, Client, uuid7
from langsmith.wrappers import wrap_openai

# Initialize clients
langsmith_client = Client()
client = wrap_openai(openai.Client())

# Configuration
THREAD_ID = str(uuid7())

# Using a local directory to store thread history. For production use, use a persistent storage solution.
THREADS_DIR = os.path.join(os.path.dirname(__file__), "threads")

# gets a history of all LLM calls in the thread to construct conversation history
def get_thread_history(thread_id: str) -> list:
    path = os.path.join(THREADS_DIR, f"{thread_id}.json")
    if not os.path.exists(path):
        return []
    with open(path, "r") as f:
        return json.load(f)

def save_thread_history(thread_id: str, messages: list):
    os.makedirs(THREADS_DIR, exist_ok=True)
    with open(os.path.join(THREADS_DIR, f"{thread_id}.json"), "w") as f:
        json.dump(messages, f, indent=2, default=str)


@traceable(name="Chat Bot", metadata={"thread_id": THREAD_ID})
def chat_pipeline(messages: list, get_chat_history: bool = False):
    # Whether to continue an existing thread or start a new one
    if get_chat_history:
        history_messages = get_thread_history(THREAD_ID)
        # Get existing conversation history and append new messages
        all_messages = history_messages + messages
    else:
        all_messages = messages

    # Invoke the model
    chat_completion = client.chat.completions.create(
        model="gpt-5.4-mini", messages=all_messages
    )

    response_message = chat_completion.choices[0].message
    print("Response from model:", response_message)

    full_conversation = all_messages + [{"role": response_message.role, "content": response_message.content}]
    save_thread_history(THREAD_ID, full_conversation)

    return {"messages": full_conversation}


# Format message
messages = [
    {
        "content": "Hi, my name is Sally",
        "role": "user"
    }
]

# Call the chat pipeline
result = chat_pipeline(messages, get_chat_history=False)
const __dirname = path.dirname(fileURLToPath(import.meta.url));

// Load environment variables from .env file
dotenv.config();

// Initialize client
const client = wrapOpenAI(new OpenAI());

// Configuration
const THREAD_ID = uuid7();

// Using a local directory to store thread history. For production use, use a persistent storage solution.
const THREADS_DIR = path.join(__dirname, "threads");

type Message = { role: string; content: string };

// Gets a history of all LLM calls in the thread to construct conversation history
function getThreadHistory(threadId: string): Message[] {
  const filePath = path.join(THREADS_DIR, `${threadId}.json`);
  if (!fs.existsSync(filePath)) return [];
  return JSON.parse(fs.readFileSync(filePath, "utf-8"));
}

function saveThreadHistory(threadId: string, messages: Message[]): void {
  fs.mkdirSync(THREADS_DIR, { recursive: true });
  fs.writeFileSync(
    path.join(THREADS_DIR, `${threadId}.json`),
    JSON.stringify(messages, null, 2)
  );
}

const chatPipeline = traceable(
  async function chatPipeline({ messages, get_chat_history = false }: { messages: Message[]; get_chat_history?: boolean }) {
    // Whether to continue an existing thread or start a new one
    if (get_chat_history) {
      const historyMessages = getThreadHistory(THREAD_ID);
      // Get existing conversation history and append new messages
      messages = [...historyMessages, ...messages];
    }

    // Invoke the model
    const chatCompletion = await client.chat.completions.create({
      model: "gpt-5.4-mini",
      messages,
    });

    const responseMessage = chatCompletion.choices[0].message;
    console.log("Response from model:", responseMessage);

    const fullConversation: Message[] = [
      ...messages,
      { role: responseMessage.role, content: responseMessage.content ?? "" },
    ];
    saveThreadHistory(THREAD_ID, fullConversation);

    return { messages: fullConversation };
  },
  { name: "Chat Bot", metadata: { thread_id: THREAD_ID } }
);

// Format message
const messages: Message[] = [{ role: "user", content: "Hi! My name is Sally" }];

// Call the chat pipeline
await chatPipeline({ messages, get_chat_history: false });

Java 和 Kotlin 示例使用专用执行器。关闭执行器并等待终止可确保在进程退出前完成后台跟踪提交。

进行以下调用以继续对话。通过传入 get_chat_history=True / get_chat_history: true / getChatHistory = true,您可以从上次中断的地方继续对话。这意味着 LLM 会接收完整的消息历史并对其作出响应,而不是仅仅响应最新消息:

# Format message
messages = [
    {
        "content": "What is my name",
        "role": "user"
    }
]

# Call the chat pipeline
result = chat_pipeline(messages, get_chat_history=True)
// Continue the conversation.
const messages: Message[] = [{ role: "user", content: "What is my name" }];

await chatPipeline({ messages, get_chat_history: true });

继续对话。由于包含了过去的消息,LLM 将记住对话内容:

# Continue the conversation.
messages = [
    {
        "content": "What was the first message I sent you?",
        "role": "user"
    }
]

chat_pipeline(messages, get_chat_history=True)
// Continue the conversation.
const messages: Message[] = [{ role: "user", content: "What was the first message I sent you?" }];

await chatPipeline({ messages, get_chat_history: true });

查看线程

您可以在 UI 中查看线程,点击任意 **线程** 标签页中的 项目详情 page. The table shows each thread's first input, last output, start times, turn count, latency (P50/P99), token usage, cost, and feedback score.

The right panel displays aggregate stats for the project, including thread and trace counts, total and median token usage, error rate, and P50/P99 latency.

然后你可以点击进入特定的线程。你可以通过三种不同的方式查看线程:

  • 消息 视图(测试版):对话层。以聊天风格的线程扫描每个轮次,显示用户和助手消息、工具调用和子代理活动。
  • 轮次 view: the per-turn summary. View each turn as a card showing its inputs and outputs, with expand/collapse and customizable input/output fields.
  • 详情 视图:调试层。深入了解特定的运行以检查输入、输出、元数据、时间、错误和子运行。周围的线程上下文保持可见,以便你可以看到该运行在更广泛的对话中的位置。

使用页面顶部的按钮或键盘快捷键在视图之间切换 M (消息), T (轮次)和 D (Details). While the Messages view is in beta, the thread side panel defaults to the Details view. The right panel shows stats for the thread, including turn count, first and last start times, P50/P99 latency, and a cost breakdown by input and output tokens. For a full description of each view, see 查看追踪.

查看反馈

反馈分数在项目的 **反馈** 线程表的列中可见 **线程** tab.

在线程内,打开消息视图并点击 **LLM调用** 链接,查看该运行的详情视图并审核该运行的反馈。你还可以查看 线程级反馈 there.

保存线程级筛选器

在项目的 **线程** 标签页上,你可以保存常用的筛选器: 设置筛选器 使用 **添加筛选器** 按钮,然后点击 **保存视图**.

相关