TypeScript Agent 基元
当你向 Craft Agents 发送消息,代码里究竟发生了什么?
Craft Agents 是什么?
想象你有一个超级助手——它不仅能回答问题,还能真正帮你做事:发邮件、查数据库、连接 Slack、运行代码。这就是 Craft Agents 做的事。它是由 craft.do 团队专门为"指挥 AI Agent 干活"而打造的 TypeScript 桌面应用。
Craft Agents 使用了 Anthropic 的 Claude Agent SDK,把复杂的"让 AI 自主行动"能力打包成了一个优美的桌面应用。
多会话管理
同时运行多个 Agent 对话,每个都有独立状态和上下文,互不干扰。
零配置接入工具
告诉 Agent "连接 Linear",它自己找 API、读文档、配好认证——你不用动手。
技能系统
把专门的指令打包成"技能",Agent 随时调用,就像给员工发操作手册。
代码里的"主演"——核心类型
Craft Agents 的代码就像一部剧,有几个主角。理解它们,你就能告诉 AI "把这个逻辑放到 Session 里,不要放到 Workspace 里"——这正是指挥 AI 的关键。
像一个独立的"工作桌",每个工作区有自己的 AI 配置、技能包和连接的工具。
一次完整的对话线程——从"你好"到任务完成。每个 Session 有独立的消息历史和状态。
每条消息的持久化存储,包含内容、发送者、token 用量和时间戳。
整个系统的"中央调度室",负责把前端的操作指令路由到正确的处理器。
代码翻译:Session 是如何定义的
下面是 Craft Agents 里 Session 的真实定义(来自 packages/core/src/types/session.ts)。左边是代码,右边是白话解释:
export interface Session {
id: string;
sdkSessionId?: string;
workspaceId: string;
name?: string;
status?: SessionStatus;
isFlagged?: boolean;
isArchived?: boolean;
}
定义一个"会话"长什么样——它有哪些属性
每个会话有一个唯一的 ID(比如 uuid),系统用它来找到你
SDK 内部用的 ID,第一条消息发送后才有——所以是"可选"的(?)
这个会话属于哪个工作区——每个会话必须"住"在某个工作区里
会话名字,可以不填(? 表示可选)
工作状态:todo / in_progress / done 等——用于任务管理流程
是否被用户标记了星标/红旗
是否已归档——归档后不在收件箱显示,但数据还在
interface 只是数据的"形状说明书",不包含任何行为。当你告诉 AI "在 Session 类型里加一个 priority 字段",AI 就知道该改 interface,而不是改某个函数。
发一条消息,幕后发生了什么
你在 Craft Agents 里打了一段话按下发送——这个简单动作触发了一条完整的指令链。点击"下一条消息",跟着数据走一遍:
检验一下你的理解
你在 Craft Agents 里同时开了三个 AI 对话:分析销售数据、写市场方案、查竞品信息。这三个对话和工作区的关系是?
你想给每个 Session 加一个"优先级"字段(1=高、2=中、3=低)。你应该让 AI 修改哪里?
Craft 的工具系统
Agent 是怎么"连上"外部世界的——从 MCP 到自定义 API
AI 为什么需要"工具"?
把 Claude 想象成一位知识渊博的顾问——但他被关在一个没有网络、没有电话的房间里。他知道该怎么查销售数据,但他进不了你的数据库。工具就是给他开的那扇门。
Claude 决定需要查销售数据
调用 get_sales 工具
工具真正连接数据库,拿到结果
结果返回给 Claude,继续分析
每一个外部集成(Slack、数据库、文件系统)都是一个"工具"。当 AI 说"我没有访问权限",通常是工具没有配置好——而不是 AI 坏了。理解这个区别,能帮你快速定位问题。
MCP——工具的"通用插口"
Craft Agents 支持 MCP(Model Context Protocol)。MCP 就像给 AI 工具定义了一种"通用 USB 接口"——任何工具只要实现这个接口,Claude 就能直接用它,不需要为每个工具单独写适配代码。
Craft Agents 运行时
外部 MCP 工具
代码翻译:Session MCP Server 的工具注册
Craft Agents 内置了一个特殊的 MCP 服务——session-mcp-server,它让 Agent 能感知到自己所在的会话上下文。下面是它如何注册一个工具(来自 packages/session-mcp-server/src/index.ts):
const server = new McpServer({
name: "craft-session",
version: "1.0.0"
});
server.registerTool(
"get_session_info",
{ description: "Get current session metadata" },
async () => ({ content: sessionData })
);
创建一个新的 MCP 服务实例,给它起个名字
服务叫 "craft-session"——Claude 会通过这个名字认识它
版本号,方便以后升级时区分新旧版本
服务创建完毕
向这个服务注册一个工具(就像在菜单上加一道菜)
工具名字叫 "get_session_info"——Claude 决定要用它时会叫这个名字
描述:获取当前会话的元数据——Claude 靠这段描述理解何时调用这个工具
工具的实际执行逻辑:返回 sessionData 里的数据
工具调用的完整数据流
当 Claude 决定调用一个工具,数据是这样在系统里流动的:
思考一下
你给 Agent 添加了一个叫 search_crm 的工具,但 Claude 从不主动使用它。最可能的原因是什么?
你的团队有一个内部开发的报表工具,现在想让 Craft Agents 里的 Claude 也能调用它。最快的方式是?
Agent 组合模式
技能、工作区和自动化——如何把 Agent 组合成更强大的系统
技能包:给 Agent 的"操作手册"
想象你雇了一个新员工,第一天上班。你不会让他什么都靠猜——你会给他一本操作手册,写着"接到客户投诉时,先查订单状态,再用这个模板回复"。Craft Agents 的技能(Skills)就是这个操作手册。
技能本质上是存储在工作区里的特殊指令,告诉 Agent 在特定场景下如何行动、使用什么语气、遵循哪些规则。
对话中输入 @销售分析技能,这个技能的所有指令就立刻生效——就算是进行中的对话也能切换,不需要重开会话。
每个技能可以绑定特定的 MCP 工具集。激活"数据分析技能",自动附带数据库查询工具和可视化工具。
代码翻译:MessagingGateway 如何路由指令
整个 Craft Agents 的核心是 MessagingGateway。它采用了一种叫 命令模式(Command Pattern) 的架构:
export class Router {
private handlers = new Map<
string,
CommandHandler
>();
register(type: string, handler: CommandHandler) {
this.handlers.set(type, handler);
}
async dispatch(cmd: Command) {
const handler = this.handlers.get(cmd.type);
return handler?.execute(cmd);
}
}
定义一个"路由器"类,类似快递公司的分拣系统
内部维护一张"处理器清单"——命令类型 → 谁来处理它
清单是个 Map:key=命令名称,value=处理这种命令的函数
register 方法:向清单里新增一种命令的处理器
就像在快递分拣台登记:"北京的件,交给张三处理"
dispatch 方法:收到一个命令,查清单,找到对应处理器
从清单里取出这个命令类型的处理器
调用处理器的 execute 方法;如果没找到处理器,什么都不做(?. 是安全调用)
这让系统极度可扩展——要加新功能,只需要 register 一个新的处理器,不需要修改 Router 本身。这是软件工程里的"开闭原则":对扩展开放,对修改关闭。
自动化:让 Agent 在后台自主运行
Craft Agents 最强大的功能之一是自动化(Automations)——基于事件触发的 Agent 任务,不需要你手动开启。这就像给 Agent 设置了一套巡逻规则:
on:label_change
当工作项标签从"待处理"变为"进行中"时,自动创建 Agent 会话,开始执行相关任务
on:schedule
按时间表触发——比如每周一早上 9 点,自动拉取上周数据并生成周报草稿
on:tool_use
当某个工具被使用时触发——比如每次查询数据库后,自动记录到审计日志
on:session_complete
会话完成时触发——把结果自动同步到 Craft 文档或 Notion 页面
应用思考
你的团队用 Craft Agents 处理客户支持。每次对话开始,Claude 都需要了解:公司的退款政策、常见问题库、回复语气要求。你最应该怎么做?
你想每周五下午自动生成一份"本周 AI 工具使用报告"发给管理层。在 Craft Agents 架构里,最合适的实现方式是?
生产部署
把 Craft Agents 跑起来——从本地开发到 Docker 生产环境
两种运行模式
Craft Agents 有两种运行形态,适合不同场景。就像同一款软件,可以安装到个人电脑上用,也可以部署到公司服务器上让全团队访问:
Electron 桌面应用
个人使用。所有数据存在本地,MCP 工具以子进程方式运行。适合个人效率工具场景。
Docker Server 模式
团队共享。通过 HTTP 提供服务,WebUI 连接服务端,多人可同时访问同一个 Agent 服务。
Craft Agents 采用 Monorepo 结构,apps/ 目录放可运行的应用,packages/ 目录放共享的代码库。两个应用(electron 和 server)共享同一套核心业务逻辑——改一处,两边都受益。
项目结构地图
理解这个目录结构,你就能告诉 AI"去 packages/messaging-gateway 里修改路由逻辑",而不是让它到处乱找:
代码翻译:Server 启动逻辑
下面是 Craft Agents 在 Server 模式下的启动流程(来自 packages/server-core/src/bootstrap/):
export async function bootstrap(config: ServerConfig) {
const gateway = await createGateway(config);
const transport = new HttpTransport(gateway);
await transport.listen(config.port);
return { gateway, transport };
}
bootstrap 函数:启动整个服务的入口,像开店前的"开业准备"
先创建 MessagingGateway——注册所有命令处理器,准备好"收件室"
创建 HTTP 传输层——把 Gateway 包装成一个 HTTP 服务,这样浏览器可以连进来
开始监听指定端口,等待外部请求。就像挂出"营业中"牌子
返回 gateway 和 transport 的引用,以便后续可以关闭或查询状态
综合考验
你发现 Craft Agents 里的"工作区切换"逻辑有个 bug,在 electron 版和 server 版里都会复现。你应该去哪里修复它?
你要在 Docker 生产环境部署 Craft Agents,需要配置 ANTHROPIC_API_KEY。最安全的做法是?
现在你知道 Session/Workspace 的边界、工具系统的工作原理、技能和自动化的价值,以及如何告诉 AI 在正确的地方做正确的修改。这就是 AI 原生开发者的技术底气。