marimo Chat UI 组件完整指南:用 mo.ui.chat 构建交互式 AI 聊天机器人
【免费下载链接】marimoA reactive notebook for Python — run reproducible experiments, query with SQL, execute as a script, deploy as an app, and version with git. Stored as pure Python. All in a modern, AI-native editor.项目地址: https://gitcode.com/GitHub_Trending/ma/marimo
marimo 的mo.ui.chat是一个内置的交互式聊天机器人 UI 元素,允许在笔记本中直接构建可与用户对话的应用界面。它可以接入自定义函数、内置的各大 AI 厂商模型(OpenAI、Anthropic、Google、Groq、AWS Bedrock),以及对 pydantic-ai 的一等支持,还能以流式(streaming)方式逐字渲染回复、携带图片附件、支持模板化提示词与 RAG 检索增强生成。读完本文,你将掌握mo.ui.chat的全部参数与用法,能够用十余行代码在 marimo 中快速搭建一个生产可用的 AI 对话应用。
一、Chat 组件是什么
mo.ui.chat(实现于 chat.py)为对话场景提供交互式聊天界面。它的核心设计非常简洁:你只需要实现一个模型函数——接收聊天消息列表,返回回复内容——其余的前端渲染、消息历史管理、流式传输都由 marimo 自动完成。
import marimo as mo def echo_model(messages, config): return f"Echo: {messages[-1].content}" chat = mo.ui.chat(echo_model, prompts=["Hello", "How are you?"]) chat运行上面的代码后,页面会渲染出一个聊天框,预设两个可一键点击的提示词。模型函数的两个入参分别是:
messages:一个ChatMessage对象列表,每个对象包含role(取值为"user"、"assistant"或"system")和content(消息文本)两个核心属性;config:一个ChatModelConfig对象,携带采样参数(温度、top_p 等),简单场景下可以完全忽略它。
值得一提的是,mo.ui.chat的返回值不限于文本——源码文档中明确指出,响应可以是任何对象,包括文本、图表(plot)甚至是 marimo UI 元素(chat.py),这让聊天机器人可以直接"吐出"数据表格、可视化或交互控件。
1.1 完整参数一览
从chat.__init__的源码签名可以看到全部可选参数:
| 参数 | 类型 | 默认值 | 说明 |
|---|---|---|---|
model | 可调用对象 | (必填) | 接收(messages, config)并返回回复的函数/对象 |
prompts | list[str] \| None | None | 预设提示词列表,供用户一键点击 |
on_message | 可调用对象 | None | 新消息产生时的回调函数 |
show_configuration_controls | bool | False | 是否在界面上显示模型采样参数调节控件 |
config | ChatModelConfigDict \| None | 见下 | 覆盖默认采样配置 |
allow_attachments | bool \| list[str] | False | 是否允许上传附件;True表示任意类型,或传入 MIME 类型白名单列表 |
max_height | int \| None | None | 聊天元素的最大高度(像素) |
disabled | bool | False | 为True时禁用输入框,用户无法发送消息 |
1.2 默认采样配置
当不传config时,marimo 会应用 DEFAULT_CONFIG:
DEFAULT_CONFIG = ChatModelConfigDict( max_tokens=4096, # 最大生成 token 数 temperature=0.5, # 采样随机性 top_p=1, # 累积概率截断 top_k=40, # 候选 token 数量 frequency_penalty=0, # 高频 token 惩罚 presence_penalty=0, # 已出现 token 惩罚 )如果你显式传入config,底层会用{**DEFAULT_CONFIG, **config}的方式合并覆盖(chat.py),因此只需要写想覆盖的字段即可,例如config={"temperature": 0.7, "max_tokens": 100}。测试 test_chat.py 验证了这一行为。
二、内置模型:mo.ai.llm 全家桶
除了自定义函数,marimo 还提供了一组开箱即用的内置模型类,全部位于mo.ai.llm命名空间(对应源码 marimo/_ai/llm/_impl.py),统一实现了ChatModel抽象基类(定义于 marimo/_ai/_types.py)。
2.1 OpenAI
import marimo as mo chat = mo.ui.chat( mo.ai.llm.openai( "gpt-4o", system_message="You are a helpful assistant.", api_key="sk-proj-...", ), show_configuration_controls=True ) chatopenai模型的 API key 解析顺序为(openai._require_api_key):显式传入的api_key参数 → 环境变量OPENAI_API_KEY→ 用户配置文件marimo_config["ai"]["open_ai"]["api_key"],最后都没有则抛出ValueError。system_message默认值为"You are a helpful assistant specializing in data science."。
它还有一个自动降级逻辑:默认以stream=True发起流式请求,如果某些模型(如o1-preview)不支持流式,会捕获包含 "streaming"/"stream" 关键词的错误并自动回退到非流式模式(openai.call)。此外,如果base_url指向*.openai.azure.com,会自动切换到AzureOpenAI客户端。
2.2 Anthropic
import marimo as mo mo.ui.chat( mo.ai.llm.anthropic( "claude-3-5-sonnet-20240620", system_message="You are a helpful assistant.", api_key="sk-ant-...", ), show_configuration_controls=True )key 解析顺序为:显式参数 → 用户配置marimo_config["ai"]["anthropic"]["api_key"]→ 环境变量ANTHROPIC_API_KEY。注意supports_temperature方法:只有claude-3开头的模型才支持temperature参数,更新的推理模型(如 claude-4 系列)不会传入该字段,避免报错(anthropic)。
2.3 Google AI
import marimo as mo mo.ui.chat( mo.ai.llm.google( "gemini-1.5-pro-latest", system_message="You are a helpful assistant.", api_key="AI..", ), show_configuration_controls=True )key 解析顺序为:显式参数 → 用户配置marimo_config["ai"]["google"]["api_key"]→ 环境变量GOOGLE_AI_API_KEY。底层通过google-genai的generate_content_stream流式生成(google)。
2.4 Groq
import marimo as mo mo.ui.chat( mo.ai.llm.groq( "llama-3.1-70b-versatile", system_message="You are a helpful assistant.", api_key="gsk-...", ), show_configuration_controls=True )key 解析顺序为:显式参数 → 环境变量GROQ_API_KEY(当前版本尚未支持用户配置)。Groq 平台提供免费 API key,是体验 Meta Llama 系列模型的低成本选择。
2.5 AWS Bedrock
源码中还有一个文档示例之外的mo.ai.llm.bedrock模型(bedrock),模型 ID 形如us.anthropic.claude-3-7-sonnet-20250219-v1:0,支持通过region_name、profile_name、credentials(或aws_access_key_id/aws_secret_access_key)配置 AWS 凭据,并针对 AccessDenied、模型未启用等常见错误给出可读的中文级错误提示。
三、Pydantic AI:一等公民支持
marimo 对 pydantic-ai 提供一等支持。用Agent类构建聊天机器人,Chat UI 会自动渲染**推理过程(reasoning steps)、工具调用(tool calls)**等结构化内容:
from pydantic_ai import Agent import marimo as mo assistant = Agent( "openai:gpt-5", system_prompt="You are a helpful assistant.", ) chat = mo.ui.chat(mo.ai.llm.pydantic_ai(assistant)) chat底层实现上,pydantic_ai 模型通过VercelAIAdapter(pydantic_ai.ui.vercel_ai)把 marimo 的ChatMessage转换为 pydantic-ai 的UIMessage,再以 SDK 版本AI_SDK_VERSION = 7编码成标准 Vercel AI 事件流(reasoning-start/delta/end、tool-input、tool-output 等),最终被前端原生解析渲染。config中的max_tokens、temperature、top_p、frequency_penalty、presence_penalty会映射到 pydantic-ai 的ModelSettings(pydantic_ai._get_model_settings)。
仓库中提供了完整的可运行示例 pydantic-ai-chat.py:它演示了用下拉框切换 Gemini / Claude / GPT 模型、启用结构化输出(output_type=[CodeOutput, str])、启用推理、挂载需要人工审批(requires_approval=True)的工具,以及手写一个产出 Vercel AI SDK 各类型 chunk 的自定义模型。
3.1 把历史消息转回 pydantic-ai 消息
当使用 pydantic-ai 时,chat.value里的消息被映射为 Vercel UI 消息格式。如果需要转回 pydantic-ai 的原生消息对象,使用官方适配器函数:
from pydantic_ai.ui.vercel_ai import VercelAIAdapter messages = VercelAIAdapter.load_messages(chat.value)四、访问聊天历史
聊天历史通过value属性获取:
chat.value返回一个ChatMessage对象列表,每个对象包含:
id:消息唯一标识;role:"user"/"assistant"/"system";parts:AI SDK 标准化的消息部件列表(TextPart、ReasoningPart、ToolInvocationPart、FilePart等),对于基本模型还会额外支持content与attachments属性;metadata:附加元数据。
ChatMessage是基于msgspec.Struct实现的数据类(marimo/_ai/_types.py),content甚至可以携带富 Python 对象(如 DataFrame),而不是只有字符串。parts中的未知 dict 会原样透传,保证未来 AI SDK 新增的部件类型也能无损往返。
五、自定义模型与额外上下文(RAG 实战)
模型函数完全可以访问外部数据源,实现检索增强生成(RAG):
import marimo as mo def rag_model(messages, config): question = messages[-1].content docs = find_relevant_docs(question) context = "\n".join(docs) prompt = f"Context: {context}\n\nQuestion: {question}\n\nAnswer:" response = query_llm(prompt, config) return response mo.ui.chat(rag_model)流程上,把用户最新提问(messages[-1].content)拿去检索相关文档,拼接成带上下文的提示词后交给 LLM,最后把答案返回给 Chat UI。仓库中还提供了更复杂的 recipe_bot.py(检索式菜谱机器人)与 llm_datasette.py(对话式数据库查询)等参考实现。
模型函数的形态非常灵活,_run_prompt的源码(chat.py)表明它支持四种写法:
- 普通函数:
def model(messages, config): return "text"; - 单参数函数:
def model(messages): ...(只接收消息列表); - 同步生成器:
def model(messages, config): yield chunk(流式); - 异步函数/异步生成器:
async def model(messages, config): ...。
六、模板化提示词(Templated Prompts)
通过prompts参数预设常用问题,用户可一键点击发送;如果在提示词中嵌入{{var}}占位符,marimo 会自动生成一个表单让用户填写变量值,再动态插入后发送:
mo.ui.chat( mo.ai.llm.openai("gpt-4o"), prompts=[ "What is the capital of France?", "What is the capital of Germany?", "What is the capital of {{country}}?", ], )用户点击第三条提示时,界面会弹出输入框要求填写country的值,最终发送的消息是填充后的完整问题。
七、图片等附件上传
通过allow_attachments参数允许用户给消息附加文件:
mo.ui.chat( rag_model, allow_attachments=["image/png", "image/jpeg"], # 或者允许任意类型附件: # allow_attachments=True, )传入 MIME 类型白名单列表可以精确控制可上传的文件种类;传True则放开所有类型。附件会以ChatAttachment对象承载,包含url(可为托管 URL 或 Data URL)、name(文件名)与content_type(媒体类型,未指定时根据 URL 扩展名自动推断)。pydantic-ai 模式下,多模态消息(如图片理解)可以通过BinaryImage输出类型与附件机制配合使用,参见 pydantic-ai-chat.py 中的output_type = BinaryImage | str用法。
八、流式响应:逐字生成体验
Chat 组件支持实时流式输出,回复像 ChatGPT 一样逐字逐句地出现。内置模型(OpenAI、Anthropic、Google、Groq、Bedrock)默认就是流式,无需任何额外配置。
8.1 流式原理:delta 增量
marimo 采用业界标准的delta 增量流式模式(与 OpenAI、Anthropic 等供应商一致):你的生成器函数每次yield的应当是一段全新的内容增量,marimo 负责累积并把渐进式回复实时推送给前端。
import marimo as mo import time def streaming_model(messages, config): """Stream responses word by word.""" response = "This response will appear word by word!" words = response.split() for word in words: yield word + " " # Yield delta chunks time.sleep(0.1) # Simulate processing delay chat = mo.ui.chat(streaming_model) chat异步版本只需把普通函数换成async def,并用asyncio.sleep模拟延迟:
import marimo as mo import asyncio async def async_streaming_model(messages, config): """Stream responses word by word asynchronously.""" response = "This response will appear word by word!" words = response.split() for word in words: yield word + " " # Yield delta chunks await asyncio.sleep(0.1) # Async processing delay chat = mo.ui.chat(async_streaming_model) chat每一次yield就是一块 delta,marimo 累积后实时渲染出不断增长的回复。可以运行仓库中的 streaming_custom.py 示例体验完整效果——它把用户消息逐词回显,并配有show_configuration_controls=True的采样参数调节面板。
8.2 重要:yield 增量,而不是累积文本
Delta vs Accumulated
✅正确(delta 模式):每个 yield 只包含新增内容
yield "Hello" yield " " yield "world" # 结果:"Hello world"❌错误(累积模式,已废弃):重复发送完整文本,浪费带宽
yield "Hello" yield "Hello " yield "Hello world"
Delta 模式更高效(长回复可减少约 99% 的带宽占用),且与各大 AI 供应商的标准流式 API 天然对齐。底层的_handle_streaming_response(chat.py)会为纯字符串 yield 自动生成标准的text-start/text-delta/text-end事件序列,并通过ChunkSerializer统一处理 pydantic-ai 的BaseChunk、普通字符串和 dict 三种 chunk 形态。测试 test_chat.py 精确断言了非流式响应应发出text-start → text-delta → text-end → final四个事件。
8.3 更高级的流式:Vercel AI SDK 协议
若希望流式输出推理过程、工具调用输入/输出、文件引用、来源链接甚至自定义 data 部件,可以直接 yield pydantic-ai 的vercel响应类型 chunk(pydantic-ai-chat.py 有完整示例):
import pydantic_ai.ui.vercel_ai.response_types as vercel async def custom_model(messages, config): # 流式输出推理/思考过程 yield vercel.ReasoningStartChunk(id="reasoning-1") yield vercel.ReasoningDeltaChunk(id="reasoning-1", delta="Let me think...") yield vercel.ReasoningEndChunk(id="reasoning-1") # 流式输出文本(也可直接 yield dict) yield {"type": "text-start", "id": "text-1"} yield vercel.TextDeltaChunk(id="text-1", delta="Here is my answer.") yield vercel.TextEndChunk(id="text-1") yield vercel.FinishChunk(finish_reason="stop") chat = mo.ui.chat(custom_model)8.4 取消生成(Stop)
当用户在界面点击 Stop 时,marimo 会取消进行中的模型调用:异步模型代码在下一个await处收到asyncio.CancelledError;同步生成器则被关闭,内部会抛出GeneratorExit。如果你的模型持有 HTTP 客户端、文件句柄、数据库游标等资源,应在try/finally中释放,以便取消时及时清理——不要在同步生成器里试图捕获CancelledError;而吞掉CancelledError的异步生成器实际上不会停止,可能继续消耗上游 token(chat.py)。取消时后端会尽力补齐未闭合的流式块并发送AbortChunk(reason="user_cancelled"),让前端干净地结束渲染(chat.py)。
九、支持任意 OpenAI 兼容端点
任何遵循 OpenAI API 格式的端点都可以通过base_url接入,典型案例如下:
# Cerebras chatbot = mo.ui.chat( mo.ai.llm.openai( model="llama3.1-8b", api_key="csk-...", # 填入你的 key base_url="https://api.cerebras.ai/v1/", ), ) chatbot# Groq chatbot = mo.ui.chat( mo.ai.llm.openai( model="llama-3.1-70b-versatile", api_key="gsk_...", # 填入你的 key base_url="https://api.groq.com/openai/v1/", ), ) chatbot# xAI chatbot = mo.ui.chat( mo.ai.llm.openai( model="grok-beta", api_key=key, # 填入你的 key base_url="https://api.x.ai/v1", ), ) chatbotGroq 和 Cerebras 都提供免费 API key,非常适合低成本体验 Meta 的 Llama 系列模型。如果某个厂商不遵循 OpenAI 标准格式,可以在仓库的 issues 中提交 feature request。仓库示例目录 examples/ai/chat/ 还收录了groq_example.py、anthropic_example.py、gemini.py、deepseek_example.py、bedrock_example.py、openai_example.py等各厂商的完整可运行示例。若需在 marimo 编辑器中配置更多 AI 提供方(含 API key 存储),可参考 AI completion 文档。
十、类型速查
三个核心公开类型均由 marimo/_ai/_types.py 导出,并统一从mo.ai命名空间暴露(见 marimo/_ai/init.py):
10.1 ChatMessage
@dataclass class ChatMessage: role: Literal["user", "assistant", "system"] # 消息角色 content: Any # 内容,可为富 Python 对象 id: str = "" # 消息 ID parts: list[ChatPart] = [] # AI SDK 标准部件 attachments: list[ChatAttachment] | None = None # 附件(基础模型) metadata: Any | None = None # 元数据10.2 ChatModelConfig
@dataclass class ChatModelConfig: max_tokens: int | None # 最大生成 token 数 temperature: float | None # 随机性 top_p: float | None # 累积概率截断 top_k: int | None # 候选 token 数 frequency_penalty: float | None # 高频词惩罚 presence_penalty: float | None # 已出现词惩罚mo.ui.chat也接受一个符合该结构的 dict 作为初始配置(即config参数)。
10.3 ChatAttachment
@dataclass class ChatAttachment: url: str # 托管 URL 或 Data URL name: str = "attachment" # 文件名 content_type: str | None = None # 媒体类型,缺省时按扩展名推断十一、快速上手清单
- 最小实现:
mo.ui.chat(lambda messages, config: "Hi!"),一条 lambda 即可获得完整聊天界面; - 接入真实模型:
mo.ui.chat(mo.ai.llm.openai("gpt-4o")),并确保 API key 通过参数、环境变量或用户配置提供; - Agent 化:
mo.ui.chat(mo.ai.llm.pydantic_ai(agent)),自动获得推理过程与工具调用渲染; - 流式体验:把模型函数写成
yield增量 chunk 的生成器(内置模型默认已流式); - 富交互:组合
prompts(模板化提示词)、allow_attachments(附件)、show_configuration_controls(采样参数面板)以及chat.value(程序化读取对话历史),即可构建从简单问答到 RAG 检索、多模态、工具调用的完整 AI 应用。
【免费下载链接】marimoA reactive notebook for Python — run reproducible experiments, query with SQL, execute as a script, deploy as an app, and version with git. Stored as pure Python. All in a modern, AI-native editor.项目地址: https://gitcode.com/GitHub_Trending/ma/marimo
创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考