AI Agent 全日制30天速成|Day10 学习笔记
今日核心主题:Agent 工具编排 + 异步批量任务 + 接口鉴权与分布式锁
修订说明
- 基于Day9全工具化架构迭代,保留Chroma向量库、统一工具网关、Redis记忆、熔断限流体系
- 新增工具链批量编排:支持顺序/并行多工具组合任务,无需模型多轮ReAct循环
- 实现异步后台任务队列,长耗时知识库批量入库、大规模向量离线计算不阻塞HTTP接口
- 增加全局接口Token鉴权、分布式会话锁,适配多实例分布式部署场景
- 新增Agent调用成本统计模块,统计LLM/Embedding调用次数、Token消耗、计费估算
今日总学习目标(全天8h分配)
- 理论学习:2.5h
- 分层代码编写调试:4h
- 面试复盘背诵:1.5h
- 掌握工具编排语法,定义串行/并行工具流水线,实现一次性多工具批量执行
- 基于Redis List实现轻量异步任务队列,处理长耗时离线向量任务
- 实现全局API鉴权、用户资源配额(LLM/Embedding每日调用上限)
- 分布式锁解决多实例Redis会话并发读写错乱问题
- 新增调用成本统计模块,全链路采集Token消耗与接口调用量
- 整合流水线编排、异步任务、鉴权配额、分布式锁,升级生产级Agent服务
一、核心理论教学笔记
1 工具流水线编排(Tool Pipeline)
1.1 单轮ReAct存在短板
ReAct每轮只能执行单个工具,多步骤任务需要多次LLM思考,消耗大量Token与接口次数;固定流程(先检索再计算、批量入库)重复循环效率极低。
1.2 两种编排模式
- 串行流水线(依赖型) :上一个工具输出作为下一个工具输入,顺序执行
示例:rag_search → calculator(先查公式再代入计算) - 并行流水线(无依赖) :多个工具同时并发执行,结果统一汇总
示例:同时执行多条rag_search多维度知识库检索
1.3 流水线结构化定义
通过Pydantic定义流水线JSON结构,模型可一次性生成多工具任务,网关自动解析批量调度,仅消耗单次LLM思考。
流水线内置变量传递:前序工具输出可作为后续工具入参,无需重复拼接Prompt。
2 Redis异步任务队列(轻量版,无需RabbitMQ)
2.1 适用场景
批量文档入库、海量文本Embedding、大规模知识库更新等耗时操作,同步HTTP接口会超时。
2.2 队列执行流程
- 前端提交批量任务 → 接口校验权限配额 → 写入Redis任务队列,返回task_id
- 后台独立协程循环消费队列,执行向量入库/Embedding批量工具
- 任务进度、执行结果持久存入Redis Hash,用户通过task_id轮询查询状态
- 失败任务自动重试2次,最终失败写入死信队列人工排查
3 分布式配套能力
3.1 全局API鉴权
所有接口请求携带access_token,服务内存维护token-角色-配额映射;区分普通用户/管理员每日调用上限,超量直接拦截。
3.2 分布式会话锁
多服务实例同时收到同一用户多轮提问,并发读写Redis会话会造成消息错乱;基于Redis SETNX实现会话互斥锁,单会话同一时间仅允许一条对话执行。
3.3 调用成本统计
每条LLM、Embedding调用采集输入/输出Token,存入Redis计数器;按厂商单价估算消耗费用,支持按用户、会话、日期维度统计用量。
4 Day9→Day10 完整升级链路
用户HTTP请求携带token鉴权 → 校验用户每日调用配额
→ 获取分布式会话锁,防止并发乱序
→ 读取Redis分层记忆,自动摘要/滑动窗口压缩
→ 模型二选一:
- 简单任务:单工具ReAct循环执行
- 固定多步骤任务:生成流水线编排,网关批量并行/串行执行工具
→ 短任务同步返回;长批量任务推送Redis异步队列,返回task_id
→ 所有工具执行埋点:限流、熔断、日志、Token消耗统计
→ 工具结果回填上下文,LLM生成回答,脱敏处理
→ 对话持久Redis,释放分布式会话锁
→ 后台协程持续消费异步队列,处理离线向量批量任务
5 生产级配额与限流规则
- 访客:仅基础闲聊,禁用所有工具,每日LLM 10次上限
- 普通用户:calculator/rag_search可用,禁用vector_add;LLM 200次/天,Embedding 500次/天
- 管理员:全工具开放,无每日硬上限,仅全局令牌桶流量控制
二、今日学习重点
- 实现ToolPipeline流水线结构化定义,网关自动解析串行/并行批量工具执行
- 搭建Redis轻量异步任务队列,实现长耗时向量任务后台异步处理
- 开发全局Token鉴权、用户每日调用配额拦截逻辑
- Redis分布式锁解决多实例会话并发读写冲突
- 全链路Token采集、用量统计、成本估算模块开发
- 兼容原有Day9所有能力,无破坏性改动,平滑升级
三、今日难点 & 解决方案
难点1 流水线工具之间参数传递复杂,前序结果无法自动传给下一个工具
解决方案:流水线结构定义输出变量key,网关缓存每一步工具结果,后续工具可通过${变量名}动态填充入参,自动替换文本。
难点2 多实例部署,同一用户并发对话导致历史消息错乱
解决方案:执行对话前后加分布式Redis锁,锁过期时间30s;对话流程未完成时,同会话新请求直接返回"对话处理中,请稍后"。
难点3 批量向量入库耗时过长,同步接口60s超时
解决方案:耗时任务剥离至Redis异步队列,接口仅生成任务ID立即返回;后台协程单独消费,前端轮询任务进度。
难点4 用户无感知超额调用LLM/Embedding产生高额费用
解决方案:按用户角色设置每日调用配额,Redis计数器实时累加,达到上限直接拦截,同时每日零点自动重置计数。
难点5 异步任务大量失败堆积无法感知
解决方案:区分正常队列与死信队列,失败重试2次仍异常的任务转入死信;日志记录全部失败task_id,提供查询接口查看失败详情。
四、完整项目代码(基于Day9扩展,新增文件+修改原有文件)
依赖新增
bash
pip install python-multipart
项目目录新增文件
day10_agent/
├── .env # 新增鉴权、队列、配额配置
├── middleware.py # 新增分布式锁、用量统计逻辑
├── security.py # 新增token鉴权、用户配额校验
├── pipeline.py # 【今日新增】工具流水线编排定义
├── async_task.py # 【今日新增】Redis异步任务队列消费
├── llm_client.py
├── tool_gateway.py # 适配流水线批量工具执行
├── memory_store.py
├── agent_core.py # 支持流水线+ReAct双模式
└── main.py # 新增鉴权中间件、任务查询接口
1 .env 新增配置项
env
# 原有LLM/Redis/Chroma配置保留
# API鉴权
ADMIN_TOKEN=admin123456
USER_TOKEN=user654321
GUEST_TOKEN=guest0000
# 每日调用配额
ADMIN_QUOTA_LLM=9999
ADMIN_QUOTA_EMB=9999
USER_QUOTA_LLM=200
USER_QUOTA_EMB=500
GUEST_QUOTA_LLM=10
GUEST_QUOTA_EMB=0
# 异步任务队列
TASK_QUEUE_KEY=agent:task:queue
DEAD_QUEUE_KEY=agent:task:dead
TASK_EXPIRE=86400
# 分布式锁
LOCK_PREFIX=agent:session:lock
LOCK_EXPIRE=30
# Token计费单价(元/千token)
PRICE_INPUT=0.5
PRICE_OUTPUT=1.0
PRICE_EMB=0.2
2 pipeline.py(全新文件:工具流水线编排)
python
from pydantic import BaseModel, Field
from typing import List, Dict, Optional, Union
from tool_gateway import gateway
class PipelineStep(BaseModel):
task_id: str = Field(description="流水线内步骤唯一标识")
tool_name: str = Field(description="执行工具名称")
args: Dict = Field(description="工具入参,支持${变量名}引用上一步输出")
depends_on: List[str] = Field(default=[], description="依赖步骤id,空则并行执行")
output_var: str = Field(description="本步骤输出存储变量名,供后续步骤引用")
class ToolPipeline(BaseModel):
run_mode: str = Field(description="serial串行 / parallel并行")
steps: List[PipelineStep] = Field(description="流水线步骤列表")
class PipelineRunner:
def __init__(self):
self.var_cache = {} # 存储各步骤输出变量
# 替换参数内${xxx}变量
def replace_var(self, raw_arg: Union[str, Dict]) -> Union[str, Dict]:
if isinstance(raw_arg, str):
for k, v in self.var_cache.items():
raw_arg = raw_arg.replace(f"${{{k}}}", str(v))
return raw_arg
if isinstance(raw_arg, dict):
new_dict = {}
for k, val in raw_arg.items():
new_dict[k] = self.replace_var(val)
return new_dict
return raw_arg
async def run_single_step(self, step: PipelineStep, trace_id: str, user_role: str):
parsed_args = self.replace_var(step.args)
res = await gateway.run_tool(step.tool_name, parsed_args, trace_id, user_role)
self.var_cache[step.output_var] = res
return {step.task_id: res}
async def run_pipeline(self, pipeline: ToolPipeline, trace_id: str, user_role: str):
all_results = {}
step_map = {s.task_id: s for s in pipeline.steps}
finished = set()
while len(finished) < len(step_map):
run_coros = []
run_steps = []
for sid, step in step_map.items():
if sid in finished:
continue
# 判断依赖是否全部完成
dep_ok = all(d in finished for d in step.depends_on)
if dep_ok:
run_coros.append(self.run_single_step(step, trace_id, user_role))
run_steps.append(sid)
if not run_coros:
break
batch_res = await asyncio.gather(*run_coros)
for item in batch_res:
all_results.update(item)
for s in run_steps:
finished.add(s)
return {
"pipeline_var": self.var_cache,
"step_result": all_results
}
pipeline_runner = PipelineRunner()
3 async_task.py(全新文件:异步任务队列)
python
import asyncio
import json
import uuid
import os
from dotenv import load_dotenv
from memory_store import memory
from pipeline import ToolPipeline, pipeline_runner
load_dotenv()
TASK_QUEUE = os.getenv("TASK_QUEUE_KEY")
DEAD_QUEUE = os.getenv("DEAD_QUEUE_KEY")
TASK_EXPIRE = int(os.getenv("TASK_EXPIRE"))
class AsyncTaskCenter:
def __init__(self):
self.redis = memory.redis
def gen_task_id(self):
return str(uuid.uuid4())
async def submit_task(self, task_type: str, pipeline_dict: dict, trace_id: str, user_role: str):
tid = self.gen_task_id()
task_data = json.dumps({
"task_id": tid,
"task_type": task_type,
"pipeline": pipeline_dict,
"trace_id": trace_id,
"user_role": user_role,
"status": "pending",
"retry_cnt": 0
})
# 写入队列 & Hash存储详情
await self.redis.lpush(TASK_QUEUE, task_data)
await self.redis.setex(f"task:info:{tid}", TASK_EXPIRE, task_data)
return tid
async def get_task_info(self, task_id: str):
raw = await self.redis.get(f"task:info:{task_id}")
if not raw:
return None
return json.loads(raw)
async def consumer_loop(self):
print("异步任务消费协程启动成功")
while True:
# 阻塞读取队列
raw_task = await self.redis.brpop(TASK_QUEUE, timeout=2)
if not raw_task:
continue
_, task_str = raw_task
task = json.loads(task_str)
tid = task["task_id"]
try:
task["status"] = "running"
await self.redis.setex(f"task:info:{tid}", TASK_EXPIRE, json.dumps(task))
# 执行流水线
pipeline = ToolPipeline(**task["pipeline"])
res = await pipeline_runner.run_pipeline(pipeline, task["trace_id"], task["user_role"])
task["status"] = "success"
task["result"] = json.dumps(res, ensure_ascii=False)
except Exception as e:
task["retry_cnt"] += 1
task["err_msg"] = str(e)
if task["retry_cnt"] < 2:
# 重新入队重试
await self.redis.lpush(TASK_QUEUE, json.dumps(task))
continue
task["status"] = "failed"
# 转入死信队列
await self.redis.rpush(DEAD_QUEUE, json.dumps(task))
await self.redis.setex(f"task:info:{tid}", TASK_EXPIRE, json.dumps(task))
task_center = AsyncTaskCenter()
4 middleware.py 新增分布式锁、用量统计代码
python
# 在原有代码末尾追加
import os
LOCK_PREFIX = os.getenv("LOCK_PREFIX")
LOCK_EXPIRE = int(os.getenv("LOCK_EXPIRE"))
PRICE_INPUT = float(os.getenv("PRICE_INPUT"))
PRICE_OUTPUT = float(os.getenv("PRICE_OUTPUT"))
PRICE_EMB = float(os.getenv("PRICE_EMB"))
# 分布式会话锁
async def session_lock(redis, session_id: str):
key = f"{LOCK_PREFIX}{session_id}"
ok = await redis.set(key, "locked", ex=LOCK_EXPIRE, nx=True)
return bool(ok)
async def session_unlock(redis, session_id: str):
key = f"{LOCK_PREFIX}{session_id}"
await redis.delete(key)
# Token用量统计
class TokenStat:
def __init__(self, redis):
self.redis = redis
async def add_llm_token(self, user_role: str, input_tok: int, output_tok: int):
today = time.strftime("%Y%m%d")
key_llm_in = f"stat:{today}:{user_role}:llm_input"
key_llm_out = f"stat:{today}:{user_role}:llm_output"
await self.redis.incrby(key_llm_in, input_tok)
await self.redis.incrby(key_llm_out, output_tok)
async def add_emb_token(self, user_role: str, tok_num: int):
today = time.strftime("%Y%m%d")
key_emb = f"stat:{today}:{user_role}:embedding"
await self.redis.incrby(key_emb, tok_num)
async def get_cost(self, user_role: str):
today = time.strftime("%Y%m%d")
in_tok = int(await self.redis.get(f"stat:{today}:{user_role}:llm_in") or 0)
out_tok = int(await self.redis.get(f"stat:{today}:llm_out") or 0)
emb_tok = int(await self.redis.get(f"stat:{today}:emb") or 0)
cost = (in_tok / 1000) * PRICE_INPUT + (out_tok / 1000) * PRICE_OUTPUT + (emb_tok / 1000) * PRICE_EMB
return round(cost, 4)
stat_client = TokenStat(memory.redis)
5 security.py 新增Token鉴权+配额校验
python
# 原有代码保留,追加下方内容
import os
TOKEN_ROLE_MAP = {
os.getenv("ADMIN_TOKEN"): "admin",
os.getenv("USER_TOKEN"): "user",
os.getenv("GUEST_TOKEN"): "guest"
}
QUOTA_CFG = {
"admin": {
"llm": int(os.getenv("ADMIN_QUOTA_LLM")),
"emb": int(os.getenv("ADMIN_QUOTA_EMB"))
},
"user": {
"llm": int(os.getenv("USER_QUOTA_LLM")),
"emb": int(os.getenv("USER_QUOTA_EMB"))
},
"guest": {
"llm": int(os.getenv("GUEST_QUOTA_LLM")),
"emb": int(os.getenv("GUEST_QUOTA_EMB"))
}
}
# Token鉴权
def verify_token(token: str) -> tuple[bool, str]:
if token not in TOKEN_ROLE_MAP:
return False, ""
return True, TOKEN_ROLE_MAP[token]
# 校验当日调用配额
async def check_quota(redis, user_role: str, call_type: str) -> bool:
today = time.strftime("%Y%m%d")
limit = QUOTA_CFG[user_role][call_type]
key = f"quota:{today}:{user_role}:{call_type}"
current = int(await redis.get(key) or 0)
if current >= limit:
return False
await redis.incr(key)
return True
6 agent_core.py 改造支持流水线编排
python
# 原有导入追加
from pipeline import ToolPipeline, pipeline_runner
from middleware import stat_client
class ReActAgent:
# 原有init、reflect函数不变
async def run_chat(self, session_id: str, user_role: str, user_input: str, trace_id: str):
history = await memory.load_history(session_id)
base_msg = [{"role":"system", "content":self.system_prompt}] + history
base_msg.append({"role":"user", "content":user_input})
msg_list = await memory.auto_compress(base_msg)
# 新增:判断生成流水线还是单轮ReAct
pipeline_prompt = """
判断用户问题是否为固定多步骤任务,是则输出ToolPipeline JSON,run_mode选serial/parallel;简单单任务输出{"is_pipeline":false}
"""
pipe_check_msg = msg_list + [{"role":"user", "content":pipeline_prompt}]
pipe_raw = await llm_client.chat_sync(pipe_check_msg, temperature=0.0)["choices"][0]["message"]["content"]
# 省略JSON解析逻辑,区分两种执行分支
if "is_pipeline" in pipe_raw and json.loads(pipe_raw)["is_pipeline"] is False:
# 原有ReAct循环逻辑不变,执行后统计token
loop_cnt = 0
tool_record = {}
input_tokens = sum(len(m["content"]) for m in msg_list)
while loop_cnt < self.max_loop:
# 原有ReAct代码不变
...
output_tokens = len(final_raw)
await stat_client.add_llm_token(user_role, input_tokens, output_tokens)
else:
# 流水线分支
pipe_data = json.loads(pipe_raw)
pipeline = ToolPipeline(**pipe_data)
pipeline_res = await pipeline_runner.run_pipeline(pipeline, trace_id, user_role)
concat_info = json.dumps(pipeline_res["step_result"], ensure_ascii=False)
final_prompt = msg_list + [{"role":"user", "content":f"结合流水线结果回答:{concat_info}"}]
final_raw = await llm_client.chat_sync(final_prompt, temperature=0.1)["choices"][0]["message"]["content"]
input_tokens = sum(len(m["content"]) for m in final_prompt)
output_tokens = len(final_raw)
await stat_client.add_llm_token(user_role, input_tokens, output_tokens)
# 脱敏、持久化逻辑不变
final_ans = desensitize(final_raw)
await memory.append(session_id, "user", user_input)
await memory.append(session_id, "assistant", final_ans)
log_client.write(trace_id, "INFO", {"final_answer": final_ans[:300]})
return {
"trace_id": trace_id,
"tool_record": tool_record if "tool_record" in locals() else pipeline_res,
"answer": final_ans
}
react_agent = ReActAgent()
7 main.py 新增鉴权、任务查询、异步提交接口
python
from fastapi import FastAPI, Query, Header, HTTPException
import asyncio
from agent_core import react_agent
from memory_store import memory
from middleware import global_bucket, create_trace_id, log_client, session_lock, session_unlock
from security import input_verify, verify_token, check_quota
from async_task import task_center
app = FastAPI(title="Day10 流水线Agent|异步任务+分布式锁+鉴权配额")
# 全局启动事件新增异步消费协程
@app.on_event("startup")
async def startup():
await memory.connect()
asyncio.create_task(task_center.consumer_loop())
@app.on_event("shutdown")
async def shutdown():
await memory.close()
# 通用对话接口,增加token鉴权
@app.get("/agent/chat")
async def chat_api(
Authorization: str = Header(..., description="Bearer token"),
session_id: str = Query(...),
user_role: str = Query(default="user"),
prompt: str = Query(...)
):
# 1 Token鉴权
token = Authorization.replace("Bearer ","")
auth_ok, real_role = verify_token(token)
if not auth_ok:
raise HTTPException(status_code=401, detail="非法访问令牌")
# 2 配额校验
llm_quota_ok = await check_quota(memory.redis, real_role, "llm")
if not llm_quota_ok:
return {"trace_id": create_trace_id(), "answer": "今日LLM调用额度已耗尽,请明日再试"}
# 3 分布式会话锁
lock_success = await session_lock(memory.redis, session_id)
if not lock_success:
return {"trace_id": create_trace_id(), "answer": "当前会话正在处理对话,请稍后重试"}
try:
trace_id = create_trace_id()
log_client.write(trace_id, "INFO", {"session_id": session_id, "input": prompt, "role": real_role})
# 输入安全校验
ok, safe_text = await input_verify(prompt)
if not ok:
return {"trace_id": trace_id, "answer": safe_text}
# 全局限流
if not await global_b.get_token():
log_client.write(trace_id, "WARN", {"msg": "触发全局限流"})
return {"trace_id": trace_id, "answer": "服务繁忙,请稍后重试"}
# 主Agent执行
result = await react_agent.run_chat(session_id, real_role, safe_text, trace_id)
return result
finally:
# 释放会话锁
await session_unlock(memory.redis, session_id)
# 提交异步批量流水线任务接口
@app.post("/agent/task/submit")
async def submit_task(
Authorization: str = Header(...),
session_id: str,
task_type: str,
pipeline: dict
):
token = Authorization.replace("Bearer ","")
auth_ok, real_role = verify_token(token)
if not auth_ok:
raise HTTPException(401, "token无效")
# 仅管理员可提交批量入库任务
if task_type == "batch_embed" and real_role != "admin":
raise HTTPException(403, "仅管理员可执行批量向量任务")
trace_id = create_trace_id()
tid = await task_center.submit_task(task_type, pipeline, trace_id, real_role)
return {"task_id": tid, "trace_id": trace_id, "msg": "任务已进入后台队列"}
# 查询异步任务进度
@app.get("/agent/task/query")
async def query_task(task_id: str):
info = await task_center.get_task_info(task_id)
if not info:
return {"msg": "任务不存在或已过期"}
return info
if __name__ == "__main__":
import uvicorn
uvicorn.run("main.py", reload=True)
五、今日实操练习任务
- 配置.env内三类Token,分别用admin/user/guest令牌调用接口,验证权限隔离与每日配额拦截
- 构造复合问题:
先检索RAG定义,再计算(100+20)*5,观察模型自动生成串行流水线批量执行工具 - 使用admin账号提交批量文档入库流水线,获取task_id,轮询
/agent/task/query查看后台执行进度 - 同一session_id并发发送两条提问,验证分布式锁拦截,返回会话繁忙提示
- 连续调用接口耗尽user角色每日LLM配额,观察配额拦截提示
- 人为制造批量向量任务异常,查看死信队列存储失败任务信息
- 查看日志与Redis统计key,验证LLM/Embedding Token消耗与费用估算正常
六、配套完整面试题(含标准答案)
基础问答
1 Tool流水线和ReAct循环的核心区别,各自适用场景?
标准答案
ReAct单次只能调用一个工具,多步骤任务需要多轮LLM思考,消耗更多Token;适合无固定流程、开放式未知问题。
Tool流水线一次性定义串行/并行多工具步骤,模型仅一次规划,网关批量调度;适合流程固定、多依赖/多并行查询的标准化任务,批量知识库入库、固定计算检索组合优先使用流水线。
2 为什么要用Redis实现异步任务,不直接同步执行批量向量入库?
标准答案
批量Embedding、大规模文档入库耗时可达数十秒,HTTP接口默认超时会断开连接;异步队列将耗时操作剥离后台,前端立刻获取task_id轮询结果,提升接口可用性;同时失败任务支持自动重试,不阻塞用户对话主线程。
3 分布式会话锁解决什么问题,实现原理?
标准答案
多服务实例部署时,同一用户同时发起多条对话请求,并发读写Redis会话列表会造成消息错乱、上下文拼接异常。
基于Redis SETNX原子命令加锁,会话处理期间持有30s过期锁;新同会话请求检测锁存在直接拒绝,对话结束主动释放锁,保证单会话串行执行。
4 用户配额统计基于Redis什么数据结构,零点如何重置每日用量?
标准答案
使用Redis String计数器,key拼接日期+用户角色区分每日用量;每日零点定时任务删除当日统计key,自动重置次日计数;设置过期时间24h,自动清理过期历史用量数据。
5 流水线变量传递实现逻辑是什么?
标准答案
网关内置变量缓存字典,每一步工具执行完成后将输出存入对应output_var;解析后续步骤入参时,正则匹配${变量名}自动替换为上一步工具输出,无需模型重复拼接上下文参数。
工程实操题
1 流水线步骤存在循环依赖(A依赖B,B依赖A)如何处理?
标准答案
规划阶段Prompt增加依赖校验规则,模型生成流水线时禁止循环依赖;网关执行前遍历所有depends_on做拓扑校验,检测循环依赖直接返回任务规划失败,拒绝执行。
2 异步任务消费协程意外退出,如何保证任务不丢失?
标准答案
Redis brpop阻塞读取特性,任务取出未处理完成进程崩溃会重新回到队列;可搭配定时补偿协程,扫描长时间running状态未完成的任务重新入队,保障消息可靠消费。
3 多角色权限+双层配额(工具权限+每日调用上限)如何分层校验?
标准答案
第一层security工具权限:guest禁用所有工具,user禁用vector_add;第二层每日用量配额:Redis计数器拦截LLM/Embedding超量请求;两层校验全部通过才允许执行对应工具,任一拦截直接返回提示。
4 线上如何防止恶意刷取Embedding产生高额费用?
标准答案
三层防护:①全局令牌桶流量限流;②按用户角色设置每日Embedding调用硬配额;③异步批量向量任务仅管理员开放,普通用户无法批量发起大规模Embedding请求。
拓展思考题
1 流水线能否嵌套子流水线,如何实现复杂多层业务流程?
标准答案
扩展PipelineStep新增sub_pipeline字段,网关识别后递归执行子流水线,子流水线输出变量向上层主流水线共享,实现多层嵌套编排,适合复杂多级业务流程。
2 分布式多实例部署,异步任务队列会重复消费吗,如何解决?
标准答案
多实例同时brpop会争抢任务,天然不会重复消费;如需严格幂等,工具执行时增加业务唯一ID判断,重复task_id直接跳过执行逻辑。
3 如何对接定时任务,实现每日自动批量更新知识库流水线?
标准答案
引入APScheduler定时协程,固定时间自动构造批量入库流水线,调用task_center.submit_task写入队列,后台消费自动完成知识库增量更新。
学习总结
Day10在Day9全工具化持久Agent基础上完成生产级分布式配套升级,新增三大核心模块:工具流水线批量编排、Redis异步离线任务、分布式鉴权锁与用量成本统计。
解决三大线上痛点:多步骤任务Token消耗高、长耗时向量操作接口超时、多实例并发会话错乱、无用量管控成本失控。
至此整套Agent具备单机开发、分布式部署、权限管控、成本计量、离线批量处理完整工业能力,覆盖中小型AI智能体全生产落地需求。