{"id":255047,"date":"2025-12-02T07:00:16","date_gmt":"2025-12-02T15:00:16","guid":{"rendered":"https:\/\/devblogs.microsoft.com\/visualstudio\/?p=255047"},"modified":"2025-12-03T12:08:19","modified_gmt":"2025-12-03T20:08:19","slug":"delegate-the-analysis-not-the-performance","status":"publish","type":"post","link":"https:\/\/devblogs.microsoft.com\/visualstudio\/delegate-the-analysis-not-the-performance\/","title":{"rendered":"Profiler Agent &#8211; Delegate the analysis, not the performance"},"content":{"rendered":"<p><span class=\"TextRun SCXW184313651 BCX8\" lang=\"EN-US\" xml:lang=\"EN-US\" data-contrast=\"auto\"><span class=\"NormalTextRun SCXW184313651 BCX8\">In Visual Studio <\/span><span class=\"NormalTextRun SCXW184313651 BCX8\">2026<\/span><span class=\"NormalTextRun SCXW184313651 BCX8\"> we introduced Copilot Profiler Agent, a new AI-powered assistant that helps you analyze and optimize performance bottlenecks in your code. By combining the power of GitHub Copilot with Visual Studio&#8217;s performance profiler, you can now ask natural language questions about <\/span><span class=\"NormalTextRun SCXW184313651 BCX8\">performance<\/span><span class=\"NormalTextRun SCXW184313651 BCX8\">, get insights into hot paths, and quickly <\/span><span class=\"NormalTextRun SCXW184313651 BCX8\">identify<\/span><span class=\"NormalTextRun SCXW184313651 BCX8\"> optimization opportunities. <\/span><span class=\"NormalTextRun SCXW184313651 BCX8\">Let&#8217;s<\/span><span class=\"NormalTextRun SCXW184313651 BCX8\"> walk through a real-world example of how this tool can help you make meaningful performance improvements.<\/span><\/span><span class=\"EOP SCXW184313651 BCX8\" data-ccp-props=\"{}\">\u00a0<\/span><\/p>\n<h2>Benchmarking a real project<\/h2>\n<p><span data-contrast=\"auto\">To demonstrate the capabilities of the Copilot Profiler Agent, let&#8217;s optimize <a href=\"https:\/\/joshclose.github.io\/CsvHelper\/\">CsvHelper<\/a>, a popular open-source project. You can follow along by cloning <a href=\"https:\/\/github.com\/karpinsn\/CsvHelper.git\">my fork of the repo<\/a><\/span><span data-contrast=\"auto\"> then checking out to right before my fix that we will detail below with <code>git checkout 435ff7c<\/code>. If you just want to see it in action you can watch along in our <a href=\"https:\/\/www.youtube.com\/live\/t0j6R4p_uTA?t=471s\">toolbox episode<\/a> demonstrating this.<\/span><\/p>\n<p><span data-contrast=\"auto\">In one of my <a href=\"https:\/\/devblogs.microsoft.com\/visualstudio\/benchmarking-with-visual-studio-profiler\/\">previous blog posts<\/a><\/span><span data-contrast=\"auto\"> I added a CsvHelper.Benchmarks project that contains a benchmark for reading CSV records. This time I want to see if we can optimize writing CSV records instead. Normally I would start this investigation by creating a benchmark for the code that I want to optimize, and while we will still do that we can have Copilot do the toil work for us. In the Copilot Chat window I can ask <code>@Profiler Help me write a benchmark for the #WriteRecords method<\/code>. The <code>@Profiler<\/code> gets us talking directly with the Copilot Profiler agent and <code>#WriteRecords<\/code> tells it exactly the method we are interested in benchmarking.<\/span><span data-ccp-props=\"{}\"> <a href=\"https:\/\/devblogs.microsoft.com\/visualstudio\/wp-content\/uploads\/sites\/4\/2025\/12\/CopilotProfiler1Full.webp\"><img decoding=\"async\" class=\"alignnone size-full wp-image-255066\" src=\"https:\/\/devblogs.microsoft.com\/visualstudio\/wp-content\/uploads\/sites\/4\/2025\/12\/CopilotProfiler1Full.webp\" alt=\"CopilotProfiler1Full image\" width=\"1600\" height=\"900\" srcset=\"https:\/\/devblogs.microsoft.com\/visualstudio\/wp-content\/uploads\/sites\/4\/2025\/12\/CopilotProfiler1Full.webp 1600w, https:\/\/devblogs.microsoft.com\/visualstudio\/wp-content\/uploads\/sites\/4\/2025\/12\/CopilotProfiler1Full-300x169.webp 300w, https:\/\/devblogs.microsoft.com\/visualstudio\/wp-content\/uploads\/sites\/4\/2025\/12\/CopilotProfiler1Full-1024x576.webp 1024w, https:\/\/devblogs.microsoft.com\/visualstudio\/wp-content\/uploads\/sites\/4\/2025\/12\/CopilotProfiler1Full-768x432.webp 768w, https:\/\/devblogs.microsoft.com\/visualstudio\/wp-content\/uploads\/sites\/4\/2025\/12\/CopilotProfiler1Full-1536x864.webp 1536w\" sizes=\"(max-width: 1600px) 100vw, 1600px\" \/><\/a><\/span><\/p>\n<p>From here Copilot starts creating our new benchmark, asking us if its ok to install the <a href=\"https:\/\/www.nuget.org\/packages\/Microsoft.VisualStudio.DiagnosticsHub.BenchmarkDotNetDiagnosers\">profiler\u2019s NuGet package<\/a>\u00a0to pull information from the benchmarks when it runs it. It also models the benchmarks after any existing benchmarks that it finds so the resulting benchmark is very similar to the one we already wrote keeping things consistent with the style of the repository. Lastly, it kicks off a build to make sure everything is good.<\/p>\n<p><a href=\"https:\/\/devblogs.microsoft.com\/visualstudio\/wp-content\/uploads\/sites\/4\/2025\/12\/CopilotProfiler2Full.webp\"><img decoding=\"async\" class=\"alignnone size-full wp-image-255067\" src=\"https:\/\/devblogs.microsoft.com\/visualstudio\/wp-content\/uploads\/sites\/4\/2025\/12\/CopilotProfiler2Full.webp\" alt=\"CopilotProfiler2Full image\" width=\"1600\" height=\"900\" srcset=\"https:\/\/devblogs.microsoft.com\/visualstudio\/wp-content\/uploads\/sites\/4\/2025\/12\/CopilotProfiler2Full.webp 1600w, https:\/\/devblogs.microsoft.com\/visualstudio\/wp-content\/uploads\/sites\/4\/2025\/12\/CopilotProfiler2Full-300x169.webp 300w, https:\/\/devblogs.microsoft.com\/visualstudio\/wp-content\/uploads\/sites\/4\/2025\/12\/CopilotProfiler2Full-1024x576.webp 1024w, https:\/\/devblogs.microsoft.com\/visualstudio\/wp-content\/uploads\/sites\/4\/2025\/12\/CopilotProfiler2Full-768x432.webp 768w, https:\/\/devblogs.microsoft.com\/visualstudio\/wp-content\/uploads\/sites\/4\/2025\/12\/CopilotProfiler2Full-1536x864.webp 1536w\" sizes=\"(max-width: 1600px) 100vw, 1600px\" \/><\/a><\/p>\n<p>Once it&#8217;s done, it provides some useful follow-up prompts to start the investigation. We could click one of these to launch into our investigation, though I want to edit things slightly in the benchmark.<\/p>\n<p><a href=\"https:\/\/devblogs.microsoft.com\/visualstudio\/wp-content\/uploads\/sites\/4\/2025\/12\/CopilotProfiler3Full.webp\"><img decoding=\"async\" class=\"alignnone size-full wp-image-255068\" src=\"https:\/\/devblogs.microsoft.com\/visualstudio\/wp-content\/uploads\/sites\/4\/2025\/12\/CopilotProfiler3Full.webp\" alt=\"CopilotProfiler3Full image\" width=\"1600\" height=\"900\" srcset=\"https:\/\/devblogs.microsoft.com\/visualstudio\/wp-content\/uploads\/sites\/4\/2025\/12\/CopilotProfiler3Full.webp 1600w, https:\/\/devblogs.microsoft.com\/visualstudio\/wp-content\/uploads\/sites\/4\/2025\/12\/CopilotProfiler3Full-300x169.webp 300w, https:\/\/devblogs.microsoft.com\/visualstudio\/wp-content\/uploads\/sites\/4\/2025\/12\/CopilotProfiler3Full-1024x576.webp 1024w, https:\/\/devblogs.microsoft.com\/visualstudio\/wp-content\/uploads\/sites\/4\/2025\/12\/CopilotProfiler3Full-768x432.webp 768w, https:\/\/devblogs.microsoft.com\/visualstudio\/wp-content\/uploads\/sites\/4\/2025\/12\/CopilotProfiler3Full-1536x864.webp 1536w\" sizes=\"(max-width: 1600px) 100vw, 1600px\" \/><\/a><\/p>\n<p>I tweaked the benchmark to have a few more fields for us to write, in this case 2 int fields and 2 string fields. When I originally had Copilot do this, before writing it up for this blog, instead of writing to the same memory stream it wrote to a new one each time. Writing into the same memory stream is probably the better way to go about things, you win this time Copilot, but in my original PR to CsvHelper I didn\u2019t and it should be fine.<\/p>\n<pre class=\"prettyprint language-cs language-csharp\"><code class=\"language-cs language-csharp\">public class BenchmarkWriteCsv \r\n{ \r\n\u202f \u202f private const int entryCount = 10000; \r\n\u202f \u202f private readonly List records = new(entryCount); \r\n\r\n\u202f \u202f public class Simple \r\n\u202f \u202f { \r\n\u202f \u202f \u202f \u202f public int Id1 { get; set; } \r\n\u202f \u202f \u202f \u202f public int Id2 { get; set; } \r\n\u202f \u202f \u202f \u202f public string Name1 { get; set; } \r\n\u202f \u202f \u202f \u202f public string Name2 { get; set; } \r\n\u202f \u202f } \r\n\r\n\u202f \u202f [GlobalSetup] \r\n\u202f \u202f public void GlobalSetup() \r\n\u202f \u202f { \r\n\u202f \u202f \u202f \u202f var random = new Random(42); \r\n\u202f \u202f \u202f \u202f var chars = new char[10]; \r\n\r\n\u202f \u202f \u202f \u202f string getRandomString() \r\n\u202f \u202f \u202f \u202f { \r\n\u202f \u202f \u202f \u202f \u202f \u202f for (int i = 0; i &lt; 10; ++i) \r\n\u202f \u202f \u202f \u202f \u202f \u202f \u202f \u202f chars[i] = (char)random.Next('a', 'z' + 1); \r\n\u202f \u202f \u202f \u202f \u202f \u202f return new string(chars); \r\n\u202f \u202f \u202f \u202f } \r\n\r\n\u202f \u202f \u202f \u202f for (int i = 0; i &lt; entryCount; ++i) \r\n\u202f \u202f \u202f \u202f { \r\n\u202f \u202f \u202f \u202f \u202f \u202f records.Add(new Simple \r\n\u202f \u202f \u202f \u202f \u202f \u202f { \r\n\u202f \u202f \u202f \u202f \u202f \u202f \u202f \u202f Id1 = random.Next(), \r\n\u202f \u202f \u202f \u202f \u202f \u202f \u202f \u202f Id2 = random.Next(), \r\n\u202f \u202f \u202f \u202f \u202f \u202f \u202f \u202f Name1 = getRandomString(), \r\n\u202f \u202f \u202f \u202f \u202f \u202f \u202f \u202f Name2 = getRandomString(), \r\n\u202f \u202f \u202f \u202f \u202f \u202f }); \r\n\u202f \u202f \u202f \u202f } \r\n\u202f \u202f } \r\n\r\n\u202f \u202f [Benchmark] \r\n\u202f \u202f public void WriteRecords() \r\n\u202f \u202f { \r\n\u202f \u202f \u202f \u202f using var stream = new MemoryStream(); \r\n\u202f \u202f \u202f \u202f using var streamWriter = new StreamWriter(stream); \r\n\u202f \u202f \u202f \u202f using var writer = new CsvHelper.CsvWriter(streamWriter, CultureInfo.InvariantCulture); \r\n\u202f \u202f \u202f \u202f writer.WriteRecords(records); \r\n\u202f \u202f \u202f \u202f streamWriter.Flush(); \r\n\u202f \u202f } \r\n} <\/code><\/pre>\n<h1>Getting insight into the benchmark<\/h1>\n<p>Now to get started with the analysis I can either ask Profiler Agent to run the benchmark or just click on the follow up prompt for <code>@Profiler Run the benchmark and analyze results<\/code>. From here Copilot edits my main method which at first glance might seem odd but when looking at the changes I see it made the necessary changes to use BenchmarkSwitcher so it can choose which benchmarks to run:<\/p>\n<pre class=\"prettyprint language-cs language-csharp\"><code class=\"language-cs language-csharp\">static void Main(string[] args)\r\n{\r\n\u202f \u202f \/\/ Use assembly-wide discovery so all benchmarks in this assembly are run,\r\n\u202f \u202f \/\/ including the newly added BenchmarkWriteRecords.\r\n\u202f \u202f _ = BenchmarkSwitcher.FromAssembly(typeof(BenchmarkEnumerateRecords).Assembly).Run(args);\r\n}<\/code><\/pre>\n<p>Then it kicks off a benchmarking run and when it&#8217;s done I&#8217;m presented with a diagsession where we can begin investigating.<\/p>\n<h1>Using Copilot Profiler Agent to find bottlenecks<\/h1>\n<p>Now comes the exciting part. After running the benchmark, the Profiler agent analyzes the trace and highlights where time is spent. I can ask the Profiler Agent questions about the trace and have it explain why code is slow or why certain optimizations could help. It has already pointed out that most of the time is spent in delegate compilation and invocation, which is done for each field in the CSV record. For a record with 4 fields written 10,000 times, that means 40,000 delegate invocations. Each invocation has overhead, and this is showing up as a hot path in the profiler.<\/p>\n<p><a href=\"https:\/\/devblogs.microsoft.com\/visualstudio\/wp-content\/uploads\/sites\/4\/2025\/12\/CopilotProfiler5Full.webp\"><img decoding=\"async\" class=\"alignnone size-full wp-image-255070\" src=\"https:\/\/devblogs.microsoft.com\/visualstudio\/wp-content\/uploads\/sites\/4\/2025\/12\/CopilotProfiler5Full.webp\" alt=\"CopilotProfiler5Full image\" width=\"1600\" height=\"900\" srcset=\"https:\/\/devblogs.microsoft.com\/visualstudio\/wp-content\/uploads\/sites\/4\/2025\/12\/CopilotProfiler5Full.webp 1600w, https:\/\/devblogs.microsoft.com\/visualstudio\/wp-content\/uploads\/sites\/4\/2025\/12\/CopilotProfiler5Full-300x169.webp 300w, https:\/\/devblogs.microsoft.com\/visualstudio\/wp-content\/uploads\/sites\/4\/2025\/12\/CopilotProfiler5Full-1024x576.webp 1024w, https:\/\/devblogs.microsoft.com\/visualstudio\/wp-content\/uploads\/sites\/4\/2025\/12\/CopilotProfiler5Full-768x432.webp 768w, https:\/\/devblogs.microsoft.com\/visualstudio\/wp-content\/uploads\/sites\/4\/2025\/12\/CopilotProfiler5Full-1536x864.webp 1536w\" sizes=\"(max-width: 1600px) 100vw, 1600px\" \/><\/a><\/p>\n<p>I can ask the Profiler Agent: &#8220;How can I reduce the delegate invocation overhead?&#8221; or \u201cWhy is delegate invocation slow\u201d and the agent like a patient teacher will explain concepts and suggest fixes.<\/p>\n<h1>Implementing the fix<\/h1>\n<p>I\u2019m going to click the <code>@Profiler Optimize library to produce a single compiled write delegate (reduce multicast invokes)<\/code> and see what it comes up with. The Profiler Agent makes an edit to <code>ObjectRecordWriter<\/code> and I can click on that in the chat window to see the diff of the changes it makes.<\/p>\n<p>Looking at the current implementation, the code builds a list of delegates, one for each field:<\/p>\n<pre class=\"prettyprint language-cs language-csharp\"><code class=\"language-cs language-csharp\">var delegates = new List&lt;Action&gt;();\r\n\r\nforeach (var memberMap in members)\r\n{\r\n\u202f \u202f \/\/ ... field writing logic ...\r\n\r\n\u202f \u202f delegates.Add(Expression.Lambda&lt;Action&gt;(writeFieldMethodCall, recordParameter).Compile());\r\n}\r\n\r\nvar action = CombineDelegates(delegates) ?? new Action((T parameter) =&gt; { });\r\nreturn action;<\/code><\/pre>\n<p>The issue is that <code>CombineDelegates<\/code> creates a multicast delegate which invokes each individual delegate separately in series. Instead, Profiler Agent is suggesting we use <code>Expression.Block<\/code> to combine all the expressions before compiling:<\/p>\n<pre class=\"prettyprint language-cs language-csharp\"><code class=\"language-cs language-csharp\">var expressions = new List&lt;Expression&gt;(members.Count);\r\n\r\nforeach (var memberMap in members)\r\n{\r\n\u202f \u202f \/\/ ... field writing logic ...\r\n\r\n\u202f \u202f expressions.Add(writeFieldMethodCall);\r\n}\r\n\r\nif (expressions.Count == 0)\r\n{\r\n\u202f \u202f return new Action&lt;T&gt;((T parameter) =&gt; { });\r\n}\r\n\r\n\/\/ Combine all field writes into a single block\r\nvar block = Expression.Block(expressions);\r\nreturn Expression.Lambda&lt;Action&lt;T&gt;&gt;(block, recordParameter).Compile();<\/code><\/pre>\n<p>This change is small but elegant: instead of creating multiple delegates and invoking them sequentially, we create a single block expression containing all the field writes, then compile it once. Now all fields are written in a single call when we invoke the delegate for each record, with no additional delegate overhead.<\/p>\n<h2>Measuring the impact<\/h2>\n<p>After making this change, Copilot automatically reruns the benchmarks to measure the improvement. The results show approximately 24% better performance in this run with the profiler. Our previously staged <a href=\"https:\/\/github.com\/JoshClose\/CsvHelper\/pull\/2350\">PR for CsvHelper<\/a> shows ~15% better performance. The CPU profiler confirms that we&#8217;ve eliminated the delegate invocation overhead and instead of 40,000 delegate calls for our 10,000 records with 4 fields each, we now have just 10,000 delegate calls.<\/p>\n<p><a href=\"https:\/\/devblogs.microsoft.com\/visualstudio\/wp-content\/uploads\/sites\/4\/2025\/12\/CopilotProfiler6Full.webp\"><img decoding=\"async\" class=\"alignnone size-full wp-image-255071\" src=\"https:\/\/devblogs.microsoft.com\/visualstudio\/wp-content\/uploads\/sites\/4\/2025\/12\/CopilotProfiler6Full.webp\" alt=\"CopilotProfiler6Full image\" width=\"1600\" height=\"900\" srcset=\"https:\/\/devblogs.microsoft.com\/visualstudio\/wp-content\/uploads\/sites\/4\/2025\/12\/CopilotProfiler6Full.webp 1600w, https:\/\/devblogs.microsoft.com\/visualstudio\/wp-content\/uploads\/sites\/4\/2025\/12\/CopilotProfiler6Full-300x169.webp 300w, https:\/\/devblogs.microsoft.com\/visualstudio\/wp-content\/uploads\/sites\/4\/2025\/12\/CopilotProfiler6Full-1024x576.webp 1024w, https:\/\/devblogs.microsoft.com\/visualstudio\/wp-content\/uploads\/sites\/4\/2025\/12\/CopilotProfiler6Full-768x432.webp 768w, https:\/\/devblogs.microsoft.com\/visualstudio\/wp-content\/uploads\/sites\/4\/2025\/12\/CopilotProfiler6Full-1536x864.webp 1536w\" sizes=\"(max-width: 1600px) 100vw, 1600px\" \/><\/a><\/p>\n<p>This is a meaningful win for a library that&#8217;s already heavily optimized. For applications writing large CSV files with many fields, this improvement translates directly to reduced CPU time and faster processing. And because CsvHelper has millions of downloads, this optimization benefits a huge number of users. From here I went ahead and <a href=\"https:\/\/github.com\/JoshClose\/CsvHelper\/pull\/2350\">staged the PR<\/a>, though Copilot helpfully provides more follow up prompts regarding the type conversion and ShouldQuote logic so that I could continue to improve performance further.<\/p>\n<h1>The power of Copilot Profiler Agent<\/h1>\n<p>What makes this workflow powerful is the combination of precise performance data from the Visual Studio Profiler with the analytical and code generation capabilities of Copilot. Instead of manually digging through CPU traces and trying to understand what the hot paths mean, you can ask natural language questions, get actionable insights, and quickly test ideas.<\/p>\n<p>The agent doesn&#8217;t just tell you what&#8217;s slow &#8211; it helps you understand why it&#8217;s slow and suggests concrete ways to fix it. In this case, it identified that delegate invocation overhead was the bottleneck and suggested the <code>Expression.Block<\/code> optimization, which is exactly the right solution for this problem. It even reran the benchmarks to confirm the optimization!<\/p>\n<h1>Let us know what you think<\/h1>\n<p>We&#8217;ve shown how the Copilot Profiler Agent can help you take a real-world project, identify performance bottlenecks through natural language queries, and make meaningful improvements backed by data. The measure\/change\/measure cycle becomes much faster when you can ask questions about your performance data and get intelligent answers. We&#8217;d love to hear what you think!<\/p>\n<p><div  class=\"d-flex justify-content-left\"><a class=\"cta_button_link btn-primary mb-24\" href=\"https:\/\/www.surveymonkey.com\/r\/CNLLPSQ\" target=\"_blank\">Share your feedback<\/a><\/div><\/p>\n","protected":false},"excerpt":{"rendered":"<p>In Visual Studio 2026 we introduced Copilot Profiler Agent, a new AI-powered assistant that helps you analyze and optimize performance bottlenecks in your code. By combining the power of GitHub Copilot with Visual Studio&#8217;s performance profiler, you can now ask natural language questions about performance, get insights into hot paths, and quickly identify optimization opportunities. [&hellip;]<\/p>\n","protected":false},"author":54885,"featured_media":254203,"comment_status":"open","ping_status":"closed","sticky":false,"template":"","format":"standard","meta":{"_acf_changed":false,"footnotes":""},"categories":[6868,155],"tags":[7011,6883,53,6743],"class_list":["post-255047","post","type-post","status-publish","format-standard","has-post-thumbnail","hentry","category-performance","category-visual-studio","tag-agent","tag-copilot","tag-performance","tag-profiling"],"acf":[],"blog_post_summary":"<p>In Visual Studio 2026 we introduced Copilot Profiler Agent, a new AI-powered assistant that helps you analyze and optimize performance bottlenecks in your code. By combining the power of GitHub Copilot with Visual Studio&#8217;s performance profiler, you can now ask natural language questions about performance, get insights into hot paths, and quickly identify optimization opportunities. [&hellip;]<\/p>\n","_links":{"self":[{"href":"https:\/\/devblogs.microsoft.com\/visualstudio\/wp-json\/wp\/v2\/posts\/255047","targetHints":{"allow":["GET"]}}],"collection":[{"href":"https:\/\/devblogs.microsoft.com\/visualstudio\/wp-json\/wp\/v2\/posts"}],"about":[{"href":"https:\/\/devblogs.microsoft.com\/visualstudio\/wp-json\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"https:\/\/devblogs.microsoft.com\/visualstudio\/wp-json\/wp\/v2\/users\/54885"}],"replies":[{"embeddable":true,"href":"https:\/\/devblogs.microsoft.com\/visualstudio\/wp-json\/wp\/v2\/comments?post=255047"}],"version-history":[{"count":0,"href":"https:\/\/devblogs.microsoft.com\/visualstudio\/wp-json\/wp\/v2\/posts\/255047\/revisions"}],"wp:featuredmedia":[{"embeddable":true,"href":"https:\/\/devblogs.microsoft.com\/visualstudio\/wp-json\/wp\/v2\/media\/254203"}],"wp:attachment":[{"href":"https:\/\/devblogs.microsoft.com\/visualstudio\/wp-json\/wp\/v2\/media?parent=255047"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/devblogs.microsoft.com\/visualstudio\/wp-json\/wp\/v2\/categories?post=255047"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/devblogs.microsoft.com\/visualstudio\/wp-json\/wp\/v2\/tags?post=255047"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}