12-Factor Agents - 构建可靠LLM应用的设计原则
12-Factor Agents是一套方法论,就像软件开发领域的"12-Factor App"原则一样,它把传统软件工程里经过验证的最佳实践,创造性地用到了大模型驱动的Agent开发里。其核心目标是弥补Agent从原型开发到生产级应用的鸿沟,让Agent在可靠性、可伸缩性、可维护性、可调试性和安全性上,达到企业级应用…
目录
- 自然语言到工具调用
- 掌控你的提示词
- 掌控你的上下文窗口
- 工具只是结构化输出
- 统一执行状态和业务状态
- 通过简单API启动/暂停/恢复
- 通过工具调用联系人工
- 掌控你的控制流
- 将错误压缩到上下文窗口
- 小而专注的Agent
- 从任何地方触发,在用户所在的地方与他们见面
- 使你的Agent成为一个无状态的reducer
文本版 · 供搜索与朗读
12-Factor Agents - 构建可靠LLM应用的设计原则
12-Factor Agents
构建可靠LLM应用的设计原则
12-Factor Agents是一套方法论,就像软件开发领域的"12-Factor App"原则一样,它把传统软件工程里经过验证的最佳实践,创造性地用到了大模型驱动的Agent开发里。其核心目标是弥补Agent从原型开发到生产级应用的鸿沟,让Agent在可靠性、可伸缩性、可维护性、可调试性和安全性上,达到企业级应用的标准。
psychology
settings
integration_instructions
1
自然语言到工具调用
将用户自然语言指令结构化转换为API调用,利用LLM的意图识别能力
2
掌控你的提示词
开发者应完全控制LLM的提示词,提示词应像代码一样被设计、版本控制和测试
3
掌控你的上下文窗口
主动管理和构建传递给LLM的上下文信息,包含LLM决策所需的一切信息
4
工具只是结构化输出
Agent中工具调用的本质是LLM生成的结构化数据,触发确定性代码执行
5
统一执行状态和业务状态
简化并统一执行状态和业务状态,Agent执行状态可从上下文窗口推断
6
通过简单API启动/暂停/恢复
为Agent提供简单、直观的API接口,方便各种系统轻松交互
7
通过工具调用联系人工
LLM通过工具调用明确与人工交互,而不是直接输出自然语言请求
8
掌控你的控制流
开发者应完全控制Agent的控制流,而不是依赖框架的预设流程
9
将错误压缩到上下文窗口
将错误信息压缩并整合到上下文窗口中,以便LLM能够理解和处理错误
10
小而专注的Agent
构建小型、专注的Agent,每个Agent负责特定任务,而不是大型多功能Agent
11
从任何地方触发,在用户所在的地方与他们见面
Agent应能从各种渠道触发,并在用户所在的地方交互
12
使你的Agent成为一个无状态的reducer
Agent应设计为无状态的reducer,接收输入状态并返回新状态
这套原则不是一个像LangChain这样可以直接拿来用的"工具箱",而是一套"方法论",帮助开发者构建生产级的LLM应用。
讨论回复(23)
文本版 · 供搜索与朗读
12-Factor Agents - 背景与发展历史
12-Factor Agents - 背景与发展历史
从软件工程到智能代理的演变历程
account_tree
软件工程的历史演变60年前
软件本质上是有向图(DG),早期我们用流程图表示程序。20年前,DAG编排器如Airflow、Prefect等开始流行,增加了可观察性、模块化、重试机制等功能。
psychology
机器学习与DAG的结合10-15年前
当ML模型开始变得足够有用时,我们开始在DAG中加入ML模型,如文本摘要、分类等任务。但本质上仍是确定性软件。
auto_awesome
Agent的承诺现在
Agent让我们可以抛弃DAG,不再需要软件工程师编写每个步骤和边缘情况。只需给Agent一个目标和一组转换,让LLM实时决策路径。
Agent的核心循环:LLM决定下一步 → 执行工具调用 → 结果添加到上下文窗口 → 重复直到完成
warning
Agent循环模式的挑战
最大问题:当上下文窗口过长时,Agent会迷失方向,反复尝试相同但无效的方法。即使模型支持更长的上下文窗口,小而专注的提示词和上下文总能带来更好的结果。
即使Agent正确率达到90%,也远未达到"可以交给客户使用"的标准
lightbulb
实际有效的解决方案:微代理(Micro Agents)
将Agent模式融入更广泛的确定性DAG中。语言模型管理范围明确的任务集,便于整合实时人工反馈,将其转化为工作流步骤,而不会陷入上下文错误循环。
真实案例:部署机器人(Deploybot)处理部署流程,通过LLM解析人类反馈并提出更新方案,隔离任务和上下文以保持LLM专注
architecture
Agent的本质
一个Agent由四个核心组件构成:
提示词 - 告诉LLM如何行为,有哪些可用工具
Switch语句 - 根据LLM返回的JSON决定下一步行动
累积上下文 - 存储已发生步骤及其结果
For循环 - 直到LLM发出某种"终止"工具调用
12-Factor Agents将这些模式形式化,使其可应用于任何软件项目,添加令人印象深刻的AI功能,无需完全采用"AI Agent"的传统实现/定义。
文本版 · 供搜索与朗读
原则1:自然语言到工具调用
原则1:自然语言到工具调用
将用户自然语言指令结构化转换为API调用
translate
核心概念
这是Agent构建中最常见的模式之一,将自然语言转换为结构化的工具调用。这种模式允许构建能够推理任务并执行它们的Agent,利用LLM的意图识别能力。
从自然语言请求到结构化API调用,LLM作为翻译桥梁,连接人类意图与机器执行
compare_arrows
工作原理
LLM接收自然语言输入,理解用户意图,然后返回一个结构化对象,描述需要执行的工具调用。确定性代码随后接收这个结构化对象并执行相应的操作。
# LLM接收自然语言并返回结构化对象
nextStep = await llm.determineNextStep(
"""
create a payment link for $750 to Jeff
for sponsoring the february AI tinkerers meetup
"""
)
# 根据函数名称处理结构化输出
if nextStep.function == 'create_payment_link':
stripe.paymentlinks.create(nextStep.parameters)
return
elif nextStep.function == 'something_else':
# ... 其他情况
pass
else: # 模型调用了我们不知道的工具
# 执行其他操作
pass
code
实际应用示例
以下是将自然语言请求转换为Stripe API调用的示例:
自然语言请求
can you create a payment link for $750 to Terri for sponsoring the february AI tinkerers meetup?
结构化API调用
{
"function": {
"name": "create_payment_link",
"parameters": {
"amount": 750,
"customer": "cust_128934ddasf9",
"product": "prod_8675309",
"price": "prc_09874329fds",
"quantity": 1,
"memo": "Hey Jeff - see below for the payment link for the february ai tinkerers meetup"
}
}
}
实际应用中,Agent可能需要先列出客户、产品和价格,以构建包含正确ID的有效载荷,或将这些ID包含在提示词/上下文窗口中。
自然语言到工具调用是Agent构建的基础模式,它将人类意图转换为机器可执行的结构化操作,是连接用户与系统的关键桥梁。
文本版 · 供搜索与朗读
原则2:掌控你的提示词
原则2:掌控你的提示词
不要将提示词工程外包给框架
code
核心概念
开发者应该完全控制LLM的提示词,提示词应像代码一样被设计、版本控制和测试。不要依赖框架提供的"黑盒"方法,而是将提示词作为一等代码来管理。
提示词是应用逻辑和LLM之间的主要接口,拥有完全控制权对于生产级Agent至关重要
compare
框架方法 vs 直接掌控
block框架的"黑盒"方法
agent = Agent(
role="...",
goal="...",
personality="...",
tools=[tool1, tool2, tool3]
)
task = Task(
instructions="...",
expected_output=OutputModel
)
result = agent.run(task)
这种方法虽然可以快速上手,但难以调整和逆向工程,无法精确控制模型接收的指令。
check_circle直接掌控提示词
function DetermineNextStep(thread: string) ->
DoneForNow | ListGitTags | DeployBackend |
DeployFrontend | RequestMoreInformation {
prompt #"
{{ _.role("system") }}
You are a helpful assistant that manages deployments
for frontend and backend systems.
You work diligently to ensure safe and successful
deployments by following best practices...
{{ _.role("user") }}
{{ thread }}
What should the next step be?
"#
}
直接掌控提示词,可以精确控制指令,便于测试和迭代,提高透明度。
stars
掌控提示词的好处
control_camera
完全控制
编写Agent所需的确切指令,无需黑盒抽象
checklist
测试和评估
像对待其他代码一样为提示词构建测试和评估
loop
快速迭代
根据实际性能快速修改提示词
visibility
透明度
清楚了解Agent正在使用的指令
psychology
角色利用
利用支持非标准用户/助手角色使用的API
我不知道什么是最佳提示词,但我知道你希望拥有能够尝试一切的灵活性。掌控你的提示词,为生产级Agent提供所需的灵活性和精确控制。
文本版 · 供搜索与朗读
原则3:掌控你的上下文窗口
原则3:掌控你的上下文窗口
主动管理和构建传递给LLM的上下文信息
layers
核心概念
不一定需要使用标准的基于消息的格式向LLM传递上下文。上下文工程是关键——LLM是无状态函数,将输入转换为输出,要获得最佳输出,需要提供最佳输入。
在任何给定的时刻,Agent对LLM的输入都是"这是到目前为止发生的事情,下一步是什么"
compare
标准与自定义上下文格式
format_list_bulleted标准消息格式
[
{
"role": "system",
"content": "You are a helpful assistant..."
},
{
"role": "user",
"content": "Can you deploy the backend?"
},
{
"role": "assistant",
"content": null,
"tool_calls": [
{
"id": "1",
"name": "list_git_tags",
"arguments": "{}"
}
]
}
]
标准格式适用于大多数用例,但可能不是最高效的上下文传递方式。
code自定义上下文格式
[
{
"role": "system",
"content": "You are a helpful assistant..."
},
{
"role": "user",
"content": |
Here's everything that happened so far:
<slack_message>
From: @alex
Channel: #deployments
Text: Can you deploy the backend?
</slack_message>
<list_git_tags>
intent: "list_git_tags"
</list_git_tags>
<list_git_tags_result>
tags: [...]
</list_git_tags_result>
what's the next step?
}
]
自定义格式可以根据用例优化,提高令牌效率和LLM理解能力。
code
上下文示例
以下是使用XML风格格式的上下文窗口示例:
<slack_message>
From: @alex
Channel: #deployments
Text: Can you deploy the latest backend to production?
</slack_message>
<list_git_tags>
intent: "list_git_tags"
</list_git_tags>
<list_git_tags_result>
tags:
- name: "v1.2.3"
commit: "abc123"
date: "2024-03-15T10:00:00Z"
- name: "v1.2.2"
commit: "def456"
date: "2024-03-14T15:30:00Z"
</list_git_tags_result>
XML风格格式只是其中一种示例——关键是你可以构建适合自己应用的格式。拥有尝试不同上下文结构的灵活性将获得更好的质量。
stars
掌控上下文窗口的好处
data_object
信息密度
以最大化LLM理解的方式构建信息
healing
错误处理
以帮助LLM恢复的格式包含错误信息
security
安全性
控制传递给LLM的信息,过滤敏感数据
autorenew
灵活性
根据学习到的最佳实践调整格式
speed
令牌效率
优化上下文格式以提高令牌效率
上下文窗口是与LLM的主要接口。控制如何构建和呈现信息可以显著提高Agent的性能。我不知道什么是最佳方法,但我知道你希望拥有能够尝试一切的灵活性。
文本版 · 供搜索与朗读
原则4:工具只是结构化输出
原则4:工具只是结构化输出
Agent中工具调用的本质是LLM生成的结构化数据,触发确定性代码执行
data_object
核心概念
工具调用本质上只是LLM生成的结构化输出,而不是特殊的API调用。LLM生成描述要执行的操作的结构化数据,然后确定性代码执行该操作。
工具调用是LLM决策与实际执行之间的桥梁,通过结构化数据实现清晰分离
sync_alt
工作流程
psychology
LLM接收上下文
生成结构化输出
arrow_forward
code
确定性代码
解析结构化数据
arrow_forward
play_arrow
执行相应操作
返回结果
// LLM生成的结构化输出示例
{
"function": {
"name": "deploy_backend",
"parameters": {
"tag": "v1.2.3",
"environment": "production"
}
}
}
// 确定性代码处理
if (output.function.name === "deploy_backend") {
const { tag, environment } = output.function.parameters;
deployBackend(tag, environment);
}
stars
优势与应用
architecture
清晰分离
LLM决策与实际执行操作之间有明确界限
bug_report
易于调试
可以检查结构化输出,更容易定位问题
settings_suggest
灵活交互
支持更复杂的交互和工作流程
integration_instructions
测试友好
可以模拟结构化输出进行单元测试
这种模式比传统函数调用更灵活,允许更复杂的交互和工作流,同时保持代码的可测试性和可维护性
将工具调用视为结构化输出而非特殊API调用,可以带来更好的灵活性和可维护性。这种思维方式帮助构建更可靠、更易于调试的Agent系统。
文本版 · 供搜索与朗读
原则5:统一执行状态和业务状态
原则5:统一执行状态和业务状态
简化并统一执行状态和业务状态,Agent执行状态可从上下文窗口推断
merge_type
核心概念
简化并统一执行状态和业务状态,使Agent执行状态可以从上下文窗口中推断。这种方法减少了复杂性,使理解和调试代理更容易。
上下文窗口成为代理状态的单一真实来源,减少状态不一致的风险
compare_arrows
状态统一
play_circle执行状态
Agent当前执行的位置、已完成的步骤、待处理的任务等执行流程相关信息。
business业务状态
业务数据、用户信息、配置参数等与业务逻辑相关的状态信息。
all_inclusive统一状态
将执行状态和业务状态统一存储在上下文窗口中,使Agent可以从上下文推断出完整状态。这种方式使状态管理更加简单,便于暂停、恢复和测试。
code
实现示例
// 从上下文窗口推断执行状态
function inferExecutionState(context) {
const lastEvent = context[context.length - 1];
if (lastEvent.type === 'deploy_backend_result') {
return {
status: 'BACKEND_DEPLOYED',
nextStep: 'deploy_frontend',
data: lastEvent.data
};
}
if (lastEvent.type === 'error') {
return {
status: 'ERROR_OCCURRED',
nextStep: 'handle_error',
error: lastEvent.data
};
}
// 默认状态
return {
status: 'INITIAL',
nextStep: 'determine_next_action'
};
}
// 暂停和恢复Agent
async function pauseAgent(context) {
// 序列化上下文以供稍后恢复
await saveToDatabase(context);
return "Agent paused successfully";
}
async function resumeAgent(agentId) {
// 从数据库恢复上下文
const context = await loadFromDatabase(agentId);
// 从上下文推断执行状态
const state = inferExecutionState(context);
// 继续执行
return await continueExecution(state, context);
}
stars
优势
psychology
简化复杂性
减少状态管理的复杂性,使系统更易理解
pause_circle
易于暂停/恢复
通过保存上下文轻松暂停和恢复Agent执行
bug_report
便于调试
通过检查上下文更容易定位和解决问题
verified
状态一致性
减少状态不一致的风险,提高系统健壮性
通过统一执行状态和业务状态,我们可以构建更可靠、更易于维护的Agent系统。上下文窗口成为状态的单一真实来源,简化了状态管理并提高了系统的健壮性。
文本版 · 供搜索与朗读
原则6:通过简单API启动/暂停/恢复
原则6:通过简单API启动/暂停/恢复
为Agent提供简单、直观的API接口,方便各种系统轻松交互
api
核心概念
为Agent提供简单、直观的API接口,使各种系统能够轻松与Agent交互。这些API应该易于使用、一致且直观,特别对于长时间运行的任务,暂停和恢复能力尤为重要。
简单API使Agent能够无缝集成到现有系统和工作流中,提高生产环境的可用性
touch_app
核心API操作
play_arrow
启动
初始化Agent并开始执行任务,可传入初始上下文和配置
pause
暂停
临时停止Agent执行,保存当前状态以便后续恢复
resume
恢复
从暂停点继续执行Agent,恢复之前保存的状态
// 简单的Agent API示例
class AgentAPI {
// 启动Agent
async launch(initialContext, config) {
const agentId = generateUniqueId();
await initializeAgent(agentId, initialContext, config);
return { agentId, status: 'running' };
}
// 暂停Agent
async pause(agentId) {
const context = await getCurrentContext(agentId);
await saveContext(agentId, context);
await stopExecution(agentId);
return { agentId, status: 'paused' };
}
// 恢复Agent
async resume(agentId) {
const context = await loadContext(agentId);
await restoreExecution(agentId, context);
return { agentId, status: 'running' };
}
}
stars
优势与应用
integration_instructions
易于集成
简单API使Agent能够轻松集成到现有系统和工作流中
update
状态管理
暂停/恢复功能使长时间运行任务的状态管理更加简单
settings
可配置性
通过API参数轻松配置Agent行为,适应不同场景
monitor_heart
监控与调试
通过API获取Agent状态,便于监控和调试
简单、一致的API接口是Agent在生产环境中可靠运行的关键,它们使开发者能够轻松控制Agent生命周期,并将其无缝集成到更广泛的系统中
通过提供简单直观的API接口,我们可以使Agent更加易于使用和集成。这种设计理念使开发者能够专注于业务逻辑,而不是复杂的Agent控制机制。
文本版 · 供搜索与朗读
原则7:通过工具调用联系人工
原则7:通过工具调用联系人工
LLM通过工具调用明确与人工交互,而不是直接输出自然语言请求
contact_support
核心概念
LLM应该使用工具调用来明确与人类交互,而不是直接输出自然语言请求。这种方法使交互更加结构化和可预测,便于将人类反馈集成到代理的工作流中。
通过工具调用明确请求人类输入或批准,比依赖自然语言解析人类响应更加健壮和可靠
compare
交互方式对比
text_format自然语言请求
LLM直接输出自然语言请求人类帮助,这种方式难以解析和处理,可能导致误解或不一致的响应。
// LLM直接输出自然语言请求
"我需要您的帮助才能继续。请告诉我是否应该部署到生产环境?"
build工具调用请求
LLM使用结构化工具调用请求人类输入,这种方式更加明确,便于系统处理和跟踪。
// LLM使用工具调用请求人类输入
{
"function": {
"name": "request_human_approval",
"parameters": {
"action": "deploy_to_production",
"details": "部署后端v1.2.3到生产环境"
}
}
}
sync_alt
交互流程
psychology
LLM识别需要人类干预
arrow_forward
build
生成结构化工具调用
arrow_forward
person
人类接收并响应请求
arrow_forward
play_arrow
LLM继续执行任务
// 工具调用示例 - 请求部署批准
{
"function": {
"name": "request_approval",
"parameters": {
"action": "deploy_backend",
"version": "v1.2.3",
"environment": "production",
"reason": "修复了关键安全漏洞"
}
}
}
// 人类响应后的处理
if (humanResponse.approved) {
// 继续执行部署
deployBackend(version, environment);
} else {
// 处理拒绝情况
handleRejection(humanResponse.reason);
}
stars
优势
architecture
结构化交互
明确的工具调用使交互更加结构化和可预测
integration_instructions
易于集成
便于将人类反馈集成到代理的工作流中
security
健壮性
比依赖自然语言解析人类响应更加健壮
fact_check
可审计性
便于跟踪和审计与代理的人类交互
通过工具调用明确与人类交互,可以使Agent系统更加可靠和可预测。这种方法不仅提高了交互质量,还使人类反馈能够更有效地集成到AI工作流中。
文本版 · 供搜索与朗读
原则8:掌控你的控制流
原则8:掌控你的控制流
开发者应完全控制Agent的控制流,而不是依赖框架的预设流程
account_tree
核心概念
开发者应该完全控制Agent的控制流,而不是依赖框架的预设流程。这种方法为开发者提供了更多的灵活性和对Agent行为的控制,使为特定用例定制代理变得更加容易。
通过拥有控制流,开发者可以实现自定义逻辑和决策过程,使Agent更加适应特定需求
compare
控制流对比
settings_applications框架控制流
框架提供预设的控制流程,开发者只需配置参数和规则,难以实现自定义逻辑。
1
初始化Agent
2
框架处理输入
3
执行预设流程
4
返回结果
code自定义控制流
开发者完全控制控制流,可以实现自定义逻辑和决策过程,更加灵活和适应性强。
1
接收输入
2
自定义预处理
3
条件判断与分支
4
自定义后处理
code
实现示例
settings框架方式
// 使用框架的预设控制流
from some_framework import Agent
// 创建Agent,配置预设参数
agent = Agent(
name="deployment_agent",
goal="Deploy applications to production",
tools=[deploy_tool, check_status_tool]
)
// 运行Agent,框架控制整个流程
result = agent.run("Deploy backend to production")
框架控制整个流程,开发者难以干预或自定义特定步骤。
developer_mode自定义方式
// 自定义控制流
async function deployWithCustomFlow(request) {
// 自定义预处理
const context = await preprocessRequest(request);
// 自定义条件判断
if (context.environment === "production") {
// 生产环境特殊处理
const approval = await requestApproval(context);
if (!approval.approved) {
return handleRejection(approval.reason);
}
}
// 自定义部署逻辑
const result = await customDeployLogic(context);
// 自定义后处理
return await postProcessResult(result);
}
开发者完全控制控制流,可以实现自定义逻辑和决策过程。
stars
优势
tune
完全控制
开发者可以完全控制Agent的行为和决策过程
settings_suggest
自定义逻辑
可以实现特定业务逻辑和决策规则
autorenew
灵活性
根据不同场景和需求灵活调整控制流
bug_report
易于调试
自定义控制流使调试和优化更加容易
掌控你的控制流使开发者能够构建更加灵活和适应性强的Agent系统。通过自定义控制流,开发者可以实现特定业务逻辑,使Agent更好地满足实际需求。
文本版 · 供搜索与朗读
原则9:将错误压缩到上下文窗口
原则9:将错误压缩到上下文窗口
将错误信息压缩并整合到上下文窗口中,以便LLM能够理解和处理错误
compress
核心概念
将错误信息压缩并整合到上下文窗口中,以便LLM能够理解和处理错误。这种方法使LLM能够更有效地理解和处理错误,防止上下文窗口变得过大,并使LLM更容易从错误中恢复。
关键是在不占用过多上下文空间的情况下,提供足够的错误信息,使LLM能够理解出了什么问题以及如何恢复
compare
错误处理对比
error_outline完整错误详情
将完整错误堆栈和详细信息包含在上下文中,占用大量空间,可能导致上下文窗口溢出。
<deploy_backend_result>
error: ConnectionError: Failed to connect to deployment service at https://api.deploy.example.com/v1/deploy
at Object.deploy (/app/src/deploy.js:45:13)
at processTicksAndRejections (node:internal/process/task_queues.js:95:5)
at async DeploymentService.execute (/app/src/services/deployment.js:23:19)
at async Agent.handleToolCall (/app/src/agent.js:67:8)
Request details: {
method: 'POST',
url: 'https://api.deploy.example.com/v1/deploy',
headers: {
'Content-Type': 'application/json',
'Authorization': 'Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9...'
},
body: '{"tag":"v1.2.3","environment":"production"}'
}
Response status: 503 Service Unavailable
Response headers: {
'content-type': 'application/json',
'retry-after': '30'
}
Response body: '{"error":"Service temporarily unavailable","retry_after":30}'
</deploy_backend_result>
data_object压缩错误信息
将错误信息压缩为关键要点,保留足够信息供LLM理解和恢复,同时节省上下文空间。
<deploy_backend_result>
error: ConnectionError
message: Failed to connect to deployment service
service: https://api.deploy.example.com/v1/deploy
status: 503 Service Unavailable
retry_after: 30 seconds
attempted_action: deploy backend v1.2.3 to production
</deploy_backend_result>
code
实现示例
// 错误压缩函数
function compressError(error, action) {
return {
type: "error",
error_type: error.name,
message: error.message,
service: error.config?.url || "unknown",
status: error.response?.status,
retry_after: error.response?.headers?.['retry-after'],
attempted_action: action
};
}
// 在Agent中使用压缩错误
async function handleToolCall(toolCall) {
try {
const result = await executeTool(toolCall);
return { type: "success", data: result };
} catch (error) {
// 压缩错误信息
const compressedError = compressError(error, toolCall);
return compressedError;
}
}
// 将压缩错误添加到上下文
async function addToContext(context, event) {
if (event.type === "error") {
// 对于已解决的错误,可以选择隐藏或简化
if (event.resolved) {
context.push({
type: "error_resolved",
original_error: event.error_type,
resolution: event.resolution
});
} else {
context.push(event);
}
} else {
context.push(event);
}
}
stars
优势
memory
上下文效率
压缩错误信息节省上下文空间,防止窗口溢出
healing
错误恢复
LLM更容易理解错误并采取恢复措施
auto_fix_high
智能处理
可以隐藏已解决的错误,避免重复处理
track_changes
错误跟踪
便于跟踪和管理Agent工作流中的错误
通过将错误信息压缩到上下文窗口,我们可以构建更加高效和可靠的Agent系统。这种方法不仅节省了宝贵的上下文空间,还使LLM能够更好地理解和处理错误情况。
文本版 · 供搜索与朗读
原则10:小而专注的Agent
原则10:小而专注的Agent
创建小而专注的Agent,专注于做好一件事,而不是试图做所有事情的大型通用Agent
center_focus_strong
核心概念
创建小而专注的Agent,每个Agent专注于做好一件事,而不是构建试图做所有事情的大型通用Agent。这种方法使代理更加可靠且易于维护,也使测试和调试代理变得更加容易。
通过专注于特定任务,Agent可以针对该任务进行优化,比构建大型、单体Agent更具可扩展性
compare
Agent设计对比
all_inclusive单体通用Agent
一个大型Agent试图处理所有任务,包含复杂的逻辑和决策过程,难以维护和优化。
// 单体通用Agent示例
class MonolithicAgent {
async handleRequest(request) {
// 处理各种类型的请求
if (request.type === "deploy") {
// 复杂的部署逻辑
return await this.handleDeploy(request);
} else if (request.type === "monitor") {
// 复杂的监控逻辑
return await this.handleMonitor(request);
} else if (request.type === "report") {
// 复杂的报告逻辑
return await this.handleReport(request);
}
// ... 更多复杂的处理逻辑
}
}
widgets小而专注的Agent
多个小型Agent,每个专注于特定任务,逻辑简单清晰,易于维护和优化。
// 专注的部署Agent
class DeploymentAgent {
async handleDeploy(request) {
// 专注于部署逻辑
const deployment = await this.prepareDeployment(request);
return await this.executeDeployment(deployment);
}
}
// 专注的监控Agent
class MonitoringAgent {
async handleMonitor(request) {
// 专注于监控逻辑
const metrics = await this.collectMetrics(request);
return await this.analyzeMetrics(metrics);
}
}
view_module
专注Agent示例
rocket_launch
部署Agent
专注于应用程序部署流程,处理版本控制、环境配置和部署验证
add
monitor_heart
监控Agent
专注于系统监控和性能分析,收集指标并生成警报
add
assessment
报告Agent
专注于数据分析和报告生成,创建可视化图表和摘要
// 组合专注Agent的工作流
async function deployAndMonitor(appConfig) {
// 1. 使用部署Agent
const deployAgent = new DeploymentAgent();
const deploymentResult = await deployAgent.handleDeploy(appConfig);
// 2. 使用监控Agent
const monitorAgent = new MonitoringAgent();
const monitoringResult = await monitorAgent.handleMonitor({
appId: deploymentResult.appId,
duration: "1h"
});
// 3. 使用报告Agent
const reportAgent = new ReportAgent();
const reportResult = await reportAgent.generateReport({
deployment: deploymentResult,
monitoring: monitoringResult
});
return reportResult;
}
stars
优势
verified
可靠性
专注的Agent更可靠,错误隔离,不会影响整个系统
build
易于维护
小型Agent代码简单,逻辑清晰,易于理解和修改
speed
性能优化
可以针对特定任务进行优化,提高执行效率
extension
可扩展性
易于添加新功能,只需创建新的专注Agent
bug_report
易于调试
问题定位更容易,每个Agent职责明确
repeat
可重用性
专注的Agent可以在不同工作流中重复使用
通过创建小而专注的Agent,我们可以构建更加可靠、可维护和可扩展的系统。每个Agent专注于做好一件事,然后通过组合这些专注的Agent来构建复杂的工作流。
文本版 · 供搜索与朗读
原则11:从任何地方触发,在用户所在的地方与他们互动
原则11:从任何地方触发,在用户所在的地方与他们互动
使用户能够从任何渠道触发Agent,并通过相同渠道获得响应
核心概念
使用户能够从Slack、电子邮件、短信或任何其他他们想要的渠道触发Agent,并使Agent能够通过相同的渠道进行响应。这种方法打破了传统应用的界限,让AI能够无缝融入用户现有的工作流程和沟通渠道。
让Agent能够从任何地方被触发,并在用户所在的地方与他们互动,创造更自然、更高效的人机协作体验
多渠道集成
Agent应该能够无缝集成到用户现有的沟通渠道和工作流中,而不是要求用户适应新的界面或工具。
Slack
电子邮件
短信
日历
交互流程
用户触发
Agent处理
原渠道响应
无论用户从哪个渠道发起请求,Agent都应该能够理解并处理,然后通过相同的渠道返回响应,创造无缝的交互体验。
核心优势
在用户所在的地方与他们互动
这有助于构建感觉像真实人类或至少是数字同事的AI应用程序。用户无需学习新工具或改变工作流程,可以在他们熟悉的渠道中与AI交互。
外循环Agent
使Agent能够被非人类触发(例如事件、定时任务、故障等),它们可能工作5、20或90分钟,但当到达关键点时,可以联系人类寻求帮助、反馈或批准。
高风险工具
如果您能够快速引入各种人类,您可以让Agent访问更高风险的操作,如发送外部电子邮件、更新生产数据等。保持清晰的标准使您能够审计并对执行更大更好事情的Agent充满信心。
通过使Agent能够从任何地方触发并在用户所在的地方互动,我们可以创造更加自然、高效的人机协作体验,打破传统应用的界限,让AI无缝融入现有工作流程。
文本版 · 供搜索与朗读
原则12:使你的Agent成为一个无状态的reducer
Just for fun!
原则12:使你的Agent成为一个无状态的reducer
Agent应设计为无状态的reducer,接收输入状态并返回新状态
核心概念
Agent应设计为无状态的reducer,接收输入状态并返回新状态。这种方法使Agent更加可预测、可测试和可扩展,同时简化了状态管理和恢复。
将Agent设计为纯函数,给定相同的输入总是产生相同的输出,不依赖于外部状态或副作用
Reducer模式
输入状态
Agent处理
输出状态
// Agent作为reducer的示例
function agentReducer(currentState, action) {
// 根据当前状态和动作计算新状态
switch (action.type) {
case 'DEPLOY_REQUEST':
return {
...currentState,
status: 'deploying',
deployment: action.payload
};
case 'DEPLOY_SUCCESS':
return {
...currentState,
status: 'deployed',
lastDeployment: action.payload
};
case 'DEPLOY_ERROR':
return {
...currentState,
status: 'error',
error: action.payload
};
default:
return currentState;
}
}
状态管理
在无状态reducer模式中,Agent不维护内部状态,而是接收完整的状态作为输入,并返回新的状态。状态由外部系统管理,使Agent更加可预测和可测试。
状态转换示例
// 初始状态
{
status: 'idle',
deployments: [],
error: null
}
// 部署请求后
{
status: 'deploying',
deployments: [],
error: null,
currentDeployment: { app: 'myapp', version: 'v1.2.3' }
}
// 部署成功后
{
status: 'deployed',
deployments: [{ app: 'myapp', version: 'v1.2.3', time: '2023-06-15T10:30:00Z' }],
error: null
}
优势
易于测试
纯函数易于单元测试,可以轻松模拟各种输入和状态
可重现性
给定相同的输入总是产生相同的输出,便于调试和问题重现
可扩展性
无状态设计使Agent更容易水平扩展和并行处理
时间旅行调试
可以轻松回放状态序列,便于调试和分析Agent行为
暂停/恢复
通过保存和恢复状态,可以轻松暂停和恢复Agent执行
容错性
状态与处理逻辑分离,使系统更加健壮和容错
通过将Agent设计为无状态的reducer,我们可以构建更加可预测、可测试和可扩展的系统。这种模式简化了状态管理,提高了系统的可靠性和可维护性。
这是测试回复第1条,用于测试懒加载功能。
这是测试回复第2条,懒加载应该会触发。
这是测试回复第3条,看看HTMX的revealed触发器是否正常工作。
这是测试回复第4条,测试分页加载功能。
这是测试回复第5条,验证懒加载条件判断。
这是测试回复第6条,检查回复列表的渲染。
这是测试回复第7条,测试HTMX的hx-swap功能。
这是测试回复第8条,验证hx-target是否正确指向回复列表。
这是测试回复第9条,测试懒加载指示器是否显示。
这是测试回复第10条,最后一条测试回复。