DifyでLLMが議論するAI agentを構築してみた
はじめに
この記事では、Difyプラットフォームを使って構築したITプロジェクトというAI Agentについて解説します。このAI Agentは、5つの異なる役割を持つAIが協力して、ITプロジェクトの課題について多角的な議論を行うシステムです。
実際に動かしている様子システムの概要
このシステムの核となるアイデアは非常にシンプルです。現実のIT開発チームと同様に、異なる専門性や視点を持つメンバーが集まって議論することで、より優れた意思決定ができるようにしています。
以下の5つの役割を持つAIエージェントが登場します:
- ファシリテーター: ユーザーの質問を理解し、最適な専門家AIに振り分ける司会者的役割
- プロジェクトマネージャー(PM): ビジネス価値やROIを重視した意見を提供
- リードエンジニア: 技術的実現可能性や実装方法の専門家
- QAエンジニア: 品質保証やテスト計画の観点から意見を提供
- ユーザー代表: エンドユーザーの視点から使いやすさや価値を評価
システムの技術構成
1. 使用しているAIモデル
このシステムでは、異なる役割に適したAIモデルを使い分けています:
- Claude 3.7 Sonnet: ファシリテーター、PMなど複雑な役割の対話に使用
- GPT-4o Mini: 質問分類や要約など、比較的シンプルなタスクに使用
両モデルの特性を活かして、コスト効率と性能のバランスを取っています。
2. ワークフローの構造
このAI Agentは以下のような流れで動作しています:
- ユーザーからの質問を受け取る
- ファシリテーターAIが質問を分析
- 質問分類器が適切な専門家AIを選択
- 選ばれた専門家AIが回答を生成
- 各専門家の意見を集約して表示
- 履歴を保存してループに戻る
なお、Loopを使って複数回の対話を可能にしている点です。これにより、継続的なディスカッションが実現できます。
主要部分の解説
1. 各AIの役割定義
各LLMでAIエージェントの「人格」や「視点」が明確に定義されています。例えば、プロジェクトマネージャーのプロンプト設定を見てみましょう:
あなたはプロダクトマネージャー(PM)として回答してください。
常に以下の特性を持って発言してください:
- ビジネス価値とROIを最重視する
- 前向きで建設的な提案を行う
- 市場トレンドやユーザーニーズを理解している
- 長期的なビジョンと戦略を持っている
- データに基づいた意思決定を重視する
議論においては、新機能の可能性に焦点を当て、競合との差別化ポイントを強調し、ビジネス成長に貢献する側面を積極的に提案してください。技術的な制約よりも、まずは「何ができるか」「何をすべきか」の視点から発言してください。
このように、各AIには明確な「キャラクター設定」が与えられており、専門性の異なる意見を引き出せるようになっています。
2. 質問の振り分けロジック
ファシリテーターは、ユーザーの質問を分析し、最も適切な専門家に振り分けます。
あなたはIT開発プロジェクトの議論ファシリテーターです。ユーザーの質問や提案に基づいて、適切な役割の専門家に質問や議題を振り分けてください。
【重要】必ず以下の4つの役割のうち1つだけを選び、その役割に直接質問や議題を提示してください。
【重要】会話履歴を参考に、意見を聞き先の役割に偏りがでないようにバランスよくファシリテートしてください。
[プロダクトマネージャー(PM)]
- ビジネス価値、市場戦略、ロードマップに関する質問
- 新機能の提案や優先順位付けに関する議題
- 収益性やROIに関する内容
- ビジョンや長期計画に関する相談
[リードエンジニア]
- 技術的実現可能性や実装方法に関する質問
- システムアーキテクチャや技術選定に関する議題
- 開発工数や技術的負債に関する内容
- スケーラビリティやパフォーマンスに関する技術的相談
[QAエンジニア]
- 品質保証やテスト計画に関する質問
- バグの可能性や品質リスクに関する議題
- セキュリティや安定性に関する内容
- エッジケースやテスト範囲に関する相談
[ユーザー代表]
- ユーザー体験やユーザビリティに関する質問
- エンドユーザーの視点からの機能評価に関する議題
- 実際の使用シナリオや顧客ニーズに関する内容
- UI/UXデザインや使いやすさに関する相談
【出力形式】
役割: [選択した役割の名前]
質問: [選択した役割に向けた具体的な質問や議題]
ユーザーの入力から適切な役割を判断できない場合は、内容に最も近い専門家を選んでください。前回までの会話の流れも考慮し、異なる役割にも質問が振られるよう配慮してください。
ファシリテータは、質問のコンテキストを理解して、最適な専門家を選択します。例えば、コスト削減についての質問はPMに、技術的な実装についての質問はリードエンジニアに振り分けられる想定です。
3. 会話履歴の管理
会話の流れを保存して、一貫性のある対話を実現しています。
この部分は、会話履歴をlog_array
変数に追加しており、過去の対話を参照できるようにしています。
YAMLファイル
このAIエージェントのYAMLファイルをエクスポートしました!「DSLをインポート」で皆さんのDifyに読み込めます。ぜひ参考にしてください。
このAIエージェントの定義ファイル
app:
description: ''
icon: 🤖
icon_background: '#FFEAD5'
mode: advanced-chat
name: ITプロジェクト
use_icon_as_answer_icon: false
dependencies:
- current_identifier: null
type: marketplace
value:
marketplace_plugin_unique_identifier: langgenius/openai:0.0.15@5f610705111ac49d93c4ea5eaac690abaeb3b4a87599c48e5165b549b4c47f10
- current_identifier: null
type: marketplace
value:
marketplace_plugin_unique_identifier: langgenius/anthropic:0.0.13@3c4d4e14652baa9ce1f10e934da9995b3cde0f2ec94ee6638b73fecf1ed53288
kind: app
version: 0.1.5
workflow:
conversation_variables:
- description: ''
id: bfa88f66-efd2-4238-a635-4f89b952ad6a
name: log_array
selector:
- conversation
- log_array
value: []
value_type: array[string]
environment_variables: []
features:
file_upload:
allowed_file_extensions:
- .JPG
- .JPEG
- .PNG
- .GIF
- .WEBP
- .SVG
allowed_file_types:
- image
allowed_file_upload_methods:
- local_file
- remote_url
enabled: false
fileUploadConfig:
audio_file_size_limit: 50
batch_count_limit: 5
file_size_limit: 15
image_file_size_limit: 10
video_file_size_limit: 100
workflow_file_upload_limit: 10
image:
enabled: false
number_limits: 3
transfer_methods:
- local_file
- remote_url
number_limits: 3
opening_statement: ''
retriever_resource:
enabled: false
sensitive_word_avoidance:
enabled: false
speech_to_text:
enabled: false
suggested_questions: []
suggested_questions_after_answer:
enabled: false
text_to_speech:
enabled: false
language: ''
voice: ''
graph:
edges:
- data:
isInIteration: false
isInLoop: true
loop_id: '1743813274053'
sourceType: loop-start
targetType: llm
id: 1743813274053start-source-1743813343457-target
selected: false
source: 1743813274053start
sourceHandle: source
target: '1743813343457'
targetHandle: target
type: custom
zIndex: 1002
- data:
isInIteration: false
isInLoop: true
loop_id: '1743813274053'
sourceType: question-classifier
targetType: llm
id: 1743813387618-1-1743813533524-target
source: '1743813387618'
sourceHandle: '1'
target: '1743813533524'
targetHandle: target
type: custom
zIndex: 1002
- data:
isInIteration: false
isInLoop: true
loop_id: '1743813274053'
sourceType: question-classifier
targetType: llm
id: 1743813387618-2-1743813538562-target
source: '1743813387618'
sourceHandle: '2'
target: '1743813538562'
targetHandle: target
type: custom
zIndex: 1002
- data:
isInIteration: false
isInLoop: true
loop_id: '1743813274053'
sourceType: question-classifier
targetType: llm
id: 1743813387618-1743813473559-1743813542420-target
source: '1743813387618'
sourceHandle: '1743813473559'
target: '1743813542420'
targetHandle: target
type: custom
zIndex: 1002
- data:
isInIteration: false
isInLoop: true
loop_id: '1743813274053'
sourceType: question-classifier
targetType: llm
id: 1743813387618-1743813503448-1743813545102-target
source: '1743813387618'
sourceHandle: '1743813503448'
target: '1743813545102'
targetHandle: target
type: custom
zIndex: 1002
- data:
isInIteration: false
isInLoop: true
loop_id: '1743813274053'
sourceType: llm
targetType: template-transform
id: 1743813533524-source-1743819934143-target
source: '1743813533524'
sourceHandle: source
target: '1743819934143'
targetHandle: target
type: custom
zIndex: 1002
- data:
isInIteration: false
isInLoop: true
loop_id: '1743813274053'
sourceType: llm
targetType: template-transform
id: 1743813538562-source-1743820010384-target
source: '1743813538562'
sourceHandle: source
target: '1743820010384'
targetHandle: target
type: custom
zIndex: 1002
- data:
isInIteration: false
isInLoop: true
loop_id: '1743813274053'
sourceType: llm
targetType: template-transform
id: 1743813542420-source-1743820065116-target
source: '1743813542420'
sourceHandle: source
target: '1743820065116'
targetHandle: target
type: custom
zIndex: 1002
- data:
isInIteration: false
isInLoop: true
loop_id: '1743813274053'
sourceType: llm
targetType: template-transform
id: 1743813545102-source-1743820115180-target
source: '1743813545102'
sourceHandle: source
target: '1743820115180'
targetHandle: target
type: custom
zIndex: 1002
- data:
isInIteration: false
isInLoop: true
loop_id: '1743813274053'
sourceType: template-transform
targetType: variable-aggregator
id: 1743819934143-source-1743819640139-target
source: '1743819934143'
sourceHandle: source
target: '1743819640139'
targetHandle: target
type: custom
zIndex: 1002
- data:
isInIteration: false
isInLoop: true
loop_id: '1743813274053'
sourceType: template-transform
targetType: variable-aggregator
id: 1743820010384-source-1743819640139-target
source: '1743820010384'
sourceHandle: source
target: '1743819640139'
targetHandle: target
type: custom
zIndex: 1002
- data:
isInIteration: false
isInLoop: true
loop_id: '1743813274053'
sourceType: template-transform
targetType: variable-aggregator
id: 1743820065116-source-1743819640139-target
source: '1743820065116'
sourceHandle: source
target: '1743819640139'
targetHandle: target
type: custom
zIndex: 1002
- data:
isInIteration: false
isInLoop: true
loop_id: '1743813274053'
sourceType: template-transform
targetType: variable-aggregator
id: 1743820115180-source-1743819640139-target
source: '1743820115180'
sourceHandle: source
target: '1743819640139'
targetHandle: target
type: custom
zIndex: 1002
- data:
isInLoop: false
sourceType: assigner
targetType: loop
id: 1743820282903-source-1743813274053-target
source: '1743820282903'
sourceHandle: source
target: '1743813274053'
targetHandle: target
type: custom
zIndex: 0
- data:
isInIteration: false
isInLoop: true
loop_id: '1743813274053'
sourceType: assigner
targetType: question-classifier
id: 1743814532457-source-1743813387618-target
source: '1743814532457'
sourceHandle: source
target: '1743813387618'
targetHandle: target
type: custom
zIndex: 1002
- data:
isInIteration: false
isInLoop: true
loop_id: '1743813274053'
sourceType: variable-aggregator
targetType: assigner
id: 1743819640139-source-1743820695464-target
source: '1743819640139'
sourceHandle: source
target: '1743820695464'
targetHandle: target
type: custom
zIndex: 1002
- data:
isInIteration: false
isInLoop: true
loop_id: '1743813274053'
sourceType: assigner
targetType: llm
id: 1743820695464-source-1743821775718-target
source: '1743820695464'
sourceHandle: source
target: '1743821775718'
targetHandle: target
type: custom
zIndex: 1002
- data:
isInIteration: false
isInLoop: true
loop_id: '1743813274053'
sourceType: llm
targetType: answer
id: 1743821775718-source-1743814982892-target
source: '1743821775718'
sourceHandle: source
target: '1743814982892'
targetHandle: target
type: custom
zIndex: 1002
- data:
isInIteration: false
isInLoop: false
sourceType: loop
targetType: llm
id: 1743813274053-source-1743830200503-target
source: '1743813274053'
sourceHandle: source
target: '1743830200503'
targetHandle: target
type: custom
zIndex: 0
- data:
isInIteration: false
isInLoop: false
sourceType: llm
targetType: answer
id: 1743830200503-source-1743830296471-target
source: '1743830200503'
sourceHandle: source
target: '1743830296471'
targetHandle: target
type: custom
zIndex: 0
- data:
isInLoop: false
sourceType: start
targetType: template-transform
id: 1743813238501-source-1743830928763-target
source: '1743813238501'
sourceHandle: source
target: '1743830928763'
targetHandle: target
type: custom
zIndex: 0
- data:
isInLoop: false
sourceType: template-transform
targetType: assigner
id: 1743830928763-source-1743820282903-target
source: '1743830928763'
sourceHandle: source
target: '1743820282903'
targetHandle: target
type: custom
zIndex: 0
- data:
isInIteration: false
isInLoop: true
loop_id: '1743813274053'
sourceType: llm
targetType: template-transform
id: 1743813343457-source-1743831085445-target
source: '1743813343457'
sourceHandle: source
target: '1743831085445'
targetHandle: target
type: custom
zIndex: 1002
- data:
isInIteration: false
isInLoop: true
loop_id: '1743813274053'
sourceType: template-transform
targetType: assigner
id: 1743831085445-source-1743814532457-target
source: '1743831085445'
sourceHandle: source
target: '1743814532457'
targetHandle: target
type: custom
zIndex: 1002
nodes:
- data:
desc: ''
selected: false
title: 開始
type: start
variables: []
height: 54
id: '1743813238501'
position:
x: 279.3816393220244
y: 52
positionAbsolute:
x: 279.3816393220244
y: 52
selected: false
sourcePosition: right
targetPosition: left
type: custom
width: 244
- data:
break_conditions: []
desc: ''
error_handle_mode: terminated
height: 472
logical_operator: and
loop_count: 5
selected: false
start_node_id: 1743813274053start
title: ループ
type: loop
width: 1719
height: 472
id: '1743813274053'
position:
x: 325
y: 160.56913160698082
positionAbsolute:
x: 325
y: 160.56913160698082
selected: false
sourcePosition: right
targetPosition: left
type: custom
width: 1719
zIndex: 1
- data:
desc: ''
isInLoop: true
selected: false
title: ''
type: loop-start
draggable: false
height: 48
id: 1743813274053start
parentId: '1743813274053'
position:
x: 24
y: 68
positionAbsolute:
x: 349
y: 228.56913160698082
selectable: false
sourcePosition: right
targetPosition: left
type: custom-loop-start
width: 44
zIndex: 1002
- data:
context:
enabled: false
variable_selector: []
desc: ''
isInIteration: false
isInLoop: true
loop_id: '1743813274053'
model:
completion_params: {}
mode: chat
name: claude-3-7-sonnet-20250219
provider: langgenius/anthropic/anthropic
prompt_template:
- id: ed50054f-6ef8-4b72-ab3f-32e8800bbd13
role: system
text: 'あなたはIT開発プロジェクトの議論ファシリテーターです。ユーザーの質問や提案に基づいて、適切な役割の専門家に質問や議題を振り分けてください。
【重要】必ず以下の4つの役割のうち1つだけを選び、その役割に直接質問や議題を提示してください。
【重要】会話履歴を参考に、意見を聞き先の役割に偏りがでないようにバランスよくファシリテートしてください。
[プロダクトマネージャー(PM)]
- ビジネス価値、市場戦略、ロードマップに関する質問
- 新機能の提案や優先順位付けに関する議題
- 収益性やROIに関する内容
- ビジョンや長期計画に関する相談
[リードエンジニア]
- 技術的実現可能性や実装方法に関する質問
- システムアーキテクチャや技術選定に関する議題
- 開発工数や技術的負債に関する内容
- スケーラビリティやパフォーマンスに関する技術的相談
[QAエンジニア]
- 品質保証やテスト計画に関する質問
- バグの可能性や品質リスクに関する議題
- セキュリティや安定性に関する内容
- エッジケースやテスト範囲に関する相談
[ユーザー代表]
- ユーザー体験やユーザビリティに関する質問
- エンドユーザーの視点からの機能評価に関する議題
- 実際の使用シナリオや顧客ニーズに関する内容
- UI/UXデザインや使いやすさに関する相談
【出力形式】
役割: [選択した役割の名前]
質問: [選択した役割に向けた具体的な質問や議題]
ユーザーの入力から適切な役割を判断できない場合は、内容に最も近い専門家を選んでください。前回までの会話の流れも考慮し、異なる役割にも質問が振られるよう配慮してください。'
- id: 83b1f4da-9677-4239-bb03-310c684c81cf
role: user
text: '{{#sys.query#}}
---
# 会話履歴
{{#conversation.log_array#}}'
selected: false
title: ファシリテータ
type: llm
variables: []
vision:
enabled: false
height: 90
id: '1743813343457'
parentId: '1743813274053'
position:
x: 88
y: 65
positionAbsolute:
x: 413
y: 225.56913160698082
selected: false
sourcePosition: right
targetPosition: left
type: custom
width: 244
zIndex: 1002
- data:
classes:
- id: '1'
name: プロジェクトマネージャ(PM)
- id: '2'
name: リードエンジニア
- id: '1743813473559'
name: QAエンジニア
- id: '1743813503448'
name: ユーザ代表
desc: ''
instruction: ''
instructions: ''
isInIteration: false
isInLoop: true
loop_id: '1743813274053'
model:
completion_params:
temperature: 0.7
mode: chat
name: gpt-4o-mini
provider: langgenius/openai/openai
query_variable_selector:
- '1743813343457'
- text
selected: false
title: 質問分類器
topics: []
type: question-classifier
vision:
enabled: false
height: 248
id: '1743813387618'
parentId: '1743813274053'
position:
x: 356.49030914390505
y: 65
positionAbsolute:
x: 681.490309143905
y: 225.56913160698082
selected: false
sourcePosition: right
targetPosition: left
type: custom
width: 244
zIndex: 1002
- data:
context:
enabled: false
variable_selector: []
desc: ''
isInIteration: false
isInLoop: true
loop_id: '1743813274053'
model:
completion_params: {}
mode: chat
name: claude-3-7-sonnet-20250219
provider: langgenius/anthropic/anthropic
prompt_template:
- id: f092b746-6ffd-4186-9e36-5e15aaa15bfc
role: system
text: 'あなたはプロダクトマネージャー(PM)として回答してください。
常に以下の特性を持って発言してください:
- ビジネス価値とROIを最重視する
- 前向きで建設的な提案を行う
- 市場トレンドやユーザーニーズを理解している
- 長期的なビジョンと戦略を持っている
- データに基づいた意思決定を重視する
議論においては、新機能の可能性に焦点を当て、競合との差別化ポイントを強調し、ビジネス成長に貢献する側面を積極的に提案してください。技術的な制約よりも、まずは「何ができるか」「何をすべきか」の視点から発言してください。'
- id: e7bac3fb-8002-434b-ab7a-bc7d9196f282
role: user
text: '{{#1743813343457.text#}}'
selected: false
title: プロジェクトマネージャ(PM)
type: llm
variables: []
vision:
enabled: false
height: 90
id: '1743813533524'
parentId: '1743813274053'
position:
x: 636.4379198395859
y: 65
positionAbsolute:
x: 961.4379198395859
y: 225.56913160698082
selected: true
sourcePosition: right
targetPosition: left
type: custom
width: 244
zIndex: 1002
- data:
context:
enabled: false
variable_selector: []
desc: ''
isInIteration: false
isInLoop: true
loop_id: '1743813274053'
model:
completion_params: {}
mode: chat
name: claude-3-7-sonnet-20250219
provider: langgenius/anthropic/anthropic
prompt_template:
- id: 766ebd51-52c6-4a36-9090-f64965fe81cb
role: system
text: 'あなたはリードエンジニアとして回答してください。
常に以下の特性を持って発言してください:
- 技術的な実現可能性を客観的に評価する
- 開発工数とリソースを現実的に見積もる
- 技術的課題やリスクを明確に指摘する
- 代替案や技術的解決策を提案する
- コードの品質と保守性を重視する
議論においては、PMやユーザー代表の提案に対して技術的観点からフィードバックを行い、実装上の課題や懸念点を具体的に指摘してください。同時に、より効率的な代替案や技術的な改善策も提案してください。'
- id: 43e8743e-d036-4809-b2a0-26c3f9efa4c8
role: user
text: '{{#1743813343457.text#}}'
selected: false
title: リードエンジニア
type: llm
variables: []
vision:
enabled: false
height: 90
id: '1743813538562'
parentId: '1743813274053'
position:
x: 635.8160337142124
y: 163.63509000055961
positionAbsolute:
x: 960.8160337142124
y: 324.20422160754043
selected: false
sourcePosition: right
targetPosition: left
type: custom
width: 244
zIndex: 1002
- data:
context:
enabled: false
variable_selector: []
desc: ''
isInIteration: false
isInLoop: true
loop_id: '1743813274053'
model:
completion_params: {}
mode: chat
name: claude-3-7-sonnet-20250219
provider: langgenius/anthropic/anthropic
prompt_template:
- id: 7502238c-1ac7-46ab-8f85-e6ebf0c145d6
role: system
text: 'あなたはQAエンジニアとして回答してください。
常に以下の特性を持って発言してください:
- 品質とテスト容易性を最重視する
- 潜在的なバグやエッジケースを先回りして指摘する
- システムの安定性とセキュリティに敏感である
- ユーザー体験の一貫性を守る視点を持つ
- テスト計画と品質保証の観点から発言する
議論においては、新機能や変更に伴うリスクを慎重に評価し、品質担保のために必要な作業や懸念点を指摘してください。単に問題点を挙げるだけでなく、品質を向上させるための具体的な提案も行ってください。'
- id: 8fe53748-c46a-434b-bd4d-fc18826a8fff
role: user
text: '{{#1743813343457.text#}}'
selected: false
title: QAエンジニア
type: llm
variables: []
vision:
enabled: false
height: 90
id: '1743813542420'
parentId: '1743813274053'
position:
x: 633.1286927261056
y: 258.0404993336798
positionAbsolute:
x: 958.1286927261056
y: 418.6096309406606
selected: false
sourcePosition: right
targetPosition: left
type: custom
width: 244
zIndex: 1002
- data:
context:
enabled: false
variable_selector: []
desc: ''
isInIteration: false
isInLoop: true
loop_id: '1743813274053'
model:
completion_params: {}
mode: chat
name: claude-3-7-sonnet-20250219
provider: langgenius/anthropic/anthropic
prompt_template:
- id: 585adfe4-2678-4047-9320-1130d636de01
role: system
text: 'あなたはエンドユーザーの代表として回答してください。
常に以下の特性を持って発言してください:
- 実際のユーザー視点で機能を評価する
- 技術的な専門用語を避け、平易な言葉で話す
- 使いやすさと価値を最重視する
- 具体的な使用シナリオに基づいて意見を述べる
- 時に感情的な反応(興奮や不満など)も率直に表現する
議論においては、提案された機能がどのように自分の生活や業務に役立つか(または役立たないか)を具体的に述べ、実際のユーザーが何を求めているのかの視点を提供してください。競合製品との比較や、実体験に基づく意見も積極的に共有してください。'
- id: 4c4c8841-ffba-4793-9e29-09d79344eb68
role: user
text: '{{#1743813343457.text#}}'
selected: false
title: ユーザ代表
type: llm
variables: []
vision:
enabled: false
height: 90
id: '1743813545102'
parentId: '1743813274053'
position:
x: 629.6648520558845
y: 356.35659024398547
positionAbsolute:
x: 954.6648520558845
y: 516.9257218509663
selected: false
sourcePosition: right
targetPosition: left
type: custom
width: 244
zIndex: 1002
- data:
desc: ''
isInIteration: false
isInLoop: true
items:
- input_type: variable
operation: append
value:
- '1743813343457'
- text
variable_selector:
- conversation
- log_array
write_mode: over-write
loop_id: '1743813274053'
selected: false
title: 会話記録 ファシリテータ
type: assigner
version: '2'
height: 88
id: '1743814532457'
parentId: '1743813274053'
position:
x: 89.0515103233405
y: 253.7263028667998
positionAbsolute:
x: 414.0515103233405
y: 414.29543447378063
selected: false
sourcePosition: right
targetPosition: left
type: custom
width: 244
zIndex: 1002
- data:
answer: '思考過程 {{#1743813387618.class_name#}}:
{{#1743821775718.text#}}
'
desc: ''
isInIteration: false
isInLoop: true
loop_id: '1743813274053'
selected: false
title: 思考過程
type: answer
variables: []
height: 138
id: '1743814982892'
parentId: '1743813274053'
position:
x: 1444.917928633726
y: 168.01310334047383
positionAbsolute:
x: 1769.917928633726
y: 328.58223494745465
selected: false
sourcePosition: right
targetPosition: left
type: custom
width: 244
zIndex: 1002
- data:
desc: ''
isInIteration: false
isInLoop: true
loop_id: '1743813274053'
output_type: string
selected: false
title: 変数集約器
type: variable-aggregator
variables:
- - '1743819934143'
- output
- - '1743820010384'
- output
- - '1743820065116'
- output
- - '1743820115180'
- output
height: 174
id: '1743819640139'
parentId: '1743813274053'
position:
x: 1186.4803153091948
y: 67.18965534891635
positionAbsolute:
x: 1511.4803153091948
y: 227.75878695589716
selected: false
sourcePosition: right
targetPosition: left
type: custom
width: 244
zIndex: 1002
- data:
desc: ''
isInIteration: false
isInLoop: true
loop_id: '1743813274053'
selected: false
template: "\r\n---\r\n\r\n## プロジェクトマネージャ(PM)\r\n\r\n{{ LLM_output }}\r\n\r\
\n"
title: プロジェクトマネージャ(PM) ANS
type: template-transform
variables:
- value_selector:
- '1743813533524'
- text
variable: LLM_output
height: 54
id: '1743819934143'
parentId: '1743813274053'
position:
x: 912.98160333654
y: 65
positionAbsolute:
x: 1237.98160333654
y: 225.56913160698082
selected: false
sourcePosition: right
targetPosition: left
type: custom
width: 244
zIndex: 1002
- data:
desc: ''
isInIteration: false
isInLoop: true
loop_id: '1743813274053'
selected: false
template: "\r\n---\r\n\r\n## リードエンジニア\r\n\r\n{{ LLM_output }}\r\n\r\n"
title: リードエンジニア ANS
type: template-transform
variables:
- value_selector:
- '1743813538562'
- text
variable: LLM_output
height: 54
id: '1743820010384'
parentId: '1743813274053'
position:
x: 909.9990770642044
y: 163.63509000055956
positionAbsolute:
x: 1234.9990770642044
y: 324.2042216075404
selected: false
sourcePosition: right
targetPosition: left
type: custom
width: 244
zIndex: 1002
- data:
desc: ''
isInIteration: false
isInLoop: true
loop_id: '1743813274053'
selected: false
template: "\r\n---\r\n\r\n## QAエンジニア\r\n\r\n{{ LLM_output }}\r\n\r\n"
title: QAエンジニア ANS
type: template-transform
variables:
- value_selector:
- '1743813542420'
- text
variable: LLM_output
height: 54
id: '1743820065116'
parentId: '1743813274053'
position:
x: 905.2361452469338
y: 258.0404993336797
positionAbsolute:
x: 1230.2361452469338
y: 418.6096309406605
selected: false
sourcePosition: right
targetPosition: left
type: custom
width: 244
zIndex: 1002
- data:
desc: ''
isInIteration: false
isInLoop: true
loop_id: '1743813274053'
selected: false
template: "\r\n---\r\n\r\n## ユーザ代表\r\n\r\n{{ LLM_output }}\r\n\r\n"
title: ユーザ代表 ANS
type: template-transform
variables:
- value_selector:
- '1743813545102'
- text
variable: LLM_output
height: 54
id: '1743820115180'
parentId: '1743813274053'
position:
x: 903.0437362756113
y: 356.35659024398547
positionAbsolute:
x: 1228.0437362756113
y: 516.9257218509663
selected: false
sourcePosition: right
targetPosition: left
type: custom
width: 244
zIndex: 1002
- data:
desc: ''
items:
- input_type: variable
operation: append
value:
- '1743830928763'
- output
variable_selector:
- conversation
- log_array
write_mode: over-write
selected: false
title: 会話記録 開始
type: assigner
version: '2'
height: 88
id: '1743820282903'
position:
x: 791.208374235543
y: 52
positionAbsolute:
x: 791.208374235543
y: 52
selected: false
sourcePosition: right
targetPosition: left
type: custom
width: 244
- data:
desc: ''
isInIteration: false
isInLoop: true
items:
- input_type: variable
operation: append
value:
- '1743819640139'
- output
variable_selector:
- conversation
- log_array
write_mode: over-write
loop_id: '1743813274053'
selected: false
title: 会話記録 専門家の意見
type: assigner
version: '2'
height: 88
id: '1743820695464'
parentId: '1743813274053'
position:
x: 1180.2436763240407
y: 252.54180449024142
positionAbsolute:
x: 1505.2436763240407
y: 413.11093609722224
selected: false
sourcePosition: right
targetPosition: left
type: custom
width: 244
zIndex: 1002
- data:
context:
enabled: false
variable_selector: []
desc: ''
isInIteration: false
isInLoop: true
loop_id: '1743813274053'
model:
completion_params: {}
mode: chat
name: gpt-4o-mini
provider: langgenius/openai/openai
prompt_template:
- id: c24280e5-6f61-4138-b4f1-439e1767a36b
role: system
text: メッセージを100文字にサマリして
- id: a7907ab6-f740-4181-8ce1-e79075227d43
role: user
text: '{{#1743819640139.output#}}'
selected: false
title: 100文字にサマリ
type: llm
variables: []
vision:
enabled: false
height: 90
id: '1743821775718'
parentId: '1743813274053'
position:
x: 1444.8676871932632
y: 69.79185388794247
positionAbsolute:
x: 1769.8676871932632
y: 230.36098549492328
selected: false
sourcePosition: right
targetPosition: left
type: custom
width: 244
zIndex: 1002
- data:
context:
enabled: false
variable_selector: []
desc: ''
model:
completion_params: {}
mode: chat
name: claude-3-7-sonnet-20250219
provider: langgenius/anthropic/anthropic
prompt_template:
- id: 31fe52bf-27bd-468c-8c06-7e4dee7e1f46
role: system
text: メッセージをサマリしてください。
- id: 3f0e71bf-9505-4f79-adfe-293ab8c487da
role: user
text: '{{#conversation.log_array#}}'
selected: false
title: サマリ
type: llm
variables: []
vision:
enabled: false
height: 90
id: '1743830200503'
position:
x: 325
y: 648.2750498965903
positionAbsolute:
x: 325
y: 648.2750498965903
selected: false
sourcePosition: right
targetPosition: left
type: custom
width: 244
- data:
answer: '{{#1743830200503.text#}}'
desc: ''
selected: false
title: 回答 サマリ
type: answer
variables: []
height: 104
id: '1743830296471'
position:
x: 592.6771604896334
y: 648.2750498965903
positionAbsolute:
x: 592.6771604896334
y: 648.2750498965903
selected: false
sourcePosition: right
targetPosition: left
type: custom
width: 244
- data:
desc: ''
selected: false
template: "## 問い\r\n\r\n{{ Query }}\r\n\r\n"
title: 開始 ANS
type: template-transform
variables:
- value_selector:
- sys
- query
variable: Query
height: 54
id: '1743830928763'
position:
x: 536.4482576135123
y: 52
positionAbsolute:
x: 536.4482576135123
y: 52
selected: false
sourcePosition: right
targetPosition: left
type: custom
width: 244
- data:
desc: ''
isInIteration: false
isInLoop: true
loop_id: '1743813274053'
selected: false
template: "\r\n---\r\n\r\n## ファシリテータ\r\n\r\n{{ LLM_output }}\r\n\r\n"
title: ファシリテータ ANS
type: template-transform
variables:
- value_selector:
- '1743813343457'
- text
variable: LLM_output
height: 54
id: '1743831085445'
parentId: '1743813274053'
position:
x: 92.08777138125606
y: 179.00503301690287
positionAbsolute:
x: 417.08777138125606
y: 339.5741646238837
selected: false
sourcePosition: right
targetPosition: left
type: custom
width: 244
zIndex: 1002
viewport:
x: -178.36175912590647
y: 65.37349796034047
zoom: 0.9635810545596508
また、英語化したものですが、Githubにもアップロードしました。ぜひ、ご参考になさってください!
このシステムの応用例
このようなマルチAIエージェントシステムは、以下のような場面で活用できると考えています。
- プロジェクト計画段階での多角的検討:新機能の追加や技術選定の際に、異なる視点からの意見を集約
- リスク分析とトレードオフの検討:技術的実現可能性、ビジネス価値、品質保証の観点からバランスの取れた判断を行う
- チームミーティングの事前準備:実際のミーティングの前に、考慮すべき様々な視点を洗い出す
「実装のポイントと学んだこと」セクションを、ご指摘いただいた課題を踏まえて改訂します。
実装のポイントと学んだこと
このシステムを構築する中で、いくつかの重要な洞察が得られました。
-
各AIの役割の明確化:
それぞれのAIが独自の視点と専門性を持つよう設計することで、多角的な意見を引き出すことができました。 -
バランスの取れた意見収集:
一つの専門領域に偏らないよう、異なる専門家からの意見を引き出す仕組みが機能しました。 -
会話の流れの管理:
過去の対話を参照して一貫性のある会話を実現できました。 -
議論の欠如という課題:
現状のシステムでは、専門家たちが個別に意見を述べるだけで、それらの意見間での真の「議論」が発生していないことが大きな課題です。各専門家は独立して自分の視点を述べているものの、お互いの意見に対して反応したり、異なる視点をすり合わせたりする相互作用が不足しています。 -
ファシリテーターの役割強化の必要性:
ファシリテーターは現在、質問の振り分けに重点を置いていますが、本来は異なる意見をぶつけ合わせ、議論を深める役割も担うべきです。例えば下記のような形で専門家間の意見の対立点や共通点を見出し、より深い議論に導くプロンプト設計が必要です。- 「PMの視点とエンジニアの視点が異なりますが、この相違についてユーザー代表としてどう考えますか?」
- 「QAエンジニアが指摘した品質リスクに対して、PMとして別の優先順位づけを提案できますか?」
-
Difyのワークフロー改善の可能性:
現在のワークフローでは、各専門家の意見が一方向に集約されるだけですが、これを改善するためには下記が考えられます。- 専門家間で意見を共有するフィードバックループの設計
- 前の専門家の意見を次の専門家に入力として渡す連鎖的な設計
- 意見の対立点を自動検出して焦点を当てる仕組み
-
議論の進行管理:
単に意見を集めるだけでなく、「問題定義→各専門家の視点提示→意見の対立点の特定→解決策の提案→合意形成」といった議論のステージを明示的に設計することで、より構造化された議論が可能になるでしょう。
このような課題と可能性を認識することで、次のバージョンでは真のマルチエージェント討議システムへと進化させることができるはずです。特にファシリテーターのプロンプト設計は、単なる司会者から議論の触媒へと役割を拡張する鍵となるでしょう。
おわりに
Difyを使ったこのマルチエージェントシステムは、AIの活用方法として非常に興味深いアプローチと考えています。単一のAIに全てを任せるのではなく、専門性や視点の異なる複数のAIを組み合わせることで、より包括的な検討が可能になります。
Discussion