Skip to content

Commit c92a69b

Browse files
ai-response-according-to-user-preference
1 parent 1dfeee1 commit c92a69b

File tree

4 files changed

+185
-9
lines changed

4 files changed

+185
-9
lines changed

app/agents/voice/automatic/__init__.py

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -280,8 +280,10 @@ async def run_normal_mode(args):
280280
)
281281
)
282282

283-
# Personalize the system prompt if a user name is provided
284-
system_prompt = get_system_prompt(args.user_name, tts_provider, args.shop_id)
283+
# Personalize the system prompt if a user name is provided (with user context from mem0)
284+
system_prompt = await get_system_prompt(
285+
args.user_name, tts_provider, args.shop_id, args.user_email
286+
)
285287

286288
# Configure VAD - use pre-initialized model if available
287289
global _silero_vad_cache

app/agents/voice/automatic/prompts/__init__.py

Lines changed: 11 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,9 @@
55
from app.agents.voice.automatic.prompts.system.performance_directives import (
66
get_combined_directives,
77
)
8-
from app.agents.voice.automatic.prompts.system.personalization import append_user_info
8+
from app.agents.voice.automatic.prompts.system.personalization import (
9+
create_personalized_prompt,
10+
)
911
from app.agents.voice.automatic.prompts.system.tool_scope import (
1012
get_tool_scope_instructions,
1113
)
@@ -23,12 +25,16 @@
2325
from app.services.langfuse.prompts import fetch_prompt
2426

2527

26-
def get_system_prompt(
27-
user_name: str | None, tts_provider: TTSProvider | None, shop_id: str | None
28+
async def get_system_prompt(
29+
user_name: str | None,
30+
tts_provider: TTSProvider | None,
31+
shop_id: str | None,
32+
user_email: str | None = None,
2833
) -> str:
2934
"""
3035
Generates a personalized system prompt based on the user's name and TTS service.
3136
First attempts to fetch from LangFuse, then falls back to hardcoded prompt.
37+
Now includes user context retrieval from memory service.
3238
"""
3339
langfuse_prompt = None
3440

@@ -54,6 +60,7 @@ def get_system_prompt(
5460

5561
if user_name:
5662
logger.info(f"Personalizing prompt for user: {user_name}")
57-
prompt += append_user_info(user_name)
63+
# Use create_personalized_prompt which handles mem0 configuration internally
64+
prompt += await create_personalized_prompt(user_name, user_email)
5865

5966
return prompt
Lines changed: 112 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,12 @@
1-
def append_user_info(user_name: str) -> str:
1+
def append_user_info(user_name: str, user_context: str = None) -> str:
22
"""
33
Appends user personalization instructions to the system prompt.
4+
5+
Args:
6+
user_name: The user's name for personalization
7+
user_context: Optional memory-based context about the user's preferences and history
48
"""
5-
return f"""
9+
base_instructions = f"""
610
USER PERSONALIZATION
711
The user's name is {user_name}. Use it only when it adds genuine value to the conversation.
812
@@ -11,4 +15,110 @@ def append_user_info(user_name: str) -> str:
1115
- In **emotionally significant moments**, such as celebrating a win, expressing empathy, or addressing a concern directly.
1216
1317
Avoid using the name in closing lines, suggestions, or tool-generated follow-ups unless absolutely necessary. Never repeat the name within the same message. Prioritize a warm, natural tone — use the name only when it feels truly warranted in spoken conversation.
18+
19+
GREETING GUIDELINES:
20+
- Keep initial greetings simple and natural
21+
- Don't overwhelm users with multiple options or questions at the start
22+
"""
23+
24+
# Add general personalization instructions regardless of whether we have specific user context
25+
personalization_instructions = """
26+
27+
PERSONALIZATION INSTRUCTIONS:
28+
This conversation may include user context from previous interactions. When available, you MUST:
29+
30+
1. TOOL PARAMETER PERSONALIZATION
31+
- When filling or suggesting tool parameters, prioritize previously used or preferred values.
32+
- If the user has configured or used specific settings before, default to those unless they explicitly override them.
33+
- Maintain consistency with the user's preferred formats, units, or naming conventions (e.g., currency, date format, region).
34+
- Reference successful tool usage patterns from their history when proposing configurations.
35+
- When no relevant memory is found, use safe, explainable defaults and clearly state that these can be customized.
36+
37+
2. SOLUTION PRIORITIZATION
38+
- When multiple valid solutions exist, prioritize approaches that worked for this user in the past.
39+
- Reference prior successful resolutions or troubleshooting steps they responded well to.
40+
- Avoid methods the user has previously rejected, struggled with, or shown disinterest in.
41+
- Build upon their existing knowledge level — neither over-explain nor skip crucial context.
42+
- When introducing new concepts, connect them to something familiar from their past interactions.
43+
44+
3. PROACTIVE & CONTEXT-AWARE ASSISTANCE
45+
- Anticipate next steps based on user patterns — e.g., follow-up actions they usually take.
46+
- Address likely concerns early, before the user asks (e.g., known blockers, compatibility issues).
47+
- Recommend optimizations or shortcuts that align with their prior workflow.
48+
- Warn about issues they've encountered before or suggest preventive checks.
49+
- When appropriate, remind the user why a suggestion matches their past behavior ("Since you usually prefer X, I've set Y accordingly.")
50+
51+
4. TOOL USAGE GUIDANCE
52+
- Always check the conversation context and memory data before applying defaults.
53+
- Respect the user's configuration history, even across sessions or devices.
54+
- Keep data format consistency (JSON vs YAML, metric vs imperial, etc.) unless instructed otherwise.
55+
- Adapt your explanations and verbosity to their known preference — concise vs detailed.
56+
- Clearly log or communicate assumptions when inferring parameters from memory.
57+
58+
"""
59+
60+
if user_context:
61+
memory_section = f"""
62+
63+
CURRENT USER CONTEXT:
64+
{user_context}
65+
"""
66+
return base_instructions + memory_section + personalization_instructions
67+
68+
return base_instructions
69+
70+
71+
async def create_personalized_prompt(user_name: str, user_email: str = None) -> str:
72+
"""
73+
Create a comprehensive personalized prompt by retrieving user context from memory.
74+
75+
This function checks mem0 configuration internally and creates its own memory service
76+
to retrieve user context if available.
77+
78+
Args:
79+
user_name: The user's name
80+
user_email: User's email for memory service (optional)
81+
82+
Returns:
83+
Complete personalized prompt string with user context
1484
"""
85+
# Import here to avoid circular imports
86+
from app.agents.voice.automatic.services.mem0.memory import (
87+
ImprovedMem0MemoryService,
88+
)
89+
from app.core import config
90+
91+
# Check if mem0 is enabled and configured
92+
if (
93+
config.MEM0_ENABLED
94+
and user_email
95+
and user_email.strip()
96+
and config.MEM0_API_KEY
97+
and config.MEM0_API_KEY.strip()
98+
):
99+
100+
try:
101+
# Create memory service instance internally
102+
memory_params = ImprovedMem0MemoryService.InputParams()
103+
memory_service = ImprovedMem0MemoryService(
104+
api_key=config.MEM0_API_KEY,
105+
user_id=user_email,
106+
params=memory_params,
107+
)
108+
user_context_data = await memory_service.get_all_user_context()
109+
110+
if user_context_data.get("error"):
111+
return append_user_info(user_name)
112+
113+
user_context = user_context_data.get("context", "")
114+
115+
if user_context:
116+
return append_user_info(user_name, user_context)
117+
else:
118+
return append_user_info(user_name)
119+
120+
except Exception as e:
121+
print(f"Error retrieving user context: {e}")
122+
return append_user_info(user_name)
123+
else:
124+
return append_user_info(user_name)

app/agents/voice/automatic/services/mem0/memory.py

Lines changed: 58 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@
22
import datetime
33
import hashlib
44
import time
5-
from typing import Any, Dict, List
5+
from typing import Any, Dict, List, Optional
66

77
from pipecat.frames.frames import Frame, LLMMessagesFrame
88
from pipecat.processors.aggregators.openai_llm_context import (
@@ -147,6 +147,63 @@ def _handle_memory_success(self, operation: str):
147147
)
148148
self._consecutive_failures = 0
149149

150+
async def get_all_user_context(
151+
self, user_id: Optional[str] = None
152+
) -> Dict[str, Any]:
153+
"""
154+
Retrieve all user context from mem0 for LLM personalization.
155+
"""
156+
target_user_id = user_id or getattr(self, "user_id", None)
157+
158+
if not target_user_id:
159+
return {"error": "No user ID available", "context": ""}
160+
161+
# Check circuit breaker
162+
if not self._check_memory_health():
163+
logger.info("User context retrieval skipped - circuit breaker OPEN")
164+
return {"error": "Memory service unavailable", "context": ""}
165+
166+
try:
167+
# Get ALL memories for the user
168+
memories_result = self.memory_client.get_all(user_id=target_user_id)
169+
170+
# Extract memory text
171+
memories = []
172+
if isinstance(memories_result, list):
173+
for item in memories_result:
174+
if isinstance(item, dict):
175+
memory_text = (
176+
item.get("memory")
177+
or item.get("text")
178+
or item.get("content")
179+
or str(item)
180+
)
181+
if memory_text and memory_text.strip():
182+
memories.append(memory_text.strip())
183+
184+
formatted_context = (
185+
"\n".join(f"- {memory}" for memory in memories) if memories else ""
186+
)
187+
188+
# Handle successful retrieval
189+
self._handle_memory_success("context_retrieval")
190+
191+
return {
192+
"user_id": target_user_id,
193+
"context": formatted_context,
194+
"memory_count": len(memories),
195+
}
196+
197+
except Exception as e:
198+
# Handle memory failure
199+
self._handle_memory_failure("context_retrieval", e)
200+
201+
return {
202+
"error": f"Context retrieval failed: {str(e)}",
203+
"context": "",
204+
"user_id": target_user_id,
205+
}
206+
150207
def _validate_and_clean_messages(
151208
self, messages: List[Dict[str, Any]]
152209
) -> List[Dict[str, str]]:

0 commit comments

Comments
 (0)