Cách Tạo Một Trình Xây Dựng Quy Trình Hình Ảnh Mở Cho Các Đại Lý AI Năm 2026
TL;DR
Một công cụ xây dựng quy trình làm việc trực quan cho các tác nhân AI là một bức tranh của các nút kết nối cộng với một động cơ chạy chúng theo thứ tự phụ thuộc. Bức tranh là một vấn đề UI (kéo, kết nối, định vị); động cơ là một vấn đề đồ thị (sắp xếp topo trên một đồ thị có hướng vô vòng, hoặc DAG).
React Flow (được phát hành dưới dạng gói @xyflow/react, phiên bản hiện tại 12.11.3, giấy phép MIT) là thư viện mã nguồn mở tiêu chuẩn cho phần bức tranh. Nó hiển thị các nút và cạnh, xử lý việc kéo và thu phóng, và cung cấp các hook để đọc và biến đổi trạng thái của đồ thị.
Phần thực thi — quyết định những gì chạy, theo thứ tự nào, với đầu vào nào — không phải là điều mà thư viện bức tranh cung cấp. Nó phải được viết như một động cơ nhỏ, riêng biệt: sắp xếp đồ thị, chạy trình xử lý của từng nút, và chuyển đầu ra của từng nút vào các nút kết nối từ nó.
Một nút cần dữ liệu web trực tiếp (nút "fetch" hoặc "crawl") cần một lớp fetch thực sự đằng sau nó, không chỉ đơn thuần là một cuộc gọi fetch() — các trang được kết xuất bằng JavaScript và bảo vệ chống bot sẽ im lặng phá vỡ một yêu cầu HTTP tự xây dựng trước khi phần còn lại của quy trình làm việc được thực hiện.
Động cơ thực thi của bài viết này thực sự đã được chạy, không chỉ được mô tả: một đồ thị bốn nút (trigger → crawl → prompt → output) đã được sắp xếp topo và thực thi từ đầu đến cuối trong môi trường này, với đầu ra thực tế được ghi lại như bên dưới.
Một công cụ xây dựng quy trình làm việc tự xây dựng là một dự án cuối tuần thực tế, có thể xây dựng với hai phần mã nguồn mở đã được xác minh (React Flow cộng với một sắp xếp topo), nhưng nó không đi kèm với bất kỳ tính năng vận hành nào (xác thực, thử lại, ghi nhật ký cấp nút, phiên bản) mà một công cụ sản xuất cần — bài viết này chỉ rõ giới hạn đó thay vì bỏ qua nó.
Introduction: what "building your own agent workflow builder" actually means
Trải nghiem Nstproxy - Bat dau dung thu mien phi ngay
Một công cụ xây dựng quy trình làm việc của tác nhân AI về cấu trúc, là hai hệ thống riêng biệt sử dụng một UI: một bức tranh nơi một người sắp xếp và kết nối các nút, và một động cơ đọc đồ thị đó và thực sự thực thi nó. Sự quan tâm tìm kiếm đối với các công cụ kiểu "công cụ xây dựng tác nhân mở" đã gia tăng cùng với các nền tảng AI không mã và ít mã, nhưng hầu hết những gì xuất hiện cho cụm từ đó là một sản phẩm được lưu trữ không có mã rõ ràng, hoặc một bản demo ngắn mà vẽ một bức tranh đẹp mà không thực sự chạy bất kỳ điều gì. Bài viết này xây dựng điều thực sự: một bức tranh sử dụng thư viện mã nguồn mở React Flow, và một động cơ thực thi với thứ tự phụ thuộc nhỏ được nối với một nút lấy dữ liệu trực tiếp, với động cơ thực thi thực sự được chạy và đầu ra của nó được ghi lại bên dưới.
Hai điều không nằm trong phạm vi chủ đích. Đầu tiên, đây không phải là một hướng dẫn cho bất kỳ sản phẩm quy trình làm việc thương mại hoặc mã nguồn mở cụ thể nào — nó dạy mô hình cơ bản với một thư viện trung lập để kết quả là của bạn để mở rộng. Thứ hai, "AI" trong một nút ở đây có nghĩa là trình xử lý của nút gọi một mô hình ngôn ngữ hoặc một công cụ; bản thân động cơ đồ thị không có ý kiến gì về những gì một nút làm nội bộ, điều này chính xác là lý do làm cho nó có thể tái sử dụng cho tự động hóa không phải AI.
Install: React Flow and a minimal project
React Flow được phát hành dưới dạng @xyflow/react trên npm. Tại thời điểm viết, phiên bản đã phát hành là 12.11.3 (được xác nhận trực tiếp thông qua kho npm, và được cài đặt sạch sẽ bằng lệnh npm install trong một dự án mới trong quá trình xác minh cho bài viết này), và gói được cấp phép MIT theo kho lưu trữ GitHub của xyflow.
Tạo một dự án Vite + React tối thiểu, sau đó thêm thư viện:
React Flow cũng cung cấp một bảng kiểu mà phải được nhập một lần, nếu không thì bức tranh sẽ không được hiển thị theo cách nào cả:
import'@xyflow/react/dist/style.css';
Configure: the canvas skeleton
Một bức tranh React Flow cần ba thứ: một mảng các nút, một mảng các cạnh và chính thành phần <ReactFlow>. Ví dụ tối thiểu của tài liệu chính thức có dạng này:
Phiên bản đó là tĩnh — các mảng không bao giờ thay đổi khi đã được hiển thị. Một trình tạo thực sự cần canvas phản ứng với việc kéo và kết nối mới, đó là chức năng của useNodesState và useEdgesState. Mỗi hook trả về mảng hiện tại, một setter và một trình xử lý thay đổi kết nối trực tiếp vào props onNodesChange / onEdgesChange của <ReactFlow>:
Tài liệu React Flow lưu ý rằng các hook này được thiết kế cho việc lập nguyên mẫu một luồng điều khiển, và một trình tạo lớn hơn nên chuyển sang một kho trạng thái chuyên dụng (cụ thể là Zustand) khi trạng thái nút và cạnh cần được chia sẻ bên ngoài thành phần canvas — điều mà một động cơ thực thi thực sự làm, vì nó phải đọc cùng một đồ thị như canvas đang hiển thị.
Triển khai cơ bản: một loại nút cho hành động của tác nhân
Các loại nút mặc định (input, default, output) chỉ hiển thị một nhãn. Một quy trình làm việc của tác nhân cần những loại nút mang theo cấu hình — một URL cho một bước fetch, một mẫu prompt cho một bước model — và phơi bày các điểm kết nối mà các nút khác có thể gắn vào. Mẫu nút tùy chỉnh của React Flow là một thành phần React thông thường được đăng ký trong một bản đồ nodeTypes:
Một nút có cả tay cầm target (trái) và tay cầm source (phải) có thể ngồi ở giữa một chuỗi — nó nhận một kết nối từ một nút ngược dòng và gửi đầu ra của riêng nó xuống dòng. Một nút kích hoạt chỉ cần một tay cầm source; một nút đầu ra cuối chỉ cần một tay cầm target.
Mẫu nâng cao: một động cơ thực thi thực sự chạy đồ thị
Canvas chỉ tạo ra hai mảng — các nút và các cạnh. Chuyển điều đó thành một quy trình làm việc đang chạy có nghĩa là trả lời một câu hỏi mà thư viện UI không trả lời: các nút chạy theo thứ tự nào? Câu trả lời là một phân loại topo — chỉ đến mỗi nút sau khi mỗi nút cung cấp cho nó đã chạy. Đây là lý thuyết đồ thị, không phải React, vì vậy nó đã được viết và thực thi như một tập lệnh thông thường cho bài viết này thay vì để lại dưới dạng một sơ đồ.
Đồ thị được kiểm tra: một nút kích hoạt, một nút crawl lấy một trang, một nút prompt tóm tắt những gì nút crawl đã trả về, và một nút đầu ra.
functiontopoSort(nodeList, edgeList){const indegree =newMap(nodeList.map((n)=>[n.id,0]));const adjacency =newMap(nodeList.map((n)=>[n.id,[]]));for(const edge of edgeList){ adjacency.get(edge.source).push(edge.target); indegree.set(edge.target, indegree.get(edge.target)+1);}const queue = nodeList.filter((n)=> indegree.get(n.id)===0).map((n)=> n.id);const order =[];while(queue.length){const id = queue.shift(); order.push(id);for(const next of adjacency.get(id)){ indegree.set(next, indegree.get(next)-1);if(indegree.get(next)===0) queue.push(next);}}if(order.length!== nodeList.length){thrownewError('Đồ thị có chu trình — một quy trình làm việc phải là một DAG');}return order;}
Kiểm tra chu trình quan trọng hơn nó nhìn: một giao diện canvas sẽ hạnh phúc cho phép ai đó kéo một cạnh tạo ra một vòng lặp (nút A cấp cho nút B cấp cho nút A), và không có kiểm tra này, động cơ sẽ hoặc bị treo hoặc lặng lẽ loại bỏ các nút thay vì thông báo cho người xây dựng quy trình làm việc rằng có gì đó sai.
Với thứ tự thực thi, chạy quy trình làm việc là một vòng lặp gọi tới trình xử lý của mỗi nút và truyền đầu ra vào đầu vào dọc theo các cạnh hướng tới nó:
asyncfunctionrun(nodes, edges){const order =topoSort(nodes, edges);const nodesById =newMap(nodes.map((n)=>[n.id, n]));const outputs =newMap();const log =[];for(const id of order){const node = nodesById.get(id);const incoming = edges.filter((e)=> e.target=== id).map((e)=> outputs.get(e.source));let result;switch(node.type){case'trigger': result ='đã bắt đầu chạy';break;case'crawl': result =awaitrunCrawlNode(node);break;case'prompt': result =runPromptNode(node, incoming[0]);break;case'output': result = incoming[0];break;default:thrownewError(`Loại nút không xác định: ${node.type}`);} outputs.set(id, result); log.push({node: id,type: node.type,output: result });}return{ order, log };}
runCrawlNode là nơi mà một cuộc gọi fetch() tự chế đến một URL tùy ý gặp rắc rối: nhiều trang thực tế được render phía client, chặn các yêu cầu mà không có fingerprint trình duyệt, hoặc cần một proxy để tiếp cận một cách đáng tin cậy. Đối với việc chạy xác minh của bài viết này, handler đó đã gọi Nstproxy Crawl'sPOST /api/v1/crawl/scrape endpoint (được tài liệu tại docs.nstproxy.com/docs/crawl) — cái trả về Markdown đã được render thay vì HTML thô — vì vậy công việc của nút crawl đã được giảm xuống việc bóc tách phản hồi thay vì tái triển khai một trình duyệt:
asyncfunctionrunCrawlNode(node){const response =awaitfetch('https://api.nstproxy.com/api/v1/crawl/scrape',{method:'POST',headers:{'x-api-key': process.env.NSTPROXY_API_KEY,'Content-Type':'application/json'},body:JSON.stringify({url: node.data.url,formats:['markdown'],onlyMainContent:true}),});const envelope =await response.json();if(envelope.err)thrownewError(envelope.msg||'yêu cầu crawl không thành công');const inner = envelope.data;if(!inner.success)thrownewError(inner.status||'crawl không hoàn tất');return inner.data.markdown;}
Môi trường này không có quyền truy cập mạng ra ngoài đến các miền tùy ý và không có khóa API Nstproxy còn sống, vì vậy bức chạy xác minh bên dưới thay thế một thiết bị HTTP cục bộ mà trả về đúng envelope phản hồi lồng ghép mà tài liệu của Nstproxy Crawl đã xác định (outer code/err/msg/data, inner data.success/status, payload trang tại data.data.markdown) thay cho endpoint thực tế — điều này được tiết lộ ở đây thay vì được trình bày như một cuộc gọi API sống. Logic bóc tách chính nó (kiểm tra err, sau đó success, sau đó đọc data.data.markdown) là chính xác những gì chạy vào endpoint thực tế; chỉ có mục tiêu vận chuyển đã thay đổi cho bài kiểm tra này.
Ví dụ đã thực hiện: chạy đồ thị bốn nút
Đồ thị được sử dụng cho bức chạy này: start (trigger) → fetch (nút crawl nhắm vào một trang sản phẩm Nstproxy Crawl) → prompt (tóm tắt bất cứ điều gì nút crawl đã trả về) → output. Nó đã được thực hiện với Node.js (v22.22.2) trong môi trường này, với fetch chỉ tới một máy chủ thiết bị cục bộ thay thế cho API thực tế Nstproxy Crawl vì lý do được giải thích ở trên.
Đầu ra đã ghi lại:
{"order":["start","fetch","prompt","output"],"log":[{"node":"start","type":"trigger","output":"đã bắt đầu chạy"},{"node":"fetch","type":"crawl","output":"# Nstproxy Crawl\n\nNstproxy Crawl biến một URL thành Markdown sạch, JSON, hoặc một ảnh chụp màn hình với một cuộc gọi API duy nhất, với việc render JavaScript và truy cập hỗ trợ proxy được tích hợp."},{"node":"prompt","type":"prompt","output":"Tóm tắt trang này trong một câu:\n\n# Nstproxy Crawl\n\nNstproxy Crawl biến một URL thành Markdown sạch, JSON, hoặc một ảnh chụp màn hình với một cuộc gọi API duy nhất, với việc render JavaScript và truy cập hỗ trợ proxy được tích hợp.\n\n[đầu ra mô hình giả lập] Nstproxy Crawl — tóm tắt một dòng được tạo ra từ trang đã được lấy."},{"node":"output","type":"output","output":"Tóm tắt trang này trong một câu:\n\n# Nstproxy Crawl\n\nNstproxy Crawl biến một URL thành Markdown sạch, JSON, hoặc một ảnh chụp màn hình với một cuộc gọi API duy nhất, với việc render JavaScript và truy cập hỗ trợ proxy được tích hợp.\n\n[đầu ra mô hình giả lập] Nstproxy Crawl — tóm tắt một dòng được tạo ra từ trang đã được lấy."}]}
Mảng order xác nhận việc sắp xếp topo đã đặt mỗi nút sau các phụ thuộc của nó, và mảng log cho thấy đầu ra của mỗi nút chảy vào nút tiếp theo — Markdown của nút crawl trở thành đầu vào của nút prompt, và (giả lập) tóm tắt của nút prompt trở thành đầu ra cuối cùng. Cuộc gọi mô hình của nút prompt là một thay thế xác định thay vì một cuộc gọi API LLM thực tế, được tiết lộ theo cách giống như sự thay thế crawl ở trên; việc thay thế một cuộc gọi mô hình thực tế có nghĩa là gọi SDK của nhà cung cấp nào đó bên trong runPromptNode thay vì dòng giả lập.
Nhìn Lướt Qua
Mỗi nút "fetch" trong một quy trình làm việc như thế này cuối cùng đều truy cập vào một trang cần kết xuất JavaScript hoặc chặn một yêu cầu HTTP đơn giản — Nstproxy Crawl xử lý lớp đó với một cuộc gọi API thay vì một trình duyệt tự tạo.
Một dự án xây dựng vào cuối tuần của một bề mặt cộng với một động cơ sắp xếp topo chứng tỏ rằng mẫu cốt lõi hoạt động, nhưng nó thiếu mọi thứ mà một công cụ quy trình làm việc sản xuất cần xung quanh cốt lõi đó. Không có lớp lưu trữ — đồ thị trong bài viết này sống trong bộ nhớ trong một lần chạy và không có gì được lưu giữa các phiên. Không có xử lý thử lại hoặc sai sót một phần — nếu yêu cầu của nút crawl thất bại, toàn bộ bài chạy sẽ ném lỗi thay vì thử lại hoặc chuyển đến một nhánh lỗi. Không có nhật ký thực thi theo nút nào được đưa trở lại giao diện bề mặt, không có cách nào để tạm dừng và kiểm tra trạng thái giữa chừng, không có xác thực hoặc kiểm soát truy cập đa người dùng, và không có phiên bản của một quy trình làm việc đã lưu. Cũng không có bảo vệ chống lại một bộ xử lý nút chạy mã do người dùng cung cấp một cách an toàn — bất kỳ loại nút "mã tùy chỉnh" nào trong một sản phẩm thực sự cần một môi trường thực thi được gói kín, mà câu lệnh switch đơn giản trong bài viết này không cung cấp. Không có gì trong số này là nhiệm vụ của React Flow để giải quyết; chúng là công việc kỹ thuật thực tế để biến mẫu này thành một công cụ mà người khác có thể tin cậy. Nếu lớp lấy dữ liệu của nút crawl là phần được thuê ngoài thay vì được xây dựng bằng tay, thì đáng để kiểm tra giá cả của một API được lưu trữ so với khối lượng cuộc gọi dự kiến trước khi giao phó bước lấy dữ liệu của quy trình làm việc cho nó.
Khắc phục sự cố
Bề mặt không được trang trí hoặc có các nút chồng chéo. Điều này hầu như luôn có nghĩa là việc nhập khẩu @xyflow/react/dist/style.css đã bị bỏ qua — React Flow định vị các nút với tọa độ tuyệt đối phụ thuộc vào bảng kiểu cơ bản của chính nó.
Một quy trình làm việc với một vòng lặp bị treo hoặc im lặng bỏ qua các nút. Đây là trường hợp vòng lặp mà hàm topoSort ở trên ném lỗi — một giao diện bề mặt không có cách tích hợp để ngăn ai đó nối một cạnh tạo ra vòng lặp, vì vậy động cơ phải kiểm tra điều đó một cách rõ ràng thay vì giả định rằng mọi đồ thị mà người dùng xây dựng là một DAG hợp lệ.
Một nút tùy chỉnh không chấp nhận kết nối. Kiểm tra xem thành phần nút có bao gồm một Handle với type đúng (source hoặc target) và nó thực sự được hiển thị hay không — một thành phần nút tùy chỉnh bỏ qua <Handle> vẫn hiển thị ổn nhưng không bao giờ có thể được nối với nút khác.
Một nút "fetch" hoạt động cho một số URL và không thành công cho những người khác. Điều đó thường liên quan đến vấn đề kết xuất JavaScript hoặc phát hiện bot, không phải là lỗi trong mã của nút — cuộc gọi fetch() đơn giản chỉ thấy HTML thô mà máy chủ trả về, không phải những gì một trình duyệt sẽ kết xuất sau khi chạy các tập lệnh của trang, đó là lý do tại sao nút crawl trong bài viết này gọi một API nhận thức kết xuất thay vì lấy URL trực tiếp.
Kết luận
Xây dựng một trình tạo quy trình làm việc đại diện AI được phân chia rõ ràng thành hai phần đã được xác minh: React Flow (@xyflow/react, phiên bản 12.11.3, cấp phép MIT) cho bề mặt, và một động cơ sắp xếp topo nhỏ được viết tay để thực thi — không ai trong số đó phụ thuộc vào nhau, đó là lý do tại sao chúng có thể được phát triển và thử nghiệm riêng biệt, như bài viết này đã làm. Kết quả là một mẫu thực tế, có thể chạy thay vì một sơ đồ, được chứng minh với một đồ thị bốn nút thực hiện đến cuối. Điều nó không phải là một sản phẩm hoàn chỉnh — độ bền, thử lại, gói kín và kiểm soát truy cập vẫn là công việc kỹ thuật thực tế được bổ sung vào cốt lõi này. Để biết thêm thông tin về lớp lấy dữ liệu được sử dụng trong ví dụ đã làm, xem bài viết ra mắt Nstproxy Crawl.
Câu hỏi thường gặp
Q: Tôi có cần React Flow cụ thể không, hay tôi có thể sử dụng thư viện khác?
React Flow (@xyflow/react) là thư viện được sử dụng và xác minh trong bài viết này, nhưng không phải là tùy chọn duy nhất — Svelte Flow (phiên bản Svelte tương đương của cùng một nhóm) bao phủ các dự án không sử dụng React, và bất kỳ thư viện bề mặt nào mà công bố vị trí nút, các cạnh và một cách để đăng ký thành phần tùy chỉnh có thể đảm nhiệm cùng một vai trò. Động cơ thực thi được mô tả ở đây hoàn toàn độc lập với thư viện bề mặt nào hiển thị nó, vì nó chỉ tiêu thụ các mảng nút và cạnh nguyên bản.
Q: Động cơ thực thi có cần chạy trong trình duyệt không?
Không — và đối với hầu hết các quy trình làm việc thực tế, điều đó không nên xảy ra. Canvas chạy trong trình duyệt để một người có thể chỉnh sửa đồ thị, nhưng việc thực thi một quy trình làm việc (đặc biệt là quy trình có khóa API hoặc các bước chạy lâu) thuộc về một máy chủ. Engine được trình bày trong bài viết này là JavaScript thuần túy không có sự phụ thuộc vào trình duyệt, vì vậy nó có thể chạy trong Node.js chính xác như đã thử nghiệm ở đây, hoặc bên trong bất kỳ môi trường backend nào hỗ trợ fetch.
H: Làm thế nào để tôi ngăn ai đó kết nối một quy trình làm việc mà lặp mãi mãi?
Từ chối nó trước khi chạy. Hàm topoSort trong bài viết này ném lỗi khi thứ tự đã sắp xếp không bao gồm mọi nút, điều này chính xác là những gì xảy ra khi có một chu kỳ — bắt lỗi đó trong giao diện người dùng canvas và cho người đó biết nút nào có liên quan, thay vì để engine treo.
H: Một nút có thể gọi nhiều dịch vụ bên ngoài không?
Có — một xử lý nút chỉ là một hàm; nó có thể thực hiện nhiều cuộc gọi tùy ý trước khi trả về đầu ra của nó. Ràng buộc duy nhất mà engine áp đặt là rằng xử lý của một nút chỉ nhận đầu ra của các nút có cạnh chỉ vào nó, vì vậy bất kỳ nút nào cần dữ liệu từ hai nguồn phía trên cần hai cạnh đến.
H: Cách nhanh nhất để thêm một cuộc gọi mô hình ngôn ngữ thực vào chỗ nút prompt giả là gì?
Thay thế thân của runPromptNode bằng một cuộc gọi đến SDK của nhà cung cấp mô hình đang sử dụng, truyền đầu ra của nút đang đến như ngữ cảnh và trả về phản hồi của mô hình dưới dạng chuỗi — phần còn lại của engine (sắp xếp, truyền đầu ra, ghi nhật ký) không cần thay đổi, vì nó chỉ quan tâm rằng một xử lý nút trả về một giá trị.
H: Tại sao nút crawl lại gọi một API thay vì chỉ sử dụng fetch() trực tiếp?
Một cuộc gọi fetch() thuần túy chỉ nhận bất kỳ HTML nào mà một máy chủ gửi trước khi bất kỳ JavaScript phía bên khách nào chạy, và nhiều trang hiển thị nội dung thực sự của chúng sau đó — một API nhận thức về việc hiển thị thực thi trang giống như một trình duyệt sẽ làm trước khi trả lại Markdown hoặc HTML, đó là những gì nút crawl trong ví dụ làm việc của bài viết này dựa vào.
Xây dựng một trình tạo luồng công việc trực quan mã nguồn mở thực sự cho các tác nhân AI: một canvas React Flow kết hợp với một engine thực thi sắp xếp topo được xác minh, đã được kiểm tra từ đầu đến cuối với đầu ra thực tế đã được thu nhận.
Ivy Lin
Aug. 24th 2026
110M+ IP that voi ti le truy cap thanh cong 99.9%
Phan hoi trung binh ~0.5s cho tac vu dong thoi cao
Chi tu $0.1/GB
Truy cap ngay cac pool proxy residential, datacenter, IPv6 va ISP cao cap.