You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
Copy file name to clipboardExpand all lines: packages/docs/v3/best-practices/caching.mdx
+101Lines changed: 101 additions & 0 deletions
Display the source diff
Display the rich diff
Original file line number
Diff line number
Diff line change
@@ -72,6 +72,107 @@ console.log(actResult.cacheStatus); // "HIT" or "MISS"
72
72
- The page URL factors in to the cache key. If the action is being made on a page with a dynamic URL, caching may not work as expected. We do filter out certain query parameters like referral trackers and analytics, but we don't catch everything just yet.
73
73
- If the page content or structure changes, the action won't get a cache `HIT` and the LLM will be called. The subsequent actions will attempt to hit the resulting cache entry.
74
74
75
+
### Best Practices
76
+
77
+
<AccordionGroup>
78
+
79
+
<Accordiontitle="Wait for the page to finish loading">
80
+
If you call a Stagehand method immediately after `page.goto()`, the page content may still be streaming in. This means the accessibility tree captured for the cache key is shorter than what the page will eventually render — so subsequent calls on the fully-loaded page will produce a different hash and miss the cache. Call `page.waitForLoadState("networkidle")` first to ensure the full tree is stable before Stagehand snapshots it.
// All content is loaded — consistent hash across runs
90
+
const result1 =awaitstagehand.observe("click the first search result");
91
+
// MISS
92
+
93
+
const result2 =awaitstagehand.observe("click the first search result");
94
+
// HIT
95
+
}
96
+
```
97
+
</Accordion>
98
+
99
+
<Accordiontitle="Scope by selector">
100
+
When targeting a specific part of a page, pass a `selector` to scope the accessibility tree snapshot to that container. This reduces token costs, speeds up inference, and — crucially for caching — means that changes *outside* the container don't affect the cache key.
const result =awaitstagehand.observe("click the first search result", {
110
+
// Scope to the search results container so ads, headers,
111
+
// and other surrounding content don't pollute the cache key.
112
+
selector: "#rcnt",
113
+
});
114
+
}
115
+
```
116
+
</Accordion>
117
+
118
+
<Accordiontitle="Use variables for dynamic values">
119
+
Using variables in `act()` lets you generalize a single cache entry across many different values. The cache key is built from the variable *keys*, not the values — so `{ email: "alice@example.com" }` and `{ email: "bob@example.com" }` share the same entry.
120
+
121
+
Without variables you'd accumulate a separate cache miss for every unique email address you type. With variables you prime the cache once and hit it forever.
122
+
123
+
```typescript
124
+
asyncfunction example(stagehand:Stagehand) {
125
+
const page =stagehand.context.pages()[0];
126
+
awaitpage.goto("https://example.com/login");
127
+
128
+
awaitpage.waitForLoadState("networkidle");
129
+
130
+
// First run with alice@example.com → MISS (primes cache)
131
+
// Any future run, regardless of email value → HIT
132
+
awaitstagehand.act(
133
+
"type %email% into the Email address field",
134
+
{ variables: { email: "alice@example.com" } },
135
+
);
136
+
}
137
+
```
138
+
</Accordion>
139
+
140
+
<Accordiontitle="Stabilize the environment">
141
+
Small differences in runtime state produce different accessibility trees and therefore different cache keys. Keep your environment as deterministic as possible:
The instruction string is part of the cache key. Even minor wording changes — synonyms, extra adjectives, punctuation — produce a new key and a cache miss.
160
+
161
+
- Anchor instructions to visible UI labels: `"click the Sign in button"` not `"click the button to log me in"`
162
+
- Keep instructions short and free of filler words
163
+
- Avoid instructions that contain runtime-variable text inline — use `variables` instead
164
+
165
+
```typescript
166
+
// Good: anchored to the visible label, no variance
167
+
awaitstagehand.act("click the Sign in button");
168
+
169
+
// Bad: inline dynamic value creates a new cache key every time
170
+
awaitstagehand.act(`type ${email} into the Email address field`);
0 commit comments