ViolaMeier commited on
Commit
0c26c6f
·
1 Parent(s): b80f4f4

feat: send-demand node to generate mail, if information are missing

Browse files
customer_support.py CHANGED
@@ -3,6 +3,7 @@ from langchain_anthropic import ChatAnthropic
3
  from langgraph.graph import StateGraph, START, END
4
 
5
  from src.edges.check_agent_handling import route_agent_processing
 
6
  from src.models import TicketState, create_ticket_state
7
  from src.nodes.create_ticket import create_ticket
8
  from src.nodes.extract_informations import extract_information
@@ -10,6 +11,7 @@ from src.nodes.forward_human_agent import forward_to_human_agent
10
  from src.nodes.generate_response import generate_response
11
  from src.nodes.identify_ticket_typ import identify_ticket_type
12
  from src.nodes.identify_user import identify_user
 
13
  from src.nodes.read_mail import read_email
14
  from src.nodes.solve_ticket_agent import solve_ticket_agent
15
  from src.nodes.separate_topics import separate_topics
@@ -25,6 +27,7 @@ ticket_graph = StateGraph(TicketState)
25
  ticket_graph.add_node("read_email", read_email)
26
  ticket_graph.add_node("extract_information", extract_information)
27
  ticket_graph.add_node("identify_user", identify_user)
 
28
  ticket_graph.add_node("separate_topics", separate_topics)
29
  ticket_graph.add_node("identify_ticket_type", identify_ticket_type)
30
  ticket_graph.add_node("create_ticket", create_ticket)
@@ -35,7 +38,17 @@ ticket_graph.add_node("generate_response", generate_response)
35
  ticket_graph.add_edge(START, "read_email")
36
  ticket_graph.add_edge("read_email", "extract_information")
37
  ticket_graph.add_edge("extract_information", "identify_user")
38
- ticket_graph.add_edge("identify_user", "separate_topics")
 
 
 
 
 
 
 
 
 
 
39
  ticket_graph.add_edge("separate_topics", "identify_ticket_type")
40
  ticket_graph.add_edge("identify_ticket_type", "create_ticket")
41
 
@@ -56,44 +69,43 @@ compiled_graph = ticket_graph.compile()
56
 
57
  compiled_graph.get_graph().draw_mermaid_png(output_file_path="graph.png")
58
 
59
- def render_separated_topics(json_string):
60
- try:
61
- json_data = json.loads(json_string) # JSON-String → Python-Dict
62
- except json.JSONDecodeError:
63
- return "<p style='color:red;'>Ungültiges JSON-Format!</p>"
64
-
65
- # Ensure there's a 'topics' key in the parsed data
66
- if "topics" not in json_data:
67
- return "<p style='color:red;'>Keine Themen im JSON gefunden!</p>"
68
-
69
- topics = json_data["topics"]
70
 
71
  # HTML structure for the topics
72
  html = """
73
- <div style="display: flex; flex-direction: column; gap: 10px; color: white; ">
74
  """
75
 
76
  # Loop through the topics and build HTML for each
77
  for topic in topics:
78
- # Default to empty string if key doesn't exist
79
- topic_num = str(topic.get('topic_num', ''))
80
- user_name = str(topic.get('user_name', ''))
81
- topic_title = str(topic.get('topic', ''))
82
- topic_description = str(topic.get('topic_description', ''))
83
- user_id = str(topic.get('user_id', ''))
84
-
85
  # Build HTML for each topic
86
  html += f"""
87
  <div style="border: 1px solid #e4e4e7; border-radius: 8px; padding: 20px; background-color: #fafafa;">
88
- <h2 style="margin-top: 0; color: #27272a;">Ticket Number: {topic_num}</h2>
89
 
90
  <div style="margin-bottom: 15px; font-size: 14px;">
91
- <strong>Name: </strong> {user_name} <br>
92
  <strong>User ID: </strong> {user_id} <br>
93
- </div>
 
 
94
 
95
- <div style="margin-bottom: 15px;">
96
- <strong style="font-size: 16px; color: #27272a;">Topic: </strong> <span style="font-size: 16px;">{topic_title}</span><br>
97
  </div>
98
 
99
  <div style="margin-top: 10px;">
@@ -122,7 +134,8 @@ def run_graph(sender, subject, body):
122
  state = create_ticket_state()
123
  state["email"] = email
124
  result = compiled_graph.invoke(state)
125
- return render_separated_topics(result['extracted_topics']), result['response']
 
126
 
127
  #return result['extracted_topics'] result['response'], result['ticket_type'], result['user_id'], result['extracted_topics'], result['extracted_information'], result
128
 
@@ -174,18 +187,20 @@ if __name__ == '__main__':
174
  # Output components
175
  with gr.Row():
176
  with gr.Tab("Tickets"):
177
- extracted_topics = gr.HTML(label="Separated Topics")
178
  with gr.Tab("Response"):
179
  final_response_output = gr.Textbox(label="E-Mail Response",
180
  lines=10,
181
  max_lines=20,
182
  interactive=True)
 
 
183
 
184
  # Click submit button
185
  btn_1.click(
186
  fn=run_graph,
187
  inputs=[sender_input, subject_input, body_input],
188
- outputs=[extracted_topics, final_response_output],
189
  )
190
 
191
  # Click delete button
@@ -205,7 +220,7 @@ if __name__ == '__main__':
205
  gr.Examples(
206
  fn=run_graph,
207
  inputs=[sender_input, subject_input, body_input],
208
- outputs=[extracted_topics, final_response_output],
209
  examples = examples,
210
  )
211
 
 
3
  from langgraph.graph import StateGraph, START, END
4
 
5
  from src.edges.check_agent_handling import route_agent_processing
6
+ from src.edges.check_user_identification import verify_user
7
  from src.models import TicketState, create_ticket_state
8
  from src.nodes.create_ticket import create_ticket
9
  from src.nodes.extract_informations import extract_information
 
11
  from src.nodes.generate_response import generate_response
12
  from src.nodes.identify_ticket_typ import identify_ticket_type
13
  from src.nodes.identify_user import identify_user
14
+ from src.nodes.send_demand import send_demand
15
  from src.nodes.read_mail import read_email
16
  from src.nodes.solve_ticket_agent import solve_ticket_agent
17
  from src.nodes.separate_topics import separate_topics
 
27
  ticket_graph.add_node("read_email", read_email)
28
  ticket_graph.add_node("extract_information", extract_information)
29
  ticket_graph.add_node("identify_user", identify_user)
30
+ ticket_graph.add_node("send_demand", send_demand)
31
  ticket_graph.add_node("separate_topics", separate_topics)
32
  ticket_graph.add_node("identify_ticket_type", identify_ticket_type)
33
  ticket_graph.add_node("create_ticket", create_ticket)
 
38
  ticket_graph.add_edge(START, "read_email")
39
  ticket_graph.add_edge("read_email", "extract_information")
40
  ticket_graph.add_edge("extract_information", "identify_user")
41
+
42
+ ticket_graph.add_conditional_edges(
43
+ "identify_user",
44
+ verify_user,
45
+ {
46
+ True: "separate_topics",
47
+ False: "send_demand"
48
+ }
49
+ )
50
+
51
+ ticket_graph.add_edge("send_demand", END)
52
  ticket_graph.add_edge("separate_topics", "identify_ticket_type")
53
  ticket_graph.add_edge("identify_ticket_type", "create_ticket")
54
 
 
69
 
70
  compiled_graph.get_graph().draw_mermaid_png(output_file_path="graph.png")
71
 
72
+ def render_separated_topics(topics, user_information, user_id):
73
+ if user_id is None:
74
+ return ""
75
+
76
+ # Get user information
77
+ first_name = user_information['first_name']
78
+ last_name = user_information['last_name']
79
+ address = user_information['address']
80
+ email = user_information['email']
81
+ phone = user_information['phone']
 
82
 
83
  # HTML structure for the topics
84
  html = """
85
+ <div style="display: flex; flex-direction: column; gap: 10px; color: white;">
86
  """
87
 
88
  # Loop through the topics and build HTML for each
89
  for topic in topics:
90
+ # Get information for each topic
91
+ title = topic['title']
92
+ topic_num = topic['topic_num']
93
+ topic_description = topic['topic_description']
94
+ print( " WITH TYPE: ", type(topic))
95
+ print("TOPIC: " ,topic['title'])
96
+
97
  # Build HTML for each topic
98
  html += f"""
99
  <div style="border: 1px solid #e4e4e7; border-radius: 8px; padding: 20px; background-color: #fafafa;">
100
+ <h2 style="margin-top: 0; color: #27272a;">Ticket {topic_num}: {title} </h2>
101
 
102
  <div style="margin-bottom: 15px; font-size: 14px;">
103
+ <strong>Name: </strong> {first_name} {last_name} <br>
104
  <strong>User ID: </strong> {user_id} <br>
105
+ <strong>Address: </strong> {address} <br>
106
+ <strong>Phone: </strong> {phone} <br>
107
+ <strong>Email: </strong> {email} <br>
108
 
 
 
109
  </div>
110
 
111
  <div style="margin-top: 10px;">
 
134
  state = create_ticket_state()
135
  state["email"] = email
136
  result = compiled_graph.invoke(state)
137
+ return render_separated_topics(result['extracted_topics'], result['extracted_information'],
138
+ result['user_id']), result, result['response']
139
 
140
  #return result['extracted_topics'] result['response'], result['ticket_type'], result['user_id'], result['extracted_topics'], result['extracted_information'], result
141
 
 
187
  # Output components
188
  with gr.Row():
189
  with gr.Tab("Tickets"):
190
+ extracted_topics = gr.HTML(label="Separated Topics")
191
  with gr.Tab("Response"):
192
  final_response_output = gr.Textbox(label="E-Mail Response",
193
  lines=10,
194
  max_lines=20,
195
  interactive=True)
196
+ with gr.Tab("Full Agent State"):
197
+ full_json_output = gr.JSON(label="Full Agent State")
198
 
199
  # Click submit button
200
  btn_1.click(
201
  fn=run_graph,
202
  inputs=[sender_input, subject_input, body_input],
203
+ outputs=[extracted_topics,full_json_output, final_response_output],
204
  )
205
 
206
  # Click delete button
 
220
  gr.Examples(
221
  fn=run_graph,
222
  inputs=[sender_input, subject_input, body_input],
223
+ outputs=[extracted_topics, full_json_output, final_response_output],
224
  examples = examples,
225
  )
226
 
graph.png CHANGED
src/data/users.py CHANGED
@@ -5,13 +5,15 @@ mock_user = {
5
  "address": "Friedrichstraße 123, 10117 Berlin",
6
  "email": "lena.meier@example.de",
7
  "phone": "+49 30 12345678",
 
8
  },
9
  "215493": {
10
  "first_name": "Markus",
11
- "last_name": "Markus",
12
  "address": "Prenzlauer Allee 45, 10405 Berlin",
13
  "email": "markus.keller@example.de",
14
  "phone": "+49 30 23456789",
 
15
  },
16
  "309572": {
17
  "first_name": "Sophie",
@@ -19,6 +21,7 @@ mock_user = {
19
  "address": "Karl-Marx-Straße 3, 12043 Berlin",
20
  "email": "sophie.brunner@example.de",
21
  "phone": "+49 30 34567890",
 
22
  },
23
  "407183": {
24
  "first_name": "Nico",
@@ -26,6 +29,7 @@ mock_user = {
26
  "address": "Kurfürstendamm 22, 10719 Berlin",
27
  "email": "nico.baumann@example.de",
28
  "phone": "+49 30 45678901",
 
29
  },
30
  "512948": {
31
  "first_name": "Mira",
@@ -33,6 +37,7 @@ mock_user = {
33
  "address": "Alexanderplatz 18, 10178 Berlin",
34
  "email": "mira.schmid@example.de",
35
  "phone": "+49 30 56789012",
 
36
  },
37
  "613405": {
38
  "first_name": "Daniel",
@@ -40,6 +45,7 @@ mock_user = {
40
  "address": "Frankfurter Allee 55, 10247 Berlin",
41
  "email": "daniel.fischer@example.de",
42
  "phone": "+49 30 67890123",
 
43
  },
44
  "728490": {
45
  "first_name": "Sara",
@@ -54,6 +60,7 @@ mock_user = {
54
  "address": "Hauptstraße 23, 10827 Berlin",
55
  "email": "julian.vogel@example.de",
56
  "phone": "+49 30 89012345",
 
57
  },
58
  "945128": {
59
  "first_name": "Laura",
@@ -61,6 +68,7 @@ mock_user = {
61
  "address": "Torstraße 6, 10119 Berlin",
62
  "email": "laura.maurer@example.de",
63
  "phone": "+49 30 90123456",
 
64
  },
65
  "159348": {
66
  "first_name": "David",
@@ -68,6 +76,7 @@ mock_user = {
68
  "address": "Potsdamer Straße 20, 10785 Berlin",
69
  "email": "david.graf@example.de",
70
  "phone": "+49 30 91234567",
 
71
  },
72
  "263759": {
73
  "first_name": "Anna",
@@ -75,6 +84,7 @@ mock_user = {
75
  "address": "Wilhelmstraße 45, 10963 Berlin",
76
  "email": "anna.weber@example.de",
77
  "phone": "+49 30 12349876",
 
78
  },
79
  "375820": {
80
  "first_name": "Lukas",
@@ -82,6 +92,7 @@ mock_user = {
82
  "address": "Müllerstraße 85, 13349 Berlin",
83
  "email": "lukas.steiner@example.de",
84
  "phone": "+49 30 23498765",
 
85
  },
86
  "481267": {
87
  "first_name": "Nina",
@@ -89,6 +100,7 @@ mock_user = {
89
  "address": "Oranienstraße 1, 10999 Berlin",
90
  "email": "nina.marti@example.de",
91
  "phone": "+49 30 34598765",
 
92
  },
93
  "596481": {
94
  "first_name": "Tim",
@@ -96,5 +108,6 @@ mock_user = {
96
  "address": "Gneisenaustraße 20, 10961 Berlin",
97
  "email": "tim.arnold@example.de",
98
  "phone": "+49 30 45612378",
 
99
  }
100
  }
 
5
  "address": "Friedrichstraße 123, 10117 Berlin",
6
  "email": "lena.meier@example.de",
7
  "phone": "+49 30 12345678",
8
+ "contract_number": "4821/9384721",
9
  },
10
  "215493": {
11
  "first_name": "Markus",
12
+ "last_name": "Keller",
13
  "address": "Prenzlauer Allee 45, 10405 Berlin",
14
  "email": "markus.keller@example.de",
15
  "phone": "+49 30 23456789",
16
+ "contract_number": "1740/2847563",
17
  },
18
  "309572": {
19
  "first_name": "Sophie",
 
21
  "address": "Karl-Marx-Straße 3, 12043 Berlin",
22
  "email": "sophie.brunner@example.de",
23
  "phone": "+49 30 34567890",
24
+ "contract_number": "0100/12576240",
25
  },
26
  "407183": {
27
  "first_name": "Nico",
 
29
  "address": "Kurfürstendamm 22, 10719 Berlin",
30
  "email": "nico.baumann@example.de",
31
  "phone": "+49 30 45678901",
32
+ "contract_number": "3652/7482956",
33
  },
34
  "512948": {
35
  "first_name": "Mira",
 
37
  "address": "Alexanderplatz 18, 10178 Berlin",
38
  "email": "mira.schmid@example.de",
39
  "phone": "+49 30 56789012",
40
+ "contract_number": "8093/6271830",
41
  },
42
  "613405": {
43
  "first_name": "Daniel",
 
45
  "address": "Frankfurter Allee 55, 10247 Berlin",
46
  "email": "daniel.fischer@example.de",
47
  "phone": "+49 30 67890123",
48
+ "contract_number": "2517/9038472",
49
  },
50
  "728490": {
51
  "first_name": "Sara",
 
60
  "address": "Hauptstraße 23, 10827 Berlin",
61
  "email": "julian.vogel@example.de",
62
  "phone": "+49 30 89012345",
63
+ "contract_number": "0100/12576250",
64
  },
65
  "945128": {
66
  "first_name": "Laura",
 
68
  "address": "Torstraße 6, 10119 Berlin",
69
  "email": "laura.maurer@example.de",
70
  "phone": "+49 30 90123456",
71
+ "contract_number": "0100/12576260",
72
  },
73
  "159348": {
74
  "first_name": "David",
 
76
  "address": "Potsdamer Straße 20, 10785 Berlin",
77
  "email": "david.graf@example.de",
78
  "phone": "+49 30 91234567",
79
+ "contract_number": "0100/12576270",
80
  },
81
  "263759": {
82
  "first_name": "Anna",
 
84
  "address": "Wilhelmstraße 45, 10963 Berlin",
85
  "email": "anna.weber@example.de",
86
  "phone": "+49 30 12349876",
87
+ "contract_number": "0100/12576280",
88
  },
89
  "375820": {
90
  "first_name": "Lukas",
 
92
  "address": "Müllerstraße 85, 13349 Berlin",
93
  "email": "lukas.steiner@example.de",
94
  "phone": "+49 30 23498765",
95
+ "contract_number": "0100/12576290",
96
  },
97
  "481267": {
98
  "first_name": "Nina",
 
100
  "address": "Oranienstraße 1, 10999 Berlin",
101
  "email": "nina.marti@example.de",
102
  "phone": "+49 30 34598765",
103
+ "contract_number": "0100/12576300",
104
  },
105
  "596481": {
106
  "first_name": "Tim",
 
108
  "address": "Gneisenaustraße 20, 10961 Berlin",
109
  "email": "tim.arnold@example.de",
110
  "phone": "+49 30 45612378",
111
+ "contract_number": "0100/12576310",
112
  }
113
  }
src/edges/check_user_identification.py ADDED
@@ -0,0 +1,5 @@
 
 
 
 
 
 
1
+ from src.models import TicketState
2
+
3
+ def verify_user(state: TicketState) -> bool:
4
+ """Verify if the user is identified"""
5
+ return state.get('user_id') is not None
src/models.py CHANGED
@@ -14,9 +14,11 @@ class ExtractedInformation(TypedDict):
14
 
15
  # TODO: ticket_type should be contained in extractedTopics => from each topic create a ticked
16
  class ExtractedTopic(TypedDict):
 
17
  topic_num: int | None = None
18
  topic: str | None = None
19
- user_id: str | None = None
 
20
 
21
 
22
 
@@ -58,9 +60,8 @@ def create_ticket_state() -> TicketState:
58
 
59
  extracted_topics: List[ExtractedTopic] = [
60
  ExtractedTopic(
 
61
  topic_num=None,
62
- user_name=None,
63
- user_id=None,
64
  topic=None,
65
  topic_description=None
66
  )
@@ -71,5 +72,5 @@ def create_ticket_state() -> TicketState:
71
  user_id=None,
72
  extracted_topics=extracted_topics,
73
  ticket_type=None,
74
- messages=[]
75
  )
 
14
 
15
  # TODO: ticket_type should be contained in extractedTopics => from each topic create a ticked
16
  class ExtractedTopic(TypedDict):
17
+ title: str | None = None
18
  topic_num: int | None = None
19
  topic: str | None = None
20
+ topic_description: str | None = None
21
+
22
 
23
 
24
 
 
60
 
61
  extracted_topics: List[ExtractedTopic] = [
62
  ExtractedTopic(
63
+ title=None,
64
  topic_num=None,
 
 
65
  topic=None,
66
  topic_description=None
67
  )
 
72
  user_id=None,
73
  extracted_topics=extracted_topics,
74
  ticket_type=None,
75
+ messages=[],
76
  )
src/nodes/generate_response.py CHANGED
@@ -25,7 +25,7 @@ def generate_response(state: TicketState) -> TicketState:
25
  response = model.invoke(messages)
26
 
27
  # Simple logic to parse the response (in a real app, you'd want more robust parsing)
28
- response_text = response.content.lower()
29
 
30
  print(f"Generated response:\n\n {response_text}")
31
 
 
25
  response = model.invoke(messages)
26
 
27
  # Simple logic to parse the response (in a real app, you'd want more robust parsing)
28
+ response_text = response.content
29
 
30
  print(f"Generated response:\n\n {response_text}")
31
 
src/nodes/identify_user.py CHANGED
@@ -11,17 +11,33 @@ def identify_user(state: TicketState):
11
  address = (extracted_information.get("address") or "").lower()
12
  email = (extracted_information.get("email") or "").lower()
13
  phone = (extracted_information.get("phone") or "").strip()
 
14
 
15
- # Check if the user exists in the mock database with fuzzy matching
16
- for user_id, user_info in mock_user.items():
17
- if (
18
- (user_info.get("first_name") or "").lower() == first_name or
19
- (user_info.get("last_name") or "").lower() == last_name or
20
- (user_info.get("address") or "").lower() == address or
21
- (user_info.get("email") or "").lower() == email or
22
- (user_info.get("phone") or "").strip() == phone
23
- ):
24
- state["user_id"] = user_id
25
- break # Stop at the first match
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
26
 
27
  return state
 
11
  address = (extracted_information.get("address") or "").lower()
12
  email = (extracted_information.get("email") or "").lower()
13
  phone = (extracted_information.get("phone") or "").strip()
14
+ contract_number = (extracted_information.get("contract_number") or "").strip()
15
 
16
+
17
+ print("IS USER VERIFIED? ", first_name, last_name, address, contract_number)
18
+ # Check if we have enough information to even try
19
+ has_name_info = all([first_name, last_name])
20
+ has_contract_number = bool(contract_number)
21
+
22
+ identified_user_id = None
23
+
24
+ if has_name_info or has_contract_number:
25
+ for user_id, user_info in mock_user.items():
26
+ if (
27
+ (has_name_info and
28
+ (user_info.get("first_name") or "").lower() == first_name and
29
+ (user_info.get("last_name") or "").lower() == last_name)
30
+ or
31
+ (has_contract_number and
32
+ (user_info.get("contract_number") or "").strip() == contract_number)
33
+ ):
34
+ identified_user_id = user_id
35
+ break # Stop at the first match
36
+
37
+ if identified_user_id:
38
+ print("IDENTIFIED USER:", user_id)
39
+ state["user_id"] = identified_user_id
40
+
41
+ print("IS USER VERIFIED? ", state.get("user_id"))
42
 
43
  return state
src/nodes/send_demand.py ADDED
@@ -0,0 +1,48 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import json
2
+ import logging
3
+
4
+ from langchain_core.messages import HumanMessage
5
+
6
+ from src.foundation_models import model
7
+ from src.models import TicketState
8
+
9
+ def send_demand(state: TicketState) -> TicketState:
10
+ """Generate a demand to the user if information are missing, using a language model. """
11
+
12
+ email = state["email"]
13
+
14
+
15
+ # prompt for LLM
16
+ prompt = f"""
17
+ Du bist ein Kundenservice-Mitarbeiter in der Immobilienbranche. Analysiere die folgende E-Mail und verfasse eine freundliche und höfliche Antwort an den Absender.
18
+ Teile der Person mit, dass zur Identifikation und Bearbeitung ihres Anliegens noch wichtige Angaben fehlen – insbesondere die Vertragsnummer.
19
+ Bitte fordere diese Information klar, aber kundenorientiert an.
20
+ Die Antwort soll kurz, klar und professionell sein. Drücke aus, dass wir gerne weiterhelfen möchten und an einer schnellen Lösung interessiert sind.
21
+
22
+ ## E-Mail:
23
+ Betreff: {email['subject']}
24
+ Inhalt: {email['body']}
25
+
26
+ Bitte antworte nur mit dem Text der Antwort-E-Mail.
27
+ Achte dabei unbedingt auf korrekte Groß- und Kleinschreibung sowie auf eine grammatikalisch saubere Formulierung.
28
+ """
29
+ # Call the LLM
30
+ messages = [HumanMessage(content=prompt)]
31
+ response = model.invoke(messages)
32
+
33
+ # Simple logic to parse the response (in a real app, you'd want more robust parsing)
34
+ response_text = response.content
35
+ print(f"Generated demand:\n\n {response_text}")
36
+
37
+ state['response'] = response_text
38
+
39
+ # Update messages for tracking
40
+ new_messages = state.get("messages", []) + [
41
+ {"role": "user", "content": prompt},
42
+ {"role": "assistant", "content": response.content}
43
+ ]
44
+
45
+ state["messages"] = new_messages
46
+
47
+ # Return state updates
48
+ return state
src/nodes/separate_topics.py CHANGED
@@ -12,28 +12,44 @@ def separate_topics(state: TicketState):
12
  email = state["email"]
13
  first_name = state["extracted_information"]["first_name"]
14
  last_name = state["extracted_information"]["last_name"]
 
 
 
15
  user_id = state["user_id"]
16
 
17
  # promp for the LLM
 
 
 
 
 
 
 
18
  prompt = f"""
19
- As a real estate customer support agent, analyze this email and extract different topics.
20
- Return the different topics as a list.
21
 
22
- ## Each JSON object should have the following structure:
23
  {{
24
- "topic_num": int,
25
- "user_name": "{first_name} {last_name}",
26
- "user_id": "{user_id}",
27
- "topic": str,
28
- "topic_description": str
29
  }}
30
 
31
- ## Email:
32
- From: {email['sender']}
33
- Subject: {email['subject']}
34
- Body: {email['body']}
35
-
36
- Make sure that the topics are well separated and the description holds all relevant information. Response only with a JSON!
 
 
 
 
 
 
 
37
  """
38
 
39
  print(state)
@@ -42,7 +58,7 @@ def separate_topics(state: TicketState):
42
  messages = [HumanMessage(content=prompt)]
43
  response = model.invoke(messages)
44
 
45
- response_text = response.content.lower()
46
 
47
  # Parse the LLM's response
48
  try:
@@ -51,7 +67,7 @@ def separate_topics(state: TicketState):
51
  logging.error(f"Failed to parse the response: {response_text}")
52
  return {}
53
 
54
- state['extracted_topics'] = response_text
55
 
56
  # Update messages for tracking
57
  new_messages = state.get("messages", []) + [
 
12
  email = state["email"]
13
  first_name = state["extracted_information"]["first_name"]
14
  last_name = state["extracted_information"]["last_name"]
15
+ address = state["extracted_information"]["address"]
16
+ mail = state["extracted_information"]["email"]
17
+ phone = state["extracted_information"]["phone"]
18
  user_id = state["user_id"]
19
 
20
  # promp for the LLM
21
+ """
22
+ "user_name": {first_name} {last_name},
23
+ "address": {address},
24
+ "email": {mail}
25
+ "phone": {phone}
26
+ "user_id": {user_id},
27
+ """
28
  prompt = f"""
29
+ Du bist ein Kundenservice-Mitarbeiter in der Immobilienbranche. Analysiere die folgende E-Mail und extrahiere daraus unterschiedliche Themen (Topics).
30
+ Gib die erkannten Themen als Liste von JSON-Objekten zurück.
31
 
32
+ ## Jedes JSON-Objekt soll folgende Struktur haben:
33
  {{
34
+ "title": str, # Kurzer, prägnanter Titel für das Thema
35
+ "topic_num": int, # Laufende Nummer des Themas innerhalb der E-Mail
36
+ "topic": str, # Kurzbeschreibung des Themas
37
+ "topic_description": str # Ausführliche Beschreibung mit allen relevanten Informationen
 
38
  }}
39
 
40
+ ## E-Mail:
41
+ Von: {email['sender']}
42
+ Betreff: {email['subject']}
43
+ Inhalt: {email['body']}
44
+
45
+ Stelle sicher, dass die Themen klar voneinander getrennt sind, sodass zusammengehörige Anliegen in einem gemeinsamen Ticket erscheinen.
46
+ Es sollen keine separaten Topics für Themen erstellt werden, die zusammengefasst werden können – außer sie betreffen klar unterschiedliche Verantwortungsbereiche.
47
+ Die Beschreibung muss alle relevanten Informationen enthalten.
48
+ Aus jeder E-Mail muss mindestens ein Topic erstellt werden.
49
+ Falls kein eindeutiges Thema erkennbar ist, soll die Original-E-Mail mit einem passenden Titel als Topic übernommen werden.
50
+
51
+ Antworte ausschließlich in einer Sprache (Deutsch oder Englisch – abhängig vom E-Mail-Inhalt) und achte auf korrekte Rechtschreibung sowie Gross- und Kleinschreibung.
52
+ Gib ausschließlich eine Liste von JSON-Objekten als Antwort zurück.
53
  """
54
 
55
  print(state)
 
58
  messages = [HumanMessage(content=prompt)]
59
  response = model.invoke(messages)
60
 
61
+ response_text = response.content
62
 
63
  # Parse the LLM's response
64
  try:
 
67
  logging.error(f"Failed to parse the response: {response_text}")
68
  return {}
69
 
70
+ state['extracted_topics'] = extracted_topics
71
 
72
  # Update messages for tracking
73
  new_messages = state.get("messages", []) + [