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。

相关推荐
snotJam1 小时前
关于Uniapp的Android自定义基座使用
android·uni-app
空堂与归1 小时前
清华VPP2登顶RoboDojo:凭预测解耦突围GPT-6
人工智能·gpt·ai·具身智能
孙启超2 小时前
【AI工程师精讲】04:思维链:CoT 为什么有效,以及它什么时候在骗你
人工智能·ai·大模型
TST-魚爷2 小时前
易视讯会议摄像头电视安装教程
android·电脑·钉钉·腾讯会议·zoom会议
宝桥南山2 小时前
.NET 11 - 尝试创建一下AGUIServer和AGUIClient(基于新版.NET SDK for AG-UI)
microsoft·ai·微软·c#·aigc·.net
空堂与归2 小时前
GPT-6 智能界面上线,对话式 Agent 真没戏了?
人工智能·gpt·ai
墨天梦11 小时前
B17_LifecycleLiveData与Flow
android·kotlin
_祝你今天愉快13 小时前
UI核心 - 绘制流程(测量、布局、绘制)
android
百数平台13 小时前
百数AI文本知识库配置详解:本地文档上传与自定义录入、解析分段策略全说明
低代码·ai