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

你以为学会调API就学会了Agent开发?Perplexity的手册第一页就在打这个脸

"不就是把LLM包一层,加几个工具调用吗?"

如果你也这么想过,那你和我当初犯了一样的错误。

三个月前,我在做一个基于Agent的客服系统。功能跑通了,演示效果很好,单轮对话完全没问题。但一上线,用户一旦进入多轮对话,系统就开始出现诡异的行为:前一步查到的订单信息,下一步就"忘了";用户说"就用刚才那个地址",Agent一脸茫然;错误信息直接透传给用户,像极了一个没有情商的程序员在和客户说话。

代码没有报错,逻辑"看起来"没问题。但这个系统是一颗定时炸弹。

直到我读到Perplexity发布的Agent Skills开发手册,第一章第一节就有这样一句话:

"Skills require a different developer mindset than traditional API integrations."

这句话我扫了一眼就跳过去了。直到系统在生产环境炸了第三次,我才回过头来认真读它。

这篇文章,就是我把这句话读懂之后,想写下来的东西。

---

第一章:为什么"能跑"不等于"对了"

先来看一个典型的"伪Skill"。

假设你要写一个"查询用户订单状态"的功能模块,用传统API思维写出来大概是这样:

# 传统API思维写法

def get_order_status(order_id: str) -> dict:

"""

参数:order_id - 必须是标准格式的订单ID

返回:订单状态字典

"""

if not order_id or not order_id.startswith("ORD-"):

raise ValueError("Invalid order_id format")

result = db.query(f"SELECT status FROM orders WHERE id = '{order_id}'")

if not result:

raise Exception("Order not found")

return {"status": result[0]["status"]}

这段代码本身没有问题。如果是一个后端接口,它很合格。

但把它包进Agent的Skills系统里,它就是一颗定时炸弹。

为什么?

因为用户不会说"请查询ORD-20240315-001的状态"。用户会说:"我刚下的那个单发货了吗?"或者"上周买的那个,到哪了?"

这段代码在简单场景下能跑——只要上游的意图解析恰好提取出了正确的order_id。但一旦上下文稍微复杂,它就会以各种你预料不到的方式失败,而且失败信息对Agent来说毫无价值,只是一个冰冷的Exception

这就是Perplexity手册那句话的真正含义:不是说你的代码写错了,而是说你的思维框架用错了。

---

第二章:差异一——输入不是参数,是"意图的碎片"

传统API的输入逻辑很清晰:你定义参数,调用方传参数,参数不符合格式就报错,边界清晰,责任分明。

但Skills的输入来自哪里?来自用户自然语言被LLM解析之后的"意图碎片"。

同一句话,在不同上下文里,语义完全不同。

"查一下我的订单"——

  • 如果用户刚刚提到了一个具体订单号,这句话指向那个订单
  • 如果用户没有提到订单号,这句话需要先查用户的最近订单列表
  • 如果用户处于投诉流程中,这句话可能隐含"我要看证据"的意图

Perplexity手册在Skills输入规范部分明确指出,Skills的输入设计必须考虑意图的不完整性。一个设计良好的Skill,不应该在输入不完整时直接抛出错误,而应该能够:

1. 识别输入的完整程度:这个意图片段是否足够我执行任务?

2. 主动请求补全:如果不够,以结构化的方式告诉Agent"我还需要什么"

3. 在模糊情况下给出最优猜测:并附上置信度,让Agent决定是否需要二次确认

用Skills思维重写上面那个例子:

# Skills思维写法

from dataclasses import dataclass

from typing import Optional

@dataclass

class SkillInput:

order_id: Optional[str] = None

user_id: Optional[str] = None

intent_context: Optional[str] = None # 原始意图上下文

@dataclass

class SkillOutput:

success: bool

data: Optional[dict] = None

confidence: float = 1.0 # 置信度信号

needs_clarification: Optional[str] = None # 需要补全的信息

fallback_action: Optional[str] = None # 失败降级路径

error_type: Optional[str] = None # 结构化错误类型

def get_order_status(skill_input: SkillInput) -> SkillOutput:

# 意图推断层:不是参数校验,而是意图补全

order_id = skill_input.order_id

if not order_id and skill_input.user_id:

# 没有订单ID但有用户ID,尝试获取最近订单

recent_orders = db.get_recent_orders(skill_input.user_id, limit=3)

if len(recent_orders) == 1:

order_id = recent_orders[0]["id"]

elif len(recent_orders) > 1:

# 有多个候选,需要澄清

return SkillOutput(

success=False,

needs_clarification="multiple_orders_found",

data={"candidates": recent_orders},

confidence=0.0

)

if not order_id:

return SkillOutput(

success=False,

needs_clarification="order_id_required",

fallback_action="ask_user_for_order_id",

confidence=0.0

)

# 执行查询

result = db.query_order(order_id)

if not result:

return SkillOutput(

success=False,

error_type="order_not_found",

fallback_action="suggest_check_order_list",

confidence=1.0 # 确定性失败,置信度高

)

return SkillOutput(

success=True,

data={"status": result["status"], "order_id": order_id},

confidence=1.0

)

看到差别了吗?传统API是"参数校验层",Skills是"意图推断层"。 前者问的是"你给的对不对",后者问的是"你想要什么,我能不能帮你找到"。

---

第三章:差异二——输出不是返回值,是"下一步行动的燃料"

传统API的输出是终点。调用方拿到结果,渲染给用户,流程结束。

Skills的输出是中间站。它的质量,直接决定了Agent下一步能不能做出正确决策。

这个差异非常反直觉,因为从代码层面看,两者都是"函数返回了一个值"。但返回值的语义完全不同

Perplexity手册在输出规范部分强调了两个必须携带的信号:

信号一:置信度(Confidence)

Skills的输出必须让Agent知道"这个结果有多可靠"。一个查询成功但结果可能不是用户想要的(比如查到了多个候选),和一个查询成功且结果高度匹配的,在Agent决策层面是完全不同的。

如果你只返回{"status": "shipped"},Agent不知道这是"精确匹配的结果"还是"我猜你问的是这个"。

信号二:失败降级路径(Fallback Action)

当Skill失败时,它不应该只说"我失败了",而应该告诉Agent"你接下来可以怎么做"。

这是Skills和传统API最本质的输出差异:传统API的错误是给人看的,Skills的错误是给Agent用的。

一个好的Skill输出,是Agent的行动说明书,不是人类可读的报告。

---

第四章:差异三——错误不是异常,是"Agent的学习信号"

这是三个差异里最反直觉的一个。

传统开发的错误处理逻辑:报错 → 捕获 → 告诉用户"出错了"。try-catch是我们的本能反应。

但在Skills开发里,这个本能是危险的。

为什么?

因为Agent需要从错误里获取信息,来决定下一步怎么规划。如果你把所有错误都捕获成一个统一的"出错了",Agent就失去了重新规划的依据,只能原地停转,或者做出错误的决策。

Perplexity手册把Skills里的错误分成两类:

可恢复错误(Recoverable Errors)

这类错误意味着Agent可以换一种方式重试,或者请求用户补充信息:

  • 输入不完整:需要更多上下文
  • 外部服务暂时不可用:可以稍后重试
  • 权限不足:可以引导用户授权
终止错误(Terminal Errors)

这类错误意味着这条路走不通,Agent需要彻底换一个方案:

  • 用户请求的资源根本不存在
  • 操作违反了业务规则
  • 需要人工介入的情况
# 错误的错误处理方式(传统API思维)

def get_order_status_bad(order_id: str):

try:

result = db.query_order(order_id)

return result

except Exception as e:

return {"error": "查询失败,请稍后重试"} # ❌ Agent拿到这个完全不知道该怎么办

正确的错误处理方式(Skills思维)

def get_order_status_good(order_id: str) -> SkillOutput:

try:

result = db.query_order(order_id)

return SkillOutput(success=True, data=result, confidence=1.0)

except OrderNotFoundError:

return SkillOutput(

success=False,

error_type="terminal.order_not_found", # ✅ 终止错误,Agent换方案

fallback_action="suggest_order_list_check"

)

except DatabaseTimeoutError:

return SkillOutput(

success=False,

error_type="recoverable.service_timeout", # ✅ 可恢复错误,Agent可重试

fallback_action="retry_after_2s",

confidence=0.0

)

except PermissionDeniedError:

return SkillOutput(

success=False,

error_type="recoverable.permission_denied", # ✅ 可恢复,引导授权

fallback_action="request_user_authorization"

)

try-catch思维在Agent里危险,不是因为它捕获了错误,而是因为它把有价值的错误信息变成了无意义的噪音。

---

第五章:三个差异合在一起,意味着什么?

把三个差异收拢来看:

传统API思维              Skills思维

─────────────────────────────────────────

输入层:参数校验 → 意图推断

输出层:结果返回 → 行动燃料

错误层:异常捕获 → 学习信号

这不是代码风格的差异,这是因果逻辑的差异

传统API的因果链是:输入 → 处理 → 输出,线性的,封闭的。

Skills的因果链是:意图 → 推断 → 执行 → 信号 → Agent重规划,循环的,开放的。

Skills开发自测清单

在你开始写下一个Skills模块之前,用这5条标准检查自己:

  • [ ] 输入层:我的函数在输入不完整时,是报错还是尝试推断?
  • [ ] 置信度:我的输出里,Agent能知道这个结果有多可靠吗?
  • [ ] 降级路径:我的输出里,包含了"如果这不对,下一步可以做什么"吗?
  • [ ] 错误分类:我的错误处理,区分了"可恢复"和"终止"两种类型吗?
  • [ ] 信息密度:我的错误信息,是给人看的还是给Agent用的?

如果有两条以上回答"否",你写的是API,不是Skill。

最小可行Skills代码骨架

from dataclasses import dataclass, field

from typing import Optional, Any

from enum import Enum

class ErrorType(Enum):

RECOVERABLE_TIMEOUT = "recoverable.timeout"

RECOVERABLE_PERMISSION = "recoverable.permission_denied"

RECOVERABLE_INCOMPLETE_INPUT = "recoverable.incomplete_input"

TERMINAL_NOT_FOUND = "terminal.not_found"

TERMINAL_BUSINESS_RULE = "terminal.business_rule_violation"

@dataclass

class SkillInput:

"""意图推断层的输入结构"""

raw_intent: str # 原始意图描述

structured_params: dict = field(default_factory=dict) # 已解析的参数

context: dict = field(default_factory=dict) # 上下文信息

user_id: Optional[str] = None

@dataclass

class SkillOutput:

"""行动燃料的输出结构"""

success: bool

data: Optional[Any] = None

confidence: float = 1.0

error_type: Optional[ErrorType] = None

needs_clarification: Optional[str] = None

fallback_action: Optional[str] = None

metadata: dict = field(default_factory=dict)

def my_skill(skill_input: SkillInput) -> SkillOutput:

"""

Skills函数模板

原则:

1. 永远不要直接raise,永远return SkillOutput

2. 失败时必须携带error_type和fallback_action

3. 成功时必须携带confidence

"""

# Step 1: 意图推断层

# 从不完整的输入中尽量推断完整意图

inferred_params = _infer_params(skill_input)

if inferred_params.get("needs_clarification"):

return SkillOutput(

success=False,

error_type=ErrorType.RECOVERABLE_INCOMPLETE_INPUT,

needs_clarification=inferred_params["needs_clarification"],

confidence=0.0

)

# Step 2: 执行层

try:

result = _execute(inferred_params)

return SkillOutput(

success=True,

data=result,

confidence=_calculate_confidence(result, skill_input)

)

except TimeoutError:

return SkillOutput(

success=False,

error_type=ErrorType.RECOVERABLE_TIMEOUT,

fallback_action="retry_after_delay"

)

except NotFoundError:

return SkillOutput(

success=False,

error_type=ErrorType.TERMINAL_NOT_FOUND,

fallback_action="suggest_alternatives"

)

代码示例里调用的模型接口,使用的是统一格式的中转层。如果你想在自己的Skills项目里复用这套结构,不想被单一平台的API格式绑定,可以试试 [api.884819.xyz](https://api.884819.xyz),兼容OpenAI格式,意图推断层和错误信号层都能直接套用,省去格式转换的心智负担。新用户注册即送体验token,国产模型(Deepseek/千问等)完全免费,没有月租。

---

结语:这套思维不只属于Perplexity

Perplexity的Skills手册是一份非常务实的文档。它没有讲大道理,而是在每一个规范背后,都隐藏着"如果你不这样做,会在哪里出问题"的工程经验。

这3个差异——意图推断 vs 参数校验、行动燃料 vs 结果返回、学习信号 vs 异常捕获——不是Perplexity专有的设计哲学。它们是所有Agent开发的底层操作系统。

不管你用的是LangChain、AutoGen、还是自己搭的框架,只要你在写Agent的功能模块,这套思维都适用。

用API思维写出来的Agent,能跑,但会在你看不见的地方悄悄烂掉。

现在你知道它在哪里烂了。

---

📌 下一篇预告

Perplexity手册里还有一个细节很少人注意:它对Skills的上下文窗口管理有一套非常具体的规范,而这套规范和LangChain的做法,在一个关键节点上是矛盾的。

到底是Perplexity的设计更接近Agent的真实运行逻辑,还是LangChain的抽象更合理?

下一篇我们来拆这个矛盾——两个主流框架在同一个问题上给出了截然相反的答案,而真相可能比你想象的更微妙。

---

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

#AI开发 #Agent开发 #Perplexity #LLM #Skills开发 #8848AI #人工智能 #开发者工具