前言
上一篇 FastMCP 專案管理工具 介紹了專案相關的 MCP 工具。這篇來實作知識庫工具,讓用戶可以透過對話:
- 搜尋知識庫
- 新增筆記
- 管理附件
- 更新和刪除知識
特別的是,系統會根據對話來源自動判定知識的 Scope(範圍),省去用戶手動指定的麻煩。
Scope 概念
知識庫中的每筆知識都有一個 Scope(範圍),決定誰可以看到和編輯:
| Scope | 說明 | 存取權限 |
|---|---|---|
global |
全域知識 | 所有人可讀,需 global_write 權限才能編輯 |
personal |
個人知識 | 僅建立者可讀寫 |
project |
專案知識 | 專案成員可讀寫 |
自動判定規則
透過 Line Bot 建立知識時,系統會根據對話來源自動設定 Scope:
┌─────────────────────────────────────────────────────────────┐
│ 對話來源 │
└──────────────────────────┬──────────────────────────────────┘
│
┌──────────────┼──────────────┐
▼ ▼ ▼
┌──────────┐ ┌──────────┐ ┌──────────┐
│ 個人對話 │ │ 群組對話 │ │ 群組對話 │
│ +已綁定 │ │ +綁定專案 │ │ 未綁定 │
└────┬─────┘ └────┬─────┘ └────┬─────┘
│ │ │
▼ ▼ ▼
┌──────────┐ ┌──────────┐ ┌──────────┐
│ personal │ │ project │ │ global │
│ 個人知識 │ │ 專案知識 │ │ 全域知識 │
└──────────┘ └──────────┘ └──────────┘
工具總覽
| 工具 | 功能 |
|---|---|
search_knowledge |
搜尋知識庫 |
get_knowledge_item |
取得完整內容 |
add_note |
新增純文字筆記 |
add_note_with_attachments |
新增筆記含附件 |
update_knowledge_item |
更新知識 |
delete_knowledge_item |
刪除知識 |
add_attachments_to_knowledge |
新增附件 |
get_knowledge_attachments |
查詢附件列表 |
update_knowledge_attachment |
更新附件說明 |
搜尋知識庫
@mcp.tool()
async def search_knowledge(
query: str,
project: str | None = None,
category: str | None = None,
limit: int = 5,
) -> str:
"""
搜尋知識庫
Args:
query: 搜尋關鍵字
project: 專案過濾(專案 ID 或名稱)
category: 分類過濾(technical, process, tool, note)
limit: 最大結果數量,預設 5
"""
from . import knowledge as kb_service
try:
result = kb_service.search_knowledge(
query=query,
project=project,
category=category,
)
if not result.items:
return f"找不到包含「{query}」的知識"
# 格式化結果
items = result.items[:limit]
output = [f"搜尋「{query}」找到 {len(result.items)} 筆結果:\n"]
for item in items:
tags_str = ", ".join(item.tags.topics) if item.tags.topics else "無標籤"
output.append(f"📄 [{item.id}] {item.title}")
output.append(f" 分類:{item.category} | 標籤:{tags_str}")
if item.snippet:
snippet = item.snippet[:100] + "..." if len(item.snippet) > 100 else item.snippet
output.append(f" 摘要:{snippet}")
output.append("")
return "\n".join(output)
except Exception as e:
return f"搜尋失敗:{str(e)}"
使用情境
用戶:找一下水切爐的資料
AI:(調用 search_knowledge)
AI:搜尋「水切爐」找到 3 筆結果:
📄 [kb-015] 水切爐標準作業程序
分類:process | 標籤:水切爐, SOP
摘要:水切爐的標準操作溫度為 850°C...
📄 [kb-023] 水切爐故障排除
分類:technical | 標籤:水切爐, 維修
摘要:常見故障代碼 E01 表示溫度感測器異常...
取得完整內容
@mcp.tool()
async def get_knowledge_item(kb_id: str) -> str:
"""
取得知識庫文件的完整內容
Args:
kb_id: 知識 ID(如 kb-001、kb-002)
"""
from . import knowledge as kb_service
from pathlib import Path
try:
item = kb_service.get_knowledge(kb_id)
# 格式化輸出
tags_str = ", ".join(item.tags.topics) if item.tags.topics else "無標籤"
output = [
f"📄 **[{item.id}] {item.title}**",
f"分類:{item.category} | 標籤:{tags_str}",
"",
"---",
"",
item.content or "(無內容)",
]
# 加入附件資訊
if item.attachments:
output.append("")
output.append("---")
output.append(f"📎 **附件** ({len(item.attachments)} 個)")
for idx, att in enumerate(item.attachments):
filename = Path(att.path).name
desc = f" - {att.description}" if att.description else ""
output.append(f" [{idx}] {att.type}: {filename}{desc}")
return "\n".join(output)
except Exception as e:
return f"找不到知識 {kb_id}:{str(e)}"
Scope 自動判定實作
這是知識庫工具的核心邏輯:
async def _determine_knowledge_scope(
line_group_id: str | None,
line_user_id: str | None,
ctos_user_id: int | None,
) -> tuple[str, str | None, str | None]:
"""判斷知識庫的 scope 和相關屬性
Returns:
tuple[scope, owner_username, project_id]
"""
scope = "global"
owner_username: str | None = None
project_id: str | None = None
# 1. 取得 CTOS 使用者名稱(如果有綁定)
if ctos_user_id:
async with get_connection() as conn:
user_row = await conn.fetchrow(
"SELECT username FROM users WHERE id = $1",
ctos_user_id,
)
if user_row:
owner_username = user_row["username"]
# 2. 判斷對話來源並設定 scope
if line_group_id:
# 群組聊天:檢查群組是否綁定專案
async with get_connection() as conn:
group_row = await conn.fetchrow(
"SELECT project_id FROM line_groups WHERE id = $1",
UUID(line_group_id),
)
if group_row and group_row["project_id"]:
# 群組已綁定專案 → scope=project
scope = "project"
project_id = str(group_row["project_id"])
else:
# 群組未綁定專案 → scope=global
scope = "global"
else:
# 個人聊天
if owner_username:
# 已綁定 CTOS 帳號 → scope=personal
scope = "personal"
else:
# 未綁定 → scope=global
scope = "global"
return scope, owner_username, project_id
新增筆記
純文字筆記
@mcp.tool()
async def add_note(
title: str,
content: str,
category: str = "note",
topics: list[str] | None = None,
project: str | None = None,
line_group_id: str | None = None,
line_user_id: str | None = None,
ctos_user_id: int | None = None,
) -> str:
"""
新增筆記到知識庫
Args:
title: 筆記標題
content: 筆記內容(Markdown 格式)
category: 分類,預設 note
topics: 主題標籤列表
project: 關聯的專案名稱
line_group_id: Line 群組 UUID(自動傳入)
line_user_id: Line 用戶 ID(自動傳入)
ctos_user_id: CTOS 用戶 ID(自動傳入)
"""
from ..models.knowledge import KnowledgeCreate, KnowledgeTags, KnowledgeSource
from . import knowledge as kb_service
try:
await ensure_db_connection()
# 自動判斷 scope
scope, owner_username, project_id = await _determine_knowledge_scope(
line_group_id, line_user_id, ctos_user_id
)
# 建立知識
tags = KnowledgeTags(
projects=[project] if project else [],
topics=topics or [],
)
source = KnowledgeSource(path="linebot")
data = KnowledgeCreate(
title=title,
content=content,
type="note",
category=category,
scope=scope,
project_id=project_id,
tags=tags,
source=source,
author=owner_username or "linebot",
)
result = kb_service.create_knowledge(
data, owner=owner_username, project_id=project_id
)
scope_text = {"global": "全域", "personal": "個人", "project": "專案"}.get(scope)
return f"✅ 筆記已新增!\nID:{result.id}\n標題:{result.title}\n範圍:{scope_text}知識"
except Exception as e:
return f"新增筆記失敗:{str(e)}"
含附件筆記
@mcp.tool()
async def add_note_with_attachments(
title: str,
content: str,
attachments: list[str],
category: str = "note",
topics: list[str] | None = None,
project: str | None = None,
line_group_id: str | None = None,
line_user_id: str | None = None,
ctos_user_id: int | None = None,
) -> str:
"""
新增筆記到知識庫並加入附件
Args:
title: 筆記標題
content: 筆記內容
attachments: 附件的 NAS 路徑列表(從 get_message_attachments 取得)
... 其他參數同 add_note
"""
# 限制附件數量
if len(attachments) > 10:
return "附件數量不能超過 10 個"
try:
await ensure_db_connection()
# 自動判斷 scope
scope, owner_username, project_id = await _determine_knowledge_scope(
line_group_id, line_user_id, ctos_user_id
)
# 建立知識
# ... 同 add_note
result = kb_service.create_knowledge(data, owner=owner_username, project_id=project_id)
kb_id = result.id
# 處理附件
success_count = 0
for nas_path in attachments:
try:
kb_service.copy_linebot_attachment_to_knowledge(kb_id, nas_path)
success_count += 1
except Exception as e:
logger.warning(f"附件複製失敗 {nas_path}: {e}")
return f"✅ 筆記已新增!\nID:{kb_id}\n標題:{result.title}\n附件:{success_count} 個"
except Exception as e:
return f"新增筆記失敗:{str(e)}"
使用情境
情境一:個人筆記
(個人對話,用戶已綁定 CTOS 帳號)
用戶:把這段記到知識庫:「水切爐的標準溫度是 850°C」
AI:(調用 add_note,scope 自動設為 personal)
AI:✅ 筆記已新增!
ID:kb-042
標題:水切爐標準溫度
範圍:個人知識
情境二:專案知識
(群組對話,群組已綁定專案「水切爐改善」)
用戶:把剛剛那張圖加到知識庫,標題叫「水切爐改善方案」
AI:(調用 get_message_attachments 取得最近的圖片)
AI:(調用 add_note_with_attachments,scope 自動設為 project)
AI:✅ 筆記已新增!
ID:kb-043
標題:水切爐改善方案
範圍:專案知識
附件:1 個
情境三:全域知識
(群組對話,群組未綁定專案)
用戶:記一下:公司 WiFi 密碼是 ABC123
AI:(調用 add_note,scope 自動設為 global)
AI:✅ 筆記已新增!
ID:kb-044
標題:公司 WiFi 密碼
範圍:全域知識
附件管理
查詢附件
@mcp.tool()
async def get_knowledge_attachments(kb_id: str) -> str:
"""取得知識庫的附件列表"""
item = kb_service.get_knowledge(kb_id)
if not item.attachments:
return f"知識 {kb_id} 沒有附件"
output = [f"📎 **{kb_id} 附件列表** ({len(item.attachments)} 個)\n"]
for idx, att in enumerate(item.attachments):
filename = Path(att.path).name
output.append(f"[{idx}] {att.type}")
output.append(f" 檔名:{filename}")
if att.description:
output.append(f" 說明:{att.description}")
output.append("")
return "\n".join(output)
更新附件說明
@mcp.tool()
async def update_knowledge_attachment(
kb_id: str,
attachment_index: int,
description: str | None = None,
) -> str:
"""更新知識庫附件的說明"""
attachment = kb_service.update_attachment(
kb_id=kb_id,
attachment_idx=attachment_index,
description=description,
)
filename = Path(attachment.path).name
return f"✅ 已更新 {kb_id} 附件 [{attachment_index}]\n檔名:{filename}\n說明:{description}"
小結
這篇實作了知識庫的 MCP 工具:
- 搜尋與查詢:search_knowledge、get_knowledge_item
- 新增筆記:add_note、add_note_with_attachments
- Scope 自動判定:根據對話來源智慧設定
- 附件管理:新增、查詢、更新說明
下一篇 MCP 工具權限控制設計 會介紹如何確保只有專案成員才能操作敏感工具。