KINTOテクノロゞヌズのブログ - TECH PLAY

TECH PLAY

KINTOテクノロゞヌズ

KINTOテクノロゞヌズ の技術ブログ

å…š1123ä»¶

Introduction Hello, I'm Angela Wang from the Group Core Systems Division at KINTO Technologies. Recently, I had the opportunity to explore Microsoft Power Automate and was impressed by how easily systems can be built using low-code. I'd like to introduce one solution example. Do you ever face these challenges in your daily work? Task instructions scattered across chat messages Progress reporting is person-dependent and difficult to track Unable to get an overview of the situation at a glance These challenges can be solved by combining Microsoft Teams with Power Platform (Power Automate/Power Apps/Power BI) to create a task management system that anyone can easily automate and visualize . 1. Architecture Overview Here is the basic concept of this solution: Achieve end-to-end task management from creation to progress tracking and report analysis, all centered around Teams. ![Architecture](/assets/blog/authors/angela.wang/architecture.png =600x688) Components Component Role Description Microsoft Teams Entry point & notification hub Execute task creation, updates, and notifications within channels Power Apps Task management app Register and update tasks with an intuitive UI Power Automate Automation workflow Automate notifications, reminders, and report generation Power BI Visualization & analysis Visualize task completion rates and delay trends in real-time SharePoint List Data storage Store task data 2. Feature Design Task Creation and Assignment (Power Apps) Create a form in Power Apps to input the following information: Task name Assignee (MS user) Status Priority Due date Created by Completion date Details ![feature1](/assets/blog/authors/angela.wang/feature1.png =431x681) After creation, the data is recorded in SharePoint List. Screens for viewing and editing the task list and task details are also created. ![feature2](/assets/blog/authors/angela.wang/feature2.png =431x681) ![feature3](/assets/blog/authors/angela.wang/feature3.png =431x681) Process Automation (Power Automate) Here are typical automation scenarios: Scenario Processing Implementation Comments Task creation Notify assignee via Teams Trigger on "When an item is created" and send to assignee via Teams Implemented Status change Update log Update event to SharePoint Example Past due Remind assignee and supervisor Conditional branching + Teams message Example Weekly report Send task summary to Teams Scheduled trigger Example Data Visualization (Power BI) In Power BI, create reports such as: Task status and progress rate Due date distribution Task count by assignee (*) *: Retrieving assignee information requires editing in Power BI Desktop, but this was not implemented this time. 3. Integrated Experience Through Teams Integration By centering operations around Teams, the following experience can be achieved: Display Power Apps as a tab within Teams channels Automatic notifications via Power Automate Bot Direct viewing of Power BI dashboards In other words, Users can complete task management without ever leaving Teams. This is the true strength of Microsoft 365. *Adding Power Apps and Power BI dashboards as tabs to Teams channels requires permissions, but this was not implemented this time. 4. Data Structure (SharePoint List) Column Name Data Type Description ID Auto number Unique task ID Title Text Task name Description Multi-line text Details Assignee User Assignee Status Choice Status: Not Started / In Progress / Completed / Delayed Priority Choice Priority: High / Medium / Low DueDate Date Due date CreatedBy User Created by CompletedDate Date Completion date 5. Implementation Steps Prepare SharePoint List (Task List) Create "TaskList" in SharePoint. ![list](/assets/blog/authors/angela.wang/list.png =760x440) Build Power Apps (Task Management App) Use the convenient "Start with an app template" feature to quickly build the app. ![powerapps1](/assets/blog/authors/angela.wang/powerapps1.png =760x238) ![powerapps2](/assets/blog/authors/angela.wang/powerapps2.png =736x318) Build Power Automate (Task Management Flow) Implement notifications to be sent to the assignee's Teams when a new task is created. ![powerplatform](/assets/blog/authors/angela.wang/powerplatform.png =760x600) Create Power BI (Task Management Dashboard) Create visuals for "Task Progress" and "Due Date Distribution" on the dashboard. ![powerbi](/assets/blog/authors/angela.wang/powerbi.png =760x328) Teams Integration Settings Add Power Apps and Power BI tabs (requires permissions, but this was not implemented this time). 6. Conclusion By leveraging Power Platform, you can create a task management solution that anyone can build, is immediately usable, and seamlessly fits into your team . Please consider implementing this in your work.
This article is the Day 5 entry of the KINTO Technologies Advent Calendar 2025🎅🎄 Introduction The KINTO Development Division Frontend Team handles frontend development using React/Next.js. We use Jira for task management and have adopted a ticket-based development flow. As the team has grown, we felt that standardizing development flow conventions and reducing cognitive overhead — such as branch naming conventions, commit message formats, and PR template selection — had become a challenge. This article introduces how we combined Claude Code with Atlassian MCP to automate these things you don't have to think about, creating an environment where developers can focus on solving real problems. Technology Stack Claude Code : AI-driven development assistant Atlassian MCP : API integration with Jira/Confluence (automatic ticket information retrieval) GitHub CLI (gh) : PR operation automation CLAUDE.md : Project-specific rule definitions Background and Challenge: The Cognitive Load Problem in Development Flows What developers should really focus on is writing code. However, in actual development, cognitive resources were being consumed by non-essential tasks like these: "What was that ticket number again? Let me open Jira to check..." "What should the branch name be? What goes after feature/JIRAKEY-1234/ ?" "Should I branch from develop? Or from the project branch?" "Which emoji was it for the commit message, :sparkles: or :wrench: ?" "Do I add :m: to the PR title or not?" "Which PR template should I use? for_dev.md ? The default one?" These may seem trivial, but they are decisions that occur multiple times a day . When accumulated, they significantly drain developers' focus. Solution: Achieving a "No-Thinking" Development Flow "Just provide the ticket number, and everything else is automated." To achieve this, we combined Claude Code with Atlassian MCP. What the Developer Does Developer: "Create a branch for JIRAKEY-1234" What Claude Code Does (Automatically) ✅ Retrieves ticket information via Jira API ✅ Checks project affiliation through epic determination ✅ Automatically generates the appropriate branch name ( feature/JIRAKEY-1234/update_claude_docs ) ✅ Automatically determines the appropriate base branch (develop or project branch) ✅ Creates the branch What the Developer Does Developer: "Commit" What Claude Code Does (Automatically) ✅ Analyzes the changes ✅ Selects the appropriate emoji shortcode ( :pencil: , :bug: , :sparkles: , etc.) ✅ Executes the commit What the Developer Does Developer: "Create a PR" What Claude Code Does (Automatically) ✅ Extracts the ticket number from the branch name ✅ Retrieves the ticket title via Jira API ✅ Automatically generates the PR title ( JIRAKEY-1234: Standardizing Claude Code Operation Rules ) ✅ Automatically determines the base branch (develop or project branch) ✅ Automatically selects the appropriate PR template ✅ Executes PR creation The developer only needs to give three instructions. Branch creation, commit execution, and PR creation are all handled automatically by Claude Code. Implementation: CLAUDE.md — A "Rulebook for AI to Read" All automation is achieved through rules written in a document called CLAUDE.md . ### Branch Naming Conventions Project base branch: `feature/project-name` - Example: `feature/simulation` - Base branch: `develop` Feature branch (under project): `feature/JIRAKEY-ticket-number/description` - Example: `feature/JIRAKEY-1234/add_simulation_list` - Base branch: `feature/project-name` Regular feature branch (outside project): `feature/JIRAKEY-ticket-number/description` - Example: `feature/JIRAKEY-1234/fix_bug` - Base branch: `develop` For parent-child tickets: - Parent branch: `feature/JIRAKEY-parent-ticket-number/develop` - Child branch: `feature/JIRAKEY-parent-ticket-number/JIRAKEY-child-ticket-number/description` Relationship Between Epics and Project Base Branches - Epic determination: If `parent.fields.issuetype.name` of a Jira ticket is "Epic", that ticket belongs to a project - Important: When creating branches for tasks under an epic, always confirm the project base branch name with the user ### Commit Message Format - Required format: `:emoji: JIRAKEY-ticket-number: subject` - Refer to `.commit_template` for emoji shortcodes - Commit examples: :bug: JIRAKEY-1234: Fix crash during login :sparkles: JIRAKEY-2345: Add user profile image upload feature :robot: JIRAKEY-3456: Add tests for login component ### PR Creation Rules - Title format: - Project base branch → develop: `:m: JIRAKEY-ticket-number: ticket-title` - Parent branch → develop: `:m: JIRAKEY-parent-ticket-number: ticket-title` - Regular feature branch → develop: `JIRAKEY-ticket-number: ticket-title` - Other PRs: `JIRAKEY-ticket-number: ticket-title` - Template usage: - PRs with `:m:`: `.github/for_dev_template.md` - PRs without `:m:`: `.github/pull_request_template.md` That's it. No code changes whatsoever. Actual Operation Flow sequenceDiagram actor Developer participant Claude Code participant Jira API participant git participant gh Note over Developer,gh: Branch Creation Flow Developer->>Claude Code: "Create a branch for JIRAKEY-1234" Claude Code->>Jira API: Retrieve ticket information Jira API-->>Claude Code: Title, epic information, etc. Claude Code->>Claude Code: Generate branch name<br/>(feature/JIRAKEY-1234/update_claude_docs) Claude Code->>git: Execute git checkout -b Claude Code-->>Developer: Branch creation complete Note over Developer,gh: Commit Flow Developer->>Claude Code: After code changes, "Commit" Claude Code->>Claude Code: Analyze changes Claude Code->>Claude Code: Auto-generate message in :pencil: format Claude Code->>git: Execute git commit Claude Code-->>Developer: Commit complete Note over Developer,gh: PR Creation Flow Developer->>Claude Code: "Create a PR" Claude Code->>Claude Code: Determine base branch (develop) Claude Code->>Claude Code: Generate PR title<br/>(JIRAKEY-1234: ticket-title) Claude Code->>Claude Code: Select template<br/>(.github/pull_request_template.md) Claude Code->>gh: Execute gh pr create gh-->>Claude Code: PR URL Claude Code-->>Developer: PR creation complete (with URL) Impact: The Value Gained from "No Thinking" Dramatic Reduction in Cognitive Load ✅ Creating branch names ✅ Checking the base branch ✅ Remembering commit message formats ✅ Copying and pasting PR titles from tickets ✅ Selecting PR templates → Everything is completed by "just providing the ticket number" Ensuring Consistency Branch names, commit messages, and PR titles are 100% compliant with project rules The hassle of reviewers pointing out "this doesn't follow the naming convention" has disappeared Reduced Onboarding Time New team members don’t need to worry about memorizing the branch naming rules." Instead of "look at CLAUDE.md", it's just "ask Claude Code" Improved Development Speed The back-and-forth of "opening Jira and copying the ticket title" has disappeared Fewer decisions make it easier to maintain flow state Future Outlook Context Window Optimization Through Sub-agent Utilization In the current implementation, there is an issue where using Atlassian MCP consumes the context window. As a solution, we are considering leveraging sub-agents for hierarchical task distribution. What Are Sub-agents? Claude Code sub-agents are AI assistants specialized for specific tasks, with independent context windows . This enables: ✅ Not polluting the main agent's context ✅ Efficient processing of specialized tasks ✅ Separating bulk information retrieval and processing Implementation Plan: Three Specialized Sub-agents 1. Jira Information Retrieval Sub-agent ( jira-researcher ) **Role**: - Retrieve ticket information via Atlassian MCP - Extract only necessary information (ticket number, title, epic, status) 2. Branch Strategy Determination Sub-agent ( branch-strategist ) **Role**: - Generate branch names from ticket information - Determine parent-child ticket relationships - Decide base branch from branching patterns 3. PR Creation Sub-agent ( pr-creator ) **Role**: - Execute PR creation branching logic - Select appropriate templates Conclusion: AI Assistants Enable "No-Thinking Development" "Just provide the ticket number, and branch creation, commits, and PR creation are all completed." This was achieved with just CLAUDE.md — a "document that AI can read" — and MCP integration. Zero code changes. Zero impact on existing systems. The key point is that we clearly identified what developers don't have to think about and delegated it to AI . By evolving AI assistants from "code completion tools" to "partners for the entire development flow", we can realize a world where developers can focus solely on solving real problems .
Introduction I'm tetsu from the Platform Group. In this article, I'll summarize the operations behind our company-wide joint study sessions at KTC. The study sessions are held monthly, with over 50 participants attending each time. I've compiled the tips and tricks for keeping them going, so this is a must-read for those who want to hold study sessions at their company or are thinking about starting ones! Overview of the Study Sessions Frequency: Once a month Format: Hybrid (in-person and Zoom) Scale: Approx. 50–100 people What the session is like: Speakers come from various divisions across the company Engineers, designers, directors, HR members, and more Each person presents for 10–15 minutes; for example, with 3 speakers, it takes an hour in total A meetup session is held after the study session where attendees can talk with the speakers Anyone who wants to present can do so, regardless of the presentation topic Examples of past presentation topics Sharing details about recently released projects Promoting tools developed internally Sessions focused on specific topics (on figma study sessions, creative generative AI, security, QA, etc.) Timetable 17:05–17:10 Opening 17:10–17:25 Presentation 1 17:25–17:40 Presentation 2 17:40–17:55 Presentation 3 17:55–18:00 Closing 18:00–19:00 Meetup session (optional) Background of the Study Sessions Before these study sessions began, each group within KTC was already holding their own study sessions and orientations. However, there was a situation where know-how was not easily shared between teams in different divisions or those with little work-related interaction. That was a concern, which some people felt. To this end, the joint study sessions were launched with an aim of creating a place where teams and divisions with little interaction can share their knowledge with each other and strengthen collaboration between them. Continuing Study Sessions Is Surprisingly Difficult While many companies share backgrounds similar to the above-mentioned one, many internal study sessions start up but eventually fizzle out. We've managed to continue for over 15 sessions so far, but it certainly hasn't been smooth sailing. In general, the barriers to continuity, which we've actually faced, include: Operations becoming dependent on specific individuals : When a heavy workload is shouldered on specific individuals, and if they transfer or leave their organizations, business operation cannot be run smoothly, which may result in the increase in operational costs. Difficulty in holding hybrid events : Meeting the demands for both online and on-site participants is harder than you expected Consistency of participants : At the launch of study sessions, things are lively, but gradually, only specific participants came to regularly engage in the sessions To overcome these barriers, we've put the following four tips into action. Tips and Tricks for Continuity Tip 1: Standardizing Operations and Implementing a Rotation System To prevent specific individuals attending study sessions from bearing an excessive burden of the operation for the sessions, we've implemented the following: Identify all necessary tasks for the operation and manage them as Jira tickets Assign the tickets to the operation team members on a monthly rotation The first point is to identify all necessary tasks and managing them as Jira tickets. We use Jira's feature to automatically create its tickets for running the joint study sessions. This allows all members to understand the tasks required for operations. The second point is to assign the ticket to the operation team members on a monthly rotation. We divide the study session tasks in the following three categories to rotate them: facilitation, venue preparation, and coordination. The task rotation helps all operations team members understand every task, which prevents dependence on specific individuals. When running study sessions, it's not uncommon for the operators to become exhausted, which causes the sessions to fall through. For joint study sessions in KTC, we've standardized the operation to reduce the workload. *JIRA tickets are issued and organized as shown below. Tip 2: Creating an Environment Where All Employees Can Easily Attend Continuing to hold study sessions, we may face issues of consistent participants or lower engagement of online attendees. We need to address these issues because study session can be more valuable when it offers opportunities not only to listen but also to discuss and share opinions among attendees about what they've learned. To prevent the issues, we've implemented the following measures: Schedule the study session on all employees' Outlook calendars Hold the session in a communication space shared on a company-wide basis Prepare a Slack channel for casual chat and opinion exchange that allows online participants to easily join in and the operations team to actively participate in the chat Avoid holding sessions during busy times like the beginning of the month The goal of the study session isn't just to attend but to increase business knowledge and technical skills through participation. That said, you can't get started without attending the session, so it is important to make a framework for casual participation. Slack for casual chat (See below) Tip 3: Responding to Survey Requests This may seem obvious, but we make sure to respond to requests from surveys regarding the study sessions. For example, we received the following feedback: "I'd like to know about case studies on how business specifications are decided" "The venue is really quiet when presentation is not provided, so I thought playing some background music might be nice" "I'd like some salty snacks at the meetup session" We receive various request. Some related to study session topics, some about improving the venue atmosphere, and so on. We read through each one, consider the background, and try to respond to it appropriately. " I'd like to know about case studies on how business specifications are determined " -> Project managers, product managers, and producers handle these business requirements, so it might be good to hear the case studies from these people with multiple perspectives! Additionally, this request probably came from an engineer, so it would be great to create a good point of connection between engineers and the people making decisions on business specifications. "The venue is really quiet when presentation is not provided, so I thought playing some background music might be nice" -> Indeed, we understand silence is uncomfortable moment... We could play the background music and have the facilitator fill the time between presentations so there's no awkward silence! "I'd like some salty snacks at the networking session" -> We may have a narrow preference of the snack flavors. Let's add some salty options! Tip 4: Continuous Improvement Cycle The tips above were established through retrospectives held among the operations team members after each study session. At KTC's joint study sessions, we use the KPT method for retrospectives as shown in the table below, continuing to keep what's good and improve problems to prevent us from the recurrence. (Improvements from this process get reflected in the operations JIRA tickets.) Category Keep (Good/Worked well/Want to continue) Problem (Issue/Challenge/Trouble) Try (Intention for improvements next time) Discussion on the day Overall This was the first time we held a session on a specific topic. We could do sessions in such style a few more times in the future Attendance might be low on Tuesdays at 5 P.M. Change the event time ... Operations before the event ... ... ... ... Preparation / clean-up on the event day ... ... ... ... Presentations ... ... ... ... Meetup session ... ... ... ... Survey ... ... ... ... Positive Effects of Continuing the Study Group for Over a Year Here are some positive effects we've noticed from continuing the study sessions. It has become a place for exchanging opinions and collaborating with people from different departments Particularly from people who work across the company, such as those in divisions engaging in shared internal tools or security-related projects, we especially receive comments, like "I'm happy to be able to exchange opinions" Requests for presentations from employees and collaborative study sessions with other departments have increased, so we no longer run out of topics We receive various requests, such as "I want to practice for an external presentation" or "I introduced a new development method to drive a project forward and want to talk about it" We, operation members, don't give a presentation, seeking speakers as volunteers to run the sessions in a manner of that we can respond to various requests, so we haven't run out of presentation topics Conclusion Finally, our study session participants are taking time out of their busy schedules to attend the sessions even though they could spend time in doing other things. To meet their expectations, we strive to provide high-quality study sessions. Of course, you can't run things perfectly from the start. The bottom line is to take that first step, and then, listen to participants' demands and continuously make improvements through retrospectives—that's what we believe matters. Thank you for reading to the end!
この蚘事は KINTOテクノロゞヌズ Advent Calendar 2025 の4日目の蚘事です🎅🎄 はじめに Platformグルヌプのtetsuです。 本蚘事では、KTCで実斜しおいる党瀟暪断の合同勉匷䌚の運営に関する内容をたずめたす。 この合同勉匷䌚は毎月開催され、毎回50人以䞊の方が参加しおいたす。 継続するための工倫やコツをたずめたしたので、「瀟内で勉匷䌚を開催したい方」「これから始めたい方」必芋です 勉匷䌚の抂芁 開催頻床月1回 圢匏ハむブリッドオフラむン・Zoom 芏暡玄50〜100名 特城 党瀟の様々な郚眲から登壇者が集たる ゚ンゞニア、デザむナヌ、ディレクタヌ、人事メンバヌなど 1人あたり10〜15分の発衚 × 3人で蚈1時間 勉匷䌚埌に登壇者ず話せる亀流䌚を実斜 テヌマを問わず、登壇したい人が登壇できるようにする 過去の登壇テヌマ䟋 盎近でリリヌスのあったプロゞェクトの内容共有 瀟内向けに䜜成したツヌルの宣䌝 特定テヌマ䌚Figma勉匷䌚、クリ゚むティブ関連の生成AI、セキュリティ、QA など タむムテヌブル 17:0517:10 オヌプニング 17:1017:25 発衚1 17:2517:40 発衚2 17:4017:55 発衚3 17:5518:00 クロヌゞング 18:0019:00 亀流䌚任意参加 勉匷䌚の実斜背景 勉匷䌚が開催される以前は元々はKTC内の各グルヌプで勉匷䌚やオリ゚ンテヌションが開催されおいたした。ただ、他郚眲や業務で関わりが少ないチヌム間ではお互いのノりハりが共有されにくい状況であり、そこに課題を感じおいる人たちがいたした。 そこで、「関わりが少ないチヌムや他郚眲間で良いノりハりを共有しあえる堎所を䜜りたい」「郚眲やチヌム間の連携を高められる堎所を䜜りたい」ずいう目的意識から、合同勉匷䌚の発足に至りたした。 勉匷䌚を継続するのっお意倖ず倧倉 ただ、䞊蚘のような実斜背景は倚くの䌁業で持ちながらも、瀟内勉匷䌚が立ち䞊がっおは消えおいくこずが倚いず思いたす。 私たちも15回以䞊継続できおいたすが、決しお順颚満垆ではありたせんでした。 䞀般的に、そしお私たちも実際に盎面した「継続を阻む壁」は以䞋のようなものです 運営の属人化 : 特定の人に負担が集䞭し、その人が異動・退職するず立ち行かなくなり、運営工数が䞊がっおしたう ハむブリッド開催の難しさ : オンラむン・オフラむン䞡方を満足させるのは想像以䞊に難しい 参加者の固定化 : 最初は盛り䞊がるが、埐々に参加者が固定化されおいく これらの課題に察しお、私たちは以䞋の4぀の工倫を実践しおきたした。 継続するための工倫・コツ 工倫1: 運営タスクの暙準化ずロヌテヌション制 運営の属人化を防ぐために、工倫しおいる内容は以䞋の通りです。 やるべきタスクを掗い出し、チケットにしお管理 チケットの担圓者を毎月運営メンバヌ内でロヌテヌションでアサむン 1点目の「やるべきタスクを掗い出し、チケットにしお管理」に぀いおは、JIRAの「自動化」機胜を䜿い、合同勉匷䌚を実斜するためのチケットを自動で䜜成するようにしおいたす。これにより誰でも運営に必芁なタスクを把握するこずが可胜です。 2点目の「チケットの担圓者を毎月運営メンバヌ内でロヌテヌションでアサむン」に぀いおは、「叞䌚」「䌚堎準備」「調敎」の3分類のタスクをロヌテヌションするようにしおいたす。ロヌテヌションするこずで運営メンバヌが党郚のタスクを経隓するこずになるため、属人化を防ぐこずができおいたす。 勉匷䌚の運営をするにあたり、運営が疲匊しお頓挫しおしたうケヌスも少なくないず思いたす。KTCの合同勉匷䌚では、運営工数を枛らせるように暙準化しおいたす。 ※JIRAのチケットは以䞋のように切っおいたす。 工倫2: 党瀟員が参加しやすい環境づくり 参加者が固定化したり、オンラむンで参加する人の゚ンゲヌゞメントが䜎くなっおしたうこずがあるず思いたす。勉匷䌚は聞くだけでなく、聞いた内容をもずに意芋を蚀い合えるずより効果があるず考えおいるため、これらは課題になりたす。これらを防げるように以䞋のように工倫しおいたす。 党瀟員のOutlook カレンダヌに勉匷䌚を登録 䌚瀟にある党瀟員が利甚できる亀流スペヌスで実斜 オンラむンの人でも参加しやすいようにワむガダ雑談・意芋亀換甚のSlackチャンネルを甚意し、運営も積極的にワむガダする 月初など忙しいタむミングの開催は避ける 勉匷䌚に参加するこずがゎヌルではなく、勉匷䌚ぞの参加を通じお業務知識や技術力の向䞊に繋がるこずが倧事ではあるず思いたすが、勉匷䌚に参加しないず始たらないので、参加しやすくするこずは倧切です。 ⇓ワむガダ甚のSlack 工倫3: アンケヌト芁望に応える 圓たり前かもしれたせんが、アンケヌトでいただいた芁望には応えるようにしたす。 䟋えば、アンケヌトには次のようなものが届きたした。 「業務仕様をどうやっお決めおいるのかが知れるような事䟋を聞きたい」 「発衚の無いずきの䌚堎がすごく静かなので、BGMずかあっおもよいかなヌず思いたした」 「亀流䌚で提䟛されるお菓子に塩蟛いものが欲しい」 勉匷䌚のテヌマに関わる芁望、䌚堎の雰囲気をいい感じにしおほしい芁望、など色々ず芁望をいただきたすが、それぞれ目を通しお、その背景を考えながら芁望に応えるようにしたす。 「業務仕様をどうやっお決めおいるのかが知れるような事䟋を聞きたい」 ⇒ プロゞェクトマネヌゞャやプロダクトマネヌゞャ、プロデュヌサヌがこういった業務芁件を怜蚎するので、これらの人から倚角的に聞けるずいいかもしれないあず゚ンゞニアの人から来おそうな芁望だから、゚ンゞニア <-> 業務仕様を決める人たちのいい接点が生たれる堎にできるずいいなあ。 「発衚の無いずきの䌚堎がすごく静かなので、BGMずかあっおもよいかなヌず思いたした」 ⇒ 確かに、無蚀の時間っお気たずい・・。BGMも改善したほうがいいし、叞䌚の人が間を繋いで気たずい時間が流れないようにしよう 「亀流䌚で提䟛されるお菓子に塩蟛いものが欲しい」 ⇒ 味が偏っおいるかもしれない。塩蟛いの远加しおみよう 工倫4: 継続的な改善サむクル 䞊蚘の工倫たちは勉匷䌚を開催埌に運営メンバヌ内で振り返りをした結果ずしお生たれたものです。 KTCの合同勉匷䌚の運営では以䞋の衚のようにKPT 法を利甚しお振り返りを行い、「良いこずを継続」「問題は再発しないように改善」を続けおいたすこの改善を通じお、運営のJIRAチケットに反映されるものが増えおいたす。 カテゎリ Keep良い/䞊手い/続けたい Problem問題だ/課題だ/困った Try次回こうしたい 圓日議論 党䜓的に 特定のテヌマに沿っお実斜したのが初。今埌も䜕回か実斜しおも良いず思う 火曜の17時だず人の集たり悪いかも 開催時間を倉曎する ・・・ 前日たでの運営 ・・・ ・・・ ・・・ ・・・ 圓日準備/片付け ・・・ ・・・ ・・・ ・・・ 発衚の郚 ・・・ ・・・ ・・・ ・・・ 亀流䌚の郚 ・・・ ・・・ ・・・ ・・・ アンケヌト ・・・ ・・・ ・・・ ・・・ 勉匷䌚を1幎以䞊続けおきた嬉しい効果 勉匷䌚を継続しおきたこずで感じた嬉しい効果を玹介したす。 他郚眲の人ず意芋亀換ができたり、プロデュヌスできる堎ずなっおる 瀟内共通ツヌルやセキュリティ関連郚眲など、瀟内暪断で仕事をする人たちから特に「意芋亀換ができお嬉しい」ずいうコメントをもらえたす 瀟員からの登壇や郚眲ごずのコラボ勉匷䌚の芁望が増えお勉匷䌚ネタに困らなくなっおきた 「倖郚登壇の緎習をしたい」「プロゞェクトを掚進するのに新しい開発手法を入れおみたから話しおみたい」など、いろいろな芁望をいただきたす 運営自身で登壇するこずはせず、登壇者を募っお実斜する圢匏にしおおり、いろいろな芁望に応えられるようにしおいるので、ネタには困っおいない状態です 最埌に 最埌に、勉匷䌚に参加される方々は、他にできるこずがある䞭で貎重な時間を割いお参加しおくれおいたす。その期埅に応えられるよう、私たちは質の高い勉匷䌚の提䟛を心がけおいたす。 もちろん、最初から完璧な運営ができるわけではありたせん。倧切なのは、たず䞀歩を螏み出すこず。そしお参加者の声に耳を傟け、振り返りを重ねながら、継続的に改善しおいくこずだず考えおいたす。 最埌たで読んでいただき、ありがずうございたした
はじめに こんにちは。KINTOテクノロゞヌズ プラットフォヌムグルヌプ Platform Engineeringチヌムで内補ツヌルの開発・運甚をおこなっおいる山田です。 過去に曞いたSpring AIずText-to-SQLの蚘事もぜひご芧ください https://blog.kinto-technologies.com/posts/2025-06-11-springAI/ https://blog.kinto-technologies.com/posts/2025-01-16-generativeAI_and_Text-to-SQL/ 今回はGitHub Copilotを掻甚しお、AWS䞊で構築しおいるプロダクトの、AWSリ゜ヌスの䟝存関係を自動で分析・収集するAI Agentを構築したお話をしたいず思いたす。 背景ず課題 Platform Engineeringチヌムでは、CMDB (Configuration Management Database) ずIncident Manager (むンシデント管理ツヌル) ずいう2぀の内補ツヌルを開発・運甚しおいたす。 CMDBは構成管理デヌタベヌスずいうシステムで、瀟内プロダクトの構成情報を䞀元管理しおいたす。CMDBにはプロダクトの担圓者や担圓チヌム、脆匱性情報の管理などさたざたな機胜があり、その䞀぀にプロダクトに関連するAWSリ゜ヌス (ECS、RDS、ALB、CloudFrontなど) のARN情報を管理する機胜がありたす。 Incident Managerでは、むンシデント発生時に迅速な原因特定ず埩旧をサポヌトするため、 むンシデントが発生したプロダクトのトポロゞヌ情報 (システム構成図) ず原因箇所を可芖化する機胜 が求められおいたした。 しかし、トポロゞヌ情報を可芖化するためには、単にAWSリ゜ヌスのARN情報を持っおいるだけでは䞍十分で、 リ゜ヌス間の䟝存関係 (䟋: CloudFront → ALB → ECS → RDS) を把握する必芁がありたした。 埓来、この䟝存関係情報は手動で蚭定する必芁があり、以䞋のような課題がありたした。 新しいリ゜ヌスが远加されるたびに手動で䟝存関係を曎新する必芁がある 耇雑なシステム構成では䟝存関係の把握が困難 人的ミスによる䟝存関係の蚭定挏れや誀り 解決アプロヌチ これらの課題を解決するために、 CMDBが管理しおいるARN情報を掻甚しお、AWSリ゜ヌスの䟝存関係を自動で分析・収集するAI Agentを構築する こずにしたした。 GitHub Copilotず察話しながら実装を進めた結果、以䞋のような機胜を持぀AI Agentが完成したした。 CMDBから取埗したARN情報を起点に、AWSリ゜ヌス間の䟝存関係を自動で分析 耇数のAWS APIを呌び出しお、セキュリティグルヌプやネットワヌク蚭定から接続関係を掚論 収集したノヌド (AWSリ゜ヌス) ず゚ッゞ (䟝存関係) の情報をデヌタベヌスに保存 Incident Managerでむンシデント発生時のトポロゞヌ図衚瀺に掻甚 技術スタック 開発支揎ツヌル: GitHub Copilot (Agentモヌド - Claude Sonnet 4.5) AI Agent Framework: LangGraph LLM: Amazon Bedrock (Claude Sonnet 4.5) 蚀語: Python 3.12 䞻芁ラむブラリ: LangChain, LangChain-AWS boto3 (AWS SDK for Python) AI Agent構築の流れ 1. 最初のプロンプト たず初めに、以䞋のプロンプトでGitHub CopilotにAI Agentを構築するための蚭蚈をお願いしたした。(䞀郚、省略・線集しおいたす) CMDBのARN管理テヌブルから取埗したAWSリ゜ヌスのARN情報を䜿っお、 リ゜ヌス間の䟝存関係を自動で分析・収集するAI Agentを実装しおください。 技術スタック: - LangGraph、LangChain - Amazon Bedrock (Claude Sonnet 4.5) - AWS SDK (boto3) - Python 3.12 機胜芁件: - API Endpoint: POST /service_configurations - パラメヌタ: sid, environment (どちらも必須) - ARN管理テヌブルからsid、environmentを条件にARNを怜玢 - 取埗したCloudFront、S3、WAF、ALB、TargetGroup、ECS、RDS、ElastiCacheのARN情報を䜿っお、Nodes、Edges情報をLLMずAgent (LangGraph) で成圢 - 収集した情報をDBに保存 䟝存関係の取埗方法 (Few-shot Examples): ### CloudFront → S3/ALB のEdge刀定方法 1. CloudFront APIでドメむン名を取埗 aws cloudfront get-distribution --id {distribution-id} 2. ビヘむビア情報からOriginを取埗しおEdge(玐づき)を刀定 - DomainNameにs3がある堎合: CloudFront → S3 - DomainNameにALBがある堎合: CloudFront → ALB ### TargetGroup → ECS のEdge刀定方法 1. elbv2のAPIでタヌゲットのIPアドレスを取埗 aws elbv2 describe-target-health --target-group-arn {arn} 2. ECS APIでタスクのIPアドレスず照合しおEdgeを䜜成 ### ECS → RDS のEdge刀定方法 (実際のアクセスではなく、セキュリティの蚱可で刀定) 1. ECSタスクからENI (Elastic Network Interface) のIDを取埗 aws ecs describe-tasks --cluster {cluster} --tasks {task-arn} 2. ENIからECSタスクのセキュリティグルヌプIDを取埗 aws ec2 describe-network-interfaces --network-interface-ids {eni-id} 3. RDSのセキュリティグルヌプを取埗 aws rds describe-db-instances --db-instance-identifier {instance-id} 4. RDSセキュリティグルヌプのむンバりンドルヌルを取埗 aws ec2 describe-security-groups --group-ids {sg-id} 5. ECSのセキュリティグルヌプがRDSのむンバりンドルヌルに含たれおいればEdge䜜成 䜿甚予定のAWS API: - cloudfront:get_distribution - cloudfront:list_distributions - wafv2:list_web_acls - wafv2:get_web_acl - wafv2:get_web_acl_for_resource - elbv2:describe_target_groups - elbv2:describe_target_health - elbv2:describe_load_balancers - elbv2:describe_load_balancer_attributes - ecs:list_clusters - ecs:describe_tasks - ecs:describe_task_definition - ecs:list_tasks - ecs:describe_services - ec2:describe_network_interfaces - ec2:describe_security_groups - rds:describe_db_clusters - rds:describe_db_instances - elasticache:describe_replication_groups - elasticache:describe_cache_clusters 関連するDDL: {ARN管理テヌブルのDDL} {Node情報テヌブルのDDL} {Edge情報テヌブルのDDL} 重芁: - たずは珟状のリポゞトリ構成を理解する - 実装を始める前に敎理しお、実装方針を確認しおから実装を行う - 呌び出すAWS APIや取埗するNodeは増える可胜性があるため、拡匵性を考慮する - コヌドは簡単でシンプルな内容にする このプロンプトに察しお、GitHub Copilotはたずリポゞトリ党䜓を分析し、既存のコヌドを理解した䞊で以䞋の実装方針を提案しおくれたした。(䞀郚、省略・線集しおいたす) (Read files...) リポゞトリの理解ができたしたので、蚭蚈方針をたずめたす。 リポゞトリ構造の理解 既存のプロゞェクトは以䞋の構成になっおいたす ・FastAPIベヌスのAPIサヌバヌ ・**Bedrock (Claude)**を䜿甚したLLM機胜 ・MySQLデヌタベヌス接続機胜text_to_sql.pyで実装䟋あり ・boto3でAWS APIを呌び出す機胜aws_diagram.pyで実装䟋あり ・LangChainの䟝存関係ありpyproject.tomlに蚘茉 実装方針 1. アヌキテクチャ蚭蚈 ARN管理テヌブルからARN情報取埗 (DB) ↓ LangGraph Agent でAWS APIを呌び出し、Edges情報を収集 ↓ Nodes/Edges情報をテヌブルに保存 (DB) ↓ レスポンス返华 2. モゞュヌル構成 (新芏䜜成ファむル) ・`service_config_router.py`: FastAPI゚ンドポむント ・`service_config_agent.py`: LangGraphによるAgent実装 ・`aws_tools.py`: AWS API呌び出しツヌル矀 ・`service_config_db.py`: DB操䜜 (ARN取埗、Node/Edge保存) 3. 実装フェヌズ ・Phase 1 (DBå±€): ARN取埗ずNode/Edge保存関数の実装 ・Phase 2 (AWSツヌル局): CloudFront、ELB、ECS、RDS等のAPI呌び出し関数をLangChain Tool圢匏で実装 ・Phase 3 (Agentå±€): LangGraphのStateGraphを構築し、LLMにツヌルを枡しおEdge情報を掚論 ・Phase 4 (Routerå±€): FastAPI゚ンドポむントずリク゚スト怜蚌・レスポンス敎圢 4. 蚭蚈の重芁ポむント ・拡匵性: ホワむトリスト方匏で新しいAWS APIを远加しやすく ・シンプルさ: StateGraphで状態管理を明確に、各ツヌルは1関数=1API呌び出し ・デヌタフロヌ: API → Agent → AWS APIs → DB保存ずいう明確な流れ 5. 実装コヌド構造擬䌌コヌド (コヌドのサンプル) この蚭蚈方針で実装を進めおよろしいでしょうか 確認いただけたしたら、順次実装を開始したす。 この蚭蚈時点で倧きく方針がずれおいなかったため、このたたGitHub Copilotに実装しおもらいたした。 2. 繰り返し修正䟝頌 プロンプトに曞き忘れた基本蚭蚈、すべお䌝えきれおいない詳现蚭蚈、プロンプトの問題など様々な芁因はありたすが、生成されたコヌドは䞀発で完璧に動くこずはなく、GitHub Copilotず䜕床もやりずりをしおコヌドの修正を繰り返したした。 アヌキテクチャのレむダヌ分けをプロンプトに曞き忘れたため、Controller局に倧量にビゞネスロゞックを実装されおしたったり、 Bedrockのモデル呌び出し凊理で存圚しない関数呌び出しをしおいたり (別バヌゞョンの関数を利甚しおいた)、 ゚ラヌハンドリングが足りなかったり、 動いたず思ったら取埗できおいないNodeずEdgeの情報があったり、 Agentのプロンプトの改善をしたり... たくさんの問題がありたしたが、数時間で想定通りの動䜜をするようになりたした。 3. 最終的に完成したコヌド 9割以䞊をGitHub Copilotにコヌディングをしおもらっお最終的にどんなコヌドになったのか、䞀郚重芁な郚分を抜粋しおご玹介しようず思いたす。 LangGraphによるAgent実装 AI Agentの栞心郚分です。LangGraphを䜿っお凊理フロヌを定矩しおいたす。(䞀郚、省略・線集しおいたす) def create_service_config_agent() -> StateGraph: """システム構成収集Agentを䜜成""" workflow = StateGraph(AgentState) # ノヌドを远加 workflow.add_node("initialize_nodes", initialize_nodes) workflow.add_node("collect_edges", collect_edges_with_llm) # ゚ントリヌポむントを蚭定 workflow.set_entry_point("initialize_nodes") # 条件分岐: ノヌドが存圚すればEdge収集、なければ終了 workflow.add_conditional_edges( "initialize_nodes", should_collect_edges, { "collect_edges": "collect_edges", "end": END } ) workflow.add_edge("collect_edges", END) return workflow.compile() def collect_edges_with_llm(state: AgentState) -> AgentState: """LLMずツヌルを䜿甚しお゚ッゞ情報を収集""" llm = get_llm_for_agent() llm_with_tools = llm.bind_tools(AWS_TOOLS) prompt = f""" あなたはAWSリ゜ヌスの䟝存関係を分析する゚キスパヌトです。 # タスク 以䞋のAWSリ゜ヌス (Nodes) のARNから、リ゜ヌス間の接続関係 (Edges) を掚枬・特定しおください。 各AWSサヌビスの特性ず䞀般的なアヌキテクチャパタヌンを理解し、適切なAWS APIを呌び出しお接続を確認しおください。 # 利甚可胜なNodes {nodes_summary} # 利甚可胜なツヌル 1. **call_aws_api**: 蚱可されたAWS APIを呌び出せるツヌル - リ゜ヌスの詳现情報、蚭定、関連リ゜ヌスを取埗できたす 2. **extract_resource_id_from_arn**: ARNからリ゜ヌスIDやその他の情報を抜出 - API呌び出しに必芁なパラメヌタ (ID、名前など) を取埗できたす # 䜿甚可胜なAWS API (これ以倖は䜿甚できたせん) - cloudfront:get_distribution - wafv2:get_web_acl_for_resource - elbv2:describe_target_groups ... # Edge怜出の方法 以䞋の芳点から、リ゜ヌス間の接続を掚枬・調査しおください ## 䞀般的な接続パタヌン 1. **フロント゚ンド局**: CloudFront → S3/ALB、WAF → CloudFront/ALB、Route53ドメむン → CloudFront 2. **ロヌドバランサヌ局**: ALB → タヌゲットグルヌプ → ECS/EC2 3. **APIå±€**: API Gateway → Lambda 4. **アプリケヌション局**: ECS → RDS/ElastiCache (セキュリティグルヌプ経由) 、Lambda → RDS (セキュリティグルヌプ経由) 5. **デヌタ局**: RDS、ElastiCache ## 接続怜出の考え方 - **蚭定ベヌス**: リ゜ヌスの蚭定に他のリ゜ヌスのARNやIDが含たれおいる堎合 (䟋: CloudFrontのOrigins蚭定) - **ネットワヌクベヌス**: セキュリティグルヌプのむンバりンドルヌルで蚱可されおいる堎合 (䟋: ECS → RDS) - **サヌビス特性**: 各AWSサヌビスの圹割から論理的に掚枬できる接続 (䟋: TargetGroup → ECS) ## 重芁な調査ポむント - **ARNの分析**: たずextract_resource_id_from_arnでARNを解析し、リ゜ヌスタむプず必芁なパラメヌタを特定 - **段階的調査**: 䞀床に党APIを呌ばず、結果を芋ながら次に必芁なAPIを刀断 - **セキュリティグルヌプ**: ECS/RDS/ElastiCacheの接続はセキュリティグルヌプのむンバりンドルヌルで確認 - ECSのENI → セキュリティグルヌプID → RDS/ElastiCacheのSGむンバりンドルヌルに含たれるかチェック - **゚ラヌ察応**: 蚱可されおいないAPIや゚ラヌが返っおも、次の調査を継続 # Few-shot Examples (必ず参考にするこず) ## Example 1: ECS → RDS の調査 1. ECSタスクのENIを取埗: call_aws_api("ecs", "describe_tasks", ...) 2. ENIからSGを取埗: call_aws_api("ec2", "describe_network_interfaces", ...) 3. RDSのSGを取埗: call_aws_api("rds", "describe_db_instances", ...) 4. SG䞀臎確認 → Edge䜜成 ... # 重芁な泚意事項 - すべおのリ゜ヌスの組み合わせをチェック - API゚ラヌが出おも次の調査を継続 - RDSの snapshot, parameter group, subnet group 等は無芖 - セキュリティグルヌプで接続可胜性を刀断 # 出力圢匏 調査が完了したら、以䞋のJSON圢匏で結果を返しおください ```json {{ "nodes": [ {{ "service_name": "cloudfront", "arn": "xxx", "resource": "" }} ], "edges": [ {{ "from_arn": "xxx", "to_arn": "xxx", "details": "xxx" }} ] }} ``` """ messages = [HumanMessage(content=prompt)] try: max_iterations = 30 # 最倧反埩回数 edges = [] for iteration in range(max_iterations): response = llm_with_tools.invoke(messages) messages.append(response) # ツヌル呌び出しがあるか確認 if hasattr(response, 'tool_calls') and response.tool_calls: # ツヌルを実行 tool_node = ToolNode(AWS_TOOLS) tool_results = tool_node.invoke({"messages": messages}) # ツヌル結果をメッセヌゞに远加 messages.extend(tool_results["messages"]) else: # ツヌル呌び出しがない堎合、最終レスポンスずしお凊理 try: # 構造化レスポンス甚のLLMを䜜成 structured_llm = llm.with_structured_output(GraphResult) # 最終結果の芁玄プロンプトを远加 final_prompt = """ 調査が完了したした。発芋したすべおの゚ッゞず新しいノヌドドメむン名などを JSON圢匏で返しおください。 """ messages.append(HumanMessage(content=final_prompt)) # 構造化レスポンスを取埗 result = structured_llm.invoke(messages) edges = [edge.model_dump() for edge in result.edges] new_nodes = [node.model_dump() for node in result.nodes] # LLMが返した新しいノヌドドメむン名などを既存のノヌドリストに远加 if new_nodes: state["nodes"].extend(new_nodes) except Exception as e: # ゚ラヌ時のフォヌルバック凊理 ... break state["edges"] = edges state["current_step"] = "edges_collected" state["messages"] = messages except Exception as e: ... state["edges"] = [] return state AWS API呌び出しツヌル Agentが䜿甚するツヌル矀です。ホワむトリスト方匏で実行可胜なAWS APIの安党性を確保しおいたす。(䞀郚、省略・線集しおいたす) # 蚱可するAWS APIのホワむトリスト ALLOWED_AWS_APIS: Set[str] = { # CloudFront "cloudfront:get_distribution", "cloudfront:list_distributions", # WAF "wafv2:list_web_acls", "wafv2:get_web_acl", "wafv2:get_web_acl_for_resource", ... } @tool def call_aws_api( service_name: str, method_name: str, parameters: Dict[str, Any], region: str = "ap-northeast-1" ) -> Dict[str, Any]: """汎甚的なAWS API呌び出しツヌル このツヌルは蚱可されたAWS APIのみを呌び出すこずができたす。 Edge情報を取埗するために必芁なAWS APIを呌び出しおください。 Args: service_name: AWSサヌビス名 (小文字) 蚱可: 'cloudfront', 'wafv2', 'elbv2', 'ecs', 'ec2', 'rds', 'elasticache', 'apigateway', 'lambda' method_name: 呌び出すメ゜ッド名 (boto3のメ゜ッド名、snake_case) 䟋: 'get_distribution', 'describe_target_health', 'describe_security_groups' parameters: メ゜ッドに枡すパラメヌタの蟞曞 䟋: {"Id": "ABC123"} や {"GroupIds": ["sg-12345"]} region: AWSリヌゞョン (デフォルト: ap-northeast-1) Returns: API呌び出し結果の蟞曞 ゚ラヌの堎合は {"error": "゚ラヌメッセヌゞ"} を返す 蚱可されおいるAPI䞀芧: - cloudfront:get_distribution - cloudfront:list_distributions - wafv2:list_web_acls - wafv2:get_web_acl - wafv2:get_web_acl_for_resource - ... Examples: # CloudFront Distribution情報を取埗 call_aws_api( service_name="cloudfront", method_name="get_distribution", parameters={"Id": "ABC123"} ) # タヌゲットグルヌプのヘルス情報を取埗 call_aws_api( service_name="elbv2", method_name="describe_target_health", parameters={"TargetGroupArn": "arn:aws:elasticloadbalancing:..."} ) # セキュリティグルヌプ情報を取埗 call_aws_api( service_name="ec2", method_name="describe_security_groups", parameters={"GroupIds": ["sg-12345"]} ) # ECSタスク情報を取埗 call_aws_api( service_name="ecs", method_name="describe_tasks", parameters={"cluster": "my-cluster", "tasks": ["arn:aws:ecs:..."]} ) """ try: # ステップ1: APIホワむトリストに含たれおいるか怜蚌 is_valid, error_message = validate_aws_api(service_name, method_name) if not is_valid: return { "error": error_message, "error_type": "unauthorized_api", "allowed_apis": get_allowed_apis_list() } # ステップ2: クラむアントを取埗 client = get_aws_client(service_name, region) # ステップ3: メ゜ッドが存圚するか確認 if not hasattr(client, method_name): return {"error": error_msg, "available_methods": dir(client)} # ステップ4: メ゜ッドを取埗しお実行 method = getattr(client, method_name) response = method(**parameters) return response except Exception as e: ... 以䞋が今回の実装で意識したポむントです。 ホワむトリスト方匏: LLMが任意のAWS APIを呌ぶこずを防ぎ、安党性を確保 動的API呌び出し: Pythonの getattr() でboto3のメ゜ッドを動的に実行 詳现なdocstring: LLMがツヌルの䜿い方を理解するため、匕数の説明、䜿甚䟋、蚱可API䞀芧を蚘茉 拡匵性: 新しいAPIを远加する堎合は、 ALLOWED_AWS_APIS に远加するだけ Incident Managerでのトポロゞヌ描画 最終的にAI Agentを䜿っお収集したNodeずEdgeの情報で、IncidentManager䞊でのシステムトポロゞヌ衚瀺はこのようになりたした。 障害発生時は原因箇所が赀くなるため、ぱっず芋で盎感的にシステム構成ず障害箇所が理解しやすいような図になったかず思いたす GitHub Copilotを䜿っお実装しおみた感想 良かった点 開発時間の倧幅な短瞮 今回の実装は玄1日で完成したした。もしGitHub Copilotなしで実装しおいたら、LangGraphの孊習から始める必芁があり、少なくずも数週間はかかっおいたず思いたす。 高品質な実装蚈画の提案 ただ改善の䜙地はありたすが最初のプロンプトで実珟したいこずず蚭蚈を詳现に䌝えたこずで、以䞋のような質の高い実装蚈画が生成されたした。 既存リポゞトリの構造を理解した䞊で、䞀貫性のある蚭蚈を提案 拡匵性ずシンプルさを䞡立した蚭蚈の提案 察話的な品質改善 実装途䞭で気になった点を指摘するず、すぐに修正しおくれたした。 アヌキテクチャの改善 ラむブラリバヌゞョンの問題 ゚ラヌハンドリングの远加 など 倧倉だった点 生成されたコヌドの怜蚌が必須 GitHub Copilotに限らず生成AIが生成したコヌドは、 指瀺をした人が責任を持っおレビュヌをする 必芁がありたす。生成AIを掻甚したコヌディングでは、レビュヌに䞀番時間がかかりたす。 以䞋のような芳点でコヌドレビュヌをおこないたした。 既存コヌドの芏玄準拠: リポゞトリの呜名芏則やコヌディングスタむルに埓っおいるか 芁件の網矅性: 指定した機胜芁件がすべお実装されおいるか、挏れがないか アヌキテクチャパタヌン: 適切なレむダヌ分けがされおいるか、責務が明確か 実装の適切性: より䞀般的な方法や簡単な実装方法がないか、無駄に耇雑になっおいないか ゚ラヌハンドリング: 䟋倖凊理が適切に実装されおいるか、゚ラヌメッセヌゞは適切か 動䜜怜蚌: 実際にアプリを起動させお、意図通りの動䜜をするか 今埌やりたいこず 収集するNodeずEdgeの远加 珟状は最初のお詊しずいうこずで、䞀郚のAWSサヌビスに絞っおNodeずEdgeを取埗するようにしたした。 プロンプトで取埗するAWSリ゜ヌスを远加しお、AWS API呌び出しツヌルの利甚するホワむトリストに、Edge情報を取埗するために蚱可するAPIを远加すれば簡単に拡匵できる実装になっおいるため、今埌少しず぀収集リ゜ヌスを増やしおいきたいず思いたす。 プロンプトテンプレヌトの䜜成 今回は最初の指瀺で考慮䞍足があったため、今埌䜿いたわせるような新機胜実装時のプロンプトテンプレヌトを䜜成しお、以䞋の内容を含めるこずで最初の指瀺からより高品質なコヌド生成ができるようにしたいず思いたす。 機胜の抂芁 技術スタックフレヌムワヌク、ラむブラリ、蚀語バヌゞョン 機胜芁件 アヌキテクチャ芁件レむダヌ構成、゚ラヌハンドリングなど 非機胜芁件拡匵性、パフォヌマンス、セキュリティ 実装前の確認事項リポゞトリ構造の理解、既存コヌドずの敎合性確認など 重芁な泚意事項プロンプトの最埌に配眮、絶察に守るべきルヌルを明蚘 たずめ 今回はGitHub Copilotを掻甚しお、システムトポロゞヌ情報を収集するAI Agentを玄1日で実装したした。 実装したAI Agentにより、埓来は手動で蚭定しおいたAWSリ゜ヌスの䟝存関係が自動収集されるようになり、Incident Manager䞊でむンシデント発生時のトポロゞヌ可芖化が実珟できたした。 今埌もGitHub Copilotをはじめずした生成AIを積極的に掻甚しお、開発生産性の向䞊を目指しおいきたいず思いたす。
Introduction Hello. I'm Yamada, and I work on developing and operating internal tools at the Platform Engineering Team, Platform Group of KINTO Technologies. Please also check out my previous articles on Spring AI and Text-to-SQL! https://blog.kinto-technologies.com/posts/2025-06-11-springAI/ https://blog.kinto-technologies.com/posts/2025-01-16-generativeAI_and_Text-to-SQL/ In this article, I'd like to share how I built an AI Agent that automatically analyzes and gathers AWS resource dependencies for products built on AWS, using GitHub Copilot. Background and Issues The Platform Engineering Team develops and operates two internal tools: Configuration Management Database (CMDB) and an incident management tool (Incident Manager). CMDB is a configuration management database system that centrally manages configuration information for internal products. It has various features including managing product owners and teams, vulnerability information management. One of these particular features is managing ARN information for AWS resources (ECS, RDS, ALB, CloudFront, etc.) associated with products. For Incident Manager, there was a requirement to visualize the topology information (system architecture diagram) of the product where an incident occurred and highlight the root cause to help us promptly identify it and recover the system after the incident. However, simply having AWS resource ARN information was insufficient to visualize topology information—we needed to understand the dependencies between resources (e.g., CloudFront -> ALB -> ECS -> RDS) . Previously, we needed to manually configure this dependency information, which led to the following issues: Manual updates to dependencies were required every time when new resources were added Complex system architectures causes the difficulty in understanding dependencies Human errors led to missing or incorrect dependency configurations Solution Approach To solve these issues, I decided to build an AI Agent that automatically analyzes and gathers AWS resource dependencies using the ARN information managed by CMDB . After interactions with GitHub Copilot to proceed with the implementation on a trial-and-error basis, I completed an AI Agent with the following capabilities: Automatically analyzes dependencies between AWS resources, based on ARN information retrieved from CMDB Calls multiple AWS APIs to infer connection relationships from security groups and network configurations Saves gathered node (AWS resource) and edge (dependency) information to the database Used for showing a topology diagram when an incident occurs in Incident Manager Technology Stack Development support tool: GitHub Copilot (Agent mode - Claude Sonnet 4.5) AI Agent Framework: LangGraph LLM: Amazon Bedrock (Claude Sonnet 4.5) Language: Python 3.12 Key Libraries: LangChain, LangChain-AWS boto3 (AWS SDK for Python) AI Agent Development Process 1. Initial Prompt First, I asked GitHub Copilot to design the AI Agent with the following prompt, which is partially abbreviated and edited. Using the AWS resource ARN information retrieved from CMDB's ARN management table, implement an AI Agent that automatically analyzes and gathers dependencies between resources. Technology Stack: - LangGraph, LangChain - Amazon Bedrock (Claude Sonnet 4.5) - AWS SDK (boto3) - Python 3.12 Functional Requirements: - API Endpoint: POST /service_configurations - Parameters: sid, environment (both required) - Search for ARNs in the ARN management table using sid and environment as conditions - Using the retrieved CloudFront, S3, WAF, ALB, TargetGroup, ECS, RDS, ElastiCache ARN information, shape Nodes and Edges information with LLM and Agent (LangGraph) - Save gathered information to DB How to Retrieve Dependencies (Few-shot Examples): ### CloudFront -> S3/ALB Edge Determination Method 1. Get domain name using CloudFront API aws cloudfront get-distribution --id {distribution-id} 2. Get Origin from behavior information to determine Edge (relationship) - If DomainName contains s3: CloudFront -> S3 - If DomainName contains ALB: CloudFront -> ALB ### TargetGroup -> ECS Edge Determination Method 1. Get target IP address using elbv2 API aws elbv2 describe-target-health --target-group-arn {arn} 2. Match with ECS task IP addresses to create Edge ### ECS -> RDS Edge Determination Method (determined by security permissions, not actual access) 1. Get ENI (Elastic Network Interface) ID from ECS task aws ecs describe-tasks --cluster {cluster} --tasks {task-arn} 2. Get ECS task security group ID from ENI aws ec2 describe-network-interfaces --network-interface-ids {eni-id} 3. Get RDS security group aws rds describe-db-instances --db-instance-identifier {instance-id} 4. Get inbound rules for RDS security group aws ec2 describe-security-groups --group-ids {sg-id} 5. Create Edge if ECS security group is included in RDS inbound rules AWS APIs to be used: - cloudfront:get_distribution - cloudfront:list_distributions - wafv2:list_web_acls - wafv2:get_web_acl - wafv2:get_web_acl_for_resource - elbv2:describe_target_groups - elbv2:describe_target_health - elbv2:describe_load_balancers - elbv2:describe_load_balancer_attributes - ecs:list_clusters - ecs:describe_tasks - ecs:describe_task_definition - ecs:list_tasks - ecs:describe_services - ec2:describe_network_interfaces - ec2:describe_security_groups - rds:describe_db_clusters - rds:describe_db_instances - elasticache:describe_replication_groups - elasticache:describe_cache_clusters Related DDL: {ARN management table DDL} {Node information table DDL} {Edge information table DDL} Important: - First understand the current repository structure - Organize and confirm the plan for the implementation before its start - Consider extensibility as AWS APIs and Nodes to retrieve may increase - Keep code simple and straightforward Following this prompt, GitHub Copilot first analyzed the entire repository, understood the existing code, and then proposed the following implementation plan, which is partially abbreviated and edited. (Read files...) I have understood the repository, so I will summarize the design approach. Understanding the Repository Structure The existing project has the following structure: ・FastAPI-based API server ・LLM functionality using **Bedrock (Claude)** ・MySQL database connection functionality (implemented in text_to_sql.py) ・Functionality to call AWS APIs with boto3 (implemented in aws_diagram.py) ・LangChain dependencies (listed in pyproject.toml) Implementation Approach 1. Architecture Design Retrieve ARN information from ARN management table (DB) ↓ Gather Edges information by calling AWS APIs with LangGraph Agent ↓ Save Nodes/Edges information to tables (DB) ↓ Return response 2. Module Structure (Newly created files) ・`service_config_router.py`: FastAPI endpoint ・`service_config_agent.py`: Agent implementation using LangGraph ・`aws_tools.py`: AWS API call tool collection ・`service_config_db.py`: DB operations (ARN retrieval, Node/Edge saving) 3. Implementation Phases ・Phase 1 (DB layer): Implement ARN retrieval and Node/Edge saving functions ・Phase 2 (AWS tools layer): Implement CloudFront, ELB, ECS, RDS, etc. API call functions in LangChain Tool format ・Phase 3 (Agent layer): Build LangGraph StateGraph, pass tools to LLM to infer Edge information ・Phase 4 (Router layer): FastAPI endpoint, request validation, and response formatting 4. Key Design Points ・Extensibility: Whitelist approach makes it easy to add new AWS APIs ・Simplicity: Clearer state management with StateGraph and calling each tool using one function per API ・Data flow: Clear flow from API, Agent, AWS APIs, to data save to DB 5. Implementation Code Structure (Pseudocode) (Code samples) Is it okay to proceed with implementation based on the above design approach? Once confirmed, I will start implementation in sequence. Since the design approach was not significantly misaligned with what was in my head at this point, I had GitHub Copilot proceed with the implementation. 2. Iterative Correction Requests Due to various factors including basic design I forgot to include in the prompt, detailed design I couldn't fully communicate, and prompt issues, the generated code didn't work perfectly on my first implementation attempt. I had to go back and forth with GitHub Copilot multiple times to repeatedly correct the code. Since I forgot to include the architecture layer separation in the prompt, a large amount of business logic was implemented in the Controller layer. There were calls to non-existent functions in the Bedrock model invocation process (it was using functions from a different version). Error handling was insufficient. Just while I thought it was working, part of Node and Edge information wasn't being retrieved. I had to improve the Agent's prompts... There were so many issues, but within a few hours, it started working as expected. 3. Final Completed Code GitHub Copilot handled over 90% of the coding. Let me share some key excerpts of what the final code looked like. Agent Implementation with LangGraph This is the core part of the AI Agent. It defines the processing flow using LangGraph. The definition is partially abbreviated and edited. def create_service_config_agent() -> StateGraph: """Create system configuration collection Agent""" workflow = StateGraph(AgentState) # Add nodes workflow.add_node("initialize_nodes", initialize_nodes) workflow.add_node("collect_edges", collect_edges_with_llm) # Set entry point workflow.set_entry_point("initialize_nodes") # Conditional branching: collect edges if nodes exist, otherwise end workflow.add_conditional_edges( "initialize_nodes", should_collect_edges, { "collect_edges": "collect_edges", "end": END } ) workflow.add_edge("collect_edges", END) return workflow.compile() def collect_edges_with_llm(state: AgentState) -> AgentState: """Collect edge information using LLM and tools""" llm = get_llm_for_agent() llm_with_tools = llm.bind_tools(AWS_TOOLS) prompt = f""" You are an expert in analyzing AWS resource dependencies. # Task From ARN information of the following AWS resources (Nodes), identify and infer the connection relationships (Edges) between resources. Understand the characteristics of each AWS service and common architecture patterns, and call appropriate AWS APIs to verify connections. # Available Nodes {nodes_summary} # Available Tools 1. **call_aws_api**: A tool that can call permitted AWS APIs - Can retrieve resource details, configurations, and related resources 2. **extract_resource_id_from_arn**: Extract resource ID and other information from ARN - Can retrieve parameters (ID, name, etc.) needed for API calls # Available AWS APIs (no others can be used) - cloudfront:get_distribution - wafv2:get_web_acl_for_resource - elbv2:describe_target_groups ... # Edge Detection Methods Infer and investigate connections between resources from the following perspectives: ## Common Connection Patterns 1. **Frontend layer**: CloudFront -> S3/ALB, WAF -> CloudFront/ALB, Route53 domain -> CloudFront 2. **Load balancer layer**: ALB -> Target Group -> ECS/EC2 3. **API layer**: API Gateway -> Lambda 4. **Application layer**: ECS -> RDS/ElastiCache (via security groups), Lambda -> RDS (via security groups) 5. **Data layer**: RDS, ElastiCache ## Connection Detection Approach - **Configuration-based**: When resource configuration contains ARNs or IDs of other resources (e.g., CloudFront Origins settings) - **Network-based**: When permitted by security group inbound rules (e.g., ECS -> RDS) - **Service characteristics**: Connections that can be logically inferred from each AWS service's role (e.g., TargetGroup -> ECS) ## Important Investigation Points - **ARN analysis**: First analyze ARN with extract_resource_id_from_arn to identify resource type and required parameters - **Incremental investigation**: Don't call all APIs at once; determine the next needed API based on results - **Security groups**: Verify ECS/RDS/ElastiCache connections through security group inbound rules - Check if ECS ENI -> Security Group ID -> is included in RDS/ElastiCache SG inbound rules - **Error handling**: Continue investigation even if unauthorized APIs or errors are returned # Few-shot Examples (must be referenced) ## Example 1: ECS -> RDS Investigation 1. Get ECS task ENI: call_aws_api("ecs", "describe_tasks", ...) 2. Get SG from ENI: call_aws_api("ec2", "describe_network_interfaces", ...) 3. Get RDS SG: call_aws_api("rds", "describe_db_instances", ...) 4. Verify SG match -> Create Edge ... # Important Notes - Check all resource combinations - Continue investigation even if API errors occur - Ignore RDS snapshot, parameter group, subnet group, etc. - Determine connectivity by security groups # Output Format When investigation is complete, return results in the following JSON format: ```json {{ "nodes": [ {{ "service_name": "cloudfront", "arn": "xxx", "resource": "" }} ], "edges": [ {{ "from_arn": "xxx", "to_arn": "xxx", "details": "xxx" }} ] }} ``` """ messages = [HumanMessage(content=prompt)] try: max_iterations = 30 # Maximum iteration count edges = [] for iteration in range(max_iterations): response = llm_with_tools.invoke(messages) messages.append(response) # Check if there are tool calls if hasattr(response, 'tool_calls') and response.tool_calls: # Execute tools tool_node = ToolNode(AWS_TOOLS) tool_results = tool_node.invoke({"messages": messages}) # Add tool results to messages messages.extend(tool_results["messages"]) else: # If no tool calls, process as final response try: # Create LLM for structured response structured_llm = llm.with_structured_output(GraphResult) # Add final result summary prompt final_prompt = """ Investigation complete. Return all discovered edges and new nodes (such as domain names) in JSON format. """ messages.append(HumanMessage(content=final_prompt)) # Get structured response result = structured_llm.invoke(messages) edges = [edge.model_dump() for edge in result.edges] new_nodes = [node.model_dump() for node in result.nodes] # Add new nodes returned by LLM (such as domain names) to existing node list if new_nodes: state["nodes"].extend(new_nodes) except Exception as e: # Fallback processing on error ... break state["edges"] = edges state["current_step"] = "edges_collected" state["messages"] = messages except Exception as e: ... state["edges"] = [] return state AWS API Call Tools These are the tools used by the Agent. A whitelist approach ensures the safety of executable AWS APIs. The tools are partially abbreviated and edited. # Whitelist of allowed AWS APIs ALLOWED_AWS_APIS: Set[str] = { # CloudFront "cloudfront:get_distribution", "cloudfront:list_distributions", # WAF "wafv2:list_web_acls", "wafv2:get_web_acl", "wafv2:get_web_acl_for_resource", ... } @tool def call_aws_api( service_name: str, method_name: str, parameters: Dict[str, Any], region: str = "ap-northeast-1" ) -> Dict[str, Any]: """General AWS API call tool This tool can only call permitted AWS APIs. Call the AWS APIs needed to retrieve Edge information. Args: service_name: AWS service name (lowercase) Permitted: 'cloudfront', 'wafv2', 'elbv2', 'ecs', 'ec2', 'rds', 'elasticache', 'apigateway', 'lambda' method_name: Method name to call (boto3 method name, snake_case) Examples: 'get_distribution', 'describe_target_health', 'describe_security_groups' parameters: Dictionary of parameters to pass to the method Examples: {"Id": "ABC123"} or {"GroupIds": ["sg-12345"]} region: AWS region (default: ap-northeast-1) Returns: Dictionary of API call results Returns {"error": "error message"} in case of error List of permitted APIs: - cloudfront:get_distribution - cloudfront:list_distributions - wafv2:list_web_acls - wafv2:get_web_acl - wafv2:get_web_acl_for_resource - ... Examples: # Get CloudFront Distribution information call_aws_api( service_name="cloudfront", method_name="get_distribution", parameters={"Id": "ABC123"} ) # Get target group health information call_aws_api( service_name="elbv2", method_name="describe_target_health", parameters={"TargetGroupArn": "arn:aws:elasticloadbalancing:..."} ) # Get security group information call_aws_api( service_name="ec2", method_name="describe_security_groups", parameters={"GroupIds": ["sg-12345"]} ) # Get ECS task information call_aws_api( service_name="ecs", method_name="describe_tasks", parameters={"cluster": "my-cluster", "tasks": ["arn:aws:ecs:..."]} ) """ try: # Step 1: Validate if API is in whitelist is_valid, error_message = validate_aws_api(service_name, method_name) if not is_valid: return { "error": error_message, "error_type": "unauthorized_api", "allowed_apis": get_allowed_apis_list() } # Step 2: Get client client = get_aws_client(service_name, region) # Step 3: Check if method exists if not hasattr(client, method_name): return {"error": error_msg, "available_methods": dir(client)} # Step 4: Get method and execute method = getattr(client, method_name) response = method(**parameters) return response except Exception as e: ... Here are the key points I focused on in this implementation: Whitelist approach: Prevents LLM from calling arbitrary AWS APIs, ensuring safety Dynamic API calls: Dynamically executes boto3 methods using Python's getattr() Detailed docstrings: Includes argument descriptions, usage examples, and permitted API list to allow LLM to understand how to use the tools Extensibility: Adding new APIs only requires adding them to ALLOWED_AWS_APIS Topology Rendering in Incident Manager I created a system topology diagram in Incident Manager using the Node and Edge information gathered by the AI Agent. Here is how it finally looked like: When a system failure occurs, the affected area turns red, helping us to intuitively understand the system architecture and failure location at a glance! Thoughts on Implementing with GitHub Copilot Positive Aspects Significant reduction in development time This implementation was completed in about a day. Without GitHub Copilot, I would have needed to start by learning LangGraph, which would have taken at least several weeks. High-quality implementation plan proposals While there's still room for improvement, following detailed information about what I wanted to achieve and the design in the initial prompt, GitHub Copilot generated the following high-quality implementation plan: Proposed a consistent design based on the understanding of the existing repository structure Proposed a design with both extensibility and simplicity Interactive quality improvement When I pointed out concerns during implementation, corrections were made immediately: Architecture improvements Library version issues Adding error handling And more Challenging Aspects Validation of generated code is essential Code generated by generative AI, not just by GitHub Copilot, requires the person who gave the instructions to take responsibility for reviewing it . In AI-assisted coding, review takes the most time. I reviewed the code from the following perspectives: Compliance with existing code conventions: Does it follow the repository's naming conventions and coding style? Requirements coverage: Are all specified functional requirements implemented without omissions? Architecture patterns: Is there appropriate layer separation? Are responsibilities clear? Implementation appropriateness: Are there more common or simpler implementation methods? Is it unnecessarily complex? Error handling: Is exception handling properly implemented? Are error messages appropriate? Operational verification: Does it work as intended when the app is actually started? Future Plans Adding more Nodes and Edges to gather Currently, as an initial trial, I limited the Nodes and Edges retrieval to specific AWS services. The prompt allows for easy extension as retrieved AWS resources were added, and then the whitelist used for the AWS API call tools includes permitted APIs to retrieve Edge information. Therefore, I plan to gradually increase the number of gathered resources going forward. Creating prompt templates Since there were oversights in the initial instructions this time, I created a prompt template, which is reusable and helpful to implement new features. By including the following content, I aim to generate higher-quality code from the initial instructions: Feature overview Technology stack (framework, libraries, language version) Functional requirements Architecture requirements (layer structure, error handling, etc.) Non-functional requirements (extensibility, performance, security) Confirmation items before implementation (understanding repository structure, checking consistency with existing code, etc.) Important notes (placed at the end of the prompt with clearly specific rules that must be followed) Summary This time, I implemented an AI Agent that gathers system topology information for about one day using GitHub Copilot. With the implemented AI Agent, AWS resource dependencies that previously required manual configuration are now automatically gathered, enabling topology visualization when an incident arises in Incident Manager. I plan to continue actively utilizing generative AI, including GitHub Copilot, to increase development productivity.
Reference: AWS Introduces Automated Reasoning Checks ^1 This article is Day 3 entry of the KINTO Technologies Advent Calendar 2025 🎅🎄 0. Introduction I'm YOU, an Infrastructure Architect in the Cloud Infrastructure Group (CIG) at KINTO Technologies. In December 2024, AWS announced Automated Reasoning at re:Invent , enabling mathematical proofs and logical reasoning for generative AI. At that time, it was introduced as Automated Reasoning Checks, a feature of AWS Bedrock Guardrails. It became generally available in some US and EU regions in August of this year. This approach fundamentally differs from probabilistic reasoning methods that address uncertainty by assigning probabilities to outcomes. In fact, Automated Reasoning Checks[^2] provide up to 99% verification accuracy, offering provable guarantees for detecting AI hallucinations while also helping detect ambiguity when model outputs are open to multiple interpretations. To briefly explain AWS Automated Reasoning: Automated Reasoning Checks help verify the accuracy of content generated by foundation models (FMs) against domain knowledge. This helps prevent factual errors caused by AI hallucinations. This policy uses mathematical logic and formal verification techniques to verify accuracy, providing deterministic rules and parameters for checking the correctness of AI responses. As described above, Automated Reasoning is designed to enable quantitative judgment, continuous tracking, response improvement, and further reduction of generative AI hallucinations. Through this approach, it fundamentally differs from probabilistic reasoning methods that rely on uncertainty, assigning probabilities to outcomes. This is why, as stated in the title, it provides up to 99% verification accuracy , offering provable guarantees for detecting AI hallucinations while also helping detect ambiguity when model outputs are open to multiple interpretations. Automated reasoning itself was not originated by AWS—it is a field rooted in mathematical logic. The original automated reasoning began when formal verification techniques from software development started being applied, and AWS now offers this methodology as a service for generative AI. For those who want to learn more about the concept of automated reasoning, please refer to these documents: Automated Reasoning (Wikipedia) What is Automated Reasoning? Since Automated Reasoning integrates with Bedrock Guardrails, understanding Bedrock Guardrails will make some aspects easier to grasp. This article explains the overall picture of Automated Reasoning functionality, so please note that explanations of Bedrock Guardrails are omitted. For those interested in Bedrock Guardrails or wanting more details, I've written separate articles: AWS Bedrock Guardrails Deployment: Part 1 - The Need for Generative AI Security : The necessity of guardrails for generative AI TECH BOOK By KINTO Technologies Vol.01 : How to implement Bedrock Guardrails on AWS 1. Target Readers, Considerations, and Objectives Initially, I thought Automated Reasoning, based on its name alone, would be an automated service that easily prevents generative AI hallucinations. However, it's quite far from being easily accessible automation—it felt quite advanced. So I'll first explain specific situations where Automated Reasoning is needed and important considerations. Target Readers Those who want to rigorously detect, track and mitigate LLM response hallucinations Those implementing generative AI with high governance or compliance requirements where errors are unacceptable Those developing generative AI applications with complex rules and requirements Those aiming to achieve AWS-centric Responsible AI Considerations This article explains findings based on testing in an English environment. When Japanese support is added in the future, behavior and specifications may change. Automated Reasoning analyzes and detects only content related to text and documents provided by users. Therefore, it is a service that verifies hallucinations and accuracy—it does not automatically restrict or process content. The official documentation also recommends using it together with existing Bedrock Guardrails filters. Amazon Bedrock Guardrails Automated Reasoning Checks do not protect against prompt injection attacks. These checks verify exactly what you submit. If malicious or manipulated content is provided as input, verification will be performed on that content as-is (inappropriate input/output). To detect and block prompt injection attacks, use content filters in combination with Automated Reasoning Checks. Automated Reasoning only analyzes and detects text related to the Automated Reasoning policy. The remaining content is ignored, and it cannot tell developers whether responses are off-topic. If you need to detect off-topic responses, use other guardrail components such as topic policies. As discussed later, Automated Reasoning automatically generates basic policies based on text and documents. However, you need to review and test these auto-generated policies. Understanding both the input context requirements and the Automated Reasoning structure is necessary, so please refer to the limitations and best practices in the links below: Constraints and Considerations Best Practices Objectives I want to focus mainly on understanding the Automated Reasoning structure mentioned in the Considerations section above. For this article, I hope you'll learn these three things to understand the big picture: Know what components Automated Reasoning has and how it works Know the flow for testing Automated Reasoning Know when to use Automated Reasoning (This requires knowledge of Guardrails, so I'll discuss it in a separate article) 2. Overview Roughly speaking, the overall picture of Automated Reasoning looks like the diagram below. The procedure is: When you input text/documents into Automated Reasoning, a Policy is automatically created The policy auto-generates Definitions of types, variables, and rules based on the input information a. Additional text or documents can be incorporated to expand policy definitions b. Generated types, variables, and rules can be edited to match requirements and ensure consistency Create Tests to verify the policy a. Manual creation: Enter hypothetical interactions in QnA pair format b. Automatic creation: Scenarios that can verify existing rules are auto-generated Check verification results to confirm expected outcomes a. Verify that expected results match actual results in test execution b. If they don't match, return to step 5 Identify the cause of mismatch and add Annotations to incorrect definitions a. Information suggesting the cause is presented in test execution results b. Annotations can be added to types, variables, and rules—mainly by modifying natural language descriptions, which triggers corresponding updates After applying annotations, suggested modifications appear; accept changes if correct Repeat steps 3-6 to complete a policy that protects the text/document content Attach the completed policy to a Guardrail Either use an existing Guardrail or create a new one Pass LLM application input/responses to the Guardrail and utilize Automated Reasoning Check results a. If successful, return results to the user as-is b. If failed, use Automated Reasoning Check results to request regeneration from the LLM application c. (Optional) Save Automated Reasoning Check results as logs and continue policy reviews In summary, Automated Reasoning is created as a Policy, completed through iterations of Definition → Test → Annotation. The completed policy is attached to AWS Bedrock Guardrails, and when the Guardrail is applied to LLM responses, Automated Reasoning Checks are performed. Subsequent processing varies by the developer's decisions, but you gain the ability to quantitatively judge whether the LLM is producing correct responses. By introducing Automated Reasoning, you can develop strategies to eliminate hallucinations while running parallel to existing Guardrails that block malicious content. Being able to verify the accuracy of generative AI with RAG and MCP that reference external information, and detect and correct erroneous LLM responses and uncontrollable incorrect inputs, is extremely attractive. Next, I'll use samples provided by AWS to explain the three main elements—Definition, Test, and Annotation—following the procedure from Policy creation. 3. Policy ![ポリシヌ䜜成画面](/assets/blog/authors/you/03/image-20251120-094018.png =800x) Basically, like other AWS resources, Automated Reasoning can be operated via console, CLI, or SDK. CloudFormation is not currently supported. CloudFormation support will be available soon. Creating an Automated Reasoning policy on the console is straightforward. Name Description (optional) Source (document/text) Source description After entering the above content and clicking the Create Policy button, the policy contents are automatically created. I used a medical PDF file prepared in the AWS sample, and the policy was generated in about 5-10 minutes. The time to create definitions likely varies depending on the length of the input document. When you check the created policy, the following screen appears: ![Overview](/assets/blog/authors/you/03/image-20251120-134516.png =800x) 4. Definitions The focus here is on the Definitions at the bottom. When you navigate from this overview screen to the definitions screen, you can confirm that rules, variables, and types are defined. Types ![Custom variable types](/assets/blog/authors/you/03/image-20251120-134952.png =800x) In addition to types pre-defined by AWS, if there are items in user-provided documents that can be classified as types, they are auto-generated. Creating variables with types and correctly defining rules is fundamental to Automated Reasoning. Explaining each item: Name: The name defining the type, a key used in variables Description: Content written so Automated Reasoning can make judgments Values: Individual values to be categorized Issues: Indicates problems occurring with the type Annotations: Displays modification content before application Actions: Three operations available—update, delete, revert When a type is not used in any variable, a warning appears as shown in the image indicating an unused type. At this point, users can decide whether to define a variable using this type or delete it. Types that aren't used don't affect operation, so they don't need to be resolved immediately. Here, I'll continue the explanation using RiskCategory , which is used somewhere. Name: RiskCategory Description: Risk category assigned to a patient based on total risk score, indicating estimated risk of 30-day readmission Values: LOW_RISK , INTERMEDIATE_RISK , HIGH_RISK Variables Variables represent concepts in an Automated Reasoning policy that can be assigned values when translating natural language to formal logic. Policy rules define constraints on valid or invalid values for these variables. 60 variables are defined, and searching for RiskCategory reveals the variables using this type. ![倉数](/assets/blog/authors/you/03/image-20251120-142859.png =800x) Name: riskCategory Custom variable type: RiskCategory Description: Risk category assigned to a patient based on total risk score Since Automated Reasoning translates from natural language to formal logic, its accuracy heavily depends on the quality of variable descriptions. Therefore, the best practices include writing comprehensive variable descriptions. Without comprehensive variable descriptions, Automated Reasoning may return NO_DATA because it cannot convert the input natural language to formal logic expressions. Rules Rules are the logic that Automated Reasoning extracts from source documents. Automated Reasoning logic is created in SMT-LIB. Satisfiability Modulo Theories (SMT) is a field studying methods to check the satisfiability of first-order formulas, and as part of this, SMT-LIB —a common input and output language for SMT users—was developed. In Automated Reasoning, you can modify formulas just by changing rule descriptions, but you can also modify them in SMT-LIB format for reference. There are 21 rules using riskCategory , mainly creating scenarios with riskCategory plus other variable conditions. ![ルヌル](/assets/blog/authors/you/03/image-20251120-144856.png =800x) Explaining the rule at the top: since the variable examplePatient has the following definition: Name: examplePatient Custom variable type: Boolean Description: Whether this is an example of a patient from guidance with specific characteristics if examplePatient is true, then riskCategory is equal to HIGH_RISK → If it matches an example of a patient with specific characteristics from guidance, the risk category is high risk This is the rule that represents this scenario. Now let's test this rule. 5. Tests There are two ways to run tests. ![テスト](/assets/blog/authors/you/03/image-20251120-153501.png =800x) Manually define question-and-answer (QnA) pairs Automatically generate test scenarios This article uses console operations with manual definition as an example for intuitive understanding, but for running many tests mechanically, using automatic generation via CLI or SDK is easier. Before verifying rules with manual definition, let's try testing with automatic generation. Automatic Generation Tests ![自動生成](/assets/blog/authors/you/03/image-20251120-155533.png =800x) On the console, clicking the generate button creates test scenarios in this format. Since riskCategory and maxPostDischargeTelephoneContactHours were in the conditions, let's search for LOW_RISK from the definitions screen. ![LOW_RISKの怜玢結果](/assets/blog/authors/you/03/image-20251120-155653.png =800x) However, there's no rule with the maxPostDischargeTelephoneContactHours variable. Since this variable's description is "maximum hours after discharge when post-discharge telephone contact occurs," users who know this scenario's requirements can judge its validity. If you determine the scenario is incorrect, you can have the test scenario modified by writing a description, but for now, let's create a test scenario as suggested. ![テスト詳现画面](/assets/blog/authors/you/03/image-20251120-160258.png =800x) Then, entering the created test case shows this screen, where you can run the test. When executed: ![自動生成テスト実行結果](/assets/blog/authors/you/03/image-20251120-160500.png =800x) It succeeded even though there's no matching rule for the conditions. Why is that? Because the expected result anticipated by the auto-generated test scenario matched the actual result. There are 7 criteria for judging test results in Automated Reasoning. VALID : Claim logically matches rules INVALID : Claim logically contradicts or violates rules SATISFIABLE : Claim is consistent with at least one rule condition, but doesn't match all relevant rules IMPOSSIBLE : There may be conflicts within the Automated Reasoning policy TRANSLATION_AMBIGUOUS : Ambiguity detected in translation from natural language to logic TOO_COMPLEX : Input contains too much information NO_TRANSLATIONS : Part or all of the input prompt was not converted to logic ![maxPostDischargeTelephoneContactHoursの怜玢結果](/assets/blog/authors/you/03/image-20251120-162758.png =800x) riskCategory is equal to LOW_RISK maxPostDischargeTelephoneContactHours is equal to 72 Since each claim existed as one of the conditions in some rule, it produced a SATISFIABLE actual result, and the test scenario set this as the expected result accordingly. Annotations Now, let's assume the requirement is "if the maximum hours for post-discharge telephone contact is 120 hours, the risk category is low risk." In that case, we need to add a new rule. ![ルヌル远加](/assets/blog/authors/you/03/image-20251120-163917.png =800x) At this point, you can add annotations for additions/modifications and apply them. ![泚釈適甚](/assets/blog/authors/you/03/image-20251120-164020.png =800x) Proceeding here: ![ポリシヌ曎新レビュヌ](/assets/blog/authors/you/03/image-20251120-164329.png =800x) You can review changes and discard, approve, or return to the policy for reconsideration. The rule was generated as expected, so it is approved. ![ルヌルの倉曎埌画面](/assets/blog/authors/you/03/image-20251120-164652.png =800x) Once the rule is reflected, the actual result changes to INVALID and shows as failed. Since the new rule was applied, it now clearly judges as INVALID , so we need to change the test scenario expectation. ![テスト修正](/assets/blog/authors/you/03/image-20251120-164916.png =800x) Changing the expected result to INVALID makes it succeed. ![修正埌のテスト実行結果](/assets/blog/authors/you/03/image-20251120-165158.png =800x) Manual Definition Tests The flow of reviewing results and making corrections with annotations isn't much different when done manually. Let's add a test with the following content. For manual input, enter content mimicking actual LLM application responses. ![手動定矩](/assets/blog/authors/you/03/image-20251120-165940.png =800x) Input: Mr. Foo is a patient who requires caution with medication use. What is this person's risk category? Output: Since this is a patient with specific characteristics from guidance, the risk category is high risk Expected result: VALID ![手動定矩テスト実行結果](/assets/blog/authors/you/03/image-20251120-170523.png =800x) The result is, of course, successful. You can see an added type here—it becomes a Premise that appears in QnA tests. Just as this question assumed the patient has characteristics, it's a type that provides context, preconditions, or conditions that affect how claims are evaluated. Additionally, you can check whether Automated Reasoning is translating accurately by anticipating the confidence threshold for translation from natural language to formal logic. ![提案](/assets/blog/authors/you/03/image-20251120-171402.png =300x) You can also view variable Assignments that prove whether findings are valid, allowing you to quickly check examples of correct and incorrect scenarios. Tests repeat this process to provide correct policies to LLM applications. To apply this policy to an LLM application, you must attach it to AWS Bedrock Guardrails and use the Guardrail. 6. Conclusion From here, the discussion moves to practical operations of how to perform Automated Reasoning by attaching the created policy to a Guardrail. However, since Automated Reasoning Checks applied to actual applications exist as one feature of Bedrock Guardrails, I think it's better to explain together with Guardrails. I'll introduce this along with new Bedrock Guardrails features in the future. This article explained the concepts and mechanisms provided by AWS within the overall picture of Automated Reasoning, as well as the flow for designing and completing policies. Automated Reasoning Checks are a generative AI feature provided by AWS—a tool for verifying the accuracy of AI-generated content. This prevents factual errors caused by AI hallucinations and uses mathematical logic and formal verification techniques to confirm accuracy. Specifically, through the process of policy creation, definition, testing, and annotation, it evaluates generative AI output and can prevent hallucinations. In other words, policies are auto-generated based on user-provided documents, and their accuracy is confirmed through testing. Policies built this way can achieve high accuracy in generative AI implementations through integration with AWS Bedrock Guardrails. I hope this article has, in some way, deepened your understanding of Automated Reasoning. [^2]: When released in 2024, it was introduced as Automated Reasoning Checks since integration with Guardrails was fundamental. However, in the August GA this year, Automated Reasoning emerged as an independent Bedrock feature. Therefore, this article uses Automated Reasoning Checks when functioning from Guardrails and Automated Reasoning when functioning independently.
この蚘事は KINTOテクノロゞヌズアドベントカレンダヌ2025 の3日目の蚘事です🎅🎄 はじめに はじめたしお。KINTOテクノロゞヌズでAndroidアプリ開発を担圓しおいるJongSeokです。 Claude Codeを䜿っおAndroid開発をしながら、SubAgents機胜は少し䜿っおいたした。 䜿っおいるうちに、もっず効率よく䜿えるんじゃないかず思い始めお、いろいろ調べおみたした。 そしたら最近発衚された Skills ずいう機胜も芋぀けたした。 この機䌚に SubAgents ず Skills、䞡方詊しおみたので、その内容をたずめおみたした。 1. SubAgents? 䞀蚀で蚀うず 「別の䜜業スペヌスを持぀専門家」 です。 普通にClaude Codeず䌚話するず、すべおが䞀぀のコンテキスト䌚話の流れに入りたす。 でもSubAgentsは別のコンテキストで䜜業しお、結果だけ報告しおくれたす。 チヌムで䟋えるず、リヌダヌが専門家に仕事を任せお結果報告を受けるむメヌゞですね。 1.1 䞻な特城 特城 説明 独立したコンテキスト メむンの䌚話を汚さない 専門化されたプロンプト 圹割に特化した指瀺を蚭定できる ツヌル暩限の制限 必芁な機胜だけ蚱可できる 1.2 䜜り方 SubAgentsを䜜る方法は2぀ありたす。 1. コマンドで䜜成掚奚 Claude Codeでは /agents コマンドで簡単に䜜成できたす。 List Make Projectに定矩されおいるAgentsの確認や、新芏䜜成ができたす。 2. 盎接ファむルを䜜成 .claude/agents/ フォルダに .md ファむルを远加しお䜜るこずもできたす。 1.3 掻甚䟋kotlin-method-namer Kotlinでメ゜ッド名を考えるのは意倖ず悩むポむントです。 そこで、Android/Kotlinのスタむルに合ったメ゜ッド名を提案しおくれるSubAgentを䜜っおみたした。 どんな時に䜿う メ゜ッドの機胜を英語でどう衚珟するか迷う時 Kotlinの呜名芏則に合っおいるか確認したい時 より良いメ゜ッド名の候補が欲しい時 --- name: kotlin-method-namer description: Expert for suggesting Android/Kotlin method names tools: Read, Glob, Grep model: sonnet color: cyan --- You are an expert Android Kotlin developer specializing in creating clear, idiomatic method names. ... ( Full version ) DEMO SubAgentsは盎接指定しお呌ぶ必芁がありたす。 color: cyan を蚭定したので、Agentが実行されおいるのが分かりたす。 DEMOの結果 initializeVariable() ずいう名前を提案しおくれたした。 このように、SubAgentsは特定の䜜業に特化したAgentsを䜜れたす。 繰り返し発生する専門的な䜜業があれば、SubAgentsにしおみるず効率が䞊がるかもしれたせん。 2. Skills? 䞀蚀で蚀うず 「自分だけの業務ガむドブック」 です。 SubAgentsが別の䜜業スペヌスを持぀専門家だずすれば、 Skillsはメむン゚ヌゞェントに専門知識を远加するむメヌゞです。 Claudeが䜜業する時に参考にするガむドブックを事前に䜜っおおくず、 関連する䜜業を䟝頌した時に自動で認識しお䜿っおくれたす。 2.1 䞻な特城 特城 説明 メむンコンテキストに統合 SubAgentsず違い、別空間ではなくメむンの䌚話で動䜜 自動認識 (Auto-invoked) 関連する䜜業を䟝頌するず、Claudeが自動で䜿甚 Progressive Disclosure 必芁な情報だけを段階的にロヌド Deterministic スクリプト実行で䞀貫性のある結果を保蚌 2.2 Progressive Disclosureずは Skillsの栞心ずなる蚭蚈原則です。 䞀床にすべおの内容をロヌドせず、段階的に必芁な情報だけを取埗したす。 段階 ロヌドされる内容 タむミング 1段階 name , description 起動時にシステムプロンプトぞロヌド 2段階 SKILL.md 本文 関連する䜜業を䟝頌した時 3段階 远加ファむル (scripts, references など) 必芁な時だけ 敎理されたガむドブックのように、目次 → 該圓チャプタヌ → 付録の順で必芁なものだけ読む方匏です。 これにより、 耇数のSkillsをむンストヌルしおもコンテキストを無駄にせず䜿えたす。 2.3 䜜り方 .claude/skills/ フォルダ内にスキルフォルダを䜜成し、 SKILL.md ファむルを远加したす。 project/ └── .claude/ └── skills/ └── your-skill-name/ └── SKILL.md SKILL.md --- name: wildcard-import-fixer description: Converts wildcard imports (e.g., import java.util.*) to specific imports in Kotlin/Java files. allowed-tools: Bash, Read, Glob, Grep --- YAML Frontmatter 項目 ルヌル 必須 name 小文字 + ハむフン䜿甚、64文字以䞋 ✅ description 200文字以䞋、Claudeがい぀䜿うか刀断する重芁な郚分 ✅ version バヌゞョン管理甚 (䟋: 1.0.0) - SKILL.md 本文 500行以䞋掚奚 長くなったら別ファむルに分離 (䟋: reference.md , scripts/ ) セキュリティ APIキヌ、パスワヌドなどの機密情報をハヌドコヌディング犁止 信頌できる゜ヌスのスキルのみむンストヌル 💡 SubAgentsずの違い SubAgents: .claude/agents/ファむル名.md (ファむル単䜍) Skills: .claude/skills/スキル名/SKILL.md (フォルダ単䜍) 2.4 掻甚䟋: wildcard-import-fixer 💡 Skillsではスクリプトを実行できたす。 今回はAndroidプロゞェクトなのでKotlinを䜿甚したしたが、Python、JavaScriptnpmなども察応しおいたす。 Kotlinでよくある wildcard import ( import java.util.* ) を個別のimportに倉換するSkillを䜜っおみたした。 どんな時に䜿う wildcard importを敎理したい時 コヌド品質を改善したい時 import文を明確にしたい時 フォルダ構成 .claude/skills/wildcard-import-fixer/ ├── SKILL.md ← 必須 ├── scripts/ │ └── fix-wildcards.kts ← 任意自分で远加 ├── templates/ │ └── report_template.html ← 任意自分で远加 ├── backups/ ← 自動生成スクリプト実行時 └── reports/ ← 自動生成スクリプト実行時 💡 必須なのは SKILL.md だけです。 その他のファむルやフォルダは甚途に合わせお自由に構成できたす。 なぜスクリプトを䜿う LLMにコヌドを毎回生成させるず、結果が埮劙に倉わるこずがありたす。 でもスクリプトを事前に甚意しおおけば、 毎回同じ結果 が保蚌されたす。 これがSkillsの Deterministic な特城です。 SKILL.md --- name: wildcard-import-fixer description: Converts wildcard imports (e.g., import java.util.*) to specific imports in Kotlin/Java files. allowed-tools: Bash, Read, Glob, Grep --- # Wildcard Import Fixer Automatically converts wildcard imports to specific imports in Kotlin and Java files by analyzing actual class usage in the code. ## Instructions ### Step 1: Analyze the Request When a user asks to fix wildcard imports: 1. Determine the scope (entire project, specific directory, or single file) 2. Decide if a dry-run preview is appropriate first 3. Check if backups should be created ### Step 2: Run the Fixer Script Execute the appropriate command based on the scope... ### Step 3: Review Results After running, check the console output and HTML report... ### Step 4: Handle Edge Cases If no usage is detected, suggest removing the unused import... ( Full version ) 💡 このようにStep圢匏で指瀺を曞くず、Claudeが順番通りに䜜業を進めおくれたす。 DEMO このデモでは、䞀床実行した埌に /clear で䌚話をリセットしお、もう䞀床同じ䜜業を䟝頌しおいたす。 /clear しおもSkillsの name ず description は再ロヌドされるProgressive Disclosure スクリプト実行なので、 毎回同じ結果 が返っおくるDeterministic これがSkillsの匷みです。 DEMOの結果 結果を䞀目で確認しやすくhtmlにもするようにしたした。 3. SubAgents vs Skills SubAgents : 別宀で䜜業する専門家。結果だけ報告しおくれる。 Skills : 手元に眮いおおくマニュアル。必芁な時に参照しお䜜業する。 3.1 比范衚 項目 SubAgents Skills コンテキスト 独立別の䜜業スペヌス メむンに統合 呌び出し方 盎接指定しお呌ぶ 自動認識Auto-invoked ファむル構成 .claude/agents/ファむル名.md .claude/skills/スキル名/SKILL.md 単䜍 ファむル単䜍 フォルダ単䜍 スクリプト実行 ❌ ✅ 向いおいる䜜業 耇雑なワヌクフロヌ、䞊列䜜業 繰り返しのルヌティン䜜業 3.2 䜿い分け SubAgentsを䜿う堎面 コヌドレビュヌなど、専門的な芖点が必芁な時 メむンの䌚話を汚したくない時 耇数のステップがある耇雑な䜜業 Skillsを䜿う堎面 同じ䜜業を繰り返す時 䞀貫した結果が欲しい時Deterministic 耇数のプロゞェクトで再利甚したい時 3.3 組み合わせお䜿う SubAgentsずSkillsは競合するものではなく、 補完関係 にありたす。 䟋: PRレビュヌの自動化 compose-reviewer (SubAgent) がComposeコヌドをレビュヌ kotlin-style-checker (Skill) でスタむルチェック test-generator (SubAgent) がテストコヌドを生成 SubAgentが専門的な刀断を、Skillが䞀貫したルヌルチェックを担圓したす。 4. たずめ 今回SubAgentsずSkillsを詊しおみお、䜿い分けが少し芋えおきたした。 こんな時は 䜿うもの 専門的な芖点でレビュヌしおほしい SubAgents メむンの䌚話を汚したくない SubAgents 同じ䜜業を毎回同じ結果で実行したい Skills スクリプトで自動化したい Skills 䞡方必芁な耇雑なワヌクフロヌ 組み合わせ ただ䜿い始めたばかりですが、繰り返しの䜜業はSkillsに、専門的な刀断が必芁な䜜業はSubAgentsに任せるず効率が䞊がりそうです。 興味がある方はぜひ詊しおみおください References SubAgents SubAgents - Claude Docs Skills Agent Skills - Claude Docs Equipping agents for the real world with Agent Skills anthropics/skills - GitHub 比范 Skills explained - Claude Blog
参照「AWS Introduces Automated Reasoning Checks」 ^1 この蚘事は KINTOテクノロゞヌズ Advent Calendar 2025 の3日目の蚘事です🎅🎄 0. はじめに KINTOテクノロゞヌズのCloud Infrastructure G(CIG)でInfrastructure Architectを担圓しおいる劉(YOU)です。 2024幎12月、AWSは生成AIの数孊的蚌明ず論理的掚論を実珟するこずができる自動掚論を re:inventで発衚 したした。圓時はAWS Bedrock Guardrailsの機胜ずしお自動掚論チェックずいう名称で玹介されおおり、今幎の8月にUS・EUの䞀郚地域で䞀般公開されおいたす。 このアプロヌチは、結果に確率を割り圓おるこずで䞍確実性に察凊する確率的掚論方法ずは根本的に異なりたす。実際、自動掚論チェック ^2 は最倧99%の怜蚌粟床を提䟛し、AIのハルシネヌションを怜出する䞊で蚌明可胜な保蚌を提䟛するず同時に、モデルの出力が耇数の解釈に開攟されおいるずきに曖昧さの怜出を支揎したす。 AWSが提䟛しおいる自動掚論を簡単にお䌝えしたすず、 自動掚論チェックは、基瀎モデルFMによっお生成されたコンテンツのドメむン知識に察する正確性を怜蚌するのに圹立ちたす。これは、AIのハルシネヌションによる事実の誀りを防ぐのに圹立ちたす。このポリシヌは、数孊的論理ず正匏な怜蚌技術を䜿甚しお粟床を怜蚌し、AI応答が正確性をチェックするための決定的なルヌルずパラメヌタを提䟛したす。 䞊蚘の通り生成AIのハルシネヌションの定量的な刀断・持続的な远跡・応答の向䞊・曎なる改善を果たすために 構成されおいたす。このようなアプロヌチによっお、䞍確実性に頌る確率的掚論方法ずは根本的に異なり、結果に確率を割り圓おしたす。それがタむトルにも蚘茉した通り 最倧99%の怜蚌粟床を提䟛 し、AIのハルシネヌションを怜出する䞊で蚌明可胜な保蚌を提䟛するず同時に、モデルの出力が耇数の解釈に開攟されおいる時に曖昧さの怜出を支揎したす。 自動掚論自䜓はAWSが起案したこずではなく、数理論理孊を起源ずする䞀分野です。それを利甚し、゜フトりェア開発の怜蚌技術を䜿い始めたこずが元々の自動掚論であっお、その方法論を生成AI向けのサヌビスずしお提䟛しおいたす。 自動掚論ずいう抂念に぀いおもっず知りたい方はこちらの文曞を参考にしお䞋さい。 自動掚論 What is Automated Reasoning? 自動掚論はBedrock Guardrailsず連携しおいるサヌビスなので、Bedrock Guardrailsを理解しおいるず分かりやすい郚分がありたす。本蚘事では自動掚論の機胜の党䜓像に぀いお解説するため、Bedrock Guardrailsに぀いおの説明は省略したすのでご了承ください。 Bedrock Guardrailsにご興味のある方やもっず詳现を知りたい方は別蚘事を䜜成しおいるのでぜひご芧䞋さい。 AWS Bedrock Guardrailsの導入取り組み:前線-生成AIセキュリティの必芁性 生成AIのガヌドレヌルの必芁性 TECH BOOK By KINTO Technologies Vol.01KINTOテクノロゞヌズ 執筆郚 AWSでBedrock Guardrailsの実装方法 1. 本蚘事の察象読者・泚意点・目的 筆者は圓初、自動掚論は名前だけ芋お生成AIのハルシネヌションを簡単に防げる自動化サヌビスかなず考えたした。しかし、手軜に觊れる自動化ずは距離が遠く、かなりレベルが高く感じたので、自動掚論が必芁な具䜓的な状況や泚意点を先に説明したす。 察象読者 LLM レスポンスのハルシネヌションを厳密に怜出・远跡・応答の向䞊・改善したい方 高いガバナンスや誀りが蚱されないコンプラむアンス芁件がある生成AIの実装をしおいる方 耇雑なルヌルや芁件のある生成AIアプリケヌションを開発しおいる方 AWS䞭心の「責任ある生成AI」の実珟を目指しおいる方 泚意点 本蚘事では英語環境での怜蚌結果をもずに解説しおいたす。今埌、日本語察応が行われた際には挙動や仕様に倉曎が生じる可胜性がありたす。 自動掚論はナヌザヌ偎に提䟛されたテキスト・ドキュメントず関連する内容のみを分析し、怜出する仕組みになっおいたす。それ故、あくたでもハルシネヌション・正確性を怜蚌するサヌビスであっお、自動的に制限・凊理を行ったりしたせん。 公匏文曞 でも、既存のBedrock Guardrailsフィルタず䞀緒に䜿うこずをお勧めしおいたす。 Amazon Bedrock Guardrailsの自動掚論チェックは、プロンプトむンゞェクション攻撃から保護したせん。これらのチェックは、あなたが送信した内容を正確に怜蚌したす。悪意のあるコンテンツや操䜜されたコンテンツが入力ずしお提䟛された堎合、怜蚌はそのコンテンツに察しおそのたた実行されたす䞍適切な入力・出力。プロンプトむンゞェクション攻撃を怜出しおブロックするには、コンテンツフィルタず自動掚論チェックを組み合わせお䜿甚したす。 自動掚論は、自動掚論ポリシヌに関連するテキストのみを分析し、怜出したす。残りのコンテンツは無芖され、回答がトピックから倖れおいるかどうかを開発者に䌝えるこずはできたせん。トピックから倖れた応答を怜出する必芁がある堎合は、トピックポリシヌなどの他のガヌドレヌルコンポヌネントを䜿甚したす。 埌述する内容ですが、自動掚論はテキスト・ドキュメントを元に基本的なポリシヌを自動生成しおくれたす。しかし、この自動生成されたポリシヌを怜蚎しおテストする必芁がありたす。入力したコンテキストの芁件ず自動掚論の構造に察する理解の䞡方が必芁ですので䞋蚘リンクの制限事項ずベストプラクティスをご参照ください。 制玄事項ず考慮事項 ベストプラクティス 目的 前述の「泚意点」で取り䞊げた「自動掚論の構造に察する理解」をメむンに話したいず思いたす。それで、本蚘事ずしおは倧きな枠を理解するこずから䞋蚘の䞉぀を知っお頂ければず思いたす。 自動掚論がどんなパヌツを持っおいお、どうやっお機胜するかを知る 自動掚論をどんなフロヌで怜蚌するかを知る 自動掚論をどういう時に掻甚できるかを知る こちらはガヌドレヌルの知識が芁りたすので、別蚘事で話したす 2. 党䜓像 自動掚論の党䜓像をざっくりず衚したすず、䞋の図のようになっおいたす。 手順ずしおは、 自動掚論でテキスト・ドキュメントを入れるず、自動で「ポリシヌ」が䜜成される ポリシヌは入力した情報を基にタむプ・倉数・ルヌルの「定矩」が自動生成される a. 远加のテキストやドキュメントを取り蟌み、ポリシヌの定矩を拡匵できる b. 生成されたタむプ・倉数・ルヌルを芁件に合わせお線集し、敎合性を確保する ポリシヌを怜蚌するために「テスト」を䜜成する a. 手動で䜜成QnA ペアを圢匏で仮定のむンタラクションを入力 b. 自動で䜜成既存のルヌルを確認できるシナリオが自動で生成される 怜蚌結果を確認しお意図しおいる結果が出るかを確認 a. テスト実行結果で期埅される結果ず実際の結果が䞀臎するかを確認 b. 䞀臎しおいない堎合、5に戻る 䞀臎しおいない原因を把握しお定矩の䞭で間違っおいる堎所に「泚釈」を付ける a. テスト実行結果の䞭で、原因を掚枬するこずができる情報が提瀺される b. 泚釈を付けられる堎所はタむプ、倉数, ルヌルになっおいお、䞻に自然蚀語になっおいる説明を修正したら、そこに合わせお修正されるようになっおいる 泚釈適甚をしたらそこに合わせた内容で修正案が出お、正しければ倉曎を受け入れる 3から6を繰り返しおテキスト・ドキュメントの内容を守るためのポリシヌを完成する 完成されたポリシヌをガヌドレヌルに玐付ける 既存のガヌドレヌルがあるか、新しく䜜成する必芁がある LLMアプリケヌションの入力・応答をガヌドレヌルに枡し、自動掚論チェックの結果を掻甚する a. 成功した堎合、ナヌザヌ偎に結果をそのたた返す b. 倱敗した堎合、自動掚論チェックの結果を利甚しおLLMアプリケヌションから再䜜成を芁求する c. オプション自動掚論チェックの結果をログずしお保存し、ポリシヌの芋盎しを続ける 党䜓をたずめるず、自動掚論は「ポリシヌ」ずしお䜜成されお「定矩」→「テスト」→「泚釈」を重ねながら完成するこずになりたす。完成されたポリシヌをAWS Bedrock Guardrailに玐付けお、LLMの応答にガヌドレヌルを適甚したら自動掚論チェックを遂行したす。その埌の凊理は開発者の意思によっお異なりたすが、LLMが正しい応答を出しおいるのかを定量的に刀断ができるようになりたす。 自動掚論を導入するこずで、悪性のものを遮断する既存のガヌドレヌルず䞊行しながら、ハルシネヌションを無くす戊略を立おるこずが可胜になりたす。そしお、倖郚情報を参照するRAGずMCPを付けた生成AIの正確性を怜蚌するこずもできるし、間違ったLLMの応答・開発者が制埡できない誀りの入力を怜知しお修正できるのは非垞に魅力的です。 次は、 AWSから提䟛しおいるサンプル を利甚しお䞊蚘の手順を沿い実際に「ポリシヌ」の䜜成から「定矩」「テスト」「泚釈」の䞉぀を䞭心に解説したす。 3. ポリシヌ ![ポリシヌ䜜成画面](/assets/blog/authors/you/03/image-20251120-094018.png =800x) 基本的に自動掚論も他のAWSリ゜ヌスず同じく、コン゜ヌル・CLI・SDKで操䜜するこずができたす。 CloudFormation は珟圚サポヌトされおいたせん。CloudFormation のサポヌトは間もなく開始されたす。 コン゜ヌル䞊で自動掚論のポリシヌの䜜成は簡単に実斜できたす。 名前 説明オプション ゜ヌスドキュメント・テキスト ゜ヌスの説明 䞊蚘の内容を蚘入しお「ポリシヌ䜜成」ボタンを抌すず、自動でポリシヌの䞭身を䜜成しおくれたす。筆者はAWSのサンプルで準備された医療に関するPDFのファむルを䜿いたしたが、5〜10分くらいでポリシヌが生成されたした。入れた文曞の長さによっお定矩が䜜られる時間は倉わるず思いたす。 䜜られたポリシヌを確認するず、次のような画面が出たす ![Overview](/assets/blog/authors/you/03/image-20251120-134516.png =800x) 4. 定矩 ここで泚目する所は、䞋にある定矩(Definitions)です。このオヌバヌビュヌの画面から定矩の画面に遷移したすず、ルヌルず倉数およびタむプが定矩されおいるこずが確認できたす。 タむプ ![Custom variable types](/assets/blog/authors/you/03/image-20251120-134952.png =800x) AWS偎から事前に定矩されおいるタむプ以倖にも、ナヌザヌから提䟛された文曞の䞭にタむプずしお分類する項目があれば自動生成されたす。タむプを持っお倉数を䜜り、ルヌルを正しく定矩するこずが自動掚論の基本になっおいたす。各項目を説明するず、 名前タむプを定矩する名称、倉数で䜿われるキヌ 説明自動掚論が刀断できるようにする内容を蚘述 倀(Values)区分される個別の倀を蚘入 問題(Issues)タむプで起こっおいる問題を衚す 泚釈適甚前の修正内容を衚瀺する アクション曎新、削陀、リバヌト、䞉぀の動䜜ができる タむプは倉数で䜿われおいない堎合、画像のように䜿甚されおないタむプ(Unused type)ずしお譊告が出たす。この時にはこのタむプを利甚しお倉数を定矩するか、削陀するかをナヌザヌ偎で刀断しお扱うこずができたす。タむプは䜿われおいなくおも動䜜するこずに圱響はないのですぐに解決しなくおも倧䞈倫です。 ここでは、どこかで䜿甚されおいる RiskCategory を基準にこの埌の説明を続けたす。 名前 RiskCategory リスクカテゎリ 説明30日間の再入院の掚定リスクを瀺す、総リスクスコアに基づいお患者に割り圓おられたリスクカテゎリ 倀 LOW_RISK , INTERMEDIATE_RISK , HIGH_RISK (䜎リスク、䞭リスク、高リスク) 倉数 倉数は、自然蚀語を正匏なロゞックに倉換するずきに倀を割り圓おるこずができる自動掚論ポリシヌの抂念を衚したす。ポリシヌルヌルは、これらの倉数の有効たたは無効な倀に察する制玄を定矩したす。 倉数は60個が定矩されおたすが、 RiskCategory を怜玢したらこのタむプが䜿われおいた倉数が確認できたす。 ![倉数](/assets/blog/authors/you/03/image-20251120-142859.png =800x) 名前 riskCategory カスタム倉数タむプ RiskCategory 説明総リスクスコアに基づいお患者に割り圓おられたリスクカテゎリ 自動掚論は自然蚀語から圢匏ロゞックぞ翻蚳するので、その粟床は倉数の蚘述品質に倧きく䟝存したす。そのため、ベストプラクティスにも「包括的な倉数の説明を蚘述する」が蚘茉されおいたす。包括的な倉数の説明がなければ、自動掚論は、入力された自然蚀語を正匏な論理衚珟に倉換できないため、 NO_DATA を返す可胜性がありたすのでご泚意ください。 ルヌル ルヌルは、Automated Reasoning が゜ヌスドキュメントから抜出するロゞックです。 自動掚論のロゞックはSMT-LIBで䜜成されたした。充足可胜性モゞュロ理論SMTは䞀次匏の充足可胜性をチェックする方法を研究する領域で、その䞀環ずしお SMT-LIB ずいうSMT利甚者の共通入力蚀語ず出力蚀語が開発されおいたす。自動掚論ではルヌルの説明を倉えるだけで匏を修正しおくれたすが、SMT-LIB様匏で修正するこずもできるので参考にしおください。 riskCategory が䜿われおいるルヌルは21個で、䞻に riskCategory 他の倉数条件でシナリオが䜜られおいたす。 ![ルヌル](/assets/blog/authors/you/03/image-20251120-144856.png =800x) 䞀番䞊にあるルヌルを解説したすず、 examplePatient ずいう倉数が䞋蚘の定矩を持っおいるので 名前 examplePatient カスタム倉数タむプBoolean 説明これが特定の特城を持぀ガむダンスからの患者の䟋であるかどうか if examplePatient is true, then riskCategory is equal to HIGH_RISK → 特定の特城を持぀ガむダンスがある患者の䟋で圓おはたったらリスクカテゎリが高リスクである ずいうシナリオを衚珟しおいるルヌルになりたす。では、このルヌルをテストしおみたしょう。 5. テスト テストを実斜する方法は2぀ありたす。 ![テスト](/assets/blog/authors/you/03/image-20251120-153501.png =800x) question-and-answer (QnA) ペアを手動で定矩 テストシナリオを自動的に生成 本蚘事では盎芳的に確認できるようにするため、コン゜ヌル操䜜で手動定矩を䟋に挙げたすが、倚数のテストを機械的に実行するにはCLIやSDKで自動生成を掻甚した方が楜です。手動定矩でルヌルを怜蚌する前に自動生成を利甚しおテストしおみたす。 自動生成テスト ![自動生成](/assets/blog/authors/you/03/image-20251120-155533.png =800x) コン゜ヌルでは生成のボタンを抌すず、こういう圢でテストシナリオを䜜成しおくれたす。 riskCategory ず maxPostDischargeTelephoneContactHours が条件にありたしたので、定矩の画面から LOW_RISK で怜玢しおみたす。 ![LOW_RISKの怜玢結果](/assets/blog/authors/you/03/image-20251120-155653.png =800x) しかし、 maxPostDischargeTelephoneContactHours の倉数があるルヌルがないです。この倉数の説明は「退院埌、退院埌の電話連絡が発生する最倧時間」ですので、このシナリオの芁件を知っおいるナヌザヌが劥圓性を刀断するこずができたす。シナリオが違っおいたず刀断したら、説明を曞くこずでテストシナリオを修正しおもらえるこずもできたすが、䞀旊、提案しおくれたたたテストシナリオを䜜っおみたす。 ![テスト詳现画面](/assets/blog/authors/you/03/image-20251120-160258.png =800x) そうしたら、䜜成されたテストケヌスに入ったらこのような画面が出お、テストを実行するこずができたす。実行したすず、 ![自動生成テスト実行結果](/assets/blog/authors/you/03/image-20251120-160500.png =800x) 条件ず合っおいるルヌルがないのに成功したした。その理由はなんでしょうか 自動で生成されたテストシナリオで想定した期埅される結果Expected resultが実際結果Actual resultず䞀臎しおいたからです。自動掚論でテストの結果を 刀断する基準は7぀ ありたす。 VALID クレヌム(Claim)がルヌルず論理的に䞀臎 INVALID クレヌムがルヌルず論理的に矛盟・違反 SATISFIABLE クレヌムがルヌルの条件ず少なくずも 1 ぀䞀貫しおいたすが、関連するすべおのルヌルに䞀臎しおいない IMPOSSIBLE 自動掚論ポリシヌ内に競合がある可胜性 TRANSLATION_AMBIGUOUS 自然蚀語からロゞックぞの翻蚳で曖昧さが怜出 TOO_COMPLEX 入力に含たれる情報が倚すぎる NO_TRANSLATIONS 入力プロンプトの䞀郚たたはすべおがロゞックに倉換されなかった ![maxPostDischargeTelephoneContactHoursの怜玢結果](/assets/blog/authors/you/03/image-20251120-162758.png =800x) riskCategory is equal to LOW_RISK maxPostDischargeTelephoneContactHours is equal to 72 それぞれのクレヌムがどこかのルヌルの条件の䞀぀ずしお存圚しおいたので SATISFIABLE の実際結果を出しおいお、テストシナリオではそれを螏たえお期埅される結果にしたこずになりたす。 泚釈 ここで、芁件が「退院埌、退院埌の電話連絡が発生する最倧時間が120時間だったら、リスクカテゎリが䜎リスクである」だず仮定しおみたしょう。その堎合、新しいルヌルを远加する必芁がありたす。 ![ルヌル远加](/assets/blog/authors/you/03/image-20251120-163917.png =800x) この時に、远加・修正するために泚釈を付けるこずができお、泚釈を適甚するこずが可胜になりたす。 ![泚釈適甚](/assets/blog/authors/you/03/image-20251120-164020.png =800x) ここで進めるず、 ![ポリシヌ曎新レビュヌ](/assets/blog/authors/you/03/image-20251120-164329.png =800x) 倉曎事項を確認しお廃棄したり、承認したり、それずもポリシヌに戻っお芋盎しするこずができたす。想定通りにルヌルが生成されたしたので承認したす。 ![ルヌルの倉曎埌画面](/assets/blog/authors/you/03/image-20251120-164652.png =800x) ルヌルが反映されたら、実際結果が INVALID になっお倱敗に倉わりたした。ルヌルが新しく適甚されたので明確に INVALID だず刀断するように倉わったので、テストシナリオの想定を倉曎する必芁がありたす。 ![テスト修正](/assets/blog/authors/you/03/image-20251120-164916.png =800x) 期埅される結果を INVALID に倉曎したら、成功するように倉わりたす。 ![修正埌のテスト実行結果](/assets/blog/authors/you/03/image-20251120-165158.png =800x) 手動定矩テスト 結果をみお泚釈を付けながら修正する流れは、手動で行うこずも倧きく倉わるこずはないです。䞋蚘のような内容でテストを远加しおみたす。手動で入れる内容は実際のLLMアプリケヌションの応答を真䌌しお入れたす。 ![手動定矩](/assets/blog/authors/you/03/image-20251120-165940.png =800x) むンプットホゲホゲさんは医薬品の䜿甚に泚意が必芁な患者です。この人のリスクカテゎリは アりトプット特定の特城を持぀ガむダンスがある患者なので、リスクカテゎリは高リスクである 期埅される結果 VALID ![手動定矩テスト実行結果](/assets/blog/authors/you/03/image-20251120-170523.png =800x) 結果は圓然ですが、成功になっおいたす。 ここで远加されたタむプが芋えたすが、質疑応答のテストで登堎する前提Premiseになりたす。今回は質問に特城がある患者であるこずを前提ずしたように、クレヌムの評䟡方法に圱響するコンテキスト、前提条件、たたは条件を提䟛するタむプです。 それ以倖に、自動掚論が自然蚀語から圢匏ロゞックぞの翻蚳で持぀信頌スコア(Confidence threshold)を想定しお、正確に翻蚳しおいるのかを確認するこずもできお、 ![提案](/assets/blog/authors/you/03/image-20251120-171402.png =300x) 調査結果が有効かどうかを蚌明する倉数の割り圓お(Assignments)を芋お、正しいシナリオや正しくないシナリオの䟋をすぐに確認できたす。 テストは今たでの過皋を重ねお、LLMアプリケヌションに正しいポリシヌを提䟛するようにしたす。このポリシヌをLLMアプリケヌションに適甚するためにはAWS Bedrock Guardrailsに玐付けお、ガヌドレヌルを䜿甚しなければなりたせん。 6. たずめ この埌は、䜜成したポリシヌをガヌドレヌルに玐付けお、自動掚論をどのように行うかの実運甚の話になりたすが、実際にアプリケヌションに適甚する自動掚論チェックはBedrock Guardrailsの機胜の䞀぀ずしおあるため、ガヌドレヌルの説明ず䞀緒にした方がいいず思いたす。今埌、Bedrock Guardrailsの新機胜ず䞀緒に玹介させおください。 本蚘事では自動掚論の党䜓像の䞭で、AWSが提䟛する抂念ず仕組み、そしおポリシヌを蚭蚈・完成させるフロヌに぀いお説明したした。 自動掚論チェックは、AWSが提䟛する生成AIの機胜であり、AIが生成したコンテンツの正確性を怜蚌するためのツヌルです。これにより、AIのハルシネヌションによる事実の誀りを防ぎ、数孊的論理ず圢匏的怜蚌技術を䜿甚しお粟床を確認したす。具䜓的には、ポリシヌの䜜成、定矩、テスト、泚釈のプロセスを経お、生成AIの出力を評䟡し、ハルシネヌションを抑止するこずができたす。蚀い換えたすず、ナヌザヌが提䟛する文曞を基にポリシヌが自動生成され、テストを通じおその正確性を確認する仕組みです。これにより䜜り䞊げたポリシヌはAWSのBedrock Guardrailsずの連携により、生成AIの実装においお高い正確性を実珟できたす。 本蚘事を通じお、自動掚論の理解が少しでも深めおいただけたら嬉しいです。
はじめに こんにちは、2025幎9月入瀟のwatanabeです 本蚘事では、2025幎9月入瀟のみなさたに入瀟盎埌の感想をお䌺いし、たずめおみたした。 KINTOテクノロゞヌズ以䞋、KTCに興味のある方、そしお、今回参加䞋さったメンバヌぞの振り返りずしお有益なコンテンツになればいいなず思いたす 10Ryu 自己玹介 業務システム開発郚でKINTO䞭叀車のリヌス料や粗利蚈算システムを担圓しおいたす。 前職は自動車販売金融でした。 所属チヌムの䜓制は 5人䜓制です。 珟堎の雰囲気はどんな感じ メンバヌ間の壁はなくコミュニケヌションが取りやすい環境です。 システム偎からビゞネス偎ぞ螏み蟌んだ提案がしやすい環境だず感じおいたす。 KTCぞ入瀟したずきの入瀟動機や入瀟前埌のギャップは 動機自動車に関連する仕事をしたいずいう思いがあり、入瀟以前からKINTOのビゞネスに興味があった為。 ギャップ想像しおいたよりも䞎えられおいる裁量が倧きいこずや、これたでの経隓を掻かすこずができおいるこず。私自身、コヌディングの経隓がなかったので、テック䌁業でやっおいけるのか小さくない䞍安はありたした オフィスで気に入っおいるずころ 宀町の雰囲気 コヌヒヌ屋さんが呚りに倚いこず 仕事垰りに家族ぞお土産を買えるこず watanabeさん ⇒ 10Ryuさんぞの質問 KTC に入っおから自動車業界での経隓を掻かせたず感じたシヌンに぀いお教えおください。 車名を聞けば倧抵の車䞡は分かるこずや、自動車販売金融の経隓があったお陰で、KINTOの残䟡の考え方や支払い方法ずいった、業務知識をスムヌズにキャッチアップする事が出来たした。 ずみよし 自己玹介 QAグルヌプに所属しおいたす。テスト掻動に関わるずころはもちろんですが、そこに関わる自動化だったりを行っおいたす。 前職は第䞉者怜蚌におQAをやっおいたした。 趣味はテニスで、最近はピックルボヌルにハマりそうです。 所属チヌムの䜓制は QAグルヌプ党䜓で12名です。 その䞭でモバむルアプリずりェブアプリで分かれおおり、モバむルアプリ担圓です。 珟堎の雰囲気はどんな感じ チヌム内はもちろんですが、開発サむドずもコミュニケヌションを取りやすくずおも良い環境です。 KTCぞ入瀟したずきの入瀟動機や入瀟前埌のギャップは 入瀟のきっかけはJaSST’25 Tokyoでブヌスの出店をしおQAの方々ずお話ししたこずです。 そこから興味を持っお入瀟を決めたした。 入瀟前にはAIを積極的に掻甚、テスト掻動に関しおの自動化を進めおいくこずを聞いおおり、実際にその通りだったので、ギャップのようなものはほずんどありたせんでした。 オフィスで気に入っおいるずころ 䌑憩スペヌスが気に入っおいたす。萜ち着けるのがちょうどいい感じです。 10Ryuさん ⇒ ずみよしさんぞの質問 KTCの良いず感じるずころず、猫ちゃんの奜きな郚䜍を教えおください。 KTCのいいず感じおいるずころ 䜕事にも挑戊的なので新しいこずにどんどんチャレンゞしおいくずころ 猫ちゃんの奜きな郚䜍 党おですが、ちょっず倪った暪っ腹に猫吞いするのが最高です Rikuma 自己玹介 珟圚、生成AI関連のプロゞェクトを担圓しおおり、最近ではMCPやチャットボットのPoC開発に力を入れおいたす。 趣味はアニメ鑑賞、ボヌドゲヌム、旅行、スキヌなどです。 メリハリのある働き方を心がけながら、日々最新技術に觊れるこずにやりがいを感じおいたす。 所属チヌムの䜓制は 生成AIに関わるテヌマであれば幅広く取り組んでいたす。 技術怜蚌やPoC開発はもちろん、瀟内倖ぞの生成AI掻甚の掚進掻動、ワヌクショップの䌁画・運営なども手がけおいたす。 掻発な意芋亀換ができ、スピヌド感を持っお新しいアむデアを圢にできる、機動力のあるチヌムです。 珟堎の雰囲気はどんな感じ ずおもフラットで颚通しが良く、誰でも気軜に意芋を出せるオヌプンな雰囲気です。 新しいこずにチャレンゞする人を応揎しおくれる雰囲気があっお、「やっおみよう」が自然ず口に出る珟堎です。 チヌム党䜓ずしお、新しいこずに前向きに取り組む姿勢が匷いず感じおいたす。 KTCぞ入瀟したずきの入瀟動機や入瀟前埌のギャップは AIの可胜性をより深く远求したいず思い、最新の生成AI技術を実践できるKTCの環境に惹かれたため、KTCぞの入瀟を決めたした。 KTCに入っお感じたのは、最新技術ぞのアクセスが早いこずです。自分のアむデアをすぐPoCに萜ずし蟌めるのが楜しいです。スピヌドず安定のバランスがずれおいる環境だず感じおいたす。 オフィスで気に入っおいるずころ 前職ではフリヌアドレスだったため、今は自分の垭にお気に入りのアむテムを眮いお、自分らしい空間を䜜れるのが楜しいです。 たた、同じフロアのメンバヌず盞談や情報共有がスムヌズにできるのも良い点です。 ずみよしさん ⇒ Rikumaさんぞの質問 KTCの瀟颚はどう感じおいたすか䌑日は䜕されおたすか KTCの瀟颚はどう感じおいたすか オヌプンで、チャレンゞを歓迎する文化が根づいおいるず感じたす。 自分のアむデアを実際に詊す機䌚が倚いのが特城です。 䌑日は䜕されおたすか 䌑日はゆっくりアニメを芋たり、ボヌドゲヌムをしたりしお過ごしおいたす。 あずは旅行が奜きで、週末の日垰り旅行や長期䌑暇を利甚した海倖旅行をしおいたす。 watanabe 自己玹介 所属するクラりドセキュリティGは、AWS や Azure、 Google Cloud など耇数クラりド環境のセキュリティを担圓しおいたす。 前職ではAWSむンフラの構築IaCやLambda開発、CI/CDの怜蚎、監芖ツヌルの蚭定など幅広く担圓しおいたした。 所属チヌムの䜓制は クラりドセキュリティG党䜓は4名で、東京に2名、倧阪に2名が圚籍しおいたす。 珟堎の雰囲気はどんな感じ チヌムは非垞にオヌプンで、䞍安や懞念点も気軜に共有できたす。 東京・倧阪にメンバヌが分かれおいたすが、オンラむン/オフラむンで頻繁にやり取りがありたすので、それほど距離は感じおいたせん。 KTCぞ入瀟したずきの入瀟動機や入瀟前埌のギャップは 前職でクラりドセキュリティに関わる機䌚があり、この領域で専門性を高めたいず考えたした。 面接を通じお感じた瀟員の方々の人柄に共感したこずも入瀟の決め手ずなりたした。 オフィスで気に入っおいるずころ 近代的なテック䌁業ずいう雰囲気でずおも働きやすい環境です。 服装も想像以䞊にラフで、良い意味で驚きたした。 Rikumaさん ⇒ watanabeさんぞの質問 入瀟しおから、印象に残っおいる業務はありたすか 珟圚、AWS関連プロゞェクトでクラりドセキュリティを担圓しおいたす。前職ではむンフラ芖点でセキュリティを意識しおいたしたが、珟職ではガバナンス領域たで関わるこずで芖野が広がり、その倉化が特に印象に残っおいたす。 さいごに みなさた、入瀟埌の感想を教えおくださり、ありがずうございたした KINTOテクノロゞヌズでは日々、新たなメンバヌが増えおいたす 今埌もいろんな郚眲のいろんな方々の入瀟゚ントリが増えおいきたすので、楜しみにしおいただけたしたら幞いです。 そしお、KINTOテクノロゞヌズでは、ただたださたざたな郚眲・職皮で䞀緒に働ける仲間を募集しおいたす 詳しくは こちら からご確認ください
Introduction Hello, I'm watanabe, and I joined in September 2025! In this article, I interviewed everyone who joined in September 2025 about their impressions right after joining. I hope this will be useful content for those interested in KINTO Technologies (hereafter KTC) and serve as a reflection for the members who participated! 10Ryu Self-introduction I work on the lease fee and gross profit calculation system for KINTO used vehicles in the Business Systems Development Division. My previous job was in automotive sales finance. How is your team structured? Our team has 5 members. What is the workplace atmosphere like? There are no barriers between members, making communication easy. It's easy for the engineering team to step in and make suggestions to the business team. What was your reason for joining KTC, and were there any surprises after joining? Reason for joining: I wanted to work in a job related to automobiles, and I was interested in KINTO's business even before joining. Surprises: I was given more autonomy than I expected, and I've been able to leverage my past experience. To be honest, I had no coding experience, so I was quite anxious about whether I could make it at a tech company. What do you like about the office? The atmosphere of Muromachi There are many coffee shops nearby Being able to buy treats for my family on the way home from work Question from watanabe to 10Ryu Please tell us an episode where you felt you could leverage your experience in the automotive industry since joining KTC. Since I can recognize most vehicles just by hearing the car name, and thanks to my experience in automotive sales finance, I was able to smoothly catch up on business knowledge such as KINTO's approach to residual value and payment methods. Tomiyoshi Self-introduction I belong to the QA Group. I'm involved in testing activities of course, but also in automation related to those activities. In my previous job, I was doing QA at a third-party verification company. My hobby is tennis, and recently I'm getting into pickleball. How is your team structured? The entire QA Group has 12 members. Within the group, we're divided into mobile apps and web apps, and I work on mobile apps. What is the workplace atmosphere like? Communication is easy not only within the team but also with the development side, making it a very good environment. What was your reason for joining KTC, and were there any gaps between your expectations and reality? When I exhibited a booth at JaSST'25 Tokyo, I talked with people from KTC's QA team. That sparked my interest and led me to decide to join. Before I joined, I heard the company was using AI a lot and automating testing. That was exactly how it turned out, so there wasn’t really any surprise. What do you like about the office? I like the break space. It's a nice, relaxing spot. Question from 10Ryu to Tomiyoshi Please tell us what you like about KTC and your favorite part of your cat's body. What I like about KTC Everyone is eager to take on new challenges. My favorite part of my cat Everything, but burying my face in a slightly chubby side belly is the best. Rikuma Self-introduction Currently, I work on generative AI-related projects, and recently I've been focusing on PoC development for MCP and chatbots. My hobbies include watching anime, board games, traveling, and skiing. I try to maintain a work-life balance while finding fulfillment in working with the latest technologies every day. How is your team structured? We work on a wide range of themes related to generative AI. In addition to technical verification and PoC development, we promote the use of generative AI internally and externally, and run workshops. It's an agile team where we can actively exchange opinions and quickly turn new ideas into reality. What is the workplace atmosphere like? It's very flat and open, with an atmosphere where anyone can freely share their opinions. There's an atmosphere that supports people who take on new challenges, and "Let's do it!" is something you hear a lot. I feel the whole team has a strong attitude toward proactively tackling new things. What was your reason for joining KTC, and were there any surprises after joining? I wanted to explore the possibilities of AI more deeply and was drawn to KTC's environment where I could practice the latest generative AI technologies, which led me to decide to join. What I noticed after joining KTC is how quickly we can access the latest technologies. It's fun to be able to quickly turn my ideas into PoCs. I feel it's an environment with a good balance of speed and stability. What do you like about the office? My previous work place had hot-desking, so now I enjoy being able to place my favorite items at my own desk and create my own personal space. Also, being able to smoothly consult and share information with members on the same floor is a good point. Question from Tomiyoshi to Rikuma How do you feel about KTC's corporate culture? What do you do on your days off? How do you feel about KTC's corporate culture? The culture here is open and really encourages you to take on new challenges. A characteristic is that there are many opportunities to try out your own ideas. What do you do on your days off? On my days off, I spend time relaxing watching anime or playing board games. I also love traveling, so I go on day trips on weekends or travel abroad during long holidays. watanabe Self-introduction The Cloud Security Group I belong to is responsible for security across multiple cloud environments including AWS, Azure, and Google Cloud. In my previous job, I was broadly responsible for AWS infrastructure construction (IaC), Lambda development, CI/CD considerations, and monitoring tool configuration. How is your team structured? The entire Cloud Security Group has 4 members, with 2 in Tokyo and 2 in Osaka. What is the workplace atmosphere like? The team is very open, and we can freely share concerns and issues. Although members are split between Tokyo and Osaka, we communicate frequently both online and in person, so the distance doesn't feel like an issue. What was your reason for joining KTC, and were there any surprises after joining? I had opportunities to be involved in cloud security at my previous job and wanted to deepen my expertise in this area. I also felt a strong connection with the people I met during the interviews, and that was a key factor in my decision to join. What do you like about the office? It has a modern tech company atmosphere and is a very comfortable working environment. The dress code is more casual than I expected, which was a pleasant surprise. Question from Rikuma to watanabe Is there any work that has left an impression on you since joining? Currently, I work on cloud security for AWS-related projects. In my previous job, I focused on security from an infrastructure perspective, but in my current role I have also been involved in the area of governance, which has broadened my perspective. That change has left a particularly strong impression on me. Conclusion Thank you all for sharing your impressions after joining the company! KINTO Technologies is constantly welcoming new members! We hope you look forward to more articles introducing newcomers from various divisions. And KINTO Technologies is still recruiting people to work with us in various divisions and positions! Please check here for details!
This article is the Day 3 entry for KINTO Technologies Advent Calendar 2025 🎅🎄 Introduction Hello, I'm JongSeok, an Android app developer at KINTO Technologies. While developing Android apps with Claude Code, I had been using the SubAgents feature a bit. As I continued using it, I started thinking there might be ways to use it more efficiently, so I did some research. That's when I discovered a recently announced feature called Skills. I took this opportunity to try both SubAgents and Skills, and here's a summary of what I learned. 1. SubAgents? In short, they are specialists with their own workspace . When you normally chat with Claude Code, everything goes into a single context (conversation flow). But SubAgents work in a separate context and only report back the results. Think of it like a team leader delegating work to a specialist and receiving a report. 1.1 Key Features Feature Description Independent Context Doesn't clutter the main conversation Specialized Prompts Can set role-specific instructions Tool Permission Restrictions Can allow only necessary features 1.2 How to Create There are two ways to create SubAgents. 1. Create via Command (Recommended) In Claude Code, you can easily create them with the /agents command. List Make You can check Agents defined in the Project or create new ones. 2. Create Files Directly You can also add .md files to the .claude/agents/ folder. 1.3 Use Case: kotlin-method-namer Coming up with method names in Kotlin can be surprisingly tricky. So I created a SubAgent that suggests method names following Android/Kotlin style. When to Use? When you're unsure how to express a method's functionality in English When you want to verify if it follows Kotlin naming conventions When you want better method name candidates --- name: kotlin-method-namer description: Expert for suggesting Android/Kotlin method names tools: Read, Glob, Grep model: sonnet color: cyan --- You are an expert Android Kotlin developer specializing in creating clear, idiomatic method names. ... ( Full version ) DEMO SubAgents need to be called directly by name. Since I set color: cyan , you can see when the Agent is running. DEMO Result It suggested the name initializeVariable() . As you can see, SubAgents let you create Agents specialized for specific tasks. If you have recurring specialized work, turning them into SubAgents might improve your efficiency. 2. Skills? In short, they are your personal work guidebook . If SubAgents are specialists with their own workspace, Skills are like adding specialized knowledge to the main agent. If you prepare a guidebook in advance for Claude to reference during work, it will automatically recognize and use it when you request related tasks. 2.1 Key Features Feature Description Integrated into Main Context Unlike SubAgents, operates in the main conversation, not a separate space Auto-invoked Claude automatically uses it when you request related work Progressive Disclosure Loads only necessary information in stages Deterministic Script execution guarantees consistent results 2.2 What is Progressive Disclosure? This is the core design principle of Skills. Instead of loading everything at once, it retrieves only the necessary information in stages. Stage Content Loaded Timing Stage 1 name , description Loaded into system prompt at startup Stage 2 SKILL.md body When related work is requested Stage 3 Additional files (scripts, references, etc.) Only when needed Like a well-organized guidebook, it reads only what's needed: table of contents -> relevant chapter -> appendix. This means you can install multiple Skills without wasting context. 2.3 How to Create Create a skill folder inside the .claude/skills/ folder and add a SKILL.md file. project/ └── .claude/ └── skills/ └── your-skill-name/ └── SKILL.md SKILL.md --- name: wildcard-import-fixer description: Converts wildcard imports (e.g., import java.util.*) to specific imports in Kotlin/Java files. allowed-tools: Bash, Read, Glob, Grep --- YAML Frontmatter Item Rule Required name Lowercase + hyphens, 64 characters or less ✅ description 200 characters or less, critical for Claude to determine when to use it ✅ version For version management (e.g., 1.0.0) - SKILL.md Body Recommended to be under 500 lines Split into separate files if it gets long (e.g., reference.md , scripts/ ) Security Never hardcode sensitive information like API keys or passwords Only install skills from trusted sources 💡 Difference from SubAgents SubAgents: .claude/agents/filename.md (file-based) Skills: .claude/skills/skill-name/SKILL.md (folder-based) 2.4 Use Case: wildcard-import-fixer 💡 Skills can execute scripts. Since this is an Android project, I used Kotlin, but Python and JavaScript (npm) are also supported. I created a Skill that converts common Kotlin wildcard imports ( import java.util.* ) to individual imports. When to Use? When you want to clean up wildcard imports When you want to improve code quality When you want to make import statements explicit Folder Structure .claude/skills/wildcard-import-fixer/ ├── SKILL.md ← Required ├── scripts/ │ └── fix-wildcards.kts ← Optional (add yourself) ├── templates/ │ └── report_template.html ← Optional (add yourself) ├── backups/ ← Auto-generated (when script runs) └── reports/ ← Auto-generated (when script runs) 💡 Only SKILL.md is required. Other files and folders can be freely structured according to your needs. Why Use Scripts? If you have the LLM generate code each time, results can vary slightly. But if you prepare scripts in advance, the same results are guaranteed every time . This is the Deterministic characteristic of Skills. SKILL.md --- name: wildcard-import-fixer description: Converts wildcard imports (e.g., import java.util.*) to specific imports in Kotlin/Java files. allowed-tools: Bash, Read, Glob, Grep --- # Wildcard Import Fixer Automatically converts wildcard imports to specific imports in Kotlin and Java files by analyzing actual class usage in the code. ## Instructions ### Step 1: Analyze the Request When a user asks to fix wildcard imports: 1. Determine the scope (entire project, specific directory, or single file) 2. Decide if a dry-run preview is appropriate first 3. Check if backups should be created ### Step 2: Run the Fixer Script Execute the appropriate command based on the scope... ### Step 3: Review Results After running, check the console output and HTML report... ### Step 4: Handle Edge Cases If no usage is detected, suggest removing the unused import... ( Full version ) 💡 Writing instructions in this Step format helps Claude follow them in order. DEMO In this demo, after running once, I reset the conversation with /clear and requested the same task again. Even after /clear , the Skills' name and description are reloaded (Progressive Disclosure) Since it's script execution, the same results are returned every time (Deterministic) This is the strength of Skills. DEMO Result I also made it generate HTML for easy result viewing. 3. SubAgents vs Skills SubAgents : Specialists working in a separate room. They only report back results. Skills : Manuals you keep on hand. Referenced when needed for work. 3.1 Comparison Table Item SubAgents Skills Context Independent (separate workspace) Integrated into main Invocation Called directly by name Auto-invoked File Structure .claude/agents/filename.md .claude/skills/skill-name/SKILL.md Unit File-based Folder-based Script Execution ❌ ✅ Best For Complex workflows, parallel tasks Repetitive routine tasks 3.2 When to Use Each When to Use SubAgents When you need a specialized perspective, like code reviews When you don't want to clutter the main conversation For complex tasks with multiple steps When to Use Skills When you repeat the same task When you want consistent results (Deterministic) When you want to reuse across multiple projects 3.3 Using Them Together SubAgents and Skills complement each other rather than compete. Example: Automating PR Reviews compose-reviewer (SubAgent) reviews Compose code kotlin-style-checker (Skill) performs style checks test-generator (SubAgent) generates test code SubAgents handle specialized judgment while Skills handle consistent rule checking. 4. Summary After trying SubAgents and Skills, I've started to see when to use each. When You Need Use A specialized perspective for reviews SubAgents To keep the main conversation clean SubAgents To run the same task with identical results every time Skills To automate with scripts Skills Complex workflows requiring both Combination I've just started using them, but it seems like delegating repetitive tasks to Skills and tasks requiring specialized judgment to SubAgents improves efficiency. If you're interested, give them a try! References SubAgents SubAgents - Claude Docs Skills Agent Skills - Claude Docs Equipping agents for the real world with Agent Skills anthropics/skills - GitHub Comparison Skills explained - Claude Blog
This article is the Day 2 entry of the KINTO Technologies Advent Calendar 2025 . Introduction Hello! I'm high-g ( @high_g_engineer ) from the Master Maintenance Tool Development Team in the KINTO Backend Development Group, KINTO Development Division at Osaka Tech Lab. In modern frontend development with heavy API integration, have you ever experienced challenges like these? Manually writing API type definitions often leads to missed updates when the spec changes Auto-generated files scattered everywhere often make it unclear where to import from Team members interpreting directory structures differently often lead to debates during code reviews The keywords to solve these challenges are type safety , schema-driven , auto-generation , and directory design . This article introduces an approach where OpenAPI serves as the single source of truth for auto-generating type-safe code, managed according to Feature-Sliced Design rules. Specifically, we'll walk through what code Orval generates and explain effective design patterns aligned with Feature-Sliced Design's directory structure. What This Article Covers The flow of outputting types and custom hooks from OpenAPI using Orval Detailed examples of the code Orval generates Feature-Sliced Design's layer structure and import rules Design patterns for managing Orval-generated code within Feature-Sliced Design's directory structure Target Audience Frontend developers tired of manually managing REST APIs and type definitions Developers using TypeScript + React Those interested in designs resilient to API changes Those interested in establishing directory structure rules Foundational Knowledge OpenAPI OpenAPI is a standard for defining HTTP APIs in a machine-readable format. By describing API specifications in YAML or JSON, you gain benefits like: Clearly defined API inputs and outputs Automated documentation generation Prevention of discrepancies between client and server Example: Partial OpenAPI Definition (Simplified) openapi: 3.1.0 paths: /posts: get: summary: Get list of posts parameters: - name: page in: query schema: type: integer responses: "200": description: Success content: application/json: schema: $ref: "#/components/schemas/GetPostsResponse" post: summary: Create a post requestBody: content: application/json: schema: $ref: "#/components/schemas/CreatePostRequest" responses: "201": description: Created content: application/json: schema: $ref: "#/components/schemas/CreatePostResponse" /posts/{postId}: put: summary: Update a post parameters: - name: postId in: path required: true schema: type: string requestBody: content: application/json: schema: $ref: "#/components/schemas/UpdatePostRequest" responses: "200": description: Success content: application/json: schema: $ref: "#/components/schemas/Post" delete: summary: Delete a post parameters: - name: postId in: path required: true schema: type: string responses: "204": description: No Content components: schemas: Post: type: object required: [id, title, createdAt, updatedAt, status] properties: id: type: string title: type: string body: type: string createdAt: type: string format: date-time updatedAt: type: string format: date-time status: type: string enum: [draft, published, archived] GetPostsResponse: type: object properties: items: type: array items: $ref: "#/components/schemas/Post" total: type: integer page: type: integer CreatePostRequest: type: object required: [title] properties: title: type: string body: type: string CreatePostResponse: allOf: - $ref: "#/components/schemas/Post" - type: object properties: createdBy: type: string UpdatePostRequest: type: object properties: title: type: string body: type: string status: type: string enum: [draft, published, archived] This YAML defines the following: /posts endpoint: list retrieval (GET) and creation (POST) /posts/{postId} endpoint: update (PUT) and deletion (DELETE) About Schema-Driven Development Problems with Traditional Manual Management Previously, frontend developers performed tasks like these manually: // Manually writing type definitions type Post = { id: string; title: string; body?: string; createdAt: string; updatedAt: string; status: "draft" | "published" | "archived"; }; // Manually writing API calls const getPost = async (id: string): Promise<Post> => { const response = await fetch(`/api/posts/${id}`); return response.json(); }; This approach has the following problems: Cost of manual updates Check OpenAPI → manually update type definitions → update all usage sites Risk of missed updates Type definitions and actual API specs get out of sync Easy to miss updates when the same type is used in multiple places Documentation and code desynchronization OpenAPI ≠ implementation code can happen The Schema-Driven Development Approach To address these manual management problems, a development methodology emerged: define the schema (API specification) first, then proceed with implementation. Traditional: Implementation → Documentation (afterthought) → Discrepancies with spec Schema-driven: Schema definition → Auto-generation → Implementation → Done (Implementation = Documentation, always in sync) Characteristics of Schema-Driven Development Implementation = Documentation : API specs and code are always synchronized Type safety : API inconsistencies detected at compile time Development efficiency : No manual type definition work Team collaboration : Both frontend and backend reference the same OpenAPI Orval Orval is a tool that auto-generates TypeScript type definitions and custom hooks with a single command from OpenAPI specifications. Main Features of Orval Feature Description Auto-generated type definitions Automatically creates types for API requests and responses Auto-generated custom hooks Also auto-generates hooks for TanStack Query and others Multiple library support Supports not just TanStack Query but also Axios and other libraries Mock generation Can also generate mock data for testing Benefits of Using Orval Time savings : Zero time spent hand-writing type definitions or API call code Error prevention : Eliminates typos and spec misreadings from manual writing Always current : Just regenerate when OpenAPI is updated to stay current Orval's Role in Schema-Driven Development Summarizing the content so far, Orval's role in schema-driven development is as follows: OpenAPI (single source of truth) ↓ Auto-generation by Orval keeps type definitions + TanStack Query hooks always in sync ↓ Low-cost, type-safe development is possible Feature-Sliced Design As mentioned at the beginning, the ongoing project adopts Feature-Sliced Design as an architectural pattern for frontend code organization. Feature-Sliced Design is an architecture that organizes the codebase using three concepts: Layers , Slices , and Segments . Concept Description Examples Layer Division by application responsibility. From top: app → pages → features → entities → shared (5 layers). app handles routing and layouts for the entire app, pages handles screens corresponding to URLs app/ , pages/ , features/ Slice Division unit by business domain or feature within each layer features/auth/ , entities/user/ Segment Division by technical role within a slice ui/ , model/ , api/ src/ ├── features/ ← Layer │ ├── auth/ ← Slice │ │ ├── ui/ ← Segment │ │ ├── model/ ← Segment │ │ └── index.ts This structure clarifies where to put what, enabling the team to unify code placement rules. Feature-Sliced Design Directory Structure Our team operates with the following directory structure. The segment divisions ( api/ , model/ , ui/ , etc.) are customized to fit the project. workspaces/typescript/src/ ├── app/ ← ① Application layer: routing, global settings │ ├── layouts/ Layouts used across all pages │ ├── routes/ Routing definitions │ └── App.tsx Root tsx file │ ├── pages/ ← ② Pages layer: each page component (corresponds to URL) │ ├── users/ │ └── login/ │ ├── features/ ← ③ Features layer: reusable business logic │ ├── {slice}/ Divided by domain into units called slices (e.g., user, auth) │ │ ├── {component}/ Components belonging to the domain │ │ │ ├── model/ Logic portion │ │ │ ├── ui/ UI portion │ │ │ └── index.ts Public API (barrel file) │ │ ... │ ... │ ├── entities/ ← ④ Entities layer: business domain definitions │ ├── user/ Various domains │ │ ├── @x Cross-import notation *described later │ │ ├── api/ Imports and uses auto-generated files from shared/ (facade) │ │ │ ├── hooks.ts API hooks │ │ │ └── index.ts Public API (barrel file) │ │ ├── model/ Domain logic │ │ ├── ui/ Minimal UI staying within the domain │ │ └── index.ts Public API (barrel file) │ ... │ └── shared/ ← â‘€ Shared layer: project-independent utilities ├── api/ │ └── generated/ Auto-generated files by Orval (modification prohibited) │ ├── types.ts │ ├── hooks.ts │ └── client.ts ├── config/ Configuration constants ├── errors/ Commonly used error functions ├── lib/ Utility functions └── ui/ Generic UI components Feature-Sliced Design Layer Import Restriction Rules The most important rule of Feature-Sliced Design: A layer can only import from layers below itself. Additionally, mutual imports between the same layer are also prohibited in principle (exception described later with @x notation). app ← Top level (highest abstraction) ↓ import allowed pages ↓ features ↓ entities * shared can be imported from any layer This means the following rules are established within the project: ✅ pages/ can import from features/, entities/, shared/ ✅ features/ can import from entities/, shared/ ✅ entities/ can import from shared/ ❌ entities/ must not import from features/ or pages/ ❌ shared/ must not import from any other layer Special Role of the Entities Layer: entities/@x (Cross-Import Notation) However, in the entities layer, business domains often relate to each other. For example, cases like "Post references User" occur. To solve this, a special import method allowed only within the entities layer is the @x notation. Directory Structure Example entities/ ├── user/ │ ├── @x/ │ │ └── post.ts # Types/functions exposed for external slices │ ├── model/ │ │ └── types.ts # Type definitions used internally │ ├── ui/ │ └── index.ts # Normal public API │ └── post/ ├── model/ │ └── usePost.ts # Wants to reference user's types from here └── index.ts Usage Example // When using entities/user from entities/post/model/usePost.ts // ❌ Normal import (Feature-Sliced Design violation: import between same layer) import type { User } from "@/entities/user"; // ✅ Cross-import using @x (allowed) import type { User } from "@/entities/user/@x/post"; // The @x directory represents "cross-import-specific API that this slice exposes externally." By using @x , it becomes explicit that something is intentionally exposed externally, making dependency tracking easier. Now that we've covered the foundational knowledge, let's get into the main topic. How to Use Orval and Output Code Setup The ongoing project uses pnpm as the package manager. Also, we'll proceed assuming OpenAPI is already defined. # Install Orval pnpm add -D orval Next, create the Orval configuration file. Note: hooks are defined to format auto-generated code with Biome. // orval.config.ts import { defineConfig } from "orval"; const API_DIR = "./src/shared/api"; const INPUT_DIR = "../../docs/api"; const GENERATED_DIR = `${API_DIR}/generated`; export default defineConfig({ postApi: { hooks: { afterAllFilesWrite: "pnpm format:write:generate", }, input: { target: `${INPUT_DIR}/openapi.yaml`, }, output: { clean: true, biome: true, client: "react-query", override: { mutator: { path: `${API_DIR}/customInstance.ts`, name: "useCustomInstance", }, query: { useSuspenseQuery: true, version: 5, }, }, schemas: `${GENERATED_DIR}/model`, target: `${GENERATED_DIR}/hooks/index.ts`, }, }, }); Next, create a custom instance that executes API requests. This is used as the mutator specified in the Orval configuration. // src/shared/api/customInstance.ts import { ApiHttpError, type ErrorDetail } from "../errors"; import { getAccessToken } from "../lib"; const BASE_URL = import.meta.env.VITE_API_BASE_URL || ""; // Type definition for request configuration export type RequestConfig = { url: string; method: "GET" | "POST" | "PUT" | "DELETE" | "PATCH"; headers?: Record<string, string>; params?: Record<string, unknown>; data?: unknown; signal?: AbortSignal; }; // Request function using Fetch API const fetchApi = async <T>(config: RequestConfig): Promise<T> => { const { url, method, headers = {}, params, data, signal } = config; // Get authentication token const token = getAccessToken(); // Build query parameters const queryString = params ? `?${new URLSearchParams(params as Record<string, string>).toString()}` : ""; const fullUrl = `${BASE_URL}${url}${queryString}`; // Build headers const requestHeaders: Record<string, string> = { "Content-Type": "application/json", ...headers, }; if (token) { requestHeaders.Authorization = `Bearer ${token}`; } // Build request options const options: RequestInit = { method, headers: requestHeaders, signal, }; if (data && ["POST", "PUT", "PATCH"].includes(method)) { options.body = JSON.stringify(data); } const response = await fetch(fullUrl, options); // Error handling if (!response.ok) { let errorMessage = `API error: ${response.status}`; let errorDetails: ErrorDetail[] = []; try { const errorData = await response.json(); errorDetails = errorData?.errors?.details ?? []; if (typeof errorData.message === "string") { errorMessage = errorData.message; } } catch { // Use default message if JSON parsing fails } throw new ApiHttpError({ status: response.status, message: errorMessage, details: errorDetails, }); } // For 204 No Content if (response.status === 204) { return null as T; } return response.json(); }; // Custom instance function used by Orval export const useCustomInstance = <T>(config: RequestConfig): Promise<T> => { const controller = new AbortController(); const promise = fetchApi<T>({ ...config, signal: controller.signal, }); // For TanStack Query's cancel functionality // @ts-expect-error dynamically adding cancel property promise.cancel = () => controller.abort(); return promise; }; export default useCustomInstance; This useCustomInstance is used when executing HTTP requests within the hooks that Orval generates. You can centralize project-specific settings here, such as attaching authentication tokens and error handling. In actual projects, token refresh processing and retry logic are often added. For details, see the Orval Official Documentation - Custom Client . All that’s left is to run the code generation. # Run code generation pnpm orval In the ongoing project, we periodically run pnpm orval to batch-apply API spec changes. Actual Examples of Orval-Generated Code Now let's look at specific examples of what Orval actually generates. Generated Output 1: Type Definitions From OpenAPI's Post schema, TypeScript types like the following are auto-generated. // src/shared/api/generated/types.ts // ↓ Auto-generated from OpenAPI export type Post = { id: string; title: string; body?: string; createdAt: string; // ISO 8601 format updatedAt: string; status: "draft" | "published" | "archived"; }; export type GetPostsResponse = { items: Post[]; total: number; page: number; }; export type CreatePostRequest = { title: string; body?: string; }; export type CreatePostResponse = Post & { createdBy: string; }; export type UpdatePostRequest = { title?: string; body?: string; status?: "draft" | "published" | "archived"; }; Important Points OpenAPI schemas become types directly enum is converted to TypeScript Union Types Required/optional ( ? ) distinction is automatically determined Since it's a generated file, do not modify it (will be overwritten on next run) Generated Output 2: TanStack Query Custom Hooks Orval also auto-generates TanStack Query hooks. The following is a simplified example for easier understanding (actual generated code includes custom instances and detailed type definitions). // src/shared/api/generated/hooks.ts // ↓ Orval generates TanStack Query hooks (simplified example) import { useSuspenseQuery, useMutation } from "@tanstack/react-query"; import type { UseSuspenseQueryOptions, UseMutationOptions, } from "@tanstack/react-query"; import type { Post, GetPostsResponse, CreatePostRequest, CreatePostResponse, UpdatePostRequest, } from "./model"; import { useCustomInstance } from "../customInstance"; type SecondParameter<T extends (...args: never) => unknown> = Parameters<T>[1]; // GET request → useSuspenseQuery hook export const useGetPosts = < TData = Awaited<ReturnType<ReturnType<typeof useCustomInstance<GetPostsResponse>>>>, TError = Error, >( options?: { query?: Partial<UseSuspenseQueryOptions<GetPostsResponse, TError, TData>>; request?: SecondParameter<ReturnType<typeof useCustomInstance>>; } ) => { const customInstance = useCustomInstance<GetPostsResponse>(); return useSuspenseQuery({ queryKey: ["posts"], queryFn: () => customInstance({ url: `/api/posts`, method: "GET" }), ...options?.query, }); }; // POST request → useMutation hook export const useCreatePost = <TError = Error, TContext = unknown>( options?: { mutation?: UseMutationOptions<CreatePostResponse, TError, CreatePostRequest, TContext>; request?: SecondParameter<ReturnType<typeof useCustomInstance>>; } ) => { const customInstance = useCustomInstance<CreatePostResponse>(); return useMutation({ mutationFn: (data: CreatePostRequest) => customInstance({ url: `/api/posts`, method: "POST", data, }), ...options?.mutation, }); }; // PUT request export const useUpdatePost = <TError = Error, TContext = unknown>( postId: string, options?: { mutation?: UseMutationOptions<Post, TError, UpdatePostRequest, TContext>; request?: SecondParameter<ReturnType<typeof useCustomInstance>>; } ) => { const customInstance = useCustomInstance<Post>(); return useMutation({ mutationFn: (data: UpdatePostRequest) => customInstance({ url: `/api/posts/${postId}`, method: "PUT", data, }), ...options?.mutation, }); }; // DELETE request export const useDeletePost = <TError = Error, TContext = unknown>( postId: string, options?: { mutation?: UseMutationOptions<void, TError, void, TContext>; request?: SecondParameter<ReturnType<typeof useCustomInstance>>; } ) => { const customInstance = useCustomInstance<void>(); return useMutation({ mutationFn: () => customInstance({ url: `/api/posts/${postId}`, method: "DELETE", }), ...options?.mutation, }); }; Convenience of These Hooks TypeScript type inference automatically infers data type as GetPostsResponse Error handling is also type-safe (Error type is determined) TanStack Query features like caching and refetching work as-is No manual API URL entry needed (prevents URL typos) Key Points for Using Orval-Generated Code Feature Benefit Automatic OpenAPI tracking API spec change → re-run → fully synchronized Types and hooks are linked Return type of useGetPosts is also auto-inferred Utilizes TypeScript generics Error handling is also type-safe Plugin extensible Can add custom generation logic Strong for API versioning Supports generation from older API spec versions Generated Code Must Not Be Modified Running pnpm orval overwrites type definitions and custom hooks, so files under src/shared/api/generated/ are modification prohibited . // ❌ Do not modify directly like this // src/shared/api/generated/hooks.ts export const useGetPosts = () => { // ↓ This code will be overwritten on Orval re-run return useSuspenseQuery({ // ... }); }; Customization Is Done in the Entities Layer When customization is needed, wrap in the entities layer to provide your own interface. This centralizes dependencies on generated code in one place. // src/entities/post/api/hooks.ts import { useGetPosts as useGetPostsGenerated } from "@/shared/api/generated"; /** * Provides a user-friendly interface * - Hides details of Orval-generated code * - Returns organized return values */ export const usePosts = () => { const { data, isLoading, error } = useGetPostsGenerated(); return { posts: data?.items ?? [], isLoading, hasError: !!error, }; }; Detailed implementation patterns are explained in the next chapter. Implementation Patterns and Structural Design From here, we'll introduce 3 design patterns for effectively using Orval-generated code. Pattern A: Simple Wrapping Scenario : API to get a list of posts Step 1: Check Orval-Generated Code The useGetPosts shown in "Generated Output 2: TanStack Query Custom Hooks" above is used as-is. Step 2: Wrap in Entities Layer // src/entities/post/api/hooks.ts import { useGetPosts as useGetPostsGenerated } from "@/shared/api/generated"; /** * Custom hook to get list of posts * Isolates dependency on shared/api/generated to entities layer */ export const usePosts = () => { const { data, isLoading, error } = useGetPostsGenerated(); return { posts: data?.items ?? [], isLoading, hasError: !!error, }; }; Step 3: Public API // src/entities/post/api/index.ts export { usePosts } from "./hooks"; Step 4: Use in Features Layer // src/features/PostManagement/ui/PostList.tsx import { usePosts } from "@/entities/post/api"; function PostList() { const { posts, isLoading } = usePosts(); if (isLoading) return <div>Loading...</div>; return ( <ul> {posts.map((post) => ( <li key={post.id}>{post.title}</li> ))} </ul> ); } Benefits of This Pattern Orval-generated code changes are limited to entities/post/api features/PostManagement only needs to know the simple interface Testing also works with mocking just entities/post/api From a Feature-Sliced Design Perspective entities/post/api creates a boundary between Orval (external) and features (internal) Features don't know the details of generated code Modification scope can be limited to entities Pattern B: Combining Multiple APIs Scenario : When both "list of posts + post details" are needed Multiple API calls need to be combined. This is also handled in the entities layer. Note: The following example assumes a useGetPostDetails hook is separately generated by Orval. Step 1: Combine Multiple APIs in Entities Layer // src/entities/post/api/hooks.ts import { useGetPosts as useGetPostsGenerated, useGetPostDetails as useGetPostDetailsGenerated, } from "@/shared/api/generated"; /** * Combines multiple API calls * Callers don't need to be aware of this complexity */ export const usePostWithDetails = (postId: string) => { const { data: posts, isLoading: postsLoading } = useGetPostsGenerated(); const { data: details, isLoading: detailsLoading } = useGetPostDetailsGenerated(postId); return { posts: posts?.items ?? [], details: details ?? null, isLoading: postsLoading || detailsLoading, // Also provide convenient derived data hasDetails: !!details, }; }; Step 2: Use from Features Layer Callers don't need to know the complexity. // src/features/PostManagement/ui/PostDetail.tsx import { usePostWithDetails } from "@/entities/post/api"; function PostDetail({ postId }: Props) { const { posts, details, isLoading, hasDetails } = usePostWithDetails(postId); // Hide Complexity in entities layer! return <div>{hasDetails && <PostInfo details={details} />}</div>; } Pattern C: Unified Error Handling Scenario : When you want to handle errors in a common format Convert Orval-generated error types to custom error types. Step 1: Define and Convert Error Types in Entities Layer // src/entities/post/api/hooks.ts export type ApiError = { message: string; code: "NETWORK_ERROR" | "NOT_FOUND" | "UNAUTHORIZED" | "SERVER_ERROR"; details?: unknown; }; export type UsePostsResult = { posts: Post[]; isLoading: boolean; error: ApiError | null; retry: () => void; }; export const usePosts = (): UsePostsResult => { const { data, isLoading, error, refetch } = useGetPostsGenerated(); // Convert Orval-generated error type to custom error type const mappedError: ApiError | null = error ? { message: error.message || "An error occurred", code: mapErrorCode(error), details: error, } : null; return { posts: data?.items ?? [], isLoading, error: mappedError, retry: () => refetch(), }; }; // Helper function // TanStack Query's error is treated as Error type // Assumes custom instance throws Error with status code type ApiErrorWithStatus = Error & { status?: number }; function mapErrorCode(error: unknown): ApiError["code"] { if (!navigator.onLine) return "NETWORK_ERROR"; const apiError = error as ApiErrorWithStatus; if (apiError.status === 404) return "NOT_FOUND"; if (apiError.status === 401) return "UNAUTHORIZED"; return "SERVER_ERROR"; } Step 2: Unified Error Processing in Features Layer Error handling becomes unified on the caller side. // src/features/PostManagement/ui/PostList.tsx import { usePosts } from "@/entities/post/api"; function PostList() { const { posts, isLoading, error, retry } = usePosts(); if (error) { return ( <div> <p>Error: {error.message}</p> <button onClick={retry}>Retry</button> </div> ); } // ... normal processing below } Architecture Diagram: Orval + Feature-Sliced Design Here's a diagram summarizing the patterns so far. Since dependency directions are unified, the scope of change impact becomes clear. shared/api/generated/ ← Orval output (modification prohibited) ├─ useGetPosts ├─ useCreatePost ├─ useGetPostDetails └─ types.ts ↓ [Boundary] ↓ entities/post/api/ ← Layer wrapping Orval output (modifiable) ├─ usePosts (customized version) ├─ usePostWithDetails (multiple API combination) ├─ ApiError type └─ index.ts (public API) ↓ features/ ← Features layer ├─ PostManagement/ │ ├─ ui/PostList.tsx │ ├─ ui/PostDetail.tsx │ ├─ lib/... │ └─ index.ts ... ↓ pages/ ← Pages layer └─ PostPage/ ↓ app/ ← Application layer ├─ routes/ └─ ... Impressions After Adoption ✅ Benefits Dramatically improved type safety : Cannot go back to development with manually typed definitions. High resilience to API changes : Modifications complete in one place (entities layer). Documentation = Code : OpenAPI and code can always stay synchronized. Improved team-wide efficiency : Smooth flow from API design → implementation → testing. Fewer bugs : Bugs from type mismatches have nearly disappeared. ⚠ Important Notes Learning cost for the entire team : Feature-Sliced Design is an architecture that takes time to master, requiring understanding from all team members. Wait time until OpenAPI is finalized : For UI implementation involving API spec changes, you need to wait for OpenAPI updates to complete. As a countermeasure, using mock APIs like MSW allows frontend development to proceed in parallel. Need for compatibility checks during Orval version upgrades : During Orval major version upgrades, generated code format may change, so checking release notes before upgrading is necessary. Summary Schema-driven development using Orval significantly improves resilience to API changes and type safety in frontend development. In the ongoing project, Orval was introduced from the start, reducing communication costs between backend and frontend engineers and nearly eliminating wasteful implementation costs. Additionally, while adopting Feature-Sliced Design took time for the entire team to understand and implement in code, the clear rules improved code readability and maintainability. If you're experiencing challenges like the following, please try the Orval × Feature-Sliced Design combination: Manually writing API types and custom hooks, incurring costs Schema-driven development is already adopted, but there are no directory structure rules Auto-generated files are imported from various places Thank you for reading to the end. References Orval Official Documentation OpenAPI Specification v3.1.0 TanStack Query Feature-Sliced Design Official Documentation
この蚘事は KINTOテクノロゞヌズ Advent Calendar 2025 の2日目の蚘事です🎅🎄 はじめに こんにちは KINTO開発郚 KINTOバック゚ンド開発G マスタヌメンテナンスツヌル開発チヌム、Osaka Tech Lab 所属の high-g @high_g_engineer です。 API 連携が倚い珟代のフロント゚ンド開発においお、こんな課題を感じたこずはないでしょうか API の型定矩を手動で曞いおいお、仕様倉曎のたびに修正挏れが発生する 自動生成ファむルの眮き堎所がバラバラで、どこから䜕を import すればいいか分からない チヌムメンバヌごずにディレクトリ構造の解釈が異なり、コヌドレビュヌで議論になる これらの課題を解決するキヌワヌドが、 「型安党」「スキヌマ駆動」「自動生成」「ディレクトリ蚭蚈」 です。 この蚘事では、OpenAPI を唯䞀の情報源ずしお型安党なコヌドを自動生成し、それを Feature-Sliced Design のルヌルに則っお管理するアプロヌチを玹介したす。 具䜓的には、Orval で「どんなコヌドが生成されるのか」を芋ながら、Feature-Sliced Design のディレクトリ構造に沿った効果的な蚭蚈パタヌンを解説したす。 この蚘事で玹介するこず OpenAPI を元に Orval から型ずカスタムフックを出力する流れ Orval が実際に出力するコヌド䟋の詳现 Feature-Sliced Design の局構造ずむンポヌトルヌル Orval 生成コヌドを Feature-Sliced Design のディレクトリ構造で管理する蚭蚈パタヌン 想定読者 REST API ず型定矩の手動管理に疲れおいるフロント゚ンド開発者 TypeScript + React を䜿っおいる方 API 倉曎に匷い蚭蚈に興味がある方 ディレクトリ構造のルヌル化に関心がある方 基瀎ずなる知識 OpenAPI に぀いお OpenAPI は、HTTP API をプログラムで解釈可胜な圢匏で定矩するための暙準です。API の仕様を YAML たたは JSON で蚘述するこずで、以䞋のようなメリットがありたす。 API の入出力が明確に定矩される ドキュメント生成が自動化される クラむアント・サヌバヌ間での霟霬を防ぐ 䟋OpenAPI の蚘述の䞀郚簡略版 openapi: 3.1.0 paths: /posts: get: summary: 投皿䞀芧を取埗 parameters: - name: page in: query schema: type: integer responses: "200": description: Success content: application/json: schema: $ref: "#/components/schemas/GetPostsResponse" post: summary: 投皿を䜜成 requestBody: content: application/json: schema: $ref: "#/components/schemas/CreatePostRequest" responses: "201": description: Created content: application/json: schema: $ref: "#/components/schemas/CreatePostResponse" /posts/{postId}: put: summary: 投皿を曎新 parameters: - name: postId in: path required: true schema: type: string requestBody: content: application/json: schema: $ref: "#/components/schemas/UpdatePostRequest" responses: "200": description: Success content: application/json: schema: $ref: "#/components/schemas/Post" delete: summary: 投皿を削陀 parameters: - name: postId in: path required: true schema: type: string responses: "204": description: No Content components: schemas: Post: type: object required: [id, title, createdAt, updatedAt, status] properties: id: type: string title: type: string body: type: string createdAt: type: string format: date-time updatedAt: type: string format: date-time status: type: string enum: [draft, published, archived] GetPostsResponse: type: object properties: items: type: array items: $ref: "#/components/schemas/Post" total: type: integer page: type: integer CreatePostRequest: type: object required: [title] properties: title: type: string body: type: string CreatePostResponse: allOf: - $ref: "#/components/schemas/Post" - type: object properties: createdBy: type: string UpdatePostRequest: type: object properties: title: type: string body: type: string status: type: string enum: [draft, published, archived] この YAML は以䞋を定矩しおいたす。 /posts ゚ンドポむント䞀芧取埗GETず䜜成POST /posts/{postId} ゚ンドポむント曎新PUTず削陀DELETE スキヌマ駆動開発に぀いお 埓来の手動管理の問題 これたで、私たちフロント゚ンド開発者は以䞋のような䜜業を手動で行っおいたした。 // 手動で型定矩を曞く type Post = { id: string; title: string; body?: string; createdAt: string; updatedAt: string; status: "draft" | "published" | "archived"; }; // API 呌び出しも手で曞く const getPost = async (id: string): Promise<Post> => { const response = await fetch(`/api/posts/${id}`); return response.json(); }; この方法には以䞋の問題がありたす。 手動修正のコスト OpenAPI を確認 → 型定矩を手で修正 → 利甚箇所をすべお修正 修正挏れのリスク 型定矩ず実際の API 仕様がズレる 耇数の箇所で同じ型を䜿っおいるず挏れが生じやすい ドキュメントずコヌドの非同期 OpenAPI ≠ 実装コヌド になるこずもある スキヌマ駆動開発のアプロヌチ 䞊蚘で挙げた手動察応の問題を解消するために考えられたのが、「スキヌマAPI 仕様を最初に定矩し、そこから実装を進める」開発手法です。 埓来実装 → ドキュメント埌付け → 仕様ずの霟霬 スキヌマ駆動スキヌマ定矩 → 自動生成 → 実装 → 完了 実装 = ドキュメント、垞に同期 スキヌマ駆動開発の特城 実装 = ドキュメント 垞に API 仕様ずコヌドが同期 型安党性 コンパむル時に API 䞍敎合を怜出 開発効率 型定矩の手䜜業が䞍芁 チヌム連携 フロント゚ンドずバック゚ンド䞡方が同じ OpenAPI を参照 Orval に぀いお Orval は、OpenAPI の仕様曞から TypeScript の型定矩やカスタムフックなどのコヌドを コマンドひず぀で自動生成 しおくれるツヌルです。 Orval の䞻な特城 特城 説明 型定矩の自動生成 API のリク゚スト・レスポンスの型を自動で䜜成 カスタムフックの自動生成 TanStack Query などのフックも自動生成 耇数ラむブラリ察応 TanStack Query だけでなく、Axios などのラむブラリにも察応 モック生成 テスト甚のモックデヌタも生成可胜 Orval を䜿うメリット 時間の節玄 型定矩や API 呌び出しコヌドを手曞きする時間がれロに ミスの防止 手曞きによるタむプミスや仕様の読み間違いがなくなる 垞に最新 OpenAPI が曎新されたら、再生成するだけで最新の状態に スキヌマ駆動開発における Orval の圹割 ここたでの内容をたずめるず、スキヌマ駆動開発における Orval の圹割は以䞋のようになりたす。 OpenAPI信頌できる唯䞀の情報源 ↓ Orval によるコヌドの自動生成で、 型定矩 + TanStack Query フックを垞に同期 ↓ 䜎コストで型安党な開発が可胜 Feature-Sliced Design に぀いお 冒頭でも觊れた通り、珟圚開発䞭のプロゞェクトでは、フロント゚ンドのコヌド構成に Feature-Sliced Design ずいうアヌキテクチャパタヌンを採甚しおいたす。 Feature-Sliced Design は、コヌドベヌスを レむダヌ 、 スラむス 、 セグメント ずいう3぀の抂念で敎理するアヌキテクチャです。 抂念 説明 䟋 レむダヌ (Layer) アプリケヌションの責務による分割。䞊䜍から app → pages → features → entities → shared の5局。 app はルヌティングやレむアりトなどアプリ党䜓の蚭定、 pages は URL に察応する画面を担圓 app/ , pages/ , features/ スラむス (Slice) 各レむダヌ内でのビゞネスドメむンや機胜ごずの分割単䜍 features/auth/ , entities/user/ セグメント (Segment) スラむス内での技術的な圹割による分割 ui/ , model/ , api/ src/ ├── features/ ← レむダヌ │ ├── auth/ ← スラむス │ │ ├── ui/ ← セグメント │ │ ├── model/ ← セグメント │ │ └── index.ts この構造により、「どこに䜕を眮くか」が明確になり、チヌム党䜓でコヌドの配眮ルヌルを統䞀できたす。 Feature-Sliced Design のディレクトリ構造 私たちのチヌムでは、以䞋のようなディレクトリ構造で運甚しおいたす。セグメント api/ 、 model/ 、 ui/ などの分け方はプロゞェクトに合わせおカスタマむズしおいたす。 workspaces/typescript/src/ ├── app/ ← ① アプリケヌション局ルヌティング、グロヌバル蚭定 │ ├── layouts/ 党䜓的なペヌゞで利甚するレむアりト │ ├── routes/ ルヌティング定矩 │ └── App.tsx ルヌトずなるtsxファむル │ ├── pages/ ← ② ペヌゞ局各ペヌゞコンポヌネントURLに察応 │ ├── users/ │ └── login/ │ ├── features/ ← ③ 機胜局再利甚可胜なビゞネスロゞック │ ├── {slice}/ ドメむンごずにスラむスずいう単䜍で分割 (䟋:user, auth) │ │ ├── {component}/ ドメむンに属するコンポヌネント │ │ │ ├── model/ ロゞック郚分 │ │ │ ├── ui/ UI郚分 │ │ │ └── index.ts 公開APIバレルファむル │ │ ... │ ... │ ├── entities/ ← ④ ゚ンティティ局ビゞネスドメむンの定矩 │ ├── user/ 各皮ドメむン │ │ ├── @x クロスむンポヌト蚘法 ※埌述 │ │ ├── api/ shared/ の自動生成ファむルを import しお利甚ファサヌド │ │ │ ├── hooks.ts apiフック │ │ │ └── index.ts 公開APIバレルファむル │ │ ├── model/ ドメむンロゞック │ │ ├── ui/ ドメむン内にずどたる最小レベルのUI │ │ └── index.ts 公開APIバレルファむル │ ... │ └── shared/ ← â‘€ 共有局プロゞェクト非䟝存のナヌティリティ ├── api/ │ └── generated/ Orval による自動生成ファむル修正犁止 │ ├── types.ts │ ├── hooks.ts │ └── client.ts ├── config/ 蚭定定数 ├── errors/ 共通利甚゚ラヌ関数 ├── lib/ ナヌティリティ関数 └── ui/ 汎甚UIコンポヌネント Feature-Sliced Design の局間むンポヌト制限ルヌル Feature-Sliced Design の最も重芁なルヌル レむダヌは自身より䞋䜍のレむダヌのみをむンポヌト可胜 です。 たた、同䞀レむダヌ間の盞互むンポヌトも原則䞍可です䟋倖は埌述の @x 蚘法。 app ← 最䞊䜍抜象床が高い ↓ import可胜 pages ↓ features ↓ entities ※ shared はどのレむダヌからもむンポヌト可胜 ぀たり、以䞋のようなルヌルがプロゞェクト内で蚭けられおいたす。 ✅ pages/ は features/、entities/、shared/ を import 可胜 ✅ features/ は entities/、shared/ を import 可胜 ✅ entities/ は shared/ を import 可胜 ❌ entities/ は features/ や pages/ を import しおはいけない ❌ shared/ は他のどのレむダヌも import しおはいけない entities 局の特別な圹割entities/@xクロスむンポヌト蚘法 しかし、entities 局ではビゞネスドメむン同士が関連するこずが倚く、䟋えば「Post が User を参照する」ずいったケヌスが発生したす。 これを解決するために、entities 局内でのみ蚱可される特別なむンポヌト方法が @x 蚘法です。 ディレクトリ構造の䟋 entities/ ├── user/ │ ├── @x/ │ │ └── post.ts # 倖郚スラむス向けに公開する型・関数 │ ├── model/ │ │ └── types.ts # 内郚で䜿甚する型定矩 │ ├── ui/ │ └── index.ts # 通垞の公開API │ └── post/ ├── model/ │ └── usePost.ts # ここから user の型を参照したい └── index.ts 䜿甚䟋 // entities/post/model/usePost.ts から entities/user を䜿甚する堎合 // ❌ 通垞のむンポヌトFeature-Sliced Design違反同䞀レむダヌ間のむンポヌト import type { User } from "@/entities/user"; // ✅ @x を䜿ったクロスむンポヌト蚱可 import type { User } from "@/entities/user/@x/post"; // @x ディレクトリは「このスラむスが倖郚に公開する、クロスむンポヌト専甚のAPI」を衚したす。 @x を䜿うこずで、「意図的に倖郚公開しおいる」こずが明瀺され、䟝存関係が远跡しやすくなりたす。 では、基瀎ずなる知識が抌さえられたずころで、ここから本題に入っおいきたす。 Orval の䜿い方ず出力コヌド セットアップ 珟圚開発䞭のプロゞェクトでは pnpm をパッケヌゞマネヌゞャヌずしお䜿甚しおいたす。 たた、OpenAPI は予め定矩された前提で話を進めたす。 # Orval のむンストヌル pnpm add -D orval 次に、Orval の蚭定ファむルを䜜成したす。 ※ hooks は、自動生成されたコヌドを Biome で format するために定矩しおいたす。 // orval.config.ts import { defineConfig } from "orval"; const API_DIR = "./src/shared/api"; const INPUT_DIR = "../../docs/api"; const GENERATED_DIR = `${API_DIR}/generated`; export default defineConfig({ postApi: { hooks: { afterAllFilesWrite: "pnpm format:write:generate", }, input: { target: `${INPUT_DIR}/openapi.yaml`, }, output: { clean: true, biome: true, client: "react-query", override: { mutator: { path: `${API_DIR}/customInstance.ts`, name: "useCustomInstance", }, query: { useSuspenseQuery: true, version: 5, }, }, schemas: `${GENERATED_DIR}/model`, target: `${GENERATED_DIR}/hooks/index.ts`, }, }, }); 次に、API リク゚ストを実行するカスタムむンスタンスを䜜成したす。これは Orval の蚭定で指定した mutator ずしお䜿甚されたす。 // src/shared/api/customInstance.ts import { ApiHttpError, type ErrorDetail } from "../errors"; import { getAccessToken } from "../lib"; const BASE_URL = import.meta.env.VITE_API_BASE_URL || ""; // リク゚スト蚭定の型定矩 export type RequestConfig = { url: string; method: "GET" | "POST" | "PUT" | "DELETE" | "PATCH"; headers?: Record<string, string>; params?: Record<string, unknown>; data?: unknown; signal?: AbortSignal; }; // Fetch API を䜿甚したリク゚スト関数 const fetchApi = async <T>(config: RequestConfig): Promise<T> => { const { url, method, headers = {}, params, data, signal } = config; // 認蚌トヌクンを取埗 const token = getAccessToken(); // ク゚リパラメヌタの構築 const queryString = params ? `?${new URLSearchParams(params as Record<string, string>).toString()}` : ""; const fullUrl = `${BASE_URL}${url}${queryString}`; // ヘッダヌの構築 const requestHeaders: Record<string, string> = { "Content-Type": "application/json", ...headers, }; if (token) { requestHeaders.Authorization = `Bearer ${token}`; } // リク゚ストオプションの構築 const options: RequestInit = { method, headers: requestHeaders, signal, }; if (data && ["POST", "PUT", "PATCH"].includes(method)) { options.body = JSON.stringify(data); } const response = await fetch(fullUrl, options); // ゚ラヌハンドリング if (!response.ok) { let errorMessage = `API error: ${response.status}`; let errorDetails: ErrorDetail[] = []; try { const errorData = await response.json(); errorDetails = errorData?.errors?.details ?? []; if (typeof errorData.message === "string") { errorMessage = errorData.message; } } catch { // JSONパヌスに倱敗した堎合はデフォルトメッセヌゞを䜿甚 } throw new ApiHttpError({ status: response.status, message: errorMessage, details: errorDetails, }); } // 204 No Content の堎合 if (response.status === 204) { return null as T; } return response.json(); }; // Orval で䜿甚するカスタムむンスタンス関数 export const useCustomInstance = <T>(config: RequestConfig): Promise<T> => { const controller = new AbortController(); const promise = fetchApi<T>({ ...config, signal: controller.signal, }); // TanStack Query のキャンセル機胜甚 // @ts-expect-error cancel プロパティを動的に远加 promise.cancel = () => controller.abort(); return promise; }; export default useCustomInstance; この useCustomInstance は Orval が生成するフック内で HTTP リク゚ストを実行する際に䜿甚されたす。認蚌トヌクンの付䞎や゚ラヌハンドリングなど、プロゞェクト固有の蚭定をここに集玄できたす。 実際のプロゞェクトでは、トヌクンリフレッシュ凊理やリトラむロゞックなどを远加するこずが倚いです。詳现は Orval 公匏ドキュメント - Custom Client を参照しおください。 あずは、コヌド生成を実行するだけです。 # コヌド生成実行 pnpm orval 珟圚開発䞭のプロゞェクトでは、定期的に pnpm orval を実行し、API 仕様の倉曎をたずめお反映する運甚をしおいたす。 Orval が生成するコヌド実䟋 それでは、Orval が実際に䜕を生成するか、具䜓䟋で芋おいきたす。 生成物 1型定矩 OpenAPIの Post スキヌマから、以䞋のような TypeScript 型が自動生成されたす。 // src/shared/api/generated/types.ts // ↓ OpenAPIから自動生成される export type Post = { id: string; title: string; body?: string; createdAt: string; // ISO 8601圢匏 updatedAt: string; status: "draft" | "published" | "archived"; }; export type GetPostsResponse = { items: Post[]; total: number; page: number; }; export type CreatePostRequest = { title: string; body?: string; }; export type CreatePostResponse = Post & { createdBy: string; }; export type UpdatePostRequest = { title?: string; body?: string; status?: "draft" | "published" | "archived"; }; 重芁なポむント OpenAPIのスキヌマがそのたた型になる enum は TypeScript の Union Type に倉換される 必須・オプション ? の区別も自動刀定される 生成ファむルなので修正しおはいけない次の再実行で䞊曞きされる 生成物 2TanStack Query カスタムフック Orval は TanStack Query のフックも自動生成したす。以䞋は理解しやすいよう簡略化した䟋です実際の生成コヌドはカスタムむンスタンスや詳现な型定矩を含みたす。 // src/shared/api/generated/hooks.ts // ↓ Orval が TanStack Query のフックを生成簡略化した䟋 import { useSuspenseQuery, useMutation } from "@tanstack/react-query"; import type { UseSuspenseQueryOptions, UseMutationOptions, } from "@tanstack/react-query"; import type { Post, GetPostsResponse, CreatePostRequest, CreatePostResponse, UpdatePostRequest, } from "./model"; import { useCustomInstance } from "../customInstance"; type SecondParameter<T extends (...args: never) => unknown> = Parameters<T>[1]; // GET リク゚スト → useSuspenseQuery フック export const useGetPosts = < TData = Awaited<ReturnType<ReturnType<typeof useCustomInstance<GetPostsResponse>>>>, TError = Error, >( options?: { query?: Partial<UseSuspenseQueryOptions<GetPostsResponse, TError, TData>>; request?: SecondParameter<ReturnType<typeof useCustomInstance>>; } ) => { const customInstance = useCustomInstance<GetPostsResponse>(); return useSuspenseQuery({ queryKey: ["posts"], queryFn: () => customInstance({ url: `/api/posts`, method: "GET" }), ...options?.query, }); }; // POST リク゚スト → useMutation フック export const useCreatePost = <TError = Error, TContext = unknown>( options?: { mutation?: UseMutationOptions<CreatePostResponse, TError, CreatePostRequest, TContext>; request?: SecondParameter<ReturnType<typeof useCustomInstance>>; } ) => { const customInstance = useCustomInstance<CreatePostResponse>(); return useMutation({ mutationFn: (data: CreatePostRequest) => customInstance({ url: `/api/posts`, method: "POST", data, }), ...options?.mutation, }); }; // PUT リク゚スト export const useUpdatePost = <TError = Error, TContext = unknown>( postId: string, options?: { mutation?: UseMutationOptions<Post, TError, UpdatePostRequest, TContext>; request?: SecondParameter<ReturnType<typeof useCustomInstance>>; } ) => { const customInstance = useCustomInstance<Post>(); return useMutation({ mutationFn: (data: UpdatePostRequest) => customInstance({ url: `/api/posts/${postId}`, method: "PUT", data, }), ...options?.mutation, }); }; // DELETE リク゚スト export const useDeletePost = <TError = Error, TContext = unknown>( postId: string, options?: { mutation?: UseMutationOptions<void, TError, void, TContext>; request?: SecondParameter<ReturnType<typeof useCustomInstance>>; } ) => { const customInstance = useCustomInstance<void>(); return useMutation({ mutationFn: () => customInstance({ url: `/api/posts/${postId}`, method: "DELETE", }), ...options?.mutation, }); }; このフックの䟿利さ TypeScript の型掚論により、 data の型が自動的に GetPostsResponse に掚論される ゚ラヌハンドリングも型安党Error の型が決たっおいる TanStack Query のキャッシング、再フェッチなどの機胜もそのたた䜿える API URL の手入力が䞍芁URL の蚘述ミスを防げる Orval 生成コヌドの掻甚ポむント 特城 メリット OpenAPI の自動远跡 API 仕様倉曎 → 再実行 → 完党に同期 型ずフックが連動 useGetPosts の戻り倀の型も自動掚論 TypeScript ゞェネリクスを掻甚 ゚ラヌハンドリングも型安党 プラグむン拡匵可胜 カスタム生成ロゞックを远加できる API のバヌゞョン管理に匷い 叀いバヌゞョンの API 仕様からの生成もサポヌト 生成コヌドは修正犁止 pnpm orval を実行するず型定矩ずカスタムフックが䞊曞きされるため、 src/shared/api/generated/ 配䞋のファむルは 修正犁止 です。 // ❌ こうやっお盎接修正しおはいけない // src/shared/api/generated/hooks.ts export const useGetPosts = () => { // ↓ このコヌドは Orval の再実行で䞊曞きされる return useSuspenseQuery({ // ... }); }; カスタマむズは entities 局で行う カスタマむズが必芁な堎合は、entities 局でラップしお独自のむンタヌフェヌスを提䟛したす。これにより、生成コヌドぞの䟝存を䞀箇所に集玄できたす。 // src/entities/post/api/hooks.ts import { useGetPosts as useGetPostsGenerated } from "@/shared/api/generated"; /** * 利甚偎に䜿いやすいむンタヌフェヌスを提䟛 * - Orval 生成コヌドの詳现を隠蔜 * - 戻り倀を敎理しお返す */ export const usePosts = () => { const { data, isLoading, error } = useGetPostsGenerated(); return { posts: data?.items ?? [], isLoading, hasError: !!error, }; }; 詳现な実装パタヌンは次章で解説したす。 実装パタヌンず構造蚭蚈 ここから、Orval が生成したコヌドを効果的に䜿うための 蚭蚈パタヌン を 3 ぀玹介したす。 パタヌン A単玔なラッピング シナリオ 投皿䞀芧を取埗する API ステップ 1Orval による生成コヌドを確認 前述の「生成物 2TanStack Query カスタムフック」で瀺した useGetPosts がそのたた䜿甚されたす。 ステップ 2entities 局でラッピング // src/entities/post/api/hooks.ts import { useGetPosts as useGetPostsGenerated } from "@/shared/api/generated"; /** * 投皿䞀芧を取埗するカスタムフック * shared/api/generated ぞの䟝存を entities局に隔離 */ export const usePosts = () => { const { data, isLoading, error } = useGetPostsGenerated(); return { posts: data?.items ?? [], isLoading, hasError: !!error, }; }; ステップ 3公開 API // src/entities/post/api/index.ts export { usePosts } from "./hooks"; ステップ 4features 局で䜿甚 // src/features/PostManagement/ui/PostList.tsx import { usePosts } from "@/entities/post/api"; function PostList() { const { posts, isLoading } = usePosts(); if (isLoading) return <div>読み蟌み䞭...</div>; return ( <ul> {posts.map((post) => ( <li key={post.id}>{post.title}</li> ))} </ul> ); } このパタヌンのメリット Orval の生成コヌド倉曎が entities/post/api に限定される features/PostManagement はシンプルなむンタヌフェヌスだけを知ればいい テストも entities/post/api をモック䞀箇所で OK Feature-Sliced Design の芖点 entities/post/api が Orval倖郚ず features内郚の 境界を䜜る features は generated コヌドの詳现を知らない 修正範囲を entities に限定できる パタヌン B耇数 API の組み合わせ シナリオ 「投皿䞀芧 + 投皿の詳现」が必芁な堎合 耇数の API 呌び出しを組み合わせる必芁がありたす。これも entities 局で察応したす。 ※ 以䞋の䟋では、投皿詳现を取埗する useGetPostDetails フックが別途 Orval で生成されおいる想定です。 ステップ 1entities 局で耇数 API を組み合わせ // src/entities/post/api/hooks.ts import { useGetPosts as useGetPostsGenerated, useGetPostDetails as useGetPostDetailsGenerated, } from "@/shared/api/generated"; /** * 耇数のAPI呌び出しを組み合わせる * 呌び出し偎はこの耇雑性を意識しない */ export const usePostWithDetails = (postId: string) => { const { data: posts, isLoading: postsLoading } = useGetPostsGenerated(); const { data: details, isLoading: detailsLoading } = useGetPostDetailsGenerated(postId); return { posts: posts?.items ?? [], details: details ?? null, isLoading: postsLoading || detailsLoading, // 䟿利な導出デヌタも提䟛 hasDetails: !!details, }; }; ステップ 2features 局から利甚 利甚偎は耇雑さを知らなくお OK です。 // src/features/PostManagement/ui/PostDetail.tsx import { usePostWithDetails } from "@/entities/post/api"; function PostDetail({ postId }: Props) { const { posts, details, isLoading, hasDetails } = usePostWithDetails(postId); // 耇雑さは entities局に隠蔜 return <div>{hasDetails && <PostInfo details={details} />}</div>; } パタヌン C゚ラヌハンドリングの統䞀 シナリオ ゚ラヌを共通のフォヌマットで扱いたい堎合 Orval 生成の゚ラヌ型を、独自の゚ラヌ型に倉換したす。 ステップ 1entities 局で゚ラヌ型を定矩・倉換 // src/entities/post/api/hooks.ts export type ApiError = { message: string; code: "NETWORK_ERROR" | "NOT_FOUND" | "UNAUTHORIZED" | "SERVER_ERROR"; details?: unknown; }; export type UsePostsResult = { posts: Post[]; isLoading: boolean; error: ApiError | null; retry: () => void; }; export const usePosts = (): UsePostsResult => { const { data, isLoading, error, refetch } = useGetPostsGenerated(); // Orval生成の゚ラヌ型を独自の゚ラヌ型に倉換 const mappedError: ApiError | null = error ? { message: error.message || "゚ラヌが発生したした", code: mapErrorCode(error), details: error, } : null; return { posts: data?.items ?? [], isLoading, error: mappedError, retry: () => refetch(), }; }; // ヘルパヌ関数 // TanStack Query の error は Error 型ずしお扱われる // カスタムむンスタンス偎でステヌタスコヌドを含めた Error を throw する想定 type ApiErrorWithStatus = Error & { status?: number }; function mapErrorCode(error: unknown): ApiError["code"] { if (!navigator.onLine) return "NETWORK_ERROR"; const apiError = error as ApiErrorWithStatus; if (apiError.status === 404) return "NOT_FOUND"; if (apiError.status === 401) return "UNAUTHORIZED"; return "SERVER_ERROR"; } ステップ 2features 局で統䞀的に゚ラヌ凊理 利甚偎でぱラヌハンドリングが統䞀されたす。 // src/features/PostManagement/ui/PostList.tsx import { usePosts } from "@/entities/post/api"; function PostList() { const { posts, isLoading, error, retry } = usePosts(); if (error) { return ( <div> <p>゚ラヌ: {error.message}</p> <button onClick={retry}>再詊行</button> </div> ); } // ... 以䞋、通垞の凊理 } Orval + Feature-Sliced Design のアヌキテクチャ図 ここたでのパタヌンを図にたずめたした。䟝存の方向が統䞀されおいるため、倉曎の圱響範囲が明確になりたす。 shared/api/generated/ ← Orvalの生成物修正犁止 ├─ useGetPosts ├─ useCreatePost ├─ useGetPostDetails └─ types.ts ↓ [境界線] ↓ entities/post/api/ ← Orvalの生成物をラッピングする局修正可胜 ├─ usePostsカスタマむズ版 ├─ usePostWithDetails耇数API組み合わせ ├─ ApiError型 └─ index.ts公開API ↓ features/ ← 機胜局 ├─ PostManagement/ │ ├─ ui/PostList.tsx │ ├─ ui/PostDetail.tsx │ ├─ lib/... │ └─ index.ts ... ↓ pages/ ← ペヌゞ局 └─ PostPage/ ↓ app/ ← アプリケヌション局 ├─ routes/ └─ ... 導入しおみた感想 ✅ メリット 型安党性が圧倒的に向䞊 手入力で型を定矩する開発には戻れたせん。 API 倉曎ぞの耐性が高い 䞀箇所修正entities 局で党おが完了したす。 ドキュメント = コヌド OpenAPI ずコヌドを垞に同期できたす。 チヌム党䜓の効率が向䞊 API 蚭蚈 → 実装 → テストの流れがスムヌズです。 バグが枛る 型䞍敎合によるバグがほがなくなりたす。 ⚠ 泚意点 チヌム党䜓での孊習コストがかかる Feature-Sliced Design は習埗に時間がかかるアヌキテクチャであり、チヌム党員の理解が必芁です。 OpenAPI 確定たでの埅ち時間が発生 API 仕様倉曎を䌎う UI 実装では、OpenAPI の曎新完了を埅぀必芁がありたす。察策ずしお MSW などのモック API を掻甚するこずで、フロント゚ンド開発を䞊行しお進められたす。 Orval のバヌゞョンアップ時の互換性確認の必芁性 Orval のメゞャヌバヌゞョンアップ時には生成コヌドの圢匏が倉わる可胜性があるため、アップグレヌド前にリリヌスノヌトの確認が必芁です。 たずめ Orval を利甚したスキヌマ駆動開発は、フロント゚ンド開発における 「API 倉曎ぞの耐性」 ず 「型安党性」 を倧幅に向䞊させたす。 珟圚開発䞭のプロゞェクトでは、立ち䞊げ圓初から Orval を導入しおおり、バック゚ンドずフロント゚ンドの゚ンゞニア間のコミュニケヌションコストが軜枛され、無駄な実装コストもほずんどなくなりたした。 たた、Feature-Sliced Design の導入により、チヌム党䜓で理解しコヌドに萜ずし蟌むたでに時間がかかったものの、明確なルヌルのおかげでコヌドの可読性ず保守性が向䞊したした。 以䞋のような課題を感じおいる方は、ぜひ Orval × Feature-Sliced Design の組み合わせを詊しおみおください。 API の型やカスタムフックを手曞きしおいお、コストがかかっおいる スキヌマ駆動開発は導入枈みだが、ディレクトリ構造にルヌルがない 自動生成ファむルを様々な堎所から import しおいる状態 最埌たでお読みいただきありがずうございたした。 参考文献 Orval 公匏ドキュメント OpenAPI Specification v3.1.0 TanStack Query Feature-Sliced Design 公匏ドキュメント
こんにちは、Engineering Officeの守谷emimです。 この蚘事では、 KINTOテクノロゞヌズ Advent Calendar 2025 ず アクセシビリティ Advent Calendar 2025 の12月2日のクロスポストずしお衚題の件をレポヌトしおいきたす。 普段わたしは瀟内で、組織の開発力を䞊げるこずをミッションにした郚眲Engineering Officeでデザむン呚りのこずを考える傍ら、個人掻動でラむフワヌクずしおいるアクセシビリティの啓発掻動を行っおいたす。 そこで、想定倖に自発的に玠敵なアクセシビリティ掻動を行っおくれたスタッフがいたので、その内容ず心意気に぀いお䌺いたした。 X旧Twitterポストに誰が代替テキストを たずは、以䞋のポストをぜひご芧ください。 https://x.com/KintoTech_Dev/status/1976163362016043509?s=20 ただの匊瀟犏岡オフィスのご玹介に芋えたすか よくご芧ください、添付されおいる写真に、なんずも玠敵な代替テキストaltが付䞎されおいたす。 ![KINTOテクノロゞヌズの10月9日のX投皿に4枚の写真が添付されおいお、すべおに「ALT」ボタンが衚瀺されおいる様子のキャプチャヌ](/assets/blog/authors/emim/2025-12-02-SNSA11y/2025-12-02-SNSA11y_post1.png =600x) ![ビルの写真に「KINTOテクノロゞヌズの犏岡オフィスが入居する犏岡倧名ガヌデンシティのビル倖芳」ず代替テキストが付いおいるのをポップアップしたずころ](/assets/blog/authors/emim/2025-12-02-SNSA11y/2025-12-02-SNSA11y_post2.png =500x) 瀟内のチャットに「team-accessibility」ずいうものがあり、普段から情報共有などを行っおいたす。しかしこのポストを行っおくれたのは、そこで前のめりで発蚀しおいる方でもなかったので、わたしは仰倩したした。 そんな方でも適切な代替テキストを付けおくれるずは 今回お話しを䌺ったのは、以䞋の2人です。 竹䞭さん 経歎SIer䌁業で開発やPMを12幎経隓した埌、銀行の内補開発でスクラムマスタヌを担圓 珟圚は技術広報のマネヌゞャヌを務める ポストをされた匵本人 ゆかちさん 経歎1瀟目は旅行業界、コロナ犍の打撃を受けIT業界ぞ KINTOテクノロゞヌズではもずもず経理事務を担圓〜時を経お技術広報専任ずなり、むベント運営やSNSの発信などを担圓しおいる アクセシビリティに぀いお今ずおも関心を高めおいる人 身内ですが敬称付きで統䞀しお掲茉しおいたす。 このポストの代替テキストに぀いお この4枚の投皿に付けられた代替テキスト。ずあるスクリヌンリヌダヌナヌザヌは「クオリティが高くお玠敵ですねずおも読みやすい聞きやすいです」「抜象的な衚珟なのですが、そよ颚が吹くような代替テキスト」ず評しおくれたした。 そこで、どうしお今回代替テキストを付けようず思ったか、竹䞭さんに聞いおみるず、以䞋のような意芋をいただきたした。 ―― 竹䞭さんアクセシビリティは聞いたこずがあったけれど、内容に぀いおはあたり理解しおいたせんでした。今回は、X投皿時にたたたた「ALT」ず曞かれたボタンが目立っおいたこずず、「team-accessibility」のチャットで日々アクセシビリティの情報に觊れおおり、䞖間でも泚目が高たっおいるず聞いおいたので、蚭定しようず思いたした。 普段から誰に届くずもわからず䌝えおいたこずが、こんな所でサブリミナル効果を発揮するずは 蚘述方法など困らなかったかも聞いおみたした。 ―― 竹䞭さん蚘茉の必芁があるこずはわかったけれど具䜓がわからなかったため、盎前にネット怜玢を行い「簡朔に、本文に曞いおいない内容を入れない」こずを意識したした。投皿されるたでどんな感じで入るのかわからなかった為、投皿されたものを確認しお「こうなるのか」ず初めおわかりたした。 改めお、普段䜕気なく利甚しおいるず気付かない機胜だずいうこずがわかりたした。アクセシビリティに慣れおいる人だず、「ALT」ず曞かれたボタンを目芖できたりもしたすが、そこも気付かない人も居るずいうこずを意識する必芁がありそうです。 「代替テキスト」を通しおの気付き 「アクセシビリティを普段意識しおない」人が「代替テキストを぀ける」䞖界線、ずおも玠敵ではないですか個人的にびっくりほっこりしたので「誰が付けたか瀟内で探し出したよ」ずいう旚をポストしたら、結構な「いいね」をいただきたした。 https://x.com/emim/status/1977921547869561149?s=20 これらの反響があったこずを、曎に改めお共有をしたら感動しおくれたのが、ゆかちさんです。ゆかちさんも普段は゚ンゞニアではないこずもありアクセシビリティのチャットには出おこない方です。 2人にあらためお、この反響に぀いお尋ねおみたした。 ―― 竹䞭さん話題になるんだずびっくりしたした。代替テキストだけで反応があるこずに、なるほどず思いたした。 ―― ゆかちさんシンプルに「代替テキストを付けられる」こずを把握しおいなかったし、なおか぀これを぀けるこずで助かる人がいるんだな、ずいうこずを初めお知りたした。アクセシビリティずいう抂念党䜓が今たで気にしおいなかったゞャンルだし、自分でもできるこずがあるのかなず気になりだしたした。 この蚀葉を聞いお、ちょっずしたこずでもフィヌドバックずしお䌝えるず「人の行動に圱響を䞎える」ずいうこずが明らかになったように思う回答を埗られたした。たた、さらにこのような意芋も加えおくれたした。 ―― ゆかちさん倧事なこずだずは思うけれど、䜕をしおいいのかがわからないため、初心者でもわかる孊習機䌚がもっずあるずいいですね。 昚今、アクセシビリティ界隈では過去に比べ、確かに勉匷䌚は増えおきおいたす。それでも前提を飛ばした䞊玚者向けになっおきおいるのも業界課題です。 䞀方で、デゞタル庁が10月にたずめお公開しおくれた「 デゞタル瀟䌚掚進暙準ガむドラむン 」に、広報担圓者アクセシビリティ初心者向けにたずめた「DS-672.1 りェブアクセシビリティ広報向けガむドブック」などがありたす。 https://www.digital.go.jp/resources/standard_guidelines こういった公開資料を利甚しお、技術広報メンバヌ向けの勉匷䌚などを䌁画しようず考えおいたす。きちんずデゞタル庁の担圓の方にも確認をしたら「いくらでも䜿っおください」ずの回答をいただきたした。䜙裕があったら、倖郚の方も招埅する圢での勉匷䌚などもやっおみたいず考えおいたすので、来幎の我々に乞うご期埅を
Hello, I'm Moriya (emim) from the Engineering Office. In this article, I'll be reporting on the topic mentioned in the title as a cross-post for December 2nd of both the KINTO Technologies Advent Calendar 2025 and the Accessibility Advent Calendar 2025 . I work in the Engineering Office, a division dedicated to strengthening our organization’s development capabilities, where I focus on design. Alongside that, as a personal ongoing activity, I engage in advocacy to raise awareness about digital accessibility. I was amazed by a staff member who spontaneously did some wonderful accessibility work, and I asked him about what he did and what motivated him. Who Added Alt Text to the X (formerly Twitter) Post!? First, please take a look at the following post. https://x.com/KintoTech_Dev/status/1976163362016043509?s=20 Does it just look like an introduction to our Fukuoka office? Take a close look: the attached photo has been given wonderfully thoughtful alt text. ![KINTOテクノロゞヌズの10月9日のX投皿に4枚の写真が添付されおいお、すべおに「ALT」ボタンが衚瀺されおいる様子のキャプチャヌ](/assets/blog/authors/emim/2025-12-02-SNSA11y/2025-12-02-SNSA11y_post1.png =600x) ![ビルの写真に「KINTOテクノロゞヌズの犏岡オフィスが入居する犏岡倧名ガヌデンシティのビル倖芳」ず代替テキストが付いおいるのをポップアップしたずころ](/assets/blog/authors/emim/2025-12-02-SNSA11y/2025-12-02-SNSA11y_post2.png =500x) We have a chat channel called team-accessibility within the company where we regularly share information. However, the person who made this post wasn't someone who actively participated in those discussions, so I was absolutely astonished. Someone unexpectedly added proper alt text!!! I spoke with the following two people for this article: Takenaka Background: After 12 years of development and PM experience at a system integrator, worked as a Scrum Master for in-house development at a bank. Currently serves as manager of the Developer Relations Group. Is the one who made the X post Yukachi Background: First job was in the travel industry; moved to IT after being hit by the COVID-19 pandemic. At KINTO Technologies, originally handled accounting work, then over time transitioned to a dedicated role in the Developer Relations Group, handling event management and social media outreach. Is very interested in accessibility recently About the Alt Text in This Post The alt text added to these four images in the post. One screen reader user commented that it was high quality and wonderful, very readable (or rather, easy to listen to), and described it with an abstract expression as alt text that feels like a gentle breeze. So I asked Takenaka why he added alt text this time, and received the following response. —— (Takenaka) I had heard of digital accessibility, but didn't really understand what it meant. This time, the ALT button happened to stand out when I was posting on X, and I'd been exposed to accessibility information daily through the team-accessibility chat. I'd also heard it was gaining attention, so I gave it a go. I’d been putting things out there without knowing who’d see them, and it’s wild that they had a subliminal impact here! I also asked if they had any difficulty writing it. —— (Takenaka) I understood that I needed to write it, but I didn’t know how. So right before, I did a quick internet search and tried to keep it concise, making sure not to include anything outside the main text. I only found out how it would actually appear after checking the published post. I understood that this is a feature often overlooked unless you pay attention. For people familiar with accessibility, the button labeled ALT may be visible at a glance, but it’s important to keep in mind that some people won’t notice it at all. Insights Gained Through Alt Text Isn’t it wonderful to imagine a world where even those who aren’t very aware of accessibility add alternative text? I was both surprised and touched, so I posted about how I tracked down who had added it internally, and that post received quite a lot of likes. https://x.com/emim/status/1977921547869561149?s=20 When I shared the feedback again, Yukachi was really moved. She’s not an engineer, so she usually doesn’t show up in the accessibility chat. I asked both of them again about these responses. —— (Takenaka) I was surprised it became a topic, and I realized that even alt text alone could spark reactions. —— (Yukachi) I simply wasn't aware that you could add alt text, and I learned for the first time that adding it helps some people. Accessibility was something I’d never really thought about, but now I’m curious if there’s something I can do too. Hearing these words, I felt I'd received answers that clearly showed how even small things, when communicated as feedback, can influence people's behavior. She also added the following opinion. —— (Yukachi) I think it's important, but since I don't know what to do, it would be nice to have more learning opportunities that even beginners can understand. In recent years, accessibility-related study groups have certainly increased compared to the past. However, one of the industry’s challenges is that they tend to skip the basics and cater more to advanced participants. On the other hand, the Digital Agency released the Digital Society Promotion Standard Guidelines in October, which includes DS-672.1 Web Accessibility Guidebook for Public Relations, summarized for PR personnel (accessibility beginners). https://www.digital.go.jp/resources/standard_guidelines We’re planning to use these publicly available materials to organize study sessions for the Developer Relations Group. After checking with the Digital Agency, we received confirmation that we’re free to use them as much as we like. If we have the capacity, we’d also like to invite external participants to join these sessions. So please look forward to what we’ll be doing next year!
This article is the Day 1 entry of the KINTO Technologies Advent Calendar 2025:santa::christmas_tree: I'm okapi from the Mobile team in the QA Group. When creating images with AI, have you ever experienced telling the AI exactly what you want through prompts, only to have your intent misunderstood, forcing you to redo it multiple times? This time, I've created a prompt that can generate tech blog cover images in a single generation! This approach can be applied to other image creation tasks as well. Please give it a try! How to Use AI Image‑generation to Use Microsoft Copilot *Basically, any generative AI tool that supports image generation will work (e.g., ChatGPT, DALL-E, Midjourney, etc.) Prompt Modification Points There are only 4 points to modify in the prompt! Modification Points Content 1. Display Title Setting Paste your tech blog title into Display Title 2. Style Selection Choose 1 from ■Style and delete the other 2 3. Overview Setting Paste your tech blog content into ■Tech Blog Content 4. Previous Cover Image Attachment Attach your previously used cover image to the prompt Actual Prompt Used Please create a cover image for a tech blog. Please use the same format as the image used in the previous tech blog I'm attaching. ■Purpose I want to create a cover image for the following blog article. ■Display Title (Paste article title here) ■Notes ・Please ensure there are no unnatural Japanese expressions. ・Please keep the image style and layout the same as before. ・The title should be placed in the center or a prominent position ■Style (Choose one from the following 3) ・Tech feel (technical atmosphere) ・AI and machine learning style (collaboration with generative AI) ・Motion graphics style (even as a still image) ■Tech Blog Content (Paste article overview here) Previous Cover Image Attached Images Actually Created for This Article For this article, I created images in the 3 patterns mentioned above. Style Generated Image Tech feel (technical atmosphere) AI and machine learning style (collaboration with generative AI) Motion graphics style (even as a still image) Key Points of Prompt Design Explained Point Explanation 1. Minimize modification points By narrowing down the parts that need modification in the prompt to 4 points, I created a template that anyone can easily use with copy-paste. 2. Attach reference images Simply saying "with the same atmosphere as before" in words doesn't accurately convey to AI. By attaching actual images, you can share ambiguous parts that are difficult to express such as layout, color scheme, and font feel. 3. Structured instructions By clearly categorizing information into purpose, title, notes, style, and content, it becomes easier for AI to understand the priority of each element. 4. Specify elements to avoid By including negative instructions like "avoid unnatural Japanese expressions," it becomes easier to select natural Japanese. 5. Present options (style) Instead of leaving everything to AI, by making it a format where you choose from 3 styles, simply adding this option makes the prompt customizable. Not Complete (Still Not Perfect) This template can mass-produce images while maintaining reproducibility just by replacing 4 points: title, style, overview, and reference image. Actually, There Are 2 Issues Although I could generate images in one shot, upon closer inspection, the following 2 problems were found. Issue 1: Japanese Typos Despite stating "avoid unnatural Japanese expressions" in the prompt, typos occurred. Tech feel (technical atmosphere): It's written as "自動化化", where "化" is repeated The display of "開発" is distorted AI and machine learning style (collaboration with generative AI): It's written as "機械孊", where "習" is missing Issue 2: Logo Modification I wanted the reference image's logo to be reproduced, but the generative AI subtly modified the logo. At first glance they look the same, but upon closer inspection, you can see that the design has subtly changed . From a copyright and brand guidelines perspective, we want to avoid logo modifications . Tried to Improve Response to Issue 1: Japanese Typos I adjusted the prompt notes so the Japanese wouldn’t sound awkward, and tried out a few versions. Trial Changes Result ① ・Please use correct kanji and phrases for Japanese text. ・Ensure no typos or meaningless Japanese is included. ・Make Japanese text in the image natural and readable. No change - Same typos occurred ② ・Avoid unnatural Japanese expressions. ・Please review that kanji are correct before creating. All kanji disappeared - Text stopped displaying ③ Attached image with "Please correct 自動化化 in the upper left to 自動化" Not corrected - Original typo remained Response to Issue 2: Logo Modification To accurately reproduce the logo, I tried the following prompt adjustments. Additional prompt: I want the logo below to be exactly the same, so please attach the logo and use the attached file(rogo.png). As a result, a logo close to the original was created, but the angle and thickness were slightly different. Conclusion While image creation itself can be done in one shot, there were the following 2 issues: Japanese kanji expressions - Difficult to control perfectly with prompts alone Accurate logo reproduction - AI automatically modifies it Through this trial and error process, I think we can understand the characteristics and limitations of generative AI and find more effective ways to use it. Practical operational method: Leave layout, atmosphere, and background generation to the image‑generation AI (AI's strong point) Add logos later with image editing tools (humans control accurately) With this combination, I found that we can leverage the strengths of image-generation AI while ensuring a certain level of quality . This tech blog cover image was created using the above operation with the logo manually pasted. If anyone has a prompt that can accurately reproduce kanji and logos in AI-generated images, please let me know! I’ll write more articles when I find new and interesting AI applications, along with challenges and solutions.
この蚘事は KINTOテクノロゞヌズ Advent Calendar 2025 の1日目の蚘事です🎅🎄 はじめに QAグルヌプのMobileチヌムのokapiです。 AIで画像を䜜成するずき、「こういう画像が欲しい」ずプロンプトで䌝えたはずなのに、なぜか意図が䌝わらず、䜕床もやり盎す矜目に 。そんな経隓、ありたせんか? そこで今回は、テックブログのカバヌ画像を「䞀床の生成」で䜜れるプロンプトを䜜成したした! この考え方は、他の画像䜜成にも応甚可胜です。ぜひ掻甚しおみおください。 䜿い方 䜿甚する画像生成AI Microsoft Copilot ※画像生成に察応した生成AIツヌルであれば、基本的にどれでもOKです䟋ChatGPT、DALL-E、Midjourneyなど プロンプトの修正箇所 プロンプトの修正箇所は、たったの4点だけ! 修正箇所 内容 1. 衚瀺タむトルの蚭定 執筆したテックブログのタむトルを「衚瀺タむトル」に貌り付ける 2. スタむルの遞択 「■スタむル」から1぀遞び、残り2぀を削陀 3. 抂芁の蚭定 執筆したテックブログの内容を「■テックブログの蚘茉内容」に貌り付ける 4. 以前のカバヌ画像添付 以前䜿っおいたカバヌ画像を「プロンプト」に添付 実際に䜿ったプロンプト テックブログのカバヌ画像を䜜成しおください。 添付しおいる前回のテックブログで䜿甚した画像ず同じ圢匏でお願いしたす。 ■目的 以䞋のブログ蚘事のカバヌ画像を䜜成したいです。 ■衚瀺タむトル ここに蚘事のタむトルを貌り付け ■泚意点 ・日本語衚珟に䞍自然な点が入らないようにしおください。 ・画像のテむストやレむアりトは前回ず同様でお願いしたす。 ・タむトルが䞭倮たたは目立぀䜍眮に配眮されおいるこず ■スタむル 䞋蚘3぀からお奜きなのを遞ぶ ・テック感技術的な雰囲気 ・AIず機械孊習系生成AIずの協働 ・モヌショングラフィック颚静止画でも ■テックブログの蚘茉内容 ここに蚘事の抂芁を貌り付け 添付した以前のカバヌ画像 本蚘事で実際に䜜った画像 本蚘事では、䞊蚘3パタヌンのスタむルで䜜りたした。 スタむル 生成された画像 テック感技術的な雰囲気 AIず機械孊習系生成AIずの協働 モヌショングラフィック颚静止画でも プロンプト蚭蚈のポむント解説 ポむント 解説 1. 修正箇所を最小限に蚭定 プロンプト内で修正が必芁な箇所を「4点に絞り蟌む」こずで、誰でも簡単にコピペで䜿いやすいテンプレヌトずしたした。 2. 参考画像の添付 蚀葉だけで「前回ず同じ雰囲気で」ず䌝えおも、AIには正確に䌝わりたせん。実際の画像を添付するこずで、レむアりト・配色・フォント感などの「䌝えるのが難しい曖昧な郚分」を共有できたす。 3. 構造化された指瀺 目的・タむトル・泚意点・スタむル・内容ず、情報を明確に分類するこずで、AIが各芁玠の優先床を理解しやすくなりたす。 4. 避けたい芁玠の明蚘 「日本語衚珟に䞍自然な点が入らないように」ずいう吊定圢の指瀺を入れるこずで、自然な日本語を遞定しやすくなりたす。 5. 遞択肢を提瀺(スタむル) AIに完党に任せるのではなく、3぀のスタむルから遞ぶ圢匏にするこずで、ここを増やすだけで、カスタマむズが可胜なプロンプトずなりたす。 おわらないただ完璧ではない 今回のテンプレヌトは「タむトル・スタむル・抂芁・参考画像」の4点を差し替えるだけで、再珟性を保ったたた量産できたす。 実は課題が2぀ありたす 䞀発で画像生成できたものの、よく芋るず以䞋の2぀の問題が発生しおいたした。 課題1: 日本語の誀字 「日本語衚珟に䞍自然な点が入らないように」ずプロンプトに蚘茉しおいるにも関わらず、誀字が発生しおいたした。 テック感(技術的な雰囲気): 「自動化 化 」ずなっおいる(「化」が重耇) 「開発」が衚瀺厩れ AIず機械孊習系(生成AIずの協働): 「機械孊」ずなっおいる(「習」が抜けおいる) 課題2: ロゎの改倉 参考画像のロゎを再珟しおほしかったのですが、生成AIによっおロゎが埮劙に改倉されおしたいたした。 䞀芋するず同じように芋えたすが、よく芋るず 现郚のデザむンが倉わっおいる こずがわかりたす。 著䜜暩やブランドガむドラむンの芳点から、 ロゎの改倉は避けたい ずころです。 改善を詊みたした 課題1ぞの察応: 日本語の誀字 プロンプトの泚意点(日本語衚珟に䞍自然な点が入らないように)をカスタマむズしお耇数パタヌンを詊しおみたした。 詊行 倉曎内容 結果 ① ・日本語の文字は、正しい挢字・語句を䜿甚しおください。 ・誀字脱字や意味䞍明な日本語が含たれないようにしおください。 ・画像内の日本語テキストは、自然で読みやすい衚珟にしおください。 倉わらず - 同様の誀字が発生 ② ・日本語衚珟に䞍自然な点が入らないようにしおください。 ・䜜成する前に挢字は正しい挢字ずなっおいるかレビュヌしおから䜜成しおください。 挢字自䜓が党郚消される - テキストが衚瀺されなくなる ③ 画像を添付の䞊、「巊䞊の『自動化化』→『自動化』に修正しおください」 修正されず - 元の誀字がそのたた残る 課題2ぞの察応: ロゎの改倉 ロゎを正確に再珟するため、以䞋のプロンプト調敎を詊したした。 远加プロンプトロゎを添付の䞊、䞋のロゎは党く同じずしたいので、添付しおいるrogo.pngを貌り付けお䜿っおください。 を远加した結果、本物に近いロゎができたすが、埮劙に角床や倪さが異なりたした。 おわりに 画像䜜成自䜓は䞀発でできるものの、以䞋の2぀の課題がありたした: 日本語の挢字衚珟 - プロンプトだけでは完璧な制埡が難しい ロゎの正確な再珟 - AIが自動的に改倉しおしたう このようなトラむ&゚ラヌを繰り返すこずで、生成AIの特性や限界を理解でき、より効果的な掻甚方法を芋぀けられるず感じおいたす。 珟実的な運甚方法: 画像生成AIに「レむアりト・雰囲気・背景」の生成を任せる(画像生成AIの埗意分野) 「ロゎ」は画像線集ツヌルで埌から远加(人間が正確に制埡) この組み合わせで、 画像生成AIの匷みを掻かし぀぀、䞀定の品質も担保できる ず分かりたした。 本テックブログカバヌ画像は、䞊蚘運甚で手動でロゎを貌り付けお䜜りたした。 もし「AIで生成した画像内の挢字やロゎを正確に再珟できるプロンプト」をお持ちの方がいらっしゃいたしたら、ぜひ教えおください! 今埌も「おもしろいAIの掻甚方法」や「぀たずいた課題ず解決策」を芋぀けたら、蚘事を執筆しおいきたす。
この蚘事は KINTOテクノロゞヌズ Advent Calendar 2025 の 1 日目の蚘事です🎅🎄 こんにちは、技術広報の ゆかち(@ukcpo) です KINTOテクノロゞヌズは今幎もアドベントカレンダヌを実斜したす☆* アドベントカレンダヌ、今幎で 5 回目 今幎はフリヌテヌマで 2 シリヌズ、蚈 50 蚘事公開予定です @ card 過去アドベントカレンダヌは技術広報から各郚眲ぞ個別で声がけをしお集めおおりたしたが、 今回はSlackチャンネルでの公募でほずんどの数が集たりたした(^^) ちなみに合蚈 50 蚘事䞭半分のメンバヌが執筆デビュヌずなりたす せっかくなので・・ずチャレンゞしおくれるメンバヌが倚くお玠敵 䜙談ですが、去幎のアドベントカレンダヌからアドベントの執筆時期は技術広報メンバヌが毎日 30 分、 Slackのハドルにおゆるっず盞談䌚を開いおおりたす。 ちょっず困った時にハドルに入ればサクッず聞けちゃうの、 盞談のハヌドルが䞋がるず思うので開催しおいる偎ずしお良い取り組みな気がしおいたすがいかがでしょう " 初の執筆なので初歩的な質問なのですがいいですか・・ " などちょこちょこ盞談いただけお嬉しいです ![スクリヌンショット](/assets/blog/authors/uka/advent/advent1.png =400x) 盞談しやすい雰囲気を぀くるのが埗意なマネ そんなこんなで匊瀟の "リアル" をアドベントカレンダヌを通しお楜しんでいただけたら嬉しいです 曎新内容は 公匏X(@KINTOTech_Dev) にお毎日曎新予定です。 気になる蚘事があればぜひ目を通しおみおください♩ 今幎もあず1ヶ月、頑匵りたしょう〜(^^)
This article is the Day 1 entry for the KINTO Technologies Advent Calendar 2025 🎅🎄 Hello, I'm Yukachi (@ukcpo) from the Developer Relations Group! KINTO Technologies is doing an Advent Calendar again this year☆* This is the fifth year for our Advent Calendar! This year, we have two series on free topics with a total of 50 articles planned! @ card In the past, the Developer Relations Group would individually reach out to each division to gather contributions for the Advent Calendar, but this time, most of the participants were recruited through a Slack channel call for submissions(^^) By the way, half of the 50 total articles will be written by first-time contributors! It's wonderful to see so many members taking on the challenge, thinking "why not give it a try"! As a side note, since last year's Advent Calendar, the Developer Relations Group has been hosting a casual 30-minute consultation session every day via Slack huddle during the writing period. Being able to pop into the huddle and quickly ask questions when you're a bit stuck lowers the barrier to seeking help, so from our perspective as the hosts, it feels like a great initiative. What do you think!? We're happy to receive questions like "This is my first time writing, so is it okay to ask a basic question...?" ![スクリヌンショット](/assets/blog/authors/uka/advent/advent1.png =400x) Our manager who's great at creating an approachable atmosphere With all that said, we hope you will enjoy our company's "real" through the Advent Calendar! Updates will be posted daily on our official X account (@KINTOTech_Dev) . If any articles catch your interest, please give them a read♩ One more month left this year—let's do our best(^^)