news 2026/3/8 16:32:22

进阶教程:在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/3/7 14:36:29

FaceFusion能否用于游戏角色换脸?游戏MOD圈热捧

FaceFusion能否用于游戏角色换脸&#xff1f;游戏MOD圈热捧在《赛博朋克2077》的霓虹街头&#xff0c;一个NPC缓缓转头——那张脸&#xff0c;竟是你上传自十年前毕业照中的自己。这不是电影情节&#xff0c;而是如今无数MOD玩家正在实现的日常。随着AI生成技术的平民化&#x…

作者头像 李华
网站建设 2026/3/5 12:09:51

FaceFusion图形界面版来了!无需代码也能操作

FaceFusion图形界面版来了&#xff01;无需代码也能操作在短视频、直播和数字人内容爆发的今天&#xff0c;一张“换脸”图或一段趣味变脸视频&#xff0c;往往能在社交平台上引发热议。背后支撑这类创意的技术——人脸融合&#xff08;Face Fusion&#xff09;&#xff0c;早已…

作者头像 李华
网站建设 2026/3/7 12:31:56

Langchain-Chatchat构建品牌知识一致性管理体系

Langchain-Chatchat构建品牌知识一致性管理体系 在大型企业中&#xff0c;一个看似简单的问题——“我们最新的品牌LOGO使用规范是什么&#xff1f;”——却可能引发连锁反应。市场部引用的是去年的VI手册&#xff0c;客服团队依据的是内部培训PPT&#xff0c;而区域代理商收到…

作者头像 李华
网站建设 2026/3/7 5:42:44

14、Visual C 2005 开发 CE 设备应用指南

Visual C# 2005 开发 CE 设备应用指南 1. 开发背景与注意事项 在 CE 设备上编写 C# 代码与在 XP、Vista 等 Windows 版本上编写代码颇为相似。Visual Studio 2005 IDE 为开发 CE 设备的 C# 应用程序提供了高效的环境。有桌面 Windows 环境下 C# 代码编写经验的开发者能轻松适…

作者头像 李华
网站建设 2026/3/3 15:03:56

公众号 SVG 交互内容怎么做?一次关于 E2 编辑器的工具选型记录

在做公众号内容时&#xff0c;大多数人对「编辑器」的理解&#xff0c;通常停留在排版层面&#xff1a;字体、样式、段落、模板。但当内容开始涉及 SVG 动画、点击交互、轮播、弹窗 等能力时&#xff0c;传统排版编辑器往往就不太够用了。这篇文章记录的是我在做 公众号 SVG 交…

作者头像 李华