跳转至

Web UI

Web UI 是 lovia 自带的本地聊天应用,适合直接使用 Agent,或在开发自定义前端前验证功能。

一条命令启动

pip install "lovia[web]"
lovia web

访问 http://127.0.0.1:8000,首次启动时配置一个模型即可。之后可用 lovia web --check 检查配置并探测端点,而不启动服务。

模型配置有两级,项目级文件存在时会整体覆盖用户级配置,而不是逐项合并:

路径 作用域
~/.lovia/config.json 当前用户的默认配置
./.lovia/config.json 当前项目的完整覆盖配置

密钥和聊天数据所在的 .lovia/ 会自动加入 .gitignore。在支持 Unix 权限的平台上, 配置文件只允许当前用户读写。

CLI 创建的默认 Agent 包含 Todo、Memory、时间与 HTTP Tool、Web 搜索、定时任务,以及以 当前目录为根的 coding 模式 Workspace。它还会自动读取:

  • AGENTS.md:作为 Agent instructions;
  • ./.agents/skills 与 ~/.agents/skills:作为 Skills 目录(项目目录优先; 目录列表在设置 → 技能中管理);
  • ./.lovia/memory:作为 Memory 目录。

旧的 ./skills 不再自动加载;可以迁移到 ./.agents/skills,或在设置 → 技能中 添加。Web 搜索在配置 Tavily Key 后使用 Tavily,否则尝试可选的 DuckDuckGo 后端。

默认功能会产生主 Run 之外的模型调用:Memory 默认在每个已完成 Run 后提炼一次,定期整理时 再调用一次;标题和追问建议也各自独立调用。可以用 --no-memory、--no-followups 关闭对应功能, 或把辅助任务指派给更便宜的模型。Memory 的完整成本边界见记忆。

默认仅供本机使用

回环地址 127.0.0.1 默认不鉴权。绑定其他地址时必须设置 --token 或 LOVIA_WEB_TOKEN;未设置时服务会自动生成。持有 token 的客户端可以使用 Agent 的 全部能力,包括编辑文件和执行 Shell 命令,因此应把 token 当作密码。允许其他设备访问时, 建议同时使用 --readonly;多用户部署请参阅生产部署。

模型配置与切换

模型档案保存在 config.json 中。连接测试会真实检查端点可达性、认证、模型列表和上下文窗口; 端点未列出的模型 ID 仍可手动填写。

API Key 只写不读:服务端只返回是否已设置及脱敏提示,不会把完整密钥传回浏览器。配置更新会 整体校验、原子写入并立即生效,无需重启。

切换模型从下一条消息开始生效,包括旧对话的后续消息、定时任务和后台子 Agent;已经开始的 回复仍使用原模型。视觉理解以及标题、追问建议等辅助任务,可以分别指派给其他模型档案。

额外请求参数(高级)是一个 JSON 对象,原样合并进发给该端点的每个请求——即 provider 的 extra_body。推理相关的配置就靠它,按端点自己的 写法填,因为一个 OpenAI 兼容格式背后藏着许多方言:OpenAI、DeepSeek、Qwen 3.8 和 vLLM 用 {"reasoning_effort": "medium"};vLLM/SGLang 部署的 Qwen、GLM 用 {"chat_template_kwargs": {"enable_thinking": false}};DeepSeek 与智谱官方 API 用 {"thinking": {"type": "disabled"}};Anthropic 用 {"output_config": {"effort": "medium"}}。 值为 null 表示删除适配器本来会发的字段(拒绝 stream_options 的网关可写 {"stream_options": null})。不做任何转换,端点接受什么由端点决定——官方 API 会在下一条消息 时拒绝不认识的字段,vLLM 则可能静默忽略;对话里有没有思考痕迹才是真正的检验。编辑框下方的 快捷片段只负责写入这些形状,不代表模型支持。

经过这个档案的每一次调用都会带上这些字段——主回复、后台子 Agent、上下文压缩摘要,以及被指派为 视觉或辅助角色时的图片描述、标题和追问。同一端点想要第二套参数,就是第二个档案:复制在服务端 完成(连密钥一起,因为页面从不持有它),所以"标题不思考"就是复制一份、写上 enable_thinking: false、指派给辅助角色。思考模型需要输出空间:这里的 max_tokens 对该端点 优先于 --max-tokens,但上下文策略的输出预留不会跟着变。

搜索后端、Tavily Key 与 Skill 目录也保存在 config.json 中。模型连接、多模型档案、 角色指派、搜索和 Skills 配置没有对应的 CLI 参数;请通过设置页或直接维护配置文件。

Skills

设置 → 技能管理 Skill 目录——一个有序列表;每个 skill 是含 SKILL.md 的子目录 (详见 Skills)。skill 重名时靠前的目录优先。列表默认为 ./.agents/skills 和 ~/.agents/skills;移除某个默认目录即禁用它,也可以添加任意 其他目录(支持 ~)。面板会展示每个目录的贡献——发现的 skills、解析问题、被遮蔽的 重名项;不存在的目录会被直接跳过,过期条目不会阻止服务启动。列表变更立即生效; 向已配置的目录里新增 skill 则无需任何操作。

加载自定义 Agent

创建 app.py:

from lovia import Agent

assistant = Agent(
    name="assistant",
    instructions="清晰回答,并在有助于提高准确性时使用工具。",
    model="<model>",
)

然后运行:

lovia web --app app:assistant

--app MODULE:ATTR 接受一个 Agent、{name: agent} 映射,或由 create_app() 构建的应用 (也可以是返回其中任意一种的无参工厂函数)。应用会按构建时的配置原样运行,沿用自定义的认证、 存储和 UI 选项;CLI 仍负责 host、port、日志和启动信息。原本会传给 create_app() 的选项 (--token、--db、--title 等)会被忽略并给出警告。Python 部署和 ASGI 集成详见 Web 服务端。

界面支持 GitHub 风格 Markdown、代码高亮、Mermaid 和内嵌图片。默认 Agent 已知道这些能力; 自定义 Agent 不会自动获得相关提示。如果希望模型主动使用这些格式,可以加入 SURFACE_NOTE:

from lovia.web import SURFACE_NOTE

assistant = Agent(
    name="assistant",
    instructions="清晰回答。\n\n" + SURFACE_NOTE,
    model="<model>",
)

图片与文件

附件会写入 Workspace 的 uploads/,消息中保存的是相对路径。因此,即使模型不能直接读取附件, Agent 仍可通过 Workspace Tool 打开文件。--no-workspace 会同时关闭这条路径。

Markdown 图片可以直接引用 Workspace 内的文件——![chart](uploads/chart.png) 会显示该文件本身, 回复和文件面板的 Markdown 预览都是如此。路径先按文档自身所在目录解析,再按 Workspace 根目录解析; 读取走的是与预览相同的只读接口。SVG 也能显示,走的是该接口的附件形式:浏览器对图片子资源会忽略 Content-Disposition(此时 SVG 里的脚本被禁用),对导航则会遵守(此时脚本本会执行)——于是图能显示, 而 URL 本身只会下载。

链接([report](report.md))指向同一个接口:在回复中,浏览器能直接显示的文件(图片、PDF)会在 新标签页打开,其余文件直接下载,对话本身不会被导航掉;在文件面板内,同样的链接则在查看器中打开。 回复里的外部链接同样在新标签页打开。

图片是否内联发送由视觉配置决定:

  • 官方 OpenAI 和 Anthropic 端点默认按支持视觉处理;其他视觉端点需要把该模型配置的视觉能力 设为“支持”(即 config.json 中的 "vision": "on")。
  • 纯文本主模型可以把视觉角色指派给另一个模型配置。此时主模型通过 describe_image Tool 获得文字结果,图片原始数据不会写入主模型的对话历史。

单个文件默认不超过 25 MiB,可用 LOVIA_MAX_UPLOAD_MB 调整。带扩展名的文件受内置白名单限制; LOVIA_UPLOAD_ALLOWED_EXT 可覆盖白名单,使用逗号或空格分隔,* 表示不限。

通过文件面板上传的文件不会自动附加到下一条消息。"最近"列表不列环境噪音(node_modules/、 venv/、__pycache__/、*.pyc)和 Agent 的临时目录 tmp/,"新文件"角标也只统计会出现在 "最近"里的写入——这只是展示规则:目录浏览显示所有目录,Agent 引用的任何路径(回复里 tmp/ 下的图片、工具卡片的"在文件面板打开")只要 Agent 自己能读就能打开。删除聊天也不会删除 uploads/ 中的文件,需要由应用或用户清理。

后台进程属于聊天而不是单次 Run:Run 结束不会终止它们;删除聊天或停止 Web 服务时会统一清理, 服务重启后也不会恢复。更完整的生命周期见工作区。

图表

show_chart 工具可以在聊天中绘制交互式图表,支持折线、柱状、饼图、散点、热力图、K 线、 桑基图等 Apache ECharts 的系列类型。模型把 ECharts option 写成调用参数,卡片负责渲染;实时对话和重新打开会话时都会显示。lovia web 的默认 Agent 已带有这个工具;使用自己的 Agent 时,把工具加进去:tools=[show_chart](从 lovia.web 导入)。任何工具也都 可以产出图表:return ui_result("chart", option),直接用真实数据作图,不需要模型抄写数字 (见UI 组件)。如果工具的 option 来自模型(比如执行查询后把结果行放进 option 的 dataset),先用 validate_chart_option(option) 检查:它抛出与 show_chart 相同的 工具错误,模型可以据此修正 option 后重试。

图表标题显示为卡片标题,位于图表上方,不会与图例重叠。标题旁的工具栏(鼠标悬停时显示)可以 把图表下载为 PNG 或 SVG(包含标题)、把数据下载为 CSV(树图、桑基图、关系图没有表格结构, 下载为 JSON),也可以全屏查看。

须知:

  • ECharts(约 1 MB,已随包内置)在出现第一张图时才加载,没有图表的会话不会请求它。
  • 把聊天导出为 HTML 时,每张图表以 SVG 图片的形式包含在内。
  • option 在绘制前会经过过滤:图片、链接、graphic 和 toolbox 组件、HTML 形式的 tooltip 都会被去掉;需要代码或地图数据的系列类型(custom、map)也不支持。
  • 团队的作图约定(偏好的图表类型、单位、标题写法等)写进 instructions 或一个 Skill 即可, 不需要写代码。
  • 配色和文字颜色都是 CSS 变量(--lv-chart-1 … --lv-chart-8、--lv-chart-text、 --lv-chart-title、--lv-chart-axis、--lv-chart-grid),在自定义模板 的 head 块中覆盖即可匹配品牌风格。

表格

show_table 工具用交互式表格代替 Markdown 表格展示给用户:点击表头排序(再点一次倒序,第三次 恢复原顺序),行数较多时可以输入关键字筛选,每页 20 行翻页浏览。数字右对齐并按数值排序。 工具栏可以复制表格(制表符分隔,可直接粘贴到表格软件)、下载 CSV,或全屏查看;导出的都是当前 视图,也就是筛选、排序之后的结果。把聊天导出为 HTML 时,表格会以包含全部行的普通表格形式 包含在内。

lovia web 的默认 Agent 已带有这个工具;使用自己的 Agent 时,把工具加进去:tools=[show_table] (从 lovia.web 导入)。更常用的做法 往往是让自己的工具返回 ui_result("table", {"title": ..., "columns": [...], "rows": [[...], ...]}): 行数据直接来自你的数据源(查询、API),模型不必逐个写出数字,更快、更省,也不会出错。单元格可以是 字符串、数字、布尔值或 null,按原样显示。表格不认识的键会被忽略,所以同一份 props 里还可以附带 给模型看的信息,比如总行数或生成这些数据的 SQL。结果很大时,可以用 put_ui_data 把行数据挡在 模型上下文之外:模型只看预览,表格仍显示全部行(见大块数据)。

页面

show_html 工具可以在聊天中直接显示一个交互式页面:计算器、模拟、自定义可视化,凡是图表和表格 做不到的都可以。模型写出一个自包含的页面(CSS 和 JavaScript 都内联),卡片在沙箱中运行它。 lovia web 的默认 Agent 已带有这个工具;使用自己的 Agent 时,把工具加进去:tools=[show_html] (从 lovia.web 导入)。你自己的工具也可以返回 ui_result("html", {"html": page, "title": ...})。

页面能做什么、不能做什么:

  • 访问不到应用,也连不上网络。页面运行在不透明源中(没有 cookie、存储,也碰不到聊天内容), 其安全策略拦下所有请求:没有 fetch、CDN 脚本、网络字体和远程图片。链接不会跳转。
  • 可以通过 CSS 变量和聊天界面保持一致:var(--lv-ink)、var(--lv-surface)、var(--lv-border)、 var(--lv-accent)、var(--lv-chart-1) … var(--lv-chart-8),浅色和深色主题都适用。
  • 按钮可以以用户身份追问:lovia.send("按地区拆开看")。只有在页面内真实点击时才会生效。
  • 卡片随页面内容变高,最多占据大半个屏幕;支持全屏查看和下载 HTML。
  • 重新打开聊天时,页面要点击 运行 才会启动,这样陷入死循环的页面不会在每次打开时卡住聊天。 切换主题会重新加载页面。
  • 导出的 HTML 不含脚本,因此只显示页面的源码。

常用 CLI 选项

除模型、搜索和 Skills 配置外,其余选项按“命令行参数 → 环境变量 → 默认值”解析。

命令行选项 环境变量 默认值
--host / --port LOVIA_HOST / LOVIA_PORT 127.0.0.1 / 8000
--root-path LOVIA_ROOT_PATH 无;代理发布应用时使用的路径前缀(详见)
--token LOVIA_WEB_TOKEN 回环地址无需设置;其他地址自动生成
--db LOVIA_DB ./.lovia/<agent>.db
--app MODULE:ATTR LOVIA_APP 创建默认 Agent(或使用自己的 Agent、映射或应用)
--memory-dir / --no-memory LOVIA_MEMORY_DIR ./.lovia/memory
--workspace,--readonly / --trusted / --no-workspace LOVIA_WORKSPACE、LOVIA_WORKSPACE_MODE .(coding 模式)
--instructions-file LOVIA_INSTRUCTIONS_FILE 若存在则使用 AGENTS.md
--max-retries / --max-turns LOVIA_MAX_RETRIES / LOVIA_MAX_TURNS 4 / 50
--max-tokens LOVIA_MAX_TOKENS 沿用 Provider 的默认值;模型配置“额外请求参数”里的 max_tokens 优先
--title LOVIA_TITLE lovia
--log-level LOVIA_LOG_LEVEL info
--no-followups LOVIA_FOLLOWUPS 追问建议默认开启
--no-subagents — 后台子 Agent 默认开启
--check — 检查模型配置并退出

完整列表以 lovia web --help 为准。

来自 Agent 的提问

默认 Agent 带有 ask_human Tool。等待中的问题保存在服务端, 所以刷新或重新连接不会丢失。默认 10 分钟无人回答时,调用会取消,模型收到 Tool 错误后继续; 这也避免定时任务和后台任务长期占用运行槽位。

自定义 Agent 需要把同一个 HumanChannel 同时交给 Tool 和 Web 应用:

from lovia import Agent
from lovia.tools import HumanChannel, ask_human
from lovia.web import create_app, serve

channel = HumanChannel()
agent = Agent(name="bot", model="<model>", tools=[ask_human(channel)])
serve(create_app(agent, question_channel=channel, question_timeout=600))

追问建议

追问建议来自一次独立模型调用,不会写入 Transcript。关闭它可以避免这次额外调用;服务端使用 --no-followups,也可以把辅助任务角色指派给更便宜的模型(见模型配置与切换)。 通过 create_app() 托管自定义 Agent 时,该功能默认关闭,需要显式开启,见 Web 服务端。

运行与连接的边界

Run 由服务端托管,刷新或关闭页面不会取消它;重新连接后会从服务端状态继续。显式停止 Run 时, 已经完成的 Turn 仍保留在 Session 中。

服务优雅关闭时会保留主 Run 的 Checkpoint,重启后可以恢复;审批和 SSE 订阅等进程内状态不会恢复。 Shell 后台进程和后台子 Agent 也不会随 Checkpoint 恢复。

Run 执行期间发送的消息有两种处理方式,按条决定:

  • 排队(Enter):消息显示在输入框上方,等当前 Run 成功完成后作为单独的 Run 发送。多条排队消息逐条发送, 每条都等上一条的 Run 完成。Run 失败或被停止时队列会暂停,可以点「立即发送」或撤回消息继续。 排队消息只保存在当前浏览器标签页中:仅在该对话打开时发送,刷新页面后会丢失。
  • 插话(⌥/Alt+Enter,或排队消息上的「插话」):作为当前 Run 下一 Turn 的追加指令;如果当前 Run 恰好结束,用 ⌥/Alt+Enter 发出的会按顺序进入新的 Run,从队列插话的则回到队首,照常排队。 插话没有收到应答(例如网络中断)时,它可能已经送达,所以不会自动重发:它停在队首并暂停,等你核对对话。

Run 执行期间也可以添加附件,它们随消息一起发送,无论排队还是插话。有附件还在上传时,「发送」会等上传完成。 排队消息还可以放回输入框编辑(输入框和附件栏都为空时,文字和附件一起放回),或者撤回。

分支对话

每条已完成的回复上都有 在新对话中分支:它新建一个对话,包含到这条回复为止的全部内容, 你可以从这里把对话引向另一个方向。从最后一条回复分支,就是复制整个对话。某条回复之后若有 仍在运行的 Run,这条回复要等该 Run 结束后才会出现分支按钮。分支复制的是服务端当时保存的 内容。原对话不受影响,新对话会链接回原对话,删除其中一个不影响另一个。

分支复制的是对话,不是工作区:分支点之后写入的文件保持当前状态。

让 agent “在新对话里”做某件事时,它可以自己新建对话(start_session)。点击工具卡片即可打开 新对话;新对话不带当前对话的任何内容,并会链接回发起它的对话。

后台子 Agent

lovia web 默认启用后台子 Agent,可用 --no-subagents 关闭。 默认子 Agent 保留 Skills、Todo、Tool 和 Workspace,但不加载 Scheduling、Memory 或 Subagents, 因此不会递归派生。

自定义 Agent 需要自行添加 Subagents Plugin。create_app() 会把使用默认执行方式的 Plugin 接入 Web 托管;可用 create_app(..., wire_subagents=False) 禁用。直接挂载 build_api_router 时,需要调用一次 wire_subagents(deps)。

每个后台任务都有独立 Session。任务完成后,报告会送回父对话:父 Run 仍在执行时进入下一 Turn; 父对话空闲时,服务端会自动启动一次 Run 处理报告。浏览器无需保持在线。

后台任务不属于父 Run,因此停止父 Run 不会停止它;删除父对话则会停止它的后台任务,并一并删除。等待审批超过 approval_timeout 时会自动拒绝; 服务重启后,尚未完成的后台任务不会恢复。

延伸阅读