[{"data":1,"prerenderedAt":757},["ShallowReactive",2],{"/ja-jp/blog/learn-advanced-rust-programming-with-a-little-help-from-ai-code-suggestions":3,"navigation-ja-jp":42,"banner-ja-jp":442,"footer-ja-jp":452,"blog-post-authors-ja-jp-Michael Friedrich":658,"blog-related-posts-ja-jp-learn-advanced-rust-programming-with-a-little-help-from-ai-code-suggestions":672,"assessment-promotions-ja-jp":710,"next-steps-ja-jp":748},{"id":4,"title":5,"authorSlugs":6,"body":8,"categorySlug":9,"config":10,"content":14,"description":8,"extension":29,"isFeatured":12,"meta":30,"navigation":31,"path":32,"publishedDate":20,"seo":33,"stem":37,"tagSlugs":38,"__hash__":41},"blogPosts/ja-jp/blog/learn-advanced-rust-programming-with-a-little-help-from-ai-code-suggestions.yml","Learn Advanced Rust Programming With A Little Help From Ai Code Suggestions",[7],"michael-friedrich",null,"ai-ml",{"slug":11,"featured":12,"template":13},"learn-advanced-rust-programming-with-a-little-help-from-ai-code-suggestions",false,"BlogPost",{"title":15,"description":16,"authors":17,"heroImage":19,"date":20,"body":21,"category":9,"tags":22,"updatedDate":28},"AIを活用して学ぶ、Rustの高度なプログラミング","このガイド付きチュートリアルでは、AIを搭載したGitLab Duoのコード提案を活用しながら、Rustの高度なプログラミングを学ぶことができます。",[18],"Michael Friedrich","https://res.cloudinary.com/about-gitlab-com/image/upload/v1749662439/Blog/Hero%20Images/codewithheart.png","2023-10-12","20年以上前に新しいプログラミング言語を学び始めたとき、私たちは6枚のCD-ROMからインストールしたVisual Studio 6のMSDNライブラリにアクセスしていました。ペンと紙でアルゴリズムを記録し、設計パターンの本を読み漁り、MSDNで正しい型を調べていましたが、こうした作業に時間がかかることが多々ありました。しかし、リモートコラボレーションや人工知能（AI）の時代が到来し、プログラミング言語の学び方は根本的に変わりました。今では[リモート開発環境](https://about.gitlab.com/blog/quick-start-guide-for-gitlab-workspaces/)をすばやく立ち上げて画面を共有し、グループでのプログラミングセッションを行えるようになりました。[GitLab Duoのコード提案](https://about.gitlab.com/ja-jp/gitlab-duo/)を使用すれば、AIというインテリジェントなパートナーにいつでも頼ることができます。コード提案機能は、ユーザーのプログラミングのスタイルと経験に基づいて学習します。この機能は、インプットとコンテキストさえあれば、最も効率的な提案を提供してくれるのです。\n\nこのチュートリアルでは、[入門編のブログ記事](/blog/learning-rust-with-a-little-help-from-ai-code-suggestions-getting-started/)（英語）からさらに一歩踏み込み、シンプルなフィードリーダーアプリケーションの設計と作成に取り組みます。\n\n- 準備\n    - コード提案\n- Rustの学習の継続\n    - 「Hello, Reader!」アプリ\n    - プロジェクトの初期化\n    - RSSフィードURLの定義\n- モジュール\n    - main() 関数によるモジュール関数の呼び出し\n- クレート\n    - feed-rs：XMLフィードの解析\n- ランタイム設定：プログラム引数\n    - ユーザー入力のエラーハンドリング\n- 永続性とデータ保存\n- 最適化\n    - 非同期実行\n    - スレッドの生成\n    - 関数スコープ、スレッド、クロージャ\n- フィードのXMLの解析およびオブジェクト型への変換\n    - 汎用的なフィードデータ型のマッピング\n    - Option::unwrap()によるエラーハンドリング\n- ベンチマーク\n    - 逐次実行と並列実行のベンチマークの比較\n    - Rustのキャッシュを使用したCI/CD\n- 次のステップ\n    - 非同期学習の演習\n    - フィードバックの共有\n\n## 準備\nソースコードを参照する前に、[VS Code](/blog/learning-rust-with-a-little-help-from-ai-code-suggestions-getting-started/#vs-code)と[Rustの開発環境](/blog/learning-rust-with-a-little-help-from-ai-code-suggestions-getting-started/#development-environment-for-rust)をセットアップしてください。\n\n### コード提案\n実際に提案機能を検証する前に、まずはこの機能の使い方を理解しましょう。GitLab Duoのコード提案は、特定のキーボードショートカットを必要とせず、キーを入力するだけで使用できます。たとえば、コード提案を受け入れるには、`Tab` キーを押します。また、新しく作成したコードの方が、既存のコードをリファクタリングしたものよりもエラー発生率が低くなる点も覚えておきましょう。AIは非決定的であるため、一度削除したコード提案は再び同じ形で提示されない可能性があります。コード提案は現在ベータ版であり、GitLabは、同機能が生成するコンテンツの全体的な精度向上に取り組んでいます。\n\n**ヒント**：コード提案の最新リリースでは、複数行の指示文に対応しています。ニーズに合わせて指示文を調整することで、よりよい提案を得ることができます。\n\n```rust\n\n    // Create a function that iterates over the source array\n    // and fetches the data using HTTP from the RSS feed items.\n    // Store the results in a new hash map.\n    // Print the hash map to the terminal.\n\n```\n\nVS Code拡張機能のオーバーレイは、提案を提示する際に表示されます。提案された行を受け入れるには `Tab` キーを使用し、一単語だけ受け入れるには `Cmd + 右カーソル` キーを使用します。さらに、三点リーダーメニューからツールバーを常に表示するオプションを選択することも可能です。\n\n![VS CodeにおけるGitLab Duoの指示文とコード提案のオーバーレイ](https://about.gitlab.com/images/blogimages/learn-rust-with-ai-code-suggestions-advanced-programming/vs_code_code_suggestions_options_overlay_keep_toolbar.png){: .shadow}\n\n## Rustの学習の継続\nでは、引き続きRustについて学んでいきましょう。Rustは[コード提案でサポートされている言語](https://docs.gitlab.com/ee/user/project/repository/code_suggestions/)のひとつです。[Rust by Example](https://doc.rust-lang.org/rust-by-example/)（英語）では、初心者に最適なチュートリアルが提供されており、公式の[Rust Book](https://doc.rust-lang.org/book/)（英語）を確認しながら進めると効果的です。どちらのリソースもこのブログの執筆の際に参考にしています。\n\n### 「Hello, Reader!」アプリ\nアプリケーションの作成やRustの学習には、さまざまなアプローチがあります。その中には、既存のRustライブラリ、いわゆる `Crates` を利用するものがあり、このブログ記事の後半でも使用します。たとえば、画像を処理して、その結果をファイルに書き込むコマンドラインアプリを作成することができます。また、昔ながらの迷路をクリアしたり、数独を解いたりするアプリケーションを作成するのも楽しいでしょうし、ゲーム開発を行うこともできます。たとえば、[Hands-on Rust](https://hands-on-rust.com/)（英語）というガイドブックでは、ダンジョンクローラー（迷宮探検）ゲームを作りながら、Rustを体系的に学習できるコースを提供しています。筆者の同僚であるFatima Sarah Khalidは、[AIを活用してC++でDragon Realmの制作](/blog/building-a-text-adventure-using-cplusplus-and-code-suggestions/)（英語）を始めたそうです。ぜひそちらもご覧ください。\n\nここで、実際の問題を解決するのに役立つ実用的なユースケースをひとつご紹介します。それは、異なるソースから重要な情報をRSSフィードに集約するというものです。RSSフィードには、セキュリティリリース、ブログ記事、およびソーシャルディスカッションフォーラム（Hacker Newsなど）の最新情報が含まれます。アップデートに含まれる特定のキーワードやバージョンを絞り込んで検索したいと考えることがよくあります。こうしたニーズを基に、アプリケーションの要件をリスト化できます。具体的には、次のような要件です。\n\n1. HTTPウェブサイト、REST API、RSSフィードなどの異なるソースからデータをフェッチ（取得）する（最初の段階ではRSSフィードを使用）。\n1. データを解析する。\n1. データをユーザーに提示する、またはディスクに書き込む。\n1. パフォーマンスを最適化する。\n\nこのブログ記事の学習ステップを完了すると、以下のサンプルアプリケーションの出力が得られます。\n\n![VS Codeのターミナルでのcargo runの実行と、形成されたフィードエントリの出力](https://about.gitlab.com/images/blogimages/learn-rust-with-ai-code-suggestions-advanced-programming/vs_code_terminal_cargo_run_formatted_output_final.png)\n\nアプリケーションはモジュール化されている必要があり、また、将来的にデータ型やフィルター、アクションをトリガーするフックを追加できる基盤も整えておく必要があります。\n\n### プロジェクトの初期化\nリマインダー：`cargo init` をプロジェクトのルートディレクトリで実行すると、 `main ()` エントリポイントを含んだファイル構造が作成されます。これを踏まえ、次のステップでは、Rustのモジュールの作成と使用方法を学びます。\n\n`learn-rust-ai-app-reader` という名前のディレクトリを新規作成し、そのディレクトリに移動したら、`cargo init` を実行します。このコマンドは、暗黙的に `git init` を実行し、新しいGitリポジトリをローカルで初期化します。残りのステップでは、Gitリモートリポジトリのパスを設定していきます。たとえば、`https://gitlab.com/gitlab-da/use-cases/ai/learn-with-ai/learn-rust-ai-app-reader` のように設定します（ご自身のネームスペースに合わせてパスを置き換えてください）。[Gitリポジトリをプッシュ](https://about.gitlab.com/ja-jp/blog/mastering-the-basics-of-git-push-tag/)すると、[GitLabで新しいプロジェクトが自動的に作成](https://docs.gitlab.com/ee/user/project/#create-a-new-project-with-git-push)されます。\n\n```shell\nmkdir learn-rust-ai-app-reader\ncd learn-rust-ai-app-reader\n\ncargo init\n\ngit remote add origin https://gitlab.com/gitlab-da/use-cases/ai/learn-with-ai/learn-rust-ai-app-reader.git\ngit push --set-upstream origin main\n```\n\n新しく作成されたディレクトリからVS Codeを開きます。`code` コマンドラインインターフェース（CLI）によって、macOS上で新しいVS Codeのウィンドウが起動します。\n\n```shell\ncode .\n```\n\n### RSSフィードURLの定義\n新しいHashMapを追加して、`src/main.rs` ファイル内の `main()` 関数にRSSフィードのURLを保存します。複数行の指示コメントを入力することで、GitLab Duoのコード提案に対し、[`HashMap`](https://doc.rust-lang.org/stable/std/collections/struct.HashMap.html) オブジェクトを作成して、Hacker NewsやTechCrunchのデフォルト値で初期化するよう指示できます。注：提案に含まれるURLが正しいことを確認してください。\n\n```rust\nfn main() {##$_0A$##    // Define RSS feed URLs in the variable rss_feeds##$_0A$##    // Use a HashMap##$_0A$##    // Add Hacker News and TechCrunch##$_0A$##    // Ensure to use String as type##$_0A$####$_0A$##}\n```\n\nコードコメントでは以下の点を指示しています。\n\n1. 変数名 `rss_feeds`\n2. `HashMap` タイプ\n3. 3. 初期のseedキー/バリューペア\n4. String 型（`to_string ()` 呼び出しで確認可能）\n\n考えられるコードの例は次の通りです。\n\n```rust\nuse std::collections::HashMap;\n\nfn main() {##$_0A$##    // Define RSS feed URLs in the variable rss_feeds##$_0A$##    // Use a HashMap##$_0A$##    // Add Hacker News and TechCrunch##$_0A$##    // Ensure to use String as type##$_0A$##    let rss_feeds = HashMap::from([##$_0A$##        (\"Hacker News\".to_string(), \"https://news.ycombinator.com/rss\".to_string()),##$_0A$## (\"TechCrunch\".to_string(), \"https://techcrunch.com/feed/\".to_string()),##$_0A$## ]);##$_0A$####$_0A$##}\n```\n\n![VS Codeにおける、コード提案を活用したHacker NewsとTechCrunchのRSSフィードURLの提案](https://about.gitlab.com/images/blogimages/learn-rust-with-ai-code-suggestions-advanced-programming/vs_code_main_array_rss_feed_urls_suggested.png)\n\nVS Codeで新しいターミナルを開き（cmd + shift + p で `terminal` を検索）、`cargo build` を実行して変更をビルドします。エラーメッセージが表示され、`use std::collections::HashMap;` のインポートを追加するよう指示されます。\n\n次のステップでは、RSSフィードのURLを使用して操作を行います。[以前のブログ記事](/blog/learning-rust-with-a-little-help-from-ai-code-suggestions-getting-started/)（英語）では、コードを関数に分割する方法を解説しました。今回は、リーダーアプリケーションのコードをよりモジュール化して整理し、Rustのモジュールを使用します。\n\n## モジュール\n[モジュール](https://doc.rust-lang.org/rust-by-example/mod.html)は、 コードの整理に役立ちます。また、関数をモジュールのスコープ内に隠し、main()スコープからのアクセスを制限することも可能です。リーダーアプリケーションでは、RSSフィードのコンテンツを取得し、XMLレスポンスを解析したいため、`main()` からは、`get_feeds()` 関数のみにアクセスできるようにし、それ以外の機能はモジュール内のみで使用できるように制限します。\n\n`src/` ディレクトリに `feed_reader.rs` という名前の新しいファイルを作成します。コード提案に、`feed_reader` という名前の公開モジュールと、String HashMapをインプットとして受け取る `get_feeds()` という公開関数を作成するよう指示します。重要：[Rustのモジュール構造](https://doc.rust-lang.org/book/ch07-02-defining-modules-to-control-scope-and-privacy.html)に従って、ファイル名とモジュール名を同じにする必要があります。\n\n![コード提案：関数と入力型を使った公開モジュールの作成](https://about.gitlab.com/images/blogimages/learn-rust-with-ai-code-suggestions-advanced-programming/code_suggestions_rust_public_module_function_input.png){: .shadow}\n\nコード提案に入力変数名と型を指定すると、必要な `std::collections::HashMap` モジュールが自動的にインポートされます。ヒント：最適な結果を得られるよう、コメントを使って変数の型を調整してみましょう。また、Rustでは関数のパラメータをオブジェクト参照として渡すのがベストプラクティスとされています。以下に例を示します。\n\n```rust\n// Create public module feed_reader\n// Define get_feeds() function which takes rss_feeds as String HashMap reference as input\npub mod feed_reader {##$_0A$##    use std::collections::HashMap;##$_0A$####$_0A$##    pub fn get_feeds(rss_feeds: &HashMap\u003CString, String>) {##$_0A$##        // Do something with the RSS feeds##$_0A$##    }##$_0A$##}\n```\n\n![コード提案：`get_feeds()` 関数と提案された入力変数を含む公開モジュール](https://about.gitlab.com/images/blogimages/learn-rust-with-ai-code-suggestions-advanced-programming/code_suggestions_rust_public_module_function_input.png){: .shadow}\n\n関数内では、コード提案に以下の手順を指示します。\n\n1. `// Iterate over the RSS feed URLs` （RSSフィードURLを反復処理する）\n2. `// Fetch URL content` （URLコンテンツを取得する）\n3. `// Parse XML body` （XMLの本文を解析する）\n4. `// Print the result`  (結果を出力する)\n\n![コード提案：`get_feeds()` 関数を含む公開モジュール、ステップ1：イテレート](https://about.gitlab.com/images/blogimages/learn-rust-with-ai-code-suggestions-advanced-programming/code_suggestions_rust_module_function_01_iterate.png){: .shadow}\n\n![コード提案：`get_feeds()` 関数を含む公開モジュール、ステップ2：URLコンテンツの取得](https://about.gitlab.com/images/blogimages/learn-rust-with-ai-code-suggestions-advanced-programming/code_suggestions_rust_module_function_02_fetch_content.png){: .shadow}\n\n![コード提案：`get_feeds()` 関数を含む公開モジュール、ステップ3：XML本文の解析](https://about.gitlab.com/images/blogimages/learn-rust-with-ai-code-suggestions-advanced-programming/code_suggestions_rust_module_function_03_parse_body.png){: .shadow}\n\n![コード提案：`get_feeds()` 関数を含む公開モジュール、ステップ4：結果の出力](https://about.gitlab.com/images/blogimages/learn-rust-with-ai-code-suggestions-advanced-programming/code_suggestions_rust_module_function_04_print_result.png){: .shadow}\n\n次のコードが提案されます。\n\n```rust\n// Create public module feed_reader\n// Define get_feeds() function which takes rss_feeds as String HashMap reference as input\npub mod feed_reader {##$_0A$##    use std::collections::HashMap;##$_0A$####$_0A$##    pub fn get_feeds(rss_feeds: &HashMap\u003CString, String>) {##$_0A$##        // Iterate over the RSS feed URLs##$_0A$##        for (name, url) in rss_feeds {##$_0A$## println!(\"{}: {}\", name, url);##$_0A$####$_0A$##            // Fetch URL content##$_0A$##            let body = reqwest::blocking::get(url).unwrap().text().unwrap();##$_0A$####$_0A$## // Parse XML body##$_0A$##            let parsed_body = roxmltree::Document::parse(&body).unwrap();##$_0A$####$_0A$##            // Print the result##$_0A$##            println!(\"{:#?}\", parsed_body);##$_0A$##        }##$_0A$##    }##$_0A$##}\n```\n\nここで新しいキーワード [`unwrap()`](https://doc.rust-lang.org/rust-by-example/error/option_unwrap.html)が登場します。Rustは `null` 値をサポートしておらず、すべての値に対して [`Option` 型](https://doc.rust-lang.org/rust-by-example/std/option.html)を使用します。たとえば、`Text` や `String` といった特定のラップされた型を使用することが確定している場合、`unwrap()` メソッドを呼び出してその値を取得できます。ただし、値が `None` の場合、`unwrap()` メソッドはパニックを起こします。\n\n**注意**：コード提案は、`// Fetch URL content` のコメント指示に従って、`reqwest::blocking::get` 関数を参照します。[`reqwest`](https://docs.rs/reqwest/latest/reqwest/)というクレーと名は意図的なものであり、タイポではありません。非同期リクエストとブロッキングリクエストの処理に役立つ、優れた利便性と高レベルのHTTPクライアントの機能を提供します。\n\nXMLの本文の解析は難しく、異なる結果が得られることがあります。また、スキーマはRSSフィードURLごとに異なる可能性があります。まずは `get_feeds()` 関数を呼び出し、その後でコードの改善に取り組みましょう。\n\n### main() 関数によるモジュール関数の呼び出し\n\n現在、main() 関数は `get_feeds()` 関数を認識していないため、まずそのモジュールをインポートする必要があります。他のプログラミング言語では `include` や `import` といったキーワードを目にすることがありますが、Rustのモジュールシステムは異なります。\n\nモジュールはパスディレクトリに整理されます。今回の例では、両方のソースファイルが同じディレクトリレベルに存在しています。`feed_reader.rs` はクレートとして解釈され、その中に `feed_reader` というモジュールがあり、そのモジュールが `get_feeds()` 関数を定義しています。\n\n```text\nsrc/\n  main.rs\n  feed_reader.rs\n\n```\n\n`feed_reader.rs` ファイルの `get_feeds()` 関数にアクセスするためには、まず `main.rs` のスコープに[モジュールパスを取り込み](https://doc.rust-lang.org/book/ch07-04-bringing-paths-into-scope-with-the-use-keyword.html)、その後フルパスで関数を呼び出します。\n\n```rust\nmod feed_reader;\n\nfn main() {\n\n    feed_reader::feed_reader::get_feeds(&rss_feeds);\n\n```\n\nあるいは、`use` キーワードを使って関数のフルパスをインポートし、その後短い関数名で呼び出すこともできます。\n\n```rust\nmod feed_reader;\nuse feed_reader::feed_reader::get_feeds;\n\nfn main() {\n\n    get_feeds(&rss_feeds);\n\n```\n\n**ヒント**：Rustのモジュールシステムを視覚的によりよく理解するために、[Rustモジュールシステムについてわかりやすく説明したブログ記事](https://www.sheshbabu.com/posts/rust-module-system/)（英語）をお読みください。\n\n```diff\nfn main() {\n    // ...\n\n    // Print feed_reader get_feeds() output\n    println!(\"{}\", feed_reader::get_feeds(&rss_feeds));\n\n```\n\n```rust\nuse std::collections::HashMap;\n\nmod feed_reader;\n// Alternative: Import full function path\n//use feed_reader::feed_reader::get_feeds;\n\nfn main() {##$_0A$##    // Define RSS feed URLs in the variable rss_feeds##$_0A$##    // Use a HashMap##$_0A$##    // Add Hacker News and TechCrunch##$_0A$##    // Ensure to use String as type##$_0A$##    let rss_feeds = HashMap::from([##$_0A$##        (\"Hacker News\".to_string(), \"https://news.ycombinator.com/rss\".to_string()),##$_0A$## (\"TechCrunch\".to_string(), \"https://techcrunch.com/feed/\".to_string()),##$_0A$## ]);##$_0A$####$_0A$##    // Call get_feeds() from feed_reader module##$_0A$## feed_reader::feed_reader::get_feeds(&rss_feeds);##$_0A$##    // Alternative: Imported full path, use short path here.##$_0A$## //get_feeds(&rss_feeds);##$_0A$##}\n```\n\nターミナルで `cargo build` を再実行しコードをビルドします。\n\n```shell\ncargo build\n```\n\n以下は、HTTPリクエストやXML解析に関する一般的なコードを参照した際に発生する可能性のあるビルドエラーの例です。\n\n1. エラー： `could not find blocking in reqwest`\n解決策：`Config.toml` ファイルで `reqwest` クレートの `blocking` 機能を有効にします（`reqwest = { version = \"0.11.20\", features = [\"blocking\"] }`）\n2. エラー：`failed to resolve: use of undeclared crate or module reqwest`\n解決策：`reqwest` クレートを追加します\n3. エラー：`failed to resolve: use of undeclared crate or module roxmltree`\n解決策：`roxmltree` クレートを追加します\n\n```shell\nvim Config.toml\n\nreqwest = { version = \"0.11.20\", features = [\"blocking\"] }\n```\n\n```shell\ncargo add reqwest\ncargo add roxmltree\n```\n\n**ヒント**：エラーメッセージの文字列を `Rust \u003Cerror message>` としてブラウザで検索し、欠落しているクレートが利用可能であるかどうか確認しましょう。通常、検索結果に「crates.io」が表示され、そこから不足している依存関係を追加できます。\n\nビルドが成功したら、`cargo run` でコードを実行し、Hacker NewsのRSSフィードの出力を確認します。\n\n![VS Codeのターミナルでcargo runを実行して、Hacker NewsのXMLフィードを取得](https://about.gitlab.com/images/blogimages/learn-rust-with-ai-code-suggestions-advanced-programming/vs_code_terminal_fetch_rss_feed_output_hacker_news.png){: .shadow}\n\nXML本文を人間が読める形式に解析するにはどうすればいいでしょうか？次のセクションでは、既存の解決策とRustのクレートがどのように機能するかを説明します。\n\n## クレート\nRSSフィードは共通のプロトコルと仕様に基づいており、XMLを解析して下位のオブジェクト構造を理解するのは、例えば車輪のように、すでに存在しているものを再び深く掘り下げて再発明するかのようです。。こういったタスクに対するおすすめのアプローチは、過去に同じ問題に直面した人がいないか、また、その問題を解決するためのコードがすでに作られていないかを調べることです。\n\nRustでの再利用可能なライブラリコードは [`Crates`](https://doc.rust-lang.org/rust-by-example/crates.html)と呼ばれる単位で整理され、パッケージで提供されます。これらはcrates.ioのパッケージレジストリで利用可能です。これらの依存関係をプロジェクトに追加するには、`Config.toml` ファイルの `[dependencies]` セクションを編集するか、`cargo add \u003Cname>` コマンドを使用します。\n\nリーダーアプリケーションでは、[feed-rs クレート](https://crates.io/crates/feed-rs)を使用したいため、新しいターミナルを開き、次のコマンドを実行してください。\n\n```shell\ncargo add feed-rs\n```\n\n![VS Codeのターミナル：クレートを追加し、Config.tomlで確認](https://about.gitlab.com/images/blogimages/learn-rust-with-ai-code-suggestions-advanced-programming/vs_code_rust_crate_add_feed-rs_explained.png)\n\n### feed-rs：XMLフィードの解析\n`src/feed_reader.rs` に移動し、XML本文を解析する部分に対して修正を加えます。コード提案は、`feed-rs` クレートの `parser::parse` 関数をどのように呼び出すか理解していますが、ひとつだけ特別な点があります。`feed-rs` は文字列をrawバイトとして入力し、自らエンコーディングを判断します。ただし、コメントに指示を追加することで、期待の結果を得ることは可能です。\n\n```rust\n\n            // Parse XML body with feed_rs parser, input in bytes\n            let parsed_body = feed_rs::parser::parse(body.as_bytes()).unwrap();\n\n```\n\n![コード提案：`get_feeds()` 関数を含む公開モジュール、ステップ5：XMLパーサーをfeed-rsに変更](https://about.gitlab.com/images/blogimages/learn-rust-with-ai-code-suggestions-advanced-programming/code_suggestions_rust_module_function_05_use_feed_rs_to_parse.png){: .shadow}\n\n`feed-rs` を使用するメリットは即座に可視化できるものでなく、`cargo run` で出力を確認するとその効果が明らかになります。すべてのキーと値がそれぞれのRustオブジェクト型にマッピングされ、さらに高度な処理に利用できるようになります。\n\n![VS Codeのターミナル、cargo runを実行してHacker NewsのXMLフィードを取得](https://about.gitlab.com/images/blogimages/learn-rust-with-ai-code-suggestions-advanced-programming/vs_code_terminal_fetch_rss_feed_output_hacker_news_feed_rs.png){: .shadow}\n\n## ランタイム設定：プログラム引数\nここまで、コンパイル時にバイナリに埋め込まれ、ハードコードされたRSSフィードの値を使用してプログラムを実行してきました。次のステップでは、実行時にRSSフィードを設定できるようにします。\n\nRustでは、標準miscライブラリに[プログラム引数](https://doc.rust-lang.org/rust-by-example/std_misc/arg.html)を処理するための機能が用意されています。[プログラム引数を解析](https://doc.rust-lang.org/rust-by-example/std_misc/arg/matching.html)することで、高度なプログラム引数パーサー（たとえば[clap](https://docs.rs/clap/latest/clap/)クレート）を使用したり、プログラムパラメータを構成ファイルやフォーマット（[TOML](https://toml.io/en/)やYAML）に移したりするよりも、簡単かつ効率的に学習を進めることができます。実際にいくつかの方法を試した結果、学習効果を最大限に高めるには、この方法が最適であると判断しましたが、唯一の方法ではありません。他の方法でRSSフィードの設定を試してみる価値もあります。\n\n単純な解決策としては、コマンドパラメータを `\"name,url\"` の文字列ペアとして渡してから、`,` で分割して名前とURLの値を抽出します。コード提案に、これらの操作を実行して新しい値で `rss_feeds` ハッシュマップを拡張するようコメントで指示します。変数が変更可能でない可能性があるため、`let rss_feeds` を `let mut rss_feeds` に変更する必要がある点にご注意ください。\n\n`src/main.rs` に移動し、`rss_feeds` 変数の後に次のコードを `main()` 関数の追加します。まずはコメントでプログラム引数を定義し、提案されたコードスニペットを確認します。\n\n```rust\n\n    // Program args, format \"name,url\"\n    // Split value by , into name, url and add to rss_feeds\n\n```\n\n![プログラム引数に関するコード提案、および rss_feeds 変数のために名前とURLの値を分割するコード提案](https://about.gitlab.com/images/blogimages/learn-rust-with-ai-code-suggestions-advanced-programming/code_suggestions_rust_program_args_boring_solution.png){: .shadow}\n\nコード全体の例は次のようになります。\n\n```rust\nfn main() {##$_0A$##    // Define RSS feed URLs in the variable rss_feeds##$_0A$##    // Use a HashMap##$_0A$##    // Add Hacker News and TechCrunch##$_0A$##    // Ensure to use String as type##$_0A$##    let mut rss_feeds = HashMap::from([##$_0A$##        (\"Hacker News\".to_string(), \"https://news.ycombinator.com/rss\".to_string()),##$_0A$## (\"TechCrunch\".to_string(), \"https://techcrunch.com/feed/\".to_string()),##$_0A$## ]);##$_0A$####$_0A$##    // Program args, format \"name,url\"##$_0A$##    // Split value by , into name, url and add to rss_feeds##$_0A$##    for arg in std::env::args().skip(1) {##$_0A$##        let mut split = arg.split(\",\");##$_0A$##        let name = split.next().unwrap();##$_0A$##        let url = split.next().unwrap();##$_0A$##        rss_feeds.insert(name.to_string(), url.to_string());##$_0A$##    }##$_0A$####$_0A$##    // Call get_feeds() from feed_reader module##$_0A$## feed_reader::feed_reader::get_feeds(&rss_feeds);##$_0A$##    // Alternative: Imported full path, use short path here.##$_0A$## //get_feeds(&rss_feeds);##$_0A$##}\n```\n\nプログラムの引数を `cargo run` コマンドに直接渡すことができます。その際は引数の前に `--`をつけます。 すべての引数をダブルクォートで囲み、名前の後にカンマを付けてRSSフィードのURLを引数として渡します。引数は空白で区切ります。\n\n```shell\ncargo build\n\ncargo run -- \"GitLab Blog,https://about.gitlab.com/atom.xml\" \"CNCF,https://www.cncf.io/feed/\"\n```\n\n![VS Codeターミナル、GitLabブログのRSSフィード出力例](https://about.gitlab.com/images/blogimages/learn-rust-with-ai-code-suggestions-advanced-programming/vs_code_terminal_gitlab_blog_rss_feed_example.png){: .shadow}\n\n### ユーザー入力のエラーハンドリング\n提供されたユーザー入力がプログラムで想定される内容と異なる場合、[エラーを発生](https://doc.rust-lang.org/rust-by-example/error.html)させ、呼び出し元がプログラム引数を修正できるようにする必要があります。たとえば、不正なURL形式が渡された場合、それをランタイムエラーとして処理する必要があります。コード提案に対し、URLが無効な場合はエラーをスローするようコメントで指示します。\n\n```rust\n\n    // Ensure that URL contains a valid format, otherwise throw an error\n\n```\n\nひとつの解決策として、`url` 変数が `http://` または `https://` で始まっているかを確認し、そうでなければ [panic! マクロ](https://doc.rust-lang.org/rust-by-example/std/panic.html)を使ってエラーをスローする方法があります。コード全体の例は次のようになります。\n\n```rust\n\n    // Program args, format \"name,url\"\n    // Split value by , into name, url and add to rss_feeds\n    for arg in std::env::args().skip(1) {\n        let mut split = arg.split(\",\");\n        let name = split.next().unwrap();\n        let url = split.next().unwrap();\n\n        // Ensure that URL contains a valid format, otherwise throw an error\n        if !url.starts_with(\"http://\") && !url.starts_with(\"https://\") {\n            panic!(\"Invalid URL format: {}\", url);\n        }\n\n        rss_feeds.insert(name.to_string(), url.to_string());\n    }\n\n```\n\n任意のURL文字列から `:` を削除してエラーハンドリングをテストします。`RUST_BACKTRACE=full` 環境変数を追加すると、`panic()` 呼び出しが発生した際に詳細な出力を取得できます。\n\n```shell\nRUST_BACKTRACE=full cargo run -- \"GitLab Blog,https://about.gitlab.com/atom.xml\" \"CNCF,https//www.cncf.io/feed/\"\n```\n\n![間違ったURL形式によるパニックエラーのバックトレースが表示されたVS Codeターミナル](https://about.gitlab.com/images/blogimages/learn-rust-with-ai-code-suggestions-advanced-programming/vs_code_terminal_url_format_error_panic_backtrace.png){: .shadow}\n\n## 永続性とデータ保存\nフィードデータを保存する単純な解決策は、解析されたデータを新しいファイルに書き出すことです。コード提案に、RSSフィードの名前と現在のISO日付を含むパターンでファイルを保存するよう指示します。\n\n```rust\n\n    // Parse XML body with feed_rs parser, input in bytes\n    let parsed_body = feed_rs::parser::parse(body.as_bytes()).unwrap();\n\n    // Print the result\n    println!(\"{:#?}\", parsed_body);\n\n    // Dump the parsed body to a file, as name-current-iso-date.xml\n    let now = chrono::offset::Local::now();\n    let filename = format!(\"{}-{}.xml\", name, now.format(\"%Y-%m-%d\"));\n    let mut file = std::fs::File::create(filename).unwrap();\n    file.write_all(body.as_bytes()).unwrap();\n\n```\n\nこの処理の一例として、[chronoクレート](https://crates.io/crates/chrono)を使用する方法があります。`cargo add chrono` を実行して追加し、その後 `cargo build` と `cargo run` を再度実行します。\n\nファイルは `cargo run` が実行されたディレクトリに保存されます。バイナリを `target/debug/` ディレクトリで直接実行している場合、すべてのファイルはそのディレクトリにダンプされます。\n\n![CNCFのRSSフィード内容を含むファイルがディスクに保存された状態のVS Code](https://about.gitlab.com/images/blogimages/learn-rust-with-ai-code-suggestions-advanced-programming/vs_code_cncf_rss_feed_saved_on_disk.png)\n\n## 最適化\n`rss_feeds` 変数内のエントリは逐次実行されています。もし100件以上のURLがリストに設定されている場合、データの取得と処理にはかなりの時間がかかるでしょう。複数のフェッチリクエストを並行して実行できたらどうでしょうか？\n\n### 非同期実行\nRustでは、[スレッド](https://doc.rust-lang.org/book/ch16-01-threads.html)を使用した非同期実行が可能です。\n\n最も簡単な解決策は、各RSSフィードURLごとにスレッドを生成することです。最適化戦略については後のセクションで説明します。並行実行を開始する前に、 `time` コマンドの前に`cargo run` をつけて、逐次実行コードの実行時間を測定しましょう。\n\n```text\ntime cargo run -- \"GitLab Blog,https://about.gitlab.com/atom.xml\" \"CNCF,https://www.cncf.io/feed/\"\n\n0.21s user 0.08s system 10% cpu 2.898 total\n```\n\nなお、この演習では、手動のコーディング作業が多くなる場合があります。並列実行の影響をより効果的に比較するには、逐次実行の動作状態を新しいGitコミットとブランチ `sequential-exec` に保存してください。\n```shell\ngit commit -avm \"Sequential execution working\"\ngit checkout -b sequential-exec\ngit push -u origin sequential-exec\n\ngit checkout main\n```\n\n### スレッドの生成\n`src/feed_reader.rs` を開き、`get_feeds()` 関数をリファクタリングします。まず、現在の状態をGitコミットで保存し、その後、関数のスコープ内の内容を削除します。次に、コード提案への指示と以下のコードコメントを入力します。\n\n1. `// Store threads in vector`：スレッドのハンドルをベクターに格納し、関数呼び出しの最後にスレッドが終了するまで待機できるようにします。\n2. `// Loop over rss_feeds and spawn threads`：すべてのRSSフィードを反復処理するためのコードを作成し、新しいスレッドを作成します。\n\n`thread` モジュールと `time` モジュールを扱うには、次の `use` 文を追加します。\n\n```rust\n\n    use std::thread;\n    use std::time::Duration;\n\n```\n\nその後、forループを閉じるまでコードを書き進めます。コード提案は、自動的にスレッドハンドルを `threads` ベクター変数に追加し、関数の最後でスレッドを結合（join）するよう提案します。\n\n```rust\n\n    pub fn get_feeds(rss_feeds: &HashMap\u003CString, String>) {\n\n        // Store threads in vector\n        let mut threads: Vec\u003Cthread::JoinHandle\u003C()>> = Vec::new();\n\n        // Loop over rss_feeds and spawn threads\n        for (name, url) in rss_feeds {\n            let thread_name = name.clone();\n            let thread_url = url.clone();\n            let thread = thread::spawn(move || {\n\n            });\n            threads.push(thread);\n        }\n\n        // Join threads\n        for thread in threads {\n            thread.join().unwrap();\n        }\n    }\n\n```\n\n次に、`thread` クレートを追加し、もう一度コードをビルドし実行します。\n\n```shell\ncargo add thread\n\ncargo build\n\ncargo run -- \"GitLab Blog,https://about.gitlab.com/atom.xml\" \"CNCF,https://www.cncf.io/feed/\"\n```\n\nこの段階では、データの処理や出力は行われていません。次のステップに移る前に、ここで新たに導入されたキーワードについて解説します。\n\n### 関数スコープ、スレッド、クロージャ\n提案されるコードには新しいキーワードやデザインパターンが含まれることもあるため、これらについて理解しておくことが重要になります。スレッドハンドルは `thread::JoinHandle` という型で、スレッドが終わるのを待機するために使用します（[join()](https://doc.rust-lang.org/book/ch16-01-threads.html#waiting-for-all-threads-to-finish-using-join-handles)メソッドを使います）。\n\n`thread::spawn()` は新しいスレッドを生成し、このスレッド内では、関数オブジェクトを渡すことができます。この場合、[クロージャ](https://doc.rust-lang.org/book/ch13-01-closures.html)式が無名関数として渡されます。また、クロージャ入力は `||` 構文を使用して渡されますが、この際には[`move` クロージャ](https://doc.rust-lang.org/book/ch16-01-threads.html#using-move-closures-with-threads)が関数スコープの変数をスレッドスコープに移動します。これにより、どの変数を新しい関数やクロージャスコープに渡すかを手動で指定する必要がなくなります。\n\nただし、これには制限があります。`rss_feeds` は参照 `&` で、`get_feeds()` 関数の呼び出し元からパラメータとして渡されています。この変数は関数スコープ内でのみ有効です。このエラーを引き起こすには、次のコードスニペットを使用します。\n\n```rust\npub fn get_feeds(rss_feeds: &HashMap\u003CString, String>) {##$_0A$####$_0A$## // Store threads in vector##$_0A$##    let mut threads: Vec\u003Cthread::JoinHandle\u003C()>> = Vec::new();##$_0A$####$_0A$##    // Loop over rss_feeds and spawn threads##$_0A$##    for (key, value) in rss_feeds {##$_0A$##        let thread = thread::spawn(move || {##$_0A$## println!(\"{}\", key);##$_0A$##        });##$_0A$##    }##$_0A$##}\n```\n\n![VS Codeターミナル、参照とスレッドのmoveクロージャに関する変数スコープエラー](https://about.gitlab.com/images/blogimages/learn-rust-with-ai-code-suggestions-advanced-programming/vs_code_terminal_cargo_build_error_function_threads_variable_scopes.png){: .shadow}\n\n`key` 変数は関数スコープ内で作成されていますが、`rss_feeds` 変数を参照しているため、スレッドスコープに移動することはできません。関数パラメータ `rss_feeds` のハッシュマップからアクセスされる値は、`clone()` を使ってローカルコピーを作成する必要があります。\n\n![VS Codeターミナル、cloneを使用したスレッドの生成](https://about.gitlab.com/images/blogimages/learn-rust-with-ai-code-suggestions-advanced-programming/code_suggestions_rust_thread_spawn_clone.png){: .shadow}\n\n```rust\npub fn get_feeds(rss_feeds: &HashMap\u003CString, String>) {\n\n    // Store threads in vector\n    let mut threads: Vec\u003Cthread::JoinHandle\u003C()>> = Vec::new();\n\n    // Loop over rss_feeds and spawn threads\n    for (name, url) in rss_feeds {\n        let thread_name = name.clone();\n        let thread_url = url.clone();\n        let thread = thread::spawn(move || {\n            // Use thread_name and thread_url as values, see next chapter for instructions.\n\n```\n\n## フィードのXMLの解析およびオブジェクト型への変換\n次のステップは、スレッド内のクロージャでRSSフィードの解析手順を繰り返すことです。コード提案の指示とともに以下のコードコメントを追加します。\n\n1. `// Parse XML body with feed_rs parser, input in bytes`：コード提案に対し、RSSフィードのURLコンテンツを取得し、`feed_rs` クレートの関数で解析したいという要望を伝えます。\n2. `// Check feed_type attribute feed_rs::model::FeedType::RSS2 or Atom and print its name`：`feed_type` 属性を [`feed_rs::model::FeedType`](https://docs.rs/feed-rs/latest/feed_rs/model/enum.FeedType.html) と照合してフィードの種類を抽出します。この場合、コード提案に対して、照合の対象とする正確なEnum値を指示する必要があります。\n\n![コード提案に特定のフィードタイプと照合するよう指示](https://about.gitlab.com/images/blogimages/learn-rust-with-ai-code-suggestions-advanced-programming/code_suggestions_feed_rs_type_condition.png){: .shadow}\n\n```rust\n\n            // Parse XML body with feed_rs parser, input in bytes\n            let body = reqwest::blocking::get(thread_url).unwrap().bytes().unwrap();\n            let feed = feed_rs::parser::parse(body.as_ref()).unwrap();\n\n            // Check feed_type attribute feed_rs::model::FeedType::RSS2 or Atom and print its name\n            if feed.feed_type == feed_rs::model::FeedType::RSS2 {\n                println!(\"{} is an RSS2 feed\", thread_name);\n            } else if feed.feed_type == feed_rs::model::FeedType::Atom {\n                println!(\"{} is an Atom feed\", thread_name);\n            }\n\n```\n\nプログラムをもう一度ビルドして実行し、出力を確認します。\n\n```text\ntime cargo run -- \"GitLab Blog,https://about.gitlab.com/atom.xml\" \"CNCF,https://www.cncf.io/feed/\"\n\nCNCF is an RSS2 feed\nTechCrunch is an RSS2 feed\nGitLab Blog is an Atom feed\nHacker News is an RSS2 feed\n```\n\nフィードURLをブラウザで開くか、以前にダウンロードしたファイルを調べて、この出力を確認します。\n\nHacker News はRSS 2.0をサポートしており、`channel(title,link,description,item(title,link,pubDate,comments))` の構造を持っています。TechCrunchとCNCFブログは同様の構造をしています。\n```xml\n\u003Crss version=\"2.0\">\u003Cchannel>\u003Ctitle>Hacker News\u003C/title>\u003Clink>https://news.ycombinator.com/\u003C/link>\u003Cdescription>Links for the intellectually curious, ranked by readers.\u003C/description>\u003Citem>\u003Ctitle>Writing a debugger from scratch: Breakpoints\u003C/title>\u003Clink>https://www.timdbg.com/posts/writing-a-debugger-from-scratch-part-5/\u003C/link>\u003CpubDate>Wed, 27 Sep 2023 06:31:25 +0000\u003C/pubDate>\u003Ccomments>https://news.ycombinator.com/item?id=37670938\u003C/comments>\u003Cdescription>\u003C![CDATA[\u003Ca href=\"https://news.ycombinator.com/item?id=37670938\">Comments\u003C/a>]]>\u003C/description>\u003C/item>\u003Citem>\n```\n\nGitLabブログには[Atom](https://datatracker.ietf.org/doc/html/rfc4287)フィード形式が使用されています。RSSに類似していますが異なる解析ロジックが必要です。\n```xml\n\u003C?xml version='1.0' encoding='utf-8' ?>\n\u003Cfeed xmlns='http://www.w3.org/2005/Atom'>\n\u003C!-- / Get release posts -->\n\u003C!-- / Get blog posts -->\n\u003Ctitle>GitLab\u003C/title>\n\u003Cid>https://about.gitlab.com/blog\u003C/id>\n\u003Clink href='https://about.gitlab.com/blog/' />\n\u003Cupdated>2023-09-26T00:00:00+00:00\u003C/updated>\n\u003Cauthor>\n\u003Cname>The GitLab Team\u003C/name>\n\u003C/author>\n\u003Centry>\n\u003Ctitle>Atlassian Server ending: Goodbye disjointed toolchain, hello DevSecOps platform\u003C/title>\n\u003Clink href='https://about.gitlab.com/blog/atlassian-server-ending-move-to-a-single-devsecops-platform/' rel='alternate' />\n\u003Cid>https://about.gitlab.com/blog/atlassian-server-ending-move-to-a-single-devsecops-platform/\u003C/id>\n\u003Cpublished>2023-09-26T00:00:00+00:00\u003C/published>\n\u003Cupdated>2023-09-26T00:00:00+00:00\u003C/updated>\n\u003Cauthor>\n\u003Cname>Dave Steer, Justin Farris\u003C/name>\n\u003C/author>\n```\n\n### 汎用的なフィードデータ型のマッピング\n[`roxmltree::Document::parse`](https://docs.rs/roxmltree/latest/roxmltree/struct.Document.html) を使用する場合、XMLノードツリーとその特定のタグ名を理解する必要があります。幸い、[`feed_rs::model::Feed`](https://docs.rs/feed-rs/latest/feed_rs/model/struct.Feed.html) はRSSとAtomフィードの統合モデルを提供しているため、引き続き `feed_rs` クレートを使用して進めましょう。\n\n1. Atom：Feed->Feed、Entry->Entry\n2. RSS：Channel->Feed、Item->Entry\n\n上記のマッピングに加えて、必要な属性を抽出し、それらのデータ型をマッピングする必要があります。[feed_rs::modelのドキュメント](https://docs.rs/feed-rs/latest/feed_rs/model/index.html)を開き、構造体やそのフィールド、実装について理解しながら進めることをお勧めします。これは、`feed_rs` の実装に特有の型変換エラーやコンパイルエラーが発生するのを防ぐためです。\n\n[`Feed`](https://docs.rs/feed-rs/latest/feed_rs/model/struct.Feed.html) 構造体は `title` を提供しますが、その型は `Option\u003CText>` で、値が設定されているか、何もないかのいずれかです。[`Entry`](https://docs.rs/feed-rs/latest/feed_rs/model/struct.Entry.html) 構造体は以下の情報を提供します。\n\n1. `title`：`Option\u003CText>` 型で、[`Text`](https://docs.rs/feed-rs/latest/feed_rs/model/struct.Text.html) には `content` フィールドが `String` 型として含まれています。\n2. `published`：`Option\u003CDateTime\u003CUtc>>` 型で、[`DateTime`](https://docs.rs/chrono/latest/chrono/struct.DateTime.html) は [`format()` メソッド](https://docs.rs/chrono/latest/chrono/struct.DateTime.html#method.format)を持ちます。\n3. `summary`：`Option\u003CText>` 型で、[`Text`](https://docs.rs/feed-rs/latest/feed_rs/model/struct.Text.html) には `content` フィールドが `String` 型として含まれています。\n4. `links`：`Vec\u003CLink>` 型で、[`Link`](https://docs.rs/feed-rs/latest/feed_rs/model/struct.Link.html) 項目のベクターです。`href` 属性が生のURL文字列を提供します。\n\n1から4に従って、フィードエントリから必要なデータを抽出します。繰り返しますが、すべての `Option` 型には `unwrap()` を呼び出す必要があります。このため、コード提案に対してより直接的な指示が求められます。\n\n```rust\n\n                // https://docs.rs/feed-rs/latest/feed_rs/model/struct.Feed.html\n                // https://docs.rs/feed-rs/latest/feed_rs/model/struct.Entry.html\n                // Loop over all entries, and print\n                // title.unwrap().content\n                // published.unwrap().format\n                // summary.unwrap().content\n                // links href as joined string\n                for entry in feed.entries {\n                    println!(\"Title: {}\", entry.title.unwrap().content);\n                    println!(\"Published: {}\", entry.published.unwrap().format(\"%Y-%m-%d %H:%M:%S\"));\n                    println!(\"Summary: {}\", entry.summary.unwrap().content);\n                    println!(\"Links: {:?}\", entry.links.iter().map(|link| link.href.clone()).collect::\u003CVec\u003CString>>().join(\", \"));\n                    println!();\n                }\n\n```\n\n![特定の要件に基づいてフィードエントリの型を出力するためのコード提案](https://about.gitlab.com/images/blogimages/learn-rust-with-ai-code-suggestions-advanced-programming/code_suggestions_print_feed_entries_fields_with_rust_type_specifics.png){: .shadow}\n\n### Option::unwrap()によるエラーハンドリング\nプログラムを再ビルドして実行した後、複数行の指示を引き続き処理します。補足：`unwrap()` は空の値に遭遇すると `panic!` マクロを呼び出し、プログラムを強制終了させます。これは、フィードデータ内に `summary` のようなフィールドが設定されていない場合に発生します。\n\n```shell\nGitLab Blog is an Atom feed\nTitle: How the Colmena project uses GitLab to support citizen journalists\nPublished: 2023-09-27 00:00:00\nthread '\u003Cunnamed>' panicked at 'called `Option::unwrap()` on a `None` value', src/feed_reader.rs:40:59\n```\n\n解決策のひとつとして、[`std::Option::unwrap_or_else`](https://doc.rust-lang.org/std/option/enum.Option.html#method.unwrap_or_else) を使用し、デフォルト値として空の文字列を設定することが挙げられます。この構文には、空の `Text` 構造体のインスタンスを返すクロージャが必要です。\n\n問題を解決する上で、正しい初期化方法を見つけるために試行錯誤を重ねました。単に空の文字列を渡すだけではカスタム型ではうまく機能しませんでした。以下に、筆者が試したすべてのアプローチとリサーチの過程をまとめました。\n\n```rust\n// Problem: The `summary` attribute is not always initialized. unwrap() will panic! then.\n// Requires use mime; and use feed_rs::model::Text;\n/*\n// 1st attempt: Use unwrap() to extraxt Text from Option\u003CText> type.\nprintln!(\"Summary: {}\", entry.summary.unwrap().content);\n// 2nd attempt. Learned about unwrap_or_else, passing an empty string.\nprintln!(\"Summary: {}\", entry.summary.unwrap_or_else(|| \"\").content);\n// 3rd attempt. summary is of the Text type, pass a new struct instantiation.\nprintln!(\"Summary: {}\", entry.summary.unwrap_or_else(|| Text{}).content);\n// 4th attempt. Struct instantiation requires 3 field values.\nprintln!(\"Summary: {}\", entry.summary.unwrap_or_else(|| Text{\"\", \"\", \"\"}).content);\n// 5th attempt. Struct instantation with public fields requires key: value syntax\nprintln!(\"Summary: {}\", entry.summary.unwrap_or_else(|| Text{content_type: \"\", src: \"\", content: \"\"}).content);\n// 6th attempt. Reviewed expected Text types in https://docs.rs/feed-rs/latest/feed_rs/model/struct.Text.html and created Mime and String objects\nprintln!(\"Summary: {}\", entry.summary.unwrap_or_else(|| Text{content_type: mime::TEXT_PLAIN, src: String::new(), content: String::new()}).content);\n// 7th attempt: String and Option\u003CString> cannot be casted automagically. Compiler suggested using `Option::Some()`.\nprintln!(\"Summary: {}\", entry.summary.unwrap_or_else(|| Text{content_type: mime::TEXT_PLAIN, src: Option::Some(), content: String::new()}).content);\n*/\n\n// xth attempt: Solution. Option::Some() requires a new String object.\nprintln!(\"Summary: {}\", entry.summary.unwrap_or_else(|| Text{content_type: mime::TEXT_PLAIN, src: Option::Some(String::new()), content: String::new()}).content);\n```\n\nこのアプローチは、コードが複雑で読みにくく、さらにコード提案の支援がなかったため、手動でコーディングする必要があり、満足のいくものではありませんでした。一旦立ち止まり、アプローチを見直しました。`Option` が `none` の場合に`unwrap()` はエラーをスローするのであれば、そのエラーを処理する簡単な方法があるのではと考え、コード提案に新しいコメントで質問しました。\n\n```shell\n\n                // xth attempt: Solution. Option::Some() requires a new String object.\n                println!(\"Summary: {}\", entry.summary.unwrap_or_else(|| Text{content_type: mime::TEXT_PLAIN, src: Option::Some(String::new()), content: String::new()}).content);\n\n                // Alternatively, use Option.is_none()\n\n```\n\n![コード提案によるOptions.is_noneを使った代替案](https://about.gitlab.com/images/blogimages/learn-rust-with-ai-code-suggestions-advanced-programming/code_suggestions_after_complex_unwrap_or_else_ask_for_alternative_option.png){: .shadow}\n\n結果として、読みやすさが向上し、`unwrap()` による無駄なCPUサイクルが減りました。複雑な問題を解決することから単純な解決策を使用することまでの学習過程を大幅に短縮できました。まさにウィンウィンです。\n\n忘れないうちに、XMLデータをディスクに保存する指示を再度追加して、リーダーアプリを完成させましょう。\n\n```rust\n\n                // Dump the parsed body to a file, as name-current-iso-date.xml\n                let file_name = format!(\"{}-{}.xml\", thread_name, chrono::Local::now().format(\"%Y-%m-%d-%H-%M-%S\"));\n                let mut file = std::fs::File::create(file_name).unwrap();\n                file.write_all(body.as_ref()).unwrap();\n\n```\n\nプログラムをビルドして実行し、出力を確認します。\n\n```shell\ncargo build\n\ntime cargo run -- \"GitLab Blog,https://about.gitlab.com/atom.xml\" \"CNCF,https://www.cncf.io/feed/\"\n```\n\n![VS Codeターミナルでcargo runを実行し、フォーマットされたフィードエントリの出力](https://about.gitlab.com/images/blogimages/learn-rust-with-ai-code-suggestions-advanced-programming/vs_code_terminal_cargo_run_formatted_output_final.png)\n\n## ベンチマーク\n\n### 逐次実行と並列実行のベンチマークの比較\nそれぞれ5つのサンプルを作成して、実行時間のベンチマークを比較します。\n\n1. 逐次実行\n2. 並列実行\n\n```shell\n# Sequential\ngit checkout sequential-exec\n\ntime cargo run -- \"GitLab Blog,https://about.gitlab.com/atom.xml\" \"CNCF,https://www.cncf.io/feed/\"\n\n0.21s user 0.08s system 10% cpu 2.898 total\n0.21s user 0.08s system 11% cpu 2.585 total\n0.21s user 0.09s system 10% cpu 2.946 total\n0.19s user 0.08s system 10% cpu 2.714 total\n0.20s user 0.10s system 10% cpu 2.808 total\n```\n\n```shell\n# Parallel\ngit checkout parallel-exec\n\ntime cargo run -- \"GitLab Blog,https://about.gitlab.com/atom.xml\" \"CNCF,https://www.cncf.io/feed/\"\n\n0.19s user 0.08s system 17% cpu 1.515 total\n0.18s user 0.08s system 16% cpu 1.561 total\n0.18s user 0.07s system 17% cpu 1.414 total\n0.19s user 0.08s system 18% cpu 1.447 total\n0.17s user 0.08s system 16% cpu 1.453 total\n```\n\n4つのRSSフィードスレッドを並列実行した場合、CPU使用率は増加しましたが、逐次実行に比べて全体の実行時間はほぼ半減しました。この点を踏まえて、Rustの学習を続け、コードと機能を最適化していきます。\n\nなお、ここではCargoを使ってデバッグビルドを実行しており、最適化されたリリースビルドはまだ実行していません。また、並列実行にはいくつか注意点があります。一部のHTTPエンドポイントはレート制限を設けており、並列処理がこの制限に引っかかりやすくなる可能性があります。\n\n複数のスレッドを並列実行するシステムも過負荷になる可能性があります。これは、カーネル内でのコンテキストスイッチ（スレッド間の切り替え）を通じて各スレッドにリソースを割り当てる必要があるためです。1つのスレッドが計算リソースを使用している間、他のスレッドは待機状態になります。スレッドが多すぎると、システム全体が遅くなり、処理がスピードアップするどころか逆に遅くなることもあります。解決策としては、呼び出し元がキューにタスクを追加し、定義された数のワーカースレッドが非同期実行のためにタスクを処理する[ワークキュー](https://docs.rs/work-queue/latest/work_queue/)などの設計パターンがあります。\n\nRustでは、スレッド間のデータ同期を行う[チャンネル](https://doc.rust-lang.org/rust-by-example/std_misc/channels.html)を利用することもできます。競合状態を防ぐために、セーフロックを提供する[ミューテックス](https://doc.rust-lang.org/std/sync/struct.Mutex.html)も利用可能です。\n\n### Rustのキャッシュを使用したCI/CD\n以下のCI/CD構成を `.gitlab-ci.yml` ファイルに追加します。この `run-latest` ジョブは `cargo run` をRSSフィードURLの例とともに呼び出し、実行時間を継続的に計測します。\n\n```text\nstages:\n  - build\n  - test\n  - run\n\ndefault:\n  image: rust:latest\n  cache:\n    key: ${CI_COMMIT_REF_SLUG}\n    paths:\n      - .cargo/bin\n      - .cargo/registry/index\n      - .cargo/registry/cache\n      - target/debug/deps\n      - target/debug/build\n    policy: pull-push\n\n# Cargo data needs to be in the project directory for being cached.\nvariables:\n  CARGO_HOME: ${CI_PROJECT_DIR}/.cargo\n\nbuild-latest:\n  stage: build\n  script:\n    - cargo build --verbose\n\ntest-latest:\n  stage: build\n  script:\n    - cargo test --verbose\n\nrun-latest:\n  stage: run\n  script:\n    - time cargo run -- \"GitLab Blog,https://about.gitlab.com/atom.xml\" \"CNCF,https://www.cncf.io/feed/\"\n\n```\n\n![Rust用のGitLab CI/CDパイプライン、cargo runの出力](https://about.gitlab.com/images/blogimages/learn-rust-with-ai-code-suggestions-advanced-programming/gitlab_cicd_pipeline_rust_cargo_run_output.png){: .shadow}\n\n## 次のステップ\nこのブログ記事の執筆は、筆者自身が高度なRustプログラミング技術を学びつつ、コード提案を使って最適な学習過程を見出すという点で難しいものでした。コード提案は、単なる定型的なコードだけでなく、ローカルコンテキストを理解し、記述されたコードが多いほどアルゴリズムの目的や範囲をよりよく把握した迅速なコード生成にも大いに役立ちます。このブログ記事を読むことで、全てではないにしろ、いくつかの課題や解決策についてご理解いただけたかと思います。\n\nRSSフィードの解析は、外部HTTPリクエストや並列最適化を伴うデータ構造が関わるため、ハードルが高めです。経験豊富なRustユーザーであれば、`なぜstd::rssクレートを使わなかったのか？` と疑問に思うかもしれません。その理由は、std::rssは高度な非同期実行に最適化されており、このブログ記事でご説明したさまざまなRustの機能を示したり説明したりすることができないためです。ぜひ、非同期の演習として、[`rss` クレート](https://docs.rs/rss/latest/rss/)を使ってコードを書き直してみてください。\n\n### 非同期学習のエクササイズ\nこのブログ記事で学んだ内容は、永続的なデータ保存やデータ表示に関する理解を今後も深めていく上で基礎となります。引き続きRustを学びながら、リーダーアプリを最適化していくためのアイデアをいくつかご紹介します。\n\n1. データ保存：sqliteなどのデータベースを使用し、RSSフィードの更新履歴を追跡する。\n2. 通知：子プロセスを生成して、Telegramなどに通知を送る機能を追加する。\n3. 機能：リーダータイプを拡張して、REST APIをサポートする。\n4. 構成：RSSフィードやAPIなどの構成ファイルのサポートを追加する。\n5. 効率性：フィルタリングやサブスクライブしたタグのサポートを追加する。\n6. デプロイ：Webサーバーを使用して、Prometheusメトリクスを収集して[Kubernetes](https://about.gitlab.com/ja-jp/blog/what-is-kubernetes/)にデプロイする。\n\n今後のブログ記事では、これらのアイデアのいくつかを取り上げ、その実装方法について説明します。既存のRSSフィードの実装を詳しく調べ、どのように既存のコードをリファクタリングしてRustのライブラリ（`crates`）を活用できるか学びましょう。\n\n### フィードバックの共有\n[GitLab Duo](https://about.gitlab.com/ja-jp/gitlab-duo/)のコード提案をご使用になった方は、ぜひ[ご意見をフィードバックイシューにお寄せください](https://gitlab.com/gitlab-org/gitlab/-/issues/405152)。\n\n*監修：佐々木 直晴 [@naosasaki](https://gitlab.com/naosasaki) \u003Cbr>\n（GitLab合同会社 ソリューションアーキテクト本部 シニアソリューションアーキテクト）*\n",[23,24,25,26,27],"DevSecOps","careers","tutorial","workflow","AI/ML","2025-01-24","yml",{},true,"/ja-jp/blog/learn-advanced-rust-programming-with-a-little-help-from-ai-code-suggestions",{"title":15,"description":16,"ogTitle":15,"ogDescription":16,"noIndex":12,"ogImage":19,"ogUrl":34,"ogSiteName":35,"ogType":36,"canonicalUrls":34},"https://about.gitlab.com/blog/learn-advanced-rust-programming-with-a-little-help-from-ai-code-suggestions","https://about.gitlab.com","article","ja-jp/blog/learn-advanced-rust-programming-with-a-little-help-from-ai-code-suggestions",[39,24,25,26,40],"devsecops","aiml","_xkRwjVm6p5V0SaoqRPDpQX6lrQKKQ8o1A-7-JQDPJQ",{"data":43},{"logo":44,"freeTrial":49,"sales":54,"login":59,"items":64,"search":372,"minimal":405,"duo":422,"pricingDeployment":432},{"config":45},{"href":46,"dataGaName":47,"dataGaLocation":48},"/ja-jp/","gitlab logo","header",{"text":50,"config":51},"無料トライアルを開始",{"href":52,"dataGaName":53,"dataGaLocation":48},"https://gitlab.com/-/trial_registrations/new?glm_source=about.gitlab.com&glm_content=default-saas-trial/","free trial",{"text":55,"config":56},"お問い合わせ",{"href":57,"dataGaName":58,"dataGaLocation":48},"/ja-jp/sales/","sales",{"text":60,"config":61},"サインイン",{"href":62,"dataGaName":63,"dataGaLocation":48},"https://gitlab.com/users/sign_in/","sign in",[65,92,188,193,294,354],{"text":66,"config":67,"cards":69},"プラットフォーム",{"dataNavLevelOne":68},"platform",[70,76,84],{"title":66,"description":71,"link":72},"DevSecOpsに特化したインテリジェントオーケストレーションプラットフォーム",{"text":73,"config":74},"プラットフォームを詳しく見る",{"href":75,"dataGaName":68,"dataGaLocation":48},"/ja-jp/platform/",{"title":77,"description":78,"link":79},"GitLab Duo Agent Platform","ソフトウェアライフサイクル全体を支えるエージェント型AI",{"text":80,"config":81},"GitLab Duoのご紹介",{"href":82,"dataGaName":83,"dataGaLocation":48},"/ja-jp/gitlab-duo-agent-platform/","gitlab duo agent platform",{"title":85,"description":86,"link":87},"GitLabが選ばれる理由","エンタープライズがGitLabを選ぶ主な理由をご覧ください",{"text":88,"config":89},"詳細はこちら",{"href":90,"dataGaName":91,"dataGaLocation":48},"/ja-jp/why-gitlab/","why gitlab",{"text":93,"left":31,"config":94,"link":96,"lists":100,"footer":170},"製品",{"dataNavLevelOne":95},"solutions",{"text":97,"config":98},"すべてのソリューションを表示",{"href":99,"dataGaName":95,"dataGaLocation":48},"/ja-jp/solutions/",[101,126,148],{"title":102,"description":103,"link":104,"items":109},"自動化","CI/CDと自動化でデプロイを加速",{"config":105},{"icon":106,"href":107,"dataGaName":108,"dataGaLocation":48},"AutomatedCodeAlt","/ja-jp/solutions/delivery-automation/","automated software delivery",[110,114,117,122],{"text":111,"config":112},"CI/CD",{"href":113,"dataGaLocation":48,"dataGaName":111},"/ja-jp/solutions/continuous-integration/",{"text":77,"config":115},{"href":82,"dataGaLocation":48,"dataGaName":116},"gitlab duo agent platform - product menu",{"text":118,"config":119},"ソースコード管理",{"href":120,"dataGaLocation":48,"dataGaName":121},"/ja-jp/solutions/source-code-management/","Source Code Management",{"text":123,"config":124},"自動化されたソフトウェアデリバリー",{"href":107,"dataGaLocation":48,"dataGaName":125},"Automated software delivery",{"title":127,"description":128,"link":129,"items":134},"セキュリティ","セキュリティを犠牲にすることなくコード作成を高速化",{"config":130},{"href":131,"dataGaName":132,"dataGaLocation":48,"icon":133},"/ja-jp/solutions/application-security-testing/","security and compliance","ShieldCheckLight",[135,139,144],{"text":136,"config":137},"Application Security Testing",{"href":131,"dataGaName":138,"dataGaLocation":48},"Application security testing",{"text":140,"config":141},"ソフトウェアサプライチェーンの安全性",{"href":142,"dataGaLocation":48,"dataGaName":143},"/ja-jp/solutions/supply-chain/","Software supply chain security",{"text":145,"config":146},"Software Compliance",{"href":147,"dataGaName":145,"dataGaLocation":48},"/ja-jp/solutions/software-compliance/",{"title":149,"link":150,"items":155},"測定",{"config":151},{"icon":152,"href":153,"dataGaName":154,"dataGaLocation":48},"DigitalTransformation","/ja-jp/solutions/visibility-measurement/","visibility and measurement",[156,160,165],{"text":157,"config":158},"可視性と測定",{"href":153,"dataGaLocation":48,"dataGaName":159},"Visibility and Measurement",{"text":161,"config":162},"バリューストリーム管理",{"href":163,"dataGaLocation":48,"dataGaName":164},"/ja-jp/solutions/value-stream-management/","Value Stream Management",{"text":166,"config":167},"分析とインサイト",{"href":168,"dataGaLocation":48,"dataGaName":169},"/ja-jp/solutions/analytics-and-insights/","Analytics and insights",{"title":171,"items":172},"GitLabが活躍する場所",[173,178,183],{"text":174,"config":175},"Enterprise",{"href":176,"dataGaLocation":48,"dataGaName":177},"/ja-jp/enterprise/","enterprise",{"text":179,"config":180},"スモールビジネス",{"href":181,"dataGaLocation":48,"dataGaName":182},"/ja-jp/small-business/","small business",{"text":184,"config":185},"公共機関",{"href":186,"dataGaLocation":48,"dataGaName":187},"/ja-jp/solutions/public-sector/","public sector",{"text":189,"config":190},"価格",{"href":191,"dataGaName":192,"dataGaLocation":48,"dataNavLevelOne":192},"/ja-jp/pricing/","pricing",{"text":194,"config":195,"link":197,"lists":201,"feature":281},"関連リソース",{"dataNavLevelOne":196},"resources",{"text":198,"config":199},"すべてのリソースを表示",{"href":200,"dataGaName":196,"dataGaLocation":48},"/ja-jp/resources/",[202,235,253],{"title":203,"items":204},"はじめに",[205,210,215,220,225,230],{"text":206,"config":207},"インストール",{"href":208,"dataGaName":209,"dataGaLocation":48},"/ja-jp/install/","install",{"text":211,"config":212},"クイックスタートガイド",{"href":213,"dataGaName":214,"dataGaLocation":48},"/ja-jp/get-started/","quick setup checklists",{"text":216,"config":217},"学ぶ",{"href":218,"dataGaLocation":48,"dataGaName":219},"https://university.gitlab.com/","learn",{"text":221,"config":222},"製品ドキュメント",{"href":223,"dataGaName":224,"dataGaLocation":48},"https://docs.gitlab.com/","product documentation",{"text":226,"config":227},"ベストプラクティスビデオ",{"href":228,"dataGaName":229,"dataGaLocation":48},"/ja-jp/getting-started-videos/","best practice videos",{"text":231,"config":232},"インテグレーション",{"href":233,"dataGaName":234,"dataGaLocation":48},"/ja-jp/integrations/","integrations",{"title":236,"items":237},"検索する",[238,243,248],{"text":239,"config":240},"お客様成功事例",{"href":241,"dataGaName":242,"dataGaLocation":48},"/ja-jp/customers/","customer success stories",{"text":244,"config":245},"ブログ",{"href":246,"dataGaName":247,"dataGaLocation":48},"/ja-jp/blog/","blog",{"text":249,"config":250},"リモート",{"href":251,"dataGaName":252,"dataGaLocation":48},"https://handbook.gitlab.com/handbook/company/culture/all-remote/","remote",{"title":254,"items":255},"つなげる",[256,261,266,271,276],{"text":257,"config":258},"GitLabサービス",{"href":259,"dataGaName":260,"dataGaLocation":48},"/ja-jp/services/","services",{"text":262,"config":263},"コミュニティ",{"href":264,"dataGaName":265,"dataGaLocation":48},"/community/","community",{"text":267,"config":268},"フォーラム",{"href":269,"dataGaName":270,"dataGaLocation":48},"https://forum.gitlab.com/","forum",{"text":272,"config":273},"イベント",{"href":274,"dataGaName":275,"dataGaLocation":48},"/events/","events",{"text":277,"config":278},"パートナー",{"href":279,"dataGaName":280,"dataGaLocation":48},"/ja-jp/partners/","partners",{"backgroundColor":282,"textColor":283,"text":284,"image":285,"link":289},"#2f2a6b","#fff","ソフトウェア開発の未来への洞察",{"altText":286,"config":287},"ソースプロモカード",{"src":288},"https://res.cloudinary.com/about-gitlab-com/image/upload/v1758208064/dzl0dbift9xdizyelkk4.svg",{"text":290,"config":291},"最新情報を読む",{"href":292,"dataGaName":293,"dataGaLocation":48},"/ja-jp/the-source/","the source",{"text":295,"config":296,"lists":298},"会社情報",{"dataNavLevelOne":297},"company",[299],{"items":300},[301,306,312,314,319,324,329,334,339,344,349],{"text":302,"config":303},"GitLabについて",{"href":304,"dataGaName":305,"dataGaLocation":48},"/ja-jp/company/","about",{"text":307,"config":308,"footerGa":311},"採用情報",{"href":309,"dataGaName":310,"dataGaLocation":48},"/jobs/","jobs",{"dataGaName":310},{"text":272,"config":313},{"href":274,"dataGaName":275,"dataGaLocation":48},{"text":315,"config":316},"経営陣",{"href":317,"dataGaName":318,"dataGaLocation":48},"/company/team/e-group/","leadership",{"text":320,"config":321},"チーム",{"href":322,"dataGaName":323,"dataGaLocation":48},"/company/team/","team",{"text":325,"config":326},"ハンドブック",{"href":327,"dataGaName":328,"dataGaLocation":48},"https://handbook.gitlab.com/","handbook",{"text":330,"config":331},"投資家向け情報",{"href":332,"dataGaName":333,"dataGaLocation":48},"https://ir.gitlab.com/","investor relations",{"text":335,"config":336},"トラストセンター",{"href":337,"dataGaName":338,"dataGaLocation":48},"/ja-jp/security/","trust center",{"text":340,"config":341},"AI Transparency Center",{"href":342,"dataGaName":343,"dataGaLocation":48},"/ja-jp/ai-transparency-center/","ai transparency center",{"text":345,"config":346},"ニュースレター",{"href":347,"dataGaName":348,"dataGaLocation":48},"/company/contact/#contact-forms","newsletter",{"text":350,"config":351},"プレス",{"href":352,"dataGaName":353,"dataGaLocation":48},"/press/","press",{"text":55,"config":355,"lists":356},{"dataNavLevelOne":297},[357],{"items":358},[359,362,367],{"text":55,"config":360},{"href":57,"dataGaName":361,"dataGaLocation":48},"talk to sales",{"text":363,"config":364},"サポートポータル",{"href":365,"dataGaName":366,"dataGaLocation":48},"https://support.gitlab.com","support portal",{"text":368,"config":369},"カスタマーポータル",{"href":370,"dataGaName":371,"dataGaLocation":48},"https://customers.gitlab.com/customers/sign_in/","customer portal",{"close":373,"login":374,"suggestions":381},"閉じる",{"text":375,"link":376},"リポジトリとプロジェクトを検索するには、次にログインします",{"text":377,"config":378},"GitLab.com",{"href":62,"dataGaName":379,"dataGaLocation":380},"search login","search",{"text":382,"default":383},"提案",[384,386,391,393,397,401],{"text":77,"config":385},{"href":82,"dataGaName":77,"dataGaLocation":380},{"text":387,"config":388},"コード提案（AI）",{"href":389,"dataGaName":390,"dataGaLocation":380},"/ja-jp/solutions/code-suggestions/","Code Suggestions (AI)",{"text":111,"config":392},{"href":113,"dataGaName":111,"dataGaLocation":380},{"text":394,"config":395},"GitLab on AWS",{"href":396,"dataGaName":394,"dataGaLocation":380},"/ja-jp/partners/technology-partners/aws/",{"text":398,"config":399},"GitLab on Google Cloud",{"href":400,"dataGaName":398,"dataGaLocation":380},"/ja-jp/partners/technology-partners/google-cloud-platform/",{"text":402,"config":403},"GitLabを選ぶ理由",{"href":90,"dataGaName":404,"dataGaLocation":380},"Why GitLab?",{"freeTrial":406,"mobileIcon":410,"desktopIcon":415,"secondaryButton":418},{"text":50,"config":407},{"href":408,"dataGaName":53,"dataGaLocation":409},"https://gitlab.com/-/trials/new/","nav",{"altText":411,"config":412},"GitLabアイコン",{"src":413,"dataGaName":414,"dataGaLocation":409},"https://res.cloudinary.com/about-gitlab-com/image/upload/v1758203874/jypbw1jx72aexsoohd7x.svg","gitlab icon",{"altText":411,"config":416},{"src":417,"dataGaName":414,"dataGaLocation":409},"https://res.cloudinary.com/about-gitlab-com/image/upload/v1758203875/gs4c8p8opsgvflgkswz9.svg",{"text":203,"config":419},{"href":420,"dataGaName":421,"dataGaLocation":409},"https://gitlab.com/-/trial_registrations/new?glm_source=about.gitlab.com/ja-jp/compare/gitlab-vs-github/","get started",{"freeTrial":423,"mobileIcon":428,"desktopIcon":430},{"text":424,"config":425},"GitLab Duoの詳細について",{"href":426,"dataGaName":427,"dataGaLocation":409},"/ja-jp/gitlab-duo/","gitlab duo",{"altText":411,"config":429},{"src":413,"dataGaName":414,"dataGaLocation":409},{"altText":411,"config":431},{"src":417,"dataGaName":414,"dataGaLocation":409},{"freeTrial":433,"mobileIcon":438,"desktopIcon":440},{"text":434,"config":435},"料金ページに戻る",{"href":191,"dataGaName":436,"dataGaLocation":409,"icon":437},"back to pricing","GoBack",{"altText":411,"config":439},{"src":413,"dataGaName":414,"dataGaLocation":409},{"altText":411,"config":441},{"src":417,"dataGaName":414,"dataGaLocation":409},{"title":443,"button":444,"config":449},"エージェント型AIがソフトウェア配信をどのように変革するかをご覧ください",{"text":445,"config":446},"GitLab Transcendを今すぐ視聴",{"href":447,"dataGaName":448,"dataGaLocation":48},"/ja-jp/events/transcend/virtual/","transcend event",{"layout":450,"icon":451},"release","AiStar",{"data":453},{"text":454,"source":455,"edit":461,"contribute":466,"config":471,"items":476,"minimal":650},"GitはSoftware Freedom Conservancyの商標です。当社は「GitLab」をライセンスに基づいて使用しています",{"text":456,"config":457},"ページのソースを表示",{"href":458,"dataGaName":459,"dataGaLocation":460},"https://gitlab.com/gitlab-com/marketing/digital-experience/about-gitlab-com/","page source","footer",{"text":462,"config":463},"このページを編集",{"href":464,"dataGaName":465,"dataGaLocation":460},"https://gitlab.com/gitlab-com/marketing/digital-experience/about-gitlab-com/-/blob/main/content/","web ide",{"text":467,"config":468},"ご協力をお願いします",{"href":469,"dataGaName":470,"dataGaLocation":460},"https://gitlab.com/gitlab-com/marketing/digital-experience/about-gitlab-com/-/blob/main/CONTRIBUTING.md/","please contribute",{"twitter":472,"facebook":473,"youtube":474,"linkedin":475},"https://twitter.com/gitlab","https://www.facebook.com/gitlab","https://www.youtube.com/channel/UCnMGQ8QHMAnVIsI3xJrihhg","https://www.linkedin.com/company/gitlab-com",[477,500,554,584,619],{"title":66,"links":478,"subMenu":483},[479],{"text":480,"config":481},"DevSecOpsプラットフォーム",{"href":75,"dataGaName":482,"dataGaLocation":460},"devsecops platform",[484],{"title":189,"links":485},[486,490,495],{"text":487,"config":488},"プランの表示",{"href":191,"dataGaName":489,"dataGaLocation":460},"view plans",{"text":491,"config":492},"Premiumを選ぶ理由",{"href":493,"dataGaName":494,"dataGaLocation":460},"/ja-jp/pricing/premium/","why premium",{"text":496,"config":497},"Ultimateを選ぶ理由",{"href":498,"dataGaName":499,"dataGaLocation":460},"/ja-jp/pricing/ultimate/","why ultimate",{"title":501,"links":502},"ソリューション",[503,508,511,513,518,523,527,530,533,538,540,542,544,549],{"text":504,"config":505},"デジタルトランスフォーメーション",{"href":506,"dataGaName":507,"dataGaLocation":460},"/ja-jp/topics/digital-transformation/","digital transformation",{"text":509,"config":510},"セキュリティとコンプライアンス",{"href":131,"dataGaName":138,"dataGaLocation":460},{"text":123,"config":512},{"href":107,"dataGaName":108,"dataGaLocation":460},{"text":514,"config":515},"アジャイル開発",{"href":516,"dataGaName":517,"dataGaLocation":460},"/ja-jp/solutions/agile-delivery/","agile delivery",{"text":519,"config":520},"クラウドトランスフォーメーション",{"href":521,"dataGaName":522,"dataGaLocation":460},"/ja-jp/topics/cloud-native/","cloud transformation",{"text":524,"config":525},"SCM",{"href":120,"dataGaName":526,"dataGaLocation":460},"source code management",{"text":111,"config":528},{"href":113,"dataGaName":529,"dataGaLocation":460},"continuous integration & delivery",{"text":161,"config":531},{"href":163,"dataGaName":532,"dataGaLocation":460},"value stream management",{"text":534,"config":535},"GitOps",{"href":536,"dataGaName":537,"dataGaLocation":460},"/ja-jp/solutions/gitops/","gitops",{"text":174,"config":539},{"href":176,"dataGaName":177,"dataGaLocation":460},{"text":179,"config":541},{"href":181,"dataGaName":182,"dataGaLocation":460},{"text":184,"config":543},{"href":186,"dataGaName":187,"dataGaLocation":460},{"text":545,"config":546},"教育",{"href":547,"dataGaName":548,"dataGaLocation":460},"/ja-jp/solutions/education/","education",{"text":550,"config":551},"金融サービス",{"href":552,"dataGaName":553,"dataGaLocation":460},"/ja-jp/solutions/finance/","financial services",{"title":194,"links":555},[556,558,560,562,565,567,570,572,574,576,578,580,582],{"text":206,"config":557},{"href":208,"dataGaName":209,"dataGaLocation":460},{"text":211,"config":559},{"href":213,"dataGaName":214,"dataGaLocation":460},{"text":216,"config":561},{"href":218,"dataGaName":219,"dataGaLocation":460},{"text":221,"config":563},{"href":223,"dataGaName":564,"dataGaLocation":460},"docs",{"text":244,"config":566},{"href":246,"dataGaName":247},{"text":568,"config":569},"お客様の成功事例",{"href":241,"dataGaLocation":460},{"text":239,"config":571},{"href":241,"dataGaName":242,"dataGaLocation":460},{"text":249,"config":573},{"href":251,"dataGaName":252,"dataGaLocation":460},{"text":257,"config":575},{"href":259,"dataGaName":260,"dataGaLocation":460},{"text":262,"config":577},{"href":264,"dataGaName":265,"dataGaLocation":460},{"text":267,"config":579},{"href":269,"dataGaName":270,"dataGaLocation":460},{"text":272,"config":581},{"href":274,"dataGaName":275,"dataGaLocation":460},{"text":277,"config":583},{"href":279,"dataGaName":280,"dataGaLocation":460},{"title":585,"links":586},"Company",[587,589,591,593,595,597,599,603,608,610,612,614],{"text":302,"config":588},{"href":304,"dataGaName":297,"dataGaLocation":460},{"text":307,"config":590},{"href":309,"dataGaName":310,"dataGaLocation":460},{"text":315,"config":592},{"href":317,"dataGaName":318,"dataGaLocation":460},{"text":320,"config":594},{"href":322,"dataGaName":323,"dataGaLocation":460},{"text":325,"config":596},{"href":327,"dataGaName":328,"dataGaLocation":460},{"text":330,"config":598},{"href":332,"dataGaName":333,"dataGaLocation":460},{"text":600,"config":601},"Sustainability",{"href":602,"dataGaName":600,"dataGaLocation":460},"/sustainability/",{"text":604,"config":605},"ダイバーシティ、インクルージョン、ビロンギング（DIB）",{"href":606,"dataGaName":607,"dataGaLocation":460},"/ja-jp/diversity-inclusion-belonging/","Diversity, inclusion and belonging",{"text":335,"config":609},{"href":337,"dataGaName":338,"dataGaLocation":460},{"text":345,"config":611},{"href":347,"dataGaName":348,"dataGaLocation":460},{"text":350,"config":613},{"href":352,"dataGaName":353,"dataGaLocation":460},{"text":615,"config":616},"現代奴隷制の透明性に関する声明",{"href":617,"dataGaName":618,"dataGaLocation":460},"https://handbook.gitlab.com/handbook/legal/modern-slavery-act-transparency-statement/","modern slavery transparency statement",{"title":55,"links":620},[621,623,628,630,635,640,645],{"text":55,"config":622},{"href":57,"dataGaName":58,"dataGaLocation":460},{"text":624,"config":625},"サポートを受ける",{"href":626,"dataGaName":627,"dataGaLocation":460},"/support/","get help",{"text":368,"config":629},{"href":370,"dataGaName":371,"dataGaLocation":460},{"text":631,"config":632},"ステータス",{"href":633,"dataGaName":634,"dataGaLocation":460},"https://status.gitlab.com/","status",{"text":636,"config":637},"利用規約",{"href":638,"dataGaName":639,"dataGaLocation":460},"/terms/","terms of use",{"text":641,"config":642},"プライバシーに関する声明",{"href":643,"dataGaName":644,"dataGaLocation":460},"/ja-jp/privacy/","privacy statement",{"text":646,"config":647},"Cookieの設定",{"dataGaName":648,"dataGaLocation":460,"id":649,"isOneTrustButton":31},"cookie preferences","ot-sdk-btn",{"items":651},[652,654,656],{"text":636,"config":653},{"href":638,"dataGaName":639,"dataGaLocation":460},{"text":641,"config":655},{"href":643,"dataGaName":644,"dataGaLocation":460},{"text":646,"config":657},{"dataGaName":648,"dataGaLocation":460,"id":649,"isOneTrustButton":31},[659],{"id":660,"title":18,"body":8,"config":661,"content":663,"description":8,"extension":29,"meta":667,"navigation":31,"path":668,"seo":669,"stem":670,"__hash__":671},"blogAuthors/en-us/blog/authors/michael-friedrich.yml",{"template":662},"BlogAuthor",{"name":18,"config":664},{"headshot":665,"ctfId":666},"https://res.cloudinary.com/about-gitlab-com/image/upload/v1749659879/Blog/Author%20Headshots/dnsmichi-headshot.jpg","dnsmichi",{},"/en-us/blog/authors/michael-friedrich",{},"en-us/blog/authors/michael-friedrich","lJ-nfRIhdG49Arfrxdn1Vv4UppwD51BB13S3HwIswt4",[673,685,698],{"content":674,"config":683},{"title":675,"description":676,"heroImage":677,"date":678,"body":679,"category":9,"tags":680,"updatedDate":682},"エージェント型SDLC：GitLabとタタ・コンサルタンシー・サービシズ（TCS社）が企業全体でインテリジェントオーケストレーションを提供","開発者と連携してワークフローを自動化し、コンプライアンスを強化し、デリバリーを加速するAIエージェントでDevSecOpsをスケールします。","https://res.cloudinary.com/about-gitlab-com/image/upload/v1771866240/l16gpgupgz8uelyc8jfy.png","2026-02-24","GitLabとタタ・コンサルタンシー・サービシズ（以下TCS社）は、企業がイノベーション速度を大規模に加速できるよう支援するパートナーシップを発表しました。\n企業は迅速で安全なソフトウェアデリバリーを必要としていますが、断片化されたツールチェーン、一貫性のないセキュリティ制御、手動のコンプライアンスプロセスによってソフトウェアデリバリーが遅くなることがよくあります。AI生成コードとAI主導の脅威により、新たな複雑さが加わっています。\nGitLabとTCS Center of Excellence（CoE）アクセラレーターは連携して、移行の摩擦を軽減し、ガードレールを体系化し、DevSecOpsの大規模な導入を産業化します。両社は共に、開発中に必要な監査可能なガードレールを備えた標準化からインテリジェントオーケストレーションへの道筋を可能にします。\n\n## 将来に対応した企業の支援\n\nお客様は、数年ごとに大規模な再エンジニアリングを強いることなく、長期間使用できるよう構築されたDevSecOpsプラットフォームを求めています。GitLabの統合データモデルは、ソフトウェアライフサイクル全体を単一のコンテキストソースに接続し、企業がパイプライン、制御、メトリクスを大規模に標準化できるようにします。GitLabのAI主導機能における継続的なイノベーションは、企業がエージェント型ワークフローを採用して価値実現までの時間を短縮する中で、その長期的な関連性を強化します。\n\nGitLabとTCS社は、マルチエージェントオーケストレーション、動的プランニング、信頼度スコア付き意思決定、継続的学習サイクルを同期させて、コーディング、レビュー、テスト、セキュリティ、CI/CDワークフローを自動化します。\n\n[GitLab Duo Agent Platform](https://about.gitlab.com/gitlab-duo-agent-platform/)は、コンテキスト認識型の自律的アクション、マルチステップ推論、コードモダナイゼーション、セキュリティスキャン、フロー自動化を通じて、ソフトウェアライフサイクル全体でインテリジェントオーケストレーションを提供し、ソフトウェア開発を合理化・加速します。これは、IT運用のためのTCS社の構造化されたエージェント階層と自然に整合し、動的推論、プランニング、ドメインエージェントがMCP主導の統合と豊富なプロジェクトコンテキストフローを通じてGitLab Duo Platformの専門エージェント（プランナー、セキュリティアナリスト、コードレビューなど）を呼び出すことを可能にし、GitLabのAIネイティブDevSecOps制御によって管理されます。\n\n## プラットフォームエンジニアリングによるDevSecOpsのスケール\n\nプラットフォームエンジニアリングは、個々のパイプラインとツールチェーンの管理から、組織全体でソフトウェアの構築、保護、テスト、デプロイ方法を標準化する内部開発者プラットフォーム（IDP）の構築へと焦点を移します。\n\n 企業は、プラットフォームエンジニアリングを通じて開発者エクスペリエンスを製品化し、セルフサービスのゴールデンパスでIDPを運用することでスケールします。セキュリティ、コンプライアンス、ガバナンスは、policy-as-codeを通じてデフォルトで組み込まれ、Day 2運用を標準化します。GitLabはIDPコントロールプレーンとなり、TCS社はコントロールプレーン上のラッパーとしてセルフサービスの設計と展開を産業化し、強力な開発者エクスペリエンスを提供します。ソリューションアーキテクトとして、TCS社はセルフサービスパスを構築し、GitLab Duo Agent PlatformはSDLC全体で開発を自動化するエージェント型AIを追加します。\n\n| カテゴリ                       | 詳細                                                                                                    |\n| -------------------------- | ----------------------------------------------------------------------------------------------------- |\n| エクスペリエンスレイヤー（IDP）          | • 開発者セルフサービススキャフォールディング \u003Cbr> • ワンクリック環境/Runner/スキャン \u003Cbr> • 標準化されたオンボーディング                             |\n| プラットフォームコントロールプレーン（GitLab） | • 制御ポイントとしてのマージリクエスト \u003Cbr> • 統合CI/CD \u003Cbr> • セキュリティ \u003Cbr> • ソフトウェア部品表（SBOM） \u003Cbr> • 承認 \u003Cbr> • テレメトリ       |\n| ガードレールとガバナンス               | • ポリシーベースのガバナンス \u003Cbr> • コンプライアンス as code \u003Cbr> • リスク階層化されたゴールデンパス \u003Cbr> • 手動ゲートなしの必須制御                   |\n| インフラストラクチャとランタイム           | • クラウドランディングゾーン \u003Cbr> • Kubernetes/VMランタイム \u003Cbr> • GitOps主導の望ましい状態の強制                                   |\n| ゴールデンパス                    | • 製品が継続的に改善され、安全に拡張可能であることを保証 \u003Cbr> • 自律性を保持しながらパイプラインドリフトを排除                                          |\n| Day 2運用                    | • 自動ロールバック \u003Cbr> • リリースポリシーに関連付けられたランタイムSLO \u003Cbr> • 脆弱性SLA \u003Cbr> • コスト可視性 \u003Cbr> • プラットフォームに組み込まれた運用エクセレンス |\n\n## DevSecOpsからインテリジェントオーケストレーションへ\n\n統合DevSecOpsプラットフォームは企業に基盤を提供しますが、AIエージェントがソフトウェアライフサイクルの積極的な参加者になるにつれて、プラットフォームはコードとパイプラインの管理以上のことを行う必要があります。人間とAIエージェントの作業を、完全なライフサイクルコンテキストとフローに組み込まれたガードレールとともにオーケストレーションする必要があります。これが、GitLab Duo Agent Platformが可能にするDevSecOpsからインテリジェントオーケストレーションへの移行であり、時間の経過とともにソフトウェアデリバリーの品質を向上させます。\n\n### GitLab Duo Agent Platform\n\nGitLab Duo Agent Platformは、開発者と協力者として連携するAIエージェントをソフトウェア開発ライフサイクルに導入します。複数のAIエージェントが、コード生成やテストからCI/CD修正まで、タスクを並行して処理し、ボトルネックを削減してリリースを高速化します。開発者は定義されたルールを使用してこれらのエージェントを操縦・誘導し、反復的な作業をオフロードしながら制御を維持します。このエージェントオーケストレーションは、複雑なワークフロー（壊れたパイプラインの自動修正など）に取り組み、チームがより価値の高い作業に集中できるようにします。\n\nAIエージェントはGitLabの統合データモデル内で動作し、マージリクエストを作成し、コードを改善し、コンプライアンスをサポートすることで、生産性と速度を向上させます。すべてのエージェントアクションには完全なプロジェクトコンテキストがあり、監査可能で、ポリシーに準拠しているため、企業は数千人のエンジニアにわたってAIを自信を持ってスケールし、すべての自動化されたワークフローでセキュリティと規制コンプライアンスを維持できます。これにより、アプリケーションエンジニア、DevSecOpsエンジニア、スクラムマスター、プロダクトマネージャーの負担が軽減されます。\n\n## リファレンスアーキテクチャの理解\n\n![GitLab TCSリファレンスアーキテクチャ](https://res.cloudinary.com/about-gitlab-com/image/upload/v1771866349/ynfgc7ugqjasyj1uhew0.png)\n\n## GitLab + TCS：強力な組み合わせ\n\n GitLabは、ソフトウェアチームとAIエージェントが開発ライフサイクル全体で連携するDevSecOpsのインテリジェントオーケストレーションプラットフォームを提供します。TCS社は、産業化された導入エンジン、実証済みのリファレンスアーキテクチャ、大規模移行ファクトリー、エンタープライズグレードのセキュリティベースライン、エンタープライズAI機能、AI Trust & Riskマネジメントツールとフレームワーク、プラットフォーム運用のプロダクトマインドセットを提供します。\n\nこの組み合わせを真に差別化するのは、業界、地域、規制環境を超えて数十年にわたってお客様と協力してきたTCS社の文脈的知識です。この経験により、TCS社はレガシー資産、コンプライアンス要件、運用モデル、スケールの課題などの企業制約に対処するためにGitLab機能を文脈化することができます。これは、ツールを単独で実装するのではなく、企業の制約を考慮したアプローチです。GitLabとTCS社は共に、組み込まれたコンプライアンスを備えたクラウド全体での迅速で確実なエンタープライズスケールデリバリーを可能にします。\n\n> GitLab + TCSの詳細については、ecosystem@gitlab.comまでお問い合わせください。",[27,681],"product","2026-02-26",{"featured":12,"template":13,"slug":684},"agentic-sdlc-gitlab-and-tcs-deliver-intelligent-orchestration-across-the-enterprise",{"content":686,"config":696},{"title":687,"description":688,"authors":689,"heroImage":691,"date":692,"body":693,"category":9,"tags":694},"エージェント型AIを自社の制御下で：セルフホスト対応Duo Agent Platformと自社モデル持ち込み（BYOM）","GitLab 18.9がセルフホスト対応Duo Agent Platformと自社モデル持ち込み（Bring Your Own Model）サポートにより、規制対応エンタープライズにガバナンスを備えたエージェント型AIを提供する方法をご紹介します。",[690],"Rebecca Carter","https://res.cloudinary.com/about-gitlab-com/image/upload/v1771438388/t6sts5qw4z8561gtlxiq.png","2026-02-19","規制産業に属する組織がAIによる自動化を進める上では、避けられない制約があります。データレジデンシー、ベンダー管理、ガバナンスはいずれも譲れない要件であり、多くの組織はすでに自社モデルに多大な投資を重ね、その運用方法と運用場所を厳格な承認プロセスで管理しています。\n\n[GitLab 18.9](https://about.gitlab.com/ja-jp/blog/gitlab-18-09-release/)では、こうしたエンタープライズのお客様が直面する重要な戦略的ギャップを埋める2つの機能を提供します。[GitLab Duo Agent Platform](https://about.gitlab.com/ja-jp/gitlab-duo-agent-platform/)を、最も厳格な規制環境でも本番運用できる、ガバナンス対応のAIコントロールプレーンへと進化させる機能です。\n\n## オンライン クラウドライセンス向けGitLab Duo Agent Platformセルフホスト版\nGitLab Duo Agent Platformを活用すると、エンジニアリングチームはAI駆動のフローを構築し、サービスのリファクタリングやCI/CDパイプラインの強化、脆弱性のトリアージといった一連のタスクを自動化できます。しかしこれまで、セルフホストモデルを使った本番環境での利用は、主にオフラインまたはアドオンライセンスを前提とした構成に限られており、厳格な規制下で事業を行うオンライン クラウド ライセンスのお客様には対応していませんでした。\n\nこのたび一般提供を開始した[オンライン クラウドライセンス向けセルフホスト版](https://docs.gitlab.com/ja-jp/subscriptions/subscription-add-ons/#gitlab-duo-agent-platform-self-hosted)は、[GitLabクレジット](https://docs.gitlab.com/ja-jp/subscriptions/gitlab_credits/)を基盤とした従量課金モデルを採用。エンタープライズが内部チャージバックや信頼性確保に求める、透明性の高い予測可能なメタリングを実現します。\n* **データレジデンシーと制御**：オンライン クラウドライセンスのまま、自社インフラまたは承認済みクラウド環境上のモデルを使って本番稼働が可能です。モデルの実行場所や推論トラフィックのルーティングを、承認済み環境の範囲内で管理できます。\n* **コストの透明性とチャージバック**：GitLabクレジットとリクエスト単位のメタリングにより、詳細なコスト把握が可能になります。正確な内部チャージバックや規制対応のレポーティングに不可欠な仕組みです。\n* **導入加速**：金融サービス、政府機関、重要インフラなど、外部AIベンダーへのデータ送信が認められないセクターにおける、エージェント型AI導入の大きな障壁を取り除きます。\nGitLab 18.9から、Duo Agent Platformがオンライン クラウドライセンスの正式対応機能となります。\n\n## Bring Your Own Model（BYOM）/自分のモデルを持ち込む\nオーケストレーションレイヤーのセルフホスト化は、あくまでも出発点です。規制対応を求められるお客様の多くは、すでに自社モデルに相当な投資をしています。ドメイン特化型のLLM、データ主権のためのリージョン内またはエアギャップ環境へのデプロイ、自社のリスク方針に基づいて開発されたクローズドソースの社内モデルなど、その形は様々です。\n\n**Bring Your Own Model**（BYOM）は、GitLab Duo Agent Platformの柔軟性をさらに拡げる機能です。管理者は[GitLab AIゲートウェイ](https://docs.gitlab.com/ja-jp/administration/gitlab_duo/gateway/)を通じて、自社で開発、調達、承認したモデルをそのままGitLab Duo Agent Platformに接続して活用できます。外部ベンダーのモデルに依存することなく、モデルの選択と管理権限は完全にお客様の手に委ねられます。\n* **統合とガバナンス**：BYOMモデル（お客様が持ち込んだモデル）は、GitLabのAIコントロールプレーン上でGitLab管理モデルと同等のエンタープライズ対応オプションとして扱われます。自社承認済みのモデルを、GitLab環境にシームレスに組み込むことができます。\n* **詳細なマッピング**：AIゲートウェイへの登録後、各モデルを特定のDuo Agent Platformフローや機能に紐づけることができます。どのエージェントやフローがどのモデルを使うか、きめ細かな制御が可能です。\nなお、モデルの検証、パフォーマンス評価、リスク管理の責任は管理者が担います。互換性、性能、リスク評価は、導入するモデルを選んだ側の責任となります。\n\nこれら2つの機能を組み合わせることで、エンタープライズのエンジニアリングリーダーはエージェント型AIを包括的に制御できるようになります。乱立するポイントソリューションや、管理不在のいわば野放しのAIツールを置き換え、エージェント型AIを一元管理する単一のガバナンス対応コントロールプレーンが手に入ります。自社で開発、調達、承認したモデルをそのまま持ち込みながら、強固なガバナンスも確保できる、すでに信頼しているDevSecOpsプラットフォームの中でそれを実現できる、これが規制対応組織の求めていた答えです。\n\n> GitLab Duo Agent Platformにご興味のある方は、[お問い合わせいただくか、今すぐ無料トライアルにお申し込みください](https://about.gitlab.com/ja-jp/gitlab-duo-agent-platform/)。\n\n-----------\n\n_本ブログには、1933年証券法第27A条（改正版）および1934年証券取引所法第21E条に定める「将来の見通しに関する記述」が含まれています。これらの記述に反映されている期待は合理的と考えていますが、実際の結果や成果が大きく異なる可能性のある既知/未知のリスク、不確実性、前提条件、その他の要因が存在します。これらのリスクおよびその他の要因の詳細は、SECへの提出書類における「リスク要因」の項目をご参照ください。法律で義務付けられている場合を除き、本ブログ投稿の公開日以降にこれらの記述を更新または修正する義務を負いません。_",[27,681,695],"features",{"featured":31,"template":13,"slug":697},"agentic-ai-enterprise-control-self-hosted-duo-agent-platform-and-byom",{"content":699,"config":708},{"title":700,"description":701,"authors":702,"heroImage":704,"date":705,"body":706,"category":9,"tags":707},"GitLab Duo Agent Platformの正式版リリースを発表","ソフトウェアライフサイクル全体におけるエージェント型AIが、コーディングの高速化をイノベーションサイクルの加速へと変える仕組みをご紹介します。",[703],"Bill Staples","https://res.cloudinary.com/about-gitlab-com/image/upload/v1768314192/llizjeumcduj2enqpdi4.png","2026-01-15","GitLab Duo Agent Platformの正式版リリースを発表できることを大変うれしく思います。これはGitLab、お客様、そして業界全体にとって重要な節目です。ソフトウェア開発ライフサイクル全体にエージェント型AIを導入するというビジョンを実現する最初の一歩となります。\n\nAIツールは、デベロッパーがコードを書く能力を急速に向上させており、場合によっては生産性が10倍向上したという報告もあります。しかし残念ながら、デベロッパーの時間のうちコードを書くことに費やされるのは約20%に過ぎないため、AIによって得られるイノベーション速度と提供の全体的な向上は段階的なものにとどまります。これはソフトウェア提供における[AIパラドックス](https://about.gitlab.com/press/releases/2025-11-10-gitlab-survey-reveals-the-ai-paradox/)としてよく知られています。\n\nさらに、多くのチームでは、コード作成の速度が上がったことで、コードレビュー、セキュリティ脆弱性、コンプライアンスチェック、下流のバグ修正など、新たなボトルネックが生じています。\n\nGitLab Duo Agent Platformは、ソフトウェアライフサイクル全体でインテリジェントなオーケストレーションとエージェント型AI自動化を実現することで、AIパラドックスに対処します。\n\n詳しくは以下の動画をご覧いただき、さらに詳細は下記をお読みください。\n\n\u003Ciframe src=\"https://player.vimeo.com/video/1154785472?badge=0&amp;autopause=0&amp;player_id=0&amp;app_id=58479\" frameborder=\"0\" allow=\"autoplay; fullscreen; picture-in-picture; clipboard-write; encrypted-media; web-share\" referrerpolicy=\"strict-origin-when-cross-origin\" style=\"position:absolute;top:0;left:0;width:100%;height:100%;\" title=\"18.8 Release Video V2\">\u003C/iframe>\u003Cscript src=\"https://player.vimeo.com/api/player.js\">\u003C/script>\n\n> :bulb: 2月10日のGitLab Transcendにご参加いただき、エージェント型AIがソフトウェア提供をどのように変革するかをご覧ください。お客様の事例を聞き、独自のモダナイゼーションの取り組みを開始する方法をご確認ください。[今すぐ登録](https://about.gitlab.com/events/transcend/virtual/)\n\nまた、GitLab PremiumおよびUltimateサブスクリプションをご利用中のお客様に、追加費用なしでユーザーあたりそれぞれ12ドルおよび24ドルのGitLabクレジットを付与いたします。\\*これらのクレジットは毎月更新され、GitLab Duo Agent Platformのすべての機能をご利用いただけます。\n\nGitLabクレジットの仕組みを簡単に説明します。GitLabクレジットは、GitLabの使用量ベースの製品に使用される仮想通貨です。GitLab Duo Agent Platformの使用量は、上記の含まれるクレジットから消費されます。その後、組織全体で共有クレジットプールへのコミットメントを決定するか、オンデマンドで月単位でお支払いいただくことができます。詳細については、[GitLabクレジットの紹介記事](https://about.gitlab.com/blog/introducing-gitlab-credits/)をご覧ください。\n\nGitLab Duo ProまたはDuo Enterpriseサブスクリプションをご利用のお客様は、引き続きこれらの製品をご利用いただくか、いつでもDuo Agent Platformに移行していただけます。Duo Enterpriseの契約残高は、いつでもGitLabクレジットに変換できます。詳細については、GitLab担当者にお問い合わせください。\n\n本日からお試しいただける機能とユースケースをご紹介します。\n\n### 人間とエージェントが協働するための統合エクスペリエンス\n\n[GitLab Duo Agent Platform](https://docs.gitlab.com/ja-jp/user/duo_agent_platform/?utm_source=chatgpt.com)は、人間とAIエージェントがGitLab内でシームレスに統合できるように設計された、統一されたユーザーエクスペリエンスを提供します。デベロッパーとそのチームは、ほぼすべてのページでDuo Agentic Chatを利用し、コンテキストに応じた質問をしたり、非同期エージェントセッションをフォローしたり、イシュー、マージリクエスト、パイプラインアクティビティなどの使い慣れたワークフロー内でエージェントとやり取りしたりできます。これにより、AIのアクションが透明になり、日常業務を通じて簡単にガイドできるようになります。\n\n### Agentic Chat:インテリジェントでコンテキスト認識型のアシスタンス\n\n[Gitlab Duo Agentic Chat](https://docs.gitlab.com/ja-jp/user/gitlab_duo_chat/agentic_chat/)は、GitLab Web UIおよびIDEで真のマルチステップ推論を実現し、イシュー、マージリクエスト、パイプライン、セキュリティ調査結果などからのライフサイクル全体のコンテキストを活用します。以前リリースされたDuo Chatをベースに、Agentic Chatはユーザーに代わって自律的にアクションを実行し、複雑な質問により包括的に答えることができます。ソフトウェアチームの各メンバーに、オンボーディング、コード品質、提供速度の向上に役立つ、正確でコンテキスト認識型のガイダンスを提供します。\n\nGitLab Duo Agentic Chatは、デベロッパーとAIの協働を可能にする数多くの[ユースケース](https://about.gitlab.com/gitlab-duo/prompt-library/)をサポートしています。開始方法の詳細については、[「GitLab Duo Agent Platform完全ガイド」](https://about.gitlab.com/blog/gitlab-duo-agent-platform-complete-getting-started-guide/)をご覧いただき、こちらの[拡張中の推奨プロンプト集](https://about.gitlab.com/gitlab-duo/prompt-library/)もご確認ください。\n\n* **分析**\n  Web UIでは、Agentic Chatはイシュー、エピック、マージリクエストを作成し、要約を提供し、重要な発見を強調し、特定のプロジェクト、イシュー、エピック、マージリクエストなどからのリアルタイムコンテキストに基づいた実用的なガイダンスを提供できます。Agentic Chatは、デベロッパーがIDEまたはGitLabリポジトリ内で、馴染みのないコード、依存関係、アーキテクチャ、プロジェクト構造を理解するのに役立ちます。\n\n\n* **コード**\n  Agentic Chatは、幅広い言語とフレームワークにわたって、コード、設定、Infrastructure as Codeを生成できます。バグの修正、アーキテクチャとコードのモダナイゼーション、テストの生成、より迅速なオンボーディングのためのドキュメント作成を支援します。デベロッパーの手元で、Agentic ChatはVS Code、JetBrains IDE、Cursor、Windsurfにおける協働パートナーであり、オプションのユーザーレベルおよびワークスペースレベルのルールで応答をカスタマイズできます。\n\n* **CI/CD**\n  Agentic Chatは、既存のパイプラインをより深く理解し、設定し、トラブルシューティングしたり、新しいパイプラインをゼロから作成したりするのに役立ちます。\n\n* **セキュリティ**\n  Agentic Chatは、脆弱性を説明し、到達可能性に基づいて問題に優先順位を付け、時間を節約できる修正を推奨します。\n\n## エージェント:オンデマンドで協働する専門家\n\nGitLab Duo Agent Platformにより、デベロッパーは専門のエージェントにタスクを委任できます。このプラットフォームは、基本エージェント、カスタムエージェント、外部エージェントの独自の組み合わせを提供し、すべてGitLabユーザーエクスペリエンスにシームレスに統合されているため、あらゆるタスクに適したエージェントを簡単に選択できます。\n\n**[基本エージェント](https://docs.gitlab.com/ja-jp/user/duo_agent_platform/agents/foundational_agents/)**は、GitLabの専門家によって事前に構築されており、ソフトウェア提供サイクルで最も複雑なタスクを処理する準備が整っています。以下の基本エージェントは、GitLab Duo Agent Platformの正式版の一部として含まれており、その他のエージェントは現在ベータ版で、近日中に提供予定です。\n\n* [**プランナーエージェント**](https://docs.gitlab.com/ja-jp/user/duo_agent_platform/agents/foundational_agents/planner/)は、チームがGitLab内で直接作業を構造化、優先順位付け、分割するのを支援し、計画をより明確に、より迅速に、より実行しやすくします。\n\n* [**セキュリティ分析エージェント**](https://docs.gitlab.com/ja-jp/user/duo_agent_platform/agents/foundational_agents/security_analyst_agent/)は、脆弱性とセキュリティシグナルをレビューし、その影響を平易な言葉で説明し、チームが最初に対処すべきことを理解するのを支援します。\n\n[**カスタムエージェント**](https://docs.gitlab.com/ja-jp/user/duo_agent_platform/ai_catalog/)は、AIカタログを使用して構築できます。AIカタログは、チームがカスタムエージェントとフローを作成、公開、管理、共有するための中央リポジトリです。チームは、エンジニアリングチームの働き方を再現し、エンジニアが使用するエンジニアリング基準とガバナンスの仕組みを使用して問題に取り組むために、特定のコンテキストと機能を持つエージェントとフローを作成できます。\n\n[**外部エージェント**](https://docs.gitlab.com/ja-jp/user/duo_agent_platform/agents/external/)は、GitLabにシームレスに統合されており、AnthropicのClaude CodeやOpenAIのCodex CLIなど、最高クラスのAIツールが含まれています。ユーザーは、コード生成、コードレビュー、分析などのユースケースで、透明性の高いセキュリティと組み込みLLMサブスクリプションにより、これらのツールをGitLabからネイティブにアクセスできます。\n\nこれらのアプローチにより、チームは専門エージェント、組織固有の自動化、外部AIツールの統合など、エージェント型AIをどのように採用するかについて柔軟性を得ることができます。すべて単一のガバナンスされたプラットフォーム内で実現します。\n\n## フロー:複数ステップの作業を反復可能でガイド付きの進捗へ\n\nフローは、複数のエージェントワークフローを使用して複雑なタスクを最初から最後まで自動化します。\n\nエンジニアリングチームは、正式版に含まれるいくつかのフローを構築しており、さらに多くのフローが提供予定です。\n\n* [**デベロッパー(Issue to MR)**](https://docs.gitlab.com/ja-jp/user/duo_agent_platform/flows/foundational_flows/issue_to_mr/)フローは、明確に定義されたイシューから構造化されたマージリクエストを作成し、チームがすぐに作業を開始できるようにします。\n\n* [**GitLab CI/CD変換**](https://docs.gitlab.com/ja-jp/user/duo_agent_platform/flows/foundational_flows/convert_to_gitlab_ci/)フローは、チームが手動で書き直すことなくパイプライン設定を移行またはモダナイズするのに役立ちます。\n\n* [**CI/CDパイプライン修復**](https://docs.gitlab.com/ja-jp/user/duo_agent_platform/flows/foundational_flows/fix_pipeline/)フローは、障害を分析し、考えられる原因を特定し、推奨される変更を準備します。\n\n* [**コードレビュー**](https://docs.gitlab.com/ja-jp/user/duo_agent_platform/flows/foundational_flows/code_review/)フローは、コード変更、マージリクエストコメントなどを分析し、AIネイティブの分析とフィードバックでコードレビューを効率化します。\n\n* [**ソフトウェア開発**](https://docs.gitlab.com/ja-jp/user/duo_agent_platform/flows/foundational_flows/software_development/)フローは、日常的な開発とレビュー段階を通じて作業をガイドします。\n\n## MCP Client:GitLab Duo Agent Platformをチームが既に使用しているツールに接続\n\n[MCPクライアント](https://docs.gitlab.com/ja-jp/user/gitlab_duo/model_context_protocol/mcp_clients/)により、IDE内のGitLab Duo Agent PlatformがJira、Slack、Confluence、その他のMCP対応ツールなどの外部システムに安全に接続し、コンテキストを取り込み、DevSecOpsツールチェーン全体でアクションを実行できるようになります。\n\n個々のツール内でAIアシスタンスがサイロ化されるのではなく、MCPクライアントはGitLab Duo Agent Platformが、計画、協働、実行が実際に行われるシステム全体を理解し、動作することを可能にします。これにより、手動でのコンテキスト切り替えが削減され、チームが実際に作業する方法を反映した、より完全なエンドツーエンドのAI駆動ワークフローが実現します。\n\n正式版に含まれる機能:\n\n* Jira、Confluence、Slack、Playwright、GrafanaなどのMCP対応外部システムへの接続\n* ワークスペースレベルおよびユーザーレベルでの設定\n* MCPの使用を有効化または制限するためのグループレベルの制御\n* ツールアクセスのユーザー承認フロー\n* IDE拡張機能でのAgentic Chat全体でのサポート\n\n現在ベータ版のGitLab MCPサーバー機能にさらに多くの機能を追加し、今後のリリースで正式版にする予定です。\n\n## チームとワークロードに適したモデルを選択\n\nGitLab Duo Agent Platformは、チームがプライバシー、セキュリティ、コンプライアンスのニーズに合わせてプラットフォームを調整できる柔軟なモデル選択フレームワークに基づいて構築されています。GitLabは各機能に最適なLLMをデフォルトで設定していますが、管理者はOpenAI GPT-5バリアント、Mistral、Meta Llama、Anthropic Claudeなどのサポートされているモデルから選択することもできます。これにより、チームは組織の基準に基づいて、各特定のユースケースでチャット、コーディングタスク、エージェントインタラクションに何を使用するかについて、より正確な制御と柔軟性を得ることができます。サポートされているモデルの完全なリストとモデル選択設定の詳細については、ドキュメントの[モデル選択](https://docs.gitlab.com/ja-jp/administration/gitlab_duo/model_selection/)セクションをご覧ください。\n\n### ガバナンス、可視性、デプロイの柔軟性\n\nGitLab Duo Agent Platformは、組織がAIを責任を持って採用するために必要な制御と透明性を提供すると同時に、さまざまな環境で機能する柔軟なデプロイオプションを提供します。\n\n正式版に含まれる機能:\n\n* **すべてのプラットフォームで利用可能:**GitLab.com、GitLab Self-Managed、GitLab DedicatedでGitLab 18.8リリースサイクルの一部として利用できます。\n\n* **ガバナンスと可視性:**チームは、エージェントがどのように使用され、どのようなアクションを実行し、作業にどのように貢献しているかを確認できます。使用状況とアクティビティの詳細により、リーダーは採用状況を把握し、影響を測定し、AIが適切に使用されていることを確認できます。これらの制御により、自信を持って大規模にAIを展開しやすくなります。\n\n* **グループベースのアクセス制御:**管理者は、どのユーザーがGitLab Duo Agent Platform機能にアクセスできるかを管理する名前空間レベルのルールを定義でき、組織全体での即時有効化から段階的なロールアウトまで、柔軟な採用をサポートします。LDAPおよびSAML統合により、手動設定なしで大規模なガバナンスを実現できます。\n\n* **モデル選択とセルフホスト型オプション:**LLM選択は、GitLab.com、Self-Managed、Dedicatedのすべての正式版機能で利用できます。トップレベルの名前空間所有者がモデルを選択し、サブグループはそれらの設定を自動的に継承します。より多くの制御が必要な組織のために、プラットフォームはGitLab Self-Managedデプロイメント向けのセルフホスト型モデルをサポートしています。\n\nGitLab Duo Agent Platformの実際の動作をご覧ください:\n\n\u003Ciframe src=\"https://player.vimeo.com/video/1154786333?badge=0&amp;autopause=0&amp;player_id=0&amp;app_id=58479\" frameborder=\"0\" allow=\"autoplay; fullscreen; picture-in-picture; clipboard-write; encrypted-media; web-share\" referrerpolicy=\"strict-origin-when-cross-origin\" style=\"position:absolute;top:0;left:0;width:100%;height:100%;\" title=\"18.8 Demo\">\u003C/iframe>\u003Cscript src=\"https://player.vimeo.com/api/player.js\">\u003C/script>\n\n## GitLabの最新情報を入手\n\n最新の機能、セキュリティアップデート、パフォーマンス向上を確実に入手するために、GitLabインスタンスを最新の状態に保つことをお勧めします。以下のリソースは、アップグレードの計画と完了に役立ちます。\n\n* [アップグレードパスツール](https://gitlab-com.gitlab.io/support/toolbox/upgrade-path/) – 現在のバージョンを入力すると、インスタンスの正確なアップグレード手順が表示されます\n* [アップグレードに関するドキュメント](https://docs.gitlab.com/ja-jp/update/upgrade_paths/) – サポートされている各バージョンの詳細なガイド(要件、ステップバイステップの手順、ベストプラクティスを含む)\n\n定期的にアップグレードすることで、チームが最新のGitLab機能を活用し、安全でサポートされた状態を維持できます。\n\nハンズオフアプローチをお望みの組織には、[GitLabのManaged Maintenanceサービス](https://content.gitlab.com/viewer/d1fe944dddb06394e6187f0028f010ad#1)をご検討ください。Managed Maintenanceは、チームがイノベーションに集中し続ける一方で、GitLabの専門家がSelf-Managedインスタンスの確実なアップグレード、セキュリティ確保、DevSecOpsリーダーシップの準備を維持するのを支援します。詳細については、アカウントマネージャーにお問い合わせください。\n\n---\n\n\\* GitLab PremiumおよびUltimateサブスクリプションをご利用中のお客様には、ユーザーあたりそれぞれ12ドルおよび24ドルの含まれるクレジットが自動的に提供され、毎月リセットされます。これらのクレジットは期間限定で提供され、変更される可能性があります([プロモーション規約を参照](https://about.gitlab.com/pricing/terms/))。\n\n*このブログ投稿には、改正1933年証券法第27A条および1934年証券取引法第21E条の意味における「将来予想に関する記述」が含まれています。これらの記述に反映された期待は合理的であると考えていますが、実際の結果または成果が大きく異なる可能性のある既知および未知のリスク、不確実性、仮定、その他の要因の影響を受けます。これらのリスクおよびその他の要因の詳細については、SECへの提出書類の「リスク要因」の項をご参照ください。このブログ投稿の日付以降、法律で義務付けられている場合を除き、これらの記述を更新または改訂する義務は負いません。*",[27,681,695],{"featured":31,"template":13,"slug":709},"gitlab-duo-agent-platform-is-generally-available",{"promotions":711},[712,725,736],{"id":713,"categories":714,"header":715,"text":716,"button":717,"image":722},"ai-modernization",[9],"Is AI achieving its promise at scale?","Quiz will take 5 minutes or less",{"text":718,"config":719},"Get your AI maturity score",{"href":720,"dataGaName":721,"dataGaLocation":247},"/assessments/ai-modernization-assessment/","modernization assessment",{"config":723},{"src":724},"https://res.cloudinary.com/about-gitlab-com/image/upload/v1772138786/qix0m7kwnd8x2fh1zq49.png",{"id":726,"categories":727,"header":728,"text":716,"button":729,"image":733},"devops-modernization",[681,39],"Are you just managing tools or shipping innovation?",{"text":730,"config":731},"Get your DevOps maturity score",{"href":732,"dataGaName":721,"dataGaLocation":247},"/assessments/devops-modernization-assessment/",{"config":734},{"src":735},"https://res.cloudinary.com/about-gitlab-com/image/upload/v1772138785/eg818fmakweyuznttgid.png",{"id":737,"categories":738,"header":740,"text":716,"button":741,"image":745},"security-modernization",[739],"security","Are you trading speed for security?",{"text":742,"config":743},"Get your security maturity score",{"href":744,"dataGaName":721,"dataGaLocation":247},"/assessments/security-modernization-assessment/",{"config":746},{"src":747},"https://res.cloudinary.com/about-gitlab-com/image/upload/v1772138786/p4pbqd9nnjejg5ds6mdk.png",{"header":749,"blurb":750,"button":751,"secondaryButton":755},"今すぐ開発をスピードアップ","DevSecOpsに特化したインテリジェントオーケストレーションプラットフォームで実現できることをご確認ください。\n",{"text":50,"config":752},{"href":753,"dataGaName":53,"dataGaLocation":754},"https://gitlab.com/-/trial_registrations/new?glm_content=default-saas-trial&glm_source=about.gitlab.com/","feature",{"text":55,"config":756},{"href":57,"dataGaName":58,"dataGaLocation":754},1772652102115]