Spaces:
Running
Running
lingyit1108
commited on
Commit
β’
8434471
1
Parent(s):
7a98d38
finished answer evaluation
Browse files
database/{mock_qna.db β mock_qna.sqlite}
RENAMED
@@ -1,3 +1,3 @@
|
|
1 |
version https://git-lfs.github.com/spec/v1
|
2 |
-
oid sha256:
|
3 |
-
size
|
|
|
1 |
version https://git-lfs.github.com/spec/v1
|
2 |
+
oid sha256:8949591dc84ad447843c2741803c39e545dc11c6e39cefca75ab1416a6140e3a
|
3 |
+
size 20480
|
notebooks/003_create_mock_qna.ipynb
CHANGED
@@ -36,7 +36,7 @@
|
|
36 |
"metadata": {},
|
37 |
"outputs": [],
|
38 |
"source": [
|
39 |
-
"db_path = \"../database/mock_qna.
|
40 |
"nature_of_run = \"new\" if not os.path.exists(db_path) else \"existing\"\n",
|
41 |
"\n",
|
42 |
"qna_path = \"../database/mock_qna_source.csv\""
|
@@ -61,7 +61,10 @@
|
|
61 |
"source": [
|
62 |
"qna_data = pd.read_csv( qna_path )\n",
|
63 |
"qna_cols = list(qna_data.columns)\n",
|
64 |
-
"qna_data.shape"
|
|
|
|
|
|
|
65 |
]
|
66 |
},
|
67 |
{
|
@@ -124,7 +127,26 @@
|
|
124 |
" )\n",
|
125 |
" \"\"\")\n",
|
126 |
" print(\"created table `qna_tbl`\")\n",
|
127 |
-
" print(f\"columns for `qna_tbl` are {qna_cols_str}\")"
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
128 |
]
|
129 |
},
|
130 |
{
|
|
|
36 |
"metadata": {},
|
37 |
"outputs": [],
|
38 |
"source": [
|
39 |
+
"db_path = \"../database/mock_qna.sqlite\"\n",
|
40 |
"nature_of_run = \"new\" if not os.path.exists(db_path) else \"existing\"\n",
|
41 |
"\n",
|
42 |
"qna_path = \"../database/mock_qna_source.csv\""
|
|
|
61 |
"source": [
|
62 |
"qna_data = pd.read_csv( qna_path )\n",
|
63 |
"qna_cols = list(qna_data.columns)\n",
|
64 |
+
"print(\"qna_data.shape\", qna_data.shape)\n",
|
65 |
+
"\n",
|
66 |
+
"qna_data = qna_data[ qna_data[\"question\"].notnull() ].reset_index(drop=True)\n",
|
67 |
+
"print(\"qna_data.shape\", qna_data.shape)"
|
68 |
]
|
69 |
},
|
70 |
{
|
|
|
127 |
" )\n",
|
128 |
" \"\"\")\n",
|
129 |
" print(\"created table `qna_tbl`\")\n",
|
130 |
+
" print(f\"columns for `qna_tbl` are {qna_cols_str}\")\n",
|
131 |
+
"\n",
|
132 |
+
" \n",
|
133 |
+
" cur.execute(f\"\"\"CREATE TABLE answer_tbl (\n",
|
134 |
+
" id, correct_answer, user_answer\n",
|
135 |
+
" )\n",
|
136 |
+
" \"\"\")\n",
|
137 |
+
" print(\"created table `answer_tbl`\")"
|
138 |
+
]
|
139 |
+
},
|
140 |
+
{
|
141 |
+
"cell_type": "code",
|
142 |
+
"execution_count": null,
|
143 |
+
"id": "31b6fe49-f55d-44f0-90c1-248158eac96c",
|
144 |
+
"metadata": {},
|
145 |
+
"outputs": [],
|
146 |
+
"source": [
|
147 |
+
"if False:\n",
|
148 |
+
" cur.execute(\"DELETE FROM answer_tbl\")\n",
|
149 |
+
" con.commit()"
|
150 |
]
|
151 |
},
|
152 |
{
|
notebooks/004_qna_prompting_with_function_calling.ipynb
CHANGED
@@ -37,7 +37,7 @@
|
|
37 |
"metadata": {},
|
38 |
"outputs": [],
|
39 |
"source": [
|
40 |
-
"db_path = \"../database/mock_qna.
|
41 |
]
|
42 |
},
|
43 |
{
|
|
|
37 |
"metadata": {},
|
38 |
"outputs": [],
|
39 |
"source": [
|
40 |
+
"db_path = \"../database/mock_qna.sqlite\""
|
41 |
]
|
42 |
},
|
43 |
{
|
notebooks/005_qna_prompting_with_pydantic_embeddings.ipynb
CHANGED
@@ -45,7 +45,7 @@
|
|
45 |
"metadata": {},
|
46 |
"outputs": [],
|
47 |
"source": [
|
48 |
-
"db_path = \"../database/mock_qna.
|
49 |
"con = sqlite3.connect(db_path)\n",
|
50 |
"cur = con.cursor()"
|
51 |
]
|
|
|
45 |
"metadata": {},
|
46 |
"outputs": [],
|
47 |
"source": [
|
48 |
+
"db_path = \"../database/mock_qna.sqlite\"\n",
|
49 |
"con = sqlite3.connect(db_path)\n",
|
50 |
"cur = con.cursor()"
|
51 |
]
|
qna_prompting.py
CHANGED
@@ -1,13 +1,15 @@
|
|
1 |
import sqlite3
|
|
|
2 |
from pydantic import BaseModel, Field
|
3 |
from llama_index.core.tools import FunctionTool
|
4 |
|
|
|
5 |
|
6 |
-
db_path = "./database/mock_qna.
|
7 |
-
|
8 |
-
Use this tool to extract the chapter
|
9 |
-
|
10 |
-
|
11 |
The format of the function argument looks as follow:
|
12 |
It should be in the format with `Chapter_` as prefix.
|
13 |
Example 1: `Chapter_1` for first chapter
|
@@ -15,36 +17,68 @@ description = """
|
|
15 |
Example 3: `Chapter_5` for fifth chapter
|
16 |
Thereafter, the chapter_n argument will be passed to the function for Q&A question retrieval.
|
17 |
"""
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
18 |
|
19 |
-
class
|
20 |
-
chapter_n: str =
|
21 |
-
|
22 |
-
|
23 |
-
|
24 |
-
|
25 |
-
|
26 |
-
|
27 |
-
|
28 |
-
|
29 |
-
|
30 |
)
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
31 |
|
32 |
def get_qna_question(chapter_n: str) -> str:
|
33 |
"""
|
34 |
-
Use this tool to extract the chapter
|
35 |
-
|
36 |
-
|
|
|
|
|
37 |
Example 1: `Chapter_1` for first chapter
|
38 |
Example 2: For chapter 12 of the textbook, you should return `Chapter_12`
|
39 |
Example 3: `Chapter_5` for fifth chapter
|
40 |
Thereafter, the chapter_n argument will be passed to the function for Q&A question retrieval.
|
|
|
41 |
"""
|
42 |
con = sqlite3.connect(db_path)
|
43 |
cur = con.cursor()
|
44 |
|
45 |
-
|
46 |
-
|
47 |
-
|
|
|
|
|
48 |
""" + filter_clause
|
49 |
|
50 |
res = cur.execute(sql_string)
|
@@ -66,13 +100,86 @@ def get_qna_question(chapter_n: str) -> str:
|
|
66 |
"C) " + option_3 + "\n" + \
|
67 |
"D) " + option_4
|
68 |
|
|
|
|
|
|
|
69 |
con.close()
|
70 |
|
71 |
return qna_str
|
72 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
73 |
get_qna_question_tool = FunctionTool.from_defaults(
|
74 |
fn=get_qna_question,
|
75 |
name="Extract_Question",
|
76 |
-
description=
|
77 |
-
fn_schema=
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
78 |
)
|
|
|
1 |
import sqlite3
|
2 |
+
import streamlit as st
|
3 |
from pydantic import BaseModel, Field
|
4 |
from llama_index.core.tools import FunctionTool
|
5 |
|
6 |
+
import time
|
7 |
|
8 |
+
db_path = "./database/mock_qna.sqlite"
|
9 |
+
qna_question_description = """
|
10 |
+
Use this tool to extract the chapter number from the body of input text,
|
11 |
+
thereafter, chapter number will be used as a filtering criteria for
|
12 |
+
extracting the right questions set from database.
|
13 |
The format of the function argument looks as follow:
|
14 |
It should be in the format with `Chapter_` as prefix.
|
15 |
Example 1: `Chapter_1` for first chapter
|
|
|
17 |
Example 3: `Chapter_5` for fifth chapter
|
18 |
Thereafter, the chapter_n argument will be passed to the function for Q&A question retrieval.
|
19 |
"""
|
20 |
+
qna_answer_description = """
|
21 |
+
Use this tool to trigger the evaluation of user's provided input with the
|
22 |
+
correct answer of the Q&A question asked. When user provides answer to the
|
23 |
+
question asked, they can reply in natural language or giving the alphabet
|
24 |
+
symbol of which selected answer they think it's most reasonable.
|
25 |
+
The format of the function argument `user_selected_answer` looks as follow:
|
26 |
+
It should be in the format with character such as A, B, C and D.
|
27 |
+
Example 1: User's answer is `a`, it means choice `A`.
|
28 |
+
Example 2: User's answer is contextually closer to 3rd answer choice, it means `C`.
|
29 |
+
Example 3: User says last is the answer, it means `D`.
|
30 |
+
Thereafter, the `user_selected_answer` argument will be passed to the
|
31 |
+
function for Q&A question evaluation.
|
32 |
+
"""
|
33 |
|
34 |
+
class Question_Model(BaseModel):
|
35 |
+
chapter_n: str = \
|
36 |
+
Field(...,
|
37 |
+
pattern=r'^Chapter_\d*$',
|
38 |
+
description=(
|
39 |
+
"which chapter to extract, the format of this function argumet"
|
40 |
+
"is with `Chapter_` as prefix concatenated with chapter number"
|
41 |
+
"in integer. For example, `Chapter_2`, `Chapter_10`."
|
42 |
+
"if no chapter number specified or user requested for random question"
|
43 |
+
"or user has no preference over which chapter of textbook to be tested"
|
44 |
+
"return `Chapter_0`"
|
45 |
)
|
46 |
+
)
|
47 |
+
class Answer_Model(BaseModel):
|
48 |
+
user_selected_answer: str = \
|
49 |
+
Field(...,
|
50 |
+
pattern=r'^[ABCD]$',
|
51 |
+
description=(
|
52 |
+
"which answer choice `A`, `B`, `C`, `D`"
|
53 |
+
"user selected. The return format should be"
|
54 |
+
"in single character such as A, B, C and D."
|
55 |
+
"if user's answer is contextually closer to a "
|
56 |
+
"particular answer choice, return the corresponding"
|
57 |
+
"alphabet A, B, C or D for the answer "
|
58 |
+
"is closest."
|
59 |
+
))
|
60 |
|
61 |
def get_qna_question(chapter_n: str) -> str:
|
62 |
"""
|
63 |
+
Use this tool to extract the chapter number from the body of input text,
|
64 |
+
thereafter, chapter number will be used as a filtering criteria for
|
65 |
+
extracting the right questions set from database.
|
66 |
+
The format of the function argument looks as follow:
|
67 |
+
It should be in the format with `Chapter_` as prefix.
|
68 |
Example 1: `Chapter_1` for first chapter
|
69 |
Example 2: For chapter 12 of the textbook, you should return `Chapter_12`
|
70 |
Example 3: `Chapter_5` for fifth chapter
|
71 |
Thereafter, the chapter_n argument will be passed to the function for Q&A question retrieval.
|
72 |
+
Once the question is retrieved from database, be reminded to ask user the question.
|
73 |
"""
|
74 |
con = sqlite3.connect(db_path)
|
75 |
cur = con.cursor()
|
76 |
|
77 |
+
|
78 |
+
filter_clause = "WHERE a.id IS NULL" if chapter_n == "Chapter_0" else f"WHERE a.id IS NULL AND chapter='{chapter_n}'"
|
79 |
+
sql_string = """SELECT q.id, question, option_1, option_2, option_3, option_4, q.correct_answer
|
80 |
+
FROM qna_tbl q LEFT JOIN answer_tbl a
|
81 |
+
ON q.id = a.id
|
82 |
""" + filter_clause
|
83 |
|
84 |
res = cur.execute(sql_string)
|
|
|
100 |
"C) " + option_3 + "\n" + \
|
101 |
"D) " + option_4
|
102 |
|
103 |
+
st.session_state.question_id = id
|
104 |
+
st.session_state.qna_answer = c_answer
|
105 |
+
|
106 |
con.close()
|
107 |
|
108 |
return qna_str
|
109 |
|
110 |
+
def evaluate_qna_answer(user_selected_answer: str) -> str:
|
111 |
+
"""
|
112 |
+
Use this tool to trigger the evaluation of user's provided input with the
|
113 |
+
correct answer of the Q&A question asked. When user provides answer to the
|
114 |
+
question asked, they can reply in natural language or giving the alphabet
|
115 |
+
symbol of which selected answer they think it's most reasonable.
|
116 |
+
The format of the function argument `user_selected_answer` looks as follow:
|
117 |
+
It should be in the format with character such as A, B, C and D.
|
118 |
+
Example 1: User's answer is `a`, it means choice `A`.
|
119 |
+
Example 2: User's answer is contextually closer to 3rd answer choice, it means `C`.
|
120 |
+
Example 3: User says last is the answer, it means `D`.
|
121 |
+
Thereafter, the `user_selected_answer` argument will be passed to the
|
122 |
+
function for Q&A question evaluation.
|
123 |
+
"""
|
124 |
+
answer_mapping = {
|
125 |
+
"A": 1,
|
126 |
+
"B": 2,
|
127 |
+
"C": 3,
|
128 |
+
"D": 4
|
129 |
+
}
|
130 |
+
num_mapping = dict((v,k) for k,v in answer_mapping.items())
|
131 |
+
|
132 |
+
user_answer_numeric = answer_mapping.get(user_selected_answer, None)
|
133 |
+
if user_answer_numeric is None:
|
134 |
+
raise Exception(f"User's answer can't be found: {user_selected_answer}")
|
135 |
+
|
136 |
+
question_id = st.session_state.question_id
|
137 |
+
qna_answer = st.session_state.qna_answer
|
138 |
+
qna_answer_alphabet = num_mapping[qna_answer]
|
139 |
+
|
140 |
+
con = sqlite3.connect(db_path)
|
141 |
+
cur = con.cursor()
|
142 |
+
sql_string = f"""INSERT INTO answer_tbl
|
143 |
+
VALUES ({question_id}, {qna_answer}, {user_answer_numeric})
|
144 |
+
"""
|
145 |
+
|
146 |
+
res = cur.execute(sql_string)
|
147 |
+
con.commit()
|
148 |
+
con.close()
|
149 |
+
|
150 |
+
if qna_answer == user_answer_numeric:
|
151 |
+
st.toast('Hooray!', icon='π')
|
152 |
+
time.sleep(0.3)
|
153 |
+
st.toast('Hooray!', icon='π')
|
154 |
+
time.sleep(0.3)
|
155 |
+
st.toast('Hooray!', icon='π')
|
156 |
+
st.balloons()
|
157 |
+
else:
|
158 |
+
st.toast('Omg..', icon='π
')
|
159 |
+
time.sleep(0.3)
|
160 |
+
st.toast('Omg..', icon='π
')
|
161 |
+
time.sleep(0.3)
|
162 |
+
st.toast('Omg..', icon='π
')
|
163 |
+
st.snow()
|
164 |
+
|
165 |
+
|
166 |
+
qna_answer_response = (
|
167 |
+
f"Your selected answer is `{user_selected_answer}`, "
|
168 |
+
f"but the actual answer is `{qna_answer_alphabet}`. "
|
169 |
+
)
|
170 |
+
|
171 |
+
return qna_answer_response
|
172 |
+
|
173 |
get_qna_question_tool = FunctionTool.from_defaults(
|
174 |
fn=get_qna_question,
|
175 |
name="Extract_Question",
|
176 |
+
description=qna_question_description,
|
177 |
+
fn_schema=Question_Model
|
178 |
+
)
|
179 |
+
|
180 |
+
evaluate_qna_answer_tool = FunctionTool.from_defaults(
|
181 |
+
fn=evaluate_qna_answer,
|
182 |
+
name="Evaluate_Answer",
|
183 |
+
description=qna_answer_description,
|
184 |
+
fn_schema=Answer_Model
|
185 |
)
|
reset_database.py
ADDED
@@ -0,0 +1,13 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
import os
|
2 |
+
import sqlite3
|
3 |
+
|
4 |
+
db_path = "./database/mock_qna.sqlite"
|
5 |
+
|
6 |
+
con = sqlite3.connect(db_path)
|
7 |
+
cur = con.cursor()
|
8 |
+
sql_string = "DELETE FROM answer_tbl"
|
9 |
+
res = cur.execute(sql_string)
|
10 |
+
con.commit()
|
11 |
+
con.close()
|
12 |
+
|
13 |
+
print("done cleaning up `answer_tbl`.")
|
streamlit_app.py
CHANGED
@@ -22,7 +22,7 @@ from llama_index.agent.openai import OpenAIAgent
|
|
22 |
from llama_index.core import Settings
|
23 |
|
24 |
from vision_api import get_transcribed_text
|
25 |
-
from qna_prompting import get_qna_question_tool
|
26 |
|
27 |
import nest_asyncio
|
28 |
nest_asyncio.apply()
|
@@ -107,6 +107,12 @@ if "feedback_key" not in st.session_state:
|
|
107 |
if "release_file" not in st.session_state:
|
108 |
st.session_state.release_file = "false"
|
109 |
|
|
|
|
|
|
|
|
|
|
|
|
|
110 |
def clear_chat_history():
|
111 |
st.session_state.messages = [{"role": "assistant",
|
112 |
"content": "How may I assist you today?",
|
@@ -193,18 +199,24 @@ def get_query_engine(input_files, llm_model, temperature,
|
|
193 |
streaming=True
|
194 |
)
|
195 |
|
|
|
|
|
|
|
|
|
|
|
|
|
196 |
hi_query_tool = QueryEngineTool.from_defaults(
|
197 |
query_engine=hi_content_engine,
|
198 |
name="vector_tool",
|
199 |
-
description=
|
200 |
-
"Provides information about Health Insurance landscape in Singapore. "
|
201 |
-
"Use a detailed plain text question as input to the tool."
|
202 |
-
)
|
203 |
)
|
|
|
204 |
agent = OpenAIAgent.from_tools(tools=[
|
205 |
hi_query_tool,
|
206 |
-
get_qna_question_tool
|
|
|
207 |
],
|
|
|
208 |
llm=llm,
|
209 |
verbose=True)
|
210 |
print("loaded AI agent, let's begin the chat!")
|
@@ -299,6 +311,7 @@ if st.session_state.messages[-1]["role"] != "assistant":
|
|
299 |
placeholder = st.empty()
|
300 |
full_response = ""
|
301 |
for token in response.response_gen:
|
|
|
302 |
full_response += token
|
303 |
placeholder.markdown(full_response)
|
304 |
placeholder.markdown(full_response)
|
|
|
22 |
from llama_index.core import Settings
|
23 |
|
24 |
from vision_api import get_transcribed_text
|
25 |
+
from qna_prompting import get_qna_question_tool, evaluate_qna_answer_tool
|
26 |
|
27 |
import nest_asyncio
|
28 |
nest_asyncio.apply()
|
|
|
107 |
if "release_file" not in st.session_state:
|
108 |
st.session_state.release_file = "false"
|
109 |
|
110 |
+
if "question_id" not in st.session_state:
|
111 |
+
st.session_state.question_id = None
|
112 |
+
|
113 |
+
if "qna_answer" not in st.session_state:
|
114 |
+
st.session_state.qna_answer = None
|
115 |
+
|
116 |
def clear_chat_history():
|
117 |
st.session_state.messages = [{"role": "assistant",
|
118 |
"content": "How may I assist you today?",
|
|
|
199 |
streaming=True
|
200 |
)
|
201 |
|
202 |
+
hi_textbook_query_description = """
|
203 |
+
Use this tool to extract content from Health Insurance textbook
|
204 |
+
that has 15 chapters in total. When user wants to learn more about a
|
205 |
+
particular chapter, this tool will help to assist user to get better
|
206 |
+
understanding of the content of the textbook.
|
207 |
+
"""
|
208 |
hi_query_tool = QueryEngineTool.from_defaults(
|
209 |
query_engine=hi_content_engine,
|
210 |
name="vector_tool",
|
211 |
+
description=hi_textbook_query_description
|
|
|
|
|
|
|
212 |
)
|
213 |
+
|
214 |
agent = OpenAIAgent.from_tools(tools=[
|
215 |
hi_query_tool,
|
216 |
+
get_qna_question_tool,
|
217 |
+
evaluate_qna_answer_tool
|
218 |
],
|
219 |
+
max_function_calls=1,
|
220 |
llm=llm,
|
221 |
verbose=True)
|
222 |
print("loaded AI agent, let's begin the chat!")
|
|
|
311 |
placeholder = st.empty()
|
312 |
full_response = ""
|
313 |
for token in response.response_gen:
|
314 |
+
token = token.replace("\n", " \n")
|
315 |
full_response += token
|
316 |
placeholder.markdown(full_response)
|
317 |
placeholder.markdown(full_response)
|