quachtiensinh27 commited on
Commit
7fee926
·
1 Parent(s): 7a4edb9

feat: upgrade tool and add doc

Browse files
Files changed (4) hide show
  1. tools/__init__.py +1 -0
  2. tools/summarizer.py +20 -13
  3. tools/utils.py +43 -4
  4. tools/web.py +62 -0
tools/__init__.py CHANGED
@@ -7,6 +7,7 @@ from .base import get_tool_schemas, execute_tool, get_langchain_tools, get_llm
7
  from .scheduler import tool_get_schedule, tool_add_event
8
  from .summarizer import tool_summarize_chat
9
  from .memory import tool_save_memory, tool_get_memories
 
10
 
11
  # The actual list to be used by the agent can be derived from the registry
12
  # or explicitly exported here.
 
7
  from .scheduler import tool_get_schedule, tool_add_event
8
  from .summarizer import tool_summarize_chat
9
  from .memory import tool_save_memory, tool_get_memories
10
+ from .web import tool_read_link
11
 
12
  # The actual list to be used by the agent can be derived from the registry
13
  # or explicitly exported here.
tools/summarizer.py CHANGED
@@ -10,44 +10,42 @@ from langchain_core.output_parsers import JsonOutputParser
10
  from langchain_core.prompts import ChatPromptTemplate
11
 
12
  from .base import register_tool, get_llm
13
- from .utils import preprocess_messages
14
- from ..redis_client import redis_client
15
-
16
- logger = logging.getLogger(__name__)
17
 
18
  # --- Pydantic Schemas ---
19
  class ThreadSummary(BaseModel):
20
  """Schema cho tóm tắt của một thread."""
21
  thread_id: str = Field(description="ID hoặc tên chủ đề của thread")
22
  main_discussion: str = Field(description="Nội dung chính đang thảo luận")
 
23
  status: str = Field(description="Trạng thái: 'Đã chốt' hoặc 'Chưa chốt'")
24
  conclusion: str = Field(description="Kết luận cuối cùng")
25
 
26
  class TLDRResponse(BaseModel):
27
  """Schema cho response JSON tổng thể."""
28
- summary: list[ThreadSummary] = Field(description="Danh sách tóm tắt")
 
 
29
 
30
  # --- System Prompt ---
31
  SYSTEM_PROMPT_TLDR = """
32
  Bạn là một THƯ KÝ DỰ ÁN chuyên nghiệp.
33
  Nhiệm vụ: tóm tắt các đoạn chat nhóm thành báo cáo chính xác tuyệt đối.
 
34
  - Chỉ chốt các thông tin có keyword "ok", "chốt", "thống nhất"...
35
  - Nếu đang tranh luận chưa ngã ngũ -> status = "Chưa chốt".
36
  - Bỏ qua tin nhắn rác.
37
  - Trả về JSON đúng schema.
38
  """
39
 
40
- def _extract_token_usage(response_metadata: dict) -> dict:
41
- """Helper to extract token usage."""
42
- # (Simplified for now, can be expanded if needed)
43
- return response_metadata.get("token_usage", {"total_tokens": 0})
44
 
45
  @register_tool(
46
  name="summarize_chat",
47
- description="Tóm tắt các tin nhắn trong group chat theo từng luồng (thread) bằng AI. Cần cung cấp room_id.",
48
  parameters=[
49
- {"name": "room_id", "type": "string", "description": "ID của phòng chat hoặc nhóm chat cần tóm tắt. Bắt buộc phải có để lấy tin nhắn.", "required": True},
50
- {"name": "limit", "type": "integer", "description": "Số lượng tin nhắn tối đa cần lấy. Mặc định 100.", "required": False}
51
  ]
52
  )
53
  def tool_summarize_chat(
@@ -64,9 +62,14 @@ def tool_summarize_chat(
64
  messages = redis_client.get_room_messages(room_id, limit)
65
 
66
  if not messages:
67
- return {"status": "success", "data": {"summary": []}, "metrics": {"processing_time_sec": 0}}
68
 
 
 
 
 
69
  formatted_threads = preprocess_messages(messages)
 
70
  llm = get_llm()
71
  parser = JsonOutputParser(pydantic_object=TLDRResponse)
72
 
@@ -81,6 +84,10 @@ def tool_summarize_chat(
81
  "format_instructions": parser.get_format_instructions(),
82
  })
83
 
 
 
 
 
84
  processing_time = round(time.time() - start_time, 2)
85
  return {
86
  "status": "success",
 
10
  from langchain_core.prompts import ChatPromptTemplate
11
 
12
  from .base import register_tool, get_llm
13
+ from .utils import preprocess_messages, extract_metadata_from_messages
 
 
 
14
 
15
  # --- Pydantic Schemas ---
16
  class ThreadSummary(BaseModel):
17
  """Schema cho tóm tắt của một thread."""
18
  thread_id: str = Field(description="ID hoặc tên chủ đề của thread")
19
  main_discussion: str = Field(description="Nội dung chính đang thảo luận")
20
+ conversation_flow: str = Field(description="Tóm tắt diễn biến cuộc hội thoại (ai hỏi, ai trả lời, mâu thuẫn/đồng thuận)")
21
  status: str = Field(description="Trạng thái: 'Đã chốt' hoặc 'Chưa chốt'")
22
  conclusion: str = Field(description="Kết luận cuối cùng")
23
 
24
  class TLDRResponse(BaseModel):
25
  """Schema cho response JSON tổng thể."""
26
+ summary: list[ThreadSummary] = Field(description="Danh sách tóm tắt các thread")
27
+ links_found: list[str] = Field(description="Danh sách các liên kết (URL) được tìm thấy")
28
+ files_found: list[dict] = Field(description="Danh sách các tệp tin/ảnh được tìm thấy")
29
 
30
  # --- System Prompt ---
31
  SYSTEM_PROMPT_TLDR = """
32
  Bạn là một THƯ KÝ DỰ ÁN chuyên nghiệp.
33
  Nhiệm vụ: tóm tắt các đoạn chat nhóm thành báo cáo chính xác tuyệt đối.
34
+ - Xác định rõ 'conversation_flow': mô tả cách cuộc thảo luận diễn ra (ví dụ: A đề xuất, B phản đối, cuối cùng cả nhóm đồng ý).
35
  - Chỉ chốt các thông tin có keyword "ok", "chốt", "thống nhất"...
36
  - Nếu đang tranh luận chưa ngã ngũ -> status = "Chưa chốt".
37
  - Bỏ qua tin nhắn rác.
38
  - Trả về JSON đúng schema.
39
  """
40
 
41
+ # ... (execute_tool part)
 
 
 
42
 
43
  @register_tool(
44
  name="summarize_chat",
45
+ description="Tóm tắt tin nhắn, diễn biến cuộc hội thoại trích xuất Link/File bằng AI. Cần cung cấp room_id.",
46
  parameters=[
47
+ {"name": "room_id", "type": "string", "description": "ID của phòng chat.", "required": True},
48
+ {"name": "limit", "type": "integer", "description": "Số lượng tin nhắn tối đa (Mặc định: 100).", "required": False}
49
  ]
50
  )
51
  def tool_summarize_chat(
 
62
  messages = redis_client.get_room_messages(room_id, limit)
63
 
64
  if not messages:
65
+ return {"status": "success", "data": {"summary": [], "links_found": [], "files_found": []}, "metrics": {"processing_time_sec": 0}}
66
 
67
+ # 1. Trích xuất Metadata (Links/Files)
68
+ metadata = extract_metadata_from_messages(messages)
69
+
70
+ # 2. Tiền xử lý văn bản cho LLM
71
  formatted_threads = preprocess_messages(messages)
72
+
73
  llm = get_llm()
74
  parser = JsonOutputParser(pydantic_object=TLDRResponse)
75
 
 
84
  "format_instructions": parser.get_format_instructions(),
85
  })
86
 
87
+ # Merge metadata vào kết quả nếu LLM chưa tự điền (LLM thường chỉ tóm tắt text)
88
+ result["links_found"] = list(set(result.get("links_found", []) + metadata["links"]))
89
+ result["files_found"] = metadata["files"]
90
+
91
  processing_time = round(time.time() - start_time, 2)
92
  return {
93
  "status": "success",
tools/utils.py CHANGED
@@ -1,10 +1,49 @@
1
- """
2
- Utility functions for message processing and formatting.
3
- """
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
4
 
5
- from collections import defaultdict
6
 
7
  def group_messages_by_thread(messages: list[dict]) -> dict[str, list[dict]]:
 
8
  """
9
  Gom nhóm các tin nhắn có cùng roomId lại với nhau.
10
  """
 
1
+ import re
2
+ import json
3
+
4
+ def extract_metadata_from_messages(messages: list[dict]) -> dict:
5
+ """
6
+ Trích xuất danh sách Link và File từ danh sách tin nhắn.
7
+ """
8
+ links = []
9
+ files = []
10
+
11
+ # Regex đơn giản cho URL
12
+ url_pattern = r'https?://[^\s]+'
13
+
14
+ for msg in messages:
15
+ content = msg.get("content", "")
16
+ # Tìm links
17
+ found_links = re.findall(url_pattern, content)
18
+ links.extend(found_links)
19
+
20
+ # Tìm files từ trường attachment
21
+ attachment_raw = msg.get("attachment")
22
+ if attachment_raw and attachment_raw != "null":
23
+ try:
24
+ # Trường attachment có thể là string JSON hoặc dict tùy vào RedisClient
25
+ if isinstance(attachment_raw, str):
26
+ att = json.loads(attachment_raw)
27
+ else:
28
+ att = attachment_raw
29
+
30
+ files.append({
31
+ "id": att.get("id"),
32
+ "type": att.get("type"),
33
+ "name": att.get("name"),
34
+ "sender": msg.get("senderName")
35
+ })
36
+ except:
37
+ pass
38
+
39
+ return {
40
+ "links": list(set(links)), # Loại bỏ trùng
41
+ "files": files
42
+ }
43
 
 
44
 
45
  def group_messages_by_thread(messages: list[dict]) -> dict[str, list[dict]]:
46
+ # ... (rest of the file)
47
  """
48
  Gom nhóm các tin nhắn có cùng roomId lại với nhau.
49
  """
tools/web.py ADDED
@@ -0,0 +1,62 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ """
2
+ Web scraping tools for fetching content from URLs.
3
+ """
4
+
5
+ import logging
6
+ import requests
7
+ from bs4 import BeautifulSoup
8
+ from .base import register_tool
9
+
10
+ logger = logging.getLogger(__name__)
11
+
12
+ @register_tool(
13
+ name="read_link",
14
+ description="Truy cập và bóc tách nội dung chính từ một đường dẫn (URL). Giúp Agent hiểu nội dung bài báo, tài liệu online.",
15
+ parameters=[
16
+ {"name": "url", "type": "string", "description": "Đường dẫn (URL) cần đọc nội dung.", "required": True}
17
+ ]
18
+ )
19
+ def tool_read_link(url: str) -> dict:
20
+ """
21
+ Fetches and parses a URL to extract the main content.
22
+ """
23
+ try:
24
+ # 1. Fetch content with timeout
25
+ headers = {
26
+ 'User-Agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/91.0.4472.124 Safari/537.36'
27
+ }
28
+ response = requests.get(url, headers=headers, timeout=10)
29
+ response.raise_for_status()
30
+
31
+ # 2. Parse with BeautifulSoup
32
+ soup = BeautifulSoup(response.text, 'html.parser')
33
+
34
+ # Remove script and style elements
35
+ for script_or_style in soup(["script", "style", "nav", "footer", "header"]):
36
+ script_or_style.decompose()
37
+
38
+ # 3. Extract title and main text
39
+ title = soup.title.string if soup.title else "No Title"
40
+
41
+ # Simple extraction: get all paragraphs
42
+ paragraphs = soup.find_all('p')
43
+ text_content = "\n".join([p.get_text().strip() for p in paragraphs if p.get_text().strip()])
44
+
45
+ # Truncate content for LLM context (approx 3000 chars)
46
+ if len(text_content) > 3000:
47
+ text_content = text_content[:3000] + "..."
48
+
49
+ return {
50
+ "status": "success",
51
+ "data": {
52
+ "url": url,
53
+ "title": title.strip(),
54
+ "content": text_content
55
+ }
56
+ }
57
+
58
+ except requests.exceptions.Timeout:
59
+ return {"status": "error", "message": "Yêu cầu quá hạn (timeout) khi truy cập URL."}
60
+ except Exception as e:
61
+ logger.error(f"Error in read_link: {e}")
62
+ return {"status": "error", "message": f"Không thể đọc nội dung từ link: {str(e)}"}