AI & Automation

AI Integration in Optimizely CMS 12: Boosting Developer Productivity

By Ginbok6 min read

The pace of modern IT software development demands continuous optimization of our internal processes. For projects like CmsIv, which relies heavily on high-volume content delivery via Optimizely CMS 12 and Episerver Commerce 14, manual editorial tasks often become critical bottlenecks, slowing down feature deployment and developer velocity. This post outlines how we are strategically applying AI services to automate core content tasks, shifting the workload from human editors and developers to intelligent systems, thereby significantly improving project performance.

The Workload Bottleneck: Manual Content Optimization

A significant portion of a feature's development cycle involves ensuring content is optimized for SEO, accessibility, and relevance. In a traditional CMS workflow, developers build the content type, and editors manually populate fields like:

This iterative manual process requires significant back-and-forth and QA time. By offloading these repetitive, text-analysis heavy tasks to an AI service, we accelerate the critical path of development.

Designing the AI Service Abstraction Layer

To keep the core CmsIv.Web application decoupled from specific AI vendor implementations (e.g., Azure OpenAI, Google Gemini), we implement a simple abstraction layer within our .NET 8 environment. This service handles the external API calls and returns structured data that Optimizely can consume.

Defining the AI Output Contract

We first define a model representing the structured output we expect from the AI service when analyzing a block of content.

// CmsIv.Model/AiOptimizationResult.cs

namespace CmsIv.Model.AI
{
    public class AiOptimizationResult
    {
        public required string SuggestedMetaTitle { get; set; }
        public required string SuggestedMetaDescription { get; set; }
        public required string SuggestedKeywords { get; set; } // Comma-separated string
        public required string SuggestedSummary { get; set; }
    }
}

Implementing the .NET 8 AI Integration Service

The following service uses HttpClient (leveraging .NET 8's dependency injection) to connect to our dedicated AI endpoint. Crucially, the AI is prompted to return its analysis in a specific JSON format, making deserialization reliable.

// CmsIv.Web/Services/AiContentOptimizer.cs

using CmsIv.Model.AI;
using System.Text;
using System.Text.Json;

namespace CmsIv.Web.Services
{
    public interface IAiContentOptimizer
    {
        Task<AiOptimizationResult> OptimizeContentAsync(string contentBody);
    }

    public class AiContentOptimizer : IAiContentOptimizer
    {
        private readonly HttpClient _httpClient;
        private readonly ILogger<AiContentOptimizer> _logger;
        private const string AiEndpoint = "https://ai-content-svc.cmsiv.com/optimize";

        public AiContentOptimizer(HttpClient httpClient, ILogger<AiContentOptimizer> logger)
        {
            _httpClient = httpClient;
            _logger = logger;
        }

        public async Task<AiOptimizationResult> OptimizeContentAsync(string contentBody)
        {
            try
            {
                var requestPayload = new
                {
                    text = contentBody,
                    format = "json"
                };

                var content = new StringContent(
                    JsonSerializer.Serialize(requestPayload),
                    Encoding.UTF8,
                    "application/json"
                );

                var response = await _httpClient.PostAsync(AiEndpoint, content);
                response.EnsureSuccessStatusCode();

                var jsonString = await response.Content.ReadAsStringAsync();
                var result = JsonSerializer.Deserialize<AiOptimizationResult>(jsonString, 
                    new JsonSerializerOptions { PropertyNameCaseInsensitive = true });
                
                return result ?? throw new InvalidOperationException("AI service returned null result.");
            }
            catch (Exception ex)
            {
                _logger.LogError(ex, "Failed to optimize content using AI service.");
                // Return a default, empty result to prevent application failure
                return new AiOptimizationResult
                {
                    SuggestedMetaTitle = string.Empty,
                    SuggestedMetaDescription = string.Empty,
                    SuggestedKeywords = string.Empty,
                    SuggestedSummary = string.Empty
                };
            }
        }
    }
}

Automating Optimizely Content Creation Events

The key to improving workload performance is automation triggered immediately upon editor action. In Optimizely, we can leverage IContentEvents to intercept the content creation/saving process and insert our AI optimization step before the item is persisted to the SQL Server database.

We implement a scheduled initialization module that listens for the SavingContent event. This ensures the automated metadata is generated before the editor even fills the form, or as a validation/suggestion layer.

// CmsIv.Web/Initialization/AiContentAutomationModule.cs

using CmsIv.Model.Content; // Assuming ArticlePage is defined here
using CmsIv.Web.Services;
using EPiServer.Core;
using EPiServer.Framework;
using EPiServer.Framework.Initialization;
using EPiServer.ServiceLocation;
using EPiServer.SpecializedProperties;

[InitializableModule]
[ModuleDependency(typeof(EPiServer.Web.InitializationModule))]
public class AiContentAutomationModule : IConfigurableModule
{
    public void ConfigureContainer(ServiceConfigurationContext context)
    {
        // Register HttpClient for external API calls
        context.Services.AddHttpClient<IAiContentOptimizer, AiContentOptimizer>();
    }

    public void Initialize(InitializationEngine context)
    {
        var contentEvents = context.Locate.Advanced.GetInstance<IContentEvents>();
        contentEvents.SavingContent += OnSavingContent;
    }

    private async void OnSavingContent(object sender, ContentEventArgs e)
    {
        // Only run optimization when the content is newly created or major body text has changed.
        if (e.Content is ArticlePage articlePage && e.Content.Status == VersionStatus.Draft)
        {
            // We retrieve the service from the Content Service Locator
            var optimizer = ServiceLocator.Current.GetInstance<IAiContentOptimizer>();
            
            // Assuming ArticlePage has a property named MainBody (XhtmlString)
            string contentForAnalysis = articlePage.MainBody.To  String();

            if (string.IsNullOrWhiteSpace(contentForAnalysis) || contentForAnalysis.Length < 200)
            {
                return; // Not enough content to analyze
            }

            var optimizationResult = await optimizer.OptimizeContentAsync(contentForAnalysis);

            // Apply suggestions if the fields are currently empty or significantly different
            if (string.IsNullOrWhiteSpace(articlePage.MetaTitle))
            {
                articlePage.MetaTitle = optimizationResult.SuggestedMetaTitle;
            }
            
            if (string.IsNullOrWhiteSpace(articlePage.MetaDescription))
            {
                articlePage.MetaDescription = optimizationResult.SuggestedMetaDescription;
            }

            // Optional: Handle keywords, potentially merging with existing tags
            if (articlePage.Keywords == null || articlePage.Keywords.Count == 0)
            {
                articlePage.Keywords = new PropertyStringList(optimizationResult.SuggestedKeywords.Split(','));
            }
        }
    }

    public void Uninitialize(InitializationEngine context)
    {
        var contentEvents = context.Locate.Advanced.GetInstance<IContentEvents>();
        contentEvents.SavingContent -= OnSavingContent;
    }
}

Impact on Developer Workload and Performance

By implementing this automated workflow, we achieve several critical performance gains within the CmsIv team:

  1. Reduced Editorial Review Cycles: AI-generated metadata provides a strong starting point, eliminating 80% of the manual research and writing time required by editors.
  2. Consistent Quality: Ensures all content adheres to strict SEO length and keyword density rules enforced by the AI prompt, reducing QA failure rates.
  3. Accelerated Feature Delivery: Developers can deploy content types knowing that the automated optimization pipeline handles the time-consuming data population, allowing them to focus on complex feature logic rather than content field setup.

This systematic application of AI shifts the workload from tedious, high-cost manual labor to efficient, asynchronous processing, dramatically improving the overall throughput of our software development service solution.

Troubleshooting: Common AI Integration Challenges

Integrating external, asynchronous AI services into synchronous Optimizely events can introduce challenges, primarily related to thread blocking and dependency management.

Cause: Asynchronous Operations in Synchronous Events

Optimizely's IContentEvents (like SavingContent) fire synchronously. While we use an async void event handler above, attempting to perform long-running blocking calls (e.g., using .Result on the Task) within a synchronous handler will deadlock the application pool, severely impacting performance.

Solution: Leveraging Async Content Infrastructure (Recommended)

The safest long-term solution is to ensure long-running external API calls are non-blocking. While async void is used here for simplicity, the preferred advanced solution for mission-critical asynchronous content manipulations is to offload the process to an Optimizely job queue or a dedicated background worker (like Hangfire or hosted services in .NET 8) that runs the AI call outside the request lifecycle, updating the content later via IContentRepository.

Cause: Serialization Mismatches

The external AI model might sometimes return malformed JSON or change its response structure without warning, causing JsonSerializer.Deserialize to fail silently or throw exceptions.

Solution: Robust Error Handling and Validation

Always implement comprehensive try-catch blocks around external service calls, as shown in AiContentOptimizer.cs. If the AI call fails, the system must fail gracefully, returning empty strings or null values rather than preventing the content save operation entirely. Use structured logging to monitor serialization failures.

#ai#automation#optimizely#seo#workflow#asp.net-core#cms13#backend
← Back to Articles