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:3c380902975056aca9cbc32ff2948725fc9901a59ae01e2cf1634f475e1c889f
3
- size 8192
 
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.db\"\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,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.db\""
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.db\"\n",
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.db"
7
- description = """
8
- Use this tool to extract the chapter information from the body of the input text,
9
- when user wants to learn more about a particular chapter and requested to be asked
10
- with a question to test his/her understanding.
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 QnA_Model(BaseModel):
20
- chapter_n: str = Field(...,
21
- pattern=r'^Chapter_\d*$',
22
- description=(
23
- "which chapter to extract, the format of this function argumet"
24
- "is with `Chapter_` as prefix concatenated with chapter number"
25
- "in integer. For example, `Chapter_2`, `Chapter_10`."
26
- "if no chapter number specified or user requested for random question"
27
- "or user has no preference over which chapter of textbook to be tested"
28
- "return `Chapter_0`"
29
- )
30
  )
 
 
 
 
 
 
 
 
 
 
 
 
 
 
31
 
32
  def get_qna_question(chapter_n: str) -> str:
33
  """
34
- Use this tool to extract the chapter information from the body of the input text,
35
- the format looks as follow:
36
- The output should be in the format with `Chapter_` as prefix.
 
 
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
- filter_clause = "" if chapter_n == "Chapter_0" else f"WHERE chapter='{chapter_n}'"
46
- sql_string = """SELECT id, question, option_1, option_2, option_3, option_4, correct_answer
47
- FROM qna_tbl
 
 
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=description,
77
- fn_schema=QnA_Model
 
 
 
 
 
 
 
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)