Upgrade to Pro — share decks privately, control downloads, hide ads and more …

AutonomyとControlのあいだ:Graflowで記述するAIエージェント協調

 AutonomyとControlのあいだ:Graflowで記述するAIエージェント協調

AIエージェントの実用化が進む中、「エージェントにどう仕事をさせるか」だけでなく「複数のエージェントをどう協調させるか」が新たな課題となっています。 本セッションでは、エージェント協調のための次世代ワークフローエンジン「Graflow」の設計思想と、実際のAIエージェント構築事例についてお話しします。 Graflowは、Pythonの演算子(>> で逐次、| で並列)という直感的なDSLでワークフローを定義し、動的タスク生成・Human-in-the-Loop・分散スケーリング・OpenTelemetryによるFull Observabilityを備えたOSSワークフローエンジンです。 「Developer Experience」と「本番運用の堅牢性」の両立を目指し、未踏アドバンスト事業の支援を受けて開発しました。 既存のワークフローエンジン(Airflow等)やAIフレームワーク(LangGraph等)との違い、Graflow上でのマルチエージェント構築の具体例についてお話しします。

プロジェクトサイト: https://github.com/GraflowAI/graflow

Avatar for Makoto Yui

Makoto Yui

May 27, 2026

More Decks by Makoto Yui

Other Decks in Programming

Transcript

  1. >> | AutonomyとControlのあいだ Graflowで記述するAIエージェント協調 Makoto Yui @myui Treasure Data —

    Senior Principal Engineer 2025年度 未踏アドバンスト事業 イノベータ http://github.com/GraflowAI/graflow
  2. 本日のアジェンダ 2 / 21 Speaker Makoto Yui @myui Treasure Data

    Senior Principal Engineer 2025年度 未踏アドバンスト事業 イノベータ採択 過去: Apache Hivemall コミッタ 大規模データ処理基盤の開発に長年従事 Agenda 1 Autonomy(自律)とControl(制御)の両立という課題 4 min 2 Graflowの設計思想 5 min 3 既存ツールとの違い & 本番運用機能 3.5 min 4 マルチエージェント構築事例 5 min 5 Roadmap & まとめ 2 min
  3. AIエージェントの「理想」と「現実」 3 / 21 理想 自律的に考え、ツールを使いこなし、タスクを完遂 SuperAgent — Agent Loop

    Think / Plan Action Tool / Code exec Observe / Evaluate Reflect until converged 現実 自律性を「制御可能なプロセス」に載せる必要 1. 信頼性 何が起きたか・なぜ失敗したか追えず、障害対応・監査が困難 2. 再現性 挙動が状況依存で揺れやすく、検証・段階的改善が難しい 3. レイテンシ・コスト・安全性 自律的なループは予測困難で、本番投入のリスクが高い
  4. 具体例:自律エージェントの再現性問題 4 / 21 お題: 「東証上場企業で過去2年連続赤字の企業を漏れなくリストせよ」 1回目の実行 参照ソース: bloomberg.co.jp toyokeizai.net

    個人ブログ (note.com) YouTube動画 → 23社をリスト化 2回目の実行(同じプロンプト) 参照ソース: zaikai.jp 別のニュース記事群 (1回目とは異なるソース) → 18社をリスト化(網羅性も異なる) 信頼性と再現性を確保しつつ、AIエージェントの自律性を活かす アプローチが必要 — それが Agentic Workflow
  5. Autonomy Slider — 自律性は連続的なダイヤル 5 / 21 Karpathy曰く: 自律性は二者択一ではなく、信頼とリスクに応じて調整するスライダー HERE

    Deterministic 完全に決定的 Airflow / Dagster 従来ETL · RPA Agentic Workflow Controlled Autonomy Graflow 人間設計フロー × タスク内自律 Fully Autonomous 完全自律 Claude Cowork OpenClaw Andrew Ng: ほとんどの企業は「Controlled Autonomy」に賭けるべき
  6. Production Gap — 中間領域の空白地帯 6 / 21 既存ツールは両端しかカバーしない Workflow Engines

    Airflow / Dagster / Prefect ✓ 本番運用機能は充実 ✓ 分散実行 · チェックポイント · 監視 ✗ エージェント推論ループ表現不可 ✗ 動的なタスク生成が苦手 Agent Frameworks LangGraph/AutoGen/CrewAI/Google ADK/PydanticAI/Mastra ✓ ReAct ループの記述は可能 ✓ LLM呼び出しの抽象化 ✗ HITL / 分散実行 / 細粒度エラー制御 が弱い ✗ 本番要件は自作することに Production Gap: 両者の中間に「Agentic Workflow」を本番品質で支える層が無い → Graflow
  7. Graflow — 4本柱 7 / 21 Pythonベースの Agentic Workflow Orchestration

    Engine (Apache-2.0) Developer Experience 書きやすさ第一 Pythonの演算子 (>> / |) で ワークフロー定義。コードが そのまま図に見える設計 Dynamic & Flexible 実行時に組み替え可能 DAG骨格 × 動的遷移の ハイブリッド。タスク数も 依存関係も実行時に決定 Production-Grade Day 1 から本番品質 HITL · チェックポイント · 細粒度エラーポリシー · 分散実行 · Full Observability Framework-Agnostic エコシステム非依存 Google ADK / PydanticAI / OpenAI Agents SDK と統合可能。 LiteLLMで全LLMプロバイダ対応 Developer Experience × 本番運用の堅牢性 の両立を目指す
  8. 設計思想 ① Pythonic DSL — 書きやすさ 8 / 21 「コードがそのままワークフロー図に見える」

    LangGraph 5行 — エッジ列挙が必要 graph.add_edge(START, "fetch") graph.add_edge("fetch", "transform_a") graph.add_edge("fetch", "transform_b") graph.add_edge("transform_a", "store") graph.add_edge("transform_b", "store") Graflow 1行 — Diamond パターンを直感的に fetch >> (transform_a | transform_b) >> store Workflow Patterns Linear: a >> b >> c Fan-out: src >> (a | b | c) Fan-in: (a | b) >> aggregate Diamond: fetch >> (x | y) >> store >> 逐次実行 | 並列実行 edge数は ノード数 × 依存数 → グラフが大きくなると爆発的に冗長化
  9. 設計思想 ② DAG × State Machine ハイブリッド 9 / 21

    Define-and-Run (TF1.x) → Define-by-Run (PyTorch) と同じ進化 ① 静的骨格 (DAG) 読みやすさ fetch >> (validate | enrich) >> process >> save Static structure — チームで読める ✓ IDEで補完が効く ✓ グラフ可視化が容易 ✓ デフォルトの実行順序が明確 ② 動的遷移 (State Machine) 柔軟性 @task(inject_context=True) def process(ctx): r = run() if r.score < 0.8: ctx.next_iteration() elif r.has_error: ctx.next_task(handler, goto=True) else: ctx.next_task(finalize) Runtime decisions = 普通の if 文 Static readability + Dynamic flexibility
  10. LangGraph vs Graflow — Define-by-run の優位性 LangGraph — conditional routing

    from langgraph.graph import StateGraph, END builder = StateGraph(State) builder.add_node("agent", agent_node) builder.add_node("tool", tool_node) # 条件分岐は事前に全部宣言 builder.add_conditional_edges( "agent", should_continue, {"continue": "tool", "end": END}, ) builder.add_edge("tool", "agent") builder.set_entry_point("agent") # compile() でグラフ確定 — 以降は変更不可 graph = builder.compile() result = graph.invoke({...}) Graflow — define-by-run from graflow import task, workflow with workflow("agent_loop") as ctx: @task(inject_context=True) def agent(context): out = call_llm(context.get_channel().get("input")) # 分岐は普通の if 文 — 実行時に判定 if not should_continue(out): context.terminate_workflow("done") return out @task def tool(out: str): return run_tool(out) # ループは演算子で素直に表現 agent >> tool >> agent ctx.execute("agent") ✗ routing DSL 不要 ✗ END sentinel 不要 ✗ conditional-edge registry 不要 ✓ pdb でデバッグ可能 10 / 21 Define-and-compile(TF1.x流): 全エッジを事前宣言、compile時にグラフ確定 Define-by-run(PyTorch流): グラフ形状はPythonコードが実行時に決定
  11. 設計思想 ③ SuperAgent as Fat Node 11 / 21 ReActループはAgentの中。ワークフローはタスク依存だけを表現

    Graflow Workflow fetch research_agent (fat node) LLM Tool Think save workflow: fetch >> research_agent >> save 関心の分離による利点 Best-of-Breed ADK / PydanticAI を用途別に使い分け クリーンなグラフ ツール選択ロジックが ワークフローに漏れ出さない
  12. 既存ツールとの違い — Airflow / LangGraph 12 / 21 一目で分かる対比表 観点

    Airflow LangGraph Graflow タスク数 定義時に固定 compile時に固定 実行時に動的決定 分岐 / ループ BranchPythonOperator add_conditional_edges (事前定義) next_task / next_iteration (実行時) 並列の書き方 TaskGroup + dependencies add_edge を複数 >> と | で 1 行 HITL Sensor (作り込み) interrupt + 自作 UI request_feedback + REST API + UI 同梱 分散実行 Celery / K8s Executor なし (単一プロセス) Redis Worker (1 行で切替) LLM統合 — LangChain 依存 LiteLLM + ADK/PydanticAI 等 Observability Web UI / Statsd LangSmith (有償) Langfuse + OpenTelemetry (OSS)
  13. 本番運用機能 — Day 1 から本番品質 13 / 21 「本番運用の壁」に当たる前から、必要な道具が揃っている Human-in-the-Loop

    request_feedback() + Timeout時自動checkpoint REST API + UI 同梱 Checkpoint (User制御) 重要ポイントだけ明示的に保存 local / S3 切替可 並列グループ エラーポリシー Strict / Best-effort / At-least-N / Critical グループ単位で設定 分散実行 (OSS) Redis Worker で水平スケール 1行でlocal→distributed切替 Retry & Iteration @task(retry_policy=...) + max_cycles exponential backoff + jitter Full Observability Langfuse + OpenTelemetry self-hostable, OSS
  14. HITL — request_feedback() で人間判断を組込み approval / text / selection /

    multi_selection の4種類 + Timeout時の自動checkpoint・再開 Bad — 冪等でない (重複デプロイの危険) @task(inject_context=True) def deploy_with_approval( ctx: TaskExecutionContext): # 副作用を先に実行 (悪い例) deployment_id = api.deploy_to_production() # 後から承認を取る response = ctx.request_feedback( feedback_type="approval", prompt="Approve deployment?", ) # Timeout → checkpoint → 再開で # deploy が再実行されてしまう! # → 重複デプロイ発生 Good — channel フラグで冪等性確保 @task(inject_context=True) def deploy_with_approval( ctx: TaskExecutionContext, plan): channel = ctx.get_channel() if not channel.get("approved"): response = ctx.request_feedback( feedback_type="approval", prompt="Approve deployment?", timeout=300, ) if not response.approved: ctx.cancel_workflow("rejected") channel.set("approved", True) if not channel.get("deployed"): did = api.deploy_to_production(plan) channel.set("deployed", True) Timeout時に checkpoint 自動生成 → REST API でフィードバック受領 → checkpoint から再開 (タスクは冪等であること)
  15. Checkpoint / Resume — 明示的セーブポイント task_ctx.checkpoint() で保存 → CheckpointManager.resume_from_checkpoint() で再開

    Part 1 — 実行 + Checkpoint 保存 with workflow("checkpoint_demo") as wf: @task def step_1() -> str: return "data_prepared" @task(inject_context=True) def step_2(task_ctx: TaskExecutionContext): # 任意のタイミングで明示的に保存 task_ctx.checkpoint( path=checkpoint_path, metadata={"stage": "done"}, ) return "data_processed" @task def step_3() -> str: return "completed" step_1 >> step_2 >> step_3 _, ctx = wf.execute("step_1", ret_context=True) Part 2 — Checkpoint から再開 # 別プロセス・別マシンからでも再開可能 restored_ctx, metadata = ( CheckpointManager.resume_from_checkpoint( ctx.last_checkpoint_path ) ) print(metadata) # → {"stage": "done"} # 想定ユースケース: # - 長時間処理の中断・再開 # - 障害発生時の途中復元 # - HITL Timeout 後の再開 # - local / S3 切替可能 User制御の明示的checkpoint + HITL Timeout時の自動checkpoint (slide 14) — 本番運用に必須の機能
  16. Scale to Distributed — Redis Worker 分散実行 同じワークフローコードが Local でも

    Distributed でも動作 — backend を切り替えるだけ Backend 切替 — local → Redis 分散 # Define parallel extraction followed by aggregation parallel_extract = ( (extract_source_1 | extract_source_2 | extract_source_3) .set_group_name("parallel_extract") .with_execution(backend=CoordinationBackend.REDIS) ) parallel_extract >> aggregate_results Worker 起動 — 水平スケール # 別ターミナルで複数 worker を起動 $ python -m graflow.worker.main \ --worker-id worker-1 $ python -m graflow.worker.main \ --worker-id worker-2 # worker を増やせばそのまま水平スケール # Kubernetes / Docker Compose にも # そのまま展開可能 ワークフローコードは不変 — 運用フェーズに応じて local / Redis を選択。Airflow流の水平スケールを OSS で実現
  17. Iteration / Retry — max_cycles と max_retries タスク内の反復と例外時リトライを decorator 引数で宣言的に制御

    max_cycles — タスク内の反復制御 @task(inject_context=True, max_cycles=5) def counter(ctx: TaskExecutionContext): print(f" cycle {ctx.cycle_count}" f"/{ctx.max_cycles}") if ctx.can_iterate(): ctx.next_iteration() # 同一タスクを最大 5 回まで反復実行 # can_iterate() で上限到達を判定 # next_iteration() で再実行をスケジュール RetryPolicy — exponential backoff @task( retry_policy=RetryPolicy( max_retries=3, initial_interval=0.1, backoff_factor=2.0, # 0.1s → 0.2s → 0.4s ), ) def flaky_service(): global attempts attempts += 1 if attempts < 3: raise ConnectionError( f"timeout (attempt {attempts})") return "success" next_iteration() による反復は max_cycles、例外時のリトライは retry_policy — 役割が明確に分離
  18. Sandbox Execution — DockerTaskHandler 14 / 21 タスク単位で実行戦略を切り替えられる。LLM時代に効く設計 3つの主要ユースケース 1

    GPU処理 PyTorchやTensorRT等のGPU依存タスクを 適切なベースイメージで実行 2 依存関係の分離 pandas 1.x と 2.x が混在するタスク群を バージョン衝突なく実行 3 LLM生成コードの安全実行 AIが書いたコードをホスト環境から 隔離されたコンテナ内で実行 Handler 切替は decorator 引数のみ # Default — 同一プロセス内 @task def simple_task(): return "result" # Docker — 隔離コンテナ実行 @task(handler="docker", handler_kwargs={ "image": "pytorch/pytorch:2.0-gpu", "gpu": True, "volumes": {"/data": "/workspace/data"}, }) def train_on_gpu(): return train_model() # LLM生成コードを安全に実行 @task(handler="docker", handler_kwargs={ "image": "python:3.11-slim", "network": "none", "memory_limit": "512m", "timeout": 30, }) def run_generated_code(code: str): return exec_in_sandbox(code)
  19. ユースケース:大規模文書の適応的処理 15 / 21 RFP / 契約書 / 規制文書 —

    構造・長さが毎回異なる文書を適応的に分析 文書入力 構造分析 動的チャンク分割 parallel(*tasks) 法務Agent 技術Agent 価格Agent ... aggregate Critique Agent REVISE (不合格chunk のみ再処理) 1 動的ノード生成 実行時にN個のチャンク用タスクを生成 2 parallel >> reduce Nタスクを並列実行 → 1行で集約 3 選択的再帰 不合格分のみ再処理 → 90%+ コスト削減
  20. コードで見る — 3つの核心パターン ① 動的ノード生成 + parallel >> reduce @task(inject_context=True)

    def adaptive_doc(ctx, doc, model_ctx): # 1. 文書構造を把握 analysis = analyze_document(doc) # 2. 最適なチャンク分割方式を選択 chunker = choose_chunker(analysis, model_ctx) chunks = chunker(doc) # 3. チャンク別タスクを動的生成 tasks = [ create_chunk_task(c, i, model_ctx) for i, c in enumerate(chunks) ] # 4. fan-out / fan-in を1行で ctx.next_task( parallel(*tasks) >> aggregate(analysis, model_ctx) ) ② 選択的再帰 (Critique → Revise) @task(inject_context=True) def aggregate(ctx, analysis, model_ctx, chunk_results): report = merge_results(chunk_results) critique = critique_agent(report) if critique.verdict == "REVISE": # 不合格chunkのみ再処理 redo = [ create_chunk_task(c, i, model_ctx, feedback=critique.feedback_for(i)) for i, c in critique.failed_chunks ] ctx.next_task( parallel(*redo) >> partial_update(report) ) else: ctx.next_task(output_report(report)) 例: 100p RFP → 53タスク動的生成 → 不合格3つだけ再処理 → LLM コスト 90%+ 削減
  21. LLM 注入 — inject_llm_agent と inject_llm_client Dependency Injection で LLM

    Agent / Client をタスク引数として受け取る inject_llm_agent — 名前付き Agent 注入 @task(inject_llm_agent="researcher", inject_context=True) def research(agent, context: TaskExecutionContext): """Agent searches and reasons about the topic""" result = agent.run( "Compare Python async frameworks" " for web scraping") context.get_channel().set( "research", result["output"]) inject_llm_client — 軽量な LLM 呼び出し @task(inject_llm_client=True) def format_report(llm: LLMClient, research: str) -> str: """Simple LLM call to format the research into a report""" return llm.completion_text( [{"role": "user", "content": f"Format as a markdown" f" report:\n{research}"}], model="gpt-4o-mini", ) 高機能(tools / sub-agents / memory) は inject_llm_agent、純粋な completion は inject_llm_client で軽量に
  22. Local LLM Support — Ollama 統合とモデル動的選択 中期Roadmap: gemma4:e4b / gpt-oss:20b

    等の local model を ensemble・dynamic に使い分け LLM Client — モデル毎に直接指定 # LiteLLM 経由で local / cloud を統一的に detailed = llm_client.completion_text( messages=[{ "role": "user", "content": "Explain quantum" " computing in one sentence", }], model="ollama/gemma4:e4b", # Explicit model override ) # 同一インターフェイスで切替可能: # model="ollama/gpt-oss:20b" # model="ollama/gemma4:e4b" # model="gpt-4o-mini" # model="gemini-2.5-flash" LLM Agent — Agent 単位でモデル指定 # Google ADK Agent でも同じ要領 adk_agent = LlmAgent( name="weather_agent", model="ollama/gemma4:e4b", # model="gemini-2.5-flash", instruction=( "You are a weather assistant." " Use the get_weather tool" " to answer questions." ), tools=[get_weather], ) # Agent 単位で model を差し替え可能 # → タスク特性に応じてモデルを使い分け 複数モデルの並列ensemble + 入力・コスト・レイテンシ条件に応じた dynamic 切替 — Best-of-Breed × Cost Efficiency
  23. Agent Framework Integration Best-of-Breed: フレームワークを用途別に使い分け、同じ inject_llm_agent で扱える Google ADK multi-agent

    / context-compression に強い from google.adk.agents import LlmAgent from graflow.llm.agents.adk_agent \ import AdkLLMAgent adk_agent = LlmAgent( name="supervisor", model="gemini-2.5-flash", tools=[search_tool, calc_tool], sub_agents=[analyst, writer], ) ctx.register_llm_agent( "supervisor", AdkLLMAgent(adk_agent)) PydanticAI type-safe な構造化出力に強い from pydantic_ai import Agent from graflow.llm.agents import \ PydanticLLMAgent class AnalysisResult(BaseModel): sentiment: str confidence: float key_points: list[str] p_agent = Agent( model="openai:gpt-4o", output_type=AnalysisResult, ) ctx.register_llm_agent("analyzer", PydanticLLMAgent(p_agent)) 同一インターフェイス inject_llm_agent → ワークフロー側はどちらのAgentでも変わらない
  24. SuperAgent Swap — Workflow コードは不変 ADK → PydanticAI に差し替えても、ワークフロー側のタスクは無修正 変わる箇所:

    Agent 登録のみ # ADK 版 adk = LlmAgent(name="task_manager", model="gemini-2.5-flash", tools=[add_task, list_tasks, delete_task]) ctx.register_llm_agent( "task_manager", AdkLLMAgent(adk)) # ───────────────────────────── # PydanticAI 版 pa = Agent(model="openai:gpt-4o", system_prompt="...") @pa.tool def add_task_tool(ctx, title, ...): ... ctx.register_llm_agent( "task_manager", PydanticLLMAgent(pa)) 変わらない箇所: Workflow 部分 # ADK でも PydanticAI でも同一 @task(inject_llm_agent="task_manager", inject_context=True) def handle_request(llm_agent, ctx): result = llm_agent.run( "Add 'Write report' as important") print(f"Agent: {result['output']}") @task(inject_context=True) def confirm_deletion(ctx): response = ctx.request_feedback( feedback_type="approval", prompt="Delete important task?", timeout=60, ) if response.approved: delete_task(...) handle_request >> confirm_deletion HITL も同じ — request_feedback() はWorkflowタスクであって Agent の中ではない。テストも構成変更も容易
  25. Prompt Management — YAML と Langfuse 統合 プロンプトをコードから分離し、運用中の差し替え・A/Bテスト・バージョン管理を可能に PromptManagerFactory —

    YAML / Langfuse # YAML ベースの prompt manager prompts_dir = Path(__file__).parent / "prompts" pm = PromptManagerFactory.create( "yaml", prompts_dir=str(prompts_dir), ) # または Langfuse ベース # → web UI でプロンプト編集・バージョン管理 pm = PromptManagerFactory.create("langfuse") # Workflow に渡す with workflow( "my_workflow", prompt_manager=pm) as ctx: ... タスクからの利用 — ctx.prompt_manager @task(inject_context=True) def greet(ctx: TaskExecutionContext) -> str: pm = ctx.prompt_manager # text プロンプトを取得して render prompt = pm.get_text_prompt("greeting") return prompt.render( name="Alice", product="Graflow", ) # prompts/greeting.yaml: # template: | # Hi {{ name }}, welcome to {{ product }}! プロンプト改善サイクルをコードリリースから分離 — Langfuse 連携で本番プロンプトを Web UI から差替え可能
  26. 次のRoadmap — Harness Engineering 19 / 21 Agentic Workflow Engine

    → Agent Harness Platform へ エージェントが効果的に動作する「足場 (harness)」を工学的に整える Skills Support Claude Skills 互換のスキル定義 動的ロードで特定ドメインの 手順書を Agent に装着 Context Engineering チャネル機構を発展させ 関連情報の自動注入・ トリミング・要約をサポート Ralph-loop 自己反復改善ループの ファーストクラスサポート (Critique → Revise の発展形) Agent Memory 短期 / 長期 / エピソード記憶を チャネル統合 モデルではなく「モデルを取り囲む環境・記憶・道具・反復ループ」の品質で差がつく時代へ
  27. Roadmap — Agent Skills (Claude Skills 互換) ドメイン特化の手順書を skill としてAgentへ動的に装着

    — ADK / PydanticAI 両対応 Google ADK — load_skill_from_dir from google.adk import Agent from google.adk.skills import load_skill_from_dir from google.adk.tools import skill_toolset weather_skill = load_skill_from_dir( "./skills/weather_skill") toolset = skill_toolset.SkillToolset( skills=[weather_skill], additional_tools=[get_weather_tool], ) root_agent = Agent( model="gemini-flash-latest", name="skill_user_agent", tools=[toolset], ) PydanticAI — pydantic_ai_skills from pydantic_ai import Agent from pydantic_ai_skills import SkillsCapability agent = Agent( model="gateway/openai:gpt-5.2", instructions=( "You are a helpful research assistant." ), capabilities=[ SkillsCapability( directories=["./skills"]), ], ) # → どちらの Agent も同じ skill ディレクトリを # 読み込んで Graflow から inject_llm_agent で利用 Graflow ロードマップ — どちらの SuperAgent でも同一の skill 資産を活用 (Best-of-Breed × Domain Skills)
  28. まとめ — AutonomyとControlのあいだ Graflow が提供する価値 1 Strategic Simplicity SuperAgent(ReAct等)は専門frameworkへ委譲。Graflowはワークフロー編成に集中 2

    Runtime Flexibility next_task() / next_iteration() / terminate_workflow() で実行時に組み替え 3 Developer Experience Pythonic な演算子DSL (>>, |) で直感的に書ける 4 Production Readiness Checkpoint, HITL, Docker handler, Distributed exec を Day 1 から 5 Scalable Parallel Execution Airflow流の水平スケール。local → distributed が1行 全体は人間がデザイン、個々のタスクではAgentが自律 — Controlled Autonomy
  29. Thank You Get Involved with Graflow GitHub GraflowAI/graflow Apache-2.0 OSS

    Docs graflow.ai Tutorials & References X / Twitter @GraflowAI @myui (author) Issues / PRs / Feedback Welcome