> ## Documentation Index
> Fetch the complete documentation index at: https://stagehand.readme-i18n.com/llms.txt
> Use this file to discover all available pages before exploring further.

# Caching Actions

> 您可以在 Stagehand 中缓存操作以避免冗余的 LLM 调用。

在 Stagehand 中缓存操作对于执行成本高昂的操作或当底层 DOM 结构预计不会改变时非常有用。

## 使用 `observe` 预览操作

`observe` 允许您在执行操作前预览它。如果您对操作预览满意，可以直接在 `page.act` 中运行而无需额外的 LLM 调用。

<CodeGroup>
  ```typescript TypeScript theme={null}
  const [actionPreview] = await page.observe("Click the quickstart link");

  /** actionPreview is a JSON-ified version of a Playwright action:
  {
  	description: "The quickstart link",
  	method: "click",
  	selector: "/html/body/div[1]/div[1]/a",
  	arguments: [],
  }
  **/

  // NO LLM INFERENCE when calling act on the preview
  await page.act(actionPreview)
  ```

  ```python Python theme={null}
  actions = await page.observe("Click the quickstart link")
  action_preview = actions[0]

  # action_preview is a dictionary version of a Playwright action:
  # {
  #	"description": "The quickstart link",
  #	"method": "click",
  #	"selector": "/html/body/div[1]/div[1]/a",
  #	"arguments": [],
  # }

  # NO LLM INFERENCE when calling act on the preview
  await page.act(action_preview)
  ```
</CodeGroup>

## 简单缓存

让我们在这个示例中使用基于文件的简单缓存。我们将编写能够读写 JSON 文件的 getter 和 setter 函数：

<CodeGroup>
  ```typescript TypeScript theme={null}
  // Get the cached value (undefined if it doesn't exist)
  async function getCache(key: string): Promise<ObserveResult | undefined> {
    try {
      const cache = await readFile("cache.json");
      const parsed = JSON.parse(cache);
      return parsed[key];
    } catch {
      return undefined;
    }
  }

  // Set the cache value
  async function setCache(key: string, value: ObserveResult): Promise<void> {
    const cache = await readFile("cache.json");
    const parsed = JSON.parse(cache);
    parsed[key] = value;
    await writeFile("cache.json", JSON.stringify(parsed));
  }
  ```

  ```python Python theme={null}
  # Get the cached value (None if it doesn't exist)
  async def get_cache(key: str) -> Optional[Dict[str, Any]]:
      try:
          async with aiofiles.open("cache.json", 'r') as f:
              cache_content = await f.read()
              parsed = json.loads(cache_content)
              return parsed.get(key)
      except (FileNotFoundError, json.JSONDecodeError):
          return None

  # Set the cache value
  async def set_cache(key: str, value: Dict[str, Any]) -> None:
      try:
          async with aiofiles.open("cache.json", 'r') as f:
              cache_content = await f.read()
              parsed = json.loads(cache_content)
      except (FileNotFoundError, json.JSONDecodeError):
          parsed = {}
      
      parsed[key] = value
      
      async with aiofiles.open("cache.json", 'w') as f:
          await f.write(json.dumps(parsed))
  ```
</CodeGroup>

### 带缓存执行

让我们编写一个函数来检查缓存、获取操作并执行它。如果操作失败，我们将尝试"自我修复"，即直接通过 `page.act` 重试。

<CodeGroup>
  ```typescript TypeScript theme={null}
  // Check the cache, get the action, and run it
  // If selfHeal is true, we'll attempt to self-heal if the action fails
  async function actWithCache(page: Page, key: string, prompt: string, selfHeal = false) {
  	try {
  		const cacheExists = await getCache(key);

  		let action: ObserveResult;
  		if (cacheExists) {
  		// Get the cached action
  		action = await getCache(prompt);
  		} else {
  		// Get the observe result (the action)
  		[action] = await page.observe(prompt);

  		// Cache the action
  		await setCache(prompt, action);
  		}

  		// Run the action (no LLM inference)
  		await page.act(action);
  	} catch (e) {
  		console.error(e);
  		// in selfHeal mode, we'll retry the action
  		if (selfHeal) {
  			console.log("Attempting to self-heal...");
  			await page.act(prompt);
  		}
  		else {
  			throw e;
  		}
  	}
  }
  ```

  ```python Python theme={null}
  # Check the cache, get the action, and run it
  # If self_heal is true, we'll attempt to self-heal if the action fails
  async def act_with_cache(page, key: str, prompt: str, self_heal: bool = False):
      try:
          cache_exists = await get_cache(key)

          if cache_exists:
              # Get the cached action
              action = await get_cache(prompt)
          else:
              # Get the observe result (the action)
              actions = await page.observe(prompt)
              action = actions[0]

              # Cache the action
              await set_cache(prompt, action)

          # Run the action (no LLM inference)
          await page.act(action)
      except Exception as e:
          print(f"Error: {e}")
          # in self_heal mode, we'll retry the action
          if self_heal:
              print("Attempting to self-heal...")
              await page.act(prompt)
          else:
              raise e
  ```
</CodeGroup>

您现在可以使用 `actWithCache` 来运行带缓存的操作：

<CodeGroup>
  ```typescript TypeScript theme={null}
  const prompt = "Click the quickstart link";
  const key = prompt; // Simple cache key
  // Attempt cached action or self-heal
  await actWithCache(page, key, prompt);
  ```

  ```python Python theme={null}
  prompt = "Click the quickstart link"
  key = prompt  # Simple cache key
  # Attempt cached action or self-heal
  await act_with_cache(page, key, prompt)
  ```
</CodeGroup>

## 高级缓存

上述示例很简单，但您可能希望基于页面内容来缓存操作。此外，如果有重复的提示，您应该使用更唯一的键。

我们希望将缓存逻辑留给您自己实现，但为您提供实现自定义缓存策略所需的所有工具。

您可以直接从 Playwright 的 page 对象访问 DOM 和无障碍树。以下是访问页面内容的示例：

<CodeGroup>
  ```typescript TypeScript theme={null}
  // Get the page content
  const pageContent = await page.content();
  ```

  ```python Python theme={null}
  # Get the page content
  page_content = await page.content()
  ```
</CodeGroup>

您也可以利用无障碍访问树、DOM 或其他信息来生成更具唯一性的键值。参照上述示例的类似逻辑，您可以自由实现这一功能。
