☰
从零跑通第一条AI Agent工单:大模型+查询工具最小闭环实战
2026/9/28 6:40:52 网站建设 项目流程

1. 从一条文字工单说起:为什么先跑起来比什么都重要

很多人一上来就想搭一个完整的 AI Agent,规划得特别宏大——要接知识库、要接多轮对话、要接权限系统、要接工单状态机,结果两周过去,连第一条工单都没跑通。我自己也踩过这个坑。最开始做智能工单处理的时候,光在架构设计上就耗了快一周,画了一堆图,最后发现真正卡住我的不是架构,而是“大模型到底能不能稳定地调用一个查询工具,把工单里的关键信息查出来”。

所以这篇东西的核心就一件事:用大模型加一个查询工具,把一条文字工单从输入到输出完整跑通。不搞花活,不接一堆中间件,就是最小可运行闭环。它解决的是“从 0 到 1”的问题,适合刚接触 AI Agent、Tool Calling、大模型应用开发的人,也适合已经会调 API 但没真正让模型去调用过外部工具的人。你只要有一台能联网的电脑、一个能用的大模型 API、一个能查的数据库或表格,就能跟着复现。

这里的关键词是大模型、查询工具、文字工单、AI Agent、Tool Calling。文字工单就是用户提交的一段自然语言描述,比如“帮我查一下上周北京地区退货订单里金额超过 500 的那几单”。查询工具可以是一个 MySQL 查询接口、一个 HTTP API,甚至是一个本地 CSV 查询函数。大模型负责理解这句话,决定要不要调工具、调哪个工具、传什么参数,最后把查询结果整理成人能看懂的回答。Tool Calling 就是模型和工具之间的那根线。

我先把结论放前面:第一条工单能不能跑通,取决于你有没有把工具描述写清楚、参数定义写死、返回结果控制住长度。这三件事做不好,后面接再多东西都是白搭。下面我按实际搭建顺序拆开讲,包括我自己的选型逻辑、参数怎么定、代码怎么写、报错怎么查。

2. 整体设计与选型:为什么是“大模型 + 单查询工具”这个最小组合

2.1 为什么不先做多工具、多轮、多 Agent

刚上手的人最容易犯的错,是一开始就上多工具。比如同时给模型挂“查订单”“查用户”“查物流”“查退款”四个工具,结果模型在第一步就懵了——它不知道当前这句话该用哪个,于是开始瞎猜,或者干脆不调工具直接编答案。我实测下来,单工具场景下 Tool Calling 的成功率明显高于多工具,因为决策空间小,模型只需要判断“调”还是“不调”,以及“参数填什么”。

另一个原因是调试成本。单工具跑通之后,你至少知道链路是通的:模型能识别意图、能生成结构化参数、工具能执行、结果能回传、模型能总结。这时候再加第二个工具,你只需要关注“模型会不会选错工具”,而不用怀疑底层链路。这就像学开车,先在空地上把油门刹车方向盘摸熟,再上路,而不是一上来就进早高峰。

所以第一版的设计目标非常明确:一条工单进来,模型判断是否需要查询,需要就调唯一那个查询工具,拿到结果后组织语言回复;不需要就直接回复。整个流程只有一次工具调用,不做多轮,不做记忆,不做并发。

2.2 大模型怎么选:先看能不能稳定输出结构化参数

选模型这件事,我的建议是:第一版不要纠结哪个模型最强,先选一个你调用成本低、响应快、支持 Tool Calling 的。因为你要反复试几十次甚至上百次,每次都在烧钱和时间。我自己的做法是先用一个中等规模的模型把流程跑通,等提示词和工具描述稳定了,再换更强的模型对比效果。

判断一个模型能不能用于这个场景,看三点。第一,它是否支持函数调用或工具调用协议,也就是你能不能把工具定义按它要求的格式传进去。第二,它返回的工具参数是不是稳定的 JSON,而不是夹在一堆解释文字里。第三,它在参数缺失时会不会主动追问,而不是瞎填一个默认值。第三点特别重要,我见过模型把“上周”直接填成固定日期,结果查出来完全不对。

如果你用的是本地部署模型,还要注意上下文长度。工单本身可能不长,但工具定义、系统提示词、历史示例加起来很容易超过 4K token。我一般会把系统提示词压到 500 字以内,工具描述控制在 200 字以内,给模型留足空间。

2.3 查询工具怎么选:能返回结构化结果就行

查询工具这块,很多人以为必须上 MySQL。其实第一版完全可以用一个 Python 函数查 CSV,或者查 SQLite。核心不是数据库多强,而是工具能不能接收参数、执行查询、返回一个列表或字典。我最初就是用 pandas 读一个 CSV,写了个query_orders(region, min_amount, date_range)函数,跑通之后才换成 MySQL。

如果你要用 MySQL,注意两点。第一,不要让模型直接生成 SQL 字符串去执行,除非你做了严格的校验和只读权限控制。更稳的做法是模型只填参数,SQL 由你在代码里拼,或者用参数化查询。第二,查询结果要限制条数,比如LIMIT 20,否则模型拿到几百行数据,总结的时候会漏、会编、会超上下文。

选型项第一版建议原因
模型支持 Tool Calling 的中等模型成本低、迭代快
工具数量1 个降低模型决策难度
工具实现本地函数或只读查询接口易调试、风险低
返回条数限制 20 条以内控制上下文长度
参数来源模型填参,代码拼 SQL避免注入和乱查

2.4 文字工单的输入特点与预处理

文字工单最大的特点是“口语化 + 信息不全”。用户不会写“region=北京 AND amount>500 AND date BETWEEN ...”,他会写“帮我看看上周北京那边退货超过五百的”。这里面有三个信息:地区、时间、金额,但“上周”是模糊的,“五百”是中文数字,“退货”对应的是订单类型。

我的做法是在系统提示词里明确告诉模型:如果工单里缺少必要参数,不要猜,直接追问。同时在工具定义里把每个参数标成必填或可选。必填参数缺失时,模型应该返回一个追问,而不是调用工具。这一步能挡掉大量错误查询。另外,中文数字和相对时间可以在提示词里给几个转换示例,比如“上周 = 最近 7 天”“五百 = 500”,模型基本能学会。

3. 核心细节拆解:工具描述、参数定义和提示词到底怎么写

3.1 工具描述是给模型看的“说明书”

工具描述写得好不好,直接决定模型会不会用、用得对不对。我见过有人把工具描述写成“查询订单”,结果模型根本不知道什么时候该调。好的描述应该包含三部分:这个工具做什么、什么时候用、每个参数是什么意思。

比如我第一版是这么写的:

query_orders:根据地区、金额下限、时间范围查询退货订单。 当用户询问某个地区、某段时间内金额超过某个值的退货订单时使用。 参数: - region:地区名称,字符串,必填,例如“北京” - min_amount:金额下限,数字,必填,例如 500 - days:最近多少天,整数,必填,例如 7

这段描述里,“什么时候用”是关键。它把触发条件和用户表达对应起来了。模型看到“上周北京退货超过五百”,就会匹配到“地区 + 时间 + 金额”这个模式,然后去填参数。如果描述里只写“查询订单”,模型可能会在用户问“订单状态”时也去调,那就错了。

注意:工具描述不要写得太长,超过 300 字模型反而容易忽略重点。把触发条件放在第一句,参数说明用短句。

3.2 参数定义要“窄”,不要“宽”

参数定义越宽,模型越容易乱填。比如你把时间参数定义成“任意时间字符串”,模型可能填“上周”“最近”“2024 年”各种格式,你的工具根本处理不了。我的做法是把参数类型和格式写死,并且在代码里做校验。

以days为例,我定义成整数,单位是天。模型看到“上周”会填 7,看到“最近三天”会填 3。如果用户说“上个月”,模型可能填 30,也可能填 31,这没关系,因为我的查询逻辑是“最近 N 天”,30 和 31 差别不大。但如果你的业务对日期边界很敏感,那就不要用天数,直接用开始日期和结束日期两个参数,让模型填YYYY-MM-DD格式。

金额参数也一样。我定义成数字,模型会把“五百”转成 500。但如果用户说“五百左右”,模型可能填 500,也可能填 450。这时候你要么在提示词里规定“左右按下限处理”,要么在工具里做模糊匹配。我一般选择前者,因为规则越明确,模型越稳定。

参数类型必填格式要求缺失时行为
region字符串是城市名追问用户
min_amount数字是整数或小数追问用户
days整数是正整数默认 7 天并说明

3.3 系统提示词要管住三件事

系统提示词不需要写很长,但必须管住三件事:角色、工具使用规则、输出格式。我的第一版提示词大概是这样:

你是一个工单处理助手。你可以调用 query_orders 工具查询退货订单。 规则: 1. 只有当用户明确提到地区、金额、时间三个信息时,才调用工具。 2. 缺少任何一个必填参数,先向用户追问,不要调用工具。 3. 调用工具后,根据返回结果用中文总结,不要编造数据。 4. 如果查询结果为空,直接告诉用户没有符合条件的订单。

这四条规则里,第一条和第二条是防止乱调,第三条是防止幻觉,第四条是处理空结果。我实测下来,加上这四条之后,模型瞎调工具的概率明显下降。尤其是第二条,很多模型默认会“猜一个值”,明确说“不要猜”之后会好很多。

输出格式这块,我建议第一版不要限制太死。你可以要求“用一段话总结”,但不要要求“必须输出 JSON”,因为工单处理的最终读者是人,不是程序。等后面要接自动化流程了,再改成结构化输出。

3.4 工具返回结果怎么控制长度和结构

工具返回给模型的结果,直接影响到模型能不能总结好。如果你返回一个 100 行的表格,模型大概率会漏掉大部分,甚至开始编。我的做法是只返回必要字段,并且限制条数。

比如查询订单,我返回的每条记录只包含:订单号、地区、金额、日期、状态。不返回用户 ID、商品详情、物流信息这些无关字段。条数限制在 20 条以内,如果超过 20 条,我在返回结果里加一句“共查到 35 条,以下展示前 20 条”。这样模型总结的时候会说“共 35 条,其中前 20 条显示……”,不会漏掉总数。

返回格式我用 JSON,因为模型对 JSON 的解析能力比较强。结构大概是:

{ "total": 35, "shown": 20, "orders": [ {"order_id": "A001", "region": "北京", "amount": 620, "date": "2024-06-01", "status": "退货中"} ] }

提示:返回结果里不要包含换行符和特殊符号,否则模型在总结时容易断句错误。我一般会把所有字段值转成字符串,去掉多余空格。

4. 实操过程:从零把第一条工单跑通

4.1 环境准备与最小依赖

第一版不需要复杂环境。我用的是 Python,依赖就三个:openai或对应的大模型 SDK、pandas或sqlite3、json。如果你用 MySQL,再加一个pymysql。不需要 LangChain,不需要向量数据库,不需要前端。一个.py文件就能跑。

安装命令很简单:

pip install openai pandas

如果你用的是其他模型平台,把 SDK 换成对应的就行。关键是这个 SDK 要支持工具调用,也就是你能传tools参数,并且能拿到tool_calls返回。

环境变量里放 API Key,不要写死在代码里。我一般用.env文件加python-dotenv,但第一版直接export也行。跑通之后再考虑配置管理。

4.2 定义查询工具函数

工具函数本身很简单,就是接收参数、查数据、返回结果。我用 pandas 读 CSV 举例:

import pandas as pd def query_orders(region: str, min_amount: float, days: int): df = pd.read_csv("orders.csv") df["date"] = pd.to_datetime(df["date"]) cutoff = pd.Timestamp.now() - pd.Timedelta(days=days) result = df[ (df["region"] == region) & (df["amount"] >= min_amount) & (df["date"] >= cutoff) & (df["status"].str.contains("退货")) ] total = len(result) shown = result.head(20) return { "total": total, "shown": len(shown), "orders": shown[["order_id", "region", "amount", "date", "status"]].to_dict("records") }

这个函数里,days用来算截止日期,region精确匹配,min_amount用大于等于。实际业务里地区可能有“北京市”“北京”两种写法,第一版先不做模糊匹配,等跑通了再加。

4.3 把工具定义传给模型

工具定义要按模型平台要求的格式写。以常见的函数调用格式为例:

tools = [ { "type": "function", "function": { "name": "query_orders", "description": "根据地区、金额下限、时间范围查询退货订单。当用户询问某地区某时间段内金额超过某值的退货订单时使用。", "parameters": { "type": "object", "properties": { "region": {"type": "string", "description": "地区名称,例如北京"}, "min_amount": {"type": "number", "description": "金额下限,例如500"}, "days": {"type": "integer", "description": "最近多少天,例如7"} }, "required": ["region", "min_amount", "days"] } } } ]

这段定义里,description和参数说明都是给模型看的。我特意在描述里写了“当用户询问……时使用”,就是为了让模型把用户表达和工具触发条件对应起来。

4.4 完整调用流程与代码骨架

整个流程分四步:发消息给模型、判断有没有工具调用、执行工具、把结果回传给模型。代码骨架如下:

import json from openai import OpenAI client = OpenAI() def handle_ticket(ticket: str): messages = [ {"role": "system", "content": "你是一个工单处理助手。你可以调用 query_orders 工具查询退货订单。缺少必填参数时先追问,不要猜。"}, {"role": "user", "content": ticket} ] response = client.chat.completions.create( model="your-model", messages=messages, tools=tools, tool_choice="auto" ) msg = response.choices[0].message if msg.tool_calls: tool_call = msg.tool_calls[0] args = json.loads(tool_call.function.arguments) result = query_orders(**args) messages.append(msg) messages.append({ "role": "tool", "tool_call_id": tool_call.id, "content": json.dumps(result, ensure_ascii=False) }) final = client.chat.completions.create( model="your-model", messages=messages ) return final.choices[0].message.content return msg.content

这段代码里,tool_choice="auto"表示让模型自己决定调不调。如果你希望第一版更可控,可以设成强制调用,但那样模型在参数不全时也会硬调,所以我还是建议auto。

4.5 第一次实测:一条工单的完整记录

我拿一条真实工单试了一下:“帮我查一下最近 7 天北京地区退货金额超过 500 的订单。”

模型返回的tool_calls参数是:

{"region": "北京", "min_amount": 500, "days": 7}

工具执行后返回:

{"total": 3, "shown": 3, "orders": [{"order_id": "A001", "region": "北京", "amount": 620, "date": "2024-06-01", "status": "退货中"}]}

模型最终回复:“最近 7 天北京地区退货金额超过 500 的订单共有 3 条,其中一条是订单 A001,金额 620 元,状态为退货中。”

这条链路跑通之后,我做的第一件事不是加功能,而是换不同的工单反复试。试了“上海最近三天退款超过一千的”“广州上周退货五百以上的”,看模型能不能稳定填对参数。大概试了 20 条,发现两个问题:一是“上周”有时填 7 有时填 6,二是有条工单没提地区,模型自己填了“全国”。这两个问题后面都通过提示词解决了。

5. 常见问题与排查技巧实录

5.1 模型不调工具,直接编答案

这是最常见的问题。原因通常是工具描述没写清楚触发条件,或者系统提示词没强调“必须调工具”。我的排查顺序是:先看工具描述里有没有“当用户询问……时使用”,再看系统提示词有没有说“不要编造数据”。如果都写了还不调,就把tool_choice临时改成强制调用,看模型能不能正确填参数。如果能填对,说明是触发条件的问题;如果填不对,说明参数定义有问题。

还有一种情况是模型觉得“这个问题不需要查”。比如用户问“退货流程是什么”,这确实不需要查订单。这时候不调工具是对的。所以你要区分“该调没调”和“本来就不该调”。

5.2 参数填错或缺失

参数填错一般有三种:格式错、值错、漏填。格式错比如days填了“一周”而不是 7,这通常是参数类型没写清楚。值错比如地区填了“北京市”而数据库里是“北京”,这需要你在工具里做兼容,或者在提示词里规定用简称。漏填比如没提金额,模型自己填了 0,这要在提示词里明确“缺少必填参数时追问”。

我一般会在代码里加一层校验:如果args里缺少必填字段,或者类型不对,直接返回一个追问,而不是执行查询。这样即使模型犯错,也不会查出错误结果。

问题可能原因排查方法解决
不调工具描述不清看触发条件补“何时使用”
参数格式错类型没写死看参数定义明确类型和示例
参数值错没做兼容对比数据库加映射或提示词
漏填参数没强调必填看 required提示词加追问规则
结果太长没限制条数看返回加 LIMIT 和 total

5.3 查询结果为空或报错

空结果不是错误,但模型有时会把空结果说成“查询失败”。我的做法是在工具返回里明确写total: 0,并在提示词里说“如果 total 为 0,直接告诉用户没有符合条件的订单”。这样模型就不会瞎猜。

报错一般是数据库连接失败、字段名不对、日期格式不对。第一版我建议把所有异常都捕获,返回一个error字段,让模型告诉用户“查询暂时不可用”。不要让异常直接抛到模型那里,否则模型会编一个奇怪的解释。

5.4 模型总结时编造数据

这个问题最危险。模型可能查到 3 条,总结时说成 5 条,或者把金额改了。防止的办法有三个:一是返回结果里带total和shown,让模型有明确数字可引用;二是在提示词里强调“只根据返回结果总结,不要添加未返回的信息”;三是返回结果尽量短,减少模型自由发挥的空间。

我实测下来,返回结果越结构化、字段越少,模型编造的概率越低。如果你返回一大段文本,模型很容易在里面“找”到不存在的信息。

5.5 响应太慢或超时

第一版链路里,时间主要花在两次模型调用上。如果模型响应慢,可以先换一个更快的模型,或者把系统提示词缩短。工具查询本身通常很快,除非数据量特别大。我一般会给查询加一个时间限制,比如只查最近 90 天的数据,避免全表扫描。

另外,如果你用的是流式输出,注意工具调用和流式的配合。有些平台在流式模式下工具调用的返回格式不一样,第一版建议先用非流式跑通,再加流式。

6. 跑通之后:下一步可以往哪里扩展

第一条工单跑通之后,你手里就有了一个最小闭环:输入文字、模型判断、工具查询、结果总结。这个闭环虽然简单,但它是后面所有扩展的基础。我自己的扩展顺序是这样的:先加第二个工具,比如查物流;再加多轮对话,让用户能追问“那第二条呢”;然后加工单状态回写,让模型能改状态;最后才考虑接知识库和权限。

但我要提醒一句:每加一个东西,都要重新测一遍第一条工单。因为新工具、新提示词可能会影响模型对原有工具的判断。我见过加了查物流工具之后,模型把“退货订单”也拿去查物流了。所以回归测试很重要,哪怕只是手动跑几条。

如果你现在还没跑通第一条,不要急着看后面的扩展。先把工具描述改到模型能稳定调对,把参数校验加到代码里,把返回结果控制住长度。这三件事做完,你才算真正“先跑起来”了。后面的事,跑起来再说。

需要专业的网站建设服务?

联系我们获取免费的网站建设咨询和方案报价,让我们帮助您实现业务目标

立即咨询