avatar

Neo·元

算法的尽头,认知的倒影

  • 首页
  • 三千问道
  • 万法归宗
  • 诗酒田园
  • 关于NeoBlog
主页 本地大模型分离部署逻辑推理服务器实战(上)
文章

本地大模型分离部署逻辑推理服务器实战(上)

发表于 18天前 更新于 18天前
作者 Neo
13~17 分钟 阅读

1、前置准备:

Python环境:

Conda python3.10.20

Python包基础依赖:

fastapi==0.109.0 uvicorn==0.27.0 pika==1.3.2 pydantic==2.5.3 pydantic-settings==2.1.0

RabbitMQ 环境:

RabbitMQ 4.2.6 + Erlang 27.3.4.10

大模型环境:

Ollma+LangChain

2、代码准备:

接口层:

发送请求:

@router.post("/clean-and-submit")
async def submit_task(request: CleanRequest, background_tasks: BackgroundTasks):
    try:
        task_id = await process_and_publish_task(request)
        return {"task_id": task_id, "status": "submitted"}
    except Exception as e:
        raise HTTPException(status_code=500, detail=str(e))

查看结果:

@router.get("/result/{task_id}", response_model=InferenceResult)
def get_result(task_id: str):
    if task_id not in task_results:
        raise HTTPException(status_code=404, detail="Task not found")
    data = task_results[task_id]
    return InferenceResult(task_id=task_id, status=data["status"], result=data.get("result"))

流输出:


@router.get("/stream/{task_id}")
async def stream_result(task_id: str):
    async def event_generator():
        while True:
            if task_id not in task_results:
                yield f"data: {json.dumps({'error': 'Task not found'})}\n\n"
                break

            data = task_results[task_id]
            status = data["status"]

            yield f"data: {json.dumps({'status': status, 'content': data.get('result', '')})}\n\n"

            if status in ["completed", "failed"]:
                break
            await asyncio.sleep(0.5)  

    return StreamingResponse(event_generator(), media_type="text/event-stream")

清洗服务器:

(数据预处理+数据进入MQ 削峰填谷)


STOP_WORDS_SET = load_stopwords(file_path="./stopwords.txt")

def clean_jieba(raw_text: str) -> str:
    cleaned = " ".join(raw_text.strip().split())
    words = jieba.lcut(cleaned, cut_all=False)
    filtered = [w for w in words if w not in STOP_WORDS_SET and w.strip()]
    return " ".join(filtered)

def clean_text_logic(raw_text: str) -> str:
    return " ".join(raw_text.strip().split())

def get_connection():
    credentials = pika.PlainCredentials(settings.RABBITMQ_USER, settings.RABBITMQ_PASSWORD)
    parameters = pika.ConnectionParameters(host=settings.RABBITMQ_HOST, credentials=credentials)
    return pika.BlockingConnection(parameters)

async def process_and_publish_task(request: CleanRequest) -> str:
    task_id = str(uuid.uuid4())
    cleaned = clean_text_logic(request.raw_text)
    cleaned = clean_jieba(cleaned)
    task_results[task_id] = {"status": "queued", "result": None}

    msg = {"task_id": task_id, "content": cleaned, "ts": time.time()}
    conn = get_connection()
    try:
        ch = conn.channel()
        ch.queue_declare(queue=settings.INFERENCE_QUEUE, durable=True)
        ch.basic_publish('', settings.INFERENCE_QUEUE, body=json.dumps(msg), properties=pika.BasicProperties(delivery_mode=2))
    finally:
        conn.close()
    return task_id

逻辑推理服务器:


logger = logging.getLogger(__name__)

# 初始化本地大模型
llm = Ollama(model="llama3", base_url="http://localhost:11434")


def process_with_langchain(input_text: str) -> str:
    """
    使用 LangChain 进行逻辑推理
    """
    try:
        # 构建提示词,引导模型进行清洗后的深度分析
        prompt = f"请对以下经过清洗的文本进行逻辑总结:\n{input_text}"
        response = llm.invoke(prompt)
        return str(response).strip()
    except Exception as e:
        logger.error(f"LangChain inference error: {e}")
        return "Inference failed due to model error."


def start_inference_consumer():
    """
    后台线程:监听队列并执行 LangChain 推理
    """
    while True:
        connection = None
        try:
            credentials = pika.PlainCredentials(settings.RABBITMQ_USER, settings.RABBITMQ_PASSWORD)
            parameters = pika.ConnectionParameters(
                host=settings.RABBITMQ_HOST,
                credentials=credentials,
                heartbeat=600
            )
            connection = pika.BlockingConnection(parameters)
            channel = connection.channel()
            channel.queue_declare(queue=settings.INFERENCE_QUEUE, durable=True)
            channel.basic_qos(prefetch_count=1)

            def callback(ch, method, properties, body):
                try:
                    data = json.loads(body)
                    task_id = data['task_id']
                    content = data['content']

                    task_results[task_id] = {"status": "processing", "result": "", "chunks": []}

                    # 关键:使用 stream 进行流式推理
                    full_response = ""
                    for chunk in llm.stream(f"请总结:{content}"):
                        full_response += chunk
                        # 将最新片段存入状态,供 SSE 接口读取
                        if task_id in task_results:
                            task_results[task_id]["result"] = full_response
                            task_results[task_id]["last_chunk"] = chunk  # 记录最后一片段用于推送

                    task_results[task_id]["status"] = "completed"
                    ch.basic_ack(delivery_tag=method.delivery_tag)
                except Exception as e:
                    logger.error(e)
                    ch.basic_nack(delivery_tag=method.delivery_tag, requeue=True)

            channel.basic_consume(
                queue=settings.INFERENCE_QUEUE,
                on_message_callback=callback,
                auto_ack=False
            )

            logger.info("[Consumer] Waiting for messages from RabbitMQ...")
            channel.start_consuming()

        except Exception as e:
            logger.error(f"[Consumer] Connection error: {e}. Reconnecting in 5s...")
            time.sleep(5)
        finally:
            if connection and connection.is_open:
                connection.close()

万法归宗
AI
许可协议:  CC BY 4.0
分享

相关文章

8月 11, 2026

浅谈LangChain\LangGraph(下)

前言: 之前已经在本地跑通了LangChain的Demo项目了,接下来要改造成LangGraph,首先分析一下langGraph的几大优势点: 我的个人总结: 1、从线性管道到状态机节点(类似从二维升级到了三维,单线程变成多线程) 2、控制流变成循环、分支,比原先的单向高级了 3、容错性变强、支持回

8月 10, 2026

浅谈LangChain\LangGraph(上)

前言: 简单聊一下使用LangChain的一些心得,个人感觉就是操作大模型调用自己的向量数据库,然后触发自定义工具包的一个生产框架,主要包括RAG(向量数据库)和Agent(工具包)两部分核心组成。 RAG一般来源于企业生产中的文档资料,需要转换成大模型识别的向量数据库,一般要先把Word\Exce

7月 31, 2026

中小企业私有化大模型硬件配置实战

站在中小企业的视角,自研私有化大模型的核心诉求从来不是极致高并发、超大参数量模型集群,而是低成本采购、单人可运维、稳定支撑日常业务问答、文档摘要、内部知识库检索这类轻量化场景。 市面上很多算力教程都是面向互联网大厂、AI 实验室撰写,动辄多卡分布式、A100/H100 专业算力卡,完全脱离中小团队的

下一篇

window10安装最新的Erlang 和RabbitMq

上一篇

本地大模型分离部署逻辑推理服务器实战(下)

最近更新

  • 浅谈LangChain\LangGraph(下)
  • 浅谈LangChain\LangGraph(上)
  • 中小企业私有化大模型硬件配置实战
  • Token 暴涨、上下文爆炸的 5 种真实业务优化
  • FastAPI + AioRedis 消除线程阻塞实战

热门标签

AI

©2026 All Rights Reserved Neo 鲁ICP备2026037083号