ref:让MaiSaka使用麦麦原有的pompt系统,配置系统
This commit is contained in:
613
src/maisaka/cli.py
Normal file
613
src/maisaka/cli.py
Normal file
@@ -0,0 +1,613 @@
|
||||
"""
|
||||
MaiSaka - CLI 交互界面与对话引擎
|
||||
BufferCLI 整合主循环、对话引擎、子代理管理。
|
||||
"""
|
||||
|
||||
import os
|
||||
import asyncio
|
||||
from datetime import datetime
|
||||
from typing import Optional
|
||||
|
||||
from rich.panel import Panel
|
||||
from rich.markdown import Markdown
|
||||
from rich.text import Text
|
||||
from rich import box
|
||||
|
||||
from config import console, ENABLE_EMOTION_MODULE, ENABLE_COGNITION_MODULE, ENABLE_TIMING_MODULE, ENABLE_KNOWLEDGE_MODULE, ENABLE_MCP
|
||||
from input_reader import InputReader
|
||||
from debug_client import DebugViewer
|
||||
from timing import build_timing_info
|
||||
from knowledge import store_knowledge_from_context, retrieve_relevant_knowledge, build_knowledge_summary
|
||||
from knowledge_store import get_knowledge_store
|
||||
from llm_service import BaseLLMService, OpenAILLMService
|
||||
from llm_service.utils import build_message, remove_last_perception
|
||||
from mcp_client import MCPManager
|
||||
from tool_handlers import (
|
||||
ToolHandlerContext,
|
||||
handle_say,
|
||||
handle_stop,
|
||||
handle_wait,
|
||||
handle_write_file,
|
||||
handle_read_file,
|
||||
handle_list_files,
|
||||
handle_store_context,
|
||||
handle_mcp_tool,
|
||||
handle_unknown_tool,
|
||||
handle_get_qq_chat_info,
|
||||
handle_send_info,
|
||||
handle_list_qq_chats,
|
||||
)
|
||||
|
||||
|
||||
class BufferCLI:
|
||||
"""命令行交互界面"""
|
||||
|
||||
def __init__(self):
|
||||
self.llm_service: Optional[BaseLLMService] = None
|
||||
self._reader = InputReader()
|
||||
self._chat_history: Optional[list] = None # 持久化的对话历史
|
||||
self._knowledge_store = get_knowledge_store() # 了解存储实例
|
||||
|
||||
# 显示了解存储统计
|
||||
knowledge_stats = self._knowledge_store.get_stats()
|
||||
if knowledge_stats["total_items"] > 0:
|
||||
console.print(f"[success]✓ 了解系统: {knowledge_stats['total_items']}条特征信息[/success]")
|
||||
else:
|
||||
console.print("[muted]✓ 了解系统: 已初始化 (暂无数据)[/muted]")
|
||||
# Timing 模块时间戳跟踪
|
||||
self._chat_start_time: Optional[datetime] = None
|
||||
self._last_user_input_time: Optional[datetime] = None
|
||||
self._last_assistant_response_time: Optional[datetime] = None
|
||||
self._user_input_times: list[datetime] = [] # 所有用户输入时间戳
|
||||
# MCP 管理器(异步初始化,在 run() 中完成)
|
||||
self._mcp_manager: Optional[MCPManager] = None
|
||||
# Debug Viewer
|
||||
self._debug_viewer = DebugViewer()
|
||||
self._init_llm()
|
||||
|
||||
def _init_llm(self):
|
||||
"""初始化 LLM 服务"""
|
||||
api_key = os.getenv("OPENAI_API_KEY", "")
|
||||
base_url = os.getenv("OPENAI_BASE_URL", "")
|
||||
model = os.getenv("OPENAI_MODEL", "gpt-4o")
|
||||
thinking_env = os.getenv("ENABLE_THINKING", "").strip().lower()
|
||||
enable_thinking: Optional[bool] = (
|
||||
True if thinking_env == "true"
|
||||
else False if thinking_env == "false"
|
||||
else None
|
||||
)
|
||||
|
||||
if not api_key:
|
||||
console.print(
|
||||
Panel(
|
||||
"[warning]未检测到 OPENAI_API_KEY 环境变量![/warning]\n\n"
|
||||
"请设置以下环境变量(或在 .env 文件中配置):\n"
|
||||
" • OPENAI_API_KEY - 必填,API 密钥\n"
|
||||
" • OPENAI_BASE_URL - 可选,API 基地址\n"
|
||||
" • OPENAI_MODEL - 可选,模型名称(默认 gpt-4o)\n\n"
|
||||
"[muted]程序无法运行,请配置后重试。[/muted]",
|
||||
title="⚠️ 配置提示",
|
||||
border_style="yellow",
|
||||
)
|
||||
)
|
||||
return
|
||||
|
||||
self.llm_service = OpenAILLMService(
|
||||
api_key=api_key,
|
||||
base_url=base_url if base_url else None,
|
||||
model=model,
|
||||
enable_thinking=enable_thinking,
|
||||
)
|
||||
# 绑定 debug 回调
|
||||
self.llm_service.set_debug_callback(self._debug_viewer.send)
|
||||
console.print(f"[success]✓ LLM 服务已初始化[/success] [muted](模型: {model})[/muted]")
|
||||
|
||||
def _build_tool_context(self) -> ToolHandlerContext:
|
||||
"""构建工具处理器所需的上下文。"""
|
||||
ctx = ToolHandlerContext(
|
||||
llm_service=self.llm_service,
|
||||
reader=self._reader,
|
||||
user_input_times=self._user_input_times,
|
||||
)
|
||||
ctx.last_user_input_time = self._last_user_input_time
|
||||
return ctx
|
||||
|
||||
# ──────── 显示方法 ────────
|
||||
|
||||
def _show_banner(self):
|
||||
"""显示欢迎横幅"""
|
||||
banner = Text()
|
||||
banner.append("MaiSaka", style="bold cyan")
|
||||
banner.append(" v2.0\n", style="muted")
|
||||
banner.append("直接输入文字开始对话 | Ctrl+C 退出", style="muted")
|
||||
|
||||
console.print(Panel(banner, box=box.DOUBLE_EDGE, border_style="cyan", padding=(1, 2)))
|
||||
console.print()
|
||||
|
||||
# ──────── 上下文管理 ────────
|
||||
|
||||
def _get_safe_removal_indices(self, chat_history: list, count: int) -> list[int]:
|
||||
"""
|
||||
获取可以安全删除的消息索引。
|
||||
|
||||
确保 tool_calls 和 tool 响应消息成对删除,避免破坏 API 要求的配对关系。
|
||||
只删除完整的消息块(user/assistant + 可选的 tool 响应序列)。
|
||||
|
||||
保留最后 3 条非 tool 消息,避免删除可能还在处理中的内容。
|
||||
|
||||
Returns:
|
||||
可以安全删除的消息索引列表(从后往前排序)
|
||||
"""
|
||||
indices_to_remove = []
|
||||
removed_count = 0
|
||||
i = 0
|
||||
|
||||
# 计算保留的消息数量(最后 3 条非 tool 消息)
|
||||
safe_zone_count = 3
|
||||
non_tool_count = 0
|
||||
for msg in reversed(chat_history):
|
||||
if msg.get("role") != "tool":
|
||||
non_tool_count += 1
|
||||
if non_tool_count >= safe_zone_count:
|
||||
break
|
||||
|
||||
# 只处理前 (len - non_tool_count) 条消息
|
||||
max_process_index = len(chat_history) - non_tool_count
|
||||
|
||||
while i < max_process_index and removed_count < count:
|
||||
msg = chat_history[i]
|
||||
role = msg.get("role", "")
|
||||
|
||||
# 跳过 role=tool 的消息(它们会被对应的 assistant 消息一起处理)
|
||||
if role == "tool":
|
||||
i += 1
|
||||
continue
|
||||
|
||||
# 检查这是否是一个带 tool_calls 的 assistant 消息
|
||||
if role == "assistant" and "tool_calls" in msg:
|
||||
# 收集这个 assistant 消息及其后续的 tool 响应消息
|
||||
block_indices = [i]
|
||||
j = i + 1
|
||||
while j < len(chat_history):
|
||||
next_msg = chat_history[j]
|
||||
if next_msg.get("role") == "tool":
|
||||
block_indices.append(j)
|
||||
j += 1
|
||||
else:
|
||||
break
|
||||
indices_to_remove.extend(block_indices)
|
||||
removed_count += 1
|
||||
i = j
|
||||
elif role in ["user", "assistant"]:
|
||||
# 普通消息,可以直接删除
|
||||
indices_to_remove.append(i)
|
||||
removed_count += 1
|
||||
i += 1
|
||||
else:
|
||||
i += 1
|
||||
|
||||
# 从后往前排序,避免索引问题
|
||||
return sorted(indices_to_remove, reverse=True)
|
||||
|
||||
async def _manage_context_length(self, chat_history: list) -> None:
|
||||
"""
|
||||
上下文管理:当对话历史过长时进行压缩。
|
||||
|
||||
当达到 20 条上下文时:
|
||||
1. 移除最早 10 条上下文
|
||||
2. 对这 10 条内容进行 LLM 总结
|
||||
3. 将总结后的内容存入记忆
|
||||
"""
|
||||
CONTEXT_LIMIT = 20
|
||||
COMPRESS_COUNT = 10
|
||||
|
||||
# 计算实际消息数量(排除 role=tool 的工具返回消息)
|
||||
actual_messages = [m for m in chat_history if m.get("role") != "tool"]
|
||||
|
||||
if len(actual_messages) >= CONTEXT_LIMIT:
|
||||
# 获取安全删除的索引
|
||||
indices_to_remove = self._get_safe_removal_indices(chat_history, COMPRESS_COUNT)
|
||||
|
||||
if indices_to_remove:
|
||||
# 收集要总结的消息(在删除前)
|
||||
to_compress = []
|
||||
for i in sorted(indices_to_remove):
|
||||
if 0 <= i < len(chat_history):
|
||||
to_compress.append(chat_history[i])
|
||||
|
||||
if to_compress:
|
||||
# 总结上下文
|
||||
try:
|
||||
console.print("[accent]🧠 上下文过长,正在压缩并存入记忆...[/accent]")
|
||||
summary = await self.llm_service.summarize_context(to_compress)
|
||||
|
||||
# 存储了解信息(如果启用)
|
||||
if ENABLE_KNOWLEDGE_MODULE:
|
||||
try:
|
||||
knowledge_count = await store_knowledge_from_context(
|
||||
self.llm_service,
|
||||
to_compress,
|
||||
store_result_callback=lambda cat_id, cat_name, content: console.print(
|
||||
f"[muted] ✓ 存储了解信息: {cat_name}[/muted]"
|
||||
)
|
||||
)
|
||||
if knowledge_count > 0:
|
||||
console.print(f"[success]✓ 了解模块: 存储{knowledge_count}条特征信息[/success]")
|
||||
except Exception as e:
|
||||
console.print(f"[warning]了解存储失败: {e}[/warning]")
|
||||
if summary:
|
||||
# 存入记忆
|
||||
# 显示压缩结果
|
||||
console.print(
|
||||
Panel(
|
||||
Markdown(summary),
|
||||
title="📝 上下文已压缩",
|
||||
border_style="green",
|
||||
padding=(0, 1),
|
||||
style="dim",
|
||||
)
|
||||
)
|
||||
except Exception as e:
|
||||
console.print(f"[warning]上下文总结失败: {e}[/warning]")
|
||||
|
||||
# 从后往前删除
|
||||
for i in indices_to_remove:
|
||||
if 0 <= i < len(chat_history):
|
||||
chat_history.pop(i)
|
||||
|
||||
# 清理"孤儿" tool 消息(没有对应 tool_calls 的 tool 消息)
|
||||
valid_tool_call_ids = set()
|
||||
for msg in chat_history:
|
||||
if msg.get("role") == "assistant" and "tool_calls" in msg:
|
||||
for tool_call in msg["tool_calls"]:
|
||||
valid_tool_call_ids.add(tool_call.get("id", ""))
|
||||
|
||||
# 删除无效的 tool 消息(从后往前)
|
||||
i = len(chat_history) - 1
|
||||
while i >= 0:
|
||||
msg = chat_history[i]
|
||||
if msg.get("role") == "tool":
|
||||
tool_call_id = msg.get("tool_call_id", "")
|
||||
if tool_call_id not in valid_tool_call_ids:
|
||||
chat_history.pop(i)
|
||||
i -= 1
|
||||
|
||||
# ──────── LLM 循环架构 ────────
|
||||
|
||||
async def _start_chat(self, user_text: str):
|
||||
"""接收用户输入并启动/继续 LLM 对话循环"""
|
||||
if not self.llm_service:
|
||||
console.print("[warning]LLM 服务未初始化,跳过对话。[/warning]")
|
||||
return
|
||||
|
||||
now = datetime.now()
|
||||
self._last_user_input_time = now
|
||||
self._user_input_times.append(now)
|
||||
|
||||
if self._chat_history is None:
|
||||
# 首次对话:初始化上下文
|
||||
self._chat_start_time = now
|
||||
self._last_assistant_response_time = None
|
||||
self._chat_history = self.llm_service.build_chat_context(user_text)
|
||||
else:
|
||||
# 后续对话:追加用户消息到已有上下文
|
||||
self._chat_history.append({
|
||||
"role": "user",
|
||||
"content": user_text,
|
||||
})
|
||||
|
||||
await self._run_llm_loop(self._chat_history)
|
||||
|
||||
async def _run_llm_loop(self, chat_history: list):
|
||||
"""
|
||||
LLM 循环架构核心。
|
||||
|
||||
LLM 持续运行,每步可能输出文本(内心思考)和/或调用工具:
|
||||
- say(text): 对用户说话
|
||||
- wait(seconds): 暂停等待用户输入,超时或收到输入后继续
|
||||
- stop(): 结束循环,进入待机,直到用户下次输入
|
||||
- 不调用工具: 继续下一轮思考/生成
|
||||
|
||||
每轮流程:
|
||||
1. 上下文管理:达到上限时自动压缩
|
||||
2. 情商 + Timing + 了解模块(并行):分析用户情绪、对话时间节奏、检索用户特征
|
||||
*注:如果上次没有调用工具,跳过模块分析
|
||||
3. 调用主 LLM:基于完整上下文生成响应
|
||||
"""
|
||||
consecutive_errors = 0
|
||||
last_had_tool_calls = True # 第一次循环总是执行模块分析
|
||||
|
||||
while True:
|
||||
# ── 上下文管理 ──
|
||||
await self._manage_context_length(chat_history)
|
||||
|
||||
# ── 情商模块 + Timing 模块 + 了解模块(并行) ──
|
||||
# 只有上次调用了工具才重新分析(首次循环除外)
|
||||
if last_had_tool_calls:
|
||||
timing_info = build_timing_info(
|
||||
self._chat_start_time,
|
||||
self._last_user_input_time,
|
||||
self._last_assistant_response_time,
|
||||
self._user_input_times,
|
||||
)
|
||||
|
||||
# 根据配置决定要执行的模块
|
||||
tasks = []
|
||||
status_text_parts = []
|
||||
|
||||
if ENABLE_EMOTION_MODULE:
|
||||
tasks.append(("eq", self.llm_service.analyze_emotion(chat_history)))
|
||||
status_text_parts.append("🎭")
|
||||
if ENABLE_COGNITION_MODULE:
|
||||
tasks.append(("cognition", self.llm_service.analyze_cognition(chat_history)))
|
||||
status_text_parts.append("🧩")
|
||||
if ENABLE_TIMING_MODULE:
|
||||
tasks.append(("timing", self.llm_service.analyze_timing(chat_history, timing_info)))
|
||||
status_text_parts.append("⏱️🪞")
|
||||
if ENABLE_KNOWLEDGE_MODULE:
|
||||
tasks.append(("knowledge", retrieve_relevant_knowledge(self.llm_service, chat_history)))
|
||||
status_text_parts.append("👤")
|
||||
|
||||
with console.status(
|
||||
f"[info]{' '.join(status_text_parts)} {' + '.join(status_text_parts)} 模块并行分析中...[/info]",
|
||||
spinner="dots",
|
||||
):
|
||||
results = await asyncio.gather(*[task for _, task in tasks], return_exceptions=True)
|
||||
|
||||
# 解析结果
|
||||
eq_result, cognition_result, timing_result, knowledge_result = None, None, None, None
|
||||
result_idx = 0
|
||||
if ENABLE_EMOTION_MODULE:
|
||||
eq_result = results[result_idx]
|
||||
result_idx += 1
|
||||
if ENABLE_COGNITION_MODULE:
|
||||
cognition_result = results[result_idx]
|
||||
result_idx += 1
|
||||
if ENABLE_TIMING_MODULE:
|
||||
timing_result = results[result_idx]
|
||||
result_idx += 1
|
||||
if ENABLE_KNOWLEDGE_MODULE:
|
||||
knowledge_result = results[result_idx]
|
||||
result_idx += 1
|
||||
|
||||
# 处理情商模块结果
|
||||
eq_analysis = ""
|
||||
if ENABLE_EMOTION_MODULE:
|
||||
if isinstance(eq_result, Exception):
|
||||
console.print(f"[warning]情商模块分析失败: {eq_result}[/warning]")
|
||||
elif eq_result:
|
||||
eq_analysis = eq_result
|
||||
console.print(
|
||||
Panel(
|
||||
Markdown(eq_analysis),
|
||||
title="🎭 情绪感知",
|
||||
border_style="bright_yellow",
|
||||
padding=(0, 1),
|
||||
style="dim",
|
||||
)
|
||||
)
|
||||
|
||||
# 处理认知模块结果
|
||||
cognition_analysis = ""
|
||||
if ENABLE_COGNITION_MODULE:
|
||||
if isinstance(cognition_result, Exception):
|
||||
console.print(f"[warning]认知模块分析失败: {cognition_result}[/warning]")
|
||||
elif cognition_result:
|
||||
cognition_analysis = cognition_result
|
||||
console.print(
|
||||
Panel(
|
||||
Markdown(cognition_analysis),
|
||||
title="🧩 意图感知",
|
||||
border_style="bright_cyan",
|
||||
padding=(0, 1),
|
||||
style="dim",
|
||||
)
|
||||
)
|
||||
|
||||
# 处理 Timing 模块结果(含自我反思功能)
|
||||
timing_analysis = ""
|
||||
if ENABLE_TIMING_MODULE:
|
||||
if isinstance(timing_result, Exception):
|
||||
console.print(f"[warning]Timing 模块分析失败: {timing_result}[/warning]")
|
||||
elif timing_result:
|
||||
timing_analysis = timing_result
|
||||
console.print(
|
||||
Panel(
|
||||
Markdown(timing_analysis),
|
||||
title="⏱️🪞 时间感知 & 自我反思",
|
||||
border_style="bright_blue",
|
||||
padding=(0, 1),
|
||||
style="dim",
|
||||
)
|
||||
)
|
||||
|
||||
# 处理了解模块结果
|
||||
knowledge_analysis = ""
|
||||
if ENABLE_KNOWLEDGE_MODULE:
|
||||
if isinstance(knowledge_result, Exception):
|
||||
console.print(f"[warning]了解模块分析失败: {knowledge_result}[/warning]")
|
||||
elif knowledge_result:
|
||||
knowledge_analysis = knowledge_result
|
||||
console.print(
|
||||
Panel(
|
||||
Markdown(knowledge_analysis),
|
||||
title="👤 用户特征",
|
||||
border_style="bright_magenta",
|
||||
padding=(0, 1),
|
||||
style="dim",
|
||||
)
|
||||
)
|
||||
|
||||
# 注入感知信息(作为 assistant 的感知消息)
|
||||
# 移除上一条感知消息(如果存在)
|
||||
remove_last_perception(chat_history)
|
||||
|
||||
# 构建感知内容
|
||||
perception_parts = []
|
||||
if eq_analysis:
|
||||
perception_parts.append(f"情绪感知\n{eq_analysis}")
|
||||
if cognition_analysis:
|
||||
perception_parts.append(f"意图感知\n{cognition_analysis}")
|
||||
if timing_analysis:
|
||||
perception_parts.append(f"时间感知 & 自我反思\n{timing_analysis}")
|
||||
if knowledge_analysis:
|
||||
perception_parts.append(f"用户特征\n{knowledge_analysis}")
|
||||
|
||||
if perception_parts:
|
||||
# 添加感知消息(AI 的感知能力结果)
|
||||
chat_history.append(build_message(
|
||||
role="assistant",
|
||||
content="\n\n".join(perception_parts),
|
||||
msg_type="perception",
|
||||
))
|
||||
else:
|
||||
# 上次没有调用工具,跳过模块分析
|
||||
console.print("[muted]ℹ️ 上次未调用工具,跳过模块分析[/muted]")
|
||||
|
||||
|
||||
# ── 调用 LLM ──
|
||||
with console.status("[info]💬 AI 正在思考...[/info]", spinner="dots"):
|
||||
try:
|
||||
response = await self.llm_service.chat_loop_step(chat_history)
|
||||
consecutive_errors = 0
|
||||
except Exception as e:
|
||||
consecutive_errors += 1
|
||||
console.print(f"[error]LLM 调用出错: {e}[/error]")
|
||||
if consecutive_errors >= 3:
|
||||
console.print("[error]连续出错,退出对话[/error]\n")
|
||||
break
|
||||
continue
|
||||
|
||||
# 将 assistant 消息追加到历史
|
||||
chat_history.append(response.raw_message)
|
||||
self._last_assistant_response_time = datetime.now()
|
||||
|
||||
# 显示内心思考(content 部分,淡色呈现)
|
||||
if response.content:
|
||||
console.print(
|
||||
Panel(
|
||||
Markdown(response.content),
|
||||
title="💭 内心思考",
|
||||
border_style="dim",
|
||||
padding=(1, 2),
|
||||
style="dim",
|
||||
)
|
||||
)
|
||||
|
||||
# ── 处理工具调用 ──
|
||||
if response.tool_calls:
|
||||
should_stop = False
|
||||
ctx = self._build_tool_context()
|
||||
|
||||
for tc in response.tool_calls:
|
||||
if tc.name == "say":
|
||||
await handle_say(tc, chat_history, ctx)
|
||||
|
||||
elif tc.name == "stop":
|
||||
await handle_stop(tc, chat_history)
|
||||
should_stop = True
|
||||
|
||||
elif tc.name == "wait":
|
||||
tool_result = await handle_wait(tc, chat_history, ctx)
|
||||
# 同步回 timing 时间戳
|
||||
if ctx.last_user_input_time != self._last_user_input_time:
|
||||
self._last_user_input_time = ctx.last_user_input_time
|
||||
if tool_result.startswith("[[QUIT]]"):
|
||||
should_stop = True
|
||||
|
||||
elif tc.name == "write_file":
|
||||
await handle_write_file(tc, chat_history)
|
||||
|
||||
elif tc.name == "read_file":
|
||||
await handle_read_file(tc, chat_history)
|
||||
|
||||
elif tc.name == "list_files":
|
||||
await handle_list_files(tc, chat_history)
|
||||
|
||||
elif tc.name == "store_context":
|
||||
await handle_store_context(tc, chat_history, ctx)
|
||||
|
||||
elif tc.name == "get_qq_chat_info":
|
||||
await handle_get_qq_chat_info(tc, chat_history)
|
||||
|
||||
elif tc.name == "send_info":
|
||||
await handle_send_info(tc, chat_history)
|
||||
|
||||
elif tc.name == "list_qq_chats":
|
||||
await handle_list_qq_chats(tc, chat_history)
|
||||
|
||||
elif self._mcp_manager and self._mcp_manager.is_mcp_tool(tc.name):
|
||||
await handle_mcp_tool(tc, chat_history, self._mcp_manager)
|
||||
|
||||
else:
|
||||
await handle_unknown_tool(tc, chat_history)
|
||||
|
||||
if should_stop:
|
||||
console.print("[muted]对话暂停,等待新输入...[/muted]\n")
|
||||
break
|
||||
|
||||
# 调用了工具,下次循环需要重新分析模块
|
||||
last_had_tool_calls = True
|
||||
else:
|
||||
# LLM 未调用任何工具 → 继续下一轮思考
|
||||
# (不做任何额外操作,直接回到循环顶部再次调用 LLM)
|
||||
# 标记上次没有调用工具,下次循环跳过模块分析
|
||||
last_had_tool_calls = False
|
||||
|
||||
# ──────── 主循环 ────────
|
||||
|
||||
async def _init_mcp(self):
|
||||
"""初始化 MCP 服务器连接,发现并注册外部工具。"""
|
||||
config_path = os.path.join(
|
||||
os.path.dirname(os.path.abspath(__file__)), "mcp_config.json",
|
||||
)
|
||||
self._mcp_manager = await MCPManager.from_config(config_path)
|
||||
|
||||
if self._mcp_manager and self.llm_service:
|
||||
mcp_tools = self._mcp_manager.get_openai_tools()
|
||||
if mcp_tools:
|
||||
self.llm_service.set_extra_tools(mcp_tools)
|
||||
summary = self._mcp_manager.get_tool_summary()
|
||||
console.print(
|
||||
Panel(
|
||||
f"已加载 {len(mcp_tools)} 个 MCP 工具:\n{summary}",
|
||||
title="🔌 MCP 工具",
|
||||
border_style="green",
|
||||
padding=(0, 1),
|
||||
)
|
||||
)
|
||||
|
||||
async def run(self):
|
||||
"""主循环:直接输入文本即可对话"""
|
||||
# 启动调试窗口
|
||||
self._debug_viewer.start()
|
||||
|
||||
# 根据配置决定是否初始化 MCP 服务器
|
||||
if ENABLE_MCP:
|
||||
await self._init_mcp()
|
||||
else:
|
||||
console.print("[muted]🔌 MCP 已禁用 (ENABLE_MCP=false)[/muted]")
|
||||
|
||||
# 启动异步输入读取器
|
||||
self._reader.start(asyncio.get_event_loop())
|
||||
|
||||
self._show_banner()
|
||||
|
||||
try:
|
||||
while True:
|
||||
console.print("[bold cyan]> [/bold cyan]", end="")
|
||||
raw_input = await self._reader.get_line()
|
||||
|
||||
if raw_input is None: # EOF
|
||||
console.print("\n[muted]再见![/muted]")
|
||||
break
|
||||
|
||||
raw_input = raw_input.strip()
|
||||
if not raw_input:
|
||||
continue
|
||||
|
||||
await self._start_chat(raw_input)
|
||||
finally:
|
||||
self._debug_viewer.close()
|
||||
if self._mcp_manager:
|
||||
await self._mcp_manager.close()
|
||||
Reference in New Issue
Block a user