Tan Hong Ming commited on
Commit
0559e8e
·
1 Parent(s): f1e559a

tool using

Browse files
Files changed (1) hide show
  1. app.py +172 -13
app.py CHANGED
@@ -1,4 +1,6 @@
1
  import os
 
 
2
  import gradio as gr
3
  from openai import OpenAI
4
 
@@ -48,10 +50,47 @@ Rules:
48
  - Keep the draft reply professional and appropriate for business use
49
  """
50
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
51
  SAMPLE_EMAILS = {
52
- "Customer Complaint": """Subject: Delayed shipment and no updates
53
 
54
- We placed our order nearly two weeks ago and still have not received the shipment. We have also not had any meaningful update from your team despite following up twice.
55
 
56
  This delay is now affecting our own delivery commitments to customers. Please let us know what is going on and when we can expect resolution.
57
 
@@ -88,11 +127,11 @@ Please keep this confidential.
88
 
89
  Thank you.""",
90
 
91
- "Urgent Escalation": """Subject: Production outage affecting client operations
92
 
93
  Team,
94
 
95
- A major system outage started 30 minutes ago and several enterprise clients are unable to access critical workflows. We are already receiving escalation calls.
96
 
97
  We need immediate support and clear ownership on incident response.
98
 
@@ -139,16 +178,91 @@ def format_output(raw_text: str) -> str:
139
 
140
  return "\n".join(formatted_lines).strip()
141
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
142
 
143
  def run_agent(system_prompt, email, show_raw_output):
144
  if not system_prompt or not system_prompt.strip():
145
- return "Please enter a system prompt.", ""
146
 
147
  if not email or not email.strip():
148
- return "Please enter an email to analyze.", ""
149
 
150
  try:
151
- response = client.chat.completions.create(
152
  model="gpt-4o-mini",
153
  messages=[
154
  {"role": "system", "content": system_prompt},
@@ -157,16 +271,51 @@ def run_agent(system_prompt, email, show_raw_output):
157
  temperature=0.4,
158
  )
159
 
160
- raw_output = response.choices[0].message.content or ""
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
161
  formatted_output = format_output(raw_output)
162
 
163
  if show_raw_output:
164
- return formatted_output, raw_output
165
- return formatted_output, ""
166
 
167
  except Exception as e:
168
  error_message = f"Error: {str(e)}"
169
- return error_message, error_message
170
 
171
 
172
  with gr.Blocks(title="Executive Agent Lab") as demo:
@@ -199,6 +348,7 @@ with gr.Blocks(title="Executive Agent Lab") as demo:
199
  load_basic_btn = gr.Button("Load Basic")
200
  load_structured_btn = gr.Button("Load Structured")
201
  load_advanced_btn = gr.Button("Load Advanced")
 
202
 
203
  with gr.Accordion("What makes a good agent prompt?", open=False):
204
  gr.Markdown(
@@ -208,7 +358,8 @@ with gr.Blocks(title="Executive Agent Lab") as demo:
208
  - routing rules
209
  - output format
210
  - tone
211
- - handling ambiguity"""
 
212
  )
213
 
214
  with gr.Column(scale=1):
@@ -243,6 +394,10 @@ with gr.Blocks(title="Executive Agent Lab") as demo:
243
  formatted_output = gr.Markdown(
244
  value="Run the agent to see results here."
245
  )
 
 
 
 
246
 
247
  show_raw_output = gr.Checkbox(
248
  label="Show raw output",
@@ -264,12 +419,16 @@ with gr.Blocks(title="Executive Agent Lab") as demo:
264
  def load_advanced():
265
  return ADVANCED_PROMPT
266
 
 
 
 
267
  def load_email(sample_name):
268
  return SAMPLE_EMAILS[sample_name]
269
 
270
  load_basic_btn.click(fn=load_basic, outputs=system_prompt)
271
  load_structured_btn.click(fn=load_structured, outputs=system_prompt)
272
  load_advanced_btn.click(fn=load_advanced, outputs=system_prompt)
 
273
 
274
  btn_customer.click(fn=lambda: load_email("Customer Complaint"), outputs=email)
275
  btn_sales.click(fn=lambda: load_email("Sales Inquiry"), outputs=email)
@@ -280,7 +439,7 @@ with gr.Blocks(title="Executive Agent Lab") as demo:
280
  run_btn.click(
281
  fn=run_agent,
282
  inputs=[system_prompt, email, show_raw_output],
283
- outputs=[formatted_output, raw_output],
284
  )
285
 
286
  demo.launch(server_name="0.0.0.0", server_port=7860)
 
1
  import os
2
+ import re
3
+ import json
4
  import gradio as gr
5
  from openai import OpenAI
6
 
 
50
  - Keep the draft reply professional and appropriate for business use
51
  """
52
 
53
+ ADVANCED_TOOL_CALLING_PROMPT = """You are an enterprise email triage agent.
54
+
55
+ Your job is to analyze incoming emails and decide how they should be handled.
56
+
57
+ You have access to one tool:
58
+
59
+ lookup_status(reference_id)
60
+
61
+ Use this tool only when the email refers to a specific order, shipment, incident, or ticket and the current status is needed to answer well.
62
+
63
+ If you decide to use the tool, respond with exactly this format and nothing else:
64
+
65
+ CALL_TOOL: lookup_status <reference_id>
66
+
67
+ Examples:
68
+ CALL_TOOL: lookup_status SG-18427
69
+ CALL_TOOL: lookup_status INC-2041
70
+
71
+ If no tool is needed, then provide your final answer directly.
72
+
73
+ Final output format:
74
+
75
+ Intent:
76
+ Priority:
77
+ Owner:
78
+ Action:
79
+ Reasoning:
80
+ Draft Reply:
81
+
82
+ Rules:
83
+ - Be concise and practical
84
+ - If the email is ambiguous, say so clearly
85
+ - If there is urgency, reflect that in both priority and action
86
+ - Keep the draft reply professional and appropriate for business use
87
+ - Do not call the tool unless it is genuinely needed
88
+ """
89
+
90
  SAMPLE_EMAILS = {
91
+ "Customer Complaint": """Subject: Delayed shipment SG-18427
92
 
93
+ We placed our order nearly two weeks ago and still have not received shipment SG-18427. We have also not had any meaningful update from your team despite following up twice.
94
 
95
  This delay is now affecting our own delivery commitments to customers. Please let us know what is going on and when we can expect resolution.
96
 
 
127
 
128
  Thank you.""",
129
 
130
+ "Urgent Escalation": """Subject: Update on incident INC-2041
131
 
132
  Team,
133
 
134
+ A major outage is still affecting several enterprise clients. Can someone update us on incident INC-2041 and confirm the current status?
135
 
136
  We need immediate support and clear ownership on incident response.
137
 
 
178
 
179
  return "\n".join(formatted_lines).strip()
180
 
181
+ STATUS_DB = {
182
+ "SG-18427": {
183
+ "record_type": "shipment",
184
+ "status": "Delayed in customs",
185
+ "last_update": "2026-04-03 14:20 SGT",
186
+ "eta": "2026-04-09",
187
+ "owner_team": "Operations",
188
+ },
189
+ "INC-2041": {
190
+ "record_type": "incident",
191
+ "status": "Engineering investigating root cause",
192
+ "last_update": "2026-04-06 10:05 SGT",
193
+ "eta": "Next update due in 30 minutes",
194
+ "owner_team": "Operations",
195
+ },
196
+ "TKT-48392": {
197
+ "record_type": "ticket",
198
+ "status": "Assigned to support tier 2",
199
+ "last_update": "2026-04-04 17:40 SGT",
200
+ "eta": "Expected resolution within 1 business day",
201
+ "owner_team": "Support",
202
+ },
203
+ }
204
+
205
+
206
+ def lookup_status(reference_id: str) -> dict:
207
+ ref = reference_id.strip().upper()
208
+ result = STATUS_DB.get(ref)
209
+
210
+ if result:
211
+ return {
212
+ "found": True,
213
+ "reference_id": ref,
214
+ **result
215
+ }
216
+
217
+ return {
218
+ "found": False,
219
+ "reference_id": ref,
220
+ "message": "No matching record found in the status system."
221
+ }
222
+
223
+
224
+ def detect_tool_call(text: str):
225
+ pattern = r"^\s*CALL_TOOL:\s*(\w+)\s+([A-Z0-9\-]+)\s*$"
226
+ match = re.match(pattern, text.strip())
227
+ if match:
228
+ return match.group(1), match.group(2)
229
+ return None, None
230
+
231
+
232
+ def render_tool_trace(tool_used, tool_name="", tool_input=None, tool_output=None):
233
+ if not tool_used:
234
+ return """## Tool Trace
235
+
236
+ **Tool used:** No
237
+ """
238
+
239
+ input_block = json.dumps(tool_input, indent=2)
240
+ output_block = json.dumps(tool_output, indent=2)
241
+
242
+ return f"""## Tool Trace
243
+
244
+ **Tool used:** Yes
245
+ **Tool name:** `{tool_name}`
246
+
247
+ ### Tool input
248
+ ```json
249
+ {input_block}
250
+ ```
251
+ ### Tool output
252
+ ```json
253
+ {output_block}
254
+ ```
255
+ """
256
 
257
  def run_agent(system_prompt, email, show_raw_output):
258
  if not system_prompt or not system_prompt.strip():
259
+ return "Please enter a system prompt.", "", "## Tool Trace\n\nNo run yet."
260
 
261
  if not email or not email.strip():
262
+ return "Please enter an email to analyze.", "", "## Tool Trace\n\nNo run yet."
263
 
264
  try:
265
+ first_response = client.chat.completions.create(
266
  model="gpt-4o-mini",
267
  messages=[
268
  {"role": "system", "content": system_prompt},
 
271
  temperature=0.4,
272
  )
273
 
274
+ first_output = first_response.choices[0].message.content or ""
275
+ tool_name, reference_id = detect_tool_call(first_output)
276
+
277
+ if tool_name == "lookup_status" and reference_id:
278
+ tool_result = lookup_status(reference_id)
279
+
280
+ tool_trace = render_tool_trace(
281
+ tool_used=True,
282
+ tool_name=tool_name,
283
+ tool_input={"reference_id": reference_id},
284
+ tool_output=tool_result,
285
+ )
286
+
287
+ second_response = client.chat.completions.create(
288
+ model="gpt-4o-mini",
289
+ messages=[
290
+ {"role": "system", "content": system_prompt},
291
+ {"role": "user", "content": email},
292
+ {"role": "assistant", "content": first_output},
293
+ {
294
+ "role": "user",
295
+ "content": (
296
+ "Tool result:\n"
297
+ f"{json.dumps(tool_result)}\n\n"
298
+ "Now provide the final answer for the email."
299
+ ),
300
+ },
301
+ ],
302
+ temperature=0.4,
303
+ )
304
+
305
+ raw_output = second_response.choices[0].message.content or ""
306
+ else:
307
+ tool_trace = render_tool_trace(tool_used=False)
308
+ raw_output = first_output
309
+
310
  formatted_output = format_output(raw_output)
311
 
312
  if show_raw_output:
313
+ return formatted_output, raw_output, tool_trace
314
+ return formatted_output, "", tool_trace
315
 
316
  except Exception as e:
317
  error_message = f"Error: {str(e)}"
318
+ return error_message, error_message, "## Tool Trace\n\nAn error occurred."
319
 
320
 
321
  with gr.Blocks(title="Executive Agent Lab") as demo:
 
348
  load_basic_btn = gr.Button("Load Basic")
349
  load_structured_btn = gr.Button("Load Structured")
350
  load_advanced_btn = gr.Button("Load Advanced")
351
+ load_advanced_tool_btn = gr.Button("Load Advanced (Tool)")
352
 
353
  with gr.Accordion("What makes a good agent prompt?", open=False):
354
  gr.Markdown(
 
358
  - routing rules
359
  - output format
360
  - tone
361
+ - handling ambiguity
362
+ - when to use the tool and when not to use it"""
363
  )
364
 
365
  with gr.Column(scale=1):
 
394
  formatted_output = gr.Markdown(
395
  value="Run the agent to see results here."
396
  )
397
+ tool_trace = gr.Markdown(
398
+ value="## Tool Trace\n\nNo run yet."
399
+ )
400
+
401
 
402
  show_raw_output = gr.Checkbox(
403
  label="Show raw output",
 
419
  def load_advanced():
420
  return ADVANCED_PROMPT
421
 
422
+ def load_advanced_tool():
423
+ return ADVANCED_TOOL_CALLING_PROMPT
424
+
425
  def load_email(sample_name):
426
  return SAMPLE_EMAILS[sample_name]
427
 
428
  load_basic_btn.click(fn=load_basic, outputs=system_prompt)
429
  load_structured_btn.click(fn=load_structured, outputs=system_prompt)
430
  load_advanced_btn.click(fn=load_advanced, outputs=system_prompt)
431
+ load_advanced_tool_btn.click(fn=load_advanced_tool, outputs=system_prompt)
432
 
433
  btn_customer.click(fn=lambda: load_email("Customer Complaint"), outputs=email)
434
  btn_sales.click(fn=lambda: load_email("Sales Inquiry"), outputs=email)
 
439
  run_btn.click(
440
  fn=run_agent,
441
  inputs=[system_prompt, email, show_raw_output],
442
+ outputs=[formatted_output, raw_output, tool_trace],
443
  )
444
 
445
  demo.launch(server_name="0.0.0.0", server_port=7860)