<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom" xmlns:dc="http://purl.org/dc/elements/1.1/">
  <channel>
    <title>DEV Community: Phạm Nguyễn Hải Anh</title>
    <description>The latest articles on DEV Community by Phạm Nguyễn Hải Anh (@pnghaianh).</description>
    <link>https://dev.to/pnghaianh</link>
    <image>
      <url>https://media2.dev.to/dynamic/image/width=90,height=90,fit=cover,gravity=auto,format=auto/https:%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Fuser%2Fprofile_image%2F2340884%2F908cdc59-426f-411f-8499-3d82fc13f276.jpg</url>
      <title>DEV Community: Phạm Nguyễn Hải Anh</title>
      <link>https://dev.to/pnghaianh</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/pnghaianh"/>
    <language>en</language>
    <item>
      <title>From Paperwork Mountains to Digital Freedom: Building an IDP Solution with Kiro</title>
      <dc:creator>Phạm Nguyễn Hải Anh</dc:creator>
      <pubDate>Tue, 06 Jan 2026 14:58:45 +0000</pubDate>
      <link>https://dev.to/aws-builders/from-paperwork-mountains-to-digital-freedom-building-an-idp-solution-with-kiro-3m2k</link>
      <guid>https://dev.to/aws-builders/from-paperwork-mountains-to-digital-freedom-building-an-idp-solution-with-kiro-3m2k</guid>
      <description>&lt;h2&gt;
  
  
  Challenge: Office Staff Drowning in Paperwork
&lt;/h2&gt;

&lt;p&gt;Every morning, Mai arrives at the government office where she works. Her desk is already piled high with paper documents - citizen applications, official reports, meeting minutes, and forms that need to be digitized.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F3ren6bw8yuooacwcy0e4.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F3ren6bw8yuooacwcy0e4.png" alt=" " width="800" height="436"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Like thousands of office workers, Mai faces an exhausting daily routine:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;💢&lt;strong&gt;Type for hours&lt;/strong&gt;: Manually transcribing each document into Word&lt;/li&gt;
&lt;li&gt;💢 &lt;strong&gt;Hunt for errors&lt;/strong&gt;: Reviewing for typos and formatting mistakes
&lt;/li&gt;
&lt;li&gt;😫 &lt;strong&gt;Work overtime&lt;/strong&gt;: Staying late to finish the backlog&lt;/li&gt;
&lt;li&gt;😫 &lt;strong&gt;Feel exhausted&lt;/strong&gt;: Repeating the same tedious task every single day&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;A single 5-page official document takes 30-45 minutes to transcribe. With dozens arriving daily, Mai is overwhelmed.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;What if there was a better way?&lt;/strong&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  The Vision: AI-Powered Document Processing
&lt;/h2&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F3tlivuuloea4dd375uq2.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F3tlivuuloea4dd375uq2.png" alt=" " width="800" height="417"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;I wanted to build a solution where office staff could:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;strong&gt;Take photos&lt;/strong&gt; with their smartphone camera&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Wait 30 seconds&lt;/strong&gt; while AI processes them&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Download&lt;/strong&gt; perfectly formatted Word documents&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Done!&lt;/strong&gt; Ready to review and distribute&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;But I had a problem: &lt;strong&gt;I didn't know where to start.&lt;/strong&gt; &lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;How do I use AWS Bedrock for OCR?&lt;/li&gt;
&lt;li&gt;How do I create properly formatted Word documents?&lt;/li&gt;
&lt;li&gt;How do I preserve Vietnamese document headers?&lt;/li&gt;
&lt;li&gt;How do I build a web interface?&lt;/li&gt;
&lt;li&gt;How do I deploy on AWS?&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;That's when I turned to &lt;strong&gt;Kiro&lt;/strong&gt; — my AI coding assistant.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fgn293cf6aoxu4wwks9md.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fgn293cf6aoxu4wwks9md.png" alt=" " width="778" height="836"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Building with Kiro: The Journey
&lt;/h2&gt;

&lt;h3&gt;
  
  
  Step 1: The Initial Request
&lt;/h3&gt;

&lt;p&gt;I started with a simple question to Kiro:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;Me&lt;/strong&gt;: "Given the AWS Bedrock Converse API and Claude Sonnet 4, build an AI solution to OCR images from a folder and convert them to Word DOCX files. Prefer Python. Search the internet for references."&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;&lt;strong&gt;Kiro immediately got to work:&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fm27ydkgvacr6890wgb7i.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fm27ydkgvacr6890wgb7i.png" alt="Kiro searching for resources" width="800" height="445"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Within minutes, Kiro had:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Fetched AWS Bedrock Converse API documentation&lt;/li&gt;
&lt;li&gt;Researched python-docx library&lt;/li&gt;
&lt;li&gt;Created complete project structure&lt;/li&gt;
&lt;li&gt;Generated working code&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  Step 2: Fine-Tuning for Vietnamese Documents
&lt;/h3&gt;

&lt;p&gt;The first version worked, but Vietnamese official documents have a special two-column header format that wasn't captured correctly.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;My Request&lt;/strong&gt;:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;Me&lt;/strong&gt;: "The phrase 'CỘNG HÒA XÃ HỘI CHỦ NGHĨA VIỆT NAM' should be in a table with two columns. Left column has organization info, right column has the country header and date."&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;&lt;strong&gt;Kiro's Response&lt;/strong&gt;:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fpm4xn7o2swtjb5iq3s7u.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fpm4xn7o2swtjb5iq3s7u.png" alt="Kiro understanding Vietnamese document structure" width="800" height="493"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Kiro immediately:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;strong&gt;Updated the OCR prompt&lt;/strong&gt; to detect two-column headers&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Modified create_docx.py&lt;/strong&gt; to parse structured markup&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Created borderless tables&lt;/strong&gt; for proper formatting&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Tested the changes&lt;/strong&gt; automatically
&lt;/li&gt;
&lt;/ol&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="c1"&gt;# Kiro's clever solution - Structured OCR prompt
&lt;/span&gt;&lt;span class="n"&gt;STRUCTURED_OCR_PROMPT&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="sh"&gt;"""&lt;/span&gt;&lt;span class="s"&gt;
For Vietnamese official document headers with two columns, output as:
[HEADER_TABLE]
[LEFT]
organization line 1
organization line 2
Số: xx /TB-xxx
[/LEFT]
[RIGHT]
CỘNG HÒA XÃ HỘI CHỦ NGHĨA VIỆT NAM
Độc lập - Tự do - Hạnh phúc  
Tp. xxx, ngày xx tháng xx năm xxxx
[/RIGHT]
[/HEADER_TABLE]
&lt;/span&gt;&lt;span class="sh"&gt;"""&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;Result&lt;/strong&gt;: Perfect two-column headers!&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fbwy4ftjw3pnpte10xy1f.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fbwy4ftjw3pnpte10xy1f.png" alt=" " width="800" height="419"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h3&gt;
  
  
  Step 3: Intelligent Multi-Page Grouping
&lt;/h3&gt;

&lt;p&gt;I wanted images with similar names (&lt;code&gt;doc-1.png&lt;/code&gt;, &lt;code&gt;doc-2.png&lt;/code&gt;) to be combined into one Word file (&lt;code&gt;doc.docx&lt;/code&gt;).&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;My Request&lt;/strong&gt;:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;Me&lt;/strong&gt;: "If images have names with the same prefix but different numbers after a hyphen (e.g. tb-1, tb-2), group them into one DOCX file (e.g. named 'tb') with multiple pages."&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;&lt;strong&gt;Kiro's Implementation&lt;/strong&gt;:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fy0i7rfwiqpn0zj9dh3pf.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fy0i7rfwiqpn0zj9dh3pf.png" alt="Kiro implementing grouping logic" width="800" height="422"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Kiro created a smart filename parser:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;get_prefix_and_page&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;filename&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;str&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;-&amp;gt;&lt;/span&gt; &lt;span class="nb"&gt;tuple&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
    &lt;span class="sh"&gt;"""&lt;/span&gt;&lt;span class="s"&gt;
    Extract prefix and page number from filename.
    Examples:
        &lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;invoice-1.png&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt; -&amp;gt; (&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;invoice&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;, 1)
        &lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;report_2.jpg&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt; -&amp;gt; (&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;report&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;, 2)
        &lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;standalone.png&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt; -&amp;gt; (&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;standalone&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;, 0)
    &lt;/span&gt;&lt;span class="sh"&gt;"""&lt;/span&gt;
    &lt;span class="n"&gt;stem&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nc"&gt;Path&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;filename&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="n"&gt;stem&lt;/span&gt;
    &lt;span class="n"&gt;match&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;re&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;match&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sa"&gt;r&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;^(.+?)[-_](\d+)$&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;stem&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

    &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;match&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
        &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;match&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;group&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt; &lt;span class="nf"&gt;int&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;match&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;group&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;2&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;stem&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;Result&lt;/strong&gt;: Automatic multi-page document creation!&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fky1iu59au0amox7ruaqi.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fky1iu59au0amox7ruaqi.png" alt=" " width="800" height="508"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h3&gt;
  
  
  Step 4: Building the Web Interface
&lt;/h3&gt;

&lt;p&gt;Manual CLI was working, but I needed a user-friendly web interface.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;My Request&lt;/strong&gt;:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;Me&lt;/strong&gt;: "Deploy a website that accepts image uploads, stores them in a S3 bucket, processes with Bedrock, outputs DOCX files, and provides download links."&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;&lt;strong&gt;Kiro's Response&lt;/strong&gt;:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F0c6ekara1g6sujra9kb5.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F0c6ekara1g6sujra9kb5.png" alt="Kiro creating Flask app" width="800" height="508"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Within minutes, Kiro built:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Complete Flask web application&lt;/li&gt;
&lt;li&gt;Drag &amp;amp; drop upload interface&lt;/li&gt;
&lt;li&gt;S3 integration for storage&lt;/li&gt;
&lt;li&gt;Session management for multi-user support&lt;/li&gt;
&lt;li&gt;Download functionality with presigned URLs&lt;/li&gt;
&lt;li&gt;HTML templates
&lt;/li&gt;
&lt;/ul&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="nd"&gt;@app.route&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;/upload&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;methods&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;POST&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;])&lt;/span&gt;
&lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;upload_files&lt;/span&gt;&lt;span class="p"&gt;():&lt;/span&gt;
    &lt;span class="sh"&gt;"""&lt;/span&gt;&lt;span class="s"&gt;Handle file upload and processing&lt;/span&gt;&lt;span class="sh"&gt;"""&lt;/span&gt;
    &lt;span class="n"&gt;files&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;request&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;files&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;getlist&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;files&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="n"&gt;session_id&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;str&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;uuid&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;uuid4&lt;/span&gt;&lt;span class="p"&gt;())[:&lt;/span&gt;&lt;span class="mi"&gt;8&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;

    &lt;span class="c1"&gt;# Upload to S3
&lt;/span&gt;    &lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="nb"&gt;file&lt;/span&gt; &lt;span class="ow"&gt;in&lt;/span&gt; &lt;span class="n"&gt;files&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
        &lt;span class="n"&gt;s3_key&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="sa"&gt;f&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;images/&lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="n"&gt;session_id&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="s"&gt;/&lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="n"&gt;filename&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;
        &lt;span class="n"&gt;s3_client&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;upload_file&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;local_path&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;S3_BUCKET&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;s3_key&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

        &lt;span class="c1"&gt;# OCR with Bedrock
&lt;/span&gt;        &lt;span class="n"&gt;text&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;ocr_image_with_bedrock&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;local_path&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
        &lt;span class="n"&gt;ocr_results&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;filename&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;text&lt;/span&gt;

    &lt;span class="c1"&gt;# Create grouped DOCX files
&lt;/span&gt;    &lt;span class="nf"&gt;create_grouped_documents&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;ocr_results&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F6y5o7xoa5be6ad5x1hvt.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F6y5o7xoa5be6ad5x1hvt.png" alt=" " width="742" height="291"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h3&gt;
  
  
  Step 5: UI/UX Enhancement
&lt;/h3&gt;

&lt;p&gt;The first UI worked but looked basic. I wanted something modern.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;My Request&lt;/strong&gt;:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;Me&lt;/strong&gt;: "The dashboard icons and colors look unacceptable. Search for modern color schemes and upgrade the UI."&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;Kiro transformed the interface with:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Modern dark theme&lt;/strong&gt; with purple/blue gradients&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Glassmorphism effects&lt;/strong&gt; on cards&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Animated background patterns&lt;/strong&gt;&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Mobile-responsive design&lt;/strong&gt;&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Success animations&lt;/strong&gt; for feedback
&lt;/li&gt;
&lt;/ul&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight css"&gt;&lt;code&gt;&lt;span class="c"&gt;/* Kiro's modern design system */&lt;/span&gt;
&lt;span class="nd"&gt;:root&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="py"&gt;--primary&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="m"&gt;#6366f1&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="py"&gt;--secondary&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="m"&gt;#0ea5e9&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="py"&gt;--accent&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="m"&gt;#8b5cf6&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="py"&gt;--gradient&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;linear-gradient&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="m"&gt;135deg&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="m"&gt;#6366f1&lt;/span&gt; &lt;span class="m"&gt;0%&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="m"&gt;#8b5cf6&lt;/span&gt; &lt;span class="m"&gt;50%&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="m"&gt;#0ea5e9&lt;/span&gt; &lt;span class="m"&gt;100%&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;Result&lt;/strong&gt;: Beautiful professional interface!&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fofz5lyzvp49chxsdh17x.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fofz5lyzvp49chxsdh17x.png" alt=" " width="800" height="835"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;You can upload 2 image files with naming convention such as - and click &lt;strong&gt;Convert to DOCX&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fk8w4ko6tpnl2r1r22o8i.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fk8w4ko6tpnl2r1r22o8i.png" alt=" " width="800" height="605"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;It will take a while, around 1 minute to complete.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fn4b9eeumwwao8kyqsi8w.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fn4b9eeumwwao8kyqsi8w.png" alt=" " width="800" height="270"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F0kzm4ncx1htdgejko34r.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F0kzm4ncx1htdgejko34r.png" alt=" " width="800" height="626"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Demo
&lt;/h2&gt;

&lt;p&gt;Please watch this demo at &lt;a href="https://haianh-sing.s3.ap-southeast-1.amazonaws.com/2026-01-06+21-56-16.mp4" rel="noopener noreferrer"&gt;https://haianh-sing.s3.ap-southeast-1.amazonaws.com/2026-01-06+21-56-16.mp4&lt;/a&gt;.&lt;/p&gt;

&lt;h2&gt;
  
  
  What We Built Together
&lt;/h2&gt;

&lt;p&gt;After working with Kiro for just &lt;strong&gt;1 hour&lt;/strong&gt;, we had a complete, production-ready Intelligent Document Processing platform that would have taken days to build manually.&lt;/p&gt;

&lt;h3&gt;
  
  
  The Power of AI-Assisted Development
&lt;/h3&gt;

&lt;h3&gt;
  
  
  Features Kiro Built
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;AI-Powered OCR&lt;/strong&gt;: Claude Sonnet 4 with 95%+ accuracy&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Mobile-First&lt;/strong&gt;: Works with any smartphone camera&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Vietnamese Support&lt;/strong&gt;: Preserves official document formatting&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Multi-Page&lt;/strong&gt;: Automatic grouping by filename&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Web Interface&lt;/strong&gt;: Beautiful, responsive dark theme design&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Cloud-Native&lt;/strong&gt;: AWS S3, Bedrock, EC2 Graviton (ARM64)&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Secure&lt;/strong&gt;: IAM roles with least-privilege access&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Cost-Effective&lt;/strong&gt;: $0.003 per page&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Production-Ready&lt;/strong&gt;: Deployed to AWS with monitoring&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Getting Started: Deploy Your Own System
&lt;/h2&gt;

&lt;h3&gt;
  
  
  Prerequisites
&lt;/h3&gt;

&lt;p&gt;&lt;strong&gt;AWS Account Setup&lt;/strong&gt;:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;AWS Account with Bedrock access in &lt;code&gt;us-west-2&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;Enable Claude Sonnet 4 model in Bedrock console&lt;/li&gt;
&lt;li&gt;Create IAM user with admin access (for initial setup)&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;&lt;strong&gt;Local Development&lt;/strong&gt;:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Python 3.9 or higher installed&lt;/li&gt;
&lt;li&gt;AWS CLI configured (&lt;code&gt;aws configure&lt;/code&gt;)&lt;/li&gt;
&lt;li&gt;Git installed&lt;/li&gt;
&lt;/ol&gt;

&lt;h3&gt;
  
  
  Quick Start (5 Minutes)
&lt;/h3&gt;

&lt;h4&gt;
  
  
  Deployment on EC2
&lt;/h4&gt;

&lt;p&gt;&lt;strong&gt;Step 1: Create IAM Role&lt;/strong&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="c"&gt;# File iam-trust-policy.json containing trust policy&lt;/span&gt;
&lt;span class="o"&gt;{&lt;/span&gt;
    &lt;span class="s2"&gt;"Version"&lt;/span&gt;: &lt;span class="s2"&gt;"2012-10-17"&lt;/span&gt;,
    &lt;span class="s2"&gt;"Statement"&lt;/span&gt;: &lt;span class="o"&gt;[&lt;/span&gt;
        &lt;span class="o"&gt;{&lt;/span&gt;
            &lt;span class="s2"&gt;"Sid"&lt;/span&gt;: &lt;span class="s2"&gt;"S3BucketAccess"&lt;/span&gt;,
            &lt;span class="s2"&gt;"Effect"&lt;/span&gt;: &lt;span class="s2"&gt;"Allow"&lt;/span&gt;,
            &lt;span class="s2"&gt;"Action"&lt;/span&gt;: &lt;span class="o"&gt;[&lt;/span&gt;
                &lt;span class="s2"&gt;"s3:CreateBucket"&lt;/span&gt;,
                &lt;span class="s2"&gt;"s3:PutObject"&lt;/span&gt;,
                &lt;span class="s2"&gt;"s3:GetObject"&lt;/span&gt;,
                &lt;span class="s2"&gt;"s3:ListBucket"&lt;/span&gt;,
                &lt;span class="s2"&gt;"s3:DeleteObject"&lt;/span&gt;,
                &lt;span class="s2"&gt;"s3:HeadBucket"&lt;/span&gt;
            &lt;span class="o"&gt;]&lt;/span&gt;,
            &lt;span class="s2"&gt;"Resource"&lt;/span&gt;: &lt;span class="o"&gt;[&lt;/span&gt;
                &lt;span class="s2"&gt;"arn:aws:s3:::ocr-to-docx"&lt;/span&gt;,
                &lt;span class="s2"&gt;"arn:aws:s3:::ocr-to-docx/*"&lt;/span&gt;
            &lt;span class="o"&gt;]&lt;/span&gt;
        &lt;span class="o"&gt;}&lt;/span&gt;,
        &lt;span class="o"&gt;{&lt;/span&gt;
            &lt;span class="s2"&gt;"Sid"&lt;/span&gt;: &lt;span class="s2"&gt;"BedrockInvoke"&lt;/span&gt;,
            &lt;span class="s2"&gt;"Effect"&lt;/span&gt;: &lt;span class="s2"&gt;"Allow"&lt;/span&gt;,
            &lt;span class="s2"&gt;"Action"&lt;/span&gt;: &lt;span class="o"&gt;[&lt;/span&gt;
                &lt;span class="s2"&gt;"bedrock:InvokeModel"&lt;/span&gt;
            &lt;span class="o"&gt;]&lt;/span&gt;,
            &lt;span class="s2"&gt;"Resource"&lt;/span&gt;: &lt;span class="s2"&gt;"*"&lt;/span&gt;
        &lt;span class="o"&gt;}&lt;/span&gt;
    &lt;span class="o"&gt;]&lt;/span&gt;
&lt;span class="o"&gt;}&lt;/span&gt;

&lt;span class="c"&gt;# Create role with trust policy&lt;/span&gt;
aws iam create-role &lt;span class="se"&gt;\&lt;/span&gt;
    &lt;span class="nt"&gt;--role-name&lt;/span&gt; Image2Docx-EC2-Role &lt;span class="se"&gt;\&lt;/span&gt;
    &lt;span class="nt"&gt;--assume-role-policy-document&lt;/span&gt; file://iam-trust-policy.json

&lt;span class="c"&gt;# Attach permissions policy&lt;/span&gt;
aws iam put-role-policy &lt;span class="se"&gt;\&lt;/span&gt;
    &lt;span class="nt"&gt;--role-name&lt;/span&gt; Image2Docx-EC2-Role &lt;span class="se"&gt;\&lt;/span&gt;
    &lt;span class="nt"&gt;--policy-name&lt;/span&gt; Image2Docx-Policy &lt;span class="se"&gt;\&lt;/span&gt;
    &lt;span class="nt"&gt;--policy-document&lt;/span&gt; file://iam-policy.json

&lt;span class="c"&gt;# Create instance profile&lt;/span&gt;
aws iam create-instance-profile &lt;span class="se"&gt;\&lt;/span&gt;
    &lt;span class="nt"&gt;--instance-profile-name&lt;/span&gt; Image2Docx-EC2-Profile

&lt;span class="c"&gt;# Add role to profile&lt;/span&gt;
aws iam add-role-to-instance-profile &lt;span class="se"&gt;\&lt;/span&gt;
    &lt;span class="nt"&gt;--instance-profile-name&lt;/span&gt; Image2Docx-EC2-Profile &lt;span class="se"&gt;\&lt;/span&gt;
    &lt;span class="nt"&gt;--role-name&lt;/span&gt; Image2Docx-EC2-Role
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;Step 2: Launch EC2 Instance&lt;/strong&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="c"&gt;# Launch Graviton instance (ARM64 - cost efficient)&lt;/span&gt;
aws ec2 run-instances &lt;span class="se"&gt;\&lt;/span&gt;
    &lt;span class="nt"&gt;--image-id&lt;/span&gt; ami-xxxxxxxxx &lt;span class="se"&gt;\ &lt;/span&gt; &lt;span class="c"&gt;# Amazon Linux 2023 ARM64&lt;/span&gt;
    &lt;span class="nt"&gt;--instance-type&lt;/span&gt; t4g.micro &lt;span class="se"&gt;\&lt;/span&gt;
    &lt;span class="nt"&gt;--key-name&lt;/span&gt; your-key-pair &lt;span class="se"&gt;\&lt;/span&gt;
    &lt;span class="nt"&gt;--security-group-ids&lt;/span&gt; sg-xxxxxxxxx &lt;span class="se"&gt;\&lt;/span&gt;
    &lt;span class="nt"&gt;--iam-instance-profile&lt;/span&gt; &lt;span class="nv"&gt;Name&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;Image2Docx-EC2-Profile &lt;span class="se"&gt;\&lt;/span&gt;
    &lt;span class="nt"&gt;--tag-specifications&lt;/span&gt; &lt;span class="s1"&gt;'ResourceType=instance,Tags=[{Key=Name,Value=Image2Docx-Server}]'&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;Step 3: Configure Security Group&lt;/strong&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="c"&gt;# Allow inbound HTTP on port 8080&lt;/span&gt;
aws ec2 authorize-security-group-ingress &lt;span class="se"&gt;\&lt;/span&gt;
    &lt;span class="nt"&gt;--group-id&lt;/span&gt; sg-xxxxxxxxx &lt;span class="se"&gt;\&lt;/span&gt;
    &lt;span class="nt"&gt;--protocol&lt;/span&gt; tcp &lt;span class="se"&gt;\&lt;/span&gt;
    &lt;span class="nt"&gt;--port&lt;/span&gt; 8080 &lt;span class="se"&gt;\&lt;/span&gt;
    &lt;span class="nt"&gt;--cidr&lt;/span&gt; 0.0.0.0/0

&lt;span class="c"&gt;# Allow SSH for administration&lt;/span&gt;
aws ec2 authorize-security-group-ingress &lt;span class="se"&gt;\&lt;/span&gt;
    &lt;span class="nt"&gt;--group-id&lt;/span&gt; sg-xxxxxxxxx &lt;span class="se"&gt;\&lt;/span&gt;
    &lt;span class="nt"&gt;--protocol&lt;/span&gt; tcp &lt;span class="se"&gt;\&lt;/span&gt;
    &lt;span class="nt"&gt;--port&lt;/span&gt; 22 &lt;span class="se"&gt;\&lt;/span&gt;
    &lt;span class="nt"&gt;--cidr&lt;/span&gt; your-ip-address/32
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;Step 4: Deploy Application&lt;/strong&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="c"&gt;# SSH to instance&lt;/span&gt;
ssh &lt;span class="nt"&gt;-i&lt;/span&gt; &lt;span class="s2"&gt;"your-key.pem"&lt;/span&gt; ec2-user@&amp;lt;EC2-PUBLIC-IP&amp;gt;

&lt;span class="c"&gt;# Update system&lt;/span&gt;
&lt;span class="nb"&gt;sudo &lt;/span&gt;yum update &lt;span class="nt"&gt;-y&lt;/span&gt;

&lt;span class="c"&gt;# Install dependencies&lt;/span&gt;
&lt;span class="nb"&gt;sudo &lt;/span&gt;yum &lt;span class="nb"&gt;install&lt;/span&gt; &lt;span class="nt"&gt;-y&lt;/span&gt; git python3-pip

&lt;span class="c"&gt;# Clone repository&lt;/span&gt;
git clone https://github.com/PNg-HA/Image2Docx.git
&lt;span class="nb"&gt;cd &lt;/span&gt;Image2Docx

&lt;span class="c"&gt;# Install Python packages&lt;/span&gt;
pip3 &lt;span class="nb"&gt;install&lt;/span&gt; &lt;span class="nt"&gt;-r&lt;/span&gt; requirements.txt

&lt;span class="c"&gt;# Run application in background&lt;/span&gt;
&lt;span class="nb"&gt;nohup &lt;/span&gt;python3 app.py &lt;span class="o"&gt;&amp;gt;&lt;/span&gt; app.log 2&amp;gt;&amp;amp;1 &amp;amp;

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;Step 5: Access Your Application&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Open browser and navigate to:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;http://&amp;lt;EC2-PUBLIC-IP&amp;gt;:8080
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;Success!&lt;/strong&gt; Your intelligent documentation platform is live.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fqsoxbhwvvwigbrlsilnq.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fqsoxbhwvvwigbrlsilnq.png" alt=" " width="726" height="213"&gt;&lt;/a&gt;&lt;/p&gt;




&lt;h2&gt;
  
  
  Conclusion: From Vision to Reality in 1 Hour with Kiro
&lt;/h2&gt;

&lt;p&gt;We started with a simple question: &lt;strong&gt;"Can we help office staff escape the paperwork trap?"&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;The answer is a resounding &lt;strong&gt;YES&lt;/strong&gt;. But what's even more remarkable is &lt;strong&gt;HOW&lt;/strong&gt; we did it. &lt;/p&gt;

&lt;p&gt;This platform is my contribution to eliminating paperwork drudgery and freeing office workers. But more importantly, this project proves that &lt;strong&gt;anyone with vision and AI assistance can build transformative solutions&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;What will you build with Kiro?&lt;/strong&gt;&lt;/p&gt;

</description>
      <category>kiro</category>
      <category>aws</category>
      <category>idp</category>
    </item>
    <item>
      <title>Elevating Product Visualization with Amazon Nova Canvas on Bedrock</title>
      <dc:creator>Phạm Nguyễn Hải Anh</dc:creator>
      <pubDate>Mon, 19 May 2025 06:37:23 +0000</pubDate>
      <link>https://dev.to/aws-builders/elevating-product-visualization-with-amazon-nova-canvas-on-bedrock-57ba</link>
      <guid>https://dev.to/aws-builders/elevating-product-visualization-with-amazon-nova-canvas-on-bedrock-57ba</guid>
      <description>&lt;p&gt;Amazon Nova Canvas is a powerful image generation service available through AWS Bedrock, enabling developers and creatives to generate, edit, and manipulate product visuals using state-of-the-art AI models. In this guide, we focus on product visualization—how Nova Canvas empowers businesses to create high-quality, customized product images for catalogs, marketing materials, and online storefronts.&lt;/p&gt;

&lt;h2&gt;
  
  
  1. Supported Task Types &amp;amp; Parameters
&lt;/h2&gt;

&lt;p&gt;Amazon Nova Canvas supports a variety of image generation and editing tasks, each customizable via specific parameters:&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;&lt;strong&gt;Task Type&lt;/strong&gt;&lt;/th&gt;
&lt;th&gt;&lt;strong&gt;Parameter Field&lt;/strong&gt;&lt;/th&gt;
&lt;th&gt;&lt;strong&gt;Category&lt;/strong&gt;&lt;/th&gt;
&lt;th&gt;&lt;strong&gt;Description&lt;/strong&gt;&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;TEXT_IMAGE (Text only)&lt;/td&gt;
&lt;td&gt;&lt;code&gt;textToImageParams&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Generation&lt;/td&gt;
&lt;td&gt;Generates images from descriptive text prompts.&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;TEXT_IMAGE (Image conditioning)&lt;/td&gt;
&lt;td&gt;&lt;code&gt;textToImageParams&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Generation&lt;/td&gt;
&lt;td&gt;Combines a reference image with text to guide layout.&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;COLOR_GUIDED_GENERATION&lt;/td&gt;
&lt;td&gt;&lt;code&gt;colorGuidedGenerationParams&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Generation&lt;/td&gt;
&lt;td&gt;Guides generation with specified hex colors and optional image.&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;IMAGE_VARIATION&lt;/td&gt;
&lt;td&gt;&lt;code&gt;imageVariationParams&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Generation&lt;/td&gt;
&lt;td&gt;Creates stylized variations of input images.&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;INPAINTING&lt;/td&gt;
&lt;td&gt;&lt;code&gt;inPaintingParams&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Editing&lt;/td&gt;
&lt;td&gt;Modifies content inside a masked region.&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;OUTPAINTING&lt;/td&gt;
&lt;td&gt;&lt;code&gt;outPaintingParams&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Editing&lt;/td&gt;
&lt;td&gt;Extends or changes areas outside a masked region.&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;BACKGROUND_REMOVAL&lt;/td&gt;
&lt;td&gt;&lt;code&gt;backgroundRemovalParams&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Editing&lt;/td&gt;
&lt;td&gt;Removes image background to create transparency.&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;h2&gt;
  
  
  2. Input and Output Specs for Product Images
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Input Images:&lt;/strong&gt; Base64-encoded PNG or JPEG
&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Accepted Formats:&lt;/strong&gt;

&lt;ul&gt;
&lt;li&gt;PNG (supports alpha channel, but transparency not always allowed)
&lt;/li&gt;
&lt;li&gt;JPEG (recommended at 100% quality)
&lt;/li&gt;
&lt;/ul&gt;


&lt;/li&gt;

&lt;li&gt;

&lt;strong&gt;Masks:&lt;/strong&gt; Must be pure black (masked area) and pure white (non-masked). No grayscale or color pixels.
&lt;/li&gt;

&lt;li&gt;

&lt;strong&gt;Supported Resolutions:&lt;/strong&gt;

&lt;ul&gt;
&lt;li&gt;From 320 to 4096 pixels per side
&lt;/li&gt;
&lt;li&gt;Aspect ratio between 1:4 and 4:1
&lt;/li&gt;
&lt;li&gt;Maximum total pixels: 4.19 million (e.g., 2048x2048)
&lt;/li&gt;
&lt;/ul&gt;


&lt;/li&gt;

&lt;li&gt;

&lt;strong&gt;Output Image Dimensions:&lt;/strong&gt; Must be divisible by 16
&lt;/li&gt;

&lt;/ul&gt;

&lt;h2&gt;
  
  
  3. Common Configuration Parameters for Consistent Product Renders
&lt;/h2&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;&lt;strong&gt;Parameter&lt;/strong&gt;&lt;/th&gt;
&lt;th&gt;&lt;strong&gt;Description&lt;/strong&gt;&lt;/th&gt;
&lt;th&gt;&lt;strong&gt;Default&lt;/strong&gt;&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;
&lt;code&gt;width&lt;/code&gt;, &lt;code&gt;height&lt;/code&gt;
&lt;/td&gt;
&lt;td&gt;Output image size&lt;/td&gt;
&lt;td&gt;1024 x 1024&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;quality&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Image quality, &lt;code&gt;"standard"&lt;/code&gt; or &lt;code&gt;"premium"&lt;/code&gt;
&lt;/td&gt;
&lt;td&gt;&lt;code&gt;"standard"&lt;/code&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;cfgScale&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Prompt adherence strength (1.1–10)&lt;/td&gt;
&lt;td&gt;6.5&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;numberOfImages&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Number of images to generate (1–5)&lt;/td&gt;
&lt;td&gt;1&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;seed&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Random generation control for reproducibility&lt;/td&gt;
&lt;td&gt;12&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;h2&gt;
  
  
  4. Example: Creating a Product Render from Text
&lt;/h2&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight json"&gt;&lt;code&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"taskType"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"TEXT_IMAGE"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"textToImageParams"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"text"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"A futuristic city skyline at sunset"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"negativeText"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"No people, no cars"&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="p"&gt;},&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"imageGenerationConfig"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"width"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;1024&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"height"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;1024&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"quality"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"standard"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"cfgScale"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mf"&gt;6.5&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"seed"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;12&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"numberOfImages"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;Note:&lt;/strong&gt; Avoid negations like "no" or "without" in &lt;code&gt;text&lt;/code&gt; and &lt;code&gt;negativeText&lt;/code&gt;; instead, explicitly specify what to exclude in &lt;code&gt;negativeText&lt;/code&gt;.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;&lt;strong&gt;Input&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Prompt: A high-resolution studio photo of a pair of stylish white leather sneakers placed on a clean light gray background. The shoes are angled slightly to show both the side and top view. Soft diffused lighting, no shadows or background distractions. Ideal for an online product catalog.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight json"&gt;&lt;code&gt;&lt;span class="err"&gt;body&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="err"&gt;=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="err"&gt;json.dumps(&lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"taskType"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"TEXT_IMAGE"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"textToImageParams"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
        &lt;/span&gt;&lt;span class="nl"&gt;"text"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="err"&gt;prompt&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="p"&gt;},&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"imageGenerationConfig"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
        &lt;/span&gt;&lt;span class="nl"&gt;"numberOfImages"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
        &lt;/span&gt;&lt;span class="nl"&gt;"height"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;1024&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
        &lt;/span&gt;&lt;span class="nl"&gt;"width"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;1024&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
        &lt;/span&gt;&lt;span class="nl"&gt;"cfgScale"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mf"&gt;8.0&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
        &lt;/span&gt;&lt;span class="nl"&gt;"seed"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="err"&gt;)&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;Ouput&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fsing-david-gapv.s3.ap-southeast-1.amazonaws.com%2Fa1.PNG" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fsing-david-gapv.s3.ap-southeast-1.amazonaws.com%2Fa1.PNG" alt="Nova Canvas" width="800" height="800"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  5. Image Conditioning with Reference Images
&lt;/h2&gt;

&lt;p&gt;Amazon Nova supports image generation guided by a reference image with control modes such as:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;CANNY_EDGE&lt;/strong&gt; – preserves edge outlines
&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;SEGMENTATION&lt;/strong&gt; – preserves content shapes
&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;Key parameters:&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;code&gt;conditionImage&lt;/code&gt;: Base64-encoded guiding image
&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;controlMode&lt;/code&gt;: &lt;code&gt;"CANNY_EDGE"&lt;/code&gt; or &lt;code&gt;"SEGMENTATION"&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;controlStrength&lt;/code&gt;: Degree to which generated image follows guide (0 to 1)
&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;Input&lt;/strong&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;prompt = (
    "Add a small black Nike logo on the side of each white sneaker. "
    "Keep the shoes clean, white, and minimalistic, in a professional studio setting. "
    "Ensure the lighting and shadows remain soft and realistic. "
    "Maintain the same shoe position and clean background."
)
negativeText = "blurry, distorted logo, cartoon, low resolution, watermark, logo on wrong location, off-center"
body = json.dumps(
    {
        "taskType": "TEXT_IMAGE",
        "textToImageParams": {
            "text": prompt,
            "negativeText": negativeText,
            "conditionImage": input_image,
            "controlMode": "CANNY_EDGE",
        },
        "imageGenerationConfig": {
            "height": 1024,
            "width": 1024,
            "quality": "premium",
            "cfgScale": 8.0,
            "seed": 12,
            "numberOfImages": 1,
        },
    }
)
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fsing-david-gapv.s3.ap-southeast-1.amazonaws.com%2Fa2.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fsing-david-gapv.s3.ap-southeast-1.amazonaws.com%2Fa2.png" alt="Nova Canvas" width="800" height="800"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Ouput&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fsing-david-gapv.s3.ap-southeast-1.amazonaws.com%2Fa3.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fsing-david-gapv.s3.ap-southeast-1.amazonaws.com%2Fa3.png" alt="Nova Canvas" width="800" height="800"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Input&lt;/strong&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;body = json.dumps(
    {
        "taskType": "TEXT_IMAGE",
        "textToImageParams": {
            "text": prompt,
            "negativeText": negativeText,
            "conditionImage": input_image,
            "controlMode": "SEGMENTATION",
        },
        "imageGenerationConfig": {
            "height": 1024,
            "width": 1024,
            "quality": "premium",
            "cfgScale": 8.0,
            "seed": 12,
            "numberOfImages": 1,
        },
    }
)
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fsing-david-gapv.s3.ap-southeast-1.amazonaws.com%2Fa2.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fsing-david-gapv.s3.ap-southeast-1.amazonaws.com%2Fa2.png" alt="Nova Canvas" width="800" height="800"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Ouput&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fsing-david-gapv.s3.ap-southeast-1.amazonaws.com%2Fa4.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fsing-david-gapv.s3.ap-southeast-1.amazonaws.com%2Fa4.png" alt="Nova Canvas" width="800" height="800"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  6. Apply Brand Colors with Color-Guided Generation
&lt;/h2&gt;

&lt;p&gt;Specify up to 10 hex color codes to influence the color palette of generated images. This can be combined with a style reference image for artistic control.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Input&lt;/strong&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;body = json.dumps(
    {
        "taskType": "COLOR_GUIDED_GENERATION",
        "colorGuidedGenerationParams": {
            "text": "a stylish pair of pink sneakers, same shape and angle as the reference, realistic lighting, minimalistic background",
            "negativeText": "white shoes, overexposed, harsh lighting, low resolution, unrealistic shadows, cluttered background, watermark",
            "referenceImage": input_image,
            "colors": [
                "#ffc0cb",  # Pink
                "#ffb6c1",  # LightPink
                "#f4cccc",  # PalePink
                "#000000",  # (for some depth/shadow)
            ],
        },
        "imageGenerationConfig": {
            "numberOfImages": 1,
            "height": 1024,
            "width": 1024,
            "cfgScale": 8.0,
        },
    }
)
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fsing-david-gapv.s3.ap-southeast-1.amazonaws.com%2Fa5.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fsing-david-gapv.s3.ap-southeast-1.amazonaws.com%2Fa5.png" alt="Nova Canvas" width="800" height="800"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Ouput&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fsing-david-gapv.s3.ap-southeast-1.amazonaws.com%2Fa6.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fsing-david-gapv.s3.ap-southeast-1.amazonaws.com%2Fa6.png" alt="Nova Canvas" width="800" height="800"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  7. Generate Variations of Existing Product Photos
&lt;/h2&gt;

&lt;p&gt;Generate stylized versions of input images by adjusting the &lt;code&gt;similarityStrength&lt;/code&gt; parameter, which controls how much variation is applied relative to the original image.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Input&lt;/strong&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;body = json.dumps(
    {
        "taskType": "IMAGE_VARIATION",
        "imageVariationParams": {
            "text": "A more modern version of this sunscreen product tube, same layout, 3D render, clean white background",
            "negativeText": "blurry text, distorted label, different shape, wrong lighting, added elements, different background",
            "images": images,
            "similarityStrength": 0.5,  # Range: 0.2 to 1.0
        },
        "imageGenerationConfig": {
            "numberOfImages": 2,
            "height": 1024,
            "width": 1024,
            "cfgScale": 9.5,
        },
    }
)
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fsing-david-gapv.s3.ap-southeast-1.amazonaws.com%2Fa7.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fsing-david-gapv.s3.ap-southeast-1.amazonaws.com%2Fa7.png" alt="Nova Canvas" width="800" height="400"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Ouput&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fsing-david-gapv.s3.ap-southeast-1.amazonaws.com%2Fa8.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fsing-david-gapv.s3.ap-southeast-1.amazonaws.com%2Fa8.png" alt="Nova Canvas" width="800" height="800"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  8. Use Inpainting/Outpainting to Clean or Extend Product Images
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Inpainting:&lt;/strong&gt; Edit specific masked regions inside an image based on a mask or natural language description.
&lt;/li&gt;
&lt;/ul&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;body = json.dumps(
    {
        "taskType": "INPAINTING",
        "inPaintingParams": {
            "image": string (Base64 encoded image),
            "maskPrompt": string,
            "maskImage": string (Base64 encoded image),
            "text": string,
            "negativeText": string
        },
        "imageGenerationConfig": {
            "numberOfImages": int,
            "quality": "standard" | "premium",
            "cfgScale": float,
            "seed": int
        }
    }
)
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;Input&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fsing-david-gapv.s3.ap-southeast-1.amazonaws.com%2Fa18.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fsing-david-gapv.s3.ap-southeast-1.amazonaws.com%2Fa18.png" alt="Nova Canvas" width="800" height="400"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Output&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fsing-david-gapv.s3.ap-southeast-1.amazonaws.com%2Fa19.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fsing-david-gapv.s3.ap-southeast-1.amazonaws.com%2Fa19.png" alt="Nova Canvas" width="800" height="400"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Outpainting:&lt;/strong&gt; Extend image content beyond its current borders, with two modes:

&lt;ul&gt;
&lt;li&gt;
&lt;code&gt;"DEFAULT"&lt;/code&gt; (blended, natural extension)
&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;"PRECISE"&lt;/code&gt; (strict, exact extension)
&lt;/li&gt;
&lt;/ul&gt;


&lt;/li&gt;

&lt;/ul&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;body = json.dumps(
    {
        "taskType": "OUTPAINTING",
        "outPaintingParams": {
            "image": string (Base64 encoded image),
            "maskPrompt": string,
            "maskImage": string (Base64 encoded image),
            "outPaintingMode": "DEFAULT" | "PRECISE",
            "text": string,
            "negativeText": string
        },
        "imageGenerationConfig": {
            "numberOfImages": int,
            "quality": "standard" | "premium"
            "cfgScale": float,
            "seed": int
        }
    }
)
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;Input&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fsing-david-gapv.s3.ap-southeast-1.amazonaws.com%2Fa20.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fsing-david-gapv.s3.ap-southeast-1.amazonaws.com%2Fa20.png" alt="Nova Canvas" width="800" height="400"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Output&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fsing-david-gapv.s3.ap-southeast-1.amazonaws.com%2Fa21.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fsing-david-gapv.s3.ap-southeast-1.amazonaws.com%2Fa21.png" alt="Nova Canvas" width="800" height="400"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  9. Clean Cutouts with Background Removal
&lt;/h2&gt;

&lt;p&gt;Amazon Nova can isolate foreground objects by removing backgrounds, producing PNG outputs with full transparency. This is ideal for compositing workflows.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Input&lt;/strong&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;body = json.dumps(
    {
        "taskType": "BACKGROUND_REMOVAL",
        "backgroundRemovalParams": {
            "image": input_image
        }
    }
)
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fsing-david-gapv.s3.ap-southeast-1.amazonaws.com%2Fa15.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fsing-david-gapv.s3.ap-southeast-1.amazonaws.com%2Fa15.png" alt="Nova Canvas" width="800" height="800"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Ouput&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fsing-david-gapv.s3.ap-southeast-1.amazonaws.com%2Fa16.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fsing-david-gapv.s3.ap-southeast-1.amazonaws.com%2Fa16.png" alt="Nova Canvas" width="800" height="800"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  10. Error Handling
&lt;/h2&gt;

&lt;p&gt;Common error types include:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;ValidationException:&lt;/strong&gt; Invalid input format or unsupported parameters
&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;RAI Input Deflection:&lt;/strong&gt; Input blocked by responsible AI content filters
&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;RAI Output Deflection:&lt;/strong&gt; Generated image blocked due to moderation policies
&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Conclusion
&lt;/h2&gt;

&lt;p&gt;Amazon Nova Canvas offers e-commerce teams, designers, and marketers a transformative tool for creating high-quality product visuals—quickly, repeatedly, and at scale. Whether launching new SKUs, running A/B tests on creatives, or enriching catalog listings, Nova Canvas provides unparalleled creative control.&lt;/p&gt;

&lt;h2&gt;
  
  
  What’s Next?
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;Explore &lt;a href="https://docs.aws.amazon.com/bedrock/" rel="noopener noreferrer"&gt;Amazon Bedrock documentation&lt;/a&gt; for model updates.&lt;/li&gt;
&lt;li&gt;Try integrating Nova Canvas into your creative or e-commerce workflows.&lt;/li&gt;
&lt;li&gt;Stay tuned for more features like image-to-image translation and control-net guided generation.&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  References
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;&lt;a href="https://docs.aws.amazon.com/nova/latest/userguide/image-generation.html" rel="noopener noreferrer"&gt;Generating images with Amazon Nova – Amazon Nova&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://aws.amazon.com/blogs/machine-learning/exploring-creative-possibilities-a-visual-guide-to-amazon-nova-canvas/" rel="noopener noreferrer"&gt;Exploring creative possibilities: A visual guide to Amazon Nova Canvas | AWS Machine Learning Blog&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;
&lt;a href="https://github.com/aws-samples/amazon-nova-samples" rel="noopener noreferrer"&gt;GitHub – aws-samples/amazon-nova-samples&lt;/a&gt; &lt;/li&gt;
&lt;li&gt;&lt;a href="https://github.com/aws-samples/allthings-bedrock-nova-models/tree/main" rel="noopener noreferrer"&gt;aws-samples/allthings-bedrock-nova-models: Amazon Nova models – Python examples&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;
&lt;a href="https://aws.amazon.com/ai/generative-ai/nova/" rel="noopener noreferrer"&gt;Amazon Nova landing page&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;&lt;a href="https://docs.aws.amazon.com/nova/latest/userguide/image-gen-req-resp-structure.html" rel="noopener noreferrer"&gt;Request and response structure for image generation – Amazon Nova&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://docs.aws.amazon.com/bedrock/latest/userguide/model-parameters-nova-canvas.html#nova-canvas-access-usage" rel="noopener noreferrer"&gt;Image generation access and usage – Amazon Nova&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://docs.aws.amazon.com/nova/latest/userguide/image-gen-code-examples.html" rel="noopener noreferrer"&gt;Code examples – Amazon Nova&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;

</description>
      <category>aws</category>
      <category>design</category>
    </item>
    <item>
      <title>AI HR Assistant with Amazon Bedrock Agent</title>
      <dc:creator>Phạm Nguyễn Hải Anh</dc:creator>
      <pubDate>Mon, 19 May 2025 04:46:40 +0000</pubDate>
      <link>https://dev.to/aws-builders/ai-hr-assistant-with-amazon-bedrock-agent-4l3</link>
      <guid>https://dev.to/aws-builders/ai-hr-assistant-with-amazon-bedrock-agent-4l3</guid>
      <description>&lt;h1&gt;
  
  
  Introduction
&lt;/h1&gt;

&lt;p&gt;As organizations grow and evolve, so do the demands on Human Resources. Internal teams often face repetitive, time-consuming tasks—like answering common policy questions, processing leave requests, or managing onboarding queries—that can slow down productivity and strain HR capacity. An AI-powered HR assistant offers a smart, scalable solution to this challenge. This not only reduces the administrative burden on HR staff but also enhances the employee experience through quick resolution of queries and more personalized support.&lt;/p&gt;

&lt;p&gt;This blog helps you to create an AI HR assistant that can give you internal regulations, automate the Time Leave request by using Amazon Bedrock Nova Pro model.&lt;/p&gt;

&lt;h1&gt;
  
  
  Architecture
&lt;/h1&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Ff6tv5j5npg4rggxzr6c7.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Ff6tv5j5npg4rggxzr6c7.png" alt=" " width="800" height="590"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h1&gt;
  
  
  Prerequisites
&lt;/h1&gt;

&lt;ol&gt;
&lt;li&gt;AWS credential grants Bedrock, Lambda, S3, Athena, Glue, OpenSearch permissions&lt;/li&gt;
&lt;li&gt;Linux environment&lt;/li&gt;
&lt;/ol&gt;

&lt;h1&gt;
  
  
  Steps
&lt;/h1&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;git clone https://github.com/aws-samples/amazon-bedrock-samples.git
cd amazon-bedrock-samples/tree/main/agents-and-function-calling/bedrock-agents/use-case-examples/hr-assistant/shell
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Use the suitable model (in this blog I use Nova Pro) by editing file &lt;code&gt;cfn&lt;br&gt;
/hr-resources.yml&lt;/code&gt;, change &lt;code&gt;amazon.titan-text-premier-v1:0&lt;/code&gt; to &lt;code&gt;amazon.nova-pro-v1:0&lt;/code&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Ftqlig688itydrl5kec3x.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Ftqlig688itydrl5kec3x.png" alt=" " width="800" height="190"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Run the script:&lt;br&gt;
&lt;code&gt;source ./create-hr-resources.sh&lt;/code&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Feyvfdw1rvcvaqmg0vvf6.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Feyvfdw1rvcvaqmg0vvf6.png" alt=" " width="800" height="286"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Check mail:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fzk8lx7j15qds92g8iib9.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fzk8lx7j15qds92g8iib9.png" alt=" " width="800" height="222"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Subscribe topic:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Ffj1uzgpc7dxq9hjt5197.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Ffj1uzgpc7dxq9hjt5197.png" alt=" " width="800" height="351"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Once the deployment is finished. You can use the Bedrock Agent Playground:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fhcb9n8ive69ytwwsm5lr.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fhcb9n8ive69ytwwsm5lr.png" alt=" " width="800" height="330"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fb76oef89mj47f1avydzi.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fb76oef89mj47f1avydzi.png" alt=" " width="800" height="314"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Or you can create a web frontend with streamlit. First install streamlit:&lt;br&gt;
&lt;code&gt;pip install -r agents-and-function-calling/bedrock-agents/use-case-examples/hr-assistant/streamlit/requirements.txt&lt;/code&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fmbzmqh6q5lgs02lhu7di.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fmbzmqh6q5lgs02lhu7di.png" alt=" " width="800" height="73"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;then &lt;code&gt;cd hr-assistant/streamlit&lt;/code&gt; and run &lt;code&gt;streamlit run agent_streamlit.py&lt;/code&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fpteynumt1rqdpluex74z.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fpteynumt1rqdpluex74z.png" alt=" " width="636" height="375"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Check history query database employee trong Athena:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fcu1zpjcw6on1b6usaa8a.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fcu1zpjcw6on1b6usaa8a.png" alt=" " width="800" height="216"&gt;&lt;/a&gt;&lt;/p&gt;
&lt;h1&gt;
  
  
  Troubleshooting
&lt;/h1&gt;
&lt;h2&gt;
  
  
  Bug 1:
&lt;/h2&gt;

&lt;p&gt;Bug: &lt;code&gt;User: arn:aws:iam::your-account-id:user/user-name is not authorized to perform: lambda:GetLayerVersion on resource: arn:aws:lambda:us-east-1:017000801446:layer:AWSLambdaPowertoolsPythonV2:68 because no resource-based policy allows the lambda:GetLayerVersion action (Service: Lambda, Status Code: 403, ..., HandlerErrorCode: AccessDenied)&lt;/code&gt;&lt;br&gt;
Solution: Find the line containing &lt;code&gt;arn:aws:lambda:us-east-1:017000801446:layer:AWSLambdaPowertoolsPythonV2:68&lt;/code&gt; in file hr-resources.yml and change the region to where you deploy.&lt;/p&gt;
&lt;h1&gt;
  
  
  Redeploy and bug
&lt;/h1&gt;

&lt;p&gt;Bug: The script create-hr-resources.sh exits 1 and exit the terminal&lt;/p&gt;

&lt;p&gt;Explanation: The script is created for 1 time usage as creating a database and a table. Hence when you redeploy in the same region, make sure to delete all created resources, including the CloudFormation stack. To remove the database in Athena, first delete all data in the table:&lt;/p&gt;

&lt;p&gt;Delete table in Amazon Glue:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fx9ccbz8pfu6ktubjbe9r.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fx9ccbz8pfu6ktubjbe9r.png" alt=" " width="800" height="219"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;then run this command:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;aws athena start-query-execution --query-string "DROP DATABASE employee" --result-configuration "OutputLocation=s3://&amp;lt;your-accound-id&amp;gt;--hr-resources"
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fvom2uo9k3xe6ns34ywln.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fvom2uo9k3xe6ns34ywln.png" alt=" " width="800" height="44"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;You can check the status of the query in the Athena Console:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F7n3qxm574h4auv6g6ihb.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F7n3qxm574h4auv6g6ihb.png" alt=" " width="800" height="154"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;If Status Failed, ensure database has no data. &lt;/p&gt;

&lt;h2&gt;
  
  
  Bug 3:
&lt;/h2&gt;

&lt;p&gt;Error: &lt;code&gt;Runtime.ImportModuleError: Unable to import module 'lambda.index': No module named 'requests' Traceback (most recent call last)&lt;/code&gt;&lt;/p&gt;

&lt;p&gt;Solution: replace &lt;code&gt;from botocore.vendored import requests&lt;/code&gt; (ref: &lt;a href="https://stackoverflow.com/questions/48912253/aws-lambda-unable-to-import-module-lambda-function-no-module-named-requests" rel="noopener noreferrer"&gt;https://stackoverflow.com/questions/48912253/aws-lambda-unable-to-import-module-lambda-function-no-module-named-requests&lt;/a&gt;)&lt;/p&gt;

&lt;h1&gt;
  
  
  Conclusion
&lt;/h1&gt;

&lt;p&gt;Integrating an AI-powered HR assistant using Amazon Bedrock streamlines internal HR operations by automating repetitive tasks and providing instant, accurate responses to employee queries. This guide demonstrated how to deploy the solution end-to-end—from setting up resources to building a user-friendly Streamlit interface—enabling HR teams to focus on strategic initiatives while improving the employee experience. By leveraging the power of generative AI and scalable AWS services, organizations can create a more efficient, responsive, and modern HR function.&lt;/p&gt;

</description>
    </item>
    <item>
      <title>Boost Your English Reflexes with Amazon Nova Sonic</title>
      <dc:creator>Phạm Nguyễn Hải Anh</dc:creator>
      <pubDate>Sat, 17 May 2025 19:04:33 +0000</pubDate>
      <link>https://dev.to/aws-builders/boost-your-english-reflexes-with-amazon-nova-sonic-2gfc</link>
      <guid>https://dev.to/aws-builders/boost-your-english-reflexes-with-amazon-nova-sonic-2gfc</guid>
      <description>&lt;h1&gt;
  
  
  Scenario
&lt;/h1&gt;

&lt;p&gt;Many people want to improve their English speaking reflexes simply to communicate effectively at work, not to master academic grammar or pass expensive language exams. Traditional language courses or private tutors can be costly and time-consuming, creating a barrier for those who just need practical, job-related English skills. AI-powered tools offer a more affordable and flexible alternative, allowing learners to practice real-life conversations anytime, receive instant feedback, and build confidence in speaking. This makes AI a smart choice for professionals who want fast, accessible, and focused language support.&lt;/p&gt;

&lt;p&gt;In this blog, we’ll show you how to build your own AI-powered English tutor—designed specifically for everyday professionals who just want to speak up and be heard.&lt;/p&gt;

&lt;h1&gt;
  
  
  Workflow
&lt;/h1&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F2xpmockh1tzivf3gkzxt.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F2xpmockh1tzivf3gkzxt.png" alt=" " width="800" height="151"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Workflow: &lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;p&gt;Frontend (React Web Client): Provides the user interface and communicates with the backend via WebSocket for real-time interaction.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Backend (Python WebSocket Server): Manages client sessions and relays data between the frontend and the AI model.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Streaming &amp;amp; AI Integration: Streams audio/text to Amazon Bedrock Nova Sonic and returns real-time AI responses to the frontend.&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;h1&gt;
  
  
  Prerequisites
&lt;/h1&gt;

&lt;h2&gt;
  
  
  Install dependencies
&lt;/h2&gt;

&lt;p&gt;You will need:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Python v3.12+ and pip&lt;/li&gt;
&lt;li&gt;Node v14+ and NPM&lt;/li&gt;
&lt;li&gt;AWS boto3&lt;/li&gt;
&lt;li&gt;AWS credentials that granted Bedrock access&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;Run the following codes to install pip, AWS CLI for ARM (latest version), NPM (10.9.2) and Node (22.15.1) if you use Ubuntu:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;sudo apt-get update
sudo apt-get upgrade

# Install pip
sudo apt install python3-pip

# install aws cli latest version
sudo apt install unzip
curl "https://awscli.amazonaws.com/awscli-exe-linux-aarch64.zip" -o "awscliv2.zip"
unzip awscliv2.zip
sudo ./aws/install

# Download and install nvm:
curl -o- https://raw.githubusercontent.com/nvm-sh/nvm/v0.40.3/install.sh | bash
# in lieu of restarting the shell
\. "$HOME/.nvm/nvm.sh"
# Download and install Node.js:
nvm install 22
# Verify the Node.js version:
node -v # Should print "v22.15.1".
nvm current # Should print "v22.15.1".
# Verify npm version:
npm -v # Should print "10.9.2".
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h1&gt;
  
  
  Installation
&lt;/h1&gt;

&lt;p&gt;Currently, the project just works at localhost.&lt;br&gt;
Clone the repo:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;git clone https://github.com/PNg-HA/BoostEnglishSpeakingReflexes_NovaSonic.git
cd BoostEnglishSpeakingReflexes_NovaSonic
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Start the Server
&lt;/h2&gt;

&lt;p&gt;Create a virtual envirionment with venv module&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;cd python-server
python3 -m venv .venv
pip install -r requirements.txt
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fft1byxs64ny43nhc60a3.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fft1byxs64ny43nhc60a3.png" alt=" " width="800" height="265"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;then set the environment variables.&lt;/p&gt;

&lt;p&gt;Windows:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;$env:AWS_ACCESS_KEY_ID="your-key"
$env:AWS_SECRET_ACCESS_KEY="your-secret-key"
$env:AWS_DEFAULT_REGION="us-east-1"
$env:HOST="localhost"
$env:WS_PORT=8081
$env:HEALTH_PORT=8082
$env:AWS_PROFILE='default'
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Linux:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;export AWS_ACCESS_KEY_ID="your-key"
export AWS_SECRET_ACCESS_KEY="your-secret-key"
export AWS_DEFAULT_REGION="us-east-1"
export HOST="localhost"
export WS_PORT=8081
export HEALTH_PORT=8082
export AWS_PROFILE='default'
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Run the server:&lt;br&gt;
&lt;code&gt;python3 server.py&lt;/code&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fhhnjyumzbxmnsduo6otr.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fhhnjyumzbxmnsduo6otr.png" alt=" " width="800" height="167"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Note: You must leave this terminal open to run the server. For the later steps, open a new terminal.&lt;/strong&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Start frontend
&lt;/h2&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;cd BoostEnglishSpeakingReflexes_NovaSonic/react-client
npm install
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Set the environment variables:&lt;br&gt;
Linux:&lt;br&gt;
&lt;code&gt;export REACT_APP_WEBSOCKET_URL='ws://localhost:8081'&lt;/code&gt;&lt;br&gt;
Windows:&lt;br&gt;
&lt;code&gt;$env:REACT_APP_WEBSOCKET_URL = "ws://localhost:8081"&lt;/code&gt;&lt;/p&gt;

&lt;p&gt;Start the frontend:&lt;br&gt;
&lt;code&gt;npm start&lt;/code&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fmqznwisdq47cmostscqb.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fmqznwisdq47cmostscqb.png" alt=" " width="800" height="84"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Wait for seconds to load&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fhpxoawruycrdvgr2eyji.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fhpxoawruycrdvgr2eyji.png" alt=" " width="722" height="266"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;One website will be opened automatically&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fh59ptltjtwzrzkfwo1vk.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fh59ptltjtwzrzkfwo1vk.png" alt=" " width="800" height="280"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Ff7p0r6mekbaw8ngzl3mj.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Ff7p0r6mekbaw8ngzl3mj.png" alt=" " width="800" height="276"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;You can set up the scenario through the prompt by going to Setting button:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Frxw6l765lu89k7zbt9d3.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Frxw6l765lu89k7zbt9d3.png" alt=" " width="800" height="239"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h1&gt;
  
  
  Demo
&lt;/h1&gt;

&lt;p&gt;&lt;a href="https://haianh.s3.us-east-1.amazonaws.com/AIEnglishTutor.mp4" rel="noopener noreferrer"&gt;https://haianh.s3.us-east-1.amazonaws.com/AIEnglishTutor.mp4&lt;/a&gt;&lt;/p&gt;

&lt;h1&gt;
  
  
  Conclusion
&lt;/h1&gt;

&lt;p&gt;By following this guide, you now have a functional AI-powered English speaking assistant running locally — tailored for professionals who want to improve their communication skills quickly and practically. This solution offers a low-cost, customizable alternative to traditional learning methods by leveraging modern AI models and cloud infrastructure. Whether you're preparing for daily meetings, job interviews, or cross-border collaboration, this tool empowers you to practice speaking English in realistic scenarios, on your own schedule, and with immediate feedback. It's not just a tech project — it's a step toward more confident, fluent, and effective communication in the workplace.&lt;/p&gt;

</description>
    </item>
    <item>
      <title>Optimize E-Commerce with AI Agent Using Amazon Nova Models</title>
      <dc:creator>Phạm Nguyễn Hải Anh</dc:creator>
      <pubDate>Fri, 09 May 2025 12:43:23 +0000</pubDate>
      <link>https://dev.to/aws-builders/optimize-e-commerce-with-ai-agent-using-amazon-nova-models-2fl0</link>
      <guid>https://dev.to/aws-builders/optimize-e-commerce-with-ai-agent-using-amazon-nova-models-2fl0</guid>
      <description>&lt;p&gt;In today's digital age—especially with strong government support for digital transformation—building an e-commerce website is easier than ever. However, making it &lt;em&gt;attractive, user-friendly, and sales-driven&lt;/em&gt; still requires strategic optimization.&lt;br&gt;
To solve this, we built an &lt;strong&gt;AI-powered Agent&lt;/strong&gt; that assists business owners in managing and improving their e-commerce websites. This intelligent assistant detects design/content issues, suggests improvements, and enhances the overall site experience to retain customers and boost sales.&lt;/p&gt;
&lt;h2&gt;
  
  
  Use Case Overview
&lt;/h2&gt;

&lt;p&gt;In developing our AI Agent to support e-commerce, we focused on addressing the essential needs of business owners:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Deep analysis&lt;/strong&gt; of critical pages such as the Homepage, Product Listings, and Cart.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Optimization recommendations&lt;/strong&gt; for layout, content, and SEO to enhance user experience and boost conversion rates.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Automated content generation&lt;/strong&gt; for product descriptions, sidebars, and other key areas that require engaging copy.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Customer email drafting support&lt;/strong&gt; to strengthen engagement and build customer loyalty.&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;All these tasks are executed directly within the browser through an intuitive extension. Users can easily review, receive suggestions, and apply improvements with just a few clicks—without needing any technical expertise.&lt;/p&gt;
&lt;h2&gt;
  
  
  Why Amazon Nova Models?
&lt;/h2&gt;

&lt;p&gt;After a thorough process of testing, evaluating, and comparing different AI models based on real-world requirements, I identified three potential options for deploying my AI Agent.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fu1n3yvsc2nsgz2n0095a.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fu1n3yvsc2nsgz2n0095a.png" alt=" " width="800" height="443"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;However, with a strong focus on cost optimization and leveraging cloud environments as the primary platform, I decided to choose &lt;strong&gt;Amazon Nova&lt;/strong&gt; as the core model for my AI Agent.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;p&gt;Nova Lite: Used for tasks that require high speed and low cost, such as quickly analyzing page layouts, identifying basic issues, and providing instant improvement suggestions.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Nova Pro: Ideal for tasks that demand high-quality content and deeper contextual understanding, such as generating product descriptions, drafting customer engagement emails, or recommending personalized user experiences.&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;By flexibly combining Nova Lite and Nova Pro, I can perfectly balance speed, cost, and output quality, effectively meeting the optimization needs of e-commerce websites.&lt;/p&gt;
&lt;h2&gt;
  
  
  How We Utilized Amazon Nova (Technical Execution)
&lt;/h2&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fwhwlt9nm08e1c6gywbsa.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fwhwlt9nm08e1c6gywbsa.png" alt=" " width="800" height="635"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;In our latest project, we built a robust architecture that maximizes the use of Amazon Bedrock and the Nova model line to transform the way we interact with e-commerce websites. Let’s explore the technical steps and how we designed the system for optimal performance and scalability.&lt;br&gt;
Currently, we deep dive in this Architecture.&lt;/p&gt;
&lt;h3&gt;
  
  
  Preparation: Grant Access to Nova Lite and Nova Pro Models
&lt;/h3&gt;

&lt;p&gt;Before we start building the system, the first and most crucial step is:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Registering for access to Nova Lite and Nova Pro on Amazon Bedrock.&lt;/li&gt;
&lt;li&gt;Ensuring that IAM accounts, Agent policies, and the API Gateway have sufficient permissions to call the Nova models.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fvvmkzg8f9y32qhuhjqdq.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fvvmkzg8f9y32qhuhjqdq.png" alt=" " width="800" height="320"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;This ensures the system can directly call the Nova model when generating content and is ready to integrate additional AI models in the future if needed.&lt;/p&gt;
&lt;h2&gt;
  
  
  Building the Process with Bedrock Agent
&lt;/h2&gt;

&lt;p&gt;We designed a custom Bedrock Agent workflow to seamlessly coordinate all the steps in the pipeline. Here’s how it works:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Create an S3 Bucket to Store Crawled Data (HTML, Screenshot). This bucket will store the HTML content, text extractions, and screenshots from crawled websites.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F37rku0tc57fb1en7qwc4.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F37rku0tc57fb1en7qwc4.png" alt=" " width="800" height="209"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Create a Lambda Function: Website Crawler
&lt;/li&gt;
&lt;/ul&gt;
&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;import requests
from bs4 import BeautifulSoup
import boto3
import json
import uuid
import base64
from selenium import webdriver
from selenium.webdriver.chrome.options import Options
import io
from PIL import Image

s3 = boto3.client('s3')

def lambda_handler(event, context):
    print("Received event:", event)

    url = event.get('url')
    if not url:
        return {"statusCode": 400, "body": "URL is required."}

    bucket_name = "bedrock-agent-webcrawler-data"  # Replace with your actual bucket name
    file_id = str(uuid.uuid4())

    try:
        # Fetch HTML content
        response = requests.get(url)
        html_content = response.text

        # Save HTML to S3
        html_key = f"crawl/{file_id}/content.html"
        s3.put_object(
            Bucket=bucket_name,
            Key=html_key,
            Body=html_content,
            ContentType='text/html'
        )

        # Parse and extract text from HTML
        soup = BeautifulSoup(html_content, 'html.parser')
        text_content = soup.get_text(separator='\n')

        # Save extracted text to S3
        text_key = f"crawl/{file_id}/content.txt"
        s3.put_object(
            Bucket=bucket_name,
            Key=text_key,
            Body=text_content,
            ContentType='text/plain'
        )

        return {
            "statusCode": 200,
            "body": {
                "message": "Website crawled and saved successfully.",
                "file_id": file_id,
                "html_path": f"s3://{bucket_name}/{html_key}",
                "text_path": f"s3://{bucket_name}/{text_key}",
                "url": url
            }
        }
    except Exception as e:
        print(f"Error: {str(e)}")
        return {"statusCode": 500, "body": str(e)}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;


&lt;p&gt;Set Permissions, Click &lt;strong&gt;"Add permissions"&lt;/strong&gt; → &lt;strong&gt;"Attach policies"&lt;/strong&gt;. Search for and select &lt;strong&gt;"AmazonS3FullAccess"&lt;/strong&gt;. Then click &lt;strong&gt;"Add permissions"&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;This permission allows the Lambda function to upload data to your S3 bucket.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Create a Lambda Function: Content Pre-Processor
&lt;/li&gt;
&lt;/ul&gt;
&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;import boto3
import json
from bs4 import BeautifulSoup
import re

s3 = boto3.client('s3')

def clean_text(text):
    text = re.sub(r'\s+', ' ', text)
    return text.strip()

def lambda_handler(event, context):
    print("Received event:", event)

    bucket = event.get('bucket', 'bedrock-agent-webcrawler-data') # Replace with your actual bucket name
    html_key = event.get('html_path', '').split('/')[-1]
    file_id = event.get('file_id', '')

    if not file_id:
        return {"statusCode": 400, "body": "Missing file_id"}

    html_key = f"crawl/{file_id}/content.html"

    try:
        response = s3.get_object(Bucket=bucket, Key=html_key)
        html_content = response['Body'].read().decode('utf-8')

        # Parse HTML
        soup = BeautifulSoup(html_content, 'html.parser')

        text = soup.get_text()
        cleaned_text = clean_text(text)

        processed_content = {
            "original_url": event.get('url', 'Unknown'),
            "title": soup.title.string if soup.title else "No title",
            "content": cleaned_text[:100000]  # Giới hạn độ dài
        }

        processed_key = f"processed/{file_id}/content.json"
        s3.put_object(
            Bucket=bucket,
            Key=processed_key,
            Body=json.dumps(processed_content),
            ContentType='application/json'
        )

        return {
            "statusCode": 200,
            "body": {
                "message": "Content processed successfully",
                "processed_file": f"s3://{bucket}/{processed_key}",
                "file_id": file_id
            }
        }
    except Exception as e:
        print(f"Error: {str(e)}")
        return {"statusCode": 500, "body": str(e)}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;


&lt;ul&gt;
&lt;li&gt;Create a DynamoDB Table&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fvhxoslirel2l5u0hti32.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fvhxoslirel2l5u0hti32.png" alt=" " width="800" height="394"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Enable Time to Live (TTL) to automatically expire items&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Create Free Chat Lambda Function
&lt;/li&gt;
&lt;/ul&gt;
&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;import boto3
import json
import hashlib
import time

dynamodb = boto3.resource('dynamodb')
table = dynamodb.Table('BedrockAgentCache')
bedrock_agent = boto3.client('bedrock-agent-runtime')

def lambda_handler(event, context):
    print("Received event:", event)

    query = event.get('query')
    if not query:
        return {"statusCode": 400, "body": "Query is required"}

    query_id = hashlib.md5(query.encode()).hexdigest()

    try:
        response = table.get_item(Key={'QueryId': query_id})
        if 'Item' in response:
            return {
                "statusCode": 200,
                "body": {
                    "result": response['Item']['Result'],
                    "source": "cache"
                }
            }
    except Exception as e:
        print(f"Error checking cache: {str(e)}")

    try:
        agent_id = "your-agent-id"  # Thay bằng ID của agent
        agent_alias_id = "your-agent-alias-id"  # Thay bằng alias ID

        response = bedrock_agent.invoke_agent(
            agentId=agent_id,
            agentAliasId=agent_alias_id,
            sessionId=f"session-{int(time.time())}",
            inputText=query
        )

        result = ""
        for event in response.get('completion', []):
            if 'chunk' in event:
                result += event['chunk']['bytes'].decode('utf-8')

        table.put_item(Item={
            'QueryId': query_id,
            'Query': query,
            'Result': result,
            'TTL': int(time.time()) + 86400  # 24 giờ
        })

        return {
            "statusCode": 200,
            "body": {
                "result": result,
                "source": "bedrock"
            }
        }
    except Exception as e:
        print(f"Error calling Bedrock Agent: {str(e)}")
        return {"statusCode": 500, "body": str(e)}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;


&lt;p&gt;In the AWS Lambda console, click &lt;strong&gt;"Add permissions"&lt;/strong&gt; &amp;gt; &lt;strong&gt;"Attach policies"&lt;/strong&gt;.&lt;br&gt;
Search for and select &lt;strong&gt;"AmazonDynamoDBFullAccess"&lt;/strong&gt; and &lt;strong&gt;"AmazonBedrockFullAccess"&lt;/strong&gt;, then click &lt;strong&gt;"Add permissions"&lt;/strong&gt;.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Create Bedrock Agent&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Prompt: “You are an assistant specialized in answering questions based on content collected from websites.&lt;br&gt;
When given a URL, you must access the site, crawl and analyze its content, and answer questions strictly based on the actual data retrieved.&lt;br&gt;
You must adhere closely to the collected content and are not allowed to invent or assume information beyond what is available.”&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fhq5zzkx0aijun8xokq3j.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fhq5zzkx0aijun8xokq3j.png" alt=" " width="800" height="525"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Create Action Group&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Enter the following &lt;strong&gt;OpenAPI schema&lt;/strong&gt;:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;{
  "openapi": "3.0.0",
  "info": {
    "title": "Website Crawler API",
    "version": "1.0.0"
  },
  "paths": {
    "/crawl": {
      "post": {
        "summary": "Crawl a website",
        "operationId": "crawlWebsite",
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "properties": {
                  "url": {
                    "type": "string",
                    "description": "URL of the website to crawl"
                  }
                },
                "required": ["url"]
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "Successful operation",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "message": {
                      "type": "string"
                    },
                    "file_id": {
                      "type": "string"
                    }
                  }
                }
              }
            }
          }
        }
      }
    }
  }
}

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F82wtd97eakvod0c64ypf.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F82wtd97eakvod0c64ypf.png" alt=" " width="734" height="597"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Update Free Chat Lambda Function with Agent ID&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Go back to the &lt;strong&gt;Lambda console&lt;/strong&gt;.&lt;br&gt;
Open the &lt;strong&gt;"BedrockAgentChatFunction"&lt;/strong&gt;.&lt;br&gt;
Update the &lt;strong&gt;agent_id&lt;/strong&gt; and &lt;strong&gt;agent_alias_id&lt;/strong&gt; in the code with the information from the agent you just created.&lt;br&gt;
Click &lt;strong&gt;"Deploy"&lt;/strong&gt; to save the changes.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Test Agent&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Enter the following test prompt: "Crawl the website &lt;a href="https://example.com" rel="noopener noreferrer"&gt;https://example.com&lt;/a&gt; and tell me what it's about"&lt;/p&gt;

&lt;h2&gt;
  
  
  Result
&lt;/h2&gt;

&lt;p&gt;Through the clever integration of Bedrock Agent, custom Lambda Functions, the Nova model, and serverless AWS services like S3 and DynamoDB, we have built a flexible, cost-effective, and scalable architecture to serve personalized content creation at scale.&lt;/p&gt;

&lt;p&gt;This system is not only a technical achievement in cloud computing but also a prime example of how smart automation can enhance customer experiences and drive business growth in the e-commerce industry.&lt;br&gt;
And this is just the beginning of the journey!&lt;/p&gt;

</description>
    </item>
    <item>
      <title>AI retailer assistant with Amazon Bedrock agent</title>
      <dc:creator>Phạm Nguyễn Hải Anh</dc:creator>
      <pubDate>Fri, 09 May 2025 07:35:33 +0000</pubDate>
      <link>https://dev.to/aws-builders/ai-retailer-assistant-with-amazon-bedrock-agent-7hj</link>
      <guid>https://dev.to/aws-builders/ai-retailer-assistant-with-amazon-bedrock-agent-7hj</guid>
      <description>&lt;h2&gt;
  
  
  Scenario
&lt;/h2&gt;

&lt;p&gt;A shoe website wants a virtual retailer that can chat with customers, find suitable products in the inventory and add them to the cart.&lt;/p&gt;

&lt;h2&gt;
  
  
  Architecture
&lt;/h2&gt;

&lt;p&gt;The virtual retailer assistant is built by using Amazon Bedrock Agent Nova Pro. &lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fsyx1x5dne1mq0ynzraev.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fsyx1x5dne1mq0ynzraev.png" alt=" " width="781" height="281"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Here is the workflow:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;A customer asks the retailer agent for a specific type of product, with some requirements including colors, prices, purposes.
2-3-4. Referred to the OpenAPI schema and based on the scenario, the agent invokes a Lambda function to query to the inventory table to list all products.&lt;/li&gt;
&lt;li&gt;The agent infers from the response of the Lambda function, combines it with the customer's preferences, and recommends suitable products.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;In case the customer choose product(s) from the response of the agent, the agent will add the product to the cart by invoking a different Lambda function to interact with the Cart table. &lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F99yphw3rs3r3u9u6p50r.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F99yphw3rs3r3u9u6p50r.png" alt=" " width="461" height="421"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Prerequisites
&lt;/h2&gt;

&lt;p&gt;The customer must enable access to Nova Pro in the specific AWS Region.&lt;/p&gt;

&lt;h2&gt;
  
  
  Steps
&lt;/h2&gt;

&lt;p&gt;In this scope of demo, I create a SQLite file with 2 tables given above. If you want to create a SQLite database, you can download the &lt;code&gt;SQLite Editor&lt;/code&gt; extension in VS Code.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fbbzdwx85ef9rjg4y3t35.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fbbzdwx85ef9rjg4y3t35.png" alt=" " width="603" height="212"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Step 1. Setup table ShoeInventory and table OrderDetails in database named demo_csbot_db_2&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fbgqauomdv8pthleu9fc3.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fbgqauomdv8pthleu9fc3.png" alt=" " width="800" height="232"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;The last column of the ShoeInventory table (S3Location) is for showing the images of the products.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fd94u62obpyrr3qnu35qa.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fd94u62obpyrr3qnu35qa.png" alt=" " width="774" height="471"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Step 2. Create the OpenAPI schema&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;{
    "openapi": "3.0.0",
    "info": {
        "title": "Customer Service Bot API",
        "version": "1.0.0",
        "description": "Customer service APIs for a retail store selling shoes"
    },
    "paths": {
        "/place_order": {
            "get": {
                "summary": "Sub task to place an order on behalf of the customer",
                "description": "Place an order for the number of shoes by creating an Order record and updating inventory in the database",
                "operationId": "get_place_order",
                "parameters": [{
                    "name": "ShoeID",
                    "in": "query",
                    "description": "Shoe ID to place an order",
                    "required": true,
                    "schema": {
                        "type": "integer"
                    }
                },
                {
                    "name": "CustomerID",
                    "in": "query",
                    "description": "Customer ID to place an order",
                    "required": true,
                    "schema": {
                        "type": "integer"
                    }
                },
                {
                    "name": "NumberOfProducts",
                    "in": "query",
                    "description": "Number of shoes to order",
                    "required": true,
                    "schema": {
                        "type": "integer"
                    }
                }],
                "responses": {
                    "200": {
                        "description": "Order has been placed",
                        "content": {
                            "application/json": {
                                "schema": {
                                    "type": "object",
                                    "properties": {
                                        "message": {
                                            "type": "string",
                                            "description": "Your order has been placed"
                                        }
                                    }
                                }
                            }
                        }
                    }
                }
            }
        },
        "/check_inventory": {
            "get": {
                "summary": "Returns all details related to shoes, including inventory details",
                "description": "Checks inventory for shoes and returns all available information about available shoes, including shoe ID, shoe colors, inventory, best fit activity, style description, s3 location and price ",
                "operationId": "get_check_inventory",
                "responses": {
                    "200": {
                        "description": "Returns Shoe information",
                        "content": {
                            "application/json": {
                                "schema": {
                                    "type": "object",
                                    "properties": {
                                        "ShoeID": {
                                            "type": "integer",
                                            "description": "This is the shoe ID for this shoe"
                                        },
                                            "BestFitActivity": {
                                            "type": "string",
                                            "description": "Best fit activity for this shoe"
                                        },
                                            "StyleDesc": {
                                            "type": "string",
                                            "description": "Detailed description of the shoe"
                                        },
                                            "ShoeColors": {
                                            "type": "string",
                                            "description": "The colors of this shoe"
                                        },
                                            "Price": {
                                            "type": "string",
                                            "description": "Price of this shoe"
                                        },
                                            "InvCount": {
                                            "type": "integer",
                                            "description": "Inventory count"
                                        },
                                            "Size": {
                                            "type": "integer",
                                            "description": "Inventory size"
                                        },
                                        "S3Location": {
                                            "type": "string",
                                            "description": "Image of the shoe"
                                        }
                                    }
                                }
                            }
                        }
                    }
                }
            }
        }
    }    
}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Step 3. Create Lambda code, one function to query the ShoeInventory table and one function to add to the OrderDetail table. Also, give it the necessary permissions.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;import json
import boto3
import sqlite3
from datetime import datetime
import random
import logging
import os

logger = logging.getLogger()
logger.setLevel(logging.INFO)

s3 = boto3.client('s3')
bucket = os.environ.get('BUCKET_NAME')  #Name of bucket with data file and OpenAPI file
db_name = 'demo_csbot_db_2.sqlite' #Location of data file in S3
local_db = '/tmp/csbot.db' #Location in Lambda /tmp folder where data file will be copied

#Download data file from S3
s3.download_file(bucket, db_name, local_db)

cursor = None
conn = None

#Initial data load and SQLite3 cursor creation 
def load_data():
    #load SQL Lite database from S3
    # create the db
    global conn
    conn = sqlite3.connect(local_db)
    cursor = conn.cursor()
    logger.info('Completed initial data load ')

    return cursor

#Function returns all customer info for a particular customerId
def return_customer_info(custName):
    query = 'SELECT customerId, customerName, Addr1, Addr2, City, State, Zipcode, PreferredActivity, ShoeSize, OtherInfo from CustomerInfo where customerName like "%' +  custName +'%"'
    cursor.execute(query)
    resp = cursor.fetchall()
    #adding column names to response values
    names = [description[0] for description in cursor.description]
    valDict = {}
    index = 0
    for name in names:
        valDict[name]=resp[0][index]
        index = index + 1
    logger.info('Customer Info retrieved', valDict)
    return valDict


#Function returns shoe inventory for a particular shoeid 
def return_shoe_inventory():
    query = 'SELECT ShoeID, BestFitActivity, StyleDesc, ShoeColors, Price, InvCount, Size, S3Location from ShoeInventory' 
    cursor.execute(query)
    resp = cursor.fetchall()
    print ("query: ", resp)

    #adding column names to response values
    names = [description[0] for description in cursor.description]
    valDict = []
    interimDict = {}
    index = 0
    for item in resp:
        for name in names:
            interimDict[name]=item[index]
            index = index + 1
        index = 0
        valDict.append(interimDict)
        interimDict={}
    logger.info('Shoe info retrieved')
    return valDict

def return_shoe_image_inventory():
    query = 'SELECT ShoeID, S3Location from ShoeInventory' 
    cursor.execute(query)
    resp = cursor.fetchall()
    print ("query: ", resp)
    #adding column names to response values
    names = [description[0] for description in cursor.description]
    valDict = []
    interimDict = {}
    index = 0
    for item in resp:
        for name in names:
            interimDict[name]=item[index]
            index = index + 1
        index = 0
        valDict.append(interimDict)
        interimDict={}
    logger.info('Shoe and image info retrieved')
    return valDict

#function places order -- reduces shoe inventory, updates order_details table --&amp;gt; all actions resulting from a shoe purchase  
def place_shoe_order(ssId, custId, InvCnt):
    global cursor
    global conn
    query = 'Update ShoeInventory set InvCount = InvCount - 1 where ShoeID = ' + str(ssId)
    ret = cursor.execute(query)

    today = datetime.today().strftime('%Y-%m-%d')
    query = 'INSERT INTO OrderDetails (orderdate, shoeId, CustomerId, InvCount) VALUES ("'+today+'",'+str(ssId)+','+ str(custId)+','+str(InvCnt)+')'
    ret = cursor.execute(query)
    conn.commit()

    #Writing updated db file to S3 and setting cursor to None to force reload of data
    s3.upload_file(local_db, bucket, db_name)
    cursor = None
    logger.info('Shoe order placed')
    return 1


def lambda_handler(event, context):
    responses = []
    global cursor
    if cursor == None:
        cursor = load_data()
    id = ''
    api_path = event['apiPath']
    logger.info('API Path')
    logger.info(api_path)

    if api_path == '/customer/{CustomerName}':
        parameters = event['parameters']
        for parameter in parameters:
            if parameter["name"] == "CustomerName":
                cName = parameter["value"]
        body = return_customer_info(cName)
    elif api_path == '/place_order':
        parameters = event['parameters']
        print ("place order parameters:", parameters)
        for parameter in parameters:
            if parameter["name"] == "ShoeID":
                id = parameter["value"]
            if parameter["name"] == "CustomerID":
                cid = parameter["value"]
            if parameter["name"] == "NumberOfProducts":
                numOfProd = parameter["value"]
        body = place_shoe_order(id, cid, numOfProd)
    elif api_path == '/check_inventory':
        body = return_shoe_inventory()
    elif api_path == '/check_image_inventory':
        body = return_shoe_image_inventory()
    else:
        body = {"{} is not a valid api, try another one.".format(api_path)}

    response_body = {
        'application/json': {
            'body': json.dumps(body)
        }
    }

    action_response = {
        'actionGroup': event['actionGroup'],
        'apiPath': event['apiPath'],
        'httpMethod': event['httpMethod'],
        'httpStatusCode': 200,
        'responseBody': response_body
    }

    responses.append(action_response)

    api_response = {
        'messageVersion': '1.0', 
        'response': action_response}

    return api_response
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Step 4. Setup the instruction for the Bedrock Nova Pro agent&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;You are an agent assisting customers with purchasing shoes. You must greet by saying "Hi. I am a virtual retail agent. Tell me what you're looking for, and I'll help you find the perfect fit." Then, to find the best fit, you may ask customers about their favorite color, foot length, and intended use of the shoes, but should not ask all these at once. Once you have information about foot length, favorite color and intended use, check the inventory for shoes that match these criteria. Generate a response that includes the shoe ID, style description, available colors, and sizes based on the inventory details. If multiple matches are found, display all options to the customer.

If there are no products that have the preferred size, offer the nearest size and inform the customer that the shoe can be tight if the alternative size is smaller, or loose if the alternative size is bigger than the preferred size.

Order Placement:
- After the customer selects a shoe, ask for the customer ID and the number of products that they want to buy. Use the chosen shoe ID, customer ID and the number of products that customer wants to buy to place the order, ensuring these shoes are still in stock by checking the inventory count.

Additional Considerations:
- If no matching shoes are available, inform the customer and offer alternatives.
- If the selected shoe is out of stock, notify the customer and suggest other options.
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Demo
&lt;/h2&gt;

&lt;p&gt;In this demo, the flow is that first, the customer is looking for a shoe, given specific requirements like foot length and favorite color. Then the agent will query all the shoes in the inventory, and base on the customer's requirements to list the suitable products for the customer. If the customer choose product(s), the agent will ask for the customer name and add the order to the Order table for further processing in the future.&lt;/p&gt;

&lt;p&gt;Here is the demo for the retailer agent in the Bedrock playground: &lt;a href="https://haianh.s3.us-east-1.amazonaws.com/EnglishRetailer.mp4" rel="noopener noreferrer"&gt;https://haianh.s3.us-east-1.amazonaws.com/EnglishRetailer.mp4&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Conclusion
&lt;/h2&gt;

&lt;p&gt;This blog has provided a use case for applying Amazon Bedrock Nova Pro model for a virtual retailer.&lt;/p&gt;

</description>
    </item>
    <item>
      <title>Free SSL subdomain assigned to AWS EC2 instance with Certbot</title>
      <dc:creator>Phạm Nguyễn Hải Anh</dc:creator>
      <pubDate>Mon, 05 May 2025 12:51:23 +0000</pubDate>
      <link>https://dev.to/aws-builders/free-ssl-subdomain-assigned-to-aws-ec2-instance-with-certbot-2a1n</link>
      <guid>https://dev.to/aws-builders/free-ssl-subdomain-assigned-to-aws-ec2-instance-with-certbot-2a1n</guid>
      <description>&lt;h2&gt;
  
  
  Scenario
&lt;/h2&gt;

&lt;p&gt;Sometimes, the EC2 instances, not ALB, need to be assigned SSL certificates as there are some functions required SSL. This blog will guide you how to assign an SSL certificate with a given subdomain and Certbot-NginX.&lt;/p&gt;

&lt;h2&gt;
  
  
  Prerequisites
&lt;/h2&gt;

&lt;ol&gt;
&lt;li&gt;An AWS IAM user with admin role&lt;/li&gt;
&lt;li&gt;A IP public EC2 Ubuntu instance (I test with version 22.04 LTS)&lt;/li&gt;
&lt;li&gt;A domain (I bought in TenTen)&lt;/li&gt;
&lt;/ol&gt;

&lt;h2&gt;
  
  
  Steps
&lt;/h2&gt;

&lt;h3&gt;
  
  
  Associate a AWS EIP to EC2
&lt;/h3&gt;

&lt;p&gt;Step 1. Choose orange button "Allocate Elastic IP address"&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fq0m8h2lolyy9hmnvdg5q.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fq0m8h2lolyy9hmnvdg5q.png" alt=" " width="800" height="38"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Step 2. Keep the default selections and choose orange button "Allocate"&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Ft1nwd5aqi00k3rbkpruw.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Ft1nwd5aqi00k3rbkpruw.png" alt=" " width="800" height="374"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Step 3. Choose the created EIP -&amp;gt; Actions -&amp;gt; Associate EIP address&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fwpyoff602ajq26f7mzi3.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fwpyoff602ajq26f7mzi3.png" alt=" " width="800" height="109"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Step 4. Choose instance -&amp;gt; ID of the EC2 instance you want to assign the certificate -&amp;gt; Associate&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F0b4gd78tsb2o8009jwgr.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F0b4gd78tsb2o8009jwgr.png" alt=" " width="800" height="300"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;The reason why you need EIP is to keep the IP of the EC2 instance when it turns off.&lt;/p&gt;

&lt;h3&gt;
  
  
  Assign the subdomain to EC2 instance
&lt;/h3&gt;

&lt;p&gt;In this section, I assume that you have already bought a domain, may be through GoDaddy or somewhere else.&lt;/p&gt;

&lt;p&gt;Step 1. Go to your domain configuration website and add an A record with value is the IP of EC2 instance. The name is the first part before dot in the domain that you want to configure. For example, you want the EC2 instance hosting a subdomain named transcribe.pngha.io.vn, then the value in the Name column is "transcribe".&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fh5tb5f3pnj7rq4c9he2o.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fh5tb5f3pnj7rq4c9he2o.png" alt=" " width="800" height="105"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Step 2. Wait for at least 1 minute for the record to be activated. Then access the URL of the subdomain:port, for e.g. transcribe.pngha.io.vn:3000. &lt;/p&gt;

&lt;p&gt;Congratulation! Now you have linked the IP of EC2 instance to the given domain. In the next section, you will explore how to configure the SSL certificate to encrypt the connection to the domain of the EC2 instance.&lt;/p&gt;

&lt;h3&gt;
  
  
  Assign free SSL certificates to AWS EC2 instance
&lt;/h3&gt;

&lt;p&gt;Step 1. Run command &lt;code&gt;sudo apt update -y&lt;/code&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F0yibb2dbgmms3qelvyya.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F0yibb2dbgmms3qelvyya.png" alt=" " width="800" height="192"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Step 2. Run command &lt;code&gt;sudo apt upgrade -y&lt;/code&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fmn0rldbsm5tpkqvkrcsz.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fmn0rldbsm5tpkqvkrcsz.png" alt=" " width="800" height="156"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Step 3. Run command &lt;code&gt;sudo apt install nginx -y&lt;/code&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F424e0ge0y82fzqpywo14.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F424e0ge0y82fzqpywo14.png" alt=" " width="800" height="320"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Step 4. Run command &lt;code&gt;sudo apt-get install software-properties-common&lt;/code&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fd8bwvs5ff21bw4ufpr2y.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fd8bwvs5ff21bw4ufpr2y.png" alt=" " width="800" height="123"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Step 5. Run command &lt;code&gt;sudo add-apt-repository ppa:certbot/certbot&lt;/code&gt;. If you encounter the following error:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fljjwl4f9y63bncpx83re.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fljjwl4f9y63bncpx83re.png" alt=" " width="800" height="322"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Then change the command to &lt;code&gt;sudo apt-add-repository -r ppa:certbot/certbot&lt;/code&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fox1t91qkkns139i8eth3.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fox1t91qkkns139i8eth3.png" alt=" " width="800" height="244"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Step 6. Then run &lt;code&gt;sudo apt update&lt;/code&gt; and &lt;code&gt;sudo apt-get update&lt;/code&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fbvslzpi7of3iw5iqlbyv.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fbvslzpi7of3iw5iqlbyv.png" alt=" " width="800" height="321"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Step 7. Install Certbot with command &lt;code&gt;sudo apt-get install python3-certbot-nginx&lt;/code&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F8n88hrjtebsmvy7z1qbv.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F8n88hrjtebsmvy7z1qbv.png" alt=" " width="800" height="280"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Step 8. Run command &lt;code&gt;sudo certbot -d transcribe.pngha.io.vn&lt;/code&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fu4616olye7r85eyoi2dz.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fu4616olye7r85eyoi2dz.png" alt=" " width="800" height="78"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Step 9. Type y when asked. Then the certificate is requested. Wait for about 4 minutes.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F3h1la6b58hmjp4ztfdvk.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F3h1la6b58hmjp4ztfdvk.png" alt=" " width="800" height="346"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Step 10. You will be issued the certificate successfully&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fh4wdbja7qzwi3a10dbim.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fh4wdbja7qzwi3a10dbim.png" alt=" " width="800" height="263"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Step 11. You can now access the URL. However, the default website is the Nginx default. If you want custom it, especially when your website is running on a different port (e.g. 3000), you can refer the next section.&lt;/p&gt;

&lt;h3&gt;
  
  
  Custom the website for HTTPS
&lt;/h3&gt;

&lt;p&gt;Step 1. Run command &lt;code&gt;sudo nano /etc/nginx/sites-available/transcribe.pngha.io.vn&lt;/code&gt; and paste the following content:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;server {
    listen 80;
    server_name transcribe.pngha.io.vn;

    # Redirect all HTTP requests to HTTPS
    return 301 https://\$host\$request_uri;
}

server {
    listen 443 ssl;
    server_name transcribe.pngha.io.vn;

    # SSL certificate paths (these should match what Certbot created)
    ssl_certificate /etc/letsencrypt/live/transcribe.pngha.io.vn/fullchain.pem;
    ssl_certificate_key /etc/letsencrypt/live/transcribe.pngha.io.vn/privkey.pem;

    include /etc/letsencrypt/options-ssl-nginx.conf;
    ssl_dhparam /etc/letsencrypt/ssl-dhparams.pem;

    # Proxy settings for Node.js application
    location / {
        proxy_pass http://localhost:3000;
    }
}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;In this script, I use port 3000 for the website&lt;br&gt;
Step 2. Create link with the command &lt;code&gt;sudo ln -s /etc/nginx/sites-available/transcribe.pngha.io.vn /etc/nginx/sites-enabled/&lt;/code&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fr440dggeg1vjos81aqlh.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fr440dggeg1vjos81aqlh.png" alt=" " width="800" height="40"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Step 3. Check with &lt;code&gt;sudo nginx -t&lt;/code&gt;. If it shows "nginx: the configuration file /etc/nginx/nginx.conf syntax is ok&lt;br&gt;
nginx: configuration file /etc/nginx/nginx.conf test is successful", which means OK.&lt;/p&gt;

&lt;p&gt;Step 4. Then restart nginx &lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Flijvqf0byoibm04kk6b4.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Flijvqf0byoibm04kk6b4.png" alt=" " width="800" height="121"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Step 5. Now you need to make the symbol link from the default changed to your domain by deleting the default&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fmzjmexhstya9k8zowoqj.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fmzjmexhstya9k8zowoqj.png" alt=" " width="800" height="215"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Step 6. Then restart nginx&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fexh92web89kqj6z0n7ae.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fexh92web89kqj6z0n7ae.png" alt=" " width="800" height="144"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Step 7. Now start your web server with port 3000. And access transcribe.pngha.io.vn successfully.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fsipgye4l3grv0alu8twx.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fsipgye4l3grv0alu8twx.png" alt=" " width="800" height="411"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Reference
&lt;/h2&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;a href="https://www.youtube.com/watch?v=qQl93M7XpJA&amp;amp;ab_channel=SusanB" rel="noopener noreferrer"&gt;https://www.youtube.com/watch?v=qQl93M7XpJA&amp;amp;ab_channel=SusanB&lt;/a&gt; (for A record format).&lt;/li&gt;
&lt;li&gt;
&lt;a href="https://www.youtube.com/watch?v=uEAzcLw_nSI&amp;amp;ab_channel=DebduttaPanda" rel="noopener noreferrer"&gt;https://www.youtube.com/watch?v=uEAzcLw_nSI&amp;amp;ab_channel=DebduttaPanda&lt;/a&gt; (for assigning cert to subdomain with Certbot).&lt;/li&gt;
&lt;li&gt;
&lt;a href="https://stackoverflow.com/questions/56519434/how-to-add-certbot-to-another-port" rel="noopener noreferrer"&gt;https://stackoverflow.com/questions/56519434/how-to-add-certbot-to-another-port&lt;/a&gt; (johnsing answer for template).&lt;/li&gt;
&lt;li&gt;
&lt;a href="https://stackoverflow.com/questions/60249177/e-the-repository-http-ppa-launchpad-net-certbot-certbot-ubuntu-focal-release" rel="noopener noreferrer"&gt;https://stackoverflow.com/questions/60249177/e-the-repository-http-ppa-launchpad-net-certbot-certbot-ubuntu-focal-release&lt;/a&gt;.&lt;/li&gt;
&lt;li&gt;
&lt;a href="https://stackoverflow.com/questions/9063378/why-do-browsers-not-use-srv-records" rel="noopener noreferrer"&gt;https://stackoverflow.com/questions/9063378/why-do-browsers-not-use-srv-records&lt;/a&gt; (I intend to use SRV records for changing the port, but according to this link, it won't work).&lt;/li&gt;
&lt;/ol&gt;

</description>
      <category>aws</category>
      <category>ec2</category>
      <category>certificate</category>
      <category>ssl</category>
    </item>
    <item>
      <title>Live Streaming: Real-time translated subtitles with AWS</title>
      <dc:creator>Phạm Nguyễn Hải Anh</dc:creator>
      <pubDate>Sat, 03 May 2025 03:42:50 +0000</pubDate>
      <link>https://dev.to/pnghaianh/live-streaming-real-time-translated-subtile-with-aws-8pm</link>
      <guid>https://dev.to/pnghaianh/live-streaming-real-time-translated-subtile-with-aws-8pm</guid>
      <description>&lt;h2&gt;
  
  
  Scenario
&lt;/h2&gt;

&lt;p&gt;A streaming platform wants to stream sport matches commented in English (of course completely legal) for Vietnamese viewers. They demand that the subtitle needed to be translated to Vietnamese with the lowest latency as possible. This blog is a PoC (Proof of Concept) for the translated streaming subtitle, using AWS Transcribe to transcribe the voice to text, Nova Micro to fix the text and Claude 3.5 Sonnet v2 in Bedrock to translate, with the support of serverless AWS ECS Fargate and Lambda.&lt;/p&gt;

&lt;p&gt;Why not using Amazon Translate but use Claude 3.5 Sonnet v2 in Bedrock? It is because the streaming videos are in special contexts, using sports vocabulary such as player names, item names which makes AWS Translate cannot identify and translate correctly.&lt;/p&gt;

&lt;h2&gt;
  
  
  Workflow
&lt;/h2&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fryrk9oumymcks2upxu9g.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fryrk9oumymcks2upxu9g.png" alt=" " width="800" height="366"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Using OBS studio, a video stream is pushed to Amazon ECS Fargate "stream-server". "Stream-server" uses NGINX-RTMP module to deliver the stream to IVS through RTMPS and to Amazon ECS Fargate "transcribe-server" through RTMP.&lt;/li&gt;
&lt;li&gt;Amazon ECS Fargate "transcribe-server" extracts the voice from the stream using FFMEPG, calls AWS Transcribe to transcribe it to text. I have referred an algorithm to extract and appends new words transcribed to a buffer, and send the buffer (then clear it) whenever a punctuation is detected in that buffer. However, since new appended words could be a part of a complete word (e.g. 2008 transcribed to 2 words 20 and 08), Amazon Bedrock Nova Micro is invoked to fix the buffer based on the original livestream.&lt;/li&gt;
&lt;li&gt;Amazon ECS Fargate "translate-server" received the transcribed buffer and used Claude 3.5 Sonnet v2 to translate from SourceLanguague to TargetLanguage.&lt;/li&gt;
&lt;/ol&gt;

&lt;h2&gt;
  
  
  Prerequisite
&lt;/h2&gt;

&lt;ol&gt;
&lt;li&gt;Amazon Bedrock access to Nova Micro and Claude 3.5 Sonnet v2 models.&lt;/li&gt;
&lt;li&gt;The region to run this project must support Amazon IVS.&lt;/li&gt;
&lt;li&gt;Editing the package.json in Translate folder and Transcribe folder to contain the bedrock SDK package.&lt;/li&gt;
&lt;/ol&gt;

&lt;h2&gt;
  
  
  Steps
&lt;/h2&gt;

&lt;p&gt;This project is heavily based on the repository in the #1 Reference. There are few files that I have customized the code.&lt;/p&gt;

&lt;h3&gt;
  
  
  Transcribe
&lt;/h3&gt;

&lt;p&gt;Keep most of the transcribing part. Just edit the transcribe-to-translate part, which I refer the reference #2. &lt;/p&gt;

&lt;p&gt;Slicing New Items for Processing:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;const startIndex = translationContext.lastProcessedItemIndex || 0;
const newItems = items.slice(startIndex);
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The function determines the &lt;code&gt;startIndex&lt;/code&gt; from the last processed index in the translation context. It slices new items from the transcription result starting from the &lt;code&gt;startIndex&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;Updating the Last Processed Index:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;translationContext.lastProcessedItemIndex = items.length;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Updates the &lt;code&gt;lastProcessedItemIndex&lt;/code&gt; to the total number of items, marking all current items as processed.&lt;/p&gt;

&lt;p&gt;Building the Untranslated Buffer:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;if (newItems.length &amp;gt; 0) {
    const newText = reconstructTranscript(newItems);
    translationContext.untranslatedBuffer += newText;
}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;If there are new items, reconstructs their text using &lt;code&gt;reconstructTranscript&lt;/code&gt;, then appends the reconstructed text to the &lt;code&gt;untranslatedBuffer&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;Checking Translation Readiness:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;const shouldTranslate = checkShouldTranslate(translationContext.untranslatedBuffer);
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;





&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;function checkShouldTranslate(buffer) {
  // Check for sentence-ending punctuation
  const punctuationRegex = /[.!?。！？]/;
  const commaRegex = /[,]/;
  if (punctuationRegex.test(buffer)) {
    return true;
  }
  if (buffer.length &amp;gt; 60 &amp;amp;&amp;amp; commaRegex.test(buffer)) {
    console.log("more than 60 char &amp;amp; comma: ", buffer)
    return true;
  }
  // Check if word count exceeds threshold (14 words) and ends with comma
  // const words = buffer.trim().split(/\s+/);
  if (buffer.length &amp;gt; 76) {
    console.log("more than 76 char: ", buffer)
    return true;
  }
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Calls &lt;code&gt;checkShouldTranslate&lt;/code&gt; to determine if the &lt;code&gt;untranslatedBuffer&lt;/code&gt; is ready for translation.&lt;/p&gt;

&lt;p&gt;If yes, then go to text correction before sending to the "translate-server":&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;if (shouldTranslate || !result.IsPartial) {
    const prompt = `You receive a processed text and a reference text. 
    IMPORTANT: Fix ONLY the actual errors in the range of processed text by referring to the reference text. If the reference text is completely different, or has more content, return the processed text as is.
    Reference text: ${parsedTranscription.text}
    Return ONLY fixed text, no explanations.
    Example: 
      Processed text: "A strong refer needed today , Howard Webb , is that , England'sA strong refer needed today , Howard Webb , is that , England's represent at Euro 20 ."
      Reference text: "Not many managers can claim degree of success. A strong referee needed today, Howard Webb is that man, England's representative at Euro 2008"
      Response: "A strong referee needed today, Howard Webb is that man, England's representative at Euro 2008"
    Processed text: ${translationContext.untranslatedBuffer}`;

    const message = {
        content: [{ text: prompt }],
        role: ConversationRole.USER,
    };
    const request = {
      modelId,
      messages: [message],
      inferenceConfig: {
        maxTokens: 500, // The maximum response length
        temperature: 0.0, // Using temperature for randomness control
        top_K: 1,        // Alternative: use topP instead of temperature
      },
    };
    const response = await client.send(new ConverseCommand(request));
    const ModelHandledUntranslatedBuffer = response.output.message.content[0].text;
    translationContext.untranslatedBuffer = ModelHandledUntranslatedBuffer;
}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Constructs a prompt for a language model to fix errors in the &lt;code&gt;untranslatedBuffer&lt;/code&gt; by comparing it to the &lt;code&gt;parsedTranscription.text&lt;/code&gt;.&lt;br&gt;
Sends the prompt via a &lt;code&gt;ConverseCommand&lt;/code&gt; to the language model.&lt;br&gt;
Updates the &lt;code&gt;untranslatedBuffer&lt;/code&gt; with the model's corrected response.&lt;/p&gt;

&lt;p&gt;The effectiveness of Nova Micro:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;untranslatedBuffer:   200
partial live:  2008.
ModelHandledResult:  2008.
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The processed buffer can not add the &lt;code&gt;8&lt;/code&gt; character from the original subtitle. Nova Micro is invoked to fix it.&lt;/p&gt;

&lt;p&gt;Besides, add a custom vocabulary list (.txt format, separated by tab) in Amazon Transcribe in the console to better transcribe the soccer players and club names:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Phrase  IPA SoundsLike  DisplayAs
Amazon  æ m ə z ɑ n      Amazon
I.V.S.  aɪ v i ɛ s        IVS
Twitch      twitch  Twitch
Szczesny            Szczesny
Danilo          Danilo
Bonucci         Bonucci
Boucci          Bonucci
De-Ligt         De Ligt
Alex-Sandro         Alex Sandro
Alexandra           Alex Sandro
Alexandro           Alex Sandro
Chiesa          Chiesa
Chiea           Chiesa
McKennie            McKennie
Bentancur           Bentancur
Bentanco            Bentancur
Ramsey          Ramsey
Dybala          Dybala
Paulo-Dybala            Paulo-Dybala
Cristiano-Ronaldo           Cristiano-Ronaldo
Andrea-Pirlo            Andrea-Pirlo
Andrea          Andrea
Pirlo           Pirlo
Juventus            Juventus
Ju-V.           JUV
JuVe            JUV
Ju-Ve           JuV
Udinese         Udinese
Udi         Udinese
U-di            UDI
Musso           Musso
Moosa           Musso
Mooso           Musso
Mosso           Musso
Mousso          Musso
Bonifazi            Bonifazi
De-Maio         De Maio
De-Mao          De Maio
De-Ma-O         De Maio
Samir           Samir
Stryger-Larsen          Stryger-Larsen
De-Paul         De Paul
Walace          Walace
Wallace         Walace
Pereyra         Pereyra
Zeegelaar           Zeegelaar
Aal         Zeegelaar
Lasagna         Lasagna
Pussello            Pussello
Serie-A.            Serie A
amer            amer
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Translate
&lt;/h3&gt;

&lt;p&gt;Using the idea of Reference #2, which is translating the transcription but also keeping previous translated words as many as possible. The latency when invoking Claude 3.5 Sonnet is about 2.8s to 3.7s.&lt;/p&gt;

&lt;h3&gt;
  
  
  Demo
&lt;/h3&gt;

&lt;p&gt;&lt;a href="https://david-gapv.s3.ap-southeast-1.amazonaws.com/FixName_Delay5sExchangeMediumAccura.mp4" rel="noopener noreferrer"&gt;https://david-gapv.s3.ap-southeast-1.amazonaws.com/FixName_Delay5sExchangeMediumAccura.mp4&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Reference
&lt;/h2&gt;

&lt;ol&gt;
&lt;li&gt;&lt;a href="https://github.com/aws-samples/amazon-ivs-auto-captions-web-demo" rel="noopener noreferrer"&gt;https://github.com/aws-samples/amazon-ivs-auto-captions-web-demo&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://borohhov.medium.com/genai-built-my-real-time-subtitles-app-faster-than-i-wrote-this-article-046e7ad1ce48" rel="noopener noreferrer"&gt;https://borohhov.medium.com/genai-built-my-real-time-subtitles-app-faster-than-i-wrote-this-article-046e7ad1ce48&lt;/a&gt;&lt;/li&gt;
&lt;/ol&gt;

</description>
      <category>ai</category>
      <category>cloudcomputing</category>
    </item>
    <item>
      <title>How to leave AWS Organization with root access management after Landing Zone deployment</title>
      <dc:creator>Phạm Nguyễn Hải Anh</dc:creator>
      <pubDate>Sun, 26 Jan 2025 16:09:05 +0000</pubDate>
      <link>https://dev.to/aws-builders/how-to-leave-aws-organization-with-root-access-management-after-landing-zone-deployment-57ol</link>
      <guid>https://dev.to/aws-builders/how-to-leave-aws-organization-with-root-access-management-after-landing-zone-deployment-57ol</guid>
      <description>&lt;h2&gt;
  
  
  Introduction
&lt;/h2&gt;

&lt;p&gt;The Control Tower Landing Zone is a starting point for organizations to quickly deploy workloads. However, the cleaning up process is not as quick as the deployment process, as it takes 90 days to completely close an AWS account that has been deployed by Control Tower using an existing email. Consequently, the management account in that AWS Organization cannot be used to deploy other Landing Zone for 90 days. To deal with this problem, this blog proposes a solution to make the management account available more quickly for other Landing Zone deployment, with the help of a new IAM feature called root access management.&lt;/p&gt;

&lt;h2&gt;
  
  
  Prerequisites
&lt;/h2&gt;

&lt;ol&gt;
&lt;li&gt;All accounts created by Control Tower Landing Zone must use existing emails. &lt;/li&gt;
&lt;li&gt;The Landing Zone has been decommissioned.
## Implementation
Step 1: In the Management Account, go to the IAM Dashboard. Then select Account settings on the left sidebar. Click the Enable button in the “Centralized root access for member accounts” section:&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F5az931ybp6kx0i3puujj.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F5az931ybp6kx0i3puujj.png" alt=" " width="800" height="317"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Step 2: Enter the account ID of an account within the Organization. This account will become the Delegated Administrator for the IAM service:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fzfngmw6t59jiceo8x1gd.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fzfngmw6t59jiceo8x1gd.png" alt=" " width="800" height="360"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Step 3: In the IAM Dashboard, select the Root access management tab on the left sidebar. Select one account that is not delegated IAM administrator (in this case I choose the Production account) and choose "Take privileged action":&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F3kqru00a6dsfpc0qo44x.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F3kqru00a6dsfpc0qo44x.png" alt=" " width="800" height="419"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Step 4: Select “Allow password recovery”:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fhpg1sbqzczpqagokjkzh.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fhpg1sbqzczpqagokjkzh.png" alt=" " width="800" height="255"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Step 5: Log in to the root account with the email &lt;a href="mailto:haithe123123@yahoo.com"&gt;haithe123123@yahoo.com&lt;/a&gt; (log in to your account), and receive "password reset requirement" notification:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fwb3eek79qg8fuv1s8k13.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fwb3eek79qg8fuv1s8k13.png" alt=" " width="457" height="768"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Step 6: Click Forgot password then an email will be sent:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fx9e5cni5k1du6h1ft7wv.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fx9e5cni5k1du6h1ft7wv.png" alt=" " width="800" height="569"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Step 7: Check the email (in my case, I check Yahoo):&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fn5ushriuu44rq0rz9sgg.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fn5ushriuu44rq0rz9sgg.png" alt=" " width="800" height="279"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Step 8: Click the link in the email and enter a new password:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fqzyn7vycl6pzmu19rqth.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fqzyn7vycl6pzmu19rqth.png" alt=" " width="800" height="635"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Step 9: You will receive a new email notifying you that the account password has been successfully reset:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fbtwnqwkisl8n1wn5dekq.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fbtwnqwkisl8n1wn5dekq.png" alt=" " width="800" height="443"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Step 10: Log in using the new password:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fkawoa35kxo0s7eiwogpp.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fkawoa35kxo0s7eiwogpp.png" alt=" " width="758" height="914"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Step 11: Set up a payment method in the Billing Dashboard in the Payment Preferences section on the left sidebar. Click the Add payment method button:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F64jsuiexgm6a3ha9kl8v.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F64jsuiexgm6a3ha9kl8v.png" alt=" " width="800" height="239"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Step 12: Enter the card information (😊) and enable “Set as default payment method”&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F1si6cf9u8y2ys0ioxxwx.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F1si6cf9u8y2ys0ioxxwx.png" alt=" " width="800" height="466"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Step 13: Fill in the remaining information (for legal purposes) and submit:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fpuzgvgdllka47et6zx4f.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fpuzgvgdllka47et6zx4f.png" alt=" " width="742" height="164"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Step 14: Go to the Organization and leave by selecting “Leave this organization”:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fvkjl12fmxmt1rkketocr.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fvkjl12fmxmt1rkketocr.png" alt=" " width="800" height="329"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Step 15: Confirm leaving&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fvm5ebm9sxy95zhyeqooo.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fvm5ebm9sxy95zhyeqooo.png" alt=" " width="800" height="329"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Step 16: The system will display an error. Click on the link to complete the account information:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fiw0ossry504zuzxfi4uf.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fiw0ossry504zuzxfi4uf.png" alt=" " width="800" height="249"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Step 17: Enter the personal phone number&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fmgy8xzk7d1j3ljpswuvk.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fmgy8xzk7d1j3ljpswuvk.png" alt=" " width="800" height="798"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Step 18: AWS will call the phone number, switch to the keypad and enter the number provided on the browser screen&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F677lvgxkwlrbxwt0fmyy.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F677lvgxkwlrbxwt0fmyy.png" alt=" " width="800" height="399"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fvpsw04aljdngcl7u979t.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fvpsw04aljdngcl7u979t.png" alt=" " width="331" height="717"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Step 19: After entering the number, the browser will redirect to support plan selection page. Select “Basic support – Free” and click “Complete sign up”:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fokgl6l86qxro81t2ozka.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fokgl6l86qxro81t2ozka.png" alt=" " width="800" height="867"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Step 20: You can see the email about AWS Support Sign-Up Confirmation notification:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F603ti0vmn0bu5ho8e84z.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F603ti0vmn0bu5ho8e84z.png" alt=" " width="800" height="477"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Step 21: Return to the account, go to the Organization, and select remove the account. You will be notified that it may take a few days:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fzsmh5x5yvtas013rohgk.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fzsmh5x5yvtas013rohgk.png" alt=" " width="663" height="600"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;or can successfully leave the Organization&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Flaaepk2vemsysduocu3k.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Flaaepk2vemsysduocu3k.png" alt=" " width="800" height="73"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Step 22: Repeat these steps for the remaining member accounts. &lt;/p&gt;

&lt;p&gt;Eventually, you can close these member accounts, or use them as new management accounts in other unique AWS Organizations.&lt;/p&gt;

</description>
      <category>organization</category>
      <category>aws</category>
    </item>
  </channel>
</rss>
