<?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>Forem: ryo ariyama</title>
    <description>The latest articles on Forem by ryo ariyama (@ryo_ariyama_b521d7133c493).</description>
    <link>https://forem.com/ryo_ariyama_b521d7133c493</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%2F3096272%2F10881834-da71-4c47-a41c-6a2fd181f0cd.JPG</url>
      <title>Forem: ryo ariyama</title>
      <link>https://forem.com/ryo_ariyama_b521d7133c493</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://forem.com/feed/ryo_ariyama_b521d7133c493"/>
    <language>en</language>
    <item>
      <title>I Reduced a Week-Long Dev Task to 1 Hour with Claude Code</title>
      <dc:creator>ryo ariyama</dc:creator>
      <pubDate>Sun, 08 Mar 2026 08:53:23 +0000</pubDate>
      <link>https://forem.com/ryo_ariyama_b521d7133c493/i-reduced-a-week-long-dev-task-to-1-hour-with-claude-code-31pm</link>
      <guid>https://forem.com/ryo_ariyama_b521d7133c493/i-reduced-a-week-long-dev-task-to-1-hour-with-claude-code-31pm</guid>
      <description>&lt;h2&gt;
  
  
  Introduction
&lt;/h2&gt;

&lt;p&gt;Recently, I tried out Claude Code on a development project. I'm writing this down as a memo of what I learned and my thoughts on it.&lt;/p&gt;




&lt;h2&gt;
  
  
  What is Claude Code?
&lt;/h2&gt;

&lt;p&gt;Claude Code is an AI agent developed by Anthropic, specialized for code generation. The official documentation describes it as follows:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;Claude Code is an agentic coding tool that reads codebases, edits files, runs commands, and integrates with development tools. It's available in terminals, IDEs, desktop apps, and browsers. Claude Code is an AI-powered coding assistant that helps you build features, fix bugs, and automate development tasks. It can understand your entire codebase and work across multiple files and tools to complete tasks.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;In short, it's a tool that autonomously handles the full development workflow: reading code, implementing changes, running tests, and creating pull requests on GitHub.&lt;/p&gt;




&lt;h2&gt;
  
  
  Background
&lt;/h2&gt;

&lt;p&gt;In my day-to-day work, I maintain and develop various systems — one of which is an ETL tool. This ETL tool connects to various data sources to extract, transform, and load data. Development on it can broadly be divided into two workstreams:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Developing the tool's &lt;strong&gt;interfaces and shared components&lt;/strong&gt;
&lt;/li&gt;
&lt;li&gt;Developing &lt;strong&gt;plugin-style modules&lt;/strong&gt; that connect to individual data sources&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;The second workstream — plugin module development — involves a wide range of data sources such as MySQL, PostgreSQL, and Oracle, which demands significant time for development and research. As the customer base grows and development requests increase, the burden on engineers rises accordingly. Ideally, we'd be able to bring on additional engineers, but chronic understaffing has made that impossible.&lt;/p&gt;

&lt;p&gt;On the other hand, plugin development follows fairly predictable patterns. So I thought: if I could standardize the development process as much as possible and automate it with Claude Code, I could reduce the workload — and that's what led me to adopt it.&lt;/p&gt;




&lt;h2&gt;
  
  
  Installation
&lt;/h2&gt;

&lt;p&gt;Before getting started, you need to install the CLI:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;npm &lt;span class="nb"&gt;install&lt;/span&gt; &lt;span class="nt"&gt;-g&lt;/span&gt; @anthropic-ai/claude-code
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Once installed, run &lt;code&gt;claude&lt;/code&gt; and complete API authentication. Just follow the terminal prompts and you should be fine.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;npm: &lt;a href="https://www.npmjs.com/package/@anthropic-ai/claude-code" rel="noopener noreferrer"&gt;https://www.npmjs.com/package/@anthropic-ai/claude-code&lt;/a&gt;
&lt;/li&gt;
&lt;/ul&gt;




&lt;h2&gt;
  
  
  Practices
&lt;/h2&gt;

&lt;h3&gt;
  
  
  CLAUDE.md
&lt;/h3&gt;

&lt;p&gt;Starting with the official documentation, you can get up and running by following &lt;a href="https://code.claude.com/docs/en/how-claude-code-works" rel="noopener noreferrer"&gt;How Claude Code Works&lt;/a&gt;:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;When you give Claude a task, it works through three phases: &lt;strong&gt;gathering context&lt;/strong&gt;, &lt;strong&gt;taking actions&lt;/strong&gt;, and &lt;strong&gt;verifying results&lt;/strong&gt;. These phases blend together. Claude uses tools to search files to understand your code, edit them to make changes, and run tests to verify its work.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;The agent autonomously handles research, development, and testing. To enable it to do this efficiently, engineers need to provide the necessary resources. This information goes into a markdown file called &lt;code&gt;CLAUDE.md&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;&lt;code&gt;CLAUDE.md&lt;/code&gt; can be placed in several locations:&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Location&lt;/th&gt;
&lt;th&gt;Description&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;~/.claude/CLAUDE.md&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Applies to all Claude sessions&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;
&lt;code&gt;./CLAUDE.md&lt;/code&gt; (project root)&lt;/td&gt;
&lt;td&gt;Check into git to share with your team&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;./CLAUDE.local.md&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Add to &lt;code&gt;.gitignore&lt;/code&gt; for local-only settings&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Parent directories&lt;/td&gt;
&lt;td&gt;Useful for monorepo setups&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Child directories&lt;/td&gt;
&lt;td&gt;Picked up on demand when Claude works in that directory&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;For guidance on how to write a good &lt;code&gt;CLAUDE.md&lt;/code&gt;, these articles are worth reading:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;a href="https://claude.com/blog/using-claude-md-files" rel="noopener noreferrer"&gt;https://claude.com/blog/using-claude-md-files&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://code.claude.com/docs/en/features-overview" rel="noopener noreferrer"&gt;https://code.claude.com/docs/en/features-overview&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;h4&gt;
  
  
  ✅ What to include
&lt;/h4&gt;

&lt;ul&gt;
&lt;li&gt;Bash commands Claude can't infer on its own&lt;/li&gt;
&lt;li&gt;Code style rules that differ from defaults&lt;/li&gt;
&lt;li&gt;Testing instructions and recommended test runners&lt;/li&gt;
&lt;li&gt;Repository etiquette (branch naming, PR conventions)&lt;/li&gt;
&lt;li&gt;Project-specific architectural decisions&lt;/li&gt;
&lt;li&gt;Development environment quirks (required environment variables)&lt;/li&gt;
&lt;li&gt;Common pitfalls or non-obvious behaviors&lt;/li&gt;
&lt;/ul&gt;

&lt;h4&gt;
  
  
  ❌ What to exclude
&lt;/h4&gt;

&lt;ul&gt;
&lt;li&gt;Anything Claude can understand by reading the code&lt;/li&gt;
&lt;li&gt;Standard language conventions Claude already knows&lt;/li&gt;
&lt;li&gt;Detailed API documentation (link to docs instead)&lt;/li&gt;
&lt;li&gt;Frequently changing information&lt;/li&gt;
&lt;li&gt;Long explanations or tutorials&lt;/li&gt;
&lt;li&gt;Per-file codebase explanations&lt;/li&gt;
&lt;li&gt;Self-evident practices like "write clean code"&lt;/li&gt;
&lt;/ul&gt;

&lt;blockquote&gt;
&lt;p&gt;💡 &lt;strong&gt;Best practice:&lt;/strong&gt; Keep &lt;code&gt;CLAUDE.md&lt;/code&gt; under &lt;strong&gt;500 lines&lt;/strong&gt;. Since its contents are loaded into Claude Code's memory, shorter is better.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;To get started, run &lt;code&gt;/init&lt;/code&gt; to generate a sample template. Here's an example:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight markdown"&gt;&lt;code&gt;&lt;span class="gh"&gt;# Project Context&lt;/span&gt;

When working with this codebase, prioritize readability over cleverness.
Ask clarifying questions before making architectural changes.

&lt;span class="gu"&gt;## About This Project&lt;/span&gt;

FastAPI REST API for user authentication and profiles.
Uses SQLAlchemy for database operations and Pydantic for validation.

&lt;span class="gu"&gt;## Key Directories&lt;/span&gt;
&lt;span class="p"&gt;
-&lt;/span&gt; &lt;span class="sb"&gt;`app/models/`&lt;/span&gt; - database models
&lt;span class="p"&gt;-&lt;/span&gt; &lt;span class="sb"&gt;`app/api/`&lt;/span&gt; - route handlers
&lt;span class="p"&gt;-&lt;/span&gt; &lt;span class="sb"&gt;`app/core/`&lt;/span&gt; - configuration and utilities

&lt;span class="gu"&gt;## Standards&lt;/span&gt;
&lt;span class="p"&gt;
-&lt;/span&gt; Type hints required on all functions
&lt;span class="p"&gt;-&lt;/span&gt; pytest for testing (fixtures in &lt;span class="sb"&gt;`tests/conftest.py`&lt;/span&gt;)
&lt;span class="p"&gt;-&lt;/span&gt; PEP 8 with 100 character lines

&lt;span class="gu"&gt;## Common Commands&lt;/span&gt;

uvicorn app.main:app --reload  # dev server
pytest tests/ -v               # run tests

&lt;span class="gu"&gt;## Notes&lt;/span&gt;

All routes use &lt;span class="sb"&gt;`/api/v1`&lt;/span&gt; prefix. JWT tokens expire after 24 hours.
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;






&lt;h3&gt;
  
  
  Skills
&lt;/h3&gt;

&lt;p&gt;In addition to &lt;code&gt;CLAUDE.md&lt;/code&gt;, preparing &lt;strong&gt;Skills&lt;/strong&gt; files is another recommended practice.&lt;/p&gt;

&lt;p&gt;Skills function like procedure manuals that guide the agent through specific tasks. By preparing a &lt;code&gt;SKILL.md&lt;/code&gt; file, the agent will follow its steps during development.&lt;/p&gt;

&lt;p&gt;Anthropic has published example skills in a public repository — a great reference for getting started:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;a href="https://github.com/anthropics/skills" rel="noopener noreferrer"&gt;https://github.com/anthropics/skills&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;For the ETL tool I described earlier, I prepared one skill per workstream. I can then give simple instructions like &lt;em&gt;"create a new module"&lt;/em&gt; or &lt;em&gt;"add a parameter to the interface"&lt;/em&gt;, and the agent takes it from there.&lt;/p&gt;

&lt;p&gt;The directory structure looks like this:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;CLAUDE.md                          # Concise project overview &amp;amp; conventions
docs/skills/
  new-plugin.md                    # Steps for creating a new plugin
  add-interface-parameter.md       # Steps for adding an interface parameter
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;






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

&lt;p&gt;By automating development with the agent, &lt;strong&gt;tasks that would take a senior engineer about a week can now be completed in roughly an hour.&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Additionally, using near-identical template-like prompts, virtually any engineer can now produce the same quality output — achieving faster delivery without sacrificing quality.&lt;/p&gt;




&lt;h2&gt;
  
  
  Closing Thoughts
&lt;/h2&gt;

&lt;p&gt;That wraps up my introduction to Claude Code. What I've covered here is just the basics, and I'm sure there are many more ways to improve on this. I encourage you to try it out and share any useful patterns you discover!&lt;/p&gt;

&lt;p&gt;As a side note — I often hear that AI will take engineers' jobs and previously had the same idea as well, but working with Claude Code has made me think the opposite. Using Claude Code effectively requires knowing good development processes, designing modules that are easy for AI to learn from, knowing how to write proper tests, and how to write clear markdown. Right now, software engineers are best positioned to do all of this. Rather than disappearing, I think the demand for engineers who can efficiently leverage AI will only grow.&lt;/p&gt;

&lt;p&gt;What do you think? I'd love to hear your perspective.&lt;/p&gt;

</description>
      <category>webdev</category>
      <category>ai</category>
      <category>llm</category>
      <category>programming</category>
    </item>
    <item>
      <title>Multi-Tenant Design for Bedrock Knowledge Base: Solving the Account Limit with Metadata Filtering</title>
      <dc:creator>ryo ariyama</dc:creator>
      <pubDate>Thu, 01 Jan 2026 18:26:59 +0000</pubDate>
      <link>https://forem.com/ryo_ariyama_b521d7133c493/multi-tenant-design-for-bedrock-knowledge-base-solving-the-account-limit-with-metadata-filtering-e6b</link>
      <guid>https://forem.com/ryo_ariyama_b521d7133c493/multi-tenant-design-for-bedrock-knowledge-base-solving-the-account-limit-with-metadata-filtering-e6b</guid>
      <description>&lt;h2&gt;
  
  
  Introduction
&lt;/h2&gt;

&lt;p&gt;Recently, while working with Bedrock KnowledgeBase in my daily work, I encountered some challenges related to its specifications that I'd like to share.&lt;/p&gt;

&lt;h2&gt;
  
  
  Background
&lt;/h2&gt;

&lt;p&gt;Currently, I'm developing a multi-tenant application using Bedrock KnowledgeBase (referred to as KB below). To briefly explain KB, it's an orchestrator for implementing LLM RAG that handles vectorization of files into vector stores and can generate context-aware conversations when combined with Bedrock Agent.&lt;/p&gt;

&lt;p&gt;We're using OpenSearch as our vector store, and our design creates separate KBs and indices for each tenant. This approach ensures data isolation between tenants, which seemed like a natural design choice at the time.&lt;br&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%2F7dkm9mk4fkg0h7yghxq8.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%2F7dkm9mk4fkg0h7yghxq8.png" alt=" " width="800" height="465"&gt;&lt;/a&gt;&lt;/p&gt;
&lt;h2&gt;
  
  
  The Problem
&lt;/h2&gt;

&lt;p&gt;At some point, when checking Bedrock's quotas, I found &lt;code&gt;(Knowledge Bases) Knowledge bases per account&lt;/code&gt; quota. This is a limit on the maximum number of KBs you can create within an account, with a hard limit of 100. With our initial design, this meant our application could only support up to 100 tenants. Therefore, we needed to reconsider our design.&lt;/p&gt;
&lt;h2&gt;
  
  
  Solution
&lt;/h2&gt;

&lt;p&gt;In reconsidering the design, we modified it so that KBs and indices are shared across multiple tenants. Since KBs have several parameters related to vectorization, such as ChunkStrategy, we created several combinations of ChunkStrategy and MaxToken parameters and let users select from these options for sharing.&lt;/p&gt;

&lt;p&gt;An important consideration with this approach is ensuring that tenant data isn't referenced during conversations with other tenants. KB provides functionality to attach custom metadata during vectorization, so we adopted a method of attaching tenant_id-like metadata and filtering documents by that ID during conversations.&lt;br&gt;
&lt;a href="https://docs.aws.amazon.com/bedrock/latest/userguide/kb-metadata.html" rel="noopener noreferrer"&gt;https://docs.aws.amazon.com/bedrock/latest/userguide/kb-metadata.html&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Here's the conceptual approach:&lt;br&gt;
&lt;strong&gt;Architecture:&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Shared Knowledge Base across multiple tenants&lt;/li&gt;
&lt;li&gt;Custom metadata (tenant_id) attached to each document&lt;/li&gt;
&lt;li&gt;Metadata filtering during retrieval to ensure data isolation
&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%2Fqbhwkpoguge441t4wyz4.png" alt=" " width="800" height="621"&gt;
&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Below is sample code for attaching metadata to documents:&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="c1"&gt;# ingest_documents
&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;client&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;ingest_knowledge_base_documents&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
    &lt;span class="n"&gt;knowledgeBaseId&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;string&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="n"&gt;dataSourceId&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;string&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="n"&gt;clientToken&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;string&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="n"&gt;documents&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="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;metadata&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="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;type&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;IN_LINE_ATTRIBUTE&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;inlineAttributes&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="p"&gt;{&lt;/span&gt;
                        &lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;key&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;tenant_id&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;value&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="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;type&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;STRING&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;stringValue&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;$tenant_id&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="p"&gt;},&lt;/span&gt;
                &lt;span class="p"&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;content&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="bp"&gt;...&lt;/span&gt;
            &lt;span class="p"&gt;}&lt;/span&gt;
        &lt;span class="p"&gt;},&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;To filter documents by metadata when conversing with the agent, you can implement it with code like this:&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="c1"&gt;## invoke_agent
&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;boto3&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;client&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;invoke_agent&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
        &lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;knowledgeBaseConfigurations&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="p"&gt;{&lt;/span&gt;
                    &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;knowledgeBaseId&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;$vector_store_id&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;description&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;Knowledge base for document retrieval&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;retrievalConfiguration&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="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;vectorSearchConfiguration&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="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;filter&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="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;equals&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="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;key&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;tenant_id&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;value&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;$tenant_id&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="p"&gt;}&lt;/span&gt;
                    &lt;span class="p"&gt;},&lt;/span&gt;
                &lt;span class="p"&gt;}&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;h2&gt;
  
  
  Future Plans
&lt;/h2&gt;

&lt;p&gt;With the above implementation, we can now build the application while successfully avoiding the constraints.&lt;/p&gt;

&lt;p&gt;In multi-tenant applications, it's crucial to monitor that one tenant cannot access another tenant's data. I'm thinking of creating a monitoring mechanism to ensure this isn't possible. For example, I'm considering creating multiple test tenants, inserting different documents into the same vector store for each, asking questions about other tenants' documents, and verifying that no answers are returned. This script could be executed regularly in staging environments. While monitoring system resources like CPU is important, I believe it's equally crucial to monitor data to ensure that data that shouldn't exist according to system specifications doesn't exist.&lt;/p&gt;

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

&lt;p&gt;These are the issues related to KB specifications and our countermeasures. Through this experience, I realized the importance of checking cloud service specifications before deciding on system design. I hope this article will be helpful to you.&lt;/p&gt;

</description>
      <category>aws</category>
      <category>llm</category>
      <category>rag</category>
      <category>webdev</category>
    </item>
    <item>
      <title>Migrating Masking Database from Amazon Aurora to BigQuery: Performance Improvement and Security Implementation</title>
      <dc:creator>ryo ariyama</dc:creator>
      <pubDate>Thu, 01 Jan 2026 12:51:18 +0000</pubDate>
      <link>https://forem.com/ryo_ariyama_b521d7133c493/migrating-masking-database-from-amazon-aurora-to-bigquery-performance-improvement-and-security-4kdi</link>
      <guid>https://forem.com/ryo_ariyama_b521d7133c493/migrating-masking-database-from-amazon-aurora-to-bigquery-performance-improvement-and-security-4kdi</guid>
      <description>&lt;h2&gt;
  
  
  Introduction
&lt;/h2&gt;

&lt;p&gt;In a previous project, we used Amazon Aurora as our business database and synchronized this data to BigQuery for use as an analytics platform.&lt;br&gt;
We had an opportunity to improve the performance of this synchronization process, and I'd like to share what we did.&lt;/p&gt;
&lt;h2&gt;
  
  
  Target Audience
&lt;/h2&gt;

&lt;p&gt;This article is intended for:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Those who want to build an analytics platform with BigQuery&lt;/li&gt;
&lt;li&gt;Those who want to learn about BigQuery security measures such as access restrictions&lt;/li&gt;
&lt;li&gt;Those who want to know practical examples of data masking implementation&lt;/li&gt;
&lt;/ul&gt;
&lt;h2&gt;
  
  
  Background
&lt;/h2&gt;

&lt;p&gt;In that project, we used Amazon Aurora as our application database. Since we stored sensitive information such as customer data, access to the database was restricted to specific operational terminals and limited operational members only.&lt;/p&gt;

&lt;p&gt;On the other hand, the development team had the following needs, so we built a database with masked sensitive information daily and provided it to the development team:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Connect with backend applications for development&lt;/li&gt;
&lt;li&gt;Query the database for troubleshooting and debugging purposes&lt;/li&gt;
&lt;li&gt;Use as a data source for the data analytics platform&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;In the existing method, we created the masking database by cloning the production database and directly updating records with UPDATE statements.&lt;br&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%2Fyg9ecms7bxi87j1szi89.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%2Fyg9ecms7bxi87j1szi89.png" alt=" " width="800" height="340"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;We had been operating this way for some time, but the masking process performance had been degrading.&lt;br&gt;
One reason was table lock contention occurring during data masking.&lt;br&gt;
When directly updating records with UPDATE statements, tables need to be locked to synchronize with other tables.&lt;br&gt;
During the masking process, we were executing UPDATE statements on all records of almost all tables in the database, which made table locks more likely to occur, resulting in massive write wait times.&lt;/p&gt;

&lt;p&gt;The masking database was also used as a data source for the data analytics platform in addition to development and testing.&lt;br&gt;
Using this analytics platform, we provided KPI dashboards to management by a specified time, but the masking process became a bottleneck, and there was concern that dashboard creation would regularly fail to meet the deadline in the near future.&lt;br&gt;
Therefore, the challenge was to improve the masking process mechanism and reduce KPI dashboard creation time.&lt;/p&gt;
&lt;h2&gt;
  
  
  Improved Architecture
&lt;/h2&gt;

&lt;p&gt;When considering improvement plans, we explored improvements from the following perspectives:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Offload to some storage other than Aurora&lt;/li&gt;
&lt;li&gt;Perform masking processing by methods other than updating records&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;For the first perspective, since the analytics platform was using BigQuery as the data warehouse, we thought we could achieve this by transferring Aurora data to BigQuery.&lt;br&gt;
For the second perspective, instead of updating database values, we thought we could achieve this by executing the masking logic that was previously done with UPDATE statements through SELECT statements when retrieving data.&lt;/p&gt;

&lt;p&gt;Specifically, we adopted the following configuration:&lt;br&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%2F45mgulaxgwzm3fagrls8.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%2F45mgulaxgwzm3fagrls8.png" alt=" " width="800" height="216"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;We created a new project for the masking database and built a database equivalent to the masking database in BigQuery of this project with the following flow:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Export Aurora records to S3&lt;/li&gt;
&lt;li&gt;Transfer data from S3 to BigQuery tables using BigQuery Data Transfer Service&lt;/li&gt;
&lt;li&gt;Query Views with masking logic written based on the transferred data and link to analytics platform tables&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;The implemented View looks like this:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight sql"&gt;&lt;code&gt;&lt;span class="c1"&gt;-- Conventional masking with UPDATE statement&lt;/span&gt;
&lt;span class="k"&gt;UPDATE&lt;/span&gt; &lt;span class="k"&gt;User&lt;/span&gt;
&lt;span class="k"&gt;SET&lt;/span&gt; &lt;span class="n"&gt;Name&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nv"&gt;"***"&lt;/span&gt;  &lt;span class="c1"&gt;-- Replace Name column in User table with *** uniformly as it contains sensitive information&lt;/span&gt;

&lt;span class="c1"&gt;-- Improved masking with SELECT statement&lt;/span&gt;
&lt;span class="k"&gt;SELECT&lt;/span&gt;
  &lt;span class="nv"&gt;"***"&lt;/span&gt; &lt;span class="k"&gt;AS&lt;/span&gt; &lt;span class="n"&gt;Name&lt;/span&gt;
&lt;span class="k"&gt;FROM&lt;/span&gt;
  &lt;span class="k"&gt;User&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;By making these improvements, we achieved the following performance improvements and reduced processing time from about 3 hours to about 1 hour:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Improved overall query performance by offloading to BigQuery&lt;/li&gt;
&lt;li&gt;Eliminated the need to synchronize with other tables by not writing values directly&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;While we could improve performance, this configuration required storing sensitive information in BigQuery.&lt;br&gt;
In the next chapter, I'll introduce the security measures we implemented when storing sensitive information.&lt;/p&gt;
&lt;h2&gt;
  
  
  Implemented Security Measures
&lt;/h2&gt;
&lt;h3&gt;
  
  
  Applying Access Restrictions to BigQuery
&lt;/h3&gt;

&lt;p&gt;To treat BigQuery equivalently to Aurora, we needed to apply the following access restrictions to datasets containing personal information:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Only infrastructure members and system accounts necessary for the service can access&lt;/li&gt;
&lt;li&gt;Accessible only from specific operational terminals&lt;/li&gt;
&lt;li&gt;BigQuery data export is restricted to the analytics platform project&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;The first requirement could be addressed with IAM restrictions, but we needed to consider another method for the second requirement.&lt;/p&gt;

&lt;p&gt;VPC Service Controls is a service for managing access to GCP resources. By using this, we can place GCP resources in a private perimeter and restrict service access by specifying allowed IP addresses and IAM.&lt;br&gt;
We used this service to allow operations on production resources by permitting operational member IDs and the global IP used by operational terminals as inbound rules.&lt;br&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%2Ffsrhj4z6b6zzm06xe7fz.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%2Ffsrhj4z6b6zzm06xe7fz.png" alt=" " width="800" height="259"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Also, we use Terraform Cloud for deploying BigQuery resources, and we needed to make changes to the Terraform Cloud execution environment to accommodate these access restrictions.&lt;br&gt;
When using Terraform Cloud, you can select the command execution environment, which is normally within the Terraform Cloud environment. In this case, the global IP of the execution environment also changes dynamically.&lt;br&gt;
To fix the global IP permitted by inbound rules, we decided to use Terraform Cloud Agent. Using Agent allows you to specify the execution location of terraform commands to your own environment.&lt;br&gt;
We built the Agent by constructing a GCE instance and starting a container within the instance.&lt;/p&gt;

&lt;p&gt;While the Agent explanation and detailed configuration methods are described in the official documentation, by executing the following shell script as a GCE startup script based on the obtained Agent name and token, we could start the Agent when the instance runs:&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;#!/bin/bash&lt;/span&gt;

&lt;span class="nb"&gt;sudo &lt;/span&gt;apt-get update &lt;span class="o"&gt;&amp;amp;&amp;amp;&lt;/span&gt; &lt;span class="nb"&gt;sudo &lt;/span&gt;apt-get &lt;span class="nb"&gt;install&lt;/span&gt; &lt;span class="nt"&gt;-y&lt;/span&gt; &lt;span class="se"&gt;\&lt;/span&gt;
    ca-certificates &lt;span class="se"&gt;\&lt;/span&gt;
    curl &lt;span class="se"&gt;\&lt;/span&gt;
    gnupg &lt;span class="se"&gt;\&lt;/span&gt;
    lsb-release

curl &lt;span class="nt"&gt;-fsSL&lt;/span&gt; https://download.docker.com/linux/debian/gpg | &lt;span class="nb"&gt;sudo &lt;/span&gt;gpg &lt;span class="nt"&gt;--dearmor&lt;/span&gt; &lt;span class="nt"&gt;-o&lt;/span&gt; /usr/share/keyrings/docker-archive-keyring.gpg

&lt;span class="nb"&gt;echo&lt;/span&gt; &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="s2"&gt;"deb [arch=&lt;/span&gt;&lt;span class="si"&gt;$(&lt;/span&gt;dpkg &lt;span class="nt"&gt;--print-architecture&lt;/span&gt;&lt;span class="si"&gt;)&lt;/span&gt;&lt;span class="s2"&gt; signed-by=/usr/share/keyrings/docker-archive-keyring.gpg] https://download.docker.com/linux/debian &lt;/span&gt;&lt;span class="se"&gt;\&lt;/span&gt;&lt;span class="s2"&gt;
  &lt;/span&gt;&lt;span class="si"&gt;$(&lt;/span&gt;lsb_release &lt;span class="nt"&gt;-cs&lt;/span&gt;&lt;span class="si"&gt;)&lt;/span&gt;&lt;span class="s2"&gt; stable"&lt;/span&gt; | &lt;span class="nb"&gt;sudo tee&lt;/span&gt; /etc/apt/sources.list.d/docker.list &lt;span class="o"&gt;&amp;gt;&lt;/span&gt; /dev/null

&lt;span class="nb"&gt;sudo &lt;/span&gt;apt-get update &lt;span class="o"&gt;&amp;amp;&amp;amp;&lt;/span&gt; &lt;span class="nb"&gt;sudo &lt;/span&gt;apt-get &lt;span class="nb"&gt;install&lt;/span&gt; &lt;span class="nt"&gt;-y&lt;/span&gt; &lt;span class="se"&gt;\&lt;/span&gt;
  containerd.io &lt;span class="se"&gt;\&lt;/span&gt;
  docker-ce &lt;span class="se"&gt;\&lt;/span&gt;
  docker-ce-cli &lt;span class="se"&gt;\&lt;/span&gt;
  docker-compose-plugin

&lt;span class="nv"&gt;TFC_AGENT_TOKEN&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="k"&gt;${&lt;/span&gt;&lt;span class="nv"&gt;tfc_agent_token&lt;/span&gt;&lt;span class="k"&gt;}&lt;/span&gt; &lt;span class="se"&gt;\&lt;/span&gt;
&lt;span class="nv"&gt;TFC_AGENT_NAME&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="k"&gt;${&lt;/span&gt;&lt;span class="nv"&gt;tfc_agent_name&lt;/span&gt;&lt;span class="k"&gt;}&lt;/span&gt; &lt;span class="se"&gt;\&lt;/span&gt;
docker run &lt;span class="nt"&gt;-d&lt;/span&gt; &lt;span class="nt"&gt;-e&lt;/span&gt; TFC_AGENT_TOKEN &lt;span class="nt"&gt;-e&lt;/span&gt; TFC_AGENT_NAME hashicorp/tfc-agent:latest
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;For the third requirement, since we were performing ETL across GCP using Cloud Workflows, we solved this by permitting the Cloud Workflows service account and analytics platform project as outbound rules.&lt;/p&gt;

&lt;h3&gt;
  
  
  Using Service Account Keys Without Issuing Credentials
&lt;/h3&gt;

&lt;p&gt;This time, to access GCP resources from Terraform Cloud and AWS, we needed to use service accounts to operate resources from outside GCP.&lt;br&gt;
When using service accounts from outside GCP such as other clouds, there's a method of issuing credentials, but we decided not to issue them for the following reasons:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;If the key is leaked, anyone can execute the service account's permissions&lt;/li&gt;
&lt;li&gt;Credentials have no expiration date, so rotation needs to be managed manually&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Instead, we used Workload Identity to operate GCP resources from AWS and Terraform Cloud without issuing service account keys.&lt;br&gt;
For example, when using Workload Identity with Terraform Cloud, the flow is as follows:&lt;br&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%2Fkfbx4jminettq0hlsnl1.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%2Fkfbx4jminettq0hlsnl1.png" alt=" " width="800" height="254"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Following the official documentation, authentication is performed with the following steps:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Send an authentication token to GCP when executing Terraform commands&lt;/li&gt;
&lt;li&gt;Verify the token's validity, and if no problems, issue a temporary token and send to Terraform Cloud&lt;/li&gt;
&lt;li&gt;Set the temporary token in environment variables and execute commands&lt;/li&gt;
&lt;li&gt;Discard the temporary token after command completion&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;The configuration method follows the GCP Configuration guide. The procedure roughly consists of:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Create Workload Identity Pool and Provider

&lt;ul&gt;
&lt;li&gt;The attribute mapping values can be confusing, but the sample code is helpful&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;Create a service account and grant necessary permissions&lt;/li&gt;
&lt;li&gt;Set the following values in Terraform Cloud environment variables:

&lt;ul&gt;
&lt;li&gt;
&lt;code&gt;TFC_GCP_PROJECT_NUMBER&lt;/code&gt;: GCP project number&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;TFC_GCP_PROVIDER_AUTH&lt;/code&gt;: true&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;TFC_GCP_RUN_SERVICE_ACCOUNT_EMAIL&lt;/code&gt;: Email of the service account to use&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;TFC_GCP_WORKLOAD_POOL_ID&lt;/code&gt;: Workload Pool ID&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;TFC_GCP_WORKLOAD_PROVIDER_ID&lt;/code&gt;: Workload Provider ID&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;By using temporary authentication information instead of service account keys in this way, we avoided the risk of key leakage.&lt;/p&gt;

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

&lt;p&gt;By migrating the masking database from Amazon Aurora to BigQuery, we were able to reduce processing time from 3 hours to 1 hour.&lt;br&gt;
Additionally, by utilizing VPC Service Controls and Workload Identity, we were able to store sensitive information in BigQuery while ensuring security.&lt;/p&gt;

&lt;p&gt;I hope this will be helpful for those who have similar challenges.&lt;/p&gt;

</description>
      <category>googlecloud</category>
      <category>terraform</category>
      <category>programming</category>
      <category>webdev</category>
    </item>
    <item>
      <title>How I Stay Healthy While Working 250 Hours a Month as a Software Engineer</title>
      <dc:creator>ryo ariyama</dc:creator>
      <pubDate>Tue, 30 Dec 2025 11:56:54 +0000</pubDate>
      <link>https://forem.com/ryo_ariyama_b521d7133c493/how-i-stay-healthy-while-working-250-hours-a-month-as-a-software-engineer-mc5</link>
      <guid>https://forem.com/ryo_ariyama_b521d7133c493/how-i-stay-healthy-while-working-250-hours-a-month-as-a-software-engineer-mc5</guid>
      <description>&lt;h2&gt;
  
  
  Introduction
&lt;/h2&gt;

&lt;p&gt;I work as a software engineer with a full-time position plus freelance contracts, totaling around 250 hours per month. Compared to the typical 160-hour workload, that's a significant amount of time in front of a screen.&lt;/p&gt;

&lt;p&gt;Over time, I've developed habits that help me maintain both productivity and health. Here's what has worked for me, ranked by impact.&lt;/p&gt;

&lt;h2&gt;
  
  
  What Has Worked
&lt;/h2&gt;

&lt;h3&gt;
  
  
  1. Let AI Agents Do the Heavy Lifting
&lt;/h3&gt;

&lt;p&gt;This is the most impactful change I've made. The key insight: &lt;strong&gt;minimize cognitive load to maintain quality over long hours&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;My typical day starts around 10:30 AM and ends around midnight, sometimes later when deadlines hit. By 7 PM, my brain is noticeably fatigued—similar to an athlete losing stamina in the second half of a game.&lt;/p&gt;

&lt;p&gt;I noticed that bugs in my code correlated strongly with late-night commits. The root cause? Mental fatigue leading to shortcuts and oversights.&lt;/p&gt;

&lt;p&gt;Writing complex logic and tests from scratch requires peak mental energy. So now, I delegate the implementation to coding agents (like GitHub Copilot Agent or Claude Code) and focus on reviewing their output. Reviewing is cognitively lighter than creating from scratch, which lets me maintain reasonable quality even when tired.&lt;/p&gt;

&lt;p&gt;Since adopting this approach, bug reports have noticeably decreased—though I admit I haven't measured this quantitatively yet.&lt;/p&gt;

&lt;h3&gt;
  
  
  2. Take Supplements for Eye Health
&lt;/h3&gt;

&lt;p&gt;Earlier this year, eye strain became a serious problem. Staring at monitors for 12+ hours daily was catching up with me.&lt;/p&gt;

&lt;p&gt;After trying several solutions, &lt;strong&gt;lutein supplements&lt;/strong&gt; made the biggest difference for my eye fatigue. For general health, I take Ebios (a brewer's yeast supplement popular in Japan) which helps with digestion and provides B vitamins. Reducing digestive stress has noticeably improved my daily energy levels.&lt;/p&gt;

&lt;h3&gt;
  
  
  3. Use a Standing Desk
&lt;/h3&gt;

&lt;p&gt;I switched to a standing desk a while ago and rarely sit while working now.&lt;/p&gt;

&lt;p&gt;Prolonged sitting puts significant strain on your lower back. I didn't notice it much when I was younger, but recently I've started feeling the effects. Senior engineers have repeatedly warned me: "Your back will give out suddenly one day."&lt;/p&gt;

&lt;p&gt;Now I stand by default and only sit during breaks. Prevention is easier than recovery.&lt;/p&gt;

&lt;h3&gt;
  
  
  4. Build an Exercise Routine
&lt;/h3&gt;

&lt;p&gt;This one's obvious but essential:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;3 days/week&lt;/strong&gt;: 1-hour strength training at the gym&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Regular walks&lt;/strong&gt;: About 2 hours of outdoor walking&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Monthly hiking&lt;/strong&gt;: 5-6 hour climbs on nearby mountains&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Physical activity is the most effective way to offset the stress of knowledge work. I used to try unwinding with video games, but that just added more screen time and eye strain.&lt;/p&gt;

&lt;p&gt;Hiking has a bonus benefit: you're constantly looking at distant scenery, which helps your eyes recover from close-up screen focus.&lt;br&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%2Fdwk32sr2k7svxp00wb7l.jpeg" 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%2Fdwk32sr2k7svxp00wb7l.jpeg" alt=" " width="800" height="610"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Final Thoughts
&lt;/h2&gt;

&lt;p&gt;None of this is revolutionary. But consistently applying these habits has allowed me to sustain a demanding workload without burning out.&lt;/p&gt;

&lt;p&gt;Technical skills matter, but so does knowing how to maintain the body and mind that use those skills. I hope this helps someone else navigating a similar schedule.&lt;/p&gt;




&lt;p&gt;&lt;em&gt;What are your strategies for staying healthy with long coding hours? I'd love to hear what works for you in the comments.&lt;/em&gt;&lt;/p&gt;

</description>
      <category>webdev</category>
      <category>programming</category>
      <category>mentalhealth</category>
      <category>softwareengineering</category>
    </item>
    <item>
      <title>Building an Interpreter in Rust: A Journey Through Lexical Analysis, Parsing, and Evaluation</title>
      <dc:creator>ryo ariyama</dc:creator>
      <pubDate>Sun, 21 Dec 2025 16:51:20 +0000</pubDate>
      <link>https://forem.com/ryo_ariyama_b521d7133c493/building-an-interpreter-in-rust-a-journey-through-lexical-analysis-parsing-and-evaluation-2jg0</link>
      <guid>https://forem.com/ryo_ariyama_b521d7133c493/building-an-interpreter-in-rust-a-journey-through-lexical-analysis-parsing-and-evaluation-2jg0</guid>
      <description>&lt;h2&gt;
  
  
  Introduction
&lt;/h2&gt;

&lt;p&gt;I previously read &lt;a href="https://interpreterbook.com/#the-monkey-programming-language" rel="noopener noreferrer"&gt;Writing An Interpreter In Go&lt;/a&gt; and implemented the interpreter in Rust for learning purposes. I'll put what I learned in this article.&lt;/p&gt;

&lt;h2&gt;
  
  
  The Output
&lt;/h2&gt;

&lt;p&gt;The completed interpreter can be found here. Since it's Rust source code mimicking Go, I named the repository &lt;code&gt;imitation_interpreter&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://github.com/rariyama/imitation_interpreter" rel="noopener noreferrer"&gt;GitHub Repository&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Since it's for learning purposes, there are some minor issues like lack of newline support, but it can perform minimum viable programming including variable assignment, if statements, and function usage.&lt;/p&gt;

&lt;p&gt;For the runtime environment, if you have cargo*2 installed you can run it locally. If you don't but want to try it, you can run the Dockerfile with the command described in the README.&lt;/p&gt;

&lt;h2&gt;
  
  
  About the Book
&lt;/h2&gt;

&lt;p&gt;This book explains the implementation of an interpreter that evaluates a fictional C-like programming language called Monkey, centered around Go source code. By reading through this book, you can create an interpreter that evaluates programs with the following features:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;C-style syntax&lt;/li&gt;
&lt;li&gt;Variable binding&lt;/li&gt;
&lt;li&gt;Integers and booleans&lt;/li&gt;
&lt;li&gt;Arithmetic expressions&lt;/li&gt;
&lt;li&gt;Built-in functions&lt;/li&gt;
&lt;li&gt;First-class and higher-order functions&lt;/li&gt;
&lt;li&gt;Closures&lt;/li&gt;
&lt;li&gt;String data type&lt;/li&gt;
&lt;li&gt;Array data type&lt;/li&gt;
&lt;li&gt;Hash data type&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;The implementation divides the interpreter into components, implementing them step by step.&lt;br&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%2Fl9p6h8o319cze3jymbqv.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%2Fl9p6h8o319cze3jymbqv.png" alt=" " width="800" height="160"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;In the following sections, I'll explain the implementation of these components using actual source code snippets and diagrams.&lt;/p&gt;
&lt;h2&gt;
  
  
  Lexer (Lexical Analyzer)
&lt;/h2&gt;

&lt;p&gt;The first thing we develop is the lexer. Lexical analysis means converting source code into a format that's easier to interpret - splitting the program into words and assigning a type to each word.&lt;/p&gt;

&lt;p&gt;For example, if we have a program like &lt;code&gt;let x = 5;&lt;/code&gt;, this program is split into words with types: &lt;code&gt;let&lt;/code&gt;, &lt;code&gt;x&lt;/code&gt;, &lt;code&gt;=&lt;/code&gt;, &lt;code&gt;5&lt;/code&gt;, &lt;code&gt;;&lt;/code&gt;, and passed to the parser. These words are called tokens.&lt;/p&gt;

&lt;p&gt;In the implementation, we define the types of words that Monkey outputs as tokens, and the lexer analyzes them through pattern matching.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight rust"&gt;&lt;code&gt;&lt;span class="k"&gt;pub&lt;/span&gt; &lt;span class="k"&gt;enum&lt;/span&gt; &lt;span class="n"&gt;TokenKind&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="n"&gt;ILLEGAL&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;     &lt;span class="c1"&gt;// ILLEGAL&lt;/span&gt;
    &lt;span class="n"&gt;EOF&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;         &lt;span class="c1"&gt;// EOF&lt;/span&gt;

    &lt;span class="c1"&gt;// identifier and literal&lt;/span&gt;
    &lt;span class="n"&gt;IDENT&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;       &lt;span class="c1"&gt;// IDENT&lt;/span&gt;
    &lt;span class="n"&gt;INT&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;         &lt;span class="c1"&gt;// 123...&lt;/span&gt;

    &lt;span class="c1"&gt;// operator&lt;/span&gt;
    &lt;span class="n"&gt;ASSIGN&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;      &lt;span class="c1"&gt;// =&lt;/span&gt;
    &lt;span class="n"&gt;PLUS&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;        &lt;span class="c1"&gt;// +&lt;/span&gt;

    &lt;span class="n"&gt;LPAREN&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;      &lt;span class="c1"&gt;// (&lt;/span&gt;
    &lt;span class="n"&gt;RPAREN&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;      &lt;span class="c1"&gt;// )&lt;/span&gt;

    &lt;span class="c1"&gt;// keyword&lt;/span&gt;
    &lt;span class="n"&gt;FUNCTION&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;    &lt;span class="c1"&gt;// FUNCTION&lt;/span&gt;
    &lt;span class="n"&gt;LET&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;         &lt;span class="c1"&gt;// LET&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="k"&gt;pub&lt;/span&gt; &lt;span class="k"&gt;struct&lt;/span&gt; &lt;span class="n"&gt;Token&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;pub&lt;/span&gt; &lt;span class="n"&gt;token_type&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;TokenKind&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="k"&gt;pub&lt;/span&gt; &lt;span class="n"&gt;literal&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;String&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="k"&gt;pub&lt;/span&gt; &lt;span class="k"&gt;struct&lt;/span&gt; &lt;span class="n"&gt;Lexer&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="n"&gt;input&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;        &lt;span class="o"&gt;&amp;amp;&lt;/span&gt;&lt;span class="nv"&gt;'a&lt;/span&gt; &lt;span class="nb"&gt;str&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="n"&gt;position&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;     &lt;span class="nb"&gt;usize&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="c1"&gt;// current input position&lt;/span&gt;
    &lt;span class="n"&gt;read_position&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;usize&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="c1"&gt;// next input position&lt;/span&gt;
    &lt;span class="n"&gt;ch&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;           &lt;span class="nb"&gt;u8&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="c1"&gt;// a letter which is currently read&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="k"&gt;impl&lt;/span&gt; &lt;span class="n"&gt;Lexer&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;pub&lt;/span&gt; &lt;span class="k"&gt;fn&lt;/span&gt; &lt;span class="nf"&gt;new&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;input&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;&lt;/span&gt;&lt;span class="nv"&gt;'a&lt;/span&gt; &lt;span class="nb"&gt;str&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;-&amp;gt;&lt;/span&gt; &lt;span class="k"&gt;Self&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="k"&gt;let&lt;/span&gt; &lt;span class="k"&gt;mut&lt;/span&gt; &lt;span class="n"&gt;l&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;Lexer&lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;
            &lt;span class="n"&gt;input&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
            &lt;span class="n"&gt;position&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
            &lt;span class="n"&gt;read_position&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
            &lt;span class="n"&gt;ch&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;
        &lt;span class="p"&gt;};&lt;/span&gt;
        &lt;span class="n"&gt;l&lt;/span&gt;&lt;span class="nf"&gt;.read_char&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;l&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;

    &lt;span class="k"&gt;pub&lt;/span&gt; &lt;span class="k"&gt;fn&lt;/span&gt; &lt;span class="nf"&gt;new_token&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;token_type&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;TokenKind&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;ch&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;u8&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;-&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;Token&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="n"&gt;Token&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
            &lt;span class="n"&gt;token_type&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
            &lt;span class="n"&gt;literal&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nn"&gt;String&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="nf"&gt;from_utf8&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nd"&gt;vec!&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;ch&lt;/span&gt;&lt;span class="p"&gt;])&lt;/span&gt;&lt;span class="nf"&gt;.unwrap&lt;/span&gt;&lt;span class="p"&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;pub&lt;/span&gt; &lt;span class="k"&gt;fn&lt;/span&gt; &lt;span class="nf"&gt;next_token&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="o"&gt;&amp;amp;&lt;/span&gt;&lt;span class="k"&gt;mut&lt;/span&gt; &lt;span class="k"&gt;self&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;-&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;Token&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; 
        &lt;span class="c1"&gt;// Skip spaces as they have no meaning&lt;/span&gt;
        &lt;span class="k"&gt;self&lt;/span&gt;&lt;span class="nf"&gt;.skip_whitespace&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
        &lt;span class="k"&gt;let&lt;/span&gt; &lt;span class="n"&gt;token&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
        &lt;span class="k"&gt;match&lt;/span&gt; &lt;span class="k"&gt;self&lt;/span&gt;&lt;span class="py"&gt;.ch&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="c1"&gt;// Pattern match source code to define tokens&lt;/span&gt;
            &lt;span class="sc"&gt;b'*'&lt;/span&gt; &lt;span class="k"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
                &lt;span class="n"&gt;token&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;Self&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="nf"&gt;new_token&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nn"&gt;TokenKind&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="n"&gt;ASTERISK&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="k"&gt;self&lt;/span&gt;&lt;span class="py"&gt;.ch&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
            &lt;span class="p"&gt;}&lt;/span&gt;
            &lt;span class="sc"&gt;b'/'&lt;/span&gt; &lt;span class="k"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
                &lt;span class="n"&gt;token&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;Self&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="nf"&gt;new_token&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nn"&gt;TokenKind&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="n"&gt;SLASH&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="k"&gt;self&lt;/span&gt;&lt;span class="py"&gt;.ch&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
            &lt;span class="p"&gt;}&lt;/span&gt;
            &lt;span class="sc"&gt;b'&amp;lt;'&lt;/span&gt; &lt;span class="k"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
                &lt;span class="n"&gt;token&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;Self&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="nf"&gt;new_token&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nn"&gt;TokenKind&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="n"&gt;LT&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="k"&gt;self&lt;/span&gt;&lt;span class="py"&gt;.ch&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
            &lt;span class="p"&gt;}&lt;/span&gt;
            &lt;span class="sc"&gt;b'&amp;gt;'&lt;/span&gt; &lt;span class="k"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
                &lt;span class="n"&gt;token&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;Self&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="nf"&gt;new_token&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nn"&gt;TokenKind&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="n"&gt;GT&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="k"&gt;self&lt;/span&gt;&lt;span class="py"&gt;.ch&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
            &lt;span class="p"&gt;}&lt;/span&gt;
            &lt;span class="c1"&gt;// ... more patterns&lt;/span&gt;
        &lt;span class="p"&gt;}&lt;/span&gt;
        &lt;span class="k"&gt;self&lt;/span&gt;&lt;span class="nf"&gt;.read_char&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;token&lt;/span&gt;&lt;span class="p"&gt;;&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;The &lt;code&gt;new()&lt;/code&gt; method receives the source code, and the &lt;code&gt;next_token()&lt;/code&gt; method pattern matches words and returns token structures. Since Monkey doesn't assign meaning to spaces, we skip them in this method.&lt;/p&gt;

&lt;p&gt;The result for &lt;code&gt;let five = 5;&lt;/code&gt; looks like this:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Token { token_type: LET, literal: "let" }
Token { token_type: IDENT, literal: "five" }
Token { token_type: ASSIGN, literal: "=" }
Token { token_type: INT, literal: "5" }
Token { token_type: SEMICOLON, literal: ";" }
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Parser (Syntax Analyzer)
&lt;/h2&gt;

&lt;p&gt;Next, we develop the parser. By implementing this parser, we can correctly calculate expressions like &lt;code&gt;1 + 2 * 3&lt;/code&gt; with the proper precedence.&lt;/p&gt;

&lt;p&gt;In this parser implementation, we build a tree structure to simply represent the program's precedence. Taking &lt;code&gt;1 + 2 * 3&lt;/code&gt; as an example, we get the following tree:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;    +
   / \
  1   *
     / \
    2   3
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;For more complex calculations, we nest the tree deeper. For example, &lt;code&gt;4 + 5 * (6 - 7) + 8 * 9&lt;/code&gt; becomes:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;        +
       / \
      +   *
     / \ / \
    4  * 8  9
      / \
     5   -
        / \
       6   7
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This tree is called an &lt;strong&gt;Abstract Syntax Tree (AST)&lt;/strong&gt;, and we calculate it from the deepest left to right. It's called "abstract" because the tree is represented with only the minimum necessary elements. For example, parentheses are necessary for calculation but are redundant for building the tree, so they don't appear in the AST.&lt;/p&gt;

&lt;p&gt;I used arithmetic operations as an example to explain AST, but general programs are also interpreted by creating this AST.&lt;/p&gt;

&lt;p&gt;In the implementation, we parse the program with Program as the root, decomposing it into Statements and Expressions. A &lt;strong&gt;statement&lt;/strong&gt; is a language element with no return value that's complete by itself, while an &lt;strong&gt;expression&lt;/strong&gt; has a return value and becomes part of a statement.&lt;/p&gt;

&lt;p&gt;For example, when parsing &lt;code&gt;let x = 1 * (2 + 3);&lt;/code&gt;, we get an AST like the following. In this case, the statement is &lt;code&gt;let x = 1 * (2 + 3);&lt;/code&gt;, and the expression is &lt;code&gt;1 * (2 + 3)&lt;/code&gt;. &lt;code&gt;1 * (2 + 3)&lt;/code&gt; is part of the let statement, and when executed, returns 5. Understanding this might be a bit difficult, but writing expressions as ASTs on paper makes it easier to understand.&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%2Ft6pvryiks8bnbdg1uwih.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%2Ft6pvryiks8bnbdg1uwih.png" alt=" " width="800" height="711"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;In the implementation, we create Statement and Expression enums, exhaustively define patterns, and the parser analyzes them through pattern matching.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight rust"&gt;&lt;code&gt;&lt;span class="k"&gt;pub&lt;/span&gt; &lt;span class="k"&gt;struct&lt;/span&gt; &lt;span class="n"&gt;Program&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;pub&lt;/span&gt; &lt;span class="n"&gt;statements&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;Vec&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="k"&gt;pub&lt;/span&gt; &lt;span class="k"&gt;enum&lt;/span&gt; &lt;span class="n"&gt;Statement&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="n"&gt;LetStatement&lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="n"&gt;identifier&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;Expression&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
                 &lt;span class="n"&gt;value&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;Expression&lt;/span&gt;&lt;span class="p"&gt;},&lt;/span&gt;
    &lt;span class="nf"&gt;Return&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;Expression&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;
    &lt;span class="nf"&gt;ExpressionStatement&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;Expression&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;
    &lt;span class="nf"&gt;Block&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nb"&gt;Vec&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="k"&gt;pub&lt;/span&gt; &lt;span class="k"&gt;enum&lt;/span&gt; &lt;span class="n"&gt;Expression&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="nf"&gt;Identifier&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nb"&gt;String&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;
    &lt;span class="nf"&gt;String&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nb"&gt;String&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;
    &lt;span class="nf"&gt;Integer&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nb"&gt;i32&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;
    &lt;span class="c1"&gt;// ... more variants&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="k"&gt;pub&lt;/span&gt; &lt;span class="k"&gt;struct&lt;/span&gt; &lt;span class="n"&gt;Parser&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="n"&gt;lexer&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nn"&gt;lexer&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="n"&gt;Lexer&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="n"&gt;current_token&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;Token&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="n"&gt;next_token&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;Token&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="k"&gt;impl&lt;/span&gt; &lt;span class="n"&gt;Parser&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;pub&lt;/span&gt; &lt;span class="k"&gt;fn&lt;/span&gt; &lt;span class="nf"&gt;new&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;l&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nn"&gt;lexer&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="n"&gt;Lexer&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;-&amp;gt;&lt;/span&gt; &lt;span class="k"&gt;Self&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="k"&gt;let&lt;/span&gt; &lt;span class="k"&gt;mut&lt;/span&gt; &lt;span class="n"&gt;p&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;Parser&lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;
            &lt;span class="n"&gt;lexer&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;l&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
            &lt;span class="n"&gt;current_token&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;Token&lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="n"&gt;token_type&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nn"&gt;TokenKind&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="n"&gt;DEFAULT&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;literal&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="s"&gt;"default"&lt;/span&gt;&lt;span class="nf"&gt;.to_string&lt;/span&gt;&lt;span class="p"&gt;()},&lt;/span&gt;
            &lt;span class="n"&gt;next_token&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;Token&lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="n"&gt;token_type&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nn"&gt;TokenKind&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="n"&gt;DEFAULT&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;literal&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="s"&gt;"default"&lt;/span&gt;&lt;span class="nf"&gt;.to_string&lt;/span&gt;&lt;span class="p"&gt;()},&lt;/span&gt;
        &lt;span class="p"&gt;};&lt;/span&gt;
        &lt;span class="n"&gt;p&lt;/span&gt;&lt;span class="nf"&gt;.next_token&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
        &lt;span class="n"&gt;p&lt;/span&gt;&lt;span class="nf"&gt;.next_token&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
        &lt;span class="n"&gt;p&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;

    &lt;span class="k"&gt;pub&lt;/span&gt; &lt;span class="k"&gt;fn&lt;/span&gt; &lt;span class="nf"&gt;parse_program&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="o"&gt;&amp;amp;&lt;/span&gt;&lt;span class="k"&gt;mut&lt;/span&gt; &lt;span class="k"&gt;self&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;-&amp;gt;&lt;/span&gt; &lt;span class="nb"&gt;Result&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="k"&gt;let&lt;/span&gt; &lt;span class="k"&gt;mut&lt;/span&gt; &lt;span class="n"&gt;statements&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;Vec&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nd"&gt;vec!&lt;/span&gt;&lt;span class="p"&gt;[];&lt;/span&gt;

        &lt;span class="c1"&gt;// read token until it reaches the end of the sentence&lt;/span&gt;
        &lt;span class="k"&gt;while&lt;/span&gt; &lt;span class="o"&gt;!&lt;/span&gt;&lt;span class="k"&gt;self&lt;/span&gt;&lt;span class="nf"&gt;.is_current_token&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nn"&gt;TokenKind&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="n"&gt;EOF&lt;/span&gt;&lt;span class="p"&gt;){&lt;/span&gt;
            &lt;span class="k"&gt;let&lt;/span&gt; &lt;span class="n"&gt;statement&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;self&lt;/span&gt;&lt;span class="nf"&gt;.parse_statement&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;&lt;span class="o"&gt;?&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
            &lt;span class="n"&gt;statements&lt;/span&gt;&lt;span class="nf"&gt;.push&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;statement&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
            &lt;span class="k"&gt;self&lt;/span&gt;&lt;span class="nf"&gt;.next_token&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
        &lt;span class="p"&gt;};&lt;/span&gt;
        &lt;span class="nf"&gt;Ok&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;Program&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="n"&gt;statements&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;statements&lt;/span&gt;&lt;span class="p"&gt;})&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;

    &lt;span class="k"&gt;fn&lt;/span&gt; &lt;span class="nf"&gt;parse_statement&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="o"&gt;&amp;amp;&lt;/span&gt;&lt;span class="k"&gt;mut&lt;/span&gt; &lt;span class="k"&gt;self&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;-&amp;gt;&lt;/span&gt; &lt;span class="nb"&gt;Result&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="k"&gt;match&lt;/span&gt; &lt;span class="k"&gt;self&lt;/span&gt;&lt;span class="py"&gt;.current_token.token_type&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
            &lt;span class="nn"&gt;TokenKind&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="n"&gt;LET&lt;/span&gt; &lt;span class="k"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
                &lt;span class="nf"&gt;Ok&lt;/span&gt;&lt;span class="p"&gt;((&lt;/span&gt;&lt;span class="k"&gt;self&lt;/span&gt;&lt;span class="nf"&gt;.parse_let_statement&lt;/span&gt;&lt;span class="p"&gt;()&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="nn"&gt;TokenKind&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="n"&gt;RETURN&lt;/span&gt; &lt;span class="k"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
                &lt;span class="nf"&gt;Ok&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;self&lt;/span&gt;&lt;span class="nf"&gt;.parse_return_statement&lt;/span&gt;&lt;span class="p"&gt;()&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;_&lt;/span&gt; &lt;span class="k"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
                &lt;span class="nf"&gt;Ok&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;self&lt;/span&gt;&lt;span class="nf"&gt;.parse_expression_statement&lt;/span&gt;&lt;span class="p"&gt;()&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="p"&gt;}&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;

    &lt;span class="k"&gt;fn&lt;/span&gt; &lt;span class="nf"&gt;parse_expression&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="o"&gt;&amp;amp;&lt;/span&gt;&lt;span class="k"&gt;mut&lt;/span&gt; &lt;span class="k"&gt;self&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;precedence&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;Precedence&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;-&amp;gt;&lt;/span&gt; &lt;span class="nb"&gt;Result&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="k"&gt;let&lt;/span&gt; &lt;span class="k"&gt;mut&lt;/span&gt; &lt;span class="n"&gt;exp&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;match&lt;/span&gt; &lt;span class="k"&gt;self&lt;/span&gt;&lt;span class="py"&gt;.current_token.token_type&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
            &lt;span class="nn"&gt;TokenKind&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="n"&gt;IDENT&lt;/span&gt; &lt;span class="k"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="nn"&gt;Expression&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="nf"&gt;Identifier&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;self&lt;/span&gt;&lt;span class="nf"&gt;.parse_identifier&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;&lt;span class="o"&gt;?&lt;/span&gt;&lt;span class="p"&gt;)},&lt;/span&gt;
            &lt;span class="nn"&gt;TokenKind&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="n"&gt;STRING&lt;/span&gt; &lt;span class="k"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="nn"&gt;Expression&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="nf"&gt;String&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;self&lt;/span&gt;&lt;span class="nf"&gt;.parse_string&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;&lt;span class="o"&gt;?&lt;/span&gt;&lt;span class="p"&gt;)},&lt;/span&gt;
            &lt;span class="nn"&gt;TokenKind&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="n"&gt;INT&lt;/span&gt; &lt;span class="k"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="nn"&gt;Expression&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="nf"&gt;Integer&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;self&lt;/span&gt;&lt;span class="nf"&gt;.parse_integer&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;&lt;span class="o"&gt;?&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;
            &lt;span class="nn"&gt;TokenKind&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="n"&gt;TRUE&lt;/span&gt; &lt;span class="k"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="nn"&gt;Expression&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="nf"&gt;Bool&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;true&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;
            &lt;span class="n"&gt;_&lt;/span&gt; &lt;span class="k"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nf"&gt;Err&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nn"&gt;Errors&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="nf"&gt;TokenInvalid&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;self&lt;/span&gt;&lt;span class="py"&gt;.current_token&lt;/span&gt;&lt;span class="nf"&gt;.clone&lt;/span&gt;&lt;span class="p"&gt;()))&lt;/span&gt;
        &lt;span class="p"&gt;};&lt;/span&gt;
        &lt;span class="c1"&gt;// ... more logic&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;The &lt;code&gt;new()&lt;/code&gt; method receives the lexically analyzed result, and the &lt;code&gt;parse_program()&lt;/code&gt; method recursively constructs the AST.&lt;/p&gt;

&lt;p&gt;The result for &lt;code&gt;let x = 1 * (2 + 3)&lt;/code&gt; looks like this:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight rust"&gt;&lt;code&gt;&lt;span class="n"&gt;LetStatement&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; 
    &lt;span class="n"&gt;identifier&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nf"&gt;Identifier&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"x"&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;
    &lt;span class="n"&gt;value&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;InfixExpression&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; 
        &lt;span class="n"&gt;left_expression&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nf"&gt;Integer&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="n"&gt;operator&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="s"&gt;"*"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="n"&gt;right_expression&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;InfixExpression&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
            &lt;span class="n"&gt;left_expression&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nf"&gt;Integer&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="n"&gt;operator&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="s"&gt;"+"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
            &lt;span class="n"&gt;right_expression&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nf"&gt;Integer&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;3&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
        &lt;span class="p"&gt;}&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;h2&gt;
  
  
  Evaluation
&lt;/h2&gt;

&lt;p&gt;Finally, we develop the evaluation. In this book, we sequentially execute the AST constructed by the parser in order using the host language (Rust). This method is called a &lt;strong&gt;Tree-walking interpreter&lt;/strong&gt;. As a side note, this method was actually used in Ruby 1.8 and earlier. (From 1.9 onwards, to improve performance, they changed to a method that compiles the AST to bytecode and evaluates it on a virtual machine.)&lt;/p&gt;

&lt;p&gt;By the way, from around this point I started to feel comfortable with Rust syntax and implementation became easier.&lt;/p&gt;

&lt;p&gt;In the implementation, we define a type called Object to represent it in the host language, converting AST values to Objects.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight rust"&gt;&lt;code&gt;&lt;span class="k"&gt;pub&lt;/span&gt; &lt;span class="k"&gt;enum&lt;/span&gt; &lt;span class="n"&gt;Object&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="nf"&gt;Identifier&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nb"&gt;String&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;
    &lt;span class="nf"&gt;String&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nb"&gt;String&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;
    &lt;span class="nf"&gt;Integer&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nb"&gt;i32&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;
    &lt;span class="nf"&gt;Boolean&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nb"&gt;bool&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;
    &lt;span class="nf"&gt;Return&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nb"&gt;Box&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;
    &lt;span class="nf"&gt;Let&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nb"&gt;Box&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;
    &lt;span class="nf"&gt;Array&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nb"&gt;Vec&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;
    &lt;span class="c1"&gt;// ... more variants&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="k"&gt;pub&lt;/span&gt; &lt;span class="k"&gt;fn&lt;/span&gt; &lt;span class="nf"&gt;evaluate&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="o"&gt;&amp;amp;&lt;/span&gt;&lt;span class="k"&gt;mut&lt;/span&gt; &lt;span class="k"&gt;self&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;program&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;&lt;/span&gt;&lt;span class="nn"&gt;ast&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="n"&gt;Program&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;-&amp;gt;&lt;/span&gt; &lt;span class="nb"&gt;Result&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;let&lt;/span&gt; &lt;span class="k"&gt;mut&lt;/span&gt; &lt;span class="n"&gt;result&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nn"&gt;Object&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="nb"&gt;Default&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="c1"&gt;// evaluate sentence per semicolon&lt;/span&gt;
    &lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="n"&gt;statement&lt;/span&gt; &lt;span class="k"&gt;in&lt;/span&gt; &lt;span class="n"&gt;program&lt;/span&gt;&lt;span class="py"&gt;.statements&lt;/span&gt;&lt;span class="nf"&gt;.iter&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="n"&gt;result&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;self&lt;/span&gt;&lt;span class="nf"&gt;.evaluate_statement&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;statement&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="o"&gt;?&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
        &lt;span class="c1"&gt;// if statement contains 'return', process should be broken and return value&lt;/span&gt;
        &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="k"&gt;let&lt;/span&gt; &lt;span class="nn"&gt;Object&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="nf"&gt;Return&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;value&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;result&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
            &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nf"&gt;Ok&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="o"&gt;*&lt;/span&gt;&lt;span class="n"&gt;value&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
        &lt;span class="p"&gt;}&lt;/span&gt;
        &lt;span class="c1"&gt;// if the result of evaluation, process should be broken&lt;/span&gt;
        &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;result&lt;/span&gt; &lt;span class="o"&gt;==&lt;/span&gt; &lt;span class="nn"&gt;Object&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="nf"&gt;Error&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nn"&gt;Errors&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="n"&gt;InvalidInfix&lt;/span&gt;&lt;span class="p"&gt;){&lt;/span&gt;
            &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nf"&gt;Ok&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;result&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
        &lt;span class="p"&gt;}&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;
    &lt;span class="nf"&gt;Ok&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;result&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="k"&gt;fn&lt;/span&gt; &lt;span class="nf"&gt;evaluate_statement&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="o"&gt;&amp;amp;&lt;/span&gt;&lt;span class="k"&gt;mut&lt;/span&gt; &lt;span class="k"&gt;self&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;statement&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;&lt;/span&gt;&lt;span class="nn"&gt;ast&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="n"&gt;Statement&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;-&amp;gt;&lt;/span&gt; &lt;span class="nb"&gt;Result&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;match&lt;/span&gt; &lt;span class="n"&gt;statement&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="nn"&gt;ast&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="nn"&gt;Statement&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="nf"&gt;ExpressionStatement&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;expression&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="k"&gt;self&lt;/span&gt;&lt;span class="nf"&gt;.evaluate_expression&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;expression&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;
        &lt;span class="nn"&gt;ast&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="nn"&gt;Statement&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="nf"&gt;Block&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;stmt&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="k"&gt;self&lt;/span&gt;&lt;span class="nf"&gt;.evaluate_block_statements&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;stmt&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;
        &lt;span class="nn"&gt;ast&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="nn"&gt;Statement&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="nf"&gt;Return&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;expression&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
            &lt;span class="k"&gt;let&lt;/span&gt; &lt;span class="n"&gt;return_expression&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;self&lt;/span&gt;&lt;span class="nf"&gt;.evaluate_expression&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;expression&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="o"&gt;?&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
            &lt;span class="nf"&gt;Ok&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nn"&gt;Object&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="nf"&gt;Return&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nn"&gt;Box&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="nf"&gt;new&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;return_expression&lt;/span&gt;&lt;span class="p"&gt;)))&lt;/span&gt;
        &lt;span class="p"&gt;},&lt;/span&gt;
        &lt;span class="nn"&gt;ast&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="nn"&gt;Statement&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="n"&gt;LetStatement&lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="n"&gt;identifier&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;value&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
            &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="k"&gt;let&lt;/span&gt; &lt;span class="nn"&gt;Expression&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="nf"&gt;Identifier&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;identifier&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;identifier&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
                &lt;span class="c1"&gt;// if expression is identifier, evaluate value and append identifier as variable&lt;/span&gt;
                &lt;span class="k"&gt;let&lt;/span&gt; &lt;span class="n"&gt;evaluated_value&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;self&lt;/span&gt;&lt;span class="nf"&gt;.evaluate_expression&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="o"&gt;&amp;amp;&lt;/span&gt;&lt;span class="n"&gt;value&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="o"&gt;?&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
                &lt;span class="k"&gt;let&lt;/span&gt; &lt;span class="n"&gt;value&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;self&lt;/span&gt;&lt;span class="nf"&gt;.set&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;identifier&lt;/span&gt;&lt;span class="nf"&gt;.to_owned&lt;/span&gt;&lt;span class="p"&gt;(),&lt;/span&gt; &lt;span class="n"&gt;evaluated_value&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
                &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nf"&gt;Ok&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;value&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
            &lt;span class="p"&gt;}&lt;/span&gt;
            &lt;span class="nf"&gt;Ok&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nn"&gt;Object&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="n"&gt;Null&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
        &lt;span class="p"&gt;},&lt;/span&gt;
        &lt;span class="n"&gt;_&lt;/span&gt; &lt;span class="k"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="nf"&gt;Err&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nn"&gt;Errors&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="n"&gt;NodeError&lt;/span&gt;&lt;span class="p"&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;fn&lt;/span&gt; &lt;span class="nf"&gt;evaluate_expression&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="o"&gt;&amp;amp;&lt;/span&gt;&lt;span class="k"&gt;mut&lt;/span&gt; &lt;span class="k"&gt;self&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;expression&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;&lt;/span&gt;&lt;span class="nn"&gt;ast&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="n"&gt;Expression&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;-&amp;gt;&lt;/span&gt; &lt;span class="nb"&gt;Result&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;match&lt;/span&gt; &lt;span class="n"&gt;expression&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="nn"&gt;ast&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="nn"&gt;Expression&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="nf"&gt;String&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;value&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="nf"&gt;Ok&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nn"&gt;Object&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="nf"&gt;String&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;value&lt;/span&gt;&lt;span class="nf"&gt;.to_owned&lt;/span&gt;&lt;span class="p"&gt;())),&lt;/span&gt;
        &lt;span class="nn"&gt;ast&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="nn"&gt;Expression&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="nf"&gt;Integer&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;value&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="nf"&gt;Ok&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nn"&gt;Object&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="nf"&gt;Integer&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="o"&gt;*&lt;/span&gt;&lt;span class="n"&gt;value&lt;/span&gt;&lt;span class="p"&gt;)),&lt;/span&gt;
        &lt;span class="nn"&gt;ast&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="nn"&gt;Expression&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="nf"&gt;Bool&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nb"&gt;bool&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="nf"&gt;Ok&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nn"&gt;Object&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="nf"&gt;Boolean&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="o"&gt;*&lt;/span&gt;&lt;span class="nb"&gt;bool&lt;/span&gt;&lt;span class="p"&gt;)),&lt;/span&gt;
        &lt;span class="nn"&gt;ast&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="nn"&gt;Expression&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="nf"&gt;Array&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;value&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
            &lt;span class="k"&gt;let&lt;/span&gt; &lt;span class="n"&gt;array&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;self&lt;/span&gt;&lt;span class="nf"&gt;.evaluate_arguments&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;value&lt;/span&gt;&lt;span class="nf"&gt;.to_vec&lt;/span&gt;&lt;span class="p"&gt;())&lt;/span&gt;&lt;span class="o"&gt;?&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
            &lt;span class="nf"&gt;Ok&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nn"&gt;Object&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="nf"&gt;Array&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;array&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt;
        &lt;span class="p"&gt;}&lt;/span&gt;
        &lt;span class="c1"&gt;// ... more patterns&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;The &lt;code&gt;evaluate_expression()&lt;/code&gt; function continuously converts received expressions to Objects.&lt;/p&gt;

&lt;p&gt;When evaluating &lt;code&gt;let x = 5; x;&lt;/code&gt;, the result &lt;code&gt;Integer(5)&lt;/code&gt; is returned. We want the terminal to display &lt;code&gt;5&lt;/code&gt;, so I decided to use Rust's Formatter feature, which nicely formats and displays values.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight rust"&gt;&lt;code&gt;&lt;span class="k"&gt;pub&lt;/span&gt; &lt;span class="k"&gt;enum&lt;/span&gt; &lt;span class="n"&gt;Object&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="nf"&gt;Integer&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nb"&gt;i32&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;
    &lt;span class="c1"&gt;// ... more variants&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="k"&gt;impl&lt;/span&gt; &lt;span class="nn"&gt;fmt&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="n"&gt;Display&lt;/span&gt; &lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="n"&gt;Object&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;fn&lt;/span&gt; &lt;span class="nf"&gt;fmt&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="o"&gt;&amp;amp;&lt;/span&gt;&lt;span class="k"&gt;self&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;f&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;&lt;/span&gt;&lt;span class="k"&gt;mut&lt;/span&gt; &lt;span class="nn"&gt;fmt&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="n"&gt;Formatter&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;-&amp;gt;&lt;/span&gt; &lt;span class="nn"&gt;fmt&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="nb"&gt;Result&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="k"&gt;match&lt;/span&gt; &lt;span class="k"&gt;self&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
            &lt;span class="c1"&gt;// Only the internal value of Integer is displayed&lt;/span&gt;
            &lt;span class="nn"&gt;Object&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="nf"&gt;Integer&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;value&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="nd"&gt;write!&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;f&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s"&gt;"{}"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;value&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;
            &lt;span class="c1"&gt;// ... more patterns&lt;/span&gt;
        &lt;span class="p"&gt;}&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;h2&gt;
  
  
  What I Did to Become Proficient in Rust
&lt;/h2&gt;

&lt;p&gt;Rust is generally considered a language with higher learning costs compared to other languages. The main reason is that it's a feature-rich language, but separately, there's also less knowledge available on the web compared to other languages.&lt;/p&gt;

&lt;p&gt;While it's impossible to explain all of Rust's syntax in this blog, I hope to help improve the knowledge gap a bit, so I'll briefly explain the books and documents I referenced in chronological order.&lt;/p&gt;

&lt;h3&gt;
  
  
  Before Implementation
&lt;/h3&gt;

&lt;p&gt;Before starting development, I worked through the &lt;a href="https://doc.rust-lang.org/book/" rel="noopener noreferrer"&gt;official documentation&lt;/a&gt;. Since it's free and very carefully written, I think it was excellent as an initial document to read.&lt;/p&gt;

&lt;p&gt;Knowledge up to about chapter 11 seemed essential, so I read through it all, memorizing syntax while copying the number guessing game program from the early chapters. For later chapters, I only worked on parts that seemed relevant. Therefore, there are areas I barely touched, which I think is a challenge.&lt;/p&gt;

&lt;h3&gt;
  
  
  During Implementation
&lt;/h3&gt;

&lt;p&gt;During implementation, I mainly referenced "Programming Rust" alongside the official documentation. This book is written for beginners so that even those without base knowledge of Rust like C can understand it, making it very accessible.&lt;/p&gt;

&lt;p&gt;Rust has a more difficult impression regarding string handling and type conversion compared to other languages, but perhaps the author sympathized with this feeling, as they devoted many pages to carefully explaining it. It's also attractive that it contains sample code that seems useful in everyday development, such as database access and web API usage, so I can recommend it to those who want to use Rust in production.&lt;/p&gt;

&lt;p&gt;I also searched for GitHub repositories doing similar things in Rust and referred to commit logs. Some might avoid this as if looking at problem set answers, but you can learn about notation and libraries not covered in books, and by following commit logs you can see how others avoided the same pitfalls you encountered, which was very helpful.&lt;/p&gt;

&lt;h2&gt;
  
  
  Things You Should Do When Writing Rust
&lt;/h2&gt;

&lt;h3&gt;
  
  
  Build a development environment using RLS (Rust Language Server)
&lt;/h3&gt;

&lt;p&gt;Rust is a restrictive language due to its type system and unique ownership system. Therefore, you'll encounter many compile errors, and in this development too, I had the feeling that working code emerged from fixing errors (partly because I was inexperienced).&lt;/p&gt;

&lt;p&gt;I usually use VSCode as my editor, and by installing the rls*3 extension, I could check syntax errors and types in the editor, which saved development time. Especially since Rust has type constraints but it's difficult to check types in the program, being able to check types in the editor was very helpful.&lt;br&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%2Ffx1cyq5afj7k5tbaetb3.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%2Ffx1cyq5afj7k5tbaetb3.png" alt=" " width="800" height="115"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Also, when running tests, you can execute them at the function level, which was very convenient when debugging only newly developed functions in pattern matching.&lt;br&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%2F86mow6ds5l9wal5jcd0q.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%2F86mow6ds5l9wal5jcd0q.png" alt=" " width="778" height="240"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Installation reference:&lt;br&gt;
&lt;a href="https://marketplace.visualstudio.com/items?itemName=rust-lang.rust-analyzer" rel="noopener noreferrer"&gt;Rust Analyzer - Visual Studio Marketplace&lt;/a&gt;&lt;/p&gt;

&lt;h3&gt;
  
  
  Use the Result Type for Return Values
&lt;/h3&gt;

&lt;p&gt;As an implementation strategy for the parser and evaluator, we pattern match tokens and execute functions to process them. Therefore, we implement functions step by step for each token and extend the match statement, but for unimplemented tokens, there's a problem that we can't return a return value with the appropriate type because it's not implemented.&lt;/p&gt;

&lt;p&gt;In this case, we use the &lt;strong&gt;Result type&lt;/strong&gt;, which can define return values for successful termination &lt;code&gt;Ok(T)&lt;/code&gt; and abnormal termination &lt;code&gt;Err(E)&lt;/code&gt;. In the code example above, we define the Expression type defined as an enum for successful termination and the Errors type for abnormal termination as return values. Wanting to return errors within functions is common, such as in API implementations, and using the Result type allows high flexibility in implementation.&lt;/p&gt;

&lt;p&gt;I think it becomes easier to handle errors if you define an Errors type in detail in a dedicated &lt;code&gt;errors.rs&lt;/code&gt; file using an enum.&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%2F55wbrg8qoj069hxh4y7j.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%2F55wbrg8qoj069hxh4y7j.png" alt=" " width="800" height="268"&gt;&lt;/a&gt;&lt;/p&gt;

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

&lt;p&gt;I was able to implement an interpreter in about 2,000 lines including test code, and it was excellent study as an introduction to language processing and Rust. (The preface mentioned aiming for something between a technical book and a blog, which seemed accurate.)&lt;/p&gt;

&lt;p&gt;Particularly when implementing the parser logic and successfully parsing complex expressions, I felt I understood why calculation formulas work correctly. As its reputation suggests, Rust is a language with high learning costs, and I had quite a difficult time in the beginning, but learning Rust-specific syntax like the ownership system means learning programming at a lower layer, so as someone who usually develops with scripting languages, I now think it was a good language worth learning.&lt;/p&gt;

&lt;p&gt;I just learned while writing this blog that this book has a sequel called "Writing A Compiler In Go," so to prevent the Rust knowledge I've acquired from fading away, I'm thinking of trying to make a compiler next time.&lt;/p&gt;




&lt;h3&gt;
  
  
  Footnotes
&lt;/h3&gt;

&lt;p&gt;*1: A tool that can build, run, test, and manage packages of Rust code - essential for Rust development. &lt;a href="https://doc.rust-jp.rs/book/second-edition/ch01-03-hello-cargo.html" rel="noopener noreferrer"&gt;Cargo Documentation&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;*3: Abbreviation for Rust Language Server, a backend server that provides various completions for Rust programs in IDEs and editors. &lt;a href="https://github.com/rust-lang/rls" rel="noopener noreferrer"&gt;RLS GitHub&lt;/a&gt;&lt;/p&gt;




</description>
      <category>rust</category>
      <category>go</category>
      <category>computerscience</category>
      <category>webdev</category>
    </item>
    <item>
      <title>BuildingRetrieval-AugmentedGenerationRAGSystemonAmazonBedrock</title>
      <dc:creator>ryo ariyama</dc:creator>
      <pubDate>Mon, 15 Sep 2025 10:02:39 +0000</pubDate>
      <link>https://forem.com/ryo_ariyama_b521d7133c493/buildingretrieval-augmentedgenerationragsystemonamazonbedrock-642</link>
      <guid>https://forem.com/ryo_ariyama_b521d7133c493/buildingretrieval-augmentedgenerationragsystemonamazonbedrock-642</guid>
      <description>&lt;h2&gt;
  
  
  How to Build a RAG System with Amazon Bedrock
&lt;/h2&gt;

&lt;h2&gt;
  
  
  Introduction
&lt;/h2&gt;

&lt;p&gt;I am currently implementing an AI Chatbot with RAG functionality using Amazon Bedrock for a project.&lt;br&gt;
Now that things have settled down, I'd like to document what I've researched as a reference.&lt;br&gt;
Since LLM and RAG functionality cover a wide range, this article provides a conceptual overview.&lt;/p&gt;
&lt;h2&gt;
  
  
  What is RAG Functionality?
&lt;/h2&gt;
&lt;h3&gt;
  
  
  Basic Knowledge
&lt;/h3&gt;

&lt;p&gt;Before diving into the explanation, let me explain RAG.&lt;br&gt;
RAG stands for "Retrieval-Augmented Generation."&lt;br&gt;
RAG is a technique for improving the capabilities of large language models (LLMs), and it works with the following mechanism:&lt;/p&gt;

&lt;p&gt;Retrieval: Search for information related to the user's question from external databases or documents&lt;br&gt;
Augmented: Add the relevant information obtained from the search to the LLM input&lt;br&gt;
Generation: The LLM generates responses using both the original question and the searched information&lt;/p&gt;
&lt;h3&gt;
  
  
  Main Benefits of RAG
&lt;/h3&gt;

&lt;p&gt;When using traditional LLMs, responses are generated within the scope of knowledge from data used during training. In this case, if the information you want to know through dialogue is recent content or if the knowledge and terminology are not common, you may not be able to get correct answers.&lt;br&gt;
On the other hand, by using RAG, you can generate responses that better reflect local context by adding external documents as reference information.&lt;/p&gt;

&lt;p&gt;Here are some practical examples:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Chatbots that search internal company documents to answer questions&lt;/li&gt;
&lt;li&gt;Research support systems that reference the latest academic papers&lt;/li&gt;
&lt;li&gt;Customer support that searches product manuals for information&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;RAG has become an important technology for building AI systems that can better meet user-specific requirements by combining the general knowledge of LLMs with specific contexts and the latest information.&lt;/p&gt;
&lt;h2&gt;
  
  
  How to Build with AWS?
&lt;/h2&gt;
&lt;h3&gt;
  
  
  About Bedrock
&lt;/h3&gt;

&lt;p&gt;AWS has a service called Bedrock for generative AI, which we will use.&lt;br&gt;
Bedrock is a service that enables access to various LLM models like Claude and makes them accessible via API.&lt;br&gt;
Bedrock has various services, but to realize dialogue with LLMs using RAG functionality, you need to use the following services:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Agent: Provides API access to LLM models for dialogue and response generation.&lt;/li&gt;
&lt;li&gt;KnowledgeBase: A service for RAG that connects to data sources and vector stores. Performs data vectorization.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Using Agent, you can have general LLM conversations via API, but by adding KnowledgeBase, you can have conversations using RAG functionality.&lt;/p&gt;
&lt;h3&gt;
  
  
  How KnowledgeBase Works
&lt;/h3&gt;

&lt;p&gt;Bedrock KnowledgeBase is an orchestration tool to realize RAG functionality.&lt;br&gt;
It provides functions such as importing training data and data retrieval to improve LLM dialogue performance.&lt;br&gt;
KnowledgeBase connects to various data sources, vectorizes documents, and registers them in vector databases.&lt;br&gt;
Data sources include S3, SharePoint, Confluence, Salesforce, etc., and vector databases can use OpenSearch, Pinecone, Amazon Aurora, etc.&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%2Fzvikg8lqeaamn791kok3.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%2Fzvikg8lqeaamn791kok3.png" alt=" " width="654" height="561"&gt;&lt;/a&gt;&lt;/p&gt;
&lt;h3&gt;
  
  
  Vectorization Process
&lt;/h3&gt;

&lt;p&gt;In RAG systems, documents stored as context are converted to numerical vectors and stored.&lt;br&gt;
Vectorization is an important process that converts text to numerical vectors in a format that computers can understand.&lt;br&gt;
The following steps are involved in vectorization:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Chunk splitting&lt;/li&gt;
&lt;li&gt;Embedding model selection&lt;/li&gt;
&lt;li&gt;Vector conversion&lt;/li&gt;
&lt;/ol&gt;
&lt;h4&gt;
  
  
  1. Chunk Splitting
&lt;/h4&gt;

&lt;p&gt;First, long documents are divided into appropriate sizes according to the following policy:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Size: Usually around 500-1000 characters&lt;/li&gt;
&lt;li&gt;Overlap: Provide 50-100 character overlap between chunks&lt;/li&gt;
&lt;li&gt;Boundaries: Split at paragraph or sentence breaks, not in the middle of sentences
&lt;/li&gt;
&lt;/ul&gt;
&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Example:
Original document: "Amazon Bedrock is a generative AI service. It provides access to various models..."
↓
Chunk1: "Amazon Bedrock is a generative AI service. It provides access to various models and can be used via API."
Chunk2: "It can be used via API. Main features include Agent functionality and KnowledgeBase functionality."
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

&lt;h4&gt;
  
  
  2. Embedding Model Selection
&lt;/h4&gt;

&lt;p&gt;Next, select an embedding model. Use this model to vectorize the chunked text.&lt;br&gt;
Bedrock uses embedding models such as Amazon Titan Embeddings:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Amazon Titan Embeddings: General-purpose text embedding&lt;/li&gt;
&lt;li&gt;Cohere Embed: Multi-language support&lt;/li&gt;
&lt;/ul&gt;
&lt;h4&gt;
  
  
  3. Vector Conversion
&lt;/h4&gt;

&lt;p&gt;Convert each chunk to vectors and store them in the vector store. When stored, vectors are typically saved as arrays of floating-point numbers.&lt;br&gt;
During dialogue creation, similarity calculations are performed from these vectors to search for information needed for dialogue.&lt;br&gt;
During search, the user's question is also vectorized using the same method, and cosine similarity or Euclidean distance is calculated to search for documents with high similarity.&lt;/p&gt;
&lt;h3&gt;
  
  
  Sample Implementation
&lt;/h3&gt;

&lt;p&gt;Now let's write a sample implementation for Bedrock KnowledgeBase.&lt;br&gt;
The implementation language is Python, the vector store is OpenSearch managed cluster, and the data source is S3.&lt;br&gt;
The implementation flow is as follows:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Create index in OpenSearch&lt;/li&gt;
&lt;li&gt;Create VectorStore&lt;/li&gt;
&lt;li&gt;Create data source&lt;/li&gt;
&lt;li&gt;Ingest vector data into VectorStore&lt;/li&gt;
&lt;li&gt;Execute agent combined with KnowledgeBase&lt;/li&gt;
&lt;/ol&gt;
&lt;h4&gt;
  
  
  Create Index in OpenSearch
&lt;/h4&gt;

&lt;p&gt;First, as preparation, let's create an index in OpenSearch.&lt;br&gt;
There are various configuration items, but there is a &lt;a href="https://docs.aws.amazon.com/ja_jp/bedrock/latest/userguide/knowledge-base-setup.html" rel="noopener noreferrer"&gt;sample in the official documentation&lt;/a&gt;, so it's good to customize based on this.&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="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"settings"&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;"index"&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;"knn"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kc"&gt;true&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;span class="nl"&gt;"mappings"&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;"properties"&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;"&amp;lt;vector-name&amp;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;span class="nl"&gt;"type"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"knn_vector"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
                &lt;/span&gt;&lt;span class="nl"&gt;"dimension"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="err"&gt;&amp;lt;embedding-dimension&amp;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;"data_type"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"binary"&lt;/span&gt;&lt;span class="p"&gt;,&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;Only&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="err"&gt;needed&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="err"&gt;for&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="err"&gt;binary&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="err"&gt;embeddings&lt;/span&gt;&lt;span class="w"&gt;
                &lt;/span&gt;&lt;span class="nl"&gt;"space_type"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"l2"&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="s2"&gt;"hamming"&lt;/span&gt;&lt;span class="p"&gt;,&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;Use&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="err"&gt;l&lt;/span&gt;&lt;span class="mi"&gt;2&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="err"&gt;for&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="err"&gt;float&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="err"&gt;embeddings&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="err"&gt;and&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="err"&gt;hamming&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="err"&gt;for&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="err"&gt;binary&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="err"&gt;embeddings&lt;/span&gt;&lt;span class="w"&gt;
                &lt;/span&gt;&lt;span class="nl"&gt;"method"&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;"name"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"hnsw"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
                    &lt;/span&gt;&lt;span class="nl"&gt;"engine"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"faiss"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
                    &lt;/span&gt;&lt;span class="nl"&gt;"parameters"&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;"ef_construction"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;128&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
                        &lt;/span&gt;&lt;span class="nl"&gt;"m"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;24&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;span class="p"&gt;},&lt;/span&gt;&lt;span class="w"&gt;

            &lt;/span&gt;&lt;span class="nl"&gt;"AMAZON_BEDROCK_METADATA"&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;"type"&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"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
                &lt;/span&gt;&lt;span class="nl"&gt;"index"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"false"&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;"AMAZON_BEDROCK_TEXT_CHUNK"&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;"type"&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"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
                &lt;/span&gt;&lt;span class="nl"&gt;"index"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"true"&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;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;p&gt;For explanations of values like ef_construction, the OpenSearch official documentation has explanations and recommended parameter combinations, which I think would be helpful as reference.&lt;br&gt;
&lt;a href="https://opensearch.org/blog/a-practical-guide-to-selecting-hnsw-hyperparameters/#:%7E:text=efficiency%20%5B3%2C%204%5D.-,Recommended,-HNSW%20configurations" rel="noopener noreferrer"&gt;https://opensearch.org/blog/a-practical-guide-to-selecting-hnsw-hyperparameters/#:~:text=efficiency%20%5B3%2C%204%5D.-,Recommended,-HNSW%20configurations&lt;/a&gt;&lt;/p&gt;
&lt;h4&gt;
  
  
  Create VectorStore
&lt;/h4&gt;

&lt;p&gt;Next, use &lt;a href="https://boto3.amazonaws.com/v1/documentation/api/latest/reference/services/bedrock-agent/client/create_knowledge_base.html" rel="noopener noreferrer"&gt;create_knowledge_base&lt;/a&gt; to create a VectorStore.&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;boto3&lt;/span&gt;

&lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;main&lt;/span&gt;&lt;span class="p"&gt;():&lt;/span&gt;
    &lt;span class="n"&gt;client&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;boto3&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;client&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
        &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;bedrock-agent&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;region_name&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;&amp;lt;your-aws-region&amp;gt;&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;
    &lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="n"&gt;bedrock_kb_role_arn&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;&amp;lt;KB-IAMrole-arn&amp;gt;&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;
    &lt;span class="n"&gt;embedding_model_arn&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;&amp;lt;KB-Embedding-model-arn&amp;gt;&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;
    &lt;span class="n"&gt;opensearch_domain_endpoint&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;https://&amp;lt;os-domain-endpoint&amp;gt;&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;
    &lt;span class="n"&gt;opensearch_domain_arn&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;&amp;lt;os-domain-arn&amp;gt;&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;
    &lt;span class="n"&gt;vector_index_name&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;&amp;lt;vector-store-index-name&amp;gt;&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;
    &lt;span class="n"&gt;vector_field_name&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;&amp;lt;vector_field_name&amp;gt;&lt;/span&gt;&lt;span class="sh"&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;client&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;create_knowledge_base&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
                    &lt;span class="n"&gt;name&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;kb-sample&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
                    &lt;span class="n"&gt;roleArn&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="n"&gt;bedrock_kb_role_arn&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
                    &lt;span class="n"&gt;knowledgeBaseConfiguration&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;type&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;VECTOR&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;vectorKnowledgeBaseConfiguration&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="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;embeddingModelArn&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;embedding_model_arn&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
                            &lt;span class="c1"&gt;# Set this if implementing multimodal processing for images like PDFs
&lt;/span&gt;                            &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;supplementalDataStorageConfiguration&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="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;storageLocations&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="p"&gt;{&lt;/span&gt;
                                        &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;s3Location&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="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;uri&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;&amp;lt;s3-multimodal-bucket-arn&amp;gt;&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;type&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;S3&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="p"&gt;]&lt;/span&gt;
                            &lt;span class="p"&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;storageConfiguration&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;type&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;OPENSEARCH_MANAGED_CLUSTER&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;domainEndpoint&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;opensearch_domain_endpoint&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
                        &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;domainArn&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;opensearch_domain_arn&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
                        &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;vectorIndexName&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;vector_index_name&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
                        &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;fieldMapping&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="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;vectorField&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;vector_field_name&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
                            &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;textField&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;AMAZON_BEDROCK_TEXT_CHUNK&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;metadataField&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;AMAZON_BEDROCK_METADATA&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="p"&gt;}&lt;/span&gt;
                &lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="bp"&gt;...&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;If you want to vectorize images embedded in PDFs, you need to set an S3 bucket for multimodal processing in &lt;code&gt;supplementalDataStorageConfiguration&lt;/code&gt;.&lt;/p&gt;

&lt;h4&gt;
  
  
  Create Data Source
&lt;/h4&gt;

&lt;p&gt;Next, create a data source for the vector store. Here's an example when the text chunk strategy is set to fixed_size.&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;boto3&lt;/span&gt;

&lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;main&lt;/span&gt;&lt;span class="p"&gt;():&lt;/span&gt;
    &lt;span class="n"&gt;client&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;boto3&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;client&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
        &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;bedrock-agent&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;region_name&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;&amp;lt;your-aws-region&amp;gt;&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;
    &lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="n"&gt;knowledge_base_id&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="n"&gt;knowledge&lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="n"&gt;base&lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="nb"&gt;id&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;
    &lt;span class="n"&gt;datasource_s3_arn&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="n"&gt;S3&lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="n"&gt;arn&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;
    &lt;span class="n"&gt;aws_account_id&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="n"&gt;aws&lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="n"&gt;account&lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="nb"&gt;id&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;
    &lt;span class="n"&gt;inclusion_prefixes&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="n"&gt;inclusion&lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="n"&gt;prefixes&lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="n"&gt;array&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;
    &lt;span class="n"&gt;foundation_model_arn&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="n"&gt;foundation&lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="n"&gt;model&lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="n"&gt;arn&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;
    &lt;span class="n"&gt;chunking_max_tokens&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="n"&gt;chunking&lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="nb"&gt;max&lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="n"&gt;tokens&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;
    &lt;span class="n"&gt;overlap_percentage&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="n"&gt;overlap&lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="n"&gt;percentage&lt;/span&gt;&lt;span class="o"&gt;&amp;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;client&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;create_data_source&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
        &lt;span class="n"&gt;knowledgeBaseId&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="n"&gt;knowledge_base_id&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="n"&gt;name&lt;/span&gt;&lt;span class="o"&gt;=&amp;lt;&lt;/span&gt;&lt;span class="n"&gt;knowledge&lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="n"&gt;base&lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="n"&gt;name&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="n"&gt;dataSourceConfiguration&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;type&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;S3&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;s3Configuration&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="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;bucketArn&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;datasource_s3_arn&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
                &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;bucketOwnerAccountId&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;aws_account_id&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
                &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;inclusionPrefixes&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;inclusion_prefixes&lt;/span&gt;&lt;span class="p"&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;vectorIngestionConfiguration&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;chunkingConfiguration&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="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;chunkingStrategy&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;FIXED_SIZE&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;fixedSizeChunkingConfiguration&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="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;maxTokens&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;chunking_max_tokens&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
                    &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;overlapPercentage&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;overlap_percentage&lt;/span&gt;
                &lt;span class="p"&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;parsingConfiguration&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="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;bedrockFoundationModelConfiguration&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="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;modelArn&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;foundation_model_arn&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
                    &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;parsingModality&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;MULTIMODAL&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="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;parsingStrategy&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;BEDROCK_FOUNDATION_MODEL&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="p"&gt;}&lt;/span&gt;
    &lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="bp"&gt;...&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h4&gt;
  
  
  Ingest Vector Data into VectorStore
&lt;/h4&gt;

&lt;p&gt;Next, ingest vector data. This time, we'll use &lt;a href="https://boto3.amazonaws.com/v1/documentation/api/latest/reference/services/bedrock-agent/client/ingest_knowledge_base_documents.html" rel="noopener noreferrer"&gt;ingest_knowledge_base_documents&lt;/a&gt; to specify S3 files and insert vector data.&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;boto3&lt;/span&gt;

&lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;main&lt;/span&gt;&lt;span class="p"&gt;():&lt;/span&gt;
    &lt;span class="n"&gt;client&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;boto3&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;client&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
        &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;bedrock-agent&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;region_name&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;&amp;lt;your-aws-region&amp;gt;&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;
    &lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="n"&gt;knowledge_base_id&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="n"&gt;knowledge&lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="n"&gt;base&lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="nb"&gt;id&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;
    &lt;span class="n"&gt;datasource_id&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="n"&gt;datasource&lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="nb"&gt;id&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;
    &lt;span class="n"&gt;s3_uri&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="n"&gt;s3&lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="n"&gt;uri&lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="n"&gt;where&lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="n"&gt;the&lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="n"&gt;source&lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="nb"&gt;file&lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="ow"&gt;is&lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="n"&gt;located&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;
    &lt;span class="n"&gt;metadata_s3_uri&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="si"&gt;{&lt;/span&gt;&lt;span class="n"&gt;s3_uri&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="s"&gt;.metadata.json&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;
    &lt;span class="n"&gt;aws_account_id&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="n"&gt;aws&lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="n"&gt;account&lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="nb"&gt;id&lt;/span&gt;&lt;span class="o"&gt;&amp;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;client&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;ingest_knowledge_base_documents&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
        &lt;span class="n"&gt;knowledgeBaseId&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="n"&gt;knowledge_base_id&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="n"&gt;dataSourceId&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="n"&gt;datasource_id&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="n"&gt;documents&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="c1"&gt;# Set this if you want to add custom metadata
&lt;/span&gt;                &lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;metadata&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="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;type&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;S3_LOCATION&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;s3Location&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="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;uri&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;metadata_s3_uri&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
                        &lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;bucketOwnerAccountId&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;aws_account_id&lt;/span&gt;
                    &lt;span class="p"&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;content&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="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;dataSourceType&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;S3&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;s3&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="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;s3Location&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="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;uri&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;s3_uri&lt;/span&gt;
                        &lt;span class="p"&gt;}&lt;/span&gt;
                    &lt;span class="p"&gt;}&lt;/span&gt;
                &lt;span class="p"&gt;}&lt;/span&gt;
            &lt;span class="p"&gt;},&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;When importing files, you can get page numbers and source file information by default, but you can also set custom metadata. You can add metadata to the vector store by preparing a file named &lt;code&gt;filename.metadata.json&lt;/code&gt; in the same directory as the target file and specifying that path.&lt;br&gt;
&lt;a href="https://docs.aws.amazon.com/bedrock/latest/userguide/kb-metadata.html" rel="noopener noreferrer"&gt;https://docs.aws.amazon.com/bedrock/latest/userguide/kb-metadata.html&lt;/a&gt;&lt;/p&gt;
&lt;h4&gt;
  
  
  Execute Agent Combined with KnowledgeBase
&lt;/h4&gt;

&lt;p&gt;Finally, let's call the agent for dialogue. You can easily invoke dialogue by calling &lt;a href="https://boto3.amazonaws.com/v1/documentation/api/latest/reference/services/bedrock-agent-runtime/client/invoke_inline_agent.html" rel="noopener noreferrer"&gt;invoke_inline_agent&lt;/a&gt;. To simplify the explanation, I'll only include the parts related to KnowledgeBase.&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;boto3&lt;/span&gt;

&lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;main&lt;/span&gt;&lt;span class="p"&gt;():&lt;/span&gt;
    &lt;span class="n"&gt;client&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;boto3&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;client&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
        &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;bedrock-agent-runtime&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;region_name&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;&amp;lt;your-aws-region&amp;gt;&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;
    &lt;span class="p"&gt;)&lt;/span&gt;

    &lt;span class="n"&gt;vector_store_id&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;&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;store&lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="nb"&gt;id&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;
    &lt;span class="n"&gt;override_search_type&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="n"&gt;override&lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="n"&gt;search&lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="nb"&gt;type&lt;/span&gt;&lt;span class="o"&gt;&amp;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;client&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;invoke_inline_agent&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
        &lt;span class="n"&gt;knowledgeBases&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="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;knowledgeBaseId&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;vector_store_id&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
                &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;description&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;Knowledge base for document retrieval&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;retrievalConfiguration&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="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;vectorSearchConfiguration&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="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;overrideSearchType&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;override_search_type&lt;/span&gt;
                    &lt;span class="p"&gt;}&lt;/span&gt;
                &lt;span class="p"&gt;},&lt;/span&gt;
            &lt;span class="p"&gt;}&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;After the API search, you can get metadata of referenced documents from the following fields in the response:&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="p"&gt;{&lt;/span&gt;
    &lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;completion&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nc"&gt;EventStream&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;
        &lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;trace&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="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;trace&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="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;orchestrationTrace&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="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;observation&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="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;knowledgeBaseLookupOutput&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="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;metadata&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="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;clientRequestId&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;string&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;endTime&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nf"&gt;datetime&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;2015&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="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;
                                &lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;operationTotalTimeMs&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;123&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
                                &lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;startTime&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nf"&gt;datetime&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;2015&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="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;
                                &lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;totalTimeMs&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;123&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
                                &lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;usage&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="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;inputTokens&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;123&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
                                    &lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;outputTokens&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;123&lt;/span&gt;
                                &lt;span class="p"&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;retrievedReferences&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="p"&gt;{&lt;/span&gt;
                                    &lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;content&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="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;byteContent&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;string&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;row&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="p"&gt;{&lt;/span&gt;
                                                &lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;columnName&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;string&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;columnValue&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;string&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;type&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;BLOB&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="o"&gt;|&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;BOOLEAN&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="o"&gt;|&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;DOUBLE&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="o"&gt;|&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;NULL&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="o"&gt;|&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;LONG&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="o"&gt;|&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;STRING&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="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;text&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;string&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;type&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;TEXT&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="o"&gt;|&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;IMAGE&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="o"&gt;|&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;ROW&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;location&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="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;confluenceLocation&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="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;url&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;string&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;customDocumentLocation&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="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;id&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;string&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;kendraDocumentLocation&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="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;uri&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;string&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;s3Location&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="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;uri&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;string&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;salesforceLocation&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="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;url&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;string&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;sharePointLocation&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="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;url&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;string&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;sqlLocation&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="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;query&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;string&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;type&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;S3&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="o"&gt;|&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;WEB&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="o"&gt;|&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;CONFLUENCE&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="o"&gt;|&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;SALESFORCE&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="o"&gt;|&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;SHAREPOINT&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="o"&gt;|&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;CUSTOM&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="o"&gt;|&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;KENDRA&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="o"&gt;|&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;SQL&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;webLocation&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="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;url&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;string&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="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;metadata&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="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;string&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="o"&gt;|&lt;/span&gt;&lt;span class="p"&gt;[...]&lt;/span&gt;&lt;span class="o"&gt;|&lt;/span&gt;&lt;span class="mi"&gt;123&lt;/span&gt;&lt;span class="o"&gt;|&lt;/span&gt;&lt;span class="mf"&gt;123.4&lt;/span&gt;&lt;span class="o"&gt;|&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;string&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="o"&gt;|&lt;/span&gt;&lt;span class="bp"&gt;True&lt;/span&gt;&lt;span class="o"&gt;|&lt;/span&gt;&lt;span class="bp"&gt;None&lt;/span&gt;
                                    &lt;span class="p"&gt;}&lt;/span&gt;
                                &lt;span class="p"&gt;},&lt;/span&gt;
                            &lt;span class="p"&gt;]&lt;/span&gt;
                        &lt;span class="p"&gt;},&lt;/span&gt;
                    &lt;span class="p"&gt;},&lt;/span&gt;
                &lt;span class="p"&gt;},&lt;/span&gt;
            &lt;span class="p"&gt;}&lt;/span&gt;
        &lt;span class="p"&gt;},&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;Among these, retrievedReferences[*].metadata contains metadata about referenced files such as:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;code&gt;x-amz-bedrock-kb-source-uri&lt;/code&gt;: S3 URL&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;x-amz-bedrock-kb-chunk-id&lt;/code&gt;: Chunk Id used to generate response&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;x-amz-bedrock-kb-data-source-id&lt;/code&gt;: Datasource id&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;x-amz-bedrock-kb-document-page-number&lt;/code&gt;: page number of the source document
In addition, if you specify a metadata.json file, metadata will be added here.&lt;/li&gt;
&lt;/ul&gt;

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

&lt;p&gt;This has been an explanation of RAG functionality using KnowledgeBase.&lt;br&gt;
KnowledgeBase and RAG functionality have much to discuss, and it's impossible to cover everything in this blog, so I've only introduced the basics.&lt;br&gt;
If I have time, I'd like to touch on parameter tuning for OpenSearch index as well.&lt;br&gt;
I hope this article will be helpful for everyone.&lt;/p&gt;

</description>
      <category>webdev</category>
      <category>aws</category>
      <category>llm</category>
      <category>rag</category>
    </item>
    <item>
      <title>Reflections on My First 10 Blog Posts: Lessons, Challenges, and Next Steps</title>
      <dc:creator>ryo ariyama</dc:creator>
      <pubDate>Sun, 06 Jul 2025 16:33:12 +0000</pubDate>
      <link>https://forem.com/ryo_ariyama_b521d7133c493/reflections-on-my-first-10-blog-posts-lessons-challenges-and-next-steps-373f</link>
      <guid>https://forem.com/ryo_ariyama_b521d7133c493/reflections-on-my-first-10-blog-posts-lessons-challenges-and-next-steps-373f</guid>
      <description>&lt;h2&gt;
  
  
  Introduction
&lt;/h2&gt;

&lt;p&gt;I’ve now published 10 blog posts, so I’d like to take a moment to reflect on the experience so far.&lt;/p&gt;

&lt;h2&gt;
  
  
  What Went Well
&lt;/h2&gt;

&lt;p&gt;As I wrote in this post, I originally started blogging because I didn’t have many opportunities to share my thoughts or output my ideas.&lt;/p&gt;

&lt;p&gt;My purpose hasn’t really changed since then, but by writing these posts, I’ve been able to review and reinforce what I learn day to day — and that’s been really helpful.&lt;br&gt;
Personally, I tend to forget the details of what I’ve implemented after about a month, but I feel like I remember the contents of my blog posts quite well.&lt;br&gt;
Also, writing a blog often requires doing additional research, so I feel like I’ve deepened my knowledge in the process.&lt;/p&gt;

&lt;p&gt;On top of that, this experience has helped me understand what kind of engineer I am.&lt;br&gt;
I usually work across different technical areas and don’t have a clear title like “Backend Engineer.” or “Infrastructure Engineer.” So I hadn’t really known how to define myself. But looking back at the posts I’ve written, many of them are about infrastructure-related topics — so I guess I’m more of an infrastructure engineer who can also do backend development.&lt;br&gt;
I think this could be useful when I look for new opportunities in the future — though, unfortunately, I haven’t actually had any interviews during this time.&lt;/p&gt;

&lt;h2&gt;
  
  
  Challenges
&lt;/h2&gt;

&lt;p&gt;While there have been a lot of positives, there have definitely been challenges too.&lt;br&gt;
First of all, to be honest, publishing a blog post every week is pretty tough.&lt;br&gt;
I’ve often found myself thinking that I could have spent my precious days off doing something else — for example, working on a side project or planning a personal trip instead.&lt;/p&gt;

&lt;p&gt;Also, although I didn’t mention this in my introduction post, I have a personal rule: I must write each post within two hours.&lt;br&gt;
The reasons are simple — I don’t want to spend too much of my weekends on blogging, and I feel that spending more time would just result in longer, possibly harder-to-read posts.&lt;br&gt;
Keeping this rule has actually been pretty hard for me. But on the bright side, it forced me to find ways to write more efficiently, which I think was a good thing.&lt;/p&gt;

&lt;p&gt;For example, I used to create diagrams myself in draw.io, but I switched to using AI to generate ASCII art instead.&lt;br&gt;
I also experimented with generating drafts based on my headings and outlines using AI, then adding my own explanations.&lt;br&gt;
Of course, most of the sample code is also generated by AI and I even ask AI to review my drafts.&lt;br&gt;
I still come up with the article titles myself — though sometimes I get suggestions from AI too.&lt;br&gt;
Obviously, it wouldn’t make sense to have AI write a reflection post like this one, so I wrote this one entirely by myself.&lt;/p&gt;

&lt;p&gt;Sometimes I wonder if relying on AI like this is a bit of a shortcut, but if I can achieve my goal in a short amount of time, I think it’s good enough.&lt;/p&gt;

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

&lt;p&gt;I still believe that outputting my thoughts is important, so I plan to keep blogging until I run out of ideas.&lt;br&gt;
Maintaining motivation to post regularly is really tough, so I hope to keep exploring better ways to stay motivated — maybe I’ll write about that too.&lt;/p&gt;

&lt;p&gt;Ideally, I hope this blog will help me find new work opportunities or expand my network.&lt;/p&gt;

&lt;p&gt;Lastly, I’d like to say thank you to the people who shared my posts and reacted positively on SNS like X and LinkedIn.&lt;br&gt;
Thanks to them, some of my articles reached a wider audience than I ever expected.&lt;br&gt;
I really appreciate it.&lt;/p&gt;

&lt;p&gt;That’s all for now.&lt;/p&gt;

</description>
      <category>webdev</category>
      <category>programming</category>
      <category>beginners</category>
      <category>devto</category>
    </item>
    <item>
      <title>Introduction to LocalStack</title>
      <dc:creator>ryo ariyama</dc:creator>
      <pubDate>Sun, 29 Jun 2025 13:23:03 +0000</pubDate>
      <link>https://forem.com/ryo_ariyama_b521d7133c493/introduction-to-localstack-2f0k</link>
      <guid>https://forem.com/ryo_ariyama_b521d7133c493/introduction-to-localstack-2f0k</guid>
      <description>&lt;h2&gt;
  
  
  Introduction
&lt;/h2&gt;

&lt;p&gt;Recently, I've been increasingly using LocalStack to set up local development environments at work, so I'd like to explain the reasons behind this.&lt;/p&gt;

&lt;h2&gt;
  
  
  What is LocalStack?
&lt;/h2&gt;

&lt;p&gt;LocalStack is a tool that can emulate AWS services on your local machine.&lt;br&gt;
&lt;a href="https://www.localstack.cloud/" rel="noopener noreferrer"&gt;https://www.localstack.cloud/&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;An emulator is software that mimics the behavior of other software or systems and can be used as an alternative. By using LocalStack, you can reproduce AWS services like S3 and Lambda in your local environment.&lt;/p&gt;

&lt;p&gt;The main use cases are individual learning and testing in local environments.&lt;/p&gt;

&lt;p&gt;The free version supports around 30 services, which is sufficient for individual learning purposes. However, for commercial services or web services of a certain scale, I believe you would need to use the Base plan.&lt;/p&gt;
&lt;h2&gt;
  
  
  Motivation for Adoption
&lt;/h2&gt;

&lt;p&gt;In our usual backend development, we follow the flow of developing in a local environment → deploying to the cloud for functional verification.&lt;/p&gt;

&lt;p&gt;When developing in a local environment, we set up AWS profiles and SSH tunneling to resolve authentication and network issues.&lt;/p&gt;

&lt;p&gt;This is sufficient for regular development, but we decided to use LocalStack due to the following requirements:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;We want members from other departments to run the applications we operate, but for various reasons, we don't want to grant them AWS account permissions.&lt;/li&gt;
&lt;li&gt;We want to use AI agents like Devin for development, but it's difficult to resolve network and authentication issues.&lt;/li&gt;
&lt;li&gt;Even if we could resolve these issues, we want to avoid excessive usage of pay-per-use services due to AI agent malfunctions.&lt;/li&gt;
&lt;/ul&gt;
&lt;h2&gt;
  
  
  Environment Setup
&lt;/h2&gt;

&lt;p&gt;Environment setup is done using Docker and Docker Compose. Here is the image of the difference between AWS Cloud and Localstack.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;// AWS Cloud
┌─────────────────┐           ┌─────────────────────────────────┐
│  Local env      │           │         AWS Cloud               │
│                 │           │                                 │
│  ┌───────────┐  │  HTTPS    │  ┌─────────┐  ┌─────────┐       │
│  │Backend    │  │ ────────► │  │   S3    │  │ Lambda  │       │
│  │Code       │  │  Request  │  │         │  │         │       │
│  └───────────┘  │           │  └─────────┘  └─────────┘       │
│                 │           │                                 │
│  ┌───────────┐  │           │  ┌─────────┐  ┌─────────┐       │
│  │Terraform  │  │ ────────► │  │DynamoDB │  │   SQS   │       │
│  │           │  │           │  │         │  │         │       │
│  └───────────┘  │           │  └─────────┘  └─────────┘       │
└─────────────────┘           └─────────────────────────────────┘

// Localstack
┌───────────────────────────────────────────────────────────────┐
│                       Local env                               │
│                                                               │
│  ┌───────────┐           ┌─────────────────────────────────┐  │
│  │Backend    │  HTTP     │        Docker Container         │  │
│  │Code       │ ────────► │                                 │  │
│  └───────────┘  :4566    │  ┌──────────┐  ┌──────────┐     │  │
│                          │  │   S3     │  │ Lambda   │     │  │
│  ┌───────────┐           │  │(emulated)│  │(emulated)│     │  │
│  │Terraform  │ ────────► │  └──────────┘  └──────────┘     │  │
│  │           │           │                                 │  │
│  └───────────┘           │  ┌──────────┐  ┌──────────┐     │  │
│                          │  │DynamoDB  │  │   SQS    │     │  │
│                          │  │(emulated)│  │(emulated)│     │  │
│                          │  └──────────┘  └──────────┘     │  │
│                          │                                 │  │
│                          │        LocalStack Process       │  │
│                          └─────────────────────────────────┘  │
└───────────────────────────────────────────────────────────────┘
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The &lt;a href="https://docs.localstack.cloud/getting-started/installation/#docker-compose" rel="noopener noreferrer"&gt;official documentation&lt;/a&gt; provides installation instructions, and you can follow these to set it up.&lt;/p&gt;

&lt;p&gt;Prepare a YAML file like the following:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight yaml"&gt;&lt;code&gt;&lt;span class="na"&gt;version&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="s"&gt;3.8"&lt;/span&gt;

&lt;span class="na"&gt;services&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="na"&gt;localstack&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="na"&gt;container_name&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;localstack&lt;/span&gt;
    &lt;span class="na"&gt;image&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;localstack/localstack-pro:latest&lt;/span&gt;
    &lt;span class="na"&gt;ports&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="s"&gt;4566:4566"&lt;/span&gt;
    &lt;span class="na"&gt;environment&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s"&gt;LOCALSTACK_AUTH_TOKEN="${LOCALSTACK_AUTH_TOKEN}"&lt;/span&gt; &lt;span class="c1"&gt;# required for base, pro...&lt;/span&gt;
      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s"&gt;SERVICES=&amp;lt;AWS services&amp;gt;&lt;/span&gt; &lt;span class="c1"&gt;# comma delimited AWS services list like s3, lambda, batch...&lt;/span&gt;
      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s"&gt;DEFAULT_REGION=&amp;lt;aws-region&amp;gt;&lt;/span&gt;
      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s"&gt;DOCKER_HOST=unix:///var/run/docker.sock&lt;/span&gt;
    &lt;span class="na"&gt;volumes&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="s"&gt;./:/tmp/app"&lt;/span&gt;
      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="s"&gt;/var/run/docker.sock:/var/run/docker.sock"&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Start the Docker process with &lt;code&gt;docker-compose up -d&lt;/code&gt;. This process creates emulated AWS endpoints, and you can create AWS services by sending requests to these endpoints.&lt;/p&gt;

&lt;p&gt;There's an AWS CLI called &lt;a href="https://github.com/localstack/awscli-local" rel="noopener noreferrer"&gt;awslocal&lt;/a&gt; that can be used with LocalStack, which allows you to easily create resources. To create an S3 bucket, execute the following command:&lt;/p&gt;

&lt;p&gt;&lt;code&gt;awslocal s3 mb s3://${S3_BUCKET_NAME}&lt;/code&gt;&lt;/p&gt;

&lt;p&gt;You can also use Terraform with LocalStack.&lt;/p&gt;

&lt;p&gt;In the provider configuration, change the endpoint to LocalStack's endpoint as follows. The default endpoint is &lt;code&gt;http://localhost:4566&lt;/code&gt;, so use that:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight hcl"&gt;&lt;code&gt;&lt;span class="nx"&gt;provider&lt;/span&gt; &lt;span class="s2"&gt;"aws"&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nx"&gt;region&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;"&amp;lt;region&amp;gt;"&lt;/span&gt;
  &lt;span class="nx"&gt;endpoints&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="c1"&gt;// specify localstack endpoint to the services you use.&lt;/span&gt;
    &lt;span class="nx"&gt;dynamodb&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;"http://localhost:4566"&lt;/span&gt;
    &lt;span class="nx"&gt;s3&lt;/span&gt;       &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;"http://localhost:4566"&lt;/span&gt;
    &lt;span class="nx"&gt;lambda&lt;/span&gt;   &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;"http://localhost:4566"&lt;/span&gt;
    &lt;span class="p"&gt;...&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;

  &lt;span class="nx"&gt;s3_use_path_style&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="kc"&gt;true&lt;/span&gt;

  &lt;span class="nx"&gt;skip_credentials_validation&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="kc"&gt;true&lt;/span&gt;
  &lt;span class="nx"&gt;skip_metadata_api_check&lt;/span&gt;     &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="kc"&gt;true&lt;/span&gt;
  &lt;span class="nx"&gt;skip_requesting_account_id&lt;/span&gt;  &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="kc"&gt;true&lt;/span&gt;

  &lt;span class="nx"&gt;access_key&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;"test"&lt;/span&gt;
  &lt;span class="nx"&gt;secret_key&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;"test"&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;After configuring the above settings, execute &lt;code&gt;terraform apply&lt;/code&gt; to create emulated resources using Terraform with LocalStack's endpoint.&lt;/p&gt;

&lt;p&gt;That covers how to create AWS resources.&lt;/p&gt;

&lt;p&gt;To use AWS services created with LocalStack from backend code, change the AWS service endpoint to LocalStack's endpoint, similar to Terraform. Here's sample code for initializing an S3 client in Go:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight go"&gt;&lt;code&gt;&lt;span class="k"&gt;var&lt;/span&gt; &lt;span class="n"&gt;configOptions&lt;/span&gt; &lt;span class="p"&gt;[]&lt;/span&gt;&lt;span class="k"&gt;func&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="o"&gt;*&lt;/span&gt;&lt;span class="n"&gt;config&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;LoadOptions&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="kt"&gt;error&lt;/span&gt;

&lt;span class="n"&gt;cfg&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;err&lt;/span&gt; &lt;span class="o"&gt;:=&lt;/span&gt; &lt;span class="n"&gt;config&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;LoadDefaultConfig&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;context&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;TODO&lt;/span&gt;&lt;span class="p"&gt;(),&lt;/span&gt; &lt;span class="n"&gt;configOptions&lt;/span&gt;&lt;span class="o"&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;err&lt;/span&gt; &lt;span class="o"&gt;!=&lt;/span&gt; &lt;span class="no"&gt;nil&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="no"&gt;nil&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;err&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="n"&gt;localstackEndpoint&lt;/span&gt; &lt;span class="o"&gt;:=&lt;/span&gt; &lt;span class="s"&gt;"http://localhost:4566"&lt;/span&gt;
&lt;span class="n"&gt;cfg&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;BaseEndpoint&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;aws&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;String&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;localstackEndpoint&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

&lt;span class="n"&gt;client&lt;/span&gt; &lt;span class="o"&gt;:=&lt;/span&gt; &lt;span class="n"&gt;s3&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;NewFromConfig&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;cfg&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="o"&gt;...&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



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

&lt;p&gt;That's all there is to it. It can be used with relatively simple changes. The use of AI agents, as mentioned in the motivation section, will likely increase in the future, so using LocalStack as a measure to prevent excessive service usage seems like a good strategy.&lt;/p&gt;

&lt;p&gt;I hope this article will be helpful to everyone.&lt;/p&gt;

</description>
      <category>webdev</category>
      <category>programming</category>
      <category>aws</category>
      <category>infrastructureascode</category>
    </item>
    <item>
      <title>Introduction to Data Analytics Platform with Databricks</title>
      <dc:creator>ryo ariyama</dc:creator>
      <pubDate>Sun, 22 Jun 2025 17:31:21 +0000</pubDate>
      <link>https://forem.com/ryo_ariyama_b521d7133c493/introduction-to-data-analytics-platform-with-databricks-2n8c</link>
      <guid>https://forem.com/ryo_ariyama_b521d7133c493/introduction-to-data-analytics-platform-with-databricks-2n8c</guid>
      <description>&lt;h3&gt;
  
  
  Introduction
&lt;/h3&gt;

&lt;p&gt;Recently, I've had several opportunities to build data analytics platforms using Databricks.&lt;br&gt;
As a reference, I'd like to summarize what I've learned.&lt;/p&gt;
&lt;h3&gt;
  
  
  Prerequisites for this Article
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;This article explains the basic concepts of Databricks.&lt;/li&gt;
&lt;li&gt;A construction article using sample scripts will be introduced in a separate article.&lt;/li&gt;
&lt;li&gt;It is written with the assumption of building in combination with AWS.&lt;/li&gt;
&lt;/ul&gt;
&lt;h3&gt;
  
  
  What is Databricks
&lt;/h3&gt;

&lt;p&gt;Databricks is a data analytics platform based on Apache Spark.&lt;br&gt;
Databricks is a platform that can handle everything related to data analytics, including data collection, processing, analysis, and visualization.&lt;br&gt;
Construction can be performed using services from multiple cloud vendors such as AWS, Azure, and GCP.&lt;/p&gt;
&lt;h3&gt;
  
  
  Architecture Overview
&lt;/h3&gt;

&lt;p&gt;Databricks has an account as the top-level resource, with workspaces underneath it.&lt;br&gt;
The account manages billing, users, and workspaces.&lt;br&gt;
A workspace is where actual data analysis takes place, managing notebooks, jobs, SQL warehouses, and more.&lt;br&gt;
Using AWS Organizations as an example, the Databricks account corresponds to the management account, and workspaces correspond to individual AWS accounts.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;┌──────────────────────────────────────────────────────────────────┐
│                    Databricks Account                            │
│                                                                  │
│  ┌─ Management functions ──────────────────────────────────┐     │
│  │ • Billing management                                    │     │
│  │ • User management                                       │     │
│  │ • Workspace management                                  │     │
│  │ • Security configuration                                │     │
│  └─────────────────────────────────────────────────────────┘     │
│                                                                  │
│  ┌─ Workspace A ──────────────┐  ┌─ Workspace B ──────────────┐  │
│  │                            │  │                            │  │
│  │ ┌─ Control Plane ────────┐ │  │ ┌─ Control Plane ────────┐ │  │
│  │ │ • Web UI               │ │  │ │ • Web UI               │ │  │
│  │ │ • Job Scheduler        │ │  │ │ • Job Scheduler        │ │  │
│  │ │ • Metadata Store       │ │  │ │ • Metadata Store       │ │  │
│  │ │ • Security Manager     │ │  │ │ • Security Manager     │ │  │
│  │ └────────────────────────┘ │  │ └────────────────────────┘ │  │
│  │            │               │  │            │               │  │
│  │            ▼               │  │            ▼               │  │
│  │ ┌─ Compute Plane ────────┐ │  │ ┌─ Compute Plane ────────┐ │  │
│  │ │ • Clusters             │ │  │ │ • Clusters             │ │  │
│  │ │ • SQL Warehouses       │ │  │ │ • SQL Warehouses       │ │  │
│  │ │ • Notebooks            │ │  │ │ • Notebooks            │ │  │
│  │ │ • Jobs                 │ │  │ │ • Jobs                 │ │  │
│  │ └────────────────────────┘ │  │ └────────────────────────┘ │  │
│  └────────────────────────────┘  └────────────────────────────┘  │
└──────────────────────────────────────────────────────────────────┘
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Within a workspace, there are two main components: the control plane for management and the compute plane for data processing.&lt;br&gt;
The control plane is a service provided by Databricks - a web application managed by Databricks.&lt;br&gt;
The compute plane consists of resources for actual data processing. There are two configuration methods:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Classic configuration: Create clusters as EC2 instances within the user's AWS account&lt;/li&gt;
&lt;li&gt;Serverless configuration: Execute resources within Databricks-managed AWS accounts (utilizing underlying AWS resources invisibly to users)&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;The following page provides a clear explanation:&lt;br&gt;
&lt;a href="https://docs.databricks.com/aws/en/getting-started/overview" rel="noopener noreferrer"&gt;Databricks architecture overview&lt;/a&gt;&lt;/p&gt;

&lt;h3&gt;
  
  
  Databricks Features
&lt;/h3&gt;

&lt;p&gt;What are the benefits of adopting Databricks as a data analytics platform? The main Databricks-specific features for analytics platforms include:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Delta lake&lt;/li&gt;
&lt;li&gt;Unity Catalog&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Delta Lake is an open-source storage format.&lt;br&gt;
It consists of Apache Parquet format files, delta logs (JSON format), and metadata, enabling ACID transactions that were not possible with traditional data lakes and data warehouses.&lt;br&gt;
Specifically, while traditional data lakes could potentially retrieve inconsistent data when reading during data updates, Delta Lake enables reading data in a consistently coherent state through its transaction functionality.&lt;br&gt;
&lt;a href="https://docs.databricks.com/aws/en/delta" rel="noopener noreferrer"&gt;Databricks delta lake&lt;/a&gt;&lt;br&gt;
Databricks provides default support for Delta Lake.&lt;/p&gt;

&lt;p&gt;Unity Catalog is a data governance feature provided by Databricks.&lt;br&gt;
It enables access management and quality management for storage across workspaces.&lt;br&gt;
&lt;a href="https://docs.databricks.com/aws/en/data-governance/unity-catalog" rel="noopener noreferrer"&gt;Databricks unity catalog&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Specific use cases include:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;a href="https://docs.databricks.com/aws/en/data-governance/unity-catalog#securable-objects-that-unity-catalog-uses-to-manage-access-to-external-data-sources" rel="noopener noreferrer"&gt;Permission settings for external storage like S3&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://docs.databricks.com/aws/en/data-governance/unity-catalog#securable-objects-that-unity-catalog-uses-to-manage-access-to-shared-assets" rel="noopener noreferrer"&gt;Permission settings for Databricks tables&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://docs.databricks.com/aws/en/data-governance/unity-catalog/data-lineage" rel="noopener noreferrer"&gt;Table lineage acquisition&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://docs.databricks.com/aws/en/lakehouse-monitoring" rel="noopener noreferrer"&gt;Column statistics acquisition using Lakehouse monitoring&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  Why Choose Databricks
&lt;/h3&gt;

&lt;p&gt;While simple analytics systems that store data in S3 or DWH and analyze with Python or SQL could be implemented using only AWS services, Databricks offers the following advantages:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Strong data consistency guarantee: Transaction functionality through Delta Lake&lt;/li&gt;
&lt;li&gt;Integrated data quality management: Quality monitoring and governance through Unity Catalog&lt;/li&gt;
&lt;li&gt;Multi-cloud support: Enables integrated data collection and analysis even when there are multiple cloud environments within an organization
These features enable the operation of high-quality data platforms, which I believe are the main reasons for adopting Databricks.&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  Conclusion
&lt;/h3&gt;

&lt;p&gt;That's all for now. This article briefly explained the basic knowledge about Databricks.&lt;br&gt;
I plan to publish an actual construction article using Terraform in the future.&lt;/p&gt;

</description>
      <category>webdev</category>
      <category>data</category>
      <category>datascience</category>
      <category>dataengineering</category>
    </item>
    <item>
      <title>How We Manage ECS with Terraform and GitHub Repos</title>
      <dc:creator>ryo ariyama</dc:creator>
      <pubDate>Sun, 08 Jun 2025 12:43:17 +0000</pubDate>
      <link>https://forem.com/ryo_ariyama_b521d7133c493/amazon-ecs-4j7k</link>
      <guid>https://forem.com/ryo_ariyama_b521d7133c493/amazon-ecs-4j7k</guid>
      <description>&lt;h2&gt;
  
  
  Introduction
&lt;/h2&gt;

&lt;p&gt;This may seem like a thoroughly discussed topic already, but since the same conversation recently came up internally at work, I decided to write about it.&lt;/p&gt;

&lt;p&gt;In our day-to-day operations, we use Amazon ECS as the platform for running our applications. AWS resources are provisioned with Terraform, and we manage our code on GitHub with separate repositories for infrastructure and backend.&lt;/p&gt;

&lt;p&gt;A frequent point of discussion is how much of the ECS-related resources should be managed by the infrastructure side, which I’ll break down in this article.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Intended audience&lt;/strong&gt;: Engineers using ECS and Terraform, especially those working in teams with separate infrastructure and application repositories&lt;br&gt;&lt;br&gt;
&lt;strong&gt;Estimated reading time&lt;/strong&gt;: 7 minutes&lt;/p&gt;
&lt;h2&gt;
  
  
  How We Manage It
&lt;/h2&gt;

&lt;p&gt;To cut to the chase: ECS task definitions and ECS service definitions are &lt;em&gt;created&lt;/em&gt; in the infrastructure repository but &lt;em&gt;updated&lt;/em&gt; from the backend repository. Everything else is managed in the infrastructure repository.&lt;/p&gt;

&lt;p&gt;Before explaining why, let’s list the common triggers for updating these definitions:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Changing the tag of a container image in ECR&lt;/li&gt;
&lt;li&gt;Updating environment variables or CPU/MEM parameters&lt;/li&gt;
&lt;li&gt;Deploying a new revision of the task definition via the service definition&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Especially with the first point, changing the container image tag typically results from updates to backend source code rather than infrastructure changes. Because of this dependency on application implementation, we believe it’s better not to manage such values from the infrastructure repository. Instead, they should be handled in the backend repository.&lt;/p&gt;

&lt;p&gt;If you’re unsure what this looks like, consider a case where another team manages the infrastructure repository and also owns the ECS task definitions. When you want to deploy:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;You build the container and push the image to ECR&lt;/li&gt;
&lt;li&gt;You inform the infra team of the new image tag so they can create a new task definition&lt;/li&gt;
&lt;li&gt;They update the ECS service to use the new task definition&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Doing this every time you deploy creates a lot of communication overhead. Ideally, both teams should be able to deploy independently. That’s why we create the ECS service and task definitions in the infrastructure repo but handle updates from the backend.&lt;/p&gt;
&lt;h2&gt;
  
  
  Sample Implementation
&lt;/h2&gt;

&lt;p&gt;Here’s a reference implementation, starting with Terraform code for ECS:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight hcl"&gt;&lt;code&gt;&lt;span class="c1"&gt;# ECS task definition &lt;/span&gt;
&lt;span class="nx"&gt;resource&lt;/span&gt; &lt;span class="s2"&gt;"aws_ecs_task_definition"&lt;/span&gt; &lt;span class="s2"&gt;"main"&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nx"&gt;family&lt;/span&gt;             &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;var&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;container_name&lt;/span&gt;
  &lt;span class="nx"&gt;task_role_arn&lt;/span&gt;      &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;var&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;task_role_arn&lt;/span&gt;
  &lt;span class="nx"&gt;execution_role_arn&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;var&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;exec_role_arn&lt;/span&gt;

  &lt;span class="nx"&gt;requires_compatibilities&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s2"&gt;"FARGATE"&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;
  &lt;span class="nx"&gt;network_mode&lt;/span&gt;             &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;"awsvpc"&lt;/span&gt;
  &lt;span class="nx"&gt;cpu&lt;/span&gt;                      &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;var&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;task_cpu&lt;/span&gt;
  &lt;span class="nx"&gt;memory&lt;/span&gt;                   &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;var&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;task_mem&lt;/span&gt;

  &lt;span class="nx"&gt;lifecycle&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="nx"&gt;ignore_changes&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nx"&gt;container_definitions&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;

  &lt;span class="nx"&gt;container_definitions&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;jsonencode&lt;/span&gt;&lt;span class="p"&gt;([&lt;/span&gt;
    &lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="nx"&gt;essential&lt;/span&gt;   &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="kc"&gt;true&lt;/span&gt;
      &lt;span class="nx"&gt;name&lt;/span&gt;        &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;var&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;container_name&lt;/span&gt;
      &lt;span class="nx"&gt;image&lt;/span&gt;       &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;"${var.ecr_repo_url}:latest"&lt;/span&gt;
      &lt;span class="nx"&gt;cpu&lt;/span&gt;         &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;
      &lt;span class="nx"&gt;environment&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="p"&gt;[]&lt;/span&gt;
      &lt;span class="nx"&gt;logConfiguration&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="nx"&gt;logDriver&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;"awslogs"&lt;/span&gt;
        &lt;span class="nx"&gt;options&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
          &lt;span class="nx"&gt;awslogs-create-group&lt;/span&gt;  &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;"true"&lt;/span&gt;
          &lt;span class="nx"&gt;awslogs-group&lt;/span&gt;         &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;"/ecs/example"&lt;/span&gt;
          &lt;span class="nx"&gt;awslogs-region&lt;/span&gt;        &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;var&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;region&lt;/span&gt;
          &lt;span class="nx"&gt;awslogs-stream-prefix&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;"ecs"&lt;/span&gt;
        &lt;span class="p"&gt;}&lt;/span&gt;
      &lt;span class="p"&gt;}&lt;/span&gt;
      &lt;span class="nx"&gt;portMappings&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;
        &lt;span class="p"&gt;{&lt;/span&gt;
          &lt;span class="nx"&gt;appProtocol&lt;/span&gt;   &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;var&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;container_protocol&lt;/span&gt;
          &lt;span class="nx"&gt;containerPort&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;var&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;container_port&lt;/span&gt;
          &lt;span class="nx"&gt;hostPort&lt;/span&gt;      &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;var&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;container_port&lt;/span&gt;
          &lt;span class="nx"&gt;name&lt;/span&gt;          &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;"${var.container_name}_port"&lt;/span&gt;
          &lt;span class="nx"&gt;protocol&lt;/span&gt;      &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;"tcp"&lt;/span&gt;
        &lt;span class="p"&gt;}&lt;/span&gt;
      &lt;span class="p"&gt;]&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;
  &lt;span class="p"&gt;])&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="c1"&gt;# ECS service definition&lt;/span&gt;
&lt;span class="nx"&gt;resource&lt;/span&gt; &lt;span class="s2"&gt;"aws_ecs_service"&lt;/span&gt; &lt;span class="s2"&gt;"main"&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nx"&gt;name&lt;/span&gt;                               &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;var&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;service_name&lt;/span&gt;
  &lt;span class="nx"&gt;cluster&lt;/span&gt;                            &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;var&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;cluster_id&lt;/span&gt;
  &lt;span class="nx"&gt;deployment_maximum_percent&lt;/span&gt;         &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;var&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;deployment_maximum_percent&lt;/span&gt;
  &lt;span class="nx"&gt;deployment_minimum_healthy_percent&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;var&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;deployment_minimum_healthy_percent&lt;/span&gt;
  &lt;span class="nx"&gt;desired_count&lt;/span&gt;                      &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;var&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;desired_count&lt;/span&gt;
  &lt;span class="nx"&gt;enable_execute_command&lt;/span&gt;             &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;var&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;enable_execute_command&lt;/span&gt;
  &lt;span class="nx"&gt;health_check_grace_period_seconds&lt;/span&gt;  &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;var&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;health_check_grace_period_seconds&lt;/span&gt;
  &lt;span class="nx"&gt;launch_type&lt;/span&gt;                        &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;"FARGATE"&lt;/span&gt;
  &lt;span class="nx"&gt;platform_version&lt;/span&gt;                   &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;"1.4.0"&lt;/span&gt;
  &lt;span class="nx"&gt;task_definition&lt;/span&gt;                    &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;aws_ecs_task_definition&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;main&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;arn&lt;/span&gt;

  &lt;span class="nx"&gt;deployment_controller&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="nx"&gt;type&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;"ECS"&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;

  &lt;span class="nx"&gt;load_balancer&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="nx"&gt;container_name&lt;/span&gt;   &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;var&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;container_name&lt;/span&gt;
    &lt;span class="nx"&gt;container_port&lt;/span&gt;   &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;var&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;container_port&lt;/span&gt;
    &lt;span class="nx"&gt;target_group_arn&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;var&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;tg_arn&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;

  &lt;span class="nx"&gt;network_configuration&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="nx"&gt;assign_public_ip&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="kc"&gt;false&lt;/span&gt;
    &lt;span class="nx"&gt;security_groups&lt;/span&gt;  &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nx"&gt;var&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;sg_id&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;
    &lt;span class="nx"&gt;subnets&lt;/span&gt;          &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;var&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;subnets&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;

  &lt;span class="nx"&gt;lifecycle&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="nx"&gt;ignore_changes&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;
      &lt;span class="nx"&gt;task_definition&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
      &lt;span class="nx"&gt;desired_count&lt;/span&gt;
    &lt;span class="p"&gt;]&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;The &lt;code&gt;lifecycle.ignore_changes&lt;/code&gt; block ensures that attributes dependent on the backend won’t show as diffs in Terraform plans. In this example, we ignore container versions and environment variables to allow safe initial creation.&lt;/p&gt;

&lt;p&gt;For backend deployments, we use &lt;a href="https://github.com/kayac/ecspresso" rel="noopener noreferrer"&gt;ecspresso&lt;/a&gt;, a CLI tool that lets you deploy using JSON definitions for task and service configurations.&lt;/p&gt;

&lt;p&gt;Here’s what the definitions look like:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;// ecs_task_def.json
{
  "containerDefinitions": [
    {
      "cpu": {{ env `ECS_CPU` `256` }},
      "essential": true,
      "image": "{{ env `ECR_REPO_IMAGE_URL` }}:{{ env `IMAGE_TAG` }}",
      "logConfiguration": {
        "logDriver": "awslogs",
        "options": {
          "awslogs-create-group": "true",
          "awslogs-group": "/ecs/example",
          "awslogs-region": "ap-northeast-1",
          "awslogs-stream-prefix": "ecs"
        }
      },
      "memory": {{ env `ECS_MEMORY` `512` }},
      "name": "example",
      "portMappings": [
        {
          "containerPort": 80,
          "hostPort": 80,
          "protocol": "tcp"
        }
      ],
      "secrets": [
        {
          "name": "ENV",
          "valueFrom": "{{ env `SECRETS_MANAGER_ARN` }}:ENV::"
        }
      ]
    }
  ],
  "cpu": {{ env `ECS_CPU` `256` }},
  "executionRoleArn": "{{ env `EXEC_ROLE_ARN` }}",
  "family": "example",
  "memory": {{ env `ECS_MEMORY` `512` }},
  "networkMode": "awsvpc",
  "requiresCompatibilities": ["FARGATE"],
  "taskRoleArn": "{{ env `TASK_ROLE_ARN` }}"
}

// ecs_service_def.json
{
  "deploymentConfiguration": {
    "deploymentCircuitBreaker": {
      "enable": false,
      "rollback": false
    },
    "maximumPercent": 200,
    "minimumHealthyPercent": 100
  },
  "desiredCount": {{ env `DESIRED_COUNT` }},
  "enableECSManagedTags": false,
  "enableExecuteCommand": {{ env `ENABLE_ECS_EXEC` `true` }},
  "healthCheckGracePeriodSeconds": 5,
  "launchType": "FARGATE",
  "loadBalancers": [
    {
      "containerName": "example",
      "containerPort": 80,
      "targetGroupArn": "{{ env `TARGET_GROUP_ARN` }}"
    }
  ],
  "networkConfiguration": {
    "awsvpcConfiguration": {
      "subnets": [
        "{{ env `SUBNET_1` }}",
        "{{ env `SUBNET_2` }}",
        "{{ env `SUBNET_3` }}"
      ],
      "securityGroups": [
        "{{ env `SECURITY_GROUP_ID` }}"
      ],
      "assignPublicIp": "DISABLED"
    }
  },
  "placementConstraints": [],
  "placementStrategy": [],
  "platformVersion": "1.4.0",
  "schedulingStrategy": "REPLICA",
  "serviceRegistries": []
}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;You can deploy with a YAML config like this:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;# ecspresso.yaml
cluster: example
service: example
service_definition: ecs_service_def.json
task_definition: ecs_task_def.json
timeout: 15m0s

$ ECS_CPU=256 \
  ECR_REPO_IMAGE_URL=&amp;lt;ecr-repository-url&amp;gt; \
  IMAGE_TAG=&amp;lt;ecr-container-image-tag&amp;gt; \
  ECS_MEMORY=512 \
  SECRETS_MANAGER_ARN=&amp;lt;arn-of-secrets-manager&amp;gt; \
  EXEC_ROLE_ARN=&amp;lt;arn-of-ecs-task-exec-role&amp;gt; \
  TASK_ROLE_ARN=&amp;lt;arn-of-ecs-task-role&amp;gt; \
  DESIRED_COUNT=&amp;lt;amount-of-ecs-tasks&amp;gt; \
  ENABLE_ECS_EXEC=&amp;lt;enable-ecs-task-exec&amp;gt; \
  TARGET_GROUP_ARN=&amp;lt;amount-of-target-group&amp;gt; \
  SUBNET_1=&amp;lt;subnet-id1&amp;gt; \
  SUBNET_2=&amp;lt;subnet-id2&amp;gt; \
  SUBNET_3=&amp;lt;subnet-id3&amp;gt; \
  SECURITY_GROUP_ID=&amp;lt;security-group-id&amp;gt; \
  ecspresso deploy --config ecspresso.yaml
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;You simply change the &lt;code&gt;IMAGE_TAG&lt;/code&gt; based on your code updates and run the command.&lt;br&gt;
For more advanced usage, check out &lt;a href="https://github.com/kayac/ecspresso?tab=readme-ov-file#plugins" rel="noopener noreferrer"&gt;how to pull values from tfstate&lt;/a&gt; or &lt;a href="https://github.com/kayac/ecspresso?tab=readme-ov-file#github-actions" rel="noopener noreferrer"&gt;GitHub Actions integration&lt;/a&gt;, though these are beyond the scope of this article.&lt;/p&gt;

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

&lt;p&gt;That’s it. I’ve summarized our approach to managing ECS resources. I hope this is helpful for your development workflows.&lt;/p&gt;

&lt;p&gt;Deployment practices like these can vary between teams and organizations, so if you have other approaches or improvements, I’d love to hear about them.&lt;/p&gt;

</description>
      <category>webdev</category>
      <category>aws</category>
      <category>devops</category>
      <category>infrastructureascode</category>
    </item>
    <item>
      <title>Building a Scalable Event-Driven System on AWS with DynamoDB Streams, SQS, and ECS</title>
      <dc:creator>ryo ariyama</dc:creator>
      <pubDate>Sun, 25 May 2025 15:10:49 +0000</pubDate>
      <link>https://forem.com/ryo_ariyama_b521d7133c493/building-a-scalable-event-driven-system-on-aws-with-dynamodb-streams-sqs-and-ecs-5c1e</link>
      <guid>https://forem.com/ryo_ariyama_b521d7133c493/building-a-scalable-event-driven-system-on-aws-with-dynamodb-streams-sqs-and-ecs-5c1e</guid>
      <description>&lt;h2&gt;
  
  
  Introduction
&lt;/h2&gt;

&lt;p&gt;I previously built an event-driven system on AWS and would like to introduce it here. The event-driven approach I'm referring to is an architecture that processes events triggered by DynamoDB updates and similar events.&lt;br&gt;
As a prerequisite, we're building a multi-tenant system where servers are shared but each tenant has its own DynamoDB table.&lt;br&gt;
This blog can be read in about 5-10 minutes and should be helpful for those who want to learn system architecture on AWS or understand SQS auto-scaling functionality.&lt;/p&gt;
&lt;h2&gt;
  
  
  System Architecture Overview
&lt;/h2&gt;

&lt;p&gt;Originally, we were operating the following system:&lt;br&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%2Few97n8t6apxfcuu2ug03.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%2Few97n8t6apxfcuu2ug03.png" alt="Image description" width="339" height="340"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Lambda functions were triggered based on DynamoDB streams events to perform processing. While this met the requirement of processing events as they occurred, it had the following problems:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;When event sizes are too large, Lambda may timeout before processing completes within the execution time limit. Additionally, limited CPU and memory resources could lead to OOM (Out of Memory) errors.&lt;/li&gt;
&lt;li&gt;Events could be lost in the following scenarios:

&lt;ul&gt;
&lt;li&gt;When there are too many events to process within DynamoDB Streams' record retention period (24 hours) &lt;/li&gt;
&lt;li&gt;When ECS cannot operate normally due to AWS outages or other issues&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;To address these issues, we made the following improvements:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Replaced Lambda with ECS&lt;/li&gt;
&lt;li&gt;Added SQS between DynamoDB and ECS&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;These changes provide the following benefits:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Using a platform without time constraints allows processing to continue until completion, even with large inputs&lt;/li&gt;
&lt;li&gt;Larger machine resources prevent CPU and memory-related issues that Lambda couldn't handle&lt;/li&gt;
&lt;li&gt;When event processing fails, events are stored in the queue for retry once the system returns to normal&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;The updated architecture looks like this:&lt;br&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%2F7r128yytm6wlphayt3tf.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%2F7r128yytm6wlphayt3tf.png" alt="Image description" width="529" height="340"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;The pipeline between DynamoDB and SQS uses a service called EventBridge Pipes. This eliminates the need to create Lambda functions for event sending and allows filtering and error handling (previously done in code) to be handled without code, reducing maintenance costs.&lt;br&gt;
Here's a sample Terraform code using the &lt;code&gt;aws_pipes_pipe&lt;/code&gt; resource:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;###########################
# Event
###########################
resource "aws_pipes_pipe" "event_bulk" {
  depends_on = [aws_iam_role_policy.event_pipe_role]
  name       = "event-sqs"
  role_arn   = aws_iam_role.event_pipe_role.arn
  source     = aws_dynamodb_table.events.stream_arn
  target     = var.sqs_event_queue_arn

  source_parameters {
    dynamodb_stream_parameters {
      batch_size                    = var.batch_size
      starting_position             = var.starting_position
      maximum_record_age_in_seconds = 86400
    }
    filter_criteria {
      filter {
        pattern = jsonencode({
          eventName : ["INSERT"]
          dynamodb = {
            "NewImage" : {
              "status" : { "S" : ["pending"] }
            }
          }
        })
      }
    }
  }
}

###########################
# SQS
###########################
resource "aws_sqs_queue" "event" {
  name                       = "${var.batch_container_name}-sqs"
  delay_seconds              = var.sqs_delay_seconds
  max_message_size           = var.sqs_max_message_size
  message_retention_seconds  = var.sqs_message_retention_seconds
  receive_wait_time_seconds  = var.sqs_receive_wait_time_seconds
  visibility_timeout_seconds = var.sqs_visibility_timeout_seconds
  sqs_managed_sse_enabled    = true
}

###########################
# DynamoDB
###########################
resource "aws_dynamodb_table" "events" {
  name             = "events"
  billing_mode     = "PAY_PER_REQUEST"
  hash_key         = "_global_id"
  range_key        = "_event_type"
  stream_enabled   = true
  stream_view_type = "NEW_AND_OLD_IMAGES"
}

###########################
# IAM
###########################
resource "aws_iam_role" "event_pipe_role" {
  name = "event-pipe-role"
  assume_role_policy = jsonencode({
    Version = "2012-10-17"
    Statement = [
      {
        Effect = "Allow"
        Principal = {
          Service = "pipes.amazonaws.com"
        }
        Action = "sts:AssumeRole"
        Condition = {
          StringEquals = {
            "aws:SourceAccount" = var.aws_account_id
          }
        }
      }
    ]
  })
}

resource "aws_iam_role_policy" "event_pipe_role" {
  name = "event-pipe-role-policy"
  role = aws_iam_role.event_pipe_role.id
  policy = jsonencode({
    Version = "2012-10-17"
    Statement = [
      {
        Effect = "Allow"
        Action = [
          "dynamodb:DescribeStream",
          "dynamodb:GetRecords",
          "dynamodb:GetShardIterator",
          "dynamodb:ListStreams"
        ]
        Resource = aws_dynamodb_table.events.stream_arn
      },
      {
        Effect = "Allow"
        Action = [
          "sqs:SendMessage"
        ]
        Resource = aws_sqs_queue.event.arn
      }
    ]
  })
}


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

&lt;/div&gt;



&lt;p&gt;The application code within the ECS task is simplified but looks like this.&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 logging
import os
import time

import boto3


logging.basicConfig(level=logging.INFO)
logger = logging.getLogger(__name__)

# Get the SQS queue URL from environment variables
QUEUE_URL = os.environ.get("SQS_QUEUE_URL")

# Initialize the SQS client
sqs = boto3.client("sqs", region_name=os.environ.get("AWS_REGION", "ap-northeast-1"))

def process_event(event: dict):
    """
    Business logic to process a single event.
    Replace this with your own implementation.
    """
    logger.info(f"Processing event: {event}")
    # Simulate some processing time
    time.sleep(1)
    # You can raise an exception to simulate a failure
    # raise Exception("Simulated failure")

def main():
    """
    Poll messages from the SQS queue and process them one by one.
    """
    while True:
        try:
            response = sqs.receive_message(
                QueueUrl=QUEUE_URL,
                MaxNumberOfMessages=10,
                WaitTimeSeconds=20,        # Enable long polling
                VisibilityTimeout=60       # Time for processing before making the message visible again
            )

            messages = response.get("Messages", [])
            if not messages:
                logger.info("No messages received. Waiting...")
                continue

            for message in messages:
                try:
                    # Parse the message body
                    body = json.loads(message["Body"])
                    logger.info(f"Received message: {body}")

                    # If EventBridge Pipes is used, actual DynamoDB record is nested inside
                    # Uncomment and adjust based on actual structure
                    # dynamodb_record = json.loads(body["detail"])["dynamodb"]

                    process_event(body)

                    # Delete the message only if processing succeeds
                    sqs.delete_message(
                        QueueUrl=QUEUE_URL,
                        ReceiptHandle=message["ReceiptHandle"]
                    )
                    logger.info("Message processed and deleted successfully.")

                except Exception as e:
                    logger.error(f"Error processing message: {e}", exc_info=True)
                    # Do not delete the message to allow for retry

        except Exception as e:
            logger.error(f"Error polling messages: {e}", exc_info=True)
            # Wait a bit before retrying on polling failure
            time.sleep(10)
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;There are several considerations for this system, but determining how many ECS tasks to launch is one of the main topics. The next section explains this in detail.&lt;/p&gt;

&lt;h2&gt;
  
  
  ECS Task Auto Scaling
&lt;/h2&gt;

&lt;p&gt;For cost and performance optimization, we want to dynamically adjust the number of ECS tasks based on the number of messages to process. Since ECS can auto-scale based on specific events, we configured it to auto-scale based on SQS message count metrics.&lt;/p&gt;

&lt;h3&gt;
  
  
  Step scaling
&lt;/h3&gt;

&lt;p&gt;We use Step scaling policies for auto-scaling. Step scaling scales in or out based on specified thresholds. You can configure scaling settings like:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;1~100 messages -&amp;gt; Set total task to 1&lt;/li&gt;
&lt;li&gt;101~200件 messages -&amp;gt; Set total tasks to 2&lt;/li&gt;
&lt;li&gt;201~300件 messages -&amp;gt; Set total tasks to 3&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Here's the terraform code for scale-out:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;resource "aws_appautoscaling_target" "ecs_target" {
  max_capacity       = 3 // Maximum number of tasks that can be run in the ECS service.
  min_capacity       = 0
  resource_id        = "service/${aws_ecs_cluster.batch.name}/${aws_ecs_service.batch.name}"
  scalable_dimension = "ecs:service:DesiredCount"
  service_namespace  = "ecs"
}

resource "aws_appautoscaling_policy" "scale_out" {
  name               = "ecs-batch-scale-out"
  policy_type        = "StepScaling"
  resource_id        = aws_appautoscaling_target.ecs_target.resource_id
  scalable_dimension = aws_appautoscaling_target.ecs_target.scalable_dimension
  service_namespace  = aws_appautoscaling_target.ecs_target.service_namespace

  step_scaling_policy_configuration {
    adjustment_type         = "ExactCapacity"
    cooldown                = 60
    metric_aggregation_type = "Average"

    # scale out to 1 task: 0 &amp;lt; metrics value &amp;lt;= 100
    step_adjustment {
      metric_interval_lower_bound = 0  # Applied when greater than this value (boundary not included)
      metric_interval_upper_bound = 100 # Applied when less than or equal to this value (boundary included)
      scaling_adjustment          = 1
    }

    # scale out to 2 tasks: 100 &amp;lt; metrics value &amp;lt;= 200
    step_adjustment {
      metric_interval_lower_bound = 101
      metric_interval_upper_bound = 200
      scaling_adjustment          = 2
    }

    # scale out to 3 tasks: 200 &amp;lt; metrics value
    step_adjustment {
      metric_interval_lower_bound = 201
      scaling_adjustment          = 3
    }
  }
}

resource "aws_cloudwatch_metric_alarm" "message_alarm_high" {
  alarm_name = "ecs_batch_FiveSecondsApproximateNumberOfMessages_high"

  comparison_operator = "GreaterThanThreshold"
  evaluation_periods  = 1
  metric_name         = "ApproximateNumberOfMessagesVisible"
  namespace           = "AWS/SQS"
  period              = 10
  statistic           = "Maximum"

  threshold = 1

  dimensions = {
    QueueName = var.sqs_queue_name
  }

  alarm_actions = [aws_appautoscaling_policy.scale_out.arn]
}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This code works as follows&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Monitors the &lt;code&gt;ApproximateNumberOfMessagesVisible&lt;/code&gt; metric, which represents the number of pending messages, and sends alerts to the autoscaling policy when thresholds are exceeded&lt;/li&gt;
&lt;li&gt;The aws_appautoscaling_policy's step_adjustment conditions determine how many ECS tasks to set as the total count. Pay attention to the calculation formulas for lower_bound and upper_bound (inclusive vs exclusive)&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Scale-in can be configured similarly.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;resource "aws_appautoscaling_policy" "scale_in" {
  name               = "ecs-batch-${aws_ecs_service.batch.name}-scale-in"
  policy_type        = "StepScaling"
  resource_id        = aws_appautoscaling_target.ecs_target.resource_id
  scalable_dimension = aws_appautoscaling_target.ecs_target.scalable_dimension
  service_namespace  = aws_appautoscaling_target.ecs_target.service_namespace

  step_scaling_policy_configuration {
    adjustment_type         = "ExactCapacity"
    cooldown               = 1200
    metric_aggregation_type = "Average"

    # 200 &amp;lt; message &amp;lt;= 300: -1 task
    step_adjustment {
      metric_interval_lower_bound = 200
      metric_interval_upper_bound = 250
      scaling_adjustment         = 2
    }

    # 100 &amp;lt; message &amp;lt;= 200: -2 task
    step_adjustment {
      metric_interval_lower_bound = 100
      metric_interval_upper_bound = 200
      scaling_adjustment         = 1
    }
  }
}

resource "aws_appautoscaling_policy" "scale_in_to_zero" {
  name               = "ecs-batch-${aws_ecs_service.batch.name}-scale-in-to-zero"
  policy_type        = "StepScaling"
  resource_id        = aws_appautoscaling_target.ecs_target.resource_id
  scalable_dimension = aws_appautoscaling_target.ecs_target.scalable_dimension
  service_namespace  = aws_appautoscaling_target.ecs_target.service_namespace

  step_scaling_policy_configuration {
    adjustment_type         = "ExactCapacity"
    cooldown               = 1200
    metric_aggregation_type = "Average"

    step_adjustment {
      metric_interval_lower_bound = null
      metric_interval_upper_bound = null
      scaling_adjustment         = 0
    }
  }
}

resource "aws_cloudwatch_metric_alarm" "message_alarm_low" {
  alarm_name          = "ecs_batch_messages_low"
  comparison_operator = "LessThanOrEqualToThreshold"
  evaluation_periods  = 3
  metric_name         = "ApproximateNumberOfMessagesVisible"
  namespace           = "AWS/SQS"
  period              = 60
  statistic           = "Average"
  treat_missing_data  = "notBreaching"
  threshold           = 100

  dimensions = {
    QueueName = var.sqs_queue_name
  }

  alarm_actions = [aws_appautoscaling_policy.scale_in.arn]
}

resource "aws_cloudwatch_metric_alarm" "messages_completely_empty" {
  alarm_name          = "ecs_batch_completely_empty"
  comparison_operator = "GreaterThanOrEqualToThreshold"
  evaluation_periods  = 3
  threshold           = 1
  treat_missing_data  = "notBreaching"

  metric_query {
    id          = "e1"
    expression  = "IF(m1 &amp;lt;= 0 AND m2 &amp;lt;= 0, 1, 0)"
    label       = "Empty Messages Condition"
    return_data = "true"
  }

  metric_query {
    id = "m1"
    metric {
      namespace   = "AWS/SQS"
      metric_name = "ApproximateNumberOfMessagesVisible"
      dimensions = {
        QueueName = var.sqs_queue_name
      }
      stat   = "Average"
      period = 60
    }
  }

  metric_query {
    id = "m2"
    metric {
      namespace   = "AWS/SQS"
      metric_name = "ApproximateNumberOfMessagesNotVisible"
      dimensions = {
        QueueName = var.sqs_queue_name
      }
      stat   = "Average"
      period = 60
    }
  }
  alarm_actions = [aws_appautoscaling_policy.scale_in_to_zero.arn]
}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This basically reverses the scale-out settings to gradually scale in based on message count. When messages reach 0, we want to scale tasks to 0, but we need to combine this with the &lt;code&gt;ApproximateNumberOfMessagesNotVisible&lt;/code&gt; metric (representing currently processing messages) to ensure instances don't scale to 0 when messages are still being processed.&lt;/p&gt;

&lt;h2&gt;
  
  
  System monitoring
&lt;/h2&gt;

&lt;p&gt;Next, let's consider operational monitoring. For this system (and generally), monitoring items should be considered from the perspective of whether the system is operating according to requirements. For this system, the following items come to mind:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;p&gt;Tasks not starting&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Pending message count ≥ 1 and no ECS tasks running&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;Error rate monitoring&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;ApproximateNumberOfMessagesNotVisible backlog in SQS&lt;/li&gt;
&lt;li&gt;Dead Letter Queue (DLQ) transfer rate&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;Resource utilization&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;ECS task CPU/Memory usage&lt;/li&gt;
&lt;li&gt;Task OOM detection&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;Due to space constraints, I can't cover everything in this blog, but as a sample, here's how we implemented the first item using CloudWatch composite metrics:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;resource "aws_cloudwatch_metric_alarm" "sqs_has_messages" {
  alarm_name          = "event-ecs-sqs-has-messages"
  comparison_operator = "GreaterThanOrEqualToThreshold"
  evaluation_periods  = "1"
  metric_name         = "ApproximateNumberOfMessagesVisible"
  namespace           = "AWS/SQS"
  period              = "60"
  statistic           = "Average"
  threshold           = "1"
  alarm_description   = "This alarm monitors if SQS queue has messages waiting to be processed"
  dimensions = {
    QueueName = var.aws_sqs_queue_name
  }
}

resource "aws_cloudwatch_metric_alarm" "ecs_no_tasks" {
  alarm_name          = "event-ecs-no-tasks"
  comparison_operator = "LessThanOrEqualToThreshold"
  evaluation_periods  = "1"
  metric_name         = "RunningTaskCount"
  namespace           = "AWS/ECS"
  period              = "60"
  statistic           = "Average"
  threshold           = "0"
  alarm_description   = "This alarm monitors if ECS has no running tasks"
  dimensions = {
    ClusterName = aws_ecs_cluster.batch.name
    ServiceName = aws_ecs_service.batch.name
  }
}

resource "aws_cloudwatch_composite_alarm" "sqs_messages_but_no_ecs" {
  alarm_name        = "sqs-messages-but-no-ecs"
  alarm_description = "Alarm when SQS has messages but no ECS tasks are running"

  alarm_rule = "ALARM(${aws_cloudwatch_metric_alarm.sqs_has_messages.alarm_name}) AND ALARM(${aws_cloudwatch_metric_alarm.ecs_no_tasks.alarm_name})"

  alarm_actions = [var.monitoring_sqs_arn]
  ok_actions    = [var.monitoring_sqs_arn]
}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Alternative Approaches
&lt;/h2&gt;

&lt;p&gt;While we've introduced the complete system, there are still cases where problems can occur. This happens when one tenant has significantly larger data compared to other tenants, causing that tenant's data processing to take longer and affect other tenants. This is known as the "noisy neighbors" problem, which is well-known in multi-tenant systems.&lt;br&gt;
To solve this problem, you need to provide separate queues and servers for each tenant.&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%2F2vi7wbkoaieuef6vs3u4.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%2F2vi7wbkoaieuef6vs3u4.png" alt="Image description" width="569" height="360"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;However, compared to tenant-shared systems, this approach requires more servers and increases costs in various ways. Deciding which system approach to take is difficult, but it's probably best to start with a simple implementation and make comprehensive decisions while monitoring performance. If you have good decision-making methods for this or any other alternative ways to resolve this multi-tenant problem, I'd appreciate comments sharing them.&lt;/p&gt;

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

&lt;p&gt;That's it. We were able to create a simple yet scalable event-driven system using AWS. I hope this article helps with your development efforts.&lt;/p&gt;

</description>
      <category>webdev</category>
      <category>aws</category>
      <category>eventdriven</category>
      <category>multitenant</category>
    </item>
    <item>
      <title>Introduction to Burp Suite: Basic Usage of Web Application Vulnerability Assessment Tool</title>
      <dc:creator>ryo ariyama</dc:creator>
      <pubDate>Sun, 18 May 2025 16:20:35 +0000</pubDate>
      <link>https://forem.com/ryo_ariyama_b521d7133c493/introduction-to-burp-suite-basic-usage-of-web-application-vulnerability-assessment-tool-4oen</link>
      <guid>https://forem.com/ryo_ariyama_b521d7133c493/introduction-to-burp-suite-basic-usage-of-web-application-vulnerability-assessment-tool-4oen</guid>
      <description>&lt;h2&gt;
  
  
  Introduction
&lt;/h2&gt;

&lt;p&gt;[Reading time: approximately 10 minutes]&lt;br&gt;
This article explains the basic usage of "Burp Suite," a web application vulnerability assessment tool. It covers everything from installation to basic operations, simple attack methods, and the use of extensions, all explained step-by-step with actual screenshots. Recommended content for those interested in web security, especially beginners.&lt;/p&gt;

&lt;h2&gt;
  
  
  What is Burp Suite?
&lt;/h2&gt;

&lt;p&gt;Burp Suite is a tool for security testing of web applications. &lt;br&gt;
It works by modifying request parameters to input values that might exploit application vulnerabilities, and then checking the responses to determine if vulnerabilities exist. &lt;br&gt;
In this article, I'll be using the free version, Community Edition, Version 2024.9.5.&lt;/p&gt;

&lt;h2&gt;
  
  
  Basic Operations
&lt;/h2&gt;

&lt;ol&gt;
&lt;li&gt;&lt;p&gt;First, download the Community Edition from the link below.&lt;br&gt;
&lt;a href="https://portswigger.net/burp/communitydownload" rel="noopener noreferrer"&gt;https://portswigger.net/burp/communitydownload&lt;/a&gt;&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;When launched, the following screen appears. There's nothing in particular to set up, so press &lt;code&gt;Next -&amp;gt; Start Burp&lt;/code&gt; to proceed.&lt;br&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%2Fayodal1m1tfau0xhpgds.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%2Fayodal1m1tfau0xhpgds.png" alt="First" width="800" height="551"&gt;&lt;/a&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%2F6slkzisxkirzm74cjx2c.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%2F6slkzisxkirzm74cjx2c.png" alt="Second" width="800" height="561"&gt;&lt;/a&gt;&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Open the Proxy tab and click &lt;code&gt;Intercept on&lt;/code&gt;. This allows you to intercept requests, or in other words, temporarily stop requests so you can inject parameters.&lt;br&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%2F7pq9iucrgtusbz14b859.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%2F7pq9iucrgtusbz14b859.png" alt="main page" width="800" height="282"&gt;&lt;/a&gt;&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Click Open browser to launch a web browser. Let's try searching on Google.&lt;br&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%2F9706k4qrhkfkffbscaoa.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%2F9706k4qrhkfkffbscaoa.png" alt="google search" width="800" height="142"&gt;&lt;/a&gt;&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;The request is intercepted, but you can proceed by pressing Forward.&lt;br&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%2Fl4kjg2dijlnq12u2b8d4.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%2Fl4kjg2dijlnq12u2b8d4.png" alt="Forward" width="800" height="321"&gt;&lt;/a&gt;&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Another request is intercepted, but if you want to use custom input values, you can change the Pretty values.&lt;br&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%2F9h5wthsh1ghg8mxmnw42.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%2F9h5wthsh1ghg8mxmnw42.png" alt="Response body" width="800" height="397"&gt;&lt;/a&gt;&lt;/p&gt;&lt;/li&gt;
&lt;/ol&gt;

&lt;h2&gt;
  
  
  Simple attack
&lt;/h2&gt;

&lt;p&gt;Now, let's try changing parameter values to perform an attack.  &lt;strong&gt;Please note that you should never attack real web applications! Always use your own development environment or test pages where no one will be affected.&lt;/strong&gt; &lt;br&gt;
Burp Suite provides the following test page for practice, so I'll demonstrate using this page.&lt;br&gt;
&lt;a href="https://portswigger.net/web-security/logic-flaws/examples/lab-logic-flaws-excessive-trust-in-client-side-controls" rel="noopener noreferrer"&gt;https://portswigger.net/web-security/logic-flaws/examples/lab-logic-flaws-excessive-trust-in-client-side-controls&lt;/a&gt;&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;&lt;p&gt;Press the LOGIN button in the top right to go to the login page. Enter some arbitrary values and press Login.&lt;br&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%2F838hudblvg1mthhq44n7.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%2F838hudblvg1mthhq44n7.png" alt="TopPage" width="800" height="186"&gt;&lt;/a&gt;&lt;br&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%2Fwtwj2m8an6diguw9gp6g.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%2Fwtwj2m8an6diguw9gp6g.png" alt="Login" width="800" height="444"&gt;&lt;/a&gt;&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Click on the POST request to see the request body, where you can enter arbitrary values for EmailAddress and Password to send the request.&lt;br&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%2Fe5cu6ol8gneqcyp85eg0.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%2Fe5cu6ol8gneqcyp85eg0.png" alt="Email" width="800" height="519"&gt;&lt;/a&gt;&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;While this method works, you can also modify requests using the Intruder feature. Right-click on the POST request and select Send to intruder.&lt;br&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%2F248tf75top28gejl0yly.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%2F248tf75top28gejl0yly.png" alt="Image description" width="800" height="579"&gt;&lt;/a&gt;&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;You can see the API request body, surround the parts you want to change with &lt;code&gt;§&lt;/code&gt;, and enter the values you want in the Payload configuration. Then press Start attack to attack with the entered values.&lt;br&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%2F49oyjf9j1kb5e1iy43de.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%2F49oyjf9j1kb5e1iy43de.png" alt="Image description" width="800" height="519"&gt;&lt;/a&gt;&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Once the attack is performed, the payloads used and the responses are displayed.&lt;br&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%2F3egv1mvvu0koerkvpm4q.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%2F3egv1mvvu0koerkvpm4q.png" alt="Image description" width="800" height="519"&gt;&lt;/a&gt;&lt;/p&gt;&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;So far, I've introduced the basic usage of Burp Suite. Next, I'll introduce attack methods that combine other tools.&lt;/p&gt;

&lt;h2&gt;
  
  
  Import attack parameter values
&lt;/h2&gt;

&lt;p&gt;Even if you understand how to attack, it might be hard to imagine what parameters to use. Additionally, preparing parameters one by one is frankly time-consuming. If you search on GitHub, you'll find several repositories that compile typical input patterns for each vulnerability, which might be helpful to reference.&lt;br&gt;
You can import these files to perform attacks, and I'll explain how below. &lt;br&gt;
I used the following repository as an example:&lt;br&gt;
&lt;a href="https://github.com/fuzzdb-project/fuzzdb/tree/master" rel="noopener noreferrer"&gt;https://github.com/fuzzdb-project/fuzzdb/tree/master&lt;/a&gt;&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;First, clone the repository to your local environment.&lt;/li&gt;
&lt;li&gt;Go to the Intruder tab and select &lt;code&gt;Runtime file&lt;/code&gt; as the Payload type. Then select a file from &lt;code&gt;./attack&lt;/code&gt; under the &lt;code&gt;fuzzdb-project&lt;/code&gt; for Select file and click Start attack.&lt;/li&gt;
&lt;li&gt;This allows you to attack using the input values in the file.
&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%2F7lxg3aor0m87j6ikcqxu.png" alt="Image description" width="800" height="519"&gt;
&lt;/li&gt;
&lt;/ol&gt;

&lt;h2&gt;
  
  
  OpenAPI extension
&lt;/h2&gt;

&lt;p&gt;Next, I'll introduce a method to create parameters based on OpenAPI specifications. Burp Suite has various extensions, and this time we'll use one called OpenAPI Parser.&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;&lt;p&gt;First, install OpenAPI Parser from the BAppstore.&lt;br&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%2Fyn4q3k3h6tdryp8nyax0.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%2Fyn4q3k3h6tdryp8nyax0.png" alt="Image description" width="800" height="519"&gt;&lt;/a&gt;&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Select the OpenAPI specification from the Browser button in the top right and click &lt;code&gt;Load&lt;/code&gt;. This will display requests for each API path. You can select a path to transition to the Intruder screen and launch attacks.&lt;br&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%2F2b0suzx3cj3sylhdwj11.jpg" 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%2F2b0suzx3cj3sylhdwj11.jpg" alt="Image description" width="800" height="519"&gt;&lt;/a&gt;&lt;/p&gt;&lt;/li&gt;
&lt;/ol&gt;

&lt;h2&gt;
  
  
  Impressions after use
&lt;/h2&gt;

&lt;p&gt;That's how to use Burp Suite. I think the free version of the tool should be sufficient for simple diagnostics. However, one issue I felt while using it is that it would be nice to have a feature that automatically determines whether an API is vulnerable for each attack. Especially when preparing a large number of attack patterns, it's challenging to go through all the responses, so an automatic detection feature would be very helpful.&lt;br&gt;
It seems possible with Pro version extensions, but apparently not with the free version. Similar tools to Burp Suite include OWASP ZAP, but I haven't tried it, so if anyone knows if this is possible with other tools, I'd appreciate your input.&lt;/p&gt;

</description>
      <category>webdev</category>
      <category>security</category>
      <category>programming</category>
    </item>
  </channel>
</rss>
