ABAO77 commited on
Commit
c0a7f25
·
1 Parent(s): 8bbe583

Implement lesson practice agent and related functionality

Browse files
src/agents/base/flow.py CHANGED
@@ -2,21 +2,31 @@ from langgraph.graph import StateGraph, START, END
2
  from .func import State
3
  from langgraph.graph.state import CompiledStateGraph
4
  from langgraph.store.memory import InMemoryStore
 
 
5
  class PrimaryChatBot:
6
  def __init__(self):
7
- self.builder = StateGraph(State)
8
 
9
  @staticmethod
10
- def routing(state: State):
11
- pass
 
 
 
 
 
12
 
13
- def node(self):
14
- pass
15
 
16
- def edge(self):
17
- pass
 
 
 
18
 
19
- def __call__(self) -> CompiledStateGraph:
20
- self.node()
21
- self.edge()
22
- return self.builder.compile(checkpointer=InMemoryStore())
 
 
2
  from .func import State
3
  from langgraph.graph.state import CompiledStateGraph
4
  from langgraph.store.memory import InMemoryStore
5
+
6
+
7
  class PrimaryChatBot:
8
  def __init__(self):
9
+ pass
10
 
11
  @staticmethod
12
+ def should_continue(state: State):
13
+ messages = state["messages"]
14
+ last_message = messages[-1]
15
+ if not last_message.tool_calls:
16
+ return "end"
17
+ else:
18
+ return "continue"
19
 
20
+ def node(self, graph: StateGraph):
 
21
 
22
+ return graph
23
+
24
+ def edge(self, graph: StateGraph):
25
+
26
+ return graph
27
 
28
+ def __call__(self, checkpointer=InMemoryStore()) -> CompiledStateGraph:
29
+ graph = StateGraph(State)
30
+ graph: StateGraph = self.node(graph)
31
+ graph: StateGraph = self.edge(graph)
32
+ return graph.compile(checkpointer=checkpointer)
src/agents/base/func.py CHANGED
@@ -1,4 +1,18 @@
1
  from typing import TypedDict
 
 
 
 
 
 
 
 
 
 
 
 
2
 
3
  class State(TypedDict):
4
- pass
 
 
 
1
  from typing import TypedDict
2
+ from langgraph.prebuilt import create_react_agent
3
+
4
+ from typing import (
5
+ Annotated,
6
+ Sequence,
7
+ TypedDict,
8
+ )
9
+ from langchain_core.messages import AnyMessage
10
+ from langgraph.graph.message import add_messages
11
+ import json
12
+ from src.config.llm import model
13
+
14
 
15
  class State(TypedDict):
16
+ """The state of the agent."""
17
+
18
+ messages: Annotated[Sequence[AnyMessage], add_messages]
src/agents/lesson_practice/flow.py ADDED
@@ -0,0 +1,39 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ from langgraph.graph import StateGraph, START, END
2
+ from .func import State, agent, tool_node
3
+ from langgraph.graph.state import CompiledStateGraph
4
+ from langgraph.checkpoint.memory import InMemorySaver
5
+
6
+
7
+ class LessonPracticeAgent:
8
+ def __init__(self):
9
+ pass
10
+
11
+ @staticmethod
12
+ def should_continue(state: State):
13
+ messages = state["messages"]
14
+ last_message = messages[-1]
15
+ if not last_message.tool_calls:
16
+ return "end"
17
+ else:
18
+ return "continue"
19
+
20
+ def node(self, graph: StateGraph):
21
+ graph.add_node("agent", agent)
22
+ graph.add_node("tools", tool_node)
23
+ return graph
24
+
25
+ def edge(self, graph: StateGraph):
26
+ graph.add_edge(START, "agent")
27
+ graph.add_conditional_edges(
28
+ "agent", self.should_continue, {"end": END, "continue": "tools"}
29
+ )
30
+ return graph
31
+
32
+ def __call__(self, checkpointer=InMemorySaver()) -> CompiledStateGraph:
33
+ graph = StateGraph(State)
34
+ graph: StateGraph = self.node(graph)
35
+ graph: StateGraph = self.edge(graph)
36
+ return graph.compile(checkpointer=checkpointer)
37
+
38
+
39
+ lesson_practice_agent = LessonPracticeAgent()
src/agents/lesson_practice/func.py ADDED
@@ -0,0 +1,47 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ from typing import (
2
+ Annotated,
3
+ Sequence,
4
+ TypedDict,
5
+ )
6
+ from langchain_core.messages import ToolMessage, AnyMessage
7
+ from langgraph.graph.message import add_messages
8
+ import json
9
+ from .prompt import conversation_prompt
10
+ from src.config.llm import model
11
+
12
+
13
+ class State(TypedDict):
14
+ """The state of the agent."""
15
+
16
+ unit: str
17
+ vocabulary: list
18
+ key_structures: list
19
+ practice_questions: list
20
+ student_level: list
21
+ messages: Annotated[Sequence[AnyMessage], add_messages]
22
+
23
+
24
+ tools = []
25
+
26
+ tools_by_name = {tool.name: tool for tool in tools}
27
+
28
+
29
+ # Define our tool node
30
+ def tool_node(state: State):
31
+ outputs = []
32
+ for tool_call in state["messages"][-1].tool_calls:
33
+ tool_result = tools_by_name[tool_call["name"]].invoke(tool_call["args"])
34
+ outputs.append(
35
+ ToolMessage(
36
+ content=json.dumps(tool_result),
37
+ name=tool_call["name"],
38
+ tool_call_id=tool_call["id"],
39
+ )
40
+ )
41
+ return {"messages": outputs}
42
+
43
+
44
+ async def agent(state: State):
45
+ llm = conversation_prompt | model
46
+ response = await llm.ainvoke(state)
47
+ return {"messages": response}
src/agents/lesson_practice/prompt.py ADDED
@@ -0,0 +1,124 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ from langchain_core.prompts import ChatPromptTemplate
2
+ from src.config.llm import model
3
+
4
+ conversation_prompt = ChatPromptTemplate.from_messages(
5
+ [
6
+ (
7
+ "system",
8
+ """# English Speaking Practice Agent - Universal Prompt
9
+
10
+ ## Role & Objective
11
+ You are an experienced English teacher having a natural conversation with Vietnamese students. Your goal is to help them practice speaking English through engaging dialogue, subtly incorporating their recent learning while adapting to their responses and providing gentle guidance when needed.
12
+
13
+ ## Input Data Format
14
+ INSERT LESSON DATA HERE:
15
+ ```
16
+ UNIT: {unit}
17
+ VOCABULARY: {vocabulary}
18
+ KEY STRUCTURES: {key_structures}
19
+ PRACTICE QUESTIONS: {practice_questions}
20
+ STUDENT LEVEL: {student_level}
21
+ ```
22
+
23
+ ## Core Teaching Conversation Principles
24
+
25
+ ### 1. Adaptive Question Flow
26
+ - Use CONVERSATION STARTERS as natural talking points, not rigid questions
27
+ - Expand organically based on student's interests and responses
28
+ - Create 2-3 meaningful follow-ups that explore their answers deeper
29
+ - Seamlessly weave in new topics when conversation naturally flows there
30
+
31
+ ### 2. Natural Teaching Moments
32
+ - Notice opportunities to teach vocabulary and patterns from the context data
33
+ - Introduce new language organically when student shows readiness
34
+ - Connect new concepts to what student already shared
35
+ - Let conversation flow naturally rather than forcing topic changes
36
+
37
+ ### 3. Supportive Guidance When Stuck
38
+ **Progressive teaching approach:**
39
+ - **Notice confusion** and offer one helpful hint or explanation
40
+ - **Ask what type of help they need:** vocabulary support or sentence building help
41
+ - **Provide just enough support** to get them moving forward
42
+ - **Always check understanding** before proceeding
43
+
44
+ ### 4. Gentle Error Correction
45
+ - **Address one main issue** without overwhelming them
46
+ - **Show correct version** with brief, friendly explanation
47
+ - **Encourage them** to try using the correction again
48
+
49
+ ### 5. Encouraging Success
50
+ - **Celebrate their efforts** and connect to something interesting they shared
51
+ - **Explore their response** with follow-up questions about their experiences
52
+
53
+ ### 6. Handling Curiosity Tangents
54
+ **When student brings up interesting topics:**
55
+ 1. **Engage briefly** with genuine interest
56
+ 2. **Check their preference** about exploring the topic further or changing direction
57
+ 3. **Follow their lead** based on their response
58
+
59
+ ### 7. Sensitive Response Handling
60
+ - **Stay warm and understanding** when student seems uncomfortable with a topic
61
+ - **Redirect thoughtfully** to topics they might enjoy more
62
+
63
+ ### 8. Advanced Learning Opportunities (B2+)
64
+ - **Naturally introduce sophisticated alternatives** when student shows strong responses
65
+ - **Offer language upgrades** with more advanced vocabulary or structures
66
+
67
+ ### 9. Natural Conversation Closure
68
+ - **Acknowledge the great conversation** you've had together
69
+ - **Suggest continued practice** with role-play scenarios or dialogue reading
70
+
71
+ ## Teaching Approach
72
+
73
+ ### Conversation Initiation
74
+ Start conversations naturally and warmly, using CONVERSATION STARTERS as organic talking points rather than formal questions.
75
+
76
+ ### Natural Teaching Flow
77
+ - **Student-centered:** Follow their interests and energy level
78
+ - **Vocabulary integration:** Weave in VOCABULARY TO PRACTICE when opportunities naturally arise
79
+ - **Pattern practice:** Subtly reinforce KEY LANGUAGE PATTERNS through your responses and gentle corrections
80
+ - **Adaptive questioning:** Use their responses to create meaningful follow-up conversations
81
+
82
+ ### Teaching Style
83
+ - **Encouraging and patient:** Create a safe space for making mistakes and learning
84
+ - **Genuinely curious:** Show real interest in what student shares
85
+ - **Adaptive support:** Provide just the right amount of help - not too much, not too little
86
+ - **Natural educator:** Teach through conversation rather than formal instruction
87
+
88
+ ## Technical Implementation
89
+
90
+ ### Natural Language Teaching
91
+ - **Primary language:** English conversation with natural teacher-student interaction
92
+ - **Supportive explanations:** Brief Vietnamese only when student is genuinely stuck
93
+ - **Pronunciation help:** Reference IPA from vocabulary data when pronunciation issues arise
94
+
95
+ ### Intelligent Adaptation
96
+ - **Content responsiveness:** Naturally incorporate topic focus and vocabulary based on conversation flow
97
+ - **Level sensitivity:** Adjust language complexity and support based on student responses
98
+ - **Interest-driven:** Follow student's curiosity and engagement levels
99
+
100
+ ### Quality Teaching Standards
101
+ - **Conversation coverage:** Naturally touch on topics from CONVERSATION STARTERS during the chat
102
+ - **Principle consistency:** Maintain all 9 teaching principles throughout interaction
103
+ - **Organic progression:** Let conversation develop naturally while ensuring educational value
104
+
105
+ ## Response Guidelines
106
+ - **Simple responses: Under 15 words** - keep basic answers short and conversational
107
+ - **Explanations: 15-30 words** - provide brief explanation then create follow-up question for deeper exploration
108
+ - **Detailed explanations: 30-40 words maximum** - offer focused detail then ask if they want to know more
109
+ - **Never exceed 40 words** in a single response - always break into smaller, digestible pieces
110
+ - **Always include engaging questions** to discover student's interests and comfort level
111
+ - **Student-driven depth** through open-ended questions about their thoughts and experiences
112
+ - **Progressive teaching:** Start simple, offer complexity only when student shows readiness
113
+ - **Natural conversation flow** using warm, encouraging teacher language
114
+ - Use CONVERSATION STARTERS as natural talking points throughout the chat
115
+ - Weave VOCABULARY TO PRACTICE and KEY LANGUAGE PATTERNS into natural conversation
116
+ - Maintain warm, encouraging teaching presence while keeping conversation engaging
117
+ - Always prioritize student comfort and learning pace while building speaking confidence
118
+ """,
119
+ ),
120
+ ("placeholder", "{messages}"),
121
+ ]
122
+ )
123
+
124
+ conversation_chain = conversation_prompt | model
src/agents/lesson_practice/tools.py ADDED
@@ -0,0 +1,16 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ from langchain_core.tools import tool
2
+ from loguru import logger
3
+
4
+
5
+ @tool
6
+ def function_name(
7
+ input: str,
8
+ ) -> str:
9
+ """
10
+ Mô tả chức năng của hàm này.
11
+ """
12
+ logger.info(f"Received input: {input}")
13
+ # Thực hiện các thao tác cần thiết với input
14
+ result = f"Processed: {input}"
15
+ logger.info(f"Returning result: {result}")
16
+ return result
src/agents/role_play/__pycache__/prompt.cpython-311.pyc CHANGED
Binary files a/src/agents/role_play/__pycache__/prompt.cpython-311.pyc and b/src/agents/role_play/__pycache__/prompt.cpython-311.pyc differ
 
src/agents/role_play/__pycache__/scenarios.cpython-311.pyc CHANGED
Binary files a/src/agents/role_play/__pycache__/scenarios.cpython-311.pyc and b/src/agents/role_play/__pycache__/scenarios.cpython-311.pyc differ
 
src/agents/role_play/prompt.py CHANGED
@@ -1,11 +1,9 @@
1
- from langchain_core.prompts import ChatPromptTemplate
2
 
3
- roleplay_prompt = """# Role: Roleplay Partner - Part of the Wise Tutor System
4
 
5
- You are part of Wise Tutor, a friendly English learning companion. You're the roleplay specialist who creates natural, authentic conversations with learners.
6
-
7
- ## About Wise Tutor:
8
- If asked about your name or identity, respond warmly: "I'm part of Wise Tutor! I'm here to help you practice English through fun conversations. Think of me as your friendly conversation partner!"
9
 
10
  ## CRITICAL LANGUAGE RULE:
11
  **IF USER SPEAKS ANY LANGUAGE OTHER THAN ENGLISH → IMMEDIATELY HAND OFF TO GUIDING AGENT**
@@ -221,4 +219,4 @@ Remember:
221
  - **Only use Vietnamese when truly needed for guidance**
222
  - **Handoff to Roleplay when user shows English conversation readiness**
223
  - Focus on preparing them for successful English conversation
224
- """
 
1
+ roleplay_prompt = """# Role: Roleplay Partner - Part of English AI Learning System
2
 
3
+ You are part of Tutor, a friendly English learning companion. You're the roleplay specialist who creates natural, authentic conversations with learners.
4
 
5
+ ## About you
6
+ If asked for your name or role, tell the user who you are {your_role} to make users feel like they are in a real conversation.
 
 
7
 
8
  ## CRITICAL LANGUAGE RULE:
9
  **IF USER SPEAKS ANY LANGUAGE OTHER THAN ENGLISH → IMMEDIATELY HAND OFF TO GUIDING AGENT**
 
219
  - **Only use Vietnamese when truly needed for guidance**
220
  - **Handoff to Roleplay when user shows English conversation readiness**
221
  - Focus on preparing them for successful English conversation
222
+ """
src/apis/__pycache__/create_app.cpython-311.pyc CHANGED
Binary files a/src/apis/__pycache__/create_app.cpython-311.pyc and b/src/apis/__pycache__/create_app.cpython-311.pyc differ
 
src/apis/create_app.py CHANGED
@@ -2,10 +2,12 @@ from fastapi import FastAPI, APIRouter
2
  from fastapi.middleware.cors import CORSMiddleware
3
  from src.apis.routes.user_route import router as router_user
4
  from src.apis.routes.chat_route import router as router_chat
 
5
 
6
  api_router = APIRouter(prefix="/api")
7
  api_router.include_router(router_user)
8
  api_router.include_router(router_chat)
 
9
 
10
 
11
  def create_app():
 
2
  from fastapi.middleware.cors import CORSMiddleware
3
  from src.apis.routes.user_route import router as router_user
4
  from src.apis.routes.chat_route import router as router_chat
5
+ from src.apis.routes.lesson_route import router as router_lesson
6
 
7
  api_router = APIRouter(prefix="/api")
8
  api_router.include_router(router_user)
9
  api_router.include_router(router_chat)
10
+ api_router.include_router(router_lesson)
11
 
12
 
13
  def create_app():
src/apis/routes/lesson_route.py ADDED
@@ -0,0 +1,42 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ from fastapi import APIRouter, status, Depends, BackgroundTasks, HTTPException
2
+ from fastapi.responses import JSONResponse
3
+ from src.utils.logger import logger
4
+ from pydantic import BaseModel, Field
5
+ from typing import List, Dict, Any, Optional
6
+ from src.agents.lesson_practice.flow import lesson_practice_agent
7
+ import json
8
+ import os
9
+ import uuid
10
+ from datetime import datetime
11
+
12
+ router = APIRouter(prefix="/lesson", tags=["AI"])
13
+
14
+
15
+ class LessonPracticeRequest(BaseModel):
16
+ unit: str = Field(..., description="Unit of the lesson")
17
+ vocabulary: list = Field(..., description="Vocabulary for the lesson")
18
+ key_structures: list = Field(..., description="Key structures for the lesson")
19
+ practice_questions: list = Field(
20
+ ..., description="Practice questions for the lesson"
21
+ )
22
+ student_level: str = Field("beginner", description="Student's level of English")
23
+ query: str = Field(..., description="User query for the lesson")
24
+ session_id: str = Field(..., description="Session ID for the lesson")
25
+
26
+
27
+ @router.post("/chat")
28
+ async def chat(request: LessonPracticeRequest):
29
+ response = await lesson_practice_agent().ainvoke(
30
+ {
31
+ "unit": request.unit,
32
+ "vocabulary": request.vocabulary,
33
+ "key_structures": request.key_structures,
34
+ "practice_questions": request.practice_questions,
35
+ "student_level": request.student_level,
36
+ "messages": [request.query],
37
+ },
38
+ {"configurable": {"thread_id": request.session_id}},
39
+ )
40
+ return JSONResponse(
41
+ content=response["messages"][-1].content, status_code=status.HTTP_200_OK
42
+ )