上一篇文章中,我分享了自己对 AI Agent 的认知转变过程。今天,我想从实战角度深入聊聊构建 AI Agent 的三个核心概念:RAG、工具调用和任务规划。这些可以说是我在过去半年开发 AI Agent 项目中最常用到的能力了。
从一个真实需求说起
还记得去年年底,我接到一个特别有意思的需求:要给公司内部开发一个技术文档助手,要求能回答任何关于公司内部技术文档的问题,并且能够执行一些简单的运维任务。
最开始的想法很简单:把所有文档扔给 GPT,让它回答问题就完了。但实际上手后,很快就发现这根本行不通:
- 文档内容太多,完全超出了模型的上下文窗口
- 文档经常会更新,每次都要重新训练太麻烦
- 很多问题需要实时查询系统状态才能回答
- 有些任务需要分步骤执行,不是单次调用能搞定的
正是这个项目,让我深入理解了 RAG、工具调用和任务规划这三个概念的重要性。
RAG:让 AI 拥有"记忆"的能力
RAG(Retrieval-Augmented Generation)说白了就是让 AI 在回答问题前,先去"查资料"。
拿我们的文档助手来说,我是这么实现的:
- 首先把所有文档切分成小块,每块大概 500-1000 字
- 用 embedding 模型把每个文档块转换成向量
- 存入向量数据库(我用的是 Milvus)
用户提问时:
- 先用同样的 embedding 模型处理问题
- 在向量数据库中找最相关的几个文档块
- 把这些文档块作为上下文,让 GPT 回答问题
效果立竿见影:
用户:如何在测试环境部署新服务?
Assistant:让我查一下相关文档...
根据我们的部署文档,测试环境部署新服务需要以下步骤:
1. 在 Jenkins 测试环境项目中创建新的部署任务
2. 配置 Dockerfile 和部署脚本
3. 设置环境变量(记得使用测试环境的配置文件)
4. 执行部署流程
需要我详细解释某个步骤吗?或者我可以帮你检查一下测试环境的状态。
这样实现有几个好处:
- 实时性:文档更新后,只需要更新向量数据库就行
- 准确性:回答都是基于实际文档,不会产生幻觉
- 灵活性:可以很容易添加新的文档来源
工具调用:赋予 AI "手脚"
光能查文档还不够,很多时候用户需要实时信息或者要执行某些操作。这时候就需要工具调用能力了。
在我们的项目中,我定义了一系列工具函数:
@tool
def check_service_status(service_name: str) -> str:
"""检查指定服务的运行状态"""
# 实际实现是调用 K8s API
return k8s_client.get_service_status(service_name)
@tool
def get_latest_logs(service_name: str, lines: int = 100) -> str:
"""获取服务的最新日志"""
return k8s_client.get_pod_logs(service_name, lines)
@tool
def restart_service(service_name: str) -> str:
"""重启指定服务(需要权限)"""
return k8s_client.restart_deployment(service_name)
有了这些工具,AI 就能回答更实际的问题了:
用户:user-service 最近有报错吗?
Assistant:让我查一下日志...
我看到 user-service 最近 10 分钟有几条错误日志:
1. 数据库连接超时(3分钟前)
2. 缓存访问失败(7分钟前)
看起来可能是数据库负载过高导致的。要我帮你重启服务吗?
关键是要让 AI 知道:
- 有哪些工具可用
- 每个工具是做什么的
- 什么时候该用哪个工具
- 如何处理工具调用的结果
任务规划:让 AI 学会"思考"
最后一个也是最重要的能力:任务规划。有了这个,AI 就能处理需要多个步骤才能完成的复杂任务。
还是拿部署服务来说,一个完整的部署流程可能是这样的:
用户:帮我部署新版本的 user-service
Assistant:好的,我来帮你规划部署步骤:
1. 首先检查当前服务状态
> 调用 check_service_status("user-service")
2. 检查新版本的配置是否正确
> 查询部署文档
> 验证配置文件格式
3. 执行部署前检查
> 检查数据库连接
> 验证依赖服务状态
4. 开始部署
> 执行部署命令
> 监控部署进度
5. 部署后验证
> 检查服务状态
> 验证基本功能
> 检查错误日志
要我开始执行这个计划吗?
实现任务规划的关键点:
- 目标分解:把复杂任务拆分成可执行的小步骤
- 依赖管理:理清楚步骤之间的依赖关系
- 状态跟踪:记录每个步骤的执行状态
- 错误处理:遇到问题时能够调整计划
实践心得
经过这半年的实践,我总结了几点心得:
循序渐进
- 先实现基础的 RAG 能力
- 再添加简单的工具调用
- 最后才考虑复杂的任务规划
注重实用性
- 工具函数要简单明确
- 多加错误处理和重试机制
- 保留人工干预的接口
持续优化
- 收集用户反馈
- 分析失败案例
- 不断调整提示词
写在最后
这三个概念看似简单,但要真正用好却不容易。关键是要在实践中不断摸索和优化。
在下一篇文章中,我会详细讲解如何选择合适的技术栈来实现这些能力。如果你也在做类似的尝试,欢迎在评论区分享你的经验。
**粗体** _斜体_ [链接](http://example.com) `代码` - 列表 > 引用
。你还可以使用@
来通知其他用户。