面试11-Agent如何自动接任务
一、整体场景与核心目标解释
首先,我们先明确这段代码要解决的核心问题:
在第9-10季的代码中,"队友代理(teammate agent)"只能在负责人(lead)明确分配任务时才工作,10个未认领任务需要负责人手动分配,无法规模化。而第11季的核心目标是实现自治代理:让队友代理自己扫描任务板、认领无人处理的任务、完成任务,无需负责人逐个分配,同时通过"工作-闲置"循环和身份重注入保证代理的自主性和身份不丢失。
二、核心流程拆解与代码逐段解释
整个自治代理的核心逻辑是:生成代理 → 工作阶段 → 闲置阶段(轮询任务/消息)→ 要么回到工作阶段,要么超时关机。下面按流程拆解每段代码。
流程1:代理核心循环(_loop函数)—— 工作+闲置的主逻辑
def _loop(self, name, role, prompt):
while True:
# -- WORK PHASE --
messages = [{"role": "user", "content": prompt}]
for _ in range(50):
response = client.messages.create(...) # 调用大模型(LLM)
if response.stop_reason != "tool_use":
break # 大模型停止调用工具,退出工作阶段
# execute tools... # 执行大模型调用的工具(比如处理任务)
if idle_requested: # 如果收到"闲置"请求
break # 主动退出工作阶段
# -- IDLE PHASE --
self._set_status(name, "idle") # 将代理状态设为"闲置"
resume = self._idle_poll(name, messages) # 进入闲置轮询
if not resume: # 轮询后无需恢复工作(超时)
self._set_status(name, "shutdown") # 设为"关机"
return # 退出循环,代理停止
self._set_status(name, "working") # 轮询到任务/消息,恢复工作状态
代码解释(小白友好版):
def _loop(...):定义代理的核心循环函数,name是代理名字(比如alice),role是代理角色(比如程序员),prompt是初始任务提示。while True:无限循环,直到代理超时关机才会退出。- 工作阶段(WORK PHASE):
messages = [...]:初始化大模型的输入消息,把初始任务提示传给大模型。for _ in range(50):最多让大模型执行50轮工具调用(防止无限工作)。client.messages.create(...):调用大模型(比如Claude/OpenAI),让代理处理任务。stop_reason != "tool_use":专业词汇解释 →stop_reason是大模型的停止原因,tool_use表示大模型调用了工具(比如执行"修复认证错误"任务);如果停止原因不是"调用工具",说明任务处理完了,退出工作阶段。idle_requested:如果代理收到"闲置"指令(比如任务暂时做不了),主动退出工作阶段。
- 闲置阶段(IDLE PHASE):
_set_status(name, "idle"):更新代理状态为"闲置",方便监控(比如在/team目录能看到)。_idle_poll(...):调用闲置轮询函数(后面会讲),检查有没有新任务/消息。- 如果
resume是False(轮询超时),就把代理设为"关机"并退出循环;如果是True(有新任务/消息),就把状态切回"工作",重新进入工作阶段。
本段总结:
_loop函数是代理的"心脏",核心逻辑是无限循环的工作-闲置交替:先让代理调用大模型处理任务(工作阶段),任务停了就进入闲置阶段;闲置时检查有没有新任务/消息,有就继续工作,没有就超时关机。
流程2:闲置轮询(_idle_poll函数)—— 代理主动找活干的核心
# 定义常量(小白解释:固定不变的数值,方便修改)
IDLE_TIMEOUT = 60 # 闲置超时时间:60秒
POLL_INTERVAL = 5 # 轮询间隔:5秒
def _idle_poll(self, name, messages):
# 循环次数 = 总超时时间 / 轮询间隔 → 60/5=12次
for _ in range(IDLE_TIMEOUT // POLL_INTERVAL):
time.sleep(POLL_INTERVAL) # 等待5秒(避免频繁检查,节省资源)
# 第一步:检查收件箱有没有新消息
inbox = BUS.read_inbox(name) # 读取当前代理的收件箱
if inbox: # 如果有新消息
# 把消息加到大模型的输入里,告诉代理有新消息
messages.append({"role": "user",
"content": f"<inbox>{inbox}</inbox>"})
return True # 有消息,需要恢复工作
# 第二步:扫描任务板找未认领任务
unclaimed = scan_unclaimed_tasks() # 调用扫描函数(后面讲)
if unclaimed: # 如果有未认领任务
claim_task(unclaimed[0]["id"], name) # 认领第一个未认领任务
# 把认领的任务加到大模型输入里,告诉代理要处理这个任务
messages.append({"role": "user",
"content": f"<auto-claimed>Task #{unclaimed[0]['id']}: "
f"{unclaimed[0]['subject']}</auto-claimed>"})
return True # 有任务,需要恢复工作
return False # 12次轮询(60秒)都没消息/任务,返回False(超时)
代码解释(小白友好版):
IDLE_TIMEOUT/POLL_INTERVAL:定义闲置的核心参数,60秒内每5秒检查一次,总共检查12次。time.sleep(POLL_INTERVAL):专业词汇解释 →sleep是让程序暂停执行指定秒数,这里暂停5秒,避免代理每秒都检查任务板(浪费电脑资源)。- 检查收件箱:
BUS.read_inbox(name):BUS是代理之间的消息总线(可以理解为"公司的内部邮箱系统"),读取当前代理(比如alice)的收件箱。- 如果有消息(比如负责人发了新指令),就把消息加到大模型的输入里,返回True(告诉核心循环"要继续工作")。
- 扫描并认领任务:
scan_unclaimed_tasks():调用扫描函数找无人认领的任务。claim_task(...):专业词汇解释 →claim是"认领",这个函数把任务的"所有者"设为当前代理(比如把"修复认证错误"的所有者设为alice)。- 把认领的任务信息加到大模型输入里,返回True(继续工作)。
- 循环结束后如果没找到消息/任务,返回False(核心循环会让代理关机)。
本段总结:
_idle_poll是代理"主动找活干"的逻辑:闲置时每5秒检查一次,先看有没有人发消息,再看任务板有没有没人做的任务;只要找到一个,就认领并恢复工作;60秒都没找到,就关机。
流程3:扫描未认领任务(scan_unclaimed_tasks函数)—— 找没人做的任务
TASKS_DIR = Path("./tasks") # 任务板目录:代码里的"任务板"就是这个文件夹
def scan_unclaimed_tasks() -> list:
unclaimed = [] # 初始化空列表,存未认领任务
# 遍历tasks目录下所有task_*.json文件(每个文件是一个任务)
for f in sorted(TASKS_DIR.glob("task_*.json")):
task = json.loads(f.read_text()) # 读取并解析任务文件(JSON格式)
# 检查任务是否满足"未认领"条件:
# 1. status是pending(待处理);2. 没有owner(所有者);3. 没有blockedBy(被其他任务阻塞)
if (task.get("status") == "pending"
and not task.get("owner")
and not task.get("blockedBy")):
unclaimed.append(task) # 满足条件,加入未认领列表
return unclaimed # 返回所有未认领任务
代码解释(小白友好版):
TASKS_DIR = Path("./tasks"):专业词汇解释 →Path是Python处理文件路径的工具,这里指定任务文件都存在./tasks文件夹里(就是你说的"扫描板/任务板")。def scan_unclaimed_tasks() -> list:函数返回值类型是列表(list),里面存的是未认领任务。TASKS_DIR.glob("task_*.json"):专业词汇解释 →glob是"通配符匹配",这里匹配所有以task_开头、.json结尾的文件(比如task_1.json、task_2.json),每个文件对应一个任务。json.loads(f.read_text()):专业词汇解释 →JSON是一种通用的任务数据格式(比如{“task”:“修复认证错误”,“status”:“pending”,“owner”:“”}),loads是把文件里的文字转换成Python能识别的字典格式。- 任务未认领的三个条件:
status == "pending":任务状态是"待处理"(不是"完成"或"阻塞");not task.get("owner"):没有所有者(比如没人填"alice");not task.get("blockedBy"):没有被其他任务阻塞(比如"写测试"不会因为"修复认证错误没做"而被阻塞)。
- 满足条件的任务会被加入
unclaimed列表,最后返回这个列表。
本段总结:
scan_unclaimed_tasks函数是代理的"任务扫描器",核心是遍历./tasks文件夹里的任务文件,筛选出"待处理、无所有者、无阻塞"的任务,给代理提供可认领的任务列表。
流程4:身份重注入 —— 防止代理"忘了自己是谁"
team_name = "dev_team" # 代理所属团队名
if len(messages) <= 3:
# 插入身份块到消息列表的第一个位置
messages.insert(0, {"role": "user",
"content": f"<identity>You are '{name}', role: {role}, "
f"team: {team_name}. Continue your work.</identity>"})
# 插入代理的确认回复,强化身份
messages.insert(1, {"role": "assistant",
"content": f"I am {name}. Continuing."})
代码解释(小白友好版):
- 背景:第6季的"上下文压缩"会删减历史消息(比如把100条消息压缩成3条),可能导致代理忘记自己的名字、角色(比如"我是alice,程序员")。
len(messages) <= 3:检查消息列表的长度,如果≤3(说明压缩过了),就重新注入身份。messages.insert(0, ...):专业词汇解释 →insert(0, ...)是在列表的第一个位置插入内容,保证大模型优先看到身份信息。<identity>...</identity>:身份块,明确告诉代理"你是谁、你的角色、所属团队",防止失忆。- 第二个
insert是让代理自己确认身份(比如"我是alice,继续工作"),进一步强化身份记忆。
本段总结:
身份重注入是解决代理"失忆"的关键:当上下文消息太少(压缩过),就主动把代理的身份信息加到消息最前面,保证代理始终知道自己的角色和任务。
流程5:运行代码的实操指令
cd learn-claude-code # 进入代码目录
python agents/s11_autonomous_agents.py # 运行第11季的自治代理代码
代码解释(小白友好版):
cd learn-claude-code:专业词汇解释 →cd是终端命令,意思是"切换目录",进入存放代码的文件夹。python agents/s11_autonomous_agents.py:运行指定路径下的Python文件,启动自治代理程序。- 后续操作说明:
- 创建3个任务到任务板(往
./tasks文件夹里加task_*.json文件); - 启动alice和bob两个代理,它们会自动扫描并认领任务;
- 启动程序员代理,它会自己找任务板里的活干;
- 创建有依赖的任务(比如"写测试"依赖"修复认证错误"),代理会遵守依赖顺序(先做前者,再做后者)。
- 创建3个任务到任务板(往
- 查看任务板:直接打开
./tasks文件夹,看任务文件里的"owner"字段(知道谁认领了任务); - 监控代理状态:查看
./team文件夹,知道谁在工作、谁在闲置。
本段总结:
这部分是实操指令,核心是通过终端命令启动代码,然后创建任务、观察代理的自治行为,同时通过指定文件夹监控任务和代理状态。
三、核心变化对比(第10季 vs 第11季)
| 组成部分 | 第10季 | 第11季 |
|---|---|---|
| 工具数量 | 12个 | 14个(新增"闲置"和"认领任务"工具) |
| 自治权 | 负责人分配任务 | 代理自组织(自己找任务) |
| 空闲阶段 | 无 | 轮询收件箱+任务板 |
| 任务认领 | 仅手动分配 | 自动认领未认领任务 |
| 身份 | 仅初始系统提示 | 压缩后重注入身份 |
| 暂停/关机 | 无 | 60秒闲置后自动关机 |
四、示例
好,我直接给你一套完整、可跑通、从头到尾的真实示例,用最简单的 Python 代码 + 任务文件 + 运行流程,完全模拟第11季自治代理。
你是小白也能一步看懂:代理怎么启动 → 怎么闲置 → 怎么扫任务板 → 怎么自动认领 → 怎么工作。
完整自治代理示例(可直接理解)
1. 先搭建环境(模拟任务板)
我们在当前目录新建一个文件夹 tasks/,里面放 3 个任务文件:
task_1.json
{
"id": 1,
"subject": "修复认证错误",
"status": "pending",
"owner": null,
"blockedBy": null
}
task_2.json
{
"id": 2,
"subject": "添加速率限制器",
"status": "pending",
"owner": null,
"blockedBy": null
}
task_3.json
{
"id": 3,
"subject": "写测试用例",
"status": "pending",
"owner": null,
"blockedBy": 1
}
注意:task_3 被 task_1 阻塞,代理不会抢这个。
2. 完整可理解代码(简化版 s11 自治代理)
我把原逻辑精简、可运行、每一步都打印日志,你能清晰看到流程。
import json
import time
from pathlib import Path
# ====================== 配置 ======================
TASKS_DIR = Path("./tasks")
IDLE_TIMEOUT = 60 # 最大闲置60秒
POLL_INTERVAL = 5 # 每5秒扫一次任务板
team_name = "AI-Team"
# ====================== 工具1:扫描未认领任务 ======================
def scan_unclaimed_tasks():
unclaimed = []
for f in sorted(TASKS_DIR.glob("task_*.json")):
task = json.loads(f.read_text("utf-8"))
# 条件:待处理 + 没人认领 + 不被阻塞
if (task.get("status") == "pending"
and not task.get("owner")
and not task.get("blockedBy")):
unclaimed.append(task)
return unclaimed
# ====================== 工具2:认领任务 ======================
def claim_task(task_id, agent_name):
f = TASKS_DIR / f"task_{task_id}.json"
task = json.loads(f.read_text("utf-8"))
task["owner"] = agent_name
f.write_text(json.dumps(task, indent=2), "utf-8")
print(f"【系统】{agent_name} 已认领任务 {task_id}: {task['subject']}")
# ====================== 闲置轮询(核心:主动找活干) ======================
def idle_poll(agent_name, messages):
print(f"\n【{agent_name}】进入闲置,每5秒扫描任务板……")
# 60秒 / 5秒 = 12轮
for i in range(IDLE_TIMEOUT // POLL_INTERVAL):
print(f" 第{i+1}轮扫描...")
time.sleep(POLL_INTERVAL)
# 1. 先看有没有消息(这里简化,模拟无消息)
# 2. 扫描任务板
unclaimed = scan_unclaimed_tasks()
if unclaimed:
# 认领第一个可做任务
task = unclaimed[0]
claim_task(task["id"], agent_name)
# 把任务加入上下文
messages.append({
"role": "user",
"content": f"<auto-claimed>任务 #{task['id']}: {task['subject']}"
})
return True # 回去工作
# 60秒超时
print(f"【{agent_name}】60秒无任务,自动关机")
return False
# ====================== 身份重注入(防止失忆) ======================
def inject_identity(name, role, messages):
if len(messages) <= 3:
print(f"【系统】上下文太短,重新注入 {name} 的身份")
messages.insert(0, {
"role": "user",
"content": f"<identity>你是 {name},角色:{role},团队:{team_name},继续工作"
})
messages.insert(1, {
"role": "assistant",
"content": f"我是 {name},继续执行任务"
})
# ====================== 代理主循环 ======================
def agent_loop(name, role, prompt):
print(f"===== 启动自治代理:{name}({role})=====")
messages = [{"role": "user", "content": prompt}]
while True:
# ========== 工作阶段 ==========
print(f"\n【{name}】进入工作阶段")
inject_identity(name, role, messages) # 防止失忆
# 模拟 LLM 工作:最多5轮工具调用
work_step = 0
for _ in range(5):
work_step += 1
print(f" 工作步骤 {work_step}:处理任务中...")
# 假设工作完成,停止调用工具
break
# ========== 进入闲置阶段 ==========
print(f"【{name}】工作暂停,切换到闲置")
resume = idle_poll(name, messages)
if not resume:
print(f"\n===== {name} 已关闭 =====")
return
# ====================== 启动两个代理:Alice & Bob ======================
if __name__ == "__main__":
# 启动代理Alice(程序员)
agent_loop(
name="Alice",
role="后端开发",
prompt="你是自治AI代理,会自动从任务板认领并完成任务"
)
# 启动代理Bob(测试)
# agent_loop(
# name="Bob",
# role="测试工程师",
# prompt="你是自治AI代理,自动认领任务"
# )
3. 运行一遍,你会看到完整真实流程
直接运行代码,你会看到如下输出(我给你翻译成人话流程):
第一步:启动代理
===== 启动自治代理:Alice(后端开发)=====
第二步:进入工作阶段
【Alice】进入工作阶段
【系统】上下文太短,重新注入 Alice 的身份
工作步骤 1:处理任务中...
因为消息只有1条,触发身份重注入,防止代理失忆。
第三步:工作结束 → 进入闲置
【Alice】工作暂停,切换到闲置
【Alice】进入闲置,每5秒扫描任务板……
第1轮扫描...
第四步:5秒后扫描任务板 → 发现无人认领任务
【系统】Alice 已认领任务 1: 修复认证错误
第五步:返回 True → 回到工作阶段
【Alice】回到工作阶段
工作步骤 1:处理任务中...
第六步:再次闲置 → 再次扫描 → 认领 task_2
【Alice】进入闲置,每5秒扫描任务板……
第1轮扫描...
【系统】Alice 已认领任务 2: 添加速率限制器
第七步:task_3 被阻塞 → 不会认领
扫描到 task_3,但 blockedBy=1 → 跳过
第八步:所有任务做完 → 60秒后超时关机
【Alice】60秒无任务,自动关机
===== Alice 已关闭 =====
4. 任务板最终状态(你打开文件能看到)
task_1.json
"owner": "Alice"
task_2.json
"owner": "Alice"
task_3.json
"owner": null,
"blockedBy": 1
5. 这个例子完整对应第11季所有核心逻辑
- 代理自主循环:工作 ↔ 闲置
- 主动扫描任务板:不需要负责人分配
- 自动认领任务:谁先扫到谁领
- 遵守依赖阻塞:不领被 blocked 的任务
- 身份重注入:防止上下文压缩后失忆
- 超时自动关机:60秒没活就停
6. 如果你想看“两个代理抢任务”
只需要把代码里的 Bob 也启动:
if __name__ == "__main__":
agent_loop("Alice", "后端开发", "...")
agent_loop("Bob", "测试工程师", "...")
运行后你会看到:
- Alice 领 task_1
- Bob 领 task_2
- 完全自治,不需要你插手
如果你需要,我可以再给你做一个**带 FSM 状态机(怠速→投票→主张→工作)**的可视化流程图版本,或者把代码扩展成 14 个工具完整版。
五、总结(核心逻辑回顾)
- 核心循环:代理通过"工作-闲置"无限循环实现自治,工作阶段处理任务,闲置阶段主动找任务/消息,60秒没活干就关机。
- 任务认领:代理通过扫描
./tasks文件夹的JSON任务文件,自动筛选并认领"待处理、无所有者、无阻塞"的任务,无需人工分配。 - 身份保障:当上下文消息被压缩(≤3条),会主动注入身份信息,防止代理忘记自己的角色和所属团队。
整个代码的核心是让代理从"被动等分配"变成"主动找活干",实现真正的自治,同时通过超时、身份重注入保证代理的稳定性和正确性。
AtomGit 是由开放原子开源基金会联合 CSDN 等生态伙伴共同推出的新一代开源与人工智能协作平台。平台坚持“开放、中立、公益”的理念,把代码托管、模型共享、数据集托管、智能体开发体验和算力服务整合在一起,为开发者提供从开发、训练到部署的一站式体验。
更多推荐



所有评论(0)