一、Python 环境与工具链
uv的定位:pip + venv + pyenv + pipx 的合体,Rust 写的,快 10~100 倍uv python install:安装独立 Python 版本,不污染系统uv python pin:修改.python-version,钉死项目 Python 版本.python-version优先级高于pyproject.toml的requires-pythonuv venv:创建隔离环境.venv/uv add:装包 + 写pyproject.toml+ 生成uv.lockuv run:在.venv里执行命令,无需手动activateuv pip show:查看已装包版本pin的含义:钉死版本,让工具别再自己猜(.python-version钉 Python,uv.lock钉依赖,==钉包版本)
二、MCP 核心概念
- MCP 是工具层/连接协议:让 LLM 标准化发现和调用外部工具,不参与推理
- MCP 与 Agent 的关系:先学 MCP(手脚),再学 Agent(大脑)
- MCP Server 的三层结构:Server 实例 →
@mcp.tool()挂工具 →mcp.run()启动 @mcp.tool()做的事:读函数名(工具名)、类型注解(参数 schema)、docstring(工具描述),自动生成 JSON Schema- 工具函数两条惯例:返回值统一
str;错误不raise,直接return字符串 FastMCP是 v1 的名字,v2 改名为MCPServer(from mcp.server.mcpserver import MCPServer)- 其他装饰器:
@mcp.resource()(只读资源)、@mcp.prompt()(提示词模板)
三、Transport 传输方式
transport="stdio":用标准输入输出管道通信,不开端口stdio工作方式:Server 监听 stdin,响应写 stdout;Client 创建子进程接管管道stdio的优点:简单、零配置、天然隔离;缺点:只能本机- 其他传输:
sse(HTTP + SSE)、streamable-http(HTTP 流式) stdio的坑:工具函数里不能往 stdout 打印普通文本,调试用 stderrstdio必须一条命令启动:Server 是 Inspector 的子进程,父子关系启动时建立,不能分开执行
四、JSON-RPC 协议交互
- 一次工具调用 = 6 次 JSON-RPC 往返:
initialize请求/响应 →tools/list请求/响应 →tools/call请求/响应 - 消息固定结构:
{"jsonrpc":"2.0","id":1,"method":"tools/call","params":{...}} - Server 的本质:把 Python 函数"翻译"成 JSON Schema 说明书,等待调用,不会思考
- 真正决定调用什么的是 Client 一侧
五、Inspector 调试工具
- Inspector 是官方参考调试工具,扮演"临时替身 Client"
- 三种形态:Web 界面(默认)、CLI、TUI
- Inspector 不读 Python 代码,只读协议消息,根据 JSON Schema 自动渲染表单
- 改代码重启 Server,表单自动更新——协议化的好处
- Inspector 的启动命令:
pnpm dlx @modelcontextprotocol/inspector uv run python server.py - 必须用带
?MCP_INSPECTOR_API_TOKEN=...的完整 URL 打开浏览器 - npm 原生绑定 bug(npm/cli#4828):Apple Silicon 上缺
darwin-arm64,用pnpm绕过
六、手搓 Client
- Client 四步:连接 Server →
list_tools()→ schema 转 Ollama 格式 → 跑 Agent 循环 - Schema 翻译几乎是透传:MCP 的
inputSchema就是标准 JSON Schema,Ollama 的parameters也接受 messages.append(response.message)不能漏:包含模型"决定调工具"的动作记录role: "tool"回填格式:{"role": "tool", "tool_name": ..., "content": ...}ollama是第三方包,需uv add ollama
七、模型决策 vs 框架执行
chat(...)是模型做决策:读对话历史 + 工具清单 → 判断调不调、调哪个、传什么参数session.call_tool(...)是框架执行决策:通过 MCP 协议真正调用工具- 模型只负责"说",框架负责"做"
- 模型选工具的依据:几乎完全来自工具的
description和参数 schema - docstring 要写"什么时候用",不只是"函数做什么"
- 三个常见误解:LLM 不会自动"发现"工具(框架每轮塞清单)、不会执行工具(只输出调用请求)、可能选错(3B 小模型工具多了易混淆)
八、最简 Agent
- 最简 Agent = 控制台输入 + LLM 决策 + MCP 工具执行 + 结果回填 + 循环
- Agent 五要素:感知(
input())、决策(chat)、行动(call_tool)、反馈(role:tool回填)、循环(内外两层while) - ReAct 模式:Reasoning + Acting 的最小实现,思考 → 行动 → 观察 → 再思考
- 最简 Agent 的四个局限:无记忆管理(messages 无限增长)、无错误恢复(异常会崩)、无多步规划(3B 易乱)、工具描述依赖人工
九、HTTP 工具实践(查天气)
wttr.in免费 API,不需要 Key- 编码坑:
'ascii' codec can't encode characters——中文城市名和 format 参数的特殊字符没完全编码 - 解决:城市名和 format 参数分开
quote(..., safe="") timeout必加:防止网络卡住导致 Agent 卡死- 错误返回字符串而非抛异常:模型能理解并决定下一步
十、三层分工总结
| 层 | 角色 | 项目对应 |
|---|---|---|
| MCP | 工具层/连接协议(手脚) | server.py 的三个工具 |
| LLM | 决策层(大脑) | 千问 2.5 3B,经 Ollama 调用 |
| Agent 框架 | 神经,连接两者、跑循环、回填结果 | client.py 的 while True + call_tool |
- 一句话:MCP 提供手脚,LLM 提供大脑,Agent 框架是神经,三者配合才能完成任务
- 后续可加:记忆管理、错误恢复、多步规划、多工具协同