智能体工作流是一个多步骤系统,语言模型在其中不仅仅是回答一次:它会规划、选择工具、执行动作、检查结果,并决定下一步做什么,直到任务完成。在实践中,这意味着将大语言模型推理层与工具调用、真实的执行环境以及评估循环相结合。如果你正在构建编码智能体、研究智能体或需要在任务中途进行调整的内部自动化系统,这通常就是你实际要构建的架构。
什么是智能体工作流?
智能体工作流是 AI 系统背后的模式,它允许系统在任务中推进,而不是停留在文本生成上。你不是向模型请求一个响应并将其返回给用户,而是让模型在一个受控循环内运行:
- 读取目标和当前状态。
- 规划下一步。
- 调用工具或运行代码。
- 观察结果。
- 评估任务是否完成。
- 如果需要则重复。
这与固定工作流不同,在固定工作流中,每一步都是预先在代码中写好的。在固定工作流中,你提前决定了路径。而在智能体工作流中,模型在你提供的边界内决定下一步要采取哪个动作。
这种区别很重要,因为许多真实的开发者任务并不是线性的。一个编码智能体可能需要先检查文件,然后才知道要运行哪个测试。一个研究智能体可能需要搜索两次,因为第一个来源不完整。一个浏览器智能体可能需要从登录失败或页面布局变化中恢复。这些都是工作流问题,但它们需要适应性。
智能体工作流与 AI 智能体有何不同?
人们经常互换使用这两个术语,但将它们区分开来更有用:
- 智能体工作流 是执行模式。
- AI 智能体 是构建在该模式之上的产品或系统。
你可以有一个范围狭窄的智能体工作流,仅用于分类支持工单;也可以有一个更广泛的 AI 智能体,协调跨多个任务的规划、工具使用、记忆和审批检查点。
如果你想要一个实用的规则,那么在谈论架构和控制流时使用 工作流,在谈论面向用户的系统时使用 ** 智能体**。
智能体工作流的核心部分是什么?
大多数生产系统最终都会包含以下五个部分。
1. 规划器
规划器将宽泛的指令转化为下一个具体的动作。有时这是一个显式的规划步骤,输出一个任务列表。有时它是隐式的,在每个工具调用轮次中发生。无论哪种方式,模型都需要足够的上下文来决定是应该读取、写入、搜索、执行还是停止。
好的规划并不意味着为每个请求生成一个冗长的提纲。它意味着让下一步行动清晰可辨。对于短任务,单步规划就足够了。对于较长的任务,例如仓库重构、浏览器自动化或文档审查,显式的规划可以减少反复折腾。
2. 工具层
工具是工作流与世界的接口。一个强大的工具层通常是狭窄且可预测的。例如:
read_file(path)write_file(path, content)search_files(query)run_command(cmd)fetch_url(url)
小工具更容易让模型正确调用,更容易记录日志,也更容易保障安全。大型的“万能”工具起初看起来很便捷,但后来会变得难以调试,因为你无法判断失败是来自模型决策、工具实现还是背后的外部系统。
3. 执行运行时
一旦模型决定行动,就需要有东西来执行该动作。对于任何涉及写入文件、安装包、运行代码或打开浏览器会话的工作流,这个运行时都需要隔离。
这就是沙箱发挥作用的地方。Novita Agent Sandbox 正是为此执行层而设计:一个独立的环境,智能体的动作可以在其中运行,而不会直接接触主机系统。这就是“模型建议了一个命令”和“工作流安全地执行了该命令”之间的区别。
4. 状态与记忆
智能体工作流需要跨步骤的工作记忆。这通常包括:
- 对话状态
- 工具输出
- 中间文件
- 执行日志
- 草稿板或简短计划
没有状态,每一步都会变成无状态的提示工程,一旦任务跨越多个动作,系统就会崩溃。
5. 评估循环
这是许多团队添加得太晚的部分。工作流需要一种方法来判断某一步是否成功以及任务是否完成。在编码工作流中,这可能意味着测试通过。在研究工作流中,这可能意味着答案引用了足够多的可靠来源。在浏览器工作流中,这可能意味着预期的 UI 状态是可见的。
没有评估,“智能体”往往就变成了“一直调用工具直到超时”。
为什么规划在构建智能体工作流中很重要
构建智能体工作流时最大的错误是假设模型应该在每一步都从头即兴发挥。
这通常会产生三个问题:
- 模型反复访问相同的文件或 URL
- 工具使用变得嘈杂且昂贵
- 工作流失去了清晰的停止条件
一个更好的模式是轻量级规划加上接地气的执行。让模型决定下一个动作,但要让它基于明确的任务、可见的当前状态和一小部分允许的工具来做决定。这样可以在需要的地方保持灵活性,在不需要的地方移除它。
在编码工作流中,规划通常看起来像这样:
- 识别涉及的文件。
- 读取当前的实现。
- 决定最小的更改。
- 进行编辑。
- 运行验证。
- 要么停止,要么修复。
这仍然是智能体的,因为当仓库出现意外时,模型可以分支。但它不会漫无目的。
在智能体工作流中,工具使用应该如何工作?
工具使用应该是显式的、类型化的和可观察的。
如果你的模型支持函数调用,就使用它。Novita 的 LLM API 提供了一个兼容 OpenAI 的端点,并直接记录了函数调用,这是让模型在工具之间进行选择的最清晰方式,而无需依赖脆弱的字符串解析。
一些规则可以使工具使用更加可靠:
- 保持工具名称具体。
- 使用带有必填字段的模式。
- 返回完整的结果,包括错误。
- 记录每次调用、参数和输出。
- 使破坏性操作变得罕见且易于控制。
工具层还应该反映真实的边界。例如,不要给编码智能体一个名为 edit_repo_and_run_tests 的巨型工具。将读取、写入和执行步骤分开,这样模型在出现故障时可以恢复。
为什么代码执行需要一个沙箱?
一个从不执行任何操作的智能体工作流通常可以留在普通的应用服务器内。一旦它开始运行 shell 命令、安装依赖项、处理下载的文件或浏览开放的网页,你就需要隔离。
沙箱解决了两个不同的问题:
- 安全性:生成的代码和工具输出可能是错误的、恶意的,或者仅仅是不可预测的。
- 状态性:多步骤任务需要一个持久的工作空间,其中文件、包和执行历史可以跨轮次存活。
对于许多团队来说,第二点与第一点同样重要。如果每一步都从一个干净的机器开始,那么一个编辑代码、运行测试、修复失败并重新运行验证的工作流是不可能实现的。
这就是为什么实用的架构通常是:
- LLM API 用于规划和工具选择
- 沙箱运行时 用于执行和持久化
Novita 自然地适应了这种分离:LLM API 充当规划器和工具调用层,而 Agent Sandbox 处理实际的执行环境。
如何评估智能体工作流?
评估必须在两个层面进行。
步骤级评估
上一个动作是否有效?
示例:
- 命令是否成功退出?
- API 是否返回了有效的 JSON?
- 预期的文件是否已创建?
- 浏览器页面是否包含目标元素?
任务级评估
工作流是否解决了用户的问题?
示例:
- 代码更改后测试是否通过?
- 摘要是否用证据回答了研究问题?
- 自动化是否在没有手动清理的情况下完成了事务?
强大的工作流两者都使用。如果你只评估最终输出,你会错过执行过程中明显的失败信号。如果你只评估步骤,工作流可以完成一长串局部有效的动作,但仍然无法完成真正的任务。
构建智能体工作流的实用架构
以下是大多数团队应该开始的架构:
- 用户请求进入你的应用。
- 你的控制器将目标、状态和可用工具发送给大语言模型。
- 大语言模型返回直接答案或工具调用。
- 你的控制器在沙箱或其他受控运行时内执行该工具。
- 工具结果被附加到对话状态中。
- 评估器检查完成、失败或审批关卡。
- 循环继续,直到工作流完成或被阻塞。
这个控制器循环可以很简单。在许多情况下,一个单一编排进程就足够了。你不需要在第一天就构建一个多智能体系统。从一个规划器、几个定义良好的工具、一个沙箱化运行时和一个清晰的评估器开始。
示例:一个用 Python 实现的智能体工作流控制器
下面的示例展示了控制循环的形态。它使用 Novita 兼容 OpenAI 的 API 进行工具调用。执行函数需要你根据自己的运行时或沙箱来实现。
import json
import os
from openai import OpenAI
client = OpenAI(
base_url="https://api.novita.ai/openai",
api_key=os.environ["NOVITA_API_KEY"],
)
tools = [
{
"type": "function",
"function": {
"name": "read_file",
"description": "从工作空间读取文件",
"parameters": {
"type": "object",
"properties": {"path": {"type": "string"}},
"required": ["path"],
},
},
},
{
"type": "function",
"function": {
"name": "run_command",
"description": "在沙箱中运行 shell 命令",
"parameters": {
"type": "object",
"properties": {"cmd": {"type": "string"}},
"required": ["cmd"],
},
},
},
]
def read_file(path: str) -> str:
# 针对你自己的工作空间或沙箱文件系统实现此函数。
raise NotImplementedError
def run_command(cmd: str) -> str:
# 针对你的沙箱运行时实现此函数。
raise NotImplementedError
dispatch = {
"read_file": read_file,
"run_command": run_command,
}
def run_workflow(task: str, model: str) -> str:
messages = [
{
"role": "system",
"content": (
"你是一个工作流控制器。在需要时使用工具,"
"在每个动作后检查结果,并在任务完成时停止。"
),
},
{"role": "user", "content": task},
]
while True:
response = client.chat.completions.create(
model=model,
messages=messages,
tools=tools,
tool_choice="auto",
)
message = response.choices[0].message
messages.append(message)
if not message.tool_calls:
return message.content
for call in message.tool_calls:
fn = dispatch[call.function.name]
args = json.loads(call.function.arguments)
result = fn(**args)
messages.append(
{
"role": "tool",
"tool_call_id": call.id,
"content": result,
}
)
这有意保持最小化。在生产环境中,你还需要添加:
- 针对瞬时错误的重试策略
- 超时和预算限制
- 敏感操作的人工审批
- 结构化的步骤日志
- 最终完成前的任务级评估器
应该使用哪个模型作为规划器?
对于智能体工作流,规划器模型不仅需要原始智能。它需要正确的形态:
- 可靠的工具调用
- 稳定的长上下文行为
- 强大的指令遵循能力
- 多轮深度下的可预测延迟
如果你想要 Novita 上的一个开源起点,Qwen3 Coder 30B A3B Instruct 是工作流规划和编码导向工具使用的实用选择。Novita 当前的模型页面列出了兼容 OpenAI 的访问、函数调用支持、结构化输出支持以及 160K 的托管上下文窗口。对于许多内部自动化和编码任务来说,这足以构建一个严肃的初版,然后再迁移到更大或更专业的规划器。
正确的模型仍然取决于具体工作。对于广泛的、推理密集型的工作流,首先选择规划质量。对于高容量自动化,延迟和成本可能与基准强度同样重要。
智能体工作流中的常见失败模式
大多数失败并不戏剧化。它们是重复且昂贵的。
过度工具化
如果每个能力都成为自己的远程依赖项,工作流花在协调上的时间会比做有用工作的时间还多。
薄弱的停止规则
如果系统永远不知道“完成”何时为真,它就会不断生成下一步。
糟糕的错误处理
如果工具返回诸如“失败”之类的模糊消息,而不是可操作的结果,模型就无法恢复。
没有沙箱边界
工作流可能在开发中工作,但一旦接触真实文件、凭据或外部系统,就会变得不安全。
没有评估器
智能体看起来很忙,但从未证明任务已正确完成。
什么时候不应该使用智能体工作流?
不要仅仅因为这个术语流行就构建一个。
如果出现以下情况,你可能 不需要 智能体工作流:
- 任务是单次生成
- 路径是固定的且很少变化
- 传统程序可以廉价地决定每一步
- 不需要工具使用或执行
例如,如果你的应用总是接收表单输入,调用一个提示,然后返回一封格式化的电子邮件,那么普通的 LLM 工作流更简单、更好。
当环境可能让系统感到意外,而系统仍然需要继续运行时,智能体工作流才值得投入。
常见问题解答
智能体工作流和函数调用是一样的吗?
不是。函数调用是工作流内部的一种机制。完整的工作流还需要控制流、状态、执行和评估。
构建智能体工作流需要多个智能体吗?
不需要。大多数团队应该从一个控制器循环和几个工具开始。多智能体设计在以后会有用,但它们不是默认的起点。
最重要的安全控制是什么?
对于运行代码或接触外部系统的工作流,最重要的控制是隔离的执行环境,结合狭窄的工具权限。
最简单的生产就绪技术栈是什么?
一个好的初始技术栈是:一个兼容 OpenAI 的 LLM API、一个小型工具注册表、一个沙箱化运行时,以及一个能够判断任务是否实际完成的评估器。
