Skip to content
LlamaParse 文档版本 跟随上游 源快照 e8b5cdf3 更新于 2026-10-16

在目录里新增、更新或删除文件之后,需要同步索引才能反映这些变更。同步会重新解析有变化的文件,并把更新后的块重新导出到向量库。

创建索引时会自动触发首次同步。此后的更新请用同步端点。

触发同步

  • Python
  • TypeScript
  • Go
  • Java
  • CLI
await client.indexes.sync("<your-index-id>")
await client.indexes.sync("<your-index-id>");
_, err := client.Indexes.Sync(ctx, "<your-index-id>", llamacloud.IndexSyncParams{})
if err != nil {
  log.Fatal(err)
}
client.indexes().sync("<your-index-id>");

终端窗口

llp beta:indexes sync --index-id "<your-index-id>"

同步是异步运行的。轮询索引以得知它何时完成 —— 一个避免竞态的轮询循环见下文的典型流程。

注意

同一个索引一次只能运行一个同步。若已有同步在进行中,API 会返回 409 Conflict。同步端点还另有速率限制 —— 短时间内反复调用会返回 429 Too Many Requests。请触发一次同步后改轮询索引状态,不要在一个循环里反复调同步端点。

下面的流程假设你的同步调用已被接受,因此结束的那次运行确实覆盖了你的变更。若你收到 409 或 429,那个已在运行的同步可能开始于你加入文件之前 —— 它结束时你的文件仍可能不在索引里。索引一个文件的 cookbook 演示了如何通过检查该文件本身来应对这种情况。

典型流程

  1. 在目录里上传新文件或移除过期文件。
  2. 读取索引当前的 last_synced_at 与 status,然后调用同步端点一次。
  3. 轮询索引,直到 status 为 ready 并且 last_synced_at 已推进过第 2 步读到的值;或者 status 在第 2 步之后发生变化并变为 failed。此后即可恢复检索或对话。

注意

触发同步后不要只靠轮询 status == "ready" 来判断。在新同步真正开始前,索引会一直保持它此前的 ready 状态,因此单看状态可能立刻返回、在还没做任何工作时就报告成功。把 last_synced_at 与你在触发之前读到的值做比较,才能确认新的运行真的结束了。在一次失败的构建之后重新同步时,failed 状态也会同样滞留;而失败的运行不会推进 last_synced_at,所以只有当状态自你触发以来确实变化过,才能把 failed 当作本次同步的结果。请给循环设置超时并在两次轮询之间退避,以免它无限空转。

只有 ready 与 failed 这两个 status 取值在各版本间保持稳定 —— 其他取值(pending、syncing、exporting 等)一律当作「还没完成」即可。

下载一份只有一页的指标报告作为 new-report.pdf,加入目录:

终端窗口

curl -fL -o new-report.pdf https://raw.githubusercontent.com/run-llama/llama-cloud-py/main/examples/extract/test-files/slide/saas_slide.pdf
  • Python
  • TypeScript
  • Go
  • Java
  • CLI
import asyncio
import time

# Add a new file to the directory
with open("new-report.pdf", "rb") as f:
    file_obj = await client.files.create(file=f, purpose="user_data")

await client.beta.directories.files.add(
    directory_id,
    file_id=file_obj.id,
)

# Note the state of the index before the sync. A freshly triggered sync keeps
# reporting the previous status until it starts, so we wait for "ready" with
# a newer last_synced_at, and trust "failed" only once the status has changed.
prev = await client.indexes.get(index_id)
before = prev.last_synced_at
before_status = prev.metadata["status"] if prev.metadata else "unknown"

# Trigger a sync (call this once — the endpoint is rate limited)
await client.indexes.sync(index_id)

# Poll until this sync finishes, backing off between attempts and
# giving up after a deadline.
deadline = time.monotonic() + 600  # 10 minutes
delay = 2
started = False  # True once the status differs from before the trigger
while True:
    idx = await client.indexes.get(index_id)
    status = idx.metadata["status"] if idx.metadata else "unknown"
    started = started or status != before_status

    if status == "ready" and idx.last_synced_at != before:
        print("Sync complete!")
        break
    if status == "failed" and started:
        print("Sync failed:", idx.metadata["error_message"])
        break
    if time.monotonic() >= deadline:
        raise TimeoutError("Sync did not complete within 10 minutes")

    await asyncio.sleep(delay)
    delay = min(delay * 2, 30)  # exponential backoff, capped at 30s
import fs from "fs";

// Add a new file
const fileObj = await client.files.create({
  file: fs.createReadStream("new-report.pdf"),
  purpose: "user_data",
});

await client.beta.directories.files.add(directoryId, {
  file_id: fileObj.id,
});

// Note the state of the index before the sync. A freshly triggered sync keeps
// reporting the previous status until it starts, so we wait for "ready" with
// a newer last_synced_at, and trust "failed" only once the status has changed.
const prev = await client.indexes.get(indexId);
const before = prev.last_synced_at;
const beforeStatus = (prev.metadata?.status as string) ?? "unknown";

// Trigger a sync (call this once — the endpoint is rate limited)
await client.indexes.sync(indexId);

// Poll until this sync finishes, backing off between attempts and
// giving up after a deadline.
const deadline = Date.now() + 600_000; // 10 minutes
let delay = 2000;
let started = false; // true once the status differs from before the trigger
while (true) {
  const idx = await client.indexes.get(indexId);
  const status = (idx.metadata?.status as string) ?? "unknown";
  started = started || status !== beforeStatus;

  if (status === "ready" && idx.last_synced_at !== before) {
    console.log("Sync complete!");
    break;
  } else if (status === "failed" && started) {
    console.error("Sync failed:", idx.metadata?.error_message);
    break;
  } else if (Date.now() >= deadline) {
    throw new Error("Sync did not complete within 10 minutes");
  }

  await new Promise((r) => setTimeout(r, delay));
  delay = Math.min(delay * 2, 30_000); // exponential backoff, capped at 30s
}
// Add a new file to the directory
f, err := os.Open("new-report.pdf")
if err != nil {
  log.Fatal(err)
}
defer f.Close()

fileObj, err := client.Files.New(ctx, llamacloud.FileNewParams{
  File:    f,
  Purpose: "user_data",
})
if err != nil {
  log.Fatal(err)
}

_, err = client.Beta.Directories.Files.Add(ctx, directoryID, llamacloud.BetaDirectoryFileAddParams{
  FileID: fileObj.ID,
})
if err != nil {
  log.Fatal(err)
}

// Note the state of the index before the sync. A freshly triggered sync keeps
// reporting the previous status until it starts, so we wait for "ready" with
// a newer LastSyncedAt, and trust "failed" only once the status has changed.
idxBefore, err := client.Indexes.Get(ctx, indexID, llamacloud.IndexGetParams{})
if err != nil {
  log.Fatal(err)
}
before := idxBefore.LastSyncedAt
beforeStatus, ok := idxBefore.Metadata["status"].(string)
if !ok {
  beforeStatus = "unknown"
}

// Trigger a sync (call this once — the endpoint is rate limited)
_, err = client.Indexes.Sync(ctx, indexID, llamacloud.IndexSyncParams{})
if err != nil {
  log.Fatal(err)
}

// Poll until this sync finishes, backing off between attempts and
// giving up after a deadline.
deadline := time.Now().Add(10 * time.Minute)
delay := 2 * time.Second
started := false // true once the status differs from before the trigger
for {
  idx, err := client.Indexes.Get(ctx, indexID, llamacloud.IndexGetParams{})
  if err != nil {
    log.Fatal(err)
  }

  status, ok := idx.Metadata["status"].(string)
  if !ok {
    status = "unknown"
  }
  if status != beforeStatus {
    started = true
  }

  if status == "ready" && !idx.LastSyncedAt.Equal(before) {
    fmt.Println("Sync complete!")
    break
  } else if status == "failed" && started {
    fmt.Println("Sync failed:", idx.Metadata["error_message"])
    break
  } else if time.Now().After(deadline) {
    log.Fatal("Sync did not complete within 10 minutes")
  }

  time.Sleep(delay)
  if delay < 30*time.Second {
    delay *= 2
    if delay > 30*time.Second {
      delay = 30 * time.Second
    }
  }
}
import java.nio.file.Paths;
import java.time.Duration;
import java.time.Instant;
import java.time.OffsetDateTime;
import java.util.Optional;

import ai.llamaindex.llamacloud.models.beta.directories.files.FileAddParams;
import ai.llamaindex.llamacloud.models.indexes.IndexGetResponse;
import ai.llamaindex.llamacloud.models.files.FileCreateParams;
import ai.llamaindex.llamacloud.models.files.FileCreateResponse;

// Add a new file to the directory
FileCreateResponse fileObj = client.files().create(
    FileCreateParams.builder()
        .file(Paths.get("new-report.pdf"))
        .purpose("user_data")
        .build());

client.beta().directories().files().add(
    directoryId,
    FileAddParams.builder()
        .fileId(fileObj.id())
        .build());

// Note the state of the index before the sync. A freshly triggered sync keeps
// reporting the previous status until it starts, so we wait for "ready" with
// a newer lastSyncedAt, and trust "failed" only once the status has changed.
IndexGetResponse prev = client.indexes().get(indexId);
Optional<OffsetDateTime> before = prev.lastSyncedAt();
String beforeStatus = prev.metadata()
    .map(m -> m._additionalProperties().get("status"))
    .map(v -> v.convert(String.class))
    .orElse("unknown");

// Trigger a sync (call this once — the endpoint is rate limited)
client.indexes().sync(indexId);

// Poll until this sync finishes, backing off between attempts and
// giving up after a deadline.
Instant deadline = Instant.now().plus(Duration.ofMinutes(10));
long delayMs = 2000;
boolean started = false; // true once the status differs from before the trigger
while (true) {
    IndexGetResponse idx = client.indexes().get(indexId);
    String status = idx.metadata()
        .map(m -> m._additionalProperties().get("status"))
        .map(v -> v.convert(String.class))
        .orElse("unknown");
    started = started || !status.equals(beforeStatus);

    if (status.equals("ready") && !idx.lastSyncedAt().equals(before)) {
        System.out.println("Sync complete!");
        break;
    } else if (status.equals("failed") && started) {
        Optional<String> errOpt = idx.metadata()
            .map(m -> m._additionalProperties().get("error_message"))
            .map(v -> v.convert(String.class));
        System.out.println("Sync failed: " + errOpt.orElse(""));
        break;
    } else if (Instant.now().isAfter(deadline)) {
        throw new RuntimeException("Sync did not complete within 10 minutes");
    }

    Thread.sleep(delayMs);
    delayMs = Math.min(delayMs * 2, 30000); // exponential backoff, capped at 30s
}

终端窗口

# Add a new file to the directory
FILE_ID=$(llp files create \
  --file ./new-report.pdf \
  --purpose user_data | jq -r '.id')

llp beta:directories:files add \
  --directory-id "$DIRECTORY_ID" \
  --file-id "$FILE_ID"

# Note the state of the index before the sync. A freshly triggered sync keeps
# reporting the previous status until it starts, so we wait for "ready" with
# a newer last_synced_at, and trust "failed" only once the status has changed.
PREV=$(llp beta:indexes get --index-id "$INDEX_ID")
BEFORE=$(printf '%s\n' "$PREV" | jq -r '.last_synced_at // ""')
BEFORE_STATUS=$(printf '%s\n' "$PREV" | jq -r '.metadata.status // "unknown"')

# Trigger a sync (call this once — the endpoint is rate limited)
llp beta:indexes sync --index-id "$INDEX_ID"

# Poll until this sync finishes, backing off between attempts and
# giving up after a deadline.
DEADLINE=$(( $(date +%s) + 600 ))  # 10 minutes
DELAY=2
STARTED=
while true; do
  IDX=$(llp beta:indexes get --index-id "$INDEX_ID") || { printf '%s\n' "$IDX" >&2; break; }
  STATUS=$(printf '%s\n' "$IDX" | jq -r '.metadata.status // "unknown"')
  SYNCED_AT=$(printf '%s\n' "$IDX" | jq -r '.last_synced_at // ""')
  if [ "$STATUS" != "$BEFORE_STATUS" ]; then STARTED=1; fi

  if [ "$STATUS" = "ready" ] && [ "$SYNCED_AT" != "$BEFORE" ]; then
    echo 'Sync complete!'
    break
  elif [ "$STATUS" = "failed" ] && [ -n "$STARTED" ]; then
    printf '%s\n' "$IDX" | jq -r '"Sync failed: \(.metadata.error_message // .)"' >&2
    break
  elif [ "$(date +%s)" -ge "$DEADLINE" ]; then
    echo "Sync did not complete within 10 minutes" >&2
    break
  fi

  sleep "$DELAY"
  DELAY=$(( DELAY * 2 ))
  [ "$DELAY" -gt 30 ] && DELAY=30
done

管理索引

获取索引

  • Python
  • TypeScript
  • Go
  • Java
  • CLI
# Get an index by ID
idx = await client.indexes.get("<your-index-id>")
print(f"{idx.id}: status={idx.metadata['status']}")
// Get an index by ID
const idx = await client.indexes.get("<your-index-id>");
console.log(`${idx.id}: status=${idx.metadata?.status}`);
// Get an index by ID
idx, err := client.Indexes.Get(ctx, "<your-index-id>", llamacloud.IndexGetParams{})
if err != nil {
  log.Fatal(err)
}
fmt.Printf("%s: status=%v\n", idx.ID, idx.Metadata["status"])
import java.util.Optional;
import ai.llamaindex.llamacloud.models.indexes.IndexGetResponse;

// Get an index by ID
IndexGetResponse idx = client.indexes().get("<your-index-id>");
Optional<String> statusOpt = idx.metadata()
    .map(m -> m._additionalProperties().get("status"))
    .map(v -> v.convert(String.class));
String status = statusOpt.orElse("unknown");
System.out.println(idx.id() + ": status=" + status);

终端窗口

# Get an index by ID
llp beta:indexes get --index-id "<your-index-id>" \
  | jq -r '"\(.id): status=\(.metadata.status)"'

删除索引

删除索引会移除其同步与导出配置。源目录及其文件不受影响。

  • Python
  • TypeScript
  • Go
  • Java
  • CLI
await client.indexes.delete("<your-index-id>")
await client.indexes.delete("<your-index-id>");
err := client.Indexes.Delete(ctx, "<your-index-id>", llamacloud.IndexDeleteParams{})
if err != nil {
  log.Fatal(err)
}
client.indexes().delete("<your-index-id>");

终端窗口

llp beta:indexes delete --index-id "<your-index-id>"

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