Workflow Reference
Phân biệt hai thứ cùng tên "workflow":
docs/workflows/ | Trang này | |
|---|---|---|
| Là gì | Thiết kế — luồng nghiệp vụ mong muốn (WF-01…WF-20) | Thi hành — cái đang chạy trong runtime, từng step, có run log |
| Đọc để | Hiểu hệ thống nên hoạt động thế nào | Debug "hôm qua chạy gì, bước nào hỏng" |
⚠️ In-process là mặc định: workflow của API chạy trong request, ghi lại bằng run log ba bảng dưới đây. Ba ngoại lệ là Cloudflare Workflows (
WorkflowEntrypoint) thật, chạy ngoài hạ tầng run log này (Audit #013 T-10 bắt được trang này chỉ khai một):
Workflow Worker · binding · class Ghi sổ ở đâu Tra run WF-19 LessonForge (SRC-632) nemo12-foundry·LESSON_FORGE·LessonForgecontent_runs+content_run_trialsGET /v1/admin/foundry/runs[/{id}]WF-20 ItemForge (SRC-638) nemo12-foundry·ITEM_FORGE·ItemForgecontent_runs+content_run_trials(16 câu/bài theo khe)như trên DictationForge (SRC-669) nemo12-api·DICTATION_FORGE·DictationForgedictation_practice(statuspending → running → ready/failed,error)GET /v1/dictation/practices?learner_id=(learner)Xem SDD-027 cho hai cái đầu,
modules/dictation/forge.tscho cái thứ ba. Xem §5 để biết khi nào một workflow in-process cần đổi sang Cloudflare Workflows.
1. Hạ tầng run log
Ba bảng, ba câu hỏi khác nhau:
| Bảng | Trả lời |
|---|---|
workflow_runs + workflow_run_steps | "Luồng nào đã chạy, bước nào hỏng, mất bao lâu?" |
engine_runs | "Engine nào đã chạy, với input nào, ra version nào?" |
learner_model_versions | "Nội dung model tại version N là gì?" |
Luật vàng: ghi log không bao giờ được làm hỏng nghiệp vụ chính. Mọi lỗi ghi log bị nuốt và chỉ console.error("RUNLOG_DEGRADED"). Learner mất một dòng log còn hơn mất bài làm.
startWorkflowRun() → workflow_runs (status='running')
recordStep() → workflow_run_steps (seq tăng dần, succeeded|failed|skipped)
runEngine() → engine_runs, tự update status khi xong/lỗi
saveModelVersion() → learner_model_versions (chỉ khi hash đổi)
finishWorkflowRun() → status='succeeded'|'failed', duration_ms, output_json2. WF-04 — Evidence → Model Update
Workflow quan trọng nhất của hệ thống. runModelUpdateWorkflow() trong modules/models/service.ts.
Trigger
trigger | Từ đâu |
|---|---|
AssessmentCompleted | Nộp bài chẩn đoán (knowledge/routes.ts), nộp đề thi (exams/routes.ts) |
GoalChanged | Khai/sửa đích thi, thêm lịch thi học kỳ (onboarding/routes.ts) — chạy dưới mã WF-15 |
EXAM_RESCHEDULED | Đổi ngày thi |
admin_recompute | Admin bấm tính lại (admin/observability.ts) |
4 step, thứ tự bắt buộc
| # | Step | Engine | Vì sao đúng thứ tự này |
|---|---|---|---|
| 1 | goal_engine | Goal | Ba step sau đều tham chiếu Goal Model |
| 2 | context_engine | Context | Cần goal urgent nhất để dựng active_goal_ref |
| 3 | learner_model_engine | Learner Model | Cần goal version để nhúng goals_summary |
| 4 | readiness_engine | Readiness | Cần biết goal trỏ tới blueprint nào để chọn target |
Đảo thứ tự sẽ cho ra model tham chiếu version cũ của Goal — không crash, nhưng sai âm thầm. Đây là loại lỗi tệ nhất.
Xử lý lỗi
Mỗi step bọc try/catch riêng: một step hỏng không dừng các step sau. Cuối cùng status = failed nếu có bất kỳ step nào hỏng, nhưng những model tính được vẫn được lưu. Suy giảm từng phần tốt hơn mất tất cả.
Hot path gọi qua runModelsSafely() — nếu cả workflow nổ thì chỉ log model_update_degraded, học sinh vẫn nộp được bài.
Output
output_json của run chứa tóm tắt từng step:
{
"goal_engine": { "goals": 3, "conflicts": 1 },
"context_engine": { "version": 7, "changed": false },
"learner_model_engine": { "version": 12, "changed": true },
"readiness_engine": { "version": 5, "changed": true }
}changed: false nghĩa là engine đã chạy nhưng nội dung model không đổi — không phải là engine bị bỏ qua.
3. WF-15 — Declarations Intake
Cùng cỗ máy WF-04 nhưng mang mã và tên riêng để tra cứu tách bạch: model đổi vì học sinh khai điều mới (đích thi, lịch thi), không phải vì làm bài.
| Điểm gọi | input |
|---|---|
| Thêm exam target | { exam_target_id, kind } |
| Sửa exam target | { exam_target_id, kind }, trigger EXAM_RESCHEDULED nếu đổi ngày |
| Thêm lịch thi học kỳ | { semester_exam_id, exam_date } |
4. Xem lại một run
| Câu hỏi | Endpoint (role admin) |
|---|---|
| Gần đây chạy gì? | GET /v1/admin/workflow-runs |
| Run này gồm bước nào? | GET /v1/admin/workflow-runs/{runId} |
| Engine nào đã chạy? | GET /v1/admin/engine-runs |
| Learner này có model gì? | GET /v1/admin/learners/{learnerId}/models |
| Lịch sử một model? | GET /v1/admin/learners/{learnerId}/models/{modelKind}/versions |
| Nội dung version cụ thể? | GET /v1/admin/model-versions/{versionId} |
| Tính lại ngay | POST /v1/admin/learners/{learnerId}/recompute-models |
Đây là cách thực hiện REQ-INT-29 (model version inspectable) — xem được nội dung đầy đủ của từng phiên bản, không chỉ bản mới nhất. Giao diện: admin.nemo12.com.
5. Workflow đã thiết kế nhưng chưa thi hành
docs/workflows/ mô tả WF-01…WF-19. Chạy thật trên hạ tầng run log: WF-04 và WF-15; chạy thật trên Cloudflare Workflows: WF-19 (LessonForge, không dùng run log — xem cảnh báo đầu trang). Các luồng còn lại hoặc đang là code inline chưa gắn run log, hoặc chưa xây.
Khi nào cần chuyển sang Cloudflare Workflows thật (thay vì in-process):
- Luồng chạy > 30 giây hoặc vượt giới hạn CPU của một request.
- Cần retry có trạng thái qua nhiều bước (crawl đề — SDD-014, generation run — SDD-003).
- Cần chạy nền không có request nào kích hoạt → xem schedules.
Cho tới lúc đó, in-process + run log là đủ và đơn giản hơn hẳn.
Trace
- REQ-INT-29 (model version inspectable), REQ-NFR-01 (không làm hỏng nghiệp vụ chính).
- Thiết kế: SDD-002 §14/§15/§19, SDD-006 §2, workflows/.
- Kiểm chứng: QG-005, QG-009.