歡迎來到第二週的學習!經過第一週的基礎建立,今天我們將深入探討 AI 助理的核心能力之一:多輪對話與上下文管理。這是讓 AI 助理真正「智能」的關鍵技術,能夠讓對話更自然、更有連貫性。
想像一下這樣的對話:
使用者:我想學習 Python
AI:好的!Python 是一門優秀的程式語言...
使用者:它難學嗎?
AI:什麼難學嗎?我需要更多資訊才能回答。
缺乏上下文管理的 AI 無法理解「它」指的是 Python,這樣的對話體驗很糟糕。今天我們要解決這個問題!
讓我們建立一個完整的上下文管理系統:
context_management/
├── main.py # 主程式
├── core/
│ ├── __init__.py
│ ├── context_manager.py # 核心上下文管理
│ ├── memory_store.py # 記憶儲存
│ └── relevance_scorer.py # 相關性評分
├── strategies/
│ ├── __init__.py
│ ├── sliding_window.py # 滑動視窗策略
│ ├── summarization.py # 總結壓縮策略
│ └── topic_based.py # 主題導向策略
├── integrations/
│ ├── __init__.py
│ ├── gemini_context.py # Gemini 上下文整合
│ └── langgraph_context.py # LangGraph 上下文整合
└── utils/
├── __init__.py
├── tokenizer.py # Token 計算
└── serializer.py # 序列化工具
from typing import List, Dict, Optional, Any, Tuple
from dataclasses import dataclass, field
from datetime import datetime
from enum import Enum
import json
import hashlib
class MessageType(Enum):
USER = "user"
ASSISTANT = "assistant"
SYSTEM = "system"
TOOL = "tool"
class ContextScope(Enum):
IMMEDIATE = "immediate" # 最近 3-5 輪對話
SHORT_TERM = "short_term" # 當前會話
LONG_TERM = "long_term" # 跨會話記憶
@dataclass
class ConversationMessage:
role: MessageType
content: str
timestamp: datetime
metadata: Dict[str, Any] = field(default_factory=dict)
tokens: int = 0
relevance_score: float = 1.0
topic_tags: List[str] = field(default_factory=list)
def __post_init__(self):
if self.tokens == 0:
self.tokens = self._estimate_tokens()
def _estimate_tokens(self) -> int:
"""粗略估算 token 數量"""
return len(self.content.split()) * 1.3 # 經驗值
def to_dict(self) -> Dict:
return {
"role": self.role.value,
"content": self.content,
"timestamp": self.timestamp.isoformat(),
"metadata": self.metadata,
"tokens": self.tokens,
"relevance_score": self.relevance_score,
"topic_tags": self.topic_tags
}
@dataclass
class ContextWindow:
messages: List[ConversationMessage]
max_tokens: int = 4000
current_topic: str = ""
topic_confidence: float = 0.0
@property
def total_tokens(self) -> int:
return sum(msg.tokens for msg in self.messages)
@property
def message_count(self) -> int:
return len(self.messages)
class AdvancedContextManager:
def __init__(self, max_tokens: int = 4000, max_messages: int = 50):
self.max_tokens = max_tokens
self.max_messages = max_messages
self.current_context = ContextWindow([], max_tokens)
self.conversation_history: List[ConversationMessage] = []
self.topic_history: List[Tuple[str, datetime, float]] = []
self.user_profile: Dict[str, Any] = {}
def add_message(self, role: MessageType, content: str, metadata: Dict = None) -> None:
"""添加新訊息到上下文"""
message = ConversationMessage(
role=role,
content=content,
timestamp=datetime.now(),
metadata=metadata or {}
)
# 分析主題和相關性
self._analyze_message(message)
# 添加到歷史記錄
self.conversation_history.append(message)
# 更新當前上下文窗口
self._update_context_window(message)
def _analyze_message(self, message: ConversationMessage) -> None:
"""分析訊息的主題和相關性"""
# 這裡可以使用 Gemini 進行更複雜的分析
# 現在使用簡化版本
content = message.content.lower()
# 簡單的主題識別
topics = []
topic_keywords = {
'python': ['python', 'py', '程式', '程式碼', 'code'],
'ai': ['ai', '人工智慧', '機器學習', 'ml', 'llm'],
'web': ['網站', 'html', 'css', 'javascript', 'web'],
'data': ['資料', '數據', 'data', '分析', 'analysis']
}
for topic, keywords in topic_keywords.items():
if any(keyword in content for keyword in keywords):
topics.append(topic)
message.topic_tags = topics
# 計算與當前主題的相關性
if self.current_context.current_topic:
if self.current_context.current_topic in topics:
message.relevance_score = 1.0
else:
message.relevance_score = 0.5
def _update_context_window(self, new_message: ConversationMessage) -> None:
"""更新上下文窗口"""
self.current_context.messages.append(new_message)
# 如果超過限制,執行壓縮策略
if (self.current_context.total_tokens > self.max_tokens or
self.current_context.message_count > self.max_messages):
self._compress_context()
def _compress_context(self) -> None:
"""壓縮上下文 - 使用混合策略"""
# 策略1: 保留最重要的訊息
important_messages = [
msg for msg in self.current_context.messages
if msg.relevance_score > 0.8 or msg.role == MessageType.SYSTEM
]
# 策略2: 保留最近的對話
recent_messages = self.current_context.messages[-10:] # 保留最近10條
# 策略3: 總結中間部分
if len(self.current_context.messages) > 20:
middle_part = self.current_context.messages[5:-10]
summary = self._summarize_messages(middle_part)
if summary:
summary_message = ConversationMessage(
role=MessageType.SYSTEM,
content=f"[對話摘要] {summary}",
timestamp=datetime.now(),
metadata={"is_summary": True}
)
important_messages.append(summary_message)
# 合併並去重
compressed_messages = []
seen_content = set()
for msg in important_messages + recent_messages:
content_hash = hashlib.md5(msg.content.encode()).hexdigest()
if content_hash not in seen_content:
compressed_messages.append(msg)
seen_content.add(content_hash)
# 按時間排序
compressed_messages.sort(key=lambda x: x.timestamp)
self.current_context.messages = compressed_messages
def _summarize_messages(self, messages: List[ConversationMessage]) -> str:
"""總結一組訊息"""
if not messages:
return ""
# 這裡可以使用 Gemini 進行總結
# 現在使用簡化版本
topics = set()
for msg in messages:
topics.update(msg.topic_tags)
return f"討論了關於 {', '.join(topics)} 的話題,共 {len(messages)} 輪對話。"
def get_context_for_prompt(self, include_summary: bool = True) -> List[Dict]:
"""獲取用於提示詞的上下文"""
context_messages = []
# 添加使用者檔案摘要(如果有的話)
if self.user_profile and include_summary:
profile_summary = self._get_user_profile_summary()
context_messages.append({
"role": "system",
"content": f"使用者背景資訊:{profile_summary}"
})
# 添加對話歷史
for msg in self.current_context.messages:
context_messages.append({
"role": msg.role.value,
"content": msg.content
})
return context_messages
def _get_user_profile_summary(self) -> str:
"""獲取使用者檔案摘要"""
if not self.user_profile:
return "新使用者"
summary_parts = []
if "name" in self.user_profile:
summary_parts.append(f"姓名:{self.user_profile['name']}")
if "interests" in self.user_profile:
summary_parts.append(f"興趣:{', '.join(self.user_profile['interests'])}")
if "experience_level" in self.user_profile:
summary_parts.append(f"經驗等級:{self.user_profile['experience_level']}")
return "; ".join(summary_parts) if summary_parts else "一般使用者"
def update_user_profile(self, updates: Dict[str, Any]) -> None:
"""更新使用者檔案"""
self.user_profile.update(updates)
def get_conversation_stats(self) -> Dict[str, Any]:
"""獲取對話統計資訊"""
total_messages = len(self.conversation_history)
total_tokens = sum(msg.tokens for msg in self.conversation_history)
topic_counts = {}
for msg in self.conversation_history:
for topic in msg.topic_tags:
topic_counts[topic] = topic_counts.get(topic, 0) + 1
return {
"total_messages": total_messages,
"total_tokens": total_tokens,
"current_context_size": self.current_context.message_count,
"current_context_tokens": self.current_context.total_tokens,
"top_topics": sorted(topic_counts.items(), key=lambda x: x[1], reverse=True)[:5],
"conversation_duration": (
self.conversation_history[-1].timestamp - self.conversation_history[0].timestamp
).total_seconds() / 60 if self.conversation_history else 0
}
import google.generativeai as genai
from core.context_manager import AdvancedContextManager, MessageType
from typing import List, Dict, Optional
import json
class GeminiContextualChat:
def __init__(self, api_key: str, model_name: str = "gemini-pro"):
genai.configure(api_key=api_key)
self.model = genai.GenerativeModel(model_name)
self.context_manager = AdvancedContextManager()
self.system_prompt = """
你是一個智能助理,具有以下特性:
1. 能夠記住對話歷史並保持上下文一致性
2. 會主動參考之前的對話內容
3. 能識別話題轉換並適當回應
4. 提供個性化的建議和回應
"""
def chat(self, user_input: str, metadata: Dict = None) -> str:
"""進行上下文感知的對話"""
# 記錄使用者輸入
self.context_manager.add_message(
MessageType.USER,
user_input,
metadata
)
# 構建上下文感知的提示詞
context_messages = self.context_manager.get_context_for_prompt()
# 建立完整的對話歷史
full_prompt = self._build_contextual_prompt(context_messages, user_input)
try:
# 調用 Gemini API
response = self.model.generate_content(full_prompt)
assistant_response = response.text
# 記錄助理回應
self.context_manager.add_message(
MessageType.ASSISTANT,
assistant_response,
{"confidence": getattr(response, 'confidence', 0.8)}
)
return assistant_response
except Exception as e:
error_response = f"抱歉,我遇到了一些技術問題:{str(e)}"
self.context_manager.add_message(
MessageType.ASSISTANT,
error_response,
{"error": True}
)
return error_response
def _build_contextual_prompt(self, context_messages: List[Dict], current_input: str) -> str:
"""構建包含上下文的提示詞"""
prompt_parts = [self.system_prompt]
# 添加對話歷史
if len(context_messages) > 1: # 除了系統訊息還有其他內容
prompt_parts.append("\n對話歷史:")
for msg in context_messages[1:]: # 跳過系統訊息
role_name = {"user": "使用者", "assistant": "助理"}.get(msg["role"], msg["role"])
prompt_parts.append(f"{role_name}:{msg['content']}")
# 添加當前問題
prompt_parts.append(f"\n當前問題:{current_input}")
# 添加回應指引
prompt_parts.append("""
請基於以上對話歷史回應,注意:
1. 保持與之前對話的一致性
2. 適當引用之前提到的內容
3. 如果話題發生轉換,請自然地過渡
4. 提供個性化且有幫助的回應
""")
return "\n".join(prompt_parts)
def analyze_context_relevance(self, query: str) -> Dict[str, float]:
"""分析查詢與當前上下文的相關性"""
context_messages = self.context_manager.get_context_for_prompt()
if len(context_messages) < 2:
return {"relevance": 0.0, "topic_continuity": 0.0}
# 使用 Gemini 分析相關性
analysis_prompt = f"""
分析以下查詢與對話上下文的相關性:
對話上下文:
{json.dumps(context_messages[-5:], ensure_ascii=False, indent=2)}
新查詢:{query}
請以 JSON 格式回應:
{{
"relevance": 0.0-1.0, // 與上下文的相關性
"topic_continuity": 0.0-1.0, // 話題連續性
"is_topic_change": true/false, // 是否為話題轉換
"suggested_context_weight": 0.0-1.0 // 建議的上下文權重
}}
"""
try:
response = self.model.generate_content(analysis_prompt)
return json.loads(response.text)
except:
return {
"relevance": 0.5,
"topic_continuity": 0.5,
"is_topic_change": False,
"suggested_context_weight": 0.5
}
def get_context_summary(self) -> str:
"""獲取上下文摘要"""
stats = self.context_manager.get_conversation_stats()
summary = f"""
📊 對話統計:
• 總訊息數:{stats['total_messages']}
• 當前上下文:{stats['current_context_size']} 則訊息
• Token 使用:{stats['current_context_tokens']}/{self.context_manager.max_tokens}
• 對話時長:{stats['conversation_duration']:.1f} 分鐘
"""
if stats['top_topics']:
summary += f"\n• 主要話題:{', '.join([topic for topic, count in stats['top_topics']])}"
return summary
def reset_context(self, keep_user_profile: bool = True) -> None:
"""重置上下文"""
user_profile = self.context_manager.user_profile.copy() if keep_user_profile else {}
self.context_manager = AdvancedContextManager()
if keep_user_profile:
self.context_manager.user_profile = user_profile
def export_conversation(self) -> List[Dict]:
"""匯出對話記錄"""
return [msg.to_dict() for msg in self.context_manager.conversation_history]
from langgraph.graph import StateGraph, END
from core.context_manager import AdvancedContextManager, MessageType
from typing import TypedDict, List, Dict, Any, Literal
from integrations.gemini_context import GeminiContextualChat
class ContextualWorkflowState(TypedDict):
user_input: str
context_analysis: Dict[str, Any]
processing_mode: str # "contextual", "fresh", "summarized"
response: str
context_updates: List[Dict]
confidence: float
def analyze_context_needs(state: ContextualWorkflowState) -> ContextualWorkflowState:
"""分析上下文需求"""
# 這裡使用 Gemini 分析是否需要上下文
# 簡化實現
user_input = state["user_input"].lower()
context_keywords = ["它", "這個", "剛才", "之前", "繼續", "那個", "上面的"]
needs_context = any(keyword in user_input for keyword in context_keywords)
processing_mode = "contextual" if needs_context else "fresh"
return {
**state,
"context_analysis": {
"needs_context": needs_context,
"confidence": 0.8 if needs_context else 0.3
},
"processing_mode": processing_mode
}
def contextual_processing(state: ContextualWorkflowState) -> ContextualWorkflowState:
"""使用完整上下文處理"""
# 這裡會使用 GeminiContextualChat 進行處理
response = f"[基於上下文] 理解您指的是之前討論的內容。{state['user_input']}"
return {
**state,
"response": response,
"confidence": 0.9
}
def fresh_processing(state: ContextualWorkflowState) -> ContextualWorkflowState:
"""獨立處理(不依賴上下文)"""
response = f"[新對話] {state['user_input']}"
return {
**state,
"response": response,
"confidence": 0.7
}
def route_processing_mode(state: ContextualWorkflowState) -> Literal["contextual", "fresh"]:
"""路由處理模式"""
return state["processing_mode"]
def create_contextual_workflow():
"""建立上下文感知工作流程"""
workflow = StateGraph(ContextualWorkflowState)
# 添加節點
workflow.add_node("analyze", analyze_context_needs)
workflow.add_node("contextual", contextual_processing)
workflow.add_node("fresh", fresh_processing)
# 設定流程
workflow.set_entry_point("analyze")
workflow.add_conditional_edges(
"analyze",
route_processing_mode,
{
"contextual": "contextual",
"fresh": "fresh"
}
)
workflow.add_edge("contextual", END)
workflow.add_edge("fresh", END)
return workflow.compile()
import os
from dotenv import load_dotenv
from integrations.gemini_context import GeminiContextualChat
load_dotenv()
def main():
"""上下文感知聊天機器人示例"""
print("🧠 智能上下文管理聊天機器人")
print("💡 我能記住我們的對話並保持上下文一致性")
print("📝 嘗試說:'它是什麼?'、'繼續說明'、'剛才提到的那個'")
print("=" * 60)
# 初始化聊天機器人
chat_bot = GeminiContextualChat(os.getenv('GEMINI_API_KEY'))
# 設定使用者檔案
chat_bot.context_manager.update_user_profile({
"name": "學習者",
"interests": ["程式設計", "AI"],
"experience_level": "中等"
})
print("🤖 助理:你好!我是你的智能助理,我會記住我們的對話內容。請問有什麼可以幫助你的?")
while True:
try:
user_input = input("\n💬 你:").strip()
if not user_input:
continue
if user_input.lower() in ['quit', 'exit', '退出']:
print("\n📊 對話總結:")
print(chat_bot.get_context_summary())
print("\n👋 再見!")
break
# 特殊指令
if user_input.startswith('/'):
if user_input == '/stats':
print(chat_bot.get_context_summary())
continue
elif user_input == '/reset':
chat_bot.reset_context()
print("🔄 對話上下文已重置")
continue
elif user_input == '/export':
conversation = chat_bot.export_conversation()
print(f"📤 已匯出 {len(conversation)} 則訊息")
continue
# 分析上下文相關性
relevance = chat_bot.analyze_context_relevance(user_input)
print(f"🔍 分析中... (相關性: {relevance.get('relevance', 0.5):.2f})")
# 獲取回應
response = chat_bot.chat(user_input)
print(f"🤖 助理:{response}")
# 顯示上下文提示
if relevance.get('is_topic_change', False):
print("💡 提示:檢測到話題轉換")
except KeyboardInterrupt:
print(f"\n📊 對話總結:")
print(chat_bot.get_context_summary())
print("\n👋 再見!")
break
except Exception as e:
print(f"\n❌ 發生錯誤:{e}")
continue
if __name__ == "__main__":
main()
💬 你:我想學習 Python 程式設計
🤖 助理:太好了!Python 是一門非常適合初學者的程式語言...
💬 你:它難學嗎?
🤖 助理:根據剛才我們討論的 Python,它其實相對容易學習...
💬 你:推薦一些學習資源
🤖 助理:基於你想學習 Python 的需求,我推薦以下資源...
💬 你:/stats
📊 對話統計:
• 總訊息數:6
• 當前上下文:6 則訊息
• Token 使用:1250/4000
• 對話時長:3.2 分鐘
• 主要話題:python, learning
def calculate_context_weight(message_age: int, relevance: float) -> float:
"""計算上下文權重"""
time_decay = 0.9 ** message_age # 時間衰減
return relevance * time_decay
def cluster_messages_by_topic(messages: List[ConversationMessage]) -> Dict[str, List]:
"""按主題聚類訊息"""
topic_clusters = {}
for msg in messages:
for topic in msg.topic_tags:
if topic not in topic_clusters:
topic_clusters[topic] = []
topic_clusters[topic].append(msg)
return topic_clusters
def smart_summarization(messages: List[ConversationMessage]) -> str:
"""智能摘要策略"""
# 按重要性和相關性選擇關鍵訊息
important_messages = [msg for msg in messages if msg.relevance_score > 0.7]
return generate_summary(important_messages)
今天我們建立了一個完整的上下文管理系統,讓 AI 助理能夠:
✅ 記憶對話歷史:保存和管理完整的對話記錄
✅ 智能上下文選擇:根據相關性篩選重要上下文
✅ 動態壓縮策略:在記憶容量限制下保留最重要的資訊
✅ 個性化體驗:記住使用者偏好和背景資訊
✅ 話題追蹤:識別和處理話題轉換
明天我們將進一步探索記憶功能的進階應用,包括長期記憶、使用者建模和個性化推薦系統!
今天的上下文管理系統為我們的 AI 助理增添了真正的「記憶」能力,明天我們將讓這個記憶變得更加智能和個性化!