源文件:chapter2/localllmserving/README.md 通用工具调用 Demo 一个跨平台的 LLM 工具调用演示,使用标准 OpenAI 兼容 API。可在 Windows、macOS 和 Linux 上无缝运行,自动为你的系统选择最合适的后端。 🌟 特性 通用兼容:单一入口( )即可在所有平台上运行 自动后端选择: 在带 NVIDIA GPU 的 Linux/Windows 上使用 vLLM 在 macOS、Windows 或无 GPU 的 Linux 上使用 Ollama 标准工具调用:仅使用 OpenAI 兼容的工具调用格式 内置工具:天气、计算器、时间,且易于添加自定义工具 交互与示例模式:用示例测试或交互式聊天 🆕
源文件:chapter2/local_llm_serving/README.md
一个跨平台的 LLM 工具调用演示,使用标准 OpenAI 兼容 API。可在 Windows、macOS 和 Linux 上无缝运行,自动为你的系统选择最合适的后端。
main.py)即可在所有平台上运行# 1. Clone the repository git clone <repository> cd chapter2/local_llm_serving # 2. Install dependencies pip install -r requirements.txt # 3. Check your system compatibility python check_compatibility.py # 4. Run the main script (auto-detects best backend) python main.py
就这么简单!脚本会自动检测你的平台并使用合适的后端。
pip install -r requirements.txt# Install Ollama brew install ollama # Start Ollama service (in separate terminal) ollama serve # Download a model ollama pull qwen3:0.6b
带 NVIDIA GPU:
无 GPU:
# Download and install Ollama # From: https://ollama.com/download/windows # Pull a model ollama pull qwen3:0.6b
带 NVIDIA GPU:
无 GPU:
# Install Ollama curl -fsSL https://ollama.com/install.sh | sh # Start service systemctl start ollama # Pull a model ollama pull qwen3:0.6b
# Run with auto-detection (recommended) python main.py # Run examples only python main.py --mode examples # Run interactive mode only python main.py --mode interactive # Force specific backend python main.py --backend ollama # Force Ollama python main.py --backend vllm # Force vLLM (requires GPU) # Show system info python main.py --info
from main import ToolCallingAgent # Initialize (auto-detects best backend) agent = ToolCallingAgent() # Send a message response = agent.chat("What's the weather in Tokyo?") print(response) # Disable tools for a query response = agent.chat("Tell me a joke", use_tools=False) # Reset conversation agent.reset_conversation()
from tools import ToolRegistry # Get the tool registry registry = ToolRegistry() # Define your tool function def my_custom_tool(param1: str, param2: int) -> str: return f"Processed {param1} with {param2}" # Register it registry.register_tool( name="my_custom_tool", function=my_custom_tool, description="My custom tool description", parameters={ "type": "object", "properties": { "param1": {"type": "string", "description": "First parameter"}, "param2": {"type": "integer", "description": "Second parameter"} }, "required": ["param1", "param2"] } )
local_llm_serving/ ├── main.py # Main entry point (auto-detects backend) ├── benchmark.py # Serving benchmark: throughput / TTFT / KV cache / batching ├── agent.py # vLLM agent implementation ├── ollama_native.py # Ollama native tool calling ├── tools.py # Tool implementations ├── config.py # Configuration settings ├── server.py # vLLM server manager ├── check_compatibility.py # System compatibility checker ├── requirements.txt # Python dependencies ├── env.example # Environment variables template └── README.md # This file
Agent 现已支持流式响应,会展示:
# Streaming is enabled by default python main.py # Disable streaming python main.py --no-stream # Toggle streaming during chat with /stream command
from main import ToolCallingAgent # Initialize agent agent = ToolCallingAgent() # Stream response for chunk in agent.chat("What's the weather in Tokyo?", stream=True): chunk_type = chunk.get("type") content = chunk.get("content", "") if chunk_type == "thinking": print(f"Thinking: {content}") elif chunk_type == "tool_call": print(f"Tool: {content['name']}") elif chunk_type == "tool_result": print(f"Result: {content}") elif chunk_type == "content": print(content, end="", flush=True)
# Run streaming demo python demo_streaming.py # Compare streaming vs regular mode python test_streaming.py --mode compare
benchmark.py)benchmark.py 是实验 2-1 的配套基准,用于测量本地部署的小模型在 服务(serving) 层面的核心指标,帮助建立对吞吐 / 延迟 / 批处理 / KV Cache 的直觉。它通过 OpenAI 兼容接口工作,vLLM 与 Ollama 均可。
所有数字都来自真实服务端的实测,脚本本身不产生任何合成数据。 如果服务端尚未启动,可用 --dry-run 离线查看每个场景将要发出的请求配置。
--scenario)| 场景 | 说明 | 对应书中要点 |
|---|---|---|
throughput |
单流解码吞吐(tok/s)与首 token 延迟(TTFT) | 实验 2-1 第 2 点:M2 上 >100 tok/s |
kv-cache |
前缀缓存 命中 vs 未命中 的 TTFT 对比 | 实验 2-1 第 5 点:改动系统提示词开头 → 缓存失效、需重算整个前缀 |
batching |
不同并发度下的聚合吞吐(批处理权衡) | 连续批处理如何提升系统吞吐 |
all |
依次运行以上全部场景(默认) | — |
# 1. 先启动服务端(二选一) python server.py # vLLM(需要 NVIDIA GPU) ollama serve && ollama pull qwen3:0.6b # Ollama(Mac / 无 GPU) # 2. 运行基准 python benchmark.py --scenario all --output results.json # 跑全部并保存 python benchmark.py --scenario kv-cache --backend ollama # 只看 KV Cache TTFT 对比 python benchmark.py --scenario batching --concurrency 1,2,4,8 # 批处理吞吐扫描 # 离线查看计划(不访问服务端),可用于验证参数 python benchmark.py --dry-run python benchmark.py --help
--backend {vllm,ollama}:推断默认地址与模型名(vLLM Qwen3-0.6B @ :8000/v1,Ollama qwen3:0.6b @ :11434/v1)--base-url / --model / --api-key:覆盖默认连接配置--repeats:throughput / kv-cache 的重复次数(默认 5)--max-tokens / --temperature:生成参数--prefix-tokens:kv-cache 场景共享前缀的近似长度,越长缓存效果越明显(默认 1024)--concurrency:batching 场景的并发度列表,逗号分隔(默认 1,2,4,8)--output:将结果写入 JSON 文件说明:
kv-cache依赖服务端的前缀缓存(vLLM 的 automatic prefix caching 默认开启)。命中组保持系统提示词逐字节不变;未命中组每次只在系统提示词开头插入一个唯一计数串,前缀被改写导致缓存全部失效——这正是书中「系统提示词一旦定下来就不要改」的实测演示。
把 env.example 复制为 .env 并自定义:
# For vLLM (if you have GPU) MODEL_NAME=Qwen/Qwen3-0.6B VLLM_HOST=localhost VLLM_PORT=8000 # Logging LOG_LEVEL=INFO
本项目使用标准 OpenAI 兼容工具调用:
{ "tool_calls": [{ "id": "call_123", "type": "function", "function": { "name": "get_weather", "arguments": {"location": "Tokyo"} } }] }
没有临时解析或自定义格式——只是各平台通用的标准。
brew install ollama && ollama servecurl -fsSL https://ollama.com/install.sh | shollama pull qwen3:0.6b # Default model used by this project
python check_compatibility.py
main.py 检测你的操作系统与 GPU 可用性本 demo 项目按"原样"提供,用于教学目的。