Python 使用 Chainlit + Ollama 快速搭建本地 AI 聊天应用

使用 Chainlit + Ollama 快速搭建本地 AI 聊天应用

大家好!今天分享一个超级简单的本地 AI 聊天界面实现方案:Chainlit + Ollama

无需部署复杂的后端,只需本地运行 Ollama,再用几行 Python 代码,就能拥有一个支持模型切换、流式输出的美观聊天界面。

效果如下(实际运行时会实时流式显示回复):

  • 支持检测本地所有 Ollama 模型
  • 自动过滤出生成型模型用于聊天
  • 支持一键切换模型
  • 流式输出,体验接近 ChatGPT

下面一步步教大家从零开始搭建。

一、环境准备

1. 安装 Ollama

前往官网下载并安装 Ollama:

https://ollama.com/download

安装完成后,打开终端运行:

bash 复制代码
ollama --version

看到版本号即表示安装成功。

2. 拉取模型(必须)

Ollama 默认没有模型,需要手动拉取。我们推荐以下生成型模型(任选其一即可):

bash 复制代码
# 推荐中文能力强的模型
ollama pull qwen2.5          # 阿里通义千问 2.5,中文极强
ollama pull qwen2.5:7b       # 更小更快版本

# 或者经典的 Llama3
ollama pull llama3
ollama pull llama3:8b

# 其他可选
ollama pull gemma2
ollama pull phi3

拉取完成后运行 ollama list 查看已安装模型。

3. 安装 Python 依赖

创建一个新文件夹(如 ollama-chainlit),进入文件夹后执行:

bash 复制代码
pip install chainlit httpx

这是本项目所需的所有第三方库,非常轻量。

二、完整代码

将以下代码保存为 app.py(文件名随意,但后缀必须是 .py):

python 复制代码
import chainlit as cl
import httpx
import json

# 同步获取 Ollama 模型列表
def get_ollama_models():
    try:
        with httpx.Client(timeout=10.0) as client:
            response = client.get("http://localhost:11434/api/tags")
            response.raise_for_status()
            data = response.json()
            return [m["name"] for m in data["models"]]
    except Exception as e:
        print(f"获取模型列表失败: {e}")
        return []

OLLAMA_MODELS = get_ollama_models()

# 过滤生成模型(排除常见的 embedding 模型)
GENERATIVE_MODELS = [
    m for m in OLLAMA_MODELS 
    if "embed" not in m.lower() and "bge" not in m.lower()
]

if not GENERATIVE_MODELS:
    print("警告:未检测到生成模型,请拉取如 ollama pull qwen2.5 或 ollama pull llama3")
    DEFAULT_MODEL = "llama3"  # fallback
else:
    DEFAULT_MODEL = GENERATIVE_MODELS[0]

# Ollama 流式生成
async def ollama_chat_stream(model: str, messages: list):
    payload = {
        "model": model,
        "messages": messages,
        "stream": True
    }
    
    async with httpx.AsyncClient(timeout=None) as client:
        try:
            async with client.stream("POST", "http://localhost:11434/api/chat", json=payload) as response:
                async for line in response.aiter_lines():
                    if not line.strip():
                        continue
                    data = json.loads(line)
                    if "message" in data and "content" in data["message"]:
                        yield data["message"]["content"]
                    if data.get("done"):
                        break
        except Exception as e:
            yield f"\n\n[错误: 调用 Ollama 失败 - {str(e)}]"

# Chainlit 应用
@cl.on_chat_start
async def on_chat_start():
    all_models_info = ', '.join(OLLAMA_MODELS) if OLLAMA_MODELS else "无"
    gen_models_info = ', '.join(GENERATIVE_MODELS) if GENERATIVE_MODELS else "无(请拉取生成模型,如 qwen2.5、llama3 等)"
    
    await cl.Message(
        content=f"欢迎使用 Chainlit + Ollama!\n"
                f"检测到所有模型: {all_models_info}\n"
                f"可用于聊天的生成模型: {gen_models_info}"
    ).send()
    
    if not GENERATIVE_MODELS:
        await cl.Message(content="提示:当前只有 embedding 模型,无法正常聊天。请运行 `ollama pull qwen2.5` 或其他生成模型后重启。").send()
        return
    
    cl.user_session.set("current_model", DEFAULT_MODEL)
    
    if len(GENERATIVE_MODELS) > 1:
        actions = [
            cl.Action(
                name="model_select",
                value=model,
                label=f"切换到 {model}"
            ) for model in GENERATIVE_MODELS
        ]
        await cl.Message(
            content=f"当前模型: **{DEFAULT_MODEL}**\n选择其他模型:",
            actions=actions
        ).send()
    else:
        await cl.Message(content=f"当前模型: **{DEFAULT_MODEL}**(唯一生成模型)").send()

@cl.action_callback("model_select")
async def on_model_select(action: cl.Action):
    new_model = action.value
    cl.user_session.set("current_model", new_model)
    await cl.Message(content=f"模型已切换为: **{new_model}**").send()

@cl.on_message
async def on_message(message: cl.Message):
    if not GENERATIVE_MODELS:
        await cl.Message(content="未检测到生成模型,无法回复。").send()
        return
    
    model_name = cl.user_session.get("current_model", DEFAULT_MODEL)
    
    messages = cl.user_session.get("messages", [])
    messages.append({"role": "user", "content": message.content})
    
    # 创建流式消息
    response_msg = cl.Message(content="")
    await response_msg.send()
    
    # 显示正在生成提示
    response_msg.content = f"[{model_name}] 正在生成..."
    await response_msg.update()
    
    # 流式输出
    full_response = ""
    async for token in ollama_chat_stream(model_name, messages):
        full_response += token
        await response_msg.stream_token(token)
    
    # 保存历史
    messages.append({"role": "assistant", "content": full_response})
    cl.user_session.set("messages", messages)
    
    # 最终更新
    await response_msg.update()

三、运行项目

确保 Ollama 服务正在运行(安装后一般会自动启动,如果没有可以手动运行 ollama serve)。

在代码所在目录打开终端,执行:

bash 复制代码
chainlit run app.py -w

-w 参数表示自动刷新浏览器,开发时很方便。

运行成功后,浏览器会自动打开 http://localhost:8000,你将看到欢迎界面和模型列表。

点击右侧的模型按钮即可切换,输入问题开始聊天!

四、常见问题

  1. 模型切换按钮不显示?

    只有检测到多个生成模型时才会显示切换按钮。如果只有一个模型,会直接显示"唯一生成模型"。

  2. 提示"正在生成..."一直不消失?

    检查 Ollama 是否正常运行,模型是否拉取成功。可以在终端运行 ollama list 确认。

  3. 想去掉"正在生成..."提示?

    可以修改 on_message 函数最后部分,把注释掉的代码取消:

    python 复制代码
    response_msg.content = f"[{model_name}]\n\n{full_response}"
    await response_msg.update()

    这样最终只会显示模型名 + 回复内容。

五、总结

整个项目只有不到 100 行代码,却实现了:

  • 自动检测本地模型
  • 模型切换
  • 流式输出
  • 聊天历史保持

非常适合本地玩大模型、快速搭建私人助手、学习 Chainlit 框架。

喜欢的话别忘了点赞 + 收藏 + 关注 三连哦~

有问题欢迎在评论区留言,我会尽快回复!

相关推荐
小北方城市网2 小时前
SpringBoot 集成 MinIO 实战(对象存储):实现高效文件管理
java·spring boot·redis·分布式·后端·python·缓存
UR的出不克2 小时前
Python实现SMZDM数据处理系统:从爬虫到数据分析的完整实践
爬虫·python·数据分析
jimmyleeee2 小时前
人工智能基础知识笔记三十四:提升RAG效果的几种技术
人工智能·笔记
不如语冰2 小时前
AI大模型入门1.3-python基础-类
人工智能·pytorch·python·类和方法
智能相对论2 小时前
【年度AI观察】2026,车企反攻智能硬件
人工智能·智能硬件
一代土怪2 小时前
django中实时更新数据库
python·django
m0_466525292 小时前
AI医疗的东软答卷:从技术破局到产业融合
人工智能
学习3人组2 小时前
AI视觉Python方向专业技术名词
开发语言·人工智能·python
静听松涛1332 小时前
通用人工智能(AGI)的阶段性定义与里程碑
人工智能