Spaces:
No application file
No application file
Liss, Alex (NYC-HUG)
commited on
Commit
·
33f621e
1
Parent(s):
c74750f
got Feature 0 working
Browse files- docs/Phase 1/Task 1.3 Memory & Persona Implementation.md +44 -3
- gradio_agent.py +81 -7
- prompts.py +2 -0
- z_utils/zep_test.py +12 -7
docs/Phase 1/Task 1.3 Memory & Persona Implementation.md
CHANGED
@@ -189,10 +189,49 @@ The user will execute **one step at a time** and confirm each works before proce
|
|
189 |
|
190 |
---
|
191 |
|
192 |
-
### 7 │
|
193 |
|
194 |
-
*
|
195 |
-
*
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
196 |
|
197 |
---
|
198 |
|
@@ -234,6 +273,8 @@ The user will execute **one step at a time** and confirm each works before proce
|
|
234 |
| Rigorous Testing | Test changes immediately after implementation to catch issues early | Ran the application after adding the radio button to verify it works |
|
235 |
| Clear Documentation | Document design decisions and patterns | Added comments explaining why global variables are declared before functions that use them |
|
236 |
| Consistent Logging | Use consistent prefixes for log messages to aid debugging | Added prefixes like "[PERSONA CHANGE]" and "[MEMORY LOAD]" |
|
|
|
|
|
237 |
|
238 |
---
|
239 |
|
|
|
189 |
|
190 |
---
|
191 |
|
192 |
+
### 7 │ Changing App Responses to Provide User Personalization
|
193 |
|
194 |
+
* Review the context provided as Zep memoery in the zep_test.py file
|
195 |
+
* create an LLM function to summarize this information into concise and declarative content, e.g. TELL THE 49ers FAN APP how to personalize its outgoing messages to deliver exactly the kind of content and experience this fan is looking for!
|
196 |
+
* review the structure of gradio_agent.py and identify where and how the AI agent can receive the instructions to personalize, using the Minimal Surgical Changes rule
|
197 |
+
* present the plan to the user and explain your rationale in detail. Prepare to debate and be open to new ideas
|
198 |
+
* once a plan has been reviewed and approved, execute along the lines of the Appendix - First Principles in Action
|
199 |
+
|
200 |
+
**Status Update:**
|
201 |
+
✅ Successfully reviewed the Zep memory contexts for both personas:
|
202 |
+
- Casual Fan persona has surface-level knowledge and is motivated by feeling included
|
203 |
+
- Super Fan persona has detailed knowledge and is motivated by strategic understanding
|
204 |
+
✅ Created a new `get_persona_instructions()` function in gradio_agent.py to return different instructions based on the current persona
|
205 |
+
✅ Updated prompts.py to include a placeholder for persona-specific instructions
|
206 |
+
✅ Modified generate_response() to incorporate persona instructions into the agent prompt
|
207 |
+
✅ Implemented two surgical changes to enhance persona-specific behavior:
|
208 |
+
- Enhanced persona instructions with more directive language and specific examples
|
209 |
+
- Added persona tag emphasizers around instructions and in user inputs
|
210 |
+
|
211 |
+
**Implementation Details:**
|
212 |
+
1. **Made Instructions More Direct and Prescriptive**:
|
213 |
+
- Rewritten instructions using "YOU MUST" language instead of suggestions
|
214 |
+
- Added numbered lists of specific behaviors to exhibit for each persona
|
215 |
+
- Included concrete examples of how responses should look for each persona
|
216 |
+
- Added "do/don't" sections to clarify expectations
|
217 |
+
|
218 |
+
2. **Enhanced Instruction Visibility in the Agent Context**:
|
219 |
+
- Added emphasis tags around persona instructions: `[ACTIVE PERSONA: {current_persona}]`
|
220 |
+
- Added persona-specific prefix to user inputs: `[RESPOND AS {current_persona.upper()}]:`
|
221 |
+
- These small but effective changes helped ensure the instructions weren't lost in context
|
222 |
+
|
223 |
+
**Results:**
|
224 |
+
✅ Successfully implemented personalization with distinctly different responses for each persona:
|
225 |
+
- **Casual Fan responses** became shorter, used inclusive "we/our" language, included excitement markers (exclamation points), and focused on big moments and star players
|
226 |
+
- **Super Fan responses** became more detailed, used technical terminology, included structured analysis, and referenced role players alongside stars
|
227 |
+
- Example: When asked about draft news, the casual fan received a brief, excited summary focusing on star players and big moments, while the super fan received a detailed, categorized analysis with specific prospect evaluations
|
228 |
+
|
229 |
+
**Future Improvements (Backlog):**
|
230 |
+
- Further enhance personalization by integrating more facts from the Zep memory context into responses
|
231 |
+
- Create a more sophisticated prompt that explicitly references relevant facts based on the current query
|
232 |
+
- Add a mechanism to track and adapt to the user's knowledge level over time
|
233 |
+
- Implement a feedback loop where users can indicate if responses are appropriately personalized
|
234 |
+
- Explore ways to make persona-specific language settings persistent across sessions
|
235 |
|
236 |
---
|
237 |
|
|
|
273 |
| Rigorous Testing | Test changes immediately after implementation to catch issues early | Ran the application after adding the radio button to verify it works |
|
274 |
| Clear Documentation | Document design decisions and patterns | Added comments explaining why global variables are declared before functions that use them |
|
275 |
| Consistent Logging | Use consistent prefixes for log messages to aid debugging | Added prefixes like "[PERSONA CHANGE]" and "[MEMORY LOAD]" |
|
276 |
+
| Sequential Approval Workflow | Present detailed plans, wait for explicit approval on each component, implement one change at a time, and provide clear explanations of data flows | Explained how the persona instructions flow from selection to prompt generation before implementing changes |
|
277 |
+
| Surgical Diff Principle | Show only the specific changes being made rather than reprinting entire code blocks | Highlighted just the 2 key modifications to implement personalization rather than presenting a large code block |
|
278 |
|
279 |
---
|
280 |
|
gradio_agent.py
CHANGED
@@ -155,6 +155,51 @@ def get_memory(session_id):
|
|
155 |
# No memory_type parameter
|
156 |
)
|
157 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
158 |
# Create the agent prompt
|
159 |
agent_prompt = PromptTemplate.from_template(AGENT_SYSTEM_PROMPT)
|
160 |
|
@@ -226,20 +271,47 @@ def generate_response(user_input, session_id=None):
|
|
226 |
Returns:
|
227 |
dict: The full response object from the agent
|
228 |
"""
|
229 |
-
print('Starting generate_response function...')
|
230 |
-
print(f'User input: {user_input}')
|
231 |
-
print(f'Session ID: {session_id}')
|
|
|
232 |
|
233 |
if not session_id:
|
234 |
session_id = get_session_id()
|
235 |
-
print(f'Generated new session ID: {session_id}')
|
236 |
|
237 |
# Initialize memory with Zep history
|
238 |
memory = initialize_memory_from_zep(session_id)
|
239 |
|
240 |
-
#
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
241 |
session_agent_executor = AgentExecutor(
|
242 |
-
agent=
|
243 |
tools=tools,
|
244 |
verbose=True,
|
245 |
memory=memory, # Use the memory we initialized
|
@@ -253,7 +325,9 @@ def generate_response(user_input, session_id=None):
|
|
253 |
try:
|
254 |
print('Invoking session agent executor...')
|
255 |
# The agent will now have access to the loaded history
|
256 |
-
|
|
|
|
|
257 |
|
258 |
# Extract the output and format it for Streamlit
|
259 |
if isinstance(response, dict):
|
|
|
155 |
# No memory_type parameter
|
156 |
)
|
157 |
|
158 |
+
# New function to generate persona-specific instructions
|
159 |
+
def get_persona_instructions():
|
160 |
+
"""Generate personalized instructions based on current persona"""
|
161 |
+
if current_persona == "Casual Fan":
|
162 |
+
return """
|
163 |
+
PERSONA DIRECTIVE: CASUAL FAN MODE - YOU MUST FOLLOW THESE RULES
|
164 |
+
|
165 |
+
YOU MUST speak to a casual 49ers fan with surface-level knowledge. This means you MUST:
|
166 |
+
1. Keep explanations BRIEF and under 3-4 sentences whenever possible
|
167 |
+
2. Use EVERYDAY LANGUAGE instead of technical football terms
|
168 |
+
3. EMPHASIZE exciting plays, scoring, and player personalities
|
169 |
+
4. FOCUS on "big moments" and "highlight-reel plays" in your examples
|
170 |
+
5. AVOID detailed strategic analysis or technical football concepts
|
171 |
+
6. CREATE a feeling of inclusion by using "we" and "our team" language
|
172 |
+
7. INCLUDE at least one exclamation point in longer responses to convey excitement!
|
173 |
+
|
174 |
+
Casual fans don't know or care about: blocking schemes, defensive alignments, or salary cap details.
|
175 |
+
Casual fans DO care about: star players, touchdowns, big hits, and feeling connected to the team.
|
176 |
+
|
177 |
+
EXAMPLE RESPONSE FOR CASUAL FAN (about the draft):
|
178 |
+
"The 49ers did a great job finding exciting new players in the draft! They picked up a speedy receiver who could make some highlight-reel plays for us next season. The team focused on adding talent that can make an immediate impact, which is exactly what we needed!"
|
179 |
+
"""
|
180 |
+
elif current_persona == "Super Fan":
|
181 |
+
return """
|
182 |
+
PERSONA DIRECTIVE: SUPER FAN MODE - YOU MUST FOLLOW THESE RULES
|
183 |
+
|
184 |
+
YOU MUST speak to a die-hard 49ers super fan with detailed football knowledge. This means you MUST:
|
185 |
+
1. Provide DETAILED analysis that goes beyond surface-level information
|
186 |
+
2. Use SPECIFIC football terminology and scheme concepts confidently
|
187 |
+
3. REFERENCE role players and their contributions, not just star players
|
188 |
+
4. ANALYZE strategic elements of plays, drafts, and team construction
|
189 |
+
5. COMPARE current scenarios to historical team contexts when relevant
|
190 |
+
6. INCLUDE specific stats, metrics, or technical details in your analysis
|
191 |
+
7. ACKNOWLEDGE the complexity of football decisions rather than simplifying
|
192 |
+
|
193 |
+
Super fans expect: scheme-specific analysis, salary cap implications, and detailed player evaluations.
|
194 |
+
Super fans value: strategic insights, historical context, and acknowledgment of role players.
|
195 |
+
|
196 |
+
EXAMPLE RESPONSE FOR SUPER FAN (about the draft):
|
197 |
+
"The 49ers' draft strategy reflected their commitment to Shanahan's outside zone running scheme while addressing defensive depth issues. Their 3rd round selection provides versatility in the secondary with potential for both slot corner and safety roles, similar to how they've historically valued positional flexibility. The late-round offensive line selections show a continuing emphasis on athletic linemen who excel in zone blocking rather than power schemes, though they'll need development in pass protection techniques to become three-down players."
|
198 |
+
"""
|
199 |
+
else:
|
200 |
+
# Default case - should not happen, but provides a fallback
|
201 |
+
return ""
|
202 |
+
|
203 |
# Create the agent prompt
|
204 |
agent_prompt = PromptTemplate.from_template(AGENT_SYSTEM_PROMPT)
|
205 |
|
|
|
271 |
Returns:
|
272 |
dict: The full response object from the agent
|
273 |
"""
|
274 |
+
print('[RESPONSE GEN] Starting generate_response function...')
|
275 |
+
print(f'[RESPONSE GEN] User input: {user_input}')
|
276 |
+
print(f'[RESPONSE GEN] Session ID: {session_id}')
|
277 |
+
print(f'[RESPONSE GEN] Current persona: {current_persona}')
|
278 |
|
279 |
if not session_id:
|
280 |
session_id = get_session_id()
|
281 |
+
print(f'[RESPONSE GEN] Generated new session ID: {session_id}')
|
282 |
|
283 |
# Initialize memory with Zep history
|
284 |
memory = initialize_memory_from_zep(session_id)
|
285 |
|
286 |
+
# DEBUG: Print conversation memory content
|
287 |
+
print(f"[DEBUG MEMORY] Memory type: {type(memory)}")
|
288 |
+
if hasattr(memory, 'chat_memory') and hasattr(memory.chat_memory, 'messages'):
|
289 |
+
print(f"[DEBUG MEMORY] Number of messages: {len(memory.chat_memory.messages)}")
|
290 |
+
for idx, msg in enumerate(memory.chat_memory.messages):
|
291 |
+
print(f"[DEBUG MEMORY] Message {idx}: {msg.type} - {msg.content[:100]}...")
|
292 |
+
|
293 |
+
# Get persona-specific instructions for the prompt
|
294 |
+
persona_instructions = get_persona_instructions()
|
295 |
+
print(f'[RESPONSE GEN] Using persona instructions for: {current_persona}')
|
296 |
+
|
297 |
+
# DEBUG: Print the persona instructions being used
|
298 |
+
print(f"[DEBUG INSTRUCTIONS] Persona instructions:\n{persona_instructions}")
|
299 |
+
|
300 |
+
# Create a personalized prompt by modifying the template with the current persona instructions
|
301 |
+
# Keep the original prompt format but insert the persona instructions at the appropriate place
|
302 |
+
persona_tag = f"[ACTIVE PERSONA: {current_persona}]"
|
303 |
+
highlighted_instructions = f"{persona_tag}\n\n{persona_instructions}\n\n{persona_tag}"
|
304 |
+
agent_system_prompt_with_persona = AGENT_SYSTEM_PROMPT.replace(
|
305 |
+
"{persona_instructions}", highlighted_instructions
|
306 |
+
)
|
307 |
+
personalized_prompt = PromptTemplate.from_template(agent_system_prompt_with_persona)
|
308 |
+
|
309 |
+
# Create a personalized agent with the updated prompt
|
310 |
+
personalized_agent = create_react_agent(agent_llm, tools, personalized_prompt)
|
311 |
+
|
312 |
+
# Create an agent executor with memory for this session and personalized prompt
|
313 |
session_agent_executor = AgentExecutor(
|
314 |
+
agent=personalized_agent,
|
315 |
tools=tools,
|
316 |
verbose=True,
|
317 |
memory=memory, # Use the memory we initialized
|
|
|
325 |
try:
|
326 |
print('Invoking session agent executor...')
|
327 |
# The agent will now have access to the loaded history
|
328 |
+
persona_prefix = f"[RESPOND AS {current_persona.upper()}]: "
|
329 |
+
augmented_input = f"{persona_prefix}{user_input}"
|
330 |
+
response = session_agent_executor.invoke({"input": augmented_input})
|
331 |
|
332 |
# Extract the output and format it for Streamlit
|
333 |
if isinstance(response, dict):
|
prompts.py
CHANGED
@@ -6,6 +6,8 @@ Do not answer any questions that do not relate to the 49ers, players, or fans.
|
|
6 |
|
7 |
Do not answer any questions using your pre-trained knowledge, only use the information provided in the context.
|
8 |
|
|
|
|
|
9 |
**IMPORTANT RESPONSE FORMATTING:**
|
10 |
- When you use a tool that generates a visual component (like "Game Recap" or "Player Information Search"), your final text answer should *only* contain the summary text.
|
11 |
- Do NOT include Markdown for images (like ``), links, or other elements that are already visually represented by the component. The visual component will be displayed separately.
|
|
|
6 |
|
7 |
Do not answer any questions using your pre-trained knowledge, only use the information provided in the context.
|
8 |
|
9 |
+
{persona_instructions}
|
10 |
+
|
11 |
**IMPORTANT RESPONSE FORMATTING:**
|
12 |
- When you use a tool that generates a visual component (like "Game Recap" or "Player Information Search"), your final text answer should *only* contain the summary text.
|
13 |
- Do NOT include Markdown for images (like ``), links, or other elements that are already visually represented by the component. The visual component will be displayed separately.
|
z_utils/zep_test.py
CHANGED
@@ -4,6 +4,7 @@ This follows step 2 of Task 1.3 Memory & Persona Implementation.
|
|
4 |
"""
|
5 |
import os
|
6 |
import json
|
|
|
7 |
from dotenv import load_dotenv
|
8 |
from zep_cloud.client import Zep
|
9 |
from langchain_core.messages import HumanMessage, AIMessage
|
@@ -19,11 +20,6 @@ if not ZEP_API_KEY:
|
|
19 |
# Initialize Zep client
|
20 |
zep = Zep(api_key=ZEP_API_KEY)
|
21 |
|
22 |
-
# Use one of the session IDs from the task document
|
23 |
-
# Casual fan: 241b3478c7634492abee9f178b5341cb
|
24 |
-
# Super fan: dedcf5cb0d71475f976f4f66d98d6400
|
25 |
-
SESSION_ID = "241b3478c7634492abee9f178b5341cb" # Using Casual fan session ID
|
26 |
-
|
27 |
def retrieve_chat_history(session_id):
|
28 |
"""
|
29 |
Retrieve chat history for a specific session from Zep.
|
@@ -71,10 +67,19 @@ def get_zep_history(session_id):
|
|
71 |
return []
|
72 |
|
73 |
def main():
|
74 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
75 |
|
76 |
# Get the memory for the session
|
77 |
-
memory = retrieve_chat_history(
|
78 |
|
79 |
if memory:
|
80 |
print("\n===== MEMORY CONTEXT =====")
|
|
|
4 |
"""
|
5 |
import os
|
6 |
import json
|
7 |
+
import argparse
|
8 |
from dotenv import load_dotenv
|
9 |
from zep_cloud.client import Zep
|
10 |
from langchain_core.messages import HumanMessage, AIMessage
|
|
|
20 |
# Initialize Zep client
|
21 |
zep = Zep(api_key=ZEP_API_KEY)
|
22 |
|
|
|
|
|
|
|
|
|
|
|
23 |
def retrieve_chat_history(session_id):
|
24 |
"""
|
25 |
Retrieve chat history for a specific session from Zep.
|
|
|
67 |
return []
|
68 |
|
69 |
def main():
|
70 |
+
# Set up command line argument parsing
|
71 |
+
parser = argparse.ArgumentParser(description='Retrieve chat history from Zep')
|
72 |
+
parser.add_argument('--session_id', type=str,
|
73 |
+
default="241b3478c7634492abee9f178b5341cb",
|
74 |
+
help='Session ID to retrieve (default: Casual Fan)')
|
75 |
+
args = parser.parse_args()
|
76 |
+
|
77 |
+
session_id = args.session_id
|
78 |
+
|
79 |
+
print(f"Retrieving chat history for session ID: {session_id}")
|
80 |
|
81 |
# Get the memory for the session
|
82 |
+
memory = retrieve_chat_history(session_id)
|
83 |
|
84 |
if memory:
|
85 |
print("\n===== MEMORY CONTEXT =====")
|