零 PII 客户情报@-部分 @2: 剖析,信号 @MS K3片段
内 部分#1 我们报导了哲学和系列概览 . 部分#1.1 我们建造了样本数据生成器... ....
现在让我们来建立核心系统... ....
- Zero-PII 配置文件结构 - 短会与持久性剖面图
- 信号和重量 - 我们追踪的是什么 它如何积累
- 区段定义 主要事件=)
- 发件箱模式预览 @-#可靠事件发布@(_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
- 会话收集器 @-捕捉行为信号@(views @,点击 @MS K3马车加上#) @_- 只有 - @ memory
- 持久性简介 计算利息=- 数据库\ - 已支持
- 分部分服务 “-”评估规则的概况“,”分配成员。
关键区别:+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;
}
为何驱赶回召回?
这是 仅 在缓存驱赶会话时 :
- 我们知道整届会议的历史
- 我们可以评估总参与情况
- 我们避免存储低端的-value 会话@ (single 页面视图 @ MS K2uncount\ )
- 我们保证会考不会不小心持续
如果我们在驱逐期间不提升 , 永永远远. 这是设计图
会话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}).
客户端指纹@( @ñero @-_Cookie 身份)
到链接会话时不跟踪 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);
}
}
为什么这重要?
- 单页视图 @ ( @% 1
0.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@)
- 统一信息和透明度 -+#"_您的利益 @"}仪表板和段探索器
线段是此系统可操作的输出@.}他们回答 @"}这是什么样的直升机?