Chapter 3.3 Multi-Agent Architecture

Chapter 3.2 established the AI data pipeline: five stages with schema contracts at each boundary, and an Assess stage that executes a single AI call against the enriched contact schema. This chapter replaces that single Assess stage with a coordinated sequence of AI calls each scoped to one assessment dimension, each with its own input and output contract, and each wired to the others through an explicit orchestration pattern.

A multi-agent system is a pipeline whose Assess stage is itself a pipeline. The outer pipeline structure from Chapter 3.2 does not change.

Learning Objectives

After completing this chapter, you will be able to:

  • Identify the three conditions that justify multi-agent architecture (ordered assessment dimensions, prompt complexity degrading quality, dimensions requiring different AI configuration) and apply them to determine when single-agent is sufficient.
  • Design agent contracts input schema, output schema, and behavioral specification for each agent in a multi-agent assessment pipeline.
  • Implement the three coordination patterns (Sequential, Parallel, Conditional) in n8n and select the correct pattern for a given assessment dependency structure.
  • Build the Orchestrator agent that synthesizes sub-agent outputs into a composite recommendation, including conflict detection between sub-agent assessments.
  • Write a Mini-ADR that documents the decision to use multi-agent architecture, with Context, Decision, and Rationale fields that a client team can interpret without engineering background.
  • Troubleshoot a multi-agent pipeline where the second agent is receiving null input fields, by diagnosing the Parse Response validation at the first agent boundary.

3.3.1 Why Single-Agent Systems Break Down

The Chapter 2.5 advisory architecture asks one AI call to evaluate all assessment dimensions simultaneously engagement type, strategic fit, urgency, complexity in a single prompt, producing a single advisory output. This is the correct architecture when dimensions are few and independent.

It becomes a ceiling under three conditions:

Prompt complexity ceiling. As assessment dimensions grow, the system prompt grows proportionally. The AI’s attention distributes across more dimensions, and per-dimension assessment quality degrades. A prompt asking twelve questions produces less reliable answers per question than twelve prompts each asking one. A system with one prompt per dimension can tune each prompt independently. A system with one combined prompt cannot.

Sequential dependency. Some dimensions cannot be assessed meaningfully in parallel. The answer to dimension A determines how dimension B should be asked. A single call cannot use its own intermediate output as the basis for a follow-up question within the same call.

Dimension-specific configuration. Different dimensions may need different context, temperature settings, or output schemas. A single call applies one configuration to all dimensions. A multi-agent system applies dimension-specific configuration per agent.

TipDesign Practice

Multi-agent architecture is not complexity for its own sake. It is the correct response when the assessment structure requires it when dimensions are ordered, when prompt complexity is degrading quality, or when different dimensions need different AI configuration. When none of these conditions hold, a single enriched call (Chapter 3.1) is the right choice.


3.3.2 Agent Contracts

Agent Contracts

Each agent in a multi-agent system is a Part I three-node triplet Build Prompt → HTTP Request → Parse Response with two contracts: an input contract defining what the agent expects to receive, and an output contract defining what it guarantees to produce.

Both contracts apply the same discipline as the Part I output contract (Section 1.5) and the Chapter 3.2 stage boundary schemas. The addition in Chapter 3.3: the output contract of Agent A is the input contract of Agent B. An agent that produces output outside its declared contract has broken the downstream agent’s input assumption. Validate at every boundary before the next Build Prompt executes.

Example Agent A (Engagement Type Classifier):

Contract Field Type Required
Input inquiry_text String Yes
Input engagement_type (raw form value) Enum Yes
Input enriched_contact_schema Object Yes
Output engagement_type_classified Enum Yes
Output classification_confidence Float 0.00–1.00 Yes
Output classification_rationale String Yes
Output classification_valid Boolean Yes

Example Agent B (Advisory Assessor):

Contract Field Type Required
Input All Agent A output fields Yes
Input enriched_contact_schema (passed through) Object Yes
Output Full advisory output contract (same as Part II) Yes

A validation node sits between Agent A and Agent B. If classification_valid is false or classification_confidence is below threshold, the pipeline routes to rule fallback Agent B does not execute. This mirrors the Part I confidence gate, applied at the inter-agent boundary.

CautionProduction Risk

Do not pass Agent A’s raw HTTP response to Agent B. Parse and validate Agent A’s output first. Agent B’s Build Prompt node references $json.engagement_type_classified if Agent A’s Parse Response node failed silently, that field is undefined and Agent B’s prompt is assembled with an empty classification. Validate at every agent boundary before the next Build Prompt executes.


3.3.3 Sequential Agents

Sequential Pattern

In the sequential pattern, Agent A completes before Agent B begins. Agent A’s validated output is a required input to Agent B’s prompt.

Use when: Assessment dimensions are ordered. The result of Agent A determines what Agent B should ask, or how Agent B should weight its assessment.

n8n implementation: Agent A’s Parse Response node connects to a validation IF node. The IF node’s true branch connects to Agent B’s Build Prompt node. Agent A’s output fields are available via expression references ($json.engagement_type_classified).

Wall time: A + B both API round-trips are paid in series.

Tradeoff: Sequential adds latency. This is justified when ordering is a structural requirement, not a preference.


3.3.4 Parallel Agents

Parallel Pattern

In the parallel pattern, Agent A and Agent B receive the same input simultaneously and run concurrently. Their outputs merge through an orchestrator node.

Use when: Assessment dimensions are independent. Neither agent requires the other’s output.

n8n implementation: Duplicate input paths or a Split In Batches node initiates two simultaneous HTTP Request calls. A Merge node collects both outputs. A synthesis Code node combines the two output contracts into a single advisory output.

Wall time: max(A, B) parallel execution reduces effective latency when both agents take similar time.

Tradeoff: Parallel requires both agents to succeed. Define explicitly what the merge node does with a partial result: proceed with requires_manual_review: true, or halt.

NoteEngineering Rationale

In the parallel pattern, add an On Error connection from each agent’s HTTP Request node that sets a {agent}_failed: true flag. The merge Code node checks both flags. If either agent failed, set requires_manual_review: true do not produce a composite score from one agent’s data alone.


3.3.5 Conditional Agents

Conditional Pattern

In the conditional pattern, a classifier agent routes to one of N specialist agents exclusively. The classifier determines which specialist is appropriate; only that specialist executes.

Use when: Input type varies and different types require different assessment expertise. Not every contact should traverse the same assessment path.

n8n implementation: A classifier agent produces a routing_decision field. A Switch node reads routing_decision and connects to the appropriate specialist’s Build Prompt. All specialist paths converge at Route + Store.

Tradeoff: The classifier itself can fail. Define a default specialist (typically the most conservative path) as the fallback when routing_decision is invalid.


3.3.6 Agent Boundary Design

Agent Boundary Design

An agent boundary is a deliberate architectural decision not a response to prompt length.

A boundary is justified when at least one of these conditions holds:

Condition Explanation
Sequential dependency Answer to question A must be known before question B can be formulated
Dimensional independence The questions can be answered without reference to each other (supports parallel)
Configuration difference The dimensions need different temperature, context, or output schemas
Auditability requirement The intermediate output requires independent logging or routing

A boundary is not justified by prompt length alone. A well-structured long prompt is better than two poorly-scoped short ones.

TipDesign Practice

Define agent scope in writing before building in n8n. For each agent, complete three sentences: “This agent receives . This agent answers . This agent produces ___.” If any sentence cannot be completed with a specific answer, the agent boundary is not well-defined.


3.3.7 Agent Failure Handling

Agent Failure Handling

Design the fallback specifically for each coordination pattern a single shared error handler does not cover the distinct failure states.

Failure modes differ by coordination pattern.

Pattern Agent A fails Agent B fails
Sequential Agent B does not execute. Route entire record to rule fallback. Agent A output is valid and preserved. Route record to manual review with A classification logged.
Parallel Merge receives only Agent B output. Proceed with requires_manual_review: true; flag A failure. Symmetric proceed with A output; flag B failure.
Conditional Classifier fails → use default specialist. Specialist fails → route to review; classifier output preserved in audit log.

In all patterns, ai_result_valid is evaluated per agent, not once for the entire multi-agent call. An agent that produces syntactically valid JSON but fails the output contract schema check is treated as failed. A system that validates per agent catches failures at the source. A system that validates only at the end cannot determine which agent caused the problem.


3.3.8 When Not to Use Multi-Agent Systems

Multi-agent systems add latency, contract surface area, and inter-agent failure modes. The decision requires explicit justification.

Prefer a single enriched call (Chapter 3.1) when: - The assessment has one primary dimension - Dimensions are few, independent, and assessable without sequential dependency - Latency is a binding constraint - Prompt complexity is manageable and per-dimension quality is acceptable

Use multi-agent when: - Dimensions are sequentially dependent - Prompt complexity is degrading per-dimension assessment quality - Different dimensions need different AI configuration - An intermediate output requires independent auditability

TipDesign Practice

A two-agent sequential system doubles the AI API cost per execution from approximately $0.002 to $0.004 at GPT-4o-mini pricing. At 200 executions per month, the delta is $0.40. The cost argument against multi-agent is rarely decisive; the added complexity and failure surface area are the more relevant factors.


Reference Diagrams

Figure 3.3.1 Sequential Agent Topology

Figure 35.1 shows the sequential pattern: Agent A → validation gate → Agent B. Agent A’s output is a required input to Agent B’s Build Prompt. On Error paths shown at each agent boundary.

%%{init:{"theme":"base","themeVariables":{"primaryColor":"#eef2ff","primaryTextColor":"#1e1b4b","primaryBorderColor":"#6366f1","lineColor":"#6366f1","clusterBkg":"#f8f9ff","clusterBorder":"#6366f1","titleColor":"#1e1b4b","edgeLabelBackground":"#f6f4ef","fontFamily":"system-ui,sans-serif","fontSize":"13px"}}}%%

flowchart TD
    T["Trigger"]:::trigger --> A["Agent A Build → HTTP → Parse"]:::process
    A --> G{"Validation Gate"}:::decision
    A -->|"On Error"| EA[["Agent A Error (exit)"]]
    G -->|"FAIL"| EA
    G -->|"PASS"| B["Agent B Build → HTTP → Parse"]:::process
    B -->|"On Error"| EB[["Agent B Error (exit)"]]
    B -->|"PASS"| O["Output"]:::success
    classDef trigger  fill:#dcfce7,stroke:#16a34a,color:#14532d,font-weight:600
    classDef process  fill:#eef2ff,stroke:#6366f1,color:#1e1b4b
    classDef decision fill:#fef9c3,stroke:#ca8a04,color:#78350f,font-weight:600
    classDef success  fill:#d1fae5,stroke:#059669,color:#064e3b,font-weight:700
    classDef fallback fill:#fee2e2,stroke:#dc2626,color:#7f1d1d
Figure 35.1: Sequential Agent Topology. Sequential Agent Topology. Agent A (Build Prompt, HTTP Request, Parse Response) connects through a validation IF node to Agent B. Agent A output fields pass to Agent B’s Build Prompt. On Error paths route to rule fallback or manual review depending on which agent fails.

Figure 3.3.2 Parallel Agent Topology

Figure 35.2 shows the parallel pattern: both agents receive the same input simultaneously. Wall time is max(A, B). The synthesis Code node handles partial results.

%%{init:{"theme":"base","themeVariables":{"primaryColor":"#eef2ff","primaryTextColor":"#1e1b4b","primaryBorderColor":"#6366f1","lineColor":"#6366f1","clusterBkg":"#f8f9ff","clusterBorder":"#6366f1","titleColor":"#1e1b4b","edgeLabelBackground":"#f6f4ef","fontFamily":"system-ui,sans-serif","fontSize":"13px"}}}%%

flowchart TD
    T["Trigger"]:::trigger --> F["FORK (wall time = max, not sum)"]:::process
    F --> A["Agent A (async)"]:::process
    F --> B["Agent B (async)"]:::process
    A -->|"completes"| S{"SYNC WAIT"}:::decision
    B -->|"completes"| S
    A -->|"On Error"| S
    B -->|"On Error"| S
    S --> M["Merge Results (partial → requires_manual_review)"]:::process
    M --> O["Output"]:::success
    classDef trigger  fill:#dcfce7,stroke:#16a34a,color:#14532d,font-weight:600
    classDef process  fill:#eef2ff,stroke:#6366f1,color:#1e1b4b
    classDef decision fill:#fef9c3,stroke:#ca8a04,color:#78350f,font-weight:600
    classDef success  fill:#d1fae5,stroke:#059669,color:#064e3b,font-weight:700
    classDef fallback fill:#fee2e2,stroke:#dc2626,color:#7f1d1d
Figure 35.2: Parallel Agent Topology. Parallel Agent Topology. Input splits to Agent A and Agent B simultaneously. Both connect to a Merge node. A synthesis Code node produces the composite output and sets requires_manual_review if either agent failed.

Figure 3.3.3 Conditional Agent Topology

Figure 35.3 shows the conditional pattern: a classifier routes exclusively to one specialist. All specialist paths converge at Route + Store. The default specialist handles invalid routing decisions.

%%{init:{"theme":"base","themeVariables":{"primaryColor":"#eef2ff","primaryTextColor":"#1e1b4b","primaryBorderColor":"#6366f1","lineColor":"#6366f1","clusterBkg":"#f8f9ff","clusterBorder":"#6366f1","titleColor":"#1e1b4b","edgeLabelBackground":"#f6f4ef","fontFamily":"system-ui,sans-serif","fontSize":"13px"}}}%%

flowchart TD
    T["Trigger"]:::trigger --> C["Classifier Agent produces routing_decision"]:::process
    C --> R{"routing_decision"}:::decision
    R -->|"complex"| SA["Specialist A"]:::process
    R -->|"standard"| SB["Specialist B"]:::process
    R -->|"fallback"| DH["Default Handler"]:::process
    SA --> OUT["Converge → Output"]:::success
    SB --> OUT
    DH --> OUT
    classDef trigger  fill:#dcfce7,stroke:#16a34a,color:#14532d,font-weight:600
    classDef process  fill:#eef2ff,stroke:#6366f1,color:#1e1b4b
    classDef decision fill:#fef9c3,stroke:#ca8a04,color:#78350f,font-weight:600
    classDef success  fill:#d1fae5,stroke:#059669,color:#064e3b,font-weight:700
    classDef fallback fill:#fee2e2,stroke:#dc2626,color:#7f1d1d
Figure 35.3: Conditional Agent Topology. Conditional Agent Topology. A classifier agent produces a routing_decision. A Switch node routes to Specialist A or Specialist B exclusively. Default specialist shown as fallback. All paths converge at Route and Store.

Mini-ADR 6.3-A What Belongs Inside an Agent?

The mini-ADR is a one-paragraph architectural decision record. This format is practiced here; the Part III Capstone expands it to the full seven-field ADR format used in Part II.

Required format:

Context: [The architectural constraint that makes this decision necessary]

Decision: [Which pattern and which dimensions belong to which agent]

Rationale: [Why this pattern over the alternatives. Name each alternative and state why it was not chosen.]

Your Mini-ADR 6.3-A task: Write a one-paragraph ADR for the Meridian Venture Partners capstone not for Vantage. The Meridian brief describes deal flow assessment of 200–300 submissions per quarter from founders and accelerators, with qualifying signals across founding team, market size, traction, and engagement type.

Consider: Are these assessment dimensions ordered or independent? Does founding team evaluation require knowing the stated market size first, or can both be assessed simultaneously? Your answer determines which pattern the capstone ADR-A should choose and why.

This ADR is the direct precursor to Capstone ADR-A. The one-paragraph form practiced here scales to the 250–400 word form required in the capstone deliverable.


Practical Exercise 3.3 Sequential Two-Agent Assessment

Business Scenario

Vantage Advisory Partners handles three distinct engagement types assessment, engagement, and transformation program each requiring a different advisory approach. The single advisory AI call in Workflow A applies generic assessment criteria regardless of engagement type, producing recommendations that sometimes miss type-specific nuances. The advisors want the classification step separated from the advisory step so each can be tuned independently.

The Problem

Asking one AI call to classify the engagement type and then apply type-specific advisory criteria in the same prompt degrades both tasks. The classification is buried in a multi-part prompt; the advisory assessment cannot reference the classification result as a first-class input.

The Architectural Solution

Split the Assess stage into two sequential agents. Agent A classifies the engagement type and produces a validated classification output. A gate validates Agent A’s result. Agent B receives Agent A’s classification alongside the enriched contact schema and applies type-appropriate advisory criteria.

Step 1 Build Agent A (Engagement Type Classifier)

Purpose

Agent A’s sole responsibility is classifying the engagement type of the current contact submission nothing else. Separating classification from advisory assessment creates two independently tunable prompts: one for classification accuracy, one for advisory quality. In the single-agent Part II architecture, both tasks share one prompt, one temperature setting, and one token budget. Separating them lets you increase max_tokens for the advisory assessment without inflating the classification call, set temperature: 0 for the classification call (deterministic output is correct here) while keeping temperature: 0.2 for advisory reasoning, and test each independently using the six-contact fixture set.


Operation Summary

Property Value
Node Type Build Prompt A (Code) → HTTP Request A → Parse Response A
Method POST
Endpoint OpenAI chat completions (or configured LLM endpoint)
Primary Function Classify engagement type from inquiry text and form field
Input Enriched contact schema from [ENRICH] stage
Output Classification output contract (four fields)

Implementation Logic

// Build Prompt A Engagement Type Classifier
const enriched = $input.first().json;

const systemMessage = `
You are an engagement type classifier for Vantage Advisory Partners.
Classify the engagement type of the following contact submission.

Engagement types (choose exactly one):
- assessment: a scoped discovery or capability evaluation engagement
- engagement: an ongoing advisory retainer or project-based engagement
- transformation_program: a multi-phase organizational change program

Return JSON with this exact schema:
{
  "engagement_type_classified": "<assessment|engagement|transformation_program>",
  "classification_confidence": <float 0.00-1.00>,
  "classification_rationale": "<one sentence>",
  "classification_valid": true
}
`.trim();

return [{
  json: {
    prompt_payload: {
      model: "gpt-4o-mini",
      messages: [
        { role: "system", content: systemMessage },
        {
          role: "user",
          content: `Inquiry: ${enriched.inquiry_text}\nStated engagement type: ${enriched.engagement_type}`
        }
      ],
      max_tokens: 150,
      temperature: 0
    }
  }
}];

max_tokens: 150 is sufficient for the four-field JSON output. temperature: 0 forces deterministic classification the same input should produce the same classification on every run, which is required for the regression test in Practical 3.7. The stated engagement_type form field is provided as a secondary signal, not as the answer the AI classifies from inquiry_text and uses the form field as a consistency check.


Request Field Table

Field Required Description
inquiry_text Yes Free-text submission field primary classification signal
engagement_type (raw) Yes Form-selected value secondary consistency check
model Yes LLM identifier
max_tokens Yes Set to 150 sufficient for four-field JSON output
temperature Yes Set to 0 for deterministic classification

Response Processing

// Parse Response A extract and validate classification output
const rawContent = $json.choices[0].message.content;
let parsed;

try {
  parsed = JSON.parse(rawContent);
} catch (e) {
  return [{
    json: {
      engagement_type_classified: null,
      classification_confidence: 0,
      classification_rationale: "Parse failure: " + e.message,
      classification_valid: false
    }
  }];
}

const validTypes = ["assessment", "engagement", "transformation_program"];
const typeValid = validTypes.includes(parsed.engagement_type_classified);
const confidenceValid = typeof parsed.classification_confidence === 'number'
  && parsed.classification_confidence >= 0
  && parsed.classification_confidence <= 1;

return [{
  json: {
    engagement_type_classified: typeValid ? parsed.engagement_type_classified : null,
    classification_confidence: confidenceValid ? parsed.classification_confidence : 0,
    classification_rationale: parsed.classification_rationale ?? null,
    classification_valid: typeValid && confidenceValid
  }
}];

The Parse Response node validates two contract fields inline: engagement_type_classified must be one of the three valid enum values, and classification_confidence must be a float in range. Setting classification_valid: false on any validation failure allows the downstream IF gate to route to rule fallback without needing to inspect individual fields.


Output Table

Output Type Description
engagement_type_classified Enum One of: assessment, engagement, transformation_program
classification_confidence Float 0–1 AI’s confidence in the classification
classification_rationale String One-sentence rationale for the classification
classification_valid Boolean true only if type and confidence both pass validation

Engineering Rationale

NoteEngineering Rationale

Agent A is the first application of the Part I three-node triplet as a scoped component within a larger multi-agent system. Build Prompt A, HTTP Request A, and Parse Response A are structurally identical to the Chapter 2.5 advisory triplet the only difference is the prompt content and output contract. Multi-agent architecture is a composition of three-node triplets, not a new pattern. Each triplet is individually testable, individually replaceable, and individually tunable.


Step 2 Add the Validation Gate

Purpose

The Validation Gate is the inter-agent boundary control. It enforces Agent A’s output contract before Agent B executes. Without this gate, Agent B’s Build Prompt node references $json.engagement_type_classified if that field is null or invalid, the advisory prompt is assembled with an empty or undefined classification, producing a miscalibrated assessment with no error logged. The Validation Gate makes contract enforcement explicit and auditable: every time a contact routes to rule fallback, it is because Agent A’s output failed a defined contract condition, not because an undefined reference silently propagated.


Operation Summary

Property Value
Node Type IF
Primary Function Enforce Agent A output contract before Agent B executes
Input Parse Response A output
Output (True) Proceeds to Agent B Build Prompt
Output (False) Routes to rule fallback; sets requires_manual_review

Decision Configuration

// n8n IF node both conditions must be true
{{ $json.classification_valid === true }}
AND
{{ $json.classification_confidence >= 0.65 }}

The confidence threshold 0.65 is the minimum acceptable confidence for classification to inform the advisory assessment. Below this threshold, the classification is treated as unreliable Agent B should not tailor its advisory criteria to a classification that has a 35%+ chance of being wrong. The rule fallback path that handles the False branch uses the raw engagement_type form field instead of the AI classification.


False Branch Configuration

On the False branch, insert a Code node that sets:

return [{
  json: {
    ...($input.first().json),
    requires_manual_review: true,
    fallback_reason: "Agent A classification failed or below confidence threshold",
    agent_a_confidence: $input.first().json.classification_confidence ?? 0
  }
}];

Route the False branch to the Part II rule fallback path. The requires_manual_review: true flag is written to HubSpot in the Route + Store stage so advisors can identify contacts whose classification fell back.


Output Table

Output (True) Description
Agent A classification All four fields passed through to Agent B Build Prompt
Output (False) Description
requires_manual_review Boolean true
fallback_reason String description of why the gate routed to fallback
agent_a_confidence The confidence value that fell below threshold

Engineering Rationale

NoteEngineering Rationale

The Validation Gate is the Part I confidence gate pattern (Section 1.5) applied at an inter-agent boundary rather than at the workflow exit. In Part I, the confidence gate determines whether the AI’s advisory output is routed to the main path or a fallback. Here, it determines whether Agent A’s classification output is routed to Agent B or a fallback. The same principle validate before acting on AI output applies at every AI boundary in the system.


Step 3 Update Agent B (Advisory Assessor)

Purpose

Agent B performs the advisory assessment using Agent A’s validated classification as a first-class input. The Build Prompt B node receives the classification not the raw form field so it can tailor advisory criteria to the confirmed engagement type. An assessment prompt that references engagement_type_classified: "transformation_program" can apply fundamentally different criteria than one that receives engagement_type: "assessment" from a form field the contact may have mislabeled. The classification confidence is also provided so the AI can calibrate its certainty proportionally.


Operation Summary

Property Value
Node Type Build Prompt B (Code) → HTTP Request B → Parse Response B
Primary Function Advisory assessment using Agent A’s classification as input
Input Agent A output + enriched contact schema (passed through)
Output Full advisory output contract (same as Part II)

Implementation Logic

// Build Prompt B Advisory Assessor (updated)
const agentAOutput = $input.first().json;
const enriched = $('Enrich Firmographic (Mock)').first().json;
const peerContext = enriched.peer_context ?? { peer_count: 0 };

const firmographicBlock = enriched.firmographic_source === 'unavailable'
  ? `FIRMOGRAPHIC CONTEXT: Unavailable.`
  : `FIRMOGRAPHIC CONTEXT:
Company size: ${enriched.company_size_category}
Industry: ${enriched.industry_vertical}`;

const systemMessage = `
You are an advisory assessment specialist for Vantage Advisory Partners.

ENGAGEMENT TYPE (AI-classified): ${agentAOutput.engagement_type_classified}
CLASSIFICATION CONFIDENCE: ${agentAOutput.classification_confidence}

Apply assessment criteria appropriate for a ${agentAOutput.engagement_type_classified} engagement.
If classification confidence is below 0.80, note in your rationale that engagement type classification
carries some uncertainty.

PEER CONTEXT (last ${peerContext.peer_count} assessed contacts):
${JSON.stringify(peerContext, null, 2)}

${firmographicBlock}

CURRENT SUBMISSION:
Inquiry text: ${enriched.inquiry_text}
Company: ${enriched.company_name}
Source: ${enriched.source}

Return the full advisory output contract as JSON.
`.trim();

return [{
  json: {
    prompt_payload: {
      model: "gpt-4o-mini",
      messages: [
        { role: "system", content: systemMessage },
        { role: "user", content: "Provide the advisory assessment." }
      ],
      max_tokens: 400,
      temperature: 0.2
    }
  }
}];

The classification confidence is injected into the system message with a behavioral instruction: if confidence is below 0.80, the rationale should acknowledge classification uncertainty. This produces more calibrated advisory outputs when the engagement type is ambiguous the AI’s confidence reflects Agent A’s confidence rather than overstating certainty.


Request Field Table

Field Required Description
engagement_type_classified Yes Validated classification from Agent A
classification_confidence Yes Agent A’s confidence; informs advisory calibration
peer_context Yes Peer scoring summary from Practical 3.1
Firmographic block Yes Enrichment context or unavailability notice
inquiry_text, company_name, source Yes Core submission fields for assessment

Response Processing

Parse Response B uses the same output contract validation as the Part II advisory call. The output schema is unchanged the advisory path, confidence score, and rationale fields are identical to what Part II produced. The only difference is the prompt that produced them.

// Parse Response B same contract as Part II advisory output
const rawContent = $json.choices[0].message.content;
let parsed;
try {
  parsed = JSON.parse(rawContent);
} catch (e) {
  return [{ json: { ai_result_valid: false, parse_error: e.message } }];
}

return [{
  json: {
    advisory_path: parsed.advisory_path,
    confidence_score: parsed.confidence_score,
    advisory_rationale: parsed.advisory_rationale,
    priority_label: parsed.priority_label,
    ai_result_valid: !!(parsed.advisory_path && parsed.confidence_score)
  }
}];

Output Table

Output Description
advisory_path Enum matching VAP advisory path values
confidence_score Float 0.00–1.00 AI’s confidence in the advisory output
advisory_rationale Free-text rationale referencing engagement type and context
priority_label Priority label string (Hot, Warm, Cold, etc.)
ai_result_valid Boolean True if required output fields are present

Engineering Rationale

NoteEngineering Rationale

Agent B’s output contract is identical to the Part II advisory output contract. This is a deliberate constraint: downstream routing, HubSpot property writes, and Slack notifications all reference advisory_path and confidence_score by name. Preserving the output contract means the Route + Store stage does not change when the Assess stage goes from single-agent to two-agent. The internal structure of the Assess stage is upgraded; its interface with Route + Store is not.


Step 4 Add On Error at Both Agent Boundaries

Purpose

Each agent’s HTTP Request node is a network call to an external API it can fail with a 429 (rate limit), 503 (service degradation), or timeout. Without On Error paths at each boundary, a single API failure halts the entire workflow, leaves the contact unprocessed, and produces no log of which agent failed or why. On Error paths at each agent boundary ensure that API failures produce defined fallback behavior, preserve whatever partial output is available, and generate an auditable record of the failure before the workflow exits cleanly.


Operation Summary

Property Value
Node Type On Error connection from each HTTP Request node
Primary Function Handle API failure at each agent boundary with defined fallback
Agent A On Error Sets classification_valid: false; routes through Validation Gate False branch
Agent B On Error Sets requires_manual_review: true; posts Slack alert; preserves Agent A output

Agent A On Error Configuration

// On Error from HTTP Request A classification API failure
return [{
  json: {
    engagement_type_classified: null,
    classification_confidence: 0,
    classification_rationale: "Agent A API failure rule fallback initiated",
    classification_valid: false
  }
}];

This output feeds directly into the Validation Gate. classification_valid: false ensures the gate routes to the False branch (rule fallback) without the gate needing to detect an error state separately. The On Error path and the low-confidence fallback path converge at the same gate.


Agent B On Error Configuration

// On Error from HTTP Request B advisory API failure
const agentAOutput = $('Parse Response A').first().json;

return [{
  json: {
    advisory_path: null,
    confidence_score: 0,
    advisory_rationale: "Agent B API failure manual review required",
    priority_label: null,
    ai_result_valid: false,
    requires_manual_review: true,
    // Preserve Agent A output for audit log
    agent_a_classification: agentAOutput.engagement_type_classified,
    agent_a_confidence: agentAOutput.classification_confidence
  }
}];

Agent B’s On Error preserves Agent A’s classification in the output so it can be written to the HubSpot audit log. Agent A succeeded and produced valid output that output should not be discarded because Agent B failed. A Slack alert posts to #vap-ops with the contact ID, Agent A’s classification, and the failure reason.


Output Table (Agent A On Error)

Output Description
classification_valid false routes Validation Gate to False branch
classification_rationale Describes the API failure for the audit log

Output Table (Agent B On Error)

Output Description
ai_result_valid false downstream does not route as successful AI
requires_manual_review true written to HubSpot for advisor review queue
agent_a_classification Preserved Agent A output for audit log

Engineering Rationale

NoteEngineering Rationale

On Error paths are not edge cases they are first-class architectural components. In a two-agent sequential system, there are two independent API call failure modes, two distinct failure states, and two different fallback behaviors. Designing each On Error path specifically for its agent’s failure mode produces a system that degrades predictably. A shared error handler that treats both failures identically obscures which agent failed and produces identical Slack alerts for operationally different situations.


Step 5 Write Bootstrap Properties

Purpose

With the transition to two-agent sequential architecture, p6_agent_call_count increments to 2. This is the first practical in Part III where that property exceeds the Part II baseline of 1. Writing p6_agent_call_count: 2 to all contacts processed by this workflow makes the transition to multi-agent architecture observable in HubSpot: any contact with p6_agent_call_count: 2 was processed by the sequential two-agent Assess stage. Chapter 3.5 uses this property to measure the proportion of contacts processed by each architecture across the contact base.


Operation Summary

Property Value
Node Type HTTP Request
Method PATCH
Endpoint /crm/v3/objects/contacts/{contactId}
Primary Function Write four p6_ audit properties, including p6_agent_call_count: 2
Output Updated contact object

Request Payload

{
  "properties": {
    "p6_last_execution_source": "ai",
    "p6_last_confidence_score": "{{ $('Parse Response B').first().json.confidence_score }}",
    "p6_last_advisory_path": "{{ $('Parse Response B').first().json.advisory_path }}",
    "p6_agent_call_count": "2"
  }
}

p6_last_execution_source, p6_last_confidence_score, and p6_last_advisory_path reflect Agent B’s output the final advisory call. Agent A’s classification is preserved in the audit log but does not appear in these four properties. If Agent B’s On Error path fired (ai_result_valid: false), write p6_last_execution_source: "rule_fallback" instead of "ai".


Output Table

Output Description
p6_last_execution_source "ai" or "rule_fallback" based on Agent B outcome
p6_last_confidence_score Agent B’s confidence score (or 0 for rule fallback)
p6_last_advisory_path Agent B’s advisory path (or rule fallback path value)
p6_agent_call_count 2 first value above Part II baseline across all contacts

Engineering Rationale

NoteEngineering Rationale

p6_agent_call_count is the observable signature of Part III multi-agent architecture. A contact base where all records show p6_agent_call_count: 1 is a Part II architecture contact base even if it was built in Part III. A contact base where records transition from 1 to 2 is evidence that the sequential two-agent architecture is live and writing. Chapter 3.5’s observability layer uses the distribution of this property to track the Part II→Part III architectural migration in real time.


Step 6 Run Six Test Payloads

Purpose

Running the six test contacts through the sequential two-agent workflow validates three properties simultaneously: Agent A classifies each contact’s engagement type correctly, Agent B uses the classified type in its advisory assessment, and p6_agent_call_count: 2 is written to all six contacts. The comparison of advisory paths and confidence scores against the Practical 3.1 baseline (single-context, single-agent) produces the second data point in the multi-practical comparison table that Capstone Deliverable 8 draws on.


Operation Summary

Property Value
Primary Function Execute all six test contacts and record two-agent outputs
Input Six test contact payloads from Deliverable 4
Output Comparison table: classification, advisory path, confidence across all three practicals

Implementation Logic

For each of the six contacts, record four values after the workflow completes:

const result = {
  contact_id: "<hubspot_contact_id>",
  agent_a_classification: "<engagement_type_classified from Agent A>",
  agent_a_confidence: <float from Agent A>,
  agent_b_advisory_path: "<from HubSpot after workflow>",
  agent_b_confidence: <float from HubSpot after workflow>,
  p6_agent_call_count: 2
};

Add agent_a_classification and agent_a_confidence as new columns in the comparison table started in Practical 3.1. Note any contacts where Agent B produced a different advisory path than the Part II baseline and whether Agent A’s engagement type classification is the likely cause.


Output Table

Output Description
agent_a_classification Engagement type classified by Agent A
agent_a_confidence Agent A’s classification confidence
agent_b_advisory_path Final advisory path from Agent B
agent_b_confidence Agent B’s advisory confidence score
p6_agent_call_count Confirms 2 written to all six contacts

Engineering Rationale

ImportantCritical Requirement

If Agent A’s classification_valid is always false, check whether Parse Response A is correctly extracting and JSON.parse()-ing $json.choices[0].message.content. The most common error is referencing the raw HTTP response structure before parsing the content string. Verify the extraction path by logging $json.choices[0].message.content directly if it is a string beginning with {, the JSON parse step is missing. If it is already an object, remove the JSON.parse() call.

Production Consideration

This practical implements two agents with individual On Error paths but no aggregate latency budget. In production, a sequential two-agent system has wall time A + B two API round-trips paid in series. If either agent exceeds its expected response time, the downstream routing may exceed HubSpot’s webhook response timeout. Chapter 3.5 (Production Observability) covers latency percentile tracking across agent calls. Monitor p6_agent_call_count alongside execution duration to establish baseline latency before adding a third agent in the capstone.

Deliverable: Sequential two-agent Workflow A with validation gate and On Error at both boundaries. Six test payloads run with p6_agent_call_count: 2. Mini-ADR 6.3-A written.

Estimated time: 2–3 hours.


Discussion Questions

  1. The validation gate routes to rule fallback when classification_confidence < 0.65. What are the operational consequences of setting this threshold too high (0.90) versus too low (0.40) for the Vantage Advisory Partners use case?

  2. Agent B receives Agent A’s classification_rationale field. Should Build Prompt B include the rationale in the context, or only engagement_type_classified? What are the risks of including the rationale?

  3. At what contact volume and submission rate would the sequential pattern’s added latency (two API calls in series) become operationally significant? At that point, would the parallel or conditional pattern be more appropriate, or would a different architectural change be required?


Chapter Summary

A multi-agent system replaces the Assess stage in the Chapter 3.2 pipeline with a coordinated sequence of AI calls, each governed by its own input and output contract. The outer pipeline structure does not change.

Three coordination patterns address different assessment structures: sequential for ordered dimensions, parallel for independent dimensions, conditional for routing to specialist agents.

Agent boundaries are architectural decisions justified by assessment structure not by prompt length. When no structural justification exists, a single enriched call from Chapter 3.1 is the correct choice. When multi-agent is justified, add On Error handling at every agent boundary and design the fallback specifically for each pattern’s failure modes.

Key Principle

Each agent in a multi-agent system must have one input contract and one output contract. The output contract of Agent A is the input contract of Agent B. Validate at every agent boundary before the next Build Prompt executes.


Transition to Chapter 3.4

Chapters 3.1 through 6.3 addressed how the AI assessment executes what context it receives and how many coordinated calls it makes. Chapter 3.4 addresses when the assessment executes: the trigger architecture.

Part II uses a Schedule Trigger for Workflow C polling HubSpot every N hours. That polling limit produces the detection latency failure scenario from 3.0.2 Structural Limits of Part II: a Qualified Prospect receives a competitor proposal and the follow-up workflow does not fire until the next poll cycle. Event-driven triggering eliminates detection latency. Chapter 3.4 teaches the design patterns that make event-driven AI systems reliable in production.


Key Takeaways

  1. A multi-agent system replaces the single Assess stage in the Chapter 3.2 pipeline with coordinated AI calls. The outer pipeline structure Ingest, Normalize, Enrich, Route + Store does not change.
  2. Each agent has an input contract and an output contract. The output contract of Agent A is the input contract of Agent B. Validate at every agent boundary before the next Build Prompt executes.
  3. Sequential: Agent A’s output is required by Agent B. Parallel: both receive the same input and run concurrently. Conditional: a classifier routes to one specialist exclusively.
  4. Add On Error at each agent’s HTTP Request node. Design the fallback specifically for the pattern’s failure mode not with a single shared error handler.
  5. Agent boundaries are justified by assessment structure: sequential dependency, dimensional independence, configuration difference, or auditability requirement.
  6. Set p6_agent_call_count: 2 after Practical 3.3. This is the first value above the Part II baseline of 1.

End of Chapter 3.3 Multi-Agent Architecture