Azure AI Foundry プロンプトフロー(Prompt Flow) 処理フロー作成

Azure AI Foundryのプロンプトフロー(Prompt Flow)を利用した処理実行について、以下の処理フローを作成し動作することを確認します。

処理 | 概要
--- | ---
単純処理 | input・処理・outputのみの最も簡潔なフロー
並列処理と結合 | １つのinputをそれぞれ２つの処理を行い、２つ結果を結合してoutputするフロー
処理分岐と結合 | 条件分岐が発生し、分岐後に実行された処理結果をoutputするフロー

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/201787/856c7b36-10ff-4b41-9280-718777183f4f.png)


:::note info
Azure AI Foundry/Prompt Flowを使用するために以下を事前に行っています。

https://qiita.com/Higemal/items/704a245125b71994b563
→Azure OpenAI Serviceのリソース作成と生成AIモデルのデプロイまで

https://qiita.com/Higemal/items/b79a2707739efe3b5479
→Azure AI Foundryのプロジェクト作成まで
:::

:::note warn
本投稿では処理フローの作成に重点を置くため、処理はすべてpythonとし、LLMは使用しません。
:::




# 1. 単純処理

最もシンプルなフローとして、input/処理/outputだけのフローを作成します。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/201787/9df63c24-cf83-459d-b940-4b5c08ad5eea.png)



## 処理概要

処理 | 概要
--- | ---
input | int型の変数numberを準備し、変数に整数を代入
process1 | inputの内容によって異なった文字列をreturnする処理
output | 処理process1のreturnを受け取る変数colorを配置

## 処理詳細
以下3か所について記述します。
・input
・process1
・output


### input
入力部にはinputとして利用する変数と、その変数値を入力します。

名称 | 用途 | 値
--- | --- | ---
名前 | 変数名 | number
種類 | 変数の型 | int
値 | 変数の値 | 1

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/201787/40678960-fb37-4e63-bf2d-dab83e5cae3e.png)

### process1
処理本体と、先行処理とのインプット連携を行います。

##### ①コード
コード本体になります。

```python:process1
from promptflow import tool

@tool

def get_color(number: int) -> str:
    if number == 1:
        return "red"
    elif number == 2:
        return "blue"
    elif number == 3:
        return "green"
    else:
        return "black"
```


##### ②入力
どの入力内容を処理のインプットとするかを定義する箇所です。
**こちらを定義することで、グラフ上の矢印が引かれます。**

名称 | 用途 | 値
--- | --- | ---
名前 | 変数名 | number
種類 | 変数の型 | int
値 | 変数の値 | ${inputs.number}

:::note warn
値 `${inputs.number}` と表現することで、input部の変数nubmerを入力値とすることを指定しています。
:::

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/201787/b857d57e-adfe-4c82-b0ab-470669cbe7a9.png)


### output
出力部にはoutputとして利用する変数と、その入力元を指定します。

名称 | 用途 | 値
--- | --- | ---
名前 | 変数名 | color
値 | 変数の値 | ${process1.output}

:::note warn
値 `${process1.output}` と表現することで、process1部のreturnを入力値とすることを指定しています。
:::

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/201787/b1a58696-c2ca-4b8e-943f-7abcfee246df.png)


## 処理結果
処理を実行し、結果を確認します。

入力変数`number`が`1`に対し、出力変数`color`が`red`を得ることができました。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/201787/b7e075a6-1e77-4371-8e3f-d164200dc0c9.png)

<br>
<br>











# 2. 並列処理と結合

次に、１つの入力から別の２つの処理を行い、２つの処理結果を結合して１の出力とする並列・結合のフローを作成します。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/201787/e8b46076-8afb-409c-af40-0b5326c4482f.png)


## 処理概要

処理 | 概要
--- | ---
input | int型の変数numberを準備し、変数に整数を代入
process1 | inputの内容によって異なった文字列をreturnする処理
process2 | inputの内容によって異なった文字列をreturnする処理
join1 | process1とprocess2の処理結果を結合する処理
output | 処理join1のreturnを受け取る変数colorを配置

## 処理詳細
以下4か所について記述します。
・input
・process1/process2
・join
・output


### input
入力部にはinputとして利用する変数と、その変数値を入力します。

名称 | 用途 | 値
--- | --- | ---
名前 | 変数名 | number
種類 | 変数の型 | int
値 | 変数の値 | 2

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/201787/40678960-fb37-4e63-bf2d-dab83e5cae3e.png)

### process1/process2
処理本体と、先行処理とのインプット連携を行います。
**process1とprocess2の違いはコードだけとなり、入力部は同様とすることでグラフ上の並列分岐を実現しています。**

##### ①コード

```python:process1
from promptflow import tool

@tool

def get_color(number: int) -> str:
    if number == 1:
        return "red"
    elif number == 2:
        return "blue"
    elif number == 3:
        return "green"
    else:
        return "black"
```

```python:process2
from promptflow import tool

@tool

def get_color(number: int) -> str:
    if number == 1:
        return "book"
    elif number == 2:
        return "sky"
    elif number == 3:
        return "apple"
    else:
        return "lotus"
```

##### ②入力

名称 | 用途 | 値
--- | --- | ---
名前 | 変数名 | number
種類 | 変数の型 | int
値 | 変数の値 | ${inputs.number}


![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/201787/fb43982c-5c71-4a1b-ac1b-6fb8b3af543f.png)
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/201787/c5a4098a-0b98-48a3-96c3-7273fef748d4.png)



### join1
先行処理の結果を結合する処理になります。

##### ①コード

```python:join1
from promptflow import tool

@tool

def join_str(output1: str,output2: str) -> str:
    return output1 + " " + output2
```

##### ②入力
２つの処理結果について、２つの入力変数を準備することでグラフ上の結合を表現しています。

名称 | 用途 | 値
--- | --- | ---
名前 | 変数名 | output1
種類 | 変数の型 | string
値 | 変数の値 | ${process1.output}


名称 | 用途 | 値
--- | --- | ---
名前 | 変数名 | output2
種類 | 変数の型 | string
値 | 変数の値 | ${process2.output}

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/201787/eb888adf-1c92-4680-8d60-b0c9d3749fa0.png)


### output
出力部にはoutputとして利用する変数と、その入力元を指定します。

名称 | 用途 | 値
--- | --- | ---
名前 | 変数名 | words
値 | 変数の値 | ${join1.output}

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/201787/a7fa92fe-54db-49ea-93c4-2227b7ec4f37.png)



## 処理結果
処理を実行し、結果を確認します。

入力変数`number`が`2`に対し、出力変数`words`が`blue sky`を得ることができました。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/201787/dbae514f-03e6-4801-bb9a-ded757aee1ad.png)

<br>
<br>







# 3. 処理分岐と結合

最後に、１つの入力から処理分岐を行い、分岐経路の結果を出力するフローを作成します。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/201787/c8b4d69a-c607-41b8-aa30-c68259ab1bdd.png)

## 処理分岐とバイパスについて
プロンプトフローで処理分岐を行う際は、各処理の`構成のアクティブ化`を利用するかたちとなります。
`構成のアクティブ化`は、各処理がアクティブとなる条件を記述しておくものとなり、その条件を満たす場合は処理を、満たさない場合は`bypass`扱いとなり処理が実行されません。
（`構成のアクティブ化`が付与された処理は、再生ボタンのようなマークがグラフ内に表現されます）
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/201787/cbf316c3-6628-4c32-9a82-d9ab2f4e7d1b.png)

そのため、今回は`構成のアクティブ化`の判定ロジックとなる処理`check1`を準備してフロー連携を実現します。



## 処理概要

処理 | 概要
--- | ---
input | int型の変数numberを準備し、変数に整数を代入
check1 | int型の変数numberによって、true/falseを出力する処理
process1 | inputの内容によって異なった文字列をreturnする処理、ただしcheck1処理により処理/bypassが決まる
process2 | inputの内容によって異なった文字列をreturnする処理、ただしcheck1処理により処理/bypassが決まる
join1 | process1とprocess2の処理結果を結合する処理
output | 処理join1のreturnを受け取る変数colorを配置

## 処理詳細
以下5か所について記述します。
・input
・check1
・process1/process2
・join
・output


### input
入力部にはinputとして利用する変数と、その変数値を入力します。

名称 | 用途 | 値
--- | --- | ---
名前 | 変数名 | number
種類 | 変数の型 | int
値 | 変数の値 | 3

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/201787/be4d404c-9749-49b0-845f-66aeb13b0bc5.png)


### check1
後続処理の実行判定/構成のアクティブ化のためのロジックになります。
入力数値が2以下の場合はtrue、そうでなけれはfalseを応答します。

##### ①コード

```python:check1
from promptflow import tool

@tool

def check(n: int) -> bool:
    return n <= 2
```

##### ②入力

名称 | 用途 | 値
--- | --- | ---
名前 | 変数名 | number
種類 | 変数の型 | int
値 | 変数の値 | ${inputs.number}




### process1/process2
処理本体と、先行処理とのインプット連携を行います。
どちらの処理も入力値は`input`部から、構成のアクティブ化は`check1`部から得ます。

##### ①コード

```python:process1
from promptflow import tool

@tool

def get_color(number: int) -> str:
    if number == 1:
        return "red"
    elif number == 2:
        return "blue"
    elif number == 3:
        return "green"
    else:
        return "black"
```

```python:process2
from promptflow import tool

@tool

def get_color(number: int) -> str:
    if number == 1:
        return "book"
    elif number == 2:
        return "sky"
    elif number == 3:
        return "apple"
    else:
        return "lotus"
```

##### ②入力

名称 | 用途 | 値
--- | --- | ---
名前 | 変数名 | number
種類 | 変数の型 | int
値 | 変数の値 | ${inputs.number}


##### ③構成のアクティブ化
＜process1＞

名称 | 用途 
--- | --- 
タイミング | ${check1.output}
（型） | bool
（値） |  **True**

＜process2＞

名称 | 用途 
--- | --- 
タイミング | ${check1.output}
（型） | bool
（値） |  **False**

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/201787/5ff104e4-c310-46eb-ae58-fcdebcd2bae7.png)
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/201787/2e1d6f2a-52dd-4430-aad9-58c3893438b1.png)



### join1
先行処理の結果を結合する処理になります。
条件分岐により、2つの入力のうち片方はnull(None)となるため、非nullの入力を回答するような処理になります。

##### ①コード

```python:join1
from promptflow import tool

@tool

def join_str(output1: str,output2: str) -> str:
    if output1 is not None:
        return output1
    else:
        return output2
```

##### ②入力
２つの処理結果について、２つの入力変数を準備することでグラフ上の結合を表現しています。

名称 | 用途 | 値
--- | --- | ---
名前 | 変数名 | output1
種類 | 変数の型 | string
値 | 変数の値 | ${process1.output}


名称 | 用途 | 値
--- | --- | ---
名前 | 変数名 | output2
種類 | 変数の型 | string
値 | 変数の値 | ${process2.output}

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/201787/20413af3-651c-4bc6-b967-49488b65b1df.png)


### output
出力部にはoutputとして利用する変数と、その入力元を指定します。

名称 | 用途 | 値
--- | --- | ---
名前 | 変数名 | out
値 | 変数の値 | ${join1.output}

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/201787/683a6620-5780-4717-81f1-319166d81956.png)


## 処理結果
処理を実行し、結果を確認します。

入力変数`number`が`3`に対し、出力変数`out`が`apple`を得ることができました。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/201787/e44615fc-35eb-4090-a1aa-daff1acc3a81.png)

<br>

また、条件分岐により`process1`が`bypassed`となっていることも確認できました。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/201787/c0b39ec0-2f6b-47e2-8823-7cbd081732ed.png)


# 最後に
条件分岐については改善の余地がある気がするので、思いついたら実践してみようと思います。