الوكيل الذي لا يستطيع أحد تشغيله
معظم أكواد الوكلاء تبدأ حياتها في الطرفية. تكتب مطالبة، فيستدعي النموذج بعض الأدوات، وتتغيّر الملفات، وينجح الأمر. ثم يطرح أحدهم السؤال البديهي: هل يمكن تشغيله وفق جدول زمني، من دونك؟
هنا تنهار الأمور عادةً. فقد كُتبت الحلقة حول وجود إنسان — شخص يوافق على الخطوة التالية، ويقرأ المخرجات، ويعيد المحاولة عند فشل أداة، ويتذكّر ما حدث في المرة السابقة. أخرِج الإنسان من المعادلة، وستجد نفسك مضطرًا لإعادة بناء الأجزاء المملّة: حالة الجلسة، ونظام ملفات يمكن للنموذج التعامل معه بأمان، وإعادة محاولات تصمد أمام الانهيار، ومكان فعلي للنشر.
إطار Flue مبني تحديدًا حول هذه الطبقة الغائبة. وصفه المشارك في تأسيس Astro، فريد ك. شوت، بعبارة صريحة: يبدو استخدامه مثل Claude Code، لكنه بلا واجهة بنسبة 100% وقابل للبرمجة بالكامل. لا واجهة طرفية، ولا واجهة رسومية، ولا افتراض بوجود مشغّل يراقب. ومعادلة الإطار نفسها هي أوضح تلخيص لتصميمه:
الوكيل = النموذج + التسخير (Harness)
النموذج سلعة تختارها بسلسلة نصية. أما التسخير — الجلسات والأدوات والمهارات وبيئة العزل ونظام ملفات دائم وهدف نشر — فهو الجزء الذي يمنحك Flue فعليًا.
يبني هذا الدرس وكيلًا حقيقيًا: وكيل مراجعة محتوى مستقل يقرأ مقالًا بصيغة Markdown، ويفحصه وفق قائمة تحقّق تحريرية، ويستدعي أداة مُوثّقة الأنواع للحصول على بيانات لا يمكنه تخمينها، ثم يكتب ملاحظاته في ملف. ستشغّله محليًا، وتستدعيه عبر HTTP، ثم تنشره على Cloudflare Workers حيث تصبح كل نسخة من الوكيل كائنًا دائمًا (Durable Object) خاصًا بها.
المتطلبات المسبقة
قبل البدء، تأكد من توفّر:
- Node.js الإصدار 22 أو أحدث ومدير حزم (npm أو pnpm أو bun)
- مفتاح واجهة برمجية من Anthropic (أو أي مزوّد مدعوم آخر) مُصدَّر كمتغيّر بيئة
- معرفة عملية بـ TypeScript — وحدات ES والدوال غير المتزامنة والأنواع العامة
- حساب Cloudflare مع أداة
wrangler، لقسم النشر فقط - الإلمام بالتحقق من المخططات مفيد؛ فـ Flue يستخدم Valibot لا Zod لمخططات الأدوات
ملاحظة بخصوص الإصدارات: أُعلن عن Flue علنًا في 1 مايو 2026، وصدرت نسخته 1.0 التجريبية في 16 يونيو 2026 بعد إعادة كتابة شملت 335 التزامًا برمجيًا. الإطار يتطور بسرعة، وقد غيّر بالفعل واجهة الأدوات الخاصة به تغييرًا كاسرًا مرة واحدة (المزيد عن ذلك في قسم استكشاف الأخطاء). ثبّت إصداراتك.
ما ستبنيه
مشروع واحد بأربعة مكوّنات متحركة:
| المكوّن | الملف | الوظيفة |
|---|---|---|
| الوكيل | agents/reviewer.ts | سياسة التنفيذ: النموذج والتوجيهات والأدوات والمهارات وبيئة العزل |
| المهارة | skills/review-checklist/SKILL.md | المعيار التحريري، بصيغة Markdown |
| الأداة | shared/tools.ts | استعلام مُوثّق الأنواع لا يستطيع النموذج تلفيقه |
| سير العمل | workflows/review-article.ts | تنسيق محدود، يُستدعى عبر HTTP |
الوكيل النهائي يستقبل مستند Markdown، ويراجعه، ويعيد ملاحظات مُهيكلة. والمصدر نفسه يُنشر على خادم Node طويل الأمد أو على حوسبة Cloudflare الطرفية دون إعادة كتابة.
الخطوة 1: تهيئة المشروع
ثبّت بيئة التشغيل كاعتمادية، وواجهة الأوامر كاعتمادية تطوير:
mkdir article-reviewer && cd article-reviewer
npm init -y
npm install @flue/runtime
npm install --save-dev @flue/cliأنشئ ملف الإعداد. يحدّد خيار --target بيئة التشغيل التي تبني لها:
npx flue init --target nodeينتج عن ذلك ملف flue.config.ts:
// flue.config.ts
import { defineConfig } from '@flue/cli/config';
export default defineConfig({
target: 'node',
});من المفيد معرفة التوقيع الكامل، لأنك ستستخدم --root في مستودع أحادي:
flue init --target <node|cloudflare> [--root <path>] [--force]الآن أنشئ بنية المجلدات التي يكتشفها Flue بالاصطلاح:
mkdir -p agents workflows skills/review-checklist sharedيتعامل Flue مع هذه المجلدات على أنها ذات دلالة:
agents/— وكيل واحد لكل ملف. يصبح اسم الملف هو مُعرّف الوكيل، فـagents/reviewer.tsهو الوكيلreviewer. الاكتشاف هنا مطلوب لمسارات الوكلاء الدائمة ولاستخدامdispatch().workflows/— سير عمل واحد لكل ملف، يُصدَّر افتراضيًا.skills/— حزم توجيهات بصيغة Markdown.shared/— وحدات عادية؛ بلا أي سلوك خاص.
الخطوة 2: تعريف أول وكيل
الوكيل في Flue ليس كائنًا طويل العمر. تستقبل الدالة defineAgent مُهيّئًا — دالة تُنفَّذ في كل مرة يبني فيها المُشغّل تسخيرًا جذريًا من التعريف. هذا الفرق مهم: لا تتعامل معه كمُنشئ لنسخة واحدة دائمة.
// agents/reviewer.ts
import { defineAgent } from '@flue/runtime';
export default defineAgent(() => ({
model: 'anthropic/claude-sonnet-4-6',
instructions: [
'You are an editorial reviewer for a technical publication.',
'Review the requested document and report only findings supported by evidence from the text.',
'Never invent a quotation, a statistic, or a source.',
].join('\n'),
}));حقلان فقط ويصبح لديك وكيل عامل. يستقبل المُهيّئ كائن سياق، وهو المكان الذي تصل فيه هوية كل تشغيل وارتباطات المنصة:
export default defineAgent(({ id, env }) => ({
model: 'anthropic/claude-sonnet-4-6',
instructions: `Reviewing under run ${id}.`,
}));الحقل id هو مُعرّف نسخة الوكيل أو مُعرّف تشغيل سير العمل. ويحمل env ارتباطات بيئة المنصة التي توفّرها بيئة التشغيل — وعلى Cloudflare هذه هي وسيلتك للوصول إلى ارتباطات الـ Worker.
سطح الإعداد الكامل AgentRuntimeConfig صغير بما يكفي لحفظه:
| الحقل | الغرض |
|---|---|
model | مُحدِّد النموذج الافتراضي، بصيغة provider/model |
instructions | تُضاف قبل سياق مساحة العمل المُكتشَف |
tools | أدوات مخصصة قابلة للاستدعاء من النموذج |
skills | مهارات Markdown مُسجّلة |
actions | إجراءات قابلة لإعادة الاستخدام تُعرض كأدوات يديرها الإطار |
subagents | ملفات تعريف مُسمّاة متاحة للتفويض عبر session.task() |
sandbox | المكان الذي تُنفَّذ فيه عمليات الكود والملفات |
cwd | مجلد العمل الخاص بالوكيل |
thinkingLevel | مستوى الاستدلال الافتراضي؛ يمكن لكل عملية تجاوزه |
profile | خط أساس قابل لإعادة الاستخدام؛ حقول الوكيل تستبدله أو توسّعه |
description | بيانات وصفية تنظيمية لهذه التهيئة |
لاحظ ما هو غائب: لا رسم بياني، ولا سجل عُقد، ولا آلة حالات. رهان Flue أن نموذجًا قادرًا مع تسخير مبني جيدًا يتفوّق على مخطط تنسيق صريح. وإن أردت الفلسفة المعاكسة، فقارنه بـ الرسوم البيانية ذات الحالة في LangGraph.
الخطوة 3: تلقين المعايير عبر مهارة
التوجيهات تعيش في تعريف الوكيل وتنطبق على كل شيء. أما المعايير التي تخصّ نوعًا واحدًا من المهام فمكانها مهارة — ملف Markdown بسيط تُسجّله بيئة التشغيل ويمكن للنموذج جلبه.
<!-- skills/review-checklist/SKILL.md -->
# Editorial review checklist
Apply every rule below. For each finding, quote the offending text.
## Accuracy
- Every version number, benchmark figure, and price must be attributable to the document itself.
- Flag any claim phrased as fact without a source.
## Structure
- The opening must state the problem before naming any product.
- Headings must be scannable and describe outcomes, not features.
- Code blocks must declare a language for syntax highlighting.
## Language
- Prefer the active voice.
- Cut hedging: "arguably", "it could be said", "quite possibly".
- Expand every acronym on first use.
## Output contract
Write findings to `review.md` as a Markdown list, most severe first.
Each entry: severity, the quoted text, and the concrete fix.
If the document passes a section cleanly, say so in one line.استوردها بواسطة سمة استيراد — بهذا يميّز Flue المهارة عن أصل Markdown عادي:
// agents/reviewer.ts
import { defineAgent } from '@flue/runtime';
import reviewChecklist from '../skills/review-checklist/SKILL.md' with { type: 'skill' };
export default defineAgent(() => ({
model: 'anthropic/claude-sonnet-4-6',
instructions: 'Review the requested document and report only evidence-backed findings.',
skills: [reviewChecklist],
}));الاحتفاظ بقائمة التحقّق بصيغة Markdown مكسب تشغيلي حقيقي: يمكن لمحرّرك تعديل المعيار في طلب دمج دون لمس TypeScript، والفرق البرمجي مقروء لمن لا يكتب كودًا.
الخطوة 4: إضافة أداة مُوثّقة الأنواع بـ Valibot
المهارات تصيغ الحكم. أما الأدوات فهي للحقائق التي لا يجوز للنموذج تخمينها — ميزانية عدد كلمات من نظام إدارة المحتوى، قائمة مصطلحات معتمدة، تاريخ نشر.
تتحقق defineTool من الأداة وتعيد تعريفًا مُجمّدًا. المخططات بـ Valibot، ويجب أن يكون مخطط input مخطط كائن من المستوى الأعلى:
// shared/tools.ts
import { defineTool } from '@flue/runtime';
import * as v from 'valibot';
import { cms } from './cms.ts';
export const lookupStyleBudget = defineTool({
name: 'lookup_style_budget',
description:
'Read the publication style budget for a content type. Use before commenting on article length or heading depth.',
input: v.object({
contentType: v.picklist(['tutorial', 'news', 'blog']),
}),
output: v.object({
minWords: v.number(),
maxWords: v.number(),
maxHeadingDepth: v.number(),
}),
async run({ input, signal }) {
return cms.getStyleBudget(input.contentType, { signal });
},
});عقد التحقّق دقيق، وهو ما يجعل عرض الأدوات آمنًا:
- يُتحقَّق من المدخلات التي يقدّمها النموذج وتُحلَّل قبل أن تصل إلى
run. - يتحوّل فشل التحقّق إلى خطأ أداة، فيقرأه النموذج ويعيد المحاولة — ولا يُسقِط التشغيل.
- عند وجود
output، يُتحقَّق من القيمة المُعادة وتُحلَّل أيضًا، ثم تُلتقط كبيانات متوافقة مع JSON وتُحوَّل إلى نص JSON للنموذج. - بدون مخطط
output، تُرسل القيمةnullإلى النموذج إذا أعادت الدالةundefined. - يجب أن تكون أسماء الأدوات فريدة بين الأدوات المدمجة والمخصصة النشطة؛ ويُفحص التعارض عند تجميع الجلسة لقائمة أدواتها.
الأدوات المُقيّدة تتفوّق على المطالبات الذكية
أقيَم نمط هنا لا علاقة له بالأنواع. تطبيقك هو من يحدّد الحدود؛ والنموذج يختار القيم داخلها فقط. أغلِق على هوية المستأجر بدلًا من قبولها كمُعامل:
// agents/support.ts
import { defineAgent, defineTool } from '@flue/runtime';
import * as v from 'valibot';
import { orders } from '../shared/orders.ts';
export default defineAgent(({ id: customerId }) => ({
model: 'anthropic/claude-haiku-4-5',
tools: [
defineTool({
name: 'lookup_customer_order',
description: 'Look up one order belonging to this customer.',
input: v.object({
orderId: v.string(),
}),
async run({ input }) {
const status = await orders.getStatus(customerId, input.orderId);
return status ?? 'No accessible order was found.';
},
}),
],
}));يأتي customerId من سياق المُهيّئ، لا من النموذج. لا يوجد أي مُعامل يستطيع النموذج ضبطه لقراءة طلبات عميل آخر، ولذلك لا يصل أي قدر من حقن المطالبات إلى تلك البيانات. قارن هذا بالدفاعات على مستوى المطالبة في دليل ضوابط أمان وكلاء الذكاء الاصطناعي — الحدود البنيوية أقوى بلا منازع، لأن أي تجاوز للحماية لا يمكنه إقناع إغلاق برمجي بالتنازل.
اربط الأداة بالمراجع:
// agents/reviewer.ts
import { defineAgent } from '@flue/runtime';
import { local } from '@flue/runtime/node';
import reviewChecklist from '../skills/review-checklist/SKILL.md' with { type: 'skill' };
import { lookupStyleBudget } from '../shared/tools.ts';
export default defineAgent(() => ({
model: 'anthropic/claude-sonnet-4-6',
instructions: 'Review the requested document and report only evidence-backed findings.',
skills: [reviewChecklist],
tools: [lookupStyleBudget],
sandbox: local(),
}));الخطوة 5: التنسيق عبر سير عمل
الوكيل سياسة. أما سير العمل فهو مهمة محدودة تستعير تلك السياسة ثم تنتهي. صدّره افتراضيًا من workflows/<name>.ts:
// workflows/review-article.ts
import { defineWorkflow } from '@flue/runtime';
import * as v from 'valibot';
import reviewer from '../agents/reviewer.ts';
export default defineWorkflow({
agent: reviewer,
input: v.object({
document: v.string(),
contentType: v.picklist(['tutorial', 'news', 'blog']),
}),
output: v.object({
review: v.string(),
}),
async run({ harness, input }) {
await harness.fs.writeFile('document.md', input.document);
const session = await harness.session();
await session.prompt(
`Review document.md as a ${input.contentType}. Apply the editorial checklist and write your findings to review.md.`,
);
return { review: await harness.fs.readFile('review.md') };
},
});في هذه الدالة القصيرة يثمر تصميم Flue. ثلاثة أمور تستحق الانتباه:
harness.fs هو نقطة التسليم. تكتب المدخل كملف، فيقرأ الوكيل الملفات ويعدّلها، ثم تقرأ النتيجة. لا تحليل هشّ لردّ نصي، ولا مطالبة النموذج بإخراج JSON نظيف مع الأمل في الأفضل. نظام الملفات هو العقد، وهذه تحديدًا طريقة عمل مهندس بشري.
harness.session() ذات حالة. تتراكم في الجلسة سياقات الأدوار المتعددة. استدعِ prompt عدة مرات وسيتذكّر الوكيل ما سبق:
async run({ harness, input }) {
await harness.fs.writeFile('document.md', input.document);
const session = await harness.session();
await session.prompt('Read document.md and list every factual claim to review.md.');
await session.prompt('Now verify each listed claim against the document. Delete any you cannot support.');
await session.prompt('Rewrite review.md sorted by severity, most severe first.');
return { review: await harness.fs.readFile('review.md') };
}كل مرور سهل التتبّع ذهنيًا، والحالة الوسيطة قابلة للفحص على القرص — وهذا أفضل بكثير لتصحيح الأخطاء من مطالبة واحدة ضخمة.
قد يكون الوكيل خاصًا. لا يلزم أن يعيش وكيل سير العمل تحت agents/. ضعه مباشرةً عندما لا يستخدمه شيء آخر:
const workflow = defineWorkflow({
agent,
async run({ harness }) {
return await (await harness.session()).prompt('Triage this incoming issue.');
},
});الاكتشاف تحت agents/ مطلوب فقط لمسارات الوكلاء الدائمة ولاستخدام dispatch().
للدالة defineWorkflow صيغة ثانية تستقبل action بدلًا من run، عندما تريد أن يعيش عقد المدخلات والمخرجات في إجراء قابل لإعادة الاستخدام:
function defineWorkflow<TAction extends ActionDefinition>(options: {
agent: AgentDefinition;
action: TAction;
}): WorkflowDefinition<TAction>;الصيغة المستخلَصة لا تقبل input أو output — فهذه العقود تنتمي إلى الإجراء نفسه.
الخطوة 6: التشغيل محليًا والاستدعاء عبر HTTP
طوّر عبر خادم التطوير:
npx flue devولإرفاق جلسة تفاعلية بوكيل أثناء التطوير:
npx flue connect reviewer local-sessionوللحصول على مُخرَج Node قابل للنشر، ابنِ وشغّل:
npx flue build --target node
PORT=8080 node dist/server.mjsيعرض هدف Node واجهة مهام غير متزامنة — وهي الشكل الصحيح لعمل الوكلاء، الذي يستغرق ثوانٍ إلى دقائق لا أجزاء من الثانية:
POST /workflows/review-articleيبدأ تشغيلًا ويعيد مُعرّف تشغيلGET /runs/[runId]يستعلم عن النتيجة
curl -X POST http://localhost:8080/workflows/review-article \
-H 'Content-Type: application/json' \
-d '{"document":"# Draft\n\nOur framework is arguably the fastest.","contentType":"tutorial"}'
# => {"runId":"run_01J..."}
curl http://localhost:8080/runs/run_01J...ولإضافة وسيط — مصادقة أو تحديد معدّل أو تسجيل طلبات — صدّر معالج مسار من سير العمل:
import type { WorkflowRouteHandler } from '@flue/runtime/node';
export const route: WorkflowRouteHandler = async (c, next) => {
if (c.req.header('authorization') !== `Bearer ${process.env.TRIGGER_TOKEN}`) {
return new Response('Unauthorized', { status: 401 });
}
return next();
};لا تتجاوز هذا الفحص. فنقطة نهاية سير العمل تُنفق مالًا في كل استدعاء، ما يجعل ترْكها بلا مصادقة ثغرة فوترة بقدر ما هي ثغرة أمنية. اقرنها بـ تحديد المعدّل عبر Upstash قبل عرضها للعموم.
الخطوة 7: اختيار بيئة العزل
تحدّد بيئة العزل ما يعنيه فعليًا "اكتب ملفًا" و"نفّذ أمرًا". يوفّر Flue خيارين بمقايضات مختلفة تمامًا.
just-bash هو الافتراضي. يعمل في الذاكرة ولا يحمل أي تكلفة بنية تحتية. يحصل الوكيل على نظام ملفات متّسق ودلالات صدفة دون أي شيء لتجهيزه. ولعمل المستندات مثل حالتنا فهو كافٍ فعلًا.
local() يمنح وصولًا حقيقيًا لنظام الملفات. استوردها من نقطة دخول Node:
import { local } from '@flue/runtime/node';
export default defineAgent(() => ({
model: 'anthropic/claude-sonnet-4-6',
cwd: '/srv/repositories/catalog-service',
sandbox: local(),
}));استخدم local() عندما يجب أن يعمل الوكيل على نسخة عمل حقيقية — مراجعة مستودع، أو تشغيل حزمة اختبارات، أو بناء مشروع. حدّد نطاقها بـ cwd، وتعامل مع ذلك المجلد كقابل للكتابة بالكامل من النموذج. المقايضة حقيقية: لقد أعطيت نموذجًا لغويًا قرصك، فشغّله بمستخدم غير مُتميّز، داخل حاوية، على جهاز لا يضايقك إعادة بنائه.
وهذا هو الوكيل الأكمل الذي ينتج عن هذا النمط:
import { defineAgent } from '@flue/runtime';
import { local } from '@flue/runtime/node';
import reviewChecklist from '../skills/review-checklist/SKILL.md' with { type: 'skill' };
import { reviewChange } from '../actions/review-change.ts';
import { repositoryTools } from '../shared/repository-tools.ts';
export default defineAgent(() => ({
model: 'anthropic/claude-sonnet-4-6',
instructions: 'Review the requested change and report only findings supported by evidence.',
cwd: '/srv/repositories/catalog-service',
actions: [reviewChange],
tools: repositoryTools,
skills: [reviewChecklist],
sandbox: local(),
}));الخطوة 8: النشر على Cloudflare Workers
إطار Flue هو أول إطار مبني على حزمة تطوير وكلاء Cloudflare، وهذه مصادفة أقل مما تبدو: فقد استحوذت Cloudflare على فريق Astro في يناير 2026.
بدّل الهدف فيتغيّر نموذج النشر من تحتك، مع بقاء كود الوكيل كما هو:
// flue.config.ts
import { defineConfig } from '@flue/cli/config';
export default defineConfig({
target: 'cloudflare',
});يصبح كل وكيل كائنًا دائمًا (Durable Object). هذه الجملة الواحدة تحلّ معظم الأجزاء الصعبة في استضافة الوكلاء. فالكائن الدائم وحدة حوسبة أحادية الخيط، قابلة للعنونة، ذات حالة وتخزين خاص، ولذلك تجد حالة الجلسة مكانها الطبيعي. لا جلسات لاصقة لإعدادها، ولا خوادم لتجهيزها، ويصبح التوسّع نتيجةً للمعمارية لا مشروعًا قائمًا بذاته.
تأتي الاستمرارية من عناصر حزمة تطوير الوكلاء — runFiber() وstash() وonFiberRecovered() — فالتشغيل الذي يتعطّل في منتصف الطريق يستطيع الاستئناف بدلًا من البدء من جديد. وإن كنت قد بنيت منطق تعويض لتشغيلات وكلاء نصف مكتملة على طابور تقليدي، فهذا هو الجزء الجدير بالانتباه.
ولتنفيذ الكود في بيئة معزولة، يستخدم هدف Cloudflare حزمة @cloudflare/codemode التي تغلّف Dynamic Workers: ينشئ نمط Code Mode عاملًا ديناميكيًا جديدًا لكل مقطع كود، ويشغّله، ثم يتخلّص منه. تبدأ العوازل في أقل من 10 ميلي ثانية بتكلفة نحو 0.002 دولار لكل تحميل. كل مقطع يحصل على بيئة نظيفة قابلة للتخلّص، وهذا عزل أقوى من إعادة استخدام حاوية واحدة طويلة العمر — ورخيص بما يكفي لاستخدامه مع كل استدعاء أداة.
طوّر مقابل بيئة تشغيل Cloudflare محليًا، بقراءة المتغيّرات من .dev.vars أو .env:
npx flue dev --target cloudflareثم ابنِ وانشر:
# One-off build for Cloudflare
npx flue build --target cloudflare
# Configure a deployed secret interactively, then deploy
npx wrangler secret put ANTHROPIC_API_KEY
npx wrangler deploy --config dist/my-agent/wrangler.jsonلاحظ أن البناء يُخرِج ملف wrangler.json خاصًا به تحت dist/؛ فوجّه أمر wrangler deploy إلى ذلك الملف بدلًا من كتابة واحد يدويًا.
ولأنك داخل منصة Cloudflare، تتوفّر الارتباطات المحيطة عبر env في مُهيّئك — بوابة الذكاء الاصطناعي، وتشغيل المتصفح، وخدمة البريد، وذاكرة الوكيل، والبحث بالذكاء الاصطناعي. وتمرير استدعاءات النموذج عبر بوابة الذكاء الاصطناعي هو المكسب السهل: تحصل على تخزين مؤقت وإعادة محاولات ورؤية للإنفاق لكل نموذج دون لمس كود الوكيل. ويغطي درس بوابة الذكاء الاصطناعي الفكرة نفسها من زاوية توجيه المزوّدين.
الخطوة 9: التفويض إلى وكلاء فرعيين
جلسة واحدة تحمل كل الاهتمامات تُنتج نافذة سياق متضخّمة وحكمًا متوسط الجودة. يعلن الحقل subagents عن ملفات تعريف مُسمّاة، وتفوّض session.task() إليها — لكل منها سياقه الخاص:
export default defineAgent(() => ({
model: 'anthropic/claude-sonnet-4-6',
instructions: 'Coordinate a multi-pass editorial review.',
subagents: [
{
description: 'Verifies factual claims against the document only.',
model: 'anthropic/claude-sonnet-4-6',
instructions: 'Check each claim. Report unsupported ones. Never speculate.',
},
{
description: 'Checks structure, headings, and code-block languages.',
model: 'anthropic/claude-haiku-4-5',
instructions: 'Report structural problems only. Ignore factual content.',
},
],
}));يمنحك هذا أمرين. يقرأ كل وكيل فرعي ما تتطلبه وظيفته فقط، فتبقى الدقة مرتفعة وإنفاق التوكنات منخفضًا — فيتولّى نموذج رخيص المرور البنيوي بينما يتولّى النموذج الأغلى التحقّق من الحقائق. ويمكن ضبط thinkingLevel لكل ملف تعريف، فيتوجّه جهد الاستدلال إلى حيث يستحق تكلفته.
اختبار التنفيذ
اختبارات الوكلاء التي تستدعي نموذجًا حقيقيًا بطيئة ومكلفة وغير حتمية. تتيح أدوات اختبار Flue كتابة ردود النموذج مسبقًا، فتتحقق من منطقك أنت:
const provider = createProvider();
provider.setResponses([
fauxAssistantMessage(fauxToolCall('lookup', { limit: '2' }), { stopReason: 'toolUse' }),
fauxAssistantMessage('Done.'),
]);
const harness = await context.initializeRootHarness(
defineAgent(() => ({
model: `${provider.getModel().provider}/${provider.getModel().id}`,
tools: [
defineTool({
name: 'lookup',
description: 'Look up values.',
input: v.object({ limit: v.pipe(v.string(), v.transform(Number)) }),
run: async ({ input }) => input.limit,
}),
],
})),
);
await (await harness.session()).prompt('Look up values.');لاحظ v.pipe(v.string(), v.transform(Number)) — تُخرج النماذج نصوصًا للمعاملات الرقمية أكثر بكثير مما ينبغي، والتحويل يستوعب ذلك عند الحدود بدلًا من تركه في جسم run.
اختبر على ثلاث طبقات:
- الأدوات منفردة. هي دوال غير متزامنة عادية. استدعِ
runمباشرةً بمدخلات صحيحة وخاطئة، وتحقّق من أن المدخل السيئ يُنتج خطأ أداة لا استثناءً يُسقط التشغيل. - منطق سير العمل بردود مكتوبة مسبقًا. تحقّق من تسليم نظام الملفات والتفريعات ومخطط المخرجات، بلا أي استدعاءات شبكية.
- حزمة صغيرة شاملة حقيقية. حفنة من التشغيلات الفعلية على مستندات معروفة، للتأكد من أن الملاحظات غير فارغة وتستشهد باقتباسات حقيقية. أبعِدها عن مسار ما قبل الالتزام البرمجي.
تحقّق من أن المسار الكامل يعمل قبل المتابعة:
npx flue build --target node
PORT=8080 node dist/server.mjs &
curl -X POST http://localhost:8080/workflows/review-article \
-H 'Content-Type: application/json' \
-d '{"document":"# Draft\n\nOur framework is arguably the fastest available.","contentType":"tutorial"}'استعلم عن مُعرّف التشغيل المُعاد. التنفيذ الصحيح يُعلّم عبارة "arguably the fastest" كادّعاء غير مدعوم، لأن قائمة التحقّق تطالب بالإسناد والمستند لا يقدّمه. وإن عادت الملاحظات فارغة، فمهارتك لا تصل إلى النموذج — تحقّق من وجود سمة الاستيراد.
استكشاف الأخطاء
رمي استثناء عند استخدام parameters أو execute في تعريف أداة. هذا هو التغيير الكاسر في النسخة 1.0 التجريبية. العلامات القديمة ترمي استثناءً بقصد التصميم. أعِد تسمية parameters إلى input، وأعِد تسمية execute(args, signal) إلى run({ input, signal })، وأعِد بيانات مُهيكلة متوافقة مع JSON مباشرةً بدلًا من استدعاء JSON.stringify(...). وأضف مخطط output حيث يجب التحقّق من الشكل.
أخطاء تعارض أسماء الأدوات. يجب أن تكون الأسماء فريدة بين الأدوات المدمجة والمخصصة، ويحدث الفحص عند تجميع الجلسة لقائمة أدواتها — لذلك قد يظهر التعارض في وقت التشغيل لا وقت البناء. ضع بادئة لأدواتك المخصصة، مثل cms_lookup_budget.
تجاهل المهارة. استيراد Markdown دون with { type: 'skill' } هو مجرد نص. سمة الاستيراد هي ما يُسجّلها.
فشل تحليل سمات الاستيراد. تحتاج إلى Node 22 أو أحدث ومُجمِّع يفهمها. إن رفضت أدواتك الصياغة، فرقِّها قبل البحث عن حلول ملتوية.
الوكيل لا يرى ملفاتك. تحقّق من sandbox وcwd. بيئة العزل الافتراضية just-bash تعمل في الذاكرة ولا تستطيع قراءة قرصك إطلاقًا — وهذا هو المقصود. انتقل إلى local() عند الحاجة لملفات حقيقية.
متغيّرات البيئة مفقودة على Cloudflare. يقرأ flue dev --target cloudflare من .dev.vars أو .env، لكن الـ Workers المنشورة لا تفعل. كل سر يحتاج wrangler secret put، والمفتاح الذي يعمل محليًا لا يثبت شيئًا عن الإنتاج.
التشغيل يتوقف في منتصف الطريق بلا خطأ. تحقّق مما إذا كنت قد بلغت حدّ سياق النموذج داخل جلسة واحدة طويلة. قسّم العمل على عدة استدعاءات prompt، أو فوّض إلى وكلاء فرعيين ليبقى كل سياق صغيرًا.
ملاحظات الإنتاج
ثلاثة أمور أهمّ مما تبدو:
التكلفة قرار تصميمي، لا بند في فاتورة. قد يُجري تشغيل مستقل واحد عشرات الاستدعاءات للنموذج. اضبط thinkingLevel بوعي، ووجّه المرورات الرخيصة إلى نموذج رخيص عبر subagents، وضع التخزين المؤقت للمطالبات أمام بادئات التوجيهات الثابتة — فمهارة قائمة التحقّق متطابقة في كل تشغيل، ما يجعلها بادئة مثالية للتخزين المؤقت.
لا يمكنك تصحيح ما لا تراه. وكيل بلا واجهة يفشل صامتًا في الثالثة فجرًا أسوأ من عدم وجود وكيل. أصدِر آثار التتبّع من البداية؛ ويغطي دليل مراقبة Langfuse الأدوات اللازمة.
قيّد كل أداة. أعِد قراءة الخطوة 4. أغلِق على هوية المستأجر ومرّر المُحدِّد الضيّق فقط كمدخل للأداة. هذا أعلى قرار أمني مردودًا في قاعدة كود وكلاء، ولا يكلّف شيئًا.
الخطوات التالية
- مخططات
flue add. أوامر مثلflue add channel slackتُنشئ مخططًا بصيغة Markdown يمكن للوكلاء تعديله ودمجه — فالإطار يتعامل مع سطح توسيعه نفسه كقابل للتعديل من الوكلاء. - الإجراءات. استخلِص عمليات قابلة لإعادة الاستخدام ومُتحقَّقًا من مخططاتها عبر
defineAction()وشاركها بين سيَر العمل. - الجدولة. وجّه مُشغّل cron إلى نقطة نهاية سير العمل لتشغيلات غير مراقَبة فعلًا، أو قارن بـ المهام الخلفية الدائمة في Trigger.dev.
- أهداف أخرى. ينشر Flue أيضًا على GitHub Actions، ما يجعل وكيل مراجعة طلبات الدمج مشروعًا ثانيًا طبيعيًا.
- قارن أطر التسخير. يحلّ نموذج التسخير وبيئة العزل في Vercel AI SDK 7 مشكلات متقاطعة بعناصر مختلفة؛ وقراءة الاثنين توضّح الغرض الحقيقي من التسخير.
- استكشف واجهة الأوامر. الأوامر
flue addوflue buildوflue devوflue docsوflue initوflue runوflue updateهي السطح الكامل. والأمرflue docsمفيد فعلًا في منتصف المهمة.
الخلاصة
إسهام Flue ليس طريقة أخرى لاستدعاء نموذج. إنه الدعوى بأن الهندسة المثيرة في الوكلاء تعيش في طبقة التسخير — وأنك إن بنيت تلك الطبقة كما ينبغي، تقلّص كود الوكيل إلى ما يمكن قراءته في جلسة واحدة.
المراجع الذي بنيته للتو نحو أربعين سطرًا من TypeScript مع قائمة تحقّق بصيغة Markdown. ولديه جلسات ذات حالة، وأدوات مُتحقَّق منها، وبيئة عزل، ونظام ملفات دائم، ومُشغّل HTTP، ومسار إلى الحوسبة الطرفية حيث تصبح كل نسخة كائنًا دائمًا خاصًا بها. لم يأتِ أيٌّ من ذلك من طقوس الإطار؛ بل جاء من اختيار هدف وترك التسخير يتولّى الباقي.
النمط الجدير بالاستخلاص، أيًّا كان الإطار الذي تستقرّ عليه: دع التطبيق يحدّد الحدود ودع النموذج يختار داخلها. الأدوات المُقيّدة، والتسليم عبر نظام الملفات بدلًا من تحليل النصوص، والسياقات الصغيرة المُفوَّضة ليست أفكارًا خاصة بـ Flue. إنها ما يفصل وكيلًا يمكنك تشغيله بلا مراقبة عن عرض توضيحي يحتاج أحدًا يراقبه.
ابدأ بـ just-bash وسير عمل واحد. أضف بيئة عزل عندما يحتاج الوكيل فعلًا لملفات حقيقية، ووكلاء فرعيين عندما تتوقف جلسة واحدة عن الكفاية. انشر النسخة المملّة أولًا — فوكيل يعمل كل ليلة من دونك أعلى قيمة من وكيل بارع لا يستطيع مغادرة طرفيتك.