
在GIS二次开发这行混了十几年最深的感受不是写过多少代码而是同一个问题真的会被反复问上百遍。今天群里来一个“字段计算器怎么自动编号”明天私聊又来一个“矢量怎么转txt”后天项目现场又有人问“ArcGIS Engine里C#怎么遍历要素”。起初我还会耐心把答案重新打一遍后来慢慢意识到我真正需要的不是再回答一次而是把自己脑子里那套知识、经验、踩坑记录、常用skill整个“克隆”成一个能随叫随到的GIS二次开发砖家。于是今年国庆前我集中一段时间把这个项目做了出来现在把它沉淀成一篇完整的经验贴记录从知识盘点、RAG知识库构建、Skill封装到Agent编排的全过程。这篇内容适合两类人一类是做GIS二次开发、想用AI把自己的经验做成知识库或智能体的开发者另一类是想用RAG和Agent思路把“一个人的经验”转成“一个可运行系统”的从业者。我会把踩过的坑、做的取舍、以及最终效果都摊开讲不美化也不藏私。1. 动手“克隆”之前先想清楚这个砖家到底要会什么1.1 GIS二次开发的知识半径远不止“写代码”很多人一听到GIS二次开发第一反应就是“写代码”——ArcGIS Engine、QGIS插件、CAD二次开发、C#或者Python。但真正在这个行当里泡久了会发现代码只是最外层的东西。中间层是数据理解坐标系、投影、地理数据库、要素类、字段类型、空间参考。底层是业务逻辑地图服务的发布、空间分析的应用、制图综合、数据质检、自动化测试。我一开始做知识盘点时把所有碎片化的经验全部摊在面前粗略归成了四类知识维度典型场景高频问题占比开发框架与接口ArcGIS Engine/C#、PyQGIS、CAD二次开发、SketchUp插件约40%数据与坐标系坐标系转换、字段计算、矢量转txt、空间参考约30%空间分析与模型缓冲区、叠加分析、图中图、专题图约20%工具链与自动化GIS自动化测试、批处理、脚本工具、SSE接口封装约10%这个表格看起来简单但对后面的知识库切片和Skill设计至关重要。因为不同维度的知识承载形式完全不一样接口类知识靠官方文档和代码片段数据类知识靠参数表和实操记录空间分析靠案例和步骤描述自动化测试则要靠一套完整的可执行代码。如果一开始不把这些分清楚后面做RAG切片时就会眉毛胡子一把抓。1.2 给“砖家”定边界哪些问题该回答哪些不该硬答做AI助手最怕的就是什么都敢答。我设计这个砖家时第一步就是给它划出能力半径。我把自己日常遇到的问题分成了三类问答型问题比如“WGS84坐标转CGCS2000用什么参数”“ArcGIS字段计算器怎么用”“缓冲区分析在哪找”。这类问题适合通过RAG检索知识库来回答答案通常来自某一段真实文档或经验记录。工具型问题比如“帮我生成一段自动编号的字段计算器脚本”“把这份Shp转成txt”。这类问题单纯靠检索不够必须封装成可执行的Skill让Agent调用脚本把活儿干完。排错型问题比如“字段计算器报错000539是什么原因”“为什么要素类无法加载”。这类问题需要把“错误现象”和“原因”建立映射知识元数据里必须有专门的报错标签。边界也很明确软件许可怎么采购、具体项目的商业技术选型、以及涉及数据授权的问题一律不硬答。砖家不是万能的明确边界反而能让用户信任。1.3 为什么选择“RAGAgentSkill封装”而不是重训模型这个问题几乎每个聊过这项目的人都会问我。答案很现实重训一个模型需要的数据量、算力和持续维护成本对个人开发者完全不现实。而且GIS二次开发的知识迭代很快ArcGIS 10.x和Pro的写法差异、不同CAD版本的接口变化如果都靠训练进参数里等模型学会可能已经过时了。我最终选择的是三层架构知识问答层基于RAG把整理好的GIS文档、笔记、代码库切成片段做向量化存储。用户提问时先检索最相关的片段再交给大模型组织答案。工具执行层把“字段计算器自动编号”“矢量转txt”“空间分析skill”“自动化测试脚本”这类高频操作封装成可调用的Skill由Agent判断意图后调用而不是让模型凭空生成。评估反馈层每个回答都记录来源片段和调用记录用户可以通过“有用/没用”标记来回流数据持续优化知识库。这个设计的核心逻辑是让模型做它擅长的事理解意图、组织语言、拆解步骤把精确计算和特定工具执行交给确定性代码把经验沉淀交给知识库。模型负责聪明系统负责靠谱。2. 把十几年的零散经验做成“底料”知识库构建才是大头2.1 文档整理从“个人文件夹”到“可用语料”这个项目最耗时间的不是写代码而是整理语料。我翻出了电脑里积压多年的文件夹包括ArcGIS官方文档的读书笔记、往年在论坛和群里回答过的典型问题、自己写过的博客草稿、随手保存的代码片段、以及项目里复用率很高的工具脚本。整理时我有三个原则只留真实使用过的内容。网上东抄西抄的技术文一律不收入宁可少也不能让砖家学到错误习惯。旧版本API要标记。ArcGIS 10.x和ArcGIS Pro的接口差异很大如果混在一起不做版本标注检索出来的答案可能在新环境下根本跑不通。我在每个文档片段头部加了一个version字段这是后面检索过滤的基础。项目中的敏感数据必须脱敏。涉及具体项目坐标、客户数据的片段都要改成虚拟示例数据否则把知识库做成服务后会有隐患。整理完之后我得到的是一批Markdown格式的语料每一份语料围绕一个主题比如“字段计算器自动编号的三种写法”“矢量转txt的ogr实现”“坐标系转换常见参数表”。这些主题就是后续切片的基本单元。2.2 GIS语料的切片策略代码块和参数表不能切碎做RAG的人都知道切片粒度是影响检索效果的关键因素。固定按300字去切对小说和新闻没问题但GIS技术文档特别容易翻车。我遇到过最典型的情况一个Python代码块被从中间拦腰截断前半段是import arcpy后半段是表达式和Code Block两个片段被单独检索出来后模型组合答案时拼出过语法错误的代码。还有参数说明表字段名和解释被切成两半检索回来只有半张表。因为踩过这些坑我不再切固定长度而是按语义块切标题对齐每个二级标题或三级标题下的内容作为一个基础块。代码块完整保留一段代码从起始标记到结束标记整体作为一个片段哪怕它有一两百行也不拆。参数表整体保留表格如果过长至少保证每一行完整不把一个字段的参数名和说明拆开。片段间设置重叠每个片段尾部保留前一个片段的最后一行方便模型理解上下文衔接。这个策略在GIS场景下很有效。后面我测试过纯固定长度切片时的答案可用率大概在六成左右改成语义块切片后代码类问题的答案可用率明显提升到八成以上。2.3 混合检索纯向量在GIS术语上很容易翻车刚开始我只用了向量检索因为实现最快。但很快发现GIS术语会让embedding模型犯迷糊。最经典的例子是“缓冲区”。在GIS领域“缓冲区”指的是空间分析里的Buffer也就是围着点、线、面做固定距离的扩展区域。但通用embedding模型训练时见过大量的“内存缓冲区”“数据缓冲区”语义空间里“缓冲区”这个token更接近通用的计算概念。结果我检索“如何做缓冲区分析”时召回的片段里混进了不少关于内存优化、缓存清理的内容反而把真正的Buffer操作指南挤下去了。对策就是用混合检索而不是纯向量检索。我同时跑了BM25关键词检索和向量语义检索再把两路结果用轻量级Rerank模型做一次融合排序。BM25能保证“缓冲区”、“Buffer”、“字段计算器”这类GIS强术语被精确命中向量检索则能处理“我想把线的周边扩一圈”这种口语化表达。为了进一步减少歧义我还在检索入口做了一个领域词表扩充如果用户问题里出现了“图层”“要素”“矢量”“栅格”“坐标系”“投影”“拓扑”“裁剪”等词就自动追加对应的英文术语和同义表达让BM25阶段的召回更加稳定。2.4 元数据标签让“砖家”知道每个片段属于哪个场景纯文本检索会让模型在组织答案时缺少场景意识。比如同样是“遍历要素”的代码C#版ArcGIS Engine的写法、Python版ArcPy的写法、QGIS里PyQGIS的写法完全不一样。如果用户问的是“ArcGIS Engine C#遍历要素”结果检索回来的全是PyQGIS片段模型就算再聪明也只能答非所问。所以我在切片时强制给每个片段打上元数据标签字段取值示例用途frameworkArcGIS Engine / QGIS / CAD / 通用过滤开发环境languageC# / Python / JavaScript过滤编程语言knowledge_typeinterface / data / spatial / tool对应知识维度function字段计算 / 坐标转换 / 矢量转txt / 自动化测试功能域匹配error_code000539 / 000366排错类问题专用这套标签在排错场景里尤其好用。用户报一串错误码BM25可以直接精确匹配到error_code字段比让模型自己理解错误描述的原因靠谱得多。后来我把这套标签系统作为元数据Schema一直延续到Skill设计里让每个Skill也自带“我要处理的功能域”和“我能接受的输入参数”整体架构非常统一。3. 让砖家不仅能答还能干活Skill体系的落地3.1 先盘高频技能从热搜和用户问题里找Skill知识库解决的是“知道什么”但很多GIS二次开发问题实际上是要“做出来”。用户在群里问“字段计算器怎么自动编号”他要的不是一篇理论而是一段能粘贴就能跑的脚本。所以我决定把高频操作封装成Skill让Agent识别到这类需求时不只是检索回答而是调用确定的代码。我结合平时群里和项目里的高频问题筛了第一批SkillSkill名称触发意图底层实现典型问题字段计算器自动编号自动编号、按字段加前缀、根据OBJECTID生成序号ArcPyCalculateField Code Block如何让编号按“CD-000001”格式递增矢量转txt把shp/gdb导出为txt、按字段生成文本文件GDAL/OGR Python矢量数据如何转txt字段分隔符怎么控制空间分析Buffer缓冲区分析、周边多少米范围geopandas / ArcPy Buffer对点要素做500米缓冲区属性怎么保留GIS自动化测试回归测试、批量验证GIS功能pytest QGIS/ArcPy脚本如何对GIS软件功能做自动化测试图中图制作在一个图框内添加局部放大图ArcGIS制图视图主图旁边放一个小比例尺的局部图首批Skill不求多只求每一个都能稳定跑通。我宁可让砖家先会5个非常靠谱的绝活也不要堆20个经常出错的半成品。3.2 字段计算器Skill把“怎么自动编号”变成可执行的脚本这是整个项目里用户问得最多的一个点所以我把它做成第一个完整Skill。核心逻辑很简单用户描述想要什么样的编号规则Agent解析出前缀和位数然后生成对应的ArcPy脚本。以最常用的“前缀序号”为例脚本是这样的# -*- coding: utf-8 -*- import arcpy def auto_code(fid): prefix CD- # fid 是要素类的 OBJECTID 字段值从1开始 return {}{:06d}.format(prefix, fid) code_block def auto_code(fid): prefix CD- return {}{:06d}.format(prefix, fid) arcpy.CalculateField_management( in_table你的要素类, field编号, expressionauto_code(!OBJECTID!), expression_typePYTHON3, code_blockcode_block )这条链路里Agent不是自己脑补代码而是从知识库里检索“字段计算器自动编号”相关片段再把它组装进固定模板里最后输出给用户。这样做的好处是输出脚本永远基于我验证过的代码而不是模型临场发挥。对于更复杂的编号规则比如“每个乡镇开头不同”“按年份顺序号”Skill会先向用户收集年份、前缀字段等参数再动态拼expression。3.3 矢量转txt Skill输入输出标准化的例子第二个我封装的是“矢量转txt”。很多非GIS专业的人拿到shp后不知道怎么看属性最直接的需求就是把属性导成txt表格。这个Skill的实现我优先用GDAL/OGR因为跨平台而且不依赖ArcGIS许可# -*- coding: utf-8 -*- from osgeo import ogr ds ogr.Open(input_shp) layer ds.GetLayer(0) feature layer.GetNextFeature() fields [field.GetName() for field in layer.GetLayerDefn()] with open(output_txt, w, encodingutf-8) as f: f.write(\t.join(fields) \n) while feature: values [str(feature.GetField(name)).replace(\n, ) for name in fields] f.write(\t.join(values) \n) feature layer.GetNextFeature()这个Skill强调三件事。第一输出编码统一用UTF-8否则在国产生态下打开txt会乱码。第二字段分隔符默认是Tab因为字段值里可能包含逗号用逗号分隔容易被Excel拆错列。第三转换前必须检查要素类的空间参考信息要在输出的开头加一行元数据注释记录坐标系和要素数量免得用户拿到txt后不知道这份数据是哪来的。这属于我平时做项目时踩过坑后的教训直接写进了Skill的默认行为。3.4 用SSE流式接口封装智能体输出从“等待转圈”到“流式返回”GIS二次开发的很多任务都偏慢尤其是大范围空间分析或者大批量要素计算。如果Agent工具调用耗时十秒以上前端却一直转圈体验非常差。所以这个项目里我特意把智能体的输出接口用SSEServer-Sent Events做了流式封装让用户看到回答是“一字一句蹦出来”的心理等待感大幅降低。后端我用FastAPI实现一个简单的流式生成接口from fastapi import FastAPI from fastapi.responses import StreamingResponse import asyncio, json app FastAPI() async def agent_stream(question: str): # 这里只是示例实际会调用检索和Skill执行 for chunk in [这是一段, 关于, 字段计算器, 自动编号, 的说明]: payload json.dumps({delta: chunk, event: answer}, ensure_asciiFalse) yield fdata: {payload}\n\n await asyncio.sleep(0.05) app.get(/api/chat) async def chat(question: str): return StreamingResponse(agent_stream(question), media_typetext/event-stream)前端解析SSE时很多人会用现成的EventSource库但EventSource只能发GET请求而且不好携带自定义Header。我实际项目中更建议用fetch ReadableStream手动解析核心是按data:前缀识别消息行遇到空行表示一条消息结束。需要注意SSE协议要求每条消息以\n\n结尾服务端在生成中文时不要因为编码问题丢掉结尾空行否则前端流会卡住。这也是我踩过的一个真实坑前端解析器处理到一半停住不动查了很久才定位到是空行缺失。同一个流里我还会把不同事件类型用event字段区分回答文本用event: answer工具开始执行用event: tool工具执行结果用event: tool_result。前端收到tool事件时就渲染一个“正在调用矢量转txt工具…”的提示条整体体验很像一个专业人员边想边说还会停下来操作一下再继续。4. Agent编排和可观测性让“砖家”保持稳定发挥4.1 框架选型Dify、DeerFlow还是自研轻量路线做Agent部分之前我认真对比过Dify、DeerFlow这一类平台也收集了社区里关于它们二次开发的讨论。最后没有直接用全套平台原因比较务实方案优势劣势我的决策Dify可视化工作流、有插件机制、上手快自定义SSE接口封装受限深层改造成本高作为参考不直接依赖DeerFlowAgent工作流设计灵活支持复杂编排更偏通用业务GIS特定Skill扩展需要自己写参考它的状态管理思想自研轻量编排完全可控SSE和Skill可以按需写需要自己维护逻辑核心链路自研我做这个项目的初衷是为了长期维护和复用所以宁可前期多花点时间也要把核心链路掌握在自己手里。自研的编排层很薄就是“意图识别 → 知识检索 → Skill分发 → 结果组装 → 流式输出”五步每一层都留出清晰的日志接口。等哪一天发现某个环节用成熟框架更省力再替换也不迟。4.2 System Prompt的设计防止模型忘了GIS场景很多人觉得System Prompt就是写个“你是GIS专家”其实远远不够。我写的System Prompt里有几条是花了很长时间打磨出来的硬约束坐标系参数必须从知识库的权威参数表中引用禁止模型自行推演或编造。尤其在“WGS84转CGCS2000”“北京54转西安80”这类问题上模型很容易一本正经给出错误七参数必须用检索片段约束。代码回答必须注明适用的GIS版本和语言环境比如ArcGIS Pro还是ArcMap 10.xPython版本是2还是3。因为GIS代码在版本间差异实在太大。当用户问题模糊到足以产生多种解释时优先反问不要猜。比如用户只问“怎么转换”就要问清楚是空间参考转换、格式转换还是字段类型转换。看起来多了一步但实际上能避免答错方向。上下文长度控制也一样重要。GIS问题的检索片段经常很长我设置了一个规则单轮回答最多引用4个关联片段每个片段超过一定长度就做摘要压缩优先保留代码块和参数表。否则上下文一大模型容易迷失重点回答又长又空。这一步我给不同模型做过多组实测固定了比较稳妥的参数比你直接丢二十个检索结果下去要稳得多。4.3 评估集与日志用真实问题检验“砖家”没有评估的AI助手都是盲人摸象。我从这几个月的群聊记录、私聊记录和项目交流里整理了100个真实问题作为测试集按“问答型”“工具型”“排错型”分类每个问题都标注了预期答案的要点。评估维度我设了四个正确性关键结论是否专业准确有没有编造。完整性能不能覆盖用户真实操作场景的所有步骤。可操作性给出的代码和步骤是否直接能跑。术语准确度GIS专业术语和英文缩写有没有用错。每次Agent回答完系统都会把“用户问题、召回片段ID、是否调用Skill、最终回答文本”整条记到日志里。我会定期挑出失败案例看看是知识库里缺文档还是切片有问题还是Skill触发条件没写清楚。这个反馈闭环是整个项目后期质量提升最快的机制没有之一。在实测过程中我还专门设计过一个评估项对同一个问题连续问十次观察答案的稳定性。因为LLM有随机性如果同一个问题十次给的答案细节都不一样用户很难信任。针对重点问题我会把稳定答案“固定”下来作为预设回答模板。5. 踩坑实录“克隆”过程中的五类翻车现场5.1 最容易翻车的是坐标系和投影参数模型会一本正经地编造大模型的通性就是“看似自信地胡说八道”在坐标系问题上报错率极高。我问砖家“WGS84经纬度转到CGCS2000高斯投影应该用什么中央经线”它第一次回答时直接给了一串精确到小数点后七位的参数看起来特别专业实际上是把某个地区的通用参数当成了全国通用。这类错误最危险因为非专业人士根本看不出问题。对策就是我在前面提到的“禁区文档”机制把常见的坐标系参数、EPSG编号、中央经线规则、七参数适用范围整理成独立的权威表并在System Prompt里明确写“坐标系参数只能引用知识库中的权威参数表如果检索结果里没有就直接说不知道禁止根据经验推算”。模型一旦被禁止自行推算就会乖乖回退到知识检索犯错概率大幅下降。5.2 代码块被切片腰斩Skill示例跑不起来这个问题前面提过但值得单独展开说因为它实在太典型了。固定长度切片遇到几十行甚至上百行的Python脚本时经常发生“前半段一个片段后半段另一个片段”的情况。检索时如果只召回后半段模型根本没有Context就会根据自己的理解补全结果补出来的是个错误接口。我最终采用“代码块边界优先”的切分规则识别到代码块开始标记时整个代码块一直到结束标记都作为不可分割的单元如果代码块过长则在其内部按函数或类定义二次切分并且每个子片段保留“这段代码属于某个完整脚本”的头尾提示。这样模型组合代码时至少知道每个片段是完整函数而不是拼不出上下文的残句。5.3 同名术语混淆缓冲区、图层、脚本工具的歧义除了“缓冲区”和内存缓冲区的歧义GIS里还有不少同名术语。“工具”既可以指ArcToolbox里的地理处理工具也可以指Python脚本工具还可以是CAD二次开发里的自定义命令。“图层”在地图文档里指Layer对象在数据库里却可能是图层表。处理这类歧义我用了两个办法领域词表与同义词扩展用户在问题中提到“Buffer”“缓冲区”时同时检索GIS空间分析方向的知识片段。主动反问确认如果用户的问题太短像“怎么建缓冲区”这种连操作对象都没有说清的Agent会反问一句“您是要对点、线还是面要素创建Buffer是在ArcGIS还是QGIS里操作”看似多了一次交互但能避掉大量错误输出。5.4 版本割裂ArcGIS 10.x和Pro写法差异混在一起GIS开发者都知道ArcGIS 10.x的ArcPy接口和ArcGIS Pro里的ArcPy接口还有老的ArcGIS Engine C/C#接口三者之间有兼容问题。比如字段计算器在不同版本里的expression_type参数就不一样Reader、Engine、Pro对栅格数据的对象模型也各有不同。我在知识库里加了version元数据后检索阶段就可以加一层过滤如果用户明确说了“ArcGIS Pro”就只召回打了version: pro标记的片段如果用户没有指定版本知识库返回结果时会优先给最新版本同时在答案开头补一句“以下代码以ArcGIS Pro 3.x为例10.x用户需要注意xxx差异”。这个细节非常有用因为GIS现场环境往往很旧你自己用Pro写的代码拿到10.2跑不通是真实项目里最常见的“二次开发劝退瞬间”。5.5 多技能需求的问题单次检索必然顾此失彼最后一个坑也是最容易理解但最难解决的真实问题是复合的。比如“把投影后的面要素按行政区批量导出为txt属性表并自动编号”这一个问题涉及“投影”“面要素查询”“按字段分组”“矢量转txt”“字段计算器自动编号”五个技能点。如果只做单次RAG检索即使召回了一堆片段模型也没有能力把它们串成一个可执行流程。这个问题的解法是让Agent先“拆解计划”。收到复合问题时先输出一个执行计划“第1步检查要素类投影是否为目标坐标系第2步按行政区字段分组第3步每组导出txt第4步在导出的txt里按组内顺序编号。”然后再按计划逐步调用对应Skill。这种显式拆解不仅让用户看得懂也让Agent每一步的上下文不会超载。由于是确定性代码在跑每一步最后合成出来的完整流程比让模型从头到尾编一段大代码要可靠得多。6. 回看效果与后续把“砖家”做成长期资产6.1 三个实测问题验证“砖家”到底有多大价值项目初步完成后我拿三个真实问题做了验收测试。第一个问题是“字段计算器怎么做自动编号且带前缀CD-”。砖家先识别出这是字段计算器Skill检索到我的模板然后生成了上一章那段ArcPy脚本并补充说明OBJECTID需要换成实际字段名字段类型需要是文本否则计算会报类型不匹配。答案可用。第二个问题是“GIS矢量数据如何转txt”。砖家调用了矢量转txt Skill先确认了输入数据路径、输出路径和编码方式然后给出ogr脚本并在输出中说明了坐标系和要素数量会被写入txt文件头部备注。答案可用。第三个问题是“ArcGIS Engine基于C#遍历属性表并输出字段值”。这次没触发Skill走的是RAG检索。砖家检索到了我在元数据里标记为framework: ArcGIS Engine, language: C#的代码片段给出了IFeatureCursor遍历的标准写法还提醒用户需要释放COM对象引用避免内存泄漏。这一点本来就是我在知识库里特别强调的重点召回后模型也准确带了出来。答案可用。整体测试下来我整理的100道题里答案“明显可用”的大约在八成左右剩下的两成里有的是缺上下文有的是知识库本身没覆盖到。对一个个人知识助手来说这个结果我觉得已经达到了“能实际拿去帮人解决问题”的水平。6.2 哪些经验不能“克隆”过去这个砖家能迁移的是技术知识和常规技能但也有明显局限。我的项目代码库里那些业务算法、特定项目的数据规则、以及多年积累的非技术判断并没有被完整克隆也不需要克隆。每个想复刻这个方案的人都需要基于自己的经验沉淀出自己的语料而不是直接搬一套现成的GIS知识库。还有一个建议是别急着做大而全。我一开始也雄心勃勃想把CAD、SketchUp、NX、PDMS二次开发全部塞进来但后来发现知识不精反而拉低质量。现在这个砖家专注在GIS生态内的几个高频场景每个场景都能做到可用。能力半径小一点可靠度就高一点。6.3 给想复制的同行几个建议如果你也想搞一个自己的“数字砖家”我的建议是把流程倒过来先攒50个高质量问题和答案再做知识库再设计两三个Skill最后才碰Agent编排。顺序反了的话很容易把大量时间花在框架上结果没有足够的高质量知识去喂它系统就会显得很“空”。这套方案对我来说最直观的价值是以前我回答问题是在消耗时间现在回答问题是给知识库喂料每答一次都在强化这个砖家。国庆前把这个项目收尾也算是给自己入行GIS二次开发这么多年的一份阶段性献礼。接下来我计划每个月做一次知识库更新把新的踩坑记录和群聊里的高频新问题吸收进去再把Skill库里增加“自动化测试”和“图中图制作”两个能力的复杂变体。最后再分享一个小技巧如果你也准备用SSE流式接口把Agent能力开放出来记得在服务端做日志时把每条回答对应的知识来源一起打出来。这个日志在后期评估时比什么功能都好用它能让每一个错误定位到具体是知识缺失、检索失败还是模型组织不当。很多AI助手的项目死在“不知道怎么改进”原因就是没留这双眼睛。