<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom" xmlns:dc="http://purl.org/dc/elements/1.1/">
  <channel>
    <title>DEV Community: SpurIQ Engineering</title>
    <description>The latest articles on DEV Community by SpurIQ Engineering (@spuriqai).</description>
    <link>https://dev.to/spuriqai</link>
    <image>
      <url>https://media2.dev.to/dynamic/image/width=90,height=90,fit=cover,gravity=auto,format=auto/https:%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Fuser%2Fprofile_image%2F3894013%2F4d850d17-4d7f-4f32-b0d0-315d6db8d912.png</url>
      <title>DEV Community: SpurIQ Engineering</title>
      <link>https://dev.to/spuriqai</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/spuriqai"/>
    <language>en</language>
    <item>
      <title>Building an AI Agent That Owns Post-Call Execution: Architecture Decisions</title>
      <dc:creator>SpurIQ Engineering</dc:creator>
      <pubDate>Wed, 29 Apr 2026 19:43:19 +0000</pubDate>
      <link>https://dev.to/spuriqai/building-an-ai-agent-that-owns-post-call-execution-architecture-decisions-3lao</link>
      <guid>https://dev.to/spuriqai/building-an-ai-agent-that-owns-post-call-execution-architecture-decisions-3lao</guid>
      <description>&lt;h2&gt;
  
  
  &lt;strong&gt;The call ended. Now what?&lt;/strong&gt;
&lt;/h2&gt;

&lt;p&gt;Here is something product teams rarely talk about: the most expensive moment in a B2B sales cycle is not the failed cold email or the missed demo request. It is the 48 hours after a good call where nothing happens.&lt;/p&gt;

&lt;p&gt;The rep hangs up. They have three more calls. The notes are rough. The CRM still says "Meeting held." The follow-up email goes out two days later, generic, rushed, missing the context that made the call valuable in the first place.&lt;/p&gt;

&lt;p&gt;We built SpurIQ to fix that specific failure. Not by reminding reps to follow up. By building an AI agent that actually owns post-call execution, the moment the call ends to the moment the next committed step is confirmed.&lt;/p&gt;

&lt;p&gt;This post walks through the architecture decisions we made, why we made them and what we learned building it.&lt;/p&gt;

&lt;h2&gt;
  
  
  &lt;strong&gt;What "owning execution" actually means in engineering terms&lt;/strong&gt;
&lt;/h2&gt;

&lt;p&gt;Before we get into the stack, it is worth being precise about what we mean by execution ownership, because it is easy to confuse this with automation.&lt;/p&gt;

&lt;p&gt;Automation sends a follow-up. Execution ownership decides:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;What the follow-up should say based on what was actually discussed&lt;/li&gt;
&lt;li&gt;When it should go based on what was committed in the call&lt;/li&gt;
&lt;li&gt;Who it should come from based on deal structure&lt;/li&gt;
&lt;li&gt;What happens next if there is no response in a defined window&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;The difference is consequential. Automation is a trigger-action loop. Execution ownership is a decision-making agent that holds state across the lifecycle of a deal.&lt;/p&gt;

&lt;p&gt;That distinction shaped every architecture decision we made.&lt;/p&gt;

&lt;h2&gt;
  
  
  &lt;strong&gt;The core architecture: Event-driven, not scheduled&lt;/strong&gt;
&lt;/h2&gt;

&lt;p&gt;Our first instinct was a cron-based system. Poll the CRM every 30 minutes, check for calls that ended, kick off a follow-up pipeline. Simple to build, easy to reason about.&lt;/p&gt;

&lt;p&gt;We scrapped it for two reasons.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;First:&lt;/strong&gt; 30-minute polling means a 30-minute average lag from call end to action. In B2B sales, that is an eternity. A prospect who just had a strong 45-minute discovery call and then hears nothing for two hours has already started doubting the purchase.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Second:&lt;/strong&gt; CRM polling is noisy. Every CRM mutation you do not care about (field edits, internal notes, task updates) fires your pipeline. You spend more time filtering events than processing them.&lt;/p&gt;

&lt;p&gt;We moved to a fully event-driven architecture using webhook subscriptions from the CRM layer and call intelligence platforms. The moment a call recording is marked as processed, an event fires. Our orchestrator picks it up immediately.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;[Call ends]
     ↓
[Call intelligence platform marks recording complete]
     ↓
[Webhook fires → Event queue (SQS)]
     ↓
[Orchestrator picks up event]
     ↓
[Post-call execution agent initializes]

Average latency from call end to agent initialization: under 90 seconds.
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  &lt;strong&gt;The agent architecture: Three layers, not one&lt;/strong&gt;
&lt;/h2&gt;

&lt;p&gt;We tried a single large agent first. One LLM prompt, full context, single output. It was brittle. The model could not reliably separate summarization from decision-making from drafting and when one step failed, the whole output failed.&lt;/p&gt;

&lt;p&gt;We landed on a three-layer agent architecture:&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Layer 1: Extraction Agent&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;This agent receives the raw call transcript and pulls structured information:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Stakeholders identified on the call&lt;/li&gt;
&lt;li&gt;Pain points surfaced (verbatim, not paraphrased)&lt;/li&gt;
&lt;li&gt;Objections raised&lt;/li&gt;
&lt;li&gt;Next steps committed to by the rep&lt;/li&gt;
&lt;li&gt;Next steps committed to by the prospect&lt;/li&gt;
&lt;li&gt;Timeline signals ("we need this by Q3", "our budget cycle closes in April")&lt;/li&gt;
&lt;li&gt;Risk signals ("we're also talking to two other vendors")&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;This is a focused extraction task. The prompt is tight, the output schema is strict JSON and we validate every field before passing downstream.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="n"&gt;python&lt;/span&gt;
&lt;span class="n"&gt;extraction_schema&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;stakeholders&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;List&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nb"&gt;str&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt;
  &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;pain_points&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;List&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nb"&gt;str&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt;
  &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;objections&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;List&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nb"&gt;str&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt;
  &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;rep_commitments&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;List&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nb"&gt;str&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt;
  &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;prospect_commitments&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;List&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nb"&gt;str&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt;
  &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;timeline_signals&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;List&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nb"&gt;str&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt;
  &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;risk_signals&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;List&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nb"&gt;str&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Why separate this from the drafting layer? Because extraction errors compound. If the extraction agent misses a key objection, every downstream agent working from that output will produce work that ignores it. Isolating extraction lets us validate before we spend tokens drafting.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Layer 2: Decision Agent&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;The decision agent takes the extraction output plus CRM context (deal stage, days since last activity, contact history, existing open tasks) and makes three decisions:&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;What actions are required?&lt;/strong&gt; (Follow-up email, internal Slack summary, CRM field updates, task creation, risk flag)&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;What is the priority order?&lt;/strong&gt; (What needs to happen in the next 2 hours vs. next 48 hours)&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Are there any escalation triggers?&lt;/strong&gt; (Deal has gone dark before, timeline is tight, multiple competitors mentioned)&lt;/p&gt;

&lt;p&gt;This is where the revenue execution in B2B sales intelligence lives. The decision agent is not just routing tasks, it is applying deal context to determine what execution actually looks like for this specific call at this specific stage.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Layer 3: Execution Agent&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;The execution agent receives the action plan and executes each item:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Drafts the follow-up email with full call context embedded&lt;/li&gt;
&lt;li&gt;Writes the internal deal summary for Slack or CRM&lt;/li&gt;
&lt;li&gt;Creates CRM tasks with specific next-step language (not "follow up", "Send security review docs before Friday per Priya's request")&lt;/li&gt;
&lt;li&gt;Updates deal fields (stage, next step, close date confidence)&lt;/li&gt;
&lt;li&gt;Flags risk in the pipeline view if escalation triggers fired&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  &lt;strong&gt;Context management: The hard part&lt;/strong&gt;
&lt;/h2&gt;

&lt;p&gt;Here is the thing nobody warns you about when building deal-aware agents: context gets expensive fast.&lt;/p&gt;

&lt;p&gt;A single deal might have 12 call transcripts, 40 email threads, 6 months of CRM history and notes from three different reps. You cannot feed all of that into every agent call. But you also cannot ignore it, the agent making decisions about call number 12 needs to know what was said in calls 4, 7 and 9.&lt;/p&gt;

&lt;p&gt;We built a context assembly layer that sits between the CRM/call store and the agent layer. Before any agent call, the context assembler:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Retrieves the full deal history&lt;/li&gt;
&lt;li&gt;Scores each piece of context for relevance to the current call (using a smaller, cheaper model)&lt;/li&gt;
&lt;li&gt;Assembles a compressed context window: recent calls in full, older calls summarized to key facts&lt;/li&gt;
&lt;li&gt;Appends CRM state, open tasks and stakeholder map&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;The compressed context fits within token limits without losing decision-relevant history. The key design principle: recency gets full fidelity, history gets structured compression.&lt;/p&gt;

&lt;h2&gt;
  
  
  &lt;strong&gt;The CRM write problem&lt;/strong&gt;
&lt;/h2&gt;

&lt;p&gt;Writing back to CRM was harder than reading from it. Every CRM has its own field structure, validation logic and update rate limits. Salesforce behaves differently from HubSpot. HubSpot behaves differently from Pipedrive.&lt;/p&gt;

&lt;p&gt;We built a CRM adapter layer that abstracts this:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="n"&gt;python&lt;/span&gt;
&lt;span class="k"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;CRMAdapter&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
    &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;update_deal&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;self&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;deal_id&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;fields&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;dict&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;-&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;UpdateResult&lt;/span&gt;
    &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;create_task&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;self&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;deal_id&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;task&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;Task&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;-&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;TaskResult&lt;/span&gt;
    &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;log_activity&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;self&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;deal_id&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;activity&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;Activity&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;-&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;ActivityResult&lt;/span&gt;
    &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;flag_risk&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;self&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;deal_id&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;risk&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;RiskFlag&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;-&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;FlagResult&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Each CRM has its own implementation of this interface. The AI revenue orchestration layer never talks to a CRM directly, it only talks to the adapter. This was the right call. When Salesforce deprecated an API endpoint last year, we fixed one adapter, not twelve agent workflows.&lt;/p&gt;

&lt;h2&gt;
  
  
  &lt;strong&gt;What we got wrong the first time&lt;/strong&gt;
&lt;/h2&gt;

&lt;p&gt;&lt;strong&gt;We let the agent write directly to CRM without a review buffer&lt;/strong&gt;: It was fast, but reps hated it. They felt the system was changing their deals without them. We added a "pending actions" queue, agents queue their writes, reps get a one-tap approval UI and unreviewed items auto-execute after 4 hours. Adoption tripled.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;We over-indexed on transcript quality:&lt;/strong&gt; Our extraction agent fell apart when call audio was poor or transcripts had errors. We added a confidence scoring layer, if extraction confidence falls below threshold, the agent flags for human review rather than proceeding with bad data.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;We treated all deals the same:&lt;/strong&gt; A $2,000 SMB deal and a $200,000 enterprise deal should not have the same execution protocol. We added deal-tier routing, larger deals get more conservative execution with more human checkpoints, smaller deals get more aggressive automation.&lt;/p&gt;

&lt;h2&gt;
  
  
  &lt;strong&gt;The outcome worth mentioning&lt;/strong&gt;
&lt;/h2&gt;

&lt;p&gt;When we look at deals where the agent executed vs. deals where it did not (early days, inconsistent coverage), the difference in 30-day progression rates is significant enough that we do not run the comparison casually in meetings anymore. It makes the case too loudly.&lt;/p&gt;

&lt;p&gt;The point is not that AI replaces reps. The point is that execution should not depend on rep memory, bandwidth, or consistency. Post-call execution is a system design problem. We just built the system.&lt;/p&gt;

&lt;p&gt;Curious about how we handle the follow-up drafting at scale with real CRM context? That's the next post.&lt;/p&gt;

</description>
      <category>ai</category>
      <category>architecture</category>
      <category>llm</category>
      <category>revenue</category>
    </item>
  </channel>
</rss>
