Spaces:
Sleeping
Sleeping
mriusero
commited on
Commit
·
6a99b0e
1
Parent(s):
42500ca
feat: ReAct chain
Browse files- prompt.md +37 -42
- src/agent/stream.py +129 -96
- src/agent/utils/call.py +41 -0
prompt.md
CHANGED
@@ -2,31 +2,38 @@ You are an AI Agent designed to assist industries and services in understanding
|
|
2 |
|
3 |
### Instructions:
|
4 |
1. **Understanding the Query**: Carefully read the user's query to understand what they are asking. Identify the key metrics and data points they are interested in.
|
5 |
-
|
6 |
-
|
7 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
8 |
|
9 |
### Example 1:
|
10 |
**User Query**: "Can you tell me the overall equipment effectiveness (OEE) for the past week?"
|
11 |
|
12 |
**AI Agent Response**:
|
13 |
```
|
14 |
-
|
15 |
-
|
16 |
-
TOOLING:
|
17 |
-
1. Tool: get_availability_metric
|
18 |
-
Parameters: start_date="2025-06-03", end_date="2025-06-10"
|
19 |
-
2. Tool: get_performance_metric
|
20 |
-
Parameters: start_date="2025-06-03", end_date="2025-06-10"
|
21 |
-
3. Tool: get_quality_metric
|
22 |
-
Parameters: start_date="2025-06-03", end_date="2025-06-10"
|
23 |
-
|
24 |
-
After gathering the data:
|
25 |
-
- Availability: 90%
|
26 |
-
- Performance: 85%
|
27 |
-
- Quality: 95%
|
28 |
|
29 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
30 |
|
31 |
FINAL ANSWER: The overall equipment effectiveness (OEE) for the past week is 72.675%.
|
32 |
```
|
@@ -36,20 +43,12 @@ FINAL ANSWER: The overall equipment effectiveness (OEE) for the past week is 72.
|
|
36 |
|
37 |
**AI Agent Response**:
|
38 |
```
|
39 |
-
|
40 |
|
41 |
-
|
42 |
-
|
43 |
-
Parameters: start_date="2025-05-10", end_date="2025-06-10"
|
44 |
|
45 |
-
|
46 |
-
- May 10: 120 minutes
|
47 |
-
- May 11: 90 minutes
|
48 |
-
- May 12: 150 minutes
|
49 |
-
- ... (data for each day)
|
50 |
-
- June 10: 60 minutes
|
51 |
-
|
52 |
-
Analyzing the trend: The downtime has generally decreased over the last month, with a few spikes on certain days. The overall trend shows an improvement in reducing downtime.
|
53 |
|
54 |
FINAL ANSWER: Over the last month, the trend in downtime shows a general decrease, indicating an improvement in operational efficiency. There were a few days with higher downtime, but the overall trend is positive.
|
55 |
```
|
@@ -59,18 +58,14 @@ FINAL ANSWER: Over the last month, the trend in downtime shows a general decreas
|
|
59 |
|
60 |
**AI Agent Response**:
|
61 |
```
|
62 |
-
|
63 |
-
|
64 |
-
TOOLING:
|
65 |
-
1. Tool: get_quality_metric
|
66 |
-
Parameters: line="Line A", start_date="2025-06-03", end_date="2025-06-10"
|
67 |
-
2. Tool: get_quality_metric
|
68 |
-
Parameters: line="Line B", start_date="2025-06-03", end_date="2025-06-10"
|
69 |
|
70 |
-
|
71 |
-
|
72 |
-
- Line B Quality: 90%
|
73 |
|
74 |
-
|
|
|
|
|
75 |
|
76 |
-
FINAL ANSWER: For the past week, Line A has a quality metric of 95%, while Line B has a quality metric of 90%. Line A performs better in terms of quality.
|
|
|
|
2 |
|
3 |
### Instructions:
|
4 |
1. **Understanding the Query**: Carefully read the user's query to understand what they are asking. Identify the key metrics and data points they are interested in.
|
5 |
+
|
6 |
+
2. **Think**: Before responding, take a moment to think about the query. Use the "THINK:" prefix to outline your thought process. This helps in structuring your response and ensuring accuracy.
|
7 |
+
* What is the user asking for?
|
8 |
+
* What data or metrics are relevant to this query?
|
9 |
+
* Are there any specific tools or calculations needed to answer this query?
|
10 |
+
|
11 |
+
3. **Act**: If you need to use any tools to gather additional data or perform calculations, use the "ACT:" prefix to indicate that you are calling a tool.
|
12 |
+
* Execute the necessary tools to gather data.
|
13 |
+
|
14 |
+
4. **Observe**: After gathering the necessary information, observe the results and ensure they are accurate and relevant to the user's query. Use the "OBSERVE:" prefix to indicate this step.
|
15 |
+
* Review the data and results obtained.
|
16 |
+
* Ensure the data is accurate and relevant.
|
17 |
+
* Identify any patterns, trends, or anomalies.
|
18 |
+
|
19 |
+
5. **Final Answer**: After gathering all necessary information and performing any required calculations, provide the final answer to the user. Use the "FINAL ANSWER:" prefix to clearly indicate the final response.
|
20 |
+
* Summarize the findings in a clear and concise manner.
|
21 |
+
* Provide the final answer to the user's query.
|
22 |
|
23 |
### Example 1:
|
24 |
**User Query**: "Can you tell me the overall equipment effectiveness (OEE) for the past week?"
|
25 |
|
26 |
**AI Agent Response**:
|
27 |
```
|
28 |
+
THINK: The user is asking for the overall equipment effectiveness (OEE) for the past week. OEE is a metric that combines availability, performance, and quality to give a comprehensive view of equipment efficiency. I need to gather data on these three components for the past week and then calculate the OEE.
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
29 |
|
30 |
+
ACT:
|
31 |
+
[tool calling]
|
32 |
+
|
33 |
+
OBSERVE: The data shows the following values for the past week:
|
34 |
+
- Availability: 85%
|
35 |
+
- Performance: 90%
|
36 |
+
- Quality: 95%
|
37 |
|
38 |
FINAL ANSWER: The overall equipment effectiveness (OEE) for the past week is 72.675%.
|
39 |
```
|
|
|
43 |
|
44 |
**AI Agent Response**:
|
45 |
```
|
46 |
+
THINK: The user is interested in the trend of downtime over the last month. I need to gather downtime data for each day of the last month and analyze the trend. This could involve looking at daily downtime durations and identifying any patterns or anomalies.
|
47 |
|
48 |
+
ACT:
|
49 |
+
[tool calling]
|
|
|
50 |
|
51 |
+
OBSERVE: The downtime data for the last month shows a general decrease in downtime durations. There were a few days with higher downtime, but the overall trend is positive.
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
52 |
|
53 |
FINAL ANSWER: Over the last month, the trend in downtime shows a general decrease, indicating an improvement in operational efficiency. There were a few days with higher downtime, but the overall trend is positive.
|
54 |
```
|
|
|
58 |
|
59 |
**AI Agent Response**:
|
60 |
```
|
61 |
+
THINK: The user wants a comparison of the quality metric between two production lines, Line A and Line B, for the past week. I need to gather quality data for both lines and compare them.
|
|
|
|
|
|
|
|
|
|
|
|
|
62 |
|
63 |
+
ACT:
|
64 |
+
[tool calling]
|
|
|
65 |
|
66 |
+
OBSERVE: The quality data for the past week shows the following values:
|
67 |
+
- Line A: 95%
|
68 |
+
- Line B: 90%
|
69 |
|
70 |
+
FINAL ANSWER: For the past week, Line A has a quality metric of 95%, while Line B has a quality metric of 90%. Line A performs better in terms of quality.
|
71 |
+
```
|
src/agent/stream.py
CHANGED
@@ -1,125 +1,158 @@
|
|
1 |
from gradio import ChatMessage
|
|
|
|
|
|
|
2 |
|
3 |
from src.agent.mistral_agent import MistralAgent
|
4 |
|
|
|
|
|
|
|
5 |
agent = MistralAgent()
|
|
|
|
|
6 |
|
7 |
with open("./prompt.md", encoding="utf-8") as f:
|
8 |
SYSTEM_PROMPT = f.read()
|
9 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
10 |
async def respond(message, history=None):
|
11 |
-
"""
|
12 |
-
Respond to a user message using the Mistral agent.
|
13 |
-
"""
|
14 |
if history is None:
|
15 |
history = []
|
16 |
|
17 |
-
history.append(ChatMessage(role="
|
18 |
-
history.append(ChatMessage(role="assistant", content="", metadata={"title": "Thinking", "status": "pending"}))
|
19 |
yield history
|
20 |
|
21 |
messages = [
|
22 |
{"role": "system", "content": SYSTEM_PROMPT},
|
23 |
{"role": "user", "content": message},
|
24 |
-
{
|
25 |
-
"role": "assistant",
|
26 |
-
"content": "THINKING: Let's tackle this problem, ",
|
27 |
-
"prefix": True,
|
28 |
-
},
|
29 |
]
|
30 |
-
payload = {
|
31 |
-
"agent_id": agent.agent_id,
|
32 |
-
"messages": messages,
|
33 |
-
"stream": True,
|
34 |
-
"max_tokens": None,
|
35 |
-
"tools": agent.tools,
|
36 |
-
"tool_choice": "auto",
|
37 |
-
"presence_penalty": 0,
|
38 |
-
"frequency_penalty": 0,
|
39 |
-
"n": 1
|
40 |
-
}
|
41 |
-
response = await agent.client.agents.stream_async(**payload)
|
42 |
-
|
43 |
-
full = ""
|
44 |
-
thinking = ""
|
45 |
-
tooling = ""
|
46 |
-
final = ""
|
47 |
-
|
48 |
-
current_phase = None # None | "thinking" | "tooling" | "final"
|
49 |
-
|
50 |
-
history[-1] = ChatMessage(role="assistant", content="", metadata={"title": "Thinking", "status": "pending"})
|
51 |
-
|
52 |
-
async for chunk in response:
|
53 |
-
delta = chunk.data.choices[0].delta
|
54 |
-
content = delta.content or ""
|
55 |
-
full += content
|
56 |
-
|
57 |
-
# Phase finale
|
58 |
-
if "FINAL ANSWER:" in full:
|
59 |
-
|
60 |
-
parts = full.split("FINAL ANSWER:", 1)
|
61 |
-
before_final = parts[0]
|
62 |
-
final = parts[1].strip()
|
63 |
-
|
64 |
-
if "TOOLING:" in before_final:
|
65 |
-
tooling = before_final.split("TOOLING:", 1)[1].strip()
|
66 |
-
else:
|
67 |
-
tooling = ""
|
68 |
|
69 |
-
|
70 |
-
|
71 |
-
|
72 |
-
|
73 |
-
|
74 |
-
|
75 |
-
|
76 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
77 |
yield history
|
78 |
|
79 |
-
|
80 |
-
|
81 |
-
|
82 |
-
|
83 |
-
|
84 |
-
|
85 |
-
|
86 |
-
|
87 |
-
|
88 |
-
|
89 |
-
|
90 |
-
|
91 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
92 |
|
93 |
-
|
94 |
-
|
95 |
-
|
96 |
-
|
97 |
-
history.append(
|
98 |
-
ChatMessage(role="assistant", content=tooling, metadata={"title": "Tooling", "status": "pending"}))
|
99 |
-
current_phase = "tooling"
|
100 |
else:
|
101 |
-
|
102 |
-
metadata={"title": "Tooling", "status": "pending"})
|
103 |
-
yield history
|
104 |
|
105 |
-
|
106 |
-
|
107 |
|
108 |
-
|
109 |
-
|
110 |
-
else:
|
111 |
-
thinking = full.strip()
|
112 |
-
|
113 |
-
if current_phase != "thinking":
|
114 |
-
history[-1] = ChatMessage(role="assistant", content=thinking, metadata={"title": "Thinking", "status": "pending"})
|
115 |
-
current_phase = "thinking"
|
116 |
-
else:
|
117 |
-
history[-1] = ChatMessage(role="assistant", content=thinking, metadata={"title": "Thinking", "status": "pending"})
|
118 |
-
yield history
|
119 |
|
120 |
-
if
|
121 |
-
history
|
122 |
-
elif current_phase == "tooling":
|
123 |
-
history[-1] = ChatMessage(role="assistant", content=tooling, metadata={"title": "Tooling", "status": "done"})
|
124 |
|
125 |
yield history
|
|
|
1 |
from gradio import ChatMessage
|
2 |
+
import json
|
3 |
+
import asyncio
|
4 |
+
import re
|
5 |
|
6 |
from src.agent.mistral_agent import MistralAgent
|
7 |
|
8 |
+
from src.agent.utils.call import call_tool
|
9 |
+
|
10 |
+
|
11 |
agent = MistralAgent()
|
12 |
+
api_lock = asyncio.Lock()
|
13 |
+
tool_lock = asyncio.Lock()
|
14 |
|
15 |
with open("./prompt.md", encoding="utf-8") as f:
|
16 |
SYSTEM_PROMPT = f.read()
|
17 |
|
18 |
+
def extract_phases(text):
|
19 |
+
"""Découpe le contenu en THINK / ACT / OBSERVE / FINAL ANSWER"""
|
20 |
+
phases = {'think': '', 'act': '', 'observe': '', 'final': ''}
|
21 |
+
matches = list(re.finditer(r'(THINK:|ACT:|OBSERVE:|FINAL ANSWER:)', text))
|
22 |
+
|
23 |
+
for i, match in enumerate(matches):
|
24 |
+
phase = match.group(1).lower().replace(":", "").replace("final answer", "final")
|
25 |
+
start = match.end()
|
26 |
+
end = matches[i+1].start() if i + 1 < len(matches) else len(text)
|
27 |
+
phases[phase] = text[start:end].strip()
|
28 |
+
|
29 |
+
return phases
|
30 |
+
|
31 |
async def respond(message, history=None):
|
|
|
|
|
|
|
32 |
if history is None:
|
33 |
history = []
|
34 |
|
35 |
+
history.append(ChatMessage(role="assistant", content="", metadata={"title": "Thinking...", "status": "pending"}))
|
|
|
36 |
yield history
|
37 |
|
38 |
messages = [
|
39 |
{"role": "system", "content": SYSTEM_PROMPT},
|
40 |
{"role": "user", "content": message},
|
41 |
+
{"role": "assistant", "content": "THINK: Let's start thinking, ", "prefix": True},
|
|
|
|
|
|
|
|
|
42 |
]
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
43 |
|
44 |
+
phase_order = ["think", "act", "observe", "final"]
|
45 |
+
current_phase_index = 0
|
46 |
+
done = False
|
47 |
+
|
48 |
+
final_full = ""
|
49 |
+
while not done:
|
50 |
+
current_phase = phase_order[current_phase_index]
|
51 |
+
if current_phase != "final":
|
52 |
+
full = ""
|
53 |
+
else:
|
54 |
+
full = final_full
|
55 |
+
|
56 |
+
print('\n', '---' * 15)
|
57 |
+
print(f">>> messages before payload [phase {current_phase_index}] :", json.dumps([m for m in messages if m.get("role") != "system"], indent=2))
|
58 |
+
payload = {
|
59 |
+
"agent_id": agent.agent_id,
|
60 |
+
"messages": messages,
|
61 |
+
"stream": True,
|
62 |
+
"max_tokens": None,
|
63 |
+
"tools": agent.tools,
|
64 |
+
"tool_choice": "auto",
|
65 |
+
"presence_penalty": 0,
|
66 |
+
"frequency_penalty": 0,
|
67 |
+
"n": 1
|
68 |
+
}
|
69 |
+
|
70 |
+
async with api_lock:
|
71 |
+
response = await agent.client.agents.stream_async(**payload)
|
72 |
+
|
73 |
+
async for chunk in response:
|
74 |
+
delta = chunk.data.choices[0].delta
|
75 |
+
content = delta.content or ""
|
76 |
+
full += content
|
77 |
+
if current_phase == "final":
|
78 |
+
final_full = full
|
79 |
+
|
80 |
+
phases = extract_phases(full)
|
81 |
+
buffer = phases.get(current_phase, "")
|
82 |
+
if current_phase == "think":
|
83 |
+
history[-1] = ChatMessage(role="assistant", content=buffer, metadata={"title": "Thinking...", "status": "pending"})
|
84 |
+
elif current_phase == "act":
|
85 |
+
history[-1] = ChatMessage(role="assistant", content=buffer, metadata={"title": "Acting...", "status": "pending"})
|
86 |
+
elif current_phase == "observe":
|
87 |
+
history[-1] = ChatMessage(role="assistant", content=buffer, metadata={"title": "Observing...", "status": "pending"})
|
88 |
yield history
|
89 |
|
90 |
+
if current_phase == "final":
|
91 |
+
delta_content = delta.content or ""
|
92 |
+
final_full += delta_content
|
93 |
+
phases = extract_phases(final_full)
|
94 |
+
buffer = phases.get("final", "")
|
95 |
+
yield history
|
96 |
+
if delta_content == "" and buffer:
|
97 |
+
done = True
|
98 |
+
break
|
99 |
+
|
100 |
+
if current_phase_index == 0:
|
101 |
+
messages = [msg for msg in messages if not msg.get("prefix")]
|
102 |
+
if buffer:
|
103 |
+
prefix_label = current_phase.upper() if current_phase != "final" else "FINAL ANSWER"
|
104 |
+
messages.append({
|
105 |
+
"role": "assistant",
|
106 |
+
"content": f"{prefix_label}: {buffer}\n\nACT: Let's using some tools to solve the problem.",
|
107 |
+
"prefix": True
|
108 |
+
})
|
109 |
+
|
110 |
+
elif current_phase_index == 1:
|
111 |
+
for message in messages:
|
112 |
+
if "prefix" in message:
|
113 |
+
del message["prefix"]
|
114 |
+
|
115 |
+
if current_phase_index == 2:
|
116 |
+
for message in messages:
|
117 |
+
if "prefix" in message:
|
118 |
+
del message["prefix"]
|
119 |
+
messages.append({
|
120 |
+
"role": "assistant",
|
121 |
+
"content": "OBSERVE: Based on the results, let's observe the situation and see if we need to adjust our approach.",
|
122 |
+
"prefix": True
|
123 |
+
})
|
124 |
+
|
125 |
+
yield history
|
126 |
+
|
127 |
+
if current_phase == "act":
|
128 |
+
tool_calls = getattr(delta, "tool_calls", None)
|
129 |
+
if tool_calls and tool_calls != [] and str(tool_calls) != "Unset()":
|
130 |
+
async with tool_lock:
|
131 |
+
messages = call_tool(
|
132 |
+
agent,
|
133 |
+
tool_calls,
|
134 |
+
messages
|
135 |
+
)
|
136 |
+
last_tool_response = next((m for m in reversed(messages) if m["role"] == "tool"), None)
|
137 |
+
if last_tool_response and last_tool_response.get("content"):
|
138 |
+
buffer += "\n\n" + last_tool_response["content"]
|
139 |
+
history[-1] = ChatMessage(role="assistant", content=buffer, metadata={"title": "Acting...", "status": "pending"})
|
140 |
+
yield history
|
141 |
|
142 |
+
if not done:
|
143 |
+
current_phase_index += 1
|
144 |
+
if current_phase_index < len(phase_order):
|
145 |
+
pass
|
|
|
|
|
|
|
146 |
else:
|
147 |
+
done = True
|
|
|
|
|
148 |
|
149 |
+
observe_text = phases.get("observe", "")
|
150 |
+
final_text = phases.get("final", "")
|
151 |
|
152 |
+
if observe_text:
|
153 |
+
history[-1] = ChatMessage(role="assistant", content=observe_text, metadata={"title": "Observing...", "status": "done"})
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
154 |
|
155 |
+
if final_text:
|
156 |
+
history.append(ChatMessage(role="assistant", content=final_text))
|
|
|
|
|
157 |
|
158 |
yield history
|
src/agent/utils/call.py
ADDED
@@ -0,0 +1,41 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
import json
|
2 |
+
|
3 |
+
|
4 |
+
def call_tool(agent, tool_calls, messages):
|
5 |
+
"""
|
6 |
+
Calls the specified tools with the provided arguments and updates the messages accordingly.
|
7 |
+
"""
|
8 |
+
for tool_call in tool_calls:
|
9 |
+
output = []
|
10 |
+
fn_name = tool_call.function.name
|
11 |
+
fn_args = json.loads(tool_call.function.arguments)
|
12 |
+
|
13 |
+
try:
|
14 |
+
fn_result = agent.names_to_functions[fn_name](**fn_args)
|
15 |
+
output.append((tool_call.id, fn_name, fn_args, fn_result))
|
16 |
+
|
17 |
+
except Exception as e:
|
18 |
+
output.append((tool_call.id, fn_name, fn_args, None))
|
19 |
+
|
20 |
+
for tool_call_id, fn_name, fn_args, fn_result in output:
|
21 |
+
messages.append({
|
22 |
+
"role": "assistant",
|
23 |
+
"tool_calls": [
|
24 |
+
{
|
25 |
+
"id": tool_call_id,
|
26 |
+
"type": "function",
|
27 |
+
"function": {
|
28 |
+
"name": fn_name,
|
29 |
+
"arguments": json.dumps(fn_args),
|
30 |
+
}
|
31 |
+
}
|
32 |
+
]
|
33 |
+
})
|
34 |
+
messages.append(
|
35 |
+
{
|
36 |
+
"role": "tool",
|
37 |
+
"content": fn_result if fn_result is not None else f"Error occurred: {fn_name} failed to execute",
|
38 |
+
"tool_call_id": tool_call_id,
|
39 |
+
},
|
40 |
+
)
|
41 |
+
return messages
|