<?xml version="1.0" encoding="UTF-8"?>


<rss version="2.0" xmlns:content="http://purl.org/rss/1.0/modules/content/"
     xmlns:dc="http://purl.org/dc/elements/1.1/"
     xmlns:atom="http://www.w3.org/2005/Atom"
	xmlns:c="http://java.sun.com/jsp/jstl/core"
     xmlns:sy="http://purl.org/rss/1.0/modules/syndication/"
     xmlns:media="http://search.yahoo.com/mrss/">




         

    <channel>
         <title> Dropbox Tech Blog </title>
        <atom:link  href="https://dropbox.tech/feed" rel="self" type="application/rss+xml" />
        <link>https://dropbox.tech</link>
        <description>null</description>
        <lastBuildDate>Thu, 02 Apr 2026 10:00:00 -0700</lastBuildDate>
        <language>en</language>
        <sy:updatePeriod>hourly</sy:updatePeriod>
        <sy:updateFrequency>1</sy:updateFrequency>

        <image>
            <url>https://dropbox.tech/cms/content/dropbox/tech-blog/en-us.thumb.319.319.png?ck=1774459002</url>
            <title>Dropbox Tech Blog</title>
            <link>https://dropbox.tech/cms/content/dropbox/tech-blog/en-us.thumb.319.319.png?ck=1774459002</link>
        </image>

            
        				<item>
                        <title>Improving storage efficiency in Magic Pocket, our immutable blob store</title>
                        
            			<link>https://dropbox.tech/infrastructure/improving-storage-efficiency-in-magic-pocket-our-immutable-blob-store</link>

                            
            			<dc:creator>
                            Facundo Agriel
            			</dc:creator>
            			
            				<category>Magic Pocket</category>
							
            				<category>storage</category>
							
            				<category>Infrastructure</category>
							
                            
            			<description><![CDATA[By turning compaction into a layered, adaptive pipeline and strengthening our monitoring and controls, we made Magic Pocket more resilient to workload changes.]]></description>
            			<guid>https://dropbox.tech/infrastructure/improving-storage-efficiency-in-magic-pocket-our-immutable-blob-store</guid>
                        <pubDate>Thu, 02 Apr 2026 10:00:00 -0700</pubDate>
                            
                        <content:encoded><![CDATA[


<div class="aem-Grid aem-Grid--12 aem-Grid--default--12 ">
    
    <div class="text parbase aem-GridColumn aem-GridColumn--default--12">
<p><a href="https://dropbox.tech/infrastructure/how-we-optimized-magic-pocket-for-cold-storage" target="_blank"><b>Magic Pocket</b></a> is the core Dropbox storage system—a custom-built, exabyte-scale blob storage system designed for durability, availability, scale, and efficiency. It holds user content, which means it must be safe, fast, and cost-effective to scale with the company. For Dropbox, storage efficiency really matters. We measure it by looking at how much total disk space we use compared to how much user data we’re actually storing.</p>
<p>Last year, we rolled out a new service that changed how <a href="https://dropbox.tech/infrastructure/increasing-magic-pocket-write-throughput-by-removing-our-ssd-cache-disks" target="_blank">data is placed across Magic Pocket</a>. The change reduced write amplification for background writes, so each write triggered fewer backend storage operations. But it also had an unintended side effect: fragmentation increased, pushing storage overhead higher. Most of that growth came from a small number of severely under-filled volumes that consumed a disproportionate share of raw capacity, and our existing compaction strategy couldn’t reclaim the space quickly enough. At exabyte scale, even modest increases in overhead translate into meaningful infrastructure and capacity costs, so bringing that number back down quickly became a priority.</p>
<p>In this post, we’ll walk through why overhead is particularly hard to control in an immutable blob store, how compaction works in Magic Pocket, and the multi-strategy approach we rolled out to drive overhead back down, even below our previous baseline.</p>

</div>
<div class="experiencefragment aem-GridColumn aem-GridColumn--default--12">
<div id="experiencefragment-05f831a228" class="cmp-experiencefragment cmp-experiencefragment--dash-cta-for-tech-blog">

    



<div class="xf-content-height">
    


<div class="aem-Grid aem-Grid--12 aem-Grid--default--12 ">
    
    <div class="responsivegrid aem-GridColumn aem-GridColumn--default--12">


<div class="aem-Grid aem-Grid--12 aem-Grid--default--12 ">
    
    <div class="a08-html-embed c17-plain-html aem-GridColumn aem-GridColumn--default--12">

<style type="text/css"> 

@font-face {
font-family: 'AtlasGrotesk';
src: url('https://cdn.prod.website-files.com/65dcd70b48edc3a7b446950e/65dce019c63112e617513c94_AtlasGrotesk-Medium-Web-vfl38XiTL.woff2') format('woff2');
font-weight: 500;
font-style: normal;
font-display: swap;
}

@font-face {
font-family: 'AtlasGrotesk';
src: url('https://cdn.prod.website-files.com/65dcd70b48edc3a7b446950e/65dce019711b648fd1ccd24a_AtlasGrotesk-Regular-Web-vflk7bxjs.woff2') format('woff2');
font-weight: 400;
font-style: normal;
font-display: swap;
}
.xf-content-height {margin: 0;}
#cta { font-family: AtlasGrotesk,sans-serif; font-size: .900rem; text-decoration: none; background: #f7f5f2; line-height: 1.69; box-sizing: border-box;}
#cta-box { padding: 15px 20px 15px 20px; }
#cta-hed {font-weight: 500;}
#cta-indent {border-left: 5px solid #1e1919; padding-left:20px;}
#cta a:link, #cta a:visited  {text-decoration: none;}
#cta p { margin: 5px 0px 0px 0px; }

.dr-theme-dark #cta {background: #000;}
.dr-theme-dark #cta-box {border: 1px solid; border-bottom: 0;}
.dr-theme-dark #cta-indent {border-left: 5px solid #f7f5f2;}
.dr-theme-dark .button {background: #000;}

.button {
    background-color: #1e1919;
    color:  #f7f5f2;
    height: 2.5rem;
    padding: 10px 5px 10px 20px;
    font-size: 1rem;
    font-weight: 500;
    line-height: 1.2;
    transition: all .3s;
}

.button:hover { background-color: #0061ff; }

img {vertical-align: middle; padding: 0px 1px 2px 0px;}

.c17-plain-html {margin-bottom: 50px}

</style>

<div id="cta">
<div id="cta-box">
<div id="cta-indent">

<p id="cta-hed"><img src="https://cdn.prod.website-files.com/65dcd70b48edc3a7b446950e/670692ee7692f74d4834e4f4_Frame%201400006055.svg" loading="lazy"> Dropbox Dash: AI that understands your work
</p>

<p>Dash knows your context, your team, and your work, so your team can stay organized, easily find and share knowledge, and keep projects secure, all from one place. And soon, Dash is coming to Dropbox.</p>

</div>
</div>

<a href="https://dash.dropbox.com/?utm=blogs" target="_blank"><div class="button">Learn more →</div></a>

</div>
</div>

    
</div>
</div>

    
</div>

</div></div>

    
</div>
<div class="section aem-GridColumn aem-GridColumn--default--12">
<div class="dr-article-content__section" id="the-cost-of-immutability">
    <h2 class="dr-article-content__section-title">The cost of immutability</h2>
</div>
</div>
<div class="text parbase aem-GridColumn aem-GridColumn--default--12">
<p>When users upload files to Dropbox, Magic Pocket breaks those files into smaller pieces called blobs and stores them across its storage fleet. A <b>blob</b> is simply a chunk of binary data—part or all of a user file—written to disk. Magic Pocket is an <a href="https://dropbox.tech/infrastructure/inside-the-magic-pocket" target="_blank">immutable blob store</a>, which means that once a blob is written, it is never modified in place. If a file is updated or deleted, new data is written and the old data remains until it is reclaimed by a compaction process.</p>
<p>At Dropbox scale, Magic Pocket stores trillions of blobs and processes millions of deletes each day. (A <b>delete</b> is a request to remove a blob when a file is deleted or updated.) Because data is immutable, deletes do not immediately free up disk space. Old data stays on-disk inside storage volumes. Once a volume is closed, it is never reopened. The tradeoff is that deletes leave unused space behind, and that waste grows over time unless we actively reclaim it.</p>
<p>Without reclamation, volumes gradually become partially filled, spreading live data across more disks than necessary. Fragmentation from lack of reclamation can have a big impact on storage overhead.</p>
<p>We address this in two steps. <b>Garbage collection</b> identifies blobs that are no longer referenced and marks them as safe to remove, but it does not free space on its own. <b>Compaction</b> performs the physical reclamation. Because volumes cannot be modified once closed, we gather the live blobs from volumes, write them into new volumes, and retire the old ones. This is how deletes eventually translate into reusable space.</p>

</div>
<div class="image c04-image aem-GridColumn aem-GridColumn--default--12">
<div class="dr-image image cq-dd-image  ">
    <figure class="dr-margin-0 dr-display-inline-block">
        
            
    

        

        
        
        

        
        
        

        <!--optimized image webp-->
        

        
        <!-- <img data-sly-test.highRes="false"
             srcset="/cms/content/dam/dropbox/tech-blog/en-us/2026/march/magic-pocket-compaction/diagram/Diagram%201.png 2x,  1x"
             src="/cms/content/dam/dropbox/tech-blog/en-us/2026/march/magic-pocket-compaction/diagram/Diagram%201.png"
             aria-hidden=""
             alt=""
             class=""
             data-sly-attribute.width="2880"
             data-sly-attribute.height="2080"
             data-aem-asset-id="138034ce-a84d-4a6c-9dd5-946f734dd0df:Diagram 1.png"
             data-trackable="true" />
        <img data-sly-test.highRes="false"
             srcset="/cms/content/dam/dropbox/tech-blog/en-us/2026/march/magic-pocket-compaction/diagram/Diagram%201.png 2x,  1x"
             src="/cms/content/dam/dropbox/tech-blog/en-us/2026/march/magic-pocket-compaction/diagram/Diagram%201.png"
             aria-hidden=""
             alt=""
             class=""
             data-sly-attribute.width="2880"
             data-sly-attribute.height="2080"
             data-aem-asset-id="138034ce-a84d-4a6c-9dd5-946f734dd0df:Diagram 1.png"
             data-trackable="true" /> -->

        
         
        <img src="/cms/content/dam/dropbox/tech-blog/en-us/2026/march/magic-pocket-compaction/diagram/Diagram%201.png/_jcr_content/renditions/Diagram%201.webp" fallbackimage="/cms/content/dam/dropbox/tech-blog/en-us/2026/march/magic-pocket-compaction/diagram/Diagram%201.png" onerror="window.failedAttempts=0;this.setAttribute('src',this.getAttribute('fallbackimage'));window.failedAttempts++;if(window.failedAttempts == 1)this.onerror=null" aria-hidden="false" alt="" data-aem-asset-id="138034ce-a84d-4a6c-9dd5-946f734dd0df:Diagram 1.png" data-trackable="true" height="2080" width="2880"/>
    

            <figcaption class="dr-typography-t5 dr-color-ink-60 dr-image-rte"><p style="text-align: center;">Compaction lifecycle of volume 1 getting compacted along with a donor volume (2). Volume 1 is then eligible to be reused.</p>
</figcaption>
        
    </figure>
</div></div>
<div class="text parbase aem-GridColumn aem-GridColumn--default--12">
<p>Compaction controls the waste created by deletes. But fragmentation isn’t the only factor that affects storage overhead—durability does too. To protect against hardware failures, we store data redundantly either as full copies or as encoded fragments distributed across different machines, so data can be recovered after disk or server failures. One approach is replication, which keeps multiple full copies of each blob and increases storage use proportionally. In Magic Pocket, we use <b>erasure coding</b> for nearly all data. Erasure coding splits data into fragments and adds a small number of parity fragments (extra pieces that let us reconstruct the original data if part of it is lost). It provides the same level of fault tolerance as replication, but with significantly less additional storage.</p>
<p>Redundancy affects overhead, but fragmentation determines how efficiently that space is used. A useful way to think about this is what percentage of a volume that contains active data. If a volume is half full of live data, we are effectively using twice the storage needed for that data. If only ten percent is live, we are using about ten times the space required. Without continuous compaction, disk capacity would eventually be exhausted even if the data redundancy scheme—how we store extra copies or fragments to protect against failures—never changed. Keeping storage overhead low in an immutable system therefore requires both efficient redundancy and constant consolidation of fragmented space.</p>

</div>
<div class="section aem-GridColumn aem-GridColumn--default--12">
<div class="dr-article-content__section" id="the-incident-that-forced-a-rethink">
    <h2 class="dr-article-content__section-title">The incident that forced a rethink</h2>
</div>
</div>
<div class="text parbase aem-GridColumn aem-GridColumn--default--12">
<p>Earlier this year, we uncovered an issue with a new service that performs on-the-fly erasure coding, which we’ll refer to as the Live Coder service. It rolled out gradually over several months to new regions. The problem, which went unnoticed for weeks, was that volumes created through this path were severely under-filled. In the worst cases, less than five percent of their allocated capacity contained live data.</p>
<p>In practical terms, that meant live data was spread across far more volumes than intended. Instead of densely packing blobs together, we were creating many mostly empty volumes. Because volumes are fixed in size, each under-filled volume consumed the same disk allocation as a full one. The result was a sharp increase in fragmentation and a corresponding rise in storage overhead.</p>
<p>We saw early signs that this was impacting our effective replication factor, a signal that more raw storage was being consumed per live byte than expected. But identifying the root cause required significant investigation. Once we understood what was happening, we also needed to design recovery mechanisms capable of bringing overhead back down efficiently. The existing compaction strategy continued to make progress, but it was not designed to handle a long tail of severely under-filled volumes at this scale.</p>
<p>This incident exposed a limitation in our steady-state approach. It forced us to rethink how compaction should work when the distribution of live data shifts, and to develop new strategies capable of reclaiming space faster and more effectively.</p>

</div>
<div class="section aem-GridColumn aem-GridColumn--default--12">
<div class="dr-article-content__section" id="what-steady-state-compaction-looks-like">
    <h2 class="dr-article-content__section-title">What steady-state compaction looks like</h2>
</div>
</div>
<div class="text parbase aem-GridColumn aem-GridColumn--default--12">
<p>In normal operation, before the incident, the distribution of data across volumes was relatively stable. Most volumes were already highly filled, and deletes accumulated gradually. In that steady state, compaction’s job was to continuously consolidate small amounts of fragmentation and keep storage overhead bounded.</p>
<p>For years, our baseline compaction strategy, which we call L1, worked well in this environment. It treats compaction as a packing problem: move live data from one or more partially filled donor volumes into a host volume that has enough free space. Over time, as donor volumes are drained of their live data, they become empty and can be removed.</p>
<p>L1 selects a host volume that is already highly filled, then chooses donor volumes whose live bytes fit into the host’s available space, and finally, writes them into a new volume. The selection logic is simple and fast, and it keeps placement risk and metadata updates bounded. However, each compaction run is relatively expensive. It may read tens of GiB across the host and donors but typically produces only a single new densely packed volume. On average, fewer than one full volume is reclaimed per run, since only donors are fully drained.</p>
<p>This approach works well when most volumes are close to full. But the incident changed that distribution. We saw overhead concentrated in a long tail of severely under-filled volumes. L1 continued to make progress, but it could not compact those volumes quickly enough. Its core assumption, that most volumes are highly filled, no longer held. To address this, we introduced two new compaction strategies, L2 and L3, each designed to handle different parts of the volume fill distribution.</p>

</div>
<div class="section aem-GridColumn aem-GridColumn--default--12">
<div class="dr-article-content__section" id="a-better-way-to-reclaim-space">
    <h2 class="dr-article-content__section-title">A better way to reclaim space</h2>
</div>
</div>
<div class="text parbase aem-GridColumn aem-GridColumn--default--12">
<p>When the distribution shifted, the limitations of L1 became clear. It was designed to top off already dense volumes, not to quickly reclaim a large population of severely under-filled ones. We needed a strategy that could reclaim space faster by combining multiple sparse volumes into a single near-full destination. </p>
<p>When we examined the distribution of live data across volumes, most of the wasted space was concentrated in a distinct subset that was less than half full. L1 wasn’t designed for this pattern; it topped off already dense volumes rather than aggressively consolidating sparse ones. Instead of incrementally packing donors into a host, L2 groups under-filled volumes together and selects combinations whose live data can nearly fill a new destination volume. Reclaiming several sparse volumes at once allows the system to recover space far more quickly.</p>

</div>
<div class="dr-code-container aem-GridColumn aem-GridColumn--default--12">




<div class="dr-code-container--title"></div>
<div class="dr-code-container-inner">

    <button class="dr-code-container__copy-button dr-button dr-typography-t17">
        Copy
    </button>
    <pre class="dr-code-container__pre"><code class="dr-code-container__code dr-typography-t5 ">Inputs:
volumes[] with LiveBytes
  maxVolBytes (destination volume capacity)
  maxVolumesToUse (count cap)
  granularity (scaling factor)
1) Scale live bytes and capacity by granularity to shrink the DP table.
2) DP over (i = volume index, k = count, c = capacity), keeping max packed bytes.
3) Track choices in a parallel “choice” table for reconstruction.
4) Backtrack from the best (k, capacity) to recover the selected volumes.</code></pre>


</div>
<div class="dr-code-container-rte"></div>
</div>
<div class="text parbase aem-GridColumn aem-GridColumn--default--12">
<p>Under the hood, L2 is a bounded packing problem solved with dynamic programming. For each run, we choose a limited set of volumes whose combined live bytes come as close as possible to the destination capacity without exceeding it. To keep this practical at production scale, we cap how many source volumes can be used in one run and coarsen byte counts to reduce the search space. This keeps compute and memory bounded while still producing tight packings. </p>
<p>In practice, we tuned granularity, batch size, and planner concurrency to balance packing quality against compute and memory cost. Those settings allowed L2 to run efficiently in production while still producing tight packings.</p>
<p>In testing, the results were strong. With data shaped to resemble production distributions, L2 consistently produced near-full volumes. In production, it reduced compaction overhead two to three times faster than L1. In cells where L2 was enabled, overhead returned to sustainable levels within days, and over the course of a week, compaction overhead was thirty to fifty percent lower compared to cells running L1 alone. (Cells, in this instance, refer to independent units of the storage system that manage their own data.)</p>

</div>
<div class="section aem-GridColumn aem-GridColumn--default--12">
<div class="dr-article-content__section" id="cleaning-up-the-sparsest-volumes">
    <h2 class="dr-article-content__section-title">Cleaning up the sparsest volumes</h2>
</div>
</div>
<div class="text parbase aem-GridColumn aem-GridColumn--default--12">
<p>L2 was effective at targeting the middle of the distribution, where volumes were under-filled but still dense enough to combine efficiently. But it was less effective at quickly reclaiming the sparsest volumes, or those with only a small fraction of live data remaining. These volumes formed the extreme tail of the distribution and required a different approach.</p>
<p>While iterating on compaction strategies, we returned to the Live Coder service. Its original purpose was to write data directly into erasure-coded volumes, bypassing the initial replicated write path. Although it isn’t ideal for latency-sensitive traffic, it’s well suited for background workflows where throughput matters more than immediacy.</p>
<p>Compaction is, in effect, a constrained form of re-encoding: take live data from one set of volumes and produce a new, durable volume. L3 builds on that idea by using Live Coder as a streaming pipeline. Instead of packing volumes together in a bounded batch, L3 continuously feeds the remaining live blobs from severely under-filled volumes into Live Coder and allows it to accumulate and encode them into new volumes over time. Once a source volume’s live data has been drained, it can be reclaimed immediately.</p>
<p>This strategy focuses on volumes that aren’t good candidates for L1 or L2. Under-filled volumes occur naturally as donors are partially drained, and they can accumulate quickly during failure modes like the incident described earlier. By prioritizing the sparsest volumes first, L3 minimizes the amount of data that needs to be rewritten per reclaimed volume and accelerates recovery of fragmented space.</p>
<p>L3 does introduce tradeoffs. Because it writes live data into entirely new volumes, every blob it moves has to be rewritten, which means new identifiers and additional metadata updates. That extra bookkeeping creates load on storage and <a href="https://dropbox.tech/infrastructure/panda-metadata-stack-petabyte-scale-transactional-key-value-store" target="_blank">metadata systems</a>. With the amount of under-filled volumes observed in steady state, that additional load is tolerable and limits are in place to prevent overwhelming those systems.</p>

</div>
<div class="section aem-GridColumn aem-GridColumn--default--12">
<div class="dr-article-content__section" id="operational-tuning-and-safeguards">
    <h2 class="dr-article-content__section-title">Operational tuning and safeguards</h2>
</div>
</div>
<div class="text parbase aem-GridColumn aem-GridColumn--default--12">
<p>To prevent compaction from competing with user traffic, we rate-limit the pipeline and keep traffic local to each cell rather than sending it across data centers. Together, L1, L2, and L3 form a layered strategy: L1 maintains steady state, L2 consolidates moderately under-filled volumes, and L3 drains the sparsest tail, thereby reclaiming space quickly without destabilizing the fleet.</p>
<p>Rolling out L2 and L3 wasn’t just about improving packing efficiency. We also had to ensure the system could absorb the additional work without creating new bottlenecks. Compaction touches storage, compute, metadata systems, and network bandwidth, so increasing its aggressiveness requires careful controls.</p>
<p>One of the most sensitive levers is the host eligibility threshold, which determines when a volume qualifies for compaction. If the threshold is too high, too few volumes are eligible and overhead rises. If it’s too low, we spend compute and I/O reclaiming very little space. We replaced static tuning with a dynamic control loop that adjusts the threshold based on fleet signals. When overhead rises, the system raises the threshold to prioritize higher-yield compactions. When overhead stabilizes, it lowers the threshold to stay responsive to deletes without over-compacting.</p>
<p>Candidate ordering is another important tuning lever. Choosing which volumes to compact first can speed up space reclamation, but it can also increase metadata work because more blobs may need to be rewritten. We tailor the ordering to each strategy. L1 stays conservative and limits how many donor volumes it touches to keep placement risk and metadata load low. L2 benefits from more aggressive grouping because denser packings reclaim more space per compaction run. L3 focuses on the sparsest volumes first, since draining them typically requires rewriting relatively little data per volume.</p>
<p>The final step was enabling L1, L2, and L3 to run concurrently without interfering with one another. Each strategy targets a different part of the volume distribution: L1 maintains steady state among highly filled volumes, L2 targets moderately under-filled volumes into dense destinations, and L3 drains the sparsest volumes. We enforce clear eligibility boundaries between strategies and rate-limit each path to protect downstream services. We also constrain traffic locality so compaction remains within a cell and avoids stressing cross-cluster bandwidth.</p>
<p>Together, these safeguards allow the system to adapt to workload shifts while keeping metadata pressure, network traffic, and compute utilization within safe limits.</p>

</div>
<div class="section aem-GridColumn aem-GridColumn--default--12">
<div class="dr-article-content__section" id="what-we-learned">
    <h2 class="dr-article-content__section-title">What we learned</h2>
</div>
</div>
<div class="text parbase aem-GridColumn aem-GridColumn--default--12">
<p>This project reinforced that compaction can’t rely on a single heuristic. L1 worked well in steady state because most volumes were already close to full, and only a small number were partially filled at any given time. When that distribution shifted and a large group of very sparsely filled volumes accumulated, L1 couldn’t recover overhead quickly enough. Splitting the problem across multiple strategies gave us coverage across the full range of volume fill levels: L1 maintains steady state for mostly full volumes, L2 consolidates moderately under-filled volumes, and L3 focuses on the sparsest volumes.</p>
<p>We also learned that manual tuning doesn’t scale. The host eligibility threshold is too sensitive to manage by hand, especially at exabyte scale. Moving to a dynamic control loop tied to fleet signals made overhead more stable and reduced the need for constant intervention. Candidate ordering and rate limits must also be tuned with awareness of downstream systems, particularly metadata services.</p>
<p>Operationally, metadata capacity turned out to be one of our biggest constraints. Not every compaction move has the same metadata cost. In L1 and L2, many blobs can stay under the same volume identity, so only donor blobs need location rewrites. In L3, blobs are written into brand-new volumes, so most blobs need new location entries. So it wasn’t enough to pack volumes efficiently; we also had to control how much rewriting we triggered. By limiting how much work L2 does in a single run, routing the sparsest volumes through L3, and keeping traffic local to each cell, we were able to reclaim space without overwhelming our metadata, storage, or network systems.</p>
<p>Finally, this work showed us that we needed better visibility into how compaction was performing. We added metrics to track how much data Live Coder is producing, how full volumes are across the fleet, and how storage overhead changes week over week. We also put monitoring in place to warn us early if compaction starts to fall behind. The goal is to catch shifts in how data is distributed before overhead rises too far so that we can respond proactively instead of scrambling to recover later.</p>
<p>Storage overhead directly determines how much raw capacity we need in order to store the same amount of live user data. Even small changes in overhead materially affect hardware purchases and fleet growth. By turning compaction into a layered, adaptive pipeline and strengthening our monitoring and controls, we made Magic Pocket more resilient to workload changes and better positioned to keep storage growth predictable over time.</p>
<p><i>Acknowledgments: Tommy Dean (contributions to L2 strategy) and Lisa Kosiachenko (contributions on automation)</i></p>
<p style="text-align: center;">~ ~ ~</p>
<p><i>If building innovative products, experiences, and infrastructure excites you, come build the future with us! Visit </i><a href="https://jobs.dropbox.com/" target="_blank"><i>jobs.dropbox.com</i></a><i> to see our open roles.</i></p>

</div>

    
</div>
]]></content:encoded>
            			
                <media:thumbnail url="https://dropbox.tech/cms/content/dam/dropbox/tech-blog/en-us/2026/march/magic-pocket-compaction/headers/Infrastructure-Optimizing compaction in Magic Pocket-1440x305-light.png" />
                <media:content url="https://dropbox.tech/cms/content/dam/dropbox/tech-blog/en-us/2026/march/magic-pocket-compaction/headers/Infrastructure-Optimizing compaction in Magic Pocket-1440x305-light.png" medium="image">
                    <media:title type="html">Improving storage efficiency in Magic Pocket, our immutable blob store</media:title>
                </media:content>
       		 </item>
                    
        				<item>
                        <title>Reducing our monorepo size to improve developer velocity</title>
                        
            			<link>https://dropbox.tech/infrastructure/reducing-our-monorepo-size-to-improve-developer-velocity</link>

                            
            			<dc:creator>
                            Facundo Agriel,Ishan Mishra
            			</dc:creator>
            			
            				<category>developer velocity</category>
							
            				<category>AI</category>
							
            				<category>Developers</category>
							
            				<category>Infrastructure</category>
							
            				<category>storage</category>
							
                            
            			<description><![CDATA[Monorepos will continue to grow as products evolve, but growth doesn’t have to mean friction.]]></description>
            			<guid>https://dropbox.tech/infrastructure/reducing-our-monorepo-size-to-improve-developer-velocity</guid>
                        <pubDate>Wed, 25 Mar 2026 10:00:00 -0700</pubDate>
                            
                        <content:encoded><![CDATA[


<div class="aem-Grid aem-Grid--12 aem-Grid--default--12 ">
    
    <div class="text parbase aem-GridColumn aem-GridColumn--default--12">
<p>At Dropbox, almost every product change flows through a single place: our server monorepo. A monorepo is a single, shared Git repository that contains many services and libraries used across the company. Instead of splitting code across dozens of smaller repositories, we keep a large portion of our backend infrastructure in one place. That architecture makes cross-service development easier, but it also means the repository sits at the center of nearly everything we build. </p>
<p>Building <a href="https://dropbox.tech/machine-learning/building-dash-rag-multi-step-ai-agents-business-users" target="_blank">AI-powered features</a> at Dropbox often requires small changes across ranking systems, retrieval pipelines, <a href="https://dropbox.tech/machine-learning/practical-blueprint-evaluating-conversational-ai-at-scale-dash" target="_blank">evaluation logic</a>, and UI surfaces. All of that work moves through the same engineering loop: pull the latest code, build and test it, get it reviewed, merge it, and ship it. Over time, we began to notice that this loop was getting slower. Our monorepo had grown to 87GB; downloading a full copy of the codebase (or “cloning” the repository) took more than an hour, and many continuous integration (CI) jobs were repeatedly paying that cost. We were also approaching GitHub’s 100GB repository size limit, which introduced real operational risk.</p>
<p>In this post, we’ll share how we reduced the repository from 87GB to 20GB (a 77% reduction), cutting the time required to clone the repository to under 15 minutes. We’ll also explain what was driving the growth and what we learned about maintaining a large monorepo at scale.</p>

</div>
<div class="experiencefragment aem-GridColumn aem-GridColumn--default--12">
<div id="experiencefragment-97d860d01d" class="cmp-experiencefragment cmp-experiencefragment--dash-cta-for-tech-blog">

    



<div class="xf-content-height">
    


<div class="aem-Grid aem-Grid--12 aem-Grid--default--12 ">
    
    <div class="responsivegrid aem-GridColumn aem-GridColumn--default--12">


<div class="aem-Grid aem-Grid--12 aem-Grid--default--12 ">
    
    <div class="a08-html-embed c17-plain-html aem-GridColumn aem-GridColumn--default--12">

<style type="text/css"> 

@font-face {
font-family: 'AtlasGrotesk';
src: url('https://cdn.prod.website-files.com/65dcd70b48edc3a7b446950e/65dce019c63112e617513c94_AtlasGrotesk-Medium-Web-vfl38XiTL.woff2') format('woff2');
font-weight: 500;
font-style: normal;
font-display: swap;
}

@font-face {
font-family: 'AtlasGrotesk';
src: url('https://cdn.prod.website-files.com/65dcd70b48edc3a7b446950e/65dce019711b648fd1ccd24a_AtlasGrotesk-Regular-Web-vflk7bxjs.woff2') format('woff2');
font-weight: 400;
font-style: normal;
font-display: swap;
}
.xf-content-height {margin: 0;}
#cta { font-family: AtlasGrotesk,sans-serif; font-size: .900rem; text-decoration: none; background: #f7f5f2; line-height: 1.69; box-sizing: border-box;}
#cta-box { padding: 15px 20px 15px 20px; }
#cta-hed {font-weight: 500;}
#cta-indent {border-left: 5px solid #1e1919; padding-left:20px;}
#cta a:link, #cta a:visited  {text-decoration: none;}
#cta p { margin: 5px 0px 0px 0px; }

.dr-theme-dark #cta {background: #000;}
.dr-theme-dark #cta-box {border: 1px solid; border-bottom: 0;}
.dr-theme-dark #cta-indent {border-left: 5px solid #f7f5f2;}
.dr-theme-dark .button {background: #000;}

.button {
    background-color: #1e1919;
    color:  #f7f5f2;
    height: 2.5rem;
    padding: 10px 5px 10px 20px;
    font-size: 1rem;
    font-weight: 500;
    line-height: 1.2;
    transition: all .3s;
}

.button:hover { background-color: #0061ff; }

img {vertical-align: middle; padding: 0px 1px 2px 0px;}

.c17-plain-html {margin-bottom: 50px}

</style>

<div id="cta">
<div id="cta-box">
<div id="cta-indent">

<p id="cta-hed"><img src="https://cdn.prod.website-files.com/65dcd70b48edc3a7b446950e/670692ee7692f74d4834e4f4_Frame%201400006055.svg" loading="lazy"> Dropbox Dash: AI that understands your work
</p>

<p>Dash knows your context, your team, and your work, so your team can stay organized, easily find and share knowledge, and keep projects secure, all from one place. And soon, Dash is coming to Dropbox.</p>

</div>
</div>

<a href="https://dash.dropbox.com/?utm=blogs" target="_blank"><div class="button">Learn more →</div></a>

</div>
</div>

    
</div>
</div>

    
</div>

</div></div>

    
</div>
<div class="section aem-GridColumn aem-GridColumn--default--12">
<div class="dr-article-content__section" id="when-repository-size-becomes-a-real-problem">
    <h2 class="dr-article-content__section-title">When repository size becomes a real problem</h2>
</div>
</div>
<div class="text parbase aem-GridColumn aem-GridColumn--default--12">
<p>To understand why repository size matters, it helps to look at how engineers actually work. The first time someone sets up their development environment, they clone the repository, meaning they download a full copy of the codebase and its history to their machine. After that initial setup, daily work is less intensive. Engineers fetch and pull incremental updates rather than redownloading everything. But that first clone is unavoidable, and when the repository reached 87GB, it regularly took more than an hour.</p>
<p>That cost didn’t just affect onboarding. Many continuous integration jobs—automated build and test workflows that run on every code change—begin from a fresh clone. That meant our CI pipelines were repeatedly incurring the same overhead. Internal systems that synchronize the repository were also handling significantly more data than before, which increased the likelihood of timeouts and degraded performance.</p>
<p>At the same time, the repository was growing steadily, typically by 20 to 60MB per day, with occasional spikes above 150MB. At that rate, we were on track to hit the GitHub Enterprise Cloud (GHEC)<b> </b>100GB repository size hard limit within months. The issue wasn’t simply that we had a large codebase. The growth rate itself didn’t match what we would expect from normal development activity, even at Dropbox’s scale. That suggested the problem wasn’t just what we were storing, but how it was being stored.</p>

</div>
<div class="section aem-GridColumn aem-GridColumn--default--12">
<div class="dr-article-content__section" id="when-compression-backfires">
    <h2 class="dr-article-content__section-title">When compression backfires</h2>
</div>
</div>
<div class="text parbase aem-GridColumn aem-GridColumn--default--12">
<p>At first, we looked for the usual causes of repository bloat: large binaries, accidentally committed dependencies, or generated files that didn’t belong in version control. None of those explained what we were seeing. The growth pattern pointed somewhere less obvious: Git’s delta compression.</p>
<p>Git doesn’t store every version of every file as a complete copy. Instead, it tries to save space by storing the differences between similar files. When multiple versions of a file exist, Git keeps one full version and represents the others as deltas, or “diffs,” against it. In most repositories, this works extremely well and keeps storage efficient.</p>
<p>The issue was how Git decides which files are similar enough to compare. By default, it uses a heuristic based on only the last 16 characters of the file path when pairing files for delta compression. In many codebases, that’s good enough. Files with similar names often contain related content. Our internationalization (i18n) files, however, followed this structure:</p>
<p><span class="dr-code" title="undefined"> i18n/metaserver/[language]/LC_MESSAGES/[filename].po</span></p>
<p>The language code appears earlier in the path, not in the final 16 characters. As a result, Git was often computing deltas between files in different languages instead of within the same language. A small update to one translation file might be compared against an unrelated file in another language. Instead of producing a compact delta, Git generated a much larger one.</p>
<p>Routine translation updates were therefore creating disproportionately large pack files. Nothing about the content was unusual. The problem was the interaction between our directory structure and Git’s compression heuristic. Once we understood that mismatch, the rapid growth of the repository finally made sense.</p>
<h3>Testing a fix locally</h3>
<p>Once we suspected that delta pairing was the root cause, we looked for ways to influence how Git grouped files during compression. We found an experimental flag called <span class="dr-code" title="undefined">--path-walk</span> that changes how Git selects candidates for delta comparison. Instead of relying on the last 16 characters of a path, it walks the full directory structure, which keeps related files closer together.</p>
<p>We ran a local repack—essentially asking Git to reorganize and recompress the objects in the repository—using this flag. The results were immediate. The repository shrank from the low-80GB range to the low-20GB range. That confirmed our hypothesis: the issue wasn’t the volume of data, but how it was being packed.</p>
<p>However, that success exposed a new constraint. GitHub told us that <span class="dr-code" title="undefined">--path-walk</span> was not compatible with certain server-side optimizations they rely on, including features like bitmaps and delta islands that make cloning and fetching fast. Even though the fix worked locally, it wouldn’t work in production.</p>
<p>We needed a solution that achieved the same size reduction while remaining compatible with GitHub’s infrastructure. That meant working within the parameters GitHub could safely support, rather than relying on an experimental client-side flag.</p>
<h3>Why we couldn't do this alone</h3>
<p>Our local experiments proved that better packing could dramatically reduce the repository size. But there was a critical limitation: you can’t repack a repository locally, push it to GitHub, and expect those improvements to persist.</p>
<p>GitHub constructs transfer packs dynamically on the server based on what each client is missing. That means the server’s own packing strategy determines clone and fetch sizes. Even if a local mirror is perfectly optimized, GitHub will rebuild the pack during transfer using its own configuration. To permanently reduce repository size and improve performance, the repack had to be executed on GitHub’s servers.</p>

</div>
<div class="dr-code-container aem-GridColumn aem-GridColumn--default--12">




<div class="dr-code-container--title"></div>
<div class="dr-code-container-inner">

    <button class="dr-code-container__copy-button dr-button dr-typography-t17">
        Copy
    </button>
    <pre class="dr-code-container__pre"><code class="dr-code-container__code dr-typography-t5 html">$ git clone --mirror git@github.com:dropbox-internal/server.git server_mirror
performance: 2795.152366000 s

$ du -sh server_mirror
84G     server_mirror

$ git repack -adf --depth=250 --window=250
performance: 31205.079533000 s (~9h)

$ du -sh server_mirror
20G     server_mirror</code></pre>


</div>
<div class="dr-code-container-rte"></div>
</div>
<div class="text parbase aem-GridColumn aem-GridColumn--default--12">
<p>We shared our findings with GitHub Support and worked with them on a solution that would be compatible with their infrastructure. Instead of relying on experimental flags, they recommended a more aggressive repack using tuned window and depth parameters. These settings control how thoroughly Git searches for similar objects and how many layers of deltas it allows. Higher values increase compute time during repacking but can significantly improve compression.</p>
<p>We tested the approach on a mirrored clone of the repository. The repack took roughly nine hours to complete, but the result was clear: the repository shrank from 84GB to 20GB. Because this method aligned with GitHub’s server-side optimizations, it could be executed safely in production.</p>

</div>
<div class="section aem-GridColumn aem-GridColumn--default--12">
<div class="dr-article-content__section" id="rolling-it-out-without-breaking-anything">
    <h2 class="dr-article-content__section-title">Rolling it out without breaking anything</h2>
</div>
</div>
<div class="text parbase aem-GridColumn aem-GridColumn--default--12">
<p>Repacking a repository changes how billions of objects are physically organized on disk. It doesn’t alter the contents of the code, but it does change the structure underlying every clone, fetch, and push. Given how central the monorepo is to our development workflow, we treated this like any other production infrastructure change.</p>
<p>Before touching the live repository, we created a test mirror and had GitHub perform the repack there first. We monitored fetch duration distributions, push success rates, and API latency to ensure the new pack structure didn’t introduce regressions. The mirror dropped from 78GB to 18GB, and while there was minor movement at the tail of fetch latency, it was well within the tradeoff we were willing to make for a fourfold size reduction. We didn’t observe stability issues.</p>
<p>With that validation in place, GitHub rolled out the production repack gradually over the course of a week. They updated one replica per day, beginning with read-write replicas and reserving buffer time at the end of the week in case a rollback was needed. This phased approach ensured that if anything unexpected surfaced, they could revert safely.</p>
<p>The final result was substantial. The repository shrank from 87GB to 20GB, and clone times dropped from over an hour to under 15 minutes in many cases. New engineers no longer begin onboarding with a long wait. CI pipelines start faster and run more reliably. Internal services that synchronize the repository are less prone to timeouts. And by moving well below GitHub’s 100GB limit, we reduced the risk of platform-level performance degradation during high-traffic periods.</p>
<p>Just as importantly, the system remained stable throughout the rollout. Fetch duration, push success rates, and API latency all stayed within expected ranges. The improvements held without introducing new operational risk.</p>

</div>
<div class="image c04-image aem-GridColumn aem-GridColumn--default--12">
<div class="dr-image image cq-dd-image  ">
    <figure class="dr-margin-0 dr-display-inline-block">
        
            
    

        

        
        
        

        
        
        

        <!--optimized image webp-->
        

        
        <!-- <img data-sly-test.highRes="false"
             srcset="/cms/content/dam/dropbox/tech-blog/en-us/2026/march/monorepo/diagrams/Diagram%201%20(2).png 2x,  1x"
             src="/cms/content/dam/dropbox/tech-blog/en-us/2026/march/monorepo/diagrams/Diagram%201%20(2).png"
             aria-hidden=""
             alt=""
             class=""
             data-sly-attribute.width="2880"
             data-sly-attribute.height="1560"
             data-aem-asset-id="481bc29b-fc95-4475-9bb1-4dd7665c66bc:Diagram 1 (2).png"
             data-trackable="true" />
        <img data-sly-test.highRes="false"
             srcset="/cms/content/dam/dropbox/tech-blog/en-us/2026/march/monorepo/diagrams/Diagram%201%20(2).png 2x,  1x"
             src="/cms/content/dam/dropbox/tech-blog/en-us/2026/march/monorepo/diagrams/Diagram%201%20(2).png"
             aria-hidden=""
             alt=""
             class=""
             data-sly-attribute.width="2880"
             data-sly-attribute.height="1560"
             data-aem-asset-id="481bc29b-fc95-4475-9bb1-4dd7665c66bc:Diagram 1 (2).png"
             data-trackable="true" /> -->

        
         
        <img src="/cms/content/dam/dropbox/tech-blog/en-us/2026/march/monorepo/diagrams/Diagram%201%20(2).png/_jcr_content/renditions/Diagram%201%20(2).webp" fallbackimage="/cms/content/dam/dropbox/tech-blog/en-us/2026/march/monorepo/diagrams/Diagram%201%20(2).png" onerror="window.failedAttempts=0;this.setAttribute('src',this.getAttribute('fallbackimage'));window.failedAttempts++;if(window.failedAttempts == 1)this.onerror=null" aria-hidden="false" alt="" data-aem-asset-id="481bc29b-fc95-4475-9bb1-4dd7665c66bc:Diagram 1 (2).png" data-trackable="true" height="1560" width="2880"/>
    

            <figcaption class="dr-typography-t5 dr-color-ink-60 dr-image-rte"><p style="text-align: center;">Project data size dropped significantly and has remained stable since.</p>
</figcaption>
        
    </figure>
</div></div>
<div class="section aem-GridColumn aem-GridColumn--default--12">
<div class="dr-article-content__section" id="what-we-learned">
    <h2 class="dr-article-content__section-title">What we learned</h2>
</div>
</div>
<div class="text parbase aem-GridColumn aem-GridColumn--default--12">
<p>Beyond the size reduction itself, this project reinforced a few broader lessons about maintaining large-scale infrastructure. The following three mattered most:</p>
<p><b>Growth isn’t just about commit volume<br />
 </b>When we first noticed the repository ballooning, the instinct was to look at what was being added: large files, unused dependencies, generated artifacts. But the root cause had nothing to do with the content of our commits. It was about how our directory structure interacted with Git’s compression heuristics. Our i18n paths encouraged Git to compute deltas across different languages rather than within the same language. Routine translation updates were therefore creating oversized pack files. The growth was structural, not behavioral.</p>
<p>Tools embed assumptions. When your usage patterns diverge from those assumptions, performance can degrade quietly over time. In our case, Git’s 16-character path heuristic worked as designed. It just didn’t work well with our repository structure. Understanding those internal mechanics was what allowed us to diagnose the issue correctly.</p>
<p><b>Some fixes require working with your platform provider<br />
 </b>We were able to identify the root cause and even validate a fix locally. But because GitHub determines how repositories are packed and transferred, a local repack wasn’t enough. The solution had to align with GitHub’s server-side infrastructure.</p>
<p>That meant bringing clear data to GitHub, testing collaboratively, and working within supported parameters. When your system depends on a managed platform, some problems live at the boundary between your code and theirs. Having strong relationships and a shared debugging process makes a meaningful difference.</p>
<p><b>Treat repo health like production infrastructure<br />
 </b>A repository repack changes the physical structure of billions of objects. Even though the code itself doesn’t change, every engineer and every automated system interacts with that underlying structure. We approached this project the same way we would approach any production infrastructure change: test on a mirror, measure real-world impact, roll out gradually, and maintain a rollback path.</p>
<p>Repositories can feel like passive storage, something that simply grows over time. At scale, they are not passive. They are critical infrastructure that directly affects developer velocity and CI reliability. As part of this work, we built a recurring stats job that tracks key health indicators for the monorepo and feeds them into an internal dashboard. It monitors things like overall repository size, how quickly that size is growing, how long a fresh clone takes, and how storage is distributed across different parts of the codebase. If growth starts accelerating again or clone times begin creeping up, we'll see it early rather than discovering it when engineers start feeling the pain. Monitoring growth trends and investigating anomalies early is part of running a healthy engineering organization.</p>

</div>
<div class="section aem-GridColumn aem-GridColumn--default--12">
<div class="dr-article-content__section" id="whats-next">
    <h2 class="dr-article-content__section-title">What’s next</h2>
</div>
</div>
<div class="text parbase aem-GridColumn aem-GridColumn--default--12">
<p>Reducing the repository from 87GB to 20GB had an immediate impact on how we build. New engineers can get started in minutes instead of waiting through a lengthy initial clone. CI pipelines spin up faster and run more reliably. Teams working on AI features—where progress often comes from many small, iterative changes across multiple services—feel that improvement in every development cycle.</p>
<p>The investigation also led to structural changes designed to prevent the same issue from resurfacing. We updated our i18n workflow to align more closely with how Git’s packing algorithm groups files, reducing the likelihood of pathological delta pairing in the future. Just as importantly, we now have better visibility into repository growth trends and a clearer understanding of what “normal” looks like.</p>
<p>More broadly, this project gave us a repeatable playbook. When growth accelerates unexpectedly, we know how to investigate at the compression layer, how to validate fixes safely, and how to work across platform boundaries when necessary. Monorepos will continue to grow as products evolve, but growth doesn’t have to mean friction. With the right tooling and discipline, it can remain invisible to the engineers who rely on it every day.</p>
<p><i>Acknowledgments: Samm Desmond, Genghis Chau</i></p>
<p style="text-align: center;">~ ~ ~</p>
<p><i>If building innovative products, experiences, and infrastructure excites you, come build the future with us! Visit </i><a href="https://jobs.dropbox.com/" target="_blank"><i>jobs.dropbox.com</i></a><i> to see our open roles.</i></p>

</div>

    
</div>
]]></content:encoded>
            			
                <media:thumbnail url="https://dropbox.tech/cms/content/dam/dropbox/tech-blog/en-us/2026/march/monorepo/headers/Infrastructure-Reducing monorepo size-375x150-dark.png" />
                <media:content url="https://dropbox.tech/cms/content/dam/dropbox/tech-blog/en-us/2026/march/monorepo/headers/Infrastructure-Reducing monorepo size-375x150-dark.png" medium="image">
                    <media:title type="html">Reducing our monorepo size to improve developer velocity</media:title>
                </media:content>
       		 </item>
                    
        				<item>
                        <title>How we optimized Dash&#039;s relevance judge with DSPy</title>
                        
            			<link>https://dropbox.tech/machine-learning/optimizing-dropbox-dash-relevance-judge-with-dspy</link>

                            
            			<dc:creator>
                            Facundo Agriel,Ishan Mishra,Eric Wang,Dmitriy Meyerzon
            			</dc:creator>
            			
            				<category>LLM</category>
							
            				<category>models</category>
							
            				<category>Dash</category>
							
            				<category>DSPy</category>
							
                            
            			<description><![CDATA[We used DSPy to turn prompt engineering for our relevance judge into a measurable, automated optimization loop, improving task performance, cost, and how reliably it works in production.]]></description>
            			<guid>https://dropbox.tech/machine-learning/optimizing-dropbox-dash-relevance-judge-with-dspy</guid>
                        <pubDate>Tue, 17 Mar 2026 10:00:00 -0700</pubDate>
                            
                        <content:encoded><![CDATA[


<div class="aem-Grid aem-Grid--12 aem-Grid--default--12 ">
    
    <div class="text parbase aem-GridColumn aem-GridColumn--default--12">
<p><a href="https://www.dash.dropbox.com/" target="_blank"><b>Dropbox Dash</b></a> brings your files, messages, and team’s knowledge together in one place, so you can ask questions and get useful answers that are actually grounded in your company’s context. Under the hood, that experience relies heavily on one deceptively simple capability: reliably judging which results are relevant to a query at scale. Relevance judges are used across multiple pipelines like ranking, training data generation, and offline evaluation. Without systematic optimization, they can become a primary source of regressions, cost blowups, and loss of trust as models change.</p>
<p>Making a relevance judge work in production is harder than it looks. A prototype might lean on a state-of-the-art model, but real systems have latency and cost budgets, which usually means migrating to smaller or cheaper models. The catch is that prompts often don’t transfer cleanly across models. We ran into this while scaling our <a href="https://dropbox.tech/machine-learning/llm-human-labeling-improving-search-relevance-dropbox-dash" target="_blank">LLM-as-a-judge work</a>: manual prompt tuning got us to a functioning judge, but quality plateaued early and every model swap—or even a small prompt edit—risked regressions in unexpected cases. </p>
<p>To address prompt brittleness and scale up relevance label generation for the long tail of candidates, we brought in <a href="https://dspy.ai/" target="_blank">DSPy</a>. DSPy is an open-source framework for systematically optimizing prompts against a measurable objective, turning a manual, fragile process into a repeatable optimization loop. In this article, we’ll show how we defined that objective, used DSPy to adapt our judge across models, and made the judge both cheaper and more reliable in production.</p>

</div>
<div class="experiencefragment aem-GridColumn aem-GridColumn--default--12">
<div id="experiencefragment-3a32c92190" class="cmp-experiencefragment cmp-experiencefragment--dash-cta-for-tech-blog">

    



<div class="xf-content-height">
    


<div class="aem-Grid aem-Grid--12 aem-Grid--default--12 ">
    
    <div class="responsivegrid aem-GridColumn aem-GridColumn--default--12">


<div class="aem-Grid aem-Grid--12 aem-Grid--default--12 ">
    
    <div class="a08-html-embed c17-plain-html aem-GridColumn aem-GridColumn--default--12">

<style type="text/css"> 

@font-face {
font-family: 'AtlasGrotesk';
src: url('https://cdn.prod.website-files.com/65dcd70b48edc3a7b446950e/65dce019c63112e617513c94_AtlasGrotesk-Medium-Web-vfl38XiTL.woff2') format('woff2');
font-weight: 500;
font-style: normal;
font-display: swap;
}

@font-face {
font-family: 'AtlasGrotesk';
src: url('https://cdn.prod.website-files.com/65dcd70b48edc3a7b446950e/65dce019711b648fd1ccd24a_AtlasGrotesk-Regular-Web-vflk7bxjs.woff2') format('woff2');
font-weight: 400;
font-style: normal;
font-display: swap;
}
.xf-content-height {margin: 0;}
#cta { font-family: AtlasGrotesk,sans-serif; font-size: .900rem; text-decoration: none; background: #f7f5f2; line-height: 1.69; box-sizing: border-box;}
#cta-box { padding: 15px 20px 15px 20px; }
#cta-hed {font-weight: 500;}
#cta-indent {border-left: 5px solid #1e1919; padding-left:20px;}
#cta a:link, #cta a:visited  {text-decoration: none;}
#cta p { margin: 5px 0px 0px 0px; }

.dr-theme-dark #cta {background: #000;}
.dr-theme-dark #cta-box {border: 1px solid; border-bottom: 0;}
.dr-theme-dark #cta-indent {border-left: 5px solid #f7f5f2;}
.dr-theme-dark .button {background: #000;}

.button {
    background-color: #1e1919;
    color:  #f7f5f2;
    height: 2.5rem;
    padding: 10px 5px 10px 20px;
    font-size: 1rem;
    font-weight: 500;
    line-height: 1.2;
    transition: all .3s;
}

.button:hover { background-color: #0061ff; }

img {vertical-align: middle; padding: 0px 1px 2px 0px;}

.c17-plain-html {margin-bottom: 50px}

</style>

<div id="cta">
<div id="cta-box">
<div id="cta-indent">

<p id="cta-hed"><img src="https://cdn.prod.website-files.com/65dcd70b48edc3a7b446950e/670692ee7692f74d4834e4f4_Frame%201400006055.svg" loading="lazy"> Dropbox Dash: AI that understands your work
</p>

<p>Dash knows your context, your team, and your work, so your team can stay organized, easily find and share knowledge, and keep projects secure, all from one place. And soon, Dash is coming to Dropbox.</p>

</div>
</div>

<a href="https://dash.dropbox.com/?utm=blogs" target="_blank"><div class="button">Learn more →</div></a>

</div>
</div>

    
</div>
</div>

    
</div>

</div></div>

    
</div>
<div class="section aem-GridColumn aem-GridColumn--default--12">
<div class="dr-article-content__section" id="how-to-measure-agreement-with-humans">
    <h2 class="dr-article-content__section-title">How to measure agreement with humans</h2>
</div>
</div>
<div class="text parbase aem-GridColumn aem-GridColumn--default--12">
<p>Before we can improve a relevance judge, we need a clear definition of what “good” means. At its core, the judge’s job is straightforward: given a query and a document, it assigns a relevance score from 1 to 5, where 5 indicates a perfect match and 1 indicates no meaningful connection to the query and user intent. To evaluate how well the judge performs, we compare its scores to those assigned by human annotators performing the same task.</p>
<p>In our evaluation dataset, humans are shown a query and a candidate document and asked to rate its relevance on that same 1–5 scale. They also provide a short explanation describing why they chose that score. These human judgments serve as our reference point. For more details on the annotation process, see our LLM-as-a-judge <a href="https://dropbox.tech/machine-learning/llm-human-labeling-improving-search-relevance-dropbox-dash" target="_blank">blog</a>. (Dropbox conducts these reviews with limited, non-sensitive internal datasets; no customer data is reviewed by humans as part of this process.)</p>
<p>We then measure how far the model’s ratings deviate from the human ratings using normalized mean squared error (NMSE), a metric that summarizes the model’s average disagreement with humans as a single number. If a human assigns a 5 and the model assigns a 4, that’s a small disagreement; if the human assigns a 5 and the model assigns a 1, that’s a much larger one. NMSE captures those differences across the entire dataset by computing the average squared gap between the model’s score and the human score, scaled to a 0–100 range. An NMSE of 0 indicates perfect agreement, while higher values indicate worse alignment.</p>
<p>We also account for structural reliability. The judge’s output is formatted as JSON; if the model returns broken JSON or fails to follow the expected structure, that output cannot be parsed and therefore cannot be used. In those cases, we treat the response as fully incorrect. These formatting failures aren’t cosmetic: if the output cannot be read, examples may be dropped, batches can fail, and evaluation metrics become unreliable.</p>
<p>Taken together, this framework gives us a clear and measurable objective: minimize disagreement with human relevance judgments while ensuring that outputs remain consistently usable in production systems. That’s the objective <a href="https://dropbox.tech/machine-learning/vp-josh-clemm-knowledge-graphs-mcp-and-dspy-dash" target="_blank">DSPy optimizes against</a>.</p>

</div>
<div class="section aem-GridColumn aem-GridColumn--default--12">
<div class="dr-article-content__section" id="adapting-our-relevance-judge-for-large-scale-use">
    <h2 class="dr-article-content__section-title">Adapting our relevance judge for large-scale use</h2>
</div>
</div>
<div class="text parbase aem-GridColumn aem-GridColumn--default--12">
<p>Our best-performing relevance judge was built on the most powerful proprietary model at the time (<a href="https://openai.com/index/introducing-o3-and-o4-mini/" target="_blank">OpenAI’s o3</a>). It produced high-quality scores and aligned closely with human ratings, but it was expensive to run at scale. As Dash grew, we needed to score orders of magnitude more query–document pairs. Running the most expensive model for every judgment wasn’t sustainable. We wanted to move to a lower-cost, open-weight model that we could run at scale.</p>
<p>We chose <b><a href="https://huggingface.co/openai/gpt-oss-120b" target="_blank">gpt-oss-120b</a></b>, an open model that offered a strong balance between cost and performance. In simple terms, it was much cheaper to run, but still capable of following complex instructions. The problem was that our carefully tuned prompt for o3 did not transfer cleanly. When we applied it to the cheaper model, quality dropped under our evaluation metric. Manual prompt rewriting could eventually recover performance, but it would require weeks of iteration and regression chasing. Instead of starting over by hand, we used DSPy to systematically adapt the judge to the new model.</p>
<h3>How DSPy helped us adapt the judge</h3>
<p>We already had everything needed to define the problem clearly. The task was fixed: given a query and a document, assign a relevance score from 1 to 5. The dataset was fixed: human-annotated examples with ratings and explanations. And the metric was fixed: NMSE, which measures how far the model’s ratings deviate from human ratings.</p>
<p>DSPy allows you to define that setup—task, data, and metric—and then systematically search for prompt variants that improve performance on that metric. We used DSPy’s GEPA optimizer (a method that iteratively improves prompts by analyzing where the model disagrees with humans and generating feedback) to adapt and optimize the relevance-judging program for a specific target model—in this case, gpt-oss-120b.</p>
<p>Rather than treating evaluation as a single score, GEPA generates structured feedback for each example where the model disagrees with a human annotator. In our case, we combined the size and direction of the gap with the human explanation and the model’s reasoning, producing concrete signals about what went wrong and why.</p>
<p>This feedback powers the DSPy reflection loop. The prompt is evaluated, its failure modes are surfaced in plain language, the prompt is revised, and the cycle repeats—all while directly optimizing against the human-alignment metric defined earlier. Instead of trying to infer improvements from a single number, the system can respond to specific patterns, such as underweighting recency relative to the human explanation or overvaluing keyword matches. To make this more concrete, here is a simplified version of how we construct that textual feedback:</p>

</div>
<div class="dr-code-container aem-GridColumn aem-GridColumn--default--12">




<div class="dr-code-container--title"></div>
<div class="dr-code-container-inner">

    <button class="dr-code-container__copy-button dr-button dr-typography-t17">
        Copy
    </button>
    <pre class="dr-code-container__pre"><code class="dr-code-container__code dr-typography-t5 python">diff = predicted_rating - expected_rating
direction = "higher" if diff > 0 else "lower"
feedback_parts = [
    f"Predicted rating {int(predicted_rating)} but expected {int(expected_rating)}.",
    f"Model rated {abs(diff):.0f} point(s) {direction} than the expected human rating.",
]

# Include human explanation if available
if gold.explanation:
    feedback_parts.append(f"Human rationale: {gold.explanation}")

# Include model's explanation for comparison
if pred.explanation:
    feedback_parts.append(f"Model's reasoning: {pred.explanation}")

feedback_parts.append(
    "Remember: when adapting the prompt, avoid overfitting to specific 
example(s). Do not include exact examples or keywords from them in the prompt. 
Also ensure you do not change the basic parameters of the task (e.g. changing the 
rating range to be anything but 1-5). Try to add a general rule to an execution 
plan to rate similar documents in the future."
)

feedback = "\n".join(feedback_parts)</code></pre>


</div>
<div class="dr-code-container-rte"></div>
</div>
<div class="text parbase aem-GridColumn aem-GridColumn--default--12">
<p>There were important caveats. In early experiments, we observed that the optimizer could overfit by copying specific keywords, usernames, or verbatim document phrases directly into prompts. That behavior improved performance on the training examples but did not generalize. To address this, we added explicit guardrails to forbid direct inclusion of example-specific content. We also found that candidate prompts sometimes modified key task parameters, such as changing the rating scale from 1–5 to 1–3 or 1–4. Additional constraints ensured that the task definition remained stable throughout optimization.</p>
<p>With this setup in place, we could move beyond intuition and measure the impact directly. Because the task, dataset, and metric were fixed, we could compare the optimized prompt to our original manually tuned prompt under identical conditions. That gave us a clear view of what changed and by how much.</p>

</div>
<div class="image c04-image aem-GridColumn aem-GridColumn--default--12">
<div class="dr-image image cq-dd-image  ">
    <figure class="dr-margin-0 dr-display-inline-block">
        
            
    

        

        
        
        

        
        
        

        <!--optimized image webp-->
        

        
        <!-- <img data-sly-test.highRes="false"
             srcset="/cms/content/dam/dropbox/tech-blog/en-us/2026/march/dspy/diagrams/Diagram%201.png 2x,  1x"
             src="/cms/content/dam/dropbox/tech-blog/en-us/2026/march/dspy/diagrams/Diagram%201.png"
             aria-hidden=""
             alt=""
             class=""
             data-sly-attribute.width="2880"
             data-sly-attribute.height="1800"
             data-aem-asset-id="304f6a81-e9ea-4960-ac52-5944ee0a78ac:Diagram 1.png"
             data-trackable="true" />
        <img data-sly-test.highRes="false"
             srcset="/cms/content/dam/dropbox/tech-blog/en-us/2026/march/dspy/diagrams/Diagram%201.png 2x,  1x"
             src="/cms/content/dam/dropbox/tech-blog/en-us/2026/march/dspy/diagrams/Diagram%201.png"
             aria-hidden=""
             alt=""
             class=""
             data-sly-attribute.width="2880"
             data-sly-attribute.height="1800"
             data-aem-asset-id="304f6a81-e9ea-4960-ac52-5944ee0a78ac:Diagram 1.png"
             data-trackable="true" /> -->

        
         
        <img src="/cms/content/dam/dropbox/tech-blog/en-us/2026/march/dspy/diagrams/Diagram%201.png/_jcr_content/renditions/Diagram%201.webp" fallbackimage="/cms/content/dam/dropbox/tech-blog/en-us/2026/march/dspy/diagrams/Diagram%201.png" onerror="window.failedAttempts=0;this.setAttribute('src',this.getAttribute('fallbackimage'));window.failedAttempts++;if(window.failedAttempts == 1)this.onerror=null" aria-hidden="false" alt="" data-aem-asset-id="304f6a81-e9ea-4960-ac52-5944ee0a78ac:Diagram 1.png" data-trackable="true" height="1800" width="2880"/>
    

            
        
    </figure>
</div></div>
<div class="text parbase aem-GridColumn aem-GridColumn--default--12">
<p>Comparing the best-performing DSPy-optimized prompt to the original manually written prompt, we reduced NMSE by 45 percent (from 8.83 to 4.86). That means the judge’s scores tracked human ratings much more closely, increasing our confidence in using it for evaluation and training signals. Model adaptation time dropped from one to two weeks of manual iteration to one to two days. That allowed us to swap in newly released models with less regression risk and keep the judge aligned with evolving product needs.</p>
<p>Because the optimized judge could run on a much cheaper model than our production o3 judge, we were also able to label 10–100 times more data at the same cost. That increased coverage and statistical power, enabled larger experiments, and reduced the risk of downstream models overfitting to a small evaluation set. Those results showed that DSPy could preserve quality while dramatically reducing cost. </p>
<p>However, optimizing for cost and human alignment still leaves an important question: can the judge behave reliably when its outputs are consumed programmatically in automated pipelines? In Dash, the relevance judge doesn’t run in isolation. It sits inside systems that score large candidate sets, generate training data, and run offline simulations. That means its outputs aren’t just read by people; they’re parsed and acted on by other components. This introduces a second requirement: operational reliability.</p>

</div>
<div class="section aem-GridColumn aem-GridColumn--default--12">
<div class="dr-article-content__section" id="improving-operational-reliability">
    <h2 class="dr-article-content__section-title">Improving operational reliability</h2>
</div>
</div>
<div class="text parbase aem-GridColumn aem-GridColumn--default--12">
<p>When we talk about judge quality, it’s easy to focus only on how closely the model’s scores match human ratings. But in practice, the judge also has to consistently produce JSON outputs that downstream systems can read and use. </p>
<p>To stress test this dimension of reliability, we introduced <a href="https://huggingface.co/google/gemma-3-12b-it" target="_blank"><b>gemma-3-12b</b></a>, a much smaller and cheaper model. Smaller models reduce cost and enable broader scaling, but they are more brittle about formatting and instruction-following. By adapting our judge to a significantly smaller model, we could measure and directly optimize what was effectively the system’s weakest link: whether a low-cost judge could produce valid, machine-readable outputs consistently enough to be usable in Dash’s pipelines.</p>
<p>In the baseline configuration, more than 40 percent of gemma-3-12b’s responses were malformed JSON. Under our evaluation rules, those responses were treated as fully incorrect. This meant that even before considering alignment with human ratings, the judge was unreliable from an operational standpoint. After DSPy optimization, malformed outputs dropped by more than 97 percent, and NMSE improved substantially:</p>

</div>
<div class="dmep-plank-frame-child c04-4-table-component aem-GridColumn aem-GridColumn--default--12">


    


<section class="table__wrapper dr-container--optimizing-dropbox-dash-relevance-judge-with-dspy dr-container--machine-learning  " data-component="table" data-component-instance="c21ffd0d-a15f-4e5d-848f-958004504a4c/content/responsivegrid/table">
  <div class="table__container">
    <div class="table__content">
          
<table width="100%" cellspacing="0" cellpadding="1" border="1">
<tbody><tr><th scope="row" width="25"><b>Version</b></th>
<th scope="row" width="25"><b>NMSE</b></th>
<th scope="row" width="25"><b>Valid Response Format</b></th>
<th scope="row" width="25"><b>Invalid Response Format</b></th>
</tr><tr><td>Original Prompt (Baseline)</td>
<td>46.88</td>
<td>498</td>
<td>358</td>
</tr><tr><td>DSPy prompt (MIPROv2)</td>
<td>17.26</td>
<td>847</td>
<td>9</td>
</tr></tbody></table>


    </div>
  </div>
</section></div>
<div class="text parbase aem-GridColumn aem-GridColumn--default--12">
<p>This result showed that DSPy was not only improving alignment with human judgments, but <i>also</i> strengthening structural reliability. Even a smaller, weaker model could become operationally dependable when optimized against the right objective.</p>
<p>At the same time, this experiment reinforced another benefit of the approach: iteration speed. Although gemma-3-12b was ultimately too weak for our highest-quality production judge paths, DSPy allowed us to reach that conclusion quickly and with measurable evidence. Instead of prolonged debate or manual trial and error, we could test the model directly against our evaluation framework and make a confident decision.</p>

</div>
<div class="section aem-GridColumn aem-GridColumn--default--12">
<div class="dr-article-content__section" id="incrementally-improving-our-o3-model">
    <h2 class="dr-article-content__section-title">Incrementally improving our o3 model</h2>
</div>
</div>
<div class="text parbase aem-GridColumn aem-GridColumn--default--12">
<p>One finding emerged across our explorations: DSPy let us control the scope of changes, from small prompt edits to broader adjustments. When adapting to a new, cheaper model (like gpt-oss-120b or gemma-3-12b), we were comfortable with full prompt rewrites, prioritizing broad exploration and end-to-end optimization. But when the target was our production o3 judge—already strong and widely depended on—the constraint flipped. Our goal was to make targeted improvements without destabilizing behavior relied on across multiple pipelines.</p>
<p>When it came to optimizing the o3-based judge, we weren’t starting from scratch. We already had a high-performing baseline. Large prompt rewrites were too risky; even small wording changes could shift behavior in corner cases, and the blast radius was high. So instead of rewriting the prompt end-to-end, we limited changes to a small, predefined set of safe edits.</p>
<p>We introduced an instruction library layer to make prompt improvement more targeted and easier to control. When we found cases where the judge’s score differed substantially from the human rating, humans wrote short explanations describing what the judge misunderstood and what it should have paid attention to instead. We then distilled those explanations into single-line instruction bullets, or small, reusable “rules of thumb” the model can follow. In this setup, the optimization module is responsible only for selecting the best bullet-instructions. DSPy can’t rewrite the entire prompt from scratch; instead, its job is to choose which instruction bullets to include (e.g. select common themes of errors), and how to combine them, so the prompt grows by assembling the most helpful additional guidance rather than be constantly rewritten.</p>
<p>This turned optimization into something closer to “small PRs with tests” than a large-scale refactor: improvements were incremental, regressions were easier to diagnose, and we could keep the baseline behavior stable while still pushing agreement upward.</p>
<p>For example, if a disagreement was explained as “the document is older than a year, so it’s less relevant for this query,” we translated that into a bullet like: “Documents older than a year should be rated at least one point lower unless they are clearly evergreen.” DSPy could then learn whether including that bullet improved alignment on the eval set without unintended side effects.</p>
<p>We can see the cumulative effect of these incremental changes in the evaluation results below:</p>

</div>
<div class="image c04-image aem-GridColumn aem-GridColumn--default--12">
<div class="dr-image image cq-dd-image  ">
    <figure class="dr-margin-0 dr-display-inline-block">
        
            
    

        

        
        
        

        
        
        

        <!--optimized image webp-->
        

        
        <!-- <img data-sly-test.highRes="false"
             srcset="/cms/content/dam/dropbox/tech-blog/en-us/2026/march/dspy/diagrams/Diagram%202.png 2x,  1x"
             src="/cms/content/dam/dropbox/tech-blog/en-us/2026/march/dspy/diagrams/Diagram%202.png"
             aria-hidden=""
             alt=""
             class=""
             data-sly-attribute.width="2880"
             data-sly-attribute.height="1800"
             data-aem-asset-id="cda51445-ce53-4c7f-8f48-8df318aa60b0:Diagram 2.png"
             data-trackable="true" />
        <img data-sly-test.highRes="false"
             srcset="/cms/content/dam/dropbox/tech-blog/en-us/2026/march/dspy/diagrams/Diagram%202.png 2x,  1x"
             src="/cms/content/dam/dropbox/tech-blog/en-us/2026/march/dspy/diagrams/Diagram%202.png"
             aria-hidden=""
             alt=""
             class=""
             data-sly-attribute.width="2880"
             data-sly-attribute.height="1800"
             data-aem-asset-id="cda51445-ce53-4c7f-8f48-8df318aa60b0:Diagram 2.png"
             data-trackable="true" /> -->

        
         
        <img src="/cms/content/dam/dropbox/tech-blog/en-us/2026/march/dspy/diagrams/Diagram%202.png/_jcr_content/renditions/Diagram%202.webp" fallbackimage="/cms/content/dam/dropbox/tech-blog/en-us/2026/march/dspy/diagrams/Diagram%202.png" onerror="window.failedAttempts=0;this.setAttribute('src',this.getAttribute('fallbackimage'));window.failedAttempts++;if(window.failedAttempts == 1)this.onerror=null" aria-hidden="false" alt="" data-aem-asset-id="cda51445-ce53-4c7f-8f48-8df318aa60b0:Diagram 2.png" data-trackable="true" height="1800" width="2880"/>
    

            
        
    </figure>
</div></div>
<div class="text parbase aem-GridColumn aem-GridColumn--default--12">
<p>Each step represents a small, testable change, but together they produce a substantial improvement over the initial prompt.</p>

</div>
<div class="section aem-GridColumn aem-GridColumn--default--12">
<div class="dr-article-content__section" id="conclusion">
    <h2 class="dr-article-content__section-title">Conclusion</h2>
</div>
</div>
<div class="text parbase aem-GridColumn aem-GridColumn--default--12">
<p>In Dash, relevance scoring is a core capability that shapes ranking, training data generation, and offline simulation. Because it sits at the center of multiple pipelines, even small changes in how we score relevance can ripple outward. If every new model or prompting idea requires manual prompt surgery, progress becomes slow and risky.</p>
<p>With DSPy, we define the objective—alignment with human relevance judgments—and systematically optimize toward it. With the task and dataset held fixed, we can swap in new models and adapt them quickly, with measurable evidence instead of intuition. The workflow becomes less about rewriting prompts and more about improving against a clear metric. Just as importantly, DSPy lets us choose how to improve depending on our risk tolerance. We can run full end-to-end optimization when exploring new, cheaper models, or apply constrained, incremental updates when stability matters for production systems like o3.</p>
<p>In a system like Dash, where relevance scoring touches ranking, training data generation, offline simulation, and cost–latency tradeoffs, prompt optimization can’t be a one-off effort. DSPy turns it into a repeatable loop: define the task, measure against human labels, optimize, and ship changes with confidence as models evolve.</p>
<p><i>Acknowledgments: This work was made possible by close collaboration across Dropbox. We’d like to thank Eider Moore, Mingming Liu, Stella Xiang, Sean Chang, Prasang Upadhyaya, Hans Sayyadi, and Josh Clemm for their thoughtful reviews, technical feedback, and help shaping both the system and the story.</i></p>
<p><i>We’re also grateful to the DSPy community for their engagement and support. In particular, we‘d like to thank Isaac Miller, Drew Breunig, Lakshya A. Agrawal, and Omar Khattab for their guidance, discussions, and responsiveness as we applied DSPy to real production systems at Dropbox. </i></p>
<p><i>Dropbox hosted a Bay Area DSPy Meetup at our San Francisco office on Wednesday, March 18, 2026, bringing together developers building real-world, in-production AI systems. Dropbox engineers shared how we’re using LLM judges and DSPy to optimize prompts and improve reliability in production. Head <a href="https://www.dropbox.com/scl/fi/cnqquakbkw68rxz461apm/Optimizing-LLM-Relevance-Judges-at-Scale.pdf?rlkey=pgu56mg1tlgv5pdx0xd30p8wk&e=1" target="_blank">here to view our presentation</a> from the event.</i></p>
<p style="text-align: center;">~ ~ ~</p>
<p><i>If building innovative products, experiences, and infrastructure excites you, come build the future with us! Visit </i><a href="https://jobs.dropbox.com/" target="_blank"><i>jobs.dropbox.com</i></a><i> to see our open roles.</i></p>

</div>

    
</div>
]]></content:encoded>
            			
                <media:thumbnail url="https://dropbox.tech/cms/content/dam/dropbox/tech-blog/en-us/2026/march/dspy/headers/Machine Learning-Optimizing Dash relevancy judge with DSPy-375x150-dark.png" />
                <media:content url="https://dropbox.tech/cms/content/dam/dropbox/tech-blog/en-us/2026/march/dspy/headers/Machine Learning-Optimizing Dash relevancy judge with DSPy-375x150-dark.png" medium="image">
                    <media:title type="html">How we optimized Dash&#039;s relevance judge with DSPy</media:title>
                </media:content>
       		 </item>
                    
        				<item>
                        <title>Using LLMs to amplify human labeling and improve Dash search relevance</title>
                        
            			<link>https://dropbox.tech/machine-learning/llm-human-labeling-improving-search-relevance-dropbox-dash</link>

                            
            			<dc:creator>
                            Facundo Agriel,Ishan Mishra,Eric Wang,Dmitriy Meyerzon,Dmitriy Meyerzon
            			</dc:creator>
            			
            				<category>LLM</category>
							
            				<category>models</category>
							
            				<category>Search</category>
							
            				<category>Machine Learning</category>
							
            				<category>Dash</category>
							
            				<category>RAG</category>
							
                            
            			<description><![CDATA[How we train Dash's search ranking models with a mix of human and LLM-assisted labeling.]]></description>
            			<guid>https://dropbox.tech/machine-learning/llm-human-labeling-improving-search-relevance-dropbox-dash</guid>
                        <pubDate>Thu, 26 Feb 2026 09:00:00 -0800</pubDate>
                            
                        <content:encoded><![CDATA[


<div class="aem-Grid aem-Grid--12 aem-Grid--default--12 ">
    
    <div class="text parbase aem-GridColumn aem-GridColumn--default--12">
<p>When someone uses <b><a href="https://www.dash.dropbox.com/" target="_blank">Dropbox Dash</a></b> to search or ask a question, it follows a retrieval-augmented generation (RAG) pattern. This means our AI first retrieves relevant company information and then uses that information to generate responses. To produce those answers, it relies on enterprise search to retrieve company-specific context and then uses that context to ground the response. Rather than responding solely from general knowledge, Dash incorporates information that already exists within an organization.</p>
<p>When a user submits a query, Dash first interprets the underlying information need and determines how to retrieve relevant content. Search returns a set of candidate documents, and a large language model (LLM) analyzes the most relevant results to generate an answer. Because there are millions (and, in very large enterprises, billions) of documents in the enterprise search index, Dash can pass along only a small subset of the retrieved documents to the LLM. This makes the quality of search ranking—and the <b>labeled relevance</b> <b>data</b> used to train it—critical to the quality of the final answer. </p>
<p>Search results in Dash are ordered by a relevance model that assigns a score to each document based on how well it matches the query. Like most modern ranking systems, this model is trained rather than hand-tuned. It learns from examples of queries paired with documents, annotated with human relevance judgments that define what high-quality search results look like. These judgments are labeled examples in which people evaluate how well a document answers a given query.</p>
<p>In this story, we explain how we train Dash's search ranking models with a mix of <b>human and LLM-assisted labeling</b>—starting with a small amount of internal, human-labeled data, and then amplifying those efforts with LLMs to produce relevance labels at scale.</p>

</div>
<div class="experiencefragment aem-GridColumn aem-GridColumn--default--12">
<div id="experiencefragment-728b5aba97" class="cmp-experiencefragment cmp-experiencefragment--dash-cta-for-tech-blog">

    



<div class="xf-content-height">
    


<div class="aem-Grid aem-Grid--12 aem-Grid--default--12 ">
    
    <div class="responsivegrid aem-GridColumn aem-GridColumn--default--12">


<div class="aem-Grid aem-Grid--12 aem-Grid--default--12 ">
    
    <div class="a08-html-embed c17-plain-html aem-GridColumn aem-GridColumn--default--12">

<style type="text/css"> 

@font-face {
font-family: 'AtlasGrotesk';
src: url('https://cdn.prod.website-files.com/65dcd70b48edc3a7b446950e/65dce019c63112e617513c94_AtlasGrotesk-Medium-Web-vfl38XiTL.woff2') format('woff2');
font-weight: 500;
font-style: normal;
font-display: swap;
}

@font-face {
font-family: 'AtlasGrotesk';
src: url('https://cdn.prod.website-files.com/65dcd70b48edc3a7b446950e/65dce019711b648fd1ccd24a_AtlasGrotesk-Regular-Web-vflk7bxjs.woff2') format('woff2');
font-weight: 400;
font-style: normal;
font-display: swap;
}
.xf-content-height {margin: 0;}
#cta { font-family: AtlasGrotesk,sans-serif; font-size: .900rem; text-decoration: none; background: #f7f5f2; line-height: 1.69; box-sizing: border-box;}
#cta-box { padding: 15px 20px 15px 20px; }
#cta-hed {font-weight: 500;}
#cta-indent {border-left: 5px solid #1e1919; padding-left:20px;}
#cta a:link, #cta a:visited  {text-decoration: none;}
#cta p { margin: 5px 0px 0px 0px; }

.dr-theme-dark #cta {background: #000;}
.dr-theme-dark #cta-box {border: 1px solid; border-bottom: 0;}
.dr-theme-dark #cta-indent {border-left: 5px solid #f7f5f2;}
.dr-theme-dark .button {background: #000;}

.button {
    background-color: #1e1919;
    color:  #f7f5f2;
    height: 2.5rem;
    padding: 10px 5px 10px 20px;
    font-size: 1rem;
    font-weight: 500;
    line-height: 1.2;
    transition: all .3s;
}

.button:hover { background-color: #0061ff; }

img {vertical-align: middle; padding: 0px 1px 2px 0px;}

.c17-plain-html {margin-bottom: 50px}

</style>

<div id="cta">
<div id="cta-box">
<div id="cta-indent">

<p id="cta-hed"><img src="https://cdn.prod.website-files.com/65dcd70b48edc3a7b446950e/670692ee7692f74d4834e4f4_Frame%201400006055.svg" loading="lazy"> Dropbox Dash: AI that understands your work
</p>

<p>Dash knows your context, your team, and your work, so your team can stay organized, easily find and share knowledge, and keep projects secure, all from one place. And soon, Dash is coming to Dropbox.</p>

</div>
</div>

<a href="https://dash.dropbox.com/?utm=blogs" target="_blank"><div class="button">Learn more →</div></a>

</div>
</div>

    
</div>
</div>

    
</div>

</div></div>

    
</div>
<div class="section aem-GridColumn aem-GridColumn--default--12">
<div class="dr-article-content__section" id="dash-search-relevance-models">
    <h2 class="dr-article-content__section-title">Dash search relevance models</h2>
</div>
</div>
<div class="text parbase aem-GridColumn aem-GridColumn--default--12">
<p>Dash’s ranking model is trained using machine learning techniques such as XGBoost rather than manually tuned rules. It learns from labeled examples of query–document pairs, where each document is evaluated based on how well it satisfies a given query. Over time, the model adjusts how it weighs different signals to reduce ranking mistakes (for example, cases where less useful documents are placed ahead of more useful ones). This framing leads directly to a core challenge: generating enough high-quality relevance labels to train the model effectively.</p>
<h3>Where relevance labels come from</h3>
<p>Training a relevance model requires examples that show what “good” and “bad” search results look like. In practice, those relevance labels can be created in several ways. One approach infers relevance from user behavior, such as clicks or skipped results. Another relies on humans manually assigning relevance scores to query and document pairs. And a third approach uses LLMs to generate relevance judgments directly.</p>
<p>This story focuses on the latter two approaches: direct <b>human labeling</b> and <b>LLM-based evaluation</b>. Signals from user behavior can still be helpful, but on their own they tend to be incomplete, influenced by existing rankings, and unevenly distributed. In practice, they work best as a supplement to labeled data rather than a replacement for it.</p>
<p>For the purposes of this article, relevance is treated as a graded score on a 1–5 scale. A score of 5 means the result closely matches what the user is trying to find, while a score of 1 means it isn’t useful enough to show. Importantly, relevance isn’t a fixed property of a document; it depends on the specific query, the user’s context, and the moment the search is made. </p>
<p><b>Human labeling<br />
 </b>Historically, search engine providers relied on teams of human judges, typically third-party vendors, to label large datasets for model training. This approach had clear advantages. Human judges could systematically evaluate full result sets for each query, ensuring comprehensive and consistent relevance coverage in a way that user feedback—which is often sparse and biased—cannot.</p>
<p>However, the drawbacks are substantial. Human labeling is expensive and difficult to scale. Judges can be inconsistent and require ongoing training. In practice, it is also nearly impossible for humans to directly evaluate sensitive or proprietary customer data. Human evaluators also require training, which is made more difficult by the diversity of types of content that need to be rated (for example, comparing a Slack message to a Jira ticket or a Salesforce contact record can require very different contextual understanding and judgment).</p>
<p><b>LLM evaluation<br />
 </b>LLMs provide an alternative mechanism for producing relevance judgments at scale. Compared to human annotators, LLMs are significantly cheaper, more consistent, and capable of evaluating much larger candidate sets across languages. They can also analyze customer content within defined compliance boundaries.</p>
<p>At the same time, LLMs are not general intelligence systems. Their performance depends heavily on both the <i>quality</i> of the underlying model and the <i>clarity</i> and precision of the instructions provided. As a result, LLM-generated relevance judgments must be evaluated and calibrated carefully before they are used for training. In practice, using LLMs for relevance evaluation requires a structured process that combines automation with human oversight.</p>

</div>
<div class="section aem-GridColumn aem-GridColumn--default--12">
<div class="dr-article-content__section" id="combining-llm-evaluation-with-human-review">
    <h2 class="dr-article-content__section-title">Combining LLM evaluation with human review</h2>
</div>
</div>
<div class="text parbase aem-GridColumn aem-GridColumn--default--12">
<p>To scale relevance evaluation without sacrificing quality, Dash pairs automation with human judgment. Before deploying an LLM to generate relevance labels at scale, its performance is validated against a small, high-quality set of human-labeled examples. (Human review is conducted by Dropbox with limited, non-sensitive internal datasets; no customer data is reviewed by humans as part of this process.)</p>
<p>A small group of human evaluators labels a dataset that is orders of magnitude smaller than what would be required for full training. These labels are used to tune the LLM prompt and model parameters. Once performance meets quality thresholds, the LLM is deployed to generate hundreds of thousands—or even millions—of relevance labels used to train Dash’s relevance model. In this setup, the LLM acts as a force multiplier for human effort: humans teach the LLM, and the LLM generates large-scale training data in return.</p>

</div>
<div class="image c04-image aem-GridColumn aem-GridColumn--default--12">
<div class="dr-image image cq-dd-image  ">
    <figure class="dr-margin-0 dr-display-inline-block">
        
            
    

        

        
        
        

        
        
        

        <!--optimized image webp-->
        

        
        <!-- <img data-sly-test.highRes="false"
             srcset="/cms/content/dam/dropbox/tech-blog/en-us/2026/february/llm-and-human-labeling/diagrams/Diagram%201.png 2x,  1x"
             src="/cms/content/dam/dropbox/tech-blog/en-us/2026/february/llm-and-human-labeling/diagrams/Diagram%201.png"
             aria-hidden=""
             alt=""
             class=""
             data-sly-attribute.width="2880"
             data-sly-attribute.height="1840"
             data-aem-asset-id="ec0fc622-af57-4b8b-9d2f-48db1fbee62e:Diagram 1.png"
             data-trackable="true" />
        <img data-sly-test.highRes="false"
             srcset="/cms/content/dam/dropbox/tech-blog/en-us/2026/february/llm-and-human-labeling/diagrams/Diagram%201.png 2x,  1x"
             src="/cms/content/dam/dropbox/tech-blog/en-us/2026/february/llm-and-human-labeling/diagrams/Diagram%201.png"
             aria-hidden=""
             alt=""
             class=""
             data-sly-attribute.width="2880"
             data-sly-attribute.height="1840"
             data-aem-asset-id="ec0fc622-af57-4b8b-9d2f-48db1fbee62e:Diagram 1.png"
             data-trackable="true" /> -->

        
         
        <img src="/cms/content/dam/dropbox/tech-blog/en-us/2026/february/llm-and-human-labeling/diagrams/Diagram%201.png/_jcr_content/renditions/Diagram%201.webp" fallbackimage="/cms/content/dam/dropbox/tech-blog/en-us/2026/february/llm-and-human-labeling/diagrams/Diagram%201.png" onerror="window.failedAttempts=0;this.setAttribute('src',this.getAttribute('fallbackimage'));window.failedAttempts++;if(window.failedAttempts == 1)this.onerror=null" aria-hidden="false" alt="" data-aem-asset-id="ec0fc622-af57-4b8b-9d2f-48db1fbee62e:Diagram 1.png" data-trackable="true" height="1840" width="2880"/>
    

            <figcaption class="dr-typography-t5 dr-color-ink-60 dr-image-rte"><p style="text-align: center;">Human labeling effort is multiplied 100x to allow deeper and more representative training datasets</p>
</figcaption>
        
    </figure>
</div></div>
<div class="text parbase aem-GridColumn aem-GridColumn--default--12">
<p>Using LLMs directly at query time to replace traditional ranking models is not currently feasible due to context window limitations and latency constraints. Instead, Dash uses LLMs offline to generate high-quality training data. In this role, the LLM functions as a teacher for smaller, more efficient relevance models that can operate at production scale.</p>
<h3>Evaluating LLM relevance judgments</h3>
<p>Improvement always starts with evaluation. Performance is measured, a change is made to the model or its instructions, and results are measured again to determine whether the system is moving in the right direction.</p>
<p>A chess engine operates under the same principle. As Garry Kasparov describes in <i>Deep Thinking</i>, reflecting on his historic match against IBM’s Deep Blue, the engine explores possible move sequences to a fixed depth and evaluates each resulting position. Poor evaluations prune entire branches of the search tree, while strong evaluations preserve promising lines of play. The overall strength of the system depends critically on the quality of the evaluation function.</p>
<p>Gradient-based optimization follows a similar pattern. Rather than enumerating the entire parameter space, machine learning algorithms compute a gradient and take incremental steps in the direction it indicates. Progress depends entirely on whether the evaluation signal accurately reflects improvement.</p>
<p>For an LLM acting as a relevance judge, evaluation follows the same logic. Dash compares LLM-generated relevance ratings with human judgments, rewarding exact matches on a 1–5 relevance scale and applying penalties for disagreement. Small differences incur small penalties, while large mismatches incur substantially larger ones. This behavior is captured using mean squared error (MSE), where the error ranges from 0 for exact agreement to 16 for the maximum possible disagreement.</p>

</div>
<div class="image c04-image aem-GridColumn aem-GridColumn--default--12">
<div class="dr-image image cq-dd-image  ">
    <figure class="dr-margin-0 dr-display-inline-block">
        
            
    

        

        
        
        

        
        
        

        <!--optimized image webp-->
        

        
        <!-- <img data-sly-test.highRes="false"
             srcset="/cms/content/dam/dropbox/tech-blog/en-us/2026/february/llm-and-human-labeling/diagrams/Diagram%202.png 2x,  1x"
             src="/cms/content/dam/dropbox/tech-blog/en-us/2026/february/llm-and-human-labeling/diagrams/Diagram%202.png"
             aria-hidden=""
             alt=""
             class=""
             data-sly-attribute.width="2880"
             data-sly-attribute.height="1840"
             data-aem-asset-id="36f51d7c-6541-4f9f-b8e9-5431fd7579a3:Diagram 2.png"
             data-trackable="true" />
        <img data-sly-test.highRes="false"
             srcset="/cms/content/dam/dropbox/tech-blog/en-us/2026/february/llm-and-human-labeling/diagrams/Diagram%202.png 2x,  1x"
             src="/cms/content/dam/dropbox/tech-blog/en-us/2026/february/llm-and-human-labeling/diagrams/Diagram%202.png"
             aria-hidden=""
             alt=""
             class=""
             data-sly-attribute.width="2880"
             data-sly-attribute.height="1840"
             data-aem-asset-id="36f51d7c-6541-4f9f-b8e9-5431fd7579a3:Diagram 2.png"
             data-trackable="true" /> -->

        
         
        <img src="/cms/content/dam/dropbox/tech-blog/en-us/2026/february/llm-and-human-labeling/diagrams/Diagram%202.png/_jcr_content/renditions/Diagram%202.webp" fallbackimage="/cms/content/dam/dropbox/tech-blog/en-us/2026/february/llm-and-human-labeling/diagrams/Diagram%202.png" onerror="window.failedAttempts=0;this.setAttribute('src',this.getAttribute('fallbackimage'));window.failedAttempts++;if(window.failedAttempts == 1)this.onerror=null" aria-hidden="false" alt="" data-aem-asset-id="36f51d7c-6541-4f9f-b8e9-5431fd7579a3:Diagram 2.png" data-trackable="true" height="1840" width="2880"/>
    

            
        
    </figure>
</div></div>
<div class="text parbase aem-GridColumn aem-GridColumn--default--12">
<h3>Document sampling for LLM evaluation</h3>
<p>At scale, not all evaluation data is equally informative. To improve LLM accuracy efficiently, Dash focuses evaluation effort on the cases most likely to surface errors. Training samples are biased toward situations where mistakes are more likely, since these offer the greatest opportunity for learning. Dash identifies such cases by analyzing discrepancies between user behavior and LLM-predicted relevance.</p>
<p>Examples include users clicking on documents the LLM rated as low relevance, or consistently skipping documents the LLM rated as highly relevant. These discrepancies are prioritized for human review and prompt refinement. (These processes are, again, limited to small internal datasets that do not include customer data.) The process is repeated iteratively until major sources of error are addressed or improvements plateau.</p>

</div>
<div class="section aem-GridColumn aem-GridColumn--default--12">
<div class="dr-article-content__section" id="evaluating-relevance-with-additional-context">
    <h2 class="dr-article-content__section-title">Evaluating relevance with additional context</h2>
</div>
</div>
<div class="text parbase aem-GridColumn aem-GridColumn--default--12">
<p>Accurate relevance evaluation often depends on context that isn’t explicitly present in the query or document text. Without this context, even well-trained models can make systematic errors. In many cases, a query and a document alone are insufficient to make a reliable relevance judgment. Additional context about internal terminology, acronyms, or organizational knowledge may be required.</p>
<p>For example, within Dropbox, the term “diet sprite” refers to an internal performance management tool rather than a soft drink, a distinction that can be difficult for LLMs to infer without additional context. Acronyms present similar challenges, as they often have multiple meanings across organizations or even within the same company. Human evaluators typically resolve this ambiguity by running additional searches or consulting internal tools.</p>
<p>To automate this process, Dash provides LLMs with tools that allow them to research query context <i>before</i> assigning relevance labels. Once the LLM understands the user’s intent, it can apply consistent, context-aware relevance labeling across large candidate result sets, often going deeper than human evaluators would in practice.</p>

</div>
<div class="image c04-image aem-GridColumn aem-GridColumn--default--12">
<div class="dr-image image cq-dd-image  ">
    <figure class="dr-margin-0 dr-display-inline-block">
        
            
    

        

        
        
        

        
        
        

        <!--optimized image webp-->
        

        
        <!-- <img data-sly-test.highRes="false"
             srcset="/cms/content/dam/dropbox/tech-blog/en-us/2026/february/llm-and-human-labeling/diagrams/Diagram%203.png 2x,  1x"
             src="/cms/content/dam/dropbox/tech-blog/en-us/2026/february/llm-and-human-labeling/diagrams/Diagram%203.png"
             aria-hidden=""
             alt=""
             class=""
             data-sly-attribute.width="2880"
             data-sly-attribute.height="1840"
             data-aem-asset-id="1ea348aa-d079-4f1c-b34c-241ddf4e704e:Diagram 3.png"
             data-trackable="true" />
        <img data-sly-test.highRes="false"
             srcset="/cms/content/dam/dropbox/tech-blog/en-us/2026/february/llm-and-human-labeling/diagrams/Diagram%203.png 2x,  1x"
             src="/cms/content/dam/dropbox/tech-blog/en-us/2026/february/llm-and-human-labeling/diagrams/Diagram%203.png"
             aria-hidden=""
             alt=""
             class=""
             data-sly-attribute.width="2880"
             data-sly-attribute.height="1840"
             data-aem-asset-id="1ea348aa-d079-4f1c-b34c-241ddf4e704e:Diagram 3.png"
             data-trackable="true" /> -->

        
         
        <img src="/cms/content/dam/dropbox/tech-blog/en-us/2026/february/llm-and-human-labeling/diagrams/Diagram%203.png/_jcr_content/renditions/Diagram%203.webp" fallbackimage="/cms/content/dam/dropbox/tech-blog/en-us/2026/february/llm-and-human-labeling/diagrams/Diagram%203.png" onerror="window.failedAttempts=0;this.setAttribute('src',this.getAttribute('fallbackimage'));window.failedAttempts++;if(window.failedAttempts == 1)this.onerror=null" aria-hidden="false" alt="" data-aem-asset-id="1ea348aa-d079-4f1c-b34c-241ddf4e704e:Diagram 3.png" data-trackable="true" height="1840" width="2880"/>
    

            
        
    </figure>
</div></div>
<div class="text parbase aem-GridColumn aem-GridColumn--default--12">
<h3>Prompt optimization</h3>
<p>As evaluation scales, prompt quality starts to matter much more. Prompt optimization ends up looking a lot like how human guidelines are developed: You review cases where the model gets relevance wrong, adjust the instructions or add missing context, and then test again. This is harder than it sounds. Small prompt changes can cause unexpected regressions, and consistency becomes harder to maintain as prompts grow longer and more complex.</p>
<p>Meta-prompting frameworks such as <a href="https://dspy.ai/" target="_blank">DSPy</a> can help manage this complexity. (DSPy is a library for programmatically optimizing LLM prompts against defined evaluation targets.) Given a clear objective and a small set of human-labeled examples, DSPy can automatically refine prompts to better match human judgments. This makes it possible to reuse the same optimization approach across different evaluation tasks and model configurations, rather than treating each case as a one-off.</p>
<p>The chart below shows how the mean squared error (MSE) for the LLM-based relevance evaluator improved over time, driven by prompt refinement, the use of a reasoning-optimized model, incorporation of query context, and automated optimization with DSPy.</p>

</div>
<div class="image c04-image aem-GridColumn aem-GridColumn--default--12">
<div class="dr-image image cq-dd-image  ">
    <figure class="dr-margin-0 dr-display-inline-block">
        
            
    

        

        
        
        

        
        
        

        <!--optimized image webp-->
        

        
        <!-- <img data-sly-test.highRes="false"
             srcset="/cms/content/dam/dropbox/tech-blog/en-us/2026/february/llm-and-human-labeling/diagrams/Diagram%204.png 2x,  1x"
             src="/cms/content/dam/dropbox/tech-blog/en-us/2026/february/llm-and-human-labeling/diagrams/Diagram%204.png"
             aria-hidden=""
             alt=""
             class=""
             data-sly-attribute.width="2880"
             data-sly-attribute.height="1800"
             data-aem-asset-id="b5655b90-f0a9-4e67-932b-c4126e700f94:Diagram 4.png"
             data-trackable="true" />
        <img data-sly-test.highRes="false"
             srcset="/cms/content/dam/dropbox/tech-blog/en-us/2026/february/llm-and-human-labeling/diagrams/Diagram%204.png 2x,  1x"
             src="/cms/content/dam/dropbox/tech-blog/en-us/2026/february/llm-and-human-labeling/diagrams/Diagram%204.png"
             aria-hidden=""
             alt=""
             class=""
             data-sly-attribute.width="2880"
             data-sly-attribute.height="1800"
             data-aem-asset-id="b5655b90-f0a9-4e67-932b-c4126e700f94:Diagram 4.png"
             data-trackable="true" /> -->

        
         
        <img src="/cms/content/dam/dropbox/tech-blog/en-us/2026/february/llm-and-human-labeling/diagrams/Diagram%204.png/_jcr_content/renditions/Diagram%204.webp" fallbackimage="/cms/content/dam/dropbox/tech-blog/en-us/2026/february/llm-and-human-labeling/diagrams/Diagram%204.png" onerror="window.failedAttempts=0;this.setAttribute('src',this.getAttribute('fallbackimage'));window.failedAttempts++;if(window.failedAttempts == 1)this.onerror=null" aria-hidden="false" alt="" data-aem-asset-id="b5655b90-f0a9-4e67-932b-c4126e700f94:Diagram 4.png" data-trackable="true" height="1800" width="2880"/>
    

            
        
    </figure>
</div></div>
<div class="section aem-GridColumn aem-GridColumn--default--12">
<div class="dr-article-content__section" id="conclusion">
    <h2 class="dr-article-content__section-title">Conclusion</h2>
</div>
</div>
<div class="text parbase aem-GridColumn aem-GridColumn--default--12">
<p>The relevance labeling approach described here is not limited to document search or tied to a specific model or evaluation framework. What matters is the underlying pattern: starting with a small amount of high-quality human judgment, using that judgment to calibrate LLM-based evaluation, and then scaling relevance labeling in a way that remains measurable, auditable, and correctable over time.</p>
<p>Because LLM-generated labels are grounded in human-reviewed reference data, they can be continuously monitored, stress-tested, and re-calibrated as models, prompts, and product requirements change. This grounding establishes a stable evaluation baseline that makes regressions detectable and improvements measurable, even as the surrounding system evolves.</p>
<p>As Dash expands to support additional content types—such as images, videos, messages, and chat—the evaluation problem becomes more complex. Each domain encodes relevance differently, and surface-level similarity is often insufficient. Human-calibrated LLM evaluation provides a shared mechanism for adapting relevance judgments across modalities without rebuilding labeling pipelines or redefining evaluation criteria from scratch.</p>
<p>Even as models improve, human grounding remains a structural requirement. Prompts drift, models change, and product expectations shift. A persistent, human-reviewed reference set anchors evaluation over time, allowing LLMs to scale judgment without eroding correctness. In short, LLMs make it possible to apply human judgment consistently and at scale, rather than replacing it.</p>
<p><i>Acknowledgments: Eric Wang, Hans Sayyadi, Josh Clemm, Mingming Liu, Andrew Yates, Marta Mendez, Jun Sun, Jay Frank, Angela Li</i></p>
<p style="text-align: center;">~ ~ ~</p>
<p><i>If building innovative products, experiences, and infrastructure excites you, come build the future with us! Visit </i><a href="https://jobs.dropbox.com/" target="_blank"><i>jobs.dropbox.com</i></a><i> to see our open roles.</i></p>

</div>

    
</div>
]]></content:encoded>
            			
                <media:thumbnail url="https://dropbox.tech/cms/content/dam/dropbox/tech-blog/en-us/2026/february/llm-and-human-labeling/headers/Machine Learning-Human Evaluations-1440x305-light.png" />
                <media:content url="https://dropbox.tech/cms/content/dam/dropbox/tech-blog/en-us/2026/february/llm-and-human-labeling/headers/Machine Learning-Human Evaluations-1440x305-light.png" medium="image">
                    <media:title type="html">Using LLMs to amplify human labeling and improve Dash search relevance</media:title>
                </media:content>
       		 </item>
                    
        				<item>
                        <title>How low-bit inference enables efficient AI</title>
                        
            			<link>https://dropbox.tech/machine-learning/how-low-bit-inference-enables-efficient-ai</link>

                            
            			<dc:creator>
                            Facundo Agriel,Ishan Mishra,Eric Wang,Dmitriy Meyerzon,Dmitriy Meyerzon,Hicham Badri,Appu Shaji
            			</dc:creator>
            			
            				<category>models</category>
							
            				<category>quantization</category>
							
            				<category>AI</category>
							
            				<category>Machine Learning</category>
							
            				<category>Dash</category>
							
            				<category>inference</category>
							
                            
            			<description><![CDATA[Making products like Dropbox Dash accessible to individuals and businesses means tackling new challenges around efficiency and resource use.]]></description>
            			<guid>https://dropbox.tech/machine-learning/how-low-bit-inference-enables-efficient-ai</guid>
                        <pubDate>Thu, 12 Feb 2026 10:00:00 -0800</pubDate>
                            
                        <content:encoded><![CDATA[


<div class="aem-Grid aem-Grid--12 aem-Grid--default--12 ">
    
    <div class="text parbase aem-GridColumn aem-GridColumn--default--12">
<p>In just the past few years, large machine learning models have made incredible strides. Today’s models are not only remarkably capable but also achieve impressive results across a range of applications, from software engineering and scientific research to content creation and data analysis. With the arrival of models like <a href="https://www.kimi.com/blog/kimi-k2-5.html" target="_blank">Kimi-K2.5</a> and <a href="https://z.ai/blog/glm-5" target="_blank">GLM-5</a>, the pace of progress shows no sign of slowing down. (Kimi-K2.5 has an impressive 1 trillion parameters, nearly twice as many as the DeepSeek V3 model family that was released just last year.) And as these models continue to grow in size and capability, so does the demand for memory, computing power, and energy.</p>
<p>One of the most effective ways teams are addressing these constraints is through low-bit inference, a set of techniques widely adopted across the industry that make AI models faster and cheaper to run by reducing how much memory and compute they need when serving real user requests. At Dropbox, products like <a href="https://www.dash.dropbox.com/" target="_blank">Dropbox Dash</a> rely on various models to deliver fast, reliable, and cost-effective AI-powered search and understanding across vast amounts of user content. Making this possible requires careful attention to model efficiency, hardware utilization, and latency constraints. And making this technology accessible to individuals and businesses means tackling new challenges around efficiency and resource use.</p>
<p>In this article, we’ll dive into the current landscape of low-bit compute for efficient inference. We’ll cover the different types of quantization, why and when they’re needed, and the key optimization challenges required to deploy advanced AI models in production.</p>

</div>
<div class="experiencefragment aem-GridColumn aem-GridColumn--default--12">
<div id="experiencefragment-eb38c73633" class="cmp-experiencefragment cmp-experiencefragment--dash-cta-for-tech-blog">

    



<div class="xf-content-height">
    


<div class="aem-Grid aem-Grid--12 aem-Grid--default--12 ">
    
    <div class="responsivegrid aem-GridColumn aem-GridColumn--default--12">


<div class="aem-Grid aem-Grid--12 aem-Grid--default--12 ">
    
    <div class="a08-html-embed c17-plain-html aem-GridColumn aem-GridColumn--default--12">

<style type="text/css"> 

@font-face {
font-family: 'AtlasGrotesk';
src: url('https://cdn.prod.website-files.com/65dcd70b48edc3a7b446950e/65dce019c63112e617513c94_AtlasGrotesk-Medium-Web-vfl38XiTL.woff2') format('woff2');
font-weight: 500;
font-style: normal;
font-display: swap;
}

@font-face {
font-family: 'AtlasGrotesk';
src: url('https://cdn.prod.website-files.com/65dcd70b48edc3a7b446950e/65dce019711b648fd1ccd24a_AtlasGrotesk-Regular-Web-vflk7bxjs.woff2') format('woff2');
font-weight: 400;
font-style: normal;
font-display: swap;
}
.xf-content-height {margin: 0;}
#cta { font-family: AtlasGrotesk,sans-serif; font-size: .900rem; text-decoration: none; background: #f7f5f2; line-height: 1.69; box-sizing: border-box;}
#cta-box { padding: 15px 20px 15px 20px; }
#cta-hed {font-weight: 500;}
#cta-indent {border-left: 5px solid #1e1919; padding-left:20px;}
#cta a:link, #cta a:visited  {text-decoration: none;}
#cta p { margin: 5px 0px 0px 0px; }

.dr-theme-dark #cta {background: #000;}
.dr-theme-dark #cta-box {border: 1px solid; border-bottom: 0;}
.dr-theme-dark #cta-indent {border-left: 5px solid #f7f5f2;}
.dr-theme-dark .button {background: #000;}

.button {
    background-color: #1e1919;
    color:  #f7f5f2;
    height: 2.5rem;
    padding: 10px 5px 10px 20px;
    font-size: 1rem;
    font-weight: 500;
    line-height: 1.2;
    transition: all .3s;
}

.button:hover { background-color: #0061ff; }

img {vertical-align: middle; padding: 0px 1px 2px 0px;}

.c17-plain-html {margin-bottom: 50px}

</style>

<div id="cta">
<div id="cta-box">
<div id="cta-indent">

<p id="cta-hed"><img src="https://cdn.prod.website-files.com/65dcd70b48edc3a7b446950e/670692ee7692f74d4834e4f4_Frame%201400006055.svg" loading="lazy"> Dropbox Dash: AI that understands your work
</p>

<p>Dash knows your context, your team, and your work, so your team can stay organized, easily find and share knowledge, and keep projects secure, all from one place. And soon, Dash is coming to Dropbox.</p>

</div>
</div>

<a href="https://dash.dropbox.com/?utm=blogs" target="_blank"><div class="button">Learn more →</div></a>

</div>
</div>

    
</div>
</div>

    
</div>

</div></div>

    
</div>
<div class="section aem-GridColumn aem-GridColumn--default--12">
<div class="dr-article-content__section" id="-the-cost-of-running-modern-models">
    <h2 class="dr-article-content__section-title"> The cost of running modern models</h2>
</div>
</div>
<div class="text parbase aem-GridColumn aem-GridColumn--default--12">
<p>At Dropbox, almost all the models used in-house are attention-based architectures used for tasks like understanding text, images, videos, and audio—core capabilities behind <a href="https://dash.dropbox.com/" target="_blank">Dash’s</a> ability to search, summarize, and reason over large collections of user content. As these models grow in size and complexity, efficiently serving them in production becomes a central challenge for delivering responsive user experiences. In attention-based models, most of the compute comes from repeated matrix multiplications in two main parts of the model:</p>
<p>The first is the <b>linear layers</b>, which compute embeddings throughout the model. These include: </p>
<ul>
<li>The layers used within attention blocks, the components that determine how different parts of the input relate to one another</li>
<li>MLP layers, which further process and refine those representations</li>
<li>The model’s final output stage, where those representations are converted into a concrete result, such as a prediction or response.</li>
</ul>
<p>The second is the <b>attention mechanism</b> itself, where the model evaluates relationships across the input to determine which information is most relevant, a step that significantly increases compute cost with longer context sizes.</p>
<p>On GPUs, these matrix multiplications are handled by specialized hardware. NVIDIA GPUs use Tensor Cores, while AMD GPUs use Matrix Cores. These dedicated processors are accessed through matrix multiply-accumulate (MMA) instructions and are designed specifically to accelerate matrix operations—the heavy-duty math that underpins large-scale linear algebra in neural networks—delivering substantial performance gains compared to executing the same work on general-purpose CUDA Cores.</p>
<p>One notable property of these cores is their scaling behavior. As numerical precision is reduced, these cores can perform more matrix operations per second, typically resulting in higher FLOPS (floating point operations per second, or how much math the hardware can do in a given time). In practice, halving the precision often allows these cores to roughly double throughput. This scaling behavior plays a key role in improving both performance and efficiency when running large-scale AI workloads.</p>

</div>
<div class="image c04-image aem-GridColumn aem-GridColumn--default--12">
<div class="dr-image image cq-dd-image  ">
    <figure class="dr-margin-0 dr-display-inline-block">
        
            
    

        

        
        
        

        
        
        

        <!--optimized image webp-->
        

        
        <!-- <img data-sly-test.highRes="false"
             srcset="/cms/content/dam/dropbox/tech-blog/en-us/2026/february/low-bit-inference/diagrams/Diagram%201.png 2x,  1x"
             src="/cms/content/dam/dropbox/tech-blog/en-us/2026/february/low-bit-inference/diagrams/Diagram%201.png"
             aria-hidden=""
             alt=""
             class=""
             data-sly-attribute.width="2880"
             data-sly-attribute.height="1440"
             data-aem-asset-id="3a157dcd-e5e0-4635-a324-f5d58d05bc85:Diagram 1.png"
             data-trackable="true" />
        <img data-sly-test.highRes="false"
             srcset="/cms/content/dam/dropbox/tech-blog/en-us/2026/february/low-bit-inference/diagrams/Diagram%201.png 2x,  1x"
             src="/cms/content/dam/dropbox/tech-blog/en-us/2026/february/low-bit-inference/diagrams/Diagram%201.png"
             aria-hidden=""
             alt=""
             class=""
             data-sly-attribute.width="2880"
             data-sly-attribute.height="1440"
             data-aem-asset-id="3a157dcd-e5e0-4635-a324-f5d58d05bc85:Diagram 1.png"
             data-trackable="true" /> -->

        
         
        <img src="/cms/content/dam/dropbox/tech-blog/en-us/2026/february/low-bit-inference/diagrams/Diagram%201.png/_jcr_content/renditions/Diagram%201.webp" fallbackimage="/cms/content/dam/dropbox/tech-blog/en-us/2026/february/low-bit-inference/diagrams/Diagram%201.png" onerror="window.failedAttempts=0;this.setAttribute('src',this.getAttribute('fallbackimage'));window.failedAttempts++;if(window.failedAttempts == 1)this.onerror=null" aria-hidden="false" alt="" data-aem-asset-id="3a157dcd-e5e0-4635-a324-f5d58d05bc85:Diagram 1.png" data-trackable="true" height="1440" width="2880"/>
    

            <figcaption class="dr-typography-t5 dr-color-ink-60 dr-image-rte"><p style="text-align: center;">Fig. 1: Tensor Core dense matrix multiplication performance (FLOPs) across different NVIDIA RTX 6000 variants and data precisions</p>
</figcaption>
        
    </figure>
</div></div>
<div class="text parbase aem-GridColumn aem-GridColumn--default--12">
<p>Lowering numerical precision is accomplished through <b>quantization</b>, a technique that reduces the number of bits used to represent numerical values. By quantizing tensors, for example, from 16-bit to 8-bit or 4-bit, the memory footprint is reduced because each element requires fewer bits. This is typically done by rescaling the data to fit within a smaller representable range. For instance, 8-bit quantization maps values to 256 bins, restricting each tensor element to one of these discrete levels while approximating the original floating-point values. Quantization to lower than 8 bits typically requires an additional process called <b>bitpacking</b>, where multiple low-bit elements are combined into a native data type such as <span class="dr-code" title="undefined">uint8</span> or <span class="dr-code" title="undefined">int32</span>, since 4-bit formats are not natively supported.</p>
<p>Lowering precision not only improves speed and memory usage, but also improves energy efficiency, since lower-bit data requires less power for both memory transfer and computation. For instance, with FP4 support, Blackwell offers <a href="https://developer.nvidia.com/blog/introducing-nvfp4-for-efficient-and-accurate-low-precision-inference/" target="_blank">significant energy savings compared to the H100</a>.</p>
<p>There have also been attempts to explore lower bits such as <a href="https://arxiv.org/abs/2402.17764" target="_blank">binary</a> and <a href="http://BitNet" target="_blank">ternary</a> weights (restricting weights to two or three discrete levels), which would offer even more theoretical energy efficiency. However, this form of quantization isn’t well suited for modern GPUs because it can’t fully leverage Tensor/Matrix Cores. Although there have been experimental efforts to explore <a href="https://arxiv.org/html/2502.16473v1" target="_blank">custom hardware or specialized accelerators</a> tailored to such schemes, this approach hasn’t yet seen broad industry adoption as a result of limited ecosystem support and model quality concerns. In short, while lower precision can dramatically improve efficiency, real-world gains depend on how well those formats are supported by existing hardware and software ecosystems.</p>
<p>In the following section, we examine different quantization configurations and highlight their key trade-offs when deployed on modern GPUs.</p>

</div>
<div class="section aem-GridColumn aem-GridColumn--default--12">
<div class="dr-article-content__section" id="understanding-quantization-formats">
    <h2 class="dr-article-content__section-title">Understanding quantization formats</h2>
</div>
</div>
<div class="text parbase aem-GridColumn aem-GridColumn--default--12">
<p>Quantization is not a single technique, but a family of approaches that differ in how numerical values are represented, scaled, and executed on hardware. These design choices directly affect model accuracy, performance, and how efficiently modern GPUs can accelerate inference. As a result, quantization formats are closely tied to the capabilities and constraints of the underlying hardware. </p>
<p>In practice, these differences matter because Dropbox runs a diverse set of AI workloads—such as multimedia understanding—across multiple generations of hardware, each with distinct performance characteristics. Some workloads are highly latency sensitive, prioritizing fast per-request execution, while others are throughput oriented and optimized for processing large volumes of data efficiently. Quantization formats influence how well a model can adapt to these constraints, determining whether computation is bound by software overhead, memory bandwidth, or specialized hardware units like Tensor Cores. Framing quantization through this lens helps clarify why different formats unlock different tradeoffs across our stack, and why no single approach is optimal for every workload we deploy.</p>
<p>With the introduction of the <a href="https://www.opencompute.org/documents/ocp-microscaling-formats-mx-v1-0-spec-final-pdf" target="_blank">MXFP</a> microscaling format, which standardizes low-bit data types with native hardware support, quantization methods for large language models can be broadly grouped into two categories: pre-MXFP formats, which rely on explicit dequantization and software-managed scaling, and MXFP formats, which move these operations directly into Tensor Core hardware. The sections below walk through both approaches, highlighting how they differ in practice and why those differences matter for real-world inference workloads.</p>

</div>
<div class="text parbase aem-GridColumn aem-GridColumn--default--12">
<h3>Pre-MXFP formats</h3>
<p>Prior to the introduction of MXFP, quantization primarily relied on integer data types for sub-byte formats. Common configurations included A16W4 (16-bit activations, 4-bit weights) for weight quantization, and either integer or floating-point formats for activations, such as A8W8 (8-bit activations, 8-bit weights). In contrast, sub-byte weight quantization generally requires calibration or more advanced algorithms to maintain model quality. For example, A16W4 relies on techniques such as <a href="https://arxiv.org/abs/2306.00978" target="_blank">AWQ</a> or <a href="https://dropbox.tech/machine-learning/halfquadratic-quantization-of-large-machine-learning-models" target="_blank">HQQ</a>—quantization methods designed to preserve model quality at low bit widths—while lower-bit formats like A16W3, A16W2, and <a href="https://github.com/microsoft/BitNet" target="_blank">BitNet</a> require increasingly sophisticated training quantization methods to achieve acceptable accuracy.</p>
<p>When activations and weights use different data types, the typical approach is to explicitly dequantize the lower-bit tensors to match the higher-precision format before performing the matrix multiplication (MMA) operation. This strategy can improve performance in memory-bound scenarios, where reducing data movement is the primary concern. However, in compute-bound workloads, the additional dequantization step can offset these gains and even slow execution due to the extra arithmetic involved.</p>
<p>This trade-off is especially visible in weight-only quantization, which reduces data transfer but does not accelerate the extra computation required to run matrix multiplications. The choice between activation quantization (such as A8W8) and weight-only quantization (such as A16W4) ultimately depends on the characteristics of the inference workload. Weight-only quantization often performs better in local deployments with smaller batch sizes and reasoning-heavy tasks, where memory bandwidth is a limiting factor. In contrast, activation quantization tends to be more effective for large-context prefills and high-throughput serving scenarios, where compute becomes the dominant bottleneck.</p>

</div>
<div class="image c04-image aem-GridColumn aem-GridColumn--default--12">
<div class="dr-image image cq-dd-image  ">
    <figure class="dr-margin-0 dr-display-inline-block">
        
            
    

        

        
        
        

        
        
        

        <!--optimized image webp-->
        

        
        <!-- <img data-sly-test.highRes="false"
             srcset="/cms/content/dam/dropbox/tech-blog/en-us/2026/february/low-bit-inference/diagrams/Diagram%202.png 2x,  1x"
             src="/cms/content/dam/dropbox/tech-blog/en-us/2026/february/low-bit-inference/diagrams/Diagram%202.png"
             aria-hidden=""
             alt=""
             class=""
             data-sly-attribute.width="2880"
             data-sly-attribute.height="2080"
             data-aem-asset-id="98afad31-040a-4713-9d71-e531f3f36502:Diagram 2.png"
             data-trackable="true" />
        <img data-sly-test.highRes="false"
             srcset="/cms/content/dam/dropbox/tech-blog/en-us/2026/february/low-bit-inference/diagrams/Diagram%202.png 2x,  1x"
             src="/cms/content/dam/dropbox/tech-blog/en-us/2026/february/low-bit-inference/diagrams/Diagram%202.png"
             aria-hidden=""
             alt=""
             class=""
             data-sly-attribute.width="2880"
             data-sly-attribute.height="2080"
             data-aem-asset-id="98afad31-040a-4713-9d71-e531f3f36502:Diagram 2.png"
             data-trackable="true" /> -->

        
         
        <img src="/cms/content/dam/dropbox/tech-blog/en-us/2026/february/low-bit-inference/diagrams/Diagram%202.png/_jcr_content/renditions/Diagram%202.webp" fallbackimage="/cms/content/dam/dropbox/tech-blog/en-us/2026/february/low-bit-inference/diagrams/Diagram%202.png" onerror="window.failedAttempts=0;this.setAttribute('src',this.getAttribute('fallbackimage'));window.failedAttempts++;if(window.failedAttempts == 1)this.onerror=null" aria-hidden="false" alt="" data-aem-asset-id="98afad31-040a-4713-9d71-e531f3f36502:Diagram 2.png" data-trackable="true" height="2080" width="2880"/>
    

            <figcaption class="dr-typography-t5 dr-color-ink-60 dr-image-rte"><p style="text-align: center;">Fig. 2: A8W8 vs. A16W4 decoding performance across various batch sizes. A8W8 tends to outperform A16W4 in more compute-bound scenarios. A16W4 tends to perform worse than 16-bit matrix multiplication due to the additional cost of explicit dequantization</p>
</figcaption>
        
    </figure>
</div></div>
<div class="text parbase aem-GridColumn aem-GridColumn--default--12">
<p>Popular methods such as <a href="https://arxiv.org/abs/2306.00978" target="_blank"><b>AWQ</b></a> and <a href="https://github.com/dropbox/hqq" target="_blank"><b>HQQ</b></a> rely on <b>linear quantization with grouping</b>, a design that balances efficiency with accuracy. In symmetric linear quantization, dequantization is expressed as a simple scaling operation. A more flexible variant, asymmetric linear quantization, introduces an additional offset, allowing dequantization to be implemented as a fused multiply-add operation that maps efficiently to modern GPU hardware.</p>
<p>Grouping further improves accuracy by assigning shared parameters to small blocks of tensor elements rather than individual values. These groups typically consist of contiguous elements of size 32, 64, or 128. While simple, this approach substantially reduces quantization error at low-bit widths and has become a core component of most practical low-bit quantization schemes.</p>

</div>
<div class="image c04-image aem-GridColumn aem-GridColumn--default--12">
<div class="dr-image image cq-dd-image  ">
    <figure class="dr-margin-0 dr-display-inline-block">
        
            
    

        

        
        
        

        
        
        

        <!--optimized image webp-->
        

        
        <!-- <img data-sly-test.highRes="false"
             srcset="/cms/content/dam/dropbox/tech-blog/en-us/2026/february/low-bit-inference/diagrams/Diagram%203.png 2x,  1x"
             src="/cms/content/dam/dropbox/tech-blog/en-us/2026/february/low-bit-inference/diagrams/Diagram%203.png"
             aria-hidden=""
             alt=""
             class=""
             data-sly-attribute.width="2880"
             data-sly-attribute.height="1560"
             data-aem-asset-id="8c6a32b1-c171-4fef-ae0a-f492a4f529a4:Diagram 3.png"
             data-trackable="true" />
        <img data-sly-test.highRes="false"
             srcset="/cms/content/dam/dropbox/tech-blog/en-us/2026/february/low-bit-inference/diagrams/Diagram%203.png 2x,  1x"
             src="/cms/content/dam/dropbox/tech-blog/en-us/2026/february/low-bit-inference/diagrams/Diagram%203.png"
             aria-hidden=""
             alt=""
             class=""
             data-sly-attribute.width="2880"
             data-sly-attribute.height="1560"
             data-aem-asset-id="8c6a32b1-c171-4fef-ae0a-f492a4f529a4:Diagram 3.png"
             data-trackable="true" /> -->

        
         
        <img src="/cms/content/dam/dropbox/tech-blog/en-us/2026/february/low-bit-inference/diagrams/Diagram%203.png/_jcr_content/renditions/Diagram%203.webp" fallbackimage="/cms/content/dam/dropbox/tech-blog/en-us/2026/february/low-bit-inference/diagrams/Diagram%203.png" onerror="window.failedAttempts=0;this.setAttribute('src',this.getAttribute('fallbackimage'));window.failedAttempts++;if(window.failedAttempts == 1)this.onerror=null" aria-hidden="false" alt="" data-aem-asset-id="8c6a32b1-c171-4fef-ae0a-f492a4f529a4:Diagram 3.png" data-trackable="true" height="1560" width="2880"/>
    

            <figcaption class="dr-typography-t5 dr-color-ink-60 dr-image-rte"><p style="text-align: center;">Fig. 3: Linear quantization overview where a matrix W is decomposed into Wq (low-bit tensor) and additional floating-point scales (s) and zero-points (z)</p>
</figcaption>
        
    </figure>
</div></div>
<div class="text parbase aem-GridColumn aem-GridColumn--default--12">
<p>On the activation side, two 8-bit approaches are commonly used: channel-wise quantization and per-block quantization. Channel-wise quantization is straightforward and efficient, making it well suited for on-the-fly inference. The required rescaling can be applied directly after matrix multiplication, allowing for a highly efficient implementation on modern GPUs.</p>
<p>Per-block quantization, popularized by systems such as <a href="https://arxiv.org/abs/2403.12422" target="_blank">JetFire</a> and <a href="https://github.com/deepseek-ai/DeepSeek-V3" target="_blank">DeepSeek V3</a>, takes a more fine-grained approach. By dividing tensors into small tiles and assigning an independent scale to each block, this method limits the impact of outliers and reduces quantization error. It is particularly effective in quantization-aware training, where preserving pre-training accuracy is critical, while still delivering practical Tensor Core speedups.</p>
<p>Beyond linear quantization, several <b>non-linear approaches</b>, including <a href="https://arxiv.org/abs/2402.04396" target="_blank">QuiP#</a> and <a href="https://arxiv.org/abs/2402.15319" target="_blank">GPTVQ</a>, have explored alternative representations to push precision even lower. While these methods can achieve higher accuracy at very low-bit widths, they face practical challenges. Linear 4-bit quantization already delivers <a href="https://arxiv.org/abs/2411.02355" target="_blank">strong accuracy</a> and can often be applied on the fly using techniques such as HQQ, avoiding expensive offline quantization passes. In addition, deploying non-linear formats efficiently requires <b>custom fused kernels</b> and deep integration into inference frameworks. Even then, low-bit weights must still be converted into a form compatible with Tensor Cores, making linear quantization both simpler and more practical on current GPU architectures.</p>
<p>Quantization techniques are also well-suited for optimizing the attention module. Methods such as <a href="https://arxiv.org/abs/2407.08608" target="_blank">Flash Attention 3</a> and <a href="https://github.com/thu-ml/SageAttention" target="_blank">Sage Attention</a> use 8-bit quantization to accelerate attention-related matrix multiplications, improving throughput and memory efficiency with minimal impact on model accuracy.</p>

</div>
<div class="text parbase aem-GridColumn aem-GridColumn--default--12">
<h3>MXFP formats</h3>
<p>The <a href="https://www.opencompute.org/documents/ocp-microscaling-formats-mx-v1-0-spec-final-pdf" target="_blank">MXFP microscaling format</a> introduces a new standard for low-bit data types that fundamentally changes how quantized models run on modern GPUs. Unlike earlier formats, MXFP provides <b>native hardware support for quantization</b>, allowing Tensor Cores to operate directly on quantized activations, weights, and their associated scaling factors in a single fused operation. In contrast, pre-MXFP approaches required explicit dequantization steps before or after matrix-multiply-accumulate (MMA) operations, adding overhead and limiting achievable performance.</p>
<p>MXFP quantizes both activations and weights using a micro-scaling approach, similar in spirit to methods like <b>AWQ</b> and <b>HQQ</b> discussed earlier, but implemented directly in hardware. It uses symmetric quantization with a fixed block size of 32 and applies shared scaling factors stored in the E8M0 format. MXFP also supports mixed-precision MMA operations on some hardware, such as MXFP8 × MXFP4, giving practitioners flexibility to balance performance and accuracy. For example, activations can use MXFP8, MXFP6, or MXFP4 while the weights can remain in MXFP4. A breakdown of the MX types is demonstrated in the table below (source: <a href="https://www.opencompute.org/documents/ocp-microscaling-formats-mx-v1-0-spec-final-pdf" target="_blank">Open Compute Project</a>, <i>OCP Microscaling Formats (MX) Specification, Version 1.0, </i>Table 1).</p>

</div>
<div class="image c04-image aem-GridColumn aem-GridColumn--default--12">
<div class="dr-image image cq-dd-image  ">
    <figure class="dr-margin-0 dr-display-inline-block">
        
            
    

        

        
        
        

        
        
        

        <!--optimized image webp-->
        

        
        <!-- <img data-sly-test.highRes="false"
             srcset="/cms/content/dam/dropbox/tech-blog/en-us/2026/february/low-bit-inference/diagrams/Diagram%204.png 2x,  1x"
             src="/cms/content/dam/dropbox/tech-blog/en-us/2026/february/low-bit-inference/diagrams/Diagram%204.png"
             aria-hidden=""
             alt=""
             class=""
             data-sly-attribute.width="2880"
             data-sly-attribute.height="1920"
             data-aem-asset-id="4e904feb-ec81-4112-ba37-af51483eb324:Diagram 4.png"
             data-trackable="true" />
        <img data-sly-test.highRes="false"
             srcset="/cms/content/dam/dropbox/tech-blog/en-us/2026/february/low-bit-inference/diagrams/Diagram%204.png 2x,  1x"
             src="/cms/content/dam/dropbox/tech-blog/en-us/2026/february/low-bit-inference/diagrams/Diagram%204.png"
             aria-hidden=""
             alt=""
             class=""
             data-sly-attribute.width="2880"
             data-sly-attribute.height="1920"
             data-aem-asset-id="4e904feb-ec81-4112-ba37-af51483eb324:Diagram 4.png"
             data-trackable="true" /> -->

        
         
        <img src="/cms/content/dam/dropbox/tech-blog/en-us/2026/february/low-bit-inference/diagrams/Diagram%204.png/_jcr_content/renditions/Diagram%204.webp" fallbackimage="/cms/content/dam/dropbox/tech-blog/en-us/2026/february/low-bit-inference/diagrams/Diagram%204.png" onerror="window.failedAttempts=0;this.setAttribute('src',this.getAttribute('fallbackimage'));window.failedAttempts++;if(window.failedAttempts == 1)this.onerror=null" aria-hidden="false" alt="" data-aem-asset-id="4e904feb-ec81-4112-ba37-af51483eb324:Diagram 4.png" data-trackable="true" height="1920" width="2880"/>
    

            <figcaption class="dr-typography-t5 dr-color-ink-60 dr-image-rte"><p style="text-align: center;">Fig. 4: MX dtype breakdown</p>
</figcaption>
        
    </figure>
</div></div>
<div class="text parbase aem-GridColumn aem-GridColumn--default--12">
<p>The E8M0 format for the scales represents positive powers of two in the range [2⁻¹²⁷, 2¹²⁷]. The scales are typically quantized as follows: <span class="dr-code" title="undefined">scale = weight.amax(axis=1, keepdim=True) / max_val</span>. As a result, scale values are effectively limited to values at or below 1, and extremely small magnitudes are rarely needed. In many cases, values as small as 2⁻¹⁵ are sufficient to capture near-zero weights. This observation suggests that scales could theoretically be represented with fewer bits than E8M0, although doing so would introduce additional complexity.</p>
<p>While E8M0 offers hardware-friendly implementation and flexibility, constraining scale values strictly to powers of two leads to a noticeable accuracy drop when using MXFP4. Fortunately, this loss can largely be mitigated through simple post-training adjustments, restoring most of the original model quality, as we demonstrated in <a href="https://dropbox.github.io/fp4_blogpost/" target="_blank">our blog post</a>.</p>
<p>To address remaining numerical limitations, NVIDIA introduced <a href="https://developer.nvidia.com/blog/introducing-nvfp4-for-efficient-and-accurate-low-precision-inference/" target="_blank">NVFP4</a> as an alternative to MXFP4. NVFP4 uses a smaller group size of 16 rather than 32 and employs E4M3 FP8 scaling factors, providing higher precision for scale representation. Because FP8 has a relatively large minimum representable value, a global per-tensor floating-point multiplier is applied to normalize the scaling range, achieving improved numerical stability.</p>
<p>Although MXFP4 and NVFP4 are standardized formats, their implementation depends on the GPU architecture. Different compute capabilities rely on different Tensor Core instructions. For example, sm_100 architectures use the <span class="dr-code" title="undefined">tcgen05.mma</span> instruction, while sm_120 architectures use <span class="dr-code" title="undefined">mma.sync</span>, both incorporating the <span class="dr-code" title="undefined">block_scale</span> modifier. As a result, kernels compiled for sm_100 are not portable to sm_120 due to these instruction-level differences. While most of the mainstream AI software stack remains focused on server-grade GPUs like the B200 and B300, there has been significant recent progress toward improving portability of low-bit workloads. Notably, <a href="https://github.com/triton-lang/triton/pull/8494" target="_blank">Triton</a> has introduced support for MXFP on sm_120 devices, enabling greater flexibility and cross-device compatibility for <a href="https://github.com/dropbox/gemlite/" target="_blank">low-bit Triton kernels</a>.</p>

</div>
<div class="section aem-GridColumn aem-GridColumn--default--12">
<div class="dr-article-content__section" id="looking-forward">
    <h2 class="dr-article-content__section-title">Looking forward</h2>
</div>
</div>
<div class="text parbase aem-GridColumn aem-GridColumn--default--12">
<p>In this article, we explored several quantization techniques that are widely adopted across the industry to accelerate AI workloads. These approaches unlock substantial gains in efficiency and throughput, making it possible to deploy increasingly large and capable models within practical hardware, cost, and energy constraints.</p>
<p>At Dropbox, these considerations are central to how we build and operate products like Dash. Dash relies on large-scale models for experiences such as conversational AI, multimodal search, document understanding, and speech processing, all of which must meet strict latency, reliability, and cost requirements. To satisfy these constraints in production, we already employ a range of quantization strategies to optimize model deployment and fully utilize modern accelerators. The techniques discussed here reflect the kinds of trade-offs we evaluate when deciding how and where to run models across our infrastructure.</p>
<p>Despite the progress, important limitations remain. In real-world deployments, adoption of formats such as MXFP and NVFP is still evolving, and support for FP4 quantization remains incomplete across popular frameworks and model stacks. For example, many open-source runtimes don’t yet provide full support across different GPU architectures, and FP4 models are not yet widely available.</p>
<p>As hardware continues to evolve and the industry pushes toward lower-bit compute, these challenges will only become more pronounced. In our view, making low-bit inference viable for production systems like Dash will require tighter software design, more mature framework support, and new quantization techniques that preserve model quality at scale. We view this as an active area of exploration, one that will directly shape how we deliver fast, reliable, and efficient AI-powered experiences to Dropbox users in the years ahead.</p>
<p style="text-align: center;">~ ~ ~</p>
<p><i>If building innovative products, experiences, and infrastructure excites you, come build the future with us! Visit </i><a href="https://jobs.dropbox.com/" target="_blank"><i>jobs.dropbox.com</i></a><i> to see our open roles.</i></p>

</div>

    
</div>
]]></content:encoded>
            			
                <media:thumbnail url="https://dropbox.tech/cms/content/dam/dropbox/tech-blog/en-us/2026/february/low-bit-inference/headers/MachineLearning-Low-Bit Inference-375x150-dark.png" />
                <media:content url="https://dropbox.tech/cms/content/dam/dropbox/tech-blog/en-us/2026/february/low-bit-inference/headers/MachineLearning-Low-Bit Inference-375x150-dark.png" medium="image">
                    <media:title type="html">How low-bit inference enables efficient AI</media:title>
                </media:content>
       		 </item>
                    
        				<item>
                        <title>Insights from our executive roundtable on AI and engineering productivity</title>
                        
            			<link>https://dropbox.tech/culture/insights-from-our-executive-roundtable-on-ai-and-engineering-productivity</link>

                            
            			<dc:creator>
                            Facundo Agriel,Ishan Mishra,Eric Wang,Dmitriy Meyerzon,Dmitriy Meyerzon,Hicham Badri,Appu Shaji,Craig Wilhite
            			</dc:creator>
            			
            				<category>Culture</category>
							
            				<category>AI</category>
							
            				<category>developer productivity</category>
							
            				<category>Events</category>
							
                            
            			<description><![CDATA[From Claude Code to Cursor, we're big adopters of AI coding tools at Dropbox. The early results have been promising, but there are still a lot of open questions about how to work with these tools most effectively and where they can have the most impact. To push this conversation forward, we hosted an executive roundtable at our San Francisco studio. Here's how it went.]]></description>
            			<guid>https://dropbox.tech/culture/insights-from-our-executive-roundtable-on-ai-and-engineering-productivity</guid>
                        <pubDate>Wed, 11 Feb 2026 09:00:00 -0800</pubDate>
                            
                        <content:encoded><![CDATA[


<div class="aem-Grid aem-Grid--12 aem-Grid--default--12 ">
    
    <div class="text parbase aem-GridColumn aem-GridColumn--default--12">
<p>Improving engineering productivity is crucial to the work we do at Dropbox. The more quickly we can deliver high-quality features to our customers, the more value they can get from our products. This rapid iteration has been key to developing tools like <a href="http://dropbox.com/dash" target="_blank">Dropbox Dash</a>, context-aware AI that connects to all your work apps, so you can search, ask questions about, and organize all your content.</p>
<p>In the process of building Dash, we’ve become big adopters of AI tools in our own work, from Claude Code to Cursor. The early results have been promising, but there are still a lot of open questions about how to work with these tools most effectively and where they can have the most impact. To push this conversation forward, <a href="https://dropbox.tech/culture/ai-adoption-productivity-dropbox-cto-ali-dasdan" target="_blank">Dropbox CTO Ali Dasdan</a> hosted an executive roundtable on December 11, 2025, at our San Francisco studio. We brought together a small group of technology leaders from top companies for an afternoon of open discussion, idea-sharing, and a deep dive into the evolving world of engineering productivity and AI. Here’s how it went.</p>

</div>
<div class="section aem-GridColumn aem-GridColumn--default--12">
<div class="dr-article-content__section" id="how-dropbox-is-accelerating-progress-with-ai">
    <h2 class="dr-article-content__section-title">How Dropbox is accelerating progress with AI</h2>
</div>
</div>
<div class="text parbase aem-GridColumn aem-GridColumn--default--12">
<p>Adopting AI tooling for the sake of AI is meaningless; it must be tied to tangible business results. As we navigate this shift, we’ve had to ask ourselves: Which approach is the right one? What existing processes need to be upgraded in light of AI workflows? To kick off the event—and show attendees how we’ve been thinking through these questions at Dropbox—Uma Namasivayam, Senior Director of Engineering Productivity, took a closer look at our own experimentation, adoption, and enablement cycle to accelerate engineering productivity with AI.</p>
<p>We started by working with Dropbox leadership to gain buy-in and establish the importance of AI tooling, and together made AI adoption a company-level priority. This turned AI from a grassroots experiment into an urgent organizational priority, and helped everyone get aligned. Teams were now empowered to experiment with tooling, and we reduced the overhead associated with getting contracts approved to pilot new tooling at Dropbox. </p>
<p>In our experimentation, Dropbox saw impact across the entire software development life cycle, from code review and documentation to debugging and testing. Like other large organizations, Dropbox has our unique challenges. Off-the-shelf AI tools don’t always fit our scale constraints—we have a very large, multi-language monorepo—so we’ve had to be deliberate about where to adopt, where to extend, and where to build our own capabilities. For example, Dropbox built our own AI tooling that listens for failed builds on pull requests and uses our AI platform to propose fixes to them.</p>
<p>As a result of our efforts, most Dropbox developers are now using at least one AI tool in their workflows. We track pull request (PR) throughput per month, per engineer as a core metric. You can see how users who are engaging more with AI coding tools have an outsized impact on the code shipped, measured by PR throughput per month.</p>

</div>
<div class="image c04-image aem-GridColumn aem-GridColumn--default--12">
<div class="dr-image image cq-dd-image  ">
    <figure class="dr-margin-0 dr-display-inline-block">
        
            
    

        

        
        
        

        
        
        

        <!--optimized image webp-->
        

        
        <!-- <img data-sly-test.highRes="false"
             srcset="/cms/content/dam/dropbox/tech-blog/en-us/2026/february/cto-roundtable/cto-roundtable-one.png 2x,  1x"
             src="/cms/content/dam/dropbox/tech-blog/en-us/2026/february/cto-roundtable/cto-roundtable-one.png"
             aria-hidden=""
             alt="A graph showing pull request throughput per month, per engineer."
             class=""
             data-sly-attribute.width="1600"
             data-sly-attribute.height="988"
             data-aem-asset-id="107192d5-ff1f-4374-8cc7-99ec27414ed3:cto-roundtable-one.png"
             data-trackable="true" />
        <img data-sly-test.highRes="false"
             srcset="/cms/content/dam/dropbox/tech-blog/en-us/2026/february/cto-roundtable/cto-roundtable-one.png 2x,  1x"
             src="/cms/content/dam/dropbox/tech-blog/en-us/2026/february/cto-roundtable/cto-roundtable-one.png"
             aria-hidden=""
             alt=""
             class=""
             data-sly-attribute.width="1600"
             data-sly-attribute.height="988"
             data-aem-asset-id="107192d5-ff1f-4374-8cc7-99ec27414ed3:cto-roundtable-one.png"
             data-trackable="true" /> -->

        
         <img src="/cms/content/dam/dropbox/tech-blog/en-us/2026/february/cto-roundtable/cto-roundtable-one.png/_jcr_content/renditions/cto-roundtable-one.webp" fallbackimage="/cms/content/dam/dropbox/tech-blog/en-us/2026/february/cto-roundtable/cto-roundtable-one.png" onerror="window.failedAttempts=0;this.setAttribute('src',this.getAttribute('fallbackimage'));window.failedAttempts++;if(window.failedAttempts == 1)this.onerror=null" aria-hidden="false" alt="A graph showing pull request throughput per month, per engineer." data-aem-asset-id="107192d5-ff1f-4374-8cc7-99ec27414ed3:cto-roundtable-one.png" data-trackable="true" height="988" width="1600"/>
        
    

            
        
    </figure>
</div></div>
<div class="text parbase aem-GridColumn aem-GridColumn--default--12">
<p>We also closely monitor the sentiment of engineers internally regarding AI tooling. As strong positive sentiment increases, we’re seeing the share of negative sentiment go down.</p>

</div>
<div class="image c04-image aem-GridColumn aem-GridColumn--default--12">
<div class="dr-image image cq-dd-image  ">
    <figure class="dr-margin-0 dr-display-inline-block">
        
            
    

        

        
        
        

        
        
        

        <!--optimized image webp-->
        

        
        <!-- <img data-sly-test.highRes="false"
             srcset="/cms/content/dam/dropbox/tech-blog/en-us/2026/february/cto-roundtable/cto-roundtable-two.png 2x,  1x"
             src="/cms/content/dam/dropbox/tech-blog/en-us/2026/february/cto-roundtable/cto-roundtable-two.png"
             aria-hidden=""
             alt="A graph showing the impact of AI on developer productivity has grown more positive over time, according to surveys of Dropboxer engineers."
             class=""
             data-sly-attribute.width="1200"
             data-sly-attribute.height="742"
             data-aem-asset-id="e425378a-2b3c-46cb-9a07-aadbd55115d0:cto-roundtable-two.png"
             data-trackable="true" />
        <img data-sly-test.highRes="false"
             srcset="/cms/content/dam/dropbox/tech-blog/en-us/2026/february/cto-roundtable/cto-roundtable-two.png 2x,  1x"
             src="/cms/content/dam/dropbox/tech-blog/en-us/2026/february/cto-roundtable/cto-roundtable-two.png"
             aria-hidden=""
             alt=""
             class=""
             data-sly-attribute.width="1200"
             data-sly-attribute.height="742"
             data-aem-asset-id="e425378a-2b3c-46cb-9a07-aadbd55115d0:cto-roundtable-two.png"
             data-trackable="true" /> -->

        
         <img src="/cms/content/dam/dropbox/tech-blog/en-us/2026/february/cto-roundtable/cto-roundtable-two.png/_jcr_content/renditions/cto-roundtable-two.webp" fallbackimage="/cms/content/dam/dropbox/tech-blog/en-us/2026/february/cto-roundtable/cto-roundtable-two.png" onerror="window.failedAttempts=0;this.setAttribute('src',this.getAttribute('fallbackimage'));window.failedAttempts++;if(window.failedAttempts == 1)this.onerror=null" aria-hidden="false" alt="A graph showing the impact of AI on developer productivity has grown more positive over time, according to surveys of Dropboxer engineers." data-aem-asset-id="e425378a-2b3c-46cb-9a07-aadbd55115d0:cto-roundtable-two.png" data-trackable="true" height="742" width="1200"/>
        
    

            
        
    </figure>
</div></div>
<div class="text parbase aem-GridColumn aem-GridColumn--default--12">
<p>Most importantly, developers feel less friction using AI to accelerate their work because we’ve made it easier to adopt tooling according to what they feel works best for their team.</p>

</div>
<div class="section aem-GridColumn aem-GridColumn--default--12">
<div class="dr-article-content__section" id="-the-executive-roundtable">
    <h2 class="dr-article-content__section-title"> The executive roundtable</h2>
</div>
</div>
<div class="text parbase aem-GridColumn aem-GridColumn--default--12">
<p>The heart of the evening was a roundtable discussion designed to cross-pollinate ideas across different industries. To facilitate this, we divided attendees into three cohorts, rotating the groups for each question so that every leader could learn from three different peer groups.</p>
<p>The discussion centered around three core pillars:</p>
<ol>
<li><b>Measuring impact.</b> What are the top three ways attendees are measuring AI-driven engineering productivity gains and what are the top three ways of measuring the resulting business impact?</li>
<li><b>Leadership alignment.</b> Describe three ways of aligning with company leadership on the progress and pace of AI deployment and use for productivity.</li>
<li><b>The human element.</b> What are the top three ways attendees are recruiting, evaluating, and growing their workforce for AI competency and productivity? What lessons can be applied to make non-developers more productive?</li>
</ol>
<p>Following the structured session, the conversation continued over a cocktail hour, where leaders shared further insights into the commitment to craft required to lead in the age of AI.</p>

</div>
<div class="section aem-GridColumn aem-GridColumn--default--12">
<div class="dr-article-content__section" id="what-we-learned-and-whats-next-">
    <h2 class="dr-article-content__section-title">What we learned, and what’s next </h2>
</div>
</div>
<div class="text parbase aem-GridColumn aem-GridColumn--default--12">
<p>The overarching themes that emerged from the roundtable discussions centered around the following:</p>
<ol>
<li><b>Balance.</b> Productivity gains must be carefully balanced against potential trade-offs in quality and long-term maintenance costs.</li>
<li><b>The role of leadership.</b> Management, particularly technical leadership, is pivotal in establishing and enforcing effective AI usage norms.</li>
<li><b>Formalization.</b> Formalizing AI competency within career frameworks signals a long-term commitment to its strategic importance.</li>
</ol>
<p>Still, there are a number of open questions, such as: If AI is giving us more capacity, where is that capacity actually going? For Dropbox, this capacity is currently being channeled into areas like addressing tech debt, executing migrations, and improving reliability. </p>

</div>
<div class="image c04-image aem-GridColumn aem-GridColumn--default--12">
<div class="dr-image image cq-dd-image  ">
    <figure class="dr-margin-0 dr-display-inline-block">
        
            
    

        

        
        
        

        
        
        

        <!--optimized image webp-->
        

        
        <!-- <img data-sly-test.highRes="false"
             srcset="/cms/content/dam/dropbox/tech-blog/en-us/2026/february/cto-roundtable/cto-roundtable-three.png 2x,  1x"
             src="/cms/content/dam/dropbox/tech-blog/en-us/2026/february/cto-roundtable/cto-roundtable-three.png"
             aria-hidden=""
             alt="A graph showing the different categories in which PR throughput has increased since we introduced AI tools for developers."
             class=""
             data-sly-attribute.width="2394"
             data-sly-attribute.height="672"
             data-aem-asset-id="f83d193c-0eca-48c9-a43b-cb5234977e1c:cto-roundtable-three.png"
             data-trackable="true" />
        <img data-sly-test.highRes="false"
             srcset="/cms/content/dam/dropbox/tech-blog/en-us/2026/february/cto-roundtable/cto-roundtable-three.png 2x,  1x"
             src="/cms/content/dam/dropbox/tech-blog/en-us/2026/february/cto-roundtable/cto-roundtable-three.png"
             aria-hidden=""
             alt=""
             class=""
             data-sly-attribute.width="2394"
             data-sly-attribute.height="672"
             data-aem-asset-id="f83d193c-0eca-48c9-a43b-cb5234977e1c:cto-roundtable-three.png"
             data-trackable="true" /> -->

        
         <img src="/cms/content/dam/dropbox/tech-blog/en-us/2026/february/cto-roundtable/cto-roundtable-three.png/_jcr_content/renditions/cto-roundtable-three.webp" fallbackimage="/cms/content/dam/dropbox/tech-blog/en-us/2026/february/cto-roundtable/cto-roundtable-three.png" onerror="window.failedAttempts=0;this.setAttribute('src',this.getAttribute('fallbackimage'));window.failedAttempts++;if(window.failedAttempts == 1)this.onerror=null" aria-hidden="false" alt="A graph showing the different categories in which PR throughput has increased since we introduced AI tools for developers." data-aem-asset-id="f83d193c-0eca-48c9-a43b-cb5234977e1c:cto-roundtable-three.png" data-trackable="true" height="672" width="2394"/>
        
    

            
        
    </figure>
</div></div>
<div class="text parbase aem-GridColumn aem-GridColumn--default--12">
<p>However, a key challenge remains in effectively connecting these productivity gains to tangible business outcomes—a challenge also voiced by many attendees during the roundtable. Therefore, the focus for 2026 will be on mapping productivity directly to specific outcomes, extending operational rigor beyond engineering teams, and ultimately driving end-to-end product velocity.</p>
<p>A huge thank you to everyone who made the trip to our San Francisco studio and contributed to such a memorable event. If you missed out this time, keep an eye on our <a href="https://events.dropbox.com/" target="_blank">events page</a> for future opportunities to connect!</p>
<p style="text-align: center;">~ ~ ~ </p>
<p><i>If building innovative products, experiences, and infrastructure excites you, come build the future with us! Visit </i><a href="https://jobs.dropbox.com/" target="_blank"><i>jobs.dropbox.com</i></a><i> to see our open roles.</i></p>

</div>

    
</div>
]]></content:encoded>
            			
                <media:thumbnail url="https://dropbox.tech/cms/content/dam/dropbox/tech-blog/en-us/2026/february/cto-roundtable/cto-roundtable-1440x305-light.png" />
                <media:content url="https://dropbox.tech/cms/content/dam/dropbox/tech-blog/en-us/2026/february/cto-roundtable/cto-roundtable-1440x305-light.png" medium="image">
                    <media:title type="html">Insights from our executive roundtable on AI and engineering productivity</media:title>
                </media:content>
       		 </item>
                    
        				<item>
                        <title>Engineering VP Josh Clemm on how we use knowledge graphs, MCP, and DSPy in Dash</title>
                        
            			<link>https://dropbox.tech/machine-learning/vp-josh-clemm-knowledge-graphs-mcp-and-dspy-dash</link>

                            
            			<dc:creator>
                            Facundo Agriel,Ishan Mishra,Eric Wang,Dmitriy Meyerzon,Dmitriy Meyerzon,Hicham Badri,Appu Shaji,Craig Wilhite,Josh Clemm
            			</dc:creator>
            			
            				<category>context engine</category>
							
            				<category>Dash</category>
							
            				<category>Leadership</category>
							
            				<category>LLM</category>
							
            				<category>knowledge graphs</category>
							
            				<category>MCP</category>
							
            				<category>DSPy</category>
							
            				<category>RAG</category>
							
                            
            			<description><![CDATA[Engineering VP Josh Clemm deep-dives into how we think about knowledge graphs, indexes, MCP, and prompt optimization using tools like DSPy.]]></description>
            			<guid>https://dropbox.tech/machine-learning/vp-josh-clemm-knowledge-graphs-mcp-and-dspy-dash</guid>
                        <pubDate>Wed, 28 Jan 2026 10:00:00 -0800</pubDate>
                            
                        <content:encoded><![CDATA[


<div class="aem-Grid aem-Grid--12 aem-Grid--default--12 ">
    
    <div class="text parbase aem-GridColumn aem-GridColumn--default--12">
<p><i>I was recently a guest speaker in Jason Liu’s online course on RAG offered by the education platform Maven. I did some mini deep-dives into what we’ve been doing at Dropbox with knowledge graphs; how we’re thinking about indexes, MCP, and tool calling in general; some of the work we do with LLM as a judge; and how we use prompt optimizers like DSPy. This is an edited and condensed version of my talk. Visit Maven to <a href="https://maven.com/p/7d35a7/dropbox-knowledge-graphs-prompt-optimizers-and-mc-ps" target="_blank">watch the full video and hear my Q&A</a> with Jason and his students. — Josh Clemm, vice president of engineering for Dropbox Dash</i></p>
<p style="text-align: center;">~ ~ ~</p>
<p>I don't know about you, but I probably have about 50 tabs open right now—and at least another 50 accounts for other SaaS apps. It’s completely overwhelming. It means your content is all over the place, and that makes it very, very hard to find what you're looking for. The good news is we have all these amazing LLMs coming out every day that can tell you about <a href="https://x.com/drewhouston/status/1981440222484451444" target="_blank">quantum physics</a>. But the bad news is they don’t have access to your content. All of your work content is proprietary. It's within your walled garden. It means most LLMs can’t help when it comes to your work.</p>
<p>That’s why we’ve been building <a href="https://dash.dropbox.com/" target="_blank">Dropbox Dash</a>. It doesn't just look at your Dropbox content. It connects to all your third-party apps and brings it into one place, so you can search, get answers, and do the agentic queries that you want to do at work.</p>
<p>Here’s a brief primer on our tech stack and how Dash works.</p>

</div>
<div class="section aem-GridColumn aem-GridColumn--default--12">
<div class="dr-article-content__section" id="the-context-engine-that-makes-dash-possible">
    <h2 class="dr-article-content__section-title">The context engine that makes Dash possible</h2>
</div>
</div>
<div class="image c04-image aem-GridColumn aem-GridColumn--default--12">
<div class="dr-image image cq-dd-image  ">
    <figure class="dr-margin-0 dr-display-inline-block">
        
            
    

        

        
        
        

        
        
        

        <!--optimized image webp-->
        

        
        <!-- <img data-sly-test.highRes="false"
             srcset="/cms/content/dam/dropbox/tech-blog/en-us/2026/january/maven/slides/yHmKaGFo.png 2x,  1x"
             src="/cms/content/dam/dropbox/tech-blog/en-us/2026/january/maven/slides/yHmKaGFo.png"
             aria-hidden=""
             alt=""
             class=""
             data-sly-attribute.width="1600"
             data-sly-attribute.height="900"
             data-aem-asset-id="190351d6-b756-40f4-9692-5cc2fb482cac:yHmKaGFo.png"
             data-trackable="true" />
        <img data-sly-test.highRes="false"
             srcset="/cms/content/dam/dropbox/tech-blog/en-us/2026/january/maven/slides/yHmKaGFo.png 2x,  1x"
             src="/cms/content/dam/dropbox/tech-blog/en-us/2026/january/maven/slides/yHmKaGFo.png"
             aria-hidden=""
             alt=""
             class=""
             data-sly-attribute.width="1600"
             data-sly-attribute.height="900"
             data-aem-asset-id="190351d6-b756-40f4-9692-5cc2fb482cac:yHmKaGFo.png"
             data-trackable="true" /> -->

        
         
        <img src="/cms/content/dam/dropbox/tech-blog/en-us/2026/january/maven/slides/yHmKaGFo.png/_jcr_content/renditions/yHmKaGFo.webp" fallbackimage="/cms/content/dam/dropbox/tech-blog/en-us/2026/january/maven/slides/yHmKaGFo.png" onerror="window.failedAttempts=0;this.setAttribute('src',this.getAttribute('fallbackimage'));window.failedAttempts++;if(window.failedAttempts == 1)this.onerror=null" aria-hidden="false" alt="" data-aem-asset-id="190351d6-b756-40f4-9692-5cc2fb482cac:yHmKaGFo.png" data-trackable="true" height="900" width="1600"/>
    

            
        
    </figure>
</div></div>
<div class="text parbase aem-GridColumn aem-GridColumn--default--12">
<p>First, we have our <b>connectors</b>. This is where we're building custom crawlers and connecting to all these different third-party apps. It’s not easy. Everything has its own rate limit, each has its own unique API quirks, each has its own ACL and permission system, etc. But getting that right is essential and getting all that content in one place is the goal.</p>
<p>Next, we're doing a lot of <b>content understanding</b>—and in certain cases, enriching the content itself. So, first we normalize a lot of the different files that come in and get it into a format like markdown. Then, we’re looking at extracting key information. We're going to be looking at titles, metadata, trying to extract links, and generate different embeddings.</p>
<p>For documents, this is fairly straightforward. Just grab the text, extract it, throw it in the index, and you're done. Images require media understanding. CLIP-based models are a good start, but complex images need true multimodal understanding. Then you get to PDFs, which might have text <i>and</i> figures and more. Audio clips need to be transcribed. And then finally you get to videos. What if a client has a video like <a href="https://www.youtube.com/watch?v=E8WaFvwtphY" target="_blank">this very famous scene from <i>Jurassic Park</i></a>. How would you find this later? There's no dialogue, so you can't really rely on pure transcription. This is where you would need to use a multimodal model and extract certain scenes, generate understanding for each one, and then store that.</p>

</div>
<div class="experiencefragment aem-GridColumn aem-GridColumn--default--12">
<div id="experiencefragment-24ea3f3b31" class="cmp-experiencefragment cmp-experiencefragment--dash-cta-for-tech-blog">

    



<div class="xf-content-height">
    


<div class="aem-Grid aem-Grid--12 aem-Grid--default--12 ">
    
    <div class="responsivegrid aem-GridColumn aem-GridColumn--default--12">


<div class="aem-Grid aem-Grid--12 aem-Grid--default--12 ">
    
    <div class="a08-html-embed c17-plain-html aem-GridColumn aem-GridColumn--default--12">

<style type="text/css"> 

@font-face {
font-family: 'AtlasGrotesk';
src: url('https://cdn.prod.website-files.com/65dcd70b48edc3a7b446950e/65dce019c63112e617513c94_AtlasGrotesk-Medium-Web-vfl38XiTL.woff2') format('woff2');
font-weight: 500;
font-style: normal;
font-display: swap;
}

@font-face {
font-family: 'AtlasGrotesk';
src: url('https://cdn.prod.website-files.com/65dcd70b48edc3a7b446950e/65dce019711b648fd1ccd24a_AtlasGrotesk-Regular-Web-vflk7bxjs.woff2') format('woff2');
font-weight: 400;
font-style: normal;
font-display: swap;
}
.xf-content-height {margin: 0;}
#cta { font-family: AtlasGrotesk,sans-serif; font-size: .900rem; text-decoration: none; background: #f7f5f2; line-height: 1.69; box-sizing: border-box;}
#cta-box { padding: 15px 20px 15px 20px; }
#cta-hed {font-weight: 500;}
#cta-indent {border-left: 5px solid #1e1919; padding-left:20px;}
#cta a:link, #cta a:visited  {text-decoration: none;}
#cta p { margin: 5px 0px 0px 0px; }

.dr-theme-dark #cta {background: #000;}
.dr-theme-dark #cta-box {border: 1px solid; border-bottom: 0;}
.dr-theme-dark #cta-indent {border-left: 5px solid #f7f5f2;}
.dr-theme-dark .button {background: #000;}

.button {
    background-color: #1e1919;
    color:  #f7f5f2;
    height: 2.5rem;
    padding: 10px 5px 10px 20px;
    font-size: 1rem;
    font-weight: 500;
    line-height: 1.2;
    transition: all .3s;
}

.button:hover { background-color: #0061ff; }

img {vertical-align: middle; padding: 0px 1px 2px 0px;}

.c17-plain-html {margin-bottom: 50px}

</style>

<div id="cta">
<div id="cta-box">
<div id="cta-indent">

<p id="cta-hed"><img src="https://cdn.prod.website-files.com/65dcd70b48edc3a7b446950e/670692ee7692f74d4834e4f4_Frame%201400006055.svg" loading="lazy"> Dropbox Dash: AI that understands your work
</p>

<p>Dash knows your context, your team, and your work, so your team can stay organized, easily find and share knowledge, and keep projects secure, all from one place. And soon, Dash is coming to Dropbox.</p>

</div>
</div>

<a href="https://dash.dropbox.com/?utm=blogs" target="_blank"><div class="button">Learn more →</div></a>

</div>
</div>

    
</div>
</div>

    
</div>

</div></div>

    
</div>
<div class="text parbase aem-GridColumn aem-GridColumn--default--12">
<p>After we understand the incoming content, we take it a step further to model all these pieces of information together as a graph. Meetings may have associated documents, associated people, transcripts, or prior notes. Building that cross-app intelligence is essential to providing better context for our users. This is where we're going to start to do the knowledge graph bundle that I'll talk more about later in depth.</p>
<p>From there, all that information (embeddings, chunks, contextual graph representations) flows into our <b>highly secure data stores</b>. Today we use both a lexical index—using BM25—and then store everything as dense vectors in a vector store. While this allows us to do hybrid retrieval, we found BM25 was very effective on its own with some relevant signals. It’s an amazing workhorse for building out an index.</p>
<p>Finally, we apply multiple ranking passes on any retrieved results so they are personalized and ACL’d to you.</p>

</div>
<div class="image c04-image aem-GridColumn aem-GridColumn--default--12">
<div class="dr-image image cq-dd-image  ">
    <figure class="dr-margin-0 dr-display-inline-block">
        
            
    

        

        
        
        

        
        
        

        <!--optimized image webp-->
        

        
        <!-- <img data-sly-test.highRes="false"
             srcset="/cms/content/dam/dropbox/tech-blog/en-us/2026/january/maven/slides/NXkT_VUD.png 2x,  1x"
             src="/cms/content/dam/dropbox/tech-blog/en-us/2026/january/maven/slides/NXkT_VUD.png"
             aria-hidden=""
             alt=""
             class=""
             data-sly-attribute.width="1600"
             data-sly-attribute.height="902"
             data-aem-asset-id="50460fde-0e9e-467e-b88a-e069a4851bd9:NXkT_VUD.png"
             data-trackable="true" />
        <img data-sly-test.highRes="false"
             srcset="/cms/content/dam/dropbox/tech-blog/en-us/2026/january/maven/slides/NXkT_VUD.png 2x,  1x"
             src="/cms/content/dam/dropbox/tech-blog/en-us/2026/january/maven/slides/NXkT_VUD.png"
             aria-hidden=""
             alt=""
             class=""
             data-sly-attribute.width="1600"
             data-sly-attribute.height="902"
             data-aem-asset-id="50460fde-0e9e-467e-b88a-e069a4851bd9:NXkT_VUD.png"
             data-trackable="true" /> -->

        
         
        <img src="/cms/content/dam/dropbox/tech-blog/en-us/2026/january/maven/slides/NXkT_VUD.png/_jcr_content/renditions/NXkT_VUD.webp" fallbackimage="/cms/content/dam/dropbox/tech-blog/en-us/2026/january/maven/slides/NXkT_VUD.png" onerror="window.failedAttempts=0;this.setAttribute('src',this.getAttribute('fallbackimage'));window.failedAttempts++;if(window.failedAttempts == 1)this.onerror=null" aria-hidden="false" alt="" data-aem-asset-id="50460fde-0e9e-467e-b88a-e069a4851bd9:NXkT_VUD.png" data-trackable="true" height="902" width="1600"/>
    

            
        
    </figure>
</div></div>
<div class="text parbase aem-GridColumn aem-GridColumn--default--12">
<p>Altogether, this is what we call our <b>context engine</b>. And once you have that, you can introduce APIs on top of it and <a href="https://dropbox.tech/machine-learning/building-dash-rag-multi-step-ai-agents-business-users" target="_blank">build entire products like Dash</a>.</p>

</div>
<div class="section aem-GridColumn aem-GridColumn--default--12">
<div class="dr-article-content__section" id="why-we-chose-index-based-retrieval">
    <h2 class="dr-article-content__section-title">Why we chose index-based retrieval</h2>
</div>
</div>
<div class="text parbase aem-GridColumn aem-GridColumn--default--12">
<p>Okay, but why build an index? Why did we even go down this route in the first place? Well, there's a bit of a choose-your-fighter kind of mentality in the world right now between federated retrieval and indexed retrieval. The difference is very classic software engineering. Are you going to process everything on the fly? That’s federated retrieval. Or are you going to try to pre-process it all at ingestion time? That's index-based retrieval. And there are pros and cons to each approach.</p>
<p>Federated retrieval is very easy to get up and running. You don't have to worry about storage costs. The data is mostly fresh. You can keep adding more MCP servers and new connectors. But there are some big-time weaknesses here. You're at the mercy of all these different APIs or MCP servers which are going to differ in speed, quality, and ranking. You’re also limited in what you can access. You can access <i>your</i> information, but you probably don’t have access to company-wide connectors—meaning you can’t access content that’s shared across the whole company. And you have to do a lot of work on-the-fly in the post-processing. Once the data comes back, you have to merge information and potentially do re-ranking. And if you're using a lot of chatbots today with MCP, you're going to see that token count go up and up. It takes a lot of tokens to reason over this amount of information.</p>
<p>On the flip side, with index-based retrieval, you <i>do</i> now have access to those company connectors. And because you have time on your side, you can pre-process that content and create these really interesting enriched data sets that don't exist on their own. You can also do a lot more offline ranking experiments. You can try different methods to improve your recall, and it’s very, very fast. But it's also a ton of work—and a lot of custom work. This is not for the faint of heart. You have to write a lot of custom connectors. As for ingestion time, you're going to have freshness issues if you're not good with understanding rate limits. It can also be extremely expensive to host this information, and then you have to decide how to store it. Am I using a vector database, like classic RAG from many years ago? Am I going the BM25 route? Do I want to do hybrid? Do I want to do a full graph RAG, which is what we ended up going with? There are a lot of decisions you have to make.</p>

</div>
<div class="image c04-image aem-GridColumn aem-GridColumn--default--12">
<div class="dr-image image cq-dd-image  ">
    <figure class="dr-margin-0 dr-display-inline-block">
        
            
    

        

        
        
        

        
        
        

        <!--optimized image webp-->
        

        
        <!-- <img data-sly-test.highRes="false"
             srcset="/cms/content/dam/dropbox/tech-blog/en-us/2026/january/maven/slides/JmCHl8Y4.png 2x,  1x"
             src="/cms/content/dam/dropbox/tech-blog/en-us/2026/january/maven/slides/JmCHl8Y4.png"
             aria-hidden=""
             alt=""
             class=""
             data-sly-attribute.width="1600"
             data-sly-attribute.height="873"
             data-aem-asset-id="9f2e4f5b-91c7-453c-adbb-89ced11372f2:JmCHl8Y4.png"
             data-trackable="true" />
        <img data-sly-test.highRes="false"
             srcset="/cms/content/dam/dropbox/tech-blog/en-us/2026/january/maven/slides/JmCHl8Y4.png 2x,  1x"
             src="/cms/content/dam/dropbox/tech-blog/en-us/2026/january/maven/slides/JmCHl8Y4.png"
             aria-hidden=""
             alt=""
             class=""
             data-sly-attribute.width="1600"
             data-sly-attribute.height="873"
             data-aem-asset-id="9f2e4f5b-91c7-453c-adbb-89ced11372f2:JmCHl8Y4.png"
             data-trackable="true" /> -->

        
         
        <img src="/cms/content/dam/dropbox/tech-blog/en-us/2026/january/maven/slides/JmCHl8Y4.png/_jcr_content/renditions/JmCHl8Y4.webp" fallbackimage="/cms/content/dam/dropbox/tech-blog/en-us/2026/january/maven/slides/JmCHl8Y4.png" onerror="window.failedAttempts=0;this.setAttribute('src',this.getAttribute('fallbackimage'));window.failedAttempts++;if(window.failedAttempts == 1)this.onerror=null" aria-hidden="false" alt="" data-aem-asset-id="9f2e4f5b-91c7-453c-adbb-89ced11372f2:JmCHl8Y4.png" data-trackable="true" height="873" width="1600"/>
    

            
        
    </figure>
</div></div>
<div class="section aem-GridColumn aem-GridColumn--default--12">
<div class="dr-article-content__section" id="making-mcp-work-at-dropbox-scale">
    <h2 class="dr-article-content__section-title">Making MCP work at Dropbox scale</h2>
</div>
</div>
<div class="text parbase aem-GridColumn aem-GridColumn--default--12">
<p>Now what about MCP? There was a lot of hype when MCP burst onto the scene about a year ago. Everybody was talking about it: “You don't need any of these APIs anymore, you just add MCP to your agent.” Sounds great, right? But there are some major challenges with how MCP is typically implemented.</p>
<p>MCP tool definitions, in particular, take up <a href="https://dropbox.tech/machine-learning/how-dash-uses-context-engineering-for-smarter-ai" target="_blank">valuable real estate in your context window</a>. We’re noticing quite a bit of degradation in the effectiveness of our chat and agents (very classic context rot). So with Dash, we're trying to cap things to about 100,000 tokens. But those tool definitions do fill up quickly. The results are quite significant, especially if you're doing retrieval. You're getting a lot of content back, and you're immediately going to fill up that context window. It's going to be very problematic. It’s also incredibly slow. So, if you’re using MCP with some agents today, even a simple query can take up to 45 seconds—whereas with the raw index, you're getting all the content coming back very quickly, within seconds. </p>
<p>Here are some of the ways we’ve solved for that:</p>
<ul>
<li>We've got our index, and we can wrap that around a tool. Let's call it a super tool. And so instead of 5-10 different retrieval tools, we just have one. This helps a ton with cleaning things up overall. </li>
<li>Modeling data within knowledge graphs can significantly cut our token usage as well, because you're really just getting the most relevant information for the query. </li>
<li>Tool results come back with a huge amount of context, so we actually choose to store that locally. We do not put that in the LLM context window. </li>
<li>Finally, we use a lot of sub-agents for very complex agentic queries, and have a classifier effectively pick the sub-agent with a much more narrow set of tools.</li>
</ul>

</div>
<div class="image c04-image aem-GridColumn aem-GridColumn--default--12">
<div class="dr-image image cq-dd-image  ">
    <figure class="dr-margin-0 dr-display-inline-block">
        
            
    

        

        
        
        

        
        
        

        <!--optimized image webp-->
        

        
        <!-- <img data-sly-test.highRes="false"
             srcset="/cms/content/dam/dropbox/tech-blog/en-us/2026/january/maven/slides/QmiszUPw.png 2x,  1x"
             src="/cms/content/dam/dropbox/tech-blog/en-us/2026/january/maven/slides/QmiszUPw.png"
             aria-hidden=""
             alt=""
             class=""
             data-sly-attribute.width="1600"
             data-sly-attribute.height="899"
             data-aem-asset-id="0aab861f-cefd-4d50-8765-3157d873a4f4:QmiszUPw.png"
             data-trackable="true" />
        <img data-sly-test.highRes="false"
             srcset="/cms/content/dam/dropbox/tech-blog/en-us/2026/january/maven/slides/QmiszUPw.png 2x,  1x"
             src="/cms/content/dam/dropbox/tech-blog/en-us/2026/january/maven/slides/QmiszUPw.png"
             aria-hidden=""
             alt=""
             class=""
             data-sly-attribute.width="1600"
             data-sly-attribute.height="899"
             data-aem-asset-id="0aab861f-cefd-4d50-8765-3157d873a4f4:QmiszUPw.png"
             data-trackable="true" /> -->

        
         
        <img src="/cms/content/dam/dropbox/tech-blog/en-us/2026/january/maven/slides/QmiszUPw.png/_jcr_content/renditions/QmiszUPw.webp" fallbackimage="/cms/content/dam/dropbox/tech-blog/en-us/2026/january/maven/slides/QmiszUPw.png" onerror="window.failedAttempts=0;this.setAttribute('src',this.getAttribute('fallbackimage'));window.failedAttempts++;if(window.failedAttempts == 1)this.onerror=null" aria-hidden="false" alt="" data-aem-asset-id="0aab861f-cefd-4d50-8765-3157d873a4f4:QmiszUPw.png" data-trackable="true" height="899" width="1600"/>
    

            
        
    </figure>
</div></div>
<div class="section aem-GridColumn aem-GridColumn--default--12">
<div class="dr-article-content__section" id="our-approach-to-knowledge-graphs">
    <h2 class="dr-article-content__section-title">Our approach to knowledge graphs</h2>
</div>
</div>
<div class="text parbase aem-GridColumn aem-GridColumn--default--12">
<p>The next question that comes up a lot: are knowledge graphs worth it? Well, let’s look at how a knowledge graph works.</p>
<p>You start by modeling these different relationships across these various apps. For example, say you've got a calendar invite. It might have attachments, meeting minutes, a transcript. Of course, it also has all the attendees, and maybe there's even a Jira project associated. Every app that we connect with has its own concept or definition of people, and so coming up with a canonical ID for who someone is is very, very impactful for us overall. Being able to model something like that is incredibly powerful. You can go view somebody's profile on Dash today, but it also helps a ton in relevance and retrieval.</p>

</div>
<div class="image c04-image aem-GridColumn aem-GridColumn--default--12">
<div class="dr-image image cq-dd-image  ">
    <figure class="dr-margin-0 dr-display-inline-block">
        
            
    

        

        
        
        

        
        
        

        <!--optimized image webp-->
        

        
        <!-- <img data-sly-test.highRes="false"
             srcset="/cms/content/dam/dropbox/tech-blog/en-us/2026/january/maven/slides/sc791hed.png 2x,  1x"
             src="/cms/content/dam/dropbox/tech-blog/en-us/2026/january/maven/slides/sc791hed.png"
             aria-hidden=""
             alt=""
             class=""
             data-sly-attribute.width="1600"
             data-sly-attribute.height="899"
             data-aem-asset-id="182ec97b-b049-4a10-9f79-9a5ef1d101ac:sc791hed.png"
             data-trackable="true" />
        <img data-sly-test.highRes="false"
             srcset="/cms/content/dam/dropbox/tech-blog/en-us/2026/january/maven/slides/sc791hed.png 2x,  1x"
             src="/cms/content/dam/dropbox/tech-blog/en-us/2026/january/maven/slides/sc791hed.png"
             aria-hidden=""
             alt=""
             class=""
             data-sly-attribute.width="1600"
             data-sly-attribute.height="899"
             data-aem-asset-id="182ec97b-b049-4a10-9f79-9a5ef1d101ac:sc791hed.png"
             data-trackable="true" /> -->

        
         
        <img src="/cms/content/dam/dropbox/tech-blog/en-us/2026/january/maven/slides/sc791hed.png/_jcr_content/renditions/sc791hed.webp" fallbackimage="/cms/content/dam/dropbox/tech-blog/en-us/2026/january/maven/slides/sc791hed.png" onerror="window.failedAttempts=0;this.setAttribute('src',this.getAttribute('fallbackimage'));window.failedAttempts++;if(window.failedAttempts == 1)this.onerror=null" aria-hidden="false" alt="" data-aem-asset-id="182ec97b-b049-4a10-9f79-9a5ef1d101ac:sc791hed.png" data-trackable="true" height="899" width="1600"/>
    

            
        
    </figure>
</div></div>
<div class="text parbase aem-GridColumn aem-GridColumn--default--12">
<p>Say that I want to find all the past <a href="https://dropbox.tech/machine-learning/how-dash-uses-context-engineering-for-smarter-ai" target="_blank">context engineering</a> talks from Jason. But who <i>is</i> Jason? How do you know that? Well, if you have this graph—this people model—you can then go ahead and fetch that and add that to the context, and it's not having to do a ton of different retrieval overall. Fantastic. And we use normalized discounted cumulative gain (NDCG) a lot to score the results to retrieve. But just by doing this people-based result we saw some really nice wins.</p>
<p>The architecture itself is complicated. I won't talk a ton here, but it's important to realize we're not just storing a one-to-one mapping of source doc to end doc. We do want to derive and create more unique characteristics. And the other key insight here is we're not storing these graphs in a graph database. We did experiment with that. The latency and query pattern were a challenge. Trying to figure out that hybrid retrieval was a challenge. And so we ended up building these graphs in a more unique way. We’re staging it more asynchronously, we're building out these relationships, and then we create these knowledge bundles. So again, it's not necessarily a graph, but think of it almost like an embedding—like a summary of that graph. And it becomes these little contexts that contain all this information. And with that context, we actually just send it on through the exact same index pipeline that we have for all the other content. So things will get chunked and things will generate embeddings for both lexical as well as semantic retrieval.</p>

</div>
<div class="image c04-image aem-GridColumn aem-GridColumn--default--12">
<div class="dr-image image cq-dd-image  ">
    <figure class="dr-margin-0 dr-display-inline-block">
        
            
    

        

        
        
        

        
        
        

        <!--optimized image webp-->
        

        
        <!-- <img data-sly-test.highRes="false"
             srcset="/cms/content/dam/dropbox/tech-blog/en-us/2026/january/maven/slides/YxJn9XDG.png 2x,  1x"
             src="/cms/content/dam/dropbox/tech-blog/en-us/2026/january/maven/slides/YxJn9XDG.png"
             aria-hidden=""
             alt=""
             class=""
             data-sly-attribute.width="1600"
             data-sly-attribute.height="899"
             data-aem-asset-id="81197e04-1b8d-4d6d-9844-1b448d4805fc:YxJn9XDG.png"
             data-trackable="true" />
        <img data-sly-test.highRes="false"
             srcset="/cms/content/dam/dropbox/tech-blog/en-us/2026/january/maven/slides/YxJn9XDG.png 2x,  1x"
             src="/cms/content/dam/dropbox/tech-blog/en-us/2026/january/maven/slides/YxJn9XDG.png"
             aria-hidden=""
             alt=""
             class=""
             data-sly-attribute.width="1600"
             data-sly-attribute.height="899"
             data-aem-asset-id="81197e04-1b8d-4d6d-9844-1b448d4805fc:YxJn9XDG.png"
             data-trackable="true" /> -->

        
         
        <img src="/cms/content/dam/dropbox/tech-blog/en-us/2026/january/maven/slides/YxJn9XDG.png/_jcr_content/renditions/YxJn9XDG.webp" fallbackimage="/cms/content/dam/dropbox/tech-blog/en-us/2026/january/maven/slides/YxJn9XDG.png" onerror="window.failedAttempts=0;this.setAttribute('src',this.getAttribute('fallbackimage'));window.failedAttempts++;if(window.failedAttempts == 1)this.onerror=null" aria-hidden="false" alt="" data-aem-asset-id="81197e04-1b8d-4d6d-9844-1b448d4805fc:YxJn9XDG.png" data-trackable="true" height="899" width="1600"/>
    

            
        
    </figure>
</div></div>
<div class="section aem-GridColumn aem-GridColumn--default--12">
<div class="dr-article-content__section" id="using-an-llm-to-judge-relevancy">
    <h2 class="dr-article-content__section-title">Using an LLM to judge relevancy</h2>
</div>
</div>
<div class="text parbase aem-GridColumn aem-GridColumn--default--12">
<p>Alright, we've indexed all this content. We've got content understanding. We've done a ton of work on trying to model these relationships. But did the retrieval quality actually improve? How do we know? </p>
<p>Take Google search, for example. You have your 10 blue links, and the audience for those results are humans. If your results are high quality, the humans will tell you by clicking. You can quickly get some amazing signal this way. The model is either working or it isn’t.</p>
<p>In the world of chat, you're still retrieving results, but it's not for the human. It's for this large language model. And so you no longer have those humans to help us out. So what do you do? That's where you want to use <a href="https://dropbox.tech/machine-learning/practical-blueprint-evaluating-conversational-ai-at-scale-dash" target="_blank">LLMs as a judge</a>. Broadly speaking, what you're trying to do is judge how relevant a piece of information is between, say, one and five, and then use that to improve over time.</p>
<p>Humans can still help here. Sometimes they give you thumbs ups and thumbs down on the quality of your results. You can also bring in human evaluators to help you. When we started these experiments, we asked ourselves: How accurate can we get our judge to match what a human will do? And so we had a bunch of our engineers label a ton of documents to see how much of a disagreement there was between the human and the LLM as a judge. The first prompt for our judge wasn’t bad—8% disagreed—but the lower, the better.</p>
<p>Next, we continued to refine the prompt. You know, classic prompt tuning like “provide explanations for what you're doing.” And sure enough, disagreements went down. Then, we just upgraded the model itself to OpenAI’s o3. It's a reasoning model, far more powerful, and guess what? Disagreements with the humans went down further. </p>
<p>Finally, a big problem with using an LLM as a judge in a work context is that it doesn't know things like acronyms. If I were to say, “What is RAG?”—and hopefully it knows what RAG is—what if it hasn’t been trained on that? Sometimes, the judge needs to go get that context. And so, this is a little tongue-in-cheek, but we call this RAG as a judge. It can't just be using pre-computed information. Sometimes it has to go fetch some context itself. And with that, we dropped disagreements even further. </p>

</div>
<div class="image c04-image aem-GridColumn aem-GridColumn--default--12">
<div class="dr-image image cq-dd-image  ">
    <figure class="dr-margin-0 dr-display-inline-block">
        
            
    

        

        
        
        

        
        
        

        <!--optimized image webp-->
        

        
        <!-- <img data-sly-test.highRes="false"
             srcset="/cms/content/dam/dropbox/tech-blog/en-us/2026/january/maven/slides/_6BAuy4w.png 2x,  1x"
             src="/cms/content/dam/dropbox/tech-blog/en-us/2026/january/maven/slides/_6BAuy4w.png"
             aria-hidden=""
             alt=""
             class=""
             data-sly-attribute.width="1600"
             data-sly-attribute.height="899"
             data-aem-asset-id="875d3cbd-4ee9-4977-b9e1-cbc09ca765a7:_6BAuy4w.png"
             data-trackable="true" />
        <img data-sly-test.highRes="false"
             srcset="/cms/content/dam/dropbox/tech-blog/en-us/2026/january/maven/slides/_6BAuy4w.png 2x,  1x"
             src="/cms/content/dam/dropbox/tech-blog/en-us/2026/january/maven/slides/_6BAuy4w.png"
             aria-hidden=""
             alt=""
             class=""
             data-sly-attribute.width="1600"
             data-sly-attribute.height="899"
             data-aem-asset-id="875d3cbd-4ee9-4977-b9e1-cbc09ca765a7:_6BAuy4w.png"
             data-trackable="true" /> -->

        
         
        <img src="/cms/content/dam/dropbox/tech-blog/en-us/2026/january/maven/slides/_6BAuy4w.png/_jcr_content/renditions/_6BAuy4w.webp" fallbackimage="/cms/content/dam/dropbox/tech-blog/en-us/2026/january/maven/slides/_6BAuy4w.png" onerror="window.failedAttempts=0;this.setAttribute('src',this.getAttribute('fallbackimage'));window.failedAttempts++;if(window.failedAttempts == 1)this.onerror=null" aria-hidden="false" alt="" data-aem-asset-id="875d3cbd-4ee9-4977-b9e1-cbc09ca765a7:_6BAuy4w.png" data-trackable="true" height="899" width="1600"/>
    

            
        
    </figure>
</div></div>
<div class="section aem-GridColumn aem-GridColumn--default--12">
<div class="dr-article-content__section" id="prompt-optimization-with-dspy">
    <h2 class="dr-article-content__section-title">Prompt optimization with DSPy</h2>
</div>
</div>
<div class="text parbase aem-GridColumn aem-GridColumn--default--12">
<p>There's a growing community around prompt optimizers, and one of the technologies in particular we've been using is DSPy. It helps optimize your prompts. It tries to get the most accurate information based on a set of evals. So by bringing in DSPy, we got even better results overall.</p>
<p>It might be impossible to get to zero disagreements. Even humans—multiple humans—will disagree on the relevance set. But we're going to keep grinding on this. And even if we can't get to zero, we're actually quite pleased with some of the results we're getting with DSPy.</p>
<p>One thing to note: We saw some really interesting emergent behavior happening with DSPy. Instead of simply telling us what the improvements could be, we noticed we could create bullet points with the different disagreements and then have DSPy try to optimize the bullets themselves. So if there were multiple disagreements, it would try to reduce those disagreements overall, and we started to create this really nice flywheel and ended up getting some nice results.</p>

</div>
<div class="image c04-image aem-GridColumn aem-GridColumn--default--12">
<div class="dr-image image cq-dd-image  ">
    <figure class="dr-margin-0 dr-display-inline-block">
        
            
    

        

        
        
        

        
        
        

        <!--optimized image webp-->
        

        
        <!-- <img data-sly-test.highRes="false"
             srcset="/cms/content/dam/dropbox/tech-blog/en-us/2026/january/maven/slides/KKgSMlQW.png 2x,  1x"
             src="/cms/content/dam/dropbox/tech-blog/en-us/2026/january/maven/slides/KKgSMlQW.png"
             aria-hidden=""
             alt=""
             class=""
             data-sly-attribute.width="1600"
             data-sly-attribute.height="901"
             data-aem-asset-id="d7bcab44-2939-449a-b2fc-26484d3fe1b6:KKgSMlQW.png"
             data-trackable="true" />
        <img data-sly-test.highRes="false"
             srcset="/cms/content/dam/dropbox/tech-blog/en-us/2026/january/maven/slides/KKgSMlQW.png 2x,  1x"
             src="/cms/content/dam/dropbox/tech-blog/en-us/2026/january/maven/slides/KKgSMlQW.png"
             aria-hidden=""
             alt=""
             class=""
             data-sly-attribute.width="1600"
             data-sly-attribute.height="901"
             data-aem-asset-id="d7bcab44-2939-449a-b2fc-26484d3fe1b6:KKgSMlQW.png"
             data-trackable="true" /> -->

        
         
        <img src="/cms/content/dam/dropbox/tech-blog/en-us/2026/january/maven/slides/KKgSMlQW.png/_jcr_content/renditions/KKgSMlQW.webp" fallbackimage="/cms/content/dam/dropbox/tech-blog/en-us/2026/january/maven/slides/KKgSMlQW.png" onerror="window.failedAttempts=0;this.setAttribute('src',this.getAttribute('fallbackimage'));window.failedAttempts++;if(window.failedAttempts == 1)this.onerror=null" aria-hidden="false" alt="" data-aem-asset-id="d7bcab44-2939-449a-b2fc-26484d3fe1b6:KKgSMlQW.png" data-trackable="true" height="901" width="1600"/>
    

            
        
    </figure>
</div></div>
<div class="text parbase aem-GridColumn aem-GridColumn--default--12">
<p>There are some other benefits of DSPy. So first, obviously, is prompt optimization. It helped us quite a bit in our LLM-as-a-judge area. Again, that's a prime place to think about DSPy right now, because LLMs as a judge have very crystal clear rubrics and evals. You know exactly what the outcome should be. You just need to have the ultimate prompt, and it’s really good for that. We're going to start to experiment with DSPy across our entire stack. We have over 30 different prompts today throughout the Dash stack, whether that's in the ingest path, LLMs as a judge, some offline evals, as well as our online agentic platform approach.</p>
<p>The next one is prompt management at scale. I mentioned we've got about 30 prompts overall, and at any given time we might have 5 to 15 different engineers tweaking these prompts and trying to get more improvements. And it's a little silly if you think about it. You've got this text string that you've checked in to your code repository; but then there's an edge case, this chat session didn't work. So you go in and fix it, but then something else breaks, and it becomes a bit of a whack-a-mole. And so it's very powerful to just define things in a more of a programmatic way and let these tools spit out the actual prompt themselves. It just works better at scale.</p>
<p>And the last really great benefit we like is around model switching. So, every model out there is a bit unique. They have their own quirks, and there's always different ways to prompt them. And anytime you bring in a new model, you have to spend a bunch of time optimizing the prompt again. But with DSPy, you just plug the model in, define your goals, and out spits the prompt that works. So you can do this model switching far more rapidly—and this is really beneficial for modern agentic systems, because you just don't have one giant LLM. You're going to have a planning LLM, you're going to have all these smaller sub-agents, and those sub-agents might be very narrowly focused. You probably want to pick a model that's highly tuned to that particular task, so having something like a prompt optimizer is really powerful.</p>

</div>
<div class="section aem-GridColumn aem-GridColumn--default--12">
<div class="dr-article-content__section" id="make-it-work-then-make-it-better">
    <h2 class="dr-article-content__section-title">Make it work, then make it better</h2>
</div>
</div>
<div class="text parbase aem-GridColumn aem-GridColumn--default--12">
<p>To wrap things up, here are some key takeaways:</p>
<ul>
<li>We do find the index is superior. It is a lot of work, so don’t approach this lightly. Understand that you have to build up quite a bit of infrastructure and different data pipelines to get this working. Thinking through your data, storage, how you want to index, the retrieval—it's a lot of work, but worth it at scale.</li>
<li>Cross-app intelligence absolutely does work. You want to create those relationships. You want to be able to bring in the org chart whenever you're adding different prompts. But it also isn't easy. If I knew the exact prompts everybody was going to ask 10 times a day, I would go build a more optimal bundle of that knowledge and store that, so it's very, very fast and accurate. You just don't have that benefit all the time.</li>
<li>On the MCP side, we highly recommend you limit tool usage. Instead, try to think about super tools. Explore tool selection. Potentially have sub-agents with limits on tool calls. Really guard your context window. </li>
<li>Investing in effective LLM judges is incredibly important. A lot of times that initial prompt is all people do. They're like, “Alright, good. Done. It's good enough.” But if you can grind that down and get the accuracy to improve, it really lifts all boats—and you're going to see some really nice outcomes across the board.</li>
<li>Prompt optimizers do work at scale. They work at any scale, but they’re absolutely essential at scale.</li>
</ul>
<p>My final, overall takeaway is the classic software engineering concept of: make it work, then make it better. A lot of the techniques and things I've described here are things that we've been doing over the last few years with a big engineering team working on this day-in and day-out. If you're just getting started, absolutely invest in those MCP tools and everything on the real-time side. And then, over time, as you start to see what your customers are doing and you start to get some more scale, look for opportunities to optimize overall.</p>
<p style="text-align: center;">~ ~ ~ </p>
<p><i>If building innovative products, experiences, and infrastructure excites you, come build the future with us! Visit </i><a href="https://jobs.dropbox.com/" target="_blank"><i>jobs.dropbox.com</i></a><i> to see our open roles.</i></p>

</div>

    
</div>
]]></content:encoded>
            			
                <media:thumbnail url="https://dropbox.tech/cms/content/dam/dropbox/tech-blog/en-us/2026/january/maven/headers/Machine Learning-Knowledge Graphs MCP and DSPy with Josh Clemm-375x150-dark.png" />
                <media:content url="https://dropbox.tech/cms/content/dam/dropbox/tech-blog/en-us/2026/january/maven/headers/Machine Learning-Knowledge Graphs MCP and DSPy with Josh Clemm-375x150-dark.png" medium="image">
                    <media:title type="html">Engineering VP Josh Clemm on how we use knowledge graphs, MCP, and DSPy in Dash</media:title>
                </media:content>
       		 </item>
                    
        				<item>
                        <title>Inside the feature store powering real-time AI in Dropbox Dash</title>
                        
            			<link>https://dropbox.tech/machine-learning/feature-store-powering-realtime-ai-in-dropbox-dash</link>

                            
            			<dc:creator>
                            Facundo Agriel,Ishan Mishra,Eric Wang,Dmitriy Meyerzon,Dmitriy Meyerzon,Hicham Badri,Appu Shaji,Craig Wilhite,Josh Clemm,Jason Shang,Artem Nabirkin
            			</dc:creator>
            			
            				<category>LLM</category>
							
            				<category>AI</category>
							
            				<category>Machine Learning</category>
							
            				<category>Dash</category>
							
                            
            			<description><![CDATA[The feature store is a critical part of how we rank and retrieve the right context across your work.]]></description>
            			<guid>https://dropbox.tech/machine-learning/feature-store-powering-realtime-ai-in-dropbox-dash</guid>
                        <pubDate>Thu, 18 Dec 2025 10:00:00 -0800</pubDate>
                            
                        <content:encoded><![CDATA[


<div class="aem-Grid aem-Grid--12 aem-Grid--default--12 ">
    
    <div class="text parbase aem-GridColumn aem-GridColumn--default--12">
<p><a href="https://www.dash.dropbox.com/" target="_blank"><b>Dropbox Dash</b></a> uses AI to understand questions about your files, work chats, and company content, bringing everything together in one place for deeper, more focused work. With tens of thousands of potential work documents to consider, both search and agents rely on a ranking system powered by real-time machine learning to find the right files fast. At the core of that ranking in Dash is our <b>feature store</b>, a system that manages and delivers the data signals (“features”) our models use to predict relevance. </p>
<p>To help users find exactly what they need, Dash has to read between the lines of user behavior across file types, company content, and the messy, fragmented realities of collaboration. Then it has to surface the most relevant documents, images, and conversations when and how they’re needed. The feature store is a critical part of how we rank and retrieve the right context across your work. It’s built to serve features quickly, keep pace as user behavior changes, and let engineers move fast from idea to production. <i>(For more on how feature stores connect to context engineering in Dash, check out our </i><a href="https://dropbox.tech/machine-learning/how-dash-uses-context-engineering-for-smarter-ai" target="_blank"><i>deep dive on context engineering right here</i></a><i>.)</i></p>
<p>In this post, we’ll walk through how we built the feature store behind Dash’s ranking system, why off-the-shelf solutions didn’t fit, how we designed for speed and scale, and what it takes to keep features fresh as user behavior changes. Along the way, we’ll share the tradeoffs we made and the lessons that shaped our approach.</p>

</div>
<div class="experiencefragment aem-GridColumn aem-GridColumn--default--12">
<div id="experiencefragment-7e9aae63c1" class="cmp-experiencefragment cmp-experiencefragment--dash-cta-for-tech-blog">

    



<div class="xf-content-height">
    


<div class="aem-Grid aem-Grid--12 aem-Grid--default--12 ">
    
    <div class="responsivegrid aem-GridColumn aem-GridColumn--default--12">


<div class="aem-Grid aem-Grid--12 aem-Grid--default--12 ">
    
    <div class="a08-html-embed c17-plain-html aem-GridColumn aem-GridColumn--default--12">

<style type="text/css"> 

@font-face {
font-family: 'AtlasGrotesk';
src: url('https://cdn.prod.website-files.com/65dcd70b48edc3a7b446950e/65dce019c63112e617513c94_AtlasGrotesk-Medium-Web-vfl38XiTL.woff2') format('woff2');
font-weight: 500;
font-style: normal;
font-display: swap;
}

@font-face {
font-family: 'AtlasGrotesk';
src: url('https://cdn.prod.website-files.com/65dcd70b48edc3a7b446950e/65dce019711b648fd1ccd24a_AtlasGrotesk-Regular-Web-vflk7bxjs.woff2') format('woff2');
font-weight: 400;
font-style: normal;
font-display: swap;
}
.xf-content-height {margin: 0;}
#cta { font-family: AtlasGrotesk,sans-serif; font-size: .900rem; text-decoration: none; background: #f7f5f2; line-height: 1.69; box-sizing: border-box;}
#cta-box { padding: 15px 20px 15px 20px; }
#cta-hed {font-weight: 500;}
#cta-indent {border-left: 5px solid #1e1919; padding-left:20px;}
#cta a:link, #cta a:visited  {text-decoration: none;}
#cta p { margin: 5px 0px 0px 0px; }

.dr-theme-dark #cta {background: #000;}
.dr-theme-dark #cta-box {border: 1px solid; border-bottom: 0;}
.dr-theme-dark #cta-indent {border-left: 5px solid #f7f5f2;}
.dr-theme-dark .button {background: #000;}

.button {
    background-color: #1e1919;
    color:  #f7f5f2;
    height: 2.5rem;
    padding: 10px 5px 10px 20px;
    font-size: 1rem;
    font-weight: 500;
    line-height: 1.2;
    transition: all .3s;
}

.button:hover { background-color: #0061ff; }

img {vertical-align: middle; padding: 0px 1px 2px 0px;}

.c17-plain-html {margin-bottom: 50px}

</style>

<div id="cta">
<div id="cta-box">
<div id="cta-indent">

<p id="cta-hed"><img src="https://cdn.prod.website-files.com/65dcd70b48edc3a7b446950e/670692ee7692f74d4834e4f4_Frame%201400006055.svg" loading="lazy"> Dropbox Dash: AI that understands your work
</p>

<p>Dash knows your context, your team, and your work, so your team can stay organized, easily find and share knowledge, and keep projects secure, all from one place. And soon, Dash is coming to Dropbox.</p>

</div>
</div>

<a href="https://dash.dropbox.com/?utm=blogs" target="_blank"><div class="button">Learn more →</div></a>

</div>
</div>

    
</div>
</div>

    
</div>

</div></div>

    
</div>
<div class="section aem-GridColumn aem-GridColumn--default--12">
<div class="dr-article-content__section" id="our-goals-and-requirements">
    <h2 class="dr-article-content__section-title">Our goals and requirements</h2>
</div>
</div>
<div class="text parbase aem-GridColumn aem-GridColumn--default--12">
<p>Building a feature store for Dash wasn’t just a matter of picking something off the shelf, and there are a few reasons why. For one, our infrastructure is split across two very different worlds: an on-premises ecosystem designed for low-latency service-to-service communication, and a Spark-native cloud environment where feature engineering and large-scale data processing happens. This split ruled out standard cloud-native feature stores and forced us to find a way to bridge both systems without slowing down development velocity.</p>
<p>On top of that, Dash’s search ranking system brought its own scaling challenge. A single user query doesn’t just pull up one document. Instead, it triggers our ranker to evaluate many files, each requiring dozens of behavioral and contextual features. What starts as one search quickly fans out into thousands of feature lookups across interaction history, metadata, collaboration patterns, and real-time signals. Ultimately, our feature store had to handle those kinds of massive parallel reads while still meeting strict, <b>sub-100ms</b> latency budgets.</p>
<p>Relevance also depends on speed and capturing user intent in real-time. If a user opens a document or joins a Slack channel, that signal should show up in their next search—within a few seconds—which meant building an ingestion pipeline that could keep up with user behavior at scale.</p>
<p>Finally, we had to reconcile two very different computation patterns. Some features naturally fit real-time streaming, while others depend on batch processing of historical data. We needed a unified framework that could support both efficiently, thereby reducing cognitive load for engineers and giving them a faster path from idea to production-ready features.</p>

</div>
<div class="section aem-GridColumn aem-GridColumn--default--12">
<div class="dr-article-content__section" id="designing-our-hybrid-feature-store">
    <h2 class="dr-article-content__section-title">Designing our hybrid feature store</h2>
</div>
</div>
<div class="text parbase aem-GridColumn aem-GridColumn--default--12">
<p>After surveying the feature store landscape—Feast, Hopsworks, Featureform, Feathr, Databricks, and Tecton—<a href="https://feast.dev/" target="_blank">Feast</a> stood out for two reasons. First, its clear separation between feature definitions and infrastructure concerns meant our machine learning engineers could focus purely on writing PySpark transformations rather than the serving, storage, or orchestration complexity. Second, Feast’s modular architecture and extensive adapter ecosystem made it straightforward to integrate with our existing infrastructure. (An adapter refers to a Feast-provided interface that integrates its framework with different backend systems.) Its <a href="https://aws.amazon.com/dynamodb/" target="_blank">AWS DynamoDB</a> adapter was particularly crucial, allowing us to leverage Dynovault—our in-house DynamoDB-compatible storage solution—to meet latency requirements while lowering costs.</p>
<p>Our Feast-based architecture combines three key components, each optimized for its role.</p>
<p><b>Feast</b> gave us the orchestration layer and serving APIs, but we swapped out its Python online serving path for our own Go service so we could actually hit the concurrency and latency numbers we needed. </p>
<p><b>Cloud-based storage</b> took care of the heavy lifting of offline indexing and storage, while <b>Spark </b>jobs handled feature ingestion and computation. </p>
<p><b>Dynovault</b> handled the instant feature lookups needed for each search query. Co-located with inference workloads and leveraging Dropbox’s <a href="https://docs.aws.amazon.com/whitepapers/latest/hybrid-cloud-with-aws/example-dropboxs-hybrid-cloud-architecture.html" target="_blank">hybrid cloud infrastructure</a>, Dynovault avoids the delay of public internet calls and reliably delivers ~20ms client-side latency while balancing cost and geographic scalability. </p>
<p>Around this core architecture, we added observability through job failure monitoring, freshness tracking, and data lineage visibility. The result is a streamlined experience: engineers choose a data source, write PySpark transformations, and request features where needed, while the infrastructure abstracts away offline and online data management, pipeline orchestration, low-latency serving, and data freshness guarantees.</p>

</div>
<div class="image c04-image aem-GridColumn aem-GridColumn--default--12">
<div class="dr-image image cq-dd-image  ">
    <figure class="dr-margin-0 dr-display-inline-block">
        
            
    

        

        
        
        

        
        
        

        <!--optimized image webp-->
        

        
        <!-- <img data-sly-test.highRes="false"
             srcset="/cms/content/dam/dropbox/tech-blog/en-us/2025/december/diagrams/Diagram%201%202.png 2x,  1x"
             src="/cms/content/dam/dropbox/tech-blog/en-us/2025/december/diagrams/Diagram%201%202.png"
             aria-hidden=""
             alt=""
             class=""
             data-sly-attribute.width="2880"
             data-sly-attribute.height="1840"
             data-aem-asset-id="c43b2db3-3a0a-45b8-ac48-0f3404d3c9c0:Diagram 1 2.png"
             data-trackable="true" />
        <img data-sly-test.highRes="false"
             srcset="/cms/content/dam/dropbox/tech-blog/en-us/2025/december/diagrams/Diagram%201%202.png 2x,  1x"
             src="/cms/content/dam/dropbox/tech-blog/en-us/2025/december/diagrams/Diagram%201%202.png"
             aria-hidden=""
             alt=""
             class=""
             data-sly-attribute.width="2880"
             data-sly-attribute.height="1840"
             data-aem-asset-id="c43b2db3-3a0a-45b8-ac48-0f3404d3c9c0:Diagram 1 2.png"
             data-trackable="true" /> -->

        
         
        <img src="/cms/content/dam/dropbox/tech-blog/en-us/2025/december/diagrams/Diagram%201%202.png/_jcr_content/renditions/Diagram%201%202.webp" fallbackimage="/cms/content/dam/dropbox/tech-blog/en-us/2025/december/diagrams/Diagram%201%202.png" onerror="window.failedAttempts=0;this.setAttribute('src',this.getAttribute('fallbackimage'));window.failedAttempts++;if(window.failedAttempts == 1)this.onerror=null" aria-hidden="false" alt="" data-aem-asset-id="c43b2db3-3a0a-45b8-ac48-0f3404d3c9c0:Diagram 1 2.png" data-trackable="true" height="1840" width="2880"/>
    

            
        
    </figure>
</div></div>
<div class="section aem-GridColumn aem-GridColumn--default--12">
<div class="dr-article-content__section" id="making-search-fast">
    <h2 class="dr-article-content__section-title">Making search fast</h2>
</div>
</div>
<div class="text parbase aem-GridColumn aem-GridColumn--default--12">
<p>With the architecture in place, the next challenge was meeting Dash’s sub-100ms latency requirements. Feature retrieval sits directly on the critical path of search and LLM answer generation, so even small delays compound quickly at scale and degrade Dash’s snappy search retrieval experience.</p>
<p>Our initial feature-serving implementation was built in Python using the Feast SDK. While parallelism helped at moderate scale, profiling revealed that CPU-bound JSON parsing and Python’s Global Interpreter Lock became the dominant bottlenecks under higher concurrency. Moving to multiple processes temporarily improved latency, but introduced coordination overhead that limited scalability.</p>
<p>To remove these constraints, we rewrote the feature serving layer in <a href="https://go.dev/" target="_blank">Go</a>. Using lightweight goroutines, shared memory, and faster JSON parsing, the Go service delivers true concurrency without the coordination costs we hit in Python. Today, it handles thousands of requests per second while adding only ~5–10ms of processing overhead on top of Dynovault’s client latency, consistently achieving p95 latencies in the ~25–35ms range.</p>
<p>This shift allowed us to meet Dash’s latency targets reliably and ensured that feature serving wouldn’t become the limiting factor as search traffic and feature complexity continued to grow.</p>

</div>
<div class="section aem-GridColumn aem-GridColumn--default--12">
<div class="dr-article-content__section" id="keeping-features-fresh">
    <h2 class="dr-article-content__section-title">Keeping features fresh</h2>
</div>
</div>
<div class="text parbase aem-GridColumn aem-GridColumn--default--12">
<p>Speed matters only when the data itself is fresh. Stale features can lower ranking quality and hurt user experience, so our feature store had to reflect new signals as soon as possible, often within minutes of user actions.</p>
<p>The challenge was scale. Many of Dash’s most important features depend on large joins, aggregations, and historical context, which makes fully real-time computation impractical. We needed an ingestion strategy that balanced freshness with reliability, without overwhelming our infrastructure or slowing development. To do that, we built a three-part ingestion system.</p>
<p><b>Batch ingestion</b> handles complex, high-volume transformations built atop the medallion architecture (a layered data model that organizes data from raw to refined stages). Rather than rewriting every feature on each run, we added intelligent change detection so only modified records are written to the online store. This reduced write volumes from hundreds of millions to under one million records per run and cut update times from more than an hour to under five minutes.</p>
<p><b>Streaming ingestion</b> captures fast-moving signals such as collaboration activity or content interactions. By processing unbounded datasets in near-real time, it ensures features stay aligned with what users are doing in the moment.</p>
<p><b>Direct writes</b> handle lightweight or precomputed features by bypassing batch pipelines entirely. For example, relevance scores produced by a separate LLM evaluation pipeline can be written directly to the online store in seconds instead of waiting for the next batch cycle.</p>
<p>Together, these approaches allow Dash to keep feature values fresh without forcing all computation onto a single ingestion path, maintaining ranking quality while scaling to real-world usage.</p>

</div>
<div class="section aem-GridColumn aem-GridColumn--default--12">
<div class="dr-article-content__section" id="what-we-learned">
    <h2 class="dr-article-content__section-title">What we learned</h2>
</div>
</div>
<div class="text parbase aem-GridColumn aem-GridColumn--default--12">
<p>Building a feature store at Dropbox scale reinforced a few hard-earned lessons about systems design. On the serving side, Python’s concurrency model became a limiting factor for high-throughput, mixed CPU and I/O workloads. Even with careful parallelism, the Global Interpreter Lock capped performance for CPU-bound work like JSON parsing, and moving to multiple processes introduced new coordination bottlenecks. Rewriting the serving layer in Go allowed us to remove those tradeoffs and scale concurrency more predictably.</p>
<p>On the data side, infrastructure changes mattered, but understanding access patterns mattered just as much. By recognizing that only 1–5% of feature values change in a typical 15-minute window, we were able to dramatically reduce write volumes and ingestion time. This shift turned hour-long batch cycles into five-minute updates, improving freshness without increasing system load.</p>
<p>These optimizations came together in a hybrid architecture that balances flexibility and performance: Feast for orchestration and consistency, Spark for large-scale computation, and Dynovault for low-latency online serving. Rather than relying on a single vendor solution, this approach let us tune each layer to its strengths while keeping training and serving aligned.</p>
<p>Ultimately, this work underscored the value of a middle path between building everything from scratch and adopting off-the-shelf systems wholesale. By combining open source foundations with internal infrastructure and tailoring them to real constraints, we were able to build a feature store that fits the needs of Dash today and, ultimately, can evolve with it in the future.</p>
<p><i>Acknowledgments: Special thanks to all current and past members of the AI/ML Platform and Data Platform teams for their contributions, as well as our lovely machine learning engineers who spin up the magic with the tooling we build. </i></p>
<p style="text-align: center;">~ ~ ~</p>
<p><i>If building innovative products, experiences, and infrastructure excites you, come build the future with us! Visit </i><a href="https://jobs.dropbox.com/" target="_blank"><i>jobs.dropbox.com</i></a><i> to see our open roles.</i></p>

</div>

    
</div>
]]></content:encoded>
            			
                <media:thumbnail url="https://dropbox.tech/cms/content/dam/dropbox/tech-blog/en-us/2025/december/headers/MachineLearning-FeatureStore-1440x305-light.png" />
                <media:content url="https://dropbox.tech/cms/content/dam/dropbox/tech-blog/en-us/2025/december/headers/MachineLearning-FeatureStore-1440x305-light.png" medium="image">
                    <media:title type="html">Inside the feature store powering real-time AI in Dropbox Dash</media:title>
                </media:content>
       		 </item>
                    
        				<item>
                        <title>Building the future: highlights from Dropbox’s 2025 summer intern class</title>
                        
            			<link>https://dropbox.tech/culture/highlights-from-dropbox-2025-summer-intern-class</link>

                            
            			<dc:creator>
                            Facundo Agriel,Ishan Mishra,Eric Wang,Dmitriy Meyerzon,Dmitriy Meyerzon,Hicham Badri,Appu Shaji,Craig Wilhite,Josh Clemm,Jason Shang,Artem Nabirkin,Dropbox Team,Ameya Bhatawdekar
            			</dc:creator>
            			
            				<category>Dash</category>
							
            				<category>Search</category>
							
            				<category>AI</category>
							
                            
            			<description><![CDATA[The Dropbox Intern Program is thoughtfully designed to cultivate growth, spark innovation, and build lasting connections.]]></description>
            			<guid>https://dropbox.tech/culture/highlights-from-dropbox-2025-summer-intern-class</guid>
                        <pubDate>Wed, 26 Nov 2025 11:00:00 -0800</pubDate>
                            
                        <content:encoded><![CDATA[


<div class="aem-Grid aem-Grid--12 aem-Grid--default--12 ">
    
    <div class="text parbase aem-GridColumn aem-GridColumn--default--12">
<p>This summer, the Emerging Talent team proudly welcomed 43 interns to Dropbox as part of our 2025 Camp Dropbox Intern Program. Representing 27 colleges and universities—including six international institutions in Canada, Poland, and Ireland—this year’s cohort brought a wealth of diverse perspectives and experiences. Of the group, 28 interns joined our Engineering teams, and over the course of 12 weeks (May through September), they immersed themselves in meaningful work, continuous learning, and our <a href="https://blog.dropbox.com/topics/company/virtual-first-2024-a-year-of-learning-and-innovation" target="_blank">Virtual First culture</a>.</p>
<p>The <a href="https://jobs.dropbox.com/teams/emerging-talent" target="_blank">Dropbox Intern Program</a> is thoughtfully designed to cultivate growth, spark innovation, and build lasting connections. Interns benefited from more than 6,000 hours of dedicated one-on-one mentorship, tackled high-impact projects aligned with team and company goals, and explored hands-on applications of AI. Many of these projects supported the development of <a href="https://www.dash.dropbox.com/" target="_blank"><b>Dropbox Dash</b></a>, our AI-powered universal search product. Robust programming—including Virtual First events, ERG activities, and the in-person Emerging Talent Summit—created further opportunities for connection and community. By the end of the summer, these interns had made meaningful contributions across our engineering organization.</p>
<p>Below, our interns share what they worked on this summer, from big technical wins to moments of creativity, collaboration, and growth that shaped their time at Dropbox.</p>

</div>
<div class="experiencefragment aem-GridColumn aem-GridColumn--default--12">
<div id="experiencefragment-03052b03f5" class="cmp-experiencefragment cmp-experiencefragment--dash-cta-for-tech-blog">

    



<div class="xf-content-height">
    


<div class="aem-Grid aem-Grid--12 aem-Grid--default--12 ">
    
    <div class="responsivegrid aem-GridColumn aem-GridColumn--default--12">


<div class="aem-Grid aem-Grid--12 aem-Grid--default--12 ">
    
    <div class="a08-html-embed c17-plain-html aem-GridColumn aem-GridColumn--default--12">

<style type="text/css"> 

@font-face {
font-family: 'AtlasGrotesk';
src: url('https://cdn.prod.website-files.com/65dcd70b48edc3a7b446950e/65dce019c63112e617513c94_AtlasGrotesk-Medium-Web-vfl38XiTL.woff2') format('woff2');
font-weight: 500;
font-style: normal;
font-display: swap;
}

@font-face {
font-family: 'AtlasGrotesk';
src: url('https://cdn.prod.website-files.com/65dcd70b48edc3a7b446950e/65dce019711b648fd1ccd24a_AtlasGrotesk-Regular-Web-vflk7bxjs.woff2') format('woff2');
font-weight: 400;
font-style: normal;
font-display: swap;
}
.xf-content-height {margin: 0;}
#cta { font-family: AtlasGrotesk,sans-serif; font-size: .900rem; text-decoration: none; background: #f7f5f2; line-height: 1.69; box-sizing: border-box;}
#cta-box { padding: 15px 20px 15px 20px; }
#cta-hed {font-weight: 500;}
#cta-indent {border-left: 5px solid #1e1919; padding-left:20px;}
#cta a:link, #cta a:visited  {text-decoration: none;}
#cta p { margin: 5px 0px 0px 0px; }

.dr-theme-dark #cta {background: #000;}
.dr-theme-dark #cta-box {border: 1px solid; border-bottom: 0;}
.dr-theme-dark #cta-indent {border-left: 5px solid #f7f5f2;}
.dr-theme-dark .button {background: #000;}

.button {
    background-color: #1e1919;
    color:  #f7f5f2;
    height: 2.5rem;
    padding: 10px 5px 10px 20px;
    font-size: 1rem;
    font-weight: 500;
    line-height: 1.2;
    transition: all .3s;
}

.button:hover { background-color: #0061ff; }

img {vertical-align: middle; padding: 0px 1px 2px 0px;}

.c17-plain-html {margin-bottom: 50px}

</style>

<div id="cta">
<div id="cta-box">
<div id="cta-indent">

<p id="cta-hed"><img src="https://cdn.prod.website-files.com/65dcd70b48edc3a7b446950e/670692ee7692f74d4834e4f4_Frame%201400006055.svg" loading="lazy"> Dropbox Dash: AI that understands your work
</p>

<p>Dash knows your context, your team, and your work, so your team can stay organized, easily find and share knowledge, and keep projects secure, all from one place. And soon, Dash is coming to Dropbox.</p>

</div>
</div>

<a href="https://dash.dropbox.com/?utm=blogs" target="_blank"><div class="button">Learn more →</div></a>

</div>
</div>

    
</div>
</div>

    
</div>

</div></div>

    
</div>
<div class="text parbase aem-GridColumn aem-GridColumn--default--12">
<p>“I tackled the <b>Dropbox file history tracking system. </b>As an engineering intern working in a large production database for the first time, I learned the importance of strongly tested, verifiable code and thoughtful system design. I really aligned with the core <a href="https://jobs.dropbox.com/blogs/dropbox-values" target="_blank">Dropbox values</a> of Be Worthy of Trust and Keep It Simple at the software level. This solution simplifies our <a href="https://dropbox.tech/infrastructure/meet-chrono-our-scalable-consistent-metadata-caching-solution" target="_blank">metadata infrastructure</a>, significantly reduces operational costs, and shows how thoughtful refactoring of legacy systems can deliver both technical elegance and substantial business value.”</p>
<p>—<i>Rhea Rai, Filesystem Data</i></p>
<p style="text-align: center;"><img src="https://paper.dropboxstatic.com/static/img/ace/emoji/1f4bb.png?version=8.0.0" height="16" alt="laptop computer" title="laptop computer"/></p>
<p>“During my internship with the ML Platform team, I worked on a system that <b>monitors the health of ML model deployments</b>. By integrating with internal inference services, AI Sentinel gives <a href="https://dropbox.tech/machine-learning" target="_blank">machine learning</a> engineers real-time operational visibility they previously had to gather manually. The result is greater deployment confidence and faster iteration cycles, ensuring reliable ML model deployments that power Dash’s intelligent features at scale.”<br />
<i>—Ben Juntilla, ML Platform</i></p>
<p style="text-align: center;"><img src="https://paper.dropboxstatic.com/static/img/ace/emoji/2728.png?version=8.0.0" height="16" alt="sparkles" title="sparkles"/></p>
<p>“I worked on <b>reducing front end latency</b> in <a href="https://dropbox.tech/infrastructure/increasing-magic-pocket-write-throughput-by-removing-our-ssd-cache-disks" target="_blank">Magic Pocket</a>. Elevated PUT latencies during scheduled disk restarts can delay updates in workflows like <a href="https://dash.dropbox.com/features/connected-apps" target="_blank">Dash connectors</a>, leaving users with outdated or missing content. To address this, I built a cache to track storage health and added a filtering option to skip degraded volumes. This health-aware routing reduces slow writes and gives operators greater control, ensuring Dropbox delivers timely, accurate search results.”<br />
<i>—Albert Joon Sung, Storage Core</i></p>
<p style="text-align: center;"><img src="https://paper.dropboxstatic.com/static/img/ace/emoji/1f468-1f4bb.png?version=8.0.0" height="16" alt="man technologist" title="man technologist"/></p>
<p>“I worked on an AI-powered tool built on top of our internal migration platform that <b>automates code migrations</b>. Developers can launch auto-migration jobs on selected folders for specific migration types. Successful runs open a pull request automatically; otherwise, you can run the command locally and submit changes manually. The tool is fully customizable via the CLI or as part of an automated workflow. With it, I completed two major migrations.”<br />
—<i>Ahmed Ibrahim, Web Developer Experience</i></p>
<p style="text-align: center;"><img src="https://paper.dropboxstatic.com/static/img/ace/emoji/1f6e0.png?version=8.0.0" height="16" alt="hammer and wrench" title="hammer and wrench"/></p>
<p>“I built tools that give <a href="https://dropbox.tech/machine-learning" target="_blank">machine learning</a> engineers access to the most up-to-date information in the <b>Dash</b> <b>persistence store</b>. With this, downstream teams can train models on fresher data and pull in additional metadata fields from third-party systems without waiting for the Connector Platform team to redownload or repackage anything.”<br />
—<i>Eddie Ormseth, Connector Platform</i></p>
<p style="text-align: center;"><img src="https://paper.dropboxstatic.com/static/img/ace/emoji/1f50e.png?version=8.0.0" height="16" alt="magnifying glass tilted right" title="magnifying glass tilted right"/></p>
<p>“I worked on expanding the <b>unified search platform</b> (USP) to support more than 20 languages. The USP powers search across Dropbox products like <a href="https://dropbox.tech/application/how-dropbox-replay-keeps-everyone-in-sync" target="_blank">Replay</a>, and my project integrated a language detection pipeline into both indexing and retrieval. This enables accurate, efficient multilingual search without the overhead of traditional solutions. By delivering native language support ahead of <a href="https://blog.dropbox.com/topics/news/fall-2025-release-dropbox-dash-context-aware-ai-teammate" target="_blank">this year’s Dash launch</a>, my work helps Dropbox scale globally, improve the developer experience, and unlock richer search for international customers, bringing us closer to our vision of an AI-first, universally accessible search platform.”<br />
—<i>Rishi Peddakama, Retrieval Platform</i></p>
<p style="text-align: center;"><img src="https://paper.dropboxstatic.com/static/img/ace/emoji/1f9e0.png?version=8.0.0" height="16" alt="brain" title="brain"/></p>
<p>“I explored <b>advanced anomaly detection techniques</b> for our <a href="https://dropbox.tech/infrastructure/monitoring-server-applications-with-vortex" target="_blank">Vortex2</a> metrics system. Traditional static alerting can miss sudden, meaningful shifts in data that don’t cross predefined thresholds (or trigger too often when changes are expected). To address this, I developed adaptive detection methods that adjust to evolving patterns. These improvements streamline alert creation and reduce alert fatigue, enhancing the on-call experience. By accounting for seasonality, the new anomaly detection functions also enable faster response times and improve the overall developer experience.”<br />
—<i>Yonatan Ginsburg, Metrics</i></p>
<p style="text-align: center;"><img src="https://paper.dropboxstatic.com/static/img/ace/emoji/1f440.png?version=8.0.0" height="16" alt="eyes" title="eyes"/></p>
<p>“I developed a seamless <b>document preview experience</b> within Dropbox Dash, allowing users to quickly <a href="https://dash.dropbox.com/resources/how-to-use-ai-to-review-documents" target="_blank">view file content</a> without leaving their search context. This enhancement supports the Dropbox mission to accelerate workflows by <a href="https://dropbox.tech/machine-learning/practical-blueprint-evaluating-conversational-ai-at-scale-dash" target="_blank">reducing context switching</a> and increasing engagement. I built interactive UI components, integrated PDF viewing, and implemented dynamic follow-up features linking to AI-powered chat.”<br />
<i>—Francesca Venditti, Find & Discover</i></p>
<p style="text-align: center;"><img src="https://paper.dropboxstatic.com/static/img/ace/emoji/1f9f1.png?version=8.0.0" height="16" alt="brick" title="brick"/></p>
<p>“This summer on the Analytics Platform team, I worked on <b>optimizing large-scale Databricks queries and ETL pipelines</b> to reduce compute cost and latency. I developed an optimization recommendation system that flagged high-cost query patterns, expensive table-column filters, and under-allocated compute resources, complete with actionable sourcing information. I also prototyped and documented an Airflow pipeline to migrate a 500 TB mobile events log to liquid clustering, paving the way for broader adoption of modern data layout techniques.”<br />
—<i>Sanjith Udupa, Analytics Platform</i></p>
<p style="text-align: center;"><img src="https://paper.dropboxstatic.com/static/img/ace/emoji/1f680.png?version=8.0.0" height="16" alt="rocket" title="rocket"/></p>
<p>“I built an <b>extensible AI web-automation agent</b> for Dropbox. I also connected Dropbox backend APIs via searchFile and uploadFile actions to fetch and upload files, using <a href="https://dropbox.github.io/" target="_blank">open-source foundations</a>. By keeping tool sets small and modular, developers can quickly compose reliable, task-specific automations, like form filling or proofreading. As demand for automating repetitive web tasks continues to grow, integrating <a href="https://help.dropbox.com/organize/dropbox-automations" target="_blank">automation tools</a> into Dash will significantly improve the user experience.”<br />
<i>—Alan Zhu, Conversational AI</i></p>
<p><i>Responses have been lightly edited for length and clarity. </i></p>
<p style="text-align: center;">~ ~ ~</p>
<p><i>If building innovative products, experiences, and infrastructure excites you, come build the future with us! Visit </i><a href="https://jobs.dropbox.com/" target="_blank"><i>jobs.dropbox.com</i></a><i> to see our open roles.</i></p>

</div>

    
</div>
]]></content:encoded>
            			
                <media:thumbnail url="https://dropbox.tech/cms/content/dam/dropbox/tech-blog/en-us/2025/november/intern-spotlight/headers/Culture-SummerInternSpotlight2025-1440x305-light.png" />
                <media:content url="https://dropbox.tech/cms/content/dam/dropbox/tech-blog/en-us/2025/november/intern-spotlight/headers/Culture-SummerInternSpotlight2025-1440x305-light.png" medium="image">
                    <media:title type="html">Building the future: highlights from Dropbox’s 2025 summer intern class</media:title>
                </media:content>
       		 </item>
                    
        				<item>
                        <title>How Dash uses context engineering for smarter AI</title>
                        
            			<link>https://dropbox.tech/machine-learning/how-dash-uses-context-engineering-for-smarter-ai</link>

                            
            			<dc:creator>
                            Facundo Agriel,Ishan Mishra,Eric Wang,Dmitriy Meyerzon,Dmitriy Meyerzon,Hicham Badri,Appu Shaji,Craig Wilhite,Josh Clemm,Jason Shang,Artem Nabirkin,Dropbox Team,Ameya Bhatawdekar,Sean-Michael Lewis
            			</dc:creator>
            			
            				<category>models</category>
							
            				<category>Search</category>
							
            				<category>Machine Learning</category>
							
            				<category>AI</category>
							
            				<category>Dash</category>
							
                            
            			<description><![CDATA[Building effective, agentic AI isn’t just about adding more; it’s about helping the model focus on what matters most.]]></description>
            			<guid>https://dropbox.tech/machine-learning/how-dash-uses-context-engineering-for-smarter-ai</guid>
                        <pubDate>Mon, 17 Nov 2025 11:00:00 -0800</pubDate>
                            
                        <content:encoded><![CDATA[


<div class="aem-Grid aem-Grid--12 aem-Grid--default--12 ">
    
    <div class="text parbase aem-GridColumn aem-GridColumn--default--12">
<p>When we first built Dash, it looked like most enterprise search systems: a traditional <a href="https://dropbox.tech/machine-learning/building-dash-rag-multi-step-ai-agents-business-users" target="_blank">RAG pipeline</a> that combined <a href="https://dropbox.tech/machine-learning/selecting-model-semantic-search-dropbox-ai" target="_blank">semantic and keyword search</a> across indexed documents. It worked well for retrieving information and generating concise answers. But as teams began using Dash for <a href="https://dropbox.tech/machine-learning/practical-blueprint-evaluating-conversational-ai-at-scale-dash" target="_blank">more than just finding content</a>—for example, asking it to interpret, summarize, and even act on what it found—we realized that retrieval alone wasn’t enough. The natural progression from “what is the status of the identity project” to “open the editor and write an executive summary of the projects that I own” required Dash to evolve from a search system into an agentic AI.</p>
<p>That shift introduced a new kind of engineering challenge: deciding what information and tools the model actually needs to see to reason and act effectively. This has been popularized as <b>context engineering</b>, the process of structuring, filtering, and delivering just the right context at the right time so the model can plan intelligently without getting overwhelmed. We started thinking about how these ideas applied inside Dash itself, including how the model planned, reasoned, and took action on a request. Instead of simply searching and summarizing results, it now plans what to do and carries out those steps.</p>
<p>At the same time, adding tools into Dash’s workflow created new tradeoffs around how context is managed. Precision in what you feed the model is critical in any RAG system, and the same lesson applies to agentic systems. Supplying the model with only the most relevant context, and not just more of it, consistently leads to better results. Below, we’ll walk through how we’ve been building better context into Dash.</p>

</div>
<div class="experiencefragment aem-GridColumn aem-GridColumn--default--12">
<div id="experiencefragment-f729ab351a" class="cmp-experiencefragment cmp-experiencefragment--dash-cta-for-tech-blog">

    



<div class="xf-content-height">
    


<div class="aem-Grid aem-Grid--12 aem-Grid--default--12 ">
    
    <div class="responsivegrid aem-GridColumn aem-GridColumn--default--12">


<div class="aem-Grid aem-Grid--12 aem-Grid--default--12 ">
    
    <div class="a08-html-embed c17-plain-html aem-GridColumn aem-GridColumn--default--12">

<style type="text/css"> 

@font-face {
font-family: 'AtlasGrotesk';
src: url('https://cdn.prod.website-files.com/65dcd70b48edc3a7b446950e/65dce019c63112e617513c94_AtlasGrotesk-Medium-Web-vfl38XiTL.woff2') format('woff2');
font-weight: 500;
font-style: normal;
font-display: swap;
}

@font-face {
font-family: 'AtlasGrotesk';
src: url('https://cdn.prod.website-files.com/65dcd70b48edc3a7b446950e/65dce019711b648fd1ccd24a_AtlasGrotesk-Regular-Web-vflk7bxjs.woff2') format('woff2');
font-weight: 400;
font-style: normal;
font-display: swap;
}
.xf-content-height {margin: 0;}
#cta { font-family: AtlasGrotesk,sans-serif; font-size: .900rem; text-decoration: none; background: #f7f5f2; line-height: 1.69; box-sizing: border-box;}
#cta-box { padding: 15px 20px 15px 20px; }
#cta-hed {font-weight: 500;}
#cta-indent {border-left: 5px solid #1e1919; padding-left:20px;}
#cta a:link, #cta a:visited  {text-decoration: none;}
#cta p { margin: 5px 0px 0px 0px; }

.dr-theme-dark #cta {background: #000;}
.dr-theme-dark #cta-box {border: 1px solid; border-bottom: 0;}
.dr-theme-dark #cta-indent {border-left: 5px solid #f7f5f2;}
.dr-theme-dark .button {background: #000;}

.button {
    background-color: #1e1919;
    color:  #f7f5f2;
    height: 2.5rem;
    padding: 10px 5px 10px 20px;
    font-size: 1rem;
    font-weight: 500;
    line-height: 1.2;
    transition: all .3s;
}

.button:hover { background-color: #0061ff; }

img {vertical-align: middle; padding: 0px 1px 2px 0px;}

.c17-plain-html {margin-bottom: 50px}

</style>

<div id="cta">
<div id="cta-box">
<div id="cta-indent">

<p id="cta-hed"><img src="https://cdn.prod.website-files.com/65dcd70b48edc3a7b446950e/670692ee7692f74d4834e4f4_Frame%201400006055.svg" loading="lazy"> Dropbox Dash: AI that understands your work
</p>

<p>Dash knows your context, your team, and your work, so your team can stay organized, easily find and share knowledge, and keep projects secure, all from one place. And soon, Dash is coming to Dropbox.</p>

</div>
</div>

<a href="https://dash.dropbox.com/?utm=blogs" target="_blank"><div class="button">Learn more →</div></a>

</div>
</div>

    
</div>
</div>

    
</div>

</div></div>

    
</div>
<div class="section aem-GridColumn aem-GridColumn--default--12">
<div class="dr-article-content__section" id="-engineering-context-precision-in-dash">
    <h2 class="dr-article-content__section-title"> Engineering context precision in Dash</h2>
</div>
</div>
<div class="text parbase aem-GridColumn aem-GridColumn--default--12">
<p>As Dash gained new capabilities—like contextual search and assisted editing—we noticed something unexpected: more tools often meant slower, less accurate decision making. A “tool” here is any external function the model can call, such as search, look-up, or summarization. Each new capability expanded the model’s decision space, creating more choices and room for confusion. Even well-designed tools made the model spend more time deciding how to act instead of acting. The problem wasn’t broken tools; it was too many good ones. In human terms, Dash was facing analysis paralysis.</p>
<p>The <a href="https://modelcontextprotocol.io/docs/getting-started/intro" target="_blank">Model Context Protocol</a> (MCP), an open standard for defining and describing the tools a server provides, helps with this by outlining what each tool does and what inputs it takes. But as we experimented with MCP servers, we ran into limitations. Each tool we added came with its own description and parameters, which all have to fit inside the model’s context window (the space it uses to read and reason about information). In practice, these definitions also consume a significant number of tokens, a resource that directly impacts both cost and performance. Further, we noticed that the overall accuracy of Dash degraded for longer-running jobs. The tool calls were adding a lot of extra context. We were seeing similar patterns of what’s been popularized as <a href="https://research.trychroma.com/context-rot" target="_blank">context rot</a>.</p>
<p>This led us to rethink context. Building effective, agentic AI isn’t just about adding more; it’s about helping the model focus on what matters most. In Dash, that means curating context so the model can make faster, better decisions through three core strategies:</p>
<ul>
<li>Limit the number of tool definitions in the context</li>
<li>Filter context to only what’s relevant</li>
<li>Introduce specialized agents for tasks that demand deeper reasoning</li>
</ul>
<p>Our principle is simple: better context leads to better outcomes. It’s about giving the model the right information, at the right time, in the right form.</p>

</div>
<div class="image c04-image aem-GridColumn aem-GridColumn--default--12">
<div class="dr-image image cq-dd-image  ">
    <figure class="dr-margin-0 dr-display-inline-block">
        
            
    

        

        
        
        

        
        
        

        <!--optimized image webp-->
        

        
        <!-- <img data-sly-test.highRes="false"
             srcset="https://aem.dropbox.com/cms/content/dam/dropbox/tech-blog/en-us/2025/november/context-engineering/diagrams/Dropbox_Connector_Flow_v05.gif 2x,  1x"
             src="https://aem.dropbox.com/cms/content/dam/dropbox/tech-blog/en-us/2025/november/context-engineering/diagrams/Dropbox_Connector_Flow_v05.gif"
             aria-hidden=""
             alt=""
             class=""
             data-sly-attribute.width=""
             data-sly-attribute.height=""
             data-aem-asset-id=""
             data-trackable="true" />
        <img data-sly-test.highRes="false"
             srcset="https://aem.dropbox.com/cms/content/dam/dropbox/tech-blog/en-us/2025/november/context-engineering/diagrams/Dropbox_Connector_Flow_v05.gif 2x,  1x"
             src="https://aem.dropbox.com/cms/content/dam/dropbox/tech-blog/en-us/2025/november/context-engineering/diagrams/Dropbox_Connector_Flow_v05.gif"
             aria-hidden=""
             alt=""
             class=""
             data-sly-attribute.width=""
             data-sly-attribute.height=""
             data-aem-asset-id=""
             data-trackable="true" /> -->

        
         
        <img src="https://aem.dropbox.com/cms/content/dam/dropbox/tech-blog/en-us/2025/november/context-engineering/diagrams/Dropbox_Connector_Flow_v05.gif" fallbackimage="https://aem.dropbox.com/cms/content/dam/dropbox/tech-blog/en-us/2025/november/context-engineering/diagrams/Dropbox_Connector_Flow_v05.gif" onerror="window.failedAttempts=0;this.setAttribute('src',this.getAttribute('fallbackimage'));window.failedAttempts++;if(window.failedAttempts == 1)this.onerror=null" aria-hidden="false" alt="" data-trackable="true"/>
    

            
        
    </figure>
</div></div>
<div class="text parbase aem-GridColumn aem-GridColumn--default--12">
<h3>Limit the number of tool definitions in the context</h3>
<p>Our first insight was that giving the model too many options for calling tools led to worse results. Dash connects to many of the apps our customers use to get work done, and each of those apps provides its own retrieval tools, such as search, find by ID, or find by name. </p>
<p>Although we have the <a href="https://learn.dropbox.com/video-library/dash-in-dropbox-search" target="_blank">Dash Search</a> index—our server-based search index that stores and manages documents and messages for fast and reliable retrieval—we did experiment with using other tools for retrieval. For example, Dash might need to consult Confluence for documentation, Google Docs for meeting notes, and Jira for project status to service one request. In our experiments with those other retrieval tools, we found that the model often had to call all of them, but it also didn’t do so reliably.</p>

</div>
<div class="image c04-image aem-GridColumn aem-GridColumn--default--12">
<div class="dr-image image cq-dd-image  ">
    <figure class="dr-margin-0 dr-display-inline-block">
        
            
    

        

        
        
        

        
        
        

        <!--optimized image webp-->
        

        
        <!-- <img data-sly-test.highRes="false"
             srcset="/cms/content/dam/dropbox/tech-blog/en-us/2025/november/context-engineering/diagrams/Diagram%201.png 2x,  1x"
             src="/cms/content/dam/dropbox/tech-blog/en-us/2025/november/context-engineering/diagrams/Diagram%201.png"
             aria-hidden=""
             alt=""
             class=""
             data-sly-attribute.width="2880"
             data-sly-attribute.height="2320"
             data-aem-asset-id="3673c5da-2bb6-4a88-93b4-10235a597bef:Diagram 1.png"
             data-trackable="true" />
        <img data-sly-test.highRes="false"
             srcset="/cms/content/dam/dropbox/tech-blog/en-us/2025/november/context-engineering/diagrams/Diagram%201.png 2x,  1x"
             src="/cms/content/dam/dropbox/tech-blog/en-us/2025/november/context-engineering/diagrams/Diagram%201.png"
             aria-hidden=""
             alt=""
             class=""
             data-sly-attribute.width="2880"
             data-sly-attribute.height="2320"
             data-aem-asset-id="3673c5da-2bb6-4a88-93b4-10235a597bef:Diagram 1.png"
             data-trackable="true" /> -->

        
         
        <img src="/cms/content/dam/dropbox/tech-blog/en-us/2025/november/context-engineering/diagrams/Diagram%201.png/_jcr_content/renditions/Diagram%201.webp" fallbackimage="/cms/content/dam/dropbox/tech-blog/en-us/2025/november/context-engineering/diagrams/Diagram%201.png" onerror="window.failedAttempts=0;this.setAttribute('src',this.getAttribute('fallbackimage'));window.failedAttempts++;if(window.failedAttempts == 1)this.onerror=null" aria-hidden="false" alt="" data-aem-asset-id="3673c5da-2bb6-4a88-93b4-10235a597bef:Diagram 1.png" data-trackable="true" height="2320" width="2880"/>
    

            
        
    </figure>
</div></div>
<div class="text parbase aem-GridColumn aem-GridColumn--default--12">
<p>We solved this by replacing all of those retrieval options with a single, purpose-built tool backed by the Dash <b>universal search index</b>.<b> </b>Instead of expecting the model to understand and choose between dozens of APIs, we created one interface that handles retrieval across all services. The key idea is simple: Giving the model one consistent way to retrieve information makes its reasoning clearer, its plans more efficient, and its context use more focused.</p>
<p>These learnings also influenced our design of the <a href="https://github.com/dropbox/mcp-server-dash" target="_blank">Dash MCP server</a>, which brings Dash’s retrieval to MCP-compatible apps like Claude, Cursor, and Goose with just one tool. It connects to the systems people already use and securely searches inside their apps. By keeping descriptions lean, more of the context window stays focused on the user’s request.</p>
<h3>Filter context to only what’s relevant</h3>
<p>Our next insight was that not everything retrieved from multiple APIs is actually useful for the task at hand. When we tried pulling data from several tools at once, we still needed a way to rank and filter the results so that only the <i>most</i> relevant information reached the model.</p>
<p>We built the Dash index to combine data from multiple sources into one unified index, then layered a <b>knowledge graph</b> on top to connect people, activity, and content across those sources. (A knowledge graph maps relationships between these sources so the system can understand how different pieces of information are connected.) These relationships help rank results based on what matters most for each query and each user. As a result, the model only sees content our platform has already determined to be relevant, which makes every piece of context meaningful. Building the index and graph in advance means Dash can focus on retrieval at runtime instead of rebuilding context, which makes the whole process faster and more efficient.</p>

</div>
<div class="image c04-image aem-GridColumn aem-GridColumn--default--12">
<div class="dr-image image cq-dd-image  ">
    <figure class="dr-margin-0 dr-display-inline-block">
        
            
    

        

        
        
        

        
        
        

        <!--optimized image webp-->
        

        
        <!-- <img data-sly-test.highRes="false"
             srcset="/cms/content/dam/dropbox/tech-blog/en-us/2025/november/context-engineering/diagrams/Diagram%202.png 2x,  1x"
             src="/cms/content/dam/dropbox/tech-blog/en-us/2025/november/context-engineering/diagrams/Diagram%202.png"
             aria-hidden=""
             alt=""
             class=""
             data-sly-attribute.width="2880"
             data-sly-attribute.height="3840"
             data-aem-asset-id="a5e860e9-38de-4e74-9094-4eb486536900:Diagram 2.png"
             data-trackable="true" />
        <img data-sly-test.highRes="false"
             srcset="/cms/content/dam/dropbox/tech-blog/en-us/2025/november/context-engineering/diagrams/Diagram%202.png 2x,  1x"
             src="/cms/content/dam/dropbox/tech-blog/en-us/2025/november/context-engineering/diagrams/Diagram%202.png"
             aria-hidden=""
             alt=""
             class=""
             data-sly-attribute.width="2880"
             data-sly-attribute.height="3840"
             data-aem-asset-id="a5e860e9-38de-4e74-9094-4eb486536900:Diagram 2.png"
             data-trackable="true" /> -->

        
         
        <img src="/cms/content/dam/dropbox/tech-blog/en-us/2025/november/context-engineering/diagrams/Diagram%202.png/_jcr_content/renditions/Diagram%202.webp" fallbackimage="/cms/content/dam/dropbox/tech-blog/en-us/2025/november/context-engineering/diagrams/Diagram%202.png" onerror="window.failedAttempts=0;this.setAttribute('src',this.getAttribute('fallbackimage'));window.failedAttempts++;if(window.failedAttempts == 1)this.onerror=null" aria-hidden="false" alt="" data-aem-asset-id="a5e860e9-38de-4e74-9094-4eb486536900:Diagram 2.png" data-trackable="true" height="3840" width="2880"/>
    

            
        
    </figure>
</div></div>
<div class="text parbase aem-GridColumn aem-GridColumn--default--12">
<p>The key lesson is that everything retrieved shapes the model’s reasoning, so relevance is critical to guiding it efficiently. Sending only what’s essential improves both performance and the quality of the entire agentic flow.</p>
<h3>Introduce specialized agents for complex tasks</h3>
<p>Our third discovery was that some tools are so complex that the model needs extra context and examples to use them effectively. We saw this firsthand as we continued to expand the Dash Search tool. Query construction turned out to be a difficult task on its own. It involves understanding user intent, mapping that intent to index fields, rewriting queries for better semantic matching, and handling edge cases such as typos, synonyms, and implicit context.</p>
<p>As the search tool grew more capable, the model needed more instruction to use it correctly. Those details started to take up a significant portion of the context window, leaving less room for reasoning about the overall task. In other words, the model was spending more of its attention on how to search than on what to do with the results.</p>
<p>We solved this by moving search into its own agent. The main planning agent decides when a search is needed and delegates the actual query construction to a specialized agent with its own prompt. This separation allows the main agent to stay focused on planning and execution while the search agent handles the specifics of retrieval. The key lesson is that when a tool demands too much explanation or context to be used effectively, it’s often better to turn it into a dedicated agent with a focused prompt.</p>

</div>
<div class="image c04-image aem-GridColumn aem-GridColumn--default--12">
<div class="dr-image image cq-dd-image  ">
    <figure class="dr-margin-0 dr-display-inline-block">
        
            
    

        

        
        
        

        
        
        

        <!--optimized image webp-->
        

        
        <!-- <img data-sly-test.highRes="false"
             srcset="/cms/content/dam/dropbox/tech-blog/en-us/2025/november/context-engineering/diagrams/Diagram%203.png 2x,  1x"
             src="/cms/content/dam/dropbox/tech-blog/en-us/2025/november/context-engineering/diagrams/Diagram%203.png"
             aria-hidden=""
             alt=""
             class=""
             data-sly-attribute.width="2880"
             data-sly-attribute.height="2360"
             data-aem-asset-id="a20471bd-cef3-4378-9793-8466422ab7c0:Diagram 3.png"
             data-trackable="true" />
        <img data-sly-test.highRes="false"
             srcset="/cms/content/dam/dropbox/tech-blog/en-us/2025/november/context-engineering/diagrams/Diagram%203.png 2x,  1x"
             src="/cms/content/dam/dropbox/tech-blog/en-us/2025/november/context-engineering/diagrams/Diagram%203.png"
             aria-hidden=""
             alt=""
             class=""
             data-sly-attribute.width="2880"
             data-sly-attribute.height="2360"
             data-aem-asset-id="a20471bd-cef3-4378-9793-8466422ab7c0:Diagram 3.png"
             data-trackable="true" /> -->

        
         
        <img src="/cms/content/dam/dropbox/tech-blog/en-us/2025/november/context-engineering/diagrams/Diagram%203.png/_jcr_content/renditions/Diagram%203.webp" fallbackimage="/cms/content/dam/dropbox/tech-blog/en-us/2025/november/context-engineering/diagrams/Diagram%203.png" onerror="window.failedAttempts=0;this.setAttribute('src',this.getAttribute('fallbackimage'));window.failedAttempts++;if(window.failedAttempts == 1)this.onerror=null" aria-hidden="false" alt="" data-aem-asset-id="a20471bd-cef3-4378-9793-8466422ab7c0:Diagram 3.png" data-trackable="true" height="2360" width="2880"/>
    

            
        
    </figure>
</div></div>
<div class="section aem-GridColumn aem-GridColumn--default--12">
<div class="dr-article-content__section" id="-looking-forward">
    <h2 class="dr-article-content__section-title"> Looking forward</h2>
</div>
</div>
<div class="text parbase aem-GridColumn aem-GridColumn--default--12">
<p>Context engineering for agentic AI systems is still an emerging discipline. While the strategies we’ve outlined—retrieval consolidation, relevant context filtering, and specialized task agents—work well for our use cases, we’re continuing to learn and iterate. As we continue to build the best tools for knowledge workers, we’ve found that the Dash index is a powerful resource for managing relevant context and helps us use other tools more effectively.</p>
<p>The work we’ve shared here focuses on one piece of the puzzle: Learning how to trim context down to what really matters, both in tool selection and retrieval. But context is expensive in more ways than one. It affects cost, speed, and how much attention a model can give to the task at hand. We’ve found that leaner contexts don’t just save resources; they also make the model smarter.</p>
<p>Next, we’re turning these lessons toward other parts of Dash’s context, like user and company profiles, as well as long and short-term memory. We think there’s even more performance to unlock by refining these areas, especially as we experiment with smaller and faster models.</p>
<p>Although our discussion centered on retrieval-based tools, action-oriented tools exhibit many of the same limitations. MCP continues to serve as a robust protocol, but effective scaling depends on reducing tool proliferation, investing in specialized agents, and enabling the LLM to generate code-based tools when appropriate, an approach that parallels our consolidation of retrieval tools into the Dash retrieval system. We’ve covered how <a href="https://dropbox.tech/machine-learning/building-dash-rag-multi-step-ai-agents-business-users" target="_blank">Dash uses code-based tools</a> in a previous blog post, and we see that other companies are <a href="https://www.anthropic.com/engineering/code-execution-with-mcp" target="_blank">approaching this problem with a similar mindset</a>.</p>
<p>Moving forward, our focus is on making context even more efficient so the model can spend its attention where it matters most.</p>
<p><i>Acknowledgments: Rene Schmidt, Josh Clemm, Marta Mendez, Nishchal Arya, Roland Hui, Noorain Noorani, Tony Xu</i></p>
<p style="text-align: center;">~ ~ ~</p>
<p><i>If building innovative products, experiences, and infrastructure excites you, come build the future with us! Visit </i><a href="https://jobs.dropbox.com/" target="_blank"><i>jobs.dropbox.com</i></a><i> to see our open roles.</i></p>

</div>

    
</div>
]]></content:encoded>
            			
                <media:thumbnail url="https://dropbox.tech/cms/content/dam/dropbox/tech-blog/en-us/2025/november/context-engineering/headers/Machine Learning-Dash Context Engineering-1440x305-light.png" />
                <media:content url="https://dropbox.tech/cms/content/dam/dropbox/tech-blog/en-us/2025/november/context-engineering/headers/Machine Learning-Dash Context Engineering-1440x305-light.png" medium="image">
                    <media:title type="html">How Dash uses context engineering for smarter AI</media:title>
                </media:content>
       		 </item>
                    
            
    </channel>
</rss>
