JPM34 commited on
Commit
9a283d6
·
1 Parent(s): 6368cc9

added module answers

Browse files
Files changed (1) hide show
  1. answers.py +205 -0
answers.py ADDED
@@ -0,0 +1,205 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import os
2
+
3
+ from langchain_core.output_parsers import StrOutputParser
4
+ from langchain_core.prompts import ChatPromptTemplate
5
+
6
+ from langgraph.graph import Graph, StateGraph, START, END
7
+
8
+ from langchain_google_genai import ChatGoogleGenerativeAI
9
+
10
+ from typing import Any, Dict
11
+ from typing_extensions import TypedDict
12
+
13
+
14
+ class AgentState(TypedDict):
15
+ """State for the final answer validation graph."""
16
+
17
+ question: str
18
+ answer: str
19
+ final_answer: str | None
20
+ agent_memory: Any
21
+ valid_answer: bool
22
+
23
+
24
+ def extract_answer(state: AgentState) -> Dict:
25
+ """Extract and format the final answer from the state.
26
+ Args:
27
+ state: The state of the agent.
28
+ Returns:
29
+ A dictionary with the formatted final answer.
30
+ """
31
+ # Extract the final answer from the state
32
+ sep_token = "FINAL ANSWER:"
33
+ raw_answer = state["answer"]
34
+
35
+ # Extract the answer after the separator if it exists
36
+ if sep_token in raw_answer:
37
+ formatted_answer = raw_answer.split(sep_token)[1].strip()
38
+ else:
39
+ formatted_answer = raw_answer.strip()
40
+
41
+ # Remove any brackets from lists
42
+ formatted_answer = formatted_answer.replace("[", "").replace("]", "")
43
+
44
+ # Remove units unless specified
45
+ if not any(
46
+ unit in formatted_answer.lower()
47
+ for unit in ["$", "%", "dollars", "percent"]
48
+ ):
49
+ formatted_answer = formatted_answer.replace("$", "").replace("%", "")
50
+
51
+ # Remove commas from numbers
52
+ parts = formatted_answer.split(",")
53
+ formatted_parts = []
54
+ for part in parts:
55
+ part = part.strip()
56
+ if part.replace(".", "").isdigit(): # Check if it's a number
57
+ part = part.replace(",", "")
58
+ formatted_parts.append(part)
59
+ formatted_answer = ", ".join(formatted_parts)
60
+
61
+ return {"final_answer": formatted_answer}
62
+
63
+
64
+ def reasoning_check(state: AgentState) -> Dict:
65
+ """
66
+ Node that checks the reasoning of the final answer.
67
+ Args:
68
+ state: The state of the agent.
69
+ Returns:
70
+ A dictionary with the reasoning check result.
71
+ """
72
+ model = ChatGoogleGenerativeAI(
73
+ model="models/gemini-2.0-flash-lite",
74
+ google_api_key=os.getenv("GEMINI_KEY"),
75
+ temperature=0.2,
76
+ )
77
+ prompt = ChatPromptTemplate.from_messages(
78
+ [
79
+ (
80
+ "system",
81
+ """You are a strict validator of answers. Your job is to check if the reasoning and results are correct.
82
+ You should have >90% confidence that the answer is correct to pass it.
83
+ First list reasons why yes/no, then write your final decision: PASS in caps lock if it is satisfactory, FAIL if it is not.""",
84
+ ),
85
+ (
86
+ "human",
87
+ """
88
+ Here is a user-given task and the agent steps: {agent_memory}
89
+ Now here is the answer that was given: {final_answer}
90
+ Please check that the reasoning process and results are correct: do they correctly answer the given task?
91
+ """,
92
+ ),
93
+ ]
94
+ )
95
+
96
+ chain = prompt | model | StrOutputParser()
97
+ output = chain.invoke(
98
+ {
99
+ "agent_memory": state["agent_memory"],
100
+ "final_answer": state["final_answer"],
101
+ }
102
+ )
103
+
104
+ print("Reasoning Feedback: ", output)
105
+ if "FAIL" in output:
106
+ return {"valid_answer": False}
107
+ return {"valid_answer": True}
108
+
109
+
110
+ def formatting_check(state: AgentState) -> Dict:
111
+ """
112
+ Node that checks the formatting of the final answer.
113
+ Args:
114
+ state: The state of the agent.
115
+ Returns:
116
+ A dictionary with the formatting check result.
117
+ """
118
+ model = ChatGoogleGenerativeAI(
119
+ model="models/gemini-2.0-flash-lite",
120
+ google_api_key=os.getenv("GEMINI_KEY"),
121
+ temperature=0.2,
122
+ )
123
+ prompt = ChatPromptTemplate.from_messages(
124
+ [
125
+ (
126
+ "system",
127
+ """You are a general AI assistant. I will ask you a question. Report your thoughts, and finish your answer with the following template: FINAL ANSWER: [YOUR FINAL ANSWER]. YOUR FINAL ANSWER should be a number OR as few words as possible OR a comma separated list of numbers and/or strings. If you are asked for a number, don't use comma to write your number neither use units such as $ or percent sign unless specified otherwise. If you are asked for a string, don't use articles, neither abbreviations (e.g. for cities), and write the digits in plain text unless specified otherwise. If you are asked for a comma separated list, apply the above rules depending of whether the element to be put in the list is a number or a string.
128
+ """,
129
+ ),
130
+ (
131
+ "human",
132
+ """
133
+ Here is a user-given task and the agent steps: {agent_memory}
134
+ Now here is the FINAL ANSWER that was given: {final_answer}
135
+ Ensure the FINAL ANSWER is in the right format as asked for by the task.
136
+ """,
137
+ ),
138
+ ]
139
+ )
140
+
141
+ chain = prompt | model | StrOutputParser()
142
+ output = chain.invoke(
143
+ {
144
+ "agent_memory": state["agent_memory"],
145
+ "final_answer": state["final_answer"],
146
+ }
147
+ )
148
+
149
+ print("Formatting Feedback: ", output)
150
+ if "FAIL" in output:
151
+ return {"valid_answer": False}
152
+ return {"valid_answer": True}
153
+
154
+
155
+ def create_final_answer_graph() -> Graph:
156
+ """Create a graph that validates the final answer.
157
+ Returns:
158
+ A graph that validates the final answer.
159
+ """
160
+ # Create the graph
161
+ workflow = StateGraph(AgentState)
162
+
163
+ # Add nodes
164
+ workflow.add_node("extract_answer", extract_answer)
165
+ workflow.add_node("reasoning_check", reasoning_check)
166
+ workflow.add_node("formatting_check", formatting_check)
167
+
168
+ # Add edges
169
+ workflow.add_edge(START, "extract_answer")
170
+ workflow.add_edge("extract_answer", "reasoning_check")
171
+ workflow.add_edge("reasoning_check", "formatting_check")
172
+ workflow.add_edge("formatting_check", END)
173
+
174
+ # Compile the graph
175
+ return workflow.compile()
176
+
177
+
178
+ def validate_answer(graph: Graph, answer: str, agent_memory: Any) -> Dict:
179
+ """Validate the answer using the LangGraph workflow.
180
+ Args:
181
+ graph: The validation graph.
182
+ answer: The answer to validate.
183
+ agent_memory: The agent's memory.
184
+ Returns:
185
+ A dictionary with validation results.
186
+ """
187
+ try:
188
+ # Initialize state
189
+ initial_state = {
190
+ "answer": answer,
191
+ "final_answer": None,
192
+ "agent_memory": agent_memory,
193
+ "valid_answer": False,
194
+ }
195
+
196
+ # Run the graph
197
+ result = graph.invoke(initial_state)
198
+
199
+ return {
200
+ "valid_answer": result.get("valid_answer", False),
201
+ "final_answer": result.get("final_answer", None),
202
+ }
203
+ except Exception as e:
204
+ print(f"Validation failed: {e}")
205
+ return {"valid_answer": False, "final_answer": None}