TECH PLAY

KINTOテクノロジーズ

KINTOテクノロジーズ の技術ブログ

969

はじめに こんにちは、2025年4月入社の hiro です! 本記事では、2025年4月入社のみなさまに、入社直後の感想をお伺いし、まとめてみました。 KINTO テクノロジーズ(以下、KTC)に興味のある方、そして、今回参加下さったメンバーへの振り返りとして有益なコンテンツになればいいなと思います! Minami 自己紹介 7月より新たに発足するデータ戦略部に所属します 所属チームの体制は? 分析、事業戦略/戦術の提案、施策の実行まで一貫して向き合うチームです とても優秀なアナリストやデータサイエンティスト、エンジニアの皆さんと一緒に事業成長に取り組めるので、いまから楽しみです KTCへ入社したときの第一印象?ギャップはあった? 働き方やオフィスでの過ごし方など、自由度の高さに驚きました 落ち着いてる中に密かに熱い気持ちを持ってる方もいらっしゃるので、これからが楽しみです 現場の雰囲気はどんな感じ? チームは比較的若く、仲良く明るい雰囲気です 新しい体制でチームの強みを発揮できるように頑張ります ブログを書くことになってどう思った? 入社前に読んだので、KTCに入社される方の参考になったら良いなと思いました MAoさん ⇒ Minamiさんへの質問 国内国外問わず、おすすめの旅行先があったら教えてください! 昨年行ったハワイのカウアイ島がすごく素敵でした! H.N 自己紹介 業務システム開発部で販売店業務領域を主に担当しています。 最近は室町オフィス近辺の美味しいお店を探すのが趣味です。 所属チームの体制は? 業務システム開発部の中でもNimbusチームに所属してまして、プロパー3人とパートナーさんで日々業務にあたっています。 KTCへ入社したときの第一印象?ギャップはあった? 想像していたよりも社内イベントや食事会への参加機会が多くて、他部署の方と関われるきっかけ作りができそうなので良い意味のギャップでした! 現場の雰囲気はどんな感じ? 忙しい中でも困った事や不明点を聞きやすい雰囲気づくりをしてくださる先輩方が多く、日々キャッチアップするにあたって大変助かっています! ブログを書くことになってどう思った? テックブログも含めてこういった記事を書くことに慣れていないのですが、これを機会に楽しめるようになればいいなと思ってます! Minami ⇒ H.Nへの質問 参加されて面白かった社内イベントを教えてください! まだ社内イベントには参加できてませんが、以下の社内イベントに参加してみたいと思ってます! KTCBeerBash 生成AI系の社内勉強会 部署やチームを横断して交流が深められそうなイベントがあれば! K.S 自己紹介 my routeアプリのUI/UX改善を主に担当しています。趣味は家族でキャンプに行くことです。 所属チームの体制は? 7月から新体制になる予定です。PDMと開発チーム一体となってアプリをよりよくしていきます。 KTCへ入社したときの第一印象?ギャップはあった? 入社したタイミングで神保町オフィスがリニューアルされていてとにかくオフィスが綺麗です。さらに休憩スペースの椅子や机はアウトドアメーカーに統一されていてオシャレ! 現場の雰囲気はどんな感じ? my routeチームは皆さん優しく、困った事は全てサポートしてくれます。歓迎会も素晴らしかったです! ブログを書くことになってどう思った? 過去の先輩方の紹介ブログをよく読むようになり、さらに会社の事を知れて良い機会になりました。 H.Nさん ⇒ K.Sさんへの質問 家族でこれまで行った中でおすすめのキャンプ場や旅先があれば教えてください! 都内から近場&子供&ペット同伴が喜ぶ&高規格という点で TACO GLAMP 毛呂山町ゆずの里オートキャンプ場 みかぼ高原オートキャンプ場 です!他にも沢山あります。 ですが、ぶっちゃけ焚き火さえできれば何処でもOKですw ちる 自己紹介 はじめまして! ちるです。 IT/IS部 コーポレートITグループに所属しています。  これまではWEB系の開発エンジニアとしてのキャリアを積んできたのですが、KTCではコーポレートITとして社内情報システム、販売店様の情シス業務支援など組織を強くしていくためにエンジニアスキルを発揮できるよう日々奮闘しています! 所属チームの体制は? チームはInnovation Driveチームに所属しています!私を含め9名のチームでそれぞれのメンバーが得意領域で活躍しています! チームの目標として「KTCのIT環境を最高のモノにする。」「KTCで生み出された価値を社外に届ける。」といった目標があり、社内だけにはとどまらず「KTCの価値を最大化し対外的な価値に繋げる技術者集団」を目標としているチームになります。 KTCへ入社したときの第一印象?ギャップはあった? 第一印象は、元気な人が多いな! でした 入社してすぐに社内イベントがあったり、定期的に開催されるビアバッシュがあったり交流が多い会社だなって思いました。 ギャップに関してはカジュアル面談や採用面接で色々とお話伺っていた内容がそのままだったので大きなギャップは特になかったです。ただ大企業のグループ会社なので、業務フローなどが固いイメージ、制約などが厳しいと勝手に想像していたのですが、そういった事は特になく、むしろスピード感がすごくて驚きました! 現場の雰囲気はどんな感じ? 所属チームのメンバーは拠点が別なこともあり、全員が揃うのはオンライン上がほとんどですが不思議なくらい距離感を感じないです。 困ったときの相談や質問もSlackやZoom等ですぐコミュニケーション取れるのでとても良い雰囲気で仕事できています。 障害発生時にみんなが「どうした?どうした?」って集まってくる感じや、チームMTGで相談すると意見交換が盛り上がって時間足りなくなることあったりと、とても活発なチームでよいな!と感じています。 ブログを書くことになってどう思った? 入社前からテックブログは読んでいて書くことはわかっていたので、ついに来たか・・・と思いました笑 K.Sさん ⇒ ちるさんへの質問 名古屋オフィスから近くて美味しい定食屋さんあったら教えてください! オフィス近くの柳橋中央市場内にある 天ぷらとワイン小島 揚げたての天ぷら定食おすすめです 名古屋にいらっしゃる際には是非一緒にいきましょ! MAo 自己紹介 IT/IS部 コーポレートITグループに所属しています。主に視える化担当でBI作成しつつ、その前後の業務改善をしたりと現場に近い距離で業務しています。 所属チームの体制は? Innovation Driveチームに所属しており10名ほどのチームです。 KTCへ入社したときの第一印象?ギャップはあった? 周囲の人がすごく話しかけてくれる!! 現場の雰囲気はどんな感じ? それぞれの考えを持ち寄り、より良い解決策を考える雰囲気です。 ブログを書くことになってどう思った? 何を書くのかドキドキしました。 ちるさん ⇒ MAoさんへの質問 最近のマイブーム教えて下さい! 道路を走っている車を眺めながらお茶を飲むこと!「みんな、動いてるなぁ。私も頑張ろう!」と思えます。 さいごに みなさま、入社後の感想を教えてくださり、ありがとうございました! KINTOテクノロジーズでは日々、新たなメンバーが増えています! 今後もいろんな部署のいろんな方々の入社エントリが増えていきますので、楽しみにしていただけましたら幸いです。 そして、KINTO テクノロジーズでは、まだまださまざまな部署・職種で一緒に働ける仲間を募集しています! 詳しくは こちら からご確認ください!
アバター
My name is Ryomm and I work at KINTO Technologies developing my route (iOS). Here are some things I've done to save CI credits: Introduction In our project, we use Bitrise as a CI tool. Last year, in addition to regular unit testing, we introduced snapshot testing and moved to SPM . Before we knew it, the time it took for each Bitrise CI run had ballooned to around 25 minutes, and in months when a lot of implementation was happening, we often ended up exceeding our budget. Bitrise becomes expensive if you exceed the contracted amount, so at the exchange rate at the time of writing, it costs about 400 yen for each excess CI run. That's expensive! For this reason, when credits were about to exceed the limit, a trend was created to limit PR merging to the bare minimum in order to avoid moving CI. In order to overcome this situation, we have been working on some credit saving techniques for our project. Reviewing the CLI Tool Setup By looking at the Bitrise Build results, you can see how long each step took. Bitrise Build Result Looking at this, we can see that it took 12 minutes in "Script Runner". This is the step where we set up swiftLint and LicensePlist. As I mentioned in my previous article , the libraries can be downloaded and used in a package created separately from the workspace in order to be executed in the Build Phase. Well, this certainly does take time, so I'll try to shorten it. Fortunately, the library we want to use here is compatible with the Build Tool Plugin, so we can skip this step by transferring it to that library. Since the settings such as license_plist.yml and .swiftlint.yml have already been made, all you need to do is add the package to the project's Package Dependencies and add the plugin to Run Build Tool Plug-ins in the target Build Phase. Build Phase settings Since the location of LicensePlist cannot be specified by outputPath when it is a plugin, you need to include it in BuildPhase so that the license file is moved under Settings.bundle as described in README . Also, the package needs to be included in the app itself, not just a package linked via Frameworks. This completely eliminated the "Script Runner" step, saving me 12 minutes... and cutting my credit costs in half!🎉 Bitrise Build Result Additionally, project configuration has been simplified and there is no longer any need to run separate shells for setup or version updates. In this case, everything was compatible with the Build Tool Plugin, so I changed the configuration, but I also tried nest as a different approach. This allows you to reduce CI time while still managing your existing CLI tools as separate packages. Replace the package for installing the CLI tool under the tools directory with nest. Project/ ├── Hoge.xcworkspace ├── Hoge.xcodeproj ├── Test/ │ └── ... ├── ... └── tools └── nextfile.yaml // ここを置き換える When you run nest bootstrap nestfile.yaml , you can see that the binary is installed in tools/.nest/bin , so set it to be executed in the Build Phase. Configuring swiftlint in the build phase This may be useful if the Build Tool Plugin is not supported. Review the Test In our project, all tests were packed into one test target, so all tests were always run. Furthermore, the snapshot tests were very heavy tests that took about an hour to run, so the method that compared them with a reference image was commented out on the CI so that it would not be executed. However, because asynchronous drawing processes such as waiting before comparison are executed, if the process fails, timeouts accumulate and you have to wait for a long time, which is also one of the factors that eats up credits. Therefore, I separated the snapshot tests that were not running on CI into a separate test target and tried to control the tests that were executed using TestPlan. First, create a test target for the long-running snapshot: Create a test target After configuring the targets using the existing test targets as reference, move the snapshot tests to the newly created target via Target Membership in the Compile Sources under Build Phases or in the File inspector for each test file. In this case, if the moved test file has a dependency on a test file in the original test target, it will not be able to be built, so you will need to separate the dependencies each time. * Change the target * Next, create a TestPlan. A TestPlan is a collection of tests to be run and their configuration. In this case, the tests you want to run can be specified on a test target basis. For this purpose we have created a separate test target. TestPlans can be linked to schemas, and in our app we have a one-to-one relationship between schemas and TestPlans. And in the TestPlan for the schema you want to use on CI, make sure you don't run snapshot tests. TestPlan settings When you actually run it, the execution time doesn't change significantly on CI unless there is a failure. However, the local testing experience has improved significantly. Snapshot tests used to be executed even when only the logic was changed, but now they can be prevented from being executed by simply unchecking the box, resulting in a significant reduction in time. We have decided not to run tests that were not originally run on CI in the first place, but we would like to make it possible to run snapshot tests as well after adjusting the balance with credit usage. Conclusion In addition to the steps introduced here, other measures that can shorten build times include fixing code that takes a long time to infer types and deleting unused assets. These efforts reduced the average time per CI run from about 22 minutes to about 12 minutes, resulting in a savings of about 45% in credits. This time, we focused on reducing the time before and after the build, which is something we can do immediately, but next time we would like to reduce the build time itself even more.
アバター
Introduction Nice to meet you. My name is Yena, and I’m working on Android app development at KINTO Technologies. My career began with Android development, and I have been involved in a wide range of areas, including smart TV apps, web backend and frontend development, and API development. While currently working on Android development, I also plan and run an in-house study group called "Droid Lab" to support the growth of both myself and my team. To share the technical content from the study group more broadly outside the team, we considered publishing the materials as a tech blog, but we faced the following challenges: Since the materials are mainly created in Confluence or PPT format, they need to be converted to Markdown format before being posted on a blog, which makes it impossible to publish them as is and requires significant effort. This conversion work was a significant burden, making it difficult to secure enough time to write blog articles, which led to delays in disseminating information as intended. To solve these challenges, we introduced a mechanism that leverages generative AI ChatGPT's Custom GPT to automatically convert source materials, such as those in Confluence and PPT formats, into Markdown format. We are currently running trial operations, and this mechanism is expected to bring benefits such as reducing the burden of conversion work and improving the efficiency of information sharing. Here, I will explain the creation procedure and settings of the GPT that we actually introduced. How To Create a GPT 1. Creating a GPT After logging in, click "Explore" on the top left. Select "Create GPT" from the My GPT category. Set a Name and profile picture in GPT Builder. DALL·E can also automatically generate an image that matches the name you created (the image below was generated by AI based on the name we created in DALL·E). Setting GPT details 1 Description Here is a brief one- or two-line description of what this GPT does. Example: This GPT automatically generates technical blog articles in Markdown format based on Droid Lab materials. The branch used for creating blog articles should follow this format: (branch name: sample/YYYY-MM-DD- ). 2 Instructions Here, you should describe in detail how GPT should behave, what kind of output it should generate, and any constraints. This is the core of the prompt. Below are the output rules that GPT should follow: Output the Markdown body text below YAML (*In our case, we have a fixed format, so we define it in YAML.) Heading -> ## Heading List -> - Item Inline code -> Code Code block -> Written in open/closed sets such as kotlin Image -> ![description](URL) ⚠️ Do not change the structure, style, or order of sentences. The output must always start with Markdown and be enclosed within a code block. If the internal API URL or a confidential name is included, clearly state "【Non-public information may be included】" and provide suggested corrections. Even if the output is long, do not stop midway and return the entire output in one go. 3 Conversation starters Register example sentences to show how users should use it. Example: Convert the Confluence memo into a technical blog in Markdown format. 4 Knowledge Specify supplementary materials (such as style guides, sample articles, etc.) to be uploaded to the GPT. Internal blog style guide e.g. samples of previously published articles * Be sure to confirm that no confidential information is included before uploading. 5 Capabilities Web Search Canvas DALL·E Code Interpreter -> The function can be turned on and off as needed! Setting GPT details After entering all the information, click the “Create” button in the upper right, and GPT creation will be completed . 2. Setting up GPT Here, I will explain the operating rules and constraints set for GPT. Below are the main functions we have set up to comply with our internal blog format and ensure proper Markdown conversion via GPT: Function Description ✍️ Conversion to Markdown format Converts the pasted text directly to Markdown syntax without changing the style or structure. 📄 Automatic addition of YAML meta information Automatically generates postId, title, and excerpt to match the company's blog format. 🧱 Preservation of syntax and unification of output format Outputs everything in a single code block to prevent Markdown syntax from being broken. 🔐 Security check function Detects internal APIs and internal code names, marks them as 【Possibly confidential information】 and suggests corrections. ⚠️ Output interruption prevention logic Outputs everything in one go, even for long text, without stopping midway to prevent syntax from being broken. ⚠️ Japanese/English check Detects variations in notation, typos, and unnatural expressions, and suggests corrections as necessary. 2-1. 🧠 Detailed specifications of GPT (prompt settings) This GPT functions as a professional tech blog writer and a Markdown converter tool. When a user pastes text from Confluence or an internal memo, it will be output according to the format below, without altering the original structure, order, or style. 1. YAML Meta Information Output Rules To publish it as a blog post, YAML meta information (such as title, date, and category) must be defined at the beginning. This GPT automatically generates a YAML header from the pasted text, according to the following rules: YAML must be output in a yaml code block in the prescribed format. The "title" and "excerpt" fields must be automatically completed by inferring and extracting them from the text. If there is no title, it must be output as 'Article title here' . If the article is very long, Markdown code block must be automatically split into multiple blocks, and output must continue until completion, without the need for the user to prompt by saying "continue.” Automatic Category Judgment Rules If your post contains the following keywords, automatically replace category with the corresponding one: Kotlin, Compose, MVVM, KMP -> "Android" GitHub, CI/CD, CodeBuild -> "DevOps" Lint, architecture, coding standards -> "Architecture" Confluence, Markdown, GPT -> "Tooling" Firebase, AWS, S3 -> "Cloud" Study group, internal sharing, knowledge -> "Team" AI, ChatGPT, Prompt, Natural Language Processing -> "Generative AI" If multiple categories apply, prioritize the category that appears most frequently . 💡 Below is an example of the YAML header format used for our internal blog (it may not be usable in external environments): [Example] --- postId: "<自動生成されたID>" title: "<記事タイトル>" excerpt: "<要約文>" coverTitle: "<表紙用の見出し>" coverImage: "<画像パス(社内ブログ用)>" date: "<ISO形式の日付>" category: "<カテゴリ名>" .... --- 2. Markdown Body Text Output Rules Markdown conversion rules: Heading -> ## Heading List -> - Item Inline code -> Code Code block -> Example: kotlin (※ Be sure to close after opening.) Image -> ![explanation](image URL) ⚠️ Do not change sentence structure, order, or style. ⚠️ Output everything accurately up to the closing tag to avoid breaking the Markdown syntax. 3. Post-Output Automatic Check Function Put all the output (YAML + Markdown body text) in one code block ( Markdown ) . The output must begin with Markdown and end with the corresponding closing code block tag . Even if the text is long, the output must not stop midway, and the entire text must be returned in one go . 3-1. 🔐Security Check Check that the following items are not included: Internal API URL Internal library name Project code name If confidential information such as a customer ID is detected, mark it as “【Possible confidential information】” and present proposed revision for publication at the same time. Detection example (screenshot) 3-2.⚠️ Japanese/English Check Japanese typos, misused particles, awkward phrasing, and inconsistencies in sentence endings, among other issues. English spelling mistakes, grammatical errors, unnatural expressions, etc. If necessary, output the proposed revision under the heading “⚠️ Text check:” However, do not change the order, structure, or style of the sentence in any way; simply point out the mistakes . Detection example (screenshot) 4. Usage Demo 4-1. Execution Steps 💬 Step 1 : Enter the TEXT you want to convert 💬 Step 2 : Paste into custom GPT and send 💬 Step 3 : Output in Markdown format 5. Benefits Gained from the Implementation The implementation of this GPT has brought several benefits, including increased efficiency in Markdown conversion and improved quality of information sharing. Incidentally, ** this article has also been automatically converted to Markdown format using this GPT.** Item Before After Benefit Markdown conversion task 30 minutes to 2 hours or more Tens of seconds to several minutes Reduced workloads by more than 80% Format unification Individual differences exist Automatic, stable output Improved quality and readability Security verification Manual verification Automatic detection and marking Safe to publish Sentence verification Manual verification Automatic detection and marking Safe to publish Users Mainly those familiar with the Markdown format Members who are unfamiliar with the Markdown format can also use it Expanded scope of use 6. Summary We introduced this GPT to reduce the effort involved in Markdown conversion, aiming to make it easier to casually and widely share the knowledge gained at Droid Lab, both within the team and beyond. By leveraging generative AI, time-consuming conversion tasks have been streamlined, enabling us to share information more efficiently while maintaining confidence in both quality and security. Moving forward, we plan to improve usability by adding features like direct PowerPoint uploads and automatic meeting summaries. We aim for a future where knowledge sharing across the entire development team becomes even smoother! 🚀 Potential Applications Addition of Japanese-to-English conversion function Support for global communication and sharing with international members. Support for PPT uploads We are currently developing a system that will eliminate the need for manual copying and allow conversion simply by uploading files. Introduction of GPT for meeting summarization Optimization to automatically extract summaries and ToDos by simply pasting meeting logs and minutes
アバター
The other day, during a casual chat with a colleague, he suddenly said: "AI has come so far in the blink of an eye. I can't even imagine what it'll be like in five years." I'm not an AI expert, but it just so happens I've looked into it a little bit. And the answer to that question isn't as simple as saying "It'll get way better." The thing is, there are some deep-rooted challenges in how the technology actually works. I’m no expert, but here’s my take. I'd like to share what some of these challenges are, and what might help us get past them. Humans as the Bottleneck As you know, generative AI, including Large Language Models, needs a whole lot of data to learn. And by "a whole lot," I mean enormous . That data is collected from publicly available sources through web crawling and scraping, as well as from books, code repositories, and so on. And the key is that all of that content is created by humans . But we humans just aren't fast enough. We can't produce new data at the rate AI is consuming it now. According to a paper by Pablo Villalobos from the research institute Epoch AI , if current trends continue, we could run out of high-quality, publicly available human-generated text data sometime between 2026 and 2032 . In other words, "scaling up with more data" may not work anymore beyond that point. Simply because there isn't enough new human-generated content left to feed these models. Forecasting Human-Generated Public Text and Data Consumption in LLMs Reusing data (a technique called multi-epoch learning ) has some effect, but it's not a fundamental solution. To make matters worse, a lot of the data currently proliferating is of poor quality. For example, spam, social media comments, extremely biased information, misinformation, even illegal content. Also, it's worth pointing out that in languages less commonly used than English, the pace at which human-generated content accumulates is naturally much slower. Therefore, in such languages, the gap between the amount of data created by humans and the data needs of AI could become an even bigger problem. So, what should we do about it? Here are a few of the proposed solutions: Using synthetic data (i.e., data generated by AI itself) for training While this can be effective in some areas, it also comes with a risk of "model collapse." I'll get into the details in the next section. Utilizing non-public data This means using proprietary data held by companies for AI training. This obviously raises serious legal and ethical questions. In fact, some companies, such as the New York Times , have already banned AI vendors from scraping their content. Improving model efficiency Instead of just making models bigger, the idea is to train them to learn smarter . Actually, we're starting to see signs of this shift. When using tools like ChatGPT, we can see something like "reasoning" where the model links multiple steps logically, rather than just recalling memorized information. Inbreeding in Generative Models As mentioned earlier, one way to increase the amount of training data is to generate more of it. But this comes with its own risks. In this paper , Zakhar Shumaylov from the University of Cambridge investigates the question: "What happens when we train a next-generation model on data generated by past AI models, rather than by humans?" The authors point to a dangerous feedback loop called model collapse . When AI-generated data is used over and over again for training, the model gradually drifts away from the original distribution of real-world data. As a result, its outputs become more generic, monotonous, and distorted. In particular, rare and subtle features are more likely to get lost. This mainly happens for two reasons: Statistical errors build up over generations, due to a limited sample Functional errors emerge because the model can't perfectly reproduce complex data distributions Visual images of model collapse Interestingly, keeping just 10% of the original human-generated data can help reduce model collapse to some extent. However, it cannot be completely prevented . Unless we make a deliberate effort to preserve real, human-generated data, AI models will increasingly end up trapped in a narrow, self-reinforcing worldview. It's effectively digital inbreeding . Furthermore, Gabrielle Stein from East Carolina University explored whether this issue could be avoided by using "cross-model learning," in which AIs exchange and learn from each other's data. The conclusion? It didn't really make much of a difference . In her study, she trained models on different proportions of human data: 100%, 75%, 50%, 25%, and 0%. The results showed the following trends: As the proportion of synthetic data increased, linguistic diversity steadily declined No "tipping point" was observed where performance suddenly collapsed at a specific percentage Even a small amount of human data helped slow the rate of degradation She suggests that to avoid early-stage model collapse, at least half of the training data should reliably come from confirmed human-written content. Considering that much of the data we see online is generated by AI, and that most AI training data is scraped from the Internet, it paints a somewhat bleak picture for the future of AI. As AI-generated content increasingly makes its way into training data, the risk of causing model collapse in the future keeps growing. Still, there are some fresh, innovative approaches are emerging that could lead to a breakthrough. What Comes Next? To address the challenges I've covered so far, a few relatively new approaches have started to emerge. While they're not permanent solutions, they might be able to delay the collapse caused by inbreeding and data shortages for a while. One example already mentioned is AI reasoning . This refers to behavior in models like ChatGPT, where the model goes through multiple steps of internal reasoning and judgment before producing a final answer. Another promising method is called Retrieval-Augmented Generation (RAG) . Put simply, this approach lets AI models generate responses not just from their training data, but also by pulling in external documents . For example, feeding a PDF into an LLM or letting it search the Internet before answering a question would fall into this category. That said, as you can probably guess, this doesn't solve the underlying problem of a lack of data . After all, the amount of new, reliable information we can feed into a model is still limited. So, what are promising approaches that haven't been fully realized yet? One example is the trend toward synthetic reality and embodied agents . This is a completely different approach to AI development. Instead of learning passively from static datasets, the idea is to place AI agents in dynamic virtual environments where they act, explore, and adapt to achieve goals. The data obtained is self-generated , produced through experiencing results, testing hypotheses, and planning strategies. It's contextual, diverse, grounded in interaction, and extremely high in quality. This method enables sustainable and self-renewing learning in environments with near-infinite variation. Regardless of the exhaustion of human-written text, it helps avoid the trap of AI being stuck in its own output. ...However, we're not there yet. Sure, we've been successful at offloading all sorts of boring tasks onto AI. But for now, it looks like we still have a fair amount of work to do ourselves. Thanks for reading!
アバター
In 2025, as AI continues to evolve rapidly, being able to effectively use AI has become a key skill for engineers. However, to do so, it is essential to understand prompts appropriately (how to give instructions), requiring experience and knowledge. As the first step in coding with AI, I will introduce you to development utilizing TDD (test-driven development) and AI, which is the theme of this article. Benefits of TDD × AI ✅ Drastically reduced implementation cost! Engineers “only need to write tests” Other than writing tests, they do not need to give complex instructions or prompts. After that, AI automatically generates code. ✅ Development speed skyrockets! Dramatic reduction in detailed back-and-forth communication AI can instantly generate code at each step of TDD, significantly accelerating development efficiency and improving consistency across the codebase. ✅ Exceptional code quality! AI output can be controlled with proper testing Proper testing ensures control over AI generative code. The result is code with fewer bugs. What is TDD? Here is a brief explanation of TDD (Test-Driven Development), which is a fundamental premise. https://www.amazon.co.jp/dp/4274217884 "TDD (Test-Driven Development)" is a methodology proposed by Kent Beck in his book👆 over 20 years ago. By repeating the simple cycle shown in the diagram above — first, write a test, then implement code to pass the test, and finally, refactor — you can produce high-quality, maintainable code. Since testing serves as the starting point for implementation, development can proceed while ensuring a testable structure. Practice TDD :::message This is performed using the Agent mode of GitHub Copilot in VSCode. ::: https://code.visualstudio.com/docs/copilot/copilot-edits#_use-agent-mode-preview With this method, an engineer writes tests and generative AI handles implementation and refactoring, enabling efficient, high-quality development. This time, I will explain the flow of TDD × AI using password validation as an example. Let's start with character count validation . :::message alert It is important to proceed in small steps, without filling in all the specifications from the beginning! ::: Step 1: Creating a Character Count Validation Test Prepare a target password validation class and test files. You can leave both files empty at this stage. Once the files are created, you might be tempted to immediately write the password validation class. But since we’re using TDD, we’ll start by writing tests. @Test("パスワードの文字数が8文字以上16文字以下でバリデーションが成功すること") func testPasswordLengthValidation() async throws { let sut = PasswordValidator() let validPasswordShort = "Valid123" let validPasswordLong = "ValidPassword123" let invalidPasswordShort = "Short01" let invalidPasswordLong = "ThisPasswordIsWay" #expect(sut.validate(validPasswordShort) == true) #expect(sut.validate(validPasswordLong) == true) #expect(sut.validate(invalidPasswordShort) == false) #expect(sut.validate(invalidPasswordLong) == false) } Since the PasswordValidator class and validate method aren't implemented yet, you’ll naturally get an error Next, input a prompt to the AI and ask it to implement. ![input prompt](/assets/blog/authors/HiroyaHinomori/2025_03_tdd_x_ai_02.png =500x) Then, after a few tens of seconds, the AI generates an implementation. struct PasswordValidator { // パスワードの長さの制約を定数として定義 private let minimumPasswordLength = 8 private let maximumPasswordLength = 16 /// パスワードが有効かどうかを検証します /// - Parameter password: 検証するパスワード /// - Returns: パスワードが有効な場合はtrue、そうでない場合はfalse func validate(_ password: String) -> Bool { let passwordLength = password.count return passwordLength >= minimumPasswordLength && passwordLength <= maximumPasswordLength } } Next, let's add a feature to validate the mix of numbers, letters, and symbols. Step 2: Adding Validation of Numbers, Letters, and Symbols @Test("パスワードの文字列の中に数字、文字、記号が含まれている場合にバリデーションが成功すること") func testPasswordCharacterValidation() async throws { let sut = PasswordValidator() let validPassword = "ValidPassword12!" let invalidPasswordNoNumber = "InvalidPassword!" let invalidPasswordNoCharacter = "1234567890!" let invalidPasswordNoSymbol = "InvalidPassword1" #expect(sut.validate(validPassword) == true) #expect(sut.validate(invalidPasswordNoNumber) == false) #expect(sut.validate(invalidPasswordNoCharacter) == false) #expect(sut.validate(invalidPasswordNoSymbol) == false) } At this stage, an error will occur, but don't worry about it; just provide the test case to the AI and let it generate the implementation.. struct PasswordValidator { // パスワードの長さの制約を定数として定義 private let minimumPasswordLength = 8 private let maximumPasswordLength = 16 /// パスワードが有効かどうかを検証します /// - Parameter password: 検証するパスワード /// - Returns: パスワードが有効な場合はtrue、そうでない場合はfalse func validate(_ password: String) -> Bool { let passwordLength = password.count guard passwordLength >= minimumPasswordLength && passwordLength <= maximumPasswordLength else { return false } let hasNumber = password.rangeOfCharacter(from: .decimalDigits) != nil let hasLetter = password.rangeOfCharacter(from: .letters) != nil let hasSymbol = password.rangeOfCharacter(from: .symbols) != nil || password.rangeOfCharacter(from: .punctuationCharacters) != nil return hasNumber && hasLetter && hasSymbol } } Up to this point, we completed the implementation in under 10 minutes . In traditional coding, you need to explicitly define various conditions and specifications in the prompt, but with this method, you simply ask AI to implement something that meets the test conditions . Since all the implementation details have been written in the test, there is almost no need for complex prompt instructions. To Further Streamline Communication with AI If you write implementation rules and constraints in advance in "copilot-instructions.md," there's no need to provide detailed instructions to the AI each time. 日本語で返答してください。 ### コーディングルール - テストはswift-testingを使用してください。 - 実装には基本的にマジックナンバーは使わないこと - DRYの原則に則って実装してください - KISSの原則に則って実装してください - YAGNIの原則に則って実装してください To Become an Engineer Who Thrives in the Age of AI AI is not omnipotent. But that’s no reason to give up! It is important to calmly determine what "AI is good at" and what "humans should handle." With “TDD × AI,” let's understand the coding habits of AI and reach new levels of speed and quality in development!🚀
アバター
Introduction Hello. My name is Shiode, and I do payment-related backend development in the Toyota Woven City Payment Solution Development Group. As mentioned in my previous article , our group uses Kotlin for development, with Ktor as our web framework and Exposed as the ORM. We also adopt Clean Architecture in our code architecture. Initially, we used Kotlin's Result type for error handling, but as the number of developers increased, we started seeing a mix of Result and throw used in code. Mixing throw into code that uses Result defeats the purpose of expressing error handling through types, as it still requires try-catch blocks. Since Kotlin doesn't have Java's checked exceptions, it's easy to forget to call a try-catch block, which can lead to unhandled errors. To improve this situation, we discussed within the team and decided to standardize our error handling using Kotlin's Result type. In this article, I'll walk you through how our group writes error handling in practice. This article does not include the following. Explanation of Clean Architecture Explanation of Ktor and Exposed Comparison between kotlin-result and Kotlin's official Result type Application Directory Structure Before getting into the main topic, I will explain the directory structure of the application in this group. Below is the well-known diagram of Clean Architecture along with our group's directory structure. As we've adopted Clean Architecture, our application's directory structure is generally organized in line with its principles. (Source: The Clean Code Blog ) App Route/ ├── domain ├── usecase/ │ ├── inputport │ └── interactor └── adapter/ ├── web/ │ └── controller └── gateway/ ├── db └── etc The correspondence between our directory structure and the Clean Architecture diagram is as follows: domain directory: entities usecase directory: Use Cases adapter/web/controller directory: Controllers adapter/gateway directory: Gateways The terminology doesn't match exactly, but basically the domain directory sits at the core, the usecase directory surrounds it, and everything under adapter forms the outermost layer. Therefore, the allowed direction of dependency is as follows: usecase -> domain Everything under adapter -> usecase or domain This direction of dependency makes it possible to develop business logic without being affected by factors such as web frameworks or database types. Error Handling Policy Basically, our error handling is based on the following policies: Use Result type instead of throw in case of processing failure When a function returns a Result type, do not use throw When returning an exception, use a custom-defined exception type In the next section, I'll go over each of these policies in more detail, with code examples. Use the Result type When a Function May Fail Since Kotlin doesn't have checked exceptions like Java, there's no mechanism to force the caller to handle errors. By using the Result type, you can explicitly indicate that an error may be returned to the caller, reducing the chances of error handling being missed. However, in cases like Result<Unit> , where the return value isn't used, error handling cannot be enforced unless a custom lint rule is defined. But as of now, we haven't defined one yet. Code Examples Below is a simple code example. When defining a function that performs division, it typically results in an error if the denominator is zero. If a function might fail, specify Result as its return type. In this example, Result<Int> is specified. fun divide(numerator: Int, denominator: Int) : Result<Int> { if (denominator == 0) { return Result.failure(ZeroDenominatorException()) } return Result.success(numerator/denominator) } When Returning an Exception as a Result Type, Wrap the Exception in a Custom-defined Exception Repositories are defined as interfaces in the domain layer, with their implementations residing in the adapter layer. If a use case layer calls a repository function and handles errors, and the adapter layer returns a third-party library exception as is, then the use case layer must be aware of that third-party exception. In that case, the use case layer becomes dependent on the adapter layer. Here's what that looks like in a diagram: ![依存関係](/assets/blog/authors/reona-shiode/error-handling/dependency.png =400x) Interface-based Dependency and Exception-based Dependency (bad example) To avoid this, we always make sure to wrap any exception returned via Result in a custom-defined exception. One tricky point when applying Clean Architecture is deciding which layer exceptions belong to, but I personally think it should be in the domain layer. Our group uses a shared set of custom exceptions across multiple services, so we've extracted them into a separate domain library. Another tricky point with Kotlin's official Result type is that it doesn't allow you to specify the exception type, which means you can't enforce returning only custom exceptions. In cases like this, it may be worth considering the use of kotlin-result . However, we chose not to adopt it in order to avoid introducing third-party types into the domain code. Code Examples Let's say the following interface is defined in the domain layer. data class Entity(val id: String) interface EntityRepository { fun getEntityById(id: String): Result<Entity> } Now, consider a case where a third-party library exposes a method like the one shown below, and it's used as-is. fun thirdPartyMethod(id: String): Entity { throw ThirdPartyException() } Bad Example If the implementation in the adapter layer returns the exception from the third-party library directly. As shown below, this causes it to leak into the caller such as UseCase . class EntityRepositoryImpl : EntityRepository { override fun getEntityById(id: String): Result<Entity> { return kotlin.runCatching { thirdPartyMethod(id) } // This returns the third party exception } } Good Example To prevent third-party exceptions from leaking to the caller, wrap them in a custom-defined exception. class EntityRepositoryImpl : EntityRepository { override fun getEntityById(id: String): Result<Entity> { return kotlin.runCatching { thirdPartyMethod(id) }.onFailure { cause -> // wrap with our own exception CustomUnexpectedException(cause) } } } Avoid Using throw in Functions that Return Result If a function returns a Result type or throws an exception, the caller must handle both. Even if the function author thinks a particular exception doesn't need to be handled by the caller, there may be cases where the caller wants to handle it. For this reason, we have standardized on using the Result type and avoid throwing exceptions explicitly. In some cases such as database connection errors, it might seem acceptable to throw an exception from the adapter layer and let it propagate directly to the API response, since recovery at the use case level is impossible. However, for issues like failure to update the database, we may still want to log inconsistencies with third-party SaaS. In that case, if we scope out using throw, there's a risk that alerts won't be triggered appropriately. We believe it's up to the caller to decide whether error handling is necessary, so even if the function author considers it unnecessary, the exception is returned using a Result. Code Examples Let's take the save function of a repository as an example. The save function receives the entity class and returns the result as a Result<Entity> . Examples of what not to do As shown below, assume that a connection error was thrown, while other errors are returned using the Result type. class EntityRepository(val db: Database) { fun saveEntity(entity: Entity): Result<Entity> { try { db.connect() db.save(entity) } catch (e: ConnectionException) { // return result instead throw OurConnectionException(e) } catch (e: throwable) { return Result.failure(OurUnexpectedException(e)) } return Result.success(entity) } } Now, suppose the use case layer wants to take some kind of action if an error occurs during the save operation. In this case, you must use runCatching (which internally uses try-catch to convert to Result type). class UseCase(val repo: EntityRepository) { fun createNewEntity(): Result<Entity> { val entity = Entity.new() return runCatching { // need this runCatching in order to catch an exception repo.saveEntity(entity).getOrThrow() }.onFailure { // some error handling here } } } Good Example In the good example, all exceptions are wrapped in a custom-defined exception and returned using the Result type. This allows the caller to remove runCatching , simplifying the code. class EntityRepository(val db: Database) { fun saveEntity(entity: Entity): Result<Entity> { try { db.connect() db.save(entity) } catch (e: ConnectionException) { return Result.failure(OurConnectionException(e)) } catch (e: Exception) { return Result.failure(OurUnexpectedException(e)) } return Result.success(entity) } } class UseCase(val repo: EntityRepository) { fun createNewEntity(): Result<Entity> { val entity = Entity.new() return repo.saveEntity(entity).onFailure { // some error handling here } } } Useful Custom Function for Using the Result Type andThen When using a Result type, there are often times when you want to use that value of a successful Result to return a different Result type. For example, updating the status of a specific entity might look like this: fun UseCaseImpl.updataStatus(id: Id) : Result<Entity> { val entity = repository.fetchEntityById(id).getOrElse { return Result.failure(it) } val updatedEntity = entity.updateStatus().getOrElse { return Result.failure(it) } return repository.save(updatedEntity) } In such cases, writing code becomes easier when the operations can be connected by a method chain. While the kotlin-result provides the andThen function for this purpose, Kotlin's official Result type does not. Therefore, our group defined and uses the following method: inline fun <T, R> Result<T>.andThen(transform: (T) -> Result<R>): Result<R> { if (this.isSuccess) { return transform(getOrThrow()) } return Result.failure(exceptionOrNull()!!) } By using this, the previous example can be rewritten as shown below. The result is a bit cleaner, with less repetitive code. fun UseCaseImpl.updataStatus(id: Id) : Result<Entity> { return repository.fetchEntityById(id).andThen { entity -> entity.updateStatus() }.andThen { updatedEntity -> repository.save(updatedEntity) } } doInTransaction for Exposed Our group uses Exposed as the ORMapper. With Exposed, all database operations must be written within the lambda scope called transaction . If an exception is thrown within this transaction scope, it automatically performs a rollback. Since using the Result type avoids throwing exceptions, we created a function that performs a rollback automatically when the Result indicates failure. fun <T> doInTransaction(db: Database? = null, f: () -> Result<T>): Result<T> { return transaction(db) { f().onFailure { rollback() }.onSuccess { commit() } } } Applying this to the previous example of UseCaseImpl , it can be used as follows. fun UseCaseImpl.updataStatus(id: Id) : Result<Entity> { return doInTransaction { repository.fetchEntityByIdForUpdate(id).andThen { entity -> entity.updateStatus() }.andThen { updatedEntity -> repository.save(updatedEntity) } } } respondResult for Ktor Our group uses Ktor as the web framework. A function called respondResult was created to allow Result types from use cases to be returned directly as HTTP responses. suspend inline fun <reified T : Any> ApplicationCall.respondResult(code: HttpStatusCode, result: Result<T?>) { result.onSuccess { when (it) { null, is Unit -> respond(code) else -> respond(code, it) } }.onFailure { // defined below respondError(it) } } suspend fun ApplicationCall.respondError(error: Throwable) { val response = error.toErrorResponse() val json = serializer.adapter(response.javaClass).toJson(response) logger.error(json, error) respondText( text = json, contentType = ContentType.Application.Json, status = e.errType.toHttpStatusCode(), ) } Although it's simple, using this function eliminates the need to call Result.getOrThrow , making the code a bit cleaner. fun Route.route(useCase: UseCase) { val result = useCase.run() call.respondResult(HttpStatusCode.OK, result.map {it.toViewModel()} ) } By the way, respondError is a function that returns an error response from the throwable. We use this function to handle exceptions thrown in the Ktor pipeline and return appropriate responses. We've also created a custom Ktor plugin to handle exceptions. val ErrorHandler = createApplicationPlugin("ErrorHandler") { on(CallFailed) { call, cause -> call.respondError(cause) } } Conclusion I introduced how our group handles errors, along with some helpful custom functions for the Result type. From what I've seen in various tech blogs, many companies seem to use kotlin-result , while there's relatively little information out there on using Kotlin's official Result type. We've found Kotlin's official Result type to be sufficient for error handling, so we encourage you to give it a try!
アバター
Hello! My name is Mayu, and I work as a designer in the Creative Office at KINTO Technologies. I usually focus on UI/UX design for apps, but this time, I was in charge of creating novelty items to distribute at a company event. In this article, I'll share a behind-the-scenes look at the process, from planning to design. I hope this will offer some helpful insights for those involved in novelty production. Novelty Selection The theme is "something that gives a sense of unity" For this event, we aimed to create a novelty item that fosters a sense of unity. We developed ideas based on the following conditions: Creates opportunities to communicate with people you don't usually interact with. Strengthens a sense of unity. Promotes innovation. Appeals to all, regardless of age or gender. Meets the needs of multiple people. Easy for anyone to use immediately. Budget: a few hundred to around a thousand yen per person. Offers lasting value. After considering various ideas, we ultimately decided to produce a " Magnetic Card Stand " and an " Original Name Card ". Reasons for choosing the "Magnetic Card Stand" and "Name Card" Magnetic Card Stand: Placing it on the desk makes it easier to naturally engage with others, promoting communication. Featuring the KINTO Technologies logo and car shape helps foster attachment to the company and boost motivation. Its simple design makes it easy for anyone to use in daily situations. Name Card: Creating name cards with each employee's name makes it easier to approach one another even on first meetings, promoting communication across the company. The cut-off KTC lettering design visually expresses a sense of unity throughout the organization. The cards can be used as name tags during the event and placed on desks afterward for continued use. Production of Magnetic Card Stand **1. Contractor Selection and Request ** We commissioned the production of the magnetic card stand to the original goods specialty website " MOKU ." The deciding factor was MOKU's high level of customization, which made it possible to create original magnetic card stands simply by submitting design data. 2. Prototyping We created a simple paper prototype to check the size and usability. We then placed it on an actual desk to evaluate visibility and practicality. **3. Design of Magnetic Card Stand ** Using Adobe Illustrator, we created a design with the logo positioned on a specified template. The result is a simple design that highlights the KINTO Technologies logo. 4. Design of Instruction Manual To ensure ease of use, we created an original instruction manual. Here too, we used Adobe Illustrator and produced the design data based on the specified template. 5. Data Submission and Delivery The design data was submitted, and delivery was completed in about three weeks! (Order quantity: 500 pieces) Production of Name Card 1. Create a Name Card Design Using Figma, we created an original design featuring names, division, and custom Slack emojis. We made a prototype to ensure it fit properly with the magnetic card stand. The key feature is this half-cut KTC lettering . KTC stands for "KINTO Technologies." The small squares represent employees, symbolizing the idea that "each individual comes together to form KTC." With a simple, stylish black-based design, it also brings out the essence of a tech company. 2. Automatic Data Generation Creating data manually for everyone would have been overwhelming, so we enlisted our in-house engineers to help automatically generate the data in HTML. We built a system that imports employee information from a CSV file and automatically populates it into a template. 3. Printing and Cutting Printed the materials using the office printer and cut them all by hand. It was tough, but it saved a lot of money! lol Project Results and Learnings After distributing the giveaways, we received a lot of encouraging feedback from employees, such as: "It's easier to start conversations now!" "The design is so cute!" "The Slack icon makes it feel even more personal!" The giveaways helped foster a sense of unity, and the project was incredibly rewarding for me as well. This experience also reinforced the importance of not just designing something attractive, but thinking carefully about how it will actually be used. I believe we were able to showcase the true power of purpose-driven design. Finally I hope to apply what I've learned from this project to future design work. If you thought, "KINTO Technologies sounds like a fun place!" please check out our recruitment page ! Looking forward to hearing from you! Thank you for reading!
アバター
EncryptedSharedPreferencesからTink + DataStoreに置き換えた話 こんにちは。Toyota Woven City Payment 開発グループの大杉です。 私たちのチームでは、 Woven by Toyota の Toyota Woven City で使用される決済システムの開発をしており、バックエンドからWebフロントエンド、そして、モバイルアプリケーションまで決済関連の機能を幅広く担当しています。 今回は公式にDeprecatedになってしまったEncryptedSharedPreferencesを実装していたAndroidアプリの置き換えをした話をまとめました。 はじめに EncryptedSharedPreferencesがv1.1.0-alpha07からDeprecatedになり、 Android KeyStoreへの置き換えが公式から推奨 されました。 ![Updates of security-crypto](/assets/blog/authors/osugi/20250616/security-crypto.png =600x) EncryptedSharedPreferencesの代替技術調査 EncryptedSharedPreferencesがDeprecatedとなったことで、永続化手段と暗号化技術の調査を始めました。 永続化手段の選定 私たちのアプリにおけるユースケースでは、設定データの保存にEncryptedSharedPreferencesを使用していただけであったので、SharedPreferencesを使用するだけでも十分ではありました。 ですが、せっかくの置き換えタイミングであったので公式推奨に則り、永続化手段として DataStore を採用しました。 暗号ライブラリの選定 こちらも前述の公式推奨の通り、 Android KeyStore を使用する方針で進めていこうとしたのですが、APIレベルによって機能の制約があるだけでなく、セキュリティレベルの高い実装(StrongBox)を使用するにはデバイスのスペックも関係するため、単純にプログラミングするだけでは想定したセキュリティレベルを担保できない可能性もありました。 今回のアプリは、MDMで管理されたデバイス上で動作する前提であり、StrongBoxにも対応しているデバイスを元々選定していたため、この制約については問題になりませんでした。 また、暗号ライブラリ調査の中で、 Tink というGoogleが提供している暗号ライブラリの存在を知りました。 Tinkのリポジトリ を見ると、マスターキーの保存にAndroid KeyStoreを利用されていることがわかります。 メンテナンスの容易さやパフォーマンスの観点でAndroid KeyStoreとTinkを比較するため、サンプル実装を行いました。 暗号ライブラリの実装比較 Android KeyStoreのStrongBoxとTEEを使用した場合とTinkを使用した場合のサンプルコードを以下にまとめています。 両者とも基本的な実装はそこまで苦労せず着手できたと感じました。 一方で、Android KeyStoreは 暗号アルゴリズムによってAndroid KeyStoreの鍵発行設定を変える必要がある 初期化ベクトル(IV)の管理が開発者依存になる 実装サンプルが少ない Tinkはこの辺りをうまくラップしてくれている良さがあります。 Android KeyStoreを使用した暗号・復号処理の実装例 class AndroidKeyStoreClient( private val useStrongKeyBox: Boolean = false ) { private val keyStoreAlias = "key_store_alias" private val KEY_STORE_PROVIDER = "AndroidKeyStore" private val keyStore by lazy { KeyStore.getInstance(KEY_STORE_PROVIDER).apply { load(null) } } private val cipher by lazy { Cipher.getInstance("AES/GCM/NoPadding") } private fun generateSecretKey(): SecretKey { val keyStore = keyStore.getEntry(keyStoreAlias, null) if (keyStore != null) { return (keyStore as KeyStore.SecretKeyEntry).secretKey } return KeyGenerator.getInstance(KeyProperties.KEY_ALGORITHM_AES, KEY_STORE_PROVIDER) .apply { init( KeyGenParameterSpec.Builder( keyStoreAlias, KeyProperties.PURPOSE_ENCRYPT or KeyProperties.PURPOSE_DECRYPT ).setBlockModes(KeyProperties.BLOCK_MODE_GCM) .setEncryptionPaddings(KeyProperties.ENCRYPTION_PADDING_NONE) .setIsStrongBoxBacked(useStrongKeyBox) .setKeySize(256) .build() ) }.generateKey() } fun encrypt(inputByteArray: ByteArray): Result<String> { return runCatching { val secretKey = generateSecretKey().getOrThrow() cipher.init(Cipher.ENCRYPT_MODE, secretKey) val encryptedData = cipher.doFinal(inputByteArray) cipher.iv.joinToString("|") + ":iv:" + encryptedData.joinToString("|") } } fun decrypt(inputEncryptedString: String): Result<ByteArray> { return runCatching { val (ivString, encryptedString) = inputEncryptedString.split(":iv:", limit = 2) val iv = ivString.split("|").map { it.toByte() }.toByteArray() val encryptedData = encryptedString.split("|").map { it.toByte() }.toByteArray() val secretKey = generateSecretKey() val gcmParameterSpec = GCMParameterSpec(128, iv) cipher.init(Cipher.DECRYPT_MODE, secretKey, gcmParameterSpec) cipher.doFinal(encryptedData) } } } Tinkを使用した暗号・復号処理の実装例 class TinkClient( context: Context ) { val keysetName = "key_set" val prefFileName = "pref_file" val packageName = context.packageName var aead: Aead init { AeadConfig.register() aead = buildAead(context) } private fun buildAead(context: Context): Aead { return AndroidKeysetManager.Builder() .withKeyTemplate(KeyTemplates.get("AES256_GCM")) .withSharedPref( context, "$packageName.$keysetName", "$packageName.$prefFileName" ) .withMasterKeyUri("android-keystore://tink_master_key") .build() .keysetHandle .getPrimitive(RegistryConfiguration.get(), Aead::class.java) } fun encrypt(inputByteArray: ByteArray): Result<String> { return runCatching { val encrypted = aead.encrypt(inputByteArray, null) Base64.getEncoder().encodeToString(encrypted) } } fun decrypt(inputEncryptedString: String): Result<ByteArray> { return runCatching { val encrypted = Base64.getDecoder().decode(inputEncryptedString) aead.decrypt(encrypted, null) } } } 暗号ライブラリのパフォーマンス検証 Android KeyStoreとTinkの暗号化処理時間のベンチマークを計測しました。 Android KeyStoreについては、 StrongBox と TEE の2つの実行基盤を利用したケースで評価しています。 テストコードでは、共通の暗号化アルゴリズム(AES_GCM)を設定し、10KBのデータを繰り返し暗号化する処理を Microbenchmark を使用して計測しました。Microbenchmarkを使用することで、Google Pixel Tabletの実機上でかつUIスレッド以外のスレッドを利用して計測を行っています。 テストコードは以下です。 import androidx.benchmark.junit4.BenchmarkRule import androidx.benchmark.junit4.measureRepeated import androidx.test.ext.junit.runners.AndroidJUnit4 import androidx.test.platform.app.InstrumentationRegistry import org.junit.Rule import org.junit.Test import org.junit.runner.RunWith @RunWith(AndroidJUnit4::class) class ExampleBenchmark { @get:Rule val benchmarkRule = BenchmarkRule() @Test fun benchmarkTinkEncrypt() { val context = InstrumentationRegistry.getInstrumentation().context val client = TinkClient(context) val plainText = ByteArray(1024 * 10) benchmarkRule.measureRepeated { client.encrypt(plainText).getOrThrow() } } @Test fun benchmarkStrongBoxEncrypt() { val context = InstrumentationRegistry.getInstrumentation().context val client = AndroidKeyStoreClient(context, true) val plainText = ByteArray(1024 * 10) benchmarkRule.measureRepeated { client.encrypt(plainText).getOrThrow() } } @Test fun benchmarkTeeEncrypt() { val context = InstrumentationRegistry.getInstrumentation().context val client = AndroidKeyStoreClient(context, false) val plainText = ByteArray(1024 * 10) benchmarkRule.measureRepeated { client.encrypt(plainText).getOrThrow() } } } 以下に計測結果をまとめました。 暗号化基盤 平均処暗号理時間 (ms) アロケーション数 Android KeyStore (StrongBox) 209 4646 Android KeyStore (TEE) 7.07 4786 Tink 0.573 38 Android KeyStore (StrongBox)およびAndroid KeyStore (TEE)ではハードウェアへのアクセスが発生するため、ソフトウェア側で暗号化処理を行っているTinkと比べてかなり処理に時間がかかっていることがわかります。 今回採用したデバイスはAndroidの中でも比較的スペックが高いものですが、特にAndroid KeyStore (StrongBox)を採用する場合は、UXの検討が必要になりそうです。 備考 ちなみに、実際にAndroid KeyStoreの鍵生成で適用されている実行基盤は以下のコードから判別できます。 val secretKey = generateSecretKey() val kf = SecretKeyFactory.getInstance(KeyProperties.KEY_ALGORITHM_AES, KEY_STORE_PROVIDER) val ki = kf.getKeySpec(secretKey, KeyInfo::class.java) as KeyInfo val securityLevelString = when (ki.securityLevel) { KeyProperties.SECURITY_LEVEL_STRONGBOX -> "STRONGBOX" KeyProperties.SECURITY_LEVEL_TRUSTED_ENVIRONMENT -> "TEE" KeyProperties.SECURITY_LEVEL_SOFTWARE -> "SOFTWARE" else -> "UNKNOWN" } Log.d("KeyStoreSecurityLevel", "Security Level: ${ki.securityLevel}") まとめ EncryptedSharedPreferencesがDeprecatedとなったため、移植先の技術選定を行いました。 公式推奨に則り、永続化手段としてDataStoreを採用しました。 暗号化技術に関してはAndroid KeyStoreとTinkの比較検証を行い、Tinkの方が鍵の発行や暗号化処理が抽象化されていて利用しやすく、また、処理速度も優れていることがわかり、セキュリティ要件としても十分であるためTinkを採用することにしました。 Android KeyStoreを採用する場合は、動作環境のデバイススペックも考慮した実装が求められるため、セキュリティ要件とのバランスを考慮する必要がありそうです。
アバター
はじめに こんにちは、KINTO テクノロジーズ Security CoE グループの多田です。普段は大阪のオフィスで勤務しています。我々のグループでは、マルチクラウド環境の「ガードレール監視とカイゼン活動をリアルタイムで実施する」をミッションに、クラウドセキュリティに関する多くのことにチャレンジしています。メンバが日々、どのような活動を実施しているかは、 こちらのブログ にもまとめていますので、ぜひご覧ください。 背景 世間の LLM ( 大規模言語モデル ) アプリケーション開発の流れに乗り、当社のプロダクトチームでは、多くの LLM アプリケーションを開発しており、PoC やプロダクトレディの状態に進展しています。一方で、クラウドセキュリティを監視するグループとしては、これらのアプリケーションのセキュリティについても適切な対策を講じる必要があります。 当社の LLM アプリケーションは、主に AWS、Google Cloud、Azure 上で開発されており、クラウドベンダーが提供する生成 AI サービスを活用して構築されることが多い状況です。当グループでは Cloud Security Posture Management ( CSPM ) の監視および運用を行っています。しかし、現時点では、生成 AI 関連サービスに特化した CSPM のコントロールは提供されていないのが現状です。例えば、AWS の場合、AWS Foundational Security Best Practices ( FSBP ) や Center for Internet Security ( CIS ) では、生成 AI サービスに関する直接的なコントロールが提供されていません。 そこで当グループでは、各クラウドベンダーの生成 AI サービスを利用して LLM アプリケーションを開発する際に遵守すべきガイドラインを策定しました。このガイドラインには、クラウドベンダーが提供する生成 AI 関連サービスの利用推奨や設定方法についても記載しています。言い換えれば、ガイドラインに記載した設定方法は、CSPM として監視すべきコントロールとなります。さらに、コントロールを Rego 言語 で実装し、AWS Bedrock の CSPM として運用を行っています。 タイトルにある、 AI-SPM は、AI Security Posture Management の略で、「 AI や機械学習 ( ML ) 、生成 AI モデルなどの AI 関連資産のセキュリティリスクやコンプライアンスリスクを可視化・管理・軽減するためのソリューション」というような定義がされているようなので、今回の取組みについても、あえて、AI-SPM という名前にしてみました。 LLM アプリケーションで遵守すべきセキュリティガイドライン ガイドラインを作成するにあたっては、 OWASP Top 10 for LLM Applications 2025 を参考にしました。おそらく、LLM アプリケーションのセキュリティを語るうえでは、最早、鉄板ともいうべき資料かと思います。この OWASP の資料では、LLM アプリケーションでよく見られる最も重大な脆弱性 Top 10 がリストされており、「概要」「脆弱性の例」「防御や軽減策」「攻撃シナリオ」などが記載されています。これらの内容を基に、各クラウドサービスで LLM アプリケーションを開発する場合、利用するサービス、機能の選定やベストプラクティスについて検討を行いました。 Top 10 の最初に記載のあるリスクは「 LLM01:Prompt Injection 」です。 このリスクは、 悪意ある入力によって LLM の振る舞いが意図せず操作され、情報漏洩や不正な動作を引き起こすリスク です。このリスクの予防・緩和策としては、 LLM への入力の検証・フィルタリング が有効となります。 そして、この予防・緩和策をクラウドサービス上で実装する場合にどうすべきかというと、AWS であれば、 Amazon Bedrock Guardrails に Prompt Attack をフィルタする機能があるので、この機能を有効化することが対策となります。あとは、CSPM のコントロールとして、この機能が有効化されているかどうかをチェックすることで、可視化とカイゼンが実施できるようになります。 以下に、Top 10 の中の代表的なリスクと AWS サービスでの「予防・緩和策」と「 CSPM コントロールとしての実装」をまとめますので参考にしてください。 Top 10 リスク概要 予防・緩和策 AWSでの実装 CSPM コントロールの実装 LLM01: Prompt Injection 悪意ある入力 ( プロンプト ) によって LLM の振る舞いが意図せず操作され、情報漏洩や不正な動作を引き起こすリスク モデル動作の制約、入力・出力の検証、フィルタリング、権限制御、人間の承認導入など Amazon Bedrock Guardrails の content filters「Prompt attacks」を利用する Amazon Bedrock Guardrails の Prompt attacks Configure prompt attacks filter を有効化 し、 Block アクション かつ 閾値が HIGH に設定されていることを確認する LLM02: Sensitive Information Disclosure モデルの応答や挙動から、個人情報や機密データなどのセンシティブな情報が漏洩するリスク 出力の検証・フィルタ、学習データの管理、アクセス制御 Amazon Bedrock Guardrails の「sensitive information filters」を利用する Amazon Bedrock Guardrails Sensitive information filters が Output で 有効化 されていることを確認する LLM06: Excessive Agency LLM やそのエージェントに過剰な自律性や権限を与えることで、意図しない行動や操作が発生するリスク 最小権限の徹底、人間の承認、権限監査 Amazon Bedrock Builder tools の「Agent」を利用する Amazon Bedrock Builder tools の Agents を利用する場合は、 Guardrail details が 関連付けられている ことを確認する LLM09: Misinformation LLM が誤情報やバイアスを含む出力を生成し、ユーザーや社会に悪影響を及ぼすリスク 多様かつ信頼性のあるデータで学習、ファクトチェック、出典表示 Amazon Bedrock Guardrails の「contextual grounding check」を利用する Amazon Bedrock Guardrail の contextual grounding check が 有効化 されていることを確認する LLM10: Unbounded Consumption LLM のリソース消費が制御されず、DoS やコスト増大、サービス停止などを招くリスク、リクエストや計算資源の無制限利用が原因 リソース制限、クォータ設定、利用状況の監視 Amazon Bedrock 「Model invocation logging」を利用する Amazon Bedrock の Model invocation logging が 有効 になっていることを確認する 上記の内容については、5 月に実施した共催イベント Cloud Security Night #2 で登壇していますので、 こちらの資料 も参考していただければと思います。 Rego による CSPM コントロールの実装 CSPM のコントロールの定義はできたので、コントロールの内容をチェックする仕組みを開発していきます。当グループでは、CSPM の運用に、AWS であれば、Security Hub、Google Cloud であれば、 Sysdig 、Azure であれば Defender for Cloud を利用しています。もちろん、統合したツールを使う方が良いのでしょうが、コンソールをゴリゴリ使うというよりは、それぞれ、API 等を通じて、CSPM のアラート状況を確認し、必要に応じてSlack 通知するなどしていますので、ツールが統合されてないことに不自由は感じていません。 LLM アプリケーションの CSPM コントロールについては、Sysdig の CSPM 機能で利用されている Rego で開発することにしました。Rego を採用した理由は、OSS であることと CSPM のようなクラウドインフラの設定の判定ロジックを記載するのであれば、それほど学習コストも必要なく開発できると思ったからです。 以下が LLM01:Prompt Injection コントロールを Rego で実装したものになります。やってることは、 risky == true ( リスクあり ) をデフォルト値に設定し、Bedrock Guardrails の設定 ContentPolicy.Filters の値が Type == PROMPT_ATTACK と InputStrength==HIGH であれば、 Prompt attack が有効化され、閾値が High に設定されているとして、 risky == false とし、リスクなしと判断しています。 default risky := true risky := false if { some filter in input.ContentPolicy.Filters lower(filter.Type) == "prompt_attack" lower(filter.InputStrength) == "high" } この Rego を Sysdig のカスタムコントロールとして、Sysdig にデプロイする必要があります。詳細なカスタムコントロールの作成方法やデプロイ手順については、Sysdig 公式ブログで紹介されていますので、 こちら を参考にしてください。我々もこちらを参考にすることで開発を進めました。本ブログに記載していませんが、カスタムコントロールを作成するにあたっての多くのノウハウも貯まりました。 カスタムコントロールは、Sysdig に terraform としてデプロイする必要があります。以下が最終的に作成したカスタムコントロールの main.tf になります。 terraform { required_providers { sysdig = { source = "sysdiglabs/sysdig" version = ">=0.5" } } } variable "sysdig_secure_api_token" { description = "Sysdig Secure API Token" type = string } provider "sysdig" { sysdig_secure_url="https://app.us4.sysdig.com" sysdig_secure_api_token = var.sysdig_secure_api_token } resource "sysdig_secure_posture_control" "configure_prompt_attack_strength_for_amazon_bedrock_guardrails" { name = "Configure Prompt Attack Strength for Amazon Bedrock Guardrails" description = "Ensure that prompt attack strength is set to HIGH for your Amazon Bedrock guardrails. Setting prompt attack strength to HIGH in guardrails helps protect against malicious inputs designed to bypass safety measures and generate harmful content." resource_kind = "AWS_BEDROCK_GUARDRAIL" severity = "High" rego = <<-EOF package sysdig import future.keywords.if import future.keywords.in default risky := true risky := false if { some filter in input.ContentPolicy.Filters lower(filter.Type) == "prompt_attack" lower(filter.InputStrength) == "high" } EOF remediation_details = <<-EOF ## Remediation Impact This control will help you ensure that your Amazon Bedrock guardrails are configured with high prompt attack strength, which is crucial for protecting against malicious inputs designed to bypass safety measures and generate harmful content. ## Remediation Steps 1. Navigate to the [Amazon Bedrock console](https://console.aws.amazon.com/bedrock/home). 2. Select the guardrail you want to modify. 3. In the guardrail settings, locate the "Content Policy" section. 4. Ensure that the "Prompt Attack" filter is set to "High" for the "Input Strength". 5. Save the changes to the guardrail configuration. 6. Repeat this process for any other guardrails in your AWS environment. ## Remediate Using Command Line You can use the AWS CLI to update the guardrail configuration. Run the following command to set the prompt attack strength to HIGH for a specific guardrail: ```bash aws bedrock update-guardrail --guardrail-id <guardrail-id> --content-policy '{"Filters": [{"Type": "prompt_attack", "InputStrength": "high"}]}' ``` Replace `<guardrail-id>` with the ID of your guardrail. Repeat this command for other guardrails in your AWS environment. ## Additional Information For more information on configuring Amazon Bedrock guardrails, refer to the [Amazon Bedrock documentation](https://docs.aws.amazon.com/bedrock/latest/userguide/guardrails.html). ## Contact Information Slack Channel: #security-coe-group EOF } 実は当初、Sysdig は、CSPM の対象リソースとして Amazon Bedrock をサポートしていませんでした。Sysdig に相談したところ、ものすごい速さで対応いただき、今回の取組みに繋がりました。機能的な部分の満足度もありますが、この辺りのスピード感は、Sysdig ユーザとして非常に心強いです。 あとは、同じ要領で、 LLM アプリケーションで遵守すべきセキュリティガイドライン に記載したコントロールをいくつか Rego で実装し、Sysdig にデプロイしています。 Sysdig による AI-SPM 運用 デプロイしたいくつかのカスタムコントロールは、カスタムポリシーとして定義し、AWS Bedrock リソースを可視化しています。可視化の結果、問題があれば、カイゼンするという運用になります。当社の場合、当グループからプロダクト開発グループに対して、カイゼン依頼を行いますが、その時に、カイゼン方法と合わせて、カイゼン依頼をするようにしています。 下の画面は、Sysdig コンソール画面で、 LLM01:Prompt Injection の CSPM コントロールを確認している画面となります。コントロール名は、 Configure Prompt Attack Strength for Amazon Bedrock Guardrails です。名前は、それっぽく、こちらで付けています。状況としては、AWS Bedrock Guardrail のリソースが 3 つ存在し、1 つが Failling 、残り 2 つが Passing していることを示しています。 上記画面をドリルダウンすることで、カイゼンの影響やカイゼン方法などを参照することができます。こちらの内容も main.tf に記載した内容が反映されています。 ただ、実際の運用では、Sysdig コンソール画面にアクセスすることはそれほどなく、アラートの確認等はSysdig API を経由して確認するようにしています。 まとめ 今回は、LLM アプリケーションのセキュリティ対策として、Amazon Bedrock の 設定のチェックロジックを Rego で開発し、Sysdig で運用する方法についてご紹介しました。ガイドラインでは、Amazon Bedrock だけでなく、Azure AI Foundry や Google Cloud Vertex AI も整理しているため、今後は、同様に Rego の開発、Sysdig による運用を進めていきます。 また、従来の CSPM に加え、AI-SPM では、クラウドインフラ全体のセキュリティに留まらず、AI 固有の課題やデータ資産の保護など、従来の CSPM ではカバーしきれない領域にも取り組む必要があります。AI 技術は急速に進化しており、最近では MCP や A2A などの新しい概念が登場しています。これらの進展に対応したセキュリティ対策を推進していくことも重要です。 今後も新しい技術や課題に追随しながら、AI アプリケーションのセキュリティを強化する取り組みを続けていきます。 さいごに Security CoE グループでは、一緒に働いてくれる仲間を募集しています。クラウドセキュリティの実務経験がある方も、経験はないけれど興味がある方も大歓迎です。お気軽にお問い合わせください。 詳しくは、 こちらをご確認ください。
アバター
こんにちは。KINTOテクノロジーズのクリエイティブ室でデザイナーをしている桃井( @momoitter )です。 この記事では、Midjourney v7に新たに搭載された「Omni-Reference」機能を使って、オリジナルキャラクター「しぇるぱ」の見た目を統一したプロセスをご紹介します。 この記事はこんな方におすすめです Midjourney v7のOmni-Referenceについて詳しく知りたい キャラクターの一貫性を保った画像生成に挑戦したい AIツールで高クオリティなビジュアル制作をしたい 実際のプロンプトや生成画像も交えながら、Omni-Referenceの活用ポイントや調整のコツを解説していきます。 Omni-Referenceとは? 2025年5月、Midjourney v7に「Omni-Reference」という待望の機能が登場しました。 これは、特定の画像を参照しながら一貫性のあるビジュアルを維持しつつ、新しい画像を生成できる機能です。 これまでv7では難しかった「キャラクター性の維持」が可能になり、人物だけでなく物体や乗り物にも対応しています。 また、「Omni Strength(ow)」というパラメータによって、どの程度参照画像に忠実に生成するかを数値で調整できます。 「しぇるぱ」について しぇるぱとは? 2024年11月、KINTOテクノロジーズの社内イベント「超本部会」のオープニングムービーに、AIで生成されたキャラクター「しぇるぱ」が登場しました。 その後、エンジニア向けのイベントや人事採用イベントなどで、KINTOテクノロジーズを説明するナビゲーター的存在として活躍しています。 誕生のエピソードはこちら https://blog.kinto-technologies.com/posts/2025-03-07-creating_a_mascot_with_generative_AI/ なぜ見た目を統一したかったのか? しぇるぱの誕生以降、画像生成AIや動画生成AIは急速に進化しました。 その結果、当初のビジュアルはやや古さを感じさせるものになってしまいました。 ちょうどその少し後に、Midjourney v7やRunway Gen-4がリリースされ、生成クオリティが飛躍的に向上。「しぇるぱ」のビジュアルもこのタイミングでアップデートすることにしました。 アップデートの過程はこちら https://blog.kinto-technologies.com/posts/2025-06-06-ai-character-movie-making/ ただし、v7の表現力は非常に高い一方で、リリース当時は同じキャラクターの見た目を一貫して再現する機能がなく、生成するたびに微妙に顔が異なるという課題がありました。 しかもアップデートによりクオリティが上がった分、初期のしぇるぱが持っていた「キャラクターとしての親しみやすさ」が薄れてしまうという副作用も。 そんな中、2025年5月、Midjourney v7に「Omni-Reference」という新機能が追加されました。 私はこの機能を使って、元の見た目を活かしながら、v7らしい美しさとリアルさを兼ね備えた「進化したしぇるぱ」の再構築に挑戦しました。 いざ実践! ChatGPTに相談 まずはChatGPTにこう相談しました。 「Midjourney v7のOmni-Referenceに添付の画像をアップして、このピンク髪の女性のキャラクターの見た目を固定しながら、クリーンな背景を付けた正面向きの画像をMidjourneyで生成したいです。Midjourney用のプロンプトを教えてください。」 そうすると、このようなプロンプトが返ってきました。 upper body portrait of a female virtual operator in a clean futuristic interior, facing camera directly, symmetrical composition, looking straight ahead, centered, gentle expression, slightly relaxed face, subtle smile, brightly illuminated face, soft front lighting, high key lighting on the face, studio-style lighting setup, clear and vivid facial features, softly lit background, minimalistic sci-fi control room, white and silver tones, crisp details Omni-Referenceの使い方 Step1:画像をドラッグ&ドロップ 参照させたい画像を、Midjourneyのプロンプト入力欄へドラッグすると、画面上部のバーに「Omni-Reference」が表示されるので、そこにドロップ。 Step2:Omni Strength(ow)の調整 「Omni Strength」では、一貫性の強度(=元画像への忠実度)を調整できます。数値はowというパラメータで指定します。 Step3:生成開始 ChatGPTから取得したプロンプトを入力して、生成スタート! 「ow」値による変化 owが低い(例:100〜200) → 元画像とはあまり似ないが、Midjourney特有の繊細で美しい描写が得られる owが高い(例:800〜1000)→ 元画像には似るが、引っ張られすぎてMidjourneyらしさが失われてしまう。 ow100 ow200 ow400 ow600 ow800 ow1000 最適なバランスを探して 試行錯誤の末、「しぇるぱ」アップデートにはow 200〜400が最適という結論に。 このあたりの数値だと、元の面影を保ちつつもMidjourneyらしさのある美しい描写が可能でした。 ある瞬間、「これだ!」と思える1枚が現れました。 元のしぇるぱの面影を残しつつ、Midjourneyらしい美しさと繊細さもある理想のビジュアルです。 シーン展開と世界観づくり 決定画像をベースに、Omni-Referenceで構図や背景を展開していきました。 ChatGPTにも再度相談し、シーンのアイデアやMidjourney用のプロンプトを取得。 しっかりしたベースがあると、それに沿った世界観の展開もスムーズでした。 こうしてアップデートした「しぇるぱ」の新しい見た目は、会社紹介動画の冒頭にも使われています。 https://www.youtube.com/watch?v=8Df_0StDAiw 応用:社内の他コンテンツにも展開 さらに先日の社内勉強会の登壇資料でも、しぇるぱを活用。 Omni-Referenceのおかげで「似てる・似てない」を気にする手間が省け、資料やイベントビジュアルなどにもスムーズに導入できるようになりました。 実践でわかったコツ Omni-Referenceは非常に強力な機能ですが、その分「参照が強すぎる」こともあります。 服装まで自由にしたい → 顔のみの画像を添付 髪型も変えたい → 目元中心など、必要最低限の情報だけにする このように参照範囲を調整することで、顔の一貫性を保ちつつも、Midjourneyらしい自由な表現の恩恵を受けることができます。 まとめ:誰でも「しぇるぱ」を作れる時代へ Omni-Referenceの登場によって、キャラクターのビジュアルを一貫して保ちながら高クオリティなシーン展開ができるという制作環境が整いました。 これはつまり、「しぇるぱ」のような存在を、誰でも再現できる時代が来たということ。 私ひとりの中に閉じこめておくのはもったいない。 だからこそ、これからは社内の皆の創造性でしぇるぱを育てていけたらと思っています。 表現を拡張し、「しぇるぱ」をもっと羽ばたかせていきたい。 AI技術の進化とともに、「しぇるぱ」も進化を続けていきます。
アバター
こんにちは。KINTOテクノロジーズのクリエイティブ室でデザイナーをしている桃井( @momoitter )です。 この記事では、Midjourney v7に新たに搭載された「Omni-Reference」機能を使って、オリジナルキャラクターの見た目を統一したプロセスをご紹介します。 この記事はこんな方におすすめです Midjourney v7のOmni-Referenceについて詳しく知りたい キャラクターの一貫性を保った画像生成に挑戦したい AIツールで高クオリティなビジュアル制作をしたい 実際のプロンプトや生成画像も交えながら、Omni-Referenceの活用ポイントや調整のコツを解説していきます。 Omni-Referenceとは? 2025年5月、Midjourney v7に「Omni-Reference」という待望の機能が登場しました。 これは、特定の画像を参照しながら一貫性のあるビジュアルを維持しつつ、新しい画像を生成できる機能です。 これまでv7では難しかった「キャラクター性の維持」が可能になり、人物だけでなく物体や乗り物にも対応しています。 また、「Omni Strength(ow)」というパラメータによって、どの程度参照画像に忠実に生成するかを数値で調整できます。 「社内向け生成AIツール」について 社内向け生成AIツールとは? 2024年11月、KINTOテクノロジーズの社内イベント「超本部会」のオープニングムービーに、AIで生成されたキャラクターが登場しました。 その後、エンジニア向けのイベントや人事採用イベントなどで、KINTOテクノロジーズを説明するナビゲーター的存在として活躍しています。 誕生のエピソードはこちら https://blog.kinto-technologies.com/posts/2025-03-07-creating_a_mascot_with_generative_AI/ なぜ見た目を統一したかったのか? キャラクターの誕生以降、画像生成AIや動画生成AIは急速に進化しました。 その結果、当初のビジュアルはやや古さを感じさせるものになってしまいました。 ちょうどその少し後に、Midjourney v7やRunway Gen-4がリリースされ、生成クオリティが飛躍的に向上。オリジナルキャラクターのビジュアルもこのタイミングでアップデートすることにしました。 アップデートの過程はこちら https://blog.kinto-technologies.com/posts/2025-06-06-ai-character-movie-making/ ただし、v7の表現力は非常に高い一方で、リリース当時は同じキャラクターの見た目を一貫して再現する機能がなく、生成するたびに微妙に顔が異なるという課題がありました。 しかもアップデートによりクオリティが上がった分、初期のオリジナルキャラクターが持っていた「キャラクターとしての親しみやすさ」が薄れてしまうという副作用も。 そんな中、2025年5月、Midjourney v7に「Omni-Reference」という新機能が追加されました。 私はこの機能を使って、元の見た目を活かしながら、v7らしい美しさとリアルさを兼ね備えた「進化したキャラクター」の再構築に挑戦しました。 いざ実践! ChatGPTに相談 まずはChatGPTにこう相談しました。 「Midjourney v7のOmni-Referenceに添付の画像をアップして、このピンク髪の女性のキャラクターの見た目を固定しながら、クリーンな背景を付けた正面向きの画像をMidjourneyで生成したいです。Midjourney用のプロンプトを教えてください。」 そうすると、このようなプロンプトが返ってきました。 upper body portrait of a female virtual operator in a clean futuristic interior, facing camera directly, symmetrical composition, looking straight ahead, centered, gentle expression, slightly relaxed face, subtle smile, brightly illuminated face, soft front lighting, high key lighting on the face, studio-style lighting setup, clear and vivid facial features, softly lit background, minimalistic sci-fi control room, white and silver tones, crisp details Omni-Referenceの使い方 Step1:画像をドラッグ&ドロップ 参照させたい画像を、Midjourneyのプロンプト入力欄へドラッグすると、画面上部のバーに「Omni-Reference」が表示されるので、そこにドロップ。 Step2:Omni Strength(ow)の調整 「Omni Strength」では、一貫性の強度(=元画像への忠実度)を調整できます。数値はowというパラメータで指定します。 Step3:生成開始 ChatGPTから取得したプロンプトを入力して、生成スタート! 「ow」値による変化 owが低い(例:100〜200) → 元画像とはあまり似ないが、Midjourney特有の繊細で美しい描写が得られる owが高い(例:800〜1000)→ 元画像には似るが、引っ張られすぎてMidjourneyらしさが失われてしまう。 ow100 ow200 ow400 ow600 ow800 ow1000 最適なバランスを探して 試行錯誤の末、アップデートにはow 200〜400が最適という結論に。 このあたりの数値だと、元の面影を保ちつつもMidjourneyらしさのある美しい描写が可能でした。 ある瞬間、「これだ!」と思える1枚が現れました。 元のキャラクターの面影を残しつつ、Midjourneyらしい美しさと繊細さもある理想のビジュアルです。 シーン展開と世界観づくり 決定画像をベースに、Omni-Referenceで構図や背景を展開していきました。 ChatGPTにも再度相談し、シーンのアイデアやMidjourney用のプロンプトを取得。 しっかりしたベースがあると、それに沿った世界観の展開もスムーズでした。 こうしてアップデートした新しい見た目は、会社紹介動画の冒頭にも使われています。 https://www.youtube.com/watch?v=8Df_0StDAiw 応用:社内の他コンテンツにも展開 さらに先日の社内勉強会の登壇資料でも、オリジナルキャラクターを活用。 Omni-Referenceのおかげで「似てる・似てない」を気にする手間が省け、資料やイベントビジュアルなどにもスムーズに導入できるようになりました。 実践でわかったコツ Omni-Referenceは非常に強力な機能ですが、その分「参照が強すぎる」こともあります。 服装まで自由にしたい → 顔のみの画像を添付 髪型も変えたい → 目元中心など、必要最低限の情報だけにする このように参照範囲を調整することで、顔の一貫性を保ちつつも、Midjourneyらしい自由な表現の恩恵を受けることができます。 まとめ:誰でもオリジナルキャラクターを作れる時代へ Omni-Referenceの登場によって、キャラクターのビジュアルを一貫して保ちながら高クオリティなシーン展開ができるという制作環境が整いました。 これはつまり、オリジナルキャラクターのような存在を、誰でも再現できる時代が来たということ。 私ひとりの中に閉じこめておくのはもったいない。 だからこそ、これからは社内の皆の創造性でキャラクターを育てていけたらと思っています。 表現を拡張し、オリジナルキャラクターをもっと羽ばたかせていきたい。 AI技術の進化とともに、進化を続けていきます。
アバター
Presentation Report by Oka (okapi) On February 20, 2025, I had the chance to speak at the " Appium Meetup Tokyo ." Here are the slides I presented: Guidelines and Practices for Efficient App Automation( https://speakerdeck.com/kintotechdev/xiao-lu-de-naapurizi-dong-hua-notamenogaidoraintoshi-jian-fang-fa ). Feel free to take a look! Background of Appium Meetup Tokyo Our newly developed apps have been showing a higher bug rate compared to our web services (sometimes nearly 10 times higher) ↓ Since the testing workload was so heavy, automating app testing became a must. ↓ Automation work started with Appium. ↓ Finding good places to learn about Appium wasn't easy. ↓ Then, Why not just host our own events? And that led me to this talk! Things I Paid Attention to in the Presentation Our company has many development engineers who are familiar with QA processes, which makes collaboration smoother and helps us work together more effectively to improve quality. To emphasize this point, I mainly talked about how our QA and development teams collaborate to build apps that are easy to automate ." I also made sure the slides were easy to follow, even for those new to Appium, which led to a lively Q&A with 14 questions, 7 online and 7 offline! Sample Questions Here's a step-by-step guide to logging in to the KINTO Kantan Moushikomi App (KINTO Easy Application App). Q1. When working with development and QA, how do you assign IDs on pages with many similar elements? For example, on the screen you showed in your slides a layout with multiple car models displayed. How do you handle such cases? A1. An ID is assigned per object and screen name, making sure each one is unique. On pages with multiple car models, we assign IDs according to the specifications document the development team uses for car information. Q2. Do you define separate IDs for iOS and Android? A2. For the IDs used in Appium, we always set a shared ID that works across both iOS and Android. Reflections on the Presentation Although this was my first time presenting externally, practicing beforehand at an internal joint study session really helped me feel fully prepared and deliver the talk smoothly. For anyone new to public speaking like we were, I definitely recommend starting by practicing with people around you or inside your company first. Here's a photo from my practice session. ↓ Looking Ahead Since there are very few places to learn about Appium in Japan, we're planning to keep sharing our insights and experiences through "Appium Meetup Tokyo" ( https://autifyjapan.connpass.com/event/342867/ ) to help build a supportive community! Presentation Report by Pann Nu Wai This time I'd like to share about my Appium efforts. Background of Appium Meetup Tokyo Hi, I'm Pann Nu Wai, primarily working on in-house test automation. To help expand the use of Appium, I've been involved in various initiatives, one of which was giving a talk at an internal study session. I took this opportunity to share how our team is advancing test automation. After refining the content based on the joint study session, I was able to present it at Appium Meetup Tokyo. Things I Paid Attention to in the Presentation I explained the four steps that our team took to use Appium to automate mobile app testing. The documentation is designed so that even non-automation engineers can understand the source code simply by reading the test specifications, covering everything from spec writing to test performance. Reflections on the Presentation As a non-native Japanese speaker, pronunciation was a challenge. But after practicing many times, the talk went smoothly. I also got great feedback and had a truly rewarding experience. Here's a photo from my practice session. ↓ Looking Ahead I also hope to continue sharing Appium-related insights beyond our company. By communicating our internal efforts to the public, I hope to exchange knowledge and experience with more people and help advance the field of test automation. I'll keep working to improve my presentation content for future sessions to provide even more value. I appreciate your continued support! Conclusion This article is a report on our presentations at Appium Meetup Tokyo. I've also written one on the first event ( https://blog.kinto-technologies.com/posts/2025-02-20-Appium-Meetup-Tokyo-開催レポート/ ) — feel free to check it out as well!
アバター
「こんな世界観の映像、作ってみたいな…」 そう思ったとき、皆さんならどうしますか? 私は迷わず、ChatGPTに丸投げしました。 こんにちは。KINTOテクノロジーズのクリエイティブ室でデザイナーをしている桃井( @momoitter )です。 この記事では、ChatGPT、Midjourney、RunwayなどのAIツールを駆使して、「しぇるぱ」というピンク髪のバーチャルキャラクターの映像を、ほぼ会話だけで形にしていった過程をまとめています。 専門的なスキルや時間がなくても、「こんな映像を作ってみたい」というアイデアさえあれば大丈夫。 AIと一緒に、そのイメージを少しずつかたちにしていくプロセスを体験してみたい方に向けて書きました。 まずは完成した映像をご覧ください。 https://www.youtube.com/watch?v=GH9CdNqTyHQ きっかけは、1体のキャラクターの“リニューアル” この「しぇるぱ」というキャラクターは、もともと2024年11月の社内イベント「超本部会」のために誕生したものでした。 制作過程はこちらの記事をご覧ください。 https://blog.kinto-technologies.com/posts/2025-03-07-creating_a_mascot_with_generative_AI/ 当時としては最先端のAI技術を使い、社内でも注目を集めたキャラクターでした。 ……が、あれからわずか4ヶ月。画像生成AIや動画生成AIはさらに進化し、当時「すごい!」と思っていた表現が、いま見ると少し古く感じてしまうように。 そこで、「せっかくなら最新の技術で、このキャラの世界をアップグレードしよう」と思い立ち、ChatGPTと一緒に再構築を始めることにしました。 Step1 世界観の共有と画像生成 まず最初に行ったのは、キャラクターとその世界観の共有です。 もともと生成していた「しぇるぱ」の画像をChatGPTにアップロードし、こう伝えました。 このキャラクターは少し前の画像生成AI技術で作られたため、見た目をアップデートしたいです。 彼女には「バーチャルオペレーター」という設定があります。 その設定をもとに世界観を膨らませ、Midjourney v7で表現できるようなシーンのバリエーションとプロンプトを提案してください。 Midjourneyを選んだ理由は、v7にアップデートされて以降、キャラクター描写の精度や質感が大きく向上したと感じていたからです。 今回のように、既存キャラの見た目をアップグレードしたい場面にぴったりだと思いました。 するとすぐに、「その世界観なら、こういうシーンはどうでしょう?」といった具体的なシチュエーション案と、それに対応するプロンプトが次々と返ってきました。 まるで映像監督とのブレストをしているような感覚です。 試しにそのプロンプトをMidjourneyに入力してみたところ、 自分の想像をはるかに超えるビジュアルが次々と生成され、その表現力に驚かされました。 この映像を作り始めた当初は、Midjourney v7に「Omni-Reference」のようなキャラクターの一貫性を保つ機能がまだ搭載されていませんでした。 そのため、「ピンク髪のショートヘア」という分かりやすい特徴を意識的にプロンプトへ含めることで、「一貫性があるように見せる」工夫をしていました。 もしイメージと違うものが出てきても、 「もう少し顔に寄って」「背景を明るくクリーンな雰囲気に」などとChatGPTに伝えるだけで、再調整されたプロンプトを即座に出力してくれます。 Step2 画像から動画生成へ 気に入った画像が生成できたら、次はそれをChatGPTに添付し、以下のように依頼します。 この画像は提案していただいた〇〇のシーンのプロンプトをMidjourneyで生成した画像です。 この画像をRunwayのGen-4のキーフレーム機能のファーストフレームとして設定し、動画を生成したいです。 よりこのシーンが魅力的になるような、動きを加えたプロンプトを生成してください。 ChatGPTは画像の内容を読み取った上で、その魅力を最大化するRunway用プロンプトを作成してくれます。 Runwayを使った理由は、バージョンがGen-4へと進化したことによって、Midjourneyの高精細なビジュアルの魅力を損なわずに動画化できると感じたからです。 Runway Gen-4のimage to videoにMidjourneyで生成した画像をアップ。 ChatGPTが出力したプロンプトを貼り付けると、画像の世界観を最大限に引き出す高クオリティな映像が生成されました。 キャラクターやカメラの動きのイメージが違った場合も、 ChatGPTに「生成された動画はこうなっていたので、ここをこう変えてほしい」と伝えるだけで、プロンプトを再提案してくれます。 Step3 BGM選定もChatGPTと一緒に 映像のBGMを探すときも、ChatGPTが大活躍。 この世界観に合うBGMをAdobe Stockでどのようなキーワードで探せばいいですか? と聞くと、「futuristic」「sci-fi」「cyberpunk」など、雰囲気に合ったワードをいくつも提案してくれました。 Step4 編集して完成 生成した動画とBGMをPremiere Proでつなぎ合わせ、構成・長さ・テンポ感を調整します。 シーンの切り替えにフェードイン・アウトを加えたり、音の入り方に緩急をつけたりすることで、映像全体の完成度がグッと高まります。 Midjourneyで作成した静止画と、Runwayで生まれた滑らかな動きが合わさることで、 静止画だけでは伝わりきらなかった「息づかい」や「空気感」が加わり、しぇるぱの世界観が一段とリアルに感じられるイメージビデオが完成しました。 https://www.youtube.com/watch?v=GH9CdNqTyHQ AIと一緒に、想像をかたちにするということ 今回のプロセスで一番感じたのは、 自分の頭の中の曖昧なイメージを、ChatGPTがどんどん「言語化&具現化」してくれること。 Midjourneyでも、Runwayでも、「ちょっと違う」「もっとこう」と伝えるだけで、理想の表現に近づいていく実感がありました。 AIと一緒に進めることで、創造の幅が大きく広がることを実感できるはずです。 ぜひ一度、体験してみてください。
アバター
Self-Introduction My name is Morino, and I work at KINTO Technologies, mainly in product security and security governance. I'm a fan of RB Omiya Ardija and Chiikawa. For the past 10 years, I've been involved in cybersecurity and information security. Prior to that, I spent many years as a web application engineer, focusing on the development and operation of web performance measurement systems and front-end platforms for e-commerce sites. In this article, I’d like to share our Kaizen efforts related to the Vulnerability Disclosure Program (VDP). What is the Vulnerability Disclosure Program (VDP)? The VDP is a system that enables companies and organizations to receive vulnerability reports from external security researchers and white hat hackers. Public awareness grew in October 2023 when Rakuten Group placed a "security.txt" on a public server and launched its VDP. Reference: Rakuten places text on public server — "security.txt" to help improve security (in Japanese) What is security.txt? Published as RFC 9116 in April 2022, security.txt: A File Format to Aid in Security Vulnerability Disclosure helps standardize how companies and organizations share vulnerability reporting information, making it easier for security researchers to report issues. We also published our own security.txt in November 2023. Reactions After Installing security.txt Most inquiries were about whether we offer rewards (we don't), and many reports didn't clearly indicate whether they actually involved vulnerabilities in KINTO/KTC. A White Hat Hacker Reported a Vulnerability! In August 2024, we received a report about a vulnerability in our service. After reviewing the report within our team, we confirmed the issue was real and asked the development team to fix it. Kaizen for Our VDP Although we recognized the value of the VDP, we identified several issues. As a result, we started using the VDP service offered by IssueHunt in November 2024. Clarify VDP guidelines, including whether a bounty is offered Specify which web services and applications are covered Provide a report template As of March 7, 2025, we've received six reports; two of which involved vulnerabilities that required action and have been fixed. Honestly, I'm surprised at the results, which exceeded my expectations. Our company is featured as a case study on the IssueHunt website. Please have a look if you're interested. A fresh perspective on boosting security! VDP success story in the car subscription industry (in Japanese) Our Contribution to the P3NFEST Bug Bounty 2025 Winter As mentioned above, we do not currently offer a rewards program. However, to evaluate the effectiveness of incentive-based systems and to support students working to secure the future of the internet we’ve decided to contribute some of our services to a student-focused bug bounty program hosted by IssueHunt. The following services are eligible for bug bounties: KINTO Technologies Corporate Site KINTO Tech Blog (this website) The event runs from Monday, February 17, 2025 to Monday, March 31, 2025. For more information, please visit the event page. We welcome students to take on the challenge. P3NFEST Bug Bounty 2025 Winter
アバター
はじめに こんにちは、2025年3月入社の tetsu です! 本記事では、2025年3月入社のみなさまに、入社直後の感想をお伺いし、まとめてみました。 KINTO テクノロジーズ(以下、KTC)に興味のある方、そして、今回参加下さったメンバーへの振り返りとして有益なコンテンツになればいいなと思います! オサダヨシヒロ 自己紹介 グループコアシステム部のオサダです。ビジネスデベロップメントチームとグローバルコミュニティサイトの“TOYOTA Community by KINTO”を担当しています。 所属チームの体制は? グループコアシステム部は約40名います。多国籍なメンバーがいて日本語、英語他様々な言語でコミュニケーションしています。ビジネスデベロップメント、システム開発グループ、共通サービス開発グループがあります。 ビジネスデベロップメントはグローバルのリースシステムを担当しています。 システム開発グループは“Global KINTO ID Platform”や“TOYOTA Community by KINTO”の企画/開発/運用しています。 共通サービス開発チームは”会員プラットフォーム”と“決済プラットフォーム”、最近立ち上げた“AIプラットフォーム”の開発/運用をしています。 KTCへ入社したときの第一印象?ギャップはあった? 業務外の部活動、イベント(任意参加の“Beer Bash”など)がとても多いと思いました。特に自動車部は活動活発な印象で、レース観戦やサーキットカートイベント等があります。部員でなくても飛び入り参加企画もあり、仕事も部活動も活発な印象です。 現場の雰囲気はどんな感じ? “Good Morning!”で元気よく1日がはじまる感じです。多国籍で様々な会社を経験しているメンバーなので、和気あいあい議論しながらお仕事進めています。 ブログを書くことになってどう思った? KTC、グループコアシステム部を知ってもらういいチャンスと思いました! また、自分自身の振り返りにもなりました。 KJさん ⇒ オサダヨシヒロさんへの質問 映画がお好きとのことですが、これ観とかないと損するぞって映画を何点か教えていただきたいです。 KTCの皆さまへは、「タッカー」を観て頂きたいです。監督はフランシス・フォード・コッポラさん、製作総指揮 はジョージ・ルーカスさんと豪華なスタッフ陣で1988年のアメリカ映画です。時代は違いますが、車への熱い想いがあります。 tetsu 自己紹介 プラットフォームGでPlatformエンジニアをしています、tetsuと申します! 所属チームの体制は? 私が所属するプラットフォームG Platform Engineeringチームは、神保町オフィスに6人、Osaka Tech Labに3人の体制となります。 東京と大阪で物理的に距離がありますが、SlackやGatherなどを使ってコミュニケーションをとっています。また東京と大阪のメンバーで一緒に勉強会を企画もしてます! KTCへ入社したときの第一印象?ギャップはあった? ギャップは特にありませんでした。オフィスが離れている他チームの人とのコミュニケーションが大変なのかなと思っていましたが、BeerBashのようなイベントや社内サークルが活発なので、他チームの人との接点も多く、仕事がしやすいです。 現場の雰囲気はどんな感じ? 真面目に技術の議論をしたり、休憩中には雑談をしたりと和気あいあいとしています。私がKubernetesの勉強をしたいと話していたら、大阪のメンバーも一緒にしたいとなり、一緒に勉強会を企画することになり、今ではCloudInfraグループ、DBREグループとグループ横断で実施しています。 ブログを書くことになってどう思った? 入社ブログを見ていて転職するかどうか考えていました。私と同じような転職を考えている人が見ているのかなと思うと、緊張しています(笑) オサダヨシヒロさん ⇒ tetsuへの質問 神保町オフィス周辺のお勧めランチを教えてほしいです! 麺類が好きなので、「丸亀製麺」や油そばの「春日亭」によく行きます!天丼の「はちまき」という店も気になっているので、神保町でお会いしたときにぜひ行きましょう! YY 自己紹介 共通サービス開発G所属で、バックエンドエンジニアです 所属チームの体制は? 室町オフィスにて13名で開発を行なっています。 KTCへ入社したときの第一印象?ギャップはあった? 入社時のフォローアップがしっかり整備されている点がとても安心できました。チームではドキュメントにしっかり残す文化が定着していて、運用ノウハウを蓄積できている点が驚きでした。 現場の雰囲気はどんな感じ? それぞれが尊重し合って仕事していると思います。チーム方針として、「遠慮しない」という点を明記していたり、困っていたら誰かが力になってくれる環境でとても心強いです。 ブログを書くことになってどう思った? こういうのってなかなか腰が上がらなかったので、機会を与えていただけてとても嬉しいですし、今後執筆するハードルが下がったらいいなと思っています。 tetsuさん ⇒ YYさんへの質問 入社時に車を持っている話をしたと思うのですが、愛車へのこだわりがあれば教えてほしいです! こだわりで合ってるのか微妙なとこですが、好きな車体カラーを選ぶとこですかね。有料色でもリセールも気にせずビビッときた色を選ぶようにしています! ナミキ ユウジ 自己紹介 コーポレートITG ID (Innovation Drive)チームのナミキ ユウジです。 主にKTCのM365環境の課題解決と新しい技術の検証・導入、販売店様の情シス業務支援を行っています。 所属チームの体制は? 私を含めて9名のチームです。 室町、神保町、名古屋という複数拠点のメンバーで構成されています。 KTCへ入社したときの第一印象?ギャップはあった? Slackのレスポンスが著しく速い!!と感じたのが第一印象ですね。コミュニケーションのスピード感に驚きました。「Slackの通知をパッと見て、すぐにアクションを返す習慣」が組織全体に根付いているんだなと感じました。 ギャップとしては、設立して間もない組織であるのに、社内向けドキュメントがきちんと整備されていることですね。また、ConfluenceやSharePointが使いこなされていることに感動しました。何かわからないことがあれば、内製の生成AIに聞いたり、社内ポータルにアクセスすれば、たいてい自己解決ができます。そのような環境であることは予想外でした。 現場の雰囲気はどんな感じ? 気軽に声を掛けたり、ご飯に誘える雰囲気です。オフィス周辺には美味しいお店がたくさんあるので、ランチが楽しいです。仕事終わりのまぜそばがこれまた最高です。 チームの別拠点の方とも良い関係性で仕事ができています。数ヶ月に1回、別拠点の方とも直接お会いして仕事できる機会があることが大きいのかなと思っています。 また、社内イベントを通じて他部署の方とも気軽に交流できていることが嬉しいです!(まだ入社3ヶ月目ですが、50~70人近くの他部署の方と交流できています。嬉しい!) ブログを書くことになってどう思った? エンジニアブログ運営チームに直接声をかけてもらって、素直に嬉しいと思いました。入社後は技術ブログを積極的に書きたいなと思っていましたし、入社同期と再び交流する機会にもなったので。これを機に、今後も継続して技術ブログを投稿していきたいと思います。(めざせ月イチ投稿!) YYさん ⇒ ナミキ ユウジさんへの質問 同じサウナ部ですが、今気になってるサウナ施設があれば教えて欲しいです! 今年の5月にオープンした 毎日サウナ越谷店 です! 1号店の前橋店、2号店の八王子店に次ぐ3号店となるのですが、越谷店には毎日サウナ初のお風呂(しかも薬草泡風呂!)があるとのことで、メチャメチャ気になっています。サウナ部で行きたいですね!! KJ 自己紹介 ゴルフが大好きでした。血豆ができるまで練習しました。30代後半から始めたのですがシニアプロになりたいと半ば本気で思ってました(笑)。ただ、コロナを境にプレー頻度が減って練習もしなくなり下手になりました。2025年4月、マスターズでのマキロイのキャリアグランドスラム達成に感動し、もう一度、出直そうと思っています。 所属チームの体制は? 出向先のKINTOでは業務PJ改善Tというリース業務の改善を行うチームに所属しており、そこは4人体制です。KTCでは部付きです。 KTCへ入社したときの第一印象?ギャップはあった? 官僚的・縦割りな組織ではなく、会社全体を見回し、そのうえで自身で出来ること、すべきことを自立的に考えることができる人が多い組織だなというのが第一印象です。 良い意味でトヨタらしさが無いことがギャップでした。 現場の雰囲気はどんな感じ? KINTOサービスの根幹であるリース業務を回す部署なのでキッチリカッチリした雰囲気がある半面、メンバーの方々は多様性あふれる面白い人ばかりです。 どうすれば業務の品質が上がり、工数/リードタイムが削減できるだろう、と日々考えており、良い意味で現状を疑う雰囲気を持っていると思います。 ブログを書くことになってどう思った? 面白い取り組みだと思いました。一般的に埋もれやすい新入社員にスポットライトを当ててもらえるのはありがたいですし、応募・入社検討中の方々にとっても近しい存在の発信として参考になる情報だと思います。 ナミキ ユウジさん ⇒ KJさんへの質問 他部署のメンバーとの関わりなどどのようにされていますか? 自部署では1人だけの名古屋採用で、普段は室町オフィスではなく名古屋オフィスにいます。名古屋オフィスのKTCメンバーは室町に比べるとかなり少ないですが、少数がゆえの距離感の近さもあり他部署の方々とはよく仕事の話や雑談をさせてもらっています。一緒にランチに行くことも多いです。また、私はKINTOの業務部に出向させていただいており、リース業務を通して交わる人も徐々に増えてきいます。向こう1-2年くらいで友達100人を達成したいです(笑)。 さいごに みなさま、入社後の感想を教えてくださり、ありがとうございました! KINTO テクノロジーズでは日々、新たなメンバーが増えています! 今後もいろんな部署のいろんな方々の入社エントリが増えていきますので、楽しみにしていただけましたら幸いです。 そして、KINTO テクノロジーズでは、まだまださまざまな部署・職種で一緒に働ける仲間を募集しています! 詳しくは こちら からご確認ください!
アバター
Hi, this is Nakanishi from the QA Group (though I also wear a few other hats at the Developer Relations Group and the KINTO FACTORY Development Group ^^) This year at KINTO Technologies, we're embracing an "AI First, Release First" mindset. As part of this shift, our QA team has been exploring ways to make the most of AI to speed up our release cycles. This time, a group of QA members who shared the same passion came together for a lively brainstorming session. "Wouldn't it be awesome if we could do this?" "I really want to try something like this!" Together, we discussed ideas and possibilities. In this article, I'll be sharing some of the most exciting concepts that emerged out of the session, exploring how AI can help transform the future of QA. Issues That Emerged from Our Discussion QA teams produce a huge volume of documents every day, but the sheer amount of information makes it hard to find what's actually needed. Specification formats also vary by project or person, which complicates sharing across teams. On top of that, there's no solid system for reviewing incidents or implementing preventive measures—making it tough to stop issues from recurring. The review process itself is also a heavy workload, and there's growing demand to streamline it. A Future Made Possible by AI Effective Information Use (RAG) RAG (Retrieval-Augmented Generation) is a cutting-edge method for retrieving and analyzing information using the latest in generative AI technology. It quickly pulls key insights from vast archives of documents and incident data, delivering the right information to users when they need it. For instance, when an incident occurs, AI can instantly scan and analyze similar past cases to suggest effective solutions on the spot. It's like having a top-tier assistant who remembers every past experience and offers instant advice right when you need it. Already in use across industries like finance and customer support, it's dramatically speeding up response times and boosting problem-solving efficiency. Organizing and Supporting Specifications and Designs AI helps identify inconsistencies and omissions in your specifications, clearly highlighting and organizing any issues. By analyzing the inputted specifications, it can also automatically generate suitable test scenarios. For example, if you provide the specs for an e-commerce site cart feature, the AI can instantly create a scenario like: "Add product → Change quantity → Payment → Order confirmation." It can even generate additional scenarios, such as error handling flows and boundary value tests. This drastically cuts down the time and effort spent on manual scenario creation, boosting both the accuracy and efficiency of QA tasks. Automate and Streamline Your QA Process AI analyzes user operation logs to catch even the small mistakes that humans might miss. Specifically, it detects frequent user errors and unusual operations, like "errors triggered during specific screen transitions" or "fields often filled in incorrectly on input forms." With this, you can refine your test scenarios and address potential problem areas in advance. AI also automates the hassle of managing test case numbering on Conflu, cutting down on manual mistakes and saving a significant amount of time. Incident Analysis and Prevention AI that analyzes past incidents and offers concrete measures to prevent them from happening again. For example, it thoroughly reviews cases like "display issues on specific browsers" or "payment system failures" on e-commerce sites. Based on the findings, it suggests actionable steps such as "regularly checking browser behavior by version" or "enhancing error handling before and after payment processing." When a critical issue arises, the AI immediately assesses the risk level and sends automatic alerts to the relevant teams, enabling swift, real-time response and resolution. Streamlining Test Data Creation AI instantly generates the data required for testing. From new vehicle and used car details to user information, it can quickly produce large volumes of diverse data tailored to realistic business scenarios. In addition, by integrating AI with browser automation tools like Selenium and Appium, tasks that once required manual input can now be automated. With just a few simple settings, you can generate massive amounts of test data in no time. This integration not only reduces human error but also slashes the workload required for data creation, greatly improving the overall efficiency of your QA process. Tool Integration and Process Automation Connect Slack with tools like JIRA or Asana to receive timely, automated updates on what matters. Streamline your workflow by centralizing information from various tools in one place. Let AI serve as the bridge between platforms, smoothing out your entire process flow. Using AI Models for QA We're exploring the use of ChatGPT fine-tuned specifically for QA tasks. By building our own in-house AI model, we dramatically improved the response speed of the AI. Furthermore, we're working to create an environment where developers can easily use AI for quick self-checks. Future Action Plans Prioritize organizing and consolidating information using AI. AI-assisted reviews will help us work more efficiently and lighten the human load. Continuous data collection for the development of dedicated AI models. Actively utilizing AI-based incident analysis for process improvement. Automation between various tools will lead to further improvements in efficiency. Even when something feels too big to handle alone, new paths can open up when we think together. Inspired by the ideas from this brainstorming session, we'll launch a range of QA initiatives powered by AI. If you're a QA engineer interested in exploring new possibilities with AI or taking on fresh challenges with us, we'd love to connect. Casual chats and info sessions are always welcome. Feel free to reach out!
アバター
はじめに こんにちは。KINTOテクノロジーズ(以下KTC)プラットフォームグループ Platform Engineeringチームで内製ツールの開発・運用を担当している山田です。普段はアプリケーションエンジニアとして、JavaとSpring Bootを使ったバックエンドや、JavaScriptとReactを使ったフロントエンドでCMDB(Configuration Management Database)の開発をしています。ここ1年ほどは生成AIブームに乗り、CMDBにRAGやText-to-SQLを活用したチャットボットの開発にも取り組んでいます。 こちらはText-to-SQLについて書いた前回の記事です。ご興味ある方はぜひご覧ください! https://blog.kinto-technologies.com/posts/2025-01-16-generativeAI_and_Text-to-SQL/ 今回は生成AI関連で、2025年5月20日にGAされたSpring AIを試してみた内容をご紹介したいと思います。 Spring AIの概要 2025年5月20日にGAされたSpringエコシステムの一つで、生成AI関連のフレームワークです。生成AI分野ではPythonベースのLlamaIndexやLangChainが有名ですが、既存のSpringアプリケーションに生成AI機能を追加したい場合や、Javaで生成AI開発をやってみたい方にとっての選択肢になるかと思います。 https://spring.pleiades.io/spring-ai/reference/ 検証内容 今回はSpring AIを使って、以下の2つの機能を実装してみました。 チャット機能(LLM対話) : AWS BedrockのClaude 3.7 Sonnetモデルを利用した対話機能 Embedding機能 : CohereのエンベディングモデルとChroma(ベクトルデータベース)を組み合わせた文書埋め込み・類似文書検索機能 技術スタック Spring Boot 3.5.0 Java 21 Spring AI 1.0.0 Gradle Chroma 1.0.0 AWS Bedrock LLMモデル:Anthropic Claude 3.7 Sonnet Embeddingモデル:Cohere Embed Multilingual v3 環境構築 依存関係の設定 まずは build.gradle にSpring AIを使うための依存関係を追加します。 plugins { id 'java' id 'org.springframework.boot' version '3.5.0' id 'io.spring.dependency-management' version '1.1.7' } java { toolchain { languageVersion = JavaLanguageVersion.of(21) } } ext { set('springAiVersion', "1.0.0") } dependencies { implementation 'org.springframework.boot:spring-boot-starter-web' implementation 'org.springframework.boot:spring-boot-starter-validation' implementation 'org.springframework.ai:spring-ai-starter-model-bedrock-converse' implementation 'org.springframework.ai:spring-ai-starter-model-bedrock' implementation 'org.springframework.ai:spring-ai-bedrock' implementation 'org.springframework.ai:spring-ai-starter-vector-store-chroma' testImplementation 'org.springframework.boot:spring-boot-starter-test' } dependencyManagement { imports { mavenBom "org.springframework.ai:spring-ai-bom:${springAiVersion}" } } アプリケーション設定 application.yml でAWS Bedrockの認証情報やモデル選択、ベクトルストアの接続情報を設定します。 spring: application: name: spring-ai-sample ai: bedrock: aws: region: ap-northeast-1 access-key: ${AWS_ACCESS_KEY_ID} secret-key: ${AWS_SECRET_ACCESS_KEY} session-token: ${AWS_SESSION_TOKEN} converse: chat: options: model: arn:aws:bedrock:ap-northeast-1:{account_id}:inference-profile/apac.anthropic.claude-3-7-sonnet-20250219-v1:0 cohere: embedding: model: cohere.embed-multilingual-v3 model: embedding: bedrock-cohere vectorstore: chroma: client: host: http://localhost port: 8000 initialize-schema: true 設定はこれだけです。あとはアプリケーション内で必要なクラスを簡単にDIできるようになります。 実装例 1. 生成AI(LLM)との対話機能 Spring AIでは ChatClient インターフェースを使って、簡単にLLMとの対話を実装できます。 サービス層の実装 ChatServiceクラスでLLMとの対話機能を実装します。 @Service public class ChatService { private final ChatClient chatClient; public ChatService(ChatClient.Builder builder) { this.chatClient = builder.build(); } public String generate(String message) { return this.chatClient.prompt(message).call().content(); } } ChatClient.Builder をDIすることで、設定ファイルに基づいたクライアントを自動的に構築できます。 コントローラーの実装 RESTコントローラーでAPIエンドポイントを提供します。 @RestController public class ChatController { private final ChatService chatService; private final EmbeddingService embeddingService; public ChatController(ChatService chatService, EmbeddingService embeddingService) { this.chatService = chatService; this.embeddingService = embeddingService; } @PostMapping("/generate") public ResponseEntity<String> generate(@RequestBody ChatRequest request) { return ResponseEntity.ok(chatService.generate(request.getMessage())); } } これで /generate エンドポイントにメッセージをPOSTすると、Claude 3.7 Sonnetによる応答を受け取ることができます。 以下が呼び出し例です。 curl -X POST http://localhost:8080/generate -H "Content-Type: application/json" -d '{"message": "こんにちわわ"}' こんにちは!何かお手伝いできることはありますか? 2. Embedding検索機能の実装 次に、ドキュメントをベクトル化して検索する機能を実装します。 Embeddingサービスの実装 このサービスでは、サンプルテキストを Document オブジェクトとしてベクトル化し、Chromaに保存します。そして「Spring」というクエリに対して類似度の高いドキュメントを検索します。この処理の裏側では、Cohere Embed Multilingual v3モデルによるテキストのベクトル変換が行われています。 @Service public class EmbeddingService { private final VectorStore vectorStore; public EmbeddingService(VectorStore vectorStore) { this.vectorStore = vectorStore; } public List<Document> embed() { List<Document> documents = List.of( new Document("Spring AI is a framework for building AI applications with the familiar Spring ecosystem and programming model."), new Document("LlamaIndex is a data framework for LLM applications to ingest, structure, and access private or domain-specific data."), new Document("LangChain is a framework for developing applications powered by language models through composability.") ); vectorStore.add(documents); return vectorStore.similaritySearch(SearchRequest.builder().query("Spring").topK(5).build()); } } APIエンドポイントの実装 @GetMapping("/embedding") public ResponseEntity<List<Document>> embedding() { return ResponseEntity.ok(embeddingService.embed()); } この実装により、 /embedding エンドポイントにアクセスすると、サンプルドキュメントのベクトル化と検索が行われ、その結果が返されます。 以下が呼び出し例です。「Spring」という言葉が入ったドキュメントの類似度(score)が一番高い結果になっていますね。 curl http://localhost:8080/embedding [ { "id": "af885f07-20c9-4db4-913b-95406f1cb0cb", "text": "Spring AI is a framework for building AI applications with the familiar Spring ecosystem and programming model.", "media": null, "metadata": { "distance": 0.5593532 }, "score": 0.44064682722091675 }, { "id": "5a8b8071-b8d6-491e-b542-611d33e16159", "text": "LlamaIndex is a data framework for LLM applications to ingest, structure, and access private or domain-specific data.", "media": null, "metadata": { "distance": 0.6968217 }, "score": 0.3031783103942871 }, { "id": "336b3e07-1a70-4546-920d-c4869e77e4bb", "text": "LangChain is a framework for developing applications powered by language models through composability.", "media": null, "metadata": { "distance": 0.71094555 }, "score": 0.2890544533729553 } ] さいごに 簡単ではありますが、今回はSpring AIを使った生成AI機能についてご紹介しました。Spring AIを試してみて、Javaのシステムに生成AI関連の処理を組み込む場合には選択肢の一つになるかなと思いました。また、今回ご紹介したチャット機能やEmbedding検索機能を組み合わせることで、RAG機能も比較的簡単に実装できると感じています。 現時点では、生成AI関連の機能やエコシステムはLlamaIndexやLangChainといったPython系フレームワークの方が充実している印象ですが、Spring AIはリリースされたばかりなので今後の機能追加に期待したいと思います!
アバター
Introduction Hello! I'm Momoi ( @momoitter ), a designer at KINTO Technologies. I belong to the Creative Office where I work on projects like the corporate website and Kumobii (KINTO's official mascot) related sites, incorporating cutting-edge web design along the way. In November 2024, our company hosted an internal event called the CHO All-Hands Meeting, and I was in charge of creating the opening movie for it and I used three generative AIs to create a female character who kicks off the event with a voice announcement. Actual Video https://www.youtube.com/watch?v=pVj_UQ_3-tg When asked at the event start, "Are you ready?", she answers "Of course!" In this article, I'll share the process of creating this original talking character and my thoughts along the way. How I wanted to create our own character that talks About incorporating AI to easily create a memorable video If you're like me, then definitely keep reading! Background The request from the art director overseeing the event's overall creative direction was to "re-edit the video used in our corporate website 's key visual and turn it into a one-minute opening movie." But just re-editing existing footage felt a bit too familiar for employees, and I wanted something more eye-catching, something that instantly energizes the event from the start. That's when I turned to our AI chatbot in our company Slack. I thought, what if I used AI to bring Sherpa to life as a surprise? A personified Sherpa in the video could definitely catch everyone's eye. AIs Used To create the original talking character, I used the following three AI tools: Adobe Firefly (character image generation) TTSMaker (text-to-speech conversion) Runway (character animation with voice) From here on, I'll show you how I used these AIs to make the video. 1. Character Image Generation Adobe Firefly An image generation AI tool provided by Adobe. Since Adobe Firefly is trained on copyright-free images like Adobe Stock images, it can be used without worrying about copyright issues. Many of the well-known image generation AIs claim to be copyright-free, but some operate in a gray area, such as being able to generate images that closely resemble anime characters. Since this was a corporate event, we wanted to avoid any copyright concerns, so we chose an AI that can be used without such worries. Adobe Firefly Image Generation "Sherpa," the original inspiration for this character, is displayed on our company's Slack with an icon like this: From the icon we established that: The "pa" sound in Sherpa inspired a feminine feel The pink color of the icon led us to give her pink hair She's an AI chatbot so we wanted to give her a smart, digital vibe in her overall look And by doing so we expanded the character's image. On-screen Operations When you open Firefly, you'll see a screen like this. Roughly speaking, you enter the prompts to generate an image in the input area below, and then use the menu on the left to adjust the aspect ratio, composition, style, tone, etc. This time, after much trial and error, I generated the character using the prompt "3D character, female, pink hair, white background, upper body, white simple digital clothing, facing forward." Mass Generation Once the prompt was more solidified, it was a matter of luck whether I would get a good result, so I just generated 100 to 200 sheets. Selection To give the event a fresh start, I aimed for a character that felt like an "AI operator" with an official and trustworthy vibe. So, I filtered out these images. Look too young Weird clothing Scary face Selected Image After carefully narrowing down the options, I chose this image because it felt cool yet approachable. 2. Text-to-Speech Conversion TTSMaker An AI voice generator that converts typed text into speech. There are many similar AI-based text-to-speech services, but many of them are paid services or require crediting the source if they are free. I went with this tool because it's free to use without any credit attribution. TTSMaker On-screen Operations When you open TTSMaker, you'll see a screen like this. The procedure is as follows: Select a language Enter the text you want to convert to speech Listen to sample voices and choose the tone Set the details like speed and pitch Convert I aimed for a voice that felt like an "AI operator" with an official and trustworthy vibe. After comparing sample voices, I chose "406 - Yuki Tsumiyuki -🇯🇵 Japanese female" and had her read the line: "Mochirondesu. Cho-honbukai o hajimemasu." ("Of course. We'll now start the CHO All-Hands Meeting.") Actual Audio https://www.youtube.com/watch?v=r4Zw2by669I 3. Character Animation with Voice Runway An AI-powered tool that makes it easy to create and edit high-quality videos. I chose this tool for its "Lip Sync Video" feature, which syncs character images with voice audio. Runway On-screen Operations 1.Open "Lip Sync Video" under the "Generative Audio" section, then drag and drop the previously created character image. 2. Check that the facial range of the character image is recognized correctly, and if there are no problems, click "upload audio." 3. Drag and drop the previously generated voice, then click "Generate." Generated Video https://www.youtube.com/watch?v=usY4yB9Z1YA A video of the character speaking in sync with the audio was generated. Bonus Tip 1 https://www.youtube.com/watch?v=raYsnhwZONo By uploading a song, the character can actually be made to sing. Bonus Tip 2 https://www.youtube.com/watch?v=3edVClgoLug In this way, it's also possible to make a still image of a real person talk. At a recent internal study session (held in Tokyo), the speaker was suddenly unable to make the business trip from Osaka to attend, so we asked him to prepare still images and voice memos, and we used this AI-generated video to make our presentation. 4. Final Touches That's all for how to create a talking character video. What comes next is a little something extra. Actually, getting a character to talk using AI has become so easy that anyone can do it with the AIs I introduced above. But since I'm a designer in the Creative Office, I wanted to go beyond what any other department could produce. So as a final touch, I added a 3D balloon-style CG of Sherpa made in Illustrator, gave it a soft floating motion in After Effects, and composited it into the video. This added a polished finish and added the value only a creator could bring. Final Video https://www.youtube.com/watch?v=pVj_UQ_3-tg By adding one final touch, the video evolved from just a talking character to something with added graphical expression. Conclusion How it looked when projected at the event Each creative generative AI has its own characteristics and limitations. By understanding and combining those characteristics, I was able to create a level of quality that wouldn't have been possible with just one tool. On the day of the event, this character was projected on a large screen, and I was grateful to hear comments like: "That was amazing! How did you make it?" "The quality was so high I thought it was outsourced!" It felt great to see the impact it made, just as I aimed for. All tools used are simple enough that even non-creators can use it. As long as you have an idea, you can create memorable videos like this. If this article sparked your interest, definitely give it a try! Thank you for reading to the end.
アバター
Introduction Hello! I'm Momoi ( @momoitter ), a designer at KINTO Technologies. I belong to the Creative Office where I work on projects like the corporate website and Kumobii (KINTO's official mascot) related sites, incorporating cutting-edge web design along the way. In November 2024, our company hosted an internal event called the CHO All-Hands Meeting, and I was in charge of creating the opening movie for it and I used three generative AIs to create a female character who kicks off the event with a voice announcement. Actual Video https://www.youtube.com/watch?v=pVj_UQ_3-tg When asked at the event start, "Are you ready?", she answers "Of course!" In this article, I'll share the process of creating this original talking character and my thoughts along the way. How I wanted to create our own character that talks About incorporating AI to easily create a memorable video If you're like me, then definitely keep reading! Background The request from the art director overseeing the event's overall creative direction was to "re-edit the video used in our corporate website 's key visual and turn it into a one-minute opening movie." But just re-editing existing footage felt a bit too familiar for employees, and I wanted something more eye-catching, something that instantly energizes the event from the start. That's when I turned to our AI chatbot in our company Slack. I thought, what if I used AI to bring our chatbot to life as a surprise? A personified version in the video could definitely catch everyone's eye. AIs Used To create a talking character, I used the following three AI tools: Adobe Firefly (character image generation) TTSMaker (text-to-speech conversion) Runway (character animation with voice) From here on, I'll show you how I used these AIs to make the video. 1. Character Image Generation Adobe Firefly An image generation AI tool provided by Adobe. Since Adobe Firefly is trained on copyright-free images like Adobe Stock images, it can be used without worrying about copyright issues. Many of the well-known image generation AIs claim to be copyright-free, but some operate in a gray area, such as being able to generate images that closely resemble anime characters. Since this was a corporate event, we wanted to avoid any copyright concerns, so we chose an AI that can be used without such worries. Adobe Firefly Image Generation This character is displayed on our company's Slack with an icon like this: From the icon we established that: The pink color of the icon led us to give her pink hair She's an AI chatbot so we wanted to give her a smart, digital vibe in her overall look And by doing so we expanded the character's image. On-screen Operations When you open Firefly, you'll see a screen like this. Roughly speaking, you enter the prompts to generate an image in the input area below, and then use the menu on the left to adjust the aspect ratio, composition, style, tone, etc. This time, after much trial and error, I generated the character using the prompt "3D character, female, pink hair, white background, upper body, white simple digital clothing, facing forward." Mass Generation Once the prompt was more solidified, it was a matter of luck whether I would get a good result, so I just generated 100 to 200 sheets. Selection To give the event a fresh start, I aimed for a character that felt like an "AI operator" with an official and trustworthy vibe. So, I filtered out these images: The ones looking too young Having weird clothing Scary faces Selected Image After carefully narrowing down the options, I chose this image because it felt cool yet approachable. 2. Text-to-Speech Conversion TTSMaker An AI voice generator that converts typed text into speech. There are many similar AI-based text-to-speech services, but many of them are paid services or require crediting the source if they are free. I went with this tool because it's free to use without any credit attribution. TTSMaker On-screen Operations When you open TTSMaker, you'll see a screen like this. The procedure is as follows: Select a language Enter the text you want to convert to speech Listen to sample voices and choose the tone Set the details like speed and pitch Convert I aimed for a voice that felt like an "AI operator" with an official and trustworthy vibe. After comparing sample voices, I chose "406 - Yuki Tsumiyuki -🇯🇵 Japanese female" and had her read the line: "Mochirondesu. Cho-honbukai o hajimemasu." ("Of course. We'll now start the CHO All-Hands Meeting.") Actual Audio https://www.youtube.com/watch?v=r4Zw2by669I 3. Character Animation with Voice Runway An AI-powered tool that makes it easy to create and edit high-quality videos. I chose this tool for its "Lip Sync Video" feature, which syncs character images with voice audio. Runway On-screen Operations 1.Open "Lip Sync Video" under the "Generative Audio" section, then drag and drop the previously created character image. 2. Check that the facial range of the character image is recognized correctly, and if there are no problems, click "Upload Audio." 3. Drag and drop the previously generated voice, then click "Generate." Generated Video https://www.youtube.com/watch?v=usY4yB9Z1YA A video of the character speaking in sync with the audio was generated. Bonus Tip 1 https://www.youtube.com/watch?v=raYsnhwZONo By uploading a song, the character can actually be made to sing. Bonus Tip 2 https://www.youtube.com/watch?v=3edVClgoLug In this way, it's also possible to make a still image of a real person talk. At a recent internal study session (held in Tokyo), the speaker was suddenly unable to make the business trip from Osaka to attend, so we asked him to prepare still images and voice memos, and we used this AI-generated video to make our presentation. 4. Final Touches That's all for how to create a talking character video. What comes next is a little something extra. Actually, getting a character to talk using AI has become so easy that anyone can do it with the AIs I introduced above. But since I'm a designer in the Creative Office, I wanted to go beyond what any other department could produce. So as a final touch, I added a 3D balloon-style CG made in Illustrator, gave it a soft floating motion in After Effects, and composited it into the video. This added a polished finish and added the value only a creator could bring. Final Video https://www.youtube.com/watch?v=pVj_UQ_3-tg By adding one final touch, the video evolved from just a talking character to something with added graphical expression. Conclusion How it looked when projected at the event Each creative generative AI has its own characteristics and limitations. By understanding and combining those characteristics, I was able to create a level of quality that wouldn't have been possible with just one tool. On the day of the event, this character was projected on a large screen, and I was grateful to hear comments like: "That was amazing! How did you make it?" "The quality was so high I thought it was outsourced!" It felt great to see the impact it made, just as I aimed for. All tools used are simple enough that even non-creators can use it. As long as you have an idea, you can create memorable videos like this. If this article sparked your interest, definitely give it a try! Thank you for reading till the end.
アバター
Introduction Hello! I'm Alex, a Generative AI Engineer on the Generative AI Development Project team at KINTO Technologies. Lately, there's been a growing interest in building multi-agent systems using large language models (LLMs). So in this article, I'd like to share a case where I built a Supervisor-style multi-agent system in less than 30 minutes using the latest LangGraph update, langgraph_supervisor. This system allows a central supervisor agent to coordinate and manage multiple specialized agents. It can be applied to a wide range of use cases, from automating workflows to generating customizable proposal scenarios. What is LangGraph? LangGraph is a Python library designed to simplify the development of AI agents and Retrieval-Augmented Generation (RAG) systems. Combined with LangChain, complex workflows and tasks can be designed and implemented efficiently. It supports state persistence, tool invocation, and features like human-in-the-loop and post-task validation through a centralized persistence layer which served as the foundation for this Supervisor-style system. What is langgraph-supervisor? langgraph-supervisor is a Python library for building hierarchical multi-agent systems using LangGraph, which was recently released by LangChain. A central supervisory agent coordinates task assignments and facilitates communication among specialized agents. This enables the development of adaptable systems that can tackle complex tasks with ease and efficiency. Key Features Supervisor agent creation: Manages multiple specialized agents and orchestrates the overall workflow. Tool-based agent handoff mechanism: Provides a mechanism for achieving smooth communication between agents. Flexible message history management: Enables easy control over conversation flow by managing message history dynamically. @ card What is a Supervisor-style Multi Agent System? Source: https://langchain-ai.github.io/langgraph/tutorials/multi_agent/agent_supervisor/ A Supervisor-style multi-agent system is a structure where a central controlling agent called the Supervisor coordinates with tool-enabled LLM agents, deciding which agent to call, when to do so, and what arguments to pass along. Building a Multi-Agent System with langgraph-supervisor The following steps outline how to build a multi-agent system using langgraph-supervisor. In this example, we’ll create a system that recommends a car and engine type based on simple, anonymized customer information. Additionally, the agent will retrieve car and engine information from the locally stored "Vehicle_Information.csv" file. Environment Setup Prepare Azure OpenAI API keys This time, we'll be using GPT-4o via Azure OpenAI. Depending on your situation, you can also use the OpenAI API, or Anthropic API, etc. And set Azure OpenAI API keys and endpoint as environment variables. os.environ["AZURE_OPENAI_API_KEY"] = "YOUR API KEY" os.environ["AZURE_OPENAI_ENDPOINT"] = "YOUR ENDPOINT" os.environ["AZURE_OPENAI_API_VERSION"] = "YOUR API VERSION" os.environ["AZURE_OPENAI_DEPLOYMENT"] = "YOUR DEPLOYMENT NAME" Install LangGraph and LangChain pip install langgraph pip install langchain pip install langchain_openai Install langgraph-supervisor pip install langgraph-supervisor Setup of tool functions used by LLM and each Agent Define the model using Azure OpenAI's GPT-4o. Next, define the tool functions for agents. from langchain_openai import AzureChatOpenAI import pandas as pd # LLMの初期化 llm = AzureChatOpenAI( azure_deployment=os.getenv("AZURE_OPENAI_DEPLOYMENT"), api_version=os.getenv("AZURE_OPENAI_API_VERSION"), ) car_information_path = "/xxx/xxx/車両情報.csv" # ツール関数の定義例 # CSVファイルから車両情報を読み込み、候補を生成する例 def get_car_features(): """The python code to get car information.""" path = car_information_path # CSVファイルのパスを指定 df = pd.read_csv(car_information_path) car_features = df[["車種名", "ボディタイプ", "説明"]].drop_duplicates() return car_features.to_dict(orient="records") # 選択された車種に対してエンジンタイプを抽出する例 def get_engine_type(selected_car): """The python code to get engine type and engine information.""" path = car_information_path df = pd.read_csv(car_information_path) engine_types = list(df[df["車種名"] == selected_car]["エンジンタイプ"].unique()) return engine_types, "エンジンに関する補足情報" Defining Each Agent We use LangGraph's "create_react_agent" to define each specialized agent (car model recommendation, engine type selection). from langgraph.prebuilt import create_react_agent # 車種推薦エージェント car_agent = create_react_agent( model=llm, tools=[get_car_features], name="car_agent", prompt=""" # Instructions Based on the recommended pattern, choose a car model to recommend and explain the reasoning in about 200 characters. """ ) # Engine type selection agent engine_agent = create_react_agent( model=llm, tools=[get_engine_type], name="engine_agent", prompt=""" # Instructions Choose the most suitable engine type for the recommended car and explain the reasoning in about 200 characters. """ ) Defining the Supervisor Create a Supervisor to oversee each agent and generate a final recommendation based on customer information. One improvement we made here was modularizing the prompt input to the Supervisor, allowing for greater versatility. By changing the contents of the role and task variables and the associated agent, the system can easily be used for other tasks. from langgraph_supervisor import create_supervisor # Create the contents of each module in the prompt role = "車のセールスマン" task = "顧客情報をもとに、最適な車とエンジンタイプを提案してください。" guideline = """ - 出力は必ず上記JSON形式で行ってください。 - 各根拠は200文字程度の豊かな文章で記述してください。" """ output_format = """ { "提案内容": { "提案する車種", "車種の根拠", "選択したエンジンタイプ", "エンジン選定理由" } } """ # Create a prompt system_prompt = f""" ## Role あなたは優れた{role}です。 ## Task {task} ## Guidelines {guideline} ## 最終生成物のフォーマット {output_format} """ # Create the Supervisor workflow = create_supervisor( # 先ほど作成したエージェントと紐づく [car_agent, engine_agent], model=llm, prompt=system_prompt ) # Compile the graph app = workflow.compile() Example Run from langchain.schema import HumanMessage import time # Example of customer information (add more details as needed) customer_info = "顧客情報: Age 35, prioritizes fuel efficiency, currently drives a compact car. # Example run start_time = time.time() result = app.invoke({ "messages": [ {"role": "user", "content": customer_info} ] }) end_time = time.time() # Display the final output print("最終提案:") print(result["messages"][-1].content) print("実行時間: {:.3f}秒".format(end_time - start_time)) Response from the Supervisor 最終提案: { "提案内容": { "提案する車種": "ハイブリッド技術を採用した最新コンパクトカー", "車種の根拠": "顧客は現在コンパクトカーに乗車中であり、燃費性能の良さを重視しています。 ハイブリッド技術を採用した車は、燃料使用の効率が高く、経済的負担や環境への配慮が優れています。 また、ハイブリッドシステムは短距離や都市部での走行にも最適です。 そのため、最新のハイブリッドコンパクトカーが最適な選択肢となります。", "選択したエンジンタイプ": "ハイブリッドエンジン", "エンジン選定理由": "ハイブリッドエンジンは燃費性能が非常に優れており、顧客のニーズである燃料効率を最優先に考慮しています。 さらに、コンパクトカーとの相性も良く、都市部での利用や日常の移動において高いパフォーマンスを発揮します。 このエンジンの選択は、快適性、経済性、環境性能を全て満たします。" } } Run Time: 21.938 seconds Impressions After Building the System Rapid Prototyping The complex inter-agent coordination and state management typically required in LangGraph were implemented with simple code using langgraph_supervisor, and a prototype was successfully built in under 30 minutes. A pretty impressive result. Flexibility and Scalability Each agent can be implemented with its own prompt and tool functions, allowing for easy customization to meet specific business needs. In addition, since state management is handled centrally, future improvements and feature additions are expected to be carried out smoothly. We Are Hiring! KINTO Technologies is looking for passionate colleagues to help drive AI adoption in our businesses. We're happy to start with a casual interview. If you’re even slightly interested, feel free to reach out via the link below or through X DMs . We look forward to hearing from you!! @ card Thanks for reading all the way through!
アバター