المتصفّح هو الواجهة البرمجية الجديدة. يحوّل Stagehand مكتبة Playwright إلى إطار عمل يدعم الذكاء الاصطناعي بشكل أصيل: بدلًا من الاعتماد على محدِّدات CSS الهشّة، تكتب تعليمات بلغة طبيعية مثل act("اضغط على زر تسجيل الدخول") و extract("السعر كرقم"). في هذا الدرس، سنبني وكيلًا إنتاجيًّا يستخرج البيانات، ويملأ النماذج، ويعمل بثبات في السحابة عبر Browserbase.
ماذا ستبني
وكيلًا مكتوبًا بلغة TypeScript يقوم بـ:
- تشغيل متصفّح حقيقي في السحابة عبر Browserbase.
- التنقّل إلى صفحة منتج واستخراج بيانات منظَّمة باستخدام Zod.
- تنفيذ مهمّة متعدّدة الخطوات (بحث، تصفية، تصفّح الصفحات) بأوامر بلغة طبيعية.
- ملاحظة الإجراءات الممكنة قبل اتخاذ قرار التنفيذ.
- تسجيل كلّ خطوة مع إعادة تشغيل كامل للجلسة لأغراض التصحيح.
في نهاية الدرس ستعرف متى تستخدم act و extract و observe ومتى تعود إلى Playwright المباشر، وكيف تحافظ على ثبات الوكيل عبر آلاف عمليات التشغيل.
المتطلّبات المسبقة
قبل البدء، تأكّد من توفّر:
- Node.js 20+ و pnpm أو npm
- حساب على Browserbase (الطبقة المجّانية كافية) مع مفتاح API ومُعرِّف المشروع
- مفتاح OpenAI أو Anthropic (يدعم Stagehand الاثنين)
- إلمام أساسي بـ TypeScript و async/await
- محرّر أكواد (يُنصح بـ VS Code)
لا حاجة لخبرة متعمّقة بـ Playwright. يغلّفها Stagehand، وتتكفّل أدوات الذكاء الاصطناعي بمعظم العمل.
لماذا Stagehand وليس Playwright المجرَّد؟
يعمل Playwright بشكل ممتاز إلى أن يتغيّر DOM. تتبدّل أسماء الأصناف، ويعيد اختبار A/B ترتيب الأزرار، فيتعطّل سكربتك الساعة الثالثة فجرًا.
يستبدل Stagehand الأجزاء الهشّة بثلاث أدوات ذكيّة:
| الأداة | ما تفعله | متى تستخدمها |
|---|---|---|
act(instruction) | تنفّذ إجراءً موصوفًا بالإنجليزية البسيطة | الضغط، الكتابة، التنقّل في الواجهة |
extract(instruction, schema) | تسحب بيانات منظَّمة، يتمّ التحقّق منها عبر Zod | استخراج الأسعار والقوائم والجداول |
observe(instruction) | تعيد الإجراءات المرشَّحة دون تنفيذها | التخطيط، التشغيل التجريبي، حلقات الوكيل |
كلّ ما عداها يمكنك إنجازه عبر Playwright المباشر مثل page.goto و page.waitForSelector. هذا الأسلوب الهجين يبقي الأجزاء الحتميّة رخيصة، والأجزاء الضبابيّة متينة.
الخطوة 1: تهيئة المشروع
أنشئ مشروعًا جديدًا ونصِّب Stagehand مع Zod للتحقّق من المخطّط.
mkdir stagehand-agent && cd stagehand-agent
pnpm init
pnpm add @browserbasehq/stagehand zod
pnpm add -D typescript tsx @types/node
npx tsc --initافتح tsconfig.json وتأكّد من ضبط هذه القيم حتى يعمل top-level await:
{
"compilerOptions": {
"target": "ES2022",
"module": "ESNext",
"moduleResolution": "Bundler",
"strict": true,
"esModuleInterop": true
}
}أضف "type": "module" إلى ملفّ package.json ليعالج Node الملفّات كـ ESM.
الخطوة 2: ضبط Browserbase ونموذج اللغة
أنشئ ملفّ .env ولا تضعه أبدًا في المستودع.
BROWSERBASE_API_KEY=bb_xxxxxxxxxxxxxxxxxxxxxxx
BROWSERBASE_PROJECT_ID=prj_xxxxxxxxxxxxxxxxxxxx
OPENAI_API_KEY=sk-xxxxxxxxxxxxxxxxxxxxxxxxثمّ أنشئ src/client.ts لتجميع مثيل Stagehand:
import { Stagehand } from "@browserbasehq/stagehand";
export function createStagehand() {
return new Stagehand({
env: "BROWSERBASE",
apiKey: process.env.BROWSERBASE_API_KEY,
projectId: process.env.BROWSERBASE_PROJECT_ID,
modelName: "gpt-4o-mini",
modelClientOptions: {
apiKey: process.env.OPENAI_API_KEY,
},
verbose: 1,
});
}ملاحظات على الإعدادات:
env: "BROWSERBASE"يشغّل الجلسة في السحابة. بدّلها إلى"LOCAL"للتشغيل المحلّي على Chromium أثناء التطوير.- يقبل
modelNameأيّ نموذج يدعمه مزوِّدك. بالنسبة لمعظم المهام، يمثّلgpt-4o-miniالموازنة المثلى بين الكلفة والدقّة. استخدمgpt-4oأوclaude-sonnet-4عندما تكون جودة الاستخراج حرجة. - يطبع
verbose: 1خطوات التفكير. اضبطه على2أثناء التصحيح.
الخطوة 3: أوّل إجراء بلغة طبيعية
لنبدأ بمثال "hello world" التقليدي: ابحث في Google عن "Stagehand Browserbase" واقرأ أوّل نتيجة.
أنشئ src/01-act.ts:
import "dotenv/config";
import { createStagehand } from "./client.js";
async function main() {
const stagehand = createStagehand();
await stagehand.init();
const page = stagehand.page;
await page.goto("https://www.google.com");
await page.act("accept cookies if a banner is showing");
await page.act("type 'Stagehand Browserbase' into the search box and press Enter");
await page.act("click the first organic result");
console.log("Final URL:", page.url());
await stagehand.close();
}
main();شغّل الملفّ:
npx tsx src/01-act.tsسيحدث أمران. أوّلًا، تُفتح جلسة على Browserbase يمكنك مراقبتها مباشرةً من لوحة التحكّم. ثانيًا، يحوِّل Stagehand كلّ تعليمة إنجليزية إلى خطّة Playwright صغيرة وينفّذها.
لاحظ أنّنا لم نكتب محدِّدًا واحدًا.
الخطوة 4: الاستخراج المنظَّم باستخدام Zod
يبرز Stagehand فعلًا في الاستخراج المنظَّم. عرِّف ما تريده باستخدام Zod، ويتكفّل الوكيل بالباقي.
أنشئ src/02-extract.ts:
import "dotenv/config";
import { z } from "zod";
import { createStagehand } from "./client.js";
const ProductSchema = z.object({
title: z.string(),
priceUsd: z.number().describe("Price in US dollars, numeric only"),
rating: z.number().min(0).max(5).optional(),
inStock: z.boolean(),
bulletPoints: z.array(z.string()).max(8),
});
async function main() {
const stagehand = createStagehand();
await stagehand.init();
const page = stagehand.page;
await page.goto("https://www.example-shop.com/products/widget-pro");
const product = await page.extract({
instruction: "Extract the product title, price, rating, stock status, and the bullet points under 'About this item'",
schema: ProductSchema,
});
console.log(product);
await stagehand.close();
}
main();استدعاءات .describe() على كلّ حقل ليست زخرفة. يقرأها النموذج ويستخدمها كتعليمات. تعامل معها كجزء من تلقينك.
إذا أعاد الاستخراج بيانات غير متماسكة، فالسبب عادةً واحد من ثلاثة:
- المخطّط فضفاض. أضف تلميحات
.describe(). - التعليمة مبهمة. اربطها بمنطقة مرئيّة من الصفحة.
- النموذج صغير جدًّا. ارفع
gpt-4o-miniإلىgpt-4oلذلك الاستدعاء تحديدًا.
الخطوة 5: لاحِظ قبل أن تنفِّذ
تعيد observe الإجراءات التي يرى النموذج أنّها متاحة دون تنفيذها. بهذا تبني حلقات الوكلاء دون انفلات في التكلفة أو أخطاء مدمِّرة.
أنشئ src/03-observe.ts:
import "dotenv/config";
import { createStagehand } from "./client.js";
async function main() {
const stagehand = createStagehand();
await stagehand.init();
const page = stagehand.page;
await page.goto("https://news.ycombinator.com");
const candidates = await page.observe({
instruction: "Find all clickable links to story comment threads on this page",
});
console.log(`Found ${candidates.length} candidate actions`);
for (const c of candidates.slice(0, 5)) {
console.log("-", c.description);
}
await page.act(candidates[0]);
console.log("Navigated to:", page.url());
await stagehand.close();
}
main();سببان لأهميّة ذلك في الإنتاج:
- التكرار الآمن. مرِّر نتيجة
ObserveResultمباشرةً إلىact، فيستخدم التشغيل التالي المحدِّد المخزَّن مؤقّتًا متجاوزًا استدعاء النموذج. - الأمان. يمكنك فحص الإجراء المقترح، أو تسجيله، أو حتى طلب تأكيد بشري قبل أيّ تعديل على الصفحة.
الخطوة 6: بناء وكيل متعدّد الخطوات
لندمج كلّ ما سبق في وكيل صغير يبحث في موقع وظائف، ويصفّي العمل عن بُعد، ويستخرج أوّل عشر وظائف.
أنشئ src/04-agent.ts:
import "dotenv/config";
import { z } from "zod";
import { createStagehand } from "./client.js";
const JobsSchema = z.object({
jobs: z.array(
z.object({
title: z.string(),
company: z.string(),
location: z.string(),
url: z.string().url(),
})
).max(10),
});
async function main() {
const stagehand = createStagehand();
await stagehand.init();
const page = stagehand.page;
await page.goto("https://example-jobs.dev");
await page.act("search for 'typescript' in the main search field and submit");
await page.act("apply the 'Remote' filter from the location facet");
await page.act("sort results by most recent");
await page.waitForLoadState("networkidle");
const { jobs } = await page.extract({
instruction: "Extract the first 10 visible job listings",
schema: JobsSchema,
});
console.table(jobs);
await stagehand.close();
}
main();بعض الأنماط الإنتاجية الجديرة بالتنويه:
- اخلط Playwright المباشر مع استدعاءات الذكاء الاصطناعي.
waitForLoadState("networkidle")حتميّ ومجّاني. استخدمه. - حدِّد حجم المصفوفات. يمنع
.max(10)على المخطّط النموذج من إرجاع مئات الصفوف وإنهاك ميزانية السياق. - اجعل التعليمات ذرّيّة. نفِّذ
actواحدًا لكلّ إجراء منطقي. التعليمات المتسلسلة يصعب على النموذج التخطيط لها بثبات.
الخطوة 7: استمرار الجلسات والمصادقة
تحتاج مهامّ الإنتاج إلى مصادقة. يدعم Browserbase سياقات مستمرّة، فتسجِّل الدخول مرّة واحدة وتعيد استخدام الجلسة عبر التشغيلات.
import { Stagehand } from "@browserbasehq/stagehand";
const stagehand = new Stagehand({
env: "BROWSERBASE",
apiKey: process.env.BROWSERBASE_API_KEY,
projectId: process.env.BROWSERBASE_PROJECT_ID,
browserbaseSessionCreateParams: {
projectId: process.env.BROWSERBASE_PROJECT_ID!,
browserSettings: {
context: {
id: "ctx_persistent_login",
persist: true,
},
},
},
});في أوّل تشغيل، نفّذ خطوات تسجيل الدخول يدويًّا داخل استدعاءات act. في التشغيلات اللاحقة، تتمّ استعادة ملفّات تعريف الارتباط والتخزين المحلّي تلقائيًّا. تعامل مع مُعرِّف السياق كسرّ لأنّه يمنح وصولًا مستأنفًا إلى المواقع التي صادقت عليها.
الخطوة 8: المراقبة وإعادة تشغيل الجلسة
تسجِّل كلّ جلسة Browserbase فيديو، وسجلّات شبكة، وخطًّا زمنيًّا لِلَقَطات DOM. عندما يخفق شيء ما، يُخبرك الرابط في لوحة التحكّم بالضبط أين تاه الوكيل.
أضف مساعدًا صغيرًا يطبع رابط إعادة التشغيل في كلّ تشغيل:
const session = await stagehand.context.browser?.sessionId;
console.log(`Replay: https://www.browserbase.com/sessions/${session}`);للوكلاء متعدّدي الخطوات، سجِّل مخرجات observe و extract في ملفّ. الجمع بين تفكير النموذج والفيديو يختصر زمن التصحيح من ساعات إلى دقائق.
الخطوة 9: نصائح للتكلفة والثبات
بعض القواعد التي تعلّمناها من تجربة تشغيل الوكلاء على نطاق واسع:
- خزِّن نتائج
observeمؤقّتًا. إعادة استخدام محدِّد مُحَلّ تكلفته صفر من رموز النموذج. - استخدم نموذجًا صغيرًا للإجراءات ونموذجًا أكبر للاستخراج. يمكنك تبديل
modelNameلكلّ استدعاء. - اضبط مهلات لكلّ شيء. يجب أن يكون لكلّ استدعاء
actوextractو Playwright حدٌّ أعلى. - افضّل الاستخراج على لقطات الشاشة. مدخلات الصور باهظة وبطيئة. الاستخراج النصّي بمخطّط جيّد أسرع وأكثر موثوقيّة.
- شغِّل المهامّ بالتوازي عبر جلسات Browserbase. الجلسات معزولة افتراضيًّا، فيمكنك توزيع العمل على عشرات الوكلاء دون تصادم المحدِّدات.
اختبار الوكيل
يتناغم Stagehand جيّدًا مع Vitest. الوصفة التي نعتمدها:
import { describe, it, expect, beforeAll, afterAll } from "vitest";
import { createStagehand } from "../src/client.js";
let stagehand: ReturnType<typeof createStagehand>;
beforeAll(async () => {
stagehand = createStagehand();
await stagehand.init();
});
afterAll(async () => {
await stagehand.close();
});
describe("product extraction", () => {
it("extracts a price as a number", async () => {
await stagehand.page.goto("https://example-shop.com/products/widget");
const { priceUsd } = await stagehand.page.extract({
instruction: "Extract the visible product price in USD",
schema: z.object({ priceUsd: z.number() }),
});
expect(priceUsd).toBeGreaterThan(0);
});
});شغِّل الاختبارات بـ vitest --no-file-parallelism حتى لا تتعارض اختبارات متعدّدة على نفس جلسة Browserbase.
استكشاف الأخطاء وإصلاحها
يضغط الوكيل على العنصر الخطأ. شدِّد التعليمة. "اضغط على الزرّ الرئيسي في قسم الـ hero" أفضل من "اضغط على الزرّ".
يعيد الاستخراج حقولًا فارغة. تأكّد أنّ البيانات في DOM فعلًا، وليست مؤجَّلة خلف intersection observer. أضف await page.waitForLoadState("networkidle") أو مرِّر السكرول إلى القسم أوّلًا.
تجاوز حدود الاستدعاء. استخدم نموذجًا أصغر لـ act، وادمج استدعاءات extract في تعليمة واحدة بمخطّط أغنى، وخزِّن نتائج observe مؤقّتًا.
اختبارات Captcha. يضمّ Browserbase حلًّا مدمجًا. فعِّله عبر browserSettings.solveCaptchas: true عند إنشاء الجلسة.
الخطوات التالية
- استكشف وثائق Stagehand للاطّلاع على واجهة برمجة كاملة
- اقرأ درسنا حول مكشطة ويب بالذكاء الاصطناعي مع Playwright لمقارنة الأساليب
- ادمج Stagehand مع Trigger.dev لتشغيل الوكلاء وفق جدول زمني
- اربطه بـ Mastra لمنظومة وكلاء متكاملة
خاتمة
أصبح لديك الآن وكيل بلغة TypeScript يتصفّح الويب كإنسان، ويستخرج البيانات كواجهة برمجية، ويمكن تصحيح أخطائه كاختبار وحدة. النموذج الذهني بسيط: Playwright الحتميّ للأجزاء التي تتحكّم بها، و act و extract للأجزاء المتغيّرة.
لم يعد المتصفّح واجهةً معاديةً للأتمتة. مع Stagehand و Browserbase، أصبح أداةً أخرى يستطيع كودك الوصول إليها، بمرونة نموذج لغة وسرعة Chromium المُجرَّد من الرأس.