diff --git a/.gitignore b/.gitignore index b7faf40..2ff72e9 100644 --- a/.gitignore +++ b/.gitignore @@ -205,3 +205,12 @@ cython_debug/ marimo/_static/ marimo/_lsp/ __marimo__/ + + +#Claude +.claude/ + +# Test folder - excluded from version control +test/ +test_*.py +test_*.ts \ No newline at end of file diff --git a/README.md b/README.md index 208c22f..516506b 100644 --- a/README.md +++ b/README.md @@ -47,7 +47,8 @@ If you are unsure on which path you should choose, ask the instructor for guidan - [Exercise 03 - Build your first agent tool](./exercises/Python/03-add-your-first-tool.md) - [Exercise 04 - Building a multi-agent system](./exercises/Python/04-building-multi-agent-system.md) - [Exercise 05 - Add the Grounding service](./exercises/Python/05-add-the-grounding-service.md) -- [Exercise 06 - Use your AI Agents to solve the crime](./exercises/Python/06-solve-the-crime.md) +- [Exercise 06 - Discover Connected Crimes with Web Search](./exercises/Python/06-discover-connected-crimes.md) +- [Exercise 07 - Use your AI Agents to solve the crime](./exercises/Python/07-solve-the-crime.md) The instructor will start you on the first exercise. Proceed to the next exercise once the instructor tells you to. diff --git a/exercises/JavaScript/06-discover-connected-crimes.md b/exercises/JavaScript/06-discover-connected-crimes.md new file mode 100644 index 0000000..51d63e9 --- /dev/null +++ b/exercises/JavaScript/06-discover-connected-crimes.md @@ -0,0 +1,563 @@ +# Discover Connected Crimes with Web Search (JavaScript/TypeScript) + +## Overview + +In Exercise 05, you learned to search internal documents using the Grounding Service. Your Evidence Analyst can now retrieve factual evidence from security logs, bank records, and termination letters without hallucination. + +But investigations don't happen in a vacuum. What if similar crimes happened elsewhere? What if your suspects have public criminal records? What if this heist is part of a larger criminal network? + +In this exercise, you'll add **web search capabilities** using Perplexity's **sonar-pro model** to gather external intelligence and discover patterns across the internet. + +--- + +## Understand Web Search with Sonar-Pro + +### Why Do We Need Web Search? + +Your current investigation system is powerful but limited to **internal data**: + +| **What You Can Do Now** | **What You Can't Do Yet** | +| ----------------------------------------------- | ---------------------------------------------------- | +| ✅ Search museum's internal evidence documents | ❌ Search public criminal databases | +| ✅ Retrieve bank records, security logs | ❌ Find similar crimes in other cities | +| ✅ Analyze suspects based on internal evidence | ❌ Check if suspects have public criminal records | +| ✅ Ground responses in factual documents | ❌ Discover criminal network connections | +| ✅ Avoid hallucination for internal data | ❌ Monitor online art markets for stolen items | + +**The Problem**: Real investigations combine **internal evidence** (what happened here) with **external intelligence** (what's happening elsewhere). Your agents need both! + +### Document Grounding vs. Web Search + +You now have access to **two complementary search capabilities**: + +| Aspect | Document Grounding (Exercise 05) | Web Search (This Exercise) | +| ---------------------- | ---------------------------------------- | ----------------------------------------- | +| **Data Source** | Internal documents (pre-uploaded) | Real-time web information | +| **Coverage** | Organization-specific evidence | Global public information | +| **Freshness** | Historical documents (static) | Current information (updated constantly) | +| **Use Cases** | Internal logs, private records, policies | News, criminal records, pattern analysis | +| **Search Method** | Vector similarity (semantic) | Web search + LLM synthesis | +| **Source Control** | You control what documents exist | Public internet (no control) | +| **Tool Function** | `callGroundingServiceTool()` | `callSonarProSearchTool()` | +| **Privacy** | Private, secure | Public information only | +| **When to Use** | "What does our evidence say?" | "What do public records show?" | + +> 🎯 **Key Insight**: These are **not alternatives** — they work **together**! The best investigations use both internal evidence AND external intelligence. + +### How Sonar-Pro Integrates with SAP Cloud SDK for AI + +Sonar-Pro is called through the **OrchestrationClient** using the SAP Cloud SDK for AI, just like GPT-4 or Claude: + +```typescript +import { OrchestrationClient } from '@sap-ai-sdk/orchestration' + +const webSearchClient = new OrchestrationClient( + { + llm: { + model_name: 'sonar-pro', // Perplexity's web search model + model_params: { + temperature: 0.2, // Lower for factual results + }, + }, + templating: { + template: [ + { + role: 'system', + content: 'Search for factual information with citations', + }, + { role: 'user', content: '{{?search_query}}' }, + ], + }, + }, + { resourceGroup: process.env.RESOURCE_GROUP }, +) +``` + +**Key Differences**: +- `model_name: 'gpt-4o'` → Regular reasoning LLM +- `model_name: 'sonar-pro'` → Web search-enabled LLM +- Both use the same `OrchestrationClient` API! + +--- + +## Add The Web Search Tool + +### Step 1: Create the Sonar-Pro Search Tool + +You'll create a tool that enables your agents to search the web for criminal patterns, suspect backgrounds, and related incidents. + +👉 Open [`/project/JavaScript/solution/src/tools.ts`](/project/JavaScript/solution/src/tools.ts) + +👉 Add this tool **after** the `callGroundingServiceTool` function (around line 68): + +```typescript +/** + * Web Search Tool - Uses Perplexity's sonar-pro model for real-time web search + */ +const webSearchClient = new OrchestrationClient( + { + llm: { + model_name: 'sonar-pro', + model_params: { + temperature: 0.2, // Lower temperature for factual search + }, + }, + templating: { + template: [ + { + role: 'system', + content: + 'You are a web search assistant specializing in criminal intelligence. Search for accurate, recent information and always provide source citations with URLs and dates.', + }, + { role: 'user', content: '{{?search_query}}' }, + ], + }, + }, + { resourceGroup: process.env.RESOURCE_GROUP }, +) + +export async function callSonarProSearchTool(search_query: string): Promise { + try { + const response = await webSearchClient.chatCompletion({ + inputParams: { search_query }, + }) + return response.getContent() ?? 'No search results found' + } catch (error) { + const errorMessage = error instanceof Error ? error.message : String(error) + console.error('❌ Sonar-pro web search failed:', errorMessage) + if (error instanceof Error && error.stack) console.error(error.stack) + return `Error calling sonar-pro web search: ${errorMessage}` + } +} +``` + +> 💡 **Understanding the Web Search Tool:** +> +> **1. OrchestrationClient Configuration** +> - `model_name: 'sonar-pro'` - Uses Perplexity's web search-enabled model +> - `temperature: 0.2` - Lower temperature for factual, consistent results +> - Templating system defines the search context and user query format +> +> **2. Search Configuration** +> - System prompt guides the search focus (criminal intelligence) +> - `{{?search_query}}` template parameter receives the search query +> - User query contains specific search (e.g., "Marcus Chen criminal history") +> +> **3. Response Format** +> - Sonar-pro returns synthesized answers with web sources +> - Includes URLs, publication dates, and source reliability +> - Agent receives structured information to inform investigation +> +> **4. Error Handling** +> - Returns error as string (agent can handle gracefully) +> - Logs detailed error information for debugging +> - Agent workflow continues even if search fails + +--- + +## Add The Intelligence Researcher Agent + +### Step 1: Add Agent Configuration + +👉 Open [`/project/JavaScript/solution/src/agentConfigs.ts`](/project/JavaScript/solution/src/agentConfigs.ts) + +👉 Add this configuration **after** the `evidenceAnalyst` section and **before** the `leadDetective` section: + +```typescript + intelligenceResearcher: { + systemPrompt: (suspectNames: string) => `You are an Open-Source Intelligence (OSINT) Researcher. + You are an OSINT specialist who excels at finding patterns across multiple crime scenes. + You search public databases, news archives, and criminal records to connect seemingly isolated incidents. + Your expertise has uncovered several international art theft rings, and you know how to distinguish professional criminals from amateurs. + + Your goal: Search the web for similar art thefts, criminal patterns, and suspect backgrounds to determine if this heist is part of a larger criminal network + + You have access to the call_sonar_pro_search tool to find recent incidents, news reports, and public criminal records. + Analyze the suspects: ${suspectNames} + + Search for: + 1. Public criminal records or prior convictions for each suspect + 2. Similar art theft incidents with the same modus operandi (insider job, no forced entry) + 3. Connections to known art theft rings or criminal networks + 4. News reports or public information about any of the suspects + 5. Recent museum heists in Europe with similar patterns`, + }, +``` + +> 💡 **Why This Agent Design?** +> +> - **Role**: OSINT Researcher - Establishes expertise in public information gathering +> - **Goal**: Specific search objectives (patterns, backgrounds, networks) with explicit tool mention +> - **Backstory**: Provides context and authority for web-based investigations +> - **System Prompt**: Includes detailed search criteria to guide the agent's web research + +### Step 2: Update Lead Detective Configuration + +👉 In the same file, update the `leadDetective` system prompt to include the intelligence report parameter: + +```typescript + leadDetective: { + systemPrompt: ( + appraisalResult: string, + evidenceAnalysis: string, + intelligenceReport: string, // NEW parameter + suspectNames: string, + ) => + `You are the lead detective on this high-profile art theft case. With years of + experience solving complex crimes, you excel at synthesizing information from + multiple sources and identifying the culprit based on evidence and expert analysis. + + Your goal: Synthesize all findings from the team to identify the most likely suspect and build a comprehensive case + + You have received the following information from your team: + + 1. INSURANCE APPRAISAL: ${appraisalResult} + 2. EVIDENCE ANALYSIS (Internal Documents): ${evidenceAnalysis} + 3. INTELLIGENCE REPORT (Web Search): ${intelligenceReport} // NEW + 4. SUSPECTS: ${suspectNames} + + Based on all the evidence and analysis, determine: + - Who is the most likely culprit? + - What evidence supports this conclusion? + - What was their motive and opportunity? + - Is this an isolated incident or part of a larger criminal network? // NEW + - Summarise the insurance appraisal values of the stolen artworks. + - Calculate the total estimated insurance value of the stolen items based on the appraisal results. + - Provide a comprehensive summary of the case. + + Be thorough and analytical in your conclusion.`, + }, +``` + +### Step 3: Update Type Definitions + +👉 Open [`/project/JavaScript/solution/src/types.ts`](/project/JavaScript/solution/src/types.ts) + +👉 Add the `intelligence_report` field to the `AgentState` interface: + +```typescript +export interface AgentState { + payload: RPT1Payload + suspect_names: string + appraisal_result?: string + evidence_analysis?: string + intelligence_report?: string // NEW + final_conclusion?: string + messages: Array<{ + role: string + content: string + }> +} +``` + +### Step 4: Add Intelligence Researcher Node to Workflow + +👉 Open [`/project/JavaScript/solution/src/investigationWorkflow.ts`](/project/JavaScript/solution/src/investigationWorkflow.ts) + +👉 **First**, update the imports at the top to include the new tool: + +```typescript +import { callRPT1Tool, callGroundingServiceTool, callSonarProSearchTool } from './tools.js' +``` + +👉 **Second**, add the Intelligence Researcher node **after** the `evidenceAnalystNode()` method and **before** the `leadDetectiveNode()` method: + +```typescript + /** + * Intelligence Researcher Agent Node - Uses web search to find criminal patterns + */ + private async intelligenceResearcherNode(state: AgentState): Promise> { + console.log('\n🔍 Intelligence Researcher starting web search...') + + try { + const suspects = state.suspect_names.split(',').map(s => s.trim()) + const intelligenceResults: string[] = [] + + // Search for criminal records for each suspect + for (const suspect of suspects) { + console.log(` Searching public records for: ${suspect}`) + const query = `${suspect} criminal record art theft security technician Europe background check` + const result = await callSonarProSearchTool(query) + intelligenceResults.push(`Background check for ${suspect}:\n${result}`) + } + + // Search for similar art theft patterns + console.log(' Searching for similar art theft incidents...') + const patternQuery = + 'museum art theft insider job no forced entry Europe similar incidents criminal network' + const patternResult = await callSonarProSearchTool(patternQuery) + intelligenceResults.push(`Similar Art Theft Patterns:\n${patternResult}`) + + const intelligenceReport = `Intelligence Research Complete: ${intelligenceResults.join('\n\n')} + Summary: Conducted OSINT research on all suspects and identified similar crime patterns` + + console.log('✅ Intelligence research complete') + + return { + intelligence_report: intelligenceReport, + messages: [...state.messages, { role: 'assistant', content: intelligenceReport }], + } + } catch (error) { + const errorMsg = error instanceof Error ? error.message : String(error) + console.error('❌ Intelligence research failed:', errorMsg) + if (error instanceof Error && error.stack) { + console.error(error.stack) + } + return { + intelligence_report: `Error during intelligence research: ${errorMsg}`, + messages: [ + ...state.messages, + { role: 'assistant', content: `Error during intelligence research: ${errorMsg}` }, + ], + } + } + } +``` + +👉 **Third**, update the `leadDetectiveNode()` method to pass the intelligence report: + +```typescript + private async leadDetectiveNode(state: AgentState): Promise> { + console.log('\n🔍 Lead Detective analyzing all findings...') + + const userMessage = 'Analyze all the evidence and identify the culprit. Provide a detailed conclusion.' + + try { + const response = await this.orchestrationClient.chatCompletion({ + messages: [ + { + role: 'system', + content: AGENT_CONFIGS.leadDetective.systemPrompt( + state.appraisal_result || 'No appraisal result available', + state.evidence_analysis || 'No evidence analysis available', + state.intelligence_report || 'No intelligence report available', // NEW + state.suspect_names, + ), + }, + { role: 'user', content: userMessage }, + ], + }) + const conclusion = response.getContent() || 'No conclusion could be drawn.' + + console.log('✅ Investigation complete') + + return { + final_conclusion: conclusion, + messages: [...state.messages, { role: 'assistant', content: conclusion }], + } + } catch (error) { + const errorMsg = `Error during final analysis: ${error}` + console.error('❌', errorMsg) + return { + final_conclusion: errorMsg, + messages: [...state.messages, { role: 'assistant', content: errorMsg }], + } + } + } +``` + +👉 **Fourth**, update the `buildGraph()` method to include the intelligence researcher node: + +```typescript + private buildGraph(): StateGraph { + const workflow = new StateGraph({ + channels: { + payload: null, + suspect_names: null, + appraisal_result: null, + evidence_analysis: null, + intelligence_report: null, // NEW + final_conclusion: null, + messages: null, + }, + }) + + // Add nodes and edges using chained API (required in LangGraph 0.2+) + workflow + .addNode('appraiser', this.appraiserNode.bind(this)) + .addNode('evidence_analyst', this.evidenceAnalystNode.bind(this)) + .addNode('intelligence_researcher', this.intelligenceResearcherNode.bind(this)) // NEW + .addNode('lead_detective', this.leadDetectiveNode.bind(this)) + .addEdge(START, 'appraiser') + .addEdge('appraiser', 'evidence_analyst') + .addEdge('evidence_analyst', 'intelligence_researcher') // NEW + .addEdge('intelligence_researcher', 'lead_detective') // NEW + .addEdge('lead_detective', END) + + return workflow + } +``` + +--- + +## Build and Run Your Enhanced Investigation + +### Step 1: Build the TypeScript Project + +👉 Build the project to compile your TypeScript changes: + +```bash +# From repository root +npm run build --prefix ./project/JavaScript/solution +``` + +```bash +# From solution folder +npm run build +``` + +### Step 2: Run the Investigation + +👉 Run your enhanced multi-agent system with web search: + +```bash +# From repository root +npm start --prefix ./project/JavaScript/solution +``` + +```bash +# From solution folder +npm start +``` + +> ⏱️ **This may take 4-7 minutes** as your agents: +> +> 1. Predict stolen item values (Appraiser with RPT-1) +> 2. Search internal evidence documents (Evidence Analyst with Grounding) +> 3. Search the web for criminal patterns (Intelligence Researcher with Sonar-Pro) ← NEW! +> 4. Synthesize all findings (Lead Detective) + +👉 Review the intelligence report from the web search: +- Did it find similar crimes? +- Do any suspects have public criminal records? +- Is there evidence of a criminal network? + +--- + +## Understanding Web Search Integration + +### What Just Happened? + +You created a complete multi-source intelligence gathering system that: + +1. **Searches Internal Documents** (Grounding Service) - Evidence from within the museum +2. **Searches External Web** (Sonar-Pro) - Public information from across the internet +3. **Combines Intelligence** - Both sources inform the investigation + +### The Enhanced Investigation Flow + +```mermaid +flowchart TD + A[Appraiser Agent] --> B[Predict Values
RPT-1 Tool] + B --> C[Evidence Analyst] + C --> D[Search Internal Docs
Grounding Service] + D --> E[Intelligence Researcher] + E --> F[Search Web
Sonar-Pro] + F --> G[Lead Detective] + G --> H[Multi-Source Analysis
Internal + External] + H --> I[Case Solved] +``` + +### When to Use Each Search Type + +**Use Document Grounding** (`callGroundingServiceTool()`) when: +- ✅ Searching organization-specific documents +- ✅ Accessing private/confidential information +- ✅ Finding internal evidence (logs, records, policies) +- ✅ You control the document collection +- ✅ Need semantic search across your own data + +**Use Web Search** (`callSonarProSearchTool()`) when: +- ✅ Searching public information +- ✅ Finding current events or recent news +- ✅ Checking criminal databases or public records +- ✅ Discovering patterns across multiple organizations +- ✅ Need real-time, up-to-date information + +**Use Both** when: +- ✅ You need comprehensive intelligence (internal + external) +- ✅ Cross-referencing private evidence with public records +- ✅ Verifying internal findings against external sources +- ✅ Building a complete picture from multiple angles + +--- + +## Key Takeaways + +- **Web Search** extends your investigation beyond internal documents to public intelligence +- **Sonar-Pro** provides real-time web search with source citations via OrchestrationClient +- **SAP Cloud SDK for AI** uses the same API pattern for both regular LLMs and web search models +- **Multi-Source Intelligence** combines internal evidence + external intelligence +- **Complementary Tools**: Document grounding and web search work together, not in competition +- **Complete Investigation**: Real detectives use both internal records AND external research +- **Pattern Discovery**: Web search reveals connections that internal documents can't show +- **TypeScript Integration**: Type-safe implementation with proper error handling + +--- + +## Next Steps + +You now have a complete intelligence gathering system with: +1. ✅ Structured data predictions (RPT-1) +2. ✅ Internal document search (Grounding Service) +3. ✅ External web intelligence (Sonar-Pro) +4. ✅ Multi-agent coordination (LangGraph) + +In the next exercise, you can refine your investigation or explore additional agent capabilities! + +--- + +## Troubleshooting + +**Issue**: `Module not found: @sap-ai-sdk/orchestration` + +- **Solution**: Ensure all dependencies are installed: + ```bash + npm install + ``` + +**Issue**: `Error: Model sonar-pro not found` + +- **Solution**: Verify that: + - Sonar-pro is available in your SAP AI Core Generative AI Hub model catalog + - Your resource group has access to Perplexity models + - Check SAP AI Launchpad → Generative AI Hub → Models for available models + +**Issue**: Web search returns no results or very generic information + +- **Solution**: Make your search queries more specific: + - ❌ Bad: "art theft" + - ✅ Good: "Marcus Chen security technician unauthorized access criminal record Europe" + - Include suspect names, locations, and specific details + +**Issue**: `TypeError: response.getContent is not a function` + +- **Solution**: Ensure you're using the latest version of `@sap-ai-sdk/orchestration`: + ```bash + npm update @sap-ai-sdk/orchestration + ``` + +**Issue**: TypeScript compilation errors + +- **Solution**: + - Run `npm run clean` to remove old build artifacts + - Run `npm run build` to rebuild + - Check that all type definitions are properly imported + +**Issue**: Web search takes too long or times out + +- **Solution**: + - Sonar-pro queries can take 10-30 seconds per search + - This is normal for real-time web crawling + - Consider adjusting timeout settings in OrchestrationClient if needed + +--- + +## Resources + +- [SAP Cloud SDK for AI Documentation](https://sap.github.io/ai-sdk/docs/js/orchestration/chat-completion) +- [Perplexity API Documentation](https://docs.perplexity.ai/) +- [SAP AI Core Orchestration Workflow](https://help.sap.com/docs/sap-ai-core/generative-ai/orchestration-workflow-v2) +- [LangGraph Documentation](https://langchain-ai.github.io/langgraphjs/) + +[Next exercise](07-solve-the-crime.md) diff --git a/exercises/JavaScript/07-solve-the-crime.md b/exercises/JavaScript/07-solve-the-crime.md new file mode 100644 index 0000000..f80d24e --- /dev/null +++ b/exercises/JavaScript/07-solve-the-crime.md @@ -0,0 +1,311 @@ +# Solve the Crime + +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. + +## Build the Lead Detective Node + +### What You Already Have + +After completing Exercise 06, your system already includes: +1. ✅ Appraiser Agent (RPT-1 predictions) +2. ✅ Evidence Analyst (Internal document search) +3. ✅ Intelligence Researcher (Web search) ← NEW in Exercise 06 +4. ✅ Lead Detective configuration in `agentConfigs.ts` ← Already updated in Exercise 06 +5. ✅ Lead Detective node in `investigationWorkflow.ts` ← Already updated in Exercise 06 + +### Verify Your Implementation + +👉 Open [`/project/JavaScript/solution/src/investigationWorkflow.ts`](/project/JavaScript/solution/src/investigationWorkflow.ts) + +👉 Verify that you have the complete workflow with all four agents: + +```typescript + private buildGraph(): StateGraph { + const workflow = new StateGraph({ + channels: { + payload: null, + suspect_names: null, + appraisal_result: null, + evidence_analysis: null, + intelligence_report: null, // Should be present from Exercise 06 + final_conclusion: null, + messages: null, + }, + }) + + workflow + .addNode('appraiser', this.appraiserNode.bind(this)) + .addNode('evidence_analyst', this.evidenceAnalystNode.bind(this)) + .addNode('intelligence_researcher', this.intelligenceResearcherNode.bind(this)) // Should be present + .addNode('lead_detective', this.leadDetectiveNode.bind(this)) + .addEdge(START, 'appraiser') + .addEdge('appraiser', 'evidence_analyst') + .addEdge('evidence_analyst', 'intelligence_researcher') // Should be present + .addEdge('intelligence_researcher', 'lead_detective') // Should be present + .addEdge('lead_detective', END) + + return workflow + } +``` + +> 💡 **The execution order is defined entirely by the edges:** +> +> 1. `START → appraiser` — workflow begins with the Appraiser +> 2. `appraiser → evidence_analyst` — after RPT-1 completes, Evidence Analyst runs +> 3. `evidence_analyst → intelligence_researcher` — after internal search completes, web search runs +> 4. `intelligence_researcher → lead_detective` — after web search completes, Lead Detective synthesizes +> 5. `lead_detective → END` — Lead Detective's conclusion becomes the final result + +### Verify main.ts + +👉 Check your [`/project/JavaScript/solution/src/main.ts`](/project/JavaScript/solution/src/main.ts): it needs no changes from Exercise 04. + +```typescript +import 'dotenv/config' +import { InvestigationWorkflow } from './investigationWorkflow.js' +import { payload } from './payload.js' + +async function main() { + console.log('═══════════════════════════════════════════════════════════') + console.log(' 🔍 ART THEFT INVESTIGATION - MULTI-AGENT SYSTEM') + console.log('═══════════════════════════════════════════════════════════\n') + + const workflow = new InvestigationWorkflow(process.env.MODEL_NAME!) + const suspectNames = 'Sophie Dubois, Marcus Chen, Viktor Petrov' + + console.log('📋 Case Details:') + console.log(` • Stolen Items: ${payload.rows.length} artworks`) + console.log(` • Suspects: ${suspectNames}`) + console.log(` • Investigation Team: 4 specialized agents\n`) // Updated to 4 agents + + const startTime = Date.now() + + const result = await workflow.kickoff({ + payload, + suspect_names: suspectNames, + }) + + const duration = ((Date.now() - startTime) / 1000).toFixed(2) + + console.log('\n═══════════════════════════════════════════════════════════') + console.log(' 📘 FINAL INVESTIGATION REPORT') + console.log('═══════════════════════════════════════════════════════════\n') + console.log(result) + console.log('\n═══════════════════════════════════════════════════════════') + console.log(` ⏱️ Investigation completed in ${duration} seconds`) + console.log('═══════════════════════════════════════════════════════════\n') +} + +main() +``` + +--- + +## Solve the Crime + +👉 Run your complete investigation workflow: + +```bash +# From repository root +npm start --prefix ./project/JavaScript/solution +``` + +```bash +# From solution folder +npm start +``` + +> ⏱️ **This may take 4-7 minutes** as your agents: +> +> 1. Predict insurance values for stolen items using SAP-RPT-1 +> 2. Search internal evidence documents for each suspect using the Grounding Service +> 3. Search the web for criminal patterns and suspect backgrounds using Sonar-Pro +> 4. Analyze all findings and identify the culprit + +👉 Review the final output. Who does your Lead Detective identify as the thief? + +👉 Call for the instructor and share your suspect. + +### If Your Answer is Incorrect + +If the Lead Detective identifies the wrong suspect, refine the system prompts in `agentConfigs.ts`. + +**Which prompts to adjust:** + +1. **Lead Detective's system prompt** (`agentConfigs.ts → leadDetective.systemPrompt`) + - Make it more specific about what evidence to prioritize + - Example: Add "Focus on alibis, financial motives, and access to the museum on the night of the theft" + +2. **Evidence Analyst's grounding query** (`investigationWorkflow.ts → evidenceAnalystNode`) + - Make the search query more specific + - Example: `"Find evidence about ${suspect}'s alibi, financial records, and museum access on the night of the theft"` + +3. **Intelligence Researcher's web queries** (`investigationWorkflow.ts → intelligenceResearcherNode`) + - Add more specific search terms + - Example: Include specific dates, locations, or patterns mentioned in internal evidence + +**Tips for improving prompts:** + +- ✅ Be specific about what to analyze (alibi, motive, opportunity) +- ✅ Ask the detective to cite specific documents and web sources +- ✅ Request cross-referencing of internal and external evidence +- ✅ Instruct the detective to explain reasoning step-by-step +- ✅ Ask the detective to assess whether it's an isolated incident or organized crime +- ❌ Avoid vague instructions like "solve the crime" without guidance +- ❌ Don't assume the LLM knows which evidence is most important + +--- + +## Understanding Multi-Agent Orchestration + +### What Just Happened? + +You completed a full multi-agent investigation system where: + +1. **Appraiser Node** — Calls SAP-RPT-1 to predict missing insurance values from structured data +2. **Evidence Analyst Node** — Searches 8 evidence documents via the Grounding Service for each suspect +3. **Intelligence Researcher Node** — Searches the web via Sonar-Pro for criminal patterns and public records +4. **Lead Detective Node** — Synthesizes all findings using an LLM to identify the culprit and calculate total losses +5. **State** — Flows through all nodes, accumulating results that later nodes build upon + +### The Complete Investigation Flow + +```mermaid +flowchart TD + A[START] --> B["Appraiser Node\nRPT-1 predictions → appraisal_result"] + B --> C["Evidence Analyst Node\nGrounding searches × 3 suspects → evidence_analysis"] + C --> D["Intelligence Researcher Node\nWeb searches × 3 suspects + patterns → intelligence_report"] + D --> E["Lead Detective Node\nLLM synthesis → final_conclusion"] + E --> F[END] +``` + +### The Role of agentConfigs.ts + +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: + +- System prompts are **functions** that accept runtime data and return a string +- No YAML parsing, no indentation errors, no key synchronization issues +- Your IDE can trace exactly where a system prompt is used and refactor it + +### Multi-Source Intelligence + +Your Lead Detective now analyzes **three independent sources**: + +1. **Structured Data** (`appraisal_result`) — Financial impact from RPT-1 +2. **Internal Documents** (`evidence_analysis`) — Private evidence from Grounding Service +3. **External Web** (`intelligence_report`) — Public intelligence from Sonar-Pro + +This multi-source approach mirrors real-world investigations: +- **Internal evidence** proves what happened at the scene +- **External intelligence** reveals patterns and backgrounds +- **Financial data** establishes motive and impact + +### Why This Matters + +Multi-agent systems with multi-source intelligence are powerful because they: + +- **Distribute Responsibilities** across specialized agents with distinct roles +- **Enable Collaboration** through task delegation and information sharing +- **Combine Data Sources** by integrating internal and external intelligence +- **Improve Reasoning** by providing multiple expert perspectives +- **Handle Complexity** by breaking down large problems into manageable subtasks +- **Scale Efficiently** as new agents and tools can be added without disrupting existing ones + +### Why This Architecture Matters + +**Benefits of multi-agent LangGraph systems:** + +- **Specialization** — Each node has exactly the tools and context it needs +- **Different models per node** — You could use GPT-4o for the detective and a cheaper model for search +- **Explicit data flow** — State fields make it clear what each node produces and consumes +- **Debuggability** — Every state transition is observable; add `console.log` to any node +- **Extensibility** — Adding a new agent is `.addNode()` + `.addEdge()` + a new node function +- **Multi-Source Analysis** — Seamlessly combine internal documents, web search, and structured data + +**Real-world applications:** + +- **Customer service**: Routing agent → Internal KB search → Web search → Escalation agent +- **Research**: Data collection agent → Internal archive search → Web research → Analysis agent → Report generation agent +- **DevOps**: Monitoring agent → Internal logs → Public status pages → Diagnosis agent → Remediation agent +- **Due Diligence**: Company info agent → Internal records → Public filings → News search → Risk assessment + +--- + +## Key Takeaways + +- **Multi-node LangGraph workflows** decompose complex problems into specialized, sequential steps +- **Shared state** is how nodes communicate: earlier results flow to later nodes via state fields +- **Multi-source intelligence** combines internal documents (Grounding) + external web (Sonar-Pro) + structured data (RPT-1) +- **System prompts with runtime data** (`AGENT_CONFIGS.leadDetective.systemPrompt(...)`) enable context-aware synthesis +- **Edges define execution order**: the Lead Detective waits for all predecessors to complete +- **`state.intelligence_report || 'No intelligence report available'`**: always provide fallbacks when reading optional state fields +- **SAP Cloud SDK for AI** provides a unified API for LLMs, web search, grounding, and structured data models +- **Prompt engineering** is iterative: run, observe, refine until the detective identifies the right suspect + +--- + +## Congratulations! + +You've successfully built a sophisticated multi-agent AI investigation system in TypeScript that can: + +- **Predict financial values** using the SAP-RPT-1 structured data model +- **Search internal evidence documents** using the SAP Grounding Service (RAG) +- **Search the web for public intelligence** using Perplexity's Sonar-Pro model +- **Synthesize multi-source findings** across multiple agents using LangGraph state +- **Solve complex problems** through collaborative, code-based agent orchestration + +--- + +## Next Steps Checklist + +1. ✅ [Understand Generative AI Hub](00-understanding-genAI-hub.md) +2. ✅ [Set up your development space](01-setup-dev-space.md) +3. ✅ [Build a basic agent](02-build-a-basic-agent.md) +4. ✅ [Add custom tools](03-add-your-first-tool.md) (RPT-1 model integration) +5. ✅ [Build a multi-agent workflow](04-building-multi-agent-system.md) +6. ✅ [Integrate the Grounding Service](05-add-the-grounding-service.md) +7. ✅ [Add web search capabilities](06-discover-connected-crimes.md) +8. ✅ [Solve the museum art theft mystery](07-solve-the-crime.md) (this exercise) + +--- + +## Troubleshooting + +**Issue**: Lead Detective's conclusion doesn't include intelligence report findings + +- **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. + +**Issue**: `state.intelligence_report` is `undefined` in the Lead Detective node + +- **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. + +**Issue**: Web search returns no relevant information + +- **Solution**: Refine the search queries in `intelligenceResearcherNode`. Make them more specific by including suspect names, locations, and dates from internal evidence. + +**Issue**: Investigation runs but conclusion doesn't cross-reference internal and external evidence + +- **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." + +**Issue**: Agent identifies the wrong suspect after multiple runs + +- **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. + +**Issue**: `Error during intelligence research: Error: 429 Too Many Requests` + +- **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. + +**Issue**: TypeScript compilation errors after adding intelligence_report + +- **Solution**: Ensure you've updated the `AgentState` interface in `types.ts` to include the `intelligence_report?: string` field. + +--- + +## Resources + +- [LangGraph.js Documentation](https://langchain-ai.github.io/langgraphjs/) +- [SAP Cloud SDK for AI (JavaScript)](https://sap.github.io/ai-sdk/docs/js/orchestration/chat-completion) +- [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) +- [SAP-RPT-1 Playground](https://rpt.cloud.sap/) +- [SAP AI Core Grounding Management](https://help.sap.com/docs/sap-ai-core/sap-ai-core-service-guide/document-grounding) +- [Perplexity API Documentation](https://docs.perplexity.ai/) diff --git a/exercises/Python/05-add-the-grounding-service.md b/exercises/Python/05-add-the-grounding-service.md index 55a61fe..2bd47ac 100644 --- a/exercises/Python/05-add-the-grounding-service.md +++ b/exercises/Python/05-add-the-grounding-service.md @@ -460,7 +460,8 @@ In the following exercises, you will: 2. ✅ Add custom tools to your agents so they can access external data 3. ✅ Create a complete crew with multiple agents working together 4. ✅ Integrate the Grounding Service for better reasoning and fact-checking (this exercise) -5. 📌 [Solve the museum art theft mystery](06-solve-the-crime.md) using your fully-featured agent team +5. 📌 [Add web search capabilities](06-discover-connected-crimes.md) to gather external intelligence +6. 📌 [Solve the museum art theft mystery](07-solve-the-crime.md) using your fully-featured agent team --- diff --git a/exercises/Python/06-discover-connected-crimes.md b/exercises/Python/06-discover-connected-crimes.md new file mode 100644 index 0000000..b55ddab --- /dev/null +++ b/exercises/Python/06-discover-connected-crimes.md @@ -0,0 +1,445 @@ +# Discover Connected Crimes with Web Search + +## Overview + +In Exercise 05, you learned to search internal documents using the Grounding Service. Your Evidence Analyst can now retrieve factual evidence from security logs, bank records, and termination letters without hallucination. + +But investigations don't happen in a vacuum. What if similar crimes happened elsewhere? What if your suspects have public criminal records? What if this heist is part of a larger criminal network? + +In this exercise, you'll add **web search capabilities** using Perplexity's **sonar-pro model** to gather external intelligence and discover patterns across the internet. + +--- + +## Understand Web Search with Sonar-Pro + +### Why Do We Need Web Search? + +Your current investigation system is powerful but limited to **internal data**: + +| **What You Can Do Now** | **What You Can't Do Yet** | +| ----------------------------------------------- | ---------------------------------------------------- | +| ✅ Search museum's internal evidence documents | ❌ Search public criminal databases | +| ✅ Retrieve bank records, security logs | ❌ Find similar crimes in other cities | +| ✅ Analyze suspects based on internal evidence | ❌ Check if suspects have public criminal records | +| ✅ Ground responses in factual documents | ❌ Discover criminal network connections | +| ✅ Avoid hallucination for internal data | ❌ Monitor online art markets for stolen items | + +**The Problem**: Real investigations combine **internal evidence** (what happened here) with **external intelligence** (what's happening elsewhere). Your agents need both! + +### Document Grounding vs. Web Search + +You now have access to **two complementary search capabilities**: + +| Aspect | Document Grounding (Exercise 05) | Web Search (This Exercise) | +| ---------------------- | ---------------------------------------- | ----------------------------------------- | +| **Data Source** | Internal documents (pre-uploaded) | Real-time web information | +| **Coverage** | Organization-specific evidence | Global public information | +| **Freshness** | Historical documents (static) | Current information (updated constantly) | +| **Use Cases** | Internal logs, private records, policies | News, criminal records, pattern analysis | +| **Search Method** | Vector similarity (semantic) | Web search + LLM synthesis | +| **Source Control** | You control what documents exist | Public internet (no control) | +| **Tool** | `call_grounding_service` | `call_sonar_pro_search` | +| **Privacy** | Private, secure | Public information only | +| **When to Use** | "What does our evidence say?" | "What do public records show?" | + +> 🎯 **Key Insight**: These are **not alternatives** — they work **together**! The best investigations use both internal evidence AND external intelligence. + +### What is Sonar-Pro? + +**Sonar-Pro** is Perplexity's AI model with built-in web search capabilities. Unlike regular LLMs: + +| Regular LLM (e.g., GPT-4, Claude) | Sonar-Pro (Perplexity) | +| --------------------------------------- | -------------------------------------------- | +| Knowledge cutoff (training data only) | Real-time web search | +| Can't access recent events | Finds current information | +| No source verification | Returns citations with URLs | +| "I think..." or "Based on my training" | "According to [source], dated [date]" | +| Static knowledge | Dynamic, up-to-date intelligence | + +**How Sonar-Pro Works**: +``` +Your Query → Sonar-Pro searches web → Retrieves relevant pages → +Synthesizes answer → Returns result with source citations +``` + +**Example**: +- **Query**: "Marcus Chen security technician criminal record" +- **Sonar-Pro**: Searches news articles, court records, public databases +- **Returns**: "According to [police-records.gov], Marcus Chen was charged with unauthorized access in 2022. Source: https://..." + +### How Sonar-Pro Integrates with SAP AI Core + +Sonar-Pro is called as a **model** through SAP's orchestration service, just like GPT-4 or Claude: + +```python +from litellm import completion + +response = completion( + model="sap/sonar-pro", # Perplexity via SAP orchestration + messages=[ + {"role": "system", "content": "Search for factual information"}, + {"role": "user", "content": "Your search query"} + ] +) +``` + +**Key Differences**: +- `sap/gpt-4o` → Regular reasoning LLM +- `sap/sonar-pro` → Web search-enabled LLM +- Both use the same `completion()` API! + +--- + +## Add The Web Search Tool + +### Step 1: Create the Sonar-Pro Search Tool + +You'll create a tool that enables your agents to search the web for criminal patterns, suspect backgrounds, and related incidents. + +👉 Open [`/project/Python/starter-project/investigator_crew.py`](/project/Python/starter-project/investigator_crew.py) + +👉 Add this tool **after** the `call_grounding_service` tool (around line 50): + +```python +@tool("call_sonar_pro_search") +def call_sonar_pro_search(search_query: str) -> str: + """Search the web using Perplexity's sonar-pro model for real-time information + about crimes, suspects, and criminal patterns. Use this to find similar incidents, + criminal networks, public records, or patterns that are not in internal documents. + + Args: + search_query: The search query about crimes, suspects, or criminal patterns + + Returns: + Search results with source citations from the web + """ + from litellm import completion + + try: + response = completion( + model="sap/sonar-pro", # Perplexity model with web search + messages=[ + { + "role": "system", + "content": "You are a web search assistant specializing in criminal intelligence. Search for accurate, recent information and always provide source citations with URLs and dates." + }, + { + "role": "user", + "content": search_query + } + ], + temperature=0.2, # Lower temperature for factual search + ) + + result = response.choices[0].message.content + return result + + except Exception as e: + return f"Error calling sonar-pro web search: {str(e)}" +``` + +> 💡 **Understanding the Web Search Tool:** +> +> **1. Model Selection** +> - `model="sap/sonar-pro"` - Uses Perplexity's web search-enabled model +> - Automatically searches the web and returns cited results +> - No additional configuration needed for basic web search +> +> **2. Search Configuration** +> - `temperature=0.2` - Lower temperature for factual, consistent results +> - System prompt guides the search focus (criminal intelligence) +> - User query contains the specific search (e.g., "Marcus Chen criminal history") +> +> **3. Response Format** +> - Sonar-pro returns synthesized answers with web sources +> - Includes URLs, publication dates, and source reliability +> - Agent receives structured information to inform investigation +> +> **4. Error Handling** +> - Returns error as string (agent can handle gracefully) +> - No exceptions raised (agent workflow continues) + +--- + +## Add The Intelligence Researcher Agent + +### Step 1: Add Agent Configuration + +👉 Open [`/project/Python/starter-project/config/agents.yaml`](/project/Python/starter-project/config/agents.yaml) + +👉 Add this configuration **after** the `evidence_analyst_agent` section: + +```yaml +intelligence_researcher_agent: + role: > + Open-Source Intelligence (OSINT) Researcher + goal: > + Search the web for similar art thefts, criminal patterns, and suspect backgrounds + to determine if this heist is part of a larger criminal network. Use the + call_sonar_pro_search tool to find recent incidents, news reports, and public + criminal records for all three suspects: {suspect_names}. + backstory: > + You are an OSINT specialist who excels at finding patterns across multiple + crime scenes. You search public databases, news archives, and criminal records + to connect seemingly isolated incidents. Your expertise has uncovered several + international art theft rings, and you know how to distinguish professional + criminals from amateurs. + llm: sap/gpt-4o +``` + +> 💡 **Why This Agent Design?** +> +> - **Role**: OSINT Researcher - Establishes expertise in public information gathering +> - **Goal**: Specific search objectives (patterns, backgrounds, networks) with explicit tool mention +> - **Backstory**: Provides context and authority for web-based investigations +> - **LLM**: Uses `sap/gpt-4o` for agent reasoning (the tool calls sonar-pro for searches) + +### Step 2: Add Task Configuration + +👉 Open [`/project/Python/starter-project/config/tasks.yaml`](/project/Python/starter-project/config/tasks.yaml) + +👉 Add this configuration **after** the `analyze_evidence_task` and **before** the `solve_crime` task: + +```yaml +research_criminal_network: + description: > + Search the web for intelligence about the three suspects ({suspect_names}) and + related crimes. Use the call_sonar_pro_search tool to find: + 1. Public criminal records or prior convictions for each suspect + 2. Similar art theft incidents with the same modus operandi (insider job, no forced entry) + 3. Connections to known art theft rings or criminal networks + 4. News reports or public information about any of the suspects + 5. Recent museum heists in Europe with similar patterns + + Cross-reference your web findings with the internal evidence analyzed by the + evidence analyst. Focus on discovering whether this is an isolated incident or + part of a larger criminal operation. + expected_output: > + A comprehensive intelligence report containing: + - Background checks for all three suspects with web sources + - List of similar art thefts found online (dates, locations, MO) + - Evidence of criminal network connections (if any) + - Assessment: isolated incident vs. organized crime ring + - All findings MUST include web sources with URLs and dates + agent: intelligence_researcher_agent +``` + +### Step 3: Add Agent and Task Methods to the Crew + +👉 Open [`/project/Python/starter-project/investigator_crew.py`](/project/Python/starter-project/investigator_crew.py) + +👉 Find the `InvestigatorCrew` class + +👉 Add these methods **after** the `analyze_evidence_task()` method and **BEFORE** the `lead_detective_agent()` method: + +```python + @agent + def intelligence_researcher_agent(self) -> Agent: + return Agent( + config=self.agents_config['intelligence_researcher_agent'], + verbose=True, + tools=[call_sonar_pro_search] # Web search tool + ) + + @task + def research_criminal_network(self) -> Task: + return Task( + config=self.tasks_config['research_criminal_network'], + context=[self.analyze_evidence_task()] # Uses internal evidence to inform web searches + ) +``` + +> 💡 **Method Positioning Matters!** +> +> Your class should now have this order: +> 1. `appraiser_agent()` method +> 2. `appraise_loss_task()` method +> 3. `evidence_analyst_agent()` method +> 4. `analyze_evidence_task()` method +> 5. **👈 `intelligence_researcher_agent()` method (NEW!)** +> 6. **👈 `research_criminal_network()` method (NEW!)** +> 7. `lead_detective_agent()` method (will be added in Exercise 07) +> 8. `solve_crime()` method (will be added in Exercise 07) +> 9. `crew()` method (stays at the end) + +> 💡 **Understanding Task Context**: +> - `context=[self.analyze_evidence_task()]` means the Intelligence Researcher receives the Evidence Analyst's findings +> - This allows the researcher to use internal evidence to formulate better web searches +> - Example: Evidence says "Marcus fired on 2024-01-15" → Researcher searches "Marcus Chen security technician fired 2024 criminal" + +--- + +## Run Your Enhanced Investigation + +👉 Run your crew to test the web search capability! + +**From repository root:** + +```bash +# macOS / Linux +python3 ./project/Python/starter-project/main.py +``` + +```powershell +# Windows (PowerShell) +python .\project\Python\starter-project\main.py +``` + +**From starter-project folder:** + +```bash +# macOS / Linux +python3 main.py +``` + +```powershell +# Windows (PowerShell) +python main.py +``` + +> ⏱️ **This may take 3-6 minutes** as your agents: +> +> 1. Predict stolen item values (Appraiser with RPT-1) +> 2. Search internal evidence documents (Evidence Analyst with Grounding) +> 3. Search the web for criminal patterns (Intelligence Researcher with Sonar-Pro) ← NEW! + +👉 Review the intelligence report from the web search: +- Did it find similar crimes? +- Do any suspects have public criminal records? +- Is there evidence of a criminal network? + +--- + +## Understanding Web Search Integration + +### What Just Happened? + +You created a complete multi-source intelligence gathering system that: + +1. **Searches Internal Documents** (Grounding Service) - Evidence from within the museum +2. **Searches External Web** (Sonar-Pro) - Public information from across the internet +3. **Combines Intelligence** - Both sources inform the investigation + +### The Enhanced Investigation Flow + +```mermaid +flowchart TD + A[Appraiser Agent] --> B[Predict Values
RPT-1 Tool] + B --> C[Evidence Analyst] + C --> D[Search Internal Docs
Grounding Service] + D --> E[Intelligence Researcher] + E --> F[Search Web
Sonar-Pro] + F --> G[Multi-Source Intelligence
Internal + External] + G --> H[Ready for Exercise 07:
Lead Detective Solves Crime] +``` + +### When to Use Each Search Type + +**Use Document Grounding** (`call_grounding_service`) when: +- ✅ Searching organization-specific documents +- ✅ Accessing private/confidential information +- ✅ Finding internal evidence (logs, records, policies) +- ✅ You control the document collection +- ✅ Need semantic search across your own data + +**Use Web Search** (`call_sonar_pro_search`) when: +- ✅ Searching public information +- ✅ Finding current events or recent news +- ✅ Checking criminal databases or public records +- ✅ Discovering patterns across multiple organizations +- ✅ Need real-time, up-to-date information + +**Use Both** when: +- ✅ You need comprehensive intelligence (internal + external) +- ✅ Cross-referencing private evidence with public records +- ✅ Verifying internal findings against external sources +- ✅ Building a complete picture from multiple angles + +### Example Investigation Workflow + +**Internal Evidence** (Grounding Service): +> "SECURITY_LOG.txt shows Marcus Chen accessed Gallery 2C at 23:47 on the night of the theft. MARCUS_TERMINATION_LETTER.txt indicates he was fired for 'unauthorized access to secured areas' on 2024-01-15." + +**External Intelligence** (Web Search): +> "Web search reveals Marcus Chen was previously investigated for a museum break-in in Berlin (2023). News reports from kunstdiebstahl-news.de show two similar heists with identical MO: former security staff, no forced entry, fine art targets." + +**Combined Analysis** (For Exercise 07): +> Internal evidence proves Marcus was at the scene + Web intelligence shows a pattern of similar crimes = Strong case that Marcus is a repeat offender and likely part of an organized theft ring. + +--- + +## Key Takeaways + +- **Web Search** extends your investigation beyond internal documents to public intelligence +- **Sonar-Pro** provides real-time web search with source citations +- **Multi-Source Intelligence** combines internal evidence + external intelligence +- **Complementary Tools**: Document grounding and web search work together, not in competition +- **Complete Investigation**: Real detectives use both internal records AND external research +- **Pattern Discovery**: Web search reveals connections that internal documents can't show + +--- + +## Next Steps + +You now have a complete intelligence gathering system with: +1. ✅ Structured data predictions (RPT-1) +2. ✅ Internal document search (Grounding Service) +3. ✅ External web intelligence (Sonar-Pro) + +In the next exercise, you'll add the **Lead Detective Agent** who will synthesize findings from all three sources to [solve the museum art theft mystery](07-solve-the-crime.md). + +--- + +## Troubleshooting + +**Issue**: `ModuleNotFoundError: No module named 'litellm'` + +- **Solution**: LiteLLM should already be installed from Exercise 02. If not, run: + ```bash + pip install litellm==1.82.6 + ``` + +**Issue**: `Error: Model sap/sonar-pro not found` + +- **Solution**: Verify that: + - Sonar-pro is available in your SAP AI Core Generative AI Hub model catalog + - Your resource group has access to Perplexity models + - Check SAP AI Launchpad → Generative AI Hub → Models for available models + +**Issue**: Web search returns no results or very generic information + +- **Solution**: Make your search queries more specific: + - ❌ Bad: "art theft" + - ✅ Good: "Marcus Chen security technician unauthorized access criminal record Europe" + - Include suspect names, locations, and specific details + +**Issue**: `AttributeError: 'NoneType' object has no attribute 'content'` + +- **Solution**: The sonar-pro API response structure might differ. Update error handling: + ```python + result = response.choices[0].message.content if response.choices else "No results" + ``` + +**Issue**: Agent doesn't use the web search tool + +- **Solution**: Ensure: + - The tool is assigned: `tools=[call_sonar_pro_search]` + - The task description explicitly mentions using `call_sonar_pro_search` + - The agent's goal references web search or OSINT + +**Issue**: Web search takes too long or times out + +- **Solution**: + - Sonar-pro queries can take 10-30 seconds per search + - This is normal for real-time web crawling + - If timeout occurs, increase LiteLLM timeout or retry + +--- + +## Resources + +- [Perplexity API Documentation](https://docs.perplexity.ai/) +- [SAP AI Core Orchestration Workflow](https://help.sap.com/docs/sap-ai-core/generative-ai/orchestration-workflow-v2) +- [LiteLLM Documentation](https://docs.litellm.ai/) + +[Next exercise](07-solve-the-crime.md) diff --git a/exercises/Python/06-solve-the-crime.md b/exercises/Python/07-solve-the-crime.md similarity index 77% rename from exercises/Python/06-solve-the-crime.md rename to exercises/Python/07-solve-the-crime.md index 7b3d73f..1c3c841 100644 --- a/exercises/Python/06-solve-the-crime.md +++ b/exercises/Python/07-solve-the-crime.md @@ -1,6 +1,13 @@ # Use your AI Agents to solve the crime -The only thing missing now is your **Lead Detective Agent**. This agent will then use the information retrieved from the other two agents to solve the crime and determine the value of the stolen items. +The only thing missing now is your **Lead Detective Agent**. This agent will synthesize information from all three specialized agents to solve the crime and determine the value of the stolen items. + +You now have complete intelligence gathering capabilities: +- 📊 **Financial predictions** from the Appraiser (RPT-1) +- 📄 **Internal evidence** from the Evidence Analyst (Grounding Service) +- 🌐 **External intelligence** from the Intelligence Researcher (Web Search) + +The Lead Detective will combine all three sources for a comprehensive conclusion. ## Build Your Lead Detective Agent @@ -30,8 +37,13 @@ lead_detective_agent: ```yaml solve_crime: description: > - Find the thief among the suspects by activating the evidence analyst agent and instructing them to look for information on each of the three suspects - using the grounding tool. They should find information on alibis and motives and return a report for you to analyze. + Find the thief among the suspects by reviewing: + 1. The insurance appraisal values from the appraiser agent + 2. The internal evidence analysis from the evidence analyst agent + 3. The web intelligence report from the intelligence researcher agent + + Synthesize all three sources to identify the culprit with high confidence. + Consider both internal evidence and external patterns/connections found online. expected_output: > The name of the thief and the total value of the stolen goods for the insurance. agent: lead_detective_agent @@ -43,7 +55,7 @@ Now you'll add the Lead Detective Agent and its task to your investigator crew. 👉 Open [`/project/Python/starter-project/investigator_crew.py`](/project/Python/starter-project/investigator_crew.py) -👉 **Inside the `InvestigatorCrew` class**, add the new agent and task methods **after** the existing `analyze_evidence_task` method and **before** the `@crew` method: +👉 **Inside the `InvestigatorCrew` class**, add the new agent and task methods **after** the existing `research_criminal_network` method and **before** the `@crew` method: ```python @agent @@ -57,25 +69,27 @@ Now you'll add the Lead Detective Agent and its task to your investigator crew. def solve_crime(self) -> Task: return Task( config=self.tasks_config['solve_crime'], - context=[self.appraise_loss_task(), self.analyze_evidence_task()] # 👈 Lead detective uses results from other tasks + context=[self.appraise_loss_task(), self.analyze_evidence_task(), self.research_criminal_network()] # Lead detective uses all three sources ) ``` -> 💡 **Where to place this code**: Add these methods inside the `InvestigatorCrew` class, after your `analyze_evidence_task()` method. The final order should be: +> 💡 **Where to place this code**: Add these methods inside the `InvestigatorCrew` class, after your `research_criminal_network()` method. The final order should be: > > 1. `appraiser_agent()` method > 2. `appraise_loss_task()` method > 3. `evidence_analyst_agent()` method > 4. `analyze_evidence_task()` method -> 5. **👈 Add `lead_detective_agent()` here** -> 6. **👈 Add `solve_crime()` here** -> 7. `crew()` method (keep at the end) +> 5. `intelligence_researcher_agent()` method (from Exercise 06) +> 6. `research_criminal_network()` method (from Exercise 06) +> 7. **👈 Add `lead_detective_agent()` here** +> 8. **👈 Add `solve_crime()` here** +> 9. `crew()` method (keep at the end) > 💡 **Understanding the `context` parameter:** > -> - `context=[self.appraise_loss_task(), self.analyze_evidence_task()]` tells CrewAI that the `solve_crime` task depends on the other two tasks -> - The Lead Detective will receive the output from both the Loss Appraiser and Evidence Analyst -> - This enables the detective to combine financial predictions with evidence analysis to solve the crime +> - `context=[self.appraise_loss_task(), self.analyze_evidence_task(), self.research_criminal_network()]` tells CrewAI that the `solve_crime` task depends on all three prior tasks +> - The Lead Detective receives output from the Appraiser, Evidence Analyst, AND Intelligence Researcher +> - This enables comprehensive analysis using financial data, internal evidence, and web intelligence ### Step 4: Verify Crew Configuration @@ -87,9 +101,9 @@ Your crew configuration should already be set from Exercise 04, but let's verify @crew def crew(self) -> Crew: return Crew( - agents=self.agents, # Automatically collected by @agent decorator (all 3 agents) - tasks=self.tasks, # Automatically collected by @task decorator (all 3 tasks) - process=Process.sequential, # Tasks run in order: appraise → analyze → solve + agents=self.agents, # Automatically collected by @agent decorator (all 4 agents) + tasks=self.tasks, # Automatically collected by @task decorator (all 4 tasks) + process=Process.sequential, # Tasks run in order: appraise → analyze → research → solve verbose=True # Print detailed execution logs ) ``` @@ -98,9 +112,9 @@ Your crew configuration should already be set from Exercise 04, but let's verify ### Step 5: Verify main.py (No Changes Needed) -Your `main.py` from Exercise 04 should already be correct. It doesn't need any changes for Exercise 06! +Your `main.py` from Exercise 04 should already be correct. It doesn't need any changes for Exercise 07! -> 💡 **What's happening:** The same `main.py` that ran 2 agents in Exercise 04 will now automatically run all 3 agents (including your new Lead Detective). CrewAI collects all `@agent` and `@task` decorated methods automatically. +> 💡 **What's happening:** The same `main.py` that ran 2 agents in Exercise 04 will now automatically run all 4 agents (Appraiser, Evidence Analyst, Intelligence Researcher, and Lead Detective). CrewAI collects all `@agent` and `@task` decorated methods automatically. 👉 (Optional) Double-check your [`/project/Python/starter-project/main.py`](/project/Python/starter-project/main.py) has both required inputs: @@ -153,11 +167,12 @@ python main.py python main.py ``` -> ⏱️ **This may take 2-5 minutes** as your agents: +> ⏱️ **This may take 3-6 minutes** as your agents: > -> 1. Search evidence documents for each suspect -> 2. Predict values of stolen items using RPT-1 -> 3. Analyze findings and identify the culprit +> 1. Search evidence documents for each suspect (Evidence Analyst) +> 2. Predict values of stolen items using RPT-1 (Appraiser) +> 3. Search the web for criminal patterns (Intelligence Researcher) +> 4. Synthesize all findings and identify the culprit (Lead Detective) 👉 Review the final output—who does your Lead Detective identify as the thief? @@ -236,11 +251,12 @@ python main.py You created a complete multi-agent system where: -1. **The Lead Detective Agent** orchestrates the investigation by delegating tasks -2. **The Evidence Analyst Agent** retrieves and analyzes evidence from documents -3. **The Loss Appraiser Agent** predicts financial values of stolen items -4. **Agent Communication** flows through task delegation and result aggregation -5. **Reasoning Integration** combines evidence, alibis, motives, and values to solve the crime +1. **The Lead Detective Agent** orchestrates the investigation by synthesizing multiple sources +2. **The Evidence Analyst Agent** retrieves and analyzes evidence from internal documents +3. **The Intelligence Researcher Agent** gathers external intelligence via web search +4. **The Loss Appraiser Agent** predicts financial values of stolen items +5. **Agent Communication** flows through task delegation and result aggregation +6. **Multi-Source Reasoning** combines internal evidence, web intelligence, and financial data to solve the crime ### The Investigation Flow @@ -252,10 +268,14 @@ flowchart TD A --> E[Loss Appraisal] E --> F[RPT-1 Predictions] F --> G[Value Determination] - D --> H[Crime Resolution] - G --> H - H --> I[Suspect Identification] - I --> J[Final Report] + A --> H[Web Intelligence] + H --> I[Sonar-Pro Search] + I --> J[Pattern Analysis] + D --> K[Crime Resolution] + G --> K + J --> K + K --> L[Suspect Identification] + L --> M[Final Report] ``` ### Why This Matters @@ -290,14 +310,16 @@ In the following exercises, you will: 2. ✅ Add custom tools to your agents so they can access external data 3. ✅ Create a complete crew with multiple agents working together 4. ✅ Integrate the Grounding Service for better reasoning and fact-checking -5. ✅ Solve the museum art theft mystery using your fully-featured agent team (this exercise) +5. ✅ Add web search for external intelligence gathering +6. ✅ Solve the museum art theft mystery using your fully-featured agent team (this exercise) Congratulations on completing the CodeJam! You've successfully built a sophisticated multi-agent AI system that can: -- Analyze evidence from documents +- Analyze evidence from internal documents +- Search the web for external intelligence - Predict financial values using the SAP-RPT-1 model - Coordinate between multiple specialized agents -- Solve complex real-world problems through collaborative reasoning +- Solve complex real-world problems through collaborative reasoning with multi-source intelligence --- diff --git a/project/JavaScript/solution/src/agentConfigs.ts b/project/JavaScript/solution/src/agentConfigs.ts index 3919ab2..bd9854e 100644 --- a/project/JavaScript/solution/src/agentConfigs.ts +++ b/project/JavaScript/solution/src/agentConfigs.ts @@ -11,24 +11,49 @@ export const AGENT_CONFIGS = { Search for evidence related to each suspect and identify connections to the crime.`, }, + intelligenceResearcher: { + systemPrompt: (suspectNames: string) => `You are an Open-Source Intelligence (OSINT) Researcher. + You are an OSINT specialist who excels at finding patterns across multiple crime scenes. + You search public databases, news archives, and criminal records to connect seemingly isolated incidents. + Your expertise has uncovered several international art theft rings, and you know how to distinguish professional criminals from amateurs. + + Your goal: Search the web for similar art thefts, criminal patterns, and suspect backgrounds to determine if this heist is part of a larger criminal network + + You have access to the call_sonar_pro_search tool to find recent incidents, news reports, and public criminal records. + Analyze the suspects: ${suspectNames} + + Search for: + 1. Public criminal records or prior convictions for each suspect + 2. Similar art theft incidents with the same modus operandi (insider job, no forced entry) + 3. Connections to known art theft rings or criminal networks + 4. News reports or public information about any of the suspects + 5. Recent museum heists in Europe with similar patterns`, + }, leadDetective: { - systemPrompt: (appraisalResult: string, evidenceAnalysis: string, suspectNames: string) => + systemPrompt: ( + appraisalResult: string, + evidenceAnalysis: string, + intelligenceReport: string, + suspectNames: string, + ) => `You are the lead detective on this high-profile art theft case. With years of experience solving complex crimes, you excel at synthesizing information from multiple sources and identifying the culprit based on evidence and expert analysis. - + Your goal: Synthesize all findings from the team to identify the most likely suspect and build a comprehensive case - + You have received the following information from your team: 1. INSURANCE APPRAISAL: ${appraisalResult} - 2. EVIDENCE ANALYSIS: ${evidenceAnalysis} - 3. SUSPECTS: ${suspectNames} + 2. EVIDENCE ANALYSIS (Internal Documents): ${evidenceAnalysis} + 3. INTELLIGENCE REPORT (Web Search): ${intelligenceReport} + 4. SUSPECTS: ${suspectNames} Based on all the evidence and analysis, determine: - Who is the most likely culprit? - What evidence supports this conclusion? - What was their motive and opportunity? + - Is this an isolated incident or part of a larger criminal network? - Summarise the insurance appraisal values of the stolen artworks. - Calculate the total estimated insurance value of the stolen items based on the appraisal results. - Provide a comprehensive summary of the case. diff --git a/project/JavaScript/solution/src/investigationWorkflow.ts b/project/JavaScript/solution/src/investigationWorkflow.ts index bafafde..c73debb 100644 --- a/project/JavaScript/solution/src/investigationWorkflow.ts +++ b/project/JavaScript/solution/src/investigationWorkflow.ts @@ -1,7 +1,7 @@ import { StateGraph, END, START } from '@langchain/langgraph' import { OrchestrationClient } from '@sap-ai-sdk/orchestration' import type { AgentState, ModelParams } from './types.js' -import { callRPT1Tool, callGroundingServiceTool } from './tools.js' +import { callRPT1Tool, callGroundingServiceTool, callSonarProSearchTool } from './tools.js' import { AGENT_CONFIGS } from './agentConfigs.js' /** @@ -102,6 +102,56 @@ export class InvestigationWorkflow { } } + /** + * Intelligence Researcher Agent Node - Uses web search to find criminal patterns + */ + private async intelligenceResearcherNode(state: AgentState): Promise> { + console.log('\n🔍 Intelligence Researcher starting web search...') + + try { + const suspects = state.suspect_names.split(',').map(s => s.trim()) + const intelligenceResults: string[] = [] + + // Search for criminal records for each suspect + for (const suspect of suspects) { + console.log(` Searching public records for: ${suspect}`) + const query = `${suspect} criminal record art theft security technician Europe background check` + const result = await callSonarProSearchTool(query) + intelligenceResults.push(`Background check for ${suspect}:\n${result}`) + } + + // Search for similar art theft patterns + console.log(' Searching for similar art theft incidents...') + const patternQuery = + 'museum art theft insider job no forced entry Europe similar incidents criminal network' + const patternResult = await callSonarProSearchTool(patternQuery) + intelligenceResults.push(`Similar Art Theft Patterns:\n${patternResult}`) + + const intelligenceReport = `Intelligence Research Complete: ${intelligenceResults.join('\n\n')} + Summary: Conducted OSINT research on all suspects and identified similar crime patterns` + + console.log('✅ Intelligence research complete') + + return { + intelligence_report: intelligenceReport, + messages: [...state.messages, { role: 'assistant', content: intelligenceReport }], + } + } catch (error) { + const errorMsg = error instanceof Error ? error.message : String(error) + console.error('❌ Intelligence research failed:', errorMsg) + if (error instanceof Error && error.stack) { + console.error(error.stack) + } + return { + intelligence_report: `Error during intelligence research: ${errorMsg}`, + messages: [ + ...state.messages, + { role: 'assistant', content: `Error during intelligence research: ${errorMsg}` }, + ], + } + } + } + /** * Lead Detective Agent Node - Synthesizes findings and identifies the culprit */ @@ -118,6 +168,7 @@ export class InvestigationWorkflow { content: AGENT_CONFIGS.leadDetective.systemPrompt( state.appraisal_result || 'No appraisal result available', state.evidence_analysis || 'No evidence analysis available', + state.intelligence_report || 'No intelligence report available', state.suspect_names, ), }, @@ -152,6 +203,7 @@ export class InvestigationWorkflow { suspect_names: null, appraisal_result: null, evidence_analysis: null, + intelligence_report: null, final_conclusion: null, messages: null, }, @@ -161,10 +213,12 @@ export class InvestigationWorkflow { workflow .addNode('appraiser', this.appraiserNode.bind(this)) .addNode('evidence_analyst', this.evidenceAnalystNode.bind(this)) + .addNode('intelligence_researcher', this.intelligenceResearcherNode.bind(this)) .addNode('lead_detective', this.leadDetectiveNode.bind(this)) .addEdge(START, 'appraiser') .addEdge('appraiser', 'evidence_analyst') - .addEdge('evidence_analyst', 'lead_detective') + .addEdge('evidence_analyst', 'intelligence_researcher') + .addEdge('intelligence_researcher', 'lead_detective') .addEdge('lead_detective', END) return workflow diff --git a/project/JavaScript/solution/src/tools.ts b/project/JavaScript/solution/src/tools.ts index 0463837..9a9f0b6 100644 --- a/project/JavaScript/solution/src/tools.ts +++ b/project/JavaScript/solution/src/tools.ts @@ -65,3 +65,42 @@ export async function callGroundingServiceTool(user_question: string): Promise { + try { + const response = await webSearchClient.chatCompletion({ + inputParams: { search_query }, + }) + return response.getContent() ?? 'No search results found' + } catch (error) { + const errorMessage = error instanceof Error ? error.message : String(error) + console.error('❌ Sonar-pro web search failed:', errorMessage) + if (error instanceof Error && error.stack) console.error(error.stack) + return `Error calling sonar-pro web search: ${errorMessage}` + } +} diff --git a/project/JavaScript/solution/src/types.ts b/project/JavaScript/solution/src/types.ts index e25a479..1568eba 100644 --- a/project/JavaScript/solution/src/types.ts +++ b/project/JavaScript/solution/src/types.ts @@ -52,6 +52,7 @@ export interface RPT1Payload { index_column: string rows: StolenItem[] parse_data_types?: boolean // Optional: control data type parsing + data_schema?: any // Optional: schema definition for data types } /** @@ -62,6 +63,7 @@ export interface AgentState { suspect_names: string appraisal_result?: string evidence_analysis?: string + intelligence_report?: string final_conclusion?: string messages: Array<{ role: string diff --git a/project/Python/solution/config/agents.yaml b/project/Python/solution/config/agents.yaml index ec5a7f0..6018a35 100644 --- a/project/Python/solution/config/agents.yaml +++ b/project/Python/solution/config/agents.yaml @@ -12,13 +12,29 @@ evidence_analyst_agent: role: > Criminal Evidence Analyst goal: > - Retrieve and analyze evidence ONLY via the call_grounding_service tool. - Search for each suspect by name: {suspect_names}. Do NOT fabricate any evidence or alibis. + Retrieve and analyze evidence ONLY via the call_grounding_service tool. + Search for each suspect by name: {suspect_names}. Do NOT fabricate any evidence or alibis. Report only what the tool returns. backstory: > You are a methodical evidence analyst who bases conclusions strictly on retrieved documents. You never assume facts. llm: sap/anthropic--claude-4.5-opus +intelligence_researcher_agent: + role: > + Open-Source Intelligence (OSINT) Researcher + goal: > + Search the web for similar art thefts, criminal patterns, and suspect backgrounds + to determine if this heist is part of a larger criminal network. Use the + call_sonar_pro_search tool to find recent incidents, news reports, and public + criminal records for all three suspects: {suspect_names}. + backstory: > + You are an OSINT specialist who excels at finding patterns across multiple + crime scenes. You search public databases, news archives, and criminal records + to connect seemingly isolated incidents. Your expertise has uncovered several + international art theft rings, and you know how to distinguish professional + criminals from amateurs. + llm: sap/gpt-4o + lead_detective_agent: role: > Lead Detective and Case Manager diff --git a/project/Python/solution/config/tasks.yaml b/project/Python/solution/config/tasks.yaml index da2a6e7..1512ee4 100644 --- a/project/Python/solution/config/tasks.yaml +++ b/project/Python/solution/config/tasks.yaml @@ -7,19 +7,45 @@ appraise_loss_task: analyze_evidence_task: description: > - Analyze the evidence of the theft that you can access via the grounding tool. - Provide any insights that can help in the investigation especially regarding alabies. - Check the evidence for all three suspect names 1. Sophie Dubois, 2. Marcus Chen and + Analyze the evidence of the theft that you can access via the grounding tool. + Provide any insights that can help in the investigation especially regarding alabies. + Check the evidence for all three suspect names 1. Sophie Dubois, 2. Marcus Chen and 3. Viktor Petrovand and provide an analysis for each of them. expected_output: > A detailed analysis of the evidence for each suspect, including any insights that can help in the investigation. agent: evidence_analyst_agent +research_criminal_network: + description: > + Search the web for intelligence about the three suspects ({suspect_names}) and + related crimes. Use the call_sonar_pro_search tool to find: + 1. Public criminal records or prior convictions for each suspect + 2. Similar art theft incidents with the same modus operandi (insider job, no forced entry) + 3. Connections to known art theft rings or criminal networks + 4. News reports or public information about any of the suspects + 5. Recent museum heists in Europe with similar patterns + + Cross-reference your web findings with the internal evidence analyzed by the + evidence analyst. Focus on discovering whether this is an isolated incident or + part of a larger criminal operation. + expected_output: > + A comprehensive intelligence report containing: + - Background checks for all three suspects with web sources + - List of similar art thefts found online (dates, locations, MO) + - Evidence of criminal network connections (if any) + - Assessment: isolated incident vs. organized crime ring + - All findings MUST include web sources with URLs and dates + agent: intelligence_researcher_agent + solve_crime: description: > - Find the thief from, the suspects by activating the evidence investigator agent and instructing him to look for the three suspects - using the grounding tool. He should find information on alibies and motives and return a report for you to analyze. And use the appraise_loss_task from - the appraiser agent to find the value of the stolen goods. + Find the thief among the suspects by reviewing: + 1. The insurance appraisal values from the appraiser agent + 2. The internal evidence analysis from the evidence analyst agent + 3. The web intelligence report from the intelligence researcher agent + + Synthesize all three sources to identify the culprit with high confidence. + Consider both internal evidence and external patterns/connections found online. expected_output: > The name of the thief and the total value of the stolen goods for the insurance. agent: lead_detective_agent \ No newline at end of file diff --git a/project/Python/solution/investigator_crew.py b/project/Python/solution/investigator_crew.py index f92dff5..a78967f 100644 --- a/project/Python/solution/investigator_crew.py +++ b/project/Python/solution/investigator_crew.py @@ -70,7 +70,45 @@ def call_grounding_service(user_question: str) -> str: response_dict = json.dumps(response.model_dump(), indent=2) # Convert to JSON string return response_dict # Return retrieved document chunks to the agent - + + +@tool("call_sonar_pro_search") +def call_sonar_pro_search(search_query: str) -> str: + """Search the web using Perplexity's sonar-pro model for real-time information + about crimes, suspects, and criminal patterns. Use this to find similar incidents, + criminal networks, public records, or patterns that are not in internal documents. + + Args: + search_query: The search query about crimes, suspects, or criminal patterns + + Returns: + Search results with source citations from the web + """ + from litellm import completion + + try: + response = completion( + model="sap/sonar-pro", # Perplexity model with web search + messages=[ + { + "role": "system", + "content": "You are a web search assistant specializing in criminal intelligence. Search for accurate, recent information and always provide source citations with URLs and dates." + }, + { + "role": "user", + "content": search_query + } + ], + temperature=0.2, # Lower temperature for factual search + ) + + result = response.choices[0].message.content + return result + + except Exception as e: + return f"Error calling sonar-pro web search: {str(e)}" + + @CrewBase class InvestigatorCrew(): """InvestigatorCrew crew""" @@ -105,7 +143,22 @@ def analyze_evidence_task(self) -> Task: return Task( config=self.tasks_config['analyze_evidence_task'] ) - + + @agent + def intelligence_researcher_agent(self) -> Agent: + return Agent( + config=self.agents_config['intelligence_researcher_agent'], + verbose=True, + tools=[call_sonar_pro_search] # Web search tool + ) + + @task + def research_criminal_network(self) -> Task: + return Task( + config=self.tasks_config['research_criminal_network'], + context=[self.analyze_evidence_task()] # Uses internal evidence to inform web searches + ) + @agent def lead_detective_agent(self) -> Agent: return Agent( @@ -117,7 +170,7 @@ def lead_detective_agent(self) -> Agent: def solve_crime(self) -> Task: return Task( config=self.tasks_config['solve_crime'], - context=[self.appraise_loss_task(), self.analyze_evidence_task()] # 👈 Lead detective uses results from other tasks + context=[self.appraise_loss_task(), self.analyze_evidence_task(), self.research_criminal_network()] # Lead detective uses all three sources ) @crew diff --git a/project/Python/solution/requirements.txt b/project/Python/solution/requirements.txt new file mode 100644 index 0000000..56a646e --- /dev/null +++ b/project/Python/solution/requirements.txt @@ -0,0 +1,4 @@ +crewai==0.86.0 +litellm==1.82.6 +python-dotenv==1.0.0 +sap-ai-sdk-gen diff --git a/project/Python/starter-project/requirements.txt b/project/Python/starter-project/requirements.txt new file mode 100644 index 0000000..56a646e --- /dev/null +++ b/project/Python/starter-project/requirements.txt @@ -0,0 +1,4 @@ +crewai==0.86.0 +litellm==1.82.6 +python-dotenv==1.0.0 +sap-ai-sdk-gen