LLM流式服务器

一个集成Ollama LLM和模型上下文协议(MCP)的Python后端服务,通过WebSocket为React前端提供实时流式响应。

  • by kdgerona
  • Python
  • 本地部署
  • 更新于
在 GitHub 查看 kdgerona/MCP-Host---Form-Builder

使用说明

Ollama MCP 流媒体服务器

一个集成了(各种功能)的完整Python后端 Ollama 大语言模型(LLMs) 与;和;带有 模型上下文协议(MCP) 服务器通过WebSocket向React前端提供实时流式响应。

特点/特性

  • 流式传输大型语言模型(LLM)响应从Ollama模型中实时逐词流式传输
  • MCP集成连接到MCP服务器以增强工具调用功能
  • WebSocket APIReact/前端应用的全双工通信
  • FastAPI 服务器具备健康检查和CORS支持的生产就绪异步服务器
  • 灵活模式使用MCP工具,在简单流媒体模式和完整代理模式之间进行选择
  • 类型安全完整的Python类型提示和验证

建筑学

�����������������
  React Frontend 
��������,��������
          WebSocket
         
��������Ľ������������������������������������
  FastAPI Server (server.py)                 
  - WebSocket endpoint (/ws)                 
  - HTTP endpoints (/health, /info, /chat)  
��������,������������������������������������
         
��������Ľ������������������������������������
  StreamingOllamaAgent                       
  - Token streaming                          
  - MCP integration                          
��������,������������������������������������
         
    ����Ľ�����        ����������
      Ollama             MCP    
      Server            Server  
    ����������        ����������

快速入门

先决条件

  • Python 3.13及以上版本
  • 紫外线 包管理器
  • 本地运行的Ollama(默认地址:http://localhost:11434)
  • MCP 服务器正在运行(默认地址:http://localhost:8006/sse)

安装

  1. 克隆并设置:
# Install dependencies
uv sync

# Copy environment configuration
cp .env.example .env

# Edit .env with your configuration
  1. 配置环境 (.env):
# MCP Server Configuration
MCP_SERVER_URL=http://localhost:8006/sse

# Ollama Configuration
OLLAMA_MODEL=skaiform-assistant:gpt-oss
OLLAMA_BASE_URL=http://localhost:11434

# Agent Configuration
MAX_STEPS=30

# WebSocket Server Configuration
SERVER_HOST=0.0.0.0
SERVER_PORT=8000
CORS_ORIGINS=http://localhost:3000,http://localhost:5173
  1. 启动服务器:
# Using uv
uv run python server.py

# Or with uvicorn directly
uvicorn server:app --reload --host 0.0.0.0 --port 8000

服务器将在 http://localhost:8000

API 文档

WebSocket 终端点: /ws

客户端-服务器消息格式

{
  "prompt": "Your question or prompt here",
  "mode": "agent"  // "agent" or "simple" (optional, default: "agent")
}

模式:

  • agent全代理模式,支持MCP工具调用和多步推理
  • simple直接LLM流式传输,无需代理复杂性

服务器-客户端消息格式

所有消息均遵循此结构:

{
  "type": "ollama" | "mcp" | "tool_call" | "tool_result" | "system" | "error" | "done",
  "data": "content string or object"
}

消息类型:

  1. **ollama**来自Ollama大型语言模型的标记块

    {"type": "ollama", "data": "Hello"}
    {"type": "ollama", "data": " world"}
    
  2. **mcp**来自MCP服务器操作的响应

    {"type": "mcp", "data": "Tool execution result"}
    
  3. **tool_call**当代理调用MCP工具时

    {
      "type": "tool_call",
      "data": {
        "name": "calculate_md5",
        "args": {"text": "Hello, world!"}
      }
    }
    
  4. **tool_result**工具执行结果

    {"type": "tool_result", "data": "Result data"}
    
  5. **system**来自服务器的状态消息

    {"type": "system", "data": "Processing your request..."}
    
  6. **error**错误信息

    {"type": "error", "data": "Error description"}
    
  7. **done**标志着流媒体传输的结束

    {"type": "done", "data": "Response completed"}
    

HTTP 端点

GET /

带有服务器信息的根端点。

回应:

{
  "name": "Ollama MCP Streaming Server",
  "version": "1.0.0",
  "status": "running",
  "endpoints": {
    "websocket": "/ws",
    "health": "/health",
    "info": "/info"
  }
}

GET /health

健康检查端点。

回应:

{
  "status": "healthy",
  "agent_initialized": true
}

GET /info

服务器配置和状态。

回应:

{
  "ollama_model": "skaiform-assistant:gpt-oss",
  "ollama_base_url": "http://localhost:11434",
  "mcp_server_url": "http://localhost:8006/sse",
  "max_steps": 30,
  "server": {
    "host": "0.0.0.0",
    "port": 8000,
    "cors_origins": ["http://localhost:3000"]
  }
}

POST /chat

用于测试的非流式HTTP终端。

请求:

{
  "prompt": "What is the meaning of life?"
}

回应:

{
  "responses": [
    {"type": "ollama", "data": "The"},
    {"type": "ollama", "data": " meaning"},
    {"type": "done", "data": "Response completed"}
  ]
}

React 前端集成

WebSocket 客户端示例

import { useEffect, useState, useRef } from 'react';

interface Message {
  type: 'ollama' | 'mcp' | 'tool_call' | 'system' | 'error' | 'done';
  data: string | object;
}

export function useOllamaStream(serverUrl: string = 'ws://localhost:8000/ws') {
  const [messages, setMessages] = useState<Message[]>([]);
  const [isConnected, setIsConnected] = useState(false);
  const [isStreaming, setIsStreaming] = useState(false);
  const ws = useRef<WebSocket | null>(null);

  useEffect(() => {
    // Connect to WebSocket
    ws.current = new WebSocket(serverUrl);

    ws.current.onopen = () => {
      console.log('WebSocket connected');
      setIsConnected(true);
    };

    ws.current.onmessage = (event) => {
      const message: Message = JSON.parse(event.data);

      setMessages((prev) => [...prev, message]);

      if (message.type === 'done') {
        setIsStreaming(false);
      }
    };

    ws.current.onerror = (error) => {
      console.error('WebSocket error:', error);
      setIsConnected(false);
    };

    ws.current.onclose = () => {
      console.log('WebSocket disconnected');
      setIsConnected(false);
    };

    return () => {
      ws.current?.close();
    };
  }, [serverUrl]);

  const sendPrompt = (prompt: string, mode: 'agent' | 'simple' = 'agent') => {
    if (ws.current && isConnected) {
      setMessages([]);
      setIsStreaming(true);
      ws.current.send(JSON.stringify({ prompt, mode }));
    }
  };

  return { messages, isConnected, isStreaming, sendPrompt };
}

在组件中的使用

function ChatInterface() {
  const { messages, isConnected, isStreaming, sendPrompt } = useOllamaStream();
  const [input, setInput] = useState('');

  // Accumulate ollama tokens into a single response
  const response = messages
    .filter((m) => m.type === 'ollama')
    .map((m) => m.data)
    .join('');

  const handleSubmit = (e: React.FormEvent) => {
    e.preventDefault();
    if (input.trim()) {
      sendPrompt(input);
      setInput('');
    }
  };

  return (
    <div>
      <div>Status: {isConnected ? '=â Connected' : '=4 Disconnected'}</div>

      <div className="response">
        {response}
        {isStreaming && <span className="cursor">Š</span>}
      </div>

      <form onSubmit={handleSubmit}>
        <input
          value={input}
          onChange={(e) => setInput(e.target.value)}
          placeholder="Ask a question..."
          disabled={!isConnected || isStreaming}
        />
        <button type="submit" disabled={!isConnected || isStreaming}>
          Send
        </button>
      </form>

      {/* Tool calls and system messages */}
      <div className="debug">
        {messages
          .filter((m) => m.type === 'tool_call' || m.type === 'system')
          .map((m, i) => (
            <div key={i}>
              <strong>{m.type}:</strong> {JSON.stringify(m.data)}
            </div>
          ))}
      </div>
    </div>
  );
}

项目结构

mcp-ollama/
�� server.py              # FastAPI WebSocket server
�� streaming_agent.py     # Streaming agent implementation
�� ollama_agent.py        # Original non-streaming agent
�� main.py               # CLI test script
�� pyproject.toml        # Dependencies
�� .env.example          # Configuration template
�� .env                  # Your configuration (create this)
�� README.md             # This file

核心组件

StreamingOllamaAgent (streaming_agent.py) 翻译为中文是:(流式处理代理脚本.py)

处理核心代理类的:

  • Ollama LLM和MCP客户端的初始化
  • 从Ollama逐个标记地进行流式传输
  • MCP工具集成
  • 错误处理与恢复

关键方法:

  • initialize()建立LLM和MCP连接
  • stream_response(prompt)全代理模式,支持工具调用
  • stream_simple(prompt)简单的大语言模型(LLM)流式处理,无需工具

FastAPI Server (server.py) 翻译为中文是:(服务器.py)

生产就绪的异步服务器,具备:

  • 用于实时流传输的WebSocket终端
  • 用于健康检查和测试的HTTP端点
  • 前端集成的CORS支持
  • 全面日志记录
  • 代理初始化的生命周期管理

发展

运行测试

# Test the basic agent (non-streaming)
python main.py

# Test WebSocket connection
# Use a WebSocket client like wscat:
npm install -g wscat
wscat -c ws://localhost:8000/ws

# Send a message:
{"prompt": "Hello!", "mode": "simple"}

代码检查和格式化

ruff check .
ruff format .

添加依赖项

uv add package-name

配置选项

环境变量

变量 默认值 描述
MCP_SERVER_URL http://localhost:8006/sse MCP服务器SSE端点
OLLAMA_MODEL skaiform-assistant:gpt-oss Ollama模型名称
OLLAMA_BASE_URL http://localhost:11434 Ollama 服务器 URL
MAX_STEPS 30 最大代理推理步骤
SERVER_HOST 0.0.0.0 WebSocket 服务器主机
SERVER_PORT 8000 WebSocket 服务器端口
CORS_ORIGINS http://localhost:3000,... 允许的CORS来源

故障排除

WebSocket 连接问题

  1. CORS 错误将您的前端URL添加到 CORS_ORIGINS 在 .env
  2. 连接被拒绝确保服务器正在运行且端口正确
  3. 超时检查Ollama和MCP服务器是否正在运行

Ollama 问题

  1. 未找到模型先拉出模型: ollama pull model-name
  2. 连接被拒绝启动Ollama: ollama serve
  3. 反应迟钝检查Ollama的资源使用情况

MCP服务器问题

  1. 连接失败验证MCP服务器是否在配置的URL上运行
  2. 工具错误检查MCP服务器日志中的工具执行错误

高级用法

定制工具处理

修改 streaming_agent.py 为特定工具类型添加自定义处理:

async for chunk in agent.stream_response(prompt):
    if chunk["type"] == "tool_call":
        tool_name = chunk["data"]["name"]
        # Add custom logic for specific tools
        if tool_name == "my_custom_tool":
            # Handle specially
            pass

    yield chunk

多个MCP服务器

在代理初始化时配置多个MCP服务器:

config = {
    "mcpServers": {
        "server1": {"url": "http://localhost:8006/sse"},
        "server2": {"url": "http://localhost:8007/sse"}
    }
}

认证

为WebSocket连接添加身份验证:

@app.websocket("/ws")
async def websocket_endpoint(websocket: WebSocket, token: str = Query(...)):
    # Verify token
    if not verify_token(token):
        await websocket.close(code=1008)
        return

    await websocket.accept()
    # ... rest of handler

许可证

[此处填写您的许可证]

贡献

欢迎贡献!请随时提交拉取请求。

支持

对于问题和疑问:

  • 在GitHub上提交一个问题
  • 检查现有问题以寻找解决方案
  • 查阅相关文件

致谢