1. 为什么我要再造一个 WorkBuddy 的轮子
先交代一下背景:我一直在用 WorkBuddy 这类 AI 工作台工具来管理日常的任务流。用得越久,心里越觉得别扭——不是功能不够用,而是它作为一个闭源产品,边界太死了。你想给它接一个内部的知识库检索接口,不行;你想让它在跑任务的时候先查一下本地数据库里的库存状态,不行;你想把任务历史完整导出成自己系统的格式,还是不行。每次都要绕到外部脚本里手动同步,久而久之我就萌生了一个想法:与其在别人的围栏里腾挪,不如自己做一个开源版本出来。
于是我花了几周时间,做了一个叫WorkDSH的项目——名字里的 DSH 我取的是 "DeepSeek Helper" 的缩写,因为底层主要接的是 DeepSeek 这类开源模型。它本质上是一个轻量级的 AI 工作台,用来承接"给模型下任务、模型调用工具、任务结果回传"这条完整链路。具体能做什么?举几个我用得最顺手的场景:让模型读取一个目录下的所有文件并生成摘要;让模型定时抓取某个网页的关键字段并写入数据库;把一段会议纪要转成结构化任务清单并逐项派发。这些在 WorkBuddy 里做要做不少配置,在 WorkDSH 里,我只需要写一个任务描述,剩下的事情由工作台自己拆解和执行。
这篇文章不打算写成那种枯燥的架构说明文档,而是想从一个真实使用者的角度,聊聊我做这个项目的动机、整体设计、核心模块的落地方式,以及我自己在实测中踩过的坑和优化方向。如果你也在用类似的工作台工具,或者正在考虑自己搭一套 AI Agent 工作流,这篇内容应该能给你一些可以直接拿去用的思路。
开工之前先声明一点:这是一个开源项目,所有代码和文档都会放出来,你可以随意拿去改、拿去用,也可以基于它做二次开发。我只希望你在用之前先想清楚一件事——你到底需要一个"别人定义好的工具",还是需要一个"你能完全掌控的框架"。如果你选的是后者,那我们继续往下聊。
2. 核心设计思路:任务不是"聊天",而是"流水线"
在 WorkDSH 里,我最坚持的一个设计理念是:AI 工作台的核心单位不是对话,而是任务。对话只是任务执行过程中的一种交互形式,它不应该是整个系统的骨架。
2.1 对话式产品到底有什么问题
WorkBuddy 这类产品的底层逻辑其实是对话驱动的:你发起一句指令,模型返回一段回复,如果需要的话再继续追问。这个交互范式在人机对话场景下没有问题,但在任务执行场景下就很别扭。举个例子,我想让模型"每天上午十点检查一次某个 API 的健康状态,如果响应时间超过 2 秒就发一条告警"。在对话式产品里,这个需求会被拆成一个"定时任务",这个任务本身没有结构化定义,它只是被塞进了一个大模型的消息流里。一旦模型上下文被其他对话冲掉,这个任务就变成僵尸任务了。
WorkDSH 从一开始就把任务建模成一条流水线。每一个任务由以下几个明确的部分组成:
- 任务定义:要做什么、输入是什么、输出是什么、验收标准是什么
- 工具链:这个任务在执行过程中可以调用哪些外部工具
- 执行策略:是单次执行、定时执行,还是事件触发执行
- 状态管理:任务当前处于什么状态,执行到哪一步,结果如何记录
这个结构的好处是:无论底层模型怎么换,只要任务描述是结构化的,整个执行逻辑就完全可控。模型只是流水线上的一个计算节点,不是整个系统的脑子。
2.2 模型无关的接口设计
我见过太多"绑定某个模型"的开源项目。看起来很方便,实际上是把未来的路堵死了。今天 DeepSeek 好用不代表明天它也最好用,更不代表你本地部署的 Qwen 不能用。所以 WorkDSH 在设计的时候,就把模型接入层做成了完全独立的接口。
所有模型的调用统一走一个抽象接口,我定义了六个核心方法:文本补全、对话补全、函数调用执行、多轮会话管理、嵌入向量生成、流式输出。不管你是接 OpenAI、DeepSeek、本地 Ollama 还是 Hugging Face 上随便一个开源模型,只要实现这六个方法,就能无缝接入工作台。
我目前的主力配置是这样:
| 场景 | 模型选择 | 接入方式 |
|---|---|---|
| 日常任务拆解 | DeepSeek-V3 | OpenAI 兼容接口 |
| 工具调用执行 | DeepSeek-V3 | Function Calling |
| 轻量文本处理 | Qwen2.5-7B | 本地 Ollama |
| 向量检索 | bge-m3 | 本地推理服务 |
为什么要这么混着用?因为不同任务的性价比差别真的很大。批量处理几百条短文本用本地小模型就够,没必要每次都走大模型的 API;但真正需要复杂推理的任务,本地方案又显得不够聪明。模型无关的设计让我可以按任务类型动态切换,而不是被一个模型锁死。
2.3 任务状态机:从"收到指令"到"全部完成"
再往下挖一层,就是任务执行的骨架——状态机。WorkDSH 里的每一个任务都会经历这么几个状态:
CREATED:任务刚刚创建,还没被调度器拾取READY:任务的所有前置条件都已满足,等待执行RUNNING:任务正在执行中,可能处于某个工具调用的中间步骤BLOCKED:任务执行受阻,等待人工介入或外部条件满足COMPLETED:任务正常完成,结果已保存FAILED:任务执行失败,记录错误原因和堆栈信息
这个状态机是整个系统里最值得花时间的地方。因为我一开始图省事,只设计了 RUNNING、COMPLETED、FAILED 三个状态,结果跑起来之后发现,很多长时间任务会因为某个中间步骤的网络波动被卡住,但系统的状态居然还是 RUNNING。这会导致调度器认为它还在正常执行,就不会去重试或者报错。加上 BLOCKED 状态之后,这类问题就暴露得很及时了。
我在任务状态上做了一个可视化的看板,类似一个简化的甘特图或者看板列表,能直观看到每个任务卡在哪个状态、占比多少。用了大概一周之后,我明显感觉到对整体系统的掌控感比原来强太多了。
3. 核心模块落地:调度器、工具沙箱和记忆库
说完了整体思路,这一节聊几个真正落地的时候比较有分量的模块。这几个模块都不是什么黑科技,但每一个在实际使用中都踩过不少坑。
3.1 任务调度器:怎么决定"什么时候跑什么"
调度模块的职责非常明确:接收任务的触发条件,判断是否满足执行条件,然后把任务投递给执行器去跑。它支持三种触发模式——手动触发、定时触发(cron 表达式)、事件触发(某个任务完成之后自动触发下一个)。
我一开始想得很简单,觉得调度器无非就是一个带时间戳的任务队列。后来真正用到事件触发才发现,任务的依赖关系才是调度器里最复杂的部分。举个例子,任务 A 是"抓取一批网页内容",任务 B 是"对抓取的内容做摘要",任务 C 是"把摘要写入数据库"。这三个任务之间是有明确的先后依赖的,如果 B 跑在 A 前面,拿到的就是空数据。
所以我在调度器里加了一个依赖校验层:每个事件触发任务都可以声明它依赖哪些前置任务,只有当所有前置任务处于 COMPLETED 状态时,它才会被投递到执行队列。这个设计特别直接,也非常有效。
底层用了 PostgreSQL 作为任务存储,配合一个轻量级的分布式锁来实现并发控制。为什么用 PostgreSQL 而不是 Redis?因为任务本身有很强的结构化属性,用数据库存储天然支持回滚、审计、复杂查询。Redis 我只用来做消息通知和缓存,不碰任务状态。
3.2 工具沙箱:让模型安全地调用外部命令
工具调用是 AI 工作台的灵魂,同时也是最容易翻车的地方。模型在 Function Calling 的过程中会生成参数,然后由工作台去实际执行这些参数对应的工具。如果对工具调用不加隔离,模型给你一个rm -rf /或者删除数据库的调用,后果不用我多说了。
WorkDSH 的工具沙箱层做了三层防护:
第一层是工具白名单。只有注册进系统且标为"允许模型自动调用"的工具,模型才有权限调用。其它工具只能由人工操作触发,模型只能生成调用建议而不能直接执行。
第二层是参数校验。每个工具的入参都有严格的 JSON Schema 定义,模型生成的参数必须通过 Schema 校验才能实际执行。举个例子,我有一个"执行 SQL 查询"工具,Schema 规定查询类型只能是SELECT,那模型无论如何都无法通过这个工具执行写操作。
第三层是执行隔离。所有工具调用都在一个受限的进程环境中运行,有独立的文件系统权限和网络权限。这个实现起来也不复杂,直接用系统容器机制包一层就行。对于不是容器环境的部署,也可以用进程级隔离加严格的文件路径校验来兜底。
这三层下来,我用了一个多月,还没有出现过一次模型乱调工具导致的事故。安全不是靠运气,是靠机制。
3.3 记忆和上下文管理:让模型"记得住"上次干了什么
AI 工作台和普通的单次对话最大的区别,在于它需要维护一个持续的工作记忆。任务 A 抓取的数据格式规范,任务 B 在做摘要的时候需要遵循这个规范,那么这些规范信息就必须在任务之间流转,否则每次任务都要把上下文全部塞给模型,Token 开销大得离谱。
我实现了一个切片式的记忆库:每一个任务的执行记录都会自动归档到记忆库里,归档的内容包括任务输入、工具调用记录、模型输出、人工纠正等。这些记忆不是简单地存起来就完事,我可以随时检索和引用——比如让系统在开始新任务之前"参考一下上次我在类似任务里最终确认的格式"。
实测下来这个设计在长周期的项目管理场景特别有用。一个项目跑了两周,中间可能有几十个任务执行记录,我把它们全部挂到一个项目记忆库里,然后在新任务执行时,自动把相关记忆切片注入到上下文中。模型每次启动任务时都带着完整的项目背景,不会出现"换了一个会话就失忆"的尴尬。
4. 实测表现:我拿真实任务试了一个月
代码写得再顺,最终还是要靠真实场景来检验。我连续用了一个月,把 WorkDSH 放在几个完全不同的任务上跑,拿到了一些一手数据。
4.1 测试一:批量文档处理与摘要生成
这个任务的核心流程是:从指定目录读取 200 个 Markdown 文件,逐篇生成结构化摘要,最后输出成一个汇总报告。在没有 WorkDSH 之前,我通常需要写一个 Python 脚本,自己拼接 Prompt,自己循环调用 API,还要自己处理失败重试和结果汇总。整个过程大概要一整天。
用 WorkDSH 之后,我只需要创建一个批处理任务,声明"读取目录下的所有 md 文件,对每篇生成一个标题、核心观点、关键数据三段的摘要,最后合并输出 JSON 文件",然后启动任务就不用管了。整个执行大概用了 40 分钟,成功处理了 198 篇,另有 2 篇因为编码问题被自动标记为 FAILED,我看了一眼错误信息后发现是文件本身损坏,跟系统无关。
这个过程里最让我满意的一点是:人的参与度被降到了最低。我没有去管中间的每一步,只在任务结束时收到了一个结构化报告。
4.2 测试二:定时数据巡检与告警
我把一个库存接口的巡检任务挂到了 WorkDSH 上,设置了每 10 分钟执行一次的 cron 计划。任务内容是获取接口的响应时间、状态码、库存水位三个指标,然后判断是否在健康范围内,如果不在就推送一条告警。
这个任务跑了整整一个月。总计执行了 4300 多次,其中出现告警的次数有 27 次。最有用的一次是有一天凌晨三点钟,接口响应时间从正常的 200ms 飙到了 8 秒,系统在第一次超时之后自动完成了三次重试确认,然后把告警推到了企业微信,同时附带了一个初步的故障分析——模型判断可能是上游数据库连接池耗尽,建议优先检查连接数配置。
如果没有这个系统,这个问题可能要等到第二天早上用户反馈了才会被发现。现在相当于有个 24 小时不休息的运维在盯着。
4.3 测试三:多步骤的复杂工作流
第三个测试比较能体现 WorkDSH 的流程编排能力。我设计了一个"竞品追踪"工作流,由四个任务串联组成:抓取竞品官网更新、提取变更内容、本地模型生成变化分析、推送周报。四个任务之间依赖关系明确,其中一个失败,后面的任务会自动进入 BLOCKED 状态,等待重试或者人工介入。
一个月跑下来,四个任务串联的成功率在大概 95% 左右。失败最多的环节是第一个抓取任务,因为目标网站有时候会换 HTML 结构导致选择器失效。不过我发现了一个很惊讶的现象:模型在工具调用的时候会有一点"自主修复"的能力——当选择器匹配不到内容时,模型会尝试重新定位页面里最相似的信息区块,而不是直接报错。
这个能力我没有刻意去训练,它更像是大模型在 Function Calling 过程中,基于工具返回的错误信息自行调整参数的一种涌现行为。这个发现让我对"工具调用 + 大模型"这个组合的信心提升了不少。
5. 开发中踩过的一些坑,希望你不用再踩
这个项目从零到真正能日常使用,大概花了三周时间。前两周基本上每天都要撞几次墙,有几个坑我觉得值得单独写出来,因为它们是项目能否稳定运行的真正关键。
5.1 动态注册工具时的"幽灵工具"问题
第一个坑出现在工具注册机制上。我最初采用的工具注册方式是运行时动态扫描,也就是每次从某个目录下加载所有工具定义文件,然后注册进系统。理论上这样很方便,新增工具只需要放一个文件,重启即可生效。
但实测发现一个幽灵工具的问题:当一个工具文件被删除之后,系统里往往还残留着旧工具的调用记录,导致一些历史任务的日志里出现"调用了不存在的工具"这种错误。原因就是任务状态里存的工具 ID 没有做关联校验,而工具面板上又显示着已经不存在的工具条目。
解决办法是在工具管理器里增加了一个工具版本号和启停标记。每次注册工具都会生成一个唯一标识,任务在创建时绑定当时的工具版本,运行时再校验版本是否有效。这样即使工具文件被删掉,旧任务的执行记录依然可以追溯,但不会被继续调度执行。
5.2 上下文记忆的 Token 开销失控
第二个坑是关于上下文记忆的。我最初的设计是不管任务大小,都会把项目记忆库里最近 20 条记录全部注入上下文,这样模型在任何时候都能"看到"前因后果。结果跑了几天,Token 消耗量直接把我整懵了——有些任务的上下文动辄上万 Token,实际用到的有效信息可能只有四分之一。
后来我改了策略:不是盲目注入最近 N 条记忆,而是在启动任务时做一次语义筛选。系统会把当前任务的目标描述转化为一个检索向量,在记忆库里召回最相关的 5 到 8 条记忆片段,再拼接到上下文中。这个改动让 Token 消耗降了差不多一半,同时也让模型的专注度更高——它不会被不相关的历史记录带偏。
5.3 模型返回 JSON 里的"小聪明"必须靠校验兜底
第三个坑,也是所有做 Agent 的人一定会遇到的:大模型在生成结构化输出时,偶尔会有一些让人哭笑不得的"小聪明"。比如你让它返回一个 JSON 数组,它会在合法 JSON 后面加一段"以上是我的回答,希望对你有帮助";或者它把布尔值true写成字符串"true";更常见的是它会在 JSON 里混入 Markdown 格式的代码块标记。
这些错误在人工对话场景下完全可以忽略,但放到自动化流水线里就是致命伤——解析失败会导致整个任务中断。我最终的解决方案很粗暴有效:所有的模型输出先经过一个"宽容解析器",它会把 Markdown 代码块剥离、修正裸布尔值、自动补全缺失的括号,只有宽容解析也搞不定的时候才会判定任务失败。
从最终效果来看,这个宽容解析器把模型输出的可解析率从 93% 提升到了 99% 以上。剩下那 1% 的失败任务会进入人工修复队列,系统会附上原始输出和解析错误信息,人工点一下就重新执行了。
6. 开源说明与后续规划
WorkDSH 目前以 MIT 协议开源,所有核心代码都放在公共仓库里。仓库里包含了完整的任务引擎、工具沙箱实现、模型接入层、执行看板,以及一套可以直接跑起来的 Demo 环境。如果你对这个项目感兴趣,下面这些信息应该能帮你快速上手。
环境要求不算高:一台能跑 Docker 的机器,16GB 内存以上,一个可以访问大模型 API 的网络环境。如果你想完全本地化部署,也可以直接接 Ollama 跑 Qwen 系列模型,唯一需要注意的是本地模型在复杂工具调用上的表现会弱一些。
部署步骤很常规:克隆代码、复制环境变量模板、配置模型 API Key、启动服务。我特意把初始化流程做成了一条命令搞定,基本上十分钟之内可以跑起来。如果你在部署阶段遇到问题,优先检查网络连通性和模型配置文件,百分之八十的问题都出在这两个地方。
最后聊一下后续的规划。我最想做的有三件事:第一是把工具沙箱升级成真正支持多租户隔离的模式;第二是加入一个可视化的流程编排界面,让不熟悉代码的人也能拖拽创建任务链路;第三是做一个插件市场,让社区贡献的集成插件可以像应用商店一样一键安装。这些事我会一件一件做,也希望有更多人能一起参与进来。开源项目一个人憋着做没意思,大家一起踩坑,一起修,一起把轮子磨圆,才像回事。