mobile-use 使用与接入第三方 LLM 网关完整指南OC

mobile-use 使用与接入第三方 LLM 网关完整指南OC

1. 拉取代码

bash 复制代码
git clone https://github.com/minitap-ai/mobile-use
cd mobile-use

2. 初始化 .env

项目根目录里有 .env.example,复制为 .env:

bash 复制代码
cp .env.example .env

3. 配置 .env(接入第三方网关)

mobile-use 通过环境变量支持多家厂商(实现见 minitap/mobile_use/services/llm.py),

包括 MINIMAX_API_KEY、OPEN_ROUTER_API_KEY、ANTHROPIC_API_KEY 等。

如果厂商不在支持列表里------或者像本教程这样想接自建 / 第三方 OpenAI 兼容网关 ------

可以走 OpenAI 协议开放的 OPENAI_BASE_URL 自定义端点这条路:

dotenv 复制代码
# .env
OPENAI_API_KEY='sk-bzi5jUbedDQKqtKJm******'
OPENAI_BASE_URL="https://token.*****.com/v1"

复制粘贴时注意行尾不要带多余空格。

4. 兼容层补丁(关键步骤)

本教程的第三方网关只实现了 OpenAI 协议的"一半"------忽略 response_format 与

tool_choice,需要打一个本地补丁。

4.1 新增导入

打开 minitap/mobile_use/services/llm.py,在文件顶部加:

python 复制代码
import json
from langchain_core.messages import HumanMessage
from langchain_core.output_parsers import PydanticOutputParser
from langchain_core.runnables import RunnableLambda
from pydantic import BaseModel

4.2 新增兼容类与辅助函数

在 get_openai_llm 定义前插入:

python 复制代码
def _json_format_instructions(schema: Any) -> str | None:
    """为给定 schema 构造一条显式的"请按 JSON 输出"指令。"""
    if schema is None:
        return None
    if isinstance(schema, dict):
        return (
            "Respond with a single valid JSON object matching this JSON Schema. "
            "Output the raw JSON only: no markdown fences, no explanations.\n"
            f"{json.dumps(schema, ensure_ascii=False)}"
        )
    if isinstance(schema, type) and issubclass(schema, BaseModel):
        return PydanticOutputParser(pydantic_object=schema).get_format_instructions()
    return None


def _append_instruction(payload: Any, instruction: str) -> Any:
    """把指令消息追加到当前输入,保持原输入形态。"""
    if isinstance(payload, list):
        return [*payload, HumanMessage(content=instruction)]
    if isinstance(payload, str):
        return [HumanMessage(content=payload), HumanMessage(content=instruction)]
    return payload


class CompatChatOpenAI(ChatOpenAI):
    """针对只支持部分 OpenAI 协议的网关定制的 ChatOpenAI。

    langchain-openai 的 ``with_structured_output`` 默认走 ``json_schema``(依赖
    ``response_format``)。很多 OpenAI 兼容网关(包 MiniMax / Anthropic / Google
    模型的代理)会静默忽略 ``response_format`` *和* ``tool_choice``:调用成功、
    模型用散文或 JSON 块回复、不发 tool call,结果 parsed 为 ``None``,下游节点
    崩溃。

    本类先走 tool calling 路径(网关真支持时一次即中),拿不到结果时再追加一条
    显式 JSON 格式指令重试 ``json_mode``。在原生 OpenAI 上首次必中,行为与
    未替换时完全一致。
    """

    def with_structured_output(
        self,
        schema=None,
        *,
        method: str = "function_calling",
        **kwargs,
    ):
        kwargs.pop("include_raw", None)
        primary = super().with_structured_output(schema, method=method, include_raw=True, **kwargs)
        instruction = _json_format_instructions(schema)
        try:
            secondary = super().with_structured_output(schema, method="json_mode", **kwargs)
        except ValueError:
            secondary = None

        def _unwrap(out: Any) -> Any:
            if isinstance(out, dict) and "raw" in out:
                return out.get("parsed")
            return out

        async def _acall(input_, config=None):
            parsed = _unwrap(await primary.ainvoke(input_, config))
            if parsed is not None or secondary is None or instruction is None:
                return parsed
            llm_logger.warning(
                "Structured output returned nothing via tool calling; retrying with an "
                "explicit JSON instruction."
            )
            return await secondary.ainvoke(_append_instruction(input_, instruction), config)

        return RunnableLambda(_acall)

4.3 替换 get_openai_llm 里的客户端类

python 复制代码
def get_openai_llm(
    model_name: str = "o3",
    temperature: float = 1,
) -> ChatOpenAI:
    assert settings.OPENAI_API_KEY is not None
    client = CompatChatOpenAI(   # ← 原先是 ChatOpenAI
        model=model_name,
        api_key=settings.OPENAI_API_KEY,
        base_url=settings.OPENAI_BASE_URL,
        temperature=temperature,
    )
    return client

⚠️ 这是对源码的本地补丁 :git pull、重建 Docker 镜像、切分支都会覆盖,

届时需要重新打。建议正式提个 PR 合入上游。

5. 配置 llm-config.override.jsonc

复制模板:

bash 复制代码
cp llm-config.override.template.jsonc llm-config.override.jsonc

本教程的第三方网关只暴露 MiniMax 系列模型 (MiniMax-M3 / MiniMax-M2.7 /

MiniMax-M2.7-highspeed),全部走 provider: "openai",按节点能力分配主备:

能力型节点(planner / orchestrator / cortex / hopper)走 MiniMax-M3,

高频型节点(executor / contextor / outputter)走更便宜的

MiniMax-M2.7-highspeed。

jsonc 复制代码
// llm-config.override.jsonc
//
// Custom OpenAI-compatible gateway: https://token.rainbowcn.com/v1
// Provider "openai" is used for every node; the base URL / API key come from
// OPENAI_BASE_URL / OPENAI_API_KEY in .env.
//
// Models exposed by this gateway (MiniMax series only):
//   - MiniMax-M3              (most capable, preferred everywhere)
//   - MiniMax-M2.7            (200k context, reasoning model)
//   - MiniMax-M2.7-highspeed  (fast / cheap variant)
{
  "planner": {
    "provider": "openai",
    "model": "MiniMax-M3",
    "fallback": {
      "provider": "openai",
      "model": "MiniMax-M2.7-highspeed"
    }
  },
  "orchestrator": {
    "provider": "openai",
    "model": "MiniMax-M3",
    "fallback": {
      "provider": "openai",
      "model": "MiniMax-M2.7-highspeed"
    }
  },
  "cortex": {
    "provider": "openai",
    "model": "MiniMax-M3",
    "fallback": {
      "provider": "openai",
      "model": "MiniMax-M2.7"
    }
  },
  "executor": {
    "provider": "openai",
    "model": "MiniMax-M2.7-highspeed",
    "fallback": {
      "provider": "openai",
      "model": "MiniMax-M3"
    }
  },
  "contextor": {
    "provider": "openai",
    "model": "MiniMax-M2.7-highspeed",
    "fallback": {
      "provider": "openai",
      "model": "MiniMax-M2.7"
    }
  },
  "utils": {
    "hopper": {
      // Needs at least a 256k context window.
      "provider": "openai",
      "model": "MiniMax-M3",
      "fallback": {
        "provider": "openai",
        "model": "MiniMax-M2.7"
      }
    },
    "outputter": {
      "provider": "openai",
      "model": "MiniMax-M2.7-highspeed",
      "fallback": {
        "provider": "openai",
        "model": "MiniMax-M3"
      }
    }
    // video_analyzer is optional - only needed when using video recording tools.
    // The gateway exposes no video-capable model, so it is left unconfigured.
  }
}

fallback 不能留空字符串 ,否则会被判为非法配置。

video_analyzer 网关无视频模型,不配(项目对它为 optional)。

6. 安装 uv

uv 是用 Rust 写的 Python 包与项目管理工具,比 pip 快一个量级。

macOS / Linux

bash 复制代码
curl -LsSf https://astral.sh/uv/install.sh | sh

Windows(PowerShell)

powershell 复制代码
powershell -ExecutionPolicy ByPass -c "irm https://astral.sh/uv/install.ps1 | iex"

也可以用 pip install uv / winget install astral-sh.uv / scoop install uv。

详见 uv 官方安装文档。

7. 创建并激活虚拟环境

bash 复制代码
# 读取 .python-version 创建 .venv
uv venv

# 激活环境
# macOS / Linux
source .venv/bin/activate
# Windows
.venv\Scripts\activate

8. 安装依赖

bash 复制代码
uv sync

这一步会根据 uv.lock 同时安装项目依赖与开发依赖。

9. 跑起来

bash 复制代码
python ./minitap/mobile_use/main.py "Go to settings and tell me my current battery level"

python ./minitap/mobile_use/main.py \
  "Open Gmail, find all unread emails, and list their sender and subject line" \
  --output-description "A JSON list of objects, each with 'sender' and 'subject' keys"

首次运行会要求连接 Android / iOS 设备(USB 调试开启 / 模拟器在跑),

具体可参考根目录的 mobile-use.sh / mobile-use.ps1。

相关推荐
紧跟先前的步伐11 小时前
Go AI Agent 实战 24|工具系统工程化收尾与阶段验收
人工智能·ai·golang·1024程序员节
驰骋工作流12 小时前
流程时效考核的表结构:从待办时限到 WF_CH 考核结果驰骋BPM驰骋低代码低代码工作流引擎
android·低代码·rxjava
xzlAwin12 小时前
Win10 安装OpenClaw 龙虾
ai·openclaw·龙虾
传奇开心果编程13 小时前
【Flutter踩坑排雷】第7课:巨型 build 函数坑|全部 UI 写在同一个 build,维护难、隐性异常
android·flutter·ios·移动开发·跨平台·dart·声明式ui
IDIOT___IDIOT16 小时前
DSH 对话回收站(dsh-session-trash)
ai·dsh
玩AI的奶茶16 小时前
MiniMax H3 出 30 秒长片:显存账、拼接法和一次的成本
大数据·人工智能·ai·gpu算力·算力租赁
犀思云17 小时前
从SD-WAN到AI WAN:企业广域网能力演进与架构变化解读
ai·sd-wan·wan·企业广域网
方白羽17 小时前
Android 双开环境底层剖析与风控检测落地
android·安全·app
weixin_5633714717 小时前
Android Studio 2024 版 Device Explorer 打开方法
android·ide·android studio
犀思云18 小时前
AI WAN实践:分布式AI训练与推理如何通过智能广域网提升效率?
ai·wan·分布式ai训练·分布式ai推理·智能广域网