主题
许多 LLM 应用都具备类似聊天机器人的界面,用户与 LLM 应用之间会进行多轮对话。为了追踪这些对话,您可以使用 LangSmith 中的 Threads(线程)功能。
将追踪记录分组到线程中
一个 Thread(线程)是代表单次对话的一系列追踪记录。每个响应都表示为它自己的追踪记录,但这些追踪记录通过属于同一个线程而链接在一起。
要将追踪记录关联在一起,您需要传入一个特殊的 metadata 键,其值是该线程的唯一标识符。键名应为以下之一:
session_idthread_idconversation_id
值可以是您想要的任何字符串,但我们建议使用 UUID,例如 f47ac10b-58cc-4372-a567-0e02b2c3d479。请查看本指南了解如何向追踪记录添加元数据。
示例
此示例演示了如何使用结构化的消息格式来记录和检索对话历史,以维持长时间的聊天会话。
python
import os
from typing import List, Dict, Any, Optional
import openai
from langsmith import traceable, Client
import langsmith as ls
from langsmith.wrappers import wrap_openai
# 初始化客户端
client = wrap_openai(openai.Client())
langsmith_client = Client()
# 配置
LANGSMITH_PROJECT = "project-with-threads"
THREAD_ID = "thread-id-1"
langsmith_extra={"project_name": LANGSMITH_PROJECT, "metadata":{"session_id": THREAD_ID}}
# 获取线程中所有 LLM 调用的历史记录以构建对话历史
def get_thread_history(thread_id: str, project_name: str):
# 按特定线程和项目筛选运行记录
filter_string = f'and(in(metadata_key, ["session_id","conversation_id","thread_id"]), eq(metadata_value, "{thread_id}"))'
# 仅获取 LLM 运行记录
runs = [r for r in langsmith_client.list_runs(project_name=project_name, filter=filter_string, run_type="llm")]
# 按开始时间排序以获取最近的交互
runs = sorted(runs, key=lambda run: run.start_time, reverse=True)
# 重建对话状态
latest_run = runs[0]
return latest_run.inputs['messages'] + [latest_run.outputs['choices'][0]['message']]
@traceable(name="Chat Bot")
def chat_pipeline(messages: list, get_chat_history: bool = False):
# 是继续现有线程还是开始新线程
if get_chat_history:
run_tree = ls.get_current_run_tree()
# 获取现有对话历史并追加新消息
history_messages = get_thread_history(run_tree.extra["metadata"]["session_id"], run_tree.session_name)
all_messages = history_messages + messages
# 为了追踪,在输入中包含完整的对话
input_messages = all_messages
else:
all_messages = messages
input_messages = messages
# 调用模型
chat_completion = client.chat.completions.create(
model="gpt-4o-mini", messages=all_messages
)
# 返回包含输入和响应的完整对话
response_message = chat_completion.choices[0].message
return {
"messages": input_messages + [response_message]
}
# 格式化消息
messages = [
{
"content": "Hi, my name is Sally",
"role": "user"
}
]
get_chat_history = False
# 调用聊天管道
result = chat_pipeline(messages, get_chat_history, langsmith_extra=langsmith_extra)typescript
import 'dotenv/config';
import OpenAI from 'openai';
import { traceable, getCurrentRunTree } from 'langsmith/traceable';
import { Client } from 'langsmith';
import { wrapOpenAI } from 'langsmith/wrappers';
// 初始化客户端
const openai = new OpenAI();
const client = wrapOpenAI(openai);
const langsmithClient = new Client();
// 配置
const LANGSMITH_PROJECT = "project-with-threads";
const THREAD_ID = "thread-id-1";
const langsmithExtra = {
project_name: LANGSMITH_PROJECT,
metadata: { session_id: THREAD_ID }
};
// 消息类型定义
interface Message {
role: 'user' | 'assistant' | 'system';
content: string;
}
interface ChatResponse {
messages: Message[];
}
interface ChatInput {
get_chat_history: boolean;
messages: Message[];
}
// 获取线程中所有 LLM 调用的历史记录以构建对话历史
async function getThreadHistory(threadId: string, projectName: string): Promise<Message[]> {
// 按特定线程和项目筛选运行记录
const filterString = `and(in(metadata_key, ["session_id","conversation_id","thread_id"]), eq(metadata_value, "${threadId}"))`;
// 仅获取 LLM 运行记录
const runs: any[] = [];
for await (const run of langsmithClient.listRuns({
projectName: projectName,
filter: filterString,
runType: "llm"
})) {
if (run.run_type === "llm") {
runs.push(run);
}
}
// 按开始时间排序以获取最近的交互
runs.sort((a: any, b: any) => new Date(b.start_time).getTime() - new Date(a.start_time).getTime());
// 检查是否有任何运行记录
if (runs.length === 0) {
return [];
}
// 对话的当前状态
const latestRun = runs[0];
const inputMessages = latestRun.inputs.messages as Message[];
const outputMessage = latestRun.outputs.choices[0].message as Message;
return [...inputMessages, outputMessage];
}
// 更新后的聊天管道,接受 JSON 输入格式
const chatPipeline = traceable(async (input: ChatInput): Promise<ChatResponse> => {
const { messages, get_chat_history } = input;
let allMessages: Message[];
let inputMessages: Message[];
// 是继续现有线程还是开始新线程
if (get_chat_history) {
const runTree = getCurrentRunTree();
// 获取现有对话历史并追加新消息
const sessionId = runTree.extra?.metadata?.session_id || THREAD_ID;
const historyMessages = await getThreadHistory(
sessionId,
runTree.project_name
);
allMessages = historyMessages.concat(messages);
// 为了追踪,在输入中包含完整的对话
inputMessages = allMessages;
} else {
allMessages = messages;
inputMessages = messages;
}
// 调用模型
const chatCompletion = await client.chat.completions.create({
model: "gpt-4o-mini",
messages: allMessages
});
// 返回包含输入和响应的完整对话
const responseMessage = chatCompletion.choices[0].message as Message;
return {
messages: [...inputMessages, responseMessage]
};
}, { name: "Chat Bot" });
// 请求的 JSON 格式的示例输入
const input: ChatInput = {
get_chat_history: false,
messages: [
{
content: "Hi, my name is Sally",
role: "user"
}
]
};
// 调用聊天管道
const result = await chatPipeline(input);等待几秒钟后,您可以进行以下调用来继续对话。通过传递 get_chat_history=True,/getChatHistory: true,您可以从上次中断的地方继续对话。这意味着 LLM 将接收整个消息历史记录并对其进行响应,而不仅仅是响应最新的消息。
python
# 继续对话。
messages = [
{
"content": "What is my name?",
"role": "user"
}
]
get_chat_history = True
chat_pipeline(messages, get_chat_history, langsmith_extra=langsmith_extra)typescript
// 继续对话。
const input: ChatInput = {
get_chat_history: true,
messages: [
{
content: "What is my name?",
role: "user"
}
]
};
await chatPipeline(input);继续对话。由于包含了过去的消息,LLM 将记住对话内容。
python
# 继续对话。
messages = [
{
"content": "What was the first message I sent you?",
"role": "user"
}
]
get_chat_history = True
chat_pipeline(messages, get_chat_history, langsmith_extra=langsmith_extra)typescript
// 继续对话。
const input: ChatInput = {
get_chat_history: true,
messages: [
{
content: "What was the first message I sent you?",
role: "user"
}
]
};
await chatPipeline(input);查看线程
您可以在任何项目详情页面中点击 Threads 选项卡来查看线程。然后您将看到所有线程的列表,按最近的活动排序。


在线程视图中使用 Polly 来分析对话线程、理解用户情绪、识别痛点并跟踪问题是否得到解决。
查看线程
然后您可以点击进入特定的线程。这将打开特定线程的历史记录。


线程可以以两种不同的方式查看:
您可以使用页面顶部的按钮在两种视图之间切换,或使用键盘快捷键 T 在两种视图之间切换。
线程概览
线程概览页面向您展示了一个类似聊天机器人的用户界面,您可以在其中看到对话每一轮的输入和输出。您可以配置在概览中显示输入和输出的哪些字段,或者通过点击 Configure 按钮显示多个字段。
输入和输出的 JSON 路径支持负索引,因此您可以使用 -1 来访问数组的最后一个元素。例如,inputs.messages[-1].content 将访问 messages 数组中的最后一条消息。
追踪视图
这里的追踪视图与查看单个运行时的追踪视图类似,不同之处在于您可以轻松访问线程中每一轮的所有运行记录。
查看反馈
查看线程时,在页面顶部您会看到一个名为 Feedback 的部分。在这里,您可以查看构成线程的每个运行的反馈。此反馈是聚合的,因此如果您为线程的每个运行评估相同的标准,您将看到所有运行的平均分数。您还可以在此处看到留下的线程级反馈。
保存线程级过滤器
与在项目级别保存过滤器类似,您也可以在线程级别保存常用的过滤器。要在线程表上保存过滤器,请使用过滤器按钮设置过滤器,然后点击 Save filter 按钮。
您可以通过分别点击 Annotate 和 Open trace,在侧面板中打开追踪记录或对其进行注释。