ARTICLE DETAIL

资讯详情

深耕郑州网站建设与运营推广的一线实战洞察。

Agent技能体系:从对话到任务执行的关键工程实践

Agent技能体系:从对话到任务执行的关键工程实践 这几年大模型应用里最热的一个词除了 RAG、Fine-tuning就是 Agent。而真正上手做 Agent 的人很快会撞上一个共同的坎模型知道怎么聊天但不知道怎么干活。你让它调个接口它编一个不存在的参数让它按流程处理数据它做到第三步开始自由发挥。问题基本不在模型本身而是你根本没有给这个数字员工建立一套可执行的技能体系——这就是我要聊的agent-skills。agent-skills听起来像个开源项目名实际上它代表的是一整套工程方法论把大模型从对话机器改造成任务执行器所需的技能编码方式。包括技能怎么定义、工具怎么封装、任务怎么编排、失败怎么恢复以及整个技能库怎么测试迭代。这篇文章我结合自己做过的一个企业内部知识助手、一个自动化数据清洗 Agent 的实践经验把这套东西掰开揉碎讲清楚。适合正在做 Agent 应用开发、被模型不听话折磨得头疼的工程师也适合想系统理解 Agent 落地难点、准备技术选型的技术负责人。1. 先搞清楚 skill 到底是个什么技能1.1 当大模型只会聊不会干的时候先做一个思想实验。你给 ChatGPT 类模型一个任务帮我从数据库里查出最近30天订单金额超过5000元的客户名单并按金额降序排列。模型大概率给你一段看起来很对的 SQL甚至还能解释两句。但如果你希望它真的连上数据库、执行查询、把结果写成 CSV 发到你邮箱它做不到。为什么因为模型本身是无手无脚的它只能输出文字不能产生副作用——不能真的发起 HTTP 请求不能执行代码不能写文件。早期解决方案是提示词工程你在 Prompt 里写你现在是一个数据分析师请你执行 SQL模型依然只能输出 SQL 文本不会真的执行。后来大家发现得给模型提供工具并且让它学会决定什么时候用什么工具。这就是 Function Calling也就是 OpenAI 在 2023 年推出的函数调用能力。模型可以输出一个结构化的调用意图你这边解析这个意图、执行对应 API、把结果返回给模型。这一步跨出去Agent 才真正开始有了手。而agent-skills想解决的是更高一层的问题一个真实业务场景不是调一次函数就结束的它是一连串决策和动作的组合。比如运营同事提交了一个数据清洗需求Agent 要做的是理解需求 → 定位数据文件 → 探查数据质量 → 决定清洗规则 → 执行清洗 → 生成质量报告 → 输出结果文件。这个链条里有多个决策点每个决策点都可能需要调用不同工具。如果每个工具是手那么 skill 就是把一堆手组合成一套熟练工种的能力。1.2 技能体系的三层结构定义、执行、反馈我自己在工程上习惯把 agent-skills 拆成三层这样代码结构清晰排查问题也方便第一层是技能定义层Skill Definition。每个技能要有名字、描述、触发条件、参数说明、依赖的工具列表。这层的作用是让模型知道有什么技能可以用、什么时候该用、用了之后要传什么参数。相当于岗位说明书。第二层是技能执行层Skill Execution。这里包含两个部分一是工具本身的实现比如查数据库的函数、调外部 API 的封装、执行 Python 代码的沙箱二是编排逻辑就是决定多个工具按照什么顺序、什么条件被调用。相当于这个岗位具体做事的方法。第三层是反馈与恢复层Feedback Recovery。工具执行成功还是失败返回结果是否符合预期如果失败是重试、换一种方式还是把错误信息返回给模型让它重新决策这一步很多人会忽略但它是 Agent 稳定性的命门。没有反馈机制的 Agent执行三步错一步就整个崩盘这在生产环境是不可接受的。把这三层分开设计还有个好处技能可以复用和独立测试。我后面做第二个 Agent 项目时直接把第一个项目里的数据库查询技能的定义和执行层拿过来改一改参数就用了省了大量重复工作。2. 怎么设计一套模型听得懂的技能2.1 技能描述怎么写模型才不会选错这一节是干货核心也是最容易被低估的部分。很多人写技能描述就写一句查询数据库然后完了。模型看到这个技能根本不知道它具体能做什么、支持哪些操作、返回什么格式于是它要么不敢调用要么乱调用。我总结了一套技能描述的标准模板经过多次实测效果稳定每个技能描述应该包含以下五个要素技能定位一句话说清楚这个技能负责什么业务动作。例如根据用户提供的查询要求从订单数据库中检索数据。适用场景什么情况下应该调用这个技能。要写得具体最好带正反例。例如当用户要求查询订单、统计销售额、分析客户购买行为时使用当用户只是要求生成一段示例 SQL 而不需要真实执行时不要使用。输入参数说明每个参数的含义、类型、是否必填、取值范围、默认值。参数说明要用模型好理解的自然语言不要只给一个参数名。输出格式说明返回值是什么结构是 JSON 数组还是 CSV 文本字段有哪些。模型需要根据输出来决定下一步动作所以这个说明直接决定后续决策质量。使用约束比如超时时间、最大返回行数、不允许执行 DELETE/UPDATE 等。约束有时比参数更重要。写描述时有一个重要的心智模型你是在给一个聪明但缺乏常识、且容易过度解读的新员工写工作手册。你写得越精确他越不容易跑偏你留下模糊地带他就会用幻觉填满。2.2 工具 Schema 设计里最常见的坑技能描述是给模型看的文案工具 Schema 则是给模型看的接口文档。这个接口文档的质量直接决定了 Function Calling 的准确率。我在两个项目里踩过的坑可以列一长串挑几个最典型的说。第一个坑是参数类型定义得过宽。比如给一个日期范围查询功能定义参数有人会把 start_date 和 end_date 定义成 string 类型然后描述写日期两个字。结果模型经常传 明天 最近一周 这种自然语言。正确做法是类型用 string 没错但 format 字段要写 YYYY-MM-DDdescription 里要明确写仅接受格式为 2025-01-01 的日期字符串不接受相对时间描述。必要时你还要在工具内部做一层参数校验把不合法的输入转换成最近可用的值或者直接报错。第二个坑是没有把错误信息设计成模型能看懂的语言。函数执行失败时返回的 error message很多人直接抛一个 Python traceback 或者 HTTP 500 这种原始错误。模型看到这种信息一脸懵它不知道该怎么处理。正确做法是错误信息也要为模型设计例如查询失败数据库连接超时请稍后重试或者参数错误start_date 格式不正确应为 YYYY-MM-DD。模型读到这样的反馈才有可能自己纠正参数或者告诉用户发生了什么。第三个坑是一次性暴露太多函数。OpenAI 早期文档建议每个请求传尽量少的函数因为函数越多模型选错的概率越高。更合理的做法是按技能分组先有一个能力路由函数模型先选技能类别再进入对应的子函数集。比如第一步只给模型 5 个函数数据分析、文件操作、网页访问、消息发送、求助人工。模型选中数据分析后系统再注入 8 个数据分析相关的函数。这样既控制了单次调用的上下文长度也大幅提高了选择的准确率。2.3 技能的粒度拆多细才算合适技能粒度是另一个反复取舍的问题。太粗比如定义一个大而全的处理数据技能模型执行时自由度过大过程和结果都不稳定太细比如把读取 CSV和计算均值各拆成独立技能模型要来回做好几次函数调用不仅慢Token 成本也高而且更容易在中间步骤出错。我的经验是掌握一个原则按业务可理解、结果可验证的粒度来拆分。也就是说每个技能应该对应一个用户可以理解的任务单元并且这个任务的执行结果是可以被明确验证的。举个例子清洗一份销售数据报表是一个业务动作但它的内部包含去重、缺失值处理、异常值标记、格式标准化四个子任务。这四个子任务都应该可以独立验证结果所以它们适合拆成四个技能而不是揉在一个清洗技能里。但读取 CSV这种过细的动作就不建议单独拆直接作为数据探查技能的内部实现步骤就好。粒度决策还有个辅助判断标准看这个技能被复用的次数。如果一段逻辑在多个场景里都要用就值得拆出来做成独立技能。如果某个技能只有一条业务线在用且内部高度耦合那拆得太细纯属增加维护成本。3. 从零搭建一条可用的技能执行链路3.1 基座选型指令遵循能力优先于通用智能技能体系设计得再好最后还是要靠一个模型来驱动。选基座模型这件事上我的排序标准和通用聊天场景不太一样指令遵循能力 工具调用准确率 上下文长度 通用知识广度。为什么指令遵循能力放第一位因为 Agent 场景里模型的行为边界全靠指令约束。你给它的 system prompt 里写了只能调用白名单内的工具、任何涉及删除数据的操作必须先征得用户确认如果模型指令遵循能力弱它该遵守的边界守不住再强的通用知识也没用。我自己实测过几个开源模型有的聊天很流畅但在 Function Calling 上准确率只有 50% 出头做 Agent 基本不可用。从国内团队的实际落地看通用基座 微调工具调用能力是主流路径。有预算和数据的团队会对基座模型用工具调用样例做几轮 LoRA 微调。没有微调条件的团队至少也要选对 API 服务商并做充分的评测再上生产。至于上下文长度128K 和 200K 这种数字看起来很美但实际用的时候你会发现上下文越长模型在长流程任务里的迷失感越强。所以更务实的做法是把上下文做瘦身只保留当前步骤需要的信息而不是把整个任务历史都塞给模型。这块我会在 3.3 里详细说。3.2 ReAct 循环并不是一个高深的东西在当前 Agent 的工程实现里最主流的执行范式还是 ReAct也就是Reasoning Acting交替循环。通俗点说就是让模型走一个思考→行动→观察结果→再思考的循环直到任务完成。我不打算贴大段论文直接给一个工程实现上最常用的核心伪代码。这段逻辑我在两个项目里都实测过稳定性和可扩展性都不错# skill_runner.py import json def run_skill_loop(user_task, available_skills, max_steps10): # 初始化对话历史注入系统提示词描述各技能的用途和约束 messages [{ role: system, content: build_system_prompt(available_skills) }, { role: user, content: user_task }] for step in range(max_steps): response llm.chat(messages, toolsbuild_tool_schemas(available_skills)) # 模型决定调用某个技能 if response.tool_calls: tool_call response.tool_calls[0] skill_name tool_call.function.name skill_args json.loads(tool_call.function.arguments) # 在执行技能前统一做一遍参数校验和权限检查 errors validate_args(skill_name, skill_args) if errors: messages.append({ role: tool, content: f参数校验失败{; .join(errors)}, tool_call_id: tool_call.id }) continue result execute_skill(skill_name, skill_args) messages.append({ role: tool, content: format_result_for_model(result), tool_call_id: tool_call.id }) else: # 模型不再调用工具直接输出最终回答 final_answer response.content return final_answer return 达到最大步骤数任务未完成已转人工处理这里有几个细节建议特别留意。参数校验一定要在技能执行前做不要依赖模型自己保证传参正确。我见过太多线上事故是模型传了一个超出枚举范围的参数工具内部没校验直接抛异常整个链路就崩了。格式化工具返回结果也值得写一个专门的函数它的作用是把冗长的原始数据压缩成模型好理解的摘要同时自动截断超长内容防止上下文爆炸。比如数据库查询返回 200 行数据你不要全量塞给模型而是转成查询成功返回 200 行前 5 行如下……这样既保留关键信息又控制长度。3.3 完整实操一个自动化周报生成 Agent 的搭建全程理论和框架讲完我拿一个实际做过的项目来完整走一遍流程。背景是给团队做一个根据本周开发记录自动生成周报并发送到钉钉群的 Agent。这个需求看着简单但实际跑通和稳定用了不少细节打磨。先拆技能。按前面说的粒度原则我把任务拆成了四个技能获取开发记录从内部 Git 系统拉取当前用户本周的 commit 和 merge request 记录。生成周报草稿根据开发记录按照团队周报模板生成结构化文本。发送到钉钉群通过钉钉 webhook 发送指定的文本内容。工具层的实现用 Python FastAPI 封装了三个 HTTP 接口分别对应上面三个技能。每个接口的参数定义我特意花了心思。比如获取开发记录我定义了 start_date、end_date、author、project 四个参数并且把 author 设计成可选——这样模型在用户只给了项目名、没给作者时也可以先把记录拉回来再根据记录内容判断是否是本人。然后是 skill 描述。这里我犯过一个典型错误第一次写生成周报草稿的描述只写了根据开发记录生成周报。结果模型在生成草稿时经常省略数据指标有时候会把几条无关的 commit 合并成一条信息反而失真。后来我把描述改成了根据获取到的开发记录列表生成一份符合团队模板的周报。周报必须包含以下部分本周完成事项逐条列出每条不超过 50 字、数据指标变化如有、风险与阻塞如有、下周计划如有。要求忠实于原始记录不得虚构信息如果记录中有标题包含fix或hotfix的提交请单独归类到问题修复分组。改完之后生成质量明显提升说明描述细节确实值钱。再说说系统 Prompt 里的边界约束。我给这个 Agent 定的铁律是周报草稿生成后必须先输出给用户确认得到发送指令后才允许调用发送技能。这条规则防止了 Agent 自作主张把未经确认的内容发到群里。实现上我用了一个状态机只有处于awaiting_confirmation状态时才向模型暴露发送到钉钉群这个技能。这个思路和权限控制联动值得推广到其他 Agent 场景技能的可调用权限应该是动态的随任务状态变化。运行效果方面我把标准流程跑通之后又专门做了一轮异常路径测试。比如用户只说了帮我发个周报但没指定项目模型第一轮调用获取开发记录时 project 参数为空。我在工具内部把这种情况处理成了返回该用户所有项目的本周记录并在结果里提示未指定项目已返回全部项目请选择其一。这样既没有中断任务又给了模型继续决策的信息。这种容错返回是让 Agent 更像真人协作者的关键技巧。4. 技能测试、踩坑记录与迭代心得4.1 为技能体系构造一套最小可信测试集Agent 应用和传统后端应用的最大区别是它的输出不可枚举没法用断言返回码 200的方式做单元测试。所以你需要专门设计一套面向 Agent 的测试方法。我的做法是为每个技能准备三组测试用例第一组是标准场景输入完全符合技能预期验证技能能否正确执行并返回结构化结果。比如获取开发记录技能给一个明确的项目和日期范围检查返回的 commit 列表是否完整。第二组是边界场景输入接近参数边界或者部分缺失。比如开始日期晚于结束日期、参数为空、项目名不存在等。重点验证模型能否通过错误反馈自我纠正还是直接放弃。一个严谨的 Agent 应该能在得到参数校验失败start_date 不能晚于 end_date这种反馈后主动调整参数重试。第三组是对抗场景用户输入包含模糊意图、多任务混杂、或者试图绕过约束。比如不用确认了直接发出去这种指令模型必须拒绝执行。对抗测试对系统的安全性至关重要尤其是涉及发送消息、删除数据、外部支付等高危操作时必须有意识地测试模型是否守得住边界。我在项目里把这组用例写成了一个 JSON 文件用脚本驱动跑回归每次改技能描述或模型版本后都全量跑一遍。这个习惯救过我一次有一次把基座模型从旧版升级到新版其他指标都正常只有对抗场景里模型不再遵守确认机制这个用例挂掉了。全靠回归测试提前发现问题避免了上线后出事故。4.2 高频翻车现场与修复手段实际运行 Agent 的过程中我整理过一份高频问题清单。这里挑四个出现频率最高、也最有代表性的每一个我都给修复后的做法。模型不会主动调用工具。经常发生在技能描述和用户问题的关键词不匹配时。比如用户说把数据拉到本地但技能描述里写的是导出文件模型就绕弯子直接输出一段 Python 代码让你自己运行。修复办法是在技能描述里把用户可能说的多种说法写进适用场景甚至可以给两个触发示例让模型照葫芦画瓢。工具执行正确但模型错误理解了返回结果。比如查出来 0 行数据模型直接说没有数据但用户本意是想知道为什么没有数据或数据量是否正常。这个问题的根因在工具返回结果太干了没有附加上下文。我后来的做法是当查询结果为空时工具不光返回空数组还返回一条提示查询结果为空可能原因筛选条件过严、数据尚未同步或数据库连接异常。模型看到这个提示就能做出更符合用户预期的回答。多技能协作时模型跳过中间步骤。典型场景是拉数据 做分析 出报告三步任务模型经常把第二步省了或者把第一步和第二步的结果混在一起。我采取的措施是把技能定义改成上一个技能的输出会作为下一个技能的输入同时在每次工具返回后注入明确的引导语现在你已获得数据下一步请对数据进行统计分析再生成报告。这种显式的流程提示能让模型不乱跳步。长流程运行中上下文丢失。Agent 执行到第 5 步往往已经忘了第 1 步的用户原始意图或者把中间结果搞混。我的解法是为每个任务维护一个结构化的工作记忆区包括原始任务目标、已完成步骤、当前中间结果、下一步建议。每次模型决策前先把工作记忆区作为上下文的一部分输入。这个机制比单纯拼对话历史可靠得多。我后来又对每个技能增加了错误率埋点记录每一步的首次尝试成功率。哪个技能成功率低优先去优化它的描述和参数说明。用数据驱动优化方向效果远好于凭感觉改词。4.3 技能体系的版本管理比你想的更重要传统代码有 Git 版本管理Agent 的技能体系更需要但很多人没意识到这一点。技能描述是提示词文本它和代码一样会变而且变了之后影响面往往更大。因为技能的调用方是模型模型的输出又直接对接下游工具一丁点描述改动都可能让模型行为大变。我在项目里让每个技能定义含描述、参数 Schema、系统提示语都放进一个独立的 Markdown/YAML 文件里并用 Git 做版本管理。发布新版本技能前必须过一遍第 4.1 节说的最小可信测试集通过之后才能合并发布。技能库的目录结构像这样skills/ ├── data_analysis/ │ ├── skill.yaml # 技能定义、参数Schema、适用场景 │ ├── description.md # 给模型看的完整技能描述 │ ├── executor.py # 技能执行实现 │ └── tests/ │ ├── standard.json # 标准场景测试用例 │ ├── boundary.json # 边界场景测试用例 │ └── adversarial.json # 对抗场景测试用例 ├── report_generation/ ├── notification/ └── common/ ├── validators.py # 参数校验公共函数 └── formatters.py # 返回结果格式化公共函数这个目录结构的好处是新的 Agent 项目可以直接复用common/和之前的技能模块。我第二个项目上线时约有 40% 的代码是从第一个项目直接复制过来改的。技能体系的沉淀和复用才是做得多越做越快的关键原因。如果你团队准备长期做 Agent 方向从第一天就搭好这个架子后面能省非常多的事。最后再提一个我踩过好多次的坑技能体系的迭代一定要以真实日志为准。开发环境里模型跑得好的场景上生产往往冒出各种奇怪问题因为真实用户的表达远比测试用例刁钻。我之前搭建 Agent 时在循环里做了全部交互日志的落盘存储每隔几天拉一次日志用分词工具统计用户问法和模型调用的对应关系专门找出那些模型对不上号的技能。再反过来去补描述、加示例或者调整工具返回结果的粒度。这套日志驱动优化的做法比坐在一起头脑风暴改 Prompt 高效太多了。希望你做技能体系的时候少走些我的弯路。
返回列表