CLI对话工具

一个基于Ollama的多轮对话CLI工具,支持显式工具指令和模型自主调用MCP工具的功能。

  • by MiaoJieF
  • Python
  • 本地部署
  • 更新于
在 GitHub 查看 MiaoJieF/LLM_mcp_pro

使用说明

LLM MCP CLI Chat(Ollama + MCP 自主工具调用)

项目简介

本项目提供一个基于 Ollama 的多轮对话 CLI,并在其上实现了 MCP 工具体系:

  • 支持显式工具指令(如 /time、/bmi)
  • 支持“模型自主决定是否调用 MCP 工具”的闭环:模型先给出 JSON 决策,程序执行工具并将结果回灌上下文,再让模型产出最终答复

安装与运行

依赖环境

  • Python 3.9+
  • 已安装并可访问的 Ollama 服务(默认 http://127.0.0.1:11434)

安装依赖

pip install -r requirements.txt

requirements.txt 中包含:

  • requests:调用 Ollama HTTP API
  • colorama:CLI 颜色输出

准备模型

ollama pull gemma3:1b

启动程序

python main.py --model gemma3:1b

可选参数:

python main.py --model gemma3:1b --host http://127.0.0.1:11434

项目结构

  • main.py:命令行入口,读取用户输入并打印模型回复
  • conversation_manager.py:对话编排与 MCP 路由(显式指令 / 自主调用 / 常规对话)
  • mcp_handler.py:MCP 工具逻辑与执行,包含 BMI 解析、分类等
  • ollama_client.py:与 Ollama 的 HTTP 接口封装
  • README.md:说明文档

功能一览

  • 多轮对话:完整保留上下文(system/user/assistant 消息)
  • 显式 MCP 指令:
    • /time 返回当前系统时间
    • /bmi 身高(米) 体重(千克) 计算 BMI 并返回状态
  • 自主 MCP 调用:
    • 模型先输出仅含 JSON 的决策:{"use_tool": bool, "tool": "time|bmi", "args": {...}}
    • 程序按决策执行工具,将结果以“(来自MCP)”记录进对话历史
    • 再次调用模型生成最终答复(可直接引用 MCP 结果)

MCP 工具创建流程(以 time / bmi 为例)

工具的核心在于“输入解析 + 业务计算 + 结构化输出”。本项目提供两种入口:

1. 显式指令入口(Slash Commands)

在 mcp_handler.py 中:

  • handle_mcp_command(text):识别以 / 开头的显式指令;
  • /time:直接返回当前时间;
  • /bmi:解析身高体重,计算 BMI 并做分级。

关键片段(简化示意):

# handle_mcp_command 内部要点
if text.lower() == "/time":
    now = datetime.now().strftime("%Y-%m-%d %H:%M:%S")
    return MCPResult(handled=True, response=f"当前时间:{now}")

if text.lower().startswith("/bmi"):
    height_m, weight_kg = parse_bmi_args(text)
    bmi = weight_kg / (height_m * height_m)
    return MCPResult(handled=True, response=f"BMI: {bmi:.2f},状态:{bmi_category(bmi)}")

2. 程序化执行入口(execute_tool)

当模型决策需要调用工具时,conversation_manager.py 会调用 mcp_handler.execute_tool(tool, args):

# mcp_handler.execute_tool 要点
if tool in {"time", "/time"}: ...
if tool in {"bmi", "/bmi"}: ...  # 接受 height_m/height 与 weight_kg/weight

要新增工具,仅需:

  1. 在 execute_tool 中添加该工具的名称分支与参数校验;
  2. 在 handle_mcp_command 中(可选)添加对应的显式指令解析;
  3. 在 README 中记录工具名称、参数与返回格式。

让模型“自动调用 MCP”的思路与实现

设计思路

  • 将“是否调用工具”的判断交给模型,但要求其以严格 JSON 给出决策,便于程序解析。
  • 若模型建议调用工具:程序执行之,并把结果写回对话历史(以“(来自MCP)”开头),引导模型将其视为已可信事实。
  • 再次询问模型生成最终、面向用户的自然语言答复。

实现要点(conversation_manager.py)

  1. 先走显式 slash 指令的快速通道:
    • 命中后直接返回结果,并把“(来自MCP)”注入历史,供后续对话引用。
  2. 否则进入自主决策流程:
    • 临时拼接一条 system 约束消息,要求模型“仅输出 JSON 决策结构”;
    • 解析模型返回的文本,提取首尾花括号之间的 JSON;
    • 若 use_tool == true,调用 execute_tool 执行;
    • 将工具结果以“(来自MCP)”记录到历史;
    • 再次请求模型,生成最终答复给用户。

伪代码(简化):

decision_text = chat([...system(JSON-only 指令), user_text])
obj = safe_json_parse(decision_text)
if obj.use_tool:
    tool_result = execute_tool(obj.tool, obj.args)
    add_assistant_message("(来自MCP)...结果:...")
    reply = chat(history_with_mcp_result)
else:
    reply = chat(history)

JSON 决策格式

{
  "use_tool": true,
  "tool": "time" | "bmi",
  "args": { "height_m": 1.75, "weight_kg": 65 },
  "reason": "可选的原因说明"
}

健壮性处理:程序会截取返回文本中的第一个 { 与最后一个 } 之间的子串再做解析,尽量容错。


交互示例

显式指令

你: /time
AI: 当前时间:2025-01-01 12:34:56

自主调用

你: 我身高1.75米,体重65公斤,帮我看是否正常?
AI: (内部先给 JSON 决策 → 执行 bmi 工具 → 写入(来自MCP) → 生成最终答复)

扩展建议

  • 新增更多工具:查询数据库、调用外部 API、读写本地资源等
  • 为工具结果添加结构化模式(如 JSON Schema),便于前端二次利用
  • 在决策阶段加入少量示例(few-shot),提升模型决策稳定性

常见问题

  • 模型输出的 JSON 不严格:已内置“首尾花括号截取”与异常捕获作为兜底。
  • BMI 参数单位混淆:本项目约定身高单位为米、体重单位为千克,execute_tool 会做范围校验并报错提示。

版权与许可

仅供学习与研究使用。