Python验证ollama部署的本地大模型API

预定义

# 绕过代理访问 localhost
os.environ['NO_PROXY'] = 'localhost,127.0.0.1'

# Ollama API 配置
OLLAMA_BASE_URL = "http://localhost:11434"

# 禁用代理的 session
session = requests.Session()
session.trust_env = False  # 不使用系统代理
MODEL_NAME = "qwen2.5-coder:7b"

检查模型状态

def check_ollama_status():
    """检查 Ollama 服务是否运行"""
    try:
        response = session.get(OLLAMA_BASE_URL, timeout=5)
        if response.status_code == 200:
            print("Ollama 服务正在运行")
            return True
    except requests.exceptions.RequestException as e:
        print(f"Ollama 服务连接失败: {e}")
        return False
    return False

列出本地可用模型(ollama list)

def list_models():
    """列出本地可用的模型"""
    try:
        response = session.get(f"{OLLAMA_BASE_URL}/api/tags", timeout=10)
        if response.status_code == 200:
            data = response.json()
            models = data.get("models", [])
            if models:
                print("\n 本地可用模型:")
                for model in models:
                    print(f"  - {model['name']} (大小: {model['size'] / (1024**3):.2f} GB)")
                return models
            else:
                print(" 没有找到本地模型")
                return []
    except requests.exceptions.RequestException as e:
        print(f" 获取模型列表失败: {e}")
        return []

大模型的输出

非流式输出

  • 模型在后台把整个回答全部写完,再一口气打包返回给你
  • 像发短信。你发送问题后,界面会“卡住”或显示加载中,等待几秒后,一大段文字突然同时弹出来
  • 等待时间长,首字延迟

流式输出

  • 模型每生成一个词或一个字,就立刻通过网络通道传回给你的程序
  • 像打字机。你发送问题后,界面几乎瞬间开始逐字显示内容
  • 不需要用户长时间等待,就像目前常见大模型聊天时的效果

非流式输出聊天

"stream": False

def chat_simple(message: str, model: str = MODEL_NAME):
    """简单聊天(非流式输出)"""
    url = f"{OLLAMA_BASE_URL}/api/chat"

    # "role": "user" 表示这是用户消息(还可以有 "system" 系统提示和 "assistant" 助手回复)
    # "stream": False — 关键参数!设为 `False` 表流式输出,模型会处理完整个请求后一次性返回完整响应

    payload = {
        "model": model,
        "messages": [
            {"role": "user", "content": message}
        ],
        "stream": False
    }
    
    try:
        print(f"\n 模型: {model}")
        print(f" 问题: {message}")
        print("-" * 50)
        
        response = session.post(url, json=payload, timeout=120)
        if response.status_code == 200: #HTTP 200 表示成功,解析 JSON 响应
            data = response.json()
            # 响应结构为 `{"message": {"role": "assistant", "content": "回答内容"}, ...}`,通过链式 `.get()` 安全提取回答文本
            answer = data.get("message", {}).get("content", "")
            print(f" 回答:\n{answer}")
            return answer
        else:
            print(f" 请求失败: HTTP {response.status_code}")
            return None
    except requests.exceptions.RequestException as e:
        print(f" 请求异常: {e}")
        return None

响应的数据结构

{
  "model": "qwen2.5-coder:7b",
  "created_at": "2026-06-16T08:00:00.000000Z",
  "message": {
    "role": "assistant",
    "content": "你好!我是Qwen,一个AI助手。"
  },
  "done": true,
  "done_reason": "stop",
  "total_duration": 5000000000,
  "load_duration": 1000000000,
  "prompt_eval_count": 15,
  "prompt_eval_duration": 500000000,
  "eval_count": 30,
  "eval_duration": 3500000000
}
提取数据拆解

answer = data.get("message", {}).get("content", "") 

第一步 `data.get("message", {})`

  1.  从响应字典 `data` 中取 `"message"` 键
  2.  如果 `"message"键存在,返回其值(即 `{"role": "assistant", "content": "回答内容"}`)
  3.  如果 `"message"` 键不存在(如 API 返回异常格式),返回默认值 `{}`(空字典),而不是抛出 `KeyError`

第二步 .get("content", "")

  1. 对上一步返回的字典取 `"content"` 键
  2. 如果 `"content"` 键存在,返回助手的回答文本
  3. 如果 `"content"` 键不存在,返回默认值 `""`(空字符串)

聊天中的角色

Chat API 的 `messages` 数组中,每条消息都有一个 `role` 字段,标识这条消息的"说话者"。三种角色各有明确分工

1. `system` — 系统角色(导演/规则制定者)

  • 作用:设定模型的行为规则和人格,不参与对话内容本身
  • 特点:
    • 通常放在 `messages` 数组最前面,且只出现一次
    • 用户看不到这条消息,但深刻影响模型的所有后续回答
    • 类似于给演员的"剧本设定"——规定了角色该怎么演
    • 可以控制:语言风格、专业领域、回答格式、禁止事项等

2. user — 用户角色(提问者)

  • 作用: 代表人类用户发送的消息/问题
  • 特点:
    • 就是用户的实际输入水电费
    • 在一次对话中可以出多次多轮对话中用户多次提问)
    • 模型会针对 `user` 消息生成回答

 3. `assistant` — 助手角色(模型/回答者)

  • 作用:代表模型生成的回答
  • 特点:
    • 是模型的回复,不是用户输入的
    • 在多轮对话中,需要把历史的 `assistant` 回复也放入 `messages`,模型才能"记住"之前说过什么
    • 如果不传历史 `assistant` 消息,模型就不知道自己之前回答了什么

流式输出聊天 

主要看下流式接收的代码


def chat_stream(message: str, model: str = MODEL_NAME):
    """流式聊天(实时输出)"""
    url = f"{OLLAMA_BASE_URL}/api/chat"
    payload = {
        "model": model,
        "messages": [
            {"role": "user", "content": message}
        ],
        "stream": True
    }
    
    try:
        print(f"\n 模型: {model}")
        print(f" 问题: {message}")
        print("-" * 50)
        print(" 回答: ", end="", flush=True)
        
        response = session.post(url, json=payload, stream=True, timeout=120)
        if response.status_code == 200:
            for line in response.iter_lines():
                if line:
                    data = json.loads(line)
                    content = data.get("message", {}).get("content", "")
                    print(content, end="", flush=True)
            print("\n")
            return True
        else:
            print(f"\n 请求失败: HTTP {response.status_code}")
            return False
    except requests.exceptions.RequestException as e:
        print(f"\n 请求异常: {e}")
        return False
非流式 vs 流式的 HTTP 响应差异:
stream: False(非流式)
─────────────────────────
客户端发送请求
    ... 等待 10 秒 ...
服务器一次性返回完整 JSON:
{"message":{"content":"你好!我是Qwen,一个AI助手。"},"done":true}
    ↑ 一次性收到全部内容

stream: True(流式)
─────────────────────────
客户端发送请求
服务器立即开始返回数据,逐行推送:
{"message":{"content":"你"},"done":false}       ← 0.5秒后
{"message":{"content":"好"},"done":false}        ← 0.6秒后
{"message":{"content":"!"},"done":false}       ← 0.7秒后
{"message":{"content":"我是"},"done":false}     ← 1.0秒后
{"message":{"content":"Qwen"},"done":false}     ← 1.2秒后
...
{"message":{"content":""},"done":true}          ← 10秒后,结束
    ↑ 边生成边返回,用户能实时看到

    打印收到的消息
    print("💡 回答: ", end="", flush=True)
    
    参数作用为什么需要
    " 回答: "打印提示文字让用户知道回答开始了
    end=""末尾不换行默认 end="\n" 会换行,这里需要后续内容接在同一行
    flush=True立即刷新输出缓冲区 默认 Python 输出有缓冲,可能不会立即显示

    代码中有两个 stream=True 
    • `payload` 中的 `"stream": True` 告诉 Ollama 服务器用流式格式返回
    • `session.post()` 的 `stream=True` — 告诉 requests模块读取响应,不要一次性下载整个响应体,而是保持连接,允许逐块读取。
    stream=False(requests 默认):
      客户端 → 服务器:给我数据
      服务器 → 客户端:[等10秒] 这是全部数据(100MB)
      客户端收到完整响应后才开始处理
    
    stream=True:
      客户端 → 服务器:给我数据
      服务器 → 客户端:第1块数据
      客户端可以立即处理第1块
      服务器 → 客户端:第2块数据
      客户端可以立即处理第2块
      ...
    

    for line in response.iter_lines():
    `iter_lines()` 是 `requests` 库提供的方法,按行分割响应体,每次迭代返回一行。
    • Ollama 流式响应的格式是 NDJSON(Newline Delimited JSON)
      {"message":{"content":"你"},"done":false}\n
      {"message":{"content":"好"},"done":false}\n
      {"message":{"content":"!"},"done":false}\n
      \n
      {"message":{"content":"我是"},"done":false}\n
      

    • iter_lines() 的处理过程:
      第1次迭代: line = b'{"message":{"content":"你"},"done":false}'
      第2次迭代: line = b'{"message":{"content":"好"},"done":false}'
      第3次迭代: line = b'{"message":{"content":"!"},"done":false}'
      第4次迭代: line = b''  ← 空行,被 ⑤ 过滤
      第5次迭代: line = b'{"message":{"content":"我是"},"done":false}'
      ...
      

    `data = json.loads(line)`  解析单行 JSON
     

      生成文本

      def generate(prompt: str, model: str = MODEL_NAME):
          """使用 generate API 生成文本"""
          url = f"{OLLAMA_BASE_URL}/api/generate"
          payload = {
              "model": model,
              "prompt": prompt,
              "stream": False
          }
          
          try:
              print(f"\n 模型: {model}")
              print(f" 提示词: {prompt}")
              print("-" * 50)
              
              response = session.post(url, json=payload, timeout=120)
              if response.status_code == 200:
                  data = response.json()
                  answer = data.get("response", "")
                  print(f" 生成结果:\n{answer}")
                  return answer
              else:
                  print(f" 请求失败: HTTP {response.status_code}")
                  return None
          except requests.exceptions.RequestException as e:
              print(f" 请求异常: {e}")
              return None
      

      聊天和生成文本的区别

      对应不同的 Ollama API 端点

      chat_streamgenerate
      API 端点/api/chat/api/generate
      API 设计目的对话式交互纯文本补全/生成

      请求数据的结构体不同

      聊天中结构体 - 对话消息格式

      payload = {
          "model": model,
          "messages": [                          # ← message 被放入 messages 列表
              {"role": "user", "content": message}  # ← 包装成结构化对话消息
          ],
          "stream": True
      }
      

      聊天可支持多轮对话

      "messages": [
          {"role": "system", "content": "你是一个翻译助手"},
          {"role": "user", "content": "翻译这句话"},
          {"role": "assistant", "content": "上一轮回答"},
          {"role": "user", "content": "新的问题"}  # ← message 在这里
      ]
      

      generate中的提示词直接传入,不支持对话历史,只是一个纯粹的"给前文,续后文"的补全接

      payload = {
          "model": model,
          "prompt": prompt,    # ← prompt 直接作为顶层字段,无包装
          "stream": False
      }
      

      响应数据的结构体不同

      聊天

      {"message": {"role": "assistant", "content": "回答内容"}, ...}
      

      生成

      {"response": "生成的内容", ...}
      

      交互聊天

      多轮对话 + 流式输出 + 上下文记忆的交互式聊天
       

      主要就是多轮对话的实现,每次对话都要把历史消息传一遍,这样大模型才会知道本轮会话的历史信息。

      代码

      def interactive_chat(model: str = MODEL_NAME):
          """交互式聊天模式"""
          print(f"\n{'='*50}")
          print(f" 交互式聊天模式 (模型: {model})")
          print("输入 'quit' 或 'exit' 退出")
          print(f"{'='*50}\n")
          
          messages = []
          
          while True:
              try:
                  user_input = input("👤 你: ").strip()
                  
                  if user_input.lower() in ['quit', 'exit', 'q']:
                      print(" 再见!")
                      break
                  
                  if not user_input:
                      continue
                  
                  # 添加用户消息到历史
                  messages.append({"role": "user", "content": user_input})
                  
                  # 调用 API(流式输出)
                  url = f"{OLLAMA_BASE_URL}/api/chat"
                  payload = {
                      "model": model,
                      "messages": messages,
                      "stream": True
                  }
                  
                  print("🤖 助手: ", end="", flush=True)
                  
                  response = session.post(url, json=payload, stream=True, timeout=120)
                  assistant_message = ""
                  
                  if response.status_code == 200:
                      for line in response.iter_lines():
                          if line:
                              data = json.loads(line)
                              content = data.get("message", {}).get("content", "")
                              assistant_message += content
                              print(content, end="", flush=True)
                      print("\n")
                      
                      # 添加助手回复到历史
                      messages.append({"role": "assistant", "content": assistant_message})
                  else:
                      print(f"\n 请求失败: HTTP {response.status_code}")
                      
              except KeyboardInterrupt:
                  print("\n\n 再见!")
                  break
              except Exception as e:
                  print(f"\n 发生错误: {e}")

      完整数据流图
       

      用户输入 "我叫小明"
        │
        ▼
      messages.append({"role": "user", "content": "我叫小明"})
      messages = [user: "我叫小明"]
        │
        ▼
      POST /api/chat  {messages: [user: "我叫小明"], stream: true}
        │
        ▼
      流式接收 → 拼接 → assistant_message = "你好小明!很高兴认识你!"
        │
        ▼
      messages.append({"role": "assistant", "content": "你好小明!..."})
      messages = [user: "我叫小明", assistant: "你好小明!..."]
        │
        ▼
      用户输入 "我叫什么名字?"
        │
        ▼
      messages.append({"role": "user", "content": "我叫什么名字?"})
      messages = [user: "我叫小明", assistant: "你好小明!", user: "我叫什么名字?"]
        │
        ▼
      POST /api/chat  {messages: [完整历史], stream: true}
        │
        ▼
      模型看到完整上下文 → 回答 "你叫小明" ✅
      

      更多推荐