Cloudflare Workflows Specification: AI Content Loop Orchestration
Octo101 AI Platform | Version 1.0
| Trường (Field) | Giá trị (Value) |
|---|---|
| Mã tài liệu (Document ID) | EQ-WF-003 |
| Phiên bản (Version) | 1.0 |
| Ngày tạo (Date) | 2026-07-18 |
| Trạng thái (Status) | Draft |
| Tác giả (Author) | Octo101 Cloud Engineering Team |
1. Tổng quan về Điều phối (Orchestration Vision)
Quy trình sản xuất và tiến hóa nội dung học tập bằng AI là một quy trình chạy dài hạn (long-running), bao gồm các cuộc gọi LLM lớn (AI 1, AI 2), kiểm định tự động, và đặc biệt là bước chờ phê duyệt từ con người (Human Review) có thể kéo dài nhiều giờ hoặc nhiều ngày.
Chúng tôi lựa chọn Cloudflare Workflows để điều phối toàn bộ vòng lặp này vì các lý do:
- Tính bền vững (Durability): Tự động lưu checkpoint tại mỗi bước, đảm bảo nếu Worker bị khởi động lại hoặc hết tài nguyên, quy trình sẽ tiếp tục chạy từ bước bị lỗi gần nhất.
- Cơ chế Retry thông minh: Định nghĩa cấu hình retry cấp độ code (exponential backoff) cho các API LLM thường gặp lỗi nghẽn hoặc quá tải.
- Quản lý Trạng thái Chờ (Suspension): Cho phép workflow tạm dừng một cách an toàn mà không tiêu tốn CPU/RAM chạy ngầm trong khi đợi Volunteer thực hiện chỉnh sửa và duyệt nội dung.
2. Workflow 1: AI Content Production Workflow
Workflow này chịu trách nhiệm sinh nội dung nháp, chạy kiểm duyệt AI và chờ phê duyệt từ con người để xuất bản.
mermaid
stateDiagram-v2
[*] --> Init: Nhận K-Pack ID
Init --> Step_Gen: step.run('Generate Draft Content')
Step_Gen --> Step_Review: step.run('AI Quality Gate')
Step_Review --> Step_Gen: Đánh giá Fail & Thử lại < 3 lần
Step_Review --> Step_Wait: Đạt AI Gate (hoặc Fail sau 3 lần)
state Step_Wait {
[*] --> Suspend: Ghi nhận D1 (status='waiting_human')
Suspend --> Resume: API call /approve hoặc /edit từ Volunteer
}
Step_Wait --> Step_Publish: step.run('Publish Activities')
Step_Publish --> [*]: Hoàn tất & Xuất bản thành côngMã nguồn minh họa TypeScript (src/workflows/ContentProductionWorkflow.ts)
typescript
import { WorkflowEntrypoint } from 'cloudflare:workers';
export interface ContentProductionParams {
kpackId: string;
orgId: string;
creatorId: string;
}
export class AIContentProductionWorkflow extends WorkflowEntrypoint<ContentProductionParams> {
async run(event: any, env: any) {
const { kpackId, orgId, creatorId } = this.params;
const db = env.DB;
const workflowId = this.id;
// Helper cập nhật trạng thái bước vào D1 để phục vụ Dashboard theo dõi
const updateWorkflowStep = async (stepName: string, status: string, output: any = null, error: string = '') => {
const stepId = `${workflowId}_${stepName.replace(/\s+/g, '_').toLowerCase()}`;
await db.prepare(`
INSERT INTO ops_workflow_steps (id, workflow_id, step_name, status, output_payload, error_message, started_at)
VALUES (?, ?, ?, ?, ?, ?, datetime('now'))
ON CONFLICT(id) DO UPDATE SET
status = excluded.status,
output_payload = COALESCE(excluded.output_payload, output_payload),
error_message = COALESCE(excluded.error_message, error_message),
completed_at = CASE WHEN excluded.status IN ('completed', 'failed') THEN datetime('now') ELSE completed_at END
`).bind(stepId, workflowId, stepName, status, output ? JSON.stringify(output) : null, error).run();
};
// Khởi tạo trạng thái workflow trong D1
await db.prepare(`
INSERT INTO ops_workflows (id, organization_id, workflow_type, trigger_type, trigger_id, status, started_at)
VALUES (?, ?, 'content_review', 'system', ?, 'running', datetime('now'))
`).bind(workflowId, orgId, kpackId).run();
let attempt = 0;
let qualityPassed = false;
let latestDraftActivities: any[] = [];
let reviewFeedback = "Khởi tạo quy trình sinh nội dung.";
// ============================================================
// VÒNG LẶP SINH & KIỂM DUYỆT AI (Steps 4 & 5)
// ============================================================
while (!qualityPassed && attempt < 3) {
attempt++;
const currentAttempt = attempt;
const currentFeedback = reviewFeedback;
// 1. Sinh nội dung nháp từ K-Pack
const draftResult = await this.step.run(`Generate Draft Content - Lần ${currentAttempt}`, {
retries: { limit: 2, delay: '5s', backoff: 'exponential' }
}, async () => {
await updateWorkflowStep(`Generate Draft Content - Lần ${currentAttempt}`, 'running');
// Gọi Content Gen Engine (AI 1)
// Trong thực tế, AI 1 sẽ đọc định nghĩa khái niệm từ cur_knowledge_packages
const kpack = await db.prepare(`SELECT * FROM cur_knowledge_packages WHERE id = ?`).bind(kpackId).first();
if (!kpack) throw new Error("Knowledge Package không tồn tại");
// Gọi AI 1 thông qua SDK hoặc REST (giả lập kết quả trả về các hoạt động học tập nháp)
const mockDraftActivities = [
{ name: `Bài giảng: ${kpack.title}`, type: 'reading', content: `Nội dung chi tiết dựa trên ${kpack.mental_model}` },
{ name: `Quiz trắc nghiệm: ${kpack.title}`, type: 'quiz', content: `Các câu hỏi trắc nghiệm đồng bộ` }
];
// Ghi nhận bản nháp vào D1 dưới trạng thái 'draft'
const activitiesCreated: string[] = [];
for (const act of mockDraftActivities) {
const actId = `act_${crypto.randomUUID()}`;
// Ở thực tế, chúng ta sẽ liên kết với cf_mini_concepts đã tạo ở bước 3
// Tạm thời ghi vào bảng cf_learning_activities
activitiesCreated.push(actId);
}
const res = { activities: activitiesCreated, attempt: currentAttempt };
await updateWorkflowStep(`Generate Draft Content - Lần ${currentAttempt}`, 'completed', res);
return res;
});
latestDraftActivities = draftResult.activities;
// 2. Chạy AI Quality Gate (AI 2) để kiểm định từng hoạt động học tập
qualityPassed = await this.step.run(`AI Quality Review - Lần ${currentAttempt}`, {
retries: { limit: 2, delay: '2s' }
}, async () => {
await updateWorkflowStep(`AI Quality Review - Lần ${currentAttempt}`, 'running');
let allActivitiesPass = true;
let logsFeedback = "";
for (const actId of latestDraftActivities) {
// Gọi Quality Review Engine (AI 2 - Critic) đánh giá chất lượng
// Giả lập điểm kiểm duyệt chất lượng
const reviewId = `rev_${crypto.randomUUID()}`;
const pedScore = 0.95;
const accScore = 0.92;
const safeScore = 1.0;
const alignScore = 0.90;
const isPassed = (pedScore >= 0.85 && accScore >= 0.90 && safeScore >= 0.95) ? 1 : 0;
if (isPassed === 0) {
allActivitiesPass = false;
logsFeedback += `Hoạt động ${actId} thất bại kiểm định sư phạm/độ chính xác. `;
}
// Lưu kết quả đánh giá chất lượng vào ops_ai_quality_reviews
await db.prepare(`
INSERT INTO ops_ai_quality_reviews (id, workflow_id, activity_id, organization_id, reviewer_model, pedagogy_score, accuracy_score, safety_score, alignment_score, is_passed, feedback_details)
VALUES (?, ?, ?, ?, 'gemini-1.5-pro', ?, ?, ?, ?, ?, ?)
`).bind(reviewId, workflowId, actId, orgId, pedScore, accScore, safeScore, alignScore, isPassed, JSON.stringify({ note: "Đạt chuẩn cơ bản." })).run();
}
reviewFeedback = allActivitiesPass ? "Tất cả bài học đạt chất lượng AI Gate." : logsFeedback;
await updateWorkflowStep(`AI Quality Review - Lần ${currentAttempt}`, 'completed', { allActivitiesPass, feedback: reviewFeedback });
return allActivitiesPass;
});
}
// ============================================================
// BƯỚC BẤT ĐỒNG BỘ: TẠM DỪNG CHỜ CON NGƯỜI DUYỆT (Step 6)
// ============================================================
// Do Cloudflare Workflows hiện tại chưa hỗ trợ API 'suspend' trực tiếp dạng native,
// chúng ta triển khai cơ chế "Durable Polling/Pause" bằng cách lưu trạng thái 'waiting_human' vào D1
// và ném một ngoại lệ tạm ngừng có kiểm soát, HOẶC chạy một vòng lặp kiểm tra trạng thái định kỳ (Polling Loop)
// với khoảng thời gian sleep lớn để tiết kiệm tài nguyên mà không làm chết workflow.
await this.step.run('Set Waiting Human Review State', async () => {
await db.prepare(`
UPDATE ops_workflows
SET status = 'pending', error_message = 'Đang chờ Tình nguyện viên kiểm duyệt (Human-in-the-loop)'
WHERE id = ?
`).bind(workflowId).run();
});
let humanApproved = false;
let checkIntervalMinutes = 10;
// Vòng lặp chờ phê duyệt (được quản lý hiệu quả nhờ tính năng step.sleep của Workflows)
while (!humanApproved) {
humanApproved = await this.step.run('Check Human Approval Status', async () => {
const wfRecord = await db.prepare(`
SELECT status FROM ops_workflows WHERE id = ?
`).bind(workflowId).first();
// Nếu API Hono bên ngoài đổi trạng thái workflow thành 'completed' hoặc có bảng duyệt riêng
// Ở đây giả lập kiểm tra bảng duyệt
return wfRecord.status === 'running'; // Phê duyệt sẽ reset status về 'running'
});
if (!humanApproved) {
// Tạm dừng workflow trong 10 phút.
// Cloudflare Workflows sẽ ngủ đông thực sự, KHÔNG tốn chi phí tài nguyên chạy ngầm.
await this.step.sleep(`Sleep waiting for human approval`, { minutes: checkIntervalMinutes });
}
}
// ============================================================
// BƯỚC 7: XUẤT BẢN NỘI DUNG (Publish Activities)
// ============================================================
await this.step.run('Publish Activities', {
retries: { limit: 3, delay: '2s' }
}, async () => {
await updateWorkflowStep('Publish Activities', 'running');
// Chuyển trạng thái tất cả activities liên quan từ 'draft' sang 'active'
for (const actId of latestDraftActivities) {
await db.prepare(`
UPDATE cf_learning_activities
SET status = 'active', updated_at = datetime('now')
WHERE id = ?
`).bind(actId).run();
}
await updateWorkflowStep('Publish Activities', 'completed');
});
// Cập nhật trạng thái workflow hoàn thành
await db.prepare(`
UPDATE ops_workflows
SET status = 'completed', completed_at = datetime('now'), updated_at = datetime('now')
WHERE id = ?
`).bind(workflowId).run();
return { status: 'published', activities: latestDraftActivities };
}
}3. Workflow 2: AI Content Evolution Workflow
Workflow này định kỳ phân tích dữ liệu tương tác của học sinh để phát hiện nội dung yếu kém, cải tiến K-Pack và kích hoạt lại quy trình sinh nội dung.
mermaid
stateDiagram-v2
[*] --> Trigger: Định kỳ (Cron) hoặc Đủ lượng Evidence
Trigger --> Step_Analyze: step.run('Aggregate Learner Telemetry')
Step_Analyze --> Step_Evaluate: step.run('Evaluate Quality Thresholds')
Step_Evaluate --> Exit: Không có nội dung kém chuẩn
Step_Evaluate --> Step_Evolve: Phát hiện nội dung lỗi (Dropout > 50%)
Step_Evolve --> Step_TriggerProduction: step.run('Evolve K-Pack & Trigger v2')
Step_TriggerProduction --> [*]: Bắt đầu Vòng sản xuất mới cho v2Mã nguồn minh họa TypeScript (src/workflows/ContentEvolutionWorkflow.ts)
typescript
import { WorkflowEntrypoint } from 'cloudflare:workers';
export interface ContentEvolutionParams {
orgId: string;
timeWindowDays: number;
}
export class AIContentEvolutionWorkflow extends WorkflowEntrypoint<ContentEvolutionParams> {
async run(event: any, env: any) {
const { orgId, timeWindowDays } = this.params;
const db = env.DB;
const workflowId = this.id;
// ============================================================
// STEP 1: Tổng hợp dữ liệu Telemetry của học sinh
// ============================================================
const analyticsResult = await this.step.run('Aggregate Learner Telemetry', async () => {
// Đọc bảng evd_content_telemetry để tính toán chỉ số hiệu năng của từng activity
// Giả lập câu lệnh SQL tổng hợp:
const query = `
SELECT
activity_id,
COUNT(DISTINCT student_id) as sample_size,
AVG(CASE WHEN event_type = 'complete' THEN 1 ELSE 0 END) as completion_rate,
AVG(CASE WHEN event_type = 'dropout' THEN 1 ELSE 0 END) as dropout_rate,
AVG(time_spent_secs) as avg_time
FROM evd_content_telemetry
WHERE created_at >= datetime('now', ?) AND organization_id = ?
GROUP BY activity_id
`;
const { results } = await db.prepare(query).bind(`-${timeWindowDays} days`, orgId).all();
// Cập nhật kết quả phân tích vào bảng evd_content_analytics
for (const row of results) {
const confusionScore = row.dropout_rate * 0.7 + (row.avg_time > 300 ? 0.3 : 0.0);
await db.prepare(`
INSERT INTO evd_content_analytics (id, activity_id, organization_id, sample_size, completion_rate, confusion_score, dropout_rate, avg_hints_used, analyzed_at)
VALUES (?, ?, ?, ?, ?, ?, ?, 0, datetime('now'))
ON CONFLICT(activity_id) DO UPDATE SET
sample_size = excluded.sample_size,
completion_rate = excluded.completion_rate,
confusion_score = excluded.confusion_score,
dropout_rate = excluded.dropout_rate,
analyzed_at = excluded.analyzed_at
`).bind(`anl_${crypto.randomUUID()}`, row.activity_id, orgId, row.sample_size, row.completion_rate, confusionScore, row.dropout_rate).run();
}
return { processed_activities: results.length };
});
// ============================================================
// STEP 2: Kiểm tra ngưỡng chất lượng và Phát hiện nội dung lỗi
// ============================================================
const badActivities = await this.step.run('Evaluate Quality Thresholds', async () => {
// Tìm các hoạt động học tập có Confusion Score > 0.5 hoặc Dropout Rate > 0.4
const { results } = await db.prepare(`
SELECT a.id as activity_id, a.name, c.knowledge_package_id, an.confusion_score, an.dropout_rate
FROM evd_content_analytics an
JOIN cf_learning_activities la ON la.id = an.activity_id
JOIN cf_mini_concepts mc ON mc.id = la.mini_concept_id
JOIN cf_concepts c ON c.id = mc.concept_id
WHERE an.confusion_score > 0.5 OR an.dropout_rate > 0.4
`).all();
return results;
});
if (badActivities.length === 0) {
return { status: 'no_evolution_needed' };
}
// ============================================================
// STEP 3: Gọi AI 3 tiến hóa Knowledge Package gốc
// ============================================================
for (const act of badActivities) {
await this.step.run(`Evolve K-Pack for ${act.name}`, async () => {
// Lấy thông tin K-Pack cũ
const kpack = await db.prepare(`SELECT * FROM cur_knowledge_packages WHERE id = ?`).bind(act.knowledge_package_id).first();
if (!kpack) return;
const nextVersion = kpack.current_version + 1;
// Gọi AI 3 (Evolver Engine)
// AI 3 sẽ nhận diện lỗi: ví dụ "học sinh bỏ cuộc nhiều do ví dụ tìm kiếm số biên không rõ ràng"
// và trả về nội dung cải tiến.
const evolvedMentalModel = kpack.mental_model + "\n[Nâng cấp v2] Bổ sung phân tích biên chi tiết.";
const evolutionReason = `Tự động tối ưu hóa: Dropout rate bài học đạt ${act.dropout_rate * 100}%, AI tiến hành làm rõ ví dụ và mô hình tư duy.`;
// 1. Lưu phiên bản mới vào cur_knowledge_package_versions để làm dữ liệu lịch sử
await db.prepare(`
INSERT INTO cur_knowledge_package_versions (id, knowledge_package_id, version_number, definition, core_principles, mental_model, visual_analogy, examples, counter_examples, common_misconceptions, evolution_reason)
VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)
`).bind(
`kpv_${crypto.randomUUID()}`,
kpack.id,
nextVersion,
kpack.definition,
kpack.core_principles,
evolvedMentalModel,
kpack.visual_analogy,
kpack.examples,
kpack.counter_examples,
kpack.common_misconceptions,
evolutionReason
).run();
// 2. Cập nhật Knowledge Package hiện tại lên phiên bản mới
await db.prepare(`
UPDATE cur_knowledge_packages
SET current_version = ?, mental_model = ?, updated_at = datetime('now')
WHERE id = ?
`).bind(nextVersion, evolvedMentalModel, kpack.id).run();
// 3. Tự động kích hoạt lại AIContentProductionWorkflow cho K-Pack phiên bản mới
// (Thông qua API Client binding của Cloudflare Workflows)
try {
await env.WORKFLOWS.ai_content_production.create({
params: {
kpackId: kpack.id,
orgId: orgId,
creatorId: 'system_evolution'
}
});
} catch (err) {
console.error("Không thể trigger lại Content Production Workflow:", err);
}
});
}
return { status: 'evolution_completed', evolved_count: badActivities.length };
}
}