| --- |
| title: RouteOpt Agent |
| emoji: 🧭 |
| colorFrom: blue |
| colorTo: green |
| sdk: gradio |
| python_version: 3.11 |
| app_file: app.py |
| pinned: false |
| short_description: Tool-calling route optimizer |
| --- |
| |
| # RouteOpt Agent |
|
|
| 基于公开大模型 API + 轻量工具调用的小规模路线优化智能体。它把用户自然语言转成路线优化任务,调用地理编码和路径矩阵工具,再用本地算法求解访问顺序,并自动生成 PDF 报告。 |
|
|
| ## 一键运行 |
|
|
| ```bash |
| chmod +x run.sh |
| ./run.sh |
| ``` |
|
|
| 启动后打开: |
|
|
| ```text |
| http://localhost:7860 |
| ``` |
|
|
| 首次运行会创建 conda 环境 `routeopt-agent` 并安装依赖。脚本会先尝试使用 `environment.yml` 创建标准环境;如果 conda 镜像下载不稳定,会自动克隆本机 base 环境,再用 pip 安装 `requirements.txt`。 |
|
|
| ## LLM 配置 |
|
|
| 默认配置不需要你注册任何网站,也不需要提供 API token: |
|
|
| - LLM:Pollinations OpenAI-compatible API,无需 token。失败时系统自动切换到本地解析和本地报告模板。 |
| - 地理编码:OpenStreetMap Nominatim 公共接口,内置缓存并限制请求频率。 |
| - 路径矩阵:OSRM public demo server。失败时自动切换到直线距离近似矩阵。 |
| - 部署:Hugging Face Spaces 免费 Gradio Space。 |
|
|
| 如果本机出现 `ProxyError('Unable to connect to proxy')`,说明 Python 请求走了不可用代理。默认 `LLM_IGNORE_PROXY=1` 会让 LLM 请求忽略系统代理;如果你的网络必须使用代理访问外网,可在 `.env` 中改成: |
|
|
| ```bash |
| LLM_IGNORE_PROXY=0 |
| ``` |
|
|
| 更稳定的做法是使用正规 API key 服务,并把 key 放到 `.env` 或 Hugging Face Secrets。老师访问公开页面时不需要知道 token。 |
|
|
| 复制配置模板: |
|
|
| ```bash |
| cp .env.example .env |
| ``` |
|
|
| 推荐方案: |
|
|
| ```bash |
| # Groq:国际网络通常较稳,OpenAI-compatible |
| LLM_PROVIDER=groq |
| GROQ_API_KEY=gsk_xxx |
| GROQ_MODEL=openai/gpt-oss-20b |
| ``` |
|
|
| ```bash |
| # SiliconFlow:国内网络通常更友好,OpenAI-compatible |
| LLM_PROVIDER=siliconflow |
| SILICONFLOW_API_KEY=sk-xxx |
| SILICONFLOW_MODEL=THUDM/GLM-Z1-9B-0414 |
| ``` |
|
|
| ```bash |
| # Gemini:Google 官方免费额度,国内网络可能需要代理 |
| LLM_PROVIDER=gemini |
| GEMINI_API_KEY=AIza... |
| GEMINI_MODEL=gemini-2.5-flash |
| ``` |
|
|
| 也可以用 `LLM_PROVIDER=auto`,程序会按已配置的 key 自动选择:Groq -> SiliconFlow -> Gemini -> OpenRouter;没有 key 时退回 Pollinations。 |
|
|
| ## 推荐演示输入 |
|
|
| 自然语言需求: |
|
|
| ```text |
| 我从上海交通大学闵行校区出发,想一天内逛完徐家汇、人民广场、外滩、陆家嘴,最后回到起点,尽量总时间短。 |
| ``` |
|
|
| 起点: |
|
|
| ```text |
| 上海交通大学闵行校区 |
| ``` |
|
|
| 目的地: |
|
|
| ```text |
| 徐家汇 |
| 人民广场 |
| 外滩 |
| 陆家嘴 |
| ``` |
|
|
| 这个样例的上海地点内置了离线坐标兜底,所以即使 Nominatim 临时限流,也能完成演示。 |
|
|
| 也可以使用北京样例: |
|
|
| ```text |
| 我从北京邮电大学出发,想一天内逛完国家大剧院、鼓楼、北海、国家植物园,最后回到起点,尽量总时间短。 |
| ``` |
|
|
| ## 输入失败时如何处理 |
|
|
| 页面不会直接抛出 Python 错误;如果任务无法完成,会说明失败发生在哪个工具,以及应该如何修改输入。 |
|
|
| 常见原因: |
|
|
| - 起点或目的地为空。 |
| - 目的地里只有起点,或者重复地点过多。 |
| - 地点不在内置演示坐标中,同时在线地理编码服务超时、限流或找不到结果。 |
| - 地名太泛,例如只写“鼓楼”“植物园”,在某些城市可能有多个匹配。 |
|
|
| 当前内置演示城市:上海、北京。 |
|
|
| 如果要演示其他城市,建议把地点写得更完整,例如“南京夫子庙”“广州塔”“深圳大学粤海校区”。如果在线地理编码仍然失败,可以直接输入坐标: |
|
|
| ```text |
| 某景点@39.9042,116.4074 |
| ``` |
|
|
| ## Agent Workflow |
|
|
| 1. `LLM tool_call: submit_route_task`:把自然语言和表单提示抽取成结构化任务。 |
| 2. `geocode_places`:把地点转换为经纬度。 |
| 3. `build_route_matrix`:调用 OSRM 获取距离/时间矩阵。 |
| 4. `solve_route`:用 Held-Karp 动态规划精确求解小规模 TSP。 |
| 5. `LLM compose_summary`:生成中文解释。 |
| 6. `generate_pdf_report`:输出包含问题定义、工具调用轨迹、算法和结果的 PDF。 |
|
|
| ## 公开部署到 Hugging Face Spaces |
|
|
| 1. 打开 Hugging Face,新建一个 Space。 |
| 2. Space SDK 选择 `Gradio`。 |
| 3. Visibility 可选 `Public`。 |
| 4. 上传本仓库所有文件,或把仓库推送到 Space。 |
| 5. Space 会根据 `requirements.txt` 自动安装依赖,入口文件是 `app.py`。 |
|
|
| 老师访问 Space 链接即可运行,不需要知道任何 token。 |
|
|
| ## 文件结构 |
|
|
| ```text |
| app.py # Gradio 页面入口 |
| routeopt_agent/agent.py # Agent 编排和工具调用轨迹 |
| routeopt_agent/geo_tools.py# 地理编码、OSRM 矩阵、兜底矩阵 |
| routeopt_agent/solver.py # Held-Karp 路线优化 |
| routeopt_agent/report.py # PDF 报告生成 |
| routeopt_agent/parsing.py # 本地自然语言解析兜底 |
| routeopt_agent/viz.py # SVG 路线示意图 |
| environment.yml # 本地 conda 环境 |
| requirements.txt # Hugging Face Spaces 依赖 |
| run.sh # 一键运行脚本 |
| ``` |
|
|