AI Agent 开发
难度等级:⭐⭐⭐⭐ 前置知识:Python 基础、API 开发 后续衔接:云原生、MLOps
学习路径
- 入门阶段:理解 Prompt Engineering,掌握基础 API 调用
- 进阶阶段:能够使用 LangChain 构建 RAG 应用和简单 Agent
- 精通阶段:能够设计多 Agent 协作系统和复杂工作流
一、大语言模型基础
1.1 LLM 工作原理
大语言模型(Large Language Model, LLM)是基于深度学习技术的自然语言处理模型,其核心架构是 Transformer。Transformer 由 Vaswani 等人在 2017 年提出,采用了自注意力机制(Self-Attention),能够并行处理序列中的所有位置,相比传统的 RNN 和 LSTM 具有更高的训练效率和更好的长距离依赖建模能力。
预训练阶段是 LLM 学习语言规律的关键过程。模型通过在海量文本数据上进行自监督学习,学习预测下一个 token 的概率分布。这个过程使模型掌握了语法、语义、常识推理等多层次的语言知识。预训练数据通常包括网页文本、书籍、代码、对话记录等,数据规模可达数万亿 token。
微调阶段则是在预训练模型的基础上,使用特定领域的标注数据进行进一步训练,使模型适应特定任务。常见的微调方式包括指令微调(Instruction Tuning)、人类反馈强化学习(RLHF)等。指令微调通过使用高质量的指令-回答对进行训练,提升模型遵循指令的能力;RLHF 则通过人类偏好数据训练奖励模型,并使用 PPO 等算法优化模型生成更符合人类期望的输出。
推理过程是模型根据输入文本生成输出的阶段。常见的解码策略包括贪婪搜索(Greedy Search)、束搜索(Beam Search)、核采样(Nucleus Sampling)等。温度参数(Temperature)控制输出的随机性,较低的温度使输出更加确定性,较高的温度则增加创造性。Top-p 和 Top-k 参数用于限制采样范围,平衡生成质量和多样性。
# LLM 推理示例 - 使用 OpenAI API
from openai import OpenAI
client = OpenAI(api_key="your-api-key")
response = client.chat.completions.create(
model="gpt-4",
messages=[
{"role": "system", "content": "你是一个专业的编程助手。"},
{"role": "user", "content": "解释一下什么是装饰器"}
],
temperature=0.7,
max_tokens=500,
top_p=0.9
)
print(response.choices[0].message.content)
1.2 主流模型对比
当前市场上存在多种主流大语言模型,每种模型在能力、成本、部署方式上各有特点。
GPT 系列(OpenAI):GPT-3.5 和 GPT-4 是目前应用最广泛的模型。GPT-4 在复杂推理、代码生成、多模态理解方面表现优异,支持 128K 上下文窗口。GPT-4o 进一步优化了多语言能力和响应速度。OpenAI 模型的优势在于生态完善、文档丰富、工具链支持好,但成本相对较高,且数据隐私方面存在顾虑。
Claude 系列(Anthropic):Claude 3(Haiku/Sonnet/Opus)在长文本处理方面表现突出,Claude 3 Opus 支持 200K 上下文。Claude 在安全性、指令遵循、复杂文档理解方面有优势,且在代码生成和数学推理上与 GPT-4 竞争激烈。Anthropic 强调模型的宪法 AI 理念,在安全性和可控性方面投入较多。
LLaMA 系列(Meta):LLaMA 2 和 LLaMA 3 是开源模型的代表,支持商业使用。LLaMA 3 提供 8B 和 70B 两种规格,在开源模型中性能领先。开源模型的优势在于可以本地部署、完全控制、数据不出域,适合对隐私和成本敏感的场景。但需要自行处理部署、优化、维护等工作。
通义千问(阿里云):Qwen 系列模型在中文理解方面表现优异,Qwen2.5 提供从 0.5B 到 72B 的多种规格。通义千问在代码生成、数学计算、多语言支持方面持续优化,且提供完整的工具链和云服务支持。
能力边界:当前 LLM 在以下方面仍存在局限:(1)知识截止性问题,训练数据之外的信息无法准确回答;(2)复杂数学计算和精确逻辑推理容易出错;(3)长文本理解存在上下文窗口限制;(4)可能产生幻觉(Hallucination),生成看似合理但实际错误的内容;(5)多模态理解仍在发展中。在实际应用中需要结合 RAG、工具调用等技术来弥补这些不足。
1.3 模型部署方式
API 调用方式是最简单直接的模型使用方式。通过调用云服务商提供的 REST API,无需关心底层基础设施即可使用模型能力。优点是零运维成本、自动更新、按需付费;缺点是数据需要传输到外部、存在网络延迟、长期使用成本较高、可能受限于速率限制。适合快速原型开发、中小规模应用、对数据隐私要求不高的场景。
# API 调用示例 - 多模型统一接口
import openai
# OpenAI
openai_client = openai.OpenAI(api_key="sk-xxx")
# 兼容 OpenAI 接口的其他模型(如通义千问、DeepSeek)
qwen_client = openai.OpenAI(
api_key="sk-xxx",
base_url="https://dashscope.aliyuncs.com/compatible-mode/v1"
)
本地部署方式适合对数据隐私和成本有严格要求的场景。可以使用 vLLM、TGI(Text Generation Inference)、Ollama 等推理框架部署开源模型。本地部署的优势在于数据完全可控、无 API 调用成本、可自定义优化;缺点是需要 GPU 资源、运维复杂度高、模型更新需要手动处理。
量化技术是降低模型部署资源需求的关键手段。常见的量化方式包括 INT8、INT4、GPTQ、AWQ 等。量化通过降低模型权重的精度来减少内存占用和计算量,例如 70B 模型在 FP16 下需要约 140GB 显存,而 INT4 量化后仅需约 35GB。量化会带来一定的精度损失,但现代量化技术已经将损失控制在可接受范围内。
vLLM 是目前最流行的高性能推理框架之一,采用 PagedAttention 技术,能够显著提升吞吐量和降低延迟。它支持多种开源模型,提供 OpenAI 兼容的 API 接口,适合生产环境部署。
# vLLM 部署示例 - 使用 vLLM Python API
from vllm import LLM, SamplingParams
llm = LLM(model="meta-llama/Llama-3-8b")
sampling_params = SamplingParams(temperature=0.8, top_p=0.95)
outputs = llm.generate(["Hello, my name is"], sampling_params)
print(outputs[0].outputs[0].text)
二、Prompt Engineering
2.1 Prompt 设计原则
Prompt Engineering 是设计和优化输入文本以引导 LLM 产生期望输出的技术和艺术。高质量的 Prompt 能够显著提升模型输出的准确性、相关性和可用性。
清晰指令是 Prompt 设计的核心原则。指令应该明确表达期望模型执行的操作,避免模糊和歧义。使用动词开头的祈使句通常效果更好,例如”请总结以下文章”优于”这篇文章说了什么”。当任务复杂时,应该将大任务分解为多个小步骤,逐步引导模型完成。
角色设定能够激活模型在特定领域的知识。通过设定”你是一个资深程序员”、”你是一位经验丰富的教师”等角色,可以让模型调整输出的风格、深度和角度。角色设定应该具体且与任务相关,过于宽泛的角色设定效果有限。
输出格式约束对于后续处理非常重要。明确指定输出的格式,如 JSON、Markdown 表格、列表等,可以使输出更易于解析和使用。可以使用示例来展示期望的格式,或者使用结构化标记如”请按照以下格式回答:1. 原因 2. 方案 3. 注意事项”。
# 好的 Prompt 示例
prompt = """
你是一个资深的技术架构师。请分析以下系统架构方案,并按照以下格式输出:
【方案概述】用一句话总结方案核心
【优点】列出 3 个主要优势
【风险】列出 2 个潜在风险
【建议】给出 1 条改进建议
待分析方案:{architecture_description}
"""
上下文提供也是关键因素。为模型提供足够的背景信息、约束条件和示例,能够显著提升输出质量。但需要注意上下文窗口的限制,合理筛选和组织输入内容。对于长文档,可以采用分块处理或摘要提取的方式。
2.2 高级技巧
Zero-shot(零样本):直接给出任务指令,不提供任何示例。适用于模型已经充分学习的常见任务。例如:”请将以下英文翻译成中文”。Zero-shot 的优势是简单快捷,但对于复杂或特定领域的任务效果可能不理想。
Few-shot(少样本):在 Prompt 中提供 2-5 个输入-输出示例,帮助模型理解任务模式。Few-shot 能够显著提升模型在特定任务上的表现,尤其是格式要求严格或领域特殊的任务。示例应该具有代表性,覆盖常见的情况,并且格式一致。
# Few-shot Prompt 示例
prompt = """
根据以下示例,提取句子中的关键信息:
输入:张三昨天在北京的会议上介绍了新的机器学习算法
输出:{"人物": "张三", "时间": "昨天", "地点": "北京", "事件": "介绍新机器学习算法"}
输入:李四团队上周完成了系统性能优化,响应时间降低了 50%
输出:{"人物": "李四团队", "时间": "上周", "地点": "未提及", "事件": "完成系统性能优化"}
输入:{user_input}
输出:
"""
Chain-of-Thought(思维链):引导模型逐步推理,而不是直接给出答案。通过添加”请逐步思考”、”让我们一步一步来分析”等提示,可以显著提升模型在数学、逻辑推理等任务上的表现。思维链让模型展示推理过程,不仅提高准确性,也便于验证和调试。
Self-Consistency(自一致性):对同一问题多次采样生成不同的推理路径,然后选择最一致的答案。这种方法通过集成多个推理路径来减少单次生成的随机性影响,特别适合数学和逻辑推理任务。实现方式是对同一 Prompt 运行多次,统计出现频率最高的答案。
Tree of Thoughts(思维树):是思维链的扩展,允许模型在推理过程中探索多条分支,评估每条分支的价值,选择最优路径。这种方法更适合需要搜索和规划的复杂任务。
2.3 Prompt 模板
Prompt 模板是将 Prompt 结构化和参数化的技术,使 Prompt 可以复用、测试和优化。良好的模板设计能够提高开发效率,保证 Prompt 质量的一致性。
变量替换是模板的基础功能。使用占位符标记需要动态填充的内容,在运行时替换为实际值。这种方式使同一个模板可以适用于不同的输入场景。
from string import Template
# 简单模板示例
code_review_template = Template("""
你是一个资深代码审查员。请审查以下 Python 代码:
```python
$code
请从以下方面评价:
- 代码规范性
- 潜在 bug
- 性能优化建议
- 安全漏洞
代码文件:$filename “””)
prompt = code_review_template.substitute( code=”def calculate(items):\n return sum(items)”, filename=”utils.py” )
**条件分支**可以根据输入类型或场景选择不同的 Prompt 策略。例如,对于不同编程语言使用不同的审查标准,对于不同难度的问题采用不同的回答深度。
**结构化输出**模板通过明确定义输出格式,便于后续程序化处理。可以要求模型输出 JSON、XML 或特定标记格式,并使用 JSON Schema 等工具验证输出格式。
```python
# 结构化输出模板
structured_prompt = """
请分析以下用户反馈,并严格按照以下 JSON 格式输出:
{
"sentiment": "positive/negative/neutral",
"topics": ["话题1", "话题2"],
"summary": "一句话总结",
"action_items": ["待办1", "待办2"]
}
用户反馈:{feedback}
"""
Prompt 版本管理也是工程实践中的重要环节。将 Prompt 作为代码的一部分进行版本控制,记录每次修改的原因和效果。可以建立 Prompt 测试集,自动化评估不同版本的质量。
三、RAG 检索增强生成
3.1 RAG 架构
RAG(Retrieval-Augmented Generation,检索增强生成)是一种将信息检索与语言模型生成相结合的技术架构。它的核心思想是:在模型生成回答之前,先从外部知识库中检索相关信息,然后将检索结果作为上下文提供给模型,从而生成更准确、更有依据的回答。
RAG 的工作流程包含以下关键步骤:
- 文档加载:从各种数据源(数据库、文件系统、API、网页等)加载原始文档
- 文档分块:将长文档切分为较小的片段,便于向量化和检索
- 向量化:使用 Embedding 模型将文本块转换为高维向量
- 存储:将向量存储到向量数据库中,建立索引
- 检索:用户提问时,将问题向量化,在向量数据库中检索最相似的文本块
- 生成:将检索到的相关文本块与用户问题组合,作为 Prompt 输入给 LLM 生成最终回答
# RAG 基础流程示例 - 使用 LangChain
from langchain_community.document_loaders import TextLoader
from langchain_text_splitters import RecursiveCharacterTextSplitter
from langchain_openai import OpenAIEmbeddings, ChatOpenAI
from langchain_chroma import Chroma
from langchain_core.prompts import ChatPromptTemplate
from langchain_core.runnables import create_retrieval_chain
# 1. 加载文档
loader = TextLoader("knowledge_base.txt")
documents = loader.load()
# 2. 分块
text_splitter = RecursiveCharacterTextSplitter(chunk_size=500, chunk_overlap=50)
chunks = text_splitter.split_documents(documents)
# 3. 向量化并存储
embeddings = OpenAIEmbeddings()
vectorstore = Chroma.from_documents(chunks, embeddings)
# 4. 检索
retriever = vectorstore.as_retriever(search_kwargs={"k": 3})
# 5. 生成
prompt = ChatPromptTemplate.from_template("""
基于以下上下文回答问题:
{context}
问题:{input}
""")
llm = ChatOpenAI(model="gpt-4")
retrieval_chain = create_retrieval_chain(retriever, prompt | llm)
response = retrieval_chain.invoke({"input": "什么是 RAG?"})
print(response["answer"])
RAG 的优势在于:(1)可以访问训练数据之外的最新信息;(2)生成的回答有明确的来源依据,减少幻觉;(3)知识库可以独立更新,无需重新训练模型;(4)适合企业私有知识的应用场景。
3.2 文档处理
文档处理是 RAG 系统的基础环节,直接影响后续的检索效果。不同类型的文档需要采用不同的处理策略。
文本分割策略是文档处理的核心。常见的分割方式包括:
- 固定长度分割:按照字符数或 token 数进行分割,实现简单但可能在不自然的位置截断
- 递归字符分割:优先在自然边界(段落、句子、单词)处分割,保持语义完整性
- Markdown 分割:根据 Markdown 标题层级进行分割,保持文档结构
- 代码分割:根据代码结构(类、函数、导入)进行分割,保持代码逻辑完整
from langchain_text_splitters import (
RecursiveCharacterTextSplitter,
MarkdownHeaderTextSplitter,
Language
)
# 递归字符分割
text_splitter = RecursiveCharacterTextSplitter(
chunk_size=1000,
chunk_overlap=200,
length_function=len,
separators=["\n\n", "\n", "。", "!", "?", " ", ""]
)
# Markdown 分割
headers_to_split_on = [
("#", "Header 1"),
("##", "Header 2"),
("###", "Header 3"),
]
markdown_splitter = MarkdownHeaderTextSplitter(headers_to_split_on)
# 代码分割
python_splitter = RecursiveCharacterTextSplitter.from_language(
language=Language.PYTHON,
chunk_size=500,
chunk_overlap=100
)
重叠策略:分块时保留相邻块之间的重叠部分(通常 10%-20%),避免关键信息被分割到不同块中导致检索遗漏。重叠大小的选择需要权衡检索精度和上下文完整性。
元数据保留:在分割过程中保留文档的元数据(来源、标题、作者、时间等),这些元数据可以用于过滤检索结果或增强生成质量。
3.3 Embedding 模型
Embedding 模型是将文本转换为高维向量的神经网络模型,其质量直接影响检索的准确性。好的 Embedding 应该能够捕捉语义相似性,即语义相近的文本在向量空间中距离较近。
OpenAI Embedding:text-embedding-ada-002 和 text-embedding-3 系列是广泛使用的商业 Embedding 模型。text-embedding-3-large 支持 3072 维,性能最优;text-embedding-3-small 支持 1536 维,性价比更高。OpenAI Embedding 的优势是质量高、多语言支持好、持续更新,但需要 API 调用,存在成本和延迟。
BGE(BAAI General Embedding):由北京智源人工智能研究院开源的 Embedding 模型,在中文场景表现优异。BGE-large-zh-v1.5 支持 1024 维,在中文检索任务上超越了许多商业模型。BGE 系列模型可以本地部署,适合数据敏感的场景。
M3E:由 MokaAI 开源的多语言 Embedding 模型,支持 1024 维。M3E 在多语言任务和中文场景都有不错的表现,且模型体积较小,部署成本低。
向量维度选择:维度越高理论上表达能力越强,但计算成本也越高。常见的维度有 256、512、768、1024、1536、3072 等。实际应用中需要在质量和性能之间取得平衡,对于大规模向量库可以考虑降维技术。
# 使用不同 Embedding 模型
from langchain_openai import OpenAIEmbeddings
from langchain_community.embeddings import HuggingFaceBgeEmbeddings
# OpenAI Embedding
openai_embeddings = OpenAIEmbeddings(
model="text-embedding-3-small",
dimensions=1536
)
# BGE Embedding(本地部署)
bge_embeddings = HuggingFaceBgeEmbeddings(
model_name="BAAI/bge-large-zh-v1.5",
model_kwargs={'device': 'cuda'},
encode_kwargs={'normalize_embeddings': True}
)
# 计算文本向量
text = "人工智能是未来的发展方向"
vector = openai_embeddings.embed_query(text)
print(f"向量维度:{len(vector)}")
3.4 检索优化
基础的向量检索在某些场景下可能不够精确,需要采用更高级的检索策略来提升召回质量。
Hybrid Search(混合检索):结合向量检索和关键词检索(BM25)的优势。向量检索擅长捕捉语义相似性,但对专有名词和精确匹配可能不够准确;关键词检索则相反,对精确匹配效果好但无法理解语义。混合检索通过加权融合两种方法的结果,达到更好的整体效果。
# 混合检索示例
from langchain.retrievers import EnsembleRetriever
from langchain_community.retrievers import BM25Retriever
from langchain_chroma import Chroma
# 向量检索器
vector_retriever = Chroma.from_documents(docs, embeddings).as_retriever(
search_kwargs={"k": 5}
)
# 关键词检索器
bm25_retriever = BM25Retriever.from_documents(docs)
bm25_retriever.k = 5
# 混合检索
ensemble_retriever = EnsembleRetriever(
retrievers=[bm25_retriever, vector_retriever],
weights=[0.4, 0.6] # 权重分配
)
Re-ranking(重排序):先使用快速但相对粗糙的检索方法召回较多候选(如 Top 20),然后使用更精确但计算成本高的重排序模型对这些候选进行精细排序,选择最终的 Top K。常见的重排序模型包括 Cohere Rerank、BGE Reranker 等。
多路召回:从多个不同的知识源或使用不同的检索策略分别召回,然后合并结果。例如,可以从结构化数据库、向量数据库、搜索引擎分别检索,或者对问题进行改写后多次检索。多路召回能够提高覆盖率,减少单一方法的盲区。
查询改写:对用户的原始问题进行优化后再检索,包括:(1)问题分解:将复杂问题拆分为多个子问题分别检索;(2)假设文档嵌入(HyDE):先生成一个假设的回答,用该回答的向量进行检索;(3)同义词扩展:增加相关词汇提高召回率。
四、LangChain 生态
4.1 LangChain Core
LangChain 是最流行的 LLM 应用开发框架,提供了一系列模块化组件,使开发者能够组合构建复杂的 AI 应用。
Chains(链):Chains 是将多个操作组合在一起的机制。最简单的链是顺序链(Sequential Chain),按固定顺序执行多个步骤。
from langchain_core.prompts import ChatPromptTemplate
from langchain_openai import ChatOpenAI
from langchain_core.output_parsers import StrOutputParser
# 构建简单链
prompt = ChatPromptTemplate.from_template("请解释以下概念:{concept}")
llm = ChatOpenAI(model="gpt-4")
output_parser = StrOutputParser()
# 使用 LCEL(LangChain Expression Language)组合
chain = prompt | llm | output_parser
result = chain.invoke({"concept": "微服务架构"})
print(result)
Prompts(提示词):LangChain 提供了 PromptTemplate 和 ChatPromptTemplate 来管理提示词。PromptTemplate 用于简单的文本提示词,支持变量替换;ChatPromptTemplate 用于对话场景,支持多角色消息。
from langchain_core.prompts import (
ChatPromptTemplate,
SystemMessagePromptTemplate,
HumanMessagePromptTemplate
)
# 对话模板
chat_prompt = ChatPromptTemplate.from_messages([
SystemMessagePromptTemplate.from_template("你是{role}专家"),
HumanMessagePromptTemplate.from_template("请分析:{question}")
])
messages = chat_prompt.format_messages(role="安全", question="SQL注入防护")
Memory(记忆):Memory 组件使链能够记住之前的交互。ConversationBufferMemory 保存所有历史消息;ConversationBufferWindowMemory 只保留最近 K 轮对话;ConversationSummaryMemory 使用 LLM 对历史进行摘要,节省上下文空间。
from langchain.memory import ConversationBufferMemory
from langchain.chains import ConversationChain
# 对话记忆
memory = ConversationBufferMemory()
conversation = ConversationChain(
llm=ChatOpenAI(temperature=0.7),
memory=memory,
verbose=True
)
# 多轮对话
conversation.run("你好,我想学习 Python")
conversation.run("推荐一些学习资源")
4.2 LangGraph
LangGraph 是 LangChain 推出的用于构建有状态、多参与者应用的框架,基于图结构定义了状态机模型,适合构建复杂的 Agent 工作流。
状态机定义:LangGraph 使用 TypedDict 定义状态结构,所有节点共享和修改这个状态。状态可以包含消息列表、任务进度、中间结果等。
from typing import TypedDict, Annotated
from langgraph.graph import StateGraph, END
from langgraph.graph.message import add_messages
# 定义状态
class AgentState(TypedDict):
messages: Annotated[list, add_messages]
task_status: str
result: str
# 定义节点函数
def research_node(state: AgentState):
return {"task_status": "researching", "result": "研究结果..."}
def review_node(state: AgentState):
return {"task_status": "reviewed", "result": state["result"] + " [已审查]"}
# 构建图
workflow = StateGraph(AgentState)
workflow.add_node("research", research_node)
workflow.add_node("review", review_node)
workflow.set_entry_point("research")
workflow.add_edge("research", "review")
workflow.add_edge("review", END)
app = workflow.compile()
条件路由:根据状态或中间结果动态决定下一步执行哪个节点,实现分支逻辑。
def should_continue(state: AgentState) -> str:
if "error" in state.get("result", ""):
return "error_handler"
return "finalizer"
workflow.add_conditional_edges("review", should_continue)
多 Agent 协作:LangGraph 天然支持多 Agent 场景,每个 Agent 可以作为图中的一个节点,通过状态传递信息和协调工作。
4.3 LangSmith
LangSmith 是 LangChain 官方提供的开发平台,用于追踪、调试和评估 LLM 应用。
追踪调试:自动记录每次 LLM 调用的输入、输出、token 消耗、延迟等信息,可视化展示执行流程。当应用出现问题时,可以快速定位是哪个环节出错。
import os
# 配置 LangSmith
os.environ["LANGCHAIN_TRACING_V2"] = "true"
os.environ["LANGCHAIN_API_KEY"] = "your-api-key"
os.environ["LANGCHAIN_PROJECT"] = "my-agent-project"
评估:提供数据集管理和评估框架,可以定义自定义评估指标,批量运行测试,比较不同配置的效果。支持准确性、相关性、安全性等多维度评估。
数据集管理:收集生产环境的交互数据,构建评估数据集。可以手动标注期望输出,用于自动化评估。
4.4 工具集成
LangChain 提供了丰富的工具集成,使 Agent 能够与外部系统交互。
内置工具:包括搜索引擎(DuckDuckGo、Google)、数据库(SQL)、计算器、代码执行器等。
from langchain.agents import create_openai_functions_agent, AgentExecutor
from langchain.tools import DuckDuckGoSearchRun, Tool
# 定义工具
search = DuckDuckGoSearchRun()
calculator = Tool(
name="Calculator",
func=lambda x: str(eval(x)),
description="用于数学计算"
)
tools = [search, calculator]
# 创建 Agent
agent = create_openai_functions_agent(
llm=ChatOpenAI(model="gpt-4"),
tools=tools,
prompt=ChatPromptTemplate.from_messages([
("system", "你是一个有帮助的助手,可以使用工具回答问题。"),
("human", "{input}")
])
)
agent_executor = AgentExecutor(agent=agent, tools=tools, verbose=True)
result = agent_executor.invoke({"input": "今天北京天气如何?123 * 456 = ?"})
自定义工具:通过继承 BaseTool 类或装饰器方式定义自己的工具,使 Agent 能够调用内部 API 或执行特定操作。
from langchain.tools import BaseTool
class DatabaseQueryTool(BaseTool):
name: str = "db_query"
description: str = "查询数据库,参数:SQL 查询语句"
def _run(self, query: str) -> str:
# 执行数据库查询
return execute_sql(query)
五、Agent 架构模式
5.1 ReAct 模式
ReAct(Reason + Act)是最经典的 Agent 架构模式之一,由 Yao 等人在 2022 年提出。其核心思想是让模型交替进行推理(Reasoning)和行动(Acting),通过 Thought-Action-Observation 循环来完成任务。
工作流程:
- Thought(思考):模型分析当前状态,决定下一步做什么
- Action(行动):模型选择并调用一个工具执行
- Observation(观察):获取工具执行结果
- 重复上述循环,直到任务完成
# ReAct Agent 示例
from langchain.agents import initialize_agent, AgentType
from langchain.tools import Tool
tools = [
Tool(
name="Search",
func=search_tool,
description="用于搜索网络信息"
),
Tool(
name="Calculator",
func=calculator_tool,
description="用于数学计算"
)
]
agent = initialize_agent(
tools=tools,
llm=ChatOpenAI(model="gpt-4"),
agent=AgentType.ZERO_SHOT_REACT_DESCRIPTION,
verbose=True
)
# Agent 会交替进行思考和行动
# Thought: 我需要先搜索相关信息
# Action: Search[查询内容]
# Observation: 搜索结果...
# Thought: 根据结果我需要计算...
# Action: Calculator[计算公式]
# Observation: 计算结果...
# Thought: 现在我可以回答问题了
# Final Answer: 最终回答
ReAct 的优势在于:(1)可解释性强,每个步骤都有明确的意图;(2)能够利用外部工具扩展能力;(3)通过观察结果可以纠正错误路径;(4)适合需要多步推理的复杂任务。
5.2 Plan-and-Execute
Plan-and-Execute 是一种先规划后执行的 Agent 模式,适合复杂任务的分解和处理。
工作流程:
- 规划阶段:LLM 分析任务,制定执行计划,将大任务分解为多个子任务
- 执行阶段:按照计划依次执行每个子任务,可以使用工具或调用其他 Agent
- 整合阶段:汇总所有子任务的结果,生成最终答案
- 动态调整:如果执行过程中发现计划不合理,可以重新规划
from langchain_core.prompts import ChatPromptTemplate
from langchain_core.pydantic_v1 import BaseModel, Field
from langchain_openai import ChatOpenAI
# 规划器
class Plan(BaseModel):
steps: list[str] = Field(description="任务步骤列表")
planner_prompt = ChatPromptTemplate.from_messages([
("system", "你是一个任务规划专家。将复杂任务分解为可执行的步骤。"),
("human", "任务:{task}")
])
llm = ChatOpenAI(model="gpt-4")
planner = planner_prompt | llm.with_structured_output(Plan)
# 执行规划
plan = planner.invoke({"task": "分析竞品产品并生成对比报告"})
print(f"执行计划:{plan.steps}")
# 执行器依次处理每个步骤
for i, step in enumerate(plan.steps):
print(f"执行步骤 {i+1}: {step}")
# result = executor.invoke({"step": step})
Plan-and-Execute 的优势在于:(1)结构化思考,避免遗漏关键步骤;(2)可以并行执行独立的子任务;(3)便于监控和调试每个步骤的进展;(4)适合需要长时间运行的复杂任务。
5.3 Multi-Agent 协作
Multi-Agent 系统通过多个具有不同角色和能力的 Agent 协作完成复杂任务,模拟人类团队的分工合作。
角色分工:每个 Agent 承担特定的职责,如研究员、程序员、审查员、项目经理等。角色分工使每个 Agent 专注于自己的领域,提高整体效率和质量。
消息传递:Agent 之间通过消息队列或共享状态进行通信,传递信息、请求和结果。消息格式需要标准化,确保不同 Agent 能够正确理解。
冲突解决:当多个 Agent 产生矛盾的结果时,需要有仲裁机制。常见的方式包括投票、优先级、专家裁决等。
CrewAI 框架:CrewAI 是专门用于构建 Multi-Agent 系统的框架,采用”角色-任务-团队”的抽象模型。
from crewai import Agent, Task, Crew
# 定义角色
researcher = Agent(
role='高级研究员',
goal='深入研究指定技术领域',
backstory='你是一位有 10 年经验的技术研究员',
verbose=True,
allow_delegation=False
)
writer = Agent(
role='技术写作者',
goal='将研究成果整理为结构化的技术文档',
backstory='你是一位擅长将复杂技术概念通俗化的写作者',
verbose=True,
allow_delegation=False
)
# 定义任务
research_task = Task(
description='研究 AI Agent 的最新发展趋势',
agent=researcher,
expected_output='研究报告'
)
writing_task = Task(
description='根据研究报告撰写技术文档',
agent=writer,
expected_output='技术文档',
context=[research_task] # 依赖前一个任务的结果
)
# 组建团队
crew = Crew(
agents=[researcher, writer],
tasks=[research_task, writing_task],
verbose=True
)
result = crew.kickoff()
5.4 Agent 记忆管理
记忆是 Agent 保持上下文、进行持续学习和提供个性化服务的关键。
短期记忆(对话上下文):保存当前会话的历史消息,使 Agent 能够理解多轮对话的上下文。通常使用滑动窗口或摘要机制控制上下文长度,避免超出模型的上下文窗口限制。
from langchain.memory import (
ConversationBufferMemory,
ConversationSummaryMemory,
ConversationBufferWindowMemory
)
# 滑动窗口记忆 - 只保留最近 5 轮
window_memory = ConversationBufferWindowMemory(k=5)
# 摘要记忆 - 使用 LLM 总结历史对话
summary_memory = ConversationSummaryMemory(
llm=ChatOpenAI(temperature=0),
max_token_limit=2000
)
长期记忆(向量数据库存储):将重要的信息、用户偏好、历史经验等存储到向量数据库中,使 Agent 能够跨会话记住关键信息。长期记忆需要设计合理的存储和检索策略,确保在需要时能够召回相关信息。
工作记忆(任务状态):在执行复杂任务时,Agent 需要保存中间结果、当前进度、待办事项等工作状态。工作记忆通常是结构化的,与当前任务紧密相关,任务完成后可以清理。
# 工作记忆示例
class TaskMemory:
def __init__(self):
self.current_task = None
self.subtasks = []
self.completed = []
self.intermediate_results = {}
def add_subtask(self, subtask: str):
self.subtasks.append(subtask)
def complete_subtask(self, index: int, result: str):
task = self.subtasks.pop(index)
self.completed.append(task)
self.intermediate_results[task] = result
六、向量数据库
6.1 核心概念
向量数据库是专门用于存储和检索高维向量数据的数据库系统,是 RAG 和语义搜索的核心组件。
向量:通过 Embedding 模型将文本、图像等非结构化数据转换为固定长度的数值数组。向量捕捉了数据的语义特征,语义相似的数据在向量空间中距离较近。
相似度计算是向量检索的基础,常见的方法包括:
- 余弦相似度(Cosine Similarity):计算两个向量夹角的余弦值,范围 [-1, 1],值越大越相似。对向量的绝对大小不敏感,适合文本语义匹配
- 欧氏距离(Euclidean Distance):计算两个向量之间的直线距离,距离越小越相似。对向量的绝对大小敏感
- 点积(Dot Product):两个向量对应元素的乘积之和。当向量经过归一化处理后,点积等价于余弦相似度
ANN 算法(Approximate Nearest Neighbor):在大规模向量库中,精确搜索所有向量的计算成本过高。ANN 算法通过牺牲一定的精度来大幅提升检索速度。常见的 ANN 算法包括:
- HNSW(Hierarchical Navigable Small World):构建多层图结构,在顶层快速定位大致区域,在底层精细搜索。适合中等规模数据,查询速度快
- IVF(Inverted File Index):将向量空间划分为多个区域,先确定查询向量所在的区域,然后只搜索该区域内的向量。适合大规模数据
- PQ(Product Quantization):将高维向量压缩为低维表示,减少存储空间和计算量。常与其他算法结合使用
6.2 Milvus
Milvus 是一个开源的分布式向量数据库,由 Zilliz 公司开发,适合生产环境的大规模向量检索场景。
架构特点:Milvus 采用存算分离架构,计算节点和存储节点可以独立扩展。支持多种索引类型(IVF、HNSW、PQ 等),提供丰富的过滤条件支持。
# Milvus 使用示例
from pymilvus import connections, Collection, FieldSchema, CollectionSchema, DataType
# 连接 Milvus
connections.connect("default", host="localhost", port="19530")
# 定义 Collection Schema
fields = [
FieldSchema(name="id", dtype=DataType.INT64, is_primary=True),
FieldSchema(name="embedding", dtype=DataType.FLOAT_VECTOR, dim=1536),
FieldSchema(name="text", dtype=DataType.VARCHAR, max_length=2000)
]
schema = CollectionSchema(fields, description="文档向量库")
# 创建 Collection
collection = Collection("documents", schema)
# 插入数据
entities = [
[1, 2], # IDs
[[0.1] * 1536, [0.2] * 1536], # Embeddings
["文本1", "文本2"] # Texts
]
collection.insert(entities)
# 创建索引
index_params = {
"index_type": "HNSW",
"metric_type": "COSINE",
"params": {"M": 16, "efConstruction": 64}
}
collection.create_index("embedding", index_params)
# 搜索
collection.load()
results = collection.search(
data=[[0.15] * 1536],
anns_field="embedding",
param={"metric_type": "COSINE", "params": {"ef": 64}},
limit=3
)
适用场景:Milvus 适合数据量大(百万级以上)、需要高并发查询、对可用性要求高的生产环境。支持 Kubernetes 部署,具有良好的水平扩展能力。
6.3 Chroma
Chroma 是一个轻量级的向量数据库,设计目标是简单易用,适合开发和原型阶段。
特点:Chroma 支持嵌入式部署,无需单独的服务进程。提供持久化存储,可以将数据保存到本地文件系统。支持元数据过滤和文档存储,与 LangChain 等框架集成良好。
# Chroma 使用示例
import chromadb
from chromadb.utils import embedding_functions
# 创建客户端(内存模式)
client = chromadb.Client()
# 或持久化模式
# client = chromadb.PersistentClient(path="./chroma_db")
# 创建 Collection
collection = client.create_collection(name="documents")
# 添加文档
collection.add(
documents=["这是第一篇文章", "这是第二篇文章"],
metadatas=[{"source": "web"}, {"source": "file"}],
ids=["id1", "id2"]
)
# 查询
results = collection.query(
query_texts=["关于什么?"],
n_results=2
)
适用场景:Chroma 适合本地开发、原型验证、小规模应用。由于是嵌入式部署,不适合高并发或大数据量的生产环境。
6.4 Pinecone
Pinecone 是一个完全托管的向量数据库云服务,提供自动化的索引管理和高可用保障。
特点:Pinecone 提供 Serverless 架构,无需管理基础设施。自动处理索引优化、数据备份、故障恢复。支持实时数据更新和增量索引。提供 REST API 和 SDK,集成简单。
# Pinecone 使用示例
import pinecone
# 初始化
pinecone.init(api_key="your-api-key", environment="us-east-1")
# 创建索引
pinecone.create_index(
name="my-index",
dimension=1536,
metric="cosine"
)
# 连接索引
index = pinecone.Index("my-index")
# 插入数据
index.upsert([
("id1", [0.1] * 1536, {"text": "内容1"}),
("id2", [0.2] * 1536, {"text": "内容2"})
])
# 查询
results = index.query(
vector=[0.15] * 1536,
top_k=3,
include_metadata=True
)
适用场景:Pinecone 适合不想运维基础设施、需要快速上线、对可用性要求高的场景。缺点是数据存储在第三方、长期使用成本较高、自定义能力有限。
6.5 选型指南
选择合适的向量数据库需要综合考虑多个因素:
| 维度 | Chroma | Milvus | Pinecone |
|---|---|---|---|
| 部署方式 | 嵌入式 | 自建集群 | 托管服务 |
| 运维成本 | 零 | 高 | 零 |
| 数据规模 | 万级 | 亿级 | 亿级 |
| 并发能力 | 低 | 高 | 高 |
| 成本 | 免费 | 服务器成本 | 按量付费 |
| 适用阶段 | 开发/原型 | 生产环境 | 生产环境 |
推荐方案:
- 本地开发:使用 Chroma,零配置快速原型验证
- 生产环境(自建):使用 Milvus,完全控制、成本可控
- 生产环境(云服务):使用 Pinecone,免运维、快速上线
- 混合方案:开发阶段用 Chroma,生产切换到 Milvus 或 Pinecone
七、主流框架对比
| 框架 | 特点 | 适用场景 | 学习曲线 |
|---|---|---|---|
| LangChain | 生态最全、组件丰富、社区活跃 | 通用 Agent 开发、RAG 应用 | 中等 |
| LlamaIndex | 数据索引强项、查询优化好 | RAG 应用、知识库问答 | 中等 |
| AutoGen | 多 Agent 对话、微软支持 | 协作场景、对话系统 | 较高 |
| CrewAI | 角色任务编排、API 简洁 | 团队型 Agent、工作流编排 | 较低 |
| LangGraph | 状态机模型、灵活控制 | 复杂工作流、多 Agent 协作 | 较高 |
LangChain 是最早也是最流行的 LLM 应用框架,提供了从 Prompt 管理、链式调用、工具集成到 Agent 构建的完整解决方案。优势在于生态完善、文档丰富、社区活跃;缺点是抽象层次多、学习曲线较陡、部分设计过于复杂。
LlamaIndex 专注于数据索引和检索,在 RAG 场景有独特优势。提供了多种数据加载器、索引策略和查询引擎。优势在于数据处理能力强、查询优化好;缺点是 Agent 能力相对较弱、生态不如 LangChain。
AutoGen 是微软开源的多 Agent 对话框架,专注于通过多 Agent 协作完成复杂任务。优势在于对话管理好、支持人类参与、可扩展性强;缺点是相对较新、文档不够完善。
CrewAI 采用更简洁的抽象,通过角色-任务-团队的模式快速构建多 Agent 系统。优势在于 API 设计优雅、学习成本低、适合业务场景;缺点是功能相对简单、定制能力有限。
八、AI Agent 最佳实践
8.1 可靠性
AI Agent 的可靠性是生产环境的关键要求,需要从多个方面进行保障。
重试机制:LLM API 调用可能因为网络、限流、服务故障等原因失败,需要实现自动重试。建议使用指数退避策略(Exponential Backoff),避免频繁重试导致的服务压力。
import time
from functools import wraps
from openai import RateLimitError, APIError
def retry_with_backoff(max_retries=3, base_delay=1):
def decorator(func):
@wraps(func)
def wrapper(*args, **kwargs):
for attempt in range(max_retries):
try:
return func(*args, **kwargs)
except (RateLimitError, APIError) as e:
if attempt == max_retries - 1:
raise
delay = base_delay * (2 ** attempt)
time.sleep(delay)
return None
return wrapper
return decorator
@retry_with_backoff(max_retries=3)
def call_llm(prompt):
return client.chat.completions.create(
model="gpt-4",
messages=[{"role": "user", "content": prompt}]
)
超时控制:为 LLM 调用设置合理的超时时间,避免长时间等待导致系统响应变慢。对于实时性要求高的场景,超时后应该快速失败并返回降级结果。
降级策略:当主模型不可用或响应质量不达标时,应该有备选方案。常见的降级策略包括:(1)切换到备用模型;(2)使用缓存的历史回答;(3)返回预设的默认回答;(4)转人工处理。
# 降级策略示例
def get_response_with_fallback(prompt):
# 尝试主模型
try:
return call_gpt4(prompt)
except Exception:
# 降级到 GPT-3.5
try:
return call_gpt35(prompt)
except Exception:
# 返回缓存结果
return get_cached_response(prompt)
8.2 成本控制
LLM 调用成本是 AI Agent 应用的主要运营成本,需要从多个方面进行优化。
Token 优化:减少不必要的输入输出。使用精确的 Prompt 避免冗余信息;对长文档进行摘要或分块处理;设置合理的 max_tokens 限制输出长度。
缓存机制:对相同或相似的请求使用缓存结果,避免重复调用。可以使用语义相似度判断是否命中缓存,而不是简单的字符串匹配。
# 语义缓存示例
from langchain.globals import set_llm_cache
from langchain.cache import SQLiteCache
# 启用缓存
set_llm_cache(SQLiteCache(database_path=".langchain.db"))
# 相同请求会命中缓存
response1 = llm.invoke("什么是 Python?")
response2 = llm.invoke("什么是 Python?") # 从缓存返回
模型路由:根据任务复杂度动态选择合适的模型。简单任务使用小模型(成本低),复杂任务使用大模型(质量好)。可以通过分类器或规则来判断任务复杂度。
# 模型路由示例
def route_model(task_complexity: str, prompt: str):
if task_complexity == "simple":
return call_small_model(prompt) # 如 GPT-3.5
elif task_complexity == "complex":
return call_large_model(prompt) # 如 GPT-4
else:
return call_medium_model(prompt) # 如 GPT-4o-mini
8.3 安全合规
AI Agent 在生产环境需要关注安全性和合规性问题。
Prompt 注入防护:恶意用户可能通过在输入中注入指令来操纵 Agent 行为。防护措施包括:(1)将用户输入与系统 Prompt 分离;(2)对输入进行过滤和转义;(3)使用专门的注入检测模型。
# Prompt 注入防护示例
def sanitize_user_input(user_input: str) -> str:
# 移除潜在的注入指令
dangerous_patterns = [
"ignore previous instructions",
"system:",
"you are now",
"disregard"
]
for pattern in dangerous_patterns:
if pattern.lower() in user_input.lower():
raise ValueError("Potential prompt injection detected")
return user_input
输出过滤:对 LLM 生成的内容进行安全检查,过滤敏感信息、不当言论、错误信息等。可以使用关键词过滤、分类模型、规则引擎等方式。
数据隐私:确保用户数据不会被不当存储或传输。对于敏感数据,应该在发送给 LLM 之前进行脱敏处理。遵守相关法规(如 GDPR、个人信息保护法)的要求。
审计日志:记录所有 LLM 调用的输入输出,便于事后审计和问题追溯。日志中应该包含时间戳、用户 ID、请求内容、响应内容、token 消耗等信息。
九、学习资源推荐
官方文档
- OpenAI API 文档:https://platform.openai.com/docs - 了解 API 参数、模型能力、最佳实践
- LangChain 文档:https://python.langchain.com/docs - 框架使用指南、概念解析、教程
- LangGraph 文档:https://langchain-ai.github.io/langgraph - 状态机 Agent 构建教程
- LlamaIndex 文档:https://docs.llamaindex.ai - RAG 应用开发指南
在线课程
- DeepLearning.AI - ChatGPT Prompt Engineering:Prompt Engineering 入门课程
- DeepLearning.AI - LangChain for LLM Application Development:LangChain 实战课程
- 吴恩达系列课程:涵盖 LLM 应用开发的多个方面
开源项目
- LangChain:https://github.com/langchain-ai/langchain - 学习框架源码
- AutoGen:https://github.com/microsoft/autogen - 多 Agent 协作实现
- CrewAI:https://github.com/crewAIInc/crewAI - 角色任务编排框架
- PrivateGPT:https://github.com/imartinez/privateGPT - 本地 RAG 应用参考
书籍推荐
- 《Building LLM Apps》:LlamaIndex 作者编写,RAG 应用开发实战
- 《LangChain 实战》:国内出版的 LangChain 教程
- 《Prompt Engineering Guide》:在线开源指南
社区与资讯
- Hugging Face:https://huggingface.co - 模型、数据集、论文
- GitHub Trending:关注 AI 相关热门项目
- 技术博客:OpenAI Blog、Anthropic Blog、Google AI Blog
实践建议
- 从简单开始:先用 API 构建简单的应用,再逐步引入框架
- 多做项目:理论知识需要结合实践,尝试构建自己的 RAG 或 Agent 项目
- 关注生态:AI 领域发展迅速,持续关注新工具和新方法
- 理解原理:不要停留在 API 调用层面,理解底层原理有助于解决复杂问题
- 参与社区:加入开源社区,贡献代码或提出问题,加速学习