stackConsult commited on
Commit
c75a06a
·
verified ·
1 Parent(s): 7c98bdb

Create research_agent.py

Browse files
Files changed (1) hide show
  1. research_agent.py +104 -0
research_agent.py ADDED
@@ -0,0 +1,104 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import csv
2
+ import json
3
+ import logging
4
+ from typing import Any, Dict, List
5
+
6
+ class ResearchAgent:
7
+ """
8
+ Collects, augments, audits, and outputs lead and signal data for outreach,
9
+ with robust error/memory handling and dynamic variable mapping.
10
+ """
11
+ def __init__(self, db: Any = None, schema: Dict[str, Any] = None):
12
+ self.db = db
13
+ self.schema = schema or {}
14
+ self.memory = []
15
+ self.errors = []
16
+ self.profiles = []
17
+ self.refined = []
18
+ self.mcp_updates = []
19
+ logging.basicConfig(level=logging.INFO)
20
+
21
+ def map_raw_data(self, raw_lead: Dict[str, Any], signals: Dict[str, Any], dynamic_vars: Dict[str, Any]) -> Dict[str, Any]:
22
+ mapped = {}
23
+ try:
24
+ for field, rule in (self.schema.get('lead', {})).items():
25
+ mapped[field] = raw_lead.get(field, rule.get('default'))
26
+ mapped.update(signals)
27
+ mapped.update(dynamic_vars)
28
+ except Exception as e:
29
+ self.errors.append(f"Mapping error: {e}")
30
+ logging.error(f"Mapping error: {e}")
31
+ return mapped
32
+
33
+ def handle_loop_errors(self, data_list: List[Dict[str, Any]]):
34
+ for idx, entry in enumerate(data_list):
35
+ try:
36
+ assert all(entry.values()), f"Missing value in {entry}"
37
+ except Exception as e:
38
+ self.errors.append(f"Loop error at {idx}: {e}")
39
+ logging.warning(f"Loop error at {idx}: {e}")
40
+
41
+ def memory_management(self, current: Dict[str, Any]):
42
+ self.memory.append(current)
43
+ if len(self.memory) > 50:
44
+ dropped = self.memory.pop(0)
45
+ logging.info(f"Dropped old memory: {dropped}")
46
+
47
+ def refine_profile(self, profile: Dict[str, Any], messaging_signals: Dict[str, Any]) -> Dict[str, Any]:
48
+ """Output the most outreach-ready version of the profile."""
49
+ refined = {**profile, **messaging_signals}
50
+ self.refined.append(refined)
51
+ return refined
52
+
53
+ def handoff(self, profiles: List[Dict[str, Any]], target: str = 'csv', path: str = 'handoff.csv'):
54
+ """Wire/export to csv/mcp/db."""
55
+ try:
56
+ if target == 'csv':
57
+ with open(path, 'w', newline='') as f:
58
+ writer = csv.DictWriter(f, fieldnames=profiles[0].keys())
59
+ writer.writeheader()
60
+ writer.writerows(profiles)
61
+ elif target == 'mcp' and self.db:
62
+ for p in profiles:
63
+ self.db.update_profile(p)
64
+ elif target == 'db' and self.db:
65
+ self.db.bulk_insert(profiles)
66
+ self.mcp_updates.extend(profiles)
67
+ logging.info(f"Profiles handed off to {target}")
68
+ except Exception as e:
69
+ self.errors.append(f"Handoff error: {e}")
70
+ logging.error(f"Handoff error: {e}")
71
+
72
+ def audit(self, profiles: List[Dict[str, Any]]):
73
+ """Audit field completeness, error/correction, and handoff readiness."""
74
+ required_fields = self.schema.get('required', set())
75
+ for idx, profile in enumerate(profiles):
76
+ missing = [f for f in required_fields if not profile.get(f)]
77
+ if missing:
78
+ self.errors.append(f"Profile {idx} missing: {missing}")
79
+ logging.warning(f"Profile {idx} missing: {missing}")
80
+
81
+ def export_refined_for_outreach(self, out_path: str = 'refined_for_outreach.csv'):
82
+ try:
83
+ with open(out_path, 'w', newline='') as f:
84
+ writer = csv.DictWriter(f, fieldnames=self.refined[0].keys())
85
+ writer.writeheader()
86
+ writer.writerows(self.refined)
87
+ except Exception as e:
88
+ self.errors.append(f"Export error: {e}")
89
+ logging.error(f"Export error: {e}")
90
+
91
+ def get_messaging_recommendations(self, profile: Dict[str, Any]) -> Dict[str, Any]:
92
+ """Stub: Replace with your ML/model recommendation logic."""
93
+ recs = {"recommendation": f"Personalize outreach for {profile.get('name', 'Unknown')}"}
94
+ return recs
95
+
96
+ def run_pipeline(self, leads, signals_list, dynamic_vars_list):
97
+ for raw_lead, signals, dynamic_vars in zip(leads, signals_list, dynamic_vars_list):
98
+ mapped = self.map_raw_data(raw_lead, signals, dynamic_vars)
99
+ self.memory_management(mapped)
100
+ self.profiles.append(mapped)
101
+ messaging = self.get_messaging_recommendations(mapped)
102
+ refined = self.refine_profile(mapped, messaging)
103
+ self.audit(self.profiles)
104
+ self.handoff(self.refined)