Difyを用いてプログラミング学習を加速する？

## 知らない言葉を調べると知らない言葉が出てくる
みなさんこんにちは。kokurenです。  

自分は生成AIからプログラミングや機械学習などに興味を持ったタイプなのですが、AIや既存のもので遊ぶだけではなく、その裏にある数理的な話や具体的な実装などについても学びたいなとずっとなんとなく思っていました。

とは言っても、中々体系立って専門外の何かを学ぶというのは難しく、**調べても知らない言葉で説明されている**、**どこからわかってないのかすらわかっていない**といった典型的な勉強できないスパイラルに陥っていました。

これは語学などでも言えることだと思うのですが、人が何かのトピックについて学ぶとき、まず語彙の獲得が識字よりも先立って、識字の獲得の後に多様な文脈に触れることで理解のネットワークが形成されるのかと考えています。

つまり、人が何かを学ぶ上で最優先なのは**知らない概念を潰すこと**なのではないかということです。

しかし、先ほど言ったとおり、何がわからないのかすらわかっていないというのに、どう知らない概念を潰していけばいいのでしょうか。そこで生成AI（簡単なLLMエージェント）が役立つと考えました。

### 学ぶべき語彙をLLMに挙げさせる

前置きが長くなりましたが本題です。面倒なことはLLMにやらせましょう。
以下のようにMermaid.jsを用いて特定の用語や概念に対するロードマップをツリー状に示させることができます。

```mermaid
graph TD;
    A[PyTorch] --> B[Tensor]
    A --> C[Autograd]
    A --> D[torch.nn]
    A --> E[torch.optim]
    A --> F[DataLoader]
    A --> G[Dataset]

    B --> H[Tensor Operations]
    B --> I[CUDA Tensors]

    C --> J[Computational Graph]
    C --> K[Gradient Descent]

    D --> L[Module]
    D --> M[Sequential]
    D --> N[Loss Function]

    E --> O[SGD]
    E --> P[Adam]

    F --> Q[Batch Processing]
    F --> R[Data Augmentation]

    G --> S[Custom Dataset]
    G --> T[Transforms]

    L --> U[Forward Method]
    L --> V[Backward Method]

```

上記はPyTorchというワードでの一例です。

上記のように、キーワードをツリー状にまとめさせることで、周辺ワードについて効率よくさらうことができます。

### 学習用資料をDifyを用いて生成する

今更Difyというツールを触ってみたのですが、なんとも面白かったです。

一応説明しますと、DifyとはオープンソースのLLMアプリケーション開発ツールで、ノードベースでLLMやカスタムコードの入出力を管理してワークフローを組むことができます。

https://github.com/langgenius/dify

今回は以下のようなフローを組んでみました。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3632347/2d92a1fb-3ac1-fcad-7049-e633abe7ecc5.png)

始めに知りたいプログラミングに関するワードを入力すると、LLMノードに渡され、Mermaid.jsの形式で単語をリストアップしてくれます。

次に、LLM2にリストアップされた単語が送られ、配列の形をした文字列に整形されます。
続いて、コードで実際のString型配列に変換され、イテレーションに学ぶべき用語リストの配列が渡され、LLM3で解説のマークダウンが作られ、最後に整形して出力という仕組みです。

そのままmarkdownをpdfやNotion用テキストなどに変換してあげれば、一瞬で自分だけのピンポイントの教材が生成されます。

<script async class="docswell-embed" src="https://www.docswell.com/assets/libs/docswell-embed/docswell-embed.min.js" data-src="https://www.docswell.com/slide/Z82WG2/embed" data-aspect="1.2938"></script><div class="docswell-link"><a href="https://www.docswell.com/s/kokuren333/Z82WG2-2024-11-28-202431">DL_PyTorch by @kokuren333</a></div>

ワードを"pytorch"にして生成した例。
ドクセルに色々載せているので見てみたら面白いかもです。

### おまけ

今回作成したワークフローのyamlを乗っけておきます。
本当はファイルをそのまま共有したかったのですがQiitaには貼れなかったので、以下をテキストエディタにコピペして.yml拡張子で保存し、Difyにインポートしてください。

アディオス。

```yaml
app:
  description: ''
  icon: 🤖
  icon_background: '#FFEAD5'
  mode: workflow
  name: programdigger
  use_icon_as_answer_icon: false
kind: app
version: 0.1.3
workflow:
  conversation_variables: []
  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: true
    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
        sourceType: start
        targetType: llm
      id: 1732761278493-source-1732762025290-target
      source: '1732761278493'
      sourceHandle: source
      target: '1732762025290'
      targetHandle: target
      type: custom
      zIndex: 0
    - data:
        isInIteration: false
        sourceType: llm
        targetType: llm
      id: 1732762025290-source-1732764987274-target
      source: '1732762025290'
      sourceHandle: source
      target: '1732764987274'
      targetHandle: target
      type: custom
      zIndex: 0
    - data:
        isInIteration: true
        iteration_id: '1732764128347'
        sourceType: iteration-start
        targetType: llm
      id: 1732764128347start-source-1732765426680-target
      source: 1732764128347start
      sourceHandle: source
      target: '1732765426680'
      targetHandle: target
      type: custom
      zIndex: 1002
    - data:
        isInIteration: false
        sourceType: llm
        targetType: code
      id: 1732764987274-source-1732765355941-target
      source: '1732764987274'
      sourceHandle: source
      target: '1732765355941'
      targetHandle: target
      type: custom
      zIndex: 0
    - data:
        isInIteration: false
        sourceType: code
        targetType: iteration
      id: 1732765355941-source-1732764128347-target
      source: '1732765355941'
      sourceHandle: source
      target: '1732764128347'
      targetHandle: target
      type: custom
      zIndex: 0
    - data:
        isInIteration: false
        sourceType: iteration
        targetType: code
      id: 1732764128347-source-1732768020473-target
      source: '1732764128347'
      sourceHandle: source
      target: '1732768020473'
      targetHandle: target
      type: custom
      zIndex: 0
    - data:
        isInIteration: false
        sourceType: code
        targetType: end
      id: 1732768020473-source-1732765086173-target
      source: '1732768020473'
      sourceHandle: source
      target: '1732765086173'
      targetHandle: target
      type: custom
      zIndex: 0
    nodes:
    - data:
        desc: 知りたいprogramming概念
        selected: true
        title: 開始
        type: start
        variables:
        - label: word
          max_length: 48
          options: []
          required: true
          type: text-input
          variable: word
      height: 117
      id: '1732761278493'
      position:
        x: 134
        y: 277
      positionAbsolute:
        x: 134
        y: 277
      selected: true
      sourcePosition: right
      targetPosition: left
      type: custom
      width: 244
    - data:
        context:
          enabled: false
          variable_selector: []
        desc: ''
        model:
          completion_params:
            temperature: 0.7
          mode: chat
          name: gpt-4o
          provider: openai
        prompt_template:
        - id: 8bece6b8-3914-4616-8a08-fdc3cb909887
          role: system
          text: 'あなたは与えられたキーワードについて学習するためのフローチャートを提供します。

            与えられた概念を理解する上で、理解する必要のあるキーワードを、Mermaid.jsを用いてロードマップとして提示してください。

            Mermaid.jsによるフローチャートのみ出力し、余計な説明やテキストは出力しないこと。


            例：PyTorch

            ```mermaid

            graph TD;

                A[PyTorch] --> B[Tensor]

                A --> C[Autograd]

                A --> D[torch.nn]

                A --> E[torch.optim]

                A --> F[DataLoader]

                A --> G[Dataset]



                B --> H[Tensor Operations]

                B --> I[CUDA Tensors]



                C --> J[Computational Graph]

                C --> K[Gradient Descent]



                D --> L[Module]

                D --> M[Sequential]

                D --> N[Loss Function]



                E --> O[SGD]

                E --> P[Adam]



                F --> Q[Batch Processing]

                F --> R[Data Augmentation]



                G --> S[Custom Dataset]

                G --> T[Transforms]



                L --> U[Forward Method]

                L --> V[Backward Method]



            ```'
        - id: c959467a-0a5e-43f7-a103-927b7be024cf
          role: user
          text: '{{#1732761278493.word#}}について理解するためのロードマップをMermaid.jsを用いて作成。要素の数は20前後までとする。

            基本的には日本語を用いるが、英語の方が一般的な名称や固有名詞は英語を使う。 すべてその分野特有の専門用語であること。'
        selected: false
        title: LLM
        type: llm
        variables: []
        vision:
          enabled: false
      height: 97
      id: '1732762025290'
      position:
        x: 431
        y: 282
      positionAbsolute:
        x: 431
        y: 282
      selected: false
      sourcePosition: right
      targetPosition: left
      type: custom
      width: 244
    - data:
        desc: ''
        error_handle_mode: terminated
        height: 185
        is_parallel: true
        iterator_selector:
        - '1732765355941'
        - result
        output_selector:
        - '1732765426680'
        - text
        output_type: array[string]
        parallel_nums: 10
        selected: false
        start_node_id: 1732764128347start
        title: イテレーション
        type: iteration
        width: 388
      height: 185
      id: '1732764128347'
      position:
        x: 330.38008978795983
        y: 452.3098893252444
      positionAbsolute:
        x: 330.38008978795983
        y: 452.3098893252444
      selected: false
      sourcePosition: right
      targetPosition: left
      type: custom
      width: 388
      zIndex: 1
    - data:
        desc: ''
        isInIteration: true
        selected: false
        title: ''
        type: iteration-start
      draggable: false
      height: 48
      id: 1732764128347start
      parentId: '1732764128347'
      position:
        x: 24
        y: 68
      positionAbsolute:
        x: 354.38008978795983
        y: 520.3098893252444
      selectable: false
      sourcePosition: right
      targetPosition: left
      type: custom-iteration-start
      width: 44
      zIndex: 1002
    - data:
        context:
          enabled: false
          variable_selector: []
        desc: ''
        model:
          completion_params:
            temperature: 0
          mode: chat
          name: gpt-4o
          provider: openai
        prompt_template:
        - id: 528d5b85-b8ee-49f0-8b6f-f4ed4b1c792a
          role: system
          text: '与えられたmermaid.jsから階層順にワードを抜き出し、pythonで使用可能なリストの形で出力する。

            リストのみを出力すること。


            format:

            [word1, word2, ...]'
        - id: e36c2b43-1e00-454d-ba09-9ce3b0f08229
          role: user
          text: '{{#1732762025290.text#}}から階層順のワードリストを抽出。リストのみ抽出。


            format:

            [''word1'', ''word2'', ...]'
        selected: false
        title: LLM 2
        type: llm
        variables: []
        vision:
          enabled: false
      height: 97
      id: '1732764987274'
      position:
        x: 715.5675294738476
        y: 282
      positionAbsolute:
        x: 715.5675294738476
        y: 282
      selected: false
      sourcePosition: right
      targetPosition: left
      type: custom
      width: 244
    - data:
        desc: ''
        outputs:
        - value_selector:
          - '1732768020473'
          - result
          variable: output
        selected: false
        title: 終了
        type: end
      height: 89
      id: '1732765086173'
      position:
        x: 1084.6420494283946
        y: 472.2084776588363
      positionAbsolute:
        x: 1084.6420494283946
        y: 472.2084776588363
      selected: false
      sourcePosition: right
      targetPosition: left
      type: custom
      width: 244
    - data:
        code: "function main({ wordlist }) {\n    let result; // 最終的に返す変数\n\n    //\
          \ 入力が null または undefined の場合に対応\n    if (!wordlist) {\n        throw new\
          \ TypeError(\"wordlist is null, undefined, or not provided.\");\n    }\n\
          \n    // 入力が文字列の場合\n    if (typeof wordlist === 'string') {\n        try\
          \ {\n            // シングルクォートをダブルクォートに変換して JSON としてパース\n            result\
          \ = JSON.parse(wordlist.replace(/'/g, '\"'));\n        } catch (error) {\n\
          \            // パースエラーをキャッチ\n            throw new TypeError(\"wordlist\
          \ is a string but cannot be parsed as JSON.\");\n        }\n    } \n   \
          \ // 入力が配列の場合\n    else if (Array.isArray(wordlist)) {\n        result =\
          \ wordlist; // 配列の場合、そのまま代入\n    } \n    // その他のデータ型はエラー\n    else {\n \
          \       throw new TypeError(\"wordlist must be a string or an array.\");\n\
          \    }\n\n    return { result }; // 最終的にオブジェクト形式で result を返す\n}\n"
        code_language: javascript
        desc: ''
        outputs:
          result:
            children: null
            type: array[string]
        selected: false
        title: コード
        type: code
        variables:
        - value_selector:
          - '1732764987274'
          - text
          variable: wordlist
      height: 53
      id: '1732765355941'
      position:
        x: 36.362293722234426
        y: 452.3098893252444
      positionAbsolute:
        x: 36.362293722234426
        y: 452.3098893252444
      selected: false
      sourcePosition: right
      targetPosition: left
      type: custom
      width: 244
    - data:
        context:
          enabled: false
          variable_selector: []
        desc: ''
        isInIteration: true
        iteration_id: '1732764128347'
        model:
          completion_params:
            temperature: 0.7
          mode: chat
          name: gpt-4o
          provider: openai
        prompt_template:
        - id: fd24568a-adb9-45b9-8822-b681613a6494
          role: system
          text: "あなたはprogrammingやゲーム制作のための教科書作成をしています。\nこちらが提示する概念や用語について、以下の点に留意しながら以下のフォーマットでまとめてください。\n\
            \n・本にできるほど詳細かつ厳密に書き記すこと\n・必要に応じて適切な数式記号や数式を用いること（TeX）\n・文中の数式はnotionのインライン数式の形式を使うこと\n\
            ・大きな式や関数はnotionの/mathに組み込める形にすること\n・主に体言止め形式を使い、常体で書き記すこと\n・数式はKaTexで表示できるように記載\n\
            ・インライン数式は$で囲み、ブロック数式は$$でかこむ\n・必要に応じてコードスニペットを提供\n・コードスニペットはコードブロックで囲むこと\n\
            \n＝＝＝＝＝\n出力フォーマット\n\n## 提示された概念名\n### 定義や意味\n - ここに厳密な定義や、意味あいについて詳細に書き記す\n\
            \ - （あれば）数式による具体例を示す\n### 嚙み砕いた説明\n - わかりやすく嚙み砕いた説明\n - わかりやすさや言葉での具体例を重視\n\
            ### 実際の応用例\n - その概念がどんな場面で応用されているかの例を挙げる\n - コードスニペットとコメントアウトを用いて具体例を説明する\n\
            \ ＝＝＝＝＝"
        - id: 21990eab-f4b4-4e87-8092-dbfb3c6fe6c4
          role: user
          text: '以下のprogramming及びゲーム制作概念{{#1732764128347.item#}}についての解説をマークダウン形式で出力。

            数式はKaTexで表示できるように記載。

            コードスニペットはコードブロックで囲む。

            {{#1732761278493.word#}}に関連しているとなお良し。'
        selected: false
        title: LLM 3
        type: llm
        variables: []
        vision:
          enabled: false
      height: 97
      id: '1732765426680'
      parentId: '1732764128347'
      position:
        x: 128
        y: 68
      positionAbsolute:
        x: 458.38008978795983
        y: 520.3098893252444
      selected: false
      sourcePosition: right
      targetPosition: left
      type: custom
      width: 244
      zIndex: 1002
    - data:
        code: "\nfunction main({ output }) {\n    if (!Array.isArray(output)) {\n\
          \        throw new TypeError(\"Expected an array of strings for 'output'\"\
          );\n    }\n\n    // 一連のマークダウンテキストを結合して整形\n    const result = output.join(\"\
          \\n\\n\");\n\n    return {\n        result, // 整形されたマークダウンを返す\n    };\n}"
        code_language: javascript
        desc: ''
        outputs:
          result:
            children: null
            type: string
        selected: false
        title: コード 2
        type: code
        variables:
        - value_selector:
          - '1732764128347'
          - output
          variable: output
      height: 53
      id: '1732768020473'
      position:
        x: 773.8593053149923
        y: 472.2084776588363
      positionAbsolute:
        x: 773.8593053149923
        y: 472.2084776588363
      selected: false
      sourcePosition: right
      targetPosition: left
      type: custom
      width: 244
    viewport:
      x: 224.74055136647993
      y: 57.26161309864435
      zoom: 0.7937005259840996
```