<?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: Techmerch</title>
    <description>The latest articles on DEV Community by Techmerch (@omegatech).</description>
    <link>https://dev.to/omegatech</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%2F4046665%2Ff38d7171-0909-4680-b2c7-1f7354f8e2d9.png</url>
      <title>DEV Community: Techmerch</title>
      <link>https://dev.to/omegatech</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/omegatech"/>
    <language>en</language>
    <item>
      <title>Simulating 50,000 e-commerce sessions with multi-agent RL: what worked, what didn't, and why I shipped the failure</title>
      <dc:creator>Techmerch</dc:creator>
      <pubDate>Sat, 25 Jul 2026 10:02:42 +0000</pubDate>
      <link>https://dev.to/omegatech/simulating-50000-e-commerce-sessions-with-multi-agent-rl-what-worked-what-didnt-and-why-i-2438</link>
      <guid>https://dev.to/omegatech/simulating-50000-e-commerce-sessions-with-multi-agent-rl-what-worked-what-didnt-and-why-i-2438</guid>
      <description>&lt;p&gt;I wanted to see if a &lt;em&gt;learned&lt;/em&gt; policy — not scripted behavior — could produce realistic e-commerce shopper sessions: browsing, getting tempted, abandoning carts, and influencing other shoppers through a social graph. This is FunnelForge: 50 RL agents trained on real review data, simulating a shopping population end to end.&lt;/p&gt;

&lt;h2&gt;
  
  
  The problem
&lt;/h2&gt;

&lt;p&gt;Most shopper simulations are scripted or rule-based. I wanted agents that &lt;em&gt;learned&lt;/em&gt; to behave like real shoppers, including the realistic parts — like abandoning carts most of the time, not just converting on demand.&lt;/p&gt;

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

&lt;p&gt;Two planes, deliberately separated:&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Data plane&lt;/strong&gt; (produces behavior):&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;A &lt;strong&gt;min-heap event scheduler&lt;/strong&gt; instead of fixed timesteps — agents act on their own continuous-time schedule rather than lockstep ticks. More realistic, more complex than a tick loop.&lt;/li&gt;
&lt;li&gt;An &lt;strong&gt;Erdős–Rényi social graph&lt;/strong&gt; with BFS influence propagation (1/2^depth decay) — one agent's purchase can nudge its neighbors, decaying with graph distance.&lt;/li&gt;
&lt;li&gt;A &lt;strong&gt;topological funnel gate&lt;/strong&gt; — agents move through browse → product_detail → cart → checkout, gated so they can't skip steps.&lt;/li&gt;
&lt;li&gt;All three compiled to &lt;strong&gt;C++ via pybind11&lt;/strong&gt;, used by default, with an automatic pure-Python fallback if the compiled modules aren't present.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;Control plane&lt;/strong&gt; (moves + observes):&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Every action publishes to Redis and Kafka, streamed over a FastAPI WebSocket to a live dashboard.&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Training
&lt;/h2&gt;

&lt;p&gt;Behavioral cloning on real e-commerce review data first, then PPO fine-tuning on top. Pure RL from scratch was impractical here — sparse reward over a large discrete action space (which product, out of thousands) doesn't converge well without a good prior. BC gives the policy a realistic starting point; PPO refines it.&lt;/p&gt;

&lt;h2&gt;
  
  
  Validation — and the honest part
&lt;/h2&gt;

&lt;p&gt;The simulation is stochastic over ~50 agents, so I validate against a &lt;strong&gt;seeded, reproducible&lt;/strong&gt; batch run (&lt;code&gt;FF_SEED=42&lt;/code&gt;), not the live dashboard feed:&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Metric&lt;/th&gt;
&lt;th&gt;Simulated&lt;/th&gt;
&lt;th&gt;Reference&lt;/th&gt;
&lt;th&gt;Verdict&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;Session length (items)&lt;/td&gt;
&lt;td&gt;6.8 ± 6.1&lt;/td&gt;
&lt;td&gt;real 8.16 ± 7.08&lt;/td&gt;
&lt;td&gt;✅ PASS&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Conversion rate&lt;/td&gt;
&lt;td&gt;10%&lt;/td&gt;
&lt;td&gt;2–20% (industry)&lt;/td&gt;
&lt;td&gt;✅ PASS&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Cart abandonment&lt;/td&gt;
&lt;td&gt;90%&lt;/td&gt;
&lt;td&gt;60–95% (industry)&lt;/td&gt;
&lt;td&gt;✅ PASS&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Social influence (top-5)&lt;/td&gt;
&lt;td&gt;0.039&lt;/td&gt;
&lt;td&gt;0.20–0.40&lt;/td&gt;
&lt;td&gt;❌ FAIL&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;Three of four metrics land inside real industry ranges. The fourth — social-influence concentration — doesn't, and I want to talk about that one specifically, because I think how you handle a failing metric matters more than getting everything to pass.&lt;/p&gt;

&lt;p&gt;I traced the cause: a fallback path in the product catalog was thinning the distribution the metric depends on, weakening the measured contagion effect. Instead of tuning parameters until the number looked right, I documented the diagnosis and left the actual fix as an open problem — with one constraint: fixing it can't regress the other three passing metrics. That turned out to be a much more interesting problem than the number itself; the easy "fix" (crank up the social-boost parameter) breaks conversion and abandonment in the process.&lt;/p&gt;

&lt;h2&gt;
  
  
  What I'd check earlier next time
&lt;/h2&gt;

&lt;p&gt;Two things surfaced late that I'd catch sooner now:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;strong&gt;A compiled component silently unused.&lt;/strong&gt; The C++ pybind11 modules existed and compiled fine — but the live simulation was importing the Python implementation instead, the whole time. Nothing crashed, nothing warned. Only caught it while packaging, by actually testing the runtime path end to end instead of trusting that "it builds" meant "it runs correctly."&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Two code paths, two different numbers.&lt;/strong&gt; I had a live-dashboard path and a separate batch-validation path, and they'd drifted apart — different numbers, and the validator was quietly broken against an API change elsewhere in the codebase. Took real effort to find one canonical, reproducible number and make sure every doc cited the same one.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;Both are the same underlying lesson: verify the actual runtime behavior, not just that the code compiles or the demo looks right.&lt;/p&gt;

&lt;h2&gt;
  
  
  Stack
&lt;/h2&gt;

&lt;p&gt;Python, PyTorch (BC + PPO), C++17/pybind11, Redis, Kafka, FastAPI, React/D3.&lt;/p&gt;

&lt;h2&gt;
  
  
  Try it
&lt;/h2&gt;

&lt;p&gt;Code + full README: &lt;strong&gt;&lt;a href="https://github.com/ojas4414/FunnelForge" rel="noopener noreferrer"&gt;github.com/ojas4414/FunnelForge&lt;/a&gt;&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Free to run and study — source-available under PolyForm Perimeter. &lt;code&gt;docker compose up&lt;/code&gt; gets you the whole stack, live dashboard included. There's also a paid documentation bundle (architecture deep-dive, guided walkthrough, graded exercises, interview prep) for anyone who wants a more structured path through it — linked in the README.&lt;/p&gt;

&lt;p&gt;Curious what people think of the min-heap-over-fixed-timesteps call, or whether Erdős–Rényi was the wrong social graph model to start with. Happy to be told either was a bad call.&lt;/p&gt;

</description>
      <category>showdev</category>
      <category>python</category>
      <category>ai</category>
      <category>machinelearning</category>
    </item>
  </channel>
</rss>
