1. 从一条自然语言指令说起:为什么 GIS 工程师需要 AI Agent
如果你做过批量空间数据处理,大概率经历过这种循环:打开 QGIS,确认图层加载没有,检查坐标系是不是投影坐标系,手动跑一遍缓冲区,再跑空间连接,导出结果,换个区域重复一遍。单次操作不难,但当成百上千条任务需要批处理时,这种手动流程就成了瓶颈。
我最近在折腾一个方向:让 AI Agent 通过自然语言直接驱动 QGIS 完成 GIS 自动化任务。核心思路不是让大模型凭空生成 PyQGIS 脚本然后盲跑,而是把 QGIS Processing 的常用算法封装成结构化工具,让 Agent 在受控的执行环境里选择工具、检查参数、观察结果、继续下一步。这套链路要跑通,第一步就是给 Agent 一个稳定的模型调用通道——我用的是 TaoToken 的统一 Key/API 通道,它兼容 OpenAI 风格的接口协议,配置成本低,适合快速验证 Agent 调用链路。
这篇文章面向需要批量处理空间数据的开发者和 GIS 工程师,从零给出可复制的config.toml与settings.json骨架,演示一条自然语言指令如何触发 QGIS 处理流程,并整理接入过程中最容易踩的坑。读完你可以直接在自己的环境里跑通「自然语言 → Agent → QGIS Processing」这条链路。
2. TaoToken 前置准备:给 Agent 一条稳定的模型通道
2.1 为什么 Agent 需要一个统一通道
GIS Agent 的执行循环里,模型调用频率很高:每一轮工具选择、参数补全、结果判断都要请求一次模型。如果每次都在代码里硬编码不同的接口地址和鉴权方式,后期切换模型或调整参数会非常痛苦。TaoToken 提供的是 OpenAI 兼容的统一入口,你只需要维护一份 Key 和一个 base_url,Agent 侧不用关心底层路由。
对 GIS 场景来说,这一点尤其重要。空间数据处理任务往往步骤长、状态多,Agent 需要频繁地在「选择工具 → 执行 → 观察结果 → 再决策」之间循环。通道稳定、协议统一,才能让这套循环跑得顺畅。
2.2 获取 Key 与确认接入信息
你需要先拿到 API Key,然后确认两个地址:
- 官网入口:https://taotoken.net/?utm_source=taotoken_aicg_blog_end&utm_medium=csdn&utm_campaign=rewrite&utm_content=
- API 基地址:https://taotoken.net/api
API Key 的创建入口在控制台的 API Keys 页面,建议单独为 GIS Agent 项目建一个 Key,方便后续按项目排查调用量。接入文档里有完整的请求格式说明,配置前可以先过一遍。
注意:API 基地址不要带 UTM 参数,只有官网跳转链接才需要带。写配置文件时把 base_url 写成
https://taotoken.net/api即可。
2.3 模型选择建议
GIS Agent 的工具选择环节对模型的指令遵循能力要求较高,因为工具 schema 里包含参数类型、必填项、枚举值等约束。建议先用对话能力较强的模型跑通链路,确认工具调用格式正确后,再根据成本考虑是否切换到更轻量的模型处理简单任务。模型对话页面可以直接测试工具调用格式是否符合预期。
3. 可复制配置:config.toml 与 settings.json 骨架
3.1 config.toml:Agent 主配置
下面这份config.toml是 Agent 侧的骨架配置,包含模型通道、QGIS 执行环境和工具集开关三部分。你可以直接复制后改 Key。
# config.toml - GIS Agent 主配置 [llm] provider = "openai_compatible" base_url = "https://taotoken.net/api" api_key = "sk-your-taotoken-key" model = "your-preferred-model" timeout_seconds = 60 max_retries = 3 [agent] max_tool_rounds = 12 enable_pending_resume = true observation_truncate_chars = 2000 [qgis] processing_backend = "qgis_processing" python_env = "qgis_python" worker_timeout_seconds = 300 allow_overwrite_output = false [toolkits] enabled = ["data_io", "vector_transform", "vector_analysis", "vector_overlay"] raster_enabled = false [workspace] state_file = "./runtime/workspace_state.json" output_dir = "./outputs"几个关键参数说明:
max_tool_rounds控制单次任务最多允许几轮工具调用。GIS 任务步骤多,设太小会导致复杂任务被截断,设太大又可能让 Agent 在错误路径上反复尝试。12 是一个比较稳的起点。
allow_overwrite_output建议先设为false。GIS 输出文件一旦被覆盖,中间结果就找不回来了。让 Agent 在输出路径冲突时进入等待确认状态,比直接覆盖安全得多。
raster_enabled默认关闭。栅格处理对内存和计算资源消耗更大,链路验证阶段先跑矢量任务,确认稳定后再打开。
3.2 settings.json:QGIS 运行时与工具映射
settings.json负责 QGIS Processing 的算法映射和工具参数约束。这份配置决定了 Agent 看到的工具长什么样。
{ "qgis_processing": { "algorithm_mapping": { "buffer_layer": "native:buffer", "clip_layer": "native:clip", "extract_by_location": "native:extractbylocation", "join_by_location": "native:joinattributesbylocation", "reproject_layer": "native:reprojectlayer", "csv_to_points": "native:createpointslayerfromtable" }, "default_output_format": "GPKG" }, "tool_schema": { "buffer_layer": { "required": ["input_layer", "distance"], "optional": ["segments", "dissolve", "output_path"], "distance_unit": "meters", "crs_check": true }, "extract_by_location": { "required": ["input_layer", "intersect_layer", "predicate"], "optional": ["output_path"], "predicate_enum": ["intersects", "contains", "within", "overlaps"] } }, "preflight_rules": { "meter_distance_requires_projected_crs": true, "field_existence_check": true, "empty_result_warning": true } }algorithm_mapping是语义工具名到 QGIS Processing 算法 ID 的映射表。Agent 看到的是buffer_layer这种任务级工具名,实际执行时映射到native:buffer。这层映射的好处是:模型不需要记住 QGIS 的算法 ID 命名规则,后端也能在映射层做参数转换和默认值填充。
preflight_rules里的meter_distance_requires_projected_crs是我强烈建议打开的检查项。地理坐标系下直接做米制缓冲,结果看起来有输出,但空间意义不可靠。让 Agent 在执行前检查 CRS,必要时先触发重投影,能避免大量静默错误。
3.3 环境变量与 Key 管理
不要把 Key 硬编码进config.toml提交到仓库。推荐用环境变量注入:
export TAOTOKEN_API_KEY="sk-your-taotoken-key" export QGIS_PYTHON_PATH="/path/to/qgis/python"然后在config.toml里改成api_key = "${TAOTOKEN_API_KEY}",由加载逻辑做变量替换。这样配置文件可以安全地进版本控制,Key 留在本地环境里。
4. 验证请求:一条自然语言指令触发 QGIS 处理流程
4.1 验证目标
配置写完后,不要急着上复杂任务。先用一条最小可验证指令跑通链路:确认 Agent 能正确选择工具、参数校验通过、QGIS 实际执行、结果文件生成。
我用的验证指令是:
给学校图层做 500 米缓冲区,导出为 GeoPackage。
这条指令包含三个可验证点:工具选择(应该是buffer_layer)、参数提取(距离 500 米)、输出格式(GeoPackage)。
4.2 发起请求的代码骨架
import json import requests API_BASE = "https://taotoken.net/api" API_KEY = "sk-your-taotoken-key" def run_gis_agent(user_input: str, workspace_state: dict): payload = { "model": "your-preferred-model", "messages": [ { "role": "system", "content": "你是 GIS 自动化助手,根据用户目标选择工具并输出结构化调用。" }, { "role": "user", "content": user_input } ], "tools": load_tool_schemas("./settings.json"), "tool_choice": "auto" } headers = { "Authorization": f"Bearer {API_KEY}", "Content-Type": "application/json" } resp = requests.post( f"{API_BASE}/v1/chat/completions", headers=headers, json=payload, timeout=60 ) resp.raise_for_status() return resp.json()load_tool_schemas从settings.json里读取工具定义,转成 OpenAI 工具调用格式。这一步是把语义工具暴露给模型的关键。
4.3 预期返回与执行链路
模型返回的tool_calls里应该包含类似这样的结构:
{ "name": "buffer_layer", "arguments": { "input_layer": "schools", "distance": 500, "output_path": "./outputs/schools_buffer_500m.gpkg" } }拿到这个调用后,Agent 侧的执行流程是:
第一步,semantic validation。检查input_layer和distance是否都存在,distance是否为数值类型。
第二步,preflight check。读取schools图层的 CRS,判断是否为投影坐标系。如果是地理坐标系,触发重投影工具或提示用户确认。
第三步,映射到 QGIS Processing。把buffer_layer映射为native:buffer,参数转换为 QGIS 算法要求的格式。
第四步,执行并观察。调用 QGIS Processing 执行缓冲,读取输出图层的要素数量、CRS、几何类型,写回工作区状态。
第五步,返回 observation。把执行结果作为下一轮模型决策的输入。
4.4 成功结果的判断标准
链路跑通后,你应该能看到:
输出目录下生成了schools_buffer_500m.gpkg文件;工作区状态文件里新增了这条产物的记录,包含来源图层、工具名、参数、要素数量;如果输入图层是地理坐标系,日志里应该有重投影或 CRS 警告的记录。
如果这三项都符合,说明「自然语言 → Agent → QGIS」这条链路已经通了。接下来可以逐步增加任务复杂度,比如在缓冲之后追加空间连接,统计缓冲区内医院数量。
5. 本篇常见错排查
5.1 模型返回的工具名不在映射表里
这是最常见的问题。模型可能返回native:buffer而不是buffer_layer,或者返回一个你根本没定义的语义工具名。
原因通常是工具 schema 描述不够清晰,或者系统提示里没有强调「只能使用已提供的工具」。解决办法是在工具描述里写清楚每个工具的适用场景,并在系统提示里加一句约束:只允许调用 tools 列表中出现的工具名。
如果模型仍然偶发返回底层算法 ID,可以在后端加一层兼容映射,把常见的native:*名称反向映射回语义工具名,同时记录日志,后续优化 schema 描述。
5.2 米制缓冲在地理坐标系下执行
这个问题的表现是:工具调用成功,输出文件也生成了,但缓冲距离明显不对。原因是你在地理坐标系(比如 EPSG:4326)下直接按米做缓冲,QGIS 会把 500 当成度数处理。
排查方法是检查preflight_rules里的meter_distance_requires_projected_crs是否生效。如果生效,Agent 应该在执行前就拦截并提示重投影。如果没有生效,检查settings.json里buffer_layer的crs_check是否为true。
修复方式有两种:让 Agent 自动触发reproject_layer先转到投影坐标系再缓冲;或者在工具 schema 里把distance_unit明确标注为meters,并在描述里提示模型先确认 CRS。
5.3 输出路径冲突导致执行失败
当allow_overwrite_output设为false时,如果输出路径已存在,QGIS Processing 会报错。这是预期行为,但需要 Agent 正确处理。
正确的处理方式是进入 pending 状态,把冲突信息返回给用户,等待确认是否覆盖或更换路径。如果你发现 Agent 直接报错退出而没有进入等待状态,检查enable_pending_resume是否为true,以及执行层是否捕获了输出路径冲突的异常并转成 pending 事件。
5.4 字段名不存在导致筛选失败
用户说「按 name 字段筛选」,但图层里实际字段叫NAME或name_zh。这类问题在 GIS 数据里非常普遍。
排查方法是打开工作区状态文件,确认 Agent 是否在执行前读取了图层字段列表。如果field_existence_check生效,Agent 应该在 preflight 阶段就发现字段不存在,并返回候选字段列表让用户确认。
如果 Agent 直接执行了筛选并返回空结果,说明字段检查没有生效。检查settings.json里对应工具的required字段是否包含字段名参数,以及 preflight 规则是否覆盖了该工具。
5.5 工具调用轮次超限
复杂任务可能需要多轮工具调用:加载数据 → 检查字段 → 重投影 → 缓冲 → 空间连接 → 导出。如果max_tool_rounds设得太小,任务会在中途被截断。
排查方法是看日志里最后一轮的工具调用是什么,以及是否还有未完成的步骤。如果是轮次不够,适当调大max_tool_rounds。但更根本的解决办法是优化工具粒度,把常用组合封装成复合工具,减少单次任务的调用轮次。
5.6 API 请求超时或鉴权失败
如果 Agent 在模型调用阶段就失败,先检查base_url是否写成了https://taotoken.net/api,以及 Key 是否正确注入。鉴权失败通常返回 401,超时通常是网络或timeout_seconds设置过短。
GIS 任务的模型调用可能因为工具 schema 较大而请求体偏大,如果频繁超时,可以适当调大timeout_seconds,或者精简工具 schema 里不必要的描述字段。
6. 继续搭建你的 GIS Agent
链路跑通之后,下一步的扩展方向取决于你的实际任务类型。
如果你主要做矢量数据的批量处理,优先把vector_analysis和vector_overlay这两个 ToolKit 的工具补齐,覆盖缓冲区、裁剪、空间连接、按位置提取这些高频操作。每个工具都需要在settings.json里定义清晰的参数 schema 和 preflight 规则。
如果你需要处理栅格数据,打开raster_enabled,但建议先单独验证栅格工具的调用链路,因为栅格处理对内存和超时更敏感。
如果你打算把 Agent 接入长期运行的编码或自动化流程,可以考虑用 Coding Plan 来管理调用配额和项目级的 Key 分配,避免多个 Agent 实例共用同一个 Key 导致排查困难。
接入过程中遇到工具调用格式或参数校验的问题,可以先到接入文档里对照请求格式,确认tools字段的结构是否符合 OpenAI 兼容规范。模型对话页面适合快速测试单条指令的工具选择结果,不用每次都跑完整执行链路。
这套配置骨架的价值在于:它把「模型通道」「工具映射」「执行校验」三层分开了。你可以单独替换模型、单独调整工具集、单独增加 preflight 规则,而不用改动其他层。GIS 自动化的复杂度主要来自数据状态和空间语义,把这三层理清楚,后面加工具、加规则、加任务类型都会顺畅很多。