跳转至

对话前端

方案选择

根据需求选择前端方案:

方案 适合场景 不适合
自定义 HTML + SSE 需要定制 UI、混合业务组件、产品级交互
Chainlit 纯对话的快速原型,不需要自定义布局 需要改布局、加自定义组件

默认选自定义 HTML。 有 AI 辅助写前端的情况下,HTML + SSE 的开发速度不比 Chainlit 慢,但灵活性远超。 只有用户明确要求用 Chainlit 或只需要最简单的对话 demo 时才用 Chainlit。

自定义 HTML + SSE(推荐)

后端:FastAPI SSE 端点

from fastapi import FastAPI, Request
from fastapi.responses import StreamingResponse, HTMLResponse
from fastapi.staticfiles import StaticFiles

app = FastAPI()

# 挂载静态文件(HTML/CSS/JS)
app.mount("/static", StaticFiles(directory="static"), name="static")

@app.get("/", response_class=HTMLResponse)
async def index():
    with open("static/index.html") as f:
        return f.read()

@app.post("/chat")
async def chat(request: Request):
    body = await request.json()
    message = body["message"]

    async def generate():
        # 替换成你的 LLM 调用(LangGraph / OpenAI / Anthropic 等)
        async for chunk in your_llm_stream(message):
            yield f"data: {chunk}\n\n"
        yield "data: [DONE]\n\n"

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

前端:最小 SSE 聊天页面

让 AI 直接写 HTML + CSS + JS 即可,核心就是一个 fetch + EventSource / getReader()

// 流式读取 SSE 响应
const response = await fetch("/chat", {
    method: "POST",
    headers: { "Content-Type": "application/json" },
    body: JSON.stringify({ message: userInput })
});

const reader = response.body.getReader();
const decoder = new TextDecoder();

while (true) {
    const { done, value } = await reader.read();
    if (done) break;

    const text = decoder.decode(value);
    // 解析 SSE data: 行,追加到聊天气泡里
    appendToChat(text);
}

自定义 HTML 方案的优势

  • 布局完全自由(侧边栏、多面板、数据看板、嵌入图表等)
  • AI 写 HTML/CSS/JS 极其成熟,几乎不出错
  • 可以直接用 Tailwind CDN、Alpine.js 等轻量工具
  • WebSocket / SSE 任选,通信方式不受框架限制

Chainlit(仅快速原型)

仅当用户明确要求或只需最简对话 demo 时使用。

与 FastAPI 集成

from fastapi import FastAPI
from chainlit.utils import mount_chainlit

app = FastAPI()

@app.get("/api/health")
def health():
    return {"status": "ok"}

mount_chainlit(app=app, target="my_cl_app.py", path="/chainlit")

Chainlit 应用文件

import chainlit as cl

@cl.on_chat_start
async def on_chat_start():
    graph = ...  # 编译好的 LangGraph
    cl.user_session.set("graph", graph)

@cl.on_message
async def on_message(message: cl.Message):
    graph = cl.user_session.get("graph")
    msg = cl.Message(content="")

    async for chunk in graph.astream(
        {"messages": [{"role": "user", "content": message.content}]},
        stream_mode="messages"
    ):
        await msg.stream_token(chunk.content)

    await msg.send()

Chainlit 注意事项

  • 有自己的配置文件 .chainlit/config.toml
  • 开发时用 chainlit run app.py -w 启用热重载
  • 高度自定义 UI 布局时会很痛苦,这时应该切换到自定义 HTML 方案