资讯动态

用LangChain重构农业问答:从RAG到Agent的工具调用与多步推理实践

发布时间:2026/10/20 3:36:28 来源:尧图企业网站定制
去年帮一位做农业信息化的朋友诊断他们那套智能问答系统农户在后台问了一句“我家大棚草莓叶子边缘发黑还卷边该打什么药”系统查了半天资料回了一段教科书式的病害科普。结果农户再没说话。回访才知道那位农户要的不是“可能是什么病”的科普而是“现在到底该不该打药、打什么、兑多少水、什么时候打”——这背后得把近期阴雨天气、棚内温湿度、作物生长阶段、病害图谱、农药登记信息串起来做综合判断是一个典型的多步推理场景。这种场景正好是普通RAG问答撑不起来的。单轮检索只能给出“像什么病”却回答不了“基于我家这块地当前的状态下一步该干什么”。所以我花了几周时间用langchain把一个农业问答机器人重构成带工具调用的Agent让它能查气象、查地块档案、查栽培日历、检索病虫害资料最后生成可执行的农事建议。这篇文章就把整套设计和实现过程完整记录下来从工具链拆解、记忆管理、混合检索到实际部署时的成本与踩坑一次讲清楚。1. 农业问答为什么必须“会调用工具”而不是“背课文”1.1 一个典型农事问题背后的五步推理链还是回到开头那个例子。“草莓叶子边缘发黑、卷边”这句话抛给模型如果只是普通聊天机器人它大概率会给出灰霉病、缺钙、药害三种可能然后罗列每种病的特征让农户自己对号入座。这话没错但等于没说。真正农技人员处理这个问题的思路是一条清晰的推理链先确认最近一周的天气和棚内湿度判断是不是高湿环境再调出这个农户地块的档案看土壤pH和钙含量排除缺钙然后定位到草莓当前所处的花果期确认哪些药剂在这个阶段允许使用最后从病虫害图鉴里检索匹配症状的病害资料找出登记的防治方案再换算成农户能操作的剂量。这五步里天气数据、地块档案、作物生育阶段、病害图谱、农药登记信息分布在完全不同的数据源里。做Agent之前我的做法是把这些资料全部塞进向量库让模型一次检索全给出来。结果可想而知检索结果混杂着适用于不同地域、不同季节、不同生育期的内容模型被互相矛盾的资料干扰给出的答案经常前后打架。换成人来看就很好理解了。一个刚入行的农技员也不可能靠背一整本《植保手册》就下地看病。他得先问农户要地块信息再查当地气象再翻图谱比对最后查药品登记表。AI要做的是模拟这套工作流程而不是把整个图书馆塞进一个提示词里。这就是Agent和聊天机器人的本质区别聊天机器人靠“记忆”回答Agent靠“行动”回答。1.2 农业数据天生分散这是Agent化的最大理由农业生产链路里的数据源非常零散。气象数据在气象服务接口土壤检测报告在检测机构出具的PDF里栽培技术规程在农技推广部门的文档里农药登记信息在监管数据库里农资价格又在各个经销商手里。没有任何一个模型预训练的时候能把所有实时数据装进去也没有任何一次检索能把所有零散数据一次性拉到上下文里。所以设计Agent时我给自己定的原则很简单**凡是会随时变化的数据一律走工具实时查询凡是相对固定的知识才走知识库检索。**气象、价格、地块档案属于前者栽培技术、病害特征、农药登记信息属于后者。这条原则帮我省了大量token也大幅减少了模型输出过时信息的概率。数据分散造成的另一个问题是不同数据源之间经常有隐性的关联。比如气象数据里的一周连续阴雨和病害图谱里“高湿环境诱发灰霉病”的知识点单独看都是孤立信息但Agent把它们串起来之后就能给出“未来三天继续阴雨不建议立即打药先通风降湿”这种真正有用的农事判断。这种跨数据源的组合推理是纯RAG系统很难做到的也是我坚持用Agent架构的根本原因。1.3 三种用户、三种问法背后得是同一套大脑农业助手的用户不只有农户。我在需求调研阶段整理了三个典型角色农户、农技员、农资店经营者。农户提问最口语化“叶子黄了用什么药好使”是典型问法农技员会问专业问题比如“这茬番茄的灰霉病病原体拉丁名是什么防治适期如何确定”农资店经营者则关心“哪个成分的药剂在当地销量好、价格稳定”。三种人的问法差异很大但底层需要的是同一套工具和知识库。Agent的好处在于它可以先识别提问者的身份和意图再决定用哪些工具、以什么粒度的语言回答。给农户的回答要直接、可操作给农技员的回答要有学名、有依据给农资店经营者的回答要带价格区间和登记信息。我实现这个能力的方式是给Agent的提示词里加了一段“用户身份识别”的规则让它先判断来问问题的人更像哪类用户再去调用工具。同时在检索知识的环节会对同一个知识点做“通俗版”和“专业版”两套摘要存储Agent根据用户类型选择读取哪一版。这个小设计非常有效后面实测阶段也验证了它能显著降低“答案专业但农户看不懂”的投诉率。2. Agent的工具集拆解我给农业助手配了五类“手”2.1 五类工具一览与设计初衷工具是Agent能“动手干活”的关键。我最初只配了两个工具——天气查询和病虫害检索跑通后很快发现不够用没有地块档案模型不知道“这块地种的是什么”没有作物日历模型分不清当前是苗期还是花果期没有农资行情模型推荐的药剂价格完全脱离当地市场。最终沉淀下来的工具分五类每一类解决一个明确的问题。我用表格梳理一下工具类别输入输出典型使用场景天气查询地区、日期温度、降水、风力、湿度摘要判断气候是否利于病害扩散地块档案查询地块ID或用户ID土壤类型、pH、种植作物、播种日期、历史病害一切水肥和用药建议的基础作物栽培日历作物、地区、当前日期当前生育阶段及对应农事操作确定喷药窗口、施肥时机病虫害图鉴检索症状关键词或图片描述候选病害特征、防治要点症状识别和诊断农资行情查询地区、农资品类附近经销商报价区间药剂和肥料采购建议工具不是越多越好每个工具都必须有明确的“输入边界”。给模型一个输入边界清晰的工具它才知道什么时候该调、该传什么参数。比如天气查询工具要求传入“地区”和“日期”模型在回答“明天能不能打药”时就会自动把“明天”转成具体日期再去查而不是凭空猜。2.2 用langchain定义工具参数Schema和描述文档的决定性作用langchain里定义一个工具非常方便核心是tool装饰器。但真正的坑在函数签名和docstring。Agent用的模型是靠函数名、参数类型和docstring来理解“这个工具是干什么的”描述写得模糊模型就会瞎调或漏调。我写工具时有一个固定套路参数名用全称docstring里写清楚“什么时候用”、“输入是什么格式”、“输出是什么样子”。下面是一个天气查询工具的示例from langchain.tools import tool tool def query_weather(region: str, query_date: str) - str: 查询指定农业区域在指定日期的天气概况。 适用场景判断是否适合喷药、灌溉、露天农事操作 需要结合近期降水、风速、温度来判断病害发生风险时使用。 参数 region: 农业区域名称如“城关镇”“柳河乡”尽量具体 query_date: 日期格式 YYYY-MM-DD可延后或提前。 返回 一段中文摘要包含最高/最低温度、降水量、平均风速、 相对湿度等关键信息已按可读性格式化。 weather_data weather_service.fetch(region, query_date) return format_weather_summary(weather_data)这段代码看着简单但docstring里的每一句话都在给模型做“行为训练”。我测试过一版把docstring写成“查询天气”四个字的工具结果模型在需要查土壤湿度时也能强行调用这个天气工具输出完全没法看。后来把所有工具的docstring都改成“适用场景参数说明返回说明”三段式误调用的次数立刻下降了一大截。还有一点容易被忽略工具返回的内容应该尽量精简。模型上下文窗口有限一次对话可能调用多个工具如果每个工具都返回几十KB的原始JSON后面的推理质量会明显下降。我的做法是每个工具在return之前都先用本地函数做一次“降噪”把原始数据压成两三句摘要只保留跟农事决策相关的那几个字段跟决策无关的数据直接丢弃。2.3 工具调用失败时的优雅降级任何生产环境里的工具都会失败气象接口可能超时数据库可能连不上图鉴服务可能返回空结果。Agent设计里最容易忽略的就是失败处理。模型在工具超时之后陷入沉默或者在失败后反复重试同一个工具这两种情况我都遇到过。我现在的处理分三层。第一层工具内部做异常捕获统一返回一个标准失败文本比如“天气查询失败接口超时请稍后再试”而不是直接抛异常让Agent崩溃。第二层在AgentExecutor里设置max_iterations参数限制最大迭代轮数防止模型卡在“调工具-失败-再调工具”的死循环里。第三层在提示词里写死兜底话术如果某个工具数据拿不到不要硬编答案直接告诉用户“当前无法获取XX数据建议咨询当地农技站”。注意第三层兜底话术非常重要。农业场景万一给错信息轻则白跑一趟重则影响收成。我宁可让Agent承认自己不知道也不要让它编一个看着合理的答案。3. 记忆管理让Agent记往地块又不被闲聊拖垮3.1 短期记忆农事追问不能把前文冲掉农业对话有一个特点农户的问题经常是一连串追问。“叶子发黑怎么办”——“需要先确认叶片是干枯还是水渍状”——“是水渍状还带一层灰毛”——“最近有没有连续阴雨”这一串对话里最开始的“叶子发黑”被后面几轮信息不断修正如果模型没记住前文后面回答就完全对不上。我用的是ConversationBufferWindowMemory维护最近几轮对话的完整历史只保留窗口内的信息。窗口大小经过测试取6轮最合适少于6轮农户追问的信息容易被截断多于6轮token开销变大而且对话主题飘远时模型容易被早期无关内容干扰。但窗口记忆有个副作用Agent在调用工具时会把整个对话历史塞给模型如果中间夹着家长里短的闲聊会稀释工具调用指令的权重。所以我在提示词里加了一条规则窗口记忆只作为背景信息当前轮次的用户问题才是需要响应的主线。这个规则虽然简单但确实让Agent在长对话中的工具调用准确率稳定了很多。3.2 长期记忆给每块地建一本“病历档案”短期记忆只管当前一次会话但农事决策最需要的其实是一个农户地块长期的数据积累去年这块地得过什么病、土壤pH是多少、上一茬种的是什么。这些信息如果每次都要农户重新说一遍体验就很糟糕而且农户自己也未必记得清。我设计了一个地块档案模块本质上是一张本地数据库表字段包括地块编号、种植作物、品种、播种日期、土壤类型、pH值、有机质含量、近年病害记录、最近农事操作记录。每次Agent回答完问题之后如果发现对话中出现了可更新的地块信息就会调用一个内部维护工具把新信息写回档案。这套设计的效果在实测中非常明显。有一个模拟农户连续问了三次同一块地的番茄问题第一次问完土壤偏酸后第二次再问“为什么叶片发黄”Agent直接调出了“该地块土壤pH 5.8低于番茄适宜范围”的档案给出的答案直接锁定在酸性土壤引起的养分吸收障碍上完全没有绕弯子。这个能力让整套系统的回答命中率上了一个台阶。3.3 从单题回答到任务规划Plan-and-Execute实践普通问答是“一个问题一个答案”但农业里很多需求是“一个目标一串操作”。比如“帮我安排这周大棚的农事计划”就需要先查未来一周天气再查当前草莓的生育阶段再查该阶段的水肥和植保要点最后还要结合地块档案给出具体哪天浇水、哪天打药。这类多步任务用基础的ReAct也能做但Agent容易东一步西一步步骤之间没有清晰的顺序依赖。我后来改成了Plan-and-Execute的思路先让模型把用户的大目标拆成一个小步骤列表再逐步执行。langchain里可以在提示词中要求模型先输出计划再开始调用工具。实际运行效果比直接ReAct稳定得多。比如“安排一周农事计划”Agent会先输出一个计划草稿查询天气、查询作物日历、查询地块档案、生成计划。然后按顺序执行每完成一步就把结果放到上下文里最后汇总生成一张按天排列的农事任务表。如果某个环节数据缺失它会在计划阶段就标出来而不是闷头一路执行到底。这也是我觉得Agent比传统流程引擎聪明的地方——它能在中途根据实际情况动态调整后面的步骤而不是死板地走固定流程。4. 知识库与混合检索回答必须带“证据链”才敢给农户看4.1 把PDF切成“有身份”的卡片而不是无脑分块做农业RAG知识库第一步就把很多文档切碎重新组装切成一个个固定长度的chunk然后一股脑灌进向量库。这种做法的后果是一条关于“草莓灰霉病防治”的知识被拦腰截断病征描述、发病条件、药剂方案各自散落在不同的向量片段里检索的时候经常只能捞到一半答案自然残缺不全。我重新设计了一套文档加工流程核心思想是“按文档自身的语义结构切分而不是按字符数量切分”。农业技术资料一般都有非常清晰的结构比如“症状识别—发病规律—防治方法—药剂选择”这四段。我用文档解析工具先把PDF转成带标题结构的文本识别出各级标题和表格然后以“病害/品种/技术主题”为单位把每个完整主题切成一张知识卡片卡片上保留主题名称、适用作物、适用地区、适用季节、正文内容和来源页码。切好之后每张卡片的元数据也要填完整作物种类、病害名称、地区标签、季节标签、文档来源。这些元数据后面做属性过滤时至关重要——没有它们向量检索就只是在大杂烩里捞相似文本有了它们才能做到“只在本作物、本地区、本季节的卡片里找答案”。4.2 向量检索关键词检索属性过滤的混合策略在做纯向量检索的时候我发现一个很典型的问题农户问“番茄叶子卷了”向量检索会把“番茄卷叶病毒病”“番茄叶片卷曲生理性原因”“辣椒卷叶病”都捞出来因为语义上都沾边。但如果先按元数据过滤掉“作物辣椒”的卡片再在番茄的卡片里做检索精确度就能提高一截。三种检索方式的定位如下检索方式最适合解决什么薄弱点向量语义检索症状描述口语化、模糊匹配对精确属性不敏感关键词精确检索查询明确的登记证号、药剂成分、专有名词无法理解语义变体元数据属性过滤限定作物、地区、季节、发病部位单独使用无法回答开放问题我的落地做法是“先用属性过滤缩小范围再混合向量和关键词结果最后按相关度加权合并”。比如“草莓 叶子边缘发黑”先按作物属性“草莓”过滤出所有草莓相关的卡片剩下卡片里做向量检索和关键词检索最后把两类命中的结果按分数合并排序。合并的具体比例我调了几版最终稳定在向量召回占七成、关键词命中占三成。因为农业症状描述太口语化农户很少在问题里提到精确的术语向量语义匹配的作用远比生硬的关键词匹配重要但关键词匹配能够在向量检索漏掉“登记证号”“有效成分”这类硬信息时兜底。4.3 防“药方幻觉”的证据链校验农业问答最危险的就是“药方幻觉”。模型在检索资料时看到一个病害名再用自己预训练知识里的药剂知识补全细节生成一个看起来合理、但实际上没登记、剂量也不对的使用方案。这种事我绝不允许发生——农业生产中用药错误不是一句空话会让农户一季的劳动打水漂。我的防线有三层。第一层所有药剂推荐必须来自知识库原文模型在输出用药建议时必须附上来源文档的标题和页码第二层我把农药登记信息做成一个独立的结构化数据表包含登记作物、防治对象、推荐剂量、安全间隔期Agent在给出任何用药建议之前必须先查这张表确保推荐的药剂在该作物和病害上有登记记录第三层在提示词里写死规则如果没有检索到明确登记信息只描述病害特征和防治原则不推荐具体药剂宁可让农户去咨询农技站。这套“证据链校验”机制上线之后我专门做了一轮压力测试故意问一些边缘的、冷门的病害组合看Agent会不会编造药剂名。效果非常理想——它开始频繁输出“根据现有资料未检索到针对该病害的登记用药方案建议联系当地农技站”这句话了。看似保守但这恰恰是农业助手最该有的态度。5. 实测翻车现场单位换算、地域物候和脏数据怎么兜底5.1 单位换算翻车Agent不知道1亩地有多少平方米第一次联调的时候我让测试Agent对着一块3亩地的草莓园推荐水肥方案。数据源里写的湿度监测值是“土壤相对含水量62%”栽培手册里建议的是“开花期土壤相对含水量60%-70%”地块档案里写的是“灌溉面积2.5亩”。Agent算了一次用水量输出的推荐值足足差了三分之一。排查之后发现问题很简单手册里的滴灌用量单位是“吨/亩”但地块档案里的面积单位是“平方米”Agent做换算时按1000平方米当一亩算了实际上1亩约等于666.67平方米。这个坑非常典型。大模型对数字单位转换并不总是可靠的尤其面对农业里五花八门的单位亩、公顷、平方米、吨、千克、克/株、升/亩。我的处理方式是在工具层彻底统一单位所有结构化数据从接口拿回来后先经过一层标准化转换再给模型。地块面积统一转成“亩”水量统一转成“吨/亩”叶面肥浓度统一转成“克/升”让模型永远只需要处理一套单位不需要自己做换算。5.2 地域物候差异同一个品种在不同地区方案完全不同还有一次实测一个模拟用户问“春小麦现在可以播种了吗”Agent按华北平原的物候期给出了“可以开始播种”的答复。但用户的地块在东北高寒地区那里这个时候地温根本不够。问题出在知识库的文档里我切分知识卡片时虽然打了“地区”标签但检索时只在病害场景启用了地域过滤农事操作类知识完全没做地域维度约束。修复方案是把地域过滤从“部分场景使用”改成“全链路强制使用”。用户首次进入系统时选择所在区域这个区域会同步到所有工具调用和检索请求里。知识卡片里没有匹配地区标签的通用知识比如“种子存放原则”这类全国通用的内容仍然可以命中但涉及播期、物候、品种选择的卡片必须地区标签匹配才允许返回。这之后再没有出现过“用华北经验回答东北问题”的情况。5.3 脏数据与重复调用工具界的“牛皮癣”实际跑起来工具的输入数据经常是脏的。气象接口某天缺测了返回的降水量字段是空值土壤检测报告里pH值单位写的是“pH值5.5”而不是纯数字农资行情表的某个品种价格被录成了“--”。这些脏数据被Agent拿到之后轻则给出错误答案重则导致Agent连续多轮重试同一个工具。我在每个工具内部加了一条统一规则拿到的原始数据先做一次清洗缺字段就补默认值格式不对就尝试解析解析不了就明确标注“数据缺失”。工具返回给模型的内容不再是原始数据而是已经标注好“哪些字段可靠、哪些字段缺失”的摘要。这样模型就不会对着一个空值编答案而是会主动告诉用户“当前数据缺失建议补充土壤检测后再做判断”。这轮改造之后Agent在测试集上的“编造缺失数据”行为彻底消失了。以前模型拿到缺降水量的天气摘要会自己编一个“未来三天降水量5毫米”出来现在它会老老实实说“未来三天的降水数据暂缺无法判断田间湿度变化趋势”。对农业场景来说这种“知道不知道”的能力比“什么都能答”更宝贵。6. 成本与落地怎么样才算真正“用得起”的农业Agent6.1 Token成本一次完整农事方案到底烧多少钱Agent的能力提升是有代价的——每次调用工具模型的上下文里都要塞进工具描述、调用日志、中间结果token消耗比普通问答高出好几倍。我统计过一个简单的单轮问答大约消耗约2000 token但一次完整的“病害诊断用药建议”流程因为涉及三次以上的工具调用和多轮推理token消耗经常高达1.5万到2万。成本优化我做了三件事。第一前面提到的所有工具都输出“降噪摘要”尽量压缩中间结果。这是个持续优化的过程每版摘要文本都尽量压短只保留决策必需字段第二对频繁查询的静态知识做结果缓存比如“某地区常年平均气温”“某品种的标准栽培日历”这类基本不变的内容直接命中缓存不走模型推理第三简单问题用轻量模型处理只有复杂农事诊断才切到更强的模型。分流策略是在提示词里让Agent自己判断问题复杂度再调用对应的模型实例。6.2 响应速度农户等不了30秒成本之外响应速度是第二个坎。我第一版Agent的完整链路是用户提问→模型理解→调用工具1→再调用工具2→模型生成答案。工具串行调用加上每个工具的网络延迟一趟下来常常要25秒以上。让农户对着手机等半分钟才看到回答基本等于失败。提速的关键是把没有依赖关系的工具调用并行化。在“草莓叶子发黑”这个案例里查询天气和查询地块档案这两件事互不依赖完全可以同时发起。我用langchain里的并行工具调用方式改造后整体响应时间从25秒压到了10秒以内。再配合轻量模型处理简单请求整个系统的体验就基本达标了。还有一个看似不起眼但很有效的优化开场白和引导语用静态模板不走模型生成省掉了每次对话启动的第一段token和延迟。6.3 真正的落地把回答变成一张“农事任务单”技术链路的最后一步是交互形态。我一开始做的Agent是标准的对话框用户问、Agent答答完就完了。但是在真实使用中很多农户要的不是“一段话”而是“接下来怎么办”的可执行清单。后来我把整套流程固化成一个输出模板只要对话中涉及农事操作Agent就会自动生成一张结构化任务单按“操作日期、操作内容、用量或参数、注意事项”四个字段列清楚。农户看完对话总结后直接照着任务单执行还能一键把任务项加入日历提醒。实测下来这个“从答案到任务单”的改动比我在工具链和模型调优上做的所有努力反馈都更强烈。这也印证了我做这个项目一直坚持的判断农业Agent的终局形态不是一个更聪明的聊天框而是一个能帮你把农活安排明白的数字农技员。

读完文章,也想定制专属网站?

尧图设计师 24 小时内与您沟通定制方案

免费获取报价 →
↑