在海外开发者社区,经常能看到类似 “What agent skills are necessary?” 的讨论。提出这个问题的人,往往已经跑通了一个能调用大模型的小 Demo,但真正开始做 Agent 产品时,才发现模型返回文本只是最外层,真正决定 Agent 能不能稳定完成任务的,是一整套围绕模型能力展开的工程组件,也就是通常所说的 Agent Skills。这篇文章会从概念、分类、最小实现、生产部署、问题排查到学习路线,把 Agent Skills 这件事讲清楚。
核心结论先放在前面:一个能回答问题的聊天机器人,和一个能在真实环境里持续执行任务的 Agent,差距不在模型本身,而在模型之外的技能编排、工具调用、记忆管理、安全确认、错误恢复和评估反馈。下面逐步展开。
1. 先建立统一认知:Agent、Skill、Tool、Harness 到底怎么区分
很多项目讨论到一半就陷入争论,根源是名词没有对齐。这里先统一概念,后续所有实现都基于这套划分。
1.1 从一次工具调用看 Agent 的基本工作链路
一个最简单的 Agent 循环可以拆成四步:
- 模型接收用户任务。
- 模型决定调用某个 Skill。
- Skill 执行并返回结构化结果。
- 模型观察结果后决定继续调用、调整方案或直接给出最终答案。
用一个极简伪代码描述这个循环:
def agent_loop(user_task: str, model, available_skills: dict, max_iterations: int = 10): messages = [{"role": "user", "content": user_task}] for i in range(max_iterations): resp = model.get_response(messages, skills=available_skills) if resp.finish_reason == "stop": return resp.content if resp.finish_reason == "tool_call": for call in resp.tool_calls: if call.name not in available_skills: raise UnknownSkillError(call.name) skill = available_skills[call.name] result = skill.run(**call.args) messages.append({ "role": "tool", "tool_call_id": call.id, "content": result.model_dump_json(), }) else: raise AgentLoopError( f"第 {i + 1} 轮出现无法处理的结束原因: {resp.finish_reason}" ) raise AgentLoopError( f"超过最大迭代次数 {max_iterations},Agent 未能在任务上收敛" )这段代码里,available_skills是 Agent 能使用的技能集合,model.get_response负责决定调用哪个技能,skill.run是技能的真实执行。如果模型连续调用多个技能,循环会继续下去;如果模型认为任务完成,就返回stop。这个循环是所有 Agent 项目的骨架。
1.2 用一张表区分几个高频概念
热搜词里反复出现 “harness 和 agent 区别”“skill 和 agent 的区别”“agent框架与编排”,说明很多人在概念层卡住了。下面是常用区分方式:
| 概念 | 通俗理解 | 在 Agent 系统中的职责 | 常见误区 |
|---|---|---|---|
| Agent | 一个能自主完成任务的执行单元 | 调度模型、技能、记忆和外部环境 | 把“调用大模型的脚本”直接叫 Agent |
| Skill | Agent 具备的一项能力 | 描述“能做什么”及其输入输出约定 | 把 Skill 等同于一段 Prompt 或一个函数 |
| Tool | Skill 的具体实现载体 | 执行真实操作,比如搜索、写文件、查数据库 | 只实现 Tool,却没有定义模型怎么理解它 |
| Harness | 承载 Agent 运行的框架与循环 | 管理模型调用、技能调度、上下文、日志和终止条件 | 以为装了 Harness 就等于有完整 Agent |
| Framework | 一站式开发框架 | 把上述组件组合成可运行应用 | 过度依赖框架,失去对关键链路控制 |
从工程角度看,Agent 是一个运行时概念,Skill 是能力抽象,Tool 是能力实现,Harness 是执行环境。四者必须协作才能构成一个可靠系统。
这里给出一个判断标准:如果项目中只有“客户端 + 一段提示词”,没有技能注册表、没有工具调用协议、没有循环终止条件,那它还不是 Agent,只是一个“增强对话脚本”。
2. 从工程角度看,哪些 Agent Skills 是必要的
可以把 Agent Skills 理解为“模型之外必须补齐的能力模块”。按照必要程度,可以分成两级:最低可用使能级和可用生产级。
2.1 最低可用 Agent 需要的一组 Skill
一个 Demo 级的 Agent 至少需要四种能力:
- 指令遵循:模型能理解用户任务,知道什么时候该结束。
- 工具调用:模型能按约定调用外部函数,并把工具结果填回上下文。
- 基础结果校验:工具返回后,模型需要判断结果是成功还是失败,不能把错误信息当作可用结果。
- 简单会话记忆:多轮对话中能记住用户刚提过的要求,而不是每次丢失上下文。
这四样跑通,一个 Agent 才真正“能干活”。如果只有模型 Prompt,没有任何工具调用协议,那本质上仍是聊天接口。
2.2 生产级 Agent 还需要补齐的能力
进入生产环境后,以下能力往往不是“加分项”,而是“缺一个就出大问题”的必需项:
| 技能域 | 解决什么问题 | 典型实现方向 | 是否必需 |
|---|---|---|---|
| 工具接入与 Function Calling | 让模型稳定调用外部函数 | 统一的参数 Schema、错误返回格式、工具注册表 | 必需 |
| 记忆管理 | 跨会话或在长任务中保留状态 | 短期上下文管理、长期存储、向量检索 | 必需 |
| 规划与任务分解 | 面对复杂任务时能拆解步骤 | Planner、子任务队列、阶段性检查点 | 高 |
| 反思与自我修正 | 发现错误或结果不可信时主动调整 | Reflector、Critic 组件、重试策略 | 高 |
| 安全与权限控制 | 防止破坏性操作、越权访问和数据泄露 | 白名单、敏感操作确认、审计日志 | 必需 |
| 人机协同 | 在关键节点找用户确认 | Human-in-the-loop、确认回调 | 高 |
| 评估与回归 | 验证输出质量,避免改一个功能坏一片 | 断言测试、评估 Prompt、对比集 | 必需 |
| 日志与可观测性 | 知道每一轮 Agent 做了什么 | 结构化日志、追踪 ID、耗时统计 | 必需 |
| 长上下文管理 | 处理超长对话和大量工具结果 | 摘要、裁剪、滑动窗口 | 视场景而定 |
| 多 Agent 协作 | 多个 Agent 分担角色并交换信息 | 消息协议、任务仲裁、共享内存 | 视场景而定 |
这张表可以直接用来做 Agent 项目的能力盘点。实际开发时,可以先根据业务场景调整优先级,但工具接入、记忆、安全、评估、日志这五项,在大多数生产场景里都建议提前考虑。
2.3 为什么“思考能力”也要被当成 Skill 来设计
规划、反思、质疑、总结这些能力,表面上是模型 Prompt 的事,但在工程里最好也抽象成 Skill。
一个常见做法是在技能注册表里增加plan、reflect、critic这类“元技能”。它们不是外部工具,而是模型内部的一轮思维过程。例如:
class PlanParams(BaseModel): goal: str = Field(description="任务目标") constraints: list[str] = Field(default=[], description="执行约束") class PlanSkill(Skill): name = "task_plan" description = "将复杂任务拆解为可执行的子任务列表" parameters_model = PlanParams def execute(self, params): # 调用模型生成计划,并把计划写入上下文 plan_prompt = f"请为任务拆解步骤:{params.goal}\n约束:{params.constraints}" result = llm_call(plan_prompt) return SkillResult(message="计划已生成", data={"plan": result})把思考过程显式建模,有两个好处:第一,可以单独测试和评估某类能力;第二,可以在循环里加入条件判断,比如“反思结果标记任务不可行时,提前终止”。
3. 动手实现一套必要的 Agent Skills
下面用一个简化示例展示如何把上面的概念落地。示例代码用于说明思路,实际项目请根据自己的包名、路径和模型协议调整。
3.1 先定义 Skill 的统一抽象:输入、输出、错误协议
如果每个 Skill 的返回格式都不一样,模型就会很难判断“这次调用到底成没成功”。因此第一步是统一协议。
from pydantic import BaseModel, Field class SkillResult(BaseModel): ok: bool = True message: str = "" data: dict | None = None error: str | None = None class Skill: name: str description: str parameters_model: type[BaseModel] requires_confirmation: bool = False def schema(self) -> dict: return { "name": self.name, "description": self.description, "parameters": self.parameters_model.model_json_schema(), } def run(self, **kwargs) -> SkillResult: params = self.parameters_model(**kwargs) return self.execute(params) def execute(self, params): raise NotImplementedError这里的关键点是:
- 所有 Skill 返回
SkillResult,模型只需要解析ok、message、data、error四个字段。 - 参数模型用 Pydantic 定义,框架可以把它转成 JSON Schema 给模型,做 Function Calling 时模型依赖这份 Schema 来生成参数。
requires_confirmation标记该 Skill 是否需要在执行前让用户确认。
3.2 写几个典型 Skill:搜索、写文件、记忆读写
搜索是典型的外部工具。示例实现里省略了真实搜索引擎 API,重点展示参数约束和结果包装:
class WebSearchParams(BaseModel): query: str = Field(description="搜索关键词") max_results: int = Field(default=5, ge=1, le=10, description="返回结果数量") class WebSearchSkill(Skill): name = "web_search" description = "搜索公开网页信息,适用于获取最新资料" parameters_model = WebSearchParams def execute(self, params): if params.max_results > 10: return SkillResult(ok=False, error="max_results 不能超过 10") # 实际项目在这里接入搜索服务的 SDK return SkillResult( message=f"搜索完成: {params.query}", data={"items": ["示例结果一", "示例结果二"]} )写文件属于高风险操作,需要单独控制覆盖行为:
import os class FileWriteParams(BaseModel): path: str = Field(description="目标文件路径") content: str = Field(description="写入内容") overwrite: bool = Field(default=False, description="是否覆盖已有文件") class FileWriteSkill(Skill): name = "file_write" description = "将内容写入本地文件,默认不允许覆盖已有文件" parameters_model = FileWriteParams requires_confirmation = True def execute(self, params): if os.path.exists(params.path) and not params.overwrite: return SkillResult( ok=False, error="目标文件已存在,请确认是否覆盖", ) with open(params.path, "w", encoding="utf-8") as f: f.write(params.content) return SkillResult(message=f"文件已写入: {params.path}")记忆读写可以先用一个简单的内存存储,后续再替换为数据库或向量库:
class EpisodicMemory: def __init__(self, max_items: int = 20): self.events = [] self.max_items = max_items def add(self, event: str): self.events.append(event) if len(self.events) > self.max_items: self.events.pop(0) def recent_facts(self, k: int = 5) -> str: return "\n".join(self.events[-k:])记忆最容易被忽略的地方是“写入策略”。不是所有工具结果都值得记,只有对后续任务有影响的事实才需要写入。否则记忆会变成上下文噪音,甚至污染模型判断。
3.3 在 Agent Loop 中把 Skill 串起来
注册所有 Skill 后,把它传给模型调用层:
SKILL_REGISTRY = { "web_search": WebSearchSkill(), "file_write": FileWriteSkill(), } def call_model(messages, tools): # 这里对接具体模型服务,OpenAI 兼容接口通常传入 tools 参数 # 实际项目需要根据模型厂商的协议调整字段名 return model_client.chat.completions.create( model="your-model", messages=messages, tools=[tool.schema() for tool in tools.values()], )在循环里,模型返回工具调用后,统一从注册表执行:
for call in resp.tool_calls: skill = SKILL_REGISTRY[call.name] result = skill.run(**call.args) messages.append({ "role": "tool", "tool_call_id": call.id, "content": result.model_dump_json(), })这里有几个容易出错的细节:
- 工具调用 ID 必须原样回传,否则模型服务无法关联工具结果。
content必须是可解析的 JSON 字符串,不能是 Python 对象。- 模型一次可能请求调用多个工具,需要逐个执行,并逐一回填结果。
3.4 给 Skill 增加安全确认:Human-in-the-loop
不是所有操作都应该由模型自主完成。写文件、发邮件、删数据、转账这类操作,建议执行前暂停一次。
def confirm(message: str) -> bool: # 实际项目中可以弹出 UI、发送审批消息或读取一个确认文件 user_input = input(f"{message} [y/N]: ") return user_input.strip().lower() in ("y", "yes") def run_skill_with_guardrail(skill_name: str, kwargs: dict): if skill_name not in SKILL_REGISTRY: raise UnknownSkillError(skill_name) skill = SKILL_REGISTRY[skill_name] if getattr(skill, "requires_confirmation", False): if not confirm(f"即将执行 {skill_name}({kwargs}),是否继续"): return SkillResult(ok=False, error="用户取消执行") return skill.run(**kwargs)这个实现非常简单,但思路值得沿用:把“能否执行”和“如何执行”分开判断。即使模型决定调用高风险 Skill,执行前仍然有人工确认环节。
3.5 给 Agent 增加评估反馈机制
没有评估,Agent 就只能靠肉眼观察结果。一个最基础的评估组件可以这样设计:
def evaluate_output(question: str, output: str, expected_keywords: list[str] | None = None): if expected_keywords: missing = [kw for kw in expected_keywords if kw not in output] if missing: return { "pass": False, "reason": f"输出缺少关键内容:{missing}", "question": question, "output": output, } return { "pass": True, "reason": "断言通过", "question": question, "output": output, }更完整的做法是准备一组回归用例,每个用例包含“问题、期望结果断言、不允许出现的内容、运行环境”。每次修改 Prompt、Skill 或模型版本后,都把这组用例跑一遍。
注意:不要只验证程序能启动,还要验证输入、输出、异常分支和日志是否符合预期。Agent 程序能运行但结果错误,比直接崩溃更难排查。
4. 参数配置与生产部署:学习环境和生产环境的差距
很多 Agent 在本地跑得通,上线就崩,问题往往出在参数配置和生产环境差异上。
4.1 一个 Skill 需要配置哪些运行参数
下面是常规应配置的参数,实际项目可根据含义增加或删减:
| 参数 | 含义 | 典型值 | 调大的影响 | 调小的影响 |
|---|---|---|---|---|
| timeout | Skill 单次执行超时 | 5-30 秒 | 等待更久,降低超时失败率 | 更快失败,但可能误杀慢任务 |
| max_retries | 失败重试次数 | 0-3 | 提高成功率,但增加耗时 | 快速失败,对偶发错误不友好 |
| max_results | 查询类结果数量上限 | 5-10 | 信息更多,但占用上下文 | 更省 token,但可能丢关键信息 |
| cache_ttl | 结果缓存时间 | 0-300 秒 | 减少重复调用,但可能返回旧数据 | 实时性更强,但成本上升 |
| concurrency_limit | 同一 Skill 并发上限 | 1-10 | 吞吐更高,但可能打爆下游 | 更稳,但任务排队时间长 |
| requires_confirmation | 是否需要人工确认 | 高风险操作设为 True | 更安全,但增加等待 | 更自动化,但风险升高 |
| log_level | 日志详细程度 | INFO / DEBUG | 排查方便,但日志量大 | 日志少,节省存储 |
调参时一定要结合业务。比如写文件的接口服务,超时设 5 秒大概率不够,因为磁盘繁忙时写大文件可能超过 10 秒;而搜索接口如果 10 秒还没返回,更可能是网络问题,重试 2 次意义有限。
4.2 Agent Loop 本身的运行参数
Agent 循环的参数也很关键,尤其是迭代上限和终止条件:
agent_loop( user_task="分析用户反馈并生成改进报告", model=model, available_skills=SKILL_REGISTRY, max_iterations=15, )这里max_iterations是防止 Agent 进入死循环的最重要防线。设置过小,复杂任务完不成;设置过大,一个问题可能跑几分钟甚至一直循环。生产环境建议分场景配置,例如简单问答 5 次,复杂任务 20 次,超限后返回“任务未完成”并附上已执行步骤。
其他相关配置:
- temperature:一般 0 到 0.3 比较适合工具调用类任务,过高会产生随机参数或不稳定决策。
- top_p:和 temperature 类似,通常保持默认或配合调整,不要两个同时大幅度调高。
- stop 条件:可以增加关键词停止,比如出现“任务完成”或“无法继续”时提前结束。
- 工具数量:一次传给模型的工具不要过多,建议按场景分组加载,避免模型在几十个工具间犹豫。
4.3 生产环境比 Demo 多考虑哪些事情
Demo 里可以只用print输出结果,但生产环境至少还要补齐这些:
- 追踪与审计:记录每一轮用户输入、模型响应、工具调用、工具结果和最终输出。
- 幂等设计:同一个工具调用如果重试,不能产生重复副作用。比如写文件可以用“覆盖前校验版本号”,发通知可以用“任务 ID 去重”。
- 限流与配额:防止单个用户的任务占用全部模型并发。
- 回滚策略:Prompt 或 Skill 代码变更后,保留上一版本,方便快速回退。
- 权限隔离:Agent 运行环境只给最小权限,不要让它拥有整个服务器权限。
- 数据安全:工具结果中如果包含敏感信息,要在发给模型前脱敏或过滤。
- 监控告警:对循环超限、工具失败率高、模型返回异常等情况设置告警。
学习环境和生产环境最大的差异是“失败成本”。Demo 中失败无非重新运行,生产环境中一次错误调用可能造成数据覆盖、资损或安全事故。这也是为什么权限和安全确认必须提前设计。
5. 高频踩坑与排查思路
实际开发中,Agent 项目的问题通常集中在工具调用、上下文、记忆和终止条件几个方向。下面列出高频问题。
5.1 模型始终不调用工具,或调用格式错误
现象:模型只返回文本,完全不调用 Skill;或者调用时参数缺字段、参数类型不对。
常见原因:
- 模型本身不支持 Function Calling,或当前模型版本要求不同的调用协议。
- 工具描述不清晰,模型不知道这个工具适合什么问题。
- JSON Schema 里有冲突定义,比如要求
required但缺少对应properties。 - 一次传了太多工具,模型选择困难。
- 模型没有在 Prompt 中被明确要求“当需要最新信息时调用搜索”。
检查方式:
- 打印传给模型的
tools列表,确认 Schema 完整。 - 用一个小测试请求,只传一个 Skill,看模型是否能调用。
- 查看模型服务返回的原始响应,确认
finish_reason是不是tool_call。
处理建议:
- 每个工具的描述写清楚“在什么场景使用”和“不要误用”。
- 参数尽量少,字段名要与常见语义一致。
- 如果模型对 JSON Schema 支持不好,可以降级为“由模型输出 JSON,再由程序解析”的模式。
5.2 工具调用成功了,但 Agent 不会利用工具结果
现象:模型发出tool_call,工具正常返回结果,但下一轮模型没有基于工具结果继续,而是开始空泛总结。
常见原因:
- 工具结果格式过碎或过长,模型提取不到关键信息。
- 工具返回的
data字段没有做摘要,直接把原始大对象塞进上下文。 - 工具结果缺少
ok/error语义,模型无法判断这次调用是否成功。
处理建议:
- 工具结果统一做摘要,尽量控制在几百字以内。
- 把返回结果里的关键条目重新组织成自然语言,而不是返回裸 JSON。
- 在 Prompt 中明确:“优先使用最新的工具结果回答,不要凭记忆猜测。”
def format_search_result(result: SkillResult) -> str: if not result.ok: return f"搜索失败:{result.error}" items = result.data.get("items", []) lines = [f"{i + 1}. {item}" for i, item in enumerate(items[:5])] return "搜索结果摘要:\n" + "\n".join(lines)这样模型看到的是可读文本,而不是难以解析的嵌套结构。
5.3 长任务出现 “Agent execution terminated due to error” 或直接中断
现象:任务执行到一半,日志里出现类似Agent execution terminated due to error.的异常,或者循环直接退出。
常见原因:
- 超过
max_iterations上限。 - 工具执行抛出未捕获异常,导致循环中断。
- 上下文长度超限,模型接口直接报错。
- 模型 API 限流或网络超时。
- 工具参数包含不可序列化对象,回填到 messages 时失败。
检查方式:
- 先看异常栈,定位是模型调用层还是 Skill 执行层。
- 查看任务是第几轮中断的,统计每轮 token 消耗。
- 检查最大迭代次数和实际调用次数。
- 检查工具返回结果是否经过
model_dump_json()或json.dumps()序列化。
处理建议:
- 在 Agent 循环的最外层包一个
try/except,把异常转成可阅读的错误结果,而不是让任务直接崩溃。 - 对每个 Skill 的执行加入独立异常捕获,保证一个 Skill 失败不影响整个循环。
- 在每次迭代前检查上下文长度,提前做摘要裁剪。
- 为外部 API 调用加入超时和指数退避重试。
def safe_run_skill(skill, kwargs): try: return skill.run(**kwargs) except Exception as e: return SkillResult( ok=False, error=f"Skill {skill.name} 执行异常: {type(e).__name__}: {e}", )# 排查时可以先从日志里筛选异常片段 grep -n "Agent execution terminated\|ToolFailed\|RateLimit" agent.log5.4 记忆污染导致前后矛盾
现象:Agent 在长会话后半段开始混淆信息,把上一轮确认过的结论又说错。
常见原因:
- 把所有历史消息都堆进上下文,不区分重要程度。
- 记忆写入过早,把中间过程当成了最终事实。
- 检索到的记忆与当前问题无关,却被强制拼进上下文。
- 多个会话共用一份记忆,没有按用户或任务隔离。
处理建议:
- 记忆写入前设置审核条件,比如“只有工具执行成功且结果与任务目标相关时才写入”。
- 记忆数据带上来源、时间和置信度,模型检索时能看到元信息。
- 按会话 ID 或用户 ID 隔离记忆空间。
| 现象 | 常见原因 | 检查方式 | 处理建议 |
|---|---|---|---|
| 同一事实前后回答不一致 | 记忆写入无审核,或没有读取记忆 | 打印每次读到的记忆片段 | 加上写入审核,记忆与当前任务做相关性过滤 |
| Agent 记不住关键要求 | 短期记忆容量太小,被工具结果挤占 | 查看上下文裁剪策略 | 为“用户关键要求”单独设置持久槽位 |
| 答非所问,内容来自旧任务 | 跨会话记忆未隔离 | 检查记忆表是否带 session_id | 按会话、用户、项目维度隔离 |
5.5 权限过于宽泛,破坏性操作没有确认
现象:Agent 可以直接覆盖生产文件、批量发消息或删除数据,一旦模型判断失误,后果严重。
常见原因:
- 所有 Skill 都没有
requires_confirmation。 - 模型 Prompt 中缺少“哪些操作不能直接执行”的约束。
- Agent 运行环境使用了过大的文件或网络权限。
处理建议:
- 默认所有写操作都标记为高风险。
- 在运行环境里用独立账号,只授予任务所需的最小权限。
- 对敏感操作保留审计日志,包括用户、时间、参数、结果。
5.6 没有回归评估,改了 Prompt 后老场景退步
现象:新功能上线后,原有场景突然表现变差,但没有人知道是哪次改动导致的。
常见原因:
- Prompt 修改没有版本记录。
- Skill 行为变化没有配套回归用例。
- 只测了“正常路径”,没有测“工具失败路径”和“拒绝路径”。
处理建议:
- 建立一份回归用例集,至少覆盖:正常调用、工具失败、参数错误、用户取消、超长上下文、模型拒绝。
- 每次变更后跑一遍用例集,并记录通过率。
- 把用例集纳入 CI,和普通代码测试一起执行。
6. 从“必要技能”倒推一条 Agent 开发学习路线
这部分回应很多人都关心的“Agent 开发学习路线”和“Agent 面试题”话题。学习路线不需要贪多,关键是先补齐必要技能。
6.1 按工程难度排序的学习路径
| 阶段 | 学习内容 | 完成标准 |
|---|---|---|
| 基础 | 大模型 API、Prompt 设计、上下文长度 | 能写出稳定输出格式的对话程序 |
| 工具能力 | Function Calling、JSON Schema、工具错误返回 | 能让模型稳定调用一个自定义函数 |
| 循环 | Agent Loop、迭代上限、结果回填 | 实现一个“调用工具-观察-继续”的最小 Agent |
| 记忆 | 短期记忆、长期存储、向量检索 | 实现跨会话记住关键事实的 Agent |
| 规划 | 任务拆解、反思、计划修正 | 实现复杂任务的自动拆步与阶段性校验 |
| 评估 | 回归用例、评估 Prompt、错误分类 | 建立一套可重复跑的 Agent 测试集 |
| 安全 | 权限控制、人工确认、审计日志 | 给高风险操作加护栏 |
| 工程化 | 日志追踪、限流、监控告警、回滚 | 把 Agent 部署成可运维的服务 |
每个阶段都尽量写一个可运行的小项目,而不是只看概念。比如学 Function Calling,就写一个“天气查询”工具;学记忆,就写一个“会议记录助手”。
6.2 面试和团队评审中会被反复追问的问题
这些问题本质上都在考察 Agent Skills 的工程完备度:
- 你的 Agent 如果陷入死循环怎么办?
- 工具调用失败后,Agent 如何恢复?
- 你怎么保证 Agent 不会执行破坏性操作?
- 长上下文到了上限,任务还没完成怎么办?
- 多个工具结果之间存在冲突,模型应该听谁的?
- 修改 Skill 后,如何证明旧场景没有被破坏?
- 记忆数据如果被写错了,怎么纠正?
如果这些问题只能回答“提示词里写了”,说明 Agent 还停留在实验阶段;如果能给出明确的检查点、默认值、失败策略和日志,才是生产级答案。
6.3 可复用的检查清单
新 Skill 上线前检查:
- [ ] 是否有明确的名称和描述?
- [ ] 参数是否有范围约束和默认值?
- [ ] 返回格式是否遵循
SkillResult统一协议? - [ ] 失败和超时是否有明确返回?
- [ ] 是否在安全评估中确认该操作的风险等级?
- [ ] 高风险操作是否设置了
requires_confirmation = True? - [ ] 是否记录了日志,包含调用方、参数、耗时、结果?
- [ ] 是否加入回归用例集?
Agent 进入生产前检查:
- [ ] 最大迭代次数是否设置,是否分场景配置?
- [ ] 上下文超限是否有自动裁剪策略?
- [ ] 工具异常是否会被捕获并返回给模型?
- [ ] 权限是否按最小权限原则配置?
- [ ] 是否有完整追踪日志,能复现一次任务?
- [ ] 是否有限流、缓存和超时控制?
- [ ] 是否有回滚方案?
- [ ] 是否跑过至少一轮完整回归?
排查 Agent 问题时按这个顺序查:
- 输入是否正确:用户指令、历史消息、附加文件。
- 模型层:是否支持工具调用,返回的
finish_reason是什么,是否有异常。 - 工具层:Skill 是否注册,参数是否解析成功,工具执行是否抛错。
- 上下文层:消息是否超长,工具结果是否被正确回填,是否有内容被意外截断。
- 权限层:是否因为缺少权限或人审导致执行被拒绝。
- 配置层:超时、重试、迭代上限、并发是否设置过小或过大。
- 评估层:是否能用一条简单命令复现问题,并有无对照用例。
7. 写在最后:先补齐必要项,再追求花哨
回到 “What agent skills are necessary?” 这个问题,真正有价值的答案不是列一个无限多的能力清单,而是找到“最低完备集”。在这个集合里,工具调用协议、记忆管理、安全确认、错误恢复、评估反馈和可观测性,每一项都比单纯的“模型换得更大”更关键。
对一个刚开始做 Agent 的团队,建议先把一个最小闭环跑通:一个能调工具的循环,一个能返回结构化结果的 Skill,一个能在失败时恢复的异常处理,一个能证明结果没跑偏的评估用例。这四件事做完,Agent 才具备交给真实用户使用的底线。
下一步可以从三个方向扩展:一是给 Agent 增加可信任的长期记忆,二是加入更完善的人工审批流,三是让多个 Agent 以清晰的消息协议协作。每一层扩展都会引入新的 Skill 需求,但底层的工程纪律不会变:所有能力都要有可解释的输入输出,所有失败都要有可追踪的日志,所有变化都要有可回归的测试。把这条主线守住,Agent 开发基本不会走上“什么都试一下但什么都不稳定”的老路。