<?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: Chanyeong Yun</title>
    <description>The latest articles on DEV Community by Chanyeong Yun (@edwardyun).</description>
    <link>https://dev.to/edwardyun</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.us-east-2.amazonaws.com%2Fuploads%2Fuser%2Fprofile_image%2F3842668%2F4de1be80-2968-420d-91a4-41721397886e.png</url>
      <title>DEV Community: Chanyeong Yun</title>
      <link>https://dev.to/edwardyun</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/edwardyun"/>
    <language>en</language>
    <item>
      <title>Beyond the Hype: What I Learned from a 45-Minute AI Research Agent Lab</title>
      <dc:creator>Chanyeong Yun</dc:creator>
      <pubDate>Sat, 01 Aug 2026 02:50:53 +0000</pubDate>
      <link>https://dev.to/edwardyun/beyond-the-hype-what-i-learned-from-a-45-minute-ai-research-agent-lab-4bm5</link>
      <guid>https://dev.to/edwardyun/beyond-the-hype-what-i-learned-from-a-45-minute-ai-research-agent-lab-4bm5</guid>
      <description>&lt;p&gt;AI Agents are everywhere right now. We've all seen the flashy demos and read countless articles, but I wanted to understand how a research agent actually works beyond the marketing buzz.&lt;/p&gt;

&lt;p&gt;So I spent about 45 minutes completing &lt;a href="https://learn.microsoft.com/credentials/applied-skills/generate-reports-with-ai-research-agents/?wt.mc_id=studentamb_597187" rel="noopener noreferrer"&gt;Microsoft's Applied Skills – Generate Reports with AI Research Agents&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;Unlike a typical certification exam, this is a hands-on lab that runs inside a virtual machine. Instead of answering multiple-choice questions, you're guided through building an AI-assisted reporting workflow using Microsoft 365 Copilot Chat and Research Agents.&lt;/p&gt;




&lt;h3&gt;
  
  
  💡 What Stood Out to Me
&lt;/h3&gt;

&lt;p&gt;The most interesting part wasn't the AI model itself—it was the &lt;strong&gt;workflow&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;Rather than asking a single prompt and hoping for a good answer, the Research Agent follows a structured process:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;strong&gt;Analyzes&lt;/strong&gt; uploaded documents&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Extracts&lt;/strong&gt; relevant information&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Synthesizes&lt;/strong&gt; the findings&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Produces&lt;/strong&gt; a structured report&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;Watching that process happen step by step made the concept of an "AI Agent" much more tangible than simply reading about it in a tech blog.&lt;/p&gt;




&lt;h3&gt;
  
  
  🔍 Is It Worth Trying?
&lt;/h3&gt;

&lt;p&gt;If you're curious about AI Agents or Microsoft 365 Copilot, I'd say yes.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Quick Setup:&lt;/strong&gt; The lab is short (around 45 minutes) and runs entirely in a pre-configured VM, so there's zero local setup required.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Practical Insights:&lt;/strong&gt; It provides a realistic introduction to how AI-assisted document automation works in an enterprise workflow.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Bonus Credential:&lt;/strong&gt; You earn an official Microsoft Applied Skills badge upon completion—which is a nice bonus, but honestly not the main reason I'd recommend it.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;If you've been wanting to experiment with AI Agents firsthand without committing to a massive course, this interactive lab is a great place to start.&lt;/p&gt;

&lt;p&gt;👉 &lt;strong&gt;&lt;a href="https://learn.microsoft.com/credentials/applied-skills/generate-reports-with-ai-research-agents/?wt.mc_id=studentamb_597187" rel="noopener noreferrer"&gt;Try the hands-on lab on Microsoft Learn&lt;/a&gt;&lt;/strong&gt;&lt;/p&gt;




&lt;p&gt;&lt;em&gt;Have you tried any hands-on AI Agent labs recently? I’d love to hear which platforms or tools you’ve found most useful!&lt;/em&gt;&lt;/p&gt;

</description>
      <category>ai</category>
      <category>microsoft</category>
      <category>githubcopilot</category>
      <category>learning</category>
    </item>
    <item>
      <title>PDF Parsing Is the Hidden Bottleneck in Your RAG Pipeline</title>
      <dc:creator>Chanyeong Yun</dc:creator>
      <pubDate>Mon, 27 Jul 2026 09:57:20 +0000</pubDate>
      <link>https://dev.to/edwardyun/pdf-parsing-is-the-hidden-bottleneck-in-your-rag-pipeline-37gn</link>
      <guid>https://dev.to/edwardyun/pdf-parsing-is-the-hidden-bottleneck-in-your-rag-pipeline-37gn</guid>
      <description>&lt;p&gt;When a RAG system returns hallucinatory or plain wrong answers, our first instinct is usually to blame the retriever or swap the LLM for a bigger one.&lt;/p&gt;

&lt;p&gt;However, while stress-testing an end-to-end RAG pipeline on table-heavy Korean documents, I realized the real culprit was located one stage earlier: &lt;strong&gt;the PDF parser.&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;If your parser shreds a table into a stream of flat text, no amount of prompt engineering or high-dimensional embeddings can recover the lost structural context.&lt;/p&gt;




&lt;h2&gt;
  
  
  How Bad Parsing Destroys Retrieval
&lt;/h2&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;strong&gt;Context Pollution:&lt;/strong&gt; When basic parsers inject broken encoding, missing whitespace, or garbage metadata into text chunks, embedding models end up vectorizing noise. Retrieval precision drops immediately.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Structure Collapse:&lt;/strong&gt; This is the ultimate killer for table-heavy PDFs. Flattening a multi-column table destroys the explicit relationships between headers, cells, and values. The LLM then hallucinates connections between unrelated numbers.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;In my test cases, every failure regarding numeric thresholds traced directly back to a table that had been flattened at ingestion.&lt;/p&gt;




&lt;h2&gt;
  
  
  Benchmarking Local PDF Parsers (Korean + Tables)
&lt;/h2&gt;

&lt;p&gt;I benchmarked standard open-source tools against complex Korean PDF documents containing merged cells and multi-line headers:&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Parser / Library&lt;/th&gt;
&lt;th&gt;Table Structure Preservation&lt;/th&gt;
&lt;th&gt;Korean Encoding&lt;/th&gt;
&lt;th&gt;Security / Local&lt;/th&gt;
&lt;th&gt;Notes&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;
&lt;strong&gt;PyPDFLoader&lt;/strong&gt; (LangChain)&lt;/td&gt;
&lt;td&gt;⭐&lt;/td&gt;
&lt;td&gt;❌ Frequent corruption&lt;/td&gt;
&lt;td&gt;✅ Fully local&lt;/td&gt;
&lt;td&gt;Fast, but treats table text as a raw line-by-line dump. Structure is completely lost.&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;pdfplumber&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;⭐⭐⭐⭐&lt;/td&gt;
&lt;td&gt;✅ Clean&lt;/td&gt;
&lt;td&gt;✅ Fully local&lt;/td&gt;
&lt;td&gt;Pure Python. Native &lt;code&gt;extract_tables()&lt;/code&gt;, allows custom spatial layout &amp;amp; coordinate-based extraction.&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;LlamaParse / Commercial APIs&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;⭐⭐⭐⭐⭐&lt;/td&gt;
&lt;td&gt;✅ Clean&lt;/td&gt;
&lt;td&gt;❌ External API&lt;/td&gt;
&lt;td&gt;Incredible extraction accuracy, but sending sensitive documents to third-party APIs was a non-starter for security.&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;h3&gt;
  
  
  Why I Built a &lt;code&gt;pdfplumber&lt;/code&gt; Pipeline
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Structure-Aware:&lt;/strong&gt; It preserves row/column coordinate boundaries, allowing us to reconstruct markdown tables accurately.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Local &amp;amp; Lightweight:&lt;/strong&gt; No external API dependency, zero risk of data exposure.&lt;/li&gt;
&lt;/ul&gt;




&lt;h2&gt;
  
  
  Overcoming &lt;code&gt;pdfplumber&lt;/code&gt; Limitations with Post-Processing
&lt;/h2&gt;

&lt;p&gt;While &lt;code&gt;pdfplumber&lt;/code&gt; is far better than raw text dumps, it isn't magic out of the box. I had to implement a few custom post-processing rules to make the output LLM-ready:&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="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;pdfplumber&lt;/span&gt;

&lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;extract_and_clean_tables&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;pdf_path&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
    &lt;span class="n"&gt;cleaned_tables&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;[]&lt;/span&gt;
    &lt;span class="k"&gt;with&lt;/span&gt; &lt;span class="n"&gt;pdfplumber&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;open&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;pdf_path&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;as&lt;/span&gt; &lt;span class="n"&gt;pdf&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
        &lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="n"&gt;page&lt;/span&gt; &lt;span class="ow"&gt;in&lt;/span&gt; &lt;span class="n"&gt;pdf&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;pages&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
            &lt;span class="n"&gt;tables&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;page&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;extract_tables&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
            &lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="n"&gt;table&lt;/span&gt; &lt;span class="ow"&gt;in&lt;/span&gt; &lt;span class="n"&gt;tables&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
                &lt;span class="c1"&gt;# 1. Handle merged cells returning as None
&lt;/span&gt;                &lt;span class="c1"&gt;# 2. Normalize in-cell line breaks (\n -&amp;gt; " ")
&lt;/span&gt;                &lt;span class="n"&gt;cleaned_table&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;[]&lt;/span&gt;
                &lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="n"&gt;row&lt;/span&gt; &lt;span class="ow"&gt;in&lt;/span&gt; &lt;span class="n"&gt;table&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
                    &lt;span class="n"&gt;cleaned_row&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;
                        &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;cell&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;replace&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="se"&gt;\n&lt;/span&gt;&lt;span class="sh"&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; &lt;/span&gt;&lt;span class="sh"&gt;"&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;cell&lt;/span&gt; &lt;span class="k"&gt;else&lt;/span&gt; &lt;span class="sh"&gt;""&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
                        &lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="n"&gt;cell&lt;/span&gt; &lt;span class="ow"&gt;in&lt;/span&gt; &lt;span class="n"&gt;row&lt;/span&gt;
                    &lt;span class="p"&gt;]&lt;/span&gt;
                    &lt;span class="n"&gt;cleaned_table&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;append&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;cleaned_row&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
                &lt;span class="n"&gt;cleaned_tables&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;append&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;cleaned_table&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;cleaned_tables&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Merged Cell Handling:&lt;/strong&gt; Blank-fill &lt;code&gt;None&lt;/code&gt; values left behind by merged spans.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;In-cell Newlines:&lt;/strong&gt; Replace internal &lt;code&gt;\n&lt;/code&gt; with spaces so multi-line text cells don't fake a new row boundary.&lt;/li&gt;
&lt;/ul&gt;




&lt;h2&gt;
  
  
  Results: Golden Set Evaluation
&lt;/h2&gt;

&lt;p&gt;I re-ran a test set of domain-specific queries against both pipelines:&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Query Type&lt;/th&gt;
&lt;th&gt;Default PyPDFLoader Pipeline&lt;/th&gt;
&lt;th&gt;Custom pdfplumber Pipeline&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;Numeric Thresholds (Reference tables)&lt;/td&gt;
&lt;td&gt;❌ Failed (retrieved wrong row)&lt;/td&gt;
&lt;td&gt;✅ Exact Match&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Formula / Term Definitions&lt;/td&gt;
&lt;td&gt;❌ Failed (text mashed together)&lt;/td&gt;
&lt;td&gt;✅ Exact Match&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Recurring Schedule Values&lt;/td&gt;
&lt;td&gt;⚠️ Partial / Hallucinated&lt;/td&gt;
&lt;td&gt;✅ Exact Match&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;




&lt;h2&gt;
  
  
  Key Takeaways
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Parsing quality IS retrieval quality:&lt;/strong&gt; Don't waste weeks tuning chunk sizes or fine-tuning embeddings if your input text is already corrupted at parsing time.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Minimize context pollution:&lt;/strong&gt; Preserve table boundaries (e.g., convert to clean Markdown tables before chunking) to give the retriever and LLM the best chance of success.&lt;/li&gt;
&lt;/ul&gt;




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

&lt;p&gt;Exploring local open-source Vision Language Models (e.g., Qwen2-VL or Florence-2) to parse extremely complex, nested image-based tables without sending data off-premise.&lt;/p&gt;

</description>
      <category>rag</category>
      <category>llm</category>
      <category>python</category>
      <category>ai</category>
    </item>
    <item>
      <title>31.8x Speedup by Changing One File: Async Embedding Calls on AWS Bedrock</title>
      <dc:creator>Chanyeong Yun</dc:creator>
      <pubDate>Mon, 27 Jul 2026 07:16:14 +0000</pubDate>
      <link>https://dev.to/edwardyun/318x-speedup-by-changing-one-file-async-embedding-calls-on-aws-bedrock-4l61</link>
      <guid>https://dev.to/edwardyun/318x-speedup-by-changing-one-file-async-embedding-calls-on-aws-bedrock-4l61</guid>
      <description>&lt;p&gt;I was testing a RAG ingestion pipeline and noticed something painful: ingesting a single document took nearly 50 seconds.&lt;/p&gt;

&lt;p&gt;Checking the metrics revealed that the CPU was doing almost nothing. The app wasn't compute-bound — it was simply sitting idle, waiting for sequential HTTP network round-trips to return.&lt;/p&gt;

&lt;p&gt;By converting the blocking embedding module into an asynchronous workflow, processing time dropped from &lt;strong&gt;49.61 seconds to 1.56 seconds&lt;/strong&gt; with zero infrastructure changes.&lt;/p&gt;




&lt;h2&gt;
  
  
  Benchmark Setup
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Model&lt;/strong&gt;: Amazon Titan Text Embeddings V2 (AWS Bedrock)&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Dataset&lt;/strong&gt;: 33 text chunks from a single document&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Region&lt;/strong&gt;: &lt;code&gt;us-east-1&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Test&lt;/strong&gt;: Sequential blocking requests vs. concurrent asynchronous requests&lt;/li&gt;
&lt;/ul&gt;




&lt;h2&gt;
  
  
  Results
&lt;/h2&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Approach&lt;/th&gt;
&lt;th&gt;Time&lt;/th&gt;
&lt;th&gt;Difference&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;
&lt;strong&gt;Sequential&lt;/strong&gt; (&lt;code&gt;requests&lt;/code&gt; blocking loop)&lt;/td&gt;
&lt;td&gt;&lt;strong&gt;49.61s&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Baseline&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;
&lt;strong&gt;Concurrent&lt;/strong&gt; (&lt;code&gt;aiohttp&lt;/code&gt; + &lt;code&gt;asyncio.gather&lt;/code&gt;)&lt;/td&gt;
&lt;td&gt;&lt;strong&gt;1.56s&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;&lt;strong&gt;31.8× faster&lt;/strong&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;




&lt;h2&gt;
  
  
  Why the Gap Was So Massive
&lt;/h2&gt;

&lt;h3&gt;
  
  
  Sequential (Blocking I/O)
&lt;/h3&gt;

&lt;p&gt;Each request must complete before the next one starts.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;chunk 1 ──&amp;gt; request ──&amp;gt; ⏳ wait (~1.5s) ──&amp;gt; response
chunk 2 ──&amp;gt; request ──&amp;gt; ⏳ wait (~1.5s) ──&amp;gt; response
chunk 3 ──&amp;gt; request ──&amp;gt; ⏳ wait (~1.5s) ──&amp;gt; response
...
Total Time ≈ (33 chunks × ~1.5s) ≈ 49.6s
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Since network latency dominates the runtime, the event loop sits completely unused.&lt;/p&gt;

&lt;h3&gt;
  
  
  Concurrent (Non-blocking Async)
&lt;/h3&gt;

&lt;p&gt;Requests are dispatched concurrently; total time collapses down to the duration of the slowest single request.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;chunk 1  ──&amp;gt; request ──────────────&amp;gt; response
chunk 2  ──&amp;gt; request ──────────────&amp;gt; response
chunk 3  ──&amp;gt; request ──────────────&amp;gt; response
...        (in flight concurrently)
Total Time ≈ slowest single request ≈ 1.56s
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;






&lt;h2&gt;
  
  
  The Code Change
&lt;/h2&gt;

&lt;h3&gt;
  
  
  Before: Sequential Execution
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;requests&lt;/span&gt;

&lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;generate_embeddings&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;texts&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;list&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;list&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nb"&gt;list&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nb"&gt;float&lt;/span&gt;&lt;span class="p"&gt;]]:&lt;/span&gt;
    &lt;span class="n"&gt;all_embeddings&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;[]&lt;/span&gt;
    &lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="n"&gt;text&lt;/span&gt; &lt;span class="ow"&gt;in&lt;/span&gt; &lt;span class="n"&gt;texts&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
        &lt;span class="n"&gt;response&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;requests&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;post&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;url&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;headers&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="n"&gt;headers&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;json&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;inputText&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;text&lt;/span&gt;&lt;span class="p"&gt;})&lt;/span&gt;
        &lt;span class="n"&gt;vector&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;response&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;json&lt;/span&gt;&lt;span class="p"&gt;().&lt;/span&gt;&lt;span class="nf"&gt;get&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;embedding&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;[])&lt;/span&gt;
        &lt;span class="n"&gt;all_embeddings&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;append&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;vector&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;all_embeddings&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  After: Concurrent Execution
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;asyncio&lt;/span&gt;
&lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;aiohttp&lt;/span&gt;

&lt;span class="k"&gt;async&lt;/span&gt; &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;fetch_embedding&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;session&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;aiohttp&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;ClientSession&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;text&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;list&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nb"&gt;float&lt;/span&gt;&lt;span class="p"&gt;]:&lt;/span&gt;
    &lt;span class="k"&gt;async&lt;/span&gt; &lt;span class="k"&gt;with&lt;/span&gt; &lt;span class="n"&gt;session&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;post&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;url&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;headers&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="n"&gt;headers&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;json&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;inputText&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;text&lt;/span&gt;&lt;span class="p"&gt;})&lt;/span&gt; &lt;span class="k"&gt;as&lt;/span&gt; &lt;span class="n"&gt;res&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
        &lt;span class="n"&gt;data&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="n"&gt;res&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;json&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;data&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;get&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;embedding&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;[])&lt;/span&gt;

&lt;span class="k"&gt;async&lt;/span&gt; &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;generate_embeddings_async&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;texts&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;list&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;list&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nb"&gt;list&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nb"&gt;float&lt;/span&gt;&lt;span class="p"&gt;]]:&lt;/span&gt;
    &lt;span class="k"&gt;async&lt;/span&gt; &lt;span class="k"&gt;with&lt;/span&gt; &lt;span class="n"&gt;aiohttp&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nc"&gt;ClientSession&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="k"&gt;as&lt;/span&gt; &lt;span class="n"&gt;session&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
        &lt;span class="n"&gt;tasks&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nf"&gt;fetch_embedding&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;session&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;text&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="n"&gt;text&lt;/span&gt; &lt;span class="ow"&gt;in&lt;/span&gt; &lt;span class="n"&gt;texts&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;
        &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="n"&gt;asyncio&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;gather&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="o"&gt;*&lt;/span&gt;&lt;span class="n"&gt;tasks&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;em&gt;Note: If you're using &lt;code&gt;boto3&lt;/code&gt; instead of raw HTTP requests, look at &lt;code&gt;aioboto3&lt;/code&gt;, or offload the blocking SDK calls with &lt;code&gt;asyncio.to_thread()&lt;/code&gt; to get similar non-blocking behaviour.&lt;/em&gt;&lt;/p&gt;




&lt;h2&gt;
  
  
  How It Scales
&lt;/h2&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Chunks&lt;/th&gt;
&lt;th&gt;Sequential (est.)&lt;/th&gt;
&lt;th&gt;Concurrent (est.)&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;31&lt;/td&gt;
&lt;td&gt;49.61s&lt;/td&gt;
&lt;td&gt;1.56s&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;100&lt;/td&gt;
&lt;td&gt;~160s (2.7 min)&lt;/td&gt;
&lt;td&gt;~2–3s&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;500&lt;/td&gt;
&lt;td&gt;~800s (13 min)&lt;/td&gt;
&lt;td&gt;~3–5s&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;1,000&lt;/td&gt;
&lt;td&gt;~1,600s (26 min)&lt;/td&gt;
&lt;td&gt;~5–10s&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;blockquote&gt;
&lt;p&gt;The more chunks you ingest, the more the concurrent approach pays off — batch ingestion jobs that took half an hour now finish in seconds.&lt;/p&gt;
&lt;/blockquote&gt;




&lt;h2&gt;
  
  
  Key Takeaways
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Check for idle waiting first.&lt;/strong&gt; Before scaling up infrastructure or adding complex worker queues, verify whether your pipeline is simply blocked on I/O.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Mind the service quotas.&lt;/strong&gt; Bedrock has per-region request-rate limits — as chunk counts grow into the thousands, cap concurrency (e.g. &lt;code&gt;asyncio.Semaphore&lt;/code&gt;) before you hit them.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Low effort, high impact.&lt;/strong&gt; Changing just one file (&lt;code&gt;embedder.py&lt;/code&gt;) removed the embedding stage as a bottleneck in the pipeline.&lt;/li&gt;
&lt;/ul&gt;

</description>
      <category>aws</category>
      <category>python</category>
      <category>performance</category>
      <category>ai</category>
    </item>
  </channel>
</rss>
