tiennguyenbnbk
commited on
Commit
•
fbe1bdb
1
Parent(s):
abd1274
Upload 4 files
Browse files- app.py +121 -0
- chains.py +23 -0
- parsers.py +113 -0
- prompts.py +46 -0
app.py
ADDED
@@ -0,0 +1,121 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
import gradio as gr
|
2 |
+
from textwrap import dedent
|
3 |
+
|
4 |
+
from chains import setup_chain_v2
|
5 |
+
|
6 |
+
chain = setup_chain_v2()
|
7 |
+
|
8 |
+
with gr.Blocks() as demo:
|
9 |
+
with gr.Row():
|
10 |
+
# --- Nhóm các thành phần nhập liệu ---
|
11 |
+
with gr.Column():
|
12 |
+
with gr.Group(): # Nhóm "Thái độ"
|
13 |
+
attitude_components = [
|
14 |
+
gr.Radio(["Con đi học đầy đủ", "Con chưa đi học đầy đủ"], label="Thái độ", show_label=True),
|
15 |
+
gr.Radio(["Con đi học đúng giờ", "Con vẫn còn đi học muộn"], show_label=False),
|
16 |
+
gr.Radio(["Con hoàn thành BTVN", "Con chưa hoàn thành đầy đủ BTVN"], show_label=False),
|
17 |
+
gr.Radio(["Con tập trung trong lớp học", "Con chưa tập trung trong lớp học"], show_label=False),
|
18 |
+
gr.Radio(["Con sôi nổi, hào hứng trong các buổi học", "Con cần được động viên, khích lệ thường xuyên"], show_label=False),
|
19 |
+
]
|
20 |
+
|
21 |
+
with gr.Group(): # Nhóm "Kiến thức"
|
22 |
+
knowleadge_components = [
|
23 |
+
gr.Radio(["Con có vốn từ vựng phong phú", "Con có vốn từ vựng cơ bản", "Con cần trau dồi vốn từ vựng"], label="Kiến thức"),
|
24 |
+
gr.Textbox(lines=1, label="Con nhớ và sử dụng linh hoạt các từ vựng đã học trong chủ điểm:", show_label=True),
|
25 |
+
gr.Textbox(lines=1, label="Con chưa nhớ từ vựng trong chủ điểm:", show_label=True),
|
26 |
+
gr.Radio(["Con có thể sử dụng cấu trúc ngữ pháp thành thạo", "Con có thể sử dụng cấu trúc ngữ pháp cơ bản", "Con cần trau dồi cấu trúc ngữ pháp"], show_label=False),
|
27 |
+
gr.Textbox(lines=1, label="Các cấu trúc con đã thành thạo:", show_label=True),
|
28 |
+
gr.Textbox(lines=1, label="Các cấu trúc con chưa thành thạo:", show_label=True),
|
29 |
+
]
|
30 |
+
|
31 |
+
with gr.Group(): # Nhóm "Tương tác"
|
32 |
+
interaction_components = [
|
33 |
+
gr.Radio(["Tích cực tham gia vào các hoạt động trên lớp", "Ít tham gia vào các hoạt động trên lớp"], label="Tương tác"),
|
34 |
+
gr.Radio(["Chăm chú nghe giảng", "Chưa chăm chú nghe giảng"], show_label=False),
|
35 |
+
gr.Radio(["Hăng hái giơ tay phát biểu và xây dựng bài", "Ít tương tác và chưa chủ động tham gia xây dựng bài"], show_label=False),
|
36 |
+
]
|
37 |
+
|
38 |
+
with gr.Column(): # Nhóm "Kỹ năng"
|
39 |
+
skill_components = [
|
40 |
+
gr.Radio(["Con nghe hiểu và phản xạ tốt đối với các yêu cầu của thầy cô", "Con phản xạ còn chậm trước các yêu cầu của thầy cô"], label="Kỹ năng", info="", show_label=True),
|
41 |
+
gr.Radio(["Con thực hành thành thạo các dạng bài nghe", "Con cần rèn luyện thêm để cải thiện kỹ năng nghe với những dạng bài khác nhau"], label="", info="", show_label=False),
|
42 |
+
gr.Radio(["Con có giọng nói to, rõ ràng", "Giọng nói đôi khi nhỏ và không rõ ràng"], label="", info="", show_label=False),
|
43 |
+
gr.Radio(["Con phát âm đúng, đặc biệt là các âm cuối", "Con phát âm chưa hoàn toàn chính xác, đặc biệt là các âm cuối"], label="", info="", show_label=False),
|
44 |
+
gr.Radio(["Con có khả năng nói trôi chảy, lưu loát", "Con cần chú ý hơn về ngữ điệu khi nói, thỉnh thoảng con còn nói ngập ngừng, chưa thực sự lưu loát"], label="", info="", show_label=False),
|
45 |
+
gr.Radio(["Con đọc hiểu tốt và làm đúng yêu cầu đề bài", "Con cần luyện tập kỹ năng đọc để nắm vững toàn bộ yêu cầu đề bài"], label="", info="", show_label=False),
|
46 |
+
gr.Radio(["Con hiểu được nội dung chính và chi tiết quan trọng của bài đọc", "Con cần luyện tập thêm để hiểu được nội dung chính và chi tiết quan trọng trong bài đọc"], label="", info="", show_label=False),
|
47 |
+
gr.Radio(["Viết đúng chính tả", "Đôi khi viết sai chính tả"], label="", info="", show_label=False),
|
48 |
+
gr.Radio(["Viết đúng ngữ pháp và cấu trúc câu", "Khi viết còn mắc lỗi ngữ pháp và cấu trúc câu"], label="", info="", show_label=False),
|
49 |
+
gr.Radio(["Sử dụng từ vựng phong phú khi viết", "Từ vựng sử dụng chưa đa dạng"], label="", info="", show_label=False),
|
50 |
+
gr.Radio(["Khi viết con diễn đạt ý tưởng rõ ràng và mạch lạc", "Khi viết con diễn đạt ý tưởng chưa rõ ràng và mạch lạc"], label="", info="", show_label=False),
|
51 |
+
]
|
52 |
+
|
53 |
+
# --- Nhóm các thành phần xuất kết quả ---
|
54 |
+
with gr.Column():
|
55 |
+
debug_output = gr.Textbox(label="Thông tin đánh giá", show_copy_button=True)
|
56 |
+
attitude_output = gr.Textbox(label="Thái độ", show_copy_button=True)
|
57 |
+
knowleadge_output = gr.Textbox(label="Kiến thức", show_copy_button=True)
|
58 |
+
skill_output = gr.Textbox(label="Kỹ năng", show_copy_button=True)
|
59 |
+
interaction_output = gr.Textbox(label="Tương tác", show_copy_button=True)
|
60 |
+
detail_output = gr.Textbox(label="Chi tiết", show_copy_button=True)
|
61 |
+
|
62 |
+
greet_btn = gr.Button("Tạo nhận xét")
|
63 |
+
|
64 |
+
# --- Hàm xử lý dữ liệu và tạo nhận xét ---
|
65 |
+
def gen(*args):
|
66 |
+
# Tách các tham số thành các nhóm tương ứng
|
67 |
+
attitude_data = args[:5]
|
68 |
+
knowleadge_data = args[5:11]
|
69 |
+
skill_data = args[11:22]
|
70 |
+
interaction_data = args[22:]
|
71 |
+
|
72 |
+
attitude_sentence = ""
|
73 |
+
for data in attitude_data:
|
74 |
+
if data:
|
75 |
+
attitude_sentence += data + ". "
|
76 |
+
|
77 |
+
knowleadge_sentence = ""
|
78 |
+
for idx, data in enumerate(knowleadge_data):
|
79 |
+
if data:
|
80 |
+
if idx == 1:
|
81 |
+
knowleadge_sentence += "Con nhớ và sử dụng linh hoạt các từ vựng đã học trong chủ điểm: " + data + ". "
|
82 |
+
elif idx == 2:
|
83 |
+
knowleadge_sentence += "Con chưa nhớ từ vựng trong chủ điểm: " + data + ". "
|
84 |
+
elif idx == 4:
|
85 |
+
knowleadge_sentence += "Các cấu trúc con đã thành thạo: " + data + ". "
|
86 |
+
elif idx == 5:
|
87 |
+
knowleadge_sentence += "Các cấu trúc con chưa thành thạo: " + data + ". "
|
88 |
+
else:
|
89 |
+
knowleadge_sentence += data + ". "
|
90 |
+
|
91 |
+
skill_sentence = ""
|
92 |
+
for data in skill_data:
|
93 |
+
if data:
|
94 |
+
skill_sentence += data + ". "
|
95 |
+
|
96 |
+
interaction_sentence = ""
|
97 |
+
for data in interaction_data:
|
98 |
+
if data:
|
99 |
+
interaction_sentence += data + ". "
|
100 |
+
|
101 |
+
full_sentence = dedent(f"""\
|
102 |
+
Thái độ: {attitude_sentence.strip() if len(attitude_sentence.strip()) else "Không có đánh giá"}
|
103 |
+
Kiến thức: {knowleadge_sentence.strip()if len(knowleadge_sentence.strip()) else "Không có đánh giá"}
|
104 |
+
Kỹ năng: {skill_sentence.strip()if len(skill_sentence.strip()) else "Không có đánh giá"}
|
105 |
+
Tương tác: {interaction_sentence.strip()if len(interaction_sentence.strip()) else "Không có đánh giá"}
|
106 |
+
""")
|
107 |
+
result = chain.invoke({"query": full_sentence})
|
108 |
+
|
109 |
+
return {
|
110 |
+
debug_output: full_sentence.strip(),
|
111 |
+
attitude_output: result['atitude'],
|
112 |
+
knowleadge_output: result['knowleadge'],
|
113 |
+
skill_output: result['skill'],
|
114 |
+
interaction_output: result['interaction'],
|
115 |
+
detail_output: result['detail']
|
116 |
+
}
|
117 |
+
|
118 |
+
greet_btn.click(gen, inputs=[*attitude_components, *knowleadge_components, *skill_components, *interaction_components], outputs=[debug_output, attitude_output, knowleadge_output, skill_output, interaction_output, detail_output])
|
119 |
+
|
120 |
+
if __name__ == "__main__":
|
121 |
+
demo.launch(server_name='192.168.8.76', server_port=7860,ssl_verify=False, auth=('admin', 'vuihoc.vn'))
|
chains.py
ADDED
@@ -0,0 +1,23 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
from langchain_google_genai import GoogleGenerativeAI
|
2 |
+
from prompts import evaluation_prompt_v1, evaluation_prompt_v2
|
3 |
+
from parsers import evaluation_parser_v1, evaluation_parser_v2
|
4 |
+
from dotenv import load_dotenv
|
5 |
+
import os
|
6 |
+
load_dotenv()
|
7 |
+
|
8 |
+
|
9 |
+
def setup_chain_v1():
|
10 |
+
llm = GoogleGenerativeAI(model='gemini-1.5-pro',
|
11 |
+
temperature=0.5,
|
12 |
+
google_api_key=os.getenv('GOOGLE_API_KEY'))
|
13 |
+
|
14 |
+
chain = evaluation_prompt_v1 | llm | evaluation_parser_v1
|
15 |
+
return chain
|
16 |
+
|
17 |
+
def setup_chain_v2():
|
18 |
+
llm = GoogleGenerativeAI(model='gemini-1.5-pro',
|
19 |
+
temperature=0.5,
|
20 |
+
google_api_key=os.getenv('GOOGLE_API_KEY'))
|
21 |
+
|
22 |
+
chain = evaluation_prompt_v2 | llm | evaluation_parser_v2
|
23 |
+
return chain
|
parsers.py
ADDED
@@ -0,0 +1,113 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
from __future__ import annotations
|
2 |
+
|
3 |
+
import json
|
4 |
+
from json import JSONDecodeError
|
5 |
+
from typing import Any, List, Optional, Type, TypeVar, Union
|
6 |
+
|
7 |
+
import jsonpatch # type: ignore[import]
|
8 |
+
import pydantic # pydantic: ignore
|
9 |
+
|
10 |
+
from langchain_core.exceptions import OutputParserException
|
11 |
+
from langchain_core.output_parsers.format_instructions import JSON_FORMAT_INSTRUCTIONS
|
12 |
+
from langchain_core.output_parsers.transform import BaseCumulativeTransformOutputParser
|
13 |
+
from langchain_core.outputs import Generation
|
14 |
+
from langchain_core.utils.json import (
|
15 |
+
parse_and_check_json_markdown,
|
16 |
+
parse_json_markdown,
|
17 |
+
parse_partial_json,
|
18 |
+
)
|
19 |
+
from langchain_core.utils.pydantic import PYDANTIC_MAJOR_VERSION
|
20 |
+
|
21 |
+
from langchain_core.pydantic_v1 import BaseModel, Field
|
22 |
+
|
23 |
+
if PYDANTIC_MAJOR_VERSION < 2:
|
24 |
+
PydanticBaseModel = pydantic.BaseModel
|
25 |
+
|
26 |
+
else:
|
27 |
+
from pydantic.v1 import BaseModel # pydantic: ignore
|
28 |
+
|
29 |
+
# Union type needs to be last assignment to PydanticBaseModel to make mypy happy.
|
30 |
+
PydanticBaseModel = Union[BaseModel, pydantic.BaseModel] # type: ignore
|
31 |
+
|
32 |
+
TBaseModel = TypeVar("TBaseModel", bound=PydanticBaseModel)
|
33 |
+
|
34 |
+
|
35 |
+
class JsonOutputParser(BaseCumulativeTransformOutputParser[Any]):
|
36 |
+
"""Parse the output of an LLM call to a JSON object.
|
37 |
+
|
38 |
+
When used in streaming mode, it will yield partial JSON objects containing
|
39 |
+
all the keys that have been returned so far.
|
40 |
+
|
41 |
+
In streaming, if `diff` is set to `True`, yields JSONPatch operations
|
42 |
+
describing the difference between the previous and the current object.
|
43 |
+
"""
|
44 |
+
|
45 |
+
pydantic_object: Optional[Type[TBaseModel]] = None # type: ignore
|
46 |
+
|
47 |
+
def _diff(self, prev: Optional[Any], next: Any) -> Any:
|
48 |
+
return jsonpatch.make_patch(prev, next).patch
|
49 |
+
|
50 |
+
def _get_schema(self, pydantic_object: Type[TBaseModel]) -> dict[str, Any]:
|
51 |
+
if PYDANTIC_MAJOR_VERSION == 2:
|
52 |
+
if issubclass(pydantic_object, pydantic.BaseModel):
|
53 |
+
return pydantic_object.model_json_schema()
|
54 |
+
elif issubclass(pydantic_object, pydantic.v1.BaseModel):
|
55 |
+
return pydantic_object.schema()
|
56 |
+
return pydantic_object.schema()
|
57 |
+
|
58 |
+
def parse_result(self, result: List[Generation], *, partial: bool = False) -> Any:
|
59 |
+
text = result[0].text
|
60 |
+
text = text.strip()
|
61 |
+
if partial:
|
62 |
+
try:
|
63 |
+
return parse_json_markdown(text)
|
64 |
+
except JSONDecodeError:
|
65 |
+
return None
|
66 |
+
else:
|
67 |
+
try:
|
68 |
+
return parse_json_markdown(text)
|
69 |
+
except JSONDecodeError as e:
|
70 |
+
msg = f"Invalid json output: {text}"
|
71 |
+
raise OutputParserException(msg, llm_output=text) from e
|
72 |
+
|
73 |
+
def parse(self, text: str) -> Any:
|
74 |
+
return self.parse_result([Generation(text=text)])
|
75 |
+
|
76 |
+
def get_format_instructions(self) -> str:
|
77 |
+
if self.pydantic_object is None:
|
78 |
+
return "Return a JSON object."
|
79 |
+
else:
|
80 |
+
# Copy schema to avoid altering original Pydantic schema.
|
81 |
+
schema = {k: v for k, v in self._get_schema(self.pydantic_object).items()}
|
82 |
+
|
83 |
+
# Remove extraneous fields.
|
84 |
+
reduced_schema = schema
|
85 |
+
if "title" in reduced_schema:
|
86 |
+
del reduced_schema["title"]
|
87 |
+
if "type" in reduced_schema:
|
88 |
+
del reduced_schema["type"]
|
89 |
+
# Ensure json in context is well-formed with double quotes.
|
90 |
+
schema_str = json.dumps(reduced_schema, ensure_ascii=False)
|
91 |
+
return JSON_FORMAT_INSTRUCTIONS.format(schema=schema_str)
|
92 |
+
|
93 |
+
@property
|
94 |
+
def _type(self) -> str:
|
95 |
+
return "simple_json_output_parser"
|
96 |
+
|
97 |
+
|
98 |
+
class Evaluation(BaseModel):
|
99 |
+
"""Nhận xét của giáo viên cho kết quả học tập của học sinh"""
|
100 |
+
summary: str = Field(description="Nhận xét ngắn gọn khoảng 50 từ")
|
101 |
+
detail: str = Field(description="Nhận xét chi tiết khoảng 200 từ")
|
102 |
+
|
103 |
+
evaluation_parser_v1 = JsonOutputParser(pydantic_object=Evaluation)
|
104 |
+
|
105 |
+
class EvaluationForStudent(BaseModel):
|
106 |
+
"""Nhận xét của giáo viên cho kết quả học tập của học sinh"""
|
107 |
+
atitude: str = Field(description="Nhận xét ngắn gọn khoảng 50 từ cho thái độ của học sinh")
|
108 |
+
knowleadge: str = Field(description="Nhận xét ngắn gọn khoảng 50 từ cho kiến thức của học sinh")
|
109 |
+
skill: str = Field(description="Nhận xét ngắn gọn khoảng 50 từ cho kỹ năng của học sinh")
|
110 |
+
interaction: str = Field(description="Nhận xét ngắn gọn khoảng 50 từ cho tương tác của học sinh")
|
111 |
+
detail: str = Field(description="Nhận xét chi tiết khoảng 200 từ")
|
112 |
+
|
113 |
+
evaluation_parser_v2 = JsonOutputParser(pydantic_object=EvaluationForStudent)
|
prompts.py
ADDED
@@ -0,0 +1,46 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
from langchain.prompts import PromptTemplate
|
2 |
+
from parsers import evaluation_parser_v1, evaluation_parser_v2
|
3 |
+
|
4 |
+
template_v1 = '''
|
5 |
+
Bạn là giáo viên của {student_name} trong suốt thời gian vừa qua tại Vui Học, hãy dựa trên những đánh giá rồi đưa ra nhận xét về kết quả học tập để gửi tới phụ huynh của học sinh {student_name}.
|
6 |
+
Hãy viết nhận xét với giọng điệu gần gũi, thân thiện, nhiều cảm xúc, xưng mình là cô gọi học sinh là con. Nhận xét của bạn sẽ giúp phụ huynh hiểu rõ hơn về tình hình học tập của học sinh.
|
7 |
+
|
8 |
+
Hãy viết nhận xét thành hai loại:
|
9 |
+
Nhận xét tóm tắt khoảng 50 từ về kết quả học tập, gọi học sinh là con thay vì tên riêng cho tóm tắt.
|
10 |
+
Nhận xét chi tiết khoảng 200 từ về kết quả học tập, hãy bắt đầu bằng lời chào phụ huynh học sinh và kết thúc bằng lời động viên.
|
11 |
+
|
12 |
+
Thông tin đánh giá:
|
13 |
+
{query}
|
14 |
+
|
15 |
+
|
16 |
+
{format_instructions}
|
17 |
+
|
18 |
+
'''
|
19 |
+
|
20 |
+
evaluation_prompt_v1 = PromptTemplate(
|
21 |
+
input_variables=["student_name", "query"],
|
22 |
+
template=template_v1,
|
23 |
+
partial_variables={"format_instructions": evaluation_parser_v1.get_format_instructions()},
|
24 |
+
)
|
25 |
+
|
26 |
+
template_v2 = '''
|
27 |
+
Bạn là giáo viên của học sinh trong suốt thời gian vừa qua tại Vui Học, hãy dựa trên những đánh giá rồi đưa ra nhận xét về kết quả học tập để gửi tới phụ huynh của học sinh.
|
28 |
+
Hãy viết nhận xét với giọng điệu gần gũi, thân thiện, nhiều cảm xúc tránh lặp từ, xưng mình là cô gọi học sinh là con thay vì tên riêng. Nhận xét của bạn sẽ giúp phụ huynh hiểu rõ hơn về tình hình học tập của học sinh.
|
29 |
+
|
30 |
+
Hãy viết nhận xét cho từng tiêu chí và tổng hợp chi tiết:
|
31 |
+
Nhận xét cho mỗi tiêu chí khoảng 50 từ, nếu không có thông tin đánh giá hãy bỏ qua tiêu chí đó.
|
32 |
+
Nhận xét chi tiết khoảng 200 từ về kết quả học tập, hãy bắt đầu bằng lời chào học sinh và kết thúc bằng lời động viên.
|
33 |
+
|
34 |
+
Thông tin đánh giá:
|
35 |
+
{query}
|
36 |
+
|
37 |
+
|
38 |
+
{format_instructions}
|
39 |
+
|
40 |
+
'''
|
41 |
+
|
42 |
+
evaluation_prompt_v2 = PromptTemplate(
|
43 |
+
input_variables=["student_name", "query"],
|
44 |
+
template=template_v2,
|
45 |
+
partial_variables={"format_instructions": evaluation_parser_v2.get_format_instructions()},
|
46 |
+
)
|