Back to "零 PII 客户情报@-部分 @2: 剖析,信号 @MS K3片段"

This is a viewer only at the moment see the article on how this works.

To update the preview hit Ctrl-Alt-R (or ⌘-Alt-R on Mac) or Enter to refresh. The Save icon lets you save the markdown file to disk

This is a preview from the server running through my markdig pipeline

C# Privacy Product Segmentation

零 PII 客户情报@-部分 @2: 剖析,信号 @MS K3片段

Wednesday, 31 December 2025

内 部分#1 我们报导了哲学和系列概览 . 部分#1.1 我们建造了样本数据生成器... ....

现在让我们来建立核心系统... ....

  1. Zero-PII 配置文件结构 - 短会与持久性剖面图
  2. 信号和重量 - 我们追踪的是什么 它如何积累
  3. 区段定义 主要事件=)
  4. 发件箱模式预览 @-#可靠事件发布@(_BARBAR_详情载于 3)}部分

这是 样本项目 (Mostlylucid.SegmentCommerce“)”“-”是一个小型的电子商务演示,它展示了核心模式 ”“.”“It' ”故意简化以清晰地显示概念“MS K4” ,但也包含真正的基础设施模式 “(”“Outbox”“I-”信息处理程序“MSC7”背景工作处理“MSKO8” JSONB 索引“MSQ9 ”您在生产中横向标定“MSG10”“D”“Z.”

将它视为“"”生产模式, 在单一的“MSC1”中, “app forms concession”."。同样的代码结构无论您是运行一个进程或者在服务之间分布都有效。“.”

技术选择

我们选择了 简单,有凝聚力的堆疊 来保持样本清晰度,同时演示生产模式 @.

PostgreSQL + pgvectorMS K1一个数据库

取代分开的矢量数据库@,信件队列 @,和缓存层,我们使用 邮件greSQL:

flowchart LR
    App[ASP.NET Core] --> PG[(PostgreSQL)]
    
    PG --> JSONB[JSONB<br/>interests]
    PG --> Vector[pgvector<br/>embeddings]
    PG --> Queue[Queue tables<br/>jobs, outbox]
    PG --> FTS[Full-text<br/>tsvector]
    
    style PG stroke:#2f9e44,stroke-width:3px
    style JSONB stroke:#1971c2,stroke-width:2px
    style Vector stroke:#1971c2,stroke-width:2px
    style Queue stroke:#1971c2,stroke-width:2px
    style FTS stroke:#1971c2,stroke-width:2px
  • 杰森布 用于柔性 schemas @ (# 没有分开的 NOSQL)@
  • pgvictor 变量 用于嵌入@( @no Qdrant @/ @Pinecone_)}
  • 听不见 用于 & M( @ no Redis@ / @ RabbitM)} 任务
  • 向量 以完整@-text @ (no Elasticsearch_ MS K2

一个连接库.一个备份MS K1一次部署.

HTMX +Alpine.jsMS K2伺服器-Driven UI

<!-- Instant search (no page reload) -->
<input 
    hx-get="/api/search" 
    hx-trigger="keyup changed delay:300ms" 
    hx-target="#results" />
  • HTMX: 伺服器提供部分 {(No JSON}→ tylatinMS K3}
  • 阿尔卑斯山:无建构步骤的自动
  • 逐步加强@:没有JS,更好用。

SPA+-}类似 UX,服务机为 @-}发送简单@.

ASP=.NET 核心}:分配模式

單一應用程式 ,}(但模式大小) *:

  • 发件箱 @ (DB 事件@)} @ I→ 换为信件总线
  • 任务队列 (=LISTEN=/=NOTIFY=NSK2}→}交换工人游泳池
  • 会会收集器 → 互换单独的API

需要时开始简单@ . @ 分配 @ MS K1 @ {

我所避免的,

  • 没有单独的矢量数据库\ ( pgvictor 位于同一位置@ MS K1
  • 没有 JS 框架 @(_HTMX #MSSK1#阿尔卑斯山比较简单#)}
  • 还没有微服务,但是在单体或分布式中工作
  • 无嵌入式拼写编程@ ( @ one DBQ,}一个 AppQ)

零-PII挑战

传统用户追踪存储库可识别信息@:name @,emails,与行为有关的用户身份@MS K3GNSR和隐私条例使得这越来越成问题_.我们的方法不同:

我们储存行为模式 , 不是身份

flowchart LR
    subgraph "Traditional Approach"
        User1[User: [email protected]] --> Behavior1[Bought headphones]
        Behavior1 --> PII1[(PII Database)]
    end
    
    subgraph "Zero-PII Approach"
        User2[Anonymous Session] --> Behavior2[Bought headphones]
        Behavior2 --> Pattern[(Category: tech<br/>Signal weight: 1.0)]
    end
    
    style PII1 stroke:#c92a2a,stroke-width:3px
    style Pattern stroke:#2f9e44,stroke-width:3px

关键洞察力 : 不需要知道WHO 有人想知道他们感兴趣的是什么.

建筑结构概览

这个系统有三个核心层 :

flowchart TB
    Browser[Browser] --> Session[Session Collector]
    Session --> Profile[Persistent Profile]
    Profile --> Segments[Segment Service]
    
    Session -->|in-memory only| Cache[(IMemoryCache)]
    Profile -->|elevated signals| DB[(PostgreSQL + JSONB)]
    Segments -->|memberships| UI[Segment Explorer UI]
    
    style Session stroke:#1971c2,stroke-width:3px
    style Profile stroke:#2f9e44,stroke-width:3px
    style Segments stroke:#fab005,stroke-width:3px
    style Cache stroke:#e64980,stroke-width:2px
  1. 会话收集器 @-捕捉行为信号@(views @,点击 @MS K3马车加上#) @_- 只有 - @ memory
  2. 持久性简介 计算利息=- 数据库\ - 已支持
  3. 分部分服务 “-”评估规则的概况“,”分配成员。

关键区别:+Sessions is epheral@.+Profiles is continually @.}这不是一个执行细节-\Qiit_'+Smission 隐私架构决定=.{

会话剖析档:严格在MS K1Memory @(LFU 缓存})

届会为 严格在@ - @ memory他们住在这里 IMemoryCache 滑动满期和 从不触动数据库.

这是一个硬性建筑限制@:会话数据无法持续 @. 它在访问中收集信号,

会话Profile@ : @ in- @ memory 模型

// Mostlylucid.SegmentCommerce/Models/SessionProfile.cs
public class SessionProfile
{
    public string SessionKey { get; set; } = string.Empty;

    // Category interest scores: { "tech": 0.75, "fashion": 0.25 }
    public Dictionary<string, double> Interests { get; set; } = new();

    // Detailed signal counts: { "tech": { "product_view": 5, "add_to_cart": 1 } }
    public Dictionary<string, Dictionary<string, int>> Signals { get; set; } = new();

    // Products viewed this session
    public List<int> ViewedProducts { get; set; } = new();

    // Session context (device, referrer domain, time-of-day)
    public SessionContext? Context { get; set; }

    // Aggregates
    public double TotalWeight { get; set; }
    public int SignalCount { get; set; }
    public int PageViews { get; set; }
    public int ProductViews { get; set; }
    public int CartAdds { get; set; }

    // Timestamps
    public DateTime StartedAt { get; set; } = DateTime.UtcNow;
    public DateTime LastActivityAt { get; set; } = DateTime.UtcNow;

    // Link to persistent profile (if fingerprint resolved)
    public Guid? PersistentProfileId { get; set; }
}

在 IMemoryCache 中存储\ ( @ or I 分布式 Cache @ MS K1 使用滑动过期

// Mostlylucid.SegmentCommerce/Services/Profiles/SessionCollector.cs
_cache.Set(sessionKey, sessionProfile, new MemoryCacheEntryOptions
{
    SlidingExpiration = TimeSpan.FromMinutes(30),
    Priority = CacheItemPriority.Normal, // LFU eviction under memory pressure
    
    // CRITICAL: Eviction callback decides whether to elevate to persistent profile
    PostEvictionCallbacks =
    {
        new PostEvictionCallbackRegistration
        {
            EvictionCallback = async (key, value, reason, state) =>
            {
                if (value is SessionProfile session && ShouldElevate(session))
                {
                    // Only NOW do we write to database (persistent profile)
                    await ElevateToProfileAsync(session);
                }
                // Otherwise: session is gone forever
            }
        }
    }
});

困难的制约

  • 零意外持久性@:会场现场直播 仅 在 %sK0 IMemory缓存或像 Rediis@)}这样的分布缓存中
  • LFU驱逐@ :Low-use 会话在记忆压力下首先驱赶
  • 流滑过期最后一次活动结束的分钟@: @ @ 30#
  • 驱逐回召@: 最后一次提升高端@-值信号的机会 在它们丢失前#'
  • 没有追回:App 重新启动 @=所有丢失的会话@( 除非使用分布式卡切},, 但仍然短暫{)}

有关驱逐的海拔决定

private bool ShouldElevate(SessionProfile session)
{
    // Elevate if:
    // - User added to cart (high intent)
    // - User completed purchase (conversion)
    // - Fingerprint was resolved (identity established)
    // - Session weight exceeds threshold (engaged visitor)
    
    return session.CartAdds > 0 
        || session.TotalWeight > 5.0 
        || session.PersistentProfileId.HasValue;
}

为何驱赶回召回?

这是 仅 在缓存驱赶会话时 :

  1. 我们知道整届会议的历史
  2. 我们可以评估总参与情况
  3. 我们避免存储低端的-value 会话@ (single 页面视图 @ MS K2uncount\ )
  4. 我们保证会考不会不小心持续

如果我们在驱逐期间不提升 , 永永远远. 这是设计图

会话Context:我们追踪到什么 ?(SafelyMS K2

public class SessionContext
{
    public string? DeviceType { get; set; }        // "mobile", "desktop"
    public string? EntryPath { get; set; }         // "/products/tech" (no query params)
    public string? ReferrerDomain { get; set; }    // "google.com" (domain only, not full URL)
    public string? TimeOfDay { get; set; }         // "morning", "afternoon"
    public string? DayType { get; set; }           // "weekday", "weekend"
}

注意什么 @' @%s 否 这里是 : IP 地址 @, @用户代理@,}完整 URLs,}跟踪像素@MS K4}我们捕捉到 上下线环境模式@, 无法识别的信息@.

什么是信号 ?

AA 信号讯号 是一个在用户动作中捕捉到的行为事实@. @ 而不是跟踪 @ "_ who,"}我们追踪了 @ MPK3}发生了什么\ ."}

这一概念来自 时空信号“- @sshort@-}在连接的窗口中存在的活性事实 ”“.在这个系统中,“,}操作发出信号如: "api.rate_limited" 或 "gateway.slow" 来协调行为,而没有紧密的联结...".

在此, 我们对用户行为应用相同的模式@ : @

  • 产品视图 → product_view @(_重量0.10)
  • 添加到手动 → add_to_cart @(_重量0.35)
  • 购买 → purchase @(_重量1.00)

信号是 短时间 与会议结束 零-PII -=YTET -伊甸园字幕组=- 翻译: 加权 (intent-AWORE}).

到链接会话时不跟踪 cookie , 我们使用 客户端%s-}侧指纹. 浏览器根据以下信号计算一个散列: @ (timezone,屏幕解析@ MS K3WebGL freater},布指纹@)并发送 只有杂草 至 /api/fingerprint.

然后服务器 散列的 hMAC 使用秘密密钥@, @ make it site\ -_ 范围化且无法在别处使用 @.}

这个代码是从我的 bott 检测项目@, 用于识别刮痕的 '@. 相同技术}#, 不同用途 *.

// Mostlylucid.SegmentCommerce/ClientFingerprint/fingerprint.js
// Collect signals (browser capabilities, not PII)
var signals = [
    Intl.DateTimeFormat().resolvedOptions().timeZone,
    navigator.language,
    screen.width + 'x' + screen.height,
    // ... (see full code)
];

// Hash locally
var hash = hash(signals.join('|'));

// Send only the hash via sendBeacon
navigator.sendBeacon('/api/fingerprint', JSON.stringify({ h: hash }));

伺服器@-side @:

// Server HMACs the client hash with a secret key
var profileKey = HMACSHA256(clientHash + secretKey);

现在我们有一个稳定的,}站点 @ -}没有 cookie 或本地端的标识符_ .}请看 完整指纹@. @js 源代码 由 mostlucid.bot detraction}).改编

信号类型和重量

不同的行动具有不同的意图级别 @.我们用 基准加权数.

信号- type @ : @ 重量分层

// Mostlylucid.SegmentCommerce/Data/Entities/Profiles/SignalEntity.cs
public static class SignalTypes
{
    // Passive signals (low intent)
    public const string PageView = "page_view";              // 0.01
    public const string CategoryBrowse = "category_browse";  // 0.03
    public const string ProductImpression = "product_impression"; // 0.02

    // Active signals (medium intent)
    public const string ProductView = "product_view";        // 0.10
    public const string ProductClick = "product_click";      // 0.08
    public const string Search = "search";                   // 0.05

    // High-intent signals
    public const string AddToCart = "add_to_cart";           // 0.35
    public const string AddToWishlist = "add_to_wishlist";   // 0.25
    public const string ViewCart = "view_cart";              // 0.15
    public const string BeginCheckout = "begin_checkout";    // 0.40

    // Conversion signals (highest intent)
    public const string Purchase = "purchase";               // 1.00
    public const string Review = "review";                   // 0.60
    public const string Share = "share";                     // 0.50

    public static readonly Dictionary<string, double> BaseWeights = new()
    {
        { PageView, 0.01 },
        { ProductView, 0.10 },
        { AddToCart, 0.35 },
        { Purchase, 1.00 },
        // ... (see full code for complete list)
    };

    public static double GetBaseWeight(string signalType)
    {
        return BaseWeights.GetValueOrDefault(signalType, 0.05);
    }
}

为什么这重要?

  • 单页视图 @ ( @% 10.01) won't 支配信号
  • 添加到马车@ ( @%0.35)是一个强烈的意向信号
  • 购买“(”1.00)是最强烈的信号

此等级防止“ "” 驱动@ MS K1 @ by browsing @ " 污染 profileQMK3}

会话召集人 @ : @ 录音信号 @ I( @ cache_ MS K2 @ 只有@ MSC3 @

// Mostlylucid.SegmentCommerce/Services/Profiles/SessionCollector.cs
public async Task<SessionProfile> RecordSignalAsync(
    SessionSignalInput input, CancellationToken ct = default)
{
    var sessionKey = input.SessionKey;
    
    // Get or create session FROM CACHE (never DB)
    var session = _cache.Get<SessionProfile>(sessionKey);
    
    if (session == null)
    {
        session = new SessionProfile
        {
            SessionKey = sessionKey,
            StartedAt = DateTime.UtcNow
        };
    }

    session.LastActivityAt = DateTime.UtcNow;

    var weight = input.Weight ?? SignalTypes.GetBaseWeight(input.SignalType);

    // Update in-memory aggregates
    session.TotalWeight += weight;
    session.SignalCount++;

    if (!string.IsNullOrEmpty(input.Category))
    {
        session.Interests.TryGetValue(input.Category, out var currentScore);
        session.Interests[input.Category] = currentScore + weight;
    }

    if (input.SignalType == SignalTypes.AddToCart)
    {
        session.CartAdds++;
    }

    // Put back in cache with sliding expiration
    _cache.Set(sessionKey, session, new MemoryCacheEntryOptions
    {
        SlidingExpiration = TimeSpan.FromMinutes(30),
        Priority = CacheItemPriority.Normal,
        PostEvictionCallbacks = { /* elevation callback */ }
    });

    return session;
}

快速是因为:

  • @ - @ mememory @ I(no DB 写着 @ MPK2 @
  • 没有序列化间接费用 {(} IMEMORYCACHE_ )}
  • 没有网络呼叫@ ( @ 本地缓存 @ MS K1

高频信号

当会话显示高意图时, 我们将信号提升到一个 持久性简介.

长-期限配置

// Mostlylucid.SegmentCommerce/Data/Entities/Profiles/PersistentProfileEntity.cs
[Table("persistent_profiles")]
public class PersistentProfileEntity
{
    [Key]
    public Guid Id { get; set; } = Guid.NewGuid();

    [Required]
    [MaxLength(256)]
    public string ProfileKey { get; set; } = string.Empty;

    // How this profile is identified (Fingerprint, Cookie, Identity)
    public ProfileIdentificationMode IdentificationMode { get; set; }

    // Behavioral data (all JSONB)
    [Column("interests", TypeName = "jsonb")]
    public Dictionary<string, double> Interests { get; set; } = new();

    [Column("affinities", TypeName = "jsonb")]
    public Dictionary<string, double> Affinities { get; set; } = new();

    [Column("brand_affinities", TypeName = "jsonb")]
    public Dictionary<string, double> BrandAffinities { get; set; } = new();

    [Column("price_preferences", TypeName = "jsonb")]
    public PricePreferences? PricePreferences { get; set; }

    [Column("traits", TypeName = "jsonb")]
    public Dictionary<string, bool> Traits { get; set; } = new();

    // Computed segments
    public ProfileSegments Segments { get; set; } = ProfileSegments.None;

    [Column("llm_segments", TypeName = "jsonb")]
    public Dictionary<string, double>? LlmSegments { get; set; }

    // Vector embedding for similarity matching
    [Column("embedding", TypeName = "vector(384)")]
    public Vector? Embedding { get; set; }

    // Statistics
    public int TotalSessions { get; set; }
    public int TotalSignals { get; set; }
    public int TotalPurchases { get; set; }
    public int TotalCartAdds { get; set; }

    public DateTime CreatedAt { get; set; } = DateTime.UtcNow;
    public DateTime LastSeenAt { get; set; } = DateTime.UtcNow;
    public DateTime UpdatedAt { get; set; } = DateTime.UtcNow;
}

仍然为零PII:

  • ProfileKey 是 HMAC hash @ (}不可逆=)
  • IdentificationMode 告诉我们它是如何被识别的 @(fingerprint@/}cookie/)}*
  • 所有数据均为 行为信号@,_# 不是个人信息

升空=: 会话 @→}配置文件

public async Task ElevateToProfileAsync(
    SessionProfileEntity session, 
    PersistentProfileEntity profile, 
    CancellationToken ct = default)
{
    if (session.IsElevated)
        return;

    // Merge interests (use higher value)
    foreach (var (category, score) in session.Interests)
    {
        if (!profile.Interests.ContainsKey(category) || 
            profile.Interests[category] < score)
        {
            profile.Interests[category] = score;
        }
    }

    // Update stats
    profile.TotalSessions++;
    profile.TotalSignals += session.SignalCount;
    profile.TotalCartAdds += session.CartAdds;
    profile.LastSeenAt = DateTime.UtcNow;
    profile.UpdatedAt = DateTime.UtcNow;

    // Mark session as elevated
    session.IsElevated = true;
    session.PersistentProfileId = profile.Id;

    // Clear segment cache (will be recomputed)
    profile.SegmentsComputedAt = null;
    profile.EmbeddingComputedAt = null;

    await _db.SaveChangesAsync(ct);
}

当升起时 @ : @ @

  • 在马车后添加“(”和“高度意图”之后
  • 购买后 @ (_ 转换@ MS K1}
  • 当用户选择持久性时 @ ( fingerprint@ MS K1} cookie /\ login )\ {

线段定义@:

现在让我们让' 提供\ . 段是 可操作输出 所有的信号收集@- @他们回答 @"}这是什么样的?

为何会籍模糊?

传统区隔是二进制@: @ you @ '}您要么在段内,要么你{'_re not.}这样会产生问题=:}

flowchart LR
    subgraph "Binary Segmentation"
        Profile1[3 purchases] -->|"Threshold: 5"| Out[NOT High-Value]
        Profile2[5 purchases] -->|"Threshold: 5"| In[High-Value]
    end

购买“4”的客户与购买“0.”的顾客待遇相同。

模糊分离 给出每个配置文件的评分@ (0-1): @% 1

“ ”购买“ ”二进制“MS K2”模糊分数“MSC3”
@ @ 0 @ @ MPK2 @ 没有 @ *0.0# # #
@ @ 2 @ @ MPK2 @ 没有 @ *0.4# # #
@ @ 4 @ @ MPK2 @ 没有 @ *0.8# # #
MS K1 是 NSK4 USK5

现在您可以按个性区分 : @ @ MS K1_ 接近高端@ -# value @ I"}客户得到的治疗与 < "\ nowhere close> @."}稍有不同

区段定义结构

// Mostlylucid.SegmentCommerce/Services/Segments/SegmentDefinition.cs
public class SegmentDefinition
{
    public string Id { get; set; }           // "tech-enthusiast"
    public string Name { get; set; }          // "Tech Enthusiasts"
    public string Description { get; set; }   // "Users with strong interest in technology"
    public string Icon { get; set; }          // "🔧"
    public string Color { get; set; }         // "#3b82f6"

    public List<SegmentRule> Rules { get; set; } = [];

    // How rules combine: All (AND), Any (OR), Weighted (sum)
    public RuleCombination Combination { get; set; } = RuleCombination.Weighted;

    // Minimum score to be "a member" (0-1)
    public double MembershipThreshold { get; set; } = 0.3;

    public List<string> Tags { get; set; } = [];  // For filtering/grouping
}

规则类型 @:}您可以检查什么

每条规则都评价剖面图的一个维度 @ : @ @

public enum RuleType
{
    CategoryInterest,  // Check interests.tech, interests.fashion, etc.
    BrandAffinity,     // Check brandAffinities.Sony, brandAffinities.Nike, etc.
    PriceRange,        // Check price preferences (budget vs luxury)
    Trait,             // Check boolean traits (prefersDeals, browsesExtensively)
    Statistic,         // Check totalPurchases, totalSessions, totalCartAdds
    TagAffinity,       // Check affinities.gadgets, affinities.organic, etc.
    Recency,           // Check days since last activity
    Expression         // Custom expressions (advanced)
}

规则操作员

public enum RuleOperator
{
    GreaterThan,       // value > threshold
    GreaterOrEqual,    // value >= threshold
    LessThan,          // value < threshold
    LessOrEqual,       // value <= threshold
    Equal,             // value == threshold
    NotEqual,          // value != threshold
    Contains,          // for array/string checks
    Between,           // for ranges
    In, NotIn          // for set membership
}

规则合并方法

多重规则如何结合到最后分数@: @

public enum RuleCombination
{
    All,      // AND logic: Score = min(all rule scores). All rules must pass.
    Any,      // OR logic: Score = max(all rule scores). Any rule can pass.
    Weighted  // Weighted sum: Score = Σ(rule.weight × rule.score) / Σ(rule.weight)
}

加权 @60%,_SERNationality matters@30%,BARBAR 品牌亲子关系$Z10%."#

真实区段示例

下面是样本工程中的默认区块@: @%

1. 技术内涵

new SegmentDefinition
{
    Id = "tech-enthusiast",
    Name = "Tech Enthusiasts",
    Description = "Users with strong interest in technology products",
    Icon = "🔧",
    Color = "#3b82f6",
    MembershipThreshold = 0.35,
    Rules =
    [
        new() { 
            Type = RuleType.CategoryInterest, 
            Field = "interests.tech", 
            Operator = RuleOperator.GreaterOrEqual, 
            Value = 0.4, 
            Weight = 0.6,  // 60% of score
            Description = "Tech interest > 40%" 
        },
        new() { 
            Type = RuleType.TagAffinity, 
            Field = "affinities.gadgets", 
            Operator = RuleOperator.GreaterOrEqual, 
            Value = 0.2, 
            Weight = 0.2,  // 20% of score
            Description = "Likes gadgets" 
        },
        new() { 
            Type = RuleType.TagAffinity, 
            Field = "affinities.electronics", 
            Operator = RuleOperator.GreaterOrEqual, 
            Value = 0.2, 
            Weight = 0.2,  // 20% of score
            Description = "Likes electronics" 
        }
    ]
}

评价实例:*

  • 配置文件有 interests.tech = 0.72, affinities.gadgets = 0.31, affinities.electronics = 0.15
  • {\fn黑体\fs22\bord1\shad0\3aHBE\4aH00\fscx67\fscy66\2cHFFFFFF\3cH808080}→ 得分...
  • {\fn黑体\fs22\bord1\shad0\3aHBE\4aH00\fscx67\fscy66\2cHFFFFFF\3cH808080}→ 得分...
  • {\fn黑体\fs22\bord1\shad0\3aHBE\4aH00\fscx67\fscy66\2cHFFFFFF\3cH808080}→分 {\fnSTKaiti\bord1\1cHF0F0F0}=0.75分{\pos(192,285)\b0} @0.2分 {\r\fnMicrosoft JhengHei\fs26\bord0\shad2\fsp1\2cH0000FF\3cHFF0000\4cHFF0000}
  • 最终的: @(1.0×0.6+1.0×0.2} @MS K4 #0.75×0.2)#@/(1.0#) @=# 0.95
  • *0.95 >= 0.35 阈值 <→ 拥有#95%信任的成员

“2.弃弹者”

new SegmentDefinition
{
    Id = "cart-abandoner",
    Name = "Cart Abandoners",
    Description = "Users who add items to cart but don't complete purchase",
    Icon = "🛒",
    Color = "#ef4444",
    MembershipThreshold = 0.4,
    Rules =
    [
        new() { 
            Type = RuleType.Statistic, 
            Field = "totalCartAdds", 
            Operator = RuleOperator.GreaterOrEqual, 
            Value = 3, 
            Weight = 0.5, 
            Description = "3+ cart adds" 
        },
        new() { 
            Type = RuleType.Statistic, 
            Field = "totalPurchases", 
            Operator = RuleOperator.LessThan, 
            Value = 2, 
            Weight = 0.5, 
            Description = "Few purchases" 
        }
    ]
}

这抓住了“"”加到马车上, 但是“MS K1” 买下“"” 模式 “-” 在恢复运动中非常成功 @.

“3. 高端”“- 贵重客户”

new SegmentDefinition
{
    Id = "high-value",
    Name = "High-Value Customers",
    Description = "Customers who make frequent purchases and spend above average",
    Icon = "💎",
    Color = "#8b5cf6",
    MembershipThreshold = 0.4,
    Rules =
    [
        new() { 
            Type = RuleType.Statistic, 
            Field = "totalPurchases", 
            Operator = RuleOperator.GreaterOrEqual, 
            Value = 3, 
            Weight = 0.4, 
            Description = "3+ purchases" 
        },
        new() { 
            Type = RuleType.PriceRange, 
            Field = "priceRange", 
            Value = "100-10000",  // High-end shoppers
            Weight = 0.3, 
            Description = "High price range" 
        },
        new() { 
            Type = RuleType.Recency, 
            Field = "lastSeen", 
            Operator = RuleOperator.LessThan, 
            Value = 30, 
            Weight = 0.3, 
            Description = "Active in last 30 days" 
        }
    ]
}

谈判猎人

new SegmentDefinition
{
    Id = "bargain-hunter",
    Name = "Bargain Hunters",
    Description = "Price-sensitive shoppers who love deals and discounts",
    Icon = "🏷️",
    Color = "#22c55e",
    MembershipThreshold = 0.3,
    Rules =
    [
        new() { 
            Type = RuleType.PriceRange, 
            Field = "priceRange", 
            Value = "0-75",  // Budget shoppers
            Weight = 0.5, 
            Description = "Low price preference" 
        },
        new() { 
            Type = RuleType.Trait, 
            Field = "traits.prefersDeals", 
            Value = true, 
            Weight = 0.3, 
            Description = "Prefers deals" 
        },
        new() { 
            Type = RuleType.Statistic, 
            Field = "totalCartAdds", 
            Operator = RuleOperator.GreaterThan, 
            Value = 5, 
            Weight = 0.2, 
            Description = "Shops around" 
        }
    ]
}

所有默认线段

样本包括覆盖共同e-的“10”部分、商业模式和“MS K2”部分。

使用 case @
@3+ 购买@,高支出}#,最近的" 贵宾治疗" ,忠心计划
科技利益 , Q 技术产品建议
@ # 时尚前进 # 👗现代兴趣# }多次访问$,#
“ 交易猎人”“ 🏷 I 低价范围”“,更喜欢交易” “MS K5销售通知”“ @
@ 新访客# 👋 I ≤2 会话 _,}没有在入机中购买@ 未在机上采购 #,}第一次-}买家出价*
@ _Cart 弃尸者 @MS K1}🛒}# }#3+#马车加增了#,#几家购入的< >恢复邮件@ #
@,}最近的活动: =家庭产品交叉@-_sell @MS K7
@ @Fitness products focus Focused 聚焦# @
5+ 购买,+#10+ 会话 @ _保留{,}奖赏@
@,BARBAR Q#比较工具@,}详细信息 {

ections service : 电子化成员

缩略 SegmentService 对照所有分区规则评价剖析档@: @%

// Mostlylucid.SegmentCommerce/Services/Segments/SegmentService.cs
public SegmentMembership EvaluateSegment(ProfileData profile, SegmentDefinition segment)
{
    var ruleScores = new List<RuleScore>();
    
    foreach (var rule in segment.Rules)
    {
        var (score, actualValue) = EvaluateRule(profile, rule);
        ruleScores.Add(new RuleScore
        {
            RuleDescription = rule.Description,
            Score = score,
            Weight = rule.Weight,
            ActualValue = actualValue  // For transparency
        });
    }

    // Combine based on segment's combination method
    double finalScore = segment.Combination switch
    {
        RuleCombination.All => ruleScores.Min(r => r.Score),
        RuleCombination.Any => ruleScores.Max(r => r.Score),
        RuleCombination.Weighted => ComputeWeightedScore(ruleScores),
        _ => 0
    };

    return new SegmentMembership
    {
        SegmentId = segment.Id,
        SegmentName = segment.Name,
        Score = Math.Round(finalScore, 3),
        IsMember = finalScore >= segment.MembershipThreshold,
        RuleScores = ruleScores,
        Confidence = score switch  // Human-readable
        {
            >= 0.8 => "Very High",
            >= 0.6 => "High",
            >= 0.4 => "Medium",
            >= 0.2 => "Low",
            _ => "Very Low"
        }
    };
}

解释性

每一成员结果包括: 实际实际价值 并导致得分=:}

// What the UI receives:
{
    "segmentId": "tech-enthusiast",
    "segmentName": "Tech Enthusiasts",
    "score": 0.95,
    "isMember": true,
    "confidence": "Very High",
    "ruleScores": [
        { "description": "Tech interest > 40%", "score": 1.0, "actualValue": "0.72" },
        { "description": "Likes gadgets", "score": 1.0, "actualValue": "0.31" },
        { "description": "Likes electronics", "score": 0.75, "actualValue": "0.15" }
    ]
}

用户可以 确切地知道为什么 这对透明度和GDPR的合规性至关重要

信号流@: @ 结束 @ - @to% MS K2_ End

这里#' @@% 如何将产品观点变成部分会籍: @%

sequenceDiagram
    participant Browser
    participant Cache as SessionCache
    participant Outbox as Outbox
    participant Segment as SegmentService

    Browser->>Cache: Product view (category: "tech")
    Cache->>Cache: Update in-memory session
    Note over Cache: interests.tech += 0.10
    
    Browser->>Cache: Add to cart (high intent)
    Cache->>Outbox: Publish elevation event
    Outbox->>Outbox: Write to outbox table
    
    Note over Outbox: Background worker processes
    Outbox->>Segment: Elevate to PersistentProfile
    
    Segment->>Segment: Evaluate segment rules
    Note over Segment: interests.tech: 0.72 >= 0.40 ✓<br/>Score: 0.95, IsMember: true
    Segment-->>Browser: Segment memberships + explanations

发件箱模式 @ (# Preview)@

所有重大行动 发箱模式我们的主要管弦机制 :

flowchart LR
    Action[Cart Add] --> TX[Single Transaction]
    TX --> DB[(Save + Outbox)]
    DB --> Worker[Background Worker]
    Worker --> Route[Route to Handlers]
    
    style TX stroke:#2f9e44,stroke-width:3px

为什么? 在一个交易中写入商业数据和事件@. 事件可以 '}%t 丢失 @. 失败会自动用指数反转_.重试

// Every action publishes to outbox in the same transaction
await using var transaction = await _db.Database.BeginTransactionAsync(ct);

cart.Items.Add(new CartItem { ProductId = productId });
await _db.SaveChangesAsync(ct);

_outbox.Publish(OutboxEventTypes.ProductAddedToCart, new { ProductId = productId });
await _db.SaveChangesAsync(ct);

await transaction.CommitAsync(ct);
// Event is now guaranteed to be processed

部分#3 包含完整发件箱执行@:}工作队列 @, listen/_NOTIFY 以用于即时抓取@MS K3}重试逻辑=,}和缩放模式.}

下一步是什么?

本部分涵盖@: @%

  • Zero-PII 配置文件结构 (- 时间会话与持久性剖面
  • 区段定义 成员模糊和加权规则
  • 分部分服务 内建@ - @ 无法解释

部分#3 进入更深的“:”

  • 实施发箱模式 -可靠的事件发布和单一路由器
  • 任务队列 具有 PostgreSQL SKIP LOCKED 供分配处理
  • 听不见 @(no volution@)
  • 统一信息和透明度 -+#"_您的利益 @"}仪表板和段探索器

线段是此系统可操作的输出@.}他们回答 @"}这是什么样的直升机?

logo

© 2026 Scott Galloway — Unlicense — All content and source code on this site is free to use, copy, modify, and sell.