如果您正在构建对话代理或任何多轮应用程序,LangSmith 会自动将您的 运行 归入 线程。查询线程可以让您重放完整对话、审计跨会话的代理行为、对对话长度和延迟构建分析,并将其馈送到微调和评估等下游工作流中。
SDK 提供了两种处理线程的方法:
| 方法 | 使用场景 |
|---|---|
list_threads / listThreads | 您想要浏览项目中的所有线程 |
read_thread / readThread | 您已经知道线程 ID 并需要其运行 |
线程工作原理
您创建的每个运行都可以携带一个 thread_id 在其元数据中。LangSmith 使用它将运行分组到线程中。后端查找 thread_id in metadata (回退到 session_id).
如果您使用的是 跟踪集成,请在运行元数据中传递 thread_id :
from langsmith import traceable, uuid7
THREAD_ID = str(uuid7())
@traceable(metadata={"thread_id": THREAD_ID})
def my_agent(user_message: str) -> str:
...
const THREAD_ID = uuid7();
const myAgent = traceable(
async (userMessage: string) => {
// ...
},
{ metadata: { thread_id: THREAD_ID } }
);
列出项目中的所有线程
list_threads / listThreads 获取项目中的所有线程并将其运行分组在一起。结果按最新活动排序。
from langsmith import Client
client = Client()
threads = client.list_threads(project_name="my-project")
for thread in threads:
print(thread["thread_id"])
print(f" {thread['count']} runs")
print(f" last active: {thread['max_start_time']}")
const client = new Client();
const threads = await client.listThreads({ projectName: "my-project" });
for (const thread of threads) {
console.log(thread.thread_id);
console.log(` ${thread.count} runs`);
console.log(` last active: ${thread.max_start_time}`);
}
结果按最新活动排序:
conv-abc123
3 runs
last active: 2026-02-25T10:05:42+00:00
conv-def456
1 runs
last active: 2026-02-25T09:30:00+00:00
参数
| 参数 | 类型 | 默认值 | 描述 |
|---|---|---|---|
project_name / projectName | string | — | 项目名称。如果未设置则为必填项。 project_id 。 |
project_id / projectId | string | — | 项目 ID。如果未设置则为必填项。 project_name 。 |
limit | int | all | 要返回的最大线程数。 |
offset | int | 0 | 要跳过的线程数(用于分页)。 |
filter | string | — | 获取运行时应用的过滤表达式,使用 LangSmith 跟踪查询语法. |
start_time / startTime | datetime / Date | 1 天前 | 仅包括在此时间之后开始的运行。扩大此范围以显示更早的线程。 |
返回值
线程对象列表,每个包含:
| 字段 | 类型 | 描述 |
|---|---|---|
thread_id | string | 线程标识符。 |
runs | Run[] | 此线程中的根运行,按时间顺序排序(最早优先)。 |
count | int | 此线程中的运行数。 |
min_start_time | `string \ | null` |
max_start_time | `string \ | null` |
读取单个线程的运行
当你已经知道 thread_id,使用 read_thread / readThread。它直接返回线程运行的迭代器,无需先获取所有线程。
from langsmith import Client
client = Client()
for run in client.read_thread(
thread_id="conv-abc123",
project_name="my-project",
):
print(run.id, run.name, run.start_time)
const client = new Client();
for await (const run of client.readThread({
threadId: "conv-abc123",
projectName: "my-project",
})) {
console.log(run.id, run.name, run.start_time);
}
不同于 list_threads,这里的每个项目都是一个 Run 对象本身——没有分组包装。默认情况下,运行按时间正序返回。
[
Run(id=UUID("a1b2..."), name="my_agent", run_type="chain", status="success", start_time=datetime(2026, 2, 25, 10, 0, 0, tzinfo=utc), ...),
Run(id=UUID("c3d4..."), name="my_agent", run_type="chain", status="success", start_time=datetime(2026, 2, 25, 10, 3, 11, tzinfo=utc), ...),
Run(id=UUID("e5f6..."), name="my_agent", run_type="chain", status="error", start_time=datetime(2026, 2, 25, 10, 5, 42, tzinfo=utc), ...),
]
参数
| 参数 | 类型 | 默认值 | 描述 |
|---|---|---|---|
thread_id / threadId | string | — | **Required.** 要查询的线程。 |
project_name / projectName | string | — | 项目名称。如果未设置 project_id ,则必填。 |
project_id / projectId | `string \ | string[]` | — |
is_root / isRoot | bool | true | 仅返回根运行。设置为 false 以包含子运行。 |
limit | int | all | 最大返回运行数。 |
filter | string | — | 额外的过滤表达式(与线程过滤器组合)。 |
order | `"asc" \ | "desc"` | "asc" |
select | string[] | all fields | 要返回的特定运行字段,以减小响应大小。 |
返回值
一个迭代器(Python)或异步迭代器(TypeScript) of Run objects.
示例
按运行属性过滤线程
传递过滤表达式以使用 LangSmith 追踪查询语法缩小结果范围。例如,仅显示包含至少一个失败运行的线程:
threads = client.list_threads(
project_name="my-project",
filter='eq(status, "error")',
)
const threads = await client.listThreads({
projectName: "my-project",
filter: 'eq(status, "error")',
});
回溯超过 24 小时
默认情况下, list_threads 仅显示最近一天内有运行的线程。传递 start_time 以扩大时间窗口:
threads = client.list_threads(
project_name="my-project",
start_time=datetime.datetime.now(datetime.timezone.utc) - datetime.timedelta(days=2),
)
const threads = await client.listThreads({
projectName: "my-project",
startTime: new Date(Date.now() - 2 * 24 * 60 * 60 * 1000),
});
重建对话
使用 read_thread 配合 order="asc" 逐条重放对话:
runs = list(
client.read_thread(
thread_id="conv-abc123",
project_name="my-project",
order="asc",
)
)
for run in runs:
user_msg = run.inputs.get("messages", [{}])[-1].get("content", "")
assistant_msg = (run.outputs or {}).get("content", "")
print(f"User: {user_msg}")
print(f"Assistant: {assistant_msg}")
print()
const runs: Run[] = [];
for await (const run of client.readThread({
threadId: "conv-abc123",
projectName: "my-project",
order: "asc",
})) {
runs.push(run);
}
for (const run of runs) {
const messages = (run.inputs?.messages ?? []) as Array<Record<string, string>>;
const userMsg = messages.at(-1)?.content ?? "";
const assistantMsg = (run.outputs as Record<string, string>)?.content ?? "";
console.log(`User: ${userMsg}`);
console.log(`Assistant: ${assistantMsg}`);
}