当前位置: 首页 > news >正文

用Colab启动Streamlit应用

在 Google Colab 中运行 Streamlit 应用的方法与本地不同。Colab 本身是一个 Jupyter Notebook 环境,直接在单元格里运行 Streamlit 代码不会像在本地那样自动弹出浏览器窗口。

需要借助一个叫做 ngrok (或者类似的隧道服务,如 localtunnel) 的工具,它可以在 Colab 服务器和你本地的浏览器之间建立一个安全的连接通道,让你能访问到在 Colab 上运行的 Streamlit 应用。pyngrokngrok 的一个 Python 封装库,使用起来更方便。

以下是在 Colab 中运行 Streamlit 应用的步骤:

步骤:

  1. 将 Streamlit 应用代码写入一个 .py 文件中: 使用 Colab 的 “magic command” %%writefile
  2. 安装必要的库: streamlitpyngrok
  3. 获取 ngrok Authtoken(推荐): 为了获得更稳定和更长时间的连接,建议注册一个免费的 ngrok 账户并获取 Authtoken。
  4. 配置 ngrok 并启动 Streamlit 应用: 使用 pyngrok 启动隧道,并后台运行 Streamlit 服务。
  5. 访问应用: pyngrok 会提供一个公共 URL,可以通过这个 URL 在浏览器中访问应用。

请按顺序在 Colab 的代码单元格中执行以下操作:

单元格 1:将 Streamlit 应用代码写入 app.py 文件
将之前修正好的、包含 DeepSeek API 密钥和所有逻辑的完整 Streamlit 应用代码粘贴到下面 %%writefile app.py 之后。

%%writefile app.pyimport os
import streamlit as st
from dotenv import load_dotenv
from langchain_core.prompts import (ChatPromptTemplate,SystemMessagePromptTemplate,HumanMessagePromptTemplate,
)
from langchain_core.output_parsers import StrOutputParser
from langchain_core.runnables import RunnableLambda
from langchain_core.exceptions import OutputParserException
from langchain_openai import ChatOpenAI
from pydantic import BaseModel, Field
from typing import Dict, Any, Iterator
import json
import traceback # 用于打印更详细的错误堆栈# --- 配置常量 ---
# 警告:不建议在生产或共享代码中硬编码API密钥!
DASHSCOPE_API_KEY = "sk-xxx" # 提供的API Key
DASHSCOPE_BASE_URL = "https://dashscope.aliyuncs.com/compatible-mode/v1"DEFAULT_MODEL_NAME = "qwen-max"
SYSTEM_MESSAGE_CONTENT = "你是一个协助用户润色文章的人工智能助手。"load_dotenv()# --- Pydantic 模型 ---
class Paragraph(BaseModel):original_paragraph: str = Field(description="原始段落")edited_paragraph: str = Field(description="改进后的段落")feedback: str = Field(description="对原始段落的建设性反馈")# --- 全局变量用于LLM实例和初始化错误 ---
llm_init_error_message: str | None = None
deterministic_llm: ChatOpenAI | None = None
creative_llm: ChatOpenAI | None = None# --- LLM 工厂与初始化 ---
def initialize_llms():global deterministic_llm, creative_llm, llm_init_error_messagetry:if not DASHSCOPE_BASE_URL:llm_init_error_message = "DashScope API Base URL 未配置 (硬编码值为空)。"print(f"错误: {llm_init_error_message}")returnif not DASHSCOPE_API_KEY:llm_init_error_message = "DashScope API Key 未配置 (硬编码值为空)。"print(f"错误: {llm_init_error_message}")returnif not DASHSCOPE_API_KEY.startswith("sk-"): # 基本的key格式检查llm_init_error_message = f"DashScope API Key 格式似乎不正确 (硬编码值: {DASHSCOPE_API_KEY[:5]}...)"print(f"错误: {llm_init_error_message}")returndeterministic_llm = ChatOpenAI(model=DEFAULT_MODEL_NAME,temperature=0.0,base_url=DASHSCOPE_BASE_URL,api_key=DASHSCOPE_API_KEY,)creative_llm = ChatOpenAI(model=DEFAULT_MODEL_NAME,temperature=0.9,base_url=DASHSCOPE_BASE_URL,api_key=DASHSCOPE_API_KEY,)# 尝试进行一次简单调用以验证API Key和连接 (可选,但有助于早期发现问题)# print("尝试与LLM进行测试性连接...")# test_response = deterministic_llm.invoke("Hello")# print(f"LLM测试连接成功: {test_response.content[:50]}...")except Exception as e:llm_init_error_message = f"初始化语言模型(DashScope Qwen-Max)时发生错误: {e}\n{traceback.format_exc()}"print(llm_init_error_message)# 清理可能部分初始化的实例deterministic_llm = Nonecreative_llm = Noneinitialize_llms() # 应用启动时初始化LLMs# --- 通用提示模板 ---
system_prompt_template = SystemMessagePromptTemplate.from_template(SYSTEM_MESSAGE_CONTENT)# --- JSON 解析和验证函数 ---
def parse_json_string_to_paragraph(json_string: str) -> Paragraph:try:cleaned_json_string = json_string.strip()if cleaned_json_string.startswith("```json"):cleaned_json_string = cleaned_json_string[7:]if cleaned_json_string.endswith("```"):cleaned_json_string = cleaned_json_string[:-3]data = json.loads(cleaned_json_string.strip())return Paragraph(**data)except json.JSONDecodeError as e:error_msg = f"LLM输出的不是有效的JSON格式。错误: {e}. 收到的内容: '{json_string[:300]}...'"print(error_msg)raise OutputParserException(error_msg) from eexcept Exception as e: error_msg = f"JSON数据未能通过Paragraph模型验证。错误: {e}. 收到的内容: '{json_string[:300]}...'"print(error_msg)raise OutputParserException(error_msg) from e# --- 核心功能函数 ---
def generate_title(article: str) -> Iterator[str]:if not creative_llm:yield f"错误: 创意型语言模型(Qwen-Max)未初始化。请检查应用启动日志。{llm_init_error_message or ''}"returnuser_prompt_template = HumanMessagePromptTemplate.from_template("""你的任务是为一篇文章起一个标题。以下是文章内容供你参考:---{article}---标题应基于文章的内容创作。要富有创意,但标题必须清晰、吸引人,且与文章主题密切相关。只能输出文章标题,不允许有任何解释或其他文字。""")title_prompt = ChatPromptTemplate.from_messages([system_prompt_template, user_prompt_template])title_chain = title_prompt | creative_llm | StrOutputParser()try:for chunk in title_chain.stream({"article": article}):yield chunkexcept Exception as e:error_detail = f"生成标题时发生错误: {e}\n{traceback.format_exc()}"print(error_detail)yield f"抱歉,生成标题时发生内部错误。详情请查看应用日志。错误概要: {e}"def generate_description(article: str) -> Iterator[str]:if not deterministic_llm:yield f"错误: 确定性语言模型(Qwen-Max)未初始化。请检查应用启动日志。{llm_init_error_message or ''}"returnuser_prompt_template = HumanMessagePromptTemplate.from_template("""你的任务是为这篇文章撰写一段描述。文章内容如下:---{article}---请输出一段符合 SEO 友好的文章描述。除描述内容外,不要输出任何其他内容。""")description_prompt = ChatPromptTemplate.from_messages([system_prompt_template, user_prompt_template])description_chain = description_prompt | deterministic_llm | StrOutputParser()try:for chunk in description_chain.stream({"article": article}):yield chunkexcept Exception as e:error_detail = f"生成描述时发生错误: {e}\n{traceback.format_exc()}"print(error_detail)yield f"抱歉,生成描述时发生内部错误。详情请查看应用日志。错误概要: {e}"def optimize_article_paragraph(article: str) -> Iterator[Dict[str, str]]:if not creative_llm:yield {"错误": f"创意型语言模型(Qwen-Max)未初始化。请检查应用启动日志。{llm_init_error_message or ''}"}returnuser_prompt_template = HumanMessagePromptTemplate.from_template("""你的任务是评审并修改所提供文章中的一个段落。以下是供你参考的文章内容:---{article}---请从中选择一个段落进行评审和修改。在修改过程中,请确保向用户提供有建设性的反馈,以便他们了解如何提升自己的写作。请严格按照以下JSON格式输出回复,确保JSON格式正确无误,不要包含任何其他说明文字或Markdown代码块标记(如 ```json ... ```):{{"original_paragraph": "这里是原始段落内容","edited_paragraph": "这里是编辑和改进后的段落内容","feedback": "这里是对原始段落的具体、建设性的反馈和改进建议"}}""")optimize_prompt = ChatPromptTemplate.from_messages([system_prompt_template, user_prompt_template])optimize_chain = (optimize_prompt| creative_llm| StrOutputParser()| RunnableLambda(parse_json_string_to_paragraph)| (lambda p_object: {"原始段落": p_object.original_paragraph,"优化后段落": p_object.edited_paragraph,"优化建议": p_object.feedback,}))try:# stream对于返回单个完整对象的链可能行为特殊,确保它能正确处理# 通常.stream() 用于处理分块的文本流。如果链返回的是单个字典,可能直接 .invoke() 更合适# 但为了与 write_stream 保持一致,并且假设LangChain的 .stream() 对这类 Runnable 也能正确处理单个对象流for item_dict in optimize_chain.stream({"article": article}):yield item_dict # 期望这里流式传输单个字典except OutputParserException as ope:error_detail = f"优化段落时解析输出失败: {ope}\n{traceback.format_exc()}"print(error_detail)yield {"错误": f"解析LLM输出失败,可能是模型未按要求返回JSON。错误概要: {ope}"}except Exception as e:error_detail = f"优化段落时发生错误: {e}\n{traceback.format_exc()}"print(error_detail)yield {"错误": f"优化段落时发生内部错误。详情请查看应用日志。错误概要: {e}"}# --- Streamlit 应用主逻辑 ---
if llm_init_error_message:st.error(f"应用无法启动,因为语言模型初始化失败:\n{llm_init_error_message}")st.error("请检查硬编码的API Key和Base URL是否正确,API Key是否有调用`qwen-max`模型的权限及足够配额,以及网络连接。详细错误信息已打印到Colab控制台/日志。")
else:st.set_page_config(page_title="AI 写作助手 (Qwen-Max)", layout="wide")st.title("📝 AI 写作助手 (Qwen-Max)")st.caption("输入您的文章,AI 将帮助生成标题、描述并优化段落。")if "messages" not in st.session_state:st.session_state.messages = []for message in st.session_state.messages:with st.chat_message(message["role"]):st.markdown(message["content"])if "results" in message and message["results"]:# 确保在显示前检查各项结果是否存在title_output = message["results"].get("title_output")description_output = message["results"].get("description_output")optimization_output = message["results"].get("optimization_output")if title_output:st.subheader("✍️ 建议标题")# 检查是否为错误信息if isinstance(title_output, str) and title_output.startswith("错误:") or title_output.startswith("抱歉,"):st.error(title_output)else:st.markdown(title_output)if description_output:st.subheader("✍️ SEO 描述")if isinstance(description_output, str) and description_output.startswith("错误:") or description_output.startswith("抱歉,"):st.error(description_output)else:st.markdown(description_output)if optimization_output:st.subheader("✍️ 段落优化与建议")if isinstance(optimization_output, dict) and "错误" in optimization_output:st.error(f"段落优化失败: {optimization_output['错误']}")elif isinstance(optimization_output, dict):st.write(optimization_output)else: # 可能是字符串错误st.error(str(optimization_output))user_input = st.chat_input("请输入您的文章或段落...")if user_input:st.session_state.messages.append({"role": "user", "content": user_input})with st.chat_message("user"):st.markdown(user_input)with st.chat_message("assistant"):# 再次检查LLM状态,以防万一if llm_init_error_message or not deterministic_llm or not creative_llm:error_content = f"抱歉,处理请求失败,因为语言模型未正确加载。错误: {llm_init_error_message or '未知初始化错误'}"st.error(error_content)st.session_state.messages.append({"role": "assistant", "content": error_content, "results": {}})else:# --- 生成标题 ---st.subheader("✍️ 建议标题")title_container = st.empty()title_full_response = ""try:with st.spinner("正在生成标题..."):for chunk in generate_title(user_input):title_full_response += chunktitle_container.markdown(title_full_response)except Exception as e: # 捕获在st.spinner内部可能发生的其他错误error_detail = f"生成标题UI时发生意外错误: {e}\n{traceback.format_exc()}"print(error_detail)title_container.error(f"生成标题时发生界面错误。详情请查看日志。概要: {e}")title_full_response = f"生成标题时发生界面错误: {e}"# --- 生成描述 ---st.subheader("✍️ SEO 描述")description_container = st.empty()description_full_response = ""try:with st.spinner("正在生成描述..."):for chunk in generate_description(user_input):description_full_response += chunkdescription_container.markdown(description_full_response)except Exception as e:error_detail = f"生成描述UI时发生意外错误: {e}\n{traceback.format_exc()}"print(error_detail)description_container.error(f"生成描述时发生界面错误。详情请查看日志。概要: {e}")description_full_response = f"生成描述时发生界面错误: {e}"# --- 优化段落 ---st.subheader("✍️ 段落优化与建议")optimization_container = st.empty()optimization_response = {} # 初始化try:with st.spinner("正在优化段落..."):final_optimization_dict = {}# optimize_article_paragraph 现在返回一个字典流 (期望是单个字典)for item_dict in optimize_article_paragraph(user_input):final_optimization_dict = item_dict # 捕获最后一个 (或唯一的) 字典optimization_container.write(final_optimization_dict) # 实时显示流内容optimization_response = final_optimization_dict # 存储最终结果# 再次检查流本身是否返回了错误字典if isinstance(optimization_response, dict) and "错误" in optimization_response:optimization_container.error(f"优化段落时发生错误: {optimization_response['错误']}")except Exception as e: # 捕获在st.spinner内部可能发生的其他错误error_detail = f"优化段落UI时发生意外错误: {e}\n{traceback.format_exc()}"print(error_detail)optimization_container.error(f"优化段落时发生界面错误。详情请查看日志。概要: {e}")optimization_response = {"错误": f"优化段落时发生界面错误: {e}"}st.session_state.messages.append({"role": "assistant","content": "我已经针对您的文章生成了以下内容 (使用 Qwen-Max):","results": {"title_output": title_full_response,"description_output": description_full_response,"optimization_output": optimization_response,}})# st.rerun() # 如果需要立即刷新整个聊天记录

单元格 2:安装库

!pip install -q streamlit pyngrok

单元格 3:配置 ngrok 并启动 Streamlit 应用

from pyngrok import ngrok, conf
import os
import getpass # 用于隐藏 Authtoken 输入# --- ngrok 配置 ---
# 1. 访问 https://dashboard.ngrok.com/get-started/your-authtoken 注册并获取您的 ngrok Authtoken。
# 2. 将获取到的 Authtoken 粘贴到下面的输入框中。
print("为了获得更稳定的ngrok连接,建议输入您的ngrok Authtoken。")
print("请访问 https://dashboard.ngrok.com/get-started/your-authtoken 获取。")
NGROK_AUTH_TOKEN = getpass.getpass("请输入您的 ngrok Authtoken (直接回车跳过,但不推荐): ")if NGROK_AUTH_TOKEN:conf.get_default().auth_token = NGROK_AUTH_TOKENprint("Ngrok Authtoken 已配置。")
else:print("警告:未配置 ngrok Authtoken,连接可能不稳定或受限。")# --- 运行 Streamlit 应用 ---
# 确保之前的ngrok和streamlit进程被杀死,以防重复运行单元格导致端口冲突
!pkill -f "ngrok"
!pkill -f "streamlit run app.py" # 更精确地杀死目标进程# 使用 nohup 和 & 在后台运行 Streamlit,并将输出重定向到日志文件
# --server.headless=true 和 --server.enableCORS=false 有助于在云环境中运行
# --server.port 8501 指定端口,ngrok将连接到此端口
print("正在后台启动 Streamlit 应用...")
get_ipython().system_raw("nohup streamlit run app.py --server.headless true --server.enableCORS false --server.port 8501 &> streamlit.log &")# 等待几秒钟让 Streamlit 服务器启动
import time
time.sleep(5)# --- 启动 ngrok 隧道 ---
try:# 连接到 Streamlit 默认运行的 8501 端口public_url = ngrok.connect(8501, proto="http")print("------------------------------------------------------------------------------------")print(f"您的 Streamlit 应用正在运行!请通过以下公共链接访问:")print(public_url)print("------------------------------------------------------------------------------------")print("注意:此链接在Colab会话激活期间有效。关闭此Notebook或会话超时将导致链接失效。")print("如果应用没有立即加载,请稍等片刻或刷新页面。")print("如果遇到问题,可以查看 streamlit.log 文件获取 Streamlit 应用的输出日志。")print("要停止应用和ngrok隧道,可以中断此单元格的执行,或在Colab中“运行时”->“中断执行”,然后“终止会话”。")except Exception as e:print(f"Ngrok 连接失败: {e}")print("可能的原因及解决方法:")print("1. Ngrok Authtoken 未设置或无效:请确保您输入了正确的Authtoken。")print("2. Colab 环境的网络限制或 ngrok 服务暂时不可用:稍后重试。")print("3. Streamlit 应用未能成功启动:请检查上面 `%%writefile app.py` 单元格中的代码,并查看 `streamlit.log` 文件。")print("   要查看日志,可以在新的Colab单元格中运行 `!cat streamlit.log`")

如何使用:

  1. 运行单元格 1: 这会将您的 Streamlit 代码保存到 app.py 文件中。请确保您已经将正确的、完整的 Streamlit 代码(包括API密钥等)粘贴到该单元格中。
  2. 运行单元格 2: 安装 streamlitpyngrok
  3. 运行单元格 3:
    • 它会提示您输入 ngrok Authtoken。您可以从 ngrok Dashboard 获取。输入后按回车。如果您跳过此步骤(直接按回车),ngrok 仍然可以工作,但连接可能不稳定或有时间限制。
    • 然后它会在后台启动您的 Streamlit 应用 (app.py)。
    • 接着,它会启动 ngrok 隧道,并打印出一个 .ngrok.io.ngrok-free.app 的URL。
  4. 访问应用: 点击输出的那个公共 URL,就应该能在浏览器中看到并使用您的 Streamlit 应用了。

重要提示:

  • API 密钥: 您在 app.py 中硬编码的 DASHSCOPE_API_KEY 必须是有效的。
  • Ngrok Authtoken: 强烈建议使用 Authtoken。
  • 会话持续时间: ngrok 链接只在您的 Colab 会话处于活动状态时有效。如果 Colab 笔记本关闭或会话超时,链接将失效,您需要重新运行单元格 3。
  • 日志: Streamlit 应用的输出(包括潜在的错误)会被重定向到 streamlit.log 文件。如果在访问 URL 时遇到问题,可以在新的 Colab 单元格中运行 !cat streamlit.log 来查看日志。
  • 停止: 要停止应用和 ngrok 隧道,您可以中断单元格 3 的执行,或者在 Colab 菜单中选择“运行时”->“中断执行”,更彻底的方式是“终止会话”。
  • LLM 初始化错误处理: 我在 app.py 的代码中加入了对 LLM 初始化失败的检查,如果模型无法加载,Streamlit 应用会显示错误信息而不是直接崩溃。

这样操作后,应该就能在 Colab 中成功运行并访问您的 Streamlit 应用了。

相关文章:

  • 机器学习 Day18 Support Vector Machine ——最优美的机器学习算法
  • 板凳-------Mysql cookbook学习 (三)
  • c/c++的opencv直方图初识
  • ADB基本操作和命令
  • 深入理解 OpenCV 的 DNN 模块:从基础到实践
  • 顺 序 表:数 据 存 储 的 “ 有 序 阵 地 ”
  • 计算机网络概要
  • 牛客网NC276055:三根木棒能否组成三角形问题详解(ACM中的A题)
  • 【从基础到模型网络】深度学习-语义分割-ROI
  • OceanBase数据库-租户
  • 小米汽车:新能源赛道的破局者与变革者
  • UE5无法编译问题解决
  • 抖音视频下载工具 v1.1 自用分享
  • 【Python 算法零基础 4.排序 ① 选择排序】
  • 为何选择Python:全面解析其优势与发展前景
  • Linux下载国外软件镜像的加速方法(以下载Python-3.8.0.tgz为例)
  • Java可变参数与Collections工具类详解
  • 系统架构设计师考前冲刺笔记-第1章-系统工程与信息系统基础
  • 深入解析Java事件监听机制与应用
  • GOP模式调节画面质量讲解
  • 证监会副主席李明:近期将出台深化科创板、创业板改革政策措施
  • 西浦国际教育创新论坛举行,聚焦AI时代教育本质的前沿探讨
  • 中国物流集团等10家央企11名领导人员职务任免
  • 网易一季度净利增长三成,丁磊:高度重视海外游戏市场
  • 年在沪纳税350亿人民币,这些全球头部企业表示“对上海承诺不会变”
  • 上海“城市文明开放麦”全城总动员,樊振东担任首位上海城市文明大使