本文最后更新于 2026-05-10,文章内容可能已经过时。

你写了个函数,但什么时候调用它——你说了不算

你有没有遇到过这种情况:写了一个函数,逻辑完全正确,测试也通过了,但它什么时候被调用,完全不是你决定的?

第一次遇到这个感觉,很多人会觉得有点失控。但当你理解了背后的设计逻辑,你会发现——这其实是 Agent 开发里最有意思的地方。

这篇文章的目标很具体:带你用 Perplexity 官方的 Agent Skills 手册,从零跑通一个最简单的技能模块。不追求完整产品,只追求流程全跑通。跑完之后,你会遇到一个让人"哦,原来如此"的认知翻转时刻——我先卖个关子,等你跑到第四章自然就明白了。

---

第一章:为什么选 Perplexity Agent Skills 手册来入门?

市面上讲 Agent 的文章不少,但大多数要么太理论(讲 ReAct、CoT、Tool Use 框架,说完你还是不知道从哪里写第一行代码),要么太产品化(直接教你用某个 no-code 平台拖拖拽拽,根本不接触底层)。

Perplexity 的 Agent Skills 文档走的是第三条路:工程师视角的接线说明书

它假设你已经有一点 API 调用基础,但从没做过 Agent,然后用最直接的方式告诉你:一个 Skill 长什么样、怎么注册、Agent 怎么识别并调用它。没有多余的哲学,全是接线图。

这正是我推荐它作为 Agent 入门材料的原因。它不会让你在"Agent 到底是什么"这个问题上绕圈子,而是直接让你动手,然后在动手过程中自然理解那些概念。

本文只搭最简单的一个技能模块——一个"查今日天气"的 Skill。不追求完整产品,只追求流程全跑通

跑完之后你会发现,有一个环节的设计逻辑和你写普通函数的直觉完全相反。理解这个"反转",才算真正入门 Agent 开发。

---

第二章:动手前先读懂手册的三个核心概念

在打开编辑器之前,有三个概念必须先搞清楚。不然代码写出来了,你也不知道哪里出了问题。

概念一:Skill 定义结构

Skill 不是一个普通的函数定义,它是一份给 AI 看的说明书

类比:想象你在一家餐厅,菜单上写着"红烧肉——选用五花肉,口感软糯,适合喜欢浓郁口味的食客"。这段描述不是给你的,是给服务员看的,让服务员知道什么时候该向什么样的客人推荐这道菜。

Skill 定义结构就是这份菜单描述。它用 JSON Schema 的格式,告诉 Agent:这个技能叫什么名字、能做什么事、需要什么输入、会返回什么输出。

概念二:输入/输出 Schema

Schema 是对数据结构的精确描述。输入 Schema 告诉 Agent"调用这个技能需要提供哪些参数",输出 Schema 告诉 Agent"这个技能会返回什么格式的数据"。

类比:输入 Schema 就像点菜单上的"必填项"——你必须告诉服务员几人份、辣不辣;输出 Schema 就像菜品的摆盘规范——上桌的时候必须是这个样子。

Schema 写得好不好,直接决定 Agent 能不能准确理解并调用你的技能。 这一点在第四章会重点展开。

概念三:Skill 的触发机制

这是最容易让新手困惑的地方。

普通函数的触发机制很直接:你在代码里写 get_weather("北京"),它就被调用了。但 Skill 的触发机制完全不同:你不调用它,Agent 读完你的 Skill 描述之后,自己决定要不要调用、什么时候调用。

整个调用链路大致是这样的:

用户发送自然语言请求

Agent 读取所有已注册的 Skill 描述

模型推理:这个请求需要用到哪些 Skill?

决策:调用 get_weather Skill,参数 city="北京"

执行后端函数,获取返回值

Agent 整合结果,生成最终回复

注意第三步——那个推理和决策过程,完全在模型内部发生,你看不到,也控制不了。你能控制的,只有你的 Skill 描述写得够不够清楚。

好,概念铺垫完毕。开始动手。

---

第三章:手把手搭建:一个"查今日天气"最简技能模块

我们要实现的效果:用户发一条"北京今天天气怎么样",Agent 能识别出需要调用天气查询技能,执行查询,然后返回结果。

步骤一:写 Skill 的 JSON Schema 描述

这是整个流程的核心文件。Agent 靠这份描述来理解你的技能。

{

"name": "get_current_weather",

"description": "获取指定城市的当前天气信息,包括温度、天气状况和湿度。当用户询问某个城市的天气、气温、是否需要带伞等问题时,调用此技能。",

"parameters": {

"type": "object",

"properties": {

"city": {

"type": "string",

"description": "需要查询天气的城市名称,例如:北京、上海、广州"

},

"unit": {

"type": "string",

"enum": ["celsius", "fahrenheit"],

"description": "温度单位,默认使用 celsius(摄氏度)"

}

},

"required": ["city"]

}

}

注意 description 字段——这不是给你自己看的注释,是给模型看的行为指南。写得越具体,模型越知道什么时候该调用这个技能。

步骤二:实现后端函数逻辑

import json

import requests

def get_current_weather(city: str, unit: str = "celsius") -> dict:

"""

实际项目中这里会调用真实天气 API(如和风天气、OpenWeatherMap)

这里用 mock 数据演示流程

"""

# mock 数据,实际替换为真实 API 调用

mock_weather = {

"北京": {"temp": 22, "condition": "晴", "humidity": 45},

"上海": {"temp": 28, "condition": "多云", "humidity": 72},

"广州": {"temp": 33, "condition": "阵雨", "humidity": 88},

}

weather = mock_weather.get(city, {"temp": 20, "condition": "未知", "humidity": 60})

temp = weather["temp"]

if unit == "fahrenheit":

temp = round(temp * 9/5 + 32, 1)

unit_symbol = "°F"

else:

unit_symbol = "°C"

return {

"city": city,

"temperature": f"{temp}{unit_symbol}",

"condition": weather["condition"],

"humidity": f"{weather['humidity']}%"

}

函数本身非常干净——只负责执行,不判断"该不该执行"。这一点很重要,第四章会详细说。

步骤三:注册到 Agent 调用链

以 OpenAI 兼容格式为例(Perplexity API 同样支持这套接口规范):

from openai import OpenAI

import json

如果你想低成本复现这个 demo,可以把 base_url 换成 api.884819.xyz

它完全兼容 OpenAI 格式,不需要改任何其他代码逻辑

client = OpenAI(

api_key="your_api_key",

base_url="https://api.884819.xyz/v1"

)

tools = [

{

"type": "function",

"function": {

"name": "get_current_weather",

"description": "获取指定城市的当前天气信息...", # 同上面的 Schema

"parameters": { ... } # 同上面的 Schema

}

}

]

发送用户请求

response = client.chat.completions.create(

model="gpt-5.1",

messages=[{"role": "user", "content": "北京今天天气怎么样?"}],

tools=tools,

tool_choice="auto"

)

步骤四:处理模型返回,执行函数

message = response.choices[0].message

if message.tool_calls:

tool_call = message.tool_calls[0]

function_name = tool_call.function.name

function_args = json.loads(tool_call.function.arguments)

# 执行对应函数

if function_name == "get_current_weather":

result = get_current_weather(**function_args)

print(f"Agent 决定调用: {function_name}")

print(f"传入参数: {function_args}")

print(f"函数返回: {result}")

预期终端输出:
Agent 决定调用: get_current_weather

传入参数: {'city': '北京', 'unit': 'celsius'}

函数返回: {'city': '北京', 'temperature': '22°C', 'condition': '晴', 'humidity': '45%'}

流程跑通了。注意看——你没有在任何地方写 if "天气" in user_input: get_current_weather()。你只是把 Skill 描述丢给了模型,模型自己决定要调用它。

这就是那个"反直觉"的地方。

---

第四章:那个"反直觉"的环节——控制权的转移

让我们正式拆解这个认知翻转。

普通函数调用的逻辑:我知道什么时候该调、我主动调用、我决定传什么参数。 Agent Skill 的逻辑:我只负责描述这个函数能做什么,调不调、什么时候调、传什么参数——模型决定。 | 维度 | 普通函数调用 | Agent Skill 调用 | | 谁决定调用时机 | 开发者(硬编码逻辑) | 模型(基于语义理解) | | 参数从哪里来 | 开发者显式传入 | 模型从用户输入中提取 | | 调试方式 | 打断点、看调用栈 | 优化 Skill 描述、看模型推理 | | 函数内部是否需要判断"该不该执行" | 有时需要 | 完全不需要 |

这个"控制权转移"带来三个具体影响:

影响一:Schema 描述的质量决定技能被调用的准确率

你的函数逻辑写得多漂亮都没用,如果 description 字段写得模糊,模型就是不会调用它。这不是 bug,这是设计。模型的决策完全依赖你给的文字描述。

影响二:函数内部逻辑可以极度简化

因为"该不该调用"的判断已经外包给了模型,你的函数只需要做一件事:给定合法输入,返回正确输出。不需要在函数里判断用户意图,不需要处理"这次调用是否合理"的问题。函数变得更纯粹、更好测试。

影响三:调试思路要变

当 Skill 没有被正确调用时,你的第一反应不应该是"函数哪里写错了",而应该是"描述写得够不够清楚"。这是一种完全不同的调试直觉。

从"指挥者"变成"规则制定者"——你从写命令式代码(我来告诉程序每一步做什么),切换到写声明式代码(我来描述这个能力是什么,让系统自己决定怎么用)。

这个感觉,和你第一次理解 CSS 的 flexbox、或者第一次用 SQL 替代手写循环时的感觉,非常像。

---

第五章:踩坑总结 + 给不同层级读者的建议

坑一:Skill 描述太模糊,Agent 从不调用

错误示例:
{

"name": "get_weather",

"description": "天气查询功能"

}

实际输出: 模型直接用自己的知识回答天气问题,完全没有调用 Skill。 原因分析: "天气查询功能"这五个字对模型来说信息量为零。模型不知道什么场景下该用它,于是选择不用。 修复方式:description 里明确写出触发条件——"当用户询问某城市的天气、气温、是否需要带伞、今天适合出行吗等问题时,调用此技能。"

---

坑二:返回格式不规范,Agent 输出乱码

错误示例: 后端函数返回了一个嵌套很深的对象,或者直接返回了一个字符串而不是结构化数据。 实际输出: Agent 的最终回复里出现原始 JSON 字符串,或者把返回值当成文本直接输出,格式很丑。 原因分析: 模型在整合 Skill 返回值时,需要能"读懂"这个返回值。如果返回的是无结构的字符串,模型不知道哪个字段是温度、哪个是天气状况,只能原样输出。 修复方式: 返回值始终用结构化的 dict,字段命名要语义清晰(temperaturet 好,weather_conditionwc 好)。

---

调试 Skill 描述准确率的时候需要反复调用模型,API 成本会累积得很快。建议用 [api.884819.xyz](https://api.884819.xyz) 的按量计费模式做调试,跑通后再切换到生产环境。新用户注册即送体验 token,国产模型(Deepseek/千问等)完全免费,没有月租。

---

给不同层级读者的下一步路径

如果你是小白: 先把这个 demo 完整跑一遍,不要跳过任何步骤。遇到报错先看错误信息,80% 的问题是 API key 没配对或者 JSON 格式写错了。 如果你是进阶用户: 尝试再注册一个 get_weather_forecast(未来几天预报)技能,然后观察当用户问"北京这周末天气怎么样"时,模型会调用哪一个。这个实验会让你对 Skill 描述的精度有更深的体感。 如果你是高阶用户: 研究 Skill 的并发调用机制——当一个请求需要同时查询两个城市的天气时,模型会串行调用还是并行调用?错误回退机制怎么设计?这些问题在生产环境里都是真实挑战。

---

这篇我们只跑了单个 Skill。但真实场景里,Agent 往往需要同时注册 5-10 个技能,然后自己决定"先调哪个、再调哪个、某个失败了怎么办"。

下一篇我想试一个更有意思的问题:当你给 Agent 注册了两个功能相近的技能,它会怎么选?会不会选错?能不能通过描述来"引导"它的选择?

如果你对这个问题也好奇,点个关注,下周见。

---

本文由8848AI原创,转载请注明出处。关注8848AI,带你从零开始学AI。

#AI教程 #Agent开发 #Perplexity #函数调用 #ToolUse #8848AI #人工智能 #AI实战