Commit 6730f95fd5
Unsigned
Layout: unified · split
content/blog/2024-08-25-n8n-sentiment-analysis.org added +154
| @@ -0,0 +1,154 @@ | ||
| 1 | #+date: <2024-08-25 Sun 09:45:30> | |
| 2 | #+title: Automatic Email Sentiment Analysis With n8n | |
| 3 | #+description: Learn how to configure a basic workflow in n8n to analyze the sentiment of incoming emails. | |
| 4 | #+filetags: :automation:n8n: | |
| 5 | #+slug: n8n-sentiment-analysis | |
| 6 | ||
| 7 | * n8n | |
| 8 | ||
| 9 | This guide will show you how to self-host [[https://n8n.io/][n8n]], a workflow automation platform, | |
| 10 | and use it to create a workflow that automatically analyzes the sentiment of all | |
| 11 | incoming emails on your account. | |
| 12 | ||
| 13 | This is a completely free process that only requires that you have access to (1) | |
| 14 | an email account with IMAP/SMTP options and (2) a computer or machine where you | |
| 15 | can install and use Docker. | |
| 16 | ||
| 17 | ** Installation | |
| 18 | ||
| 19 | To get started, read the [[https://github.com/n8n-io/self-hosted-ai-starter-kit][self-hosted-ai-starter-kit]] project README or simply | |
| 20 | clone the repository and set it up with Docker using the command below. | |
| 21 | ||
| 22 | #+begin_src sh | |
| 23 | git clone https://github.com/n8n-io/self-hosted-ai-starter-kit.git | |
| 24 | cd self-hosted-ai-starter-kit | |
| 25 | docker compose --profile cpu up | |
| 26 | #+end_src | |
| 27 | ||
| 28 | This will clone the repository, start the stack of Docker containers with | |
| 29 | =compose=, and make the n8n web page available at [[https://localhost:5678]]. The | |
| 30 | first run will ask you to configure the administrator account for n8n. | |
| 31 | ||
| 32 | ** Reverse Proxy | |
| 33 | ||
| 34 | If you want to use n8n from a public domain name, you'll need to configure a | |
| 35 | reverse proxy. I use Nginx on my server for reverse proxies. Nginx configuration | |
| 36 | locations and practices vary by distribution, but the following commands will | |
| 37 | show you my general reverse proxy configuration. | |
| 38 | ||
| 39 | #+begin_src sh | |
| 40 | cd /etc/nginx/conf.d | |
| 41 | nano n8n.conf | |
| 42 | #+end_src | |
| 43 | ||
| 44 | #+begin_src conf | |
| 45 | server { | |
| 46 | listen 443 ssl; | |
| 47 | listen [::]:443 ssl; | |
| 48 | http2 on; | |
| 49 | server_name n8n.example.com; | |
| 50 | ||
| 51 | # SSL | |
| 52 | ssl_certificate /etc/letsencrypt/live/example.com/fullchain.pem; | |
| 53 | ssl_certificate_key /etc/letsencrypt/live/example.com/privkey.pem; | |
| 54 | ssl_trusted_certificate /etc/letsencrypt/live/example.com/chain.pem; | |
| 55 | ||
| 56 | # reverse proxy | |
| 57 | location / { | |
| 58 | set $upstream_n8n http://127.0.0.1:5678; | |
| 59 | ||
| 60 | proxy_set_header Remote-User $user; | |
| 61 | proxy_set_header Remote-Email $email; | |
| 62 | proxy_set_header Remote-Groups $groups; | |
| 63 | ||
| 64 | client_body_buffer_size 128k; | |
| 65 | proxy_next_upstream error timeout invalid_header http_500 http_502 http_503; | |
| 66 | ||
| 67 | send_timeout 5m; | |
| 68 | proxy_read_timeout 360; | |
| 69 | proxy_send_timeout 360; | |
| 70 | proxy_connect_timeout 360; | |
| 71 | ||
| 72 | proxy_set_header Host $host; | |
| 73 | proxy_set_header Upgrade $http_upgrade; | |
| 74 | proxy_set_header Connection upgrade; | |
| 75 | proxy_set_header Accept-Encoding gzip; | |
| 76 | proxy_set_header X-Real-IP $remote_addr; | |
| 77 | proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for; | |
| 78 | proxy_set_header X-Forwarded-Proto $scheme; | |
| 79 | proxy_set_header X-Forwarded-Host $http_host; | |
| 80 | proxy_set_header X-Forwarded-Uri $request_uri; | |
| 81 | proxy_set_header X-Forwarded-Ssl on; | |
| 82 | proxy_redirect http:// $scheme://; | |
| 83 | proxy_http_version 1.1; | |
| 84 | proxy_set_header Connection ""; | |
| 85 | proxy_cache_bypass $cookie_session; | |
| 86 | proxy_no_cache $cookie_session; | |
| 87 | proxy_buffers 64 256k; | |
| 88 | } | |
| 89 | ||
| 90 | # HTTP redirect | |
| 91 | server { | |
| 92 | listen 80; | |
| 93 | listen [::]:80; | |
| 94 | server_name n8n.example.com; | |
| 95 | include custom.d/letsencrypt.conf; | |
| 96 | ||
| 97 | if ($host ~ ^[^.]+\.example\.com$) { | |
| 98 | return 301 https://$host$request_uri; | |
| 99 | } | |
| 100 | } | |
| 101 | #+end_src | |
| 102 | ||
| 103 | ** Workflow Configuration | |
| 104 | ||
| 105 | You can open the included workflow at | |
| 106 | [[http://localhost:5678/workflow/srOnR8PAY3u4RSwb]] or simply open the web interface | |
| 107 | and create a new configuration. | |
| 108 | ||
| 109 | #+caption: n8n Workflow | |
| 110 | [[https://img.cleberg.net/blog/20240825-n8n-sentiment-analysis/workflow.png]] | |
| 111 | ||
| 112 | This workflow contains the following nodes: | |
| 113 | 1. Email Trigger (IMAP) | |
| 114 | + Create an IMAP credential with your email's IMAP settings to allow n8n to | |
| 115 | monitor for new emails. | |
| 116 | 2. If | |
| 117 | + Check ={{ $json.subject }}= to see if it =contains= "n8n Sentiment | |
| 118 | Analysis" to ensure we don't re-check our own emails. | |
| 119 | 3. Sentiment Analysis | |
| 120 | + Analyze the ={{ $json.textPlain }}= input coming from each email. | |
| 121 | + Ensure you have =Include Detailed Results= enabled so that we can access | |
| 122 | the confidence and strength variables. | |
| 123 | 4. Ollama Chat Model | |
| 124 | + The Docker stack we used includes Ollama and Llama3, which provide the | |
| 125 | easiest way to test this workflow. | |
| 126 | 5. Send Email | |
| 127 | + Create an SMTP credential to allow n8n to send emails. | |
| 128 | + Subject: =n8n Sentiment Analysis: {{ $json.sentimentAnalysis.category }}= | |
| 129 | + Email Format: =Text= | |
| 130 | + Text (Expression): This can contain anything you want, but be sure to | |
| 131 | include the variables ={{ $json.sentimentAnalysis.category }}=, ={{ | |
| 132 | $json.sentimentAnalysis.strength }}=, and ={{ | |
| 133 | $json.sentimentAnalysis.confidence }}=. | |
| 134 | + In the additional options, I enabled the =Append n8n Attribution= option in | |
| 135 | the screenshots below. | |
| 136 | ||
| 137 | ** Testing | |
| 138 | ||
| 139 | You can use the =Test Workflow= button at the bottom of the canvas area to test | |
| 140 | the workflow. This relies on receiving new messages in your inbox, so be sure to | |
| 141 | send yourself a test email! | |
| 142 | ||
| 143 | ** Results | |
| 144 | ||
| 145 | After testing each step noted above, n8n provided the results below - it works! | |
| 146 | ||
| 147 | #+caption: Positive Results | |
| 148 | [[https://img.cleberg.net/blog/20240825-n8n-sentiment-analysis/positive_results.png]] | |
| 149 | ||
| 150 | #+caption: Negative Results | |
| 151 | [[https://img.cleberg.net/blog/20240825-n8n-sentiment-analysis/negative_results.png]] | |
| 152 | ||
| 153 | While this isn't anything earth-shattering, it does show easy it is to get | |
| 154 | started with n8n and large language models in a self-hosted environment. | |
theme/templates/index.html +7 −8
| @@ -9,7 +9,13 @@ uid [ultimate] Christian Cleberg <hello@cleberg.net></pre> | ||
| 9 | 9 | <h2>Recent Blog Posts</h2> |
| 10 | 10 | |
| 11 | 11 | <div class="post"> |
| 12 | <time date="2024-08-11 20:18:15">2024-08-11</time> | |
| 12 | <time datetime="2024-08-25 09:45:30">2024-08-25</time> | |
| 13 | <a href="/blog/n8n-sentiment-analysis.html" | |
| 14 | >Automatic Email Sentiment Analysis With n8n</a | |
| 15 | > | |
| 16 | </div> | |
| 17 | <div class="post"> | |
| 18 | <time datetime="2024-08-11 20:18:15">2024-08-11</time> | |
| 13 | 19 | <a href="/blog/org-mode-features.html" |
| 14 | 20 | >My Top 8 Most Used Org-Mode Features</a |
| 15 | 21 | > |
| @@ -19,13 +25,6 @@ uid [ultimate] Christian Cleberg <hello@cleberg.net></pre> | ||
| 19 | 25 | <a href="/blog/emacs-on-ipad.html">Emacs on iPadOS</a> |
| 20 | 26 | </div> |
| 21 | 27 | |
| 22 | <div class="post"> | |
| 23 | <time datetime="2024-06-19 08:00:00">2024-06-19</time> | |
| 24 | <a href="/blog/deprecated-trusted-gpg-fix.html" | |
| 25 | >Fixing Ubuntu Error: 'Key is stored in legacy trusted.gpg keyring'</a | |
| 26 | > | |
| 27 | </div> | |
| 28 | ||
| 29 | 28 | <br /> |
| 30 | 29 | <a href="/blog/">All Posts →</a> |
| 31 | 30 | </section> |