Điều mà agentic workflow demo không bao giờ nói đến

Hầu hết demo agentic workflow kết thúc ở happy path: agent nhận task, gọi tool, trả kết quả. Slide đẹp, flow gọn. Phần không xuất hiện trong demo: agent gọi tool với input sai ở bước 4 của 6, LLM trả về hallucinated action ở bước 3, downstream service timeout sau 15 phút, hay tệ hơn — agent chạy đúng nhưng commit side effect mà không nên commit.

Đây là những vấn đề governance thực tế. Và câu trả lời không phải là thêm try-catch rải rác trong code. Đó là thiết kế state machine với retry semantics, human approval gates, idempotency contract, và compensation flows ngay từ ngày 1.

EventBridge / SQSStep FunctionsAgentCoreLambda WorkerHuman Approverstart executioninvoke agent steptool call (async)result + compensationDataemit task token (pause)sendTaskSuccess(token)continue next statefinal result
Wait for Task Token: state machine pauses, zero compute, until human sends callback

Tại sao state machine, không phải code

Pattern phổ biến khi mới xây agentic pipeline: orchestrate bằng code. Gọi Lambda A, nhận kết quả, gọi Lambda B theo điều kiện. Đơn giản khi còn nhỏ, nhưng vỡ vụn theo những cách không hề nhỏ:

Timeout: Lambda execution limit 15 phút. Pipeline dài (voice transcription + diarization + summarization) với variable-length input — audio 2 tiếng vượt limit dễ dàng.

Không có history: Khi có production incident lúc 2 giờ sáng, không có cách nào biết execution đã đi đến đâu, fail ở step nào, với input gì.

Retry logic scattered: Mỗi step viết retry riêng, không nhất quán, không có exponential backoff chuẩn.

Human approval: Làm human-in-the-loop mà không tốn Lambda idle time đòi hỏi kiến trúc riêng — hầu hết implementation naive đều poll DynamoDB, burn tiền trong khi chờ.

AWS Step Functions giải quyết tất cả ở infrastructure level. State machine là definition (JSON/YAML), không phải code chạy. Visual debugger built-in. Execution history 90 ngày. Retry semantics define một lần ở state level:

"Retry": [{
  "ErrorEquals": ["Lambda.TooManyRequestsException", "Lambda.SdkClientException"],
  "IntervalSeconds": 2,
  "MaxAttempts": 3,
  "BackoffRate": 2
}],
"Catch": [{
  "ErrorEquals": ["States.ALL"],
  "Next": "CompensationFlow"
}]

BackoffRate: 2 với IntervalSeconds: 2MaxAttempts: 3 → retry sau 2s, 4s, 8s. Downstream service có buffer. Sau 3 lần vẫn fail → route sang CompensationFlow. Không một dòng code retry nào trong Lambda.

Code Orchestration
  • Retry logic written differently in every Lambda — no consistent BackoffRate or MaxAttempts
  • Lambda timeout (15 min) blocks long-running steps with variable-length inputs
  • No execution history: debug production incidents by adding log statements
  • Human approval requires Lambda polling DynamoDB — billed during idle wait time
Step Functions
  • MaxAttempts + BackoffRate defined once at state level — uniform across all steps
  • Wait for Task Token: pause state machine for hours/days — zero compute billing
  • 90-day execution history with visual debugger — replay any execution to any state
  • Catch → CompensationFlow: native Saga routing with full execution context preserved

Wait for Task Token: human-in-the-loop thực tế

Yêu cầu thường gặp trong enterprise agentic workflow: một bước cần human approval trước khi agent commit action. Ví dụ: agent phân tích data và đề xuất xóa 10,000 record trùng lặp → cần manager confirm trước khi execute.

Cách naive: Lambda poll DynamoDB mỗi 30s để check approval flag. Vấn đề: Lambda billed cả thời gian chờ, timeout sau 15 phút nếu manager không approve kịp.

Cách đúng: Wait for Task Token.

Step Functions emit một opaque token và pause state machine ngay lập tức — không tốn compute trong khi chờ. Token được ghi vào DynamoDB (hoặc gửi qua SNS/email). Execution có thể pause nhiều giờ, nhiều ngày.

External system (Slack bot, web dashboard, email approval link) đọc token. Human review và click approve/reject → system gọi:

await sfn.sendTaskSuccess({
  taskToken: token,
  output: JSON.stringify({ approved: true, reviewer: userId })
});

State machine resume ngay lập tức từ điểm đã pause, với output từ human reviewer như input cho step tiếp theo.

Token là contract bất biến: ai có token, người đó có quyền resume execution. Token expire sau HeartbeatSeconds nếu không có callback — sau đó Step Functions tự route sang error path.

Wait for Task Token ≠ polling

Step Functions emits an opaque token and pauses immediately — no Lambda running, no compute billed. The token is the contract: whoever holds it can resume the execution. Human approves next day? State machine resumes in milliseconds after sendTaskSuccess(token). Lambda timeout is irrelevant. Heartbeat timeout handles abandonment.

AgentCore: governance layer, không phải replacement

AWS AgentCore (ra mắt 2025) thường bị hiểu nhầm là "Bedrock Agents managed hơn" hay "replacement cho custom orchestration". Thực ra vai trò của nó định nghĩa rõ hơn thế:

AgentCore Runtime: Managed execution environment cho AI agent. Xử lý tool routing theo intent (chọn tool nào dựa trên natural language request), multi-turn conversation state, và retry khi tool call fail. Agent developer không phải viết routing logic thủ công.

AgentCore Memory: Persistent memory phân loại theo semantic type. Episodic: conversation history ("user yêu cầu gì ở turn trước"). Semantic: extracted facts từ conversation ("customer preferred delivery: express"). Procedural: learned patterns ("khi user hỏi về refund, always check order age first"). Memory persist across sessions không cần application code manage.

AgentCore Identity: Agent-level authorization. Không phải IAM role flat cho cả Lambda function — mà là agent X được phép call tool Y với data của tenant Z. Governance tại mức agent action, không phải mức infrastructure.

AgentCore Gateway: Unified API để expose tools — Lambda function, API Gateway endpoint, MCP server — mà không cần agent code biết implementation details. Clean abstraction, tool có thể migrate từ Lambda sang container mà không đổi agent logic.

AgentCore không replace Step Functions. AgentCore govern agent execution (tool routing, memory, identity). Step Functions govern workflow orchestration (state transitions, branching, error routing, human gates, compensation). Trong production pipeline: Step Functions invoke AgentCore như một step trong state machine. AgentCore govern những gì xảy ra bên trong step đó.

  1. Runtime

    Tool routing by intent, multi-turn conversation state, retry on tool call failure. Agent developer writes intent, not routing logic.

  2. Memory

    Episodic (conversation history), semantic (extracted facts), procedural (learned patterns). Persists across sessions without application code managing state.

  3. Identity

    Agent-level permissions: agent X, tool Y, tenant Z. Finer-grained than IAM roles — authorization at the agent action level, not function level.

  4. Gateway

    Unified API for Lambda, API Gateway, MCP servers. Tool implementation can migrate without touching agent logic — clean abstraction boundary.

Idempotency là không optional trong agentic workflow

Step Functions retry Lambda theo RetryPolicy. Khi Lambda fail (timeout, throttle, transient error), Step Functions invoke lại với cùng input. Nếu Lambda không idempotent, side effects xảy ra nhiều lần: email gửi 3 lần, record được tạo 2 lần, payment bị charge duplicate.

Pattern tôi dùng: executionId + stepId làm idempotency key.

const key = `${executionId}#${stepName}`
const existing = await ddb.get({ TableName: IDEMPOTENCY_TABLE, Key: { pk: key } })

if (existing?.status === 'completed') {
  return existing.result  // return cached, no side effect
}
if (existing?.status === 'in_progress') {
  throw new Error('CONCURRENT_EXECUTION')  // Step Functions retry sau
}

await ddb.put({ pk: key, status: 'in_progress', ttl: now + 86400 })
try {
  const result = await executeStep()
  await ddb.update({ pk: key, status: 'completed', result })
  return result
} catch (err) {
  await ddb.delete({ pk: key })  // allow retry
  throw err
}

executionId từ $$.Execution.Id trong Step Functions context. stepName từ $$.State.Name. TTL 24h để không accumulate stale keys vô hạn.

Lý do executionId + stepId thay vì chỉ executionId: nếu Step Functions restart từ một checkpoint (Express Workflow resume), cùng executionId nhưng step reset. stepId ensure granularity đúng.

  1. 1Check DynamoDB keyQuery executionId + stepName — unique per Step Functions execution per state
  2. 2Already completed?Return cached result — safe regardless of how many times Step Functions retried
  3. 3In progress?Concurrent retry detected — throw CONCURRENT_EXECUTION, Step Functions retries after interval
  4. 4Execute + compensateRun step logic, write compensationData for Saga rollback alongside result
  5. 5Mark completedUpdate key to status:completed with TTL 24h — stale keys auto-expire, no cleanup job needed

Saga compensation: Step Functions cho gì, bạn phải thiết kế gì

Khi step N fail sau khi step 1 đến N-1 đã commit side effects, bạn cần rollback theo thứ tự ngược lại. Saga pattern.

Step Functions Catch route execution đến CompensationFlow state. Đây là phần Step Functions làm. Phần bạn phải thiết kế:

  1. Compensation data: Mỗi Lambda step write compensationData vào DynamoDB cùng lúc với execute. Dữ liệu cần để undo: record IDs đã tạo, S3 paths đã write, external system calls đã commit.

  2. Rollback order: Compensation phải undo theo thứ tự ngược với execution. Step 4 undo trước step 3 undo trước step 2.

  3. Partial compensation: Nếu compensation step cũng fail, không cascade thành infinite loop. Write compensation failure vào dead-letter queue, alert on-call.

Không có service nào thiết kế rollback thay bạn. Step Functions cung cấp routing mechanism; domain logic là trách nhiệm của developer.

Cost model: Standard vs Express, sync vs async

Step Functions Standard Workflows: $0.025 per 1,000 state transitions, billed per state change. Execution history 90 ngày. Phù hợp cho long-running agentic workflows (phút đến ngày).

Step Functions Express Workflows: $1 per 1,000,000 state transitions + $0.00001 per GB-second. Cheaper cho high-volume, short-duration (dưới 5 phút). Không có execution history dài.

Với agentic pipeline có human approval gate — pause nhiều giờ, nhiều ngày — Standard Workflows là lựa chọn đúng. Express Workflow timeout sau 5 phút.

Về async inference: Lambda synchronous invoke SageMaker endpoint = Lambda billed trong khi model đang inference. Với Whisper Large-v3 trên audio 2 tiếng, inference mất 8–12 phút — vượt Lambda limit, burn tiền khi idle.

Solution: Lambda gửi job vào SQS → terminate ngay. SageMaker async endpoint nhận từ queue, inference, write kết quả vào S3. S3 event trigger Lambda xử lý kết quả. Lambda billing: chỉ actual execution (giây), không phải inference wait (phút). SageMaker charged per-inference, không per-second.

Pattern kết hợp trong production

Voicebot pipeline của tôi: Amazon Connect state machine govern conversation control (dead-air guard, timeout, retry on intent fail). Khi call kết thúc, S3 audio file trigger Step Functions workflow: validate audio → SageMaker async STT → diarization → summarization → structured extraction → human review gate (Wait for Task Token) → write to operational database.

Mỗi Lambda step: idempotent bằng executionId + stepName key. AgentCore Runtime handle LLM routing trong summarization step (chọn Bedrock model theo audio length và language). Compensation flow: nếu extraction fail sau STT đã complete, không re-run STT (expensive) — chỉ re-run từ checkpoint.

Governance không phải layer bạn thêm sau khi system hoạt động được. Governance là bộ xương của state machine — design nó từ ngày 1, hoặc pay technical debt sau khi production incident đã xảy ra.