<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom" xmlns:dc="http://purl.org/dc/elements/1.1/">
  <channel>
    <title>DEV Community: ABP.IO</title>
    <description>The latest articles on DEV Community by ABP.IO (@abp_io).</description>
    <link>https://dev.to/abp_io</link>
    <image>
      <url>https://media2.dev.to/dynamic/image/width=90,height=90,fit=cover,gravity=auto,format=auto/https:%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Fuser%2Fprofile_image%2F3723489%2Ffed2d127-60d2-4db0-a199-b848ed38b079.png</url>
      <title>DEV Community: ABP.IO</title>
      <link>https://dev.to/abp_io</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/abp_io"/>
    <language>en</language>
    <item>
      <title>Deep Dive on ABP AI Agent #5: MCP (Model Context Protocol)</title>
      <dc:creator>ABP.IO</dc:creator>
      <pubDate>Mon, 15 Jun 2026 06:06:24 +0000</pubDate>
      <link>https://dev.to/abp_io/deep-dive-on-abp-ai-agent-5-mcp-model-context-protocol-3471</link>
      <guid>https://dev.to/abp_io/deep-dive-on-abp-ai-agent-5-mcp-model-context-protocol-3471</guid>
      <description>&lt;h1&gt;
  
  
  Deep Dive on ABP AI Agent #5: MCP (Model Context Protocol)
&lt;/h1&gt;

&lt;p&gt;When I use an AI coding agent inside ABP Studio, most of the work starts inside the solution.&lt;/p&gt;

&lt;p&gt;The agent can read the code. It can edit files. It can use the ABP Studio tools I enabled. It can build the solution, run tasks, start applications, inspect containers, and check monitoring data when something fails. &lt;a href="https://abp.io/community/articles/deep-dive-on-abp-ai-agent-4-integrated-abp-studio-tools-be2xa2om" rel="noopener noreferrer"&gt;In the previous article of this&lt;/a&gt; series, we focused on exactly that: the integrated tools that connect the agent to the ABP Studio development environment.&lt;/p&gt;

&lt;p&gt;But there is another point where plain solution awareness is not enough.&lt;/p&gt;

&lt;p&gt;Sometimes the information I need is not in the repository. It is not in the running application. It is not in the build output, container state, or ABP Studio monitoring screen.&lt;/p&gt;

&lt;p&gt;It may be in a live Prometheus workspace. It may be in Google Search Console. It may be in an SEO analyzer, a documentation system, a database, a customer support tool, or another service that has nothing to do with the ABP solution itself.&lt;/p&gt;

&lt;p&gt;That is where &lt;strong&gt;MCP&lt;/strong&gt; becomes useful.&lt;/p&gt;

&lt;p&gt;MCP, short for &lt;strong&gt;Model Context Protocol&lt;/strong&gt; , gives ABP AI Coding Agent a standard way to reach external tools and data sources. It does not replace the built-in ABP Studio tools. It extends the agent beyond the solution boundary when the task depends on something outside that boundary.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;Note:&lt;/strong&gt; ABP AI Coding Agent is available directly to ABP license holders. License holders have predefined credits so they can try the integrated experience without setting up a separate AI workflow first. MCP is one of the ways this experience can be extended when the agent needs access to systems outside ABP Studio.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h2&gt;
  
  
  Why MCP Matters
&lt;/h2&gt;

&lt;p&gt;Most coding agents start from the same place: &lt;strong&gt;the files they can see&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;That is a good start, but real development work often needs context from somewhere else: production metrics, SEO data, analytics, search performance, customer records, internal APIs, or product knowledge.&lt;/p&gt;

&lt;p&gt;If the agent cannot reach those systems, the developer becomes the bridge:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Copy this metric result.&lt;/li&gt;
&lt;li&gt;Paste that SEO report.&lt;/li&gt;
&lt;li&gt;Export this analytics data.&lt;/li&gt;
&lt;li&gt;Summarize this dashboard.&lt;/li&gt;
&lt;li&gt;Explain which production signal matters.&lt;/li&gt;
&lt;li&gt;Tell the agent what the external system says.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;That works, but it is not the best workflow. The more information I manually copy into the chat, the easier it is to miss something, simplify too much, or give the agent an outdated snapshot.&lt;/p&gt;

&lt;p&gt;MCP reduces that manual work. It lets the agent ask an external system for context directly, within the permission boundary I configure.&lt;/p&gt;

&lt;p&gt;The important detail is that MCP is not "the agent can do anything now." It is a controlled extension point. A server is connected. Its tools are listed. Individual tools can be enabled or disabled. The agent can only use what is available in the current session.&lt;/p&gt;

&lt;p&gt;That gives me a practical balance: the ABP-aware development experience stays in ABP Studio, and external context can be added only when the task needs it.&lt;/p&gt;

&lt;h2&gt;
  
  
  What MCP Actually Is
&lt;/h2&gt;

&lt;p&gt;MCP is an open standard for connecting external tools and data to AI agents.&lt;/p&gt;

&lt;p&gt;Instead of every tool inventing its own integration for every agent, MCP defines a common shape. A program that exposes tools or resources through this standard is called an &lt;strong&gt;MCP server&lt;/strong&gt;. An agent that understands MCP can connect to that server and use what it provides.&lt;/p&gt;

&lt;p&gt;For me, the easiest way to think about it is simple:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;ABP Studio tools connect the agent to the ABP development environment.&lt;/li&gt;
&lt;li&gt;MCP servers connect the agent to systems outside that environment.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;The server might expose a tool for querying Prometheus, checking Search Console data, running an SEO audit, inspecting a database, or calling an internal API. The exact capability depends on the server.&lt;/p&gt;

&lt;p&gt;MCP is not a prompt, a rule, a skill, or a lesson. Those shape what the agent knows or how it behaves. MCP changes what the agent can reach.&lt;/p&gt;

&lt;p&gt;That makes MCP more like equipment than instruction. It gives the agent access to a tool or data source that was previously outside its working area.&lt;/p&gt;

&lt;p&gt;There are already many community and vendor MCP servers for different systems. A useful place to discover examples is the &lt;a href="https://github.com/punkpeye/awesome-mcp-servers" rel="noopener noreferrer"&gt;awesome-mcp-servers&lt;/a&gt; repository, which collects MCP servers across many categories.&lt;/p&gt;

&lt;p&gt;For concrete examples, there are MCP servers for &lt;a href="https://github.com/pab1it0/prometheus-mcp-server" rel="noopener noreferrer"&gt;Prometheus metrics&lt;/a&gt;, &lt;a href="https://github.com/awslabs/mcp/tree/main/src/prometheus-mcp-server" rel="noopener noreferrer"&gt;AWS Managed Prometheus&lt;/a&gt;, and &lt;a href="https://github.com/g-battaglia/mcp-seo" rel="noopener noreferrer"&gt;SEO analysis&lt;/a&gt;. The exact server you choose depends on your environment, but the pattern is the same: expose a focused external capability to the agent through MCP.&lt;/p&gt;

&lt;h2&gt;
  
  
  When You Actually Need It
&lt;/h2&gt;

&lt;p&gt;Most of the time, I would not start with MCP.&lt;/p&gt;

&lt;p&gt;For normal ABP development, the built-in tools are usually the right first layer. The agent can already work with the solution, run profiles, applications, containers, tasks, builds, monitoring data, and documentation. If the task is completely inside the ABP solution, MCP may not add anything.&lt;/p&gt;

&lt;p&gt;MCP becomes useful when the task crosses the solution boundary.&lt;/p&gt;

&lt;p&gt;For example, I may ask:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Run an SEO audit for https://abp.io, summarize the weak areas, and suggest which content or technical improvements would matter most.

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

&lt;/div&gt;



&lt;p&gt;Without MCP, I would need to open a separate SEO tool, run the audit, copy the results, and paste them into the chat. With the right MCP server enabled, the agent can call the SEO tool directly and reason from the structured result.&lt;/p&gt;

&lt;p&gt;Or I may ask:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Connect to our Prometheus MCP server, check the error rate and p95 latency for the AuthServer over the last 30 minutes, and tell me whether the last deployment changed anything.

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

&lt;/div&gt;



&lt;p&gt;That is a very different kind of task. The answer is not in the source code alone. It lives in a monitoring system. Prometheus MCP servers exist for this kind of workflow; for example, some expose tools for instant PromQL queries, range queries, metric discovery, and target inspection.&lt;/p&gt;

&lt;p&gt;Another realistic prompt could be:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Use Search Console data to find pages that lost traffic this month, then inspect the related docs pages and suggest focused improvements.

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

&lt;/div&gt;



&lt;p&gt;Again, the important part is not that the agent magically knows everything. The important part is that I can connect a specific external system, expose a specific set of tools, and let the agent use them when they are relevant.&lt;/p&gt;

&lt;p&gt;If the work starts outside the codebase but ends inside the codebase, MCP can help connect those two parts of the workflow.&lt;/p&gt;

&lt;h2&gt;
  
  
  Setting Up An MCP Server
&lt;/h2&gt;

&lt;p&gt;MCP servers are configured under &lt;strong&gt;Settings &amp;gt; MCP Servers&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;When no server is connected, the page is intentionally simple. It is an empty list waiting for the first server. That is important because MCP should be explicit. If I have not connected a server, the agent should not behave as if it has access to that external system.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F3t3s4gkdrvmrp3v6o281.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F3t3s4gkdrvmrp3v6o281.png" alt="The MCP Servers page in ABP Studio, before any server is added" width="800" height="376"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;When I add a server, ABP Studio asks how it should connect.&lt;/p&gt;

&lt;p&gt;There are two main connection types:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Stdio:&lt;/strong&gt; ABP Studio runs the MCP server as a local process. I provide the command, arguments, and environment variables the server needs.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;HTTP:&lt;/strong&gt; ABP Studio connects to an MCP server over the network. I provide the URL and any required headers.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F3h54pb3vx4v4sgmyxl3f.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F3h54pb3vx4v4sgmyxl3f.png" alt="Adding an MCP server: pick stdio or HTTP, then provide the command, arguments, and environment variables" width="800" height="727"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;This is useful because different MCP servers are packaged in different ways. Some are local command-line programs. Some are hosted services. Some need environment variables for tokens or configuration.&lt;/p&gt;

&lt;p&gt;In the example below, I am adding an HTTP MCP server named &lt;strong&gt;SEO Analyzer&lt;/strong&gt;. It exposes a small set of SEO-related tools through a remote MCP endpoint.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F9p9qdyifupt5qwip5w1w.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F9p9qdyifupt5qwip5w1w.png" alt="Adding the SEO Analyzer MCP server over HTTP" width="544" height="487"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;ABP Studio does not require every server to be created manually from scratch. If I already use MCP elsewhere, I can import existing configuration from tools like Cursor, Claude, VS Code, Windsurf, or a plain MCP server JSON file. It can also export configuration in the standard &lt;code&gt;mcpServers&lt;/code&gt; JSON shape.&lt;/p&gt;

&lt;p&gt;That matters because MCP is an open ecosystem. The same server definition can often move between tools. ABP Studio becomes another place where I can use that server, but now in the context of an ABP-aware agent.&lt;/p&gt;

&lt;h2&gt;
  
  
  What A Connected Server Shows
&lt;/h2&gt;

&lt;p&gt;After a server is connected, ABP Studio shows the important parts of the connection.&lt;/p&gt;

&lt;p&gt;I can see whether the server is connected, how many tools it exposes, which tools are available, and whether it provides resources. I can also inspect resources directly from the settings page.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fw7qvs9bf8m1aqpsm3eg7.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fw7qvs9bf8m1aqpsm3eg7.png" alt="A connected SEO Analyzer MCP server and its available tools" width="799" height="402"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;This visibility matters because an MCP server is not just a checkbox. It is a surface area. It may expose one tool or many. Some tools may be read-only, and some may perform actions outside the repository.&lt;/p&gt;

&lt;p&gt;The agent does not need me to call those tools manually. I describe the task, and if the tool is available and relevant, the agent can decide to use it. That is why a prompt can stay natural:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Read the product requirement from the connected knowledge base and compare it with the current implementation.

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

&lt;/div&gt;



&lt;p&gt;The prompt does not need to become a tool invocation script. The agent still owns the reasoning loop. MCP only gives it a new place to look.&lt;/p&gt;

&lt;h2&gt;
  
  
  Tool Access: What The Agent Is Allowed To Use
&lt;/h2&gt;

&lt;p&gt;The most important part of MCP in ABP Studio is not only connecting servers. It is controlling what the agent is allowed to use.&lt;/p&gt;

&lt;p&gt;Individual tools can be disabled. If a tool is disabled, it is not offered to the agent. The agent should not plan around it, call it, or assume it has access to it.&lt;/p&gt;

&lt;p&gt;That is useful for safety, but also for quality.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fojoztol28jnyi1f7k4f0.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fojoztol28jnyi1f7k4f0.png" alt="Disabling individual tools exposed by the SEO Analyzer MCP server" width="756" height="218"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Every enabled tool becomes part of the menu the agent considers. If a server exposes ten tools but the current task only needs two, I usually prefer to enable only those two. A shorter tool list is easier for the agent to choose from. It also makes the session easier for me to reason about.&lt;/p&gt;

&lt;p&gt;The permission model is simple:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;If a server is not connected, the agent cannot use it.&lt;/li&gt;
&lt;li&gt;If a server is disabled, the agent cannot use its tools.&lt;/li&gt;
&lt;li&gt;If an individual tool is disabled, the agent cannot use that tool.&lt;/li&gt;
&lt;li&gt;If the session is not in Agent mode, MCP tools are not available.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;That last point is important. MCP tools are available in &lt;strong&gt;Agent mode only&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;Plan and Ask modes are read-only by design. They are useful for understanding, planning, and discussing changes, but they do not receive MCP tools. If I want the agent to call an external MCP tool, I need to work in Agent mode.&lt;/p&gt;

&lt;p&gt;This keeps the model consistent with the rest of the ABP AI Coding Agent experience. The mode determines what kind of work the agent is allowed to perform.&lt;/p&gt;

&lt;h2&gt;
  
  
  Keeping MCP Safe
&lt;/h2&gt;

&lt;p&gt;MCP is powerful because it lets an AI agent reach systems outside the solution.&lt;/p&gt;

&lt;p&gt;That is also why it needs care.&lt;/p&gt;

&lt;p&gt;An MCP server is a program. It may read files, call APIs, query databases, send requests, or perform actions depending on how it was built. The agent is not inventing those capabilities. It is using what the server exposes.&lt;/p&gt;

&lt;p&gt;So trust matters at two levels.&lt;/p&gt;

&lt;p&gt;First, I need to trust the actions. If a tool can update an issue, send a message, change a record, or trigger a workflow, that is a real side effect.&lt;/p&gt;

&lt;p&gt;Second, I need to trust the descriptions. When an MCP server connects, the names and descriptions of its tools become part of what the model can read. A careless or hostile server can use that text to influence the model before I even write my prompt.&lt;/p&gt;

&lt;p&gt;That means MCP safety is not only about "what can this tool do?" It is also about "what instructions or descriptions does this server place in front of the model?"&lt;/p&gt;

&lt;p&gt;Two habits keep this manageable:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Connect only servers I trust.&lt;/li&gt;
&lt;li&gt;Disable tools the agent should not use for the current task.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;This sits on top of the agent's normal guardrails. Shell commands, URL fetches, downloads, and other sensitive actions can still require permission. Also, the tool list is fixed for a running session, so changing a server in the middle of a session does not quietly alter the tool surface already given to that session.&lt;/p&gt;

&lt;p&gt;That is the behavior I want from this kind of integration. MCP can extend the agent, but it should do so through visible, intentional configuration.&lt;/p&gt;

&lt;h2&gt;
  
  
  A Practical MCP Walkthrough
&lt;/h2&gt;

&lt;p&gt;Let me make this more concrete with a small scenario.&lt;/p&gt;

&lt;p&gt;Imagine I am working on the public website or documentation side of an ABP-based product. The task is not a compiler error or a failing unit test. I want to understand how the website looks from an SEO perspective and what I would improve first.&lt;/p&gt;

&lt;p&gt;Without MCP, I would open a separate SEO tool, run the audit, wait for the result, copy the score, paste the table into the chat, and then ask the agent to interpret it. That is not terrible, but it turns me into a copy-paste integration layer.&lt;/p&gt;

&lt;p&gt;With an SEO Analyzer MCP server connected, I can start from a better prompt:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Please rate the SEO work on the https://abp.io website on a scale of 1 to 10. I'd also like to know what you would do if you were in charge.

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

&lt;/div&gt;



&lt;p&gt;At that point, the agent can use the SEO Analyzer MCP tool instead of guessing from general SEO knowledge alone. It can call the audit tool, read the structured result, and turn it into a concrete improvement plan.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F60liyc8r0ryp69cbmjyr.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F60liyc8r0ryp69cbmjyr.png" alt="ABP AI Coding Agent using the SEO Analyzer MCP server" width="800" height="491"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;The value is not only that the agent saved me a few seconds. The value is that the agent starts from live tool output instead of a manually summarized report.&lt;/p&gt;

&lt;h3&gt;
  
  
  First, Without The MCP Server
&lt;/h3&gt;

&lt;p&gt;If no SEO MCP server is connected, the agent cannot run the audit directly.&lt;/p&gt;

&lt;p&gt;It may still give a reasonable answer from general knowledge. It may say that the site should have good titles, descriptions, headings, performance, backlinks, structured data, and content targeting. That advice can be useful, but it is generic.&lt;/p&gt;

&lt;p&gt;For example, I might write:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Review the SEO of abp.io and tell me what to improve.

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

&lt;/div&gt;



&lt;p&gt;The agent can reason about common SEO practices, but it does not have a fresh audit result. It does not know which categories scored well, which areas are weak, or whether the live page data confirms the concern.&lt;/p&gt;

&lt;p&gt;This is similar to debugging without monitoring tools. The agent can reason, but it is reasoning from weaker evidence.&lt;/p&gt;

&lt;h3&gt;
  
  
  Then, With The MCP Server Enabled
&lt;/h3&gt;

&lt;p&gt;Now imagine the SEO Analyzer MCP server is connected, enabled, and the relevant audit tool is enabled.&lt;/p&gt;

&lt;p&gt;I can ask:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Run an audit on https://abp.io, rate the result, and suggest the highest-impact improvements.

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

&lt;/div&gt;



&lt;p&gt;This time the agent can gather the external context first. In the screenshot, the server exposes tools like &lt;code&gt;run_audit_anonymous&lt;/code&gt;, &lt;code&gt;run_audit&lt;/code&gt;, &lt;code&gt;get_audit&lt;/code&gt;, &lt;code&gt;list_audits&lt;/code&gt;, &lt;code&gt;get_audit_pdf&lt;/code&gt;, and &lt;code&gt;get_organic_traffic&lt;/code&gt;. The agent uses the audit tool and then explains the result in human terms.&lt;/p&gt;

&lt;p&gt;That changes the workflow:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Run the external SEO audit.&lt;/li&gt;
&lt;li&gt;Read the score and category breakdown.&lt;/li&gt;
&lt;li&gt;Identify the weakest areas.&lt;/li&gt;
&lt;li&gt;Suggest concrete technical or content improvements.&lt;/li&gt;
&lt;li&gt;If needed, inspect the related website or documentation files.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;The agent is no longer guessing from best practices alone. It can connect the outside report to the actual website and then, if the relevant files are in the solution, help improve them.&lt;/p&gt;

&lt;h3&gt;
  
  
  Another Example: Live Monitoring
&lt;/h3&gt;

&lt;p&gt;SEO is only one example. Monitoring is another strong fit for MCP.&lt;/p&gt;

&lt;p&gt;Prometheus MCP servers can expose tools for PromQL instant queries, range queries, metric discovery, targets, and server information. That means I can ask operational questions in natural language while the agent queries the monitoring system through MCP.&lt;/p&gt;

&lt;p&gt;For example:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Use the Prometheus MCP server to check p95 latency, request rate, and error rate for the public web application after the last deployment. If something changed, identify the most likely area to inspect in the ABP solution.

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

&lt;/div&gt;



&lt;p&gt;That does not mean the agent should blindly change production behavior. It means the agent can start from live evidence: metrics, trends, targets, and recent changes. Then it can use ABP Studio context to inspect the related application, module, or configuration.&lt;/p&gt;

&lt;h3&gt;
  
  
  Combining MCP With ABP Studio Tools
&lt;/h3&gt;

&lt;p&gt;The best part is that MCP does not replace the ABP Studio tools from the previous article. It works beside them.&lt;/p&gt;

&lt;p&gt;In a real workflow, MCP may provide the outside signal, and ABP Studio tools may provide the inside development loop:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;MCP brings in the external context.&lt;/li&gt;
&lt;li&gt;ABP-aware reasoning maps it to the solution.&lt;/li&gt;
&lt;li&gt;ABP Studio tools help implement and validate the change.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;That is where the integration becomes more useful than a generic MCP checkbox. The agent is connected to an external tool while still understanding the ABP development environment.&lt;/p&gt;

&lt;h2&gt;
  
  
  How MCP Fits With Everything Else
&lt;/h2&gt;

&lt;p&gt;MCP is an open standard, so connecting an MCP server is not an ABP-only idea.&lt;/p&gt;

&lt;p&gt;Tools like Cursor, Claude, VS Code extensions, and other AI coding environments can also use MCP. That is part of its appeal. Teams can build or adopt a server once and use it across different tools.&lt;/p&gt;

&lt;p&gt;What makes MCP especially useful in ABP Studio is the company it keeps. ABP AI Coding Agent already understands the ABP solution structure, application layers, modules, migrations, proxies, build actions, run profiles, containers, monitoring data, and official ABP documentation. MCP adds the outside world to that picture.&lt;/p&gt;

&lt;p&gt;So I do not need to choose between:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;an agent that understands ABP,&lt;/li&gt;
&lt;li&gt;and an agent that can reach my team's external systems.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;The better experience is both together, under visible configuration. Put simply:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Built-in ABP Studio tools handle the solution and runtime environment.&lt;/li&gt;
&lt;li&gt;MCP servers handle external tools and data sources.&lt;/li&gt;
&lt;li&gt;Rules, skills, and lessons shape how the agent behaves.&lt;/li&gt;
&lt;li&gt;Agent mode decides whether tool use is available for the session.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Each part has a different role. Keeping those roles clear makes the agent easier to trust. This is also why I would start small in a real team workflow: one low-risk, high-value server, probably a read-only analytics, documentation, or monitoring server, with only the tools that support a clear workflow enabled.&lt;/p&gt;

&lt;p&gt;MCP by itself is a protocol. The difference in ABP Studio is that MCP becomes part of an ABP development session. The agent can read an external SEO report or monitoring signal, but it can also understand which ABP application, module, page, or configuration owns the behavior and validate the change with ABP Studio tools.&lt;/p&gt;

&lt;p&gt;Most real tasks are not only "call an external tool." They are more like:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Understand the requirement from outside the repository.&lt;/li&gt;
&lt;li&gt;Find where that behavior lives in the ABP solution.&lt;/li&gt;
&lt;li&gt;Make the smallest correct change.&lt;/li&gt;
&lt;li&gt;Validate it in the same development environment.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;MCP helps with the first part. ABP Studio AI helps connect the rest.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;That makes MCP valuable not because it is another tool list, but because it extends the ABP AI Coding Agent beyond the repository without disconnecting it from the ABP workflow.&lt;/strong&gt;&lt;/p&gt;

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

&lt;p&gt;MCP is the agent's connection to everything that is not already inside the ABP solution.&lt;/p&gt;

&lt;p&gt;You will not need it for every task. But when the work depends on SEO data, live metrics, external documentation, internal services, or team knowledge, MCP gives the agent a standard way to reach that context.&lt;/p&gt;

</description>
      <category>ai</category>
      <category>abpframework</category>
      <category>mcp</category>
    </item>
    <item>
      <title>Deep Dive on ABP AI Agent #4: Integrated ABP Studio Tools</title>
      <dc:creator>ABP.IO</dc:creator>
      <pubDate>Fri, 12 Jun 2026 05:36:58 +0000</pubDate>
      <link>https://dev.to/abp_io/deep-dive-on-abp-ai-agent-4-integrated-abp-studio-tools-1eim</link>
      <guid>https://dev.to/abp_io/deep-dive-on-abp-ai-agent-4-integrated-abp-studio-tools-1eim</guid>
      <description>&lt;h1&gt;
  
  
  Deep Dive on ABP AI Agent #4: Integrated ABP Studio Tools
&lt;/h1&gt;

&lt;p&gt;When I use an AI coding agent, there is a point where plain code awareness is not enough.&lt;/p&gt;

&lt;p&gt;The agent may understand the project structure. It may read the failing method. It may even guess the most likely cause of an error. But in a real development session, I usually need more than a guess. I need the latest exception, the request that caused it, the logs around it, the application that is running, the containers it depends on, the tasks I can execute, and the build result after a fix.&lt;/p&gt;

&lt;p&gt;That is where &lt;strong&gt;ABP AI Coding Agent&lt;/strong&gt; becomes different from a generic coding assistant. It is not only connected to files. It is connected to &lt;strong&gt;ABP Studio&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;ABP Studio already knows the solution, run profiles, runnable applications, containers, tasks, monitoring data, and build actions. ABP AI Coding Agent can use that context through integrated tools when those tools are enabled. So instead of copying exception details, terminal output, container names, or build logs into the chat manually, I can let the agent work with the same ABP Studio environment I am using.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Ftzruor0s28viap8y6trd.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Ftzruor0s28viap8y6trd.png" alt="ABP AI Coding Agent with tools" width="799" height="504"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;Note:&lt;/strong&gt; ABP AI Coding Agent is available directly to ABP license holders. License holders have predefined credits so they can try it without setting up a separate AI workflow first. When those credits run out, they can buy more and continue using the same integrated experience.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h2&gt;
  
  
  Why Integrated Tools Matter
&lt;/h2&gt;

&lt;p&gt;Most coding agents start from the same place: &lt;strong&gt;source code&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;That is useful, but ABP development is not only source code. A running ABP solution has applications, modules, services, containers, database connections, migrations, logs, exceptions, requests, tasks, and build steps. When these pieces are outside the agent's reach, the developer becomes the bridge:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Copy this exception.&lt;/li&gt;
&lt;li&gt;Paste that log.&lt;/li&gt;
&lt;li&gt;Run this build.&lt;/li&gt;
&lt;li&gt;Check that container.&lt;/li&gt;
&lt;li&gt;Explain which application is currently running.&lt;/li&gt;
&lt;li&gt;Tell the agent what failed after the last change.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Integrated ABP Studio tools reduce that manual work. They let the agent ask ABP Studio for runtime and solution information directly, within the permission boundary I choose.&lt;/p&gt;

&lt;p&gt;The important detail is that this access is still explicit. Tools can be enabled or disabled. If I do not want the agent to use a tool, I can keep it disabled. If I want the agent to troubleshoot with runtime information, I can enable the relevant tools and ask for a more complete investigation.&lt;/p&gt;

&lt;p&gt;That gives me a practical balance: the productivity of automation, with a visible boundary around what the agent can use.&lt;/p&gt;

&lt;h2&gt;
  
  
  Tool Access: What The Agent Is Allowed To Use
&lt;/h2&gt;

&lt;p&gt;The tools view is the control point.&lt;/p&gt;

&lt;p&gt;This is where ABP Studio shows the integrated tools that can be used by ABP AI Coding Agent. Some tools are for reading runtime information. Some are for interacting with applications. Some are for containers, tasks, or build actions.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fbt6dvl0j19ksienzbq0x.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fbt6dvl0j19ksienzbq0x.png" alt="ABP AI Coding Agent tools overview" width="800" height="669"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;The names are intentionally direct. A monitoring tool that gets exceptions is about exceptions. A build tool is about build validation. A task tool is about ABP Studio tasks. That makes the tool list easy to understand even before using it in a real prompt.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;For me, the key idea is not the individual button names. It is the permission model:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;If a tool is disabled, the agent should not act as if it has that information.&lt;/li&gt;
&lt;li&gt;If a tool is enabled, the agent can use it as part of the current session.&lt;/li&gt;
&lt;li&gt;If a task needs runtime evidence, I can enable only the tools needed for that task.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;This makes ABP AI Coding Agent feel more intentional than a black box. I can decide when it should stay in code reasoning and when it should use ABP Studio's runtime view of the solution.&lt;/p&gt;

&lt;h2&gt;
  
  
  Monitoring Tools
&lt;/h2&gt;

&lt;p&gt;Monitoring tools are the first group I reach for when something fails at runtime.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fx8xowassu4x2mara4nzt.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fx8xowassu4x2mara4nzt.png" alt="ABP AI Coding Agent monitoring tools" width="738" height="227"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;These tools help the agent inspect what happened while the application was running. In practice, this means information like &lt;strong&gt;exceptions&lt;/strong&gt; , &lt;strong&gt;logs&lt;/strong&gt; , &lt;strong&gt;events&lt;/strong&gt; , and &lt;strong&gt;request details&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;This is a big difference from a generic coding agent. Without monitoring tools, the agent can read the code and make a reasonable guess. With monitoring tools, it can work from the actual failure.&lt;/p&gt;

&lt;p&gt;For example, if a page throws an exception, I can ask:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Get the latest exception from ABP Studio Monitoring and explain what failed.

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

&lt;/div&gt;



&lt;p&gt;If the exception tool is enabled, the agent can use that runtime signal. It can look at the exception message, stack trace, request context, and related code. Then it can connect the runtime failure to the implementation.&lt;/p&gt;

&lt;p&gt;That changes the debugging loop:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Trigger the problem.&lt;/li&gt;
&lt;li&gt;Ask the agent to inspect the runtime evidence.&lt;/li&gt;
&lt;li&gt;Let it find the related code.&lt;/li&gt;
&lt;li&gt;Apply the fix.&lt;/li&gt;
&lt;li&gt;Validate again.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;The developer no longer needs to manually copy the exception from one place and paste it into another. ABP Studio becomes part of the agent's working context and give it _ &lt;strong&gt;harness&lt;/strong&gt; _!&lt;/p&gt;

&lt;h2&gt;
  
  
  Application Tools
&lt;/h2&gt;

&lt;p&gt;Application tools connect the agent to the applications defined in the active ABP Studio run profile.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fzltca6akfau6xiifzgz0.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fzltca6akfau6xiifzgz0.png" alt="ABP AI Coding Agent application tools" width="735" height="132"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;This matters because ABP solutions often contain more than one runnable application. A layered solution may have a web application, an API host, a DbMigrator, and other executable projects. A microservice solution may have several services with different roles.&lt;/p&gt;

&lt;p&gt;ABP Studio already understands these applications through the solution and run profile. When application tools are available, the agent does not need to rediscover everything from file names or ask me which project is running. It can use ABP Studio's view of the solution.&lt;/p&gt;

&lt;p&gt;That is useful for prompts like:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Check which application is running and use the relevant runtime information to investigate the problem.

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

&lt;/div&gt;



&lt;p&gt;The benefit is not only convenience. It also reduces mistakes. The agent can reason from the same run profile that I use in ABP Studio, instead of guessing from the repository structure alone.&lt;/p&gt;

&lt;h2&gt;
  
  
  Container Tools
&lt;/h2&gt;

&lt;p&gt;Many ABP applications depend on infrastructure services while running locally.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fezok0aidadc2lnj0o6nq.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fezok0aidadc2lnj0o6nq.png" alt="ABP AI Coding Agent container tools" width="737" height="134"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;A solution may need SQL Server, PostgreSQL, Redis, RabbitMQ, OpenIddict-related services, or other containers depending on the template and modules. When something fails, the cause is not always in application code. Sometimes a required container is not running. Sometimes the application cannot reach a dependency. Sometimes the runtime error is only a symptom of an infrastructure problem.&lt;/p&gt;

&lt;p&gt;Container tools give the agent a way to include that part of the environment in the investigation.&lt;/p&gt;

&lt;p&gt;Instead of asking the agent to guess why a database connection fails, I can let it check the container context that ABP Studio already has. The agent can then distinguish between:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;a code problem,&lt;/li&gt;
&lt;li&gt;a configuration problem,&lt;/li&gt;
&lt;li&gt;a missing or stopped container,&lt;/li&gt;
&lt;li&gt;or a dependency that is running but unhealthy.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;This is one of the places where ABP Studio integration is especially valuable. General coding agents can help with Docker files or connection strings, but they usually do not know the current ABP Studio container state unless I copy it into the prompt. ABP AI Coding Agent can work closer to the actual local development environment.&lt;/p&gt;

&lt;h2&gt;
  
  
  Task Tools
&lt;/h2&gt;

&lt;p&gt;ABP Studio tasks are another part of the development workflow that should not live outside the agent.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F8yejqihw56ut4opify6v.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F8yejqihw56ut4opify6v.png" alt="ABP AI Coding Agent task tools" width="735" height="82"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Tasks can represent common solution actions. They may run commands, scripts, or workflow steps that are already configured for the solution. If the team uses ABP Studio tasks to standardize local development, the agent should be able to understand and use that same layer.&lt;/p&gt;

&lt;p&gt;That means I can ask for a workflow instead of a raw command:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Use the available ABP Studio tasks to validate this change.

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

&lt;/div&gt;



&lt;p&gt;The agent can work with the task names and outputs rather than asking me to remember the exact command. This is helpful in larger solutions where the correct validation step is not obvious from a single project file.&lt;/p&gt;

&lt;p&gt;It also keeps the agent aligned with the team's development path. If ABP Studio has the task, the agent can follow that path instead of inventing a one-off command.&lt;/p&gt;

&lt;h2&gt;
  
  
  Build Tools
&lt;/h2&gt;

&lt;p&gt;Build tools close the loop after an implementation or fix.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fda587q7mkig38m3iddyd.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fda587q7mkig38m3iddyd.png" alt="ABP AI Coding Agent build tools" width="733" height="89"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;The agent should not only change files. It should also help verify that the change still builds.&lt;/p&gt;

&lt;p&gt;In a generic coding agent flow, validation often depends on shell access and manually chosen commands. That can work, but it leaves more room for guessing. Which project should be built? Which solution file should be used? Is there an ABP Studio-specific build action already configured?&lt;/p&gt;

&lt;p&gt;With ABP Studio build tools, the agent can use the build context exposed by the platform. That makes prompts like this more natural:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Apply the fix and run the available build validation.

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

&lt;/div&gt;



&lt;p&gt;For small changes, this may simply confirm that the project compiles. For larger changes, it can become part of a broader loop with tasks, application checks, and monitoring tools.&lt;/p&gt;

&lt;p&gt;The important part is that the agent can move from implementation to validation without requiring me to manually transfer output between tools.&lt;/p&gt;

&lt;h2&gt;
  
  
  A Practical Tool Access Walkthrough
&lt;/h2&gt;

&lt;p&gt;Let me make this more concrete with a small debugging scenario.&lt;/p&gt;

&lt;p&gt;In the sample application, I deliberately added a runtime exception to the &lt;code&gt;UpdateAsync&lt;/code&gt; method of &lt;code&gt;BookAppService.cs&lt;/code&gt;:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight csharp"&gt;&lt;code&gt;&lt;span class="k"&gt;throw&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nf"&gt;Exception&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"Sample exception for demonstrating integrated tools!"&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

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

&lt;/div&gt;



&lt;p&gt;Then I started the application from ABP Studio and triggered the related request from the browser. The only purpose of this setup is to create a real runtime failure that ABP Studio Monitoring can capture.&lt;/p&gt;

&lt;p&gt;The interesting part is not the exception itself. The interesting part is how the agent behaves when the monitoring tool is disabled, and how that changes when the tool is enabled.&lt;/p&gt;

&lt;h3&gt;
  
  
  First, Without The Exception Tool
&lt;/h3&gt;

&lt;p&gt;For the first run, I kept the monitoring tool that retrieves exceptions disabled.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fsyr24ym4yfdzphqna5dc.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fsyr24ym4yfdzphqna5dc.png" alt="ABP AI Coding Agent with exception monitoring tool disabled" width="688" height="228"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Then I asked:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Can you get the latest exception from ABP Studio Monitoring and explain what failed?

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

&lt;/div&gt;



&lt;p&gt;At this point, the agent did not have direct access to the exception tool. So it tried to reason around the problem in other ways. It looked for available information, tried to inspect logs from files, and checked the codebase to understand what might be happening.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fozh8keyxbhncl9d3lccv.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fozh8keyxbhncl9d3lccv.png" alt="ABP AI Coding Agent result without exception tool access" width="563" height="733"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;That is still useful in some cases, but it is not the best debugging flow. The agent is spending time and context trying to reconstruct a runtime failure indirectly. It may eventually find the suspicious code, but it is working from weaker evidence.&lt;/p&gt;

&lt;p&gt;This is exactly why tool access matters. Without the monitoring tool, the agent can reason from files. With the monitoring tool, it can inspect the actual runtime failure.&lt;/p&gt;

&lt;h3&gt;
  
  
  Then, With The Exception Tool Enabled
&lt;/h3&gt;

&lt;p&gt;For the second run, I enabled the monitoring tool that can retrieve exceptions, such as &lt;code&gt;get_exceptions&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Feevw8n5hvnhcgx10it54.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Feevw8n5hvnhcgx10it54.png" alt="ABP AI Coding Agent with exception monitoring tool enabled" width="683" height="233"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Then I asked:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Now the get_exceptions tool is enabled. Please get the latest exception from ABP Studio Monitoring, identify the failing code path, and suggest the smallest fix.

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

&lt;/div&gt;



&lt;p&gt;This time, the behavior was very different. The agent directly used the integrated exception tool, retrieved the exception details from ABP Studio Monitoring, and connected the runtime error to the failing code path.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fx1sb2s5qvxzgy3fm5du8.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fx1sb2s5qvxzgy3fm5du8.png" alt="ABP AI Coding Agent using get_exceptions result" width="558" height="781"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;It also used the enabled logging tool to verify the surrounding context instead of guessing from the source code alone.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F11ur8lytbavxyu8hmrij.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F11ur8lytbavxyu8hmrij.png" alt="ABP AI Coding Agent correlating exception with logs" width="575" height="631"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;That is the workflow I want from an integrated coding agent. It does not only say "this code looks suspicious." It checks the exception, follows the evidence, finds the source of the problem, and proposes the smallest fix.&lt;/p&gt;

&lt;p&gt;It is also faster and more efficient. The agent does not need to spend as many tokens searching for indirect clues because ABP Studio can provide the runtime signal directly.&lt;/p&gt;

&lt;h3&gt;
  
  
  Adding Logs And Requests
&lt;/h3&gt;

&lt;p&gt;After that, I asked the agent to use the available monitoring tools together:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Use the available monitoring tools to check the related logs and recent requests for this failure. Tell me whether they confirm the same root cause.

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

&lt;/div&gt;



&lt;p&gt;At this point, the agent used the enabled tools for exceptions, requests, and logs.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fv2z96o4b4u49dxxhj7gu.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fv2z96o4b4u49dxxhj7gu.png" alt="ABP AI Coding Agent checking monitoring tools together" width="551" height="308"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F1gq0jwt3gkpwd6j495cb.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F1gq0jwt3gkpwd6j495cb.png" alt="ABP AI Coding Agent reviewing exception, request, and log details" width="556" height="533"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;This is where the ABP Studio integration becomes even more valuable. A runtime problem is rarely just one line of code. There is usually a request, a log entry, an exception, and a running application context around it.&lt;/p&gt;

&lt;p&gt;When these tools are available together, the agent can correlate them. It can say, "this request caused this exception, the logs confirm it, and this code path is responsible."&lt;/p&gt;

&lt;p&gt;That is much better than asking the developer to copy each piece manually into the chat.&lt;/p&gt;

&lt;h3&gt;
  
  
  Validating The Fix
&lt;/h3&gt;

&lt;p&gt;Finally, I asked the agent to validate the result:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Run the available build or application validation tools and confirm that the problem is fixed.

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

&lt;/div&gt;



&lt;p&gt;The agent used ABP Studio tools to stop the application, build it, and run it again.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F80pkvl0n5wfrj6dbjslf.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F80pkvl0n5wfrj6dbjslf.png" alt="ABP AI Coding Agent validating the fix with ABP Studio tools" width="799" height="398"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;This completes the loop. The agent did not only identify the problem. It used the integrated tools to move through the full flow:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Read the runtime exception.&lt;/li&gt;
&lt;li&gt;Correlate it with logs and requests.&lt;/li&gt;
&lt;li&gt;Find the failing code path.&lt;/li&gt;
&lt;li&gt;Apply or suggest the focused fix.&lt;/li&gt;
&lt;li&gt;Validate the application again.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;That is the difference I want to highlight in this article. ABP AI Coding Agent is not only a model that can edit files. When ABP Studio tools are enabled, it can participate in the same development workflow I use: observing the running application, understanding the failure, fixing the code, and validating the result.&lt;/p&gt;

&lt;h2&gt;
  
  
  Why This Is Different From Generic Coding Agents
&lt;/h2&gt;

&lt;p&gt;Tools like Cursor, Claude Code, and Codex are powerful. They can read code, edit files, run commands, and help with many software projects.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;ABP AI Coding Agent has a different advantage: it is built for the ABP development experience.&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;It is aware of ABP concepts, ABP solution structure, ABP Studio run profiles, application metadata, containers, monitoring, tasks, and build actions. It is also backed by the ABP Platform: ABP Framework, ABP Commercial, ABP Suite, ABP Studio, and the workflows that connect them.&lt;/p&gt;

&lt;p&gt;That platform context matters. When I am building an ABP solution, I do not only want a model that can write C# or TypeScript. I want an assistant that understands the way ABP applications are structured and the way ABP Studio runs them.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;That makes the starting point simple:&lt;/strong&gt; &lt;em&gt;open the ABP solution, use ABP Studio, &lt;a href="https://abp.io/community/articles/deep-dive-on-abp-ai-agent-1-agent-plan-and-ask-modes-62wteg9t" rel="noopener noreferrer"&gt;choose the right mode&lt;/a&gt;, enable the tools needed for the task, and work with the agent inside the platform.&lt;/em&gt;&lt;/p&gt;

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

&lt;p&gt;Integrated tools make ABP AI Coding Agent more than a chat window next to the code. They give it a controlled way to work with the same ABP Studio context I already use: &lt;strong&gt;applications&lt;/strong&gt; , &lt;strong&gt;containers&lt;/strong&gt; , &lt;strong&gt;monitoring&lt;/strong&gt; , &lt;strong&gt;tasks&lt;/strong&gt; , and &lt;strong&gt;build actions&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;-&amp;gt; &lt;strong&gt;That helps the agent move from "I think this might be the problem" to "I checked the runtime evidence, found the related code, applied the fix, and validated it."&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;That is the real value of this part of ABP Studio AI. It brings the coding agent closer to the full development experience, from understanding the solution to running it, observing it, fixing it, and checking the result.&lt;/p&gt;

&lt;p&gt;As ABP Studio evolves, more tools can be added to this workflow. That means the agent can become more useful over time without changing the basic idea: &lt;em&gt;ABP AI Coding Agent works best when it is not isolated from the platform, but integrated into it.&lt;/em&gt;&lt;/p&gt;

</description>
      <category>abpframework</category>
      <category>abpstudio</category>
      <category>tool</category>
      <category>ai</category>
    </item>
    <item>
      <title>Deep Dive on ABP AI Agent #3: Rules, Skills and Lessons</title>
      <dc:creator>ABP.IO</dc:creator>
      <pubDate>Thu, 11 Jun 2026 06:09:39 +0000</pubDate>
      <link>https://dev.to/abp_io/deep-dive-on-abp-ai-agent-3-rules-skills-and-lessons-4m2e</link>
      <guid>https://dev.to/abp_io/deep-dive-on-abp-ai-agent-3-rules-skills-and-lessons-4m2e</guid>
      <description>&lt;h1&gt;
  
  
  Deep Dive on ABP AI Agent #3: Rules, Skills and Lessons
&lt;/h1&gt;

&lt;p&gt;Every new chat starts from zero.&lt;/p&gt;

&lt;p&gt;The model can write the API, the permissions, and the UI. What disappears between sessions is everything specific to &lt;em&gt;this&lt;/em&gt; solution: your naming rules, your feature checklist, the demo-data fix from yesterday. You end up re-teaching before you start building.&lt;/p&gt;

&lt;p&gt;That is not an intelligence problem. The model is already capable enough. What does not carry over is the setup around it.&lt;/p&gt;

&lt;h2&gt;
  
  
  Agent, Model, and Harness
&lt;/h2&gt;

&lt;p&gt;Three terms get mixed up constantly. Keeping them separate makes everything else in this article click.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;The model&lt;/strong&gt; is the LLM: the weights, the training data, the reasoning engine. You do not get to change it. You pick which one to use, and that is about it.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;The harness&lt;/strong&gt; is everything wrapped around the model so it can actually finish work in &lt;em&gt;your&lt;/em&gt; environment: the system prompt, the tools it can call, the files it may read, the checks that run on its output, the limits on what it may touch, and the instructions injected into every session. A raw model is not an agent. An agent is the model plus the harness. If you are not the model, you are building the harness.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;The agent&lt;/strong&gt; is the whole system: model plus harness, running in a loop until the task is done. The behaviour you experience is dominated by the harness, not just the model. &lt;a href="https://addyosmani.com/blog/agent-harness-engineering/" rel="noopener noreferrer"&gt;Addy Osmani&lt;/a&gt; puts the same idea plainly: &lt;em&gt;a decent model with a great harness beats a great model with a bad harness.&lt;/em&gt; The gap between what today's models can do and what you actually see them do in your codebase is mostly a harness gap.&lt;/p&gt;

&lt;p&gt;The habit that makes a harness good is simple: treat every mistake as a reason to update the setup, not just to fix the output in chat. Each time the agent gets something wrong, change what it always knows, can look up, or carries forward so it cannot repeat the same error. Do that consistently and the agent stops repeating mistakes and starts working the way your team works.&lt;/p&gt;

&lt;p&gt;In &lt;strong&gt;ABP Studio AI Coding Agent&lt;/strong&gt; , the harness includes three pieces that hold what the agent knows about your solution: &lt;strong&gt;Rules&lt;/strong&gt; , &lt;strong&gt;Skills&lt;/strong&gt; , and &lt;strong&gt;Lessons&lt;/strong&gt;. They live under &lt;strong&gt;Settings &amp;gt; Rules &amp;amp; Skills&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fkudoxnql5kt60to5lcsg.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fkudoxnql5kt60to5lcsg.png" alt="The Rules &amp;amp; Skills settings page in ABP Studio" width="800" height="295"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Rules: The House Rules On The Wall
&lt;/h2&gt;

&lt;p&gt;A &lt;strong&gt;Rule&lt;/strong&gt; is a standing instruction that the harness injects into the agent's context on every turn of every session. In harness-engineering terms, it is always-on context injection: the same role as an &lt;code&gt;AGENTS.md&lt;/code&gt; or &lt;code&gt;CLAUDE.md&lt;/code&gt; at the root of a repo, except scoped to your solution or your profile inside ABP Studio.&lt;/p&gt;

&lt;p&gt;Think of the house rules pinned to a kitchen wall: how we do things here, every shift, no exceptions. Unlike a glance at a poster, though, a Rule is not ambient background. It is read on every turn, so it costs context budget every time the agent acts.&lt;/p&gt;

&lt;p&gt;ABP Agent already follows the framework's own conventions by default. It goes through the data layer instead of hitting the database directly, uses the built-in permission system instead of hand-rolled checks, and reads user-facing text from the translation files instead of hardcoding it. You do not write those down.&lt;/p&gt;

&lt;p&gt;Rules are where &lt;em&gt;your&lt;/em&gt; conventions go. The ones the framework cannot guess because they belong to your solution and your team. The good part is that these read like plain conventions any developer would recognize, not framework trivia:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;New code goes in the same place, with the same naming, as the code already around it.&lt;/li&gt;
&lt;li&gt;Every endpoint that changes data checks permissions before it does anything.&lt;/li&gt;
&lt;li&gt;Money is stored in minor units (cents), never as a floating-point number.&lt;/li&gt;
&lt;li&gt;API errors use our standard response shape, never ad-hoc JSON.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;With those on the wall, I stop repeating them. Instead of writing this:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Add a Category screen. Use our data layer, do not query the database
directly. Check permissions on every write. Keep money in cents. Put the
files where the other features live.

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

&lt;/div&gt;



&lt;p&gt;I can write this:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Add a Category screen.

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

&lt;/div&gt;



&lt;p&gt;The conventions are not renegotiated, one prompt at a time, in every session. The agent applies them the way a teammate who has read the contributor guide would.&lt;/p&gt;

&lt;p&gt;This matters more than it looks. A model with no standing rules does not stay neutral. It falls back on the defaults from its training data. Ask a generic model for "an endpoint that returns categories" and you often get one with the database query written straight into the handler, because that is the most common shape on the public internet. Rules steer the agent's output back toward &lt;em&gt;your&lt;/em&gt; codebase.&lt;/p&gt;

&lt;p&gt;One caution: treat the rule list like a pilot's checklist, not a style guide. A Rule is injected on every turn, so keep the list short. Every line should earn its place, ideally traceable to a real failure or a hard constraint you cannot ignore. If a line does not change what the agent produces, it is noise, and it dilutes the rules that actually matter.&lt;/p&gt;

&lt;p&gt;You also choose where a rule is saved. Global rules sit under your user profile and apply to every solution on your machine. Solution rules apply only to the current solution, and only while it is open.&lt;/p&gt;

&lt;h2&gt;
  
  
  Skills: Recipes You Pull Off The Shelf
&lt;/h2&gt;

&lt;p&gt;A &lt;strong&gt;Skill&lt;/strong&gt; is the same kind of note as a Rule, but loaded only when the task calls for it. The harness keeps a short description of each skill in context at all times; the agent reads the full text only when that description matches what you asked it to do. Harness engineers call this &lt;strong&gt;progressive disclosure&lt;/strong&gt; : reveal instructions and detail only when they are needed, instead of stuffing everything into the opening prompt.&lt;/p&gt;

&lt;p&gt;A recipe is not pinned to the wall. It sits in a drawer, and the cook pulls it out only when making that dish. That is a Skill.&lt;/p&gt;

&lt;p&gt;In ABP Studio, a rule and a skill are the same kind of note with one switch between them.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fr2rbvee0qas3ykx7mlzz.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fr2rbvee0qas3ykx7mlzz.png" alt="Creating a rule or a skill: the Always Apply toggle decides which one it is" width="635" height="586"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;You write a name (which becomes the file name) and some content, then set &lt;strong&gt;Always Apply&lt;/strong&gt;. Turn it on and the note is a Rule, always in context. Turn it off and the note is a Skill, fetched on demand. So if a skill turns out to be something the agent should never skip, you flip one switch and it becomes a rule.&lt;/p&gt;

&lt;p&gt;My favorite example of a skill is a "build a feature end to end" checklist: the ordered steps your team follows to ship one complete feature. The data model comes first, then the data access layer, then the application service, then the API, then the translations, then the UI, and finally the demo data. Write it once, and the agent follows it whenever it builds a feature, instead of inventing its own order each time.&lt;/p&gt;

&lt;p&gt;Writing a skill is like writing an onboarding note for a new teammate. You are not making them smarter. You are saving them the week it would take to work out how your team does this one thing.&lt;/p&gt;

&lt;p&gt;A skill can be long without slowing anything down. The agent sees only the short description of each skill at all times, and reads the full text only when the description matches the task. The detail stays in the drawer until it is needed.&lt;/p&gt;

&lt;p&gt;Skills are also portable. A skill is plain Markdown, so you can import one written elsewhere instead of retyping it.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fhmrocc3szwkb2hw6gdaq.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fhmrocc3szwkb2hw6gdaq.png" alt="Importing skills from .md files into a solution or your global profile" width="612" height="508"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;You point the importer at a file or a folder of &lt;code&gt;.md&lt;/code&gt; files and choose whether they go into the current solution or your global profile. A procedure one person worked out can travel to the rest of the team, or to your next solution.&lt;/p&gt;

&lt;p&gt;A simple test for which one to reach for:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;If it should hold no matter what you are doing, it is a &lt;strong&gt;Rule&lt;/strong&gt;. ("Always return errors in our standard shape.")&lt;/li&gt;
&lt;li&gt;If it is a procedure you follow only for a certain kind of task, it is a &lt;strong&gt;Skill&lt;/strong&gt;. ("Here is our checklist for adding a feature end to end.")&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Lessons: What The Agent Learns On Its Own
&lt;/h2&gt;

&lt;p&gt;Rules and Skills are written by a person. &lt;strong&gt;Lessons&lt;/strong&gt; are written by the agent.&lt;/p&gt;

&lt;p&gt;Think of a shift handoff log in the kitchen: after something goes wrong, someone writes down what happened and what to do differently next time. The next cook reads it before repeating the same work. In ABP Studio, you correct the mistake; the agent records the verified fix so future sessions do not trip over it again. That entry is a Lesson.&lt;/p&gt;

&lt;p&gt;When the agent gets something wrong and is corrected, by you, by a failing build, or by the official ABP documentation, it can record the correction as a short, verified note. The harness carries that note into later turns and later sessions as high-priority context. That is the &lt;code&gt;ai-learned-lessons&lt;/code&gt; entry you saw in the settings list, growing as the agent works in your solution.&lt;/p&gt;

&lt;p&gt;The idea behind it is simple: when the agent gets something wrong, fix it once so it never gets it wrong the same way again. You already do a version of this when you edit a notes file by hand. Lessons do the recording for you, at the moment of the correction, when the reason is still fresh and you would otherwise forget to write it down.&lt;/p&gt;

&lt;p&gt;Here is the shape of a real one. I ask the agent to add a &lt;code&gt;Category&lt;/code&gt; screen. It builds the data model, the API, and the UI, but forgets to grant the new permission in the demo data. The screen compiles, the API responds, everything looks finished, and the only symptom is that real users get "access denied." I correct it, the agent fixes the demo data, and it records a note: in this solution, a new permission is not done until it is granted in the demo data. Next time, it remembers.&lt;/p&gt;

&lt;p&gt;A Lesson is not a general fact about ABP. The documentation already covers that, and the agent reads it. A Lesson is a solution-specific, verified correction: the kind of knowledge a team usually keeps in people's heads and loses when they move on. Here it is written down the moment it is learned, instead of weeks later, if ever.&lt;/p&gt;

&lt;h2&gt;
  
  
  When To Use Which?
&lt;/h2&gt;

&lt;p&gt;Once the three are clear, they sort themselves out:&lt;/p&gt;

&lt;p&gt;| Mechanism | Who writes it | When it loads | Answers | | --- | --- | --- | --- | | &lt;strong&gt;Rules&lt;/strong&gt; | You | Always, every turn | "What must always be true here?" | | &lt;strong&gt;Skills&lt;/strong&gt; | You | When relevant to the task | "How do we do this kind of task?" | | &lt;strong&gt;Lessons&lt;/strong&gt; | The agent | In later sessions, as high-priority context | "What did we get wrong before in this solution?" |&lt;/p&gt;

&lt;p&gt;They feed into each other. A Lesson that keeps coming back is a sign it should be promoted. If the agent records the same correction again and again, it is no longer a one-off note. It belongs in a Rule, if it is an always-true convention, or in a Skill, if it is a procedure. Knowledge tends to move from Lessons toward Rules and Skills: the agent finds the pattern by tripping over it, and you write it down properly once it has earned the spot.&lt;/p&gt;

&lt;p&gt;Keep an eye on the budget, because all three share the same limited space, the context window. Rules cost the most, since they are always present, so an overstuffed rule list weighs down every turn and crowds out the rules that matter. Skills are cheaper, paid for only when used, which is a good reason to move anything situational out of Rules and into a Skill. Lessons add up too, so clear out the stale ones now and then. The point is not to write down everything you know. It is to write down the few things that change what the agent produces, and put each one where it loads at the right time.&lt;/p&gt;

&lt;h2&gt;
  
  
  Why This Is Different From Generic Coding Agents?
&lt;/h2&gt;

&lt;p&gt;To be fair, the ideas themselves are not unique to ABP. Tools like Cursor, Claude Code, Codex, and Windsurf are strong general-purpose coding tools, and they already have always-on instruction files and on-demand skill files. If all you compare is whether a tool can hold rules and skills, there is no real difference, and I would not pretend otherwise.&lt;/p&gt;

&lt;p&gt;The difference is in the two places where a generic tool has to guess.&lt;/p&gt;

&lt;p&gt;The first is memory of its own mistakes. In most tools, remembering a past mistake means you stop and edit a memory file by hand. Lessons remove that step. The agent records the correction itself, the moment it happens, so the knowledge survives without you having to maintain it.&lt;/p&gt;

&lt;p&gt;The second, and the one that matters most, is what all of this sits on. A generic instruction file can say "use the data layer," but the tool still has to read your files and guess what the data layer is and where it lives. ABP Agent does not guess. It starts every session with an ABP-aware map of your solution, and when it is unsure how something should be done, it checks the official ABP documentation instead of the average of the public internet. So the rules and skills you write are not hints dropped into a tool that barely understands the project. They are backed by one that already knows the framework underneath them.&lt;/p&gt;

&lt;p&gt;There is also a smaller convenience worth a line: a rule and a skill are the same note with one switch between them, scoped to the solution or your profile from the same place, instead of two separate file formats to keep track of.&lt;/p&gt;

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

&lt;p&gt;It is best to see the three as one system, not three switches.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Rules&lt;/strong&gt; hold the conventions you refuse to repeat.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Skills&lt;/strong&gt; hold the procedures you want to reuse.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Lessons&lt;/strong&gt; hold the corrections the agent learns as it works.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Together they answer the blank-memory problem. They do not give the model new weights. They inject, at the start of each session, the smallest useful set of instructions so the agent can work as if it has been here before.&lt;/p&gt;

&lt;p&gt;A generic agent with no harness tuning stays roughly as capable on its thousandth task in your codebase as on its first. With Rules, Skills, and Lessons, ABP Agent accumulates solution-specific knowledge over time, especially through Lessons and the promotions you make from them.&lt;/p&gt;

&lt;p&gt;That is the real value: &lt;strong&gt;not just an AI that writes code, but one that learns how your solution is built and keeps that knowledge from one session to the next.&lt;/strong&gt;&lt;/p&gt;

</description>
      <category>abpframework</category>
      <category>abp</category>
      <category>ai</category>
    </item>
    <item>
      <title>Deep Dive on ABP AI Agent #2: Supported AI Models in ABP Studio + Usage Recommendations</title>
      <dc:creator>ABP.IO</dc:creator>
      <pubDate>Wed, 10 Jun 2026 05:51:56 +0000</pubDate>
      <link>https://dev.to/abp_io/deep-dive-on-abp-ai-agent-2-supported-ai-models-in-abp-studio-usage-recommendations-4km</link>
      <guid>https://dev.to/abp_io/deep-dive-on-abp-ai-agent-2-supported-ai-models-in-abp-studio-usage-recommendations-4km</guid>
      <description>&lt;h1&gt;
  
  
  Deep Dive on ABP AI Agent #2: Supported AI Models in ABP Studio + Usage Recommendations
&lt;/h1&gt;

&lt;p&gt;There is one question I ask almost as often as "Which mode should I use?":&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Which model should do this work?&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;At first, it is tempting to answer that question by always choosing the strongest model in the list. That feels safe. If a model is more capable, why not use it for everything?&lt;/p&gt;

&lt;p&gt;In real work, I do not think about it that way.&lt;/p&gt;

&lt;p&gt;When I use &lt;strong&gt;ABP Studio AI&lt;/strong&gt; , the model choice is part of the workflow. Some tasks need careful reasoning. Some need speed. Some need a large context window. Some need image support because the browser or a screenshot is involved. Some are small text-processing tasks where using the most capable model would only make the work slower and more expensive.&lt;/p&gt;

&lt;p&gt;So I treat model selection as a practical decision, not a trophy selection.&lt;/p&gt;

&lt;h2&gt;
  
  
  What ABP Studio Supports Today
&lt;/h2&gt;

&lt;p&gt;ABP Studio gives me a curated model setup by default. It keeps the first experience simple, while still letting me choose from a broader model catalog when I want to tune the setup for a specific kind of work.&lt;/p&gt;

&lt;p&gt;The important word here is &lt;strong&gt;focused&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;ABP Studio does not treat every model as an equally good choice for agent work. A coding agent needs things like a useful context window, tool support, text output, and reliable behavior in repeated agent loops. Studio keeps the model experience closer to that reality.&lt;/p&gt;

&lt;p&gt;The built-in model set currently includes:&lt;/p&gt;

&lt;p&gt;| Model | How I think about it | | --- | --- | | Claude Sonnet 4.6 | The default main model for day-to-day Ask, Plan, and Agent work. | | Claude Haiku 4.5 | A fast supporting model for research, browser work, and lightweight text processing. | | Claude Opus 4.7 | A stronger option when the task needs deeper reasoning or more careful review. | | GPT-5.5 | Another strong option for main conversations or review-style work. | | GLM-5.1 | A text/code option for tasks that do not need image input. |&lt;/p&gt;

&lt;p&gt;I do not read this list as a ranking. I read it as the default toolbox.&lt;/p&gt;

&lt;p&gt;And it is not a closed box. If I need a different model for a specific task, I can open the Models settings, search the catalog, filter by category, and add more models to my selection.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fxlncjgcrf3ip5k57brq2.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fxlncjgcrf3ip5k57brq2.png" alt="ABP Studio Models settings showing the selectable model catalog" width="799" height="426"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;That is an important distinction. The built-in models are there so I can start with sensible defaults. They are not there to force every team, every solution, or every workflow into the same model choices.&lt;/p&gt;

&lt;h2&gt;
  
  
  The Main Model
&lt;/h2&gt;

&lt;p&gt;The main model is the one I feel most directly in the conversation.&lt;/p&gt;

&lt;p&gt;It is used when I ask questions, create plans, or let ABP Agent work through an implementation. This is the model behind the normal flow of the chat.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fzemn7svp4cbpbxc6hpae.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fzemn7svp4cbpbxc6hpae.png" alt="ABP Agent model selector showing the current conversation model" width="360" height="610"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;For most work, I keep a balanced model as the main model. A Sonnet-style model is a good default because it is capable enough for real development tasks without making every small question feel heavy.&lt;/p&gt;

&lt;p&gt;This is the model I use for:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Understanding a module or package&lt;/li&gt;
&lt;li&gt;Planning a feature before editing code&lt;/li&gt;
&lt;li&gt;Applying a reviewed plan&lt;/li&gt;
&lt;li&gt;Fixing ordinary build or test failures&lt;/li&gt;
&lt;li&gt;Making changes where the expected result is easy to review&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;When the task gets broader, I become more intentional.&lt;/p&gt;

&lt;p&gt;If I am asking ABP Agent to reason across several modules, plan a risky refactor, review architecture, or inspect a subtle regression, I am more willing to switch to a stronger model. The extra capability is useful when the cost of a shallow answer is high.&lt;/p&gt;

&lt;p&gt;For a quick localization change or a small DTO update, that same choice can be wasteful. The strongest model is not always the best model for the moment.&lt;/p&gt;

&lt;h2&gt;
  
  
  Role-Based Models
&lt;/h2&gt;

&lt;p&gt;One detail I like in ABP Studio AI is that model selection is not only one global dropdown.&lt;/p&gt;

&lt;p&gt;Studio separates the main conversation model from supporting model roles. That means I can keep the main model strong enough for the conversation while using lighter models for background work.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fr8y89roso46k1elfhr4q.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fr8y89roso46k1elfhr4q.png" alt="ABP Agent model settings for main, research, browser, and text processor models" width="560" height="840"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;The roles are easier to understand if I describe them by how they feel in daily use.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Main Model&lt;/strong&gt; is the model I am actively talking to. It carries the normal Ask, Plan, and Agent experience.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Research Model&lt;/strong&gt; is for research and ABP documentation searcher work. I usually keep this lightweight because research often involves gathering, narrowing, and summarizing context before the main model decides what to do with it.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Browser Model&lt;/strong&gt; is used by the browser subagent in Agent mode. This role should stay fast and practical. When browser screenshots are involved, I choose a model that supports image input. A text-only model may be fine for code, but it is not the right fit when the work depends on seeing the UI.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Text Processor Model&lt;/strong&gt; is for smaller language tasks such as summarizing errors, generating commit messages, or consolidating learned lessons. This is exactly where I do not want to spend the most capable model every time.&lt;/p&gt;

&lt;p&gt;The Git Review model is separate too.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fhi2omyes5e9w19y7w79b.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fhi2omyes5e9w19y7w79b.png" alt="ABP Agent model settings for Git Review model selection" width="560" height="560"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;For AI Review, I like the "Ask me every time" behavior. Some reviews are routine. Some reviews deserve a stronger model because the change is large, security-sensitive, or touches architecture. Asking each time keeps that decision close to the actual change.&lt;/p&gt;

&lt;p&gt;If a team wants consistent review behavior, a fixed review model also makes sense. The key is that Git Review does not have to silently follow the same model I use for ordinary chat.&lt;/p&gt;

&lt;h2&gt;
  
  
  How I Choose In Practice
&lt;/h2&gt;

&lt;p&gt;For quick questions, I use the default main model.&lt;/p&gt;

&lt;p&gt;If I am asking "Where is this permission defined?" or "Why does this module reference that package?", I do not need to overthink the model. I want a clear answer and maybe a few source references.&lt;/p&gt;

&lt;p&gt;For planning larger work, I use a stronger main model when the decision matters.&lt;/p&gt;

&lt;p&gt;Plan mode is where the model can save me from an expensive wrong turn. If the change crosses layers, modules, permissions, UI, or database behavior, I prefer a model that can hold more context and reason carefully. I still narrow the scope where possible, because a focused prompt usually beats a huge unfocused one.&lt;/p&gt;

&lt;p&gt;For implementation, I care about reliability more than raw size.&lt;/p&gt;

&lt;p&gt;Agent mode is not only about generating code. It is about reading the solution, editing files, running checks, seeing failures, and trying again. A good main model should follow instructions consistently and use tools well. For supporting roles, I usually keep the lighter defaults.&lt;/p&gt;

&lt;p&gt;For UI and browser tasks, I check image support.&lt;/p&gt;

&lt;p&gt;If the task involves screenshots, browser interaction, visual verification, or UI state, the browser model needs to be able to understand images. This is one reason I do not treat every text/code model as interchangeable.&lt;/p&gt;

&lt;p&gt;For reviews, I choose based on risk.&lt;/p&gt;

&lt;p&gt;A small formatting or localization change does not need the same review setup as a large change in authorization, multi-tenancy, persistence, or distributed behavior. For deeper reviews, I am willing to use a stronger model because the goal is not speed. The goal is to catch what I missed.&lt;/p&gt;

&lt;p&gt;For cost and latency, I avoid using the strongest model everywhere.&lt;/p&gt;

&lt;p&gt;This is not only about credits. It is also about pace. If every background task uses a heavy model, the development loop feels slower. Keeping lightweight models for lightweight jobs makes ABP Studio AI feel more responsive.&lt;/p&gt;

&lt;h2&gt;
  
  
  A Simple Rule
&lt;/h2&gt;

&lt;p&gt;The model name matters less than the job.&lt;/p&gt;

&lt;p&gt;When I choose a model in ABP Studio AI, I usually ask:&lt;/p&gt;

&lt;p&gt;| Situation | Model choice I prefer | | --- | --- | | Normal Ask, Plan, or Agent work | Balanced main model | | Broad planning or risky implementation | Stronger main model | | Research and documentation lookup | Lightweight supporting model | | Browser tasks with screenshots | Vision-capable browser model | | Error summaries and commit messages | Lightweight text processor model | | Important AI Review | Stronger model or ask each time |&lt;/p&gt;

&lt;p&gt;That keeps the experience practical.&lt;/p&gt;

&lt;p&gt;Modes decide how much action I want: Ask, Plan, or Agent.&lt;/p&gt;

&lt;p&gt;Models decide which brain should handle the work.&lt;/p&gt;

&lt;p&gt;When those two choices are made intentionally, ABP Studio AI feels less like one generic AI button and more like a set of tools I can tune for the task in front of me.&lt;/p&gt;

</description>
      <category>abpstudio</category>
      <category>ai</category>
      <category>models</category>
      <category>gpt</category>
    </item>
    <item>
      <title>Deep Dive on ABP AI Agent #1: Agent, Plan and Ask Modes</title>
      <dc:creator>ABP.IO</dc:creator>
      <pubDate>Tue, 09 Jun 2026 07:52:20 +0000</pubDate>
      <link>https://dev.to/abp_io/deep-dive-on-abp-ai-agent-1-agent-plan-and-ask-modes-208e</link>
      <guid>https://dev.to/abp_io/deep-dive-on-abp-ai-agent-1-agent-plan-and-ask-modes-208e</guid>
      <description>&lt;h1&gt;
  
  
  Deep Dive on ABP AI Agent #1: Agent, Plan and Ask Modes
&lt;/h1&gt;

&lt;p&gt;There is a small question I like to answer before I type anything into &lt;strong&gt;ABP Agent&lt;/strong&gt; :&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Do I want an answer, a plan, or action?&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;That question looks simple, but it changes the whole experience. Sometimes I am only trying to understand why a module is structured a certain way. Sometimes I already know the direction, but I want the implementation path checked before touching files. And sometimes the task is clear enough that I want ABP Agent to do the work, run the checks, and iterate with me.&lt;/p&gt;

&lt;p&gt;That is where the three modes in ABP Studio AI become more than labels. They help me choose the right level of trust, risk, and action for the moment.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fya9y4680ssso7y23e70e.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fya9y4680ssso7y23e70e.png" alt="ABP Agent mode picker showing Agent, Plan, and Ask" width="174" height="118"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Ask Mode: When I Want To Understand
&lt;/h2&gt;

&lt;p&gt;&lt;strong&gt;Ask&lt;/strong&gt; is the mode I reach for when I want to stay in learning mode.&lt;/p&gt;

&lt;p&gt;It is useful when I am reading a solution and want to ask questions like:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;What is this module responsible for?&lt;/li&gt;
&lt;li&gt;Why is this permission checked here?&lt;/li&gt;
&lt;li&gt;How does this application service relate to the domain layer?&lt;/li&gt;
&lt;li&gt;What would happen if I changed this setting, dependency, or flow?&lt;/li&gt;
&lt;li&gt;Which ABP concept should I use for this requirement?&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;The important part is that Ask mode is read-only. I can explore the codebase, ABP concepts, architecture, or possible approaches without worrying that files will be changed as a side effect of the conversation.&lt;/p&gt;

&lt;p&gt;That makes it a comfortable starting point. I do not need to prepare a perfect prompt. I can ask a rough question, follow up with more context, and slowly turn uncertainty into something clearer.&lt;/p&gt;

&lt;p&gt;For me, Ask mode is especially helpful when I join a solution after some time away. Instead of jumping between files and trying to rebuild the story manually, I can ask ABP Agent to explain the shape of the solution in the language of ABP: modules, layers, permissions, application services, entities, settings, events, and runtime pieces.&lt;/p&gt;

&lt;h2&gt;
  
  
  Plan Mode: When I Want To Think Before Changing Code
&lt;/h2&gt;

&lt;p&gt;&lt;strong&gt;Plan&lt;/strong&gt; is the mode I use when the next step is probably implementation, but I do not want to start editing yet.&lt;/p&gt;

&lt;p&gt;This is the middle ground between a conversation and a code change. ABP Agent can inspect the solution in a read-only way, ask clarifying questions when the requirement is not clear enough, and produce a structured plan before any file is modified.&lt;/p&gt;

&lt;p&gt;That changes the feeling of working with AI. Instead of saying "go build this" and reviewing only the result, I can review the approach first:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Which files will likely be affected?&lt;/li&gt;
&lt;li&gt;Which ABP layers are involved?&lt;/li&gt;
&lt;li&gt;Does the implementation path match the existing solution style?&lt;/li&gt;
&lt;li&gt;Are there missing decisions before the work starts?&lt;/li&gt;
&lt;li&gt;Is this a small change, or is it actually a larger workflow?&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;This is useful for changes that cross boundaries: adding a new entity, adjusting an application service, introducing a permission, changing a UI flow, or touching more than one module. Those are exactly the moments where I want a second pass before code starts moving.&lt;/p&gt;

&lt;p&gt;When a plan is active, ABP Studio gives me clear actions around it. I can view the plan, detach it if it is no longer the right direction, or apply it with Agent mode when I am ready to move from planning to implementation.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fu8rt6kqxrfhgfo8dt4sd.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fu8rt6kqxrfhgfo8dt4sd.png" alt="ABP Agent plan actions for viewing, detaching, or applying a plan" width="614" height="125"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;The small detail I like here is that the plan does not disappear into the chat history. It becomes something I can review and intentionally carry into the next step.&lt;/p&gt;

&lt;h2&gt;
  
  
  Agent Mode: When I Am Ready For Action
&lt;/h2&gt;

&lt;p&gt;&lt;strong&gt;Agent&lt;/strong&gt; is the mode I choose when I am ready to let ABP Agent work on the solution.&lt;/p&gt;

&lt;p&gt;This is the action mode. ABP Agent can edit files, run commands, build projects, use ABP Studio tasks, and iterate when something fails. It is the right choice when the task is clear enough and I am comfortable letting the agent make changes that I will review afterward.&lt;/p&gt;

&lt;p&gt;For small trusted tasks, I may go directly to Agent mode:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Add a missing localization entry&lt;/li&gt;
&lt;li&gt;Fix a straightforward build error&lt;/li&gt;
&lt;li&gt;Update a simple DTO mapping&lt;/li&gt;
&lt;li&gt;Add a validation rule that matches an existing pattern&lt;/li&gt;
&lt;li&gt;Apply a plan that I have already reviewed&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;For larger work, I prefer not to start here. Agent mode is powerful, and power is better when it is intentional. If I am not sure about the shape of the change, I usually start with Ask or Plan first.&lt;/p&gt;

&lt;h2&gt;
  
  
  A Practical Workflow
&lt;/h2&gt;

&lt;p&gt;The modes are most useful when I treat them as a workflow, not as three disconnected buttons.&lt;/p&gt;

&lt;p&gt;For larger changes, my usual flow is:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;strong&gt;Ask&lt;/strong&gt; to understand the area and the existing conventions.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Plan&lt;/strong&gt; to turn the requirement into a reviewable implementation path.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Agent&lt;/strong&gt; to apply the plan, build, and iterate.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;For learning, I often stay entirely in Ask mode. If I am trying to understand ABP multi-tenancy behavior, module dependencies, permission definitions, or why a solution is organized a certain way, there is no need to involve file changes.&lt;/p&gt;

&lt;p&gt;For small tasks, I may go directly to Agent mode. The key is that I already know what I want, the risk is low, and the expected result is easy to review.&lt;/p&gt;

&lt;p&gt;Here is the simple rule I keep in mind:&lt;/p&gt;

&lt;p&gt;| Situation | Mode I Choose | Why | | --- | --- | --- | | I need an explanation | Ask | It keeps the conversation read-only. | | I need a direction before implementation | Plan | It gives me a reviewable path before changes. | | I am ready for ABP Agent to work | Agent | It can edit, build, run tasks, and iterate. |&lt;/p&gt;

&lt;h2&gt;
  
  
  Why This Matters
&lt;/h2&gt;

&lt;p&gt;AI-assisted development can feel too fast when the tool moves from idea to code before I have decided what kind of help I actually need.&lt;/p&gt;

&lt;p&gt;The three modes slow that moment down in a good way. They let me say:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;"Just explain this."&lt;/li&gt;
&lt;li&gt;"Think through the change first."&lt;/li&gt;
&lt;li&gt;"Now implement it."&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;That separation makes the work feel more intentional. It also makes the output easier to review, because the mode already tells me what kind of result I should expect.&lt;/p&gt;

&lt;p&gt;Ask gives me understanding. Plan gives me a path. Agent gives me action.&lt;/p&gt;

&lt;p&gt;Used together, they make ABP Studio AI feel less like a single big button and more like a development partner that can adapt to the level of confidence I have at each step.&lt;/p&gt;

</description>
      <category>abpframework</category>
      <category>abp</category>
      <category>ai</category>
    </item>
    <item>
      <title>Implement Automatic Method-Level Caching in ABP Framework</title>
      <dc:creator>ABP.IO</dc:creator>
      <pubDate>Mon, 08 Dec 2025 07:16:29 +0000</pubDate>
      <link>https://dev.to/abp_io/implement-automatic-method-level-caching-in-abp-framework-1ckl</link>
      <guid>https://dev.to/abp_io/implement-automatic-method-level-caching-in-abp-framework-1ckl</guid>
      <description>&lt;h1&gt;
  
  
  Implement Automatic Method-Level Caching in ABP Framework
&lt;/h1&gt;

&lt;p&gt;Caching is one of the most effective ways to improve application performance, but implementing it manually for every method can be tedious and error-prone. What if you could cache method results automatically with just an attribute? In this article, we'll explore how to build an automatic method-level caching system in ABP Framework that handles cache invalidation, supports multiple scopes, and integrates seamlessly with your existing application.&lt;/p&gt;

&lt;p&gt;By the end of this guide, you'll understand how to implement attribute-based caching that automatically invalidates when entities change, supports user-specific and global caching scopes, and provides built-in metrics for monitoring cache performance.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;💡 &lt;strong&gt;Complete Implementation Available&lt;/strong&gt; : This article is based on a working demo project. You can find the complete implementation in the &lt;a href="https://github.com/salihozkara/AbpAutoCacheDemo" rel="noopener noreferrer"&gt;AbpAutoCacheDemo repository&lt;/a&gt;, with the core AutoCache library implementation available in &lt;a href="https://github.com/salihozkara/AbpAutoCacheDemo/commit/946df1fc07de6eddd26eb14013a09968cd59329b" rel="noopener noreferrer"&gt;this commit&lt;/a&gt;.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h2&gt;
  
  
  What is Automatic Method-Level Caching?
&lt;/h2&gt;

&lt;p&gt;Automatic method-level caching is a technique that intercepts method calls and caches their results without requiring manual cache management code. Instead of writing cache logic in every method, you simply decorate methods with attributes that define caching behavior.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fraw.githubusercontent.com%2Fabpframework%2Fabp%2Fdev%2Fdocs%2Fen%2FCommunity-Articles%2F2025-12-06-Implement-Automatic-Method-Level-Caching-in-ABP-Framework%2Fimages%2Fautomatic-caching-flow.svg" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fraw.githubusercontent.com%2Fabpframework%2Fabp%2Fdev%2Fdocs%2Fen%2FCommunity-Articles%2F2025-12-06-Implement-Automatic-Method-Level-Caching-in-ABP-Framework%2Fimages%2Fautomatic-caching-flow.svg" alt="Automatic Caching Flow" width="900" height="400"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;The key benefits include:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Reduced Boilerplate:&lt;/strong&gt; No repetitive cache management code in your business logic&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Consistent Caching Strategy:&lt;/strong&gt; Centralized cache configuration and behavior&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Smart Invalidation:&lt;/strong&gt; Automatic cache clearing when related entities change&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Multiple Scopes:&lt;/strong&gt; Support for global, user-specific, and entity-specific caching&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Built-in Monitoring:&lt;/strong&gt; Track cache hits, misses, and performance metrics&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Architecture Overview
&lt;/h2&gt;

&lt;p&gt;The automatic caching system consists of several key components working together:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fraw.githubusercontent.com%2Fabpframework%2Fabp%2Fdev%2Fdocs%2Fen%2FCommunity-Articles%2F2025-12-06-Implement-Automatic-Method-Level-Caching-in-ABP-Framework%2Fimages%2Farchitecture-diagram.svg" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fraw.githubusercontent.com%2Fabpframework%2Fabp%2Fdev%2Fdocs%2Fen%2FCommunity-Articles%2F2025-12-06-Implement-Automatic-Method-Level-Caching-in-ABP-Framework%2Fimages%2Farchitecture-diagram.svg" alt="Architecture Diagram" width="1000" height="700"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Core Components:&lt;/strong&gt;&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;strong&gt;CacheAttribute:&lt;/strong&gt; The attribute you apply to methods to enable automatic caching&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;AutoCacheInterceptor:&lt;/strong&gt; Intercepts method calls and handles cache operations&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;AutoCacheManager:&lt;/strong&gt; Manages cache storage, retrieval, and key generation&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;IAutoCacheKeyManager:&lt;/strong&gt; Handles cache key mapping and invalidation&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;AutoCacheInvalidationHandler:&lt;/strong&gt; Listens to entity changes and clears related caches&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;This architecture leverages ABP's dynamic proxy system and event bus to provide seamless caching without modifying your business logic.&lt;/p&gt;

&lt;h2&gt;
  
  
  Prerequisites
&lt;/h2&gt;

&lt;p&gt;Before implementing automatic caching, ensure you have:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;ABP Framework 10.0 or later&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Implementation
&lt;/h2&gt;

&lt;blockquote&gt;
&lt;p&gt;📦 &lt;strong&gt;Repository Structure&lt;/strong&gt; : The complete implementation is available in the &lt;a href="https://github.com/salihozkara/AbpAutoCacheDemo" rel="noopener noreferrer"&gt;AbpAutoCacheDemo repository&lt;/a&gt;. The AutoCache library is located in the &lt;code&gt;src/AutoCache&lt;/code&gt; folder, making it easy to extract and reuse in your own projects.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h3&gt;
  
  
  Step - 1: Create the AutoCache Module
&lt;/h3&gt;

&lt;p&gt;First, let's create a separate module for our caching infrastructure. This makes it reusable across projects.&lt;/p&gt;

&lt;h3&gt;
  
  
  Step - 1: Create the AutoCache Module
&lt;/h3&gt;

&lt;p&gt;First, let's create a separate module for our caching infrastructure. This makes it reusable across projects.&lt;/p&gt;

&lt;p&gt;Create &lt;code&gt;AutoCache.csproj&lt;/code&gt;:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;&amp;lt;Project Sdk="Microsoft.NET.Sdk"&amp;gt;
    &amp;lt;PropertyGroup&amp;gt;
        &amp;lt;TargetFramework&amp;gt;net10.0&amp;lt;/TargetFramework&amp;gt;
        &amp;lt;Nullable&amp;gt;enable&amp;lt;/Nullable&amp;gt;
    &amp;lt;/PropertyGroup&amp;gt;

    &amp;lt;ItemGroup&amp;gt;
        &amp;lt;PackageReference Include="Volo.Abp.Caching.StackExchangeRedis" Version="10.0.0" /&amp;gt;
        &amp;lt;PackageReference Include="Volo.Abp.Core" Version="10.0.0" /&amp;gt;
        &amp;lt;PackageReference Include="Volo.Abp.Ddd.Domain" Version="10.0.0" /&amp;gt;
    &amp;lt;/ItemGroup&amp;gt;
&amp;lt;/Project&amp;gt;

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

&lt;/div&gt;



&lt;p&gt;Create the module class &lt;code&gt;AutoCacheModule.cs&lt;/code&gt;:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;using Microsoft.Extensions.DependencyInjection;
using Volo.Abp.Caching.StackExchangeRedis;
using Volo.Abp.Domain;
using Volo.Abp.Modularity;

namespace AutoCache;

[DependsOn(typeof(AbpDddDomainModule), typeof(AbpCachingStackExchangeRedisModule))]
public class AutoCacheModule : AbpModule
{
    public override void PreConfigureServices(ServiceConfigurationContext context)
    {
        context.Services.OnRegistered(AutoCacheRegister.RegisterInterceptorIfNeeded); // 👈 Register interceptor
    }
}

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

&lt;/div&gt;



&lt;p&gt;This module automatically registers the cache interceptor for any class that uses the &lt;code&gt;CacheAttribute&lt;/code&gt;.&lt;/p&gt;

&lt;h3&gt;
  
  
  Step - 2: Define the Cache Attribute
&lt;/h3&gt;

&lt;p&gt;The &lt;code&gt;CacheAttribute&lt;/code&gt; is the core of our automatic caching system. It specifies which entities affect the cache and what scope to use.&lt;/p&gt;

&lt;p&gt;Create &lt;code&gt;CacheAttribute.cs&lt;/code&gt;:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;using System;
using Volo.Abp.Domain.Entities;

namespace AutoCache;

[AttributeUsage(AttributeTargets.Method)]
public class CacheAttribute : Attribute
{
    /// &amp;lt;summary&amp;gt;
    /// Entity types that affect this cache. When these entities change, the cache will be invalidated.
    /// &amp;lt;/summary&amp;gt;
    public Type[] InvalidateOnEntities { get; set; }

    /// &amp;lt;summary&amp;gt;
    /// Scope of the cache (Global, CurrentUser, AuthenticatedUser, or Entity)
    /// &amp;lt;/summary&amp;gt;
    public AutoCacheScope Scope { get; set; } = AutoCacheScope.Global;

    /// &amp;lt;summary&amp;gt;
    /// Absolute expiration time relative to now in milliseconds (0 = use default, -1 = disabled)
    /// &amp;lt;/summary&amp;gt;
    public long AbsoluteExpirationRelativeToNow { get; set; }

    /// &amp;lt;summary&amp;gt;
    /// Sliding expiration time in milliseconds (0 = use default, -1 = disabled)
    /// &amp;lt;/summary&amp;gt;
    public long SlidingExpiration { get; set; }

    public bool ConsiderUow { get; set; }

    public string AdditionalCacheKey { get; set; }

    public CacheAttribute(params Type[] invalidateOnEntities) // 👈 Specify entities that trigger cache invalidation
    {
        foreach (var entityType in invalidateOnEntities)
        {
            ArgumentNullException.ThrowIfNull(entityType);
            if (!typeof(IEntity).IsAssignableFrom(entityType))
            {
                throw new ArgumentException($"Type {entityType.FullName} must implement IEntity interface.");
            }
        }
        InvalidateOnEntities = invalidateOnEntities;
    }
}

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

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;Key Properties:&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;InvalidateOnEntities:&lt;/strong&gt; Array of entity types that, when modified, will clear this cache&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Scope:&lt;/strong&gt; Determines cache visibility (Global, CurrentUser, AuthenticatedUser, Entity)&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;AbsoluteExpirationRelativeToNow / SlidingExpiration:&lt;/strong&gt; Control cache lifetime&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  Step - 3: Define Cache Scopes
&lt;/h3&gt;

&lt;p&gt;Cache scopes determine how cache entries are partitioned. Create &lt;code&gt;AutoCacheScope.cs&lt;/code&gt;:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;using System;

namespace AutoCache;

[Flags]
public enum AutoCacheScope
{
    /// &amp;lt;summary&amp;gt;
    /// Cache is shared globally across all users
    /// &amp;lt;/summary&amp;gt;
    Global,

    /// &amp;lt;summary&amp;gt;
    /// Cache is scoped to the current user (based on user ID)
    /// &amp;lt;/summary&amp;gt;
    CurrentUser,

    /// &amp;lt;summary&amp;gt;
    /// Cache is scoped to authenticated vs unauthenticated users
    /// &amp;lt;/summary&amp;gt;
    AuthenticatedUser,

    /// &amp;lt;summary&amp;gt;
    /// Cache is scoped to the primary key of the entity involved
    /// &amp;lt;/summary&amp;gt;
    Entity
}

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

&lt;/div&gt;



&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fraw.githubusercontent.com%2Fabpframework%2Fabp%2Fdev%2Fdocs%2Fen%2FCommunity-Articles%2F2025-12-06-Implement-Automatic-Method-Level-Caching-in-ABP-Framework%2Fimages%2Fcache-scoping-diagram.svg" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fraw.githubusercontent.com%2Fabpframework%2Fabp%2Fdev%2Fdocs%2Fen%2FCommunity-Articles%2F2025-12-06-Implement-Automatic-Method-Level-Caching-in-ABP-Framework%2Fimages%2Fcache-scoping-diagram.svg" alt="Cache Scoping Strategy" width="1000" height="500"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;When to Use Each Scope:&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Global:&lt;/strong&gt; For data that's the same for all users (e.g., configuration, public lists)&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;CurrentUser:&lt;/strong&gt; For user-specific data (e.g., user profile, user's orders)&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;AuthenticatedUser:&lt;/strong&gt; For data that differs between authenticated and anonymous users&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Entity:&lt;/strong&gt; For data tied to a specific entity instance (e.g., book details by ID)&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  Step - 4: Implement the Cache Interceptor
&lt;/h3&gt;

&lt;p&gt;The interceptor is the heart of automatic caching. It intercepts method calls, checks the cache, and stores results. Create &lt;code&gt;AutoCacheInterceptor.cs&lt;/code&gt;:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;using System;
using System.Collections.Concurrent;
using System.Linq;
using System.Reflection;
using System.Threading.Tasks;
using Microsoft.Extensions.Caching.Distributed;
using Microsoft.Extensions.Logging;
using Microsoft.Extensions.Options;
using Volo.Abp.DependencyInjection;
using Volo.Abp.DynamicProxy;

namespace AutoCache;

public class AutoCacheInterceptor : AbpInterceptor, ITransientDependency
{
    private readonly ILogger&amp;lt;AutoCacheInterceptor&amp;gt; _logger;
    private readonly AutoCacheOptions _options;
    private static readonly MethodInfo GetOrAddCacheAsyncMethod;
    private readonly AutoCacheManager _autoCacheManager;
    private static readonly ConcurrentDictionary&amp;lt;Type, MethodInfo&amp;gt; MethodCache = new();

    static AutoCacheInterceptor()
    {
        GetOrAddCacheAsyncMethod = typeof(AutoCacheInterceptor).GetMethod(
            nameof(GetOrAddCacheAsync),
            BindingFlags.NonPublic | BindingFlags.Instance
        )!;
    }

    public AutoCacheInterceptor(
        ILogger&amp;lt;AutoCacheInterceptor&amp;gt; logger,
        IOptions&amp;lt;AutoCacheOptions&amp;gt; options, 
        AutoCacheManager autoCacheManager)
    {
        _logger = logger;
        _autoCacheManager = autoCacheManager;
        _options = options.Value;
    }

    public override async Task InterceptAsync(IAbpMethodInvocation invocation)
    {
        // Check if caching is enabled and method has [Cache] attribute
        if(!_options.Enabled || 
           invocation.Method.GetCustomAttributes(typeof(CacheAttribute), true).FirstOrDefault() 
           is not CacheAttribute attribute)
        {
            await invocation.ProceedAsync(); // 👈 No caching, proceed normally
            return;
        }

        var proceeded = false;

        try
        {
            // Create generic method based on return type
            var genericMethod = MethodCache.GetOrAdd(invocation.Method.ReturnType, t =&amp;gt;
            {
                var isGenericTask = t.IsGenericType &amp;amp;&amp;amp; t.GetGenericTypeDefinition() == typeof(Task&amp;lt;&amp;gt;);
                var resultType = isGenericTask ? t.GetGenericArguments()[0] : t;
                return GetOrAddCacheAsyncMethod.MakeGenericMethod(resultType);
            });

            // Execute cache logic
            (var result, proceeded) = await (Task&amp;lt;(object, bool)&amp;gt;)genericMethod.Invoke(this, [invocation, attribute])!;
            invocation.ReturnValue = result; // 👈 Set cached or fresh result
        }
        catch (Exception e)
        {
            _logger.LogError(e, "Error occurred while caching method {MethodName}", invocation.Method.Name);

            if(e is AutoCacheExceptionWrapper exceptionWrapper)
            {
                if (_options.ThrowOnError)
                {
                    throw exceptionWrapper.OriginalException;
                }

                _logger.LogWarning(
                    "Cache operation failed, falling back to method execution for {MethodName}",
                    invocation.Method.Name
                );
            }

            if (!proceeded &amp;amp;&amp;amp; invocation.ReturnValue == null)
            {
                await invocation.ProceedAsync(); // 👈 Fallback to actual method execution
            }
        }
    }

    private async Task&amp;lt;(object?, bool)&amp;gt; GetOrAddCacheAsync&amp;lt;TResult&amp;gt;(
        IAbpMethodInvocation invocation, 
        CacheAttribute attribute)
    {
        var proceeded = false;
        var result = await _autoCacheManager.GetOrAddAsync(
            invocation.TargetObject, 
            Factory, 
            invocation.Arguments, 
            () =&amp;gt; new DistributedCacheEntryOptions
            {
                AbsoluteExpirationRelativeToNow = GetExpiration(
                    attribute.AbsoluteExpirationRelativeToNow, 
                    _options.DefaultAbsoluteExpirationRelativeToNow),
                SlidingExpiration = GetExpiration(
                    attribute.SlidingExpiration, 
                    _options.DefaultSlidingExpiration)
            }, 
            attribute.InvalidateOnEntities, 
            attribute.Scope, 
            attribute.ConsiderUow, 
            attribute.AdditionalCacheKey, 
            invocation.Method.Name);

        return (result, proceeded);

        async Task&amp;lt;TResult&amp;gt; Factory()
        {
            await invocation.ProceedAsync(); // 👈 Execute actual method on cache miss
            proceeded = true;
            return (TResult)invocation.ReturnValue;
        }
    }

    private static TimeSpan? GetExpiration(long milliseconds, long defaultValue)
    {
        return milliseconds switch
        {
            0 =&amp;gt; defaultValue &amp;gt; 0 ? TimeSpan.FromMilliseconds(defaultValue) : null,
            &amp;lt; 0 =&amp;gt; null,
            _ =&amp;gt; TimeSpan.FromMilliseconds(milliseconds)
        };
    }
}

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

&lt;/div&gt;



&lt;p&gt;The interceptor intelligently determines whether to serve cached data or execute the actual method.&lt;/p&gt;

&lt;h3&gt;
  
  
  Step - 5: Implement the Cache Manager
&lt;/h3&gt;

&lt;p&gt;The &lt;code&gt;AutoCacheManager&lt;/code&gt; handles the actual cache operations. Create a simplified version:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;using System;
using System.Runtime.CompilerServices;
using System.Threading.Tasks;
using Microsoft.Extensions.Caching.Distributed;
using Microsoft.Extensions.Logging;
using Volo.Abp.DependencyInjection;
using Volo.Abp.DynamicProxy;
using Volo.Abp.Users;

namespace AutoCache;

public class AutoCacheManager : IScopedDependency
{
    private readonly IAutoCacheKeyManager _autoCacheKeyManager;
    private readonly ICurrentUser _currentUser;
    private readonly ILogger&amp;lt;AutoCacheManager&amp;gt; _logger;
    private readonly IAutoCacheMetrics _metrics;
    private readonly AutoCacheOptions _options;

    public AutoCacheManager(
        IAutoCacheKeyManager autoCacheKeyManager, 
        ICurrentUser currentUser,
        ILogger&amp;lt;AutoCacheManager&amp;gt; logger,
        IAutoCacheMetrics metrics,
        IOptions&amp;lt;AutoCacheOptions&amp;gt; options)
    {
        _autoCacheKeyManager = autoCacheKeyManager;
        _currentUser = currentUser;
        _logger = logger;
        _metrics = metrics;
        _options = options.Value;
    }

    public async Task&amp;lt;TResult&amp;gt; GetOrAddAsync&amp;lt;TResult&amp;gt;(
        object? caller,
        Func&amp;lt;Task&amp;lt;TResult&amp;gt;&amp;gt; func,
        object?[]? parameters = null,
        Func&amp;lt;DistributedCacheEntryOptions&amp;gt;? optionsFactory = null,
        Type[]? invalidateOnEntities = null,
        AutoCacheScope scope = AutoCacheScope.Global,
        bool considerUow = false,
        string? additionalCacheKey = null,
        [CallerMemberName] string methodName = "")
    {
        if (!_options.Enabled)
        {
            return await func(); // 👈 Caching disabled, execute directly
        }

        var callerType = caller != null ? ProxyHelper.GetUnProxiedType(caller) : GetType();
        parameters ??= [];

        // Generate unique cache key based on method, parameters, and scope
        var cacheKey = GenerateCacheKey&amp;lt;TResult&amp;gt;(
            callerType.Name, 
            additionalCacheKey, 
            methodName, 
            parameters, 
            scope);

        var (cachedResult, exception, wasHit) = await GetOrAddCacheAsync(
            cacheKey,
            func,
            optionsFactory,
            considerUow
        );

        // Record metrics
        if (wasHit)
        {
            _metrics.RecordHit(cacheKey);
        }
        else
        {
            _metrics.RecordMiss(cacheKey);
        }

        if (exception != null)
        {
            _metrics.RecordError(cacheKey, exception);

            if (_options.ThrowOnError)
            {
                throw exception;
            }
        }

        return cachedResult;
    }

    private string GenerateCacheKey&amp;lt;TResult&amp;gt;(
        string callerTypeName,
        string? additionalCacheKey,
        string methodName,
        object?[] parameters,
        AutoCacheScope scope)
    {
        var keyBuilder = new StringBuilder();
        keyBuilder.Append($"{callerTypeName}:{methodName}");

        // Add parameters to key
        foreach (var param in parameters)
        {
            keyBuilder.Append($":{param}");
        }

        // Add scope-specific segments
        if (scope.HasFlag(AutoCacheScope.CurrentUser) &amp;amp;&amp;amp; _currentUser.Id.HasValue)
        {
            keyBuilder.Append($":user:{_currentUser.Id}"); // 👈 User-specific cache key
        }

        if (scope.HasFlag(AutoCacheScope.AuthenticatedUser))
        {
            keyBuilder.Append($":auth:{_currentUser.IsAuthenticated}");
        }

        if (!string.IsNullOrEmpty(additionalCacheKey))
        {
            keyBuilder.Append($":{additionalCacheKey}");
        }

        return keyBuilder.ToString();
    }

    // Additional methods for cache retrieval and storage...
}

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

&lt;/div&gt;



&lt;p&gt;The manager generates unique cache keys based on method signatures, parameters, and scope settings.&lt;/p&gt;

&lt;h3&gt;
  
  
  Step - 6: Implement Cache Invalidation
&lt;/h3&gt;

&lt;p&gt;When entities change, related caches must be cleared. Create &lt;code&gt;AutoCacheInvalidationHandler.cs&lt;/code&gt;:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;using System;
using System.Threading.Tasks;
using Microsoft.Extensions.Logging;
using Volo.Abp.Domain.Entities;
using Volo.Abp.Domain.Entities.Events;
using Volo.Abp.EventBus;
using Volo.Abp.Uow;

namespace AutoCache;

public class AutoCacheInvalidationHandler&amp;lt;TEntity&amp;gt; : 
    ILocalEventHandler&amp;lt;EntityChangedEventData&amp;lt;TEntity&amp;gt;&amp;gt; 
    where TEntity : class, IEntity
{
    private readonly IAutoCacheKeyManager _autoCacheKeyManager;
    private readonly ILogger&amp;lt;AutoCacheInvalidationHandler&amp;lt;TEntity&amp;gt;&amp;gt; _logger;
    private readonly IUnitOfWorkManager _unitOfWorkManager;

    public AutoCacheInvalidationHandler(
        IAutoCacheKeyManager autoCacheKeyManager, 
        ILogger&amp;lt;AutoCacheInvalidationHandler&amp;lt;TEntity&amp;gt;&amp;gt; logger,
        IUnitOfWorkManager unitOfWorkManager)
    {
        _autoCacheKeyManager = autoCacheKeyManager;
        _logger = logger;
        _unitOfWorkManager = unitOfWorkManager;
    }

    public async Task HandleEventAsync(EntityChangedEventData&amp;lt;TEntity&amp;gt; eventData)
    {
        try
        {
            var entityType = typeof(TEntity);
            var context = new RemoveCacheKeyContext 
            { 
                Keys = eventData.Entity.GetKeys()! 
            };

            // Clear cache after unit of work completes
            if(_unitOfWorkManager.Current != null)
            {
                _unitOfWorkManager.Current.OnCompleted(async () =&amp;gt;
                {
                    await _autoCacheKeyManager.RemoveCacheAndCacheKeys(entityType, context); // 👈 Invalidate cache
                });
            }
            else
            {
                await _autoCacheKeyManager.RemoveCacheAndCacheKeys(entityType, context);
            }
        }
        catch (Exception e)
        {
            _logger.LogError(
                e, 
                "Error occurred while clearing cache for entity type {EntityType}", 
                typeof(TEntity).FullName
            );
        }
    }
}

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

&lt;/div&gt;



&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fraw.githubusercontent.com%2Fabpframework%2Fabp%2Fdev%2Fdocs%2Fen%2FCommunity-Articles%2F2025-12-06-Implement-Automatic-Method-Level-Caching-in-ABP-Framework%2Fimages%2Fcache-invalidation-flow.svg" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fraw.githubusercontent.com%2Fabpframework%2Fabp%2Fdev%2Fdocs%2Fen%2FCommunity-Articles%2F2025-12-06-Implement-Automatic-Method-Level-Caching-in-ABP-Framework%2Fimages%2Fcache-invalidation-flow.svg" alt="Cache Invalidation Flow" width="1000" height="600"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;This handler listens to entity change events and automatically clears related caches. The invalidation happens after the unit of work completes to ensure data consistency.&lt;/p&gt;

&lt;h3&gt;
  
  
  Step - 7: Configure AutoCache in Your Application
&lt;/h3&gt;

&lt;p&gt;Add the &lt;code&gt;AutoCacheModule&lt;/code&gt; to your application module dependencies:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;[DependsOn(
    typeof(AutoCacheModule), // 👈 Add AutoCache module
    typeof(AbpCachingStackExchangeRedisModule),
    // ... other modules
)]
public class YourApplicationModule : AbpModule
{
    public override void ConfigureServices(ServiceConfigurationContext context)
    {
        Configure&amp;lt;AutoCacheOptions&amp;gt;(options =&amp;gt;
        {
            options.Enabled = true; // 👈 Enable caching
            options.DefaultAbsoluteExpirationRelativeToNow = 3600000; // 1 hour
            options.DefaultSlidingExpiration = 600000; // 10 minutes
            options.ThrowOnError = false; // Fallback to method execution on cache errors
        });

        // Configure Redis (if using distributed cache)
        Configure&amp;lt;AbpDistributedCacheOptions&amp;gt;(options =&amp;gt;
        {
            options.KeyPrefix = "YourApp:";
        });
    }
}

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

&lt;/div&gt;



&lt;h3&gt;
  
  
  Step - 8: Use Automatic Caching in Application Services
&lt;/h3&gt;

&lt;p&gt;Now comes the easy part - using automatic caching! Simply add the &lt;code&gt;[Cache]&lt;/code&gt; attribute to your methods:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;using AutoCache;

[Authorize(AutoCacheDemoPermissions.Books.Default)]
public class BookAppService : ApplicationService, IBookAppService
{
    private readonly IRepository&amp;lt;Book, Guid&amp;gt; _repository;
    private readonly AutoCacheManager _autoCacheManager;

    public BookAppService(IRepository&amp;lt;Book, Guid&amp;gt; repository, AutoCacheManager autoCacheManager)
    {
        _repository = repository;
        _autoCacheManager = autoCacheManager;
    }

    // Cache this method, invalidate when Book entity changes
    [Cache(typeof(Book), Scope = AutoCacheScope.Global)]
    public virtual async Task&amp;lt;BookDto&amp;gt; GetAsync(Guid id)
    {
        // You can also use AutoCacheManager directly for nested caching
        var book = await _autoCacheManager.GetOrAddAsync(
            this, 
            async () =&amp;gt; await _repository.GetAsync(id), 
            [id], // 👈 Method parameters
            invalidateOnEntities: [typeof(Book)], 
            scope: AutoCacheScope.Entity);

        return ObjectMapper.Map&amp;lt;Book, BookDto&amp;gt;(book!);
    }

    // Cache book list, invalidate when any Book changes
    [Cache(typeof(Book))]
    public virtual async Task&amp;lt;PagedResultDto&amp;lt;BookDto&amp;gt;&amp;gt; GetListAsync(PagedAndSortedResultRequestDto input)
    {
        var queryable = await _repository.GetQueryableAsync();
        var query = queryable
            .OrderBy(input.Sorting.IsNullOrWhiteSpace() ? "Name" : input.Sorting)
            .Skip(input.SkipCount)
            .Take(input.MaxResultCount);

        var books = await AsyncExecuter.ToListAsync(query);
        var totalCount = await AsyncExecuter.CountAsync(queryable);

        return new PagedResultDto&amp;lt;BookDto&amp;gt;(
            totalCount,
            ObjectMapper.Map&amp;lt;List&amp;lt;Book&amp;gt;, List&amp;lt;BookDto&amp;gt;&amp;gt;(books)
        );
    }

    // No caching on write operations
    [Authorize(AutoCacheDemoPermissions.Books.Create)]
    public async Task&amp;lt;BookDto&amp;gt; CreateAsync(CreateUpdateBookDto input)
    {
        var book = ObjectMapper.Map&amp;lt;CreateUpdateBookDto, Book&amp;gt;(input);
        await _repository.InsertAsync(book); // 👈 This will trigger cache invalidation
        return ObjectMapper.Map&amp;lt;Book, BookDto&amp;gt;(book);
    }
}

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

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;What Happens Here:&lt;/strong&gt;&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;When &lt;code&gt;GetAsync&lt;/code&gt; is called, the interceptor checks the cache&lt;/li&gt;
&lt;li&gt;On cache miss, the actual method executes and the result is cached&lt;/li&gt;
&lt;li&gt;When &lt;code&gt;CreateAsync&lt;/code&gt; inserts a &lt;code&gt;Book&lt;/code&gt;, the invalidation handler clears all caches related to &lt;code&gt;Book&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;Next call to &lt;code&gt;GetAsync&lt;/code&gt; will fetch fresh data&lt;/li&gt;
&lt;/ol&gt;

&lt;h2&gt;
  
  
  Advanced Features
&lt;/h2&gt;

&lt;h3&gt;
  
  
  User-Specific Caching
&lt;/h3&gt;

&lt;p&gt;For user-specific data, use &lt;code&gt;AutoCacheScope.CurrentUser&lt;/code&gt;:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;[Cache(typeof(Order), Scope = AutoCacheScope.CurrentUser)]
public virtual async Task&amp;lt;List&amp;lt;OrderDto&amp;gt;&amp;gt; GetMyOrdersAsync()
{
    var orders = await _orderRepository.GetListAsync(x =&amp;gt; x.UserId == CurrentUser.Id);
    return ObjectMapper.Map&amp;lt;List&amp;lt;Order&amp;gt;, List&amp;lt;OrderDto&amp;gt;&amp;gt;(orders);
}

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

&lt;/div&gt;



&lt;p&gt;Each user gets their own cache entry, automatically invalidated when their orders change.&lt;/p&gt;

&lt;h3&gt;
  
  
  Custom Cache Keys
&lt;/h3&gt;

&lt;p&gt;For fine-grained control, add custom cache key segments:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;[Cache(
    typeof(Product), 
    Scope = AutoCacheScope.Global,
    AdditionalCacheKey = "featured"
)]
public virtual async Task&amp;lt;List&amp;lt;ProductDto&amp;gt;&amp;gt; GetFeaturedProductsAsync()
{
    // Only featured products are cached separately
    return await GetProductsByCategoryAsync("Featured");
}

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

&lt;/div&gt;



&lt;h3&gt;
  
  
  Performance Metrics
&lt;/h3&gt;

&lt;p&gt;Monitor cache performance using &lt;code&gt;IAutoCacheMetrics&lt;/code&gt;:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;public class CacheMonitoringService : ITransientDependency
{
    private readonly IAutoCacheMetrics _metrics;

    public CacheMonitoringService(IAutoCacheMetrics metrics)
    {
        _metrics = metrics;
    }

    public AutoCacheStatistics GetStatistics()
    {
        return _metrics.GetStatistics(); // 👈 Get hit rate, miss count, error count
    }
}

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

&lt;/div&gt;



&lt;h2&gt;
  
  
  Testing the Application
&lt;/h2&gt;

&lt;h3&gt;
  
  
  1. Run the Application
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;abp new BookStore -u mvc -d ef
cd BookStore
dotnet run --project src/BookStore.Web

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

&lt;/div&gt;



&lt;h3&gt;
  
  
  2. Test Cache Behavior
&lt;/h3&gt;

&lt;p&gt;Create a simple test to verify caching:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;[Fact]
public async Task Should_Cache_Book_Results()
{
    // First call - cache miss
    var book1 = await _bookAppService.GetAsync(testBookId);

    // Second call - cache hit (should be faster)
    var book2 = await _bookAppService.GetAsync(testBookId);

    book1.Name.ShouldBe(book2.Name);
}

[Fact]
public async Task Should_Invalidate_Cache_On_Update()
{
    // Cache the book
    var book1 = await _bookAppService.GetAsync(testBookId);

    // Update the book
    await _bookAppService.UpdateAsync(testBookId, new CreateUpdateBookDto 
    { 
        Name = "Updated Name" 
    });

    // Fetch again - should get updated data (cache was invalidated)
    var book2 = await _bookAppService.GetAsync(testBookId);

    book2.Name.ShouldBe("Updated Name");
}

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

&lt;/div&gt;



&lt;h3&gt;
  
  
  3. Monitor Cache Performance
&lt;/h3&gt;

&lt;p&gt;Check your application logs for cache metrics:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;[INF] Cache Hit: BookAppService:GetAsync:book-id-123 (Response Time: 5ms)
[INF] Cache Miss: BookAppService:GetListAsync (Response Time: 156ms)
[INF] Cache Invalidation: Book entity changed, cleared 3 cache entries

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

&lt;/div&gt;



&lt;h2&gt;
  
  
  Key Takeaways
&lt;/h2&gt;

&lt;p&gt;✅ &lt;strong&gt;Automatic caching reduces boilerplate code&lt;/strong&gt; - Just add &lt;code&gt;[Cache]&lt;/code&gt; attribute to methods instead of manual cache management&lt;/p&gt;

&lt;p&gt;✅ &lt;strong&gt;Smart invalidation keeps data fresh&lt;/strong&gt; - Entity changes automatically clear related caches without manual intervention&lt;/p&gt;

&lt;p&gt;✅ &lt;strong&gt;Multiple scoping options&lt;/strong&gt; - Support for global, user-specific, authenticated, and entity-level caching strategies&lt;/p&gt;

&lt;p&gt;✅ &lt;strong&gt;Built-in fallback handling&lt;/strong&gt; - Gracefully falls back to method execution if caching fails&lt;/p&gt;

&lt;p&gt;✅ &lt;strong&gt;Performance monitoring&lt;/strong&gt; - Track cache hits, misses, and errors for optimization&lt;/p&gt;

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

&lt;p&gt;Automatic method-level caching dramatically simplifies performance optimization in ABP Framework applications. By using attributes and interceptors, you can add sophisticated caching behavior without cluttering your business logic with cache management code.&lt;/p&gt;

&lt;p&gt;The system we've built provides intelligent cache invalidation, multiple scoping strategies, and built-in monitoring - all while maintaining clean, readable code. Whether you're building a small application or an enterprise system, this approach scales elegantly and integrates seamlessly with ABP's architecture.&lt;/p&gt;

&lt;p&gt;Ready to implement this in your project? The complete working implementation is available in the &lt;a href="https://github.com/salihozkara/AbpAutoCacheDemo" rel="noopener noreferrer"&gt;AbpAutoCacheDemo repository&lt;/a&gt;. You can clone the repository, explore the code, and even extract the &lt;code&gt;src/AutoCache&lt;/code&gt; folder to use it as a standalone library in your own ABP applications. The &lt;a href="https://github.com/salihozkara/AbpAutoCacheDemo/commit/946df1fc07de6eddd26eb14013a09968cd59329b" rel="noopener noreferrer"&gt;main implementation commit&lt;/a&gt; shows all the components working together, including interceptor registration, cache key management, and automatic invalidation handlers.r you're building a small application or an enterprise system, this approach scales elegantly and integrates seamlessly with ABP's architecture.&lt;/p&gt;

&lt;p&gt;Ready to implement this in your project? Check out the complete working example in the repository linked below, and start improving your application's performance today!&lt;/p&gt;

&lt;h3&gt;
  
  
  See Also
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;&lt;a href="https://abp.io/docs/latest/framework/fundamentals/caching" rel="noopener noreferrer"&gt;ABP Caching Documentation&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://abp.io/docs/latest/framework/infrastructure/interceptors" rel="noopener noreferrer"&gt;Interceptors in ABP&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://abp.io/docs/latest/framework/infrastructure/event-bus" rel="noopener noreferrer"&gt;Event Bus Documentation&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://github.com/salihozkara/AbpAutoCacheDemo" rel="noopener noreferrer"&gt;Sample Project on GitHub&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;




&lt;h2&gt;
  
  
  References
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;&lt;a href="https://docs.abp.io" rel="noopener noreferrer"&gt;ABP Framework Documentation&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://redis.io/docs/" rel="noopener noreferrer"&gt;Redis Distributed Caching&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://en.wikipedia.org/wiki/Aspect-oriented_programming" rel="noopener noreferrer"&gt;Aspect-Oriented Programming Patterns&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;

</description>
      <category>metrics</category>
      <category>interceptors</category>
      <category>caching</category>
      <category>eventbus</category>
    </item>
    <item>
      <title>.NET Conf China 2025: Changing the World, Changing Ourselves - See You Again in Shanghai</title>
      <dc:creator>ABP.IO</dc:creator>
      <pubDate>Wed, 03 Dec 2025 08:32:26 +0000</pubDate>
      <link>https://dev.to/abp_io/net-conf-china-2025-changing-the-world-changing-ourselves-see-you-again-in-shanghai-ccj</link>
      <guid>https://dev.to/abp_io/net-conf-china-2025-changing-the-world-changing-ourselves-see-you-again-in-shanghai-ccj</guid>
      <description>&lt;h1&gt;
  
  
  .NET Conf China 2025: Changing the World, Changing Ourselves - See You Again in Shanghai
&lt;/h1&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fcjxqz78znu61ynbt2xnj.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fcjxqz78znu61ynbt2xnj.png" width="800" height="534"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;.NET Conf China 2025 is an annual community event for developers, celebrating the release of .NET 10 (LTS) and the achievements of the past year in China. As an extension of .NET Conf 2025, this event brings together local tech communities, well-known companies, and open-source organizations. It has become the largest .NET online and offline conference in China, dedicated to spreading .NET technology in Chinese and fostering collaboration and exchange.&lt;/p&gt;

&lt;h2&gt;
  
  
  Event Highlights: Key Topics and Takeaways
&lt;/h2&gt;

&lt;p&gt;This year’s conference focused on three main themes: performance improvements, AI integration, and cross-platform development. Topics covered how to achieve performance gains while maintaining engineering quality, balancing between multi-platform consistency and native capabilities, and taking generative AI from “demo-level” to “production-ready.” On the community and ecosystem side, the event showcased the .NET Foundation’s and domestic and international companies’ progress in supporting architectures like ARM, LoongArch, and RISC-V. It also highlighted best practices in DevOps, observability, and engineering toolchains, creating a complete path from ideas to implementation.&lt;/p&gt;

&lt;h3&gt;
  
  
  Opening Keynote
&lt;/h3&gt;

&lt;p&gt;Scott Hanselman kicked off .NET Conf China 2025 with a video keynote, announcing that .NET 10 is now available on the official website. He framed the release around four pillars—AI, cloud-native, cross-platform, and performance—including integration with the Microsoft Agent Framework for building and orchestrating multi-agent systems in .NET/C#, industry-leading container and Kubernetes support with .NET Aspire simplifying local containerized development, a richer cross-platform desktop ecosystem (.NET MAUI, Avalonia, Uno Platform), and major performance gains such as Native AOT and single-file publishing for faster startup and easier distribution across platforms.&lt;/p&gt;

&lt;p&gt;He underscored China’s importance as .NET’s second-largest market, with roughly 13% of users, and noted that generative AI usage in China has doubled in 2025. The local community is seeing strong momentum around ML.NET, .NET Aspire, and the C# Dev Kit in VS Code. Reflecting on his Baby Smash game written 20 years ago, which now runs cross-platform on .NET 10, he called on developers to modernize: move existing Web, WinForms, and WPF apps to the cloud, improve performance, ship as a single executable, and weave in AI capabilities.&lt;/p&gt;

&lt;p&gt;On AI, he emphasized a human-centered stance: AI and agents should augment, not replace, developers. In the future, developers will orchestrate and govern agents, and human judgment will matter more than ever. He closed by thanking the open-source community for its many proposals and pull requests, stressing that .NET is an open-source platform built together by Microsoft and the community, and wishing everyone an inspiring conference and a joyful journey with .NET 10.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fga7xbavi691b8igl0tpf.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fga7xbavi691b8igl0tpf.png" alt="2" width="800" height="534"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h3&gt;
  
  
  Roundtable Discussion
&lt;/h3&gt;

&lt;p&gt;The roundtable discussion, titled “Empowering with AI, Breaking Through Cross-Platform Barriers, and Ecosystem Innovation,” focused on practical implementation. It explored typical paths for large models and intelligent agents in enterprises, key considerations for choosing cross-platform UI frameworks, and the evolution of these frameworks. Panelists discussed questions like: How can AI capabilities be integrated into existing business processes instead of creating an “experimental” pipeline? How should cross-platform solutions be evaluated in terms of performance, ecosystem, and team skillsets? What are the unique opportunities for domestic ecosystems in the global tech landscape? And how can community collaboration help developers quickly adopt best practices? A shared consensus emerged: in the short term, focus on running scenarios; in the long term, return to engineering fundamentals. Both toolchains and methodologies are equally important.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fle58fzwqjpdm6dl82q1h.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fle58fzwqjpdm6dl82q1h.png" width="800" height="534"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h3&gt;
  
  
  In-Depth Sessions
&lt;/h3&gt;

&lt;p&gt;The afternoon featured four breakout sessions, covering a wide range of topics with deep dives into both foundational technologies and real-world project reviews:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Frontend and Cross-Platform:&lt;/strong&gt; Focused on the progress of Avalonia, Blazor, and WebAssembly, as well as the integrated experience of .NET Aspire in multi-service applications. Speakers shared insights on reusing core logic between desktop and web, shortening cold start times with incremental compilation and resource trimming, and performance profiling and optimization in WASM scenarios.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;AI Agents and Enterprise Adoption:&lt;/strong&gt; Discussed multi-agent orchestration, the MCP plugin ecosystem, and enterprise data compliance. From common pitfalls of “demo-level” AI to the “five-step method” for moving from POC to production, the session covered use cases like knowledge retrieval, process automation, intelligent customer service, and developer assistants, emphasizing evaluation metrics, prompt engineering, and monitoring governance.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;.NET Practices and Engineering:&lt;/strong&gt; Focused on the latest capabilities and performance practices of EF Core, the boundaries of NativeAOT, automated testing strategies, and observability implementation. Discussions included database migration strategies, caching and concurrency control for hot paths, end-to-end tracing, and structured logging.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Solutions and Case Studies:&lt;/strong&gt; From Clean Architecture/DDD to AI-powered business evolution, topics included application modernization, SaaS transformation, and edge-cloud collaboration in AIoT. Speakers broke down modular governance, team collaboration, and release strategies for complex systems, putting “delivering value continuously” at the center stage.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fdonj69n1yi0jp03i99fh.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fdonj69n1yi0jp03i99fh.png" width="800" height="533"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  ABP Booth Highlights: Showcases, Conversations, and Fun
&lt;/h2&gt;

&lt;p&gt;The story of ABP began with a promise to create a better starting point. From the frustration of “copy-pasting boilerplate code,” we crafted a modular, opinionated framework. We chose open source and community collaboration. We founded Volosoft to turn our vision into reality with professional tools. Today, tens of thousands of developers explore the ABP framework, and thousands of teams rely on the ABP platform to deliver production-grade .NET applications faster and more securely.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fiiyxocgqjghfy28cm8ac.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fiiyxocgqjghfy28cm8ac.png" width="800" height="1067"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;At .NET Conf China 2025, we brought our “developer platform built for developers” to every visitor. Our booth demonstrations started with “a production-ready skeleton from the start”: modular layered architecture, built-in authentication and authorization systems, multi-tenancy support, audit logging, and localization—all out of the box. On the frontend and backend, ABP offers diverse options like MVC, Blazor, and Angular, enabling teams to quickly implement solutions on familiar stacks while maintaining flexibility for future evolution. We also showcased how ABP integrates with containerization, CI/CD, and observability, emphasizing “engineering built into the framework, not reinvented by every team.”&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fagpsk4yk544zbmdziufv.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fagpsk4yk544zbmdziufv.png" width="800" height="800"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Interaction and Prizes:&lt;/strong&gt; Sharing technology should also be warm and engaging. We hosted a QR code raffle at the booth, with prizes including ABP stickers, the book &lt;em&gt;Mastering ABP Framework&lt;/em&gt;, and Bluetooth headphones. Multiple rounds of raffles and group photos made the interactions more memorable. Many developers shared their ABP experiences and plans for improvement right at the booth, and a few impromptu “code walkthroughs” naturally happened. The love and joy for technology were captured in every handshake and discussion.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fq4j77i9sxrceqgxktgeg.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fq4j77i9sxrceqgxktgeg.png" width="800" height="600"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Looking Ahead: Building the Ecosystem Together
&lt;/h2&gt;

&lt;p&gt;From an open-source journey to a complete development platform for the future, we’ve always believed that developers deserve a better starting point. Around performance, intelligence, and cross-platform capabilities, we will continue investing in engineering, ecosystem collaboration, and best practice sharing. We also welcome more partners to contribute through documentation and examples, share your experiences, and submit your ideas. Together, let’s make “useful infrastructure” more stable, efficient, and business-friendly.&lt;/p&gt;

&lt;p&gt;We look forward to exchanging ideas, sharing practices, and building the ecosystem together at the next gathering. Technology meets creativity, and the possibilities are endless. We’re on the road and waiting for you at the next event.&lt;/p&gt;

&lt;p&gt;See you next year at .NET Conf China 2026!&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fh1qc1w4skl5vrcyx5ms7.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fh1qc1w4skl5vrcyx5ms7.png" width="800" height="450"&gt;&lt;/a&gt;&lt;/p&gt;

</description>
      <category>conference</category>
      <category>dotnetconf</category>
      <category>net</category>
      <category>events</category>
    </item>
    <item>
      <title>ABP Suite：EfCoreRepository Template change DbContext</title>
      <dc:creator>ABP.IO</dc:creator>
      <pubDate>Fri, 28 Nov 2025 03:25:41 +0000</pubDate>
      <link>https://dev.to/abp_io/abp-suiteefcorerepository-template-change-dbcontext-bdi</link>
      <guid>https://dev.to/abp_io/abp-suiteefcorerepository-template-change-dbcontext-bdi</guid>
      <description>&lt;p&gt;在 ABP Suite 專案中，若有多個 DbContext，預設的 Repository Template 可能會判斷錯誤，導致產生到錯誤的 DbContext。作者提供了一個簡單方法：修改 EfCoreRepository Template，強制指定使用正確的 DbContext，以確保穩定性與一致性。&lt;br&gt;&lt;br&gt;
&lt;a href="https://jakeuj.com/abp-suite-efcorerepository-template-dbcontext.html" rel="noopener noreferrer"&gt;Go to the Post&lt;/a&gt;&lt;/p&gt;

</description>
      <category>efcore</category>
      <category>multiplecontexts</category>
      <category>entityframeworkcore</category>
      <category>abpsuite</category>
    </item>
    <item>
      <title>Auto Update OpenIddict Cret by Azure Key Vault</title>
      <dc:creator>ABP.IO</dc:creator>
      <pubDate>Wed, 26 Nov 2025 03:42:01 +0000</pubDate>
      <link>https://dev.to/abp_io/auto-update-openiddict-cret-by-azure-key-vault-251g</link>
      <guid>https://dev.to/abp_io/auto-update-openiddict-cret-by-azure-key-vault-251g</guid>
      <description>&lt;p&gt;A practical guide on implementing automated certificate rotation for OpenIddict using **Azure Key Vault**, **Managed Identity**, and **Auto-Renew policies**. It shows how to load all valid certificates at startup, always sign tokens with the newest version, and still validate tokens issued by older certificates. Includes sample code, Key Vault policy.json, and Azure CLI scripts. Ideal for ABP + OpenIddict projects running on Azure App Service.&lt;br&gt;&lt;br&gt;
&lt;a href="https://jakeuj.com/azure-key-vault-openiddict.html#azure-cli" rel="noopener noreferrer"&gt;Go to the Post&lt;/a&gt;&lt;/p&gt;

</description>
      <category>azure</category>
      <category>openiddict</category>
      <category>abp</category>
      <category>openiddictmodule</category>
    </item>
    <item>
      <title>My First Look and Experience with Google AntiGravity</title>
      <dc:creator>ABP.IO</dc:creator>
      <pubDate>Mon, 24 Nov 2025 13:18:12 +0000</pubDate>
      <link>https://dev.to/abp_io/my-first-look-and-experience-with-google-antigravity-1k9e</link>
      <guid>https://dev.to/abp_io/my-first-look-and-experience-with-google-antigravity-1k9e</guid>
      <description>&lt;h1&gt;
  
  
  My First Look and Experience with Google AntiGravity
&lt;/h1&gt;

&lt;h2&gt;
  
  
  Is Google AntiGravity Going to Replace Your Main Code Editor?
&lt;/h2&gt;

&lt;p&gt;Today, I tried the new code-editor AntiGravity by Google. &lt;em&gt;"It's beyond a code-editor&lt;/em&gt;" by Google 🙄 When I first launch it, I see the UI is almost same as Cursor. They're both based on Visual Studio Code. That's why it was not hard to find what I'm looking for.&lt;/p&gt;

&lt;p&gt;First of all, the main difference as I see from the Cursor is; when I type a prompt in the agent section &lt;strong&gt;AntiGravity first creates a Task List&lt;/strong&gt; (like a road-map) and whenever it finishes a task, it checks the corresponding task. Actually Cursor has a similar functionality but AntiGravity took it one step further.&lt;/p&gt;

&lt;p&gt;Second thing which was good to me; AntiGravity uses &lt;a href="https://gemini.google/tr/overview/image-generation/" rel="noopener noreferrer"&gt;Nano Banana 🍌&lt;/a&gt;. This is Google's AI image generation model... Why it's important because when you create an app, you don't need to search for graphics, deal with image licenses. &lt;strong&gt;AntiGravity generates images automatically and no license is required!&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Third exciting feature for me; &lt;strong&gt;AntiGravity is integrated with Google Chrome and can communicate with the running website&lt;/strong&gt;. When I first run my web project, it installed a browser extension which can see and interact with my website. It can see the results, click somewhere else on the page, scroll, fill up the forms, amazing 😵&lt;/p&gt;

&lt;p&gt;Another feature I loved is that &lt;strong&gt;you can enter a new prompt even while AntiGravity is still generating a response&lt;/strong&gt; 🧐. It instantly prioritizes the latest input and adjusts the ongoing process if needed. But in Cursor, if you add a prompt before the cursor finishes, it simply queues it and runs it later 😔.&lt;/p&gt;

&lt;p&gt;And lastly, &lt;strong&gt;AntiGravity is working very good with Gemini 3&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;Well, everything was not so perfect 😥 When I tried AntiGravity, couple of times it stucked AI generation and Agent stopped. I faced errors like this 👇&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F6fqhlat4ywgluvwi9ylb.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F6fqhlat4ywgluvwi9ylb.png" alt="Errors" width="800" height="247"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Debugging .NET Projects via AntiGravity
&lt;/h2&gt;

&lt;p&gt;⚠ There's a crucial development issue with AntiGravity (and also for Cursor, Windsurf etc...) 🤕 you &lt;strong&gt;cannot debug your .NET application with AntiGravity 🥺.&lt;/strong&gt; &lt;em&gt;This is Microsoft's policy!&lt;/em&gt; Microsoft doesn't allow debugging for 3rd party IDEs and shows the below error... That's why I cannot say it's a downside of AntiGravity. You need to use Microsft's original VS Code, Visual Studio or Rider for debugging. But wait a while there's a workaround for this, I'll let you know in the next section.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F427njb895sx8v7je8gz0.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F427njb895sx8v7je8gz0.png" alt="Debugging" width="562" height="141"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h3&gt;
  
  
  What does this error mean?
&lt;/h3&gt;

&lt;p&gt;AntiGravity, Cursor, Windsurf etc... are using Visual Studio Code and the C# extension for VS Code includes the Microsoft .NET Core Debugger "&lt;em&gt;vsdbg&lt;/em&gt;". VS Code is open-source but "&lt;em&gt;vsdbg&lt;/em&gt;" is not open-source! It's working only with Visual Studio Code, Visual Studio and Visual Studio for Mac. This is clearly stated at &lt;a href="https://github.com/dotnet/vscode-csharp/blob/main/docs/debugger/Microsoft-.NET-Core-Debugger-licensing-and-Microsoft-Visual-Studio-Code.md" rel="noopener noreferrer"&gt;Microsoft's this link&lt;/a&gt;.&lt;/p&gt;

&lt;h3&gt;
  
  
  Ok! How to resolve debugging issue with AntiGravity? and Cursor and Windsurf...
&lt;/h3&gt;

&lt;p&gt;There's a free C# debugger extension for Visual Studio Code based IDEs that supports AntiGravity, Cursor and Windsurf. The extension name is &lt;strong&gt;C#&lt;/strong&gt;. You can download this free C# debugger extension at 👉 &lt;a href="https://open-vsx.org/extension/muhammad-sammy/csharp/" rel="noopener noreferrer"&gt;open-vsx.org/extension/muhammad-sammy/csharp/&lt;/a&gt;. For AntiGravity open Extension window (&lt;em&gt;Ctrl + Shift + X&lt;/em&gt;) and search for &lt;code&gt;C#&lt;/code&gt;, there you'll see this extension.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F8bmsx9uevksosixr5w1s.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F8bmsx9uevksosixr5w1s.png" alt="C# Debugging Extension" width="800" height="259"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;After installing, I restarted AntiGravity and now I can see the red circle which allows me to add breakpoint on C# code.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F8wvqye7j1re4n80n306y.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F8wvqye7j1re4n80n306y.png" alt="Add C# Breakpoint" width="800" height="287"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h3&gt;
  
  
  Another Extension For Debugging .NET Apps on VS Code
&lt;/h3&gt;

&lt;p&gt;Recently I heard about DotRush extension from the folks. As they say DotRush works slightly faster and support Razor pages (.cshtml files). Here's the link for DotRush &lt;a href="https://github.com/JaneySprings/DotRush" rel="noopener noreferrer"&gt;https://github.com/JaneySprings/DotRush&lt;/a&gt;&lt;/p&gt;

&lt;h3&gt;
  
  
  Finding Website Running Port
&lt;/h3&gt;

&lt;p&gt;When you run the web project via C# debugger extension, normally it's not using the &lt;code&gt;launch.json&lt;/code&gt; therefore the website port is not the one when you start from Visual Studio / Rider... So what's my website's port which I just run now? Normally for ASP.NET Core &lt;strong&gt;the default port is 5000&lt;/strong&gt;. You can try navigating to &lt;a href="http://localhost:5000/" rel="noopener noreferrer"&gt;http://localhost:5000/&lt;/a&gt;. Alternatively you can write the below code in &lt;code&gt;Program.cs&lt;/code&gt; which prints the full address of your website in the logs. If you do the steps which I showed you, you can debug your C# application via AntiGravity and other VS Code derivatives.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fpvmp83cpnq9cd9rsrd15.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fpvmp83cpnq9cd9rsrd15.png" alt="Find Website Port" width="770" height="457"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  How Much is AntiGravity? 💲
&lt;/h2&gt;

&lt;p&gt;Currently there's only individual plan is available for personal accounts and that's free 👏! The contents of Team and Enterprise plans and prices are not announced yet. But &lt;strong&gt;Gemini 3 is not free&lt;/strong&gt;! I used it with my company's Google Workspace account which we normally pay for Gemini.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Foadyaetdpnqeh5s6bjtb.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Foadyaetdpnqeh5s6bjtb.png" alt="Pricing" width="800" height="153"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  More About AntiGravity
&lt;/h2&gt;

&lt;p&gt;There have been many AI assisted IDEs like &lt;a href="https://windsurf.com/" rel="noopener noreferrer"&gt;Windsurf&lt;/a&gt;, &lt;a href="https://cursor.com/" rel="noopener noreferrer"&gt;Cursor&lt;/a&gt;, &lt;a href="https://zed.dev/" rel="noopener noreferrer"&gt;Zed&lt;/a&gt;, &lt;a href="https://replit.com/" rel="noopener noreferrer"&gt;Replit&lt;/a&gt; and &lt;a href="https://www.jetbrains.com/fleet/" rel="noopener noreferrer"&gt;Fleet&lt;/a&gt;. But this time it's different, this is backed by Google. As you see from the below image AntiGravity, uses a standard grid layout as others based on VS Code editor. It's very similar to Cursor, Visual Studio, Rider.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fms2j0ri8y93wsfi6w41m.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fms2j0ri8y93wsfi6w41m.png" alt="AntiGravity UI" width="800" height="430"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Supported LLMs 🧠
&lt;/h2&gt;

&lt;p&gt;Antigravity offers the below models which supports reasoning: Gemini 3 Pro, Claude Sonnet 4.5, GPT-OSS&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F4bziqfqib1of8dyti7mq.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F4bziqfqib1of8dyti7mq.png" alt="LLMs" width="310" height="212"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Antigravity uses other models for supportive tasks in the background:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Nano banana&lt;/strong&gt; : This is used to generate images.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Gemini 2.5 Pro UI Checkpoint&lt;/strong&gt; : It's for the browser subagent to trigger browser action such as clicking, scrolling, or filling in input.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Gemini 2.5 Flash&lt;/strong&gt; : For checkpointing and context summarization, this is used.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Gemini 2.5 Flash Lite&lt;/strong&gt; : And when it's need to make a semantic search in your code-base, this is used.&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  AntiGravity Can See Your Website
&lt;/h2&gt;

&lt;p&gt;This makes a big difference from traditional IDEs. AntiGravity's browser agent is taking screenshots of your pages when it needs to check. This is achieved by a Chrome Extension as a tool to the agent, and you can also prompt the agent to take a screenshot of a page. It can iterate on website designs and implementations, it can perform UI Testing, it can monitor dashboards, it can automate routine tasks like rerunning CI. This is the link for the extension 👉 &lt;a href="https://chromewebstore.google.com/detail/antigravity-browser-exten/eeijfnjmjelapkebgockoeaadonbchdd" rel="noopener noreferrer"&gt;chromewebstore.google.com/detail/antigravity-browser-exten/eeijfnjmjelapkebgockoeaadonbchdd&lt;/a&gt;. AntiGravity will install this extension automatically on the first run.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fl7w8mzw1ddr4088kpw1x.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fl7w8mzw1ddr4088kpw1x.png" alt="Browser Extension" width="529" height="615"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fpc3zzjnog2oks7bwxod1.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fpc3zzjnog2oks7bwxod1.png" alt="Extension Features" width="800" height="389"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  MCP Integration
&lt;/h2&gt;

&lt;h3&gt;
  
  
  When Do We Need MCP in a Code Editor?
&lt;/h3&gt;

&lt;p&gt;Simply if we want to connect to a 3rd party service to complete our task we need MCP. So AntiGravity can connect to your DB and write proper SQL queries or it can pull in recent build logs from Netlify or Heroku. Also you can ask AntiGravity to to connect GitHub for finding the best authentication pattern.&lt;/p&gt;

&lt;h3&gt;
  
  
  AntiGravity Supports These MCP Servers
&lt;/h3&gt;

&lt;p&gt;Airweave, AlloyDB for PostgreSQL, Atlassian, BigQuery, Cloud SQL for PostgreSQL, Cloud SQL for MySQL, Cloud SQL for SQL Server, Dart, Dataplex, Figma Dev Mode MCP, Firebase, GitHub, Harness, Heroku, Linear, Locofy, Looker, MCP Toolbox for Databases, MongoDB, Neon, Netlify, Notion, PayPal, Perplexity Ask, Pinecone, Prisma, Redis, Sequential Thinking, SonarQube, Spanner, Stripe and Supabase.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fb6s6oicxiqrvcd3ghalh.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fb6s6oicxiqrvcd3ghalh.png" alt="MCP" width="800" height="300"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Agent Settings ⚙️
&lt;/h2&gt;

&lt;p&gt;The major settings of Agent are:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Agent Auto Fix Lints&lt;/strong&gt; : I enabled this setting because I want the Agent automatically fixes its own mistakes for invalid syntax, bad formatting, unused variables, unreachable code or following coding standards... It makes extra tool calls that's why little bit expensive 🥴.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Auto Execution&lt;/strong&gt; : Sometimes Agent tries to build application or writing test code and running it, in these cases it executes command. I choose "Turbo" 🤜 With this option, Agent always runs the terminal command and controls my browser.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Review Policy&lt;/strong&gt; : How much control you are giving to agent 🙎. I choose "Always Proceed" 👌 because I mostly trust AI 😀. The Agent will never ask for review.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fyj09jvo90vs4r38iejjo.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fyj09jvo90vs4r38iejjo.png" alt="Agent Settings" width="647" height="758"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Differences Between Cursor and AntiGravity
&lt;/h2&gt;

&lt;p&gt;While Cursor was the champion of AI code editors, &lt;strong&gt;Antigravity brings a different philosophy&lt;/strong&gt;.&lt;/p&gt;

&lt;h3&gt;
  
  
  1. "Agent-First 🤖" vs "You-First 🤠"
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Cursor:&lt;/strong&gt; It acts like an assistant; it predicts your next move, auto-completes your thoughts, and helps you refactor while you type. You are still the driver; Cursor just drives the car at 200 km/h.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Antigravity:&lt;/strong&gt; Antigravity is built to let you manage coding tasks. It is "Agent-First." You don't just type code; you assign tasks to autonomous agents (e.g., "Fix the bug in the login flow and verify it in the browser"). It behaves more like a junior developer that you supervise.&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  2. The Interface
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Cursor:&lt;/strong&gt; Looks and feels exactly like &lt;strong&gt;VS Code&lt;/strong&gt;. If you know VS Code, you know Cursor.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Antigravity:&lt;/strong&gt; Introduces 2 major layouts:&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  3. Verification &amp;amp; Trust
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Cursor:&lt;/strong&gt; You verify by reading the code diffs it suggests.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Antigravity:&lt;/strong&gt; Introduces &lt;strong&gt;Artifacts&lt;/strong&gt;... Since the agents work autonomously, they generate proof-of-work documents, screenshots of the app running, browser logs and execution plans. So you can verify what they did without necessarily reading every line of code immediately.&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  4. Capabilities
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Cursor:&lt;/strong&gt; Best-in-class &lt;strong&gt;Autocomplete&lt;/strong&gt; ("Tab" feature) and &lt;strong&gt;Composer&lt;/strong&gt; (multi-file editing). It excels at "Vibe Coding". It's getting into a flow state where the AI writes the boilerplate and you direct the logic.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Antigravity:&lt;/strong&gt; Is good at &lt;strong&gt;Autonomous Execution&lt;/strong&gt;. It has a built-in browser and terminal that the &lt;em&gt;Agent&lt;/em&gt; controls. The Agent can write code, run the server, open the browser, see the error, and fix it 😎&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  5. AI Models (Brains 🧠)
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Cursor:&lt;/strong&gt; Model Agnostic. You can switch between &lt;strong&gt;Claude 3.5 Sonnet&lt;/strong&gt; &lt;em&gt;-mostly the community uses this-&lt;/em&gt;, GPT-4o, and others.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Antigravity:&lt;/strong&gt; Built deeply around &lt;strong&gt;Gemini 3 Pro&lt;/strong&gt;. It leverages Gemini's massive context window (1M+ tokens) to understand huge mono repos without needing as much "RAG" as Cursor.&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Try It Yourself Now 🤝
&lt;/h2&gt;

&lt;p&gt;If you are ready to experience the new AI code editor by Google, download and use 👇&lt;a href="https://antigravity.google/" rel="noopener noreferrer"&gt;&lt;strong&gt;Launch Google AntiGravity&lt;/strong&gt;&lt;/a&gt;&lt;/p&gt;

</description>
      <category>cursor</category>
      <category>google</category>
      <category>antigravity</category>
      <category>ide</category>
    </item>
    <item>
      <title>Building Production-Ready LLM Applications with .NET: A Practical Guide</title>
      <dc:creator>ABP.IO</dc:creator>
      <pubDate>Mon, 24 Nov 2025 07:33:03 +0000</pubDate>
      <link>https://dev.to/abp_io/building-production-ready-llm-applications-with-net-a-practical-guide-3a8i</link>
      <guid>https://dev.to/abp_io/building-production-ready-llm-applications-with-net-a-practical-guide-3a8i</guid>
      <description>&lt;h1&gt;
  
  
  Building Production-Ready LLM Applications with .NET: A Practical Guide
&lt;/h1&gt;

&lt;p&gt;Large Language Models (LLMs) have evolved rapidly, and integrating them into production .NET applications requires staying current with the latest approaches. In this article, I'll share practical tips and patterns I've learned while building LLM-powered systems, covering everything from API changes in GPT-5 to implementing efficient RAG (Retrieval Augmented Generation) architectures.&lt;/p&gt;

&lt;p&gt;Whether you're building a chatbot, a knowledge base assistant, or integrating AI into your enterprise applications, these production-tested insights will help you avoid common pitfalls and build more reliable systems.&lt;/p&gt;

&lt;h2&gt;
  
  
  The Temperature Paradigm Shift: GPT-5 Changes Everything
&lt;/h2&gt;

&lt;p&gt;If you've been working with GPT-4 or earlier models, you're familiar with the &lt;code&gt;temperature&lt;/code&gt; and &lt;code&gt;top_p&lt;/code&gt; parameters for controlling response randomness. &lt;strong&gt;Here's the critical update&lt;/strong&gt; : GPT-5 no longer supports these parameters!&lt;/p&gt;

&lt;h3&gt;
  
  
  The Old Way (GPT-4)
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;var chatRequest = new ChatOptions
{
    Temperature = 0.7, // ✅ Worked with GPT-4
    TopP = 0.9 // ✅ Worked with GPT-4
};

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

&lt;/div&gt;



&lt;h3&gt;
  
  
  The New Way (GPT-5)
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;var chatRequest = new ChatOptions
{
    RawRepresentationFactory = (client =&amp;gt; new ChatCompletionOptions()
    {
#pragma warning disable OPENAI001
        ReasoningEffortLevel = "minimal",
#pragma warning restore OPENAI001
    })
};

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

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;Why the change?&lt;/strong&gt; GPT-5 incorporates an internal reasoning and verification process. Instead of controlling randomness, you now specify how much computational effort the model should invest in reasoning through the problem.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fraw.githubusercontent.com%2Fabpframework%2Fabp%2Fdev%2Fdocs%2Fen%2FCommunity-Articles%2F2025-11-22-building-production-ready-llm-applications%2Fimages%2Freasoning-effort-diagram.svg" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fraw.githubusercontent.com%2Fabpframework%2Fabp%2Fdev%2Fdocs%2Fen%2FCommunity-Articles%2F2025-11-22-building-production-ready-llm-applications%2Fimages%2Freasoning-effort-diagram.svg" alt="Reasoning Effort Levels" width="800" height="300"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h3&gt;
  
  
  Choosing the Right Reasoning Level
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Low&lt;/strong&gt; : Quick responses for simple queries (e.g., "What's the capital of France?")&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Medium&lt;/strong&gt; : Balanced approach for most use cases&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;High&lt;/strong&gt; : Complex reasoning tasks (e.g., code generation, multi-step problem solving)&lt;/li&gt;
&lt;/ul&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;Pro Tip&lt;/strong&gt; : Reasoning tokens are included in your API costs. Use "High" only when necessary to optimize your budget.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h2&gt;
  
  
  System Prompts: The "Lost in the Middle" Problem
&lt;/h2&gt;

&lt;p&gt;Here's a critical insight that can save you hours of debugging: &lt;strong&gt;Important rules must be repeated at the END of your prompt!&lt;/strong&gt;&lt;/p&gt;

&lt;h3&gt;
  
  
  ❌ What Doesn't Work
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;You are a helpful assistant.
RULE: Never share passwords or sensitive information.

[User Input]

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

&lt;/div&gt;



&lt;h3&gt;
  
  
  ✅ What Actually Works
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;You are a helpful assistant.
RULE: Never share passwords or sensitive information.

[User Input]

⚠️ REMINDER: Apply the rules above strictly, ESPECIALLY regarding passwords.

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

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;Why?&lt;/strong&gt; LLMs suffer from the "Lost in the Middle" phenomenon—they pay more attention to the beginning and end of the context window. Critical instructions buried in the middle are often ignored.&lt;/p&gt;

&lt;h2&gt;
  
  
  RAG Architecture: The Parent-Child Pattern
&lt;/h2&gt;

&lt;p&gt;Retrieval Augmented Generation (RAG) is essential for grounding LLM responses in your own data. The most effective pattern I've found is the &lt;strong&gt;Parent-Child approach&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fraw.githubusercontent.com%2Fabpframework%2Fabp%2Fdev%2Fdocs%2Fen%2FCommunity-Articles%2F2025-11-22-building-production-ready-llm-applications%2Fimages%2Frag-parent-child.svg" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fraw.githubusercontent.com%2Fabpframework%2Fabp%2Fdev%2Fdocs%2Fen%2FCommunity-Articles%2F2025-11-22-building-production-ready-llm-applications%2Fimages%2Frag-parent-child.svg" alt="RAG Parent-Child Architecture" width="1000" height="500"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h3&gt;
  
  
  How It Works
&lt;/h3&gt;

&lt;ol&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Split documents into hierarchies&lt;/strong&gt; :&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Store both in vector database&lt;/strong&gt; with references&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Query flow&lt;/strong&gt; :&lt;/p&gt;&lt;/li&gt;
&lt;/ol&gt;

&lt;h3&gt;
  
  
  The Overlap Strategy
&lt;/h3&gt;

&lt;p&gt;Always use overlapping chunks to prevent information loss at boundaries!&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Chunk 1: Token 0-500
Chunk 2: Token 400-900 ← 100 token overlap
Chunk 3: Token 800-1300 ← 100 token overlap

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

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;Standard recommendation&lt;/strong&gt; : 10-20% overlap (for 500 tokens, use 50-100 token overlap)&lt;/p&gt;

&lt;h3&gt;
  
  
  Implementation with Semantic Kernel
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;using Microsoft.SemanticKernel.Text;

var chunks = TextChunker.SplitPlainTextParagraphs(
    documentText, 
    maxTokensPerParagraph: 500,
    overlapTokens: 50
);

foreach (var chunk in chunks)
{
    var embedding = await embeddingService.GenerateEmbeddingAsync(chunk);
    await vectorDb.StoreAsync(chunk, embedding);
}

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

&lt;/div&gt;



&lt;h2&gt;
  
  
  PostgreSQL + pgvector: The Pragmatic Choice
&lt;/h2&gt;

&lt;p&gt;For .NET developers, choosing a vector database can be overwhelming. After evaluating multiple options, &lt;strong&gt;PostgreSQL with pgvector&lt;/strong&gt; is the most practical choice for most scenarios.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fraw.githubusercontent.com%2Fabpframework%2Fabp%2Fdev%2Fdocs%2Fen%2FCommunity-Articles%2F2025-11-22-building-production-ready-llm-applications%2Fimages%2Fpgvector-integration.svg" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fraw.githubusercontent.com%2Fabpframework%2Fabp%2Fdev%2Fdocs%2Fen%2FCommunity-Articles%2F2025-11-22-building-production-ready-llm-applications%2Fimages%2Fpgvector-integration.svg" alt="pgvector Integration" width="950" height="400"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h3&gt;
  
  
  Why pgvector?
&lt;/h3&gt;

&lt;p&gt;✅ &lt;strong&gt;Use existing SQL knowledge&lt;/strong&gt; - No new query language to learn&lt;br&gt;&lt;br&gt;
✅ &lt;strong&gt;EF Core integration&lt;/strong&gt; - Works with your existing data access layer&lt;br&gt;&lt;br&gt;
✅ &lt;strong&gt;JOIN with metadata&lt;/strong&gt; - Combine vector search with traditional queries&lt;br&gt;&lt;br&gt;
✅ &lt;strong&gt;WHERE clause filtering&lt;/strong&gt; - Filter by tenant, user, date, etc.&lt;br&gt;&lt;br&gt;
✅ &lt;strong&gt;ACID compliance&lt;/strong&gt; - Transaction support for data consistency&lt;br&gt;&lt;br&gt;
✅ &lt;strong&gt;No separate infrastructure&lt;/strong&gt; - One database for everything&lt;/p&gt;
&lt;h3&gt;
  
  
  Setting Up pgvector with EF Core
&lt;/h3&gt;

&lt;p&gt;First, install the NuGet package:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;dotnet add package Pgvector.EntityFrameworkCore

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

&lt;/div&gt;



&lt;p&gt;Define your entity:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;using Pgvector;
using Pgvector.EntityFrameworkCore;

public class DocumentChunk
{
    public Guid Id { get; set; }
    public string Content { get; set; }
    public Vector Embedding { get; set; } // 👈 pgvector type
    public Guid ParentChunkId { get; set; }
    public DateTime CreatedAt { get; set; }
}

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

&lt;/div&gt;



&lt;p&gt;Configure in DbContext:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;protected override void OnModelCreating(ModelBuilder builder)
{
    builder.HasPostgresExtension("vector");

    builder.Entity&amp;lt;DocumentChunk&amp;gt;()
        .Property(e =&amp;gt; e.Embedding)
        .HasColumnType("vector(1536)"); // 👈 OpenAI embedding dimension

    builder.Entity&amp;lt;DocumentChunk&amp;gt;()
        .HasIndex(e =&amp;gt; e.Embedding)
        .HasMethod("hnsw") // 👈 Fast approximate search
        .HasOperators("vector_cosine_ops");
}

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

&lt;/div&gt;



&lt;h3&gt;
  
  
  Performing Vector Search
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;using Pgvector.EntityFrameworkCore;

public async Task&amp;lt;List&amp;lt;DocumentChunk&amp;gt;&amp;gt; SearchAsync(string query)
{
    // 1. Convert query to embedding
    var queryVector = await _embeddingService.GetEmbeddingAsync(query);

    // 2. Search
    return await _context.DocumentChunks
        .OrderBy(c =&amp;gt; c.Embedding.L2Distance(queryVector)) // 👈 Lower is better
        .Take(5)
        .ToListAsync();
}

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

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;Source&lt;/strong&gt; : &lt;a href="https://github.com/pgvector/pgvector-dotnet?tab=readme-ov-file#entity-framework-core" rel="noopener noreferrer"&gt;Pgvector.NET on GitHub&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Smart Tool Usage: Make RAG a Tool, Not a Tax
&lt;/h2&gt;

&lt;p&gt;A common mistake is calling RAG on every single user message. This wastes tokens and money. Instead, &lt;strong&gt;make RAG a tool&lt;/strong&gt; and let the LLM decide when to use it.&lt;/p&gt;

&lt;h3&gt;
  
  
  ❌ Expensive Approach
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;// Always call RAG, even for "Hello"
var context = await PerformRAG(userMessage);
var response = await chatClient.CompleteAsync($"{context}\n\n{userMessage}");

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

&lt;/div&gt;



&lt;h3&gt;
  
  
  ✅ Smart Approach
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;[KernelFunction]
[Description("Search the company knowledge base for information")]
public async Task&amp;lt;string&amp;gt; SearchKnowledgeBase(
    [Description("The search query")] string query)
{
    var results = await _vectorDb.SearchAsync(query);
    return string.Join("\n---\n", results.Select(r =&amp;gt; r.Content));
}

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

&lt;/div&gt;



&lt;p&gt;The LLM will call &lt;code&gt;SearchKnowledgeBase&lt;/code&gt; only when needed:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;"Hello" → No tool call&lt;/li&gt;
&lt;li&gt;"What was our 2024 revenue?" → Calls tool&lt;/li&gt;
&lt;li&gt;"Tell me a joke" → No tool call&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Multilingual RAG: Query Translation Strategy
&lt;/h2&gt;

&lt;p&gt;When your documents are in one language (e.g., English) but users query in another (e.g., Turkish), you need a translation strategy.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fraw.githubusercontent.com%2Fabpframework%2Fabp%2Fdev%2Fdocs%2Fen%2FCommunity-Articles%2F2025-11-22-building-production-ready-llm-applications%2Fimages%2Fmultilingual-rag.svg" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fraw.githubusercontent.com%2Fabpframework%2Fabp%2Fdev%2Fdocs%2Fen%2FCommunity-Articles%2F2025-11-22-building-production-ready-llm-applications%2Fimages%2Fmultilingual-rag.svg" alt="Multilingual RAG Architecture" width="1000" height="400"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h3&gt;
  
  
  Solution Options
&lt;/h3&gt;

&lt;p&gt;&lt;strong&gt;Option 1&lt;/strong&gt; : Use an LLM that automatically calls tools in English&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Many modern LLMs can do this if properly instructed&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;Option 2&lt;/strong&gt; : Tool chain approach&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;[KernelFunction]
[Description("Translate text to English")]
public async Task&amp;lt;string&amp;gt; TranslateToEnglish(string text)
{
    // Translation logic
}

[KernelFunction]
[Description("Search knowledge base (English only)")]
public async Task&amp;lt;string&amp;gt; SearchKnowledgeBase(string englishQuery)
{
    // Search logic
}

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

&lt;/div&gt;



&lt;p&gt;The LLM will:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Call &lt;code&gt;TranslateToEnglish("2024 geliri nedir?")&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;Get "What was 2024 revenue?"&lt;/li&gt;
&lt;li&gt;Call &lt;code&gt;SearchKnowledgeBase("What was 2024 revenue?")&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;Return results and respond in Turkish&lt;/li&gt;
&lt;/ol&gt;

&lt;h2&gt;
  
  
  Model Context Protocol (MCP): Beyond In-Process Tools
&lt;/h2&gt;

&lt;p&gt;Microsoft and Anthropic recently released official C# SDKs for the Model Context Protocol (MCP). This is a game-changer for tool reusability.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fraw.githubusercontent.com%2Fabpframework%2Fabp%2Fdev%2Fdocs%2Fen%2FCommunity-Articles%2F2025-11-22-building-production-ready-llm-applications%2Fimages%2Fmcp-architecture.svg" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fraw.githubusercontent.com%2Fabpframework%2Fabp%2Fdev%2Fdocs%2Fen%2FCommunity-Articles%2F2025-11-22-building-production-ready-llm-applications%2Fimages%2Fmcp-architecture.svg" alt="MCP Architecture" width="900" height="500"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h3&gt;
  
  
  MCP vs. Semantic Kernel Plugins
&lt;/h3&gt;

&lt;p&gt;| Feature | SK Plugins | MCP Servers | |---------|-----------|-------------| | &lt;strong&gt;Process&lt;/strong&gt; | In-process | Out-of-process (stdio/http) | | &lt;strong&gt;Reusability&lt;/strong&gt; | Application-specific | Cross-application | | &lt;strong&gt;Examples&lt;/strong&gt; | Used within your app | VS Code Copilot, Claude Desktop |&lt;/p&gt;

&lt;h3&gt;
  
  
  Creating an MCP Server
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;using Microsoft.Extensions.Hosting;
using ModelContextProtocol.Extensions.Hosting;

var builder = Host.CreateEmptyApplicationBuilder(settings: null);

builder.Services.AddMcpServer()
.WithStdioServerTransport()
.WithToolsFromAssembly();

await builder.Build().RunAsync();

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

&lt;/div&gt;



&lt;p&gt;Define your tools:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;[McpServerToolType]
public static class FileSystemTools
{
    [McpServerTool, Description("Read a file from the file system")]
    public static async Task&amp;lt;string&amp;gt; ReadFile(string path)
    {
        // ⚠️ SECURITY: Always validate paths!
        if (!IsPathSafe(path)) 
            throw new SecurityException("Invalid path");

        return await File.ReadAllTextAsync(path);
    }

    private static bool IsPathSafe(string path)
    {
        // Implement path traversal prevention
        var fullPath = Path.GetFullPath(path);
        return fullPath.StartsWith(AllowedDirectory);
    }
}

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

&lt;/div&gt;



&lt;p&gt;Your MCP server can now be used by VS Code Copilot, Claude Desktop, or any other MCP client!&lt;/p&gt;

&lt;h2&gt;
  
  
  Chat History Management: Truncation + RAG Hybrid
&lt;/h2&gt;

&lt;p&gt;For long conversations, storing all history in the context window becomes impractical. Here's the pattern that works:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fraw.githubusercontent.com%2Fabpframework%2Fabp%2Fdev%2Fdocs%2Fen%2FCommunity-Articles%2F2025-11-22-building-production-ready-llm-applications%2Fimages%2Fchat-history-hybrid.svg" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fraw.githubusercontent.com%2Fabpframework%2Fabp%2Fdev%2Fdocs%2Fen%2FCommunity-Articles%2F2025-11-22-building-production-ready-llm-applications%2Fimages%2Fchat-history-hybrid.svg" alt="Chat History Hybrid Strategy" width="800" height="450"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h3&gt;
  
  
  ❌ Lossy Approach
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;First 50 messages → Summarize with LLM → Single summary message

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

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;Problem&lt;/strong&gt; : Detail loss (fidelity loss)&lt;/p&gt;

&lt;h3&gt;
  
  
  ✅ Hybrid Approach
&lt;/h3&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;strong&gt;Recent messages&lt;/strong&gt; (last 5-10): Keep in prompt for immediate context&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Older messages&lt;/strong&gt; : Store in vector database as a tool
&lt;/li&gt;
&lt;/ol&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;[KernelFunction]
[Description("Search conversation history for past discussions")]
public async Task&amp;lt;string&amp;gt; SearchChatHistory(
    [Description("What to search for")] string query)
{
    var relevantMessages = await _vectorDb.SearchAsync(query);
    return string.Join("\n", relevantMessages.Select(m =&amp;gt; 
        $"[{m.Timestamp}] {m.Role}: {m.Content}"));
}

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

&lt;/div&gt;



&lt;p&gt;The LLM retrieves only relevant past context when needed, avoiding summary-induced information loss.&lt;/p&gt;

&lt;h2&gt;
  
  
  RAG vs. Fine-Tuning: Choose Wisely
&lt;/h2&gt;

&lt;p&gt;A common misconception is using fine-tuning for knowledge injection. Here's when to use each:&lt;/p&gt;

&lt;p&gt;| Purpose | RAG | Fine-Tuning | |---------|-----|-------------| | &lt;strong&gt;Goal&lt;/strong&gt; | Memory (provide facts) | Behavior (teach style) | | &lt;strong&gt;Updates&lt;/strong&gt; | Dynamic (add docs anytime) | Static (requires retraining) | | &lt;strong&gt;Cost&lt;/strong&gt; | Low dev, higher inference | High dev, lower inference | | &lt;strong&gt;Hallucination&lt;/strong&gt; | Reduces | Doesn't reduce | | &lt;strong&gt;Use Case&lt;/strong&gt; | Company docs, FAQs | Brand voice, specific format |&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Common mistake&lt;/strong&gt; : "Let's fine-tune on our company documents" ❌&lt;br&gt;&lt;br&gt;
&lt;strong&gt;Better approach&lt;/strong&gt; : Use RAG! ✅&lt;/p&gt;

&lt;p&gt;Fine-tuning is for teaching the model &lt;em&gt;how&lt;/em&gt; to respond, not &lt;em&gt;what&lt;/em&gt; to know.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Source&lt;/strong&gt; : &lt;a href="https://www.oracle.com/artificial-intelligence/generative-ai/retrieval-augmented-generation-rag/rag-fine-tuning/" rel="noopener noreferrer"&gt;Oracle - RAG vs Fine-Tuning&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Bonus: Why SVG is Superior for LLM-Generated Images
&lt;/h2&gt;

&lt;p&gt;When using LLMs to generate diagrams and visualizations, always request SVG format instead of PNG or JPG.&lt;/p&gt;

&lt;h3&gt;
  
  
  Why SVG?
&lt;/h3&gt;

&lt;p&gt;✅ &lt;strong&gt;Text-based&lt;/strong&gt; → LLMs produce better results&lt;br&gt;&lt;br&gt;
✅ &lt;strong&gt;Lower cost&lt;/strong&gt; → Fewer tokens than base64-encoded images&lt;br&gt;&lt;br&gt;
✅ &lt;strong&gt;Editable&lt;/strong&gt; → Easy to modify after generation&lt;br&gt;&lt;br&gt;
✅ &lt;strong&gt;Scalable&lt;/strong&gt; → Perfect quality at any size&lt;br&gt;&lt;br&gt;
✅ &lt;strong&gt;Version control friendly&lt;/strong&gt; → Works great in Git&lt;/p&gt;

&lt;h3&gt;
  
  
  Example Prompt
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Create an architecture diagram showing PostgreSQL with pgvector integration.
Format: SVG, 800x400 pixels. Show: .NET Application → EF Core → PostgreSQL → Vector Search.
Use arrows to connect stages. Color scheme: Blue tones.

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

&lt;/div&gt;



&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fraw.githubusercontent.com%2Fabpframework%2Fabp%2Fdev%2Fdocs%2Fen%2FCommunity-Articles%2F2025-11-22-building-production-ready-llm-applications%2Fimages%2Fsvg-diagram-example.svg" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fraw.githubusercontent.com%2Fabpframework%2Fabp%2Fdev%2Fdocs%2Fen%2FCommunity-Articles%2F2025-11-22-building-production-ready-llm-applications%2Fimages%2Fsvg-diagram-example.svg" alt="SVG Diagram Example" width="800" height="400"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;All diagrams in this article were generated as SVG, resulting in excellent quality and lower token costs!&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;Pro Tip&lt;/strong&gt; : If you don't need photographs or complex renders, always choose SVG.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h2&gt;
  
  
  Architecture Roadmap: Putting It All Together
&lt;/h2&gt;

&lt;p&gt;Here's the recommended stack for building production LLM applications with .NET:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;strong&gt;Orchestration&lt;/strong&gt; : Microsoft.Extensions.AI + Semantic Kernel (when needed)&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Vector Database&lt;/strong&gt; : PostgreSQL + Pgvector.EntityFrameworkCore&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;RAG Pattern&lt;/strong&gt; : Parent-Child chunks with 10-20% overlap&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Tools&lt;/strong&gt; : MCP servers for reusability&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Reasoning&lt;/strong&gt; : ReasoningEffortLevel instead of temperature&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Prompting&lt;/strong&gt; : Critical rules at the end&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Cost Optimization&lt;/strong&gt; : Make RAG a tool, not automatic&lt;/li&gt;
&lt;/ol&gt;

&lt;h2&gt;
  
  
  Key Takeaways
&lt;/h2&gt;

&lt;p&gt;Let me summarize the most important production tips:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;strong&gt;Temperature is gone&lt;/strong&gt; → Use &lt;code&gt;ReasoningEffortLevel&lt;/code&gt; with GPT-5&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Rules at the end&lt;/strong&gt; → Combat "Lost in the Middle"&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;RAG as a tool&lt;/strong&gt; → Reduce costs significantly&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Parent-Child pattern&lt;/strong&gt; → Search small, respond with large&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Always use overlap&lt;/strong&gt; → 10-20% is the standard&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;pgvector for most cases&lt;/strong&gt; → Unless you have billions of vectors&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;MCP for reusability&lt;/strong&gt; → One codebase, works everywhere&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;SVG for diagrams&lt;/strong&gt; → Better results, lower cost&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Hybrid chat history&lt;/strong&gt; → Recent in prompt, old in vector DB&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;RAG &amp;gt; Fine-tuning&lt;/strong&gt; → For knowledge, not behavior&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;Happy coding! 🚀&lt;/p&gt;

</description>
      <category>applicationdevelopme</category>
      <category>postgres</category>
      <category>llms</category>
      <category>mcp</category>
    </item>
    <item>
      <title>Announcing Server-Side Rendering Support for ABP Framework Angular Applications</title>
      <dc:creator>ABP.IO</dc:creator>
      <pubDate>Thu, 20 Nov 2025 06:46:59 +0000</pubDate>
      <link>https://dev.to/abp_io/announcing-server-side-rendering-support-for-abp-framework-angular-applications-19hm</link>
      <guid>https://dev.to/abp_io/announcing-server-side-rendering-support-for-abp-framework-angular-applications-19hm</guid>
      <description>&lt;h1&gt;
  
  
  Announcing Server-Side Rendering (SSR) Support for ABP Framework Angular Applications
&lt;/h1&gt;

&lt;p&gt;We are pleased to announce that &lt;strong&gt;Server-Side Rendering (SSR)&lt;/strong&gt; has become available for ABP Framework Angular applications! This highly requested feature brings major gains in performance, SEO, and user experience to your Angular applications based on ABP Framework.&lt;/p&gt;

&lt;h2&gt;
  
  
  What is Server-Side Rendering (SSR)?
&lt;/h2&gt;

&lt;p&gt;Server-Side Rendering refers to an approach which renders your Angular application on the server as opposed to the browser. The server creates the complete HTML for a page and sends it to the client, which can then show the page to the user. This poses many advantages over traditional client-side rendering.&lt;/p&gt;

&lt;h2&gt;
  
  
  Why SSR Matters for ABP Angular Applications
&lt;/h2&gt;

&lt;h3&gt;
  
  
  Improved Performance
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Quicker visualization of the first contentful paint (FCP)&lt;/strong&gt;: Because prerendered HTML is sent over from the server, users will see content quicker.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Better perceived performance&lt;/strong&gt; : Even on slower devices, the page will be displaying something sooner.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Less JavaScript parsing time&lt;/strong&gt; : For example, the initial page load will not require parsing and executing a large bundle of JavaScript.&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  Enhanced SEO
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Improved indexing by search engines&lt;/strong&gt; : Search engine bots are able to crawl and index your content quicker.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Improved rankings in search&lt;/strong&gt; : The quicker the content loads and the easier it is to access, the better your SEO score.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Preview when sharing on social channels&lt;/strong&gt; : Rich previews with the appropriate meta tags are generated when sharing links on social platforms.&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  Better User Experience
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Support for low bandwidth&lt;/strong&gt; : Users with slower Internet connections will have a better experience&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Progressive enhancement&lt;/strong&gt; : Users can start accessing the content before JavaScript has loaded&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Better accessibility&lt;/strong&gt; : Screen readers and other assistive technologies can access the content immediately&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Getting Started with SSR
&lt;/h2&gt;

&lt;h3&gt;
  
  
  Adding SSR to an Existing Project
&lt;/h3&gt;

&lt;p&gt;You can easily add SSR support to your existing ABP Angular application using the Angular CLI with ABP schematics:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;Adds SSR configuration to your project&lt;br&gt;
&lt;/p&gt;
&lt;/blockquote&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;ng generate @abp/ng.schematics:ssr-add

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

&lt;/div&gt;



&lt;blockquote&gt;
&lt;p&gt;Short form&lt;br&gt;
&lt;/p&gt;
&lt;/blockquote&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;ng g @abp/ng.schematics:ssr-add

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

&lt;/div&gt;



&lt;p&gt;If you have multiple projects in your workspace, you can specify which project to add SSR to:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;ng g @abp/ng.schematics:ssr-add &lt;span class="nt"&gt;--project&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;my-project

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

&lt;/div&gt;



&lt;p&gt;If you want to skip the automatic installation of dependencies:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;ng g @abp/ng.schematics:ssr-add &lt;span class="nt"&gt;--skip-install&lt;/span&gt;

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

&lt;/div&gt;



&lt;h2&gt;
  
  
  What Gets Configured
&lt;/h2&gt;

&lt;p&gt;When you add SSR to your ABP Angular project, the schematic automatically:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;strong&gt;Installs necessary dependencies&lt;/strong&gt; : Adds &lt;code&gt;@angular/ssr&lt;/code&gt; and related packages&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Creates Server Configuration&lt;/strong&gt; : Creates &lt;code&gt;server.ts&lt;/code&gt; and related files&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Updates Project Structure&lt;/strong&gt; :

&lt;ul&gt;
&lt;li&gt;Creates &lt;code&gt;main.server.ts&lt;/code&gt; to bootstrap the server&lt;/li&gt;
&lt;li&gt;Adds &lt;code&gt;app.config.server.ts&lt;/code&gt; for standalone apps (or &lt;code&gt;app.module.server.ts&lt;/code&gt; for NgModule apps)&lt;/li&gt;
&lt;li&gt;Configures server routes in &lt;code&gt;app.routes.server.ts&lt;/code&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Updates Build Configuration&lt;/strong&gt; : updates &lt;code&gt;angular.json&lt;/code&gt; to include:

&lt;ul&gt;
&lt;li&gt;a &lt;code&gt;serve-ssr&lt;/code&gt; target for local SSR development&lt;/li&gt;
&lt;li&gt;a &lt;code&gt;prerender&lt;/code&gt; target for static site generation&lt;/li&gt;
&lt;li&gt;Proper output paths for browser and server bundles&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ol&gt;

&lt;h2&gt;
  
  
  Supported Configurations
&lt;/h2&gt;

&lt;p&gt;The ABP SSR schematic supports both modern and legacy Angular build configurations:&lt;/p&gt;

&lt;h3&gt;
  
  
  Application Builder (Suggested)
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;The new &lt;code&gt;@angular-devkit/build-angular:application&lt;/code&gt; builder&lt;/li&gt;
&lt;li&gt;Optimized for Angular 17+ apps&lt;/li&gt;
&lt;li&gt;Enhanced performance and smaller bundle sizes&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  Server Builder (Legacy)
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;The original &lt;code&gt;@angular-devkit/build-angular:server&lt;/code&gt; builder&lt;/li&gt;
&lt;li&gt;Designed for legacy Angular applications&lt;/li&gt;
&lt;li&gt;Compatible with legacy applications&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Running Your SSR Application
&lt;/h2&gt;

&lt;p&gt;After adding SSR to your project, you can run your application in SSR mode:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="c"&gt;# Development mode with SSR&lt;/span&gt;
ng serve

&lt;span class="c"&gt;# Or specifically target SSR development server&lt;/span&gt;
npm run serve:ssr

&lt;span class="c"&gt;# Build for production&lt;/span&gt;
npm run build:ssr

&lt;span class="c"&gt;# Preview production build&lt;/span&gt;
npm run serve:ssr:production

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

&lt;/div&gt;



&lt;h2&gt;
  
  
  Important Considerations
&lt;/h2&gt;

&lt;h3&gt;
  
  
  Browser-Only APIs
&lt;/h3&gt;

&lt;p&gt;Some browser APIs are not available on the server. Use platform checks to conditionally execute code:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;isPlatformBrowser&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;@angular/common&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;PLATFORM_ID&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;inject&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;@angular/core&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="k"&gt;export&lt;/span&gt; &lt;span class="kd"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;MyComponent&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="k"&gt;private&lt;/span&gt; &lt;span class="nx"&gt;platformId&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;inject&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;PLATFORM_ID&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

  &lt;span class="nf"&gt;ngOnInit&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;if &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nf"&gt;isPlatformBrowser&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;platformId&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="c1"&gt;// Code that uses browser-only APIs&lt;/span&gt;
      &lt;span class="nx"&gt;console&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;log&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;Running in browser&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
      &lt;span class="nx"&gt;localStorage&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;setItem&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;key&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;value&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

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

&lt;/div&gt;



&lt;h3&gt;
  
  
  Storage APIs
&lt;/h3&gt;

&lt;p&gt;&lt;code&gt;localStorage&lt;/code&gt; and &lt;code&gt;sessionStorage&lt;/code&gt; are not accessible on the server. Consider using:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Cookies for server-accessible data.&lt;/li&gt;
&lt;li&gt;The state transfer API for hydration.&lt;/li&gt;
&lt;li&gt;ABP's built-in storage abstractions.&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  Third-Party Libraries
&lt;/h3&gt;

&lt;p&gt;Please ensure that any third-party libraries you use are compatible with SSR. These libraries can require:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Dynamic imports for browser-only code.&lt;/li&gt;
&lt;li&gt;Platform-specific service providers.&lt;/li&gt;
&lt;li&gt;Custom Angular Universal integration.&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  ABP Framework Integration
&lt;/h2&gt;

&lt;p&gt;The SSR implementation is natively integrated with all of the ABP Framework features:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Authentication &amp;amp; Authorization&lt;/strong&gt; : The OAuth/OpenID Connect flow functions seamlessly with ABP&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Multi-tenancy&lt;/strong&gt; : Fully supports tenant resolution and switching&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Localization&lt;/strong&gt; : Server-side rendering respects the locale&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Permission Management&lt;/strong&gt; : Permission checks work on both server and client&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Configuration&lt;/strong&gt; : The ABP configuration system is SSR-ready&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Performance Tips
&lt;/h2&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;strong&gt;Utilize State Transfer&lt;/strong&gt; : Send data from server to client to eliminate redundant HTTP requests&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Optimize Images&lt;/strong&gt; : Proper image loading strategies, such as lazy loading and responsive images.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Cache API Responses&lt;/strong&gt; : At the server, implement proper caching strategies.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Monitor Bundle Size&lt;/strong&gt; : Keep your server bundle optimized&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Use Prerendering&lt;/strong&gt; : The prerender target should be used for static content.&lt;/li&gt;
&lt;/ol&gt;

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

&lt;p&gt;Server-side rendering can be a very effective feature in improving your ABP Angular application's performance, SEO, and user experience. Our new SSR schematic will make it easier than ever to add SSR to your project.&lt;/p&gt;

&lt;p&gt;Try it today and let us know what you think!&lt;/p&gt;




</description>
      <category>angular</category>
      <category>abp</category>
      <category>prerendering</category>
      <category>abpframework</category>
    </item>
  </channel>
</rss>
