Day 018|FunctionAgent 初体验:用函数工具扩展 LlamaIndex Agent
系列:100 天系统学习 AI Agent 开发
当前阶段:RAG、知识库与工具边界
今日目标:FunctionAgent 适合从简单函数工具开始,把确定性能力交给代码,把选择权交给模型。
第一个工具越普通,越容易看清边界
如果一上来就给 Agent 搜索、数据库、文件写入十几个工具,出错时很难知道是工具描述、参数、路由还是权限问题。今天我只设计一个 get_learning_plan(day):输入天数,返回对应学习任务。它看起来不起眼,正好能把“模型选工具”和“代码执行规则”分开。
FunctionAgent 的核心并不是函数前面加一个装饰器,而是让模型在合适的时候选择一个有明确契约的函数。模型可以理解“我学到第 18 天了,接下来做什么”;函数负责检查 day 是否有效、从可信数据读取任务并返回稳定结构。
先把普通函数写好
from dataclasses import dataclass
@dataclass(frozen=True)
class LearningPlan:
day: int
topic: str
task: str
source: str
PLANS = {
17: LearningPlan(
day=17,
topic="LlamaIndex 入门",
task="整理 Agent、Tools、Workflows 的责任边界",
source="roadmap.md",
),
18: LearningPlan(
day=18,
topic="FunctionAgent 初体验",
task="设计并验证 get_learning_plan(day)",
source="roadmap.md",
),
}
def get_learning_plan(day: int) -> LearningPlan:
"""返回指定天数的学习计划;仅接受已收录的 1-100 整数。"""
if isinstance(day, bool) or not isinstance(day, int):
raise TypeError("day 必须是整数")
if not 1 <= day <= 100:
raise ValueError("day 必须在 1 到 100 之间")
if day not in PLANS:
raise LookupError("该天计划尚未载入,请先读取路线文件")
return PLANS[day]
这段函数可以脱离模型单独测试。示例字典只放了两天,因此查询 Day 19 预期得到“尚未载入”,而不是编造计划。真正项目应从受控的 roadmap.md 解析或从数据库读取。
工具契约比函数名字更重要
| 项目 | 设计 |
|---|---|
| 名称 | get_learning_plan |
| 使用时机 | 用户询问某一天的主题或任务 |
| 输入 | day: 1–100 的整数 |
| 输出 | day、topic、task、source |
| 可预期错误 | 类型错误、范围错误、计划未载入 |
| 权限 | 只读 |
| 不负责 | 修改进度、生成整套课程、写文件 |
如果把它命名为 get_info(day),模型很难判断“info”是什么;如果描述只写“获取数据”,调用质量也不会稳定。好工具描述应同时说明做什么、什么时候用、不能做什么。
Agent 应该只拥有有限选择
接入 LlamaIndex FunctionAgent 时,具体导入路径、参数名和运行方法要以当前官方文档为准。下面只写框架无关伪代码,不能当作已验证 API:
agent = FunctionAgent(
tools=[get_learning_plan],
instructions="只有询问具体天数时才调用工具;没有资料就明确说明"
)
result = await agent.run(user_message)
最小测试不需要模型
def test_get_learning_plan():
assert get_learning_plan(18).source == "roadmap.md"
for bad_day in (0, 101, "18", True):
try:
get_learning_plan(bad_day)
except (TypeError, ValueError):
pass
else:
raise AssertionError(f"无效参数未被拒绝:{bad_day!r}")
还要给 Agent 层准备路由样本:
- “第 18 天学什么?”应调用一次;
- “你好”不应调用;
- “明天学什么?”若不知道当前 day,应追问;
- “把第 18 天改成数据库课”不应调用这个只读工具;
- “第 999 天”应在工具前或工具内被拒绝。
今天真正学到的不是 FunctionAgent 语法
确定性能力先写成可独立测试的普通函数,再让模型拥有有限选择权,这个顺序很重要。函数错误不该由模型悄悄改写,模型误调用也不能绕过函数校验。下一篇把这个工具放进知识库助手时,还要加检索证据、引用和无法回答的分支。
面试官会追问:FunctionAgent 调工具时,谁对结果负责?
模型只负责“建议调用”;宿主程序负责参数校验、鉴权、执行、超时、重试和审计。工具返回也不能直接当真:需要区分 success、retryable_error、user_action_required 与 fatal_error,并把给模型看的摘要和内部诊断分开。
我会用三条 trace 证明边界:
- 参数缺失:schema 在执行前拒绝,并允许模型修复一次。
- 临时超时:只对只读或带幂等键的动作重试。
- 写操作结果未知:先查询业务状态,绝不盲目重放。
如果面试官追问“工具很多怎么办”,答案不是全部塞进上下文,而是先按场景检索或路由出小规模 tool loadout,减少误选与 Token 消耗。
今日检查清单
- 普通函数不依赖模型也能测试
- 工具名、参数、返回值和错误契约具体
- 只读与写操作没有混在一个函数里
- Agent 层有“何时不调用”的样本
- SDK 伪代码明确标注,编码时核对官方版本
更多推荐


所有评论(0)