diff --git a/exercises/JavaScript/06-discover-connected-crimes.md b/exercises/JavaScript/06-discover-connected-crimes.md index 99d6fd6..bef4dcc 100644 --- a/exercises/JavaScript/06-discover-connected-crimes.md +++ b/exercises/JavaScript/06-discover-connected-crimes.md @@ -233,9 +233,22 @@ import { callRPT1Tool, callGroundingServiceTool, callSonarProSearchTool } from " 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`; + const rawResults = `Intelligence Research Complete:\n\n${intelligenceResults.join("\n\n")}`; + const response = await this.orchestrationClient.chatCompletion({ + messages: [ + { + role: "system", + content: AGENT_CONFIGS.intelligenceResearcher.systemPrompt(state.suspect_names), + }, + { + role: "user", + content: `Here are the web search results. Write a concise intelligence report:\n\n${rawResults}`, + }, + ], + }); + + const intelligenceReport = response.getContent() || "No intelligence report could be generated."; console.log("✅ Intelligence research complete"); return { diff --git a/exercises/Python-LangGraph/06-discover-connected-crimes.md b/exercises/Python-LangGraph/06-discover-connected-crimes.md index e3388d8..4d19527 100644 --- a/exercises/Python-LangGraph/06-discover-connected-crimes.md +++ b/exercises/Python-LangGraph/06-discover-connected-crimes.md @@ -148,11 +148,14 @@ def intelligence_researcher_node(state: AgentState) -> dict: pattern_result = call_sonar_pro_search(pattern_query) intelligence_results.append(f"Similar Art Theft Patterns:\n{pattern_result}") - intelligence_report = ( - "Intelligence Research Complete:\n\n" + "\n\n".join(intelligence_results) + - f"\n\nSummary: Conducted OSINT research on all suspects and identified similar crime patterns" - ) + raw_results = "Intelligence Research Complete:\n\n" + "\n\n".join(intelligence_results) + + response = model.invoke([ + SystemMessage(content=WEB_RESEARCHER_AGENT["prompt"]), + HumanMessage(content=f"Here are the web search results. Write a concise intelligence report:\n\n{raw_results}"), + ]) + intelligence_report = response.content print("✅ Intelligence research complete") return { @@ -304,7 +307,7 @@ print("="*50) print(result["intelligence_report"] or "(not set)") ``` -> 💡 **`server.py`** also constructs the initial state for `investigator_graph.ainvoke(...)`. Add `"intelligence_report": None` there too — find the dict and add the field. +> 💡 **`server.py`** also constructs the initial state for `investigator_graph.invoke(...)`. Add `"intelligence_report": None` there too — find the dict and add the field. --- diff --git a/exercises/Python-LangGraph/07-solve-the-crime.md b/exercises/Python-LangGraph/07-solve-the-crime.md index ab435c4..26bcb83 100644 --- a/exercises/Python-LangGraph/07-solve-the-crime.md +++ b/exercises/Python-LangGraph/07-solve-the-crime.md @@ -23,17 +23,20 @@ This is enough to make the system run, but the Lead Detective may stop short of 👉 Update the `_lead_detective_prompt` function in `config/agents.py` with a more specific closing instruction: ```python -def _lead_detective_prompt(appraisal_result: str, evidence_analysis: str, suspect_names: str) -> str: +def _lead_detective_prompt(appraisal_result: str, evidence_analysis: str, intelligence_report: str, suspect_names: str) -> str: return ( "You are the Lead Detective coordinating an art theft investigation. " "You have received the following information from your team:\n\n" f"1. INSURANCE APPRAISAL:\n{appraisal_result}\n\n" - f"2. EVIDENCE ANALYSIS:\n{evidence_analysis}\n\n" - f"3. SUSPECTS: {suspect_names}\n\n" + f"2. EVIDENCE ANALYSIS (Internal Documents):\n{evidence_analysis}\n\n" + f"3. INTELLIGENCE REPORT (Web Search):\n{intelligence_report}\n\n" + f"4. SUSPECTS: {suspect_names}\n\n" "Based on all the evidence and analysis, you MUST:\n" "- Name the most likely thief and explain the evidence supporting that conclusion\n" + "- Consider both internal evidence and external criminal patterns\n" "- Note any alibis or evidence that clears the other suspects\n" "- State the total insured value of the stolen goods\n" + "- Assess whether this is an isolated incident or part of a larger criminal network\n" "- Provide a comprehensive summary of the case." ) ``` diff --git a/exercises/Python/04-building-multi-agent-system.md b/exercises/Python/04-building-multi-agent-system.md index 27b0560..d2d27cb 100644 --- a/exercises/Python/04-building-multi-agent-system.md +++ b/exercises/Python/04-building-multi-agent-system.md @@ -26,13 +26,13 @@ Instead of defining agents and tasks directly in Python code, we'll move them to # agents.yaml appraiser_agent: role: > - Loss Appraiser + Stolen Goods Loss Appraiser goal: > - Predict the missing values of stolen items using the RPT-1 model via the call_rpt1 tool. - Use the payload data from inputs: {payload} + Predict the monetary value of stolen items ONLY by calling the call_rpt1 tool with payload {payload}. + Do NOT invent or estimate values yourself. If the tool call fails, report the failure. backstory: > - You are an expert insurance appraiser specializing in fine art valuation and theft assessment. - llm: sap/gpt-4o + You are an insurance appraiser who relies strictly on model predictions. You never guess values. + llm: sap/anthropic--claude-4.5-opus ``` > 💡 **What's happening here?** We're taking the agent definition from your `basic_agent.py` file (the `role`, `goal`, `backstory`, and `llm` parameters) and moving them to this YAML file. The `>` symbol in YAML allows multi-line text. diff --git a/exercises/Python/06-discover-connected-crimes.md b/exercises/Python/06-discover-connected-crimes.md index 4337978..55385a3 100644 --- a/exercises/Python/06-discover-connected-crimes.md +++ b/exercises/Python/06-discover-connected-crimes.md @@ -246,17 +246,6 @@ research_criminal_network: ) ``` -👉 Also update the `solve_crime` task to use all three sources: - -```python - @task - def solve_crime(self) -> Task: - return Task( - config=self.tasks_config['solve_crime'], - context=[self.appraise_loss_task(), self.analyze_evidence_task(), self.research_criminal_network()] - ) -``` - > 💡 **Method Positioning Matters!** > > Your class should now have this order: diff --git a/exercises/Python/07-solve-the-crime.md b/exercises/Python/07-solve-the-crime.md index 4c4bbba..c204824 100644 --- a/exercises/Python/07-solve-the-crime.md +++ b/exercises/Python/07-solve-the-crime.md @@ -57,25 +57,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 results from all three tasks ) ``` -> 💡 **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 from Exercise 06. 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 (added in Exercise 06) +> 6. `research_criminal_network()` method (added in 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 preceding tasks +> - The Lead Detective will receive the output from the Loss Appraiser, Evidence Analyst, and Intelligence Researcher +> - This enables the detective to combine financial predictions, evidence analysis, and criminal network intelligence to solve the crime ### Step 4: Verify Crew Configuration diff --git a/project/JavaScript/solution/package-lock.json b/project/JavaScript/solution/package-lock.json index 0f835d8..ce59fc4 100644 --- a/project/JavaScript/solution/package-lock.json +++ b/project/JavaScript/solution/package-lock.json @@ -1,11 +1,11 @@ { - "name": "investigator-crew-typescript", + "name": "investigator-graph-typescript", "version": "1.0.0", "lockfileVersion": 3, "requires": true, "packages": { "": { - "name": "investigator-crew-typescript", + "name": "investigator-graph-typescript", "version": "1.0.0", "license": "UNLICENSED", "dependencies": { @@ -28,6 +28,9 @@ "@types/node": "25.6.0", "tsx": "4.21.0", "typescript": "6.0.2" + }, + "engines": { + "node": "22.x" } }, "node_modules/@a2a-js/sdk": { diff --git a/project/JavaScript/solution/src/investigationWorkflow.ts b/project/JavaScript/solution/src/investigationWorkflow.ts index f3c4fc2..015d950 100644 --- a/project/JavaScript/solution/src/investigationWorkflow.ts +++ b/project/JavaScript/solution/src/investigationWorkflow.ts @@ -126,9 +126,22 @@ export class InvestigationWorkflow { 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`; + const rawResults = `Intelligence Research Complete:\n\n${intelligenceResults.join("\n\n")}`; + const response = await this.orchestrationClient.chatCompletion({ + messages: [ + { + role: "system", + content: AGENT_CONFIGS.intelligenceResearcher.systemPrompt(state.suspect_names), + }, + { + role: "user", + content: `Here are the web search results. Write a concise intelligence report:\n\n${rawResults}`, + }, + ], + }); + + const intelligenceReport = response.getContent() || "No intelligence report could be generated."; console.log("✅ Intelligence research complete"); return { diff --git a/project/Python-LangGraph/solution/investigator_graph.py b/project/Python-LangGraph/solution/investigator_graph.py index 9099a2a..a0ed855 100644 --- a/project/Python-LangGraph/solution/investigator_graph.py +++ b/project/Python-LangGraph/solution/investigator_graph.py @@ -173,11 +173,14 @@ def intelligence_researcher_node(state: AgentState) -> dict: pattern_result = call_sonar_pro_search(pattern_query) intelligence_results.append(f"Similar Art Theft Patterns:\n{pattern_result}") - intelligence_report = ( - "Intelligence Research Complete:\n\n" + "\n\n".join(intelligence_results) + - f"\n\nSummary: Conducted OSINT research on all suspects and identified similar crime patterns" - ) + raw_results = "Intelligence Research Complete:\n\n" + "\n\n".join(intelligence_results) + + response = model.invoke([ + SystemMessage(content=WEB_RESEARCHER_AGENT["prompt"]), + HumanMessage(content=f"Here are the web search results. Write a concise intelligence report:\n\n{raw_results}"), + ]) + intelligence_report = response.content print("✅ Intelligence research complete") return {