代理与工作流 API
管理代理、工作流、触发器、调度、目标和定时任务的端点。
Agent 端点
GET /api/agents
列出所有运行中的 Agent。
响应 200 OK:
[
{
"id": "a1b2c3d4-e5f6-7890-abcd-ef1234567890",
"name": "hello-world",
"state": "Running",
"created_at": "2025-01-15T10:30:00Z",
"model_provider": "groq",
"model_name": "llama-3.3-70b-versatile"
}
]
POST /api/agents
从 TOML 清单创建新 Agent。
请求体(JSON):
{
"manifest_toml": "name = \"my-agent\"\nversion = \"0.1.0\"\ndescription = \"Test agent\"\nauthor = \"me\"\nmodule = \"builtin:chat\"\n\n[model]\nprovider = \"groq\"\nmodel = \"llama-3.3-70b-versatile\"\n\n[capabilities]\ntools = [\"file_read\", \"web_fetch\"]\nmemory_read = [\"*\"]\nmemory_write = [\"self.*\"]\n"
}
响应 201 Created:
{
"agent_id": "a1b2c3d4-e5f6-7890-abcd-ef1234567890",
"name": "my-agent"
}
POST /api/agents/bulk
批量创建多个 Agent。
DELETE /api/agents/bulk
通过 ID 列表批量删除多个 Agent。
POST /api/agents/bulk/start
同时启动多个已停止的 Agent。
POST /api/agents/bulk/stop
同时停止多个运行中的 Agent。
GET /api/agents/{id}
获取单个 Agent 的详细信息。
响应 200 OK:
{
"id": "a1b2c3d4-e5f6-7890-abcd-ef1234567890",
"name": "hello-world",
"state": "Running",
"created_at": "2025-01-15T10:30:00Z",
"session_id": "s1b2c3d4-...",
"model": {
"provider": "groq",
"model": "llama-3.3-70b-versatile"
},
"capabilities": {
"tools": ["file_read", "file_list", "web_fetch"],
"network": []
},
"description": "A friendly greeting agent",
"tags": []
}
DELETE /api/agents/{id}
终止 Agent 并将其从注册表中移除。
响应 200 OK:
{
"status": "killed",
"agent_id": "a1b2c3d4-e5f6-7890-abcd-ef1234567890"
}
PATCH /api/agents/{id}
部分更新 Agent 的字段。
PUT /api/agents/{id}/update
在运行时更新 Agent 的配置。
请求体:
{
"description": "Updated description",
"system_prompt": "You are a specialized assistant.",
"tags": ["updated", "v2"]
}
响应 200 OK:
{
"status": "updated",
"agent_id": "a1b2c3d4-..."
}
PUT /api/agents/{id}/mode
设置 Agent 的运行模式。Stable 模式会固定当前模型并冻结技能注册表。Normal 模式恢复默认行为。
请求体:
{
"mode": "Stable"
}
响应 200 OK:
{
"status": "updated",
"mode": "Stable",
"agent_id": "a1b2c3d4-..."
}
PATCH /api/agents/{id}/identity
更新 Agent 的身份字段(名称、头像、人设)。
PATCH /api/agents/{id}/config
部分更新 Agent 的配置对象。
POST /api/agents/{id}/clone
将现有 Agent 克隆为新的 Agent,使用全新会话。
POST /api/agents/{id}/message
向 Agent 发送消息并接收完整响应。
请求体:
{
"message": "What files are in the current directory?",
"session_id": "optional-session-uuid"
}
可选 session_id:按请求覆盖此消息落到哪个 session。省略时用 agent 当前 session。给了就路由到那个特定 session(必须是该 agent 已存在的 session),让一个 agent 从单个客户端同时服务多条平行 chat,不需要先 POST /sessions/{id}/switch。fan-out 后端和多 tab dashboard 用得上。
响应 200 OK:
{
"response": "Here are the files in the current directory:\n- Cargo.toml\n- README.md\n...",
"input_tokens": 142,
"output_tokens": 87,
"iterations": 1
}
POST /api/agents/{id}/message/stream
发送消息并以 Server-Sent Events 流的方式接收响应(参见 SSE 流式传输)。
GET /api/agents/{id}/attach
作为额外只读订阅者附加到当前 session 的 SSE 流——多客户端联合观察时有用,CLI、Tauri 桌面、浏览器多 tab 想跟同一回合 live。
POST /message/stream 本身保持 single-consumer(只发起方拿到);本端点由按 session 的 SessionStreamHub 把每个 StreamEvent 同时复制给原调用方和任意数量的额外订阅者。订阅者不推进回合,只观察。
行为:
- 连接保持到当前回合结束。回合结束时服务端干净关闭。
- 你 attach 时没有正在跑的回合,连接等下一个。
- 单个 agent 可有多个并发 attacher,不会拖慢生产者(broadcast channel;按事件的扇出常数代价)。
- 事件格式跟
/message/stream完全一样,已有解析器无需改动。
# 第二个客户端联合观察当前回合
curl -N 'http://127.0.0.1:4545/api/agents/$AGENT/attach' \
-H 'Authorization: Bearer $LIBREFANG_API_KEY'
GET /api/agents/{id}/session
获取 Agent 当前的对话历史。
响应 200 OK:
{
"session_id": "s1b2c3d4-...",
"agent_id": "a1b2c3d4-...",
"message_count": 4,
"context_window_tokens": 1250,
"messages": [
{
"role": "User",
"content": "Hello"
},
{
"role": "Assistant",
"content": "Hello! How can I help you?"
}
]
}
GET /api/agents/{id}/sessions
列出 Agent 的所有会话。
POST /api/agents/{id}/sessions
为 Agent 创建新会话。
POST /api/agents/{id}/sessions/{session_id}/switch
切换 Agent 使用的会话。
GET /api/agents/{id}/sessions/by-label/{label}
通过用户指定的标签查找会话。
DELETE /api/agents/{id}/history
清除 Agent 的所有对话历史,但不创建新会话。
POST /api/agents/{id}/session/reset
重置 Agent 的会话,清除所有对话历史。
响应 200 OK:
{
"status": "reset",
"agent_id": "a1b2c3d4-...",
"new_session_id": "s5e6f7g8-..."
}
POST /api/agents/{id}/session/compact
触发基于 LLM 的会话压缩。Agent 的对话将由 LLM 进行摘要,仅保留最近的消息和生成的摘要。
响应 200 OK:
{
"status": "compacted",
"message": "Session compacted: 80 messages summarized, 20 kept"
}
POST /api/agents/{id}/stop
取消 Agent 当前的 LLM 运行。中止任何正在进行的生成。
响应 200 OK:
{
"status": "stopped",
"message": "Agent run cancelled"
}
PUT /api/agents/{id}/model
在运行时切换 Agent 的 LLM 模型。
请求体:
{
"model": "claude-sonnet-4-20250514"
}
响应 200 OK:
{
"status": "updated",
"model": "claude-sonnet-4-20250514"
}
GET /api/agents/{id}/tools
获取 Agent 当前的工具列表。
PUT /api/agents/{id}/tools
在运行时替换 Agent 的工具列表。
GET /api/agents/{id}/skills
获取 Agent 当前的技能列表。
PUT /api/agents/{id}/skills
在运行时替换 Agent 的技能列表。
GET /api/agents/{id}/mcp_servers
获取附加到指定 Agent 的 MCP 服务器。
PUT /api/agents/{id}/mcp_servers
设置附加到指定 Agent 的 MCP 服务器。
GET /api/agents/{id}/traces
获取 Agent 的执行追踪(工具调用、LLM 迭代、耗时等)。
GET /api/agents/{id}/metrics
获取 Agent 的运行时指标(Token 数量、延迟、错误率)。
GET /api/agents/{id}/logs
获取 Agent 进程输出的最近日志行。
GET /api/agents/{id}/deliveries
获取 Agent 从通道接收到的入站消息投递记录。
GET /api/agents/{id}/files
列出 Agent 私有文件工作区中的文件。
GET /api/agents/{id}/files/{filename}
从 Agent 的工作区下载指定文件。
PUT /api/agents/{id}/files/{filename}
上传或覆盖 Agent 工作区中的文件。
DELETE /api/agents/{id}/files/{filename}
从 Agent 的工作区中删除文件。
POST /api/agents/{id}/upload
上传文件到 Agent 的工作区(multipart 表单数据)。
GET /api/uploads/{file_id}
通过上传 ID 获取之前上传的文件。
支持的附件类型
POST /api/agents/{id}/message(或 streaming 变体)的 body 引用附件时,后端在调用 LLM 前会把每个 file_id 解析为 content block。识别三类:
| 类别 | 检测 | 产出 block |
|---|---|---|
| 图片 | Content-Type: image/*(png、jpeg、webp、gif) | 内联 base64 图片 block |
Content-Type: application/pdf | 文本 block,前缀 [Attached PDF: <filename>] 跟提取的纯文本 | |
| 文本 / 代码 | 任何 text/* MIME,列出的 application/* JSON/XML/YAML/TOML/JS/TS/SQL/GraphQL 类型,或浏览器没填 MIME 时识别已知扩展名 | 文本 block,前缀 [Attached file: <filename>] 跟 UTF-8 内容 |
识别的文本/代码扩展名包括 .txt、.md、.markdown、.rst、.csv、.tsv、.log、.json、.yaml、.yml、.toml、.xml、.ini、.conf、.cfg、.env、.html、.css、.js、.ts、.tsx、.jsx、.vue、.svelte、.py、.rs、.go、.java、.kt、.swift、.scala、.c、.cpp、.h、.rb、.php、.lua、.r、.sh、.bash、.sql、.graphql、.proto、.ipynb、Dockerfile、Makefile 等等。
上限:
- PDF 文本提取和文本文件读取都截断在 200,000 字符。
- 扫描件 / 纯图片 PDF 会带一段简短说明指出无法提取文本,模型仍能看到附件存在。
- 三类都不匹配的文件被跳过并记 warn 日志;LLM 调用照常带上其余附件继续。
本地小模型注意: 用户消息含多段文本(比如附件头 + 用户提示)时,runtime 在 session-repair 层把同 role 相邻的文本 block 合并。这个归一化在任何 driver 调用之前发生,所以经 Ollama / vLLM / LM Studio 的 chat-tuned 小模型只看到单段 user 文本加图片,依然会注意到附件。
GET /api/agents/{id}/ws
用于实时双向聊天的 WebSocket 连接(参见 WebSocket 协议)。
GET /api/agents/{id}/memory/export
将 Agent 的所有记忆条目导出为 JSON。
POST /api/agents/{id}/memory/import
从 JSON 导入 Agent 的记忆条目。
Owner Notice
agent 可以发结构化的私密旁路消息给运营者,跟用户可见的回复并行。notify_owner 内置 tool 在回合中收集一条或多条 notice;runtime 聚合到 ReplyEnvelope.owner_notice: Option<String> 字段。
通知出现在三处:
POST /message响应 body:顶层owner_notice字符串,跟response同级。POST /message/stream/GET /attachSSE:在最终Done之前的StreamEvent::OwnerNotice { text }事件。- WhatsApp gateway:设了
OWNER_JIDS环境变量(逗号分隔的 JID)后,每条 notice 扇出到这组 owner。其他 channel adapter 可以同样接入。
适合 "这个用户问了我没法在公开消息里答的事——升级给运维" 流程,或者任何不该让用户在 chat 里看到的审计轨迹。
GET /api/agents/{id}/sessions/{session_id}/trajectory
导出某个 session 的脱敏 trajectory(审计轨迹)。响应打包了该 session 的消息加最小元数据(agent 名、模型、system prompt 指纹、librefang 版本),所有消息都过隐私脱敏器再序列化。用于支持、审计、合规流程,需要分享 session 但又不能泄密时用这个。
Query 参数:
format=json(默认):单 JSON 对象,content-typeapplication/json。format=jsonl:NDJSON — 第一行是元数据头,后续每行一条消息;content-typeapplication/x-ndjson。方便 streaming 进 log pipeline 或jqfilter。
响应状态:
200— trajectory 包。400— agent 或 session id 不是合法 UUID。404— agent 或 session 不存在。
curl 'http://127.0.0.1:4545/api/agents/$AGENT/sessions/$SESSION/trajectory?format=jsonl' \
-H 'Authorization: Bearer $LIBREFANG_API_KEY' > session.jsonl
工作流端点
GET /api/workflows
列出所有已注册的工作流。
响应 200 OK:
[
{
"id": "w1b2c3d4-...",
"name": "code-review-pipeline",
"description": "Automated code review workflow",
"steps": 3,
"created_at": "2025-01-15T10:30:00Z"
}
]
POST /api/workflows
创建新的工作流定义。
请求体(JSON):
{
"name": "code-review-pipeline",
"description": "Review code changes with multiple agents",
"steps": [
{
"name": "analyze",
"agent_name": "coder",
"prompt": "Analyze this code for potential issues: {{input}}",
"mode": "sequential",
"timeout_secs": 120,
"error_mode": "fail",
"output_var": "analysis"
}
]
}
步骤配置选项:
| 字段 | 类型 | 说明 |
|---|---|---|
name | string | 步骤名称 |
agent_id | string | Agent UUID(与 agent_name 二选一) |
agent_name | string | Agent 名称(与 agent_id 二选一) |
prompt | string | 提示词模板,支持 {{input}} 和 {{output_var}} 占位符 |
mode | string | "sequential"、"fan_out"、"collect"、"conditional"、"loop" |
timeout_secs | integer | 每步超时时间(默认:120) |
error_mode | string | "fail"、"skip"、"retry" |
max_retries | integer | 用于 "retry" 错误模式(默认:3) |
output_var | string | 存储输出的变量名,供后续步骤使用 |
condition | string | 用于 "conditional" 模式 |
max_iterations | integer | 用于 "loop" 模式(默认:5) |
until | string | 用于 "loop" 模式:停止条件 |
响应 201 Created:
{
"workflow_id": "w1b2c3d4-..."
}
GET /api/workflows/{id}
获取指定的工作流定义。
PUT /api/workflows/{id}
更新已有的工作流定义。
DELETE /api/workflows/{id}
删除工作流定义。
POST /api/workflows/{id}/run
执行工作流。
请求体:
{
"input": "Review this pull request: ..."
}
响应 200 OK:
{
"run_id": "r1b2c3d4-...",
"output": "Code review summary:\n- No critical issues found\n...",
"status": "completed"
}
GET /api/workflows/{id}/runs
列出工作流的执行历史。
响应 200 OK:
[
{
"id": "r1b2c3d4-...",
"workflow_name": "code-review-pipeline",
"state": "Completed",
"steps_completed": 3,
"started_at": "2025-01-15T10:30:00Z",
"completed_at": "2025-01-15T10:32:15Z"
}
]
触发器端点
触发响应路由
触发器触发的响应路由到该 agent 的主通道 —— agent 在 [channels] 下注册的那个通道。之前的行为在调用路径上没有调用方通道时(cron 样的触发、调度器事件)会静默丢弃响应。现在响应总会落到可见的地方:agent 自己拥有的面向用户的表面。
task_posted 触发器的 assignee_match:self 过滤
task_posted 触发器可以选择只在任务被指派给 agent 自己时触发(按 agent id 匹配):
{
"pattern": {
"task_posted": {
"assignee_match": "self"
}
}
}
assignee_match 接受:
"self"— 仅当task.assignee_id == agent.id触发。"我应该处理分配给我的任务" 类 agent 用这个。"any"(默认) — 旧行为;任何task_posted事件都触发,不看 assignee。
GET /api/triggers
列出所有触发器。可按 Agent 过滤。
查询参数:
agent_id(可选):按 Agent UUID 过滤
响应 200 OK:
[
{
"id": "t1b2c3d4-...",
"agent_id": "a1b2c3d4-...",
"pattern": {"lifecycle": {}},
"prompt_template": "Event: {{event}}",
"enabled": true,
"fire_count": 5,
"max_fires": 0,
"created_at": "2025-01-15T10:30:00Z",
"cooldown_secs": 60,
"session_mode": "persistent",
"target_agent_id": null
}
]
GET /api/triggers/{id}
获取单个触发器详情。
响应 200 OK:与列表中的单条记录格式相同。
响应 404 Not Found:触发器不存在时返回。
POST /api/triggers
创建新的事件触发器。
请求体:
{
"agent_id": "a1b2c3d4-e5f6-7890-abcd-ef1234567890",
"pattern": {
"agent_spawned": {
"name_pattern": "*"
}
},
"prompt_template": "A new agent was spawned: {{event}}. Review its capabilities.",
"max_fires": 0,
"cooldown_secs": 60,
"session_mode": "persistent",
"target_agent_id": "b2c3d4e5-..."
}
除 agent_id、pattern 和 prompt_template 外,其余字段均为可选。
| 字段 | 类型 | 默认值 | 说明 |
|---|---|---|---|
agent_id | UUID | — | 触发器归属的 Agent。 |
pattern | object | — | 激活触发器的事件模式(见下方模式类型)。 |
prompt_template | string | — | 事件触发时发送给 Agent 的提示,支持 {{event}} 占位符。 |
max_fires | integer | 0 | 最多触发次数,0 表示无限制。 |
cooldown_secs | integer | 引擎默认值 | 两次触发之间的最短间隔(秒)。省略则使用引擎默认值。 |
session_mode | "persistent" | "new" | Agent 默认值 | 每次触发的会话连续性。省略则继承 Agent 配置。 |
target_agent_id | UUID | null | 将触发消息路由到指定 Agent,而非触发器所有者。 |
支持的模式类型:
| 模式 | 说明 |
|---|---|
{"lifecycle": {}} | 所有生命周期事件 |
{"agent_spawned": {"name_pattern": "*"}} | Agent 创建事件 |
{"agent_terminated": {}} | Agent 终止事件 |
{"all": {}} | 所有事件 |
响应 201 Created:
{
"trigger_id": "t1b2c3d4-...",
"agent_id": "a1b2c3d4-..."
}
PATCH /api/triggers/{id}
部分更新触发器配置。所有字段均为可选,仅更新提供的字段。
将 cooldown_secs、session_mode 或 target_agent_id 设为 null 可清除覆盖值并恢复默认。省略字段则保持不变。
请求体:
{
"prompt_template": "Updated: {{event}}",
"enabled": true,
"max_fires": 10,
"cooldown_secs": 120,
"session_mode": "new",
"target_agent_id": "b2c3d4e5-..."
}
响应 200 OK:返回完整的更新后触发器对象(与 GET /api/triggers/{id} 格式相同)。
响应 404 Not Found:触发器不存在时返回。
DELETE /api/triggers/{id}
删除触发器。
响应 200 OK:
{
"status": "removed",
"trigger_id": "t1b2c3d4-..."
}
调度端点
基于时间的 Agent 激活,使用 cron 表达式或间隔语法。
GET /api/schedules
列出所有已配置的调度。
POST /api/schedules
创建新的调度。
请求体:
{
"agent_id": "a1b2c3d4-...",
"cron": "0 9 * * 1-5",
"prompt": "Good morning! Summarize overnight events.",
"enabled": true
}
GET /api/schedules/{id}
获取指定调度。
PUT /api/schedules/{id}
更新调度。
DELETE /api/schedules/{id}
删除调度。
POST /api/schedules/{id}/run
立即触发调度执行(本次调用忽略其 cron 表达式)。
目标端点
用于多步骤 Agent 目标的层级化目标追踪系统。
GET /api/goals
列出所有目标。
POST /api/goals
创建新目标。
请求体:
{
"title": "Migrate database to PostgreSQL",
"description": "Complete migration with zero downtime",
"agent_id": "a1b2c3d4-...",
"parent_id": null,
"due_at": "2025-02-01T00:00:00Z"
}
GET /api/goals/{id}
获取指定目标。
PUT /api/goals/{id}
更新目标(标题、状态、描述、截止日期)。
DELETE /api/goals/{id}
删除目标及其所有子目标。
GET /api/goals/{id}/children
列出指定父目标的子目标。
定时任务端点
使用标准 cron 表达式语法调度周期性任务。
GET /api/cron/jobs
列出所有定时任务。
POST /api/cron/jobs
创建新的定时任务。
请求体:
{
"name": "daily-digest",
"schedule": "0 8 * * *",
"agent_id": "a1b2c3d4-...",
"prompt": "Prepare the daily digest of overnight activity.",
"enabled": true
}
GET /api/cron/jobs/{id}
获取指定定时任务的详情。
PUT /api/cron/jobs/{id}
更新定时任务(调度表达式、提示词、Agent 等)。
DELETE /api/cron/jobs/{id}
删除定时任务。
PUT /api/cron/jobs/{id}/enable
切换定时任务的启用状态。
请求体:
{
"enabled": false
}
GET /api/cron/jobs/{id}/status
获取定时任务的上次执行结果和下次预定时间。
中断正在运行的 Agent 轮次
源码: librefang-runtime/src/interrupt.rs
SessionInterrupt 是一个 Arc<AtomicBool> 信号,agent 循环在每个工具调用边界处检查它。设置该标志后,循环在当前工具调用完成后干净退出——不会强制终止线程或取消正在进行的网络请求。
级联到 agent_send 子 agent:父 agent 的 /stop 现在会传播到它通过 agent_send tool 派出的每个子 agent。级联沿着 agent_send 调用时建立的父→子边走,所以对面向用户的 agent 单次 /stop 会停掉它启动的整个 fan-out 树 — 用户放弃后不会有孤儿子 agent 循环继续。完全递归:子 agent 自己又调了 agent_send 的话继续向下传。
中断端点
POST /api/agents/{agent_id}/sessions/{session_id}/interrupt
无需请求体。信号被接受时响应 204 No Content。
curl -X POST \
"http://127.0.0.1:4545/api/agents/${AGENT_ID}/sessions/${SESSION_ID}/interrupt"
行为
| 场景 | 发生的事情 |
|---|---|
| Agent 在工具调用之间 | 循环在下次边界检查时退出,记录截止当前的部分响应。 |
| Agent 正在进行 LLM 调用 | LLM 调用正常完成,然后循环检查标志并在执行下一个工具前退出。 |
| Agent 正在执行工具 | 工具运行至完成,然后循环退出。不会在工具执行途中中止。 |
| Agent 已经完成 | 中断标志被忽略,不返回错误。 |
| 没有活跃轮次 | 仍返回 204;标志被设置,将在下次轮次时生效。 |
自动清除
中断标志在以下情况下自动清除:
- 新的传入消息开始新的 agent 轮次
- 会话被重置
轮次中断结束后无需发送"恢复"或"清除"请求。
使用场景
- 用户主动停止:用户在界面中点击"停止"按钮,前端调用中断端点,agent 在当前工具调用后停止。
- 看门狗超时:外部监控检测到长时间运行的轮次,中断它以避免 token 预算耗尽。
- 优雅关闭:重启守护进程前中断所有活跃会话,使其记录干净的停止状态而非突然断连。