news 2026/6/9 5:14:11

进阶教程:在Kotaemon中添加自定义工具调用

作者头像

张小明

前端开发工程师

1.2k 24
文章封面图
进阶教程:在Kotaemon中添加自定义工具调用

进阶教程:在Kotaemon中添加自定义工具调用

在构建现代AI系统时,一个核心挑战是让大语言模型(LLM)不再局限于“说”,而是真正能够“做”。我们早已不满足于AI只是回答问题——用户更希望它能查订单、发邮件、调用API、操作数据库。这种从对话智能行动智能的跃迁,正是当前Agent框架演进的关键方向。

Kotaemon 正是为此而生。作为一个轻量级、可扩展的智能体框架,它通过模块化设计将LLM与外部世界连接起来。其核心机制之一就是工具调用(Tool Calling)——允许开发者将自己的Python函数封装为AI可理解并调度的服务。这不仅提升了系统的实用性,也为企业私有系统的AI集成提供了灵活路径。


工具接口的本质:让AI“听懂”你的函数

要让LLM调用一个函数,首先要让它“理解”这个函数是干什么的、需要什么参数、返回什么结果。这就引出了Kotaemon中最重要的概念:工具描述 schema

为什么是JSON Schema?

你可能已经注意到,主流平台如OpenAI、Anthropic都采用JSON Schema来描述工具。这不是偶然。Schema本质上是一种结构化元数据,它告诉模型:

  • 函数叫什么名字?
  • 它的功能是什么?(自然语言描述)
  • 接受哪些参数?类型和含义分别是什么?
  • 哪些是必填项?

更重要的是,这些格式已被大量训练数据覆盖,模型对它们有天然的“语感”。例如下面这个天气查询工具的定义:

{ "type": "function", "function": { "name": "get_weather", "description": "获取指定城市的实时天气情况,包括温度和天气状况。", "parameters": { "type": "object", "properties": { "location": { "type": "string", "description": "城市中文或英文名称,例如'上海'或'Shanghai'" } }, "required": ["location"] } } }

你会发现,description写得越清晰,模型就越不容易误用。比如把“获取天气”写成“拉取气象数据”,虽然技术上没错,但模型可能无法准确关联到用户说的“今天热不热”。

💡 实践建议:不妨站在模型的角度思考——如果你只看这段schema,能不能猜出什么时候该调用它?

如何注册一个真正的可用工具?

光有schema还不够,还得有对应的执行逻辑。以下是一个完整的实现示例:

import requests from typing import Dict, Any def get_weather(location: str) -> Dict[str, Any]: """ 获取指定城市的天气数据 """ api_key = "your_openweather_api_key" # 生产环境应使用配置中心或密钥管理服务 url = f"http://api.openweathermap.org/data/2.5/weather?q={location}&appid={api_key}&units=metric" try: response = requests.get(url, timeout=5) if response.status_code == 200: data = response.json() return { "city": data["name"], "temperature": data["main"]["temp"], "condition": "晴" if "clear" in data["weather"][0]["description"].lower() else "多云/雨" } else: return {"error": f"无法获取 {location} 的天气信息"} except Exception as e: return {"error": str(e)}

注意这里的返回值设计:我们没有直接抛出异常,而是统一包装成包含error字段的对象。这是为了确保即使出错,也能被后续流程安全处理。

接下来,我们需要将函数和schema绑定到Kotaemon的运行时环境中:

from kotaemon.tools import Tool weather_tool = Tool( name="get_weather", description="获取城市天气", func=get_weather, parameters=WEATHER_TOOL_SCHEMA["function"]["parameters"] ) agent.add_tool(weather_tool)

有些开发者会问:“能不能不用手动构造schema?”当然可以!如果框架支持装饰器语法,还能进一步简化:

from kotaemon.decorators import tool @tool( description="获取指定城市的实时天气", parameters={ "location": {"type": "string", "description": "城市名称"} }, required=["location"] ) def get_weather(location: str): # 同上... pass

这种方式更符合Python开发者的直觉,同时也便于做自动化文档生成或测试注入。


模型是如何“决定”调用工具的?

很多人以为工具调用是靠关键词匹配触发的,比如听到“查天气”就去调get_weather。但实际上,现代LLM的能力远不止于此。

结构化输出:模型的新技能

GPT-3.5-turbo及以上版本经过专门训练,能够在特定提示下生成符合预定义结构的JSON输出,而不是自由文本。这就是所谓的structured output generation

Kotaemon利用这一点,在system prompt中动态注入所有已注册工具的信息。例如:

You are a helpful assistant that can use tools. Available tools: - get_weather(location: str): 获取指定城市的实时天气情况...

当用户输入“北京现在冷吗?”时,模型不会直接回答“挺冷的”,而是判断:“这个问题需要实时数据 → 应该调用工具 → 参数是 location=’北京’”。

于是它输出一段特殊标记包裹的结构化请求:

`` {“name”: “get_weather”, “arguments”: {“location”: “北京”}}

这个过程不是随机的。为了让模型稳定输出这种格式,我们在推理时通常设置: | 参数 | 推荐值 | 说明 | |------|--------|------| | `temperature` | 0.0 ~ 0.3 | 降低随机性,保证一致性 | | `max_tokens` | ≥200 | 预留足够空间用于JSON输出 | | `stop_sequences` | `['<tool_call>']` | 遇到标记即停止,防止截断 | ### 解析与执行:别小看正则表达式 虽然听起来高大上,但最初的工具调用解析其实可以用几行代码完成: ```python import re import json from typing import Optional, Dict, Any def parse_tool_call(content: str) -> Optional[Dict[str, Any]]: pattern = r"<tool_call>(.*?)</tool_call>" match = re.search(pattern, content, re.DOTALL) if not match: return None try: call_data = json.loads(match.group(1)) return { "name": call_data["name"], "arguments": call_data.get("arguments", {}) } except json.JSONDecodeError: print("Invalid JSON in tool_call") return None

这看似简单,但在实际工程中非常有效。当然,生产级系统往往会升级为基于状态机或AST的解析器,以应对嵌套调用、流式输出等复杂场景。

一旦解析成功,Kotaemon就会查找对应函数并执行:

tool_request = parse_tool_call(model_output) if tool_request: result = get_weather(**tool_request["arguments"]) # 将结果回传给模型,用于生成最终回复

此时,整个流程形成了闭环:用户提问 → 模型识别意图 → 输出工具调用 → 执行函数 → 返回结果 → 生成自然语言响应

更强大的地方在于,这个过程可以多轮进行。比如先查天气,再根据天气推荐穿衣,最后发送提醒邮件——这就是所谓的“工具链(Tool Chain)”。


真实场景落地:客服系统中的订单查询

让我们来看一个典型的企业应用案例:智能客服中的订单状态查询。

想象一位客户问:“我的订单ORD123456到哪儿了?”

如果没有工具调用,AI只能回答:“请登录官网查看。”
而有了工具能力后,它可以主动调用内部系统:

@tool( description="查询订单物流状态", parameters={"order_id": {"type": "string", "description": "订单编号"}}, required=["order_id"] ) def query_order_status(order_id: str): # 调用ERP系统API resp = requests.post("/api/order/status", json={"id": order_id}) if resp.status_code == 200: data = resp.json() return { "status": data["status"], "current_location": data["location"], "estimated_arrival": data["eta"] } else: return {"error": "订单不存在或系统繁忙"}

整个交互流程如下:

用户输入 → NLU识别意图 → 匹配query_order_status工具 ↓ 提取参数 order_id = "ORD123456" ↓ 调用后端服务,返回: { "status": "shipped", "current_location": "上海市分拣中心", "estimated_arrival": "2025-04-08" } ↓ 模型生成自然语言回复: “您的订单已发货,目前位于上海市分拣中心,预计4月8日送达。”

这套架构的优势显而易见:

传统痛点Kotaemon解决方案
AI只能被动回答主动调用系统获取真实数据
多个系统分散接入困难统一抽象为工具接口
用户表达模糊导致错误操作Schema强约束+参数校验拦截非法输入
故障难以追踪每次调用都有完整日志,支持重放调试

设计哲学与避坑指南

当你开始编写自己的工具时,以下几个原则值得牢记。

✅ 推荐实践

1. 小颗粒度设计(SRP)

每个工具只做一件事。不要写一个万能函数handle_customer_issue(type, payload),而是拆分为:

  • check_order_status
  • request_refund
  • create_support_ticket

这样模型更容易精准选择,也便于权限控制和单元测试。

2. 幂等性优先

对于涉及变更的操作(如退款),务必保证重复调用不会造成副作用。例如:

def refund_payment(order_id): if has_already_refunded(order_id): return {"message": "已退款,无需重复操作"} # 执行退款逻辑...

否则模型一旦重试,可能导致资金损失。

3. 错误透明化

永远不要让工具静默失败。返回值中应明确包含error字段,以便模型决定是否重试或提示用户。

4. 权限前置控制

敏感操作(如删除账户)应在工具层集成身份验证,而不是依赖模型“自觉不去调用”。


⚠️ 高危风险警示

❌ 绝对禁止的行为
  • 注册os.system()subprocess.run()eval()等任意命令执行函数
  • 暴露数据库原始查询接口(如sql_query("SELECT * FROM users")
  • 工具返回未脱敏的敏感信息(身份证、手机号、密码哈希)

这些相当于给AI一把万能钥匙,一旦被诱导滥用,后果不堪设想。

🔄 防止无限循环

模型有时会陷入自我调用陷阱。例如反复调用同一个工具却得不到满意结果。解决方法很简单:

# 设置最大调用次数 MAX_TOOL_CALLS = 3 call_count = 0 while call_count < MAX_TOOL_CALLS: output = model.generate(...) tool_call = parse_tool_call(output) if tool_call: result = execute_tool(tool_call) # 将结果加入上下文 conversation.append({"role": "tool", "content": result}) call_count += 1 else: break

超过阈值后强制终止,转由人工介入。

🔐 数据隐私保护

即使是合法调用,也要注意返回数据的最小化原则。比如查询用户信息时,自动过滤掉非必要的字段:

def get_user_profile(user_id): full_data = db.query("...") # 只暴露必要字段 return { "name": full_data["name"], "level": full_data["level"], # 不返回 phone, email, id_card 等 }

未来的智能中枢:不只是“调函数”

掌握自定义工具调用,意味着你已经迈出了构建真正智能代理的第一步。但这仅仅是开始。

随着Function Calling技术的普及,下一代Agent系统将呈现几个趋势:

  • 动态工具加载:工具不再硬编码,而是从数据库或配置中心动态读取,实现热更新。
  • 低代码注册界面:业务人员可通过表单配置工具,无需写代码即可接入新服务。
  • 多模态工具融合:不仅能调API,还能生成图像、合成语音、控制硬件设备。
  • 自主决策链路:AI不仅能执行单一任务,更能规划多步骤工作流,如“订机票→订酒店→发日历提醒”。

Kotaemon的轻量化架构特别适合成为这类系统的实验场。你可以逐步迭代,将它从一个简单的问答机器人,演化为一个强大、可靠、可维护的智能中枢。

当AI不仅能“知道”,还能“做到”的时候,生产力的边界才真正被打开。

创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考

版权声明: 本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若内容造成侵权/违法违规/事实不符,请联系邮箱:809451989@qq.com进行投诉反馈,一经查实,立即删除!
网站建设 2026/6/9 20:40:58

8 个降AI率工具,继续教育学生必备!

8 个降AI率工具&#xff0c;继续教育学生必备&#xff01; AI降重工具&#xff1a;让论文更自然&#xff0c;更合规 随着人工智能技术的快速发展&#xff0c;越来越多的学生和研究人员开始依赖AI写作工具来提升效率。然而&#xff0c;随之而来的“AIGC率过高”问题也成为了学术…

作者头像 李华
网站建设 2026/6/9 8:34:47

FaceFusion镜像支持TensorRT加速推理过程

FaceFusion 镜像支持 TensorRT 加速推理过程在如今 AIGC 技术迅猛发展的背景下&#xff0c;人脸交换&#xff08;Face Swap&#xff09;已不再是实验室里的概念演示&#xff0c;而是逐步走向消费级应用和工业级部署。从短视频平台的趣味换脸滤镜&#xff0c;到直播中的虚拟主播…

作者头像 李华
网站建设 2026/6/9 7:46:56

10、嵌入式开发调试与引导加载器全解析

嵌入式开发调试与引导加载器全解析 1. 远程目标控制工具 远程目标控制工具可让我们远程发送命令控制目标设备、启动程序以及查看运行进程,还能从工作站终止目标设备上的部分运行进程。使用该工具时,CE 目标设备上需运行带有 KITL 的操作系统运行时映像。 若要在模拟器上使…

作者头像 李华
网站建设 2026/6/6 21:30:06

23、C 实现机器人控制应用:串口与 Serializer .NET 库的实践

C# 实现机器人控制应用:串口与 Serializer .NET 库的实践 1. 简单机器人控制应用概述 我们将创建两个 C# 应用程序来实现机器人的简单控制。一个应用使用 .NET Compact Framework 2.0 中的串口类向机器人发送命令,另一个则使用 Serializer .NET 库来控制机器人。这两个应用…

作者头像 李华
网站建设 2026/6/8 23:12:20

【Linux命令大全】001.文件管理之cksum命令(实操篇)

【Linux命令大全】001.文件管理之cksum命令&#xff08;实操篇&#xff09; ✨ 本文全面解析Linux系统中cksum命令的功能、参数及实际应用&#xff0c;帮助系统管理员和高级用户掌握文件完整性验证的核心技术。文章涵盖参数详解、基础用法、进阶技巧以及在数据安全、备份和传输…

作者头像 李华
网站建设 2026/6/9 21:25:57

Langchain-Chatchat用于新闻稿自动生成

Langchain-Chatchat用于新闻稿自动生成 在媒体节奏日益加快的今天&#xff0c;企业公关团队常常面临一个现实困境&#xff1a;如何在极短时间内产出一篇既符合品牌调性、又具备事实支撑和传播力的新闻稿&#xff1f;传统方式依赖人工查阅资料、整理素材、反复修改&#xff0c;整…

作者头像 李华