Skip to content

Commit 6b84cd6

Browse files
committed
feat: implement 2026 Enterprise Roadmap features.
- Continuous Learning Pipeline with feedback persistence. - Multi-Language Cultural Persona Routing (JP/DE/EN). - Industry Semantic Firewall (Healthcare/Finance). - Comprehensive test coverage for all new features.
1 parent 8769668 commit 6b84cd6

9 files changed

Lines changed: 307 additions & 42 deletions

File tree

src/agents/brain.py

Lines changed: 62 additions & 26 deletions
Original file line numberDiff line numberDiff line change
@@ -23,12 +23,42 @@
2323
class CommitGuardBrain:
2424
"""
2525
Elite Agent Brain: Decoupled from LLM Providers via LLMFactory.
26+
2026 Upgrade: Cultural Persona Routing & Industry Intelligence.
2627
"""
2728

29+
CULTURAL_PROMPTS = {
30+
"en": "Standard professional tone. Focus on clarity and deadline accountability.",
31+
"en-UK": "British professional tone. Use polite understatements and avoid over-assertiveness.",
32+
"ja": "High-context Japanese tone. Prioritize harmony (wa). Use indirect observations and soft suggestions instead of direct demands.",
33+
"de": "Direct German Sachlichkeit. Focus on objective facts, precision, and straightforward feedback. No fluff.",
34+
}
35+
2836
def __init__(self):
2937
self.provider = LLMFactory.get_provider()
3038
self.model = settings.MODEL_NAME
3139

40+
async def detect_language(self, text: str) -> str:
41+
"""
42+
2026 Agentic Language Detection: Uses semantic intent to identify cultural context.
43+
"""
44+
# In a real 2026 deployment, we might use a dedicated lightweight model.
45+
# Here we use the main provider with a strict routing prompt.
46+
try:
47+
detected = await self.provider.chat_completion(
48+
response_model=str,
49+
model=self.model,
50+
messages=[
51+
{
52+
"role": "system",
53+
"content": "Detect the language and cultural nuance of the following text. Return only the code: 'en', 'ja', or 'de'.",
54+
},
55+
{"role": "user", "content": text},
56+
],
57+
)
58+
return str(detected).strip().lower() if detected in ["en", "ja", "de"] else "en"
59+
except Exception:
60+
return "en" # Fallback to English
61+
3262
async def analyze_excuse(self, user_input: str) -> ExcuseAnalysis:
3363
return await self.provider.chat_completion(
3464
response_model=ExcuseAnalysis,
@@ -93,13 +123,18 @@ async def evaluate_participation(
93123
check_in: str,
94124
reliability_score: float,
95125
consecutive_firm: int,
126+
lang: str | None = None,
127+
industry: str | None = None,
96128
) -> PipelineEvaluation:
97129
"""
98130
The Orchestration Pipeline: Decoupled and high-fidelity evaluation.
99131
"""
132+
# 0. Language Awareness
133+
target_lang = lang or await self.detect_language(check_in)
134+
target_industry = industry or settings.SELECTED_INDUSTRY
135+
100136
try:
101137
# 1. Parallel Analysis Phase (High Velocity) with Safety Timeout
102-
# We enforce a 30s SLA for the initial heuristic/LLM sweep.
103138
excuse, burnout, risk = await asyncio.wait_for(
104139
asyncio.gather(
105140
self.analyze_excuse(check_in),
@@ -115,7 +150,6 @@ async def evaluate_participation(
115150
logger.error(
116151
"orchestration_timeout", user_id=user_id, status="aborting_pipeline"
117152
)
118-
# Fallback for critical failure: supportive neutral message
119153
return PipelineEvaluation(
120154
decision=AgentDecision(
121155
action="escalate_to_manager",
@@ -124,7 +158,7 @@ async def evaluate_participation(
124158
analysis_summary="Orchestration Timeout: AI Providers failed to respond within 30s.",
125159
),
126160
excuse=ExcuseAnalysis(
127-
category=ExcuseCategory.LEGITIMATE, # Benefit of the doubt
161+
category=ExcuseCategory.LEGITIMATE,
128162
confidence_score=0.0,
129163
reasoning="Timeout: Analysis incomplete.",
130164
),
@@ -141,31 +175,32 @@ async def evaluate_participation(
141175
),
142176
)
143177

144-
# 2. Decision Synthesis
178+
# 2. Decision Synthesis (Culture & Industry Aware)
145179
with LatencyMonitor("decision_synthesis_latency", user_id):
146180
decision = await self.adapt_tone(
147181
excuse,
148182
risk,
149183
burnout,
150184
reliability_score=reliability_score,
151185
consecutive_firm_calls=consecutive_firm,
186+
lang=target_lang,
187+
industry=target_industry,
152188
)
153189

154-
# 3. Final Ethical Supervision (Elite Guardrail)
190+
# 3. Final Ethical Supervision (Semantic Firewall)
155191
supervisor = SafetySupervisor()
156192
context = (
157193
f"User: {user_id}. Reliability: {reliability_score}%. "
158-
f"Consecutive firm: {consecutive_firm}."
194+
f"Consecutive firm: {consecutive_firm}. Industry: {target_industry}."
159195
)
160196

161197
with LatencyMonitor("safety_supervisor_latency", user_id):
162198
audit = await supervisor.audit_message(
163-
decision.message, decision.tone, context
199+
decision.message, decision.tone, context, industry=target_industry
164200
)
165201

166202
intervention = None
167203

168-
# A. HARD BLOCK (HR Territory) - Immediate Escalation, No Retry
169204
if audit.is_hard_blocked:
170205
logger.critical(
171206
"hr_legal_boundary_detected", user_id=user_id, message=decision.message
@@ -182,40 +217,34 @@ async def evaluate_participation(
182217
intervention_type="block",
183218
)
184219

185-
# B. SOFT CORRECTION (Tone/Cultural) - Injection Mechanism
186220
elif not audit.is_safe:
187221
logger.warning(
188222
"safety_correction_injected", user_id=user_id, reason=audit.reasoning
189223
)
190224
original = decision.message
191-
# Capitalization Hook: Ensure correction starts with uppercase
192225
raw_correction = audit.suggested_correction or decision.message
193226
if raw_correction and len(raw_correction) > 0:
194227
raw_correction = raw_correction[0].upper() + raw_correction[1:]
195228

196229
decision.message = raw_correction
197230
decision.analysis_summary += f" | Safety Correction: {audit.reasoning}"
198231

199-
# --- SAFETY VALVE: RE-VALIDATION (Tier 4 Humility) ---
200-
# Verify the correction itself is not unsafe (e.g., hallucinated threat).
201-
# This doubles latency but ensures zero-trust safety.
202232
re_audit = await supervisor.audit_message(
203-
decision.message, decision.tone, context
233+
decision.message, decision.tone, context, industry=target_industry
204234
)
205235
if not re_audit.is_safe:
206236
logger.critical(
207237
"unsafe_correction_caught",
208238
user_id=user_id,
209239
bad_fix=decision.message,
210240
)
211-
# Fallback to HITL if the AI failed to fix it safely
212241
decision.action = "escalate_to_manager"
213242
decision.message = (
214243
"Automated correction failed safety check. Manual review required."
215244
)
216245
intervention = SafetyIntervention(
217246
original_message=original,
218-
corrected_message=None, # Discard unsafe fix
247+
corrected_message=None,
219248
reasoning="Safety Valve Triggered: Correction was still unsafe.",
220249
intervention_type="review",
221250
)
@@ -227,7 +256,6 @@ async def evaluate_participation(
227256
intervention_type="correction",
228257
)
229258

230-
# C. AMBIGUITY (Low Confidence) - Human-in-the-Loop
231259
elif (
232260
audit.requires_human_review
233261
or audit.supervisor_confidence < settings.SAFETY_CONFIDENCE_THRESHOLD
@@ -257,18 +285,26 @@ async def adapt_tone(
257285
burnout: BurnoutDetection,
258286
reliability_score: float = 100.0,
259287
consecutive_firm_calls: int = 0,
288+
lang: str = "en",
289+
industry: str = "generic",
260290
) -> AgentDecision:
261-
# ENTERPRISE LLM PROMPT (Self-Correction / Sensitivity)
291+
# 2026 Cultural Persona Logic
292+
cultural_instruction = self.CULTURAL_PROMPTS.get(lang, self.CULTURAL_PROMPTS["en"])
293+
262294
prompt = f"""
263-
Determine action and tone.
264-
User Reliability: {reliability_score}%
265-
Consecutive Strict Interventions: {consecutive_firm_calls}
266-
Manager's Cultural Directness Setting: {settings.CULTURAL_DIRECTNESS_LEVEL}
295+
Role: CommitGuard Decision Agent
296+
Focus Industry: {industry}
297+
Cultural Persona: {cultural_instruction}
298+
299+
Context:
300+
- User Reliability: {reliability_score}%
301+
- Consecutive Strict Interventions: {consecutive_firm_calls}
302+
- Cultural Directness: {settings.CULTURAL_DIRECTNESS_LEVEL}
267303
268-
RULES:
269-
- If Consecutive Strict >= 3, you MUST use SUPPORTIVE/NEUTRAL tone
270-
to avoid morale burnout.
271-
- Respect the Cultural Directness: if 'low', soften all firm feedback.
304+
Rules:
305+
- If Consecutive Strict >= 3, use SUPPORTIVE/NEUTRAL tone.
306+
- Respect the Cultural Persona above above ALL else.
307+
- If industry is 'healthcare' or 'finance', avoid any phrasing that implies legally binding commitments unless explicit.
272308
"""
273309

274310
return await self.provider.chat_completion(

src/agents/learning.py

Lines changed: 77 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,77 @@
1+
from datetime import datetime, timedelta, timezone
2+
from sqlmodel import select, func
3+
from src.core.database import AsyncSessionLocal
4+
from src.schemas.agents import SafetyFeedback
5+
from src.core.logging import logger
6+
7+
class SupervisorFeedbackLoop:
8+
"""
9+
2026 Continuous Learning Agent: Analyzes manager overrides to improve AI calibration.
10+
"""
11+
12+
@staticmethod
13+
async def log_manager_decision(
14+
intervention_id: str,
15+
user_id: str,
16+
manager_id: str,
17+
action: str,
18+
message: str,
19+
notes: str | None = None
20+
) -> None:
21+
"""
22+
Persists manager feedback to the database for historical ROI analysis.
23+
"""
24+
async with AsyncSessionLocal() as session:
25+
feedback = SafetyFeedback(
26+
intervention_id=intervention_id,
27+
user_id=user_id,
28+
manager_id=manager_id,
29+
action_taken=action,
30+
final_message_sent=message,
31+
feedback_notes=notes,
32+
created_at=datetime.now(timezone.utc)
33+
)
34+
session.add(feedback)
35+
await session.commit()
36+
37+
logger.info(
38+
"feedback_persisted",
39+
intervention_id=intervention_id,
40+
action=action,
41+
manager=manager_id
42+
)
43+
44+
@staticmethod
45+
async def calculate_intervention_acceptance(days: int = 30) -> float:
46+
"""
47+
ROI Metric: Calculates the percentage of AI corrections accepted by managers.
48+
"""
49+
since = datetime.utcnow() - timedelta(days=days)
50+
async with AsyncSessionLocal() as session:
51+
# Total count
52+
statement_total = select(func.count(SafetyFeedback.id)).where(SafetyFeedback.created_at >= since)
53+
result_total = await session.execute(statement_total)
54+
total = result_total.scalar() or 0
55+
56+
if total == 0:
57+
return 1.0 # Default to perfect if no feedback yet
58+
59+
# Accepted count
60+
statement_accepted = select(func.count(SafetyFeedback.id)).where(
61+
SafetyFeedback.created_at >= since,
62+
SafetyFeedback.action_taken == "accepted"
63+
)
64+
result_accepted = await session.execute(statement_accepted)
65+
accepted = result_accepted.scalar() or 0
66+
67+
return round(accepted / total, 2)
68+
69+
@staticmethod
70+
async def get_audit_trail(intervention_id: str) -> SafetyFeedback | None:
71+
"""
72+
Governance Feature: Retrieves the full audit trail for a specific intervention.
73+
"""
74+
async with AsyncSessionLocal() as session:
75+
statement = select(SafetyFeedback).where(SafetyFeedback.intervention_id == intervention_id)
76+
result = await session.execute(statement)
77+
return result.scalar_one_or_none()

src/agents/safety.py

Lines changed: 28 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -6,36 +6,56 @@
66
class SafetySupervisor:
77
"""
88
The 'Overwatch' Layer: An autonomous agent that audits outgoing communications.
9-
This moves ethics from simple 'if/else' into sophisticated agentic reasoning.
9+
2026 Upgrade: Industry Semantic Firewall & Intent-based Auditing.
1010
"""
1111

12+
INDUSTRY_CONFIGS = {
13+
"healthcare": {
14+
"hr_keywords": ["HIPAA", "patient data", "medical records", "PHI", "PII"],
15+
"semantic_rules": "Redact any patient identifiers. Block messages mentioning medical charts or specific treatments.",
16+
},
17+
"finance": {
18+
"hr_keywords": ["insider trading", "SEC compliance", "FINRA", "market manipulation"],
19+
"semantic_rules": "Block any phrasing that could be interpreted as financial advice or market manipulation.",
20+
},
21+
"generic": {
22+
"hr_keywords": ["Salary", "PIP", "Firing", "Legal Threats"],
23+
"semantic_rules": "Enforce standard professional conduct and HR boundaries.",
24+
}
25+
}
26+
1227
def __init__(self, provider_name: str | None = None):
1328
self.provider = LLMFactory.get_provider(provider_name)
1429
self.model = settings.MODEL_NAME
1530

1631
async def audit_message(
17-
self, message: str, tone: ToneType, user_context: str
32+
self,
33+
message: str,
34+
tone: ToneType,
35+
user_context: str,
36+
industry: str = "generic"
1837
) -> SafetyAudit:
1938
"""
2039
Performs a final safety check on the proposed message.
2140
"""
41+
config = self.INDUSTRY_CONFIGS.get(industry, self.INDUSTRY_CONFIGS["generic"])
2242

2343
prompt = f"""
24-
AUDIT REQUEST:
44+
2026 AUDIT REQUEST (INDUSTRY: {industry.upper()}):
2545
2646
CRITICAL TASKS:
2747
1. HARSHNESS: If message is too harsh (Tone Drift) or culturally
2848
insensitive, flag 'is_safe': false.
29-
2. HR BOUNDARIES (Hard Block):
30-
- Block Discussions on: Salary, Firing, PIPs, Legal Threats.
31-
- ALLOW Discussions on: Project Pricing, Budgeting, Cost Optimization.
49+
2. SEMANTIC FIREWALL (Industry Compliance):
50+
- RESTRICTED TOPICS: {", ".join(config["hr_keywords"])}
51+
- SEMANTIC RULES: {config["semantic_rules"]}
3252
- If blocked, set 'is_hard_blocked': true.
3353
3. CORRECTION STRATEGY (Hybrid):
3454
- Minor Tone Issue -> Targeted phrase replacement. Set 'correction_type': 'surgical'.
3555
- Major Toxic Issue -> Full professional rewrite. Set 'correction_type': 'full_rewrite'.
3656
3757
4. CONFIDENCE:
38-
- If unsure (e.g., ambiguous idioms), set 'supervisor_confidence' < {settings.SAFETY_CONFIDENCE_THRESHOLD}
58+
- If unsure, set 'supervisor_confidence' < {settings.SAFETY_CONFIDENCE_THRESHOLD}
3959
and flag 'requires_human_review'.
4060
4161
DATA TO AUDIT:
@@ -59,7 +79,7 @@ async def audit_message(
5979
{
6080
"role": "system",
6181
"content": (
62-
"You are a specialized HR Ethics & Morale Safety Supervisor."
82+
f"You are a specialized 2026 {industry.capitalize()} Ethics & Security Supervisor."
6383
),
6484
},
6585
{"role": "user", "content": prompt},

src/api/routes.py

Lines changed: 8 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -170,10 +170,15 @@ async def log_safety_feedback(feedback: CorrectionFeedback):
170170
"""
171171
Priority 1 Feature: Track manager acceptance of Safety Supervisor interventions.
172172
"""
173-
logger.info(
174-
"safety_feedback_received",
173+
from src.agents.learning import SupervisorFeedbackLoop
174+
175+
await SupervisorFeedbackLoop.log_manager_decision(
175176
intervention_id=feedback.intervention_id,
177+
user_id="unknown", # We'll need a way to link this to the user in the next iteration
178+
manager_id=feedback.manager_id,
176179
action=feedback.action_taken,
177-
manager=feedback.manager_id,
180+
message=feedback.final_message_sent,
181+
notes=feedback.comments,
178182
)
183+
179184
return {"status": "logged", "message": "Safety feedback recorded for model tuning."}

src/core/config.py

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -28,6 +28,16 @@ class Settings(BaseSettings):
2828
# Integrations
2929
SLACK_WEBHOOK_URL: str | None = None
3030

31+
# Roadmap: Multi-Language & Industry
32+
SUPPORTED_LANGUAGES: dict[str, str] = {
33+
"en": "English (Global)",
34+
"en-UK": "English (British idioms)",
35+
"ja": "Japanese (High-context culture)",
36+
"de": "German (Direct communication style)",
37+
}
38+
SELECTED_INDUSTRY: str = "generic" # Options: generic, healthcare, finance
39+
LEARNING_ENABLED: bool = True
40+
3141
# Security
3242

3343
# Security

0 commit comments

Comments
 (0)