首页/新闻资讯/正文详情

为AI Agent打造可视化技能管理器:统一沉淀工具、流程与提示词

发布时间:2026/11/3 21:36:43 来源:云帆数科 栏目:资讯中心
为AI Agent打造可视化技能管理器:统一沉淀工具、流程与提示词
1. 为什么我会去做一个技能管理器而不是继续堆 Agent先交代一下背景。我大概从去年年初开始接触 AI Agent前后做了几个 Rag 问答机器人、自动写周报的工作流、还有给运营团队用的竞品信息收集器。做到第三四个项目的时候一个特别明显的问题开始冒出来Agent 的技能散得到处都是。什么是技能说白了就是 Agent 能调用的工具、函数、提示词模板、外部 API 接口甚至是一套固定的多步处理流程。比如我的周报 Agent 里面有整理聊天记录的技能竞品分析 Agent 里面有抓取指定网页正文的技能问答机器人里面有查数据库表结构的技能。每一个单独拿出来都能跑但当我准备做一个新 Agent 的时候才发现这些能力全都没有被好好沉淀。最直观的体验是同一个抓网页正文的函数我在三个项目里写过几乎一模一样的代码只是参数名改了改。这个状态实在说不上健康。那段时间我也试用过几款别人的 Agent 平台有的确实内置了工具市场但问题也很明显要么绑定某个大模型厂商要么技能包上传格式奇奇怪怪要么只能在它们自家的沙箱里跑。我需要的其实很简单——本地方案能让我把所有 Agent 技能放在同一个收纳盒里面可视化地查看、编辑、启用和停用最好还有一个 HTTP 接口方便任何项目随时调用。于是这个可视化技能管理器就从一个内部工具变成了一个正经项目。它的定位很简单通用、不绑架大模型、纯本地部署。今天把这半个多月的开发过程、技术选型、踩坑记录和一些设计思考完整写出来希望能给同样在折腾 Agent 的朋友一点参考。2. 先想清楚技能管理器到底管的是什么在动手写第一行代码之前我花了整整两天时间梳理技能这个概念的边界。这个阶段想得越清楚后面数据结构设计就越省事。2.1 技能的身位工具、流程、提示词的三合一我先给技能定义了一个比较宽的范围一共分三层单次工具调用比如URL 抓正文执行 SQL发 HTTP 请求这类技能一次入参一次出参是最小的原子能力。多步流程编排比如读取本地日报文件 - 调用大模型做摘要 - 发到钉钉群这是把多个原子能力串起来的组合技能。提示词模板与上下文策略比如代码评审专家客服话术生成器这类技能本身不调用外部系统但决定了大模型怎么思考、怎么措辞。很多 Agent 框架把这三类东西分开管理工具是工具流程是流程提示词是提示词。但对我来说它们本质上是同一个东西的不同复杂度表现——都是一个 Agent 在完成某类任务时需要的能力描述。统一管理的一个直接好处是我在界面里为任意技能打上 tags就能通过标签组合出带有代码评审口吻、能读取 Git 提交记录、能调用静态分析工具这样的复合能力而不用去改任何代码。2.2 命名方式为什么用技能而不是工具坦白说早期原型里我一直叫它 Tool Registry后来在一次内部使用中彻底改了名。当时我给我妈做了一个买菜清单 Agent她的原话是这个东西是不是会自动挑便宜的菜 我解释了半天说 Agent 会调用比价工具她似懂非懂。后来我换了个说法它会用你教它的挑菜技能。 她立刻理解了。这就是我的感受对使用者来说工具是开发视角技能是使用视角。一个卖菜 Agent 可能内部要调用 5 个工具但在用户看来它只做一件事——挑菜。如果把视角定在用户那用户需要被管理的就不是工具而是它获得了哪些能力。这个措辞的转变后来也影响了整个 UI 交互设计展示给用户的永远是这个技能能干什么、什么时候被触发、当前是否启用而不是这个函数接收什么参数、内部调了什么 API。2.3 最小信息结构到底长什么样我最终的技能定义包含这几个核心字段类似技能身份证字段作用示例技能 ID唯一标识供 Agent 代码引用fetch_webpage技能名称人类可读的中文名抓取网页正文描述一句话说明能干什么供 LLM 自动选择输入网址返回页面正文 Markdown入参 Schema参数名、类型、是否必填、默认值{url: {type: string, required: true}}执行方式local_function/http/workflow/promptlocal_function执行入口指向具体的函数名、API 地址或工作流 IDplugins.web_scraper.fetch依赖标签所需模型能力、外部服务、权限级别[network, no_auth]启用状态是否允许 Agent 调用true这套结构看起来简单但它解决了一个很关键的问题——让技能是什么与技能怎么实现彻底分离。界面上管理的是前者代码里执行的是后者两者通过技能 ID 关联。这样我新建 Agent 时只需要查看技能列表、挑选需要的技能 ID然后在业务代码里像插积木一样调用管理器提供的统一执行入口即可。3. 技术选型不追流行只看这四件事这一节我会把选型的理由说透。因为市面上成熟的 Agent 框架太多了很多人不是没有得选而是不知道怎么选。3.1 后端为什么用 FastAPI 而不是 Django项目本身是个带 UI 的管理器核心操作是技能的增删改查、执行日志查看、启用状态切换说白了是一个偏重 API 和异步任务的系统。FastAPI 天然契合类型提示转 OpenAPI 文档是零成本的我前端调接口时能直接拿到自动生成的/docs页面省去了手动维护接口文档的时间。另一个让我坚定选 FastAPI 的原因是Pydantic 的 Schema 校验。技能描述里每个入参都是有类型要求的Backend 收到用户保存的 Schema 后需要在执行前做严格校验。Pydantic v2 在这块的性能和数据类体验都很好我几乎不需要额外写参数校验逻辑。注意这不是说 Django 不好。如果你做的是偏后台管理、需要大量 ORM 内置功能、团队熟 Django 的体系Django 完全没毛病。但对我这种单人开发、接口优先、要快速迭代的场景FastAPI 的轻量优势非常明显。后端目录结构非常直白skill_manager/ ├─ app/ │ ├─ main.py # FastAPI 实例 路由注册 │ ├─ models.py # SQLModel 数据模型 │ ├─ schemas.py # Pydantic 请求/响应结构 │ ├─ skill_registry.py # 技能注册与查找核心逻辑 │ ├─ executors.py # 不同执行方式的分发器 │ ├─ workflows.py # 组合流程的解释器 │ └─ api/ │ ├─ skills.py # 技能 CRUD 接口 │ ├─ execute.py # 技能执行接口 │ ├─ logs.py # 日志与统计接口 │ └─ system.py # 健康检查、配置管理 ├─ plugins/ # 所有本地函数技能放这里 │ ├─ web_scraper.py │ ├─ sql_executor.py │ └─ ... └─ storage/ ├─ skills.db └─ logs.db3.2 存储层SQLite 起步小项目不要过早引入 Postgres这可能是本篇文章最有争议的选择。我见过不少人在项目第一天就 docker-compose 起一个 Postgres Redis原因无外乎以后肯定用得着。我的态度是如果后期真的需要迁移成本是可控的但在项目成型前复杂存储带来的心智负担不值得。所以这个项目用了 SQLModel SQLite一个文件解决全部存储问题为什么不用纯 JSON 文件技能之间有依赖关系比如组合技能会引用原子技能纯 JSON 我得自己处理引用一致性。SQL 用外键就解决得很干净。为什么不用 Postgres单用户工具同时在线执行量低SQLite 的写锁问题几乎碰不到。而且备份特别简单——拷贝一个.db文件就完事。如果某天技能数量上十万、需要多进程并发写我才会考虑迁到 Postgres。到那时候代码层面只需要把数据库连接串改一下因为 SQLModel 的 ORM 设计对数据库切换是比较友好的。3.3 前端不用 npm 工程一个 HTML 加 CDN 就够这是让我最土的一个决定但也是我在真实运维中觉得最舒服的决定。技术栈是Vue 3 Element Plus通过 CDN 引用不用构建工具。很多人会觉得不严谨先听我解释原因。这个工具的使用者假设只有我一个人最多加团队两三个人它不需要复杂的路由、状态管理、权限控制核心页面就三个技能列表、技能详情编辑、执行日志。如果我用 Vite Vue 工程化搭建光 node_modules 装依赖就要几分钟更别提每次改完代码要构建、热更新为了一个 3 页面的 UI 搞出 200MB 的项目依赖明显是杀鸡用牛刀。CDN 方案启动后的效果是我只需要用 FastAPI 挂载一个静态 HTML 文件编辑器里直接改源码刷新浏览器立刻生效。开发体验和调试效率都非常高。不过我必须说清楚这个方案的边界。如果你的技能管理器要给多角色使用、要处理复杂路由和状态同步那还是老老实实走工程化吧。我这个方案是为了自用工具这个特定场景量身定的。3.4 技能执行隔离Subprocess 还是 In-Process技能的执行方式字段里有local_function意味着某个技能会直接执行一段 Python 代码。这里有个安全的灵魂拷问到底是在主进程里直接调用还是丢到子进程里执行我先用了一个最省事的方式直接在进程内调用。直到有一次我写了一个抓取网页的技能里面用到了playwright因为某个网站弹窗卡住脚本直接阻塞了 40 秒而整个 FastAPI 服务是单进程的所有请求全部排队等着。那次之后我立刻改成了asyncio.create_subprocess_exec的子进程方式每个技能都在独立子进程里跑带超时控制。超时直接 kill主服务丝毫无感。参数传递走 JSON结果回收走 stdout错误单独走一个管道。好这节的结论是选型不要对标大厂的架构要对标自己的真实使用场景。只有自己才是系统唯一的长期用户时简单直接的方案往往就是最好的方案。4. 核心功能拆解列表、编辑、执行、日志的闭环现在进入正题逐一介绍这个管理器实现的核心闭环。4.1 技能总览界面一张表看清所有 Agent 能力技能总览作为首页目标是用一张表格回答三个问题现在有哪些技能能不能用最近跑得怎样表格每一行展示技能名称、技能描述截断一行、类型、标签、启用状态、最近执行时间、最近执行结果。启用状态直接用开关切换前端调接口只更新这一个字段。表头支持按类型筛选、按标签筛选、按最近执行时间排序。这里有一个细节我觉得很值得分享表格里面一定要放最近执行结果这一列。因为很多时候技能不是不可用而是上次执行失败了但我没发现。如果列表页直接显示红色失败你一眼就知道哪个技能需要排查而不是等到 Agent 调用时报错才去翻日志。实操中这个直觉真的很好用。表格右侧是操作按钮编辑、运行、复制、删除。编辑进入技能配置页。运行弹窗让你填入参即时执行一次用于验证该技能是否正常。复制基于现有技能复制一份方便生成变体例如在抓取正文基础上改个超时参数变成慢速抓取。删除需要二次确认如果该技能被其他组合技能引用弹窗会直接列出来告诉你删了它会导致下面的流程失灵。4.2 配置编辑器写人话和写 Schema 两不误技能配置页是整个管理器技术含量最集中的地方分为三个区域。第一区域是基本信息包括技能名称、技能 ID、描述、标签、启用状态。这里我特别推动自己把描述写成对人友好、对机器也可读的一句话。比如不要写通过 requests 库获取页面内容这是实现细节要写输入网址返回网页正文自动去除导航和页脚这是能力描述。大模型做 Function Calling 时会依赖这段描述决定要不要选这个技能所以描述质量直接影响 Agent 的自动调用准确度。第二区域是入参定义用键值对形式定义参数。前端的交互是一个可视化表格每一行一个参数参数名、类型、是否必填、默认值、说明。配置完后端会把这张表转成 JSON Schema。这里我顺手做了一个小功能——校验必填参数是否设置了默认值的矛盾情况避免出现必填但又有默认值这种模棱两可的状态。第三区域是执行配置根据技能类型不同而不同执行方式配置项本地函数模块名、函数名、超时时间、Python 环境路径HTTP 调用URL、方法、Header 模板、超时时间组合流程从已有技能里按顺序添加步骤每个步骤可复制上一个步骤的输出作为参数提示词模板提示词内容、变量占位符、输出格式要求本地函数类型有一个我很喜欢的交互输入模块名和函数名后点击检查可用性后端会直接用importlib去加载该函数并返回签名信息。前端拿到签名后自动解析出参数列表然后和人工填写的 Schema 比对提示哪些参数在函数签名里有定义但 Schema 里漏了。这个小功能帮我省了大量参数对齐的时间。4.3 手动执行测试不用写代码就能验证技能每个技能的运行按钮背后是一个执行测试弹窗。输入参数以 JSON 形式填入点运行后端走统一的技能执行链路返回执行结果和耗时。这个界面的价值在于把所有验证操作可视化。以前我要测一个技能得写一个 Python 脚本调用函数、打印结果、断点调试。现在直接在页面上填入参、看输出、看报错尤其适合测试那些需要大模型参与的技能——比如根据输入生成周报我直接在弹窗里输入这周的流水就能看到输出效果。一次次的运行记录都会进入执行日志方便对比不同参数下的效果差异。对于组合流程类技能执行弹窗还支持分步骤调试执行完第 1 步停在中间结果上确认没问题后再执行第 2 步。这个是我在调试从财报页面到生成分析摘要这类流程时的救命功能不然每次都从零跑到最后一步中间哪里出错根本不知道。4.4 执行日志与统计用数据发现从没考虑过的坏技能执行日志页不只是流水账我加了两个实用维度按技能维度统计成功率和平均耗时按时序输出完整调用链。按技能维度统计让我发现了几个有意思的事实。比如抓取竞品官网这个技能成功率只有 87%失败集中在几个特定网站的反爬拦截上一看就知道需要使用专门的浏览器伪装技能。再比如发送钉钉消息这个技能平均耗时才 1.2 秒但偶尔会跑到 4 秒后来发现是某个代理节点不稳定导致重连。完整调用链则是为组合流程准备的。前端把一条流程里每个步骤、每个子技能的执行时间都放在同一时间轴上展开一眼能看出整个链路被哪个环节拖慢了。这套基础的可观测性比单纯的技能能跑想得深一步做 Agent 的人一定能体会到这有多重要——只有你知道哪个环节慢才有资格谈优化。5. 执行引擎用统一入口串起四类技能的调度逻辑技能管理器如果只是录入和展示那和 Excel 表格没有本质区别。它的灵魂在于执行——通过统一入口让任何技能在任何场景下被调用。5.1 接口设计任何 Agent 都只会看到一个execute我提供的一个核心 API 是POST /api/execute { skill_id: weekly_report_generator, params: { start_date: 2026-05-01, end_date: 2026-05-07 } }返回值统一为{ status: success, data: { ... }, duration_ms: 352 }之所以设计成只暴露一个统一执行入口是要让调用方不需要关心技能内部的四种执行方式。Agent 项目里只需要做一件事——在需要某种能力时调用/api/execute传入技能 ID 和参数剩下的调度全部由管理器处理。实践中最常用的场景是在 Agent 的业务代码里写一个call_skill函数import httpx def call_skill(skill_id: str, params: dict): resp httpx.post(http://localhost:8600/api/execute, json{ skill_id: skill_id, params: params }, timeout120) resp.raise_for_status() return resp.json()[data]这个函数可以直接打包给多个项目用。以后 Agent 想增加能力我不改业务代码只要去管理器里新登记一个技能然后在代码里加一行call_skill(新技能ID, {...})即可。5.2 执行分发逻辑根据类型路由到不同的执行器服务端收到请求之后核心分发逻辑长这样async def dispatch_skill(skill: SkillModel, params: dict): if skill.execution_type local_function: return await run_local_function(skill, params) elif skill.execution_type http: return await run_http_call(skill, params) elif skill.execution_type workflow: return await run_workflow(skill, params) elif skill.execution_type prompt: return await run_prompt_template(skill, params) else: raise SkillExecutionError(f未知执行方式: {skill.execution_type})分发逻辑本身一点都不复杂真正的复杂度在四个执行器内部。我来分别说一下各自最需要注意的部分。5.3 本地函数执行器子进程超时返回值规约本地函数执行器的第一步是动态加载import importlib, inspect module importlib.import_module(skill.module_path) func getattr(module, skill.function_name) signature inspect.signature(func)第二步是参数过滤。因为前端 Schema 里定义的可能比函数签名多我把两者做交集只传函数签名里存在的参数allowed_keys set(signature.parameters.keys()) filtered_params {k: v for k, v in params.items() if k in allowed_keys}第三步是子进程隔离我用 Python 自带的asyncio.create_subprocess_exec启动一个子解释器将函数名和参数通过 json.dumps 传给 stdin子进程执行完把结果 json.dumps 到 stdout。超时由主进程的asyncio.wait_for控制proc await asyncio.create_subprocess_exec( sys.executable, -c, RUNNER_SCRIPT, stdinasyncio.subprocess.PIPE, stdoutasyncio.subprocess.PIPE, stderrasyncio.subprocess.PIPE, ) stdout, stderr await asyncio.wait_for( proc.communicate(inputjson.dumps(payload).encode()), timeoutskill.timeout_seconds ) if proc.returncode ! 0: raise SkillExecutionError(f技能执行失败: {stderr.decode()})这里我要强调一个很重要但容易被忽略的问题返回值必须能被 JSON 序列化。我早期写技能的时候有的函数返回 datetime、Decimal、numpy 数组json.dumps 直接报错。后来我统一在每个技能函数出口加了一个 sanitize 函数把特殊情况全部转成字符串或者 ISO 格式才彻底解决了技能本身跑得好好的但管理器里无法展示结果的困惑。经验技能函数约定俗成的规范是——入参只能是 JSON 可序列化的字典返回值也只能是 JSON 可序列化的对象。这个约定一定要写在团队的技能开发规范里不然名目繁多的类型会让你在执行器里疲于应付。5.4 HTTP 调用执行器模板变量注入与响应兼容HTTP 类技能因为我几个爬虫类的需求而存在核心功能是根据配置拼接 URL、请求头、请求体。这里有一个我踩过的坑很多 API 的请求头里要带上时间戳、签名等动态内容。我的解决方案是在执行器里做一个简单模板引擎——配置里可以用{{param}}的形式引用请求参数def render_template(template: str, context: dict): for k, v in context.items(): template template.replace({{ k }}, str(v)) return template这只算一个轻量的字符串替换但已经覆盖了我日常 90% 的动态头需求。更复杂的变量提取则交给前置流程技能去处理而不是堆在 HTTP 执行器里。响应兼容方面我的执行器会根据Content-Type自动决定返回格式JSON 直接返回字典HTML 则提取文本并做精简处理其他二进制内容返回大小和下载提示。这样技能的输出不会因为格式差异让上层 Agent 困惑。5.5 组合流程执行器把技能编排变成数据组合流程技能是我花了最多心思的部分它的本质是一个轻量级工作流引擎。配置界面允许用户选择若干已有技能作为步骤支持三种特殊操作变量传递上一个技能的输出可以被下一个技能通过{{step1.output.max_price}}引用为输入。条件分支可以写一个简单的表达式比如{{step1.status}} no_result时走另一个技能。迭代执行针对列表型输出可以循环执行某个步骤。这个执行器的解释器核心逻辑大致是async def run_workflow(skill, params): context {input: params} for index, step in enumerate(skill.workflow_steps): step_params resolve_step_params(step.params_template, context) result await dispatch_skill(step.skill, step_params) context[fstep{index 1}] result if break_condition_met(step, context): break return context.get(step_last_output, context)这里的resolve_step_params就是做模板引用解析的break_condition_met则是把条件表达式简单求值。我没有引入规则引擎绝大部分场景下简单的字符串表达式已经够用。组合流程最大的意义在于它把技能从单一函数升维到了一套方法论。比如我的每周行业简报就是一个组合流程技能先抓取若干信息源、然后对每篇内容做摘要、再按固定模板汇总成 Markdown、最后发送到邮件。这样一个复合能力在管理器里就是一行数据我可以随时调整其中任何一步而不用改动业务代码。5.6 提示词模板执行器技能和 LLM 的桥接最后一类技能是提示词模板。它的执行方式不需要调用外部系统而是通过管理器配置好的 API Key 来调用大模型。配置数据是一个模板字符串变量占位符用${variable}形式。执行时async def run_prompt_template(skill, params): prompt render_template(skill.prompt_template, params) response await call_llm(skill.model_name, prompt, skill.llm_params) return {text: response, usage: ...}这个执行器帮我统一了所有文字生成类的技能。无论是写邮件、生成周报、做代码评审它们的本质都是一个大模型调用但在管理器里它们有不同的描述、不同的模板、不同参数。由于它们共用同一个底层调用逻辑我可以在管理器层面做统一并发控制、失败重试、token 消耗统计。提示如果你有多个 LLM 供应商可以在技能配置里指定 model 名称执行器再统一路由到不同网关。这也是技能与实现分离的价值——上层业务逻辑完全不用知道背后的模型商是谁。6. 安全与边界本地工具也不能裸奔技能管理器本质是一个可以直接控制本机工具、可以直接调用外部 API、可以直接促发任意本地函数的入口安全上不能大意。我做了几层防护给各位做个参考。6.1 本地访问绑定不做鉴权但绝对不开放公网第一个决策是这个服务只监听127.0.0.1:8600不做公网映射。FastAPI 起服务时绑定uvicorn app.main:app --host 127.0.0.1 --port 8600这样只有本机能调用。团队多人协作时可以用 Tailscale 之类的组网工具把几台机器组进一个虚拟内网再改成监听内网 IP。我强烈不建议图省事直接暴露公网——技能管理器比一般的 Web 应用更容易被恶意利用因为执行任意函数这个能力太危险了。6.2 技能来源校验对新增技能做人工确认因为技能的执行代码最终落在我本机所以我给新增技能这个行为设置了一个心理门槛。在代码层面第二重防护就是签名校验。调用/api/execute的请求头里要求带一个预设的 API Token这个 Token 通过环境变量注入不写死在配置文件里。哪怕服务被内网的其他设备扫描到没有 Token 也调不了执行接口。第三重防护是技能来源校验。在管理器界面上技能分两类来源内置技能项目里由我手动编写的插件函数质量有保证。自定义技能用户通过界面上传的 Python 代码或 API 配置执行前需要手动点击验证签名。我在每个本地函数技能上强制要求一个元数据声明包含开发者 ID、技能用途、期望权限级别。注册进管理器时如果元数据缺失系统会直接标红。这个约定俗成的机制帮我避免了无数次后来翻代码不知道当时为什么写这个的尴尬。6.3 超时与资源限制防止技能跑飞技能执行超时是最基础的限制我一般设 10 秒爬虫类技能 60 秒长文档处理技能 120 秒。特别重的任务比如训练类、批量生成类我会建议走异步任务队列而不是直接把执行器卡在 HTTP 请求里。另外我在本地函数执行器里加了一个 output_size 限制超过 2MB 的输出会被截断并打上输出过大标记。毕竟很多技能的输出是为了让上层 Agent 决策塞一个 10MB 的 HTML 进去除了拖慢响应没有任何价值。7. 实测一场从注册一个技能到业务里真正调用光说结构可能还是有点抽象这里我完整走一遍注册技能 - 配置测试 - 业务调用的全流程让大家看到整个系统是怎么运转的。7.1 第一步写一个本地函数技能并注册我拿一个非常常见的技能做例子查询单词释义。首先在plugins/dictionary.py里面写一个函数def lookup_word(word: str, lang: str en) - dict: # 模拟查词逻辑实际可接入词典 API 或本地词库 result {word: word, lang: lang} # ...省略具体实现 return result然后我在管理器页面上点击新增技能技能名称词典查词技能 IDdictionary_lookup描述输入单词返回释义、音标、例句类型本地函数模块路径plugins.dictionary函数名lookup_word入参 Schemawordstring必填langstring可填默认 en超时5 秒保存后在列表页点击运行弹窗里填{word: agent}执行成功返回结果整个注册流程就结束了。7.2 第二步在业务项目里接入技能现在我在一个新项目daily_learning_bot里需要让 Agent 能够回答用户问一个英文单词是什么意思的场景。我不需要 import 任何词典函数只需要在 Agent 的工具列表里加一个技能调用入口tools [ { type: function, function: { name: dictionary_lookup, description: 输入单词返回释义、音标、例句, parameters: { type: object, properties: { word: {type: string, description: 要查询的单词}, lang: {type: string, enum: [en, zh]} }, required: [word] } } } ]当 Agent 的 LLM 判定这需要查词典时就会给出{word: agent}这个参数。我再调用管理器的统一入口执行result call_skill(dictionary_lookup, {word: agent})整个接入过程大约只花 3 分钟而且不需要改动任何词典逻辑。以后如果我改了词典 API只需要在管理器里编辑那个技能的配置业务项目的代码完全不用动。7.3 第三步用组合流程技能把多个动作串起来单技能接入是基本功组合流程才是价值爆发点。我把每日学习卡片做成了一个组合流程技能dictionary_lookup查词example_sentence_generator提示词模板类技能用 LLM 生成例句send_notification把卡片发到通知渠道配置时我把第 2 步的参数模板写成{word: {{step1.output.word}}}表示自动引用第 1 步的输出。第 3 步则引用前两步的内容拼成一个消息文本。整个流程的配置都是页面操作没有一行代码。这个组合流程保存后我每天早上定时执行一次获取当天的一个单词、例句、学习说明推送给学习群。中间任何一步想换逻辑比如说想把例句生成换成从语料库检索真实例句我只需要在管理器里把它替换成另一个技能执行器会自动处理。7.4 第四步查看日志发现薄弱环节跑了一周之后我看了一眼执行日志统计发现两个值得注意的数据example_sentence_generator的平均耗时是 2.8 秒成功率 99%但send_notification偶尔会失败失败原因指向通知服务商限流。于是我给send_notification加了一个失败重试配置再给整个组合流程加了一个更长的超时上限。这个工作流如果不用管理器我得改代码、本地重启服务、跑测试、重新部署一整套下来至少半天。现在全是在界面上点几下解决的我认为这就是技能管理器真正的价值所在——不是炫技而是把 Agent 开发中的常见操作从代码层搬到配置层。8. 避坑实录开发过程中最折磨我的五个问题这一节集中写我在开发过程中遇到的坑按折磨程度排序给正在做类似系统的朋友提前打个预防针。8.1 用 importlib 加载嵌套模块时的相对路径地狱本地函数执行器第一次加载插件时不断有人报ModuleNotFoundError。原因是我在插件代码里使用了相对导入比如from .utils import normalize_url但子进程执行脚本的工作目录和我项目的根目录不一致导致相对导入完全失效。解决方法有两个我都试过方法一在子进程脚本里手动把项目根目录加入sys.path再改成绝对导入。方法二把所有插件目录变成一个正规 Python 包并在入口文件里显式注册。我最终选了第一种因为配合良好了——只需要在执行器创建子进程之前把环境变量PYTHONPATH设置为项目根目录所有插件都按绝对导入写问题彻底解决。8.2 前端 JSON Schema 编辑器的自适应难题我一开始天真地以为入参 Schema 就是一个普通 JSON 编辑器让用户自己写 JSON 就行。用了几天发现体验极其糟糕写错一个逗号整个技能就废了写一个嵌套很深的参数我在手机上根本没法定制。后来我把编辑器换成了可视化表格模式只处理两层结构参数名 类型 是否必填 默认值 说明。两层以上的复杂嵌套我才允许切到 JSON 模式手动编辑。这个默认表格、进阶 JSON的双模方案让我自己用起来舒服多了。建议如果你的技能管理器目标是普通用户甚至非程序员一定优先考虑表单式编辑。程序员容易高估自己写 JSON 的耐心。8.3 子进程通信的序列化边界前文已经提到返回值规范化但它值得单独列为一个坑。早期的技能有返回datetime的、有返回float(inf)的、有返回自定义对象的子进程的 stdout 根本不可能直接 dumps 出去。每次运行都能看到一个TypeError: Object of type ... is not JSON serializable。我的最终方案是在执行器里包裹一个 normalize 函数递归遍历返回值def make_jsonable(obj): if isinstance(obj, dict): return {k: make_jsonable(v) for k, v in obj.items()} if isinstance(obj, (list, tuple, set)): return [make_jsonable(v) for v in obj] if isinstance(obj, (datetime, date)): return obj.isoformat() if isinstance(obj, (Decimal,)): return float(obj) if hasattr(obj, to_dict): return make_jsonable(obj.to_dict()) if obj is None or isinstance(obj, (str, int, float, bool)): return obj return str(obj)注意这里有一句return str(obj)兜底看似粗暴实际救了无数次命。因为有些第三方库返回的对象既没有to_dict也不是基础类型但str()输出仍然有可读性。与其报错不如保留信息。8.4 并发技能执行时的 SQLite 锁竞争管理器支持多 Agent 并发调用技能早期我偷懒直接用 SQLite 同步写经验记录结果在高并发场景比如两个 Agent 同时执行好几个技能遇到了database is locked错误。我的解决方案是给 SQLite 开启 WAL 模式同时把写操作通过一个全局锁限制为单线程串行import sqlite3 conn sqlite3.connect(skills.db) conn.execute(PRAGMA journal_modeWAL)另外在 FastAPI 里我把数据库写入封装成一个线程池执行器避免事件循环被阻塞。这个改动让我从偶发数据库锁死变成了稳定不报错。8.5 界面里对技能依赖的展示与阻止删除组合流程技能会引用原子技能我在设计技能模型时用了外键关系所以在删除原子技能时需要先检查有没有被引用。我一开始只是用后端校验删掉时返回错误提示。但这样界面上体验不够好——用户不知道是谁引用了我。后来我在前端列表页加了一个二级联动点击查看引用者可以展开引用该技能的所有组合流程。删除前弹窗里直接列出来并给出解除引用的快捷跳转。这个功能虽然不起眼却让组合流程的维护从盲人摸象变成了一望而知。9. 如何使用、扩展与我踩完坑后的最终心得管理器现在已经在我本地稳定运行了两周多我每天都会用也把几个主力 Agent 全部切到了这个统一调用入口。如果你也想搞一个我整理了最终的部署要求和扩展思路。9.1 部署清单100 行代码以内的最小起步一套最简部署大概需要以下内容Python 3.10安装fastapi、uvicorn、sqlmodel、httpx一个静态 HTML 文件从 CDN 加载 Vue 3 和 Element Plus一个plugins/目录放你的本地函数一个启动脚本。启动命令就是uvicorn app.main:app --host 127.0.0.1 --port 8600如果你想把它打成服务可以写一个 systemd unit或者直接放 tmux 里跑。我用的 tmux因为改代码后重启方便虽然我这个CDN 前端 编辑器修改的方案其实大部分前端改动不需要重启后端。9.2 扩展方向一把技能管理器接入 OpenAPI 工具列表当前比较顺滑的用法已经演示过了在业务项目里手动写tools数组。但如果你想更彻底一点可以在管理器里加一个接口GET /api/llm/openapi.json这个接口会读取所有启用中的技能自动把它们转换成 OpenAI Function Calling 格式的 tools 数组。这样任何新的 Agent 项目只需要拉一次这个地址就能拿到全部技能列表然后直接塞进 LLM 的 tools 参数里。这个扩展我还没有完全做完但计划是必然要做的。一旦完成新建 Agent 就可以完全不用手写工具定义了。9.3 扩展方向二给技能加上版本概念我接下来要做的改进是技能版本记录。每个技能可能被多个 Agent 引用如果我改了描述或入参可能对已上线 Agent 造成破坏。所以我会在做编辑时记录一份当前版本副本组合流程里可以选择固定引用某个版本而不是永远跟随最新版。具体设计是增加一张 skill_versions 表存放每个技能的 JSON 快照。展示时有一个版本历史按钮可以对比不同版本之间的变化。这个需求是我给线上 Agent 改技能时踩到过一次坑才想清楚的——改了描述由于 description 字段变了LLM 对技能的选择倾向整个变了。9.4 扩展方向三技能市场的局域网共享目前管理器是单机使用但我计划增加一个简单的技能市场页面。团队里任何人写好技能后可以发起到市场的发布申请其他人看到后一键复制到自己的管理器。这个功能如果能做出来团队内部沉淀技能会非常高效。以后新同学接入时不需要私聊我问你的抓取函数能发我一份吗直接在技能市场里搜网页抓取一键复制就完事。9.5 最后一点真实的体会做完这个管理器我最大的感受不是代码写得多漂亮而是——做 Agent 的过程本质上是在做一套关于技能如何被沉淀、被复用、被管理的机制。项目之前我轻视了技能管理这个环节觉得 Agent 写出来能跑就行。但真到了做第三个、第四个 Agent 的时候面对一沓散乱脚本才开始后悔没有提前统一管理。现在有了这个管理器我每写一个新 Agent第一件事不是写代码而是打开技能列表看看有哪些可以复用缺了哪些只需要登记一下就能用。这个习惯一旦养成Agent 开发的速度和稳定性都会有质的提升。如果你正在经历和我类似的阶段——写了好几个 Agent 却感到重复劳动严重——我强烈建议你花一两天时间照着上面的思路搭一个属于自己的技能管理器。不用一开始就做得面面俱到把技能列表、统一执行、基础日志三件事跑通就够用了。等你自己在真实项目里用上一个月你再回来审视这个工具的改进方向自然就会知道下一步该加什么了。

相关推荐

Fast-LIO融合GNSS的紧耦合设计与工程避坑指南
Fast-LIO融合GNSS的紧耦合设计与工程避坑指南

简介:本资源是一份面向机器人与自动驾驶领域研究人员及工程师的Fast-LIO算法深度解析与工程改进文档,聚焦多传感器融合建图与定位中的关键瓶颈——点云特征缺失导致的建图失败问题。作者在原始Fast-LIO(IMU激光雷达紧耦合)基础上&… · 2026/11/3 21:35:43

2025年五大AI科研平台实测对比与选型指南
2025年五大AI科研平台实测对比与选型指南

1. 2025届AI科研平台实测背景与价值2025年,AI科研平台已经完成了从"辅助工具"到"研究基础设施"的转型。根据最新学术社区调研数据显示,超过87%的研究者会在项目启动阶段优先评估可用AI平台,而选择不当的工具链会导致平均… · 2026/11/3 21:35:43

基于PyTorch的Conformer-CTC中文语音识别实战指南
基于PyTorch的Conformer-CTC中文语音识别实战指南

1. 项目概述:基于PyTorch的Conformer-CTC中文语音识别实战最近在语音识别领域,Conformer模型凭借其融合CNN与Transformer的优势,正在逐步取代传统的LSTM和纯Transformer架构。作为一名长期从事语音算法开发的工程师,我刚刚完成了一… · 2026/11/3 21:35:43

从Demo到上线:用AgentScope打造生产级记忆型AI Agent
从Demo到上线:用AgentScope打造生产级记忆型AI Agent

最近总有朋友问我:AI Agent 到底怎么从 Demo 做到能上线?这个问题我很有发言权——我用 AgentScope 从零搭了一个带长期记忆的生产级 AI Agent,前后折腾了两个月,踩了不少坑,也沉淀了一套可以复用的方法。这篇文章就是… · 2026/11/3 22:20:10

通信技术演进:从基础原理到5G应用实践
通信技术演进:从基础原理到5G应用实践

1. 通信的本质与核心价值通信就像人类社会的神经系统,它让信息在不同主体间流动起来。我从业通信行业十二年,见证了从2G到5G的整个演进过程,深刻体会到通信技术如何重塑我们的生活方式。通信的本质是解决"信息传递"这个基础需求——… · 2026/11/3 22:20:10

永磁同步电机建模:RMxprt参数校验与Maxwell场仿真协同流程
永磁同步电机建模:RMxprt参数校验与Maxwell场仿真协同流程

简介:本资源是一份面向电力电子与传动控制方向本科生及电机仿真初学者的实践教学材料,聚焦永磁同步电机(PMSM)在ANSYS Maxwell平台中的全流程建模与仿真方法。内容覆盖RMxprt等效建模、Maxwell 2D二维精细建模、关键参数设置&… · 2026/11/3 22:20:10

计算机行业八大高含金量认证全解析
计算机行业八大高含金量认证全解析

1. 计算机行业高含金量证书全景解析 在信息技术飞速发展的今天,专业认证已成为衡量技术人员能力的重要标尺。作为从业十余年的技术老兵,我见证过太多同行通过考取权威证书实现职业跃迁的真实案例。本文将系统梳理当前计算机领域最具价值的八项认证&#… · 2026/11/3 22:20:10

Univer SDK:Web文档协同能力的底层架构与工程实践
Univer SDK:Web文档协同能力的底层架构与工程实践

1. 项目概述:Univer 是什么?它解决的不是“又一个表格组件”,而是现代 Web 应用中「文档协同能力」的底层基建问题Univer 不是一个 Office 替代品,也不是某个厂商推出的“在线版 Excel”。如果你把它理解成“网页里能打开 Excel 的… · 2026/11/3 22:20:09

学习通考试交卷状态查询与问题解决指南
学习通考试交卷状态查询与问题解决指南

1. 学习通考试交卷状态查询的必要性 在线考试系统已经成为现代教育的重要组成部分,而学习通作为国内广泛使用的教学平台,其考试功能被众多高校和教师采用。对于考生而言,确认自己是否成功交卷是考试结束后最关心的问题之一。我见过太多学生在… · 2026/11/3 22:19:09

双极步进电机控制实战:DRV8818PWPR与PIC18F86K22驱动调试
双极步进电机控制实战:DRV8818PWPR与PIC18F86K22驱动调试

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views … · 2026/11/2 13:10:28

STM32F207ZG + DRV8818PWPR 步进电机驱动实战解析
STM32F207ZG + DRV8818PWPR 步进电机驱动实战解析

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views … · 2026/11/3 6:04:24

DRV8818PWPR+dsPIC30F4013工业级双极步进电机控制方案
DRV8818PWPR+dsPIC30F4013工业级双极步进电机控制方案

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views … · 2026/11/3 5:52:46

STM32F411RE与MAX14431实现SPI隔离:工业现场抗共模干扰实战
STM32F411RE与MAX14431实现SPI隔离:工业现场抗共模干扰实战

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views … · 2026/11/3 1:01:59

ESP32+大模型AI硬件开发:从Demo到产品的8个工程问题
ESP32+大模型AI硬件开发:从Demo到产品的8个工程问题

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views … · 2026/11/3 1:01:59

基于MAX14431BAEE+与PIC18F85J10的SPI隔离方案设计与实践
基于MAX14431BAEE+与PIC18F85J10的SPI隔离方案设计与实践

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views … · 2026/11/3 1:01:59

双极步进电机控制实战:DRV8818PWPR与PIC18F86K22驱动调试
双极步进电机控制实战:DRV8818PWPR与PIC18F86K22驱动调试

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views … · 2026/11/2 13:10:28

STM32F207ZG + DRV8818PWPR 步进电机驱动实战解析
STM32F207ZG + DRV8818PWPR 步进电机驱动实战解析

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views … · 2026/11/3 6:04:24

DRV8818PWPR+dsPIC30F4013工业级双极步进电机控制方案
DRV8818PWPR+dsPIC30F4013工业级双极步进电机控制方案

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views … · 2026/11/3 5:52:46

了解更多?预约专属演示

我们的顾问将为您一对一讲解产品与方案

企业微信二维码