Các bước quy trình
Một bước quy trình có hai trục độc lập:
- Kind (
StepKind) — cách engine lập lịch bước so với các trình kích hoạt của nó. - Khóa body (
BuiltInBodyKeys) — closure body đã đăng ký nào thực thi công việc của bước.
Các loại bước
Phần tiêu đề “Các loại bước”Được định nghĩa trong packages/cc_domain/lib/features/pipelines/domain/entities/step_kind.dart.
| Kind | Ngữ nghĩa lập lịch |
|---|---|
trigger |
Điểm vào bắt buộc — đúng một cái cho mỗi mẫu, luôn là nút đầu tiên. Bản thân nó không làm gì; thứ khởi chạy lần chạy (thủ công / sự kiện / theo lịch) được theo dõi riêng dưới dạng các hàng PipelineTrigger. Body của nó là body rỗng pipeline.trigger. |
listen |
Kích hoạt khi mọi bước nguồn (trong StepTrigger.sourceStepIds) hoàn tất. |
join |
Kích hoạt khi mọi bước trong waitForStepIds đạt trạng thái kết thúc. |
router |
Rẽ nhánh có điều kiện — body trả về StepResult.route(key), và chỉ cạnh hạ nguồn có routeKey khớp mới được kích hoạt; những nhánh không được chọn bị đánh dấu là bị bỏ qua. |
forEach |
Được lập lịch giống listen (kích hoạt khi mọi sourceStepIds hoàn tất). Chính body flow.forEach đã đăng ký mới là thứ lặp; engine không sinh ra một bước con cho mỗi mục. |
terminal |
Điểm đánh dấu cho bộ lập lịch, không có body. Lần chạy quy trình hoàn tất khi một bước terminal kết thúc. |
Body của bước
Phần tiêu đề “Body của bước”Các closure body được đăng ký theo những hằng số trong BuiltInBodyKeys (packages/cc_domain/lib/features/pipelines/domain/templates/builtin_template_seeds.dart). Có 25 body, tất cả được liệt kê dưới đây. Cấu hình được đọc từ PipelineNodeConfig của bước (xem bên dưới) trừ khi có ghi chú khác; “khóa trạng thái” được đọc từ trạng thái quy trình / payload trình kích hoạt tại thời điểm thực thi.
Một bước có bodyKey không được đăng ký sẽ tạo cảnh báo lúc nạp mẫu rồi sau đó không bao giờ thực thi — nó không làm lần chạy thất bại. Các bước trigger và terminal được miễn cảnh báo đó: trigger vẫn chạy body rỗng pipeline.trigger; còn bước terminal không có body.
pipeline.trigger
Phần tiêu đề “pipeline.trigger”Body rỗng của nút trigger bắt buộc — hoàn tất ngay để engine tỏa ra các listener hạ nguồn. Không có cấu hình.
pipeline.bashScript
Phần tiêu đề “pipeline.bashScript”Bước shell không cần agent (bash -c). Các placeholder {{key}} trong script được thay bằng giá trị đã escape cho shell, lấy từ trạng thái + payload trình kích hoạt.
| Trường cấu hình | Mô tả |
|---|---|
script |
Lệnh shell cần thực thi (bắt buộc) |
outputKey |
Khóa trạng thái nhận stdout đã cắt khoảng trắng khi thoát với mã 0 |
Chạy với cwd <cc_root>/pipelines/<pipelineRunId>/ (dùng chung cho mọi bước bash trong một lần chạy) và GITHUB_TOKEN trong môi trường. Cũng ghi <stepId>_runDir vào trạng thái. Thoát với mã khác 0 làm bước thất bại kèm phần đuôi stderr.
conversation.promptAgent
Phần tiêu đề “conversation.promptAgent”Nút tổng quát gửi prompt rồi điều phối: kết xuất prompt, điều phối agent vào một hội thoại rồi tạm dừng cho đến khi lần chạy kết thúc. Payload submit_output của agent được thu về trạng thái dưới outputKey.
| Trường cấu hình | Mô tả |
|---|---|
agentId |
UUID của agent trong không gian làm việc sẽ được điều phối (bắt buộc) |
prompt |
Văn bản hướng dẫn; hỗ trợ thay thế {{key}} (bắt buộc) |
outputKey |
Khóa trạng thái cho đầu ra đã thu về |
outputSchema |
JSON Schema mà đầu ra phải thỏa mãn |
label |
Nhãn cho người dùng hiển thị trên canvas |
extras.spaceId |
Hội thoại đã có để làm việc trong đó (mặc định: một hội thoại ẩn) |
extras.conversationTitle |
Stream có tên bên trong space đó (tùy chọn) |
extras.mode |
Được đọc trên nút mở phòng (messaging.createSpace), không phải ở đây. Mặc định review. |
pipeline.condition
Phần tiêu đề “pipeline.condition”Body router — đọc config.extras, đánh giá một điều kiện rồi trả về StepResult.route(key). Điều kiện không phải biểu thức JavaScript; có ba cách viết được hỗ trợ, theo thứ tự ưu tiên:
- Cây vị từ (
extras.predicate) — một cây boolean định tuyến"true"/"false". Mỗi nút là một map có mộttype:{"type": "fileExists", "paths": [...], "baseKey": "repo_local_path", "negate": false, "recursive": false}— đúng khi bất kỳ đường dẫn nào được liệt kê tồn tại trên đĩa;negateđảo ngược kết quả;recursivecũng tìm trong các thư mục con (bỏ qua.git,node_modules,build,.dart_tool). Đường dẫn tương đối được giải dựa trênstate[baseKey](mặc địnhrepo_local_path), nếu không thì dựa trên thư mục không gian làm việc của từng lần chạy.{"type": "comparison", "left": "{{score}}", "op": "gt", "right": 80}— các toán tửequals,notEquals,contains,exists,notExists,gt,lt; đọc trạng thái quy trình chứ không đọc hệ thống tệp.{"type": "and" | "or", "of": [<predicate>, ...]}— các nhóm boolean.{"type": "not", "of": <predicate>}— phủ định.
- Switch (
extras.switchKey) —{"switchKey": "pr_class", "cases": [...], "default": "standard"}; định tuyến tới case đầu tiên mà giá trị trạng thái chứa nó (không phân biệt hoa thường), nếu không thì tớidefault. - So sánh (các khóa cấp cao nhất
extras.left/extras.op/extras.rightkiểu cũ) — tương đương một vị từcomparison, được giữ lại cho những mẫu được viết trước khi có cây vị từ.
Các cạnh đi ra khỏi nút mang một routeKey phải khớp với khóa được trả về.
prReview.comment
Phần tiêu đề “prReview.comment”Đăng các phát hiện đã tổng hợp dưới dạng bình luận đánh giá PR qua máy khách PR của GitHub.
| Khóa trạng thái | Mô tả |
|---|---|
repo_full_name |
owner/repo (bắt buộc) |
pr_number |
Số PR — số nguyên hoặc chuỗi số (bắt buộc) |
consolidated_findings |
Nội dung bình luận (bắt buộc) |
Trả về StepResult.terminal với comment_review_id và commented_at trong trạng thái.
messaging.postSpace
Phần tiêu đề “messaging.postSpace”Gửi một tin nhắn tới một không gian nhắn tin qua MessagingPort.
| Khóa trạng thái | Mô tả |
|---|---|
space_id |
Không gian đích (bắt buộc) |
content |
Nội dung tin nhắn, được đọc nguyên văn từ trạng thái (bắt buộc) |
Ghi posted_space_id và posted_at vào trạng thái.
team.dispatch
Phần tiêu đề “team.dispatch”Điều phối cả một đội thay vì một agent đơn lẻ, rồi tạm dừng cho đến khi các lần chạy của các thành viên kết thúc.
| Trường cấu hình | Mô tả |
|---|---|
teamId |
Đội thuộc không gian làm việc sẽ được điều phối (bắt buộc; loại trừ lẫn nhau với agentId) |
prompt |
Văn bản mục tiêu; hỗ trợ thay thế {{key}} (bắt buộc) |
dispatchMode |
allParallel (mặc định — một tác vụ cho mỗi thành viên) hoặc manager (chỉ điều phối trưởng nhóm để phối hợp) |
outputKey |
Khóa trạng thái cho đầu ra đã thu về của các thành viên |
outputSchema, label, extras.spaceId |
Như đối với conversation.promptAgent |
human.gate
Phần tiêu đề “human.gate”Cổng phê duyệt. Điều phối agent phê duyệt vào một hội thoại (chế độ review) với prompt của cổng và một hợp đồng đầu ra {decision, reason}, rồi tạm dừng cho đến khi agent phê duyệt gửi kết quả qua submit_output.
| Trường cấu hình | Mô tả |
|---|---|
agentId |
Agent phê duyệt (bắt buộc) |
prompt |
Prompt của cổng (tùy chọn; dùng một giá trị mặc định khi bỏ trống) |
outputKey |
Khóa trạng thái cho payload {decision, reason} đã thu về |
outputSchema |
Mặc định là một lược đồ phê duyệt (decision: approved / rejected, reason) |
label, extras.spaceId |
Như đối với conversation.promptAgent |
Cổng chỉ được giải quyết qua submit_output. BuiltInBodyKeys.humanGate, thư viện loại nút và văn bản prompt của một mẫu tích hợp sẵn đều nhắc tới hai công cụ MCP approve_step và reject_step; cả hai công cụ này đều không tồn tại trong bất kỳ package hay registry nào.
repos.cleanup
Phần tiêu đề “repos.cleanup”Gỡ các worktree cách ly cũ; chọn chế độ của nó từ payload trình kích hoạt. Tuân theo cờ dryRun của lần chạy.
| Khóa trạng thái | Mô tả |
|---|---|
ticket_id |
Giải phóng worktree của phiếu đó |
repo_full_name + pr_number |
Giải phóng worktree tạm thời của trình chỉnh sửa PR |
space_id |
Tháo dỡ worktree và thư mục của hội thoại đó |
| (không có mục nào ở trên) | Quét toàn không gian làm việc: các thư mục đã biến mất, space đã chết, thư mục hội thoại mồ côi |
outputKey (tùy chọn) nhận một chuỗi tóm tắt.
flow.forEach
Phần tiêu đề “flow.forEach”Map / fan-out trên một tập hợp trong trạng thái: kết xuất prompt một lần cho mỗi mục, rồi điều phối agent một lần với các prompt đó được ghép lại (ngăn cách bằng ---) và tạm dừng cho đến khi lần chạy đó kết thúc. Một tập hợp rỗng hoàn tất ngay với một danh sách rỗng. Đây không phải là một lần điều phối song song cho mỗi mục.
| Trường cấu hình | Mô tả |
|---|---|
agentId |
Agent được điều phối cho mỗi mục (bắt buộc) |
prompt |
Prompt cho mỗi mục; hỗ trợ thay thế {{key}} (bắt buộc) |
extras.iterableKey |
Khóa trạng thái chứa tập hợp (bắt buộc) |
extras.itemKey |
Khóa mà mục hiện tại được gán vào trong prompt (mặc định item) |
outputKey |
Khóa trạng thái cho đầu ra đã thu về (một lần điều phối, không phải một danh sách) |
flow.callPipeline
Phần tiêu đề “flow.callPipeline”Chạy một mẫu quy trình khác như một bước con lồng nhau (quy trình con chạy như một lần chạy con với parentPipelineRunId được đặt). Bước này tạm dừng cho đến khi con đạt trạng thái kết thúc, rồi trạng thái cuối của con được hợp nhất dưới outputKey.
| Trường cấu hình | Mô tả |
|---|---|
extras.templateId |
Mẫu con cần chạy (bắt buộc; một quy trình không thể tự gọi chính nó) |
inputKeys |
Các khóa trạng thái được sao chép vào payload trình kích hoạt của con |
outputKey |
Khóa trạng thái cho trạng thái cuối đã hợp nhất của con |
code.index
Phần tiêu đề “code.index”Đánh chỉ mục tree-sitter chạy nền: duyệt một kho mã, trích xuất symbol/cạnh trong các isolate worker rồi nạp chúng vào đồ thị mã của không gian làm việc. Truyền tiến độ vào hàng bước của lần chạy; hỗ trợ chạy thử. Tree-sitter và các grammar của nó là native bắt buộc — thiếu một dylib nghĩa là máy chủ chưa từng khởi động, và một grammar không giải được giữa chừng sẽ làm bước thất bại thay vì bị bỏ qua.
| Khóa trạng thái | Mô tả |
|---|---|
repo_id |
Kho mã cần đánh chỉ mục (bắt buộc) |
repo_local_path |
Đường dẫn cục bộ của bản checkout (bắt buộc) |
meeting.diarize
Phần tiêu đề “meeting.diarize”Phân tách người nói ngoại tuyến. Đọc âm thanh được lưu giữ của cuộc họp, gom cụm thành các người nói (Person 1, Person 2, …), dán nhãn lại các đoạn phiên âm và ghi lại trạng thái transcript; đồng thời phát ra diarization_spans cho bước meeting.updateTranscript chạy song song. Giữ nguyên phiên âm khi không có âm thanh nào được lưu giữ hoặc khi các mô hình chưa được cài đặt.
| Khóa trạng thái | Mô tả |
|---|---|
meeting_id |
Cuộc họp cần phân tách người nói (bắt buộc) |
meeting.saveNotes, meeting.addActionItems, meeting.addDecisions
Phần tiêu đề “meeting.saveNotes, meeting.addActionItems, meeting.addDecisions”Các body lưu bền tóm tắt cuộc họp một cách tất định. Mỗi body đọc meeting_id và payload meeting_outcome có cấu trúc của bước agent ({summary, enhancedNotes, actionItems[], decisions[]}) từ trạng thái rồi ghi phần của mình vào bảng riêng — ghi chú, mục hành động hoặc quyết định. Các hàng đã lưu trước đó không bao giờ bị xóa khi lần chạy suy giảm.
Các body tích hợp sẵn khác
Phần tiêu đề “Các body tích hợp sẵn khác”| Khóa body | Mô tả |
|---|---|
meeting.identifySpeakers |
Nhận dạng người nói xuyên cuộc họp: đối chiếu những người nói đã được phân tách với các hồ sơ giọng nói đã lưu và tự động áp dụng những kết quả khớp đủ tin cậy. Không làm gì khi thiếu hồ sơ/embedding. |
meeting.updateTranscript |
Áp dụng các nhãn Person N từ diarization_spans và hợp nhất các mảnh của từng cửa sổ thành lượt nói theo từng người nói, rồi lưu bền phiên âm đã làm sạch. Không làm gì khi phân tách người nói không tạo ra kết quả nào. |
meeting.assemblePlayback |
Ghép bản âm thanh phát lại đã trộn của cuộc họp từ các tệp WAV theo kênh được lưu giữ. Không làm gì khi không có âm thanh nào được lưu giữ. |
orchestration.markPhase |
Ở cuối DAG công việc, ghi các dấu hiệu thất bại cho những phiếu con không có đầu ra và chuyển điều phối sang synthesizing. |
orchestration.persistDeliverable |
Ghi đầu ra tổng hợp vào phiếu cha, hoàn tất phiếu đó, đăng kết quả bàn giao và đánh dấu điều phối là completed. |
orchestration.awaitApproval |
Cổng phê duyệt một phần: hoàn tất ngay khi khóa nút của bước nằm trong tập đã được phê duyệt của điều phối, nếu không thì tạm dừng cho đến khi một lần phê duyệt sau đó đánh thức nó. Đây là body duy nhất đặt extras.awaitApproval, và điều đó giúp nó được miễn thời gian chờ 24 giờ dành cho bước bị tạm dừng. |
messaging.createSpace |
Mở hội thoại mà phần còn lại của mẫu làm việc trong đó, được công bố dưới outputKey của bước (các mẫu tích hợp sẵn dùng pipeline_space_id). Danh sách repoIds rỗng thì không checkout gì cả; extras['allRepos'] checkout mọi kho mã của không gian làm việc. extras['createConversation'] (mặc định tắt) cũng mở một stream có tên. Một bước agent THAM GIA một phòng và không bao giờ tự mở phòng, nên một đợt fan-out thiếu nút này sẽ thất bại lúc điều phối — và nối nó một lần nghĩa là cả đợt fan-out dùng chung một bản checkout thay vì cấp riêng cho từng cái. |
prReview.finalize |
Bước cuối cùng của mẫu đánh giá PR: sắp xếp các node đánh giá đã tích lũy vào các nhóm đồng thuận và tính kết luận cho từng PR. |
skills.analyze |
Chạy quét chuỗi cung ứng skill trên các skill đã cài đặt của một không gian làm việc và ghi lại từng phán quyết. |
Kết quả của bước
Phần tiêu đề “Kết quả của bước”Một body của bước trả về StepResult (packages/cc_domain/lib/features/pipelines/domain/entities/step_result.dart). Tất cả các factory đều nhận một map mutatedState tùy chọn được hợp nhất vào trạng thái quy trình, trừ failed.
| Factory | Chữ ký | Mô tả |
|---|---|---|
ok |
StepResult.ok({mutatedState}) |
Hoàn tất bình thường; các listener hạ nguồn được đánh giá |
route |
StepResult.route(nextRouterKey, {mutatedState}) |
Hoàn tất kiểu router; chọn nhánh hạ nguồn có routeKey khớp |
suspendUntilEvent |
StepResult.suspendUntilEvent(eventType, {mutatedState}) |
Tạm dừng cho đến khi một loại sự kiện miền cụ thể xảy ra |
suspendUntilTasksComplete |
StepResult.suspendUntilTasksComplete(taskIds, {mutatedState}) |
Tạm dừng cho đến khi mọi tác vụ được liệt kê đạt trạng thái kết thúc |
terminal |
StepResult.terminal({mutatedState}) |
Lần chạy quy trình đã kết thúc — không còn bước nào nữa |
failed |
StepResult.failed(errorMessage) |
Bước thất bại kèm một thông báo lỗi |
StepRetryPolicy
Phần tiêu đề “StepRetryPolicy”Hành vi thử lại cho body của một nút khi thất bại (PipelineNodeConfig.retryPolicy; null tắt việc thử lại).
| Trường | Kiểu | Mô tả |
|---|---|---|
maxAttempts |
int |
Tổng số lần thử kể cả lần đầu (3 = 1 lần thử + 2 lần thử lại); phải ≥ 1 |
backoff |
String |
linear hoặc exponential (mặc định) |
initialDelayMs |
int |
Độ trễ trước lần thử lại đầu tiên, tính bằng mili giây; các độ trễ sau tăng theo backoff |
PipelineNodeConfig
Phần tiêu đề “PipelineNodeConfig”Cấu hình theo từng nút được mang bên trong định nghĩa bước (packages/cc_domain/lib/features/pipelines/domain/entities/pipeline_node_config.dart). Cả nút tích hợp sẵn lẫn nút tùy chỉnh đều dùng chung hình dạng này; mỗi body chỉ đọc những trường mà nó quan tâm.
| Trường | Kiểu | Mô tả |
|---|---|---|
prompt |
String? |
Mẫu prompt có thay thế {{key}} |
script |
String? |
Nội dung script bash có thay thế {{key}} |
agentId |
String? |
UUID của agent trong không gian làm việc sẽ được điều phối |
inputKeys |
List<String> |
Các khóa trạng thái mà nút này dùng làm đầu vào |
repoIds |
List<String> |
Các kho mã mà nút messaging.createSpace checkout (<repoId> hoặc <repoId>@<branch>). Rỗng = không có gì; extras['allRepos'] = mọi kho mã của không gian làm việc. Bị bỏ qua trên các bước agent. |
outputKey |
String? |
Khóa trạng thái mà đầu ra của nút này được ghi vào |
label |
String? |
Nhãn cho người dùng hiển thị trên canvas (mặc định là ID của bước) |
outputSchema |
Map<String, dynamic>? |
JSON Schema (tập con) mà giá trị đầu ra phải thỏa mãn |
reducer |
String? |
Chiến lược hợp nhất khi các nhánh song song cùng ghi vào một outputKey: append, mergeLists, mergeMaps, sum, hoặc override (mặc định) |
retryPolicy |
StepRetryPolicy? |
Chính sách thử lại khi body thất bại; null = chỉ thử một lần |
continueOnFail |
bool |
Khi true, một thất bại kết thúc không làm lần chạy thất bại; lỗi được cất dưới state['_stepErrors'][stepId] |
timeoutMs |
int? |
Thời gian chờ theo đồng hồ thực cho body của nút, tính bằng mili giây; null = không có thời gian chờ |
teamId |
String? |
Đội cần điều phối (các nút team.dispatch); loại trừ lẫn nhau với agentId |
dispatchMode |
String? |
Chế độ thực thi của đội: allParallel hoặc manager |
extras |
Map<String, dynamic> |
Cấu hình tự do dành riêng cho body (ví dụ iterableKey, templateId, cây điều kiện) |
Các accessor dẫn xuất: spaceId (extras['spaceId'] — hội thoại đã có để làm việc trong đó), createsConversation (extras['createConversation']), conversationTitle (extras['conversationTitle']), modeName (extras['mode'] — chat / review / plan, được đọc trên nút mở phòng) và runWhen (extras['runWhen'] — bỏ qua nút khi điều kiện không được thỏa mãn).