agent性能优化

先把问题摸透:4个核心瓶颈

优化前我先把整个流程拆开来测,发现问题就出在这四点:

  1. 向量检索(RAG)每次都重新查,光这一步就耗3-5秒,还重复执行,纯纯浪费资源;
  2. 外部数据文件每次调用都重新解析,文件不大但解析一次也要秒级,完全没必要;
  3. 工具调用链太碎,拿个用户ID和月份都要分两次调用,多一次调用就多一次网络和序列化开销;
  4. 依赖外部API,网络一波动就超时失败,测试时错误率都到7%了。

我的目标很明确:先把重复操作干掉,再简化流程,最后把稳定性提上来,不求一步到位,但要先解决高收益的问题。

第一步:给RAG检索加缓存,从3秒到0.1秒

向量检索是耗时大头,而且同一个查询会反复出现,最适合加缓存。我没搞复杂的分布式缓存,先从内存缓存入手,简单直接见效快。

核心思路就是:第一次查完把结果存起来,30分钟内再查同一个问题,直接拿缓存里的,还得定期清过期的,避免内存爆了。

python 复制代码
# 先初始化缓存和过期时间
rag_cache = {}
CACHE_EXPIRY_TIME = 30 * 60  # 30分钟过期

def get_cache_key(query: str) -> str:
    # 转小写去空格,避免大小写、空格导致缓存失效
    return f"rag_{query.lower().strip()}"

@tool(description="从向量存储中检索参考资料")
def rag_summarize(query: str) -> str:
    import time
    cache_key = get_cache_key(query)
    
    # 缓存命中直接返回
    if cache_key in rag_cache and time.time() - rag_cache[cache_key]['timestamp'] < CACHE_EXPIRY_TIME:
        logger.info(f"[缓存命中] 直接用{query}的检索结果")
        return rag_cache[cache_key]['result']
    
    # 没命中就执行检索,记个耗时
    start_time = time.time()
    result = rag.rag_summarize(query)
    logger.info(f"[检索完成] 这次查了{time.time()-start_time:.2f}秒")
    
    # 把结果存进缓存
    rag_cache[cache_key] = {
        'result': result,
        'timestamp': time.time()
    }
    # 顺手清过期缓存
    _clean_expired_cache()
    return result

def _clean_expired_cache():
    """定期清过期缓存,避免内存泄漏"""
    import time
    expired_keys = [k for k, v in rag_cache.items() if time.time() - v['timestamp'] >= CACHE_EXPIRY_TIME]
    for key in expired_keys:
        del rag_cache[key]

这么改完,只要是30分钟内查过的问题,响应时间直接从3-5秒降到0.1秒以内,重复检索的负载也少了一大半。我还加了日志,能看到缓存命中率,方便后续调整过期时间。

第二步:外部数据只加载一次,秒级变毫秒级

之前每次拿用户月度数据,都会重新读文件、解析,哪怕数据根本没变化。我就加了个全局标记,第一次调用时加载完存内存,之后直接用:

python 复制代码
_external_data_loaded = False  # 标记数据是否已加载

def generate_external_data():
    global _external_data_loaded
    if not _external_data_loaded:
        start_time = time.time()
        external_data_path = get_abs_path(agent_conf["external_data_path"])
        with open(external_data_path, "r", encoding="utf-8") as f:
            # 解析数据的逻辑...
        _external_data_loaded = True
        logger.info(f"外部数据加载完,耗时{time.time()-start_time:.2f}秒")

@tool(description="获取用户月度使用记录")
def fetch_external_data(user_id: str, month: str) -> str:
    generate_external_data()  # 确保数据已加载
    try:
        return external_data[user_id][month]
    except KeyError:
        logger.warning(f"没找到{user_id} {month}的记录")
        return ""

就加了一个布尔标记,数据加载从每次秒级变成只有第一次加载耗几秒,后续都是毫秒级访问,这个改动成本最低,收益却最明显。

第三步:合并工具调用,少走弯路

之前拿用户ID和月份要调两个工具,我直接合了一个get_user_info,一次返回JSON格式的用户ID+月份:

python 复制代码
@tool(description="获取用户ID和当前月份")
def get_user_info() -> str:
    import json
    user_info = {
        "user_id": random.choice(user_ids),
        "month": random.choice(month_arr)
    }
    return json.dumps(user_info)

工具调用链从"拿ID→拿月份→拿数据→生成报告"简化成"拿用户信息→拿数据→生成报告",调用次数少了30%左右,少一次调用就少一次网络往返和数据处理,积少成多,稳定性也跟着提了。

最后看看效果:没花大成本,却解决了大问题

优化完测了几组用例,虽然平均耗时从42.3秒降到38.92秒(个别用例因为网络波动略涨),但核心环节的提升是实打实的:

  • 向量检索缓存命中时<0.1秒,比之前快98%;
  • 外部数据加载从秒级变毫秒级,快99%;
  • 工具调用次数少了30%,网络错误率从7.14%降到0;
  • 测试通过率从92.86%涨到100%。
相关推荐
黄俊懿21 小时前
【架构师从入门到进阶】第六章:服务集群优化——第八节:线程数公式
java·服务器·数据库·分布式·性能优化·架构·系统架构
步步为营DotNet1 天前
Async 状态机要搬进运行时了:.NET 10 Runtime Async 实测,快的地方快 1.5 倍,贵的地方贵 5 倍
性能优化·c#·.net·async·runtime
CCblandy1 天前
雪花 ID 四个边界实测:时钟回拨重号 500 个,前端 JSON.parse 直接丢精度
后端·python·算法·性能优化·架构
Highcharts.js2 天前
Highcharts 发布16项最新安全公告
人工智能·安全·性能优化·highcharts·可视化图表
我不会起名字3222 天前
MySQL 深分页 LIMIT 1000000 卡死:3 种改写与 4 组实测对比
mysql·性能优化·索引·分页·limit
加宝的起点雷达2 天前
扩散语言模型跑出 2788 tokens/s,单卡就够,怎么做到的
人工智能·性能优化
CCblandy2 天前
向量检索 ANN 参数实测:换一种数据分布,recall 掉了 51 个百分点
人工智能·python·算法·自然语言处理·性能优化
Highcharts.js2 天前
Highcharts 数据过滤实战指南:从基础实现到性能优化
javascript·性能优化·bi·highcharts·前端可视化·数据过滤·交互图表
要开心吖ZSH2 天前
《通过SeaTunnel 把 MySQL 的数据搬进 Doris》番外篇:容量、切分与速度——Doris 能扛多少,比 MySQL 快多少
mysql·性能优化·doris·分区·分表·分桶
yunwei372 天前
eBPF 实例教程:BPF 动态指针处理可变长度数据
linux·后端·性能优化