RAKSUL TechBlog

RAKSULグループのエンジニアが技術トピックを発信するブログです

Difyで実装するAnthropicの「Building Effective Agents」

こんにちは。ノバセルCTOの磯部です。

この記事はノバセル テクノ場 出張版2025 Advent Calendar 2025の最終日になります。これまで長らくお付き合い頂き、本当にありがとうございます。

生成AIを活用したシステム開発において、Agentという言葉は日常的に使われるようになりました。しかし、その言葉の定義や実装方法はしばらく曖昧だったかと思います。

もう1年前のことになりますが、2024年12月にAnthropicがBuilding Effective Agentsを公開し、どこまでをWorkflowと呼び、どこからをAgentと呼ぶべきかを提案してくれました。

この記事の最大の貢献は、AgentとWorkflowを二項対立ではなく、自律性のスペクトラムとして定義したことにあると私は考えています。

今回は、ノーコードLLM開発プラットフォームであるDify(ver. 1.11.1)を使い、この記事で紹介されているEvaluator-Optimizerパターンを実装してみます。

また、Workflowだけでなく記事の後半ではAgentによる実装も行い、Workflow型とAgent型で挙動がどう変わるのか、その違いをハンズオン形式で比較検証していきます。

AgentとWorkflowのスペクトラム

実装に入る前に、Anthropicによる定義を整理しておきましょう。これらは0か1かのバイナリではなく、以下のようなスペクトラムの中に存在します。

  • Workflow: 事前に定義されたコードやパスに従ってシステムが動作します。分岐やループが存在しても、その条件は人間が設計したロジックに基づきます。「予測可能性」と「確実性」が高いのが特徴です。
  • Agent: LLMが自らプロセスを制御します。どのツールを使うか、次に何をするかを動的に決定します。「柔軟性」が高い反面、挙動の予測が難しくなる場合があります。

本記事では、この中間〜右側に位置するパターンを実際にDifyで作っていきます。

Difyのチャットフローとワークフローの違い

Difyにはアプリケーションの種類としてチャットフローとワークフローがありますが、これらは以下のように使い分けます。

特徴 チャットフロー ワークフロー
主な用途 ユーザーとの対話型アプリ。 バックエンド処理、バッチAPI。
メモリ あり。会話履歴を保持し、文脈を踏まえた応答が可能。 なし。1回のリクエストで完結するステートレスな処理。
対話機能 中間ステップで「回答」ノードを使い、ストリーミング応答が可能。 基本的に最終結果のみを出力する(Functionに近い)。


実際のところ、両者で使えるノードにほとんど違いはありません。どちらを使っても複雑なフローを組むことはできます。

今回は、ユーザーと対話しながら文章を作成する対話型アプリを作成するため、メインの実装にはチャットフローを使用します。

以降、ワークフロー(Workflow)という語はAnthropicの概念(Workflow/Agent) とDifyのアプリ種別(チャットフロー/ワークフロー) の2つの意味で登場するため、混同しないように注意してください。

実装パターンの選定

Anthropicの記事では、エージェントシステムを構築するビルディングブロックとして以下の5つが紹介されています。

  1. Prompt Chaining: LLMの出力を次の入力にする基本形。
  2. Routing: 入力を分析し、適切なパスに振り分ける。
  3. Parallelization: 複数のタスクを並列実行し、統合する。
  4. Orchestrator-Workers: 指揮者がタスクを分解し、作業者に割り振る。
  5. Evaluator-Optimizer: 生成と評価をループさせ、品質を高める。

今回はEvaluator-Optimizerを採用します。 理由は、このパターンを実装するには「生成結果を次の入力にする(Prompt Chaining)」と「評価結果で分岐する(Routing)」という要素の両方が含まれており、他の基本的なパターンの要素も同時に学習できるためです。

本稿では、執筆時点でDifyから利用可能な選択肢の中から用途に合わせて、生成にはGemini 2.5 Pro、評価にはGemini 2.5 Flashを使います。

実践1:Workflowアプローチ(明示的なループ制御)

まずは、チャットフロー機能を使って「指定回数まで推敲を繰り返す」フローを構築します。 AI任せにするのではなく、人間が「ループ」ノードを使ってループ回数を制御します。

アーキテクチャ

graph LR
    Start[開始] --> Gen["LLM<br>Generator"]
    Gen --> LoopStart{"Loop<br>(最大3回)"}
    
    subgraph Loop
        LoopStart --> Eval["LLM<br>Evaluator"]
        Eval --> Parser["コード<br>JSONパース"]
        Parser --> Check{"Router<br>PASS or FAIL?"}
        Check -- FAIL --> Opt["LLM<br>Optimizer"]
    end
    
    Opt --> Finish["回答"]
    Check -- PASS --> Finish["回答"]
  1. Generator: 初稿を作成。
  2. Loop: 最大3回まで以下のプロセスを回す。
  3. Evaluator: 現在の記事を評価。
  4. Router: PASSなら何もしない。
  5. Optimizer: FAILなら記事をリライトし、変数を上書きして次のループへ。

STEP 1: 初稿作成

  1. Difyで「最初から作成」→「チャットフロー」を選択
    • アプリ名: Optimized Writer Flow
  2. 「スタート」ノードの次に「LLM」ノードを追加し、名前をGeneratorとします。
    • モデル: 「Gemini 2.5 Pro」(論理的思考に強いモデル)

システムプロンプト:

あなたはプロのライターです。ユーザーの依頼に基づいて記事の初稿を作成してください。

ユーザープロンプト:

{{#sys.query#}}

STEP 2: ループの設定

  1. 「LLM」ノードの後に「ループ」ノードを追加し、名前をLoopとします。
    • ループ変数:
      • 変数名: current_draft
      • 変数型: 「String」
      • 入力モード: 「Variable」
      • : {{#Generator.text#}}
    • 最大ループ回数: 3

最大ループ回数を1-2回など少なめに設定すると決定論的なWorkflow寄りになりますし、10回など多めに設定すると自律的なAgent寄りになると捉えることができます。

STEP 3: 評価

  1. 反復ブロック内の開始点に「LLM」ノードを追加し、名前をEvaluatorとします。
    • モデル: 「Gemini 2.5 Flash」(高速なモデル)

システムプロンプト:

あなたは厳格な編集者です。入力された記事を評価し、JSON形式で結果を出力してください。
合格基準: ユーザーの依頼を満たし、論理的で、誤字脱字がないこと。

ユーザープロンプト:

ユーザーの依頼: {{#sys.query#}}
現在の原稿: {{#Loop.current_draft#}}

以下のJSONフォーマットのみを返してください:
{
  "status": "PASS" または "FAIL",
  "reason": "評価理由と、FAILの場合は具体的な修正指示"
}

STEP 4: JSONパース

  1. Evaluatorの後に「コード」ノードを追加し、名前をJSON_Parserとします。
    • 入力変数: json_str(String)← 値に {{#Evaluator.text#}} を設定
    • 出力変数: status(String), reason(String)

コード (Python 3):

import json
import re

def main(json_str: str) -> dict:
    try:
        # Markdownコードブロック記法への対策
        clean_str = re.sub(r'```json\s*|\s*```', '', json_str).strip()
        data = json.loads(clean_str)
        return {
            "status": data.get("status", "FAIL"),
            "reason": data.get("reason", "Unknown Error")
        }
    except Exception as e:
        return {
            "status": "FAIL", 
            "reason": f"JSON Parse Error: {str(e)}"
        }

STEP 5: 条件分岐

  1. JSON_Parserの後に「IF/ELSE」ノードを追加します。
    • 条件: 変数 {{#JSON_Parser.status#}}PASS である場合。

STEP 6: 最適化とループ制御

  1. True (PASS) の場合:
    • 「ループ完了」ノードを追加。
  2. False (FAIL) の場合:
    • 「LLM」ノードを追加し、名前をOptimizerとします。
    • モデル: 「Gemini 2.5 Pro」

システムプロンプト:

あなたはライターです。編集者の指摘に従って、記事をリライトしてください。

ユーザープロンプト:

元の依頼: {{#sys.query#}}
現在の原稿: {{#Loop.current_draft#}}
編集者の指摘: {{#JSON_Parser.reason#}}

指摘を反映した完成稿のみを出力してください。

Optimizerの後に「変数代入」ノードを置き、{{#Loop.current_draft#}}{{#Optimizer.text#}} (リライト後の文章) で上書き更新します。

STEP 7: 最終出力

反復ノードを抜け出した後に、「回答」ノードを配置し、{{#Loop.current_draft#}} を表示します。

これで、「最大3回まで、ダメ出しがある限りループして品質を高め続ける」ワークフローが完成しました。

実践2:Agentアプローチ(自律的)

次は、同じことをDifyの「エージェント」ノードを使って実装します。 こちらはフローチャートでループを組むのではなく、評価ツールをAIに渡し、AI自身に納得いくまで修正させるアプローチです。

アーキテクチャ

graph LR
    User["スタート"] --> Agent["エージェント"]
    
    subgraph "External Tool(ワークフロー)"
        ToolInput["Start"] --> ToolLLM["LLM<br>Evaluator"]
        ToolLLM --> ToolOutput["出力"]
    end

    Agent --> ToolInput
    ToolOutput --> Agent
    Agent -->|"自律判断<br>リライト or 終了"| Agent
    Agent --> Output[回答]

評価ツールの作成

まず、評価ロジックを独立したツールとして作成します。Agentが正確に判断できるよう、構造化されたデータ(statusreason)を返すように作り込みます。

  1. Difyで「最初から作成」→ 「ワークフロー」を選択します
    • 名前: Article Quality Check Tool
    • 「ユーザー入力(元の開始ノード)」を選択
  2. 「スタート」ノード
    • 入力変数: draft(Paragraph, 必須), requirements(Paragraph, 必須)
  3. 「LLM」ノードを追加し、名前をEvaluatorとします。
    • モデル: 「Gemini 2.5 Flash」

システムプロンプト:

あなたは記事の品質チェッカーです。

ユーザープロンプト:

要件: {{#requirements#}}
原稿: {{#draft#}}

以下のJSONフォーマットのみを返してください:
{
  "status": "PASS" または "FAIL",
  "reason": "評価理由と、FAILの場合は具体的な修正指示"
}
  1. 「コード」ノードを追加、名前をJSON_Parserとします。
    • LLMの出力をパースします。実践1と同じPythonコードを使用してください。
    • 入力変数: json_str(String)← 値に {{#Evaluator.text#}} を設定
    • 出力変数: status(String), reason(String)
  2. 「出力」ノード
    • 出力変数:
      • status (String) ← 値: {{#JSON_Parser.status#}}
      • reason (String) ← 値: {{#JSON_Parser.reason#}}
  3. 画面右上の「公開する」→「ワークフローをツールとして公開する」をクリック。
    • ツールコールの名前: check_article_quality
    • 説明: Checks if the draft meets requirements. Returns status (PASS/FAIL) and reason.

チャットフローでのAgent実装

ここからが本番です。Workflow版とは異なり、フロー自体は非常にシンプルです。

  1. チャットフローを新規作成します
    • 名前: Autonomous Writer Agent
  2. 開始ノードの後ろに「エージェント」ノードを追加、名前をAgentとします。
    • エージェンティック戦略: 「FunctionCalling」
    • MODEL: 「Gemini 2.5 Pro」 (Function Calling性能が高いモデル必須)
    • TOOL LIST: 先ほど作成した「Article Quality Check Tool」を追加します。
    • MAXIMUM ITERATIONS: 6

INSTRUCTION:

あなたは妥協を許さないプロのライターです。
以下の手順を自律的に実行し、最高品質の記事を作成してください。

1. ユーザーの依頼に基づいて初稿を執筆する。
2. `check_article_quality` ツールを使用して、自分の記事を評価する。
3. ツールの結果(`status`)を確認する:
   - "FAIL" の場合: `reason` を読んで記事を修正し、**再度**ツールで評価を受ける。
   - "PASS" の場合: その記事を最終回答とする。
4. "PASS" が出るまで最大5回繰り返す。

Query:

{{#sys.query#}}

最後に「回答」ノードを配置し、{{#Agent.text#}} を表示します。

比較と考察

観点 Workflow Agent
制御性 。設計図通りに動く。 。AIの判断に委ねられる。
予測可能性 。コストや時間が読める。 。ループ回数により変動する。
実装 ロジックの組み立てが必要。 プロンプトとツールの定義が主。
自己決定 なし。 あり(終了タイミング等を決定)。


Anthropicの記事にある通り、ビジネスアプリケーションでは予測可能性が重視されるため、まずはWorkflowから構築し、必要に応じてAgent的な自律性を取り入れるのが成功への近道です。

アプリケーションへの統合

最後に、Difyで作成したワークフローは、APIとして即座に利用可能です。これにより、ノーコードで検証したロジックを、そのまま自社プロダクトのバックエンドとして組み込むことができます。

例えば、今回作成したワークフローは、以下のようなリクエストで実行できます。

curl -X POST 'https://{ENDPOINT}/v1/chat-messages' \
--header 'Authorization: Bearer {API_KEY}' \
--header 'Content-Type: application/json' \
--data-raw '{
  "inputs": {},
  "query": "AIについて短くまとめて",
  "user": "abc-123",
  "response_mode": "blocking"
}'

レスポンス例:

{
  "event": "message",
  "task_id": "f6a55377-9e2f-47c3-b20a-6ca35122fb5b",
  "id": "3c8aae35-b1f5-486a-bdf6-9ac920747f56",
  "message_id": "3c8aae35-b1f5-486a-bdf6-9ac920747f56",
  "conversation_id": "3397c79e-f5a4-44bf-9142-c36381118cd6",
  "mode": "advanced-chat",
  "answer": "AI(人工知能)とは、学習・推論・判断といった...",
  "created_at": 1766620800
}

このように、「Difyでロジックをカプセル化し、アプリからはAPIを叩くだけ」という疎結合なアーキテクチャは、変化の速いAI開発において非常に有効です。ぜひ、皆さんの現場でもEffective Agents(効果的なAIエージェント)を実装してみてください。