|
| 1 | +# Solve the Crime |
| 2 | + |
| 3 | +The only thing missing now is your **Lead Detective**. This node will synthesize findings from the Appraiser, Evidence Analyst, and Intelligence Researcher to identify the culprit and calculate the total value of the stolen items. |
| 4 | + |
| 5 | +## Build the Lead Detective Node |
| 6 | + |
| 7 | +### What You Already Have |
| 8 | + |
| 9 | +After completing Exercise 06, your system already includes: |
| 10 | +1. ✅ Appraiser Agent (RPT-1 predictions) |
| 11 | +2. ✅ Evidence Analyst (Internal document search) |
| 12 | +3. ✅ Intelligence Researcher (Web search) ← NEW in Exercise 06 |
| 13 | +4. ✅ Lead Detective configuration in `agentConfigs.ts` ← Already updated in Exercise 06 |
| 14 | +5. ✅ Lead Detective node in `investigationWorkflow.ts` ← Already updated in Exercise 06 |
| 15 | + |
| 16 | +### Verify Your Implementation |
| 17 | + |
| 18 | +👉 Open [`/project/JavaScript/solution/src/investigationWorkflow.ts`](/project/JavaScript/solution/src/investigationWorkflow.ts) |
| 19 | + |
| 20 | +👉 Verify that you have the complete workflow with all four agents: |
| 21 | + |
| 22 | +```typescript |
| 23 | + private buildGraph(): StateGraph<AgentState> { |
| 24 | + const workflow = new StateGraph<AgentState>({ |
| 25 | + channels: { |
| 26 | + payload: null, |
| 27 | + suspect_names: null, |
| 28 | + appraisal_result: null, |
| 29 | + evidence_analysis: null, |
| 30 | + intelligence_report: null, // Should be present from Exercise 06 |
| 31 | + final_conclusion: null, |
| 32 | + messages: null, |
| 33 | + }, |
| 34 | + }) |
| 35 | + |
| 36 | + workflow |
| 37 | + .addNode('appraiser', this.appraiserNode.bind(this)) |
| 38 | + .addNode('evidence_analyst', this.evidenceAnalystNode.bind(this)) |
| 39 | + .addNode('intelligence_researcher', this.intelligenceResearcherNode.bind(this)) // Should be present |
| 40 | + .addNode('lead_detective', this.leadDetectiveNode.bind(this)) |
| 41 | + .addEdge(START, 'appraiser') |
| 42 | + .addEdge('appraiser', 'evidence_analyst') |
| 43 | + .addEdge('evidence_analyst', 'intelligence_researcher') // Should be present |
| 44 | + .addEdge('intelligence_researcher', 'lead_detective') // Should be present |
| 45 | + .addEdge('lead_detective', END) |
| 46 | + |
| 47 | + return workflow |
| 48 | + } |
| 49 | +``` |
| 50 | + |
| 51 | +> 💡 **The execution order is defined entirely by the edges:** |
| 52 | +> |
| 53 | +> 1. `START → appraiser` — workflow begins with the Appraiser |
| 54 | +> 2. `appraiser → evidence_analyst` — after RPT-1 completes, Evidence Analyst runs |
| 55 | +> 3. `evidence_analyst → intelligence_researcher` — after internal search completes, web search runs |
| 56 | +> 4. `intelligence_researcher → lead_detective` — after web search completes, Lead Detective synthesizes |
| 57 | +> 5. `lead_detective → END` — Lead Detective's conclusion becomes the final result |
| 58 | +
|
| 59 | +### Verify main.ts |
| 60 | + |
| 61 | +👉 Check your [`/project/JavaScript/solution/src/main.ts`](/project/JavaScript/solution/src/main.ts): it needs no changes from Exercise 04. |
| 62 | + |
| 63 | +```typescript |
| 64 | +import 'dotenv/config' |
| 65 | +import { InvestigationWorkflow } from './investigationWorkflow.js' |
| 66 | +import { payload } from './payload.js' |
| 67 | + |
| 68 | +async function main() { |
| 69 | + console.log('═══════════════════════════════════════════════════════════') |
| 70 | + console.log(' 🔍 ART THEFT INVESTIGATION - MULTI-AGENT SYSTEM') |
| 71 | + console.log('═══════════════════════════════════════════════════════════\n') |
| 72 | + |
| 73 | + const workflow = new InvestigationWorkflow(process.env.MODEL_NAME!) |
| 74 | + const suspectNames = 'Sophie Dubois, Marcus Chen, Viktor Petrov' |
| 75 | + |
| 76 | + console.log('📋 Case Details:') |
| 77 | + console.log(` • Stolen Items: ${payload.rows.length} artworks`) |
| 78 | + console.log(` • Suspects: ${suspectNames}`) |
| 79 | + console.log(` • Investigation Team: 4 specialized agents\n`) // Updated to 4 agents |
| 80 | + |
| 81 | + const startTime = Date.now() |
| 82 | + |
| 83 | + const result = await workflow.kickoff({ |
| 84 | + payload, |
| 85 | + suspect_names: suspectNames, |
| 86 | + }) |
| 87 | + |
| 88 | + const duration = ((Date.now() - startTime) / 1000).toFixed(2) |
| 89 | + |
| 90 | + console.log('\n═══════════════════════════════════════════════════════════') |
| 91 | + console.log(' 📘 FINAL INVESTIGATION REPORT') |
| 92 | + console.log('═══════════════════════════════════════════════════════════\n') |
| 93 | + console.log(result) |
| 94 | + console.log('\n═══════════════════════════════════════════════════════════') |
| 95 | + console.log(` ⏱️ Investigation completed in ${duration} seconds`) |
| 96 | + console.log('═══════════════════════════════════════════════════════════\n') |
| 97 | +} |
| 98 | + |
| 99 | +main() |
| 100 | +``` |
| 101 | + |
| 102 | +--- |
| 103 | + |
| 104 | +## Solve the Crime |
| 105 | + |
| 106 | +👉 Run your complete investigation workflow: |
| 107 | + |
| 108 | +```bash |
| 109 | +# From repository root |
| 110 | +npm start --prefix ./project/JavaScript/solution |
| 111 | +``` |
| 112 | + |
| 113 | +```bash |
| 114 | +# From solution folder |
| 115 | +npm start |
| 116 | +``` |
| 117 | + |
| 118 | +> ⏱️ **This may take 4-7 minutes** as your agents: |
| 119 | +> |
| 120 | +> 1. Predict insurance values for stolen items using SAP-RPT-1 |
| 121 | +> 2. Search internal evidence documents for each suspect using the Grounding Service |
| 122 | +> 3. Search the web for criminal patterns and suspect backgrounds using Sonar-Pro |
| 123 | +> 4. Analyze all findings and identify the culprit |
| 124 | +
|
| 125 | +👉 Review the final output. Who does your Lead Detective identify as the thief? |
| 126 | + |
| 127 | +👉 Call for the instructor and share your suspect. |
| 128 | + |
| 129 | +### If Your Answer is Incorrect |
| 130 | + |
| 131 | +If the Lead Detective identifies the wrong suspect, refine the system prompts in `agentConfigs.ts`. |
| 132 | + |
| 133 | +**Which prompts to adjust:** |
| 134 | + |
| 135 | +1. **Lead Detective's system prompt** (`agentConfigs.ts → leadDetective.systemPrompt`) |
| 136 | + - Make it more specific about what evidence to prioritize |
| 137 | + - Example: Add "Focus on alibis, financial motives, and access to the museum on the night of the theft" |
| 138 | + |
| 139 | +2. **Evidence Analyst's grounding query** (`investigationWorkflow.ts → evidenceAnalystNode`) |
| 140 | + - Make the search query more specific |
| 141 | + - Example: `"Find evidence about ${suspect}'s alibi, financial records, and museum access on the night of the theft"` |
| 142 | + |
| 143 | +3. **Intelligence Researcher's web queries** (`investigationWorkflow.ts → intelligenceResearcherNode`) |
| 144 | + - Add more specific search terms |
| 145 | + - Example: Include specific dates, locations, or patterns mentioned in internal evidence |
| 146 | + |
| 147 | +**Tips for improving prompts:** |
| 148 | + |
| 149 | +- ✅ Be specific about what to analyze (alibi, motive, opportunity) |
| 150 | +- ✅ Ask the detective to cite specific documents and web sources |
| 151 | +- ✅ Request cross-referencing of internal and external evidence |
| 152 | +- ✅ Instruct the detective to explain reasoning step-by-step |
| 153 | +- ✅ Ask the detective to assess whether it's an isolated incident or organized crime |
| 154 | +- ❌ Avoid vague instructions like "solve the crime" without guidance |
| 155 | +- ❌ Don't assume the LLM knows which evidence is most important |
| 156 | + |
| 157 | +--- |
| 158 | + |
| 159 | +## Understanding Multi-Agent Orchestration |
| 160 | + |
| 161 | +### What Just Happened? |
| 162 | + |
| 163 | +You completed a full multi-agent investigation system where: |
| 164 | + |
| 165 | +1. **Appraiser Node** — Calls SAP-RPT-1 to predict missing insurance values from structured data |
| 166 | +2. **Evidence Analyst Node** — Searches 8 evidence documents via the Grounding Service for each suspect |
| 167 | +3. **Intelligence Researcher Node** — Searches the web via Sonar-Pro for criminal patterns and public records |
| 168 | +4. **Lead Detective Node** — Synthesizes all findings using an LLM to identify the culprit and calculate total losses |
| 169 | +5. **State** — Flows through all nodes, accumulating results that later nodes build upon |
| 170 | + |
| 171 | +### The Complete Investigation Flow |
| 172 | + |
| 173 | +```mermaid |
| 174 | +flowchart TD |
| 175 | + A[START] --> B["Appraiser Node\nRPT-1 predictions → appraisal_result"] |
| 176 | + B --> C["Evidence Analyst Node\nGrounding searches × 3 suspects → evidence_analysis"] |
| 177 | + C --> D["Intelligence Researcher Node\nWeb searches × 3 suspects + patterns → intelligence_report"] |
| 178 | + D --> E["Lead Detective Node\nLLM synthesis → final_conclusion"] |
| 179 | + E --> F[END] |
| 180 | +``` |
| 181 | + |
| 182 | +### The Role of agentConfigs.ts |
| 183 | + |
| 184 | +The `AGENT_CONFIGS` object in `agentConfigs.ts` serves the same purpose as CrewAI's YAML files: it separates agent "personality" from orchestration logic. But as TypeScript objects: |
| 185 | + |
| 186 | +- System prompts are **functions** that accept runtime data and return a string |
| 187 | +- No YAML parsing, no indentation errors, no key synchronization issues |
| 188 | +- Your IDE can trace exactly where a system prompt is used and refactor it |
| 189 | + |
| 190 | +### Multi-Source Intelligence |
| 191 | + |
| 192 | +Your Lead Detective now analyzes **three independent sources**: |
| 193 | + |
| 194 | +1. **Structured Data** (`appraisal_result`) — Financial impact from RPT-1 |
| 195 | +2. **Internal Documents** (`evidence_analysis`) — Private evidence from Grounding Service |
| 196 | +3. **External Web** (`intelligence_report`) — Public intelligence from Sonar-Pro |
| 197 | + |
| 198 | +This multi-source approach mirrors real-world investigations: |
| 199 | +- **Internal evidence** proves what happened at the scene |
| 200 | +- **External intelligence** reveals patterns and backgrounds |
| 201 | +- **Financial data** establishes motive and impact |
| 202 | + |
| 203 | +### Why This Matters |
| 204 | + |
| 205 | +Multi-agent systems with multi-source intelligence are powerful because they: |
| 206 | + |
| 207 | +- **Distribute Responsibilities** across specialized agents with distinct roles |
| 208 | +- **Enable Collaboration** through task delegation and information sharing |
| 209 | +- **Combine Data Sources** by integrating internal and external intelligence |
| 210 | +- **Improve Reasoning** by providing multiple expert perspectives |
| 211 | +- **Handle Complexity** by breaking down large problems into manageable subtasks |
| 212 | +- **Scale Efficiently** as new agents and tools can be added without disrupting existing ones |
| 213 | + |
| 214 | +### Why This Architecture Matters |
| 215 | + |
| 216 | +**Benefits of multi-agent LangGraph systems:** |
| 217 | + |
| 218 | +- **Specialization** — Each node has exactly the tools and context it needs |
| 219 | +- **Different models per node** — You could use GPT-4o for the detective and a cheaper model for search |
| 220 | +- **Explicit data flow** — State fields make it clear what each node produces and consumes |
| 221 | +- **Debuggability** — Every state transition is observable; add `console.log` to any node |
| 222 | +- **Extensibility** — Adding a new agent is `.addNode()` + `.addEdge()` + a new node function |
| 223 | +- **Multi-Source Analysis** — Seamlessly combine internal documents, web search, and structured data |
| 224 | + |
| 225 | +**Real-world applications:** |
| 226 | + |
| 227 | +- **Customer service**: Routing agent → Internal KB search → Web search → Escalation agent |
| 228 | +- **Research**: Data collection agent → Internal archive search → Web research → Analysis agent → Report generation agent |
| 229 | +- **DevOps**: Monitoring agent → Internal logs → Public status pages → Diagnosis agent → Remediation agent |
| 230 | +- **Due Diligence**: Company info agent → Internal records → Public filings → News search → Risk assessment |
| 231 | + |
| 232 | +--- |
| 233 | + |
| 234 | +## Key Takeaways |
| 235 | + |
| 236 | +- **Multi-node LangGraph workflows** decompose complex problems into specialized, sequential steps |
| 237 | +- **Shared state** is how nodes communicate: earlier results flow to later nodes via state fields |
| 238 | +- **Multi-source intelligence** combines internal documents (Grounding) + external web (Sonar-Pro) + structured data (RPT-1) |
| 239 | +- **System prompts with runtime data** (`AGENT_CONFIGS.leadDetective.systemPrompt(...)`) enable context-aware synthesis |
| 240 | +- **Edges define execution order**: the Lead Detective waits for all predecessors to complete |
| 241 | +- **`state.intelligence_report || 'No intelligence report available'`**: always provide fallbacks when reading optional state fields |
| 242 | +- **SAP Cloud SDK for AI** provides a unified API for LLMs, web search, grounding, and structured data models |
| 243 | +- **Prompt engineering** is iterative: run, observe, refine until the detective identifies the right suspect |
| 244 | + |
| 245 | +--- |
| 246 | + |
| 247 | +## Congratulations! |
| 248 | + |
| 249 | +You've successfully built a sophisticated multi-agent AI investigation system in TypeScript that can: |
| 250 | + |
| 251 | +- **Predict financial values** using the SAP-RPT-1 structured data model |
| 252 | +- **Search internal evidence documents** using the SAP Grounding Service (RAG) |
| 253 | +- **Search the web for public intelligence** using Perplexity's Sonar-Pro model |
| 254 | +- **Synthesize multi-source findings** across multiple agents using LangGraph state |
| 255 | +- **Solve complex problems** through collaborative, code-based agent orchestration |
| 256 | + |
| 257 | +--- |
| 258 | + |
| 259 | +## Next Steps Checklist |
| 260 | + |
| 261 | +1. ✅ [Understand Generative AI Hub](00-understanding-genAI-hub.md) |
| 262 | +2. ✅ [Set up your development space](01-setup-dev-space.md) |
| 263 | +3. ✅ [Build a basic agent](02-build-a-basic-agent.md) |
| 264 | +4. ✅ [Add custom tools](03-add-your-first-tool.md) (RPT-1 model integration) |
| 265 | +5. ✅ [Build a multi-agent workflow](04-building-multi-agent-system.md) |
| 266 | +6. ✅ [Integrate the Grounding Service](05-add-the-grounding-service.md) |
| 267 | +7. ✅ [Add web search capabilities](06-discover-connected-crimes.md) |
| 268 | +8. ✅ [Solve the museum art theft mystery](07-solve-the-crime.md) (this exercise) |
| 269 | + |
| 270 | +--- |
| 271 | + |
| 272 | +## Troubleshooting |
| 273 | + |
| 274 | +**Issue**: Lead Detective's conclusion doesn't include intelligence report findings |
| 275 | + |
| 276 | +- **Solution**: Ensure the `leadDetective.systemPrompt` in `agentConfigs.ts` explicitly references the `intelligenceReport` parameter and asks the LLM to analyze web findings. The LLM only includes what you ask for. |
| 277 | + |
| 278 | +**Issue**: `state.intelligence_report` is `undefined` in the Lead Detective node |
| 279 | + |
| 280 | +- **Solution**: Check that the Intelligence Researcher node is returning the `intelligence_report` field in its return object. Add `console.log(state.intelligence_report)` at the start of `leadDetectiveNode` to debug. |
| 281 | + |
| 282 | +**Issue**: Web search returns no relevant information |
| 283 | + |
| 284 | +- **Solution**: Refine the search queries in `intelligenceResearcherNode`. Make them more specific by including suspect names, locations, and dates from internal evidence. |
| 285 | + |
| 286 | +**Issue**: Investigation runs but conclusion doesn't cross-reference internal and external evidence |
| 287 | + |
| 288 | +- **Solution**: Update the Lead Detective's system prompt to explicitly ask: "Cross-reference internal evidence with web findings to determine if this is an isolated incident or part of a pattern." |
| 289 | + |
| 290 | +**Issue**: Agent identifies the wrong suspect after multiple runs |
| 291 | + |
| 292 | +- **Solution**: LLMs are non-deterministic by default. Lower `temperature` in `model_params` (try `0.3`) for more consistent reasoning. Also refine the Lead Detective's system prompt to be more specific about how to weigh evidence from different sources. |
| 293 | + |
| 294 | +**Issue**: `Error during intelligence research: Error: 429 Too Many Requests` |
| 295 | + |
| 296 | +- **Solution**: You've hit the API rate limit. Wait a moment and retry. Web search is more resource-intensive than grounding, so consider reducing the number of searches or adding delays between them. |
| 297 | + |
| 298 | +**Issue**: TypeScript compilation errors after adding intelligence_report |
| 299 | + |
| 300 | +- **Solution**: Ensure you've updated the `AgentState` interface in `types.ts` to include the `intelligence_report?: string` field. |
| 301 | + |
| 302 | +--- |
| 303 | + |
| 304 | +## Resources |
| 305 | + |
| 306 | +- [LangGraph.js Documentation](https://langchain-ai.github.io/langgraphjs/) |
| 307 | +- [SAP Cloud SDK for AI (JavaScript)](https://sap.github.io/ai-sdk/docs/js/orchestration/chat-completion) |
| 308 | +- [SAP Generative AI Hub](https://help.sap.com/docs/sap-ai-core/sap-ai-core-service-guide/generative-ai-hub-in-sap-ai-core-7db524ee75e74bf8b50c167951fe34a5) |
| 309 | +- [SAP-RPT-1 Playground](https://rpt.cloud.sap/) |
| 310 | +- [SAP AI Core Grounding Management](https://help.sap.com/docs/sap-ai-core/sap-ai-core-service-guide/document-grounding) |
| 311 | +- [Perplexity API Documentation](https://docs.perplexity.ai/) |
0 commit comments