Token
是模型处理文本的最小单位,它 不等于一个汉字或一个单词,而是一个按特定规则拆分出来的片段。
大语言模型并不直接处理「字」或「词」,而是将输入文本 分词成 Token(标记) 后再输入模型。这个分词方式通常使用一种叫 Byte Pair Encoding (BPE) 或 SentencePiece 的算法。
同一个中文词句,不同 tokenizer 拆的方式可能不一样。每个 token 都会被映射成一个 ID(整数),然后送进神经网络进行计算。
Transformer
Transformer 模型是由 Vaswani 等人在 2017 年提出的,是一种完全基于注意力机制(Attention)的深度学习架构,广泛应用于自然语言处理(NLP)任务中。
大致的结构是一个编码器加一个解码器,输入是token序列,输出经过一个线性层(Linear)和 softmax,生成概率分布用于预测下一个词或 token。
预训练
数据量大(如海量文本)
通常是无监督或自监督学习(比如 Masked Language Model)
学到的是通用知识(例如语言模型的语法、语义等)
例子:ChatGPT就是使用互联网的大量文本数据进行预训练
后训练
后训练指的是在预训练模型基础上,进一步进行微调(fine-tuning)或特定任务训练。
数据量较小,针对具体任务(如情感分类、对话、摘要)
通常是有监督学习(使用标注数据)
例子:ChatGPT的后训练就是Open_ai雇了一些专家来撰写对话的例子来让模型有了对话的能力
Prompt
提示词,在模型的context窗口中利用prompt可以提高模型的准确率,而不是输出一下无关的东西。因为模型的推理是随机的sample下一个token,如果没有正确的引导,他很容易跑偏。
llm利用工具
在prompt中预设一些特殊的字符,当模型遇到这些字符的时候,按照规则去调用工具,例如联网搜索,将获取的数据结合模型的知识来输出结果。
RAG
RAG(Retrieval-Augmented Generation,检索增强生成)是一种将信息检索与文本生成相结合的人工智能技术,旨在通过引入外部知识源来增强大语言模型(LLM)的生成能力,有效解决传统语言模型存在的知识滞后、事实性错误(幻觉)等问题。
一、核心概念
1. 基本定义
RAG是一种混合架构,通过两个关键阶段工作:
-
检索阶段:从外部知识库中查找相关信息
-
生成阶段:基于检索到的内容生成最终回答
2. 与传统LLM的区别
|
特性 |
传统LLM |
RAG系统 |
|
知识来源 |
训练时的参数化知识 |
实时检索的外部知识库 |
|
知识更新 |
需要重新训练 |
仅更新知识库即可 |
|
事实准确性 |
依赖训练数据 |
可验证的来源支持 |
|
可解释性 |
黑箱生成 |
可追溯参考来源 |
二、技术架构
1. 核心组件
graph TD A[用户问题] --> B[检索模块] B --> C[知识库] C --> D[相关文档] D --> E[生成模块] A --> E E --> F[增强后的回答]
2. 工作流程
-
查询理解:分析用户意图
-
文档检索:从知识库返回相关片段
-
上下文整合:将检索结果与问题结合
-
生成回答:基于增强上下文生成最终输出
三、关键技术实现
1. 检索模块
-
向量检索:
# 使用稠密向量检索示例 from sentence_transformers import SentenceTransformer encoder = SentenceTransformer('all-MiniLM-L6-v2') query_embedding = encoder.encode("什么是RAG?") # 计算与知识库中向量的相似度 -
混合检索:结合关键词(BM25)与向量搜索
-
层级检索:先粗排后精排的多阶段检索
2. 生成模块
-
提示工程:
请基于以下上下文回答问题: 上下文:{{retrieved_documents}} 问题:{{user_question}} 要求:引用上下文中的具体证据 -
注意力机制:让生成模型聚焦检索内容
3. 知识库构建
-
文档处理:
-
分块(通常256-512 tokens)
-
添加元数据(来源、更新时间等)
-
-
向量化:
-
使用BERT、GPT等模型生成嵌入
-
存入向量数据库(Pinecone、Milvus等)
-
纯本地化RAG系统实现方案(无需联网模型)
以下是完全基于本地模型和工具构建RAG系统的完整方案,使用LangChain框架,无需依赖OpenAI等云端API:
一、本地技术栈选择
|
组件 |
推荐方案 |
替代方案 |
|
嵌入模型 |
all-mpnet-base-v2 (Sentence-Transformers) |
text2vec-base-chinese |
|
LLM |
ChatGLM3-6B (4bit量化版) |
Llama-3-8B-Chinese-Chat |
|
向量数据库 |
FAISS (本地索引) |
Chroma |
|
部署框架 |
Gradio (本地Web界面) |
FastAPI (REST服务) |
二、完整实现代码
from langchain.document_loaders import PyPDFLoader from langchain.text_splitter import RecursiveCharacterTextSplitter from langchain.embeddings import HuggingFaceEmbeddings from langchain.vectorstores import FAISS from langchain.llms import Ollama # 或使用Transformers from langchain.chains import RetrievalQA from langchain.prompts import PromptTemplate import gradio as gr import torch class LocalRAG: def __init__(self, file_path): # 1. 加载文档 loader = PyPDFLoader(file_path) documents = loader.load() # 2. 文本分割 text_splitter = RecursiveCharacterTextSplitter( chunk_size=500, chunk_overlap=50 ) self.texts = text_splitter.split_documents(documents) # 3. 本地嵌入模型 self.embeddings = HuggingFaceEmbeddings( model_name="sentence-transformers/all-mpnet-base-v2", model_kwargs={"device": "cuda" if torch.cuda.is_available() else "cpu"} ) # 4. 向量存储 self.db = FAISS.from_documents(self.texts, self.embeddings) # 5. 本地LLM配置(使用Ollama或直接加载) self.llm = Ollama(model="llama3:8b") # 本地运行的Ollama服务 # 或使用Transformers直接加载: # from langchain.llms import HuggingFacePipeline # self.llm = HuggingFacePipeline.from_model_id( # model_id="THUDM/chatglm3-6b", # device="cuda" if torch.cuda.is_available() else "cpu", # model_kwargs={"load_in_4bit": True} # ) # 6. 提示模板 self.prompt = PromptTemplate( template="""基于以下上下文信息回答问题。用中文简洁回答。 上下文: {context} 问题: {question} 答案:""", input_variables=["context", "question"] ) # 7. 检索链 self.qa = RetrievalQA.from_chain_type( llm=self.llm, chain_type="stuff", retriever=self.db.as_retriever(search_kwargs={"k": 3}), chain_type_kwargs={"prompt": self.prompt}, verbose=True ) def query(self, question): return self.qa.run(question) # 初始化系统(替换为您的文件路径) rag = LocalRAG("your_document.pdf") # 创建Gradio界面 iface = gr.Interface( fn=rag.query, inputs=gr.Textbox(label="输入问题", placeholder="请输入关于文档的问题..."), outputs=gr.Textbox(label="回答"), title="本地知识问答系统", examples=["文档的主要内容是什么?", "作者的核心观点有哪些?"] ) # 启动服务 if __name__ == "__main__": iface.launch(server_name="0.0.0.0", server_port=7860)
三、部署前准备
1. 安装本地LLM运行时(Ollama方案)
# 安装Ollama curl -fsSL https://ollama.com/install.sh | sh # 下载中文优化模型(约4.7GB) ollama pull llama3:8b-instruct-q4_0 # 启动本地服务 ollama serve
2. 或直接使用Transformers加载模型
pip install transformers accelerate bitsandbytes
四、系统优化技巧
1. 量化加载减少显存占用
from transformers import AutoModelForCausalLM, AutoTokenizer, BitsAndBytesConfig quant_config = BitsAndBytesConfig( load_in_4bit=True, bnb_4bit_quant_type="nf4", bnb_4bit_compute_dtype=torch.float16 ) model = AutoModelForCausalLM.from_pretrained( "THUDM/chatglm3-6b", quantization_config=quant_config, device_map="auto" )
2. 中文优化配置
# 使用中文嵌入模型 embeddings = HuggingFaceEmbeddings( model_name="shibing624/text2vec-base-chinese", model_kwargs={"device": "cuda"} ) # 修改提示模板为中文优化版 prompt = PromptTemplate( template="""请根据以下上下文用中文回答问题。如果不知道答案,请说"根据现有信息无法回答"。 上下文: {context} 问题: {question} 请用简体中文回答:""", input_variables=["context", "question"] )
五、硬件需求建议
|
组件 |
最低配置 |
推荐配置 |
|
CPU |
4核 |
8核以上 |
|
内存 |
8GB |
16GB+ |
|
GPU |
无(仅CPU推理) |
NVIDIA RTX 3060 8GB+ |
|
存储 |
10GB可用空间 |
50GB+ SSD |
解释
1. 嵌入模型(Embedding Model)
本质与作用
-
定义:将文本/图像等数据转化为稠密向量(一组数值)的神经网络模型
-
功能:捕获语义信息,使相似内容在向量空间中距离相近
-
示例转换:
"猫" → [0.24, -0.85, 0.73,..., 0.12] (768维向量) "犬" → [0.31, -0.72, 0.68,..., 0.09]
2. 向量数据库(Vector Database)
核心架构
graph LR A[原始文本] --> B(嵌入模型) B --> C[向量数据] C --> D{向量数据库} D --> E[近似最近邻搜索]
关键能力
-
高效检索:在亿级数据中实现毫秒级查询(如FAISS的HNSW算法)
-
动态更新:支持增量插入新数据
-
混合查询:可结合标量过滤(如时间范围+语义搜索)
3. 检索链(Retrieval Chain)
将
典型工作流程
用户问题 → 向量检索 → 结果排序 → 上下文组装 → LLM生成 → 输出回答
三组件协同关系
sequenceDiagram participant User participant Embedding participant VectorDB participant Chain participant LLM User->>+Embedding: 输入问题 Embedding->>VectorDB: 生成问题向量 VectorDB->>Chain: 返回相似文档 Chain->>LLM: 组装提示词 LLM->>-User: 生成最终回答
数据流示例:
-
用户问:"推荐几本量子力学入门书?"
-
嵌入模型将问题转为向量
-
向量数据库返回:
[《量子力学基础》(相似度0.91), 《从零学量子》(0.87)...] -
检索链组装提示:
基于以下书籍信息回答问题: - 《量子力学基础》:...(简介) - 《从零学量子》:...(目录节选) 问题:推荐几本量子力学入门书? -
LLM生成:
推荐以下入门书籍: 1. 《量子力学基础》- 系统讲解基础概念 2. 《从零学量子》- 适合数学基础薄弱的读者...
这种架构既利用了向量搜索的效率,又保留了LLM的语言理解能力,形成1+1
MCP
MCP是提供了一个标准化的协议,将tool和llm工具进行连接。
之前tool的接入是agent去做胶水层进行接入,每个tool使用方式都不一样。有了MCP后,tool使用统一的MCP协议开发MCP server,agent只需要一个统一的MCP client就能进行tool call。llm厂商需要去适配MCP,如果都支持了,最后的效果就是告诉llm,MCP服务的地址,然后他就能自己判断去调用。
MCPserver和client的通信通过jsonRPCMessage。MCP server可以是本地的,也可以是远程的。本地的通信实现是stdio(只能server,client 1对1)。远程的是SSE,SSE是单向的服务端流式向客户端推消息,SSE Session的问题在于他的双向连接实现是client先向服务端发一个建联请求,然后这个session连接就不能发消息了,只能接受SSE流,后续的请求都是通过另一个post来请求的。如果是分布式服务,如果第一个session和后面post不是发到一个服务器,那么就无法响应了。
MCP发现。 client向server发一个tool list请求,server响应tools的schema list。
和RPC的区别:RPC不需要鉴权,RPC是单向的,MCP是双向流式,MCP发现和RPC也不一样。
-
Tool call
Function call和prompt调用tool的区别:prompt需要自己去告诉大模型有哪些tool,如何使用。准确率低,和模型绑定,开发效率低。function call是制定了一个标准的协议(json),通过明确的函数定义和参数描述来告诉大模型应该如何调用tool。原理就是大模型厂商将function call翻译为prompt,这个prompt就是厂商训练模型使用的prompt,所以他的准确率高,而且标准协议提高了开发效率。但是不同厂商的function call还是不一样,而且调用tool还是本地调用。
Agent
是什么
大语言模型是你告诉他一个指令,他回去给你一个回答,例如你问美国的首都,他回答北京。
agent是你告诉他一个目标,他自己去规划,设计,行动,观察结果,分析,下一步行动或者返回结果。例如你下围棋,你告诉他你要赢,然后他会落一个子,然后观察棋盘,棋盘变化后(对手落子),他会分析,再采取一个行动。
历史
没有llm之前,通常用强化学习(RL)实现agent。RL是你定义一个reward,然后模型产生行为,根据环境变化,最大化reward。但是他的局限在于每一个任务都需要用RL定义一个reward,训练一个模型。例如alphaGo可以下围棋,但是不能下象棋。
LLM
现在讨论的agent都是用llm来实现,所以不是说发明了agent,而是出现了llm,人们考虑用llm来实现agent。
llm是语言模型,你要告诉他规则,目标。然后他会进行规划,产生行动,行动也是语言输出,所以需要agent将语言变为行动(调用tool等)。agent会观察行动的结果(环境的变化),然后进行下一步行动。ReAct Observation框架。
LLM agent相对于RL agent的优势在于,RL只能做实现设定的行为(例如下围棋)。而LLM可以讲任何话,输出没有限制,可以解决更多问题。另一种角度,之前人们使用LLM,是问一个问题,然后人为去做,然后人为观察,再问LLM。LLM agent相当于解放了人的行为。
如何实现agent
-
根据上下文调整行为
agent很重要一个特点是更具环境调整行为,llm本身就是有token 上下文的,通过上下文窗口输入就可以调整llm的输出(不需要调整参数)。但是上下文窗口长度是有限制的,如果记忆太多,无法利用长期的记忆。可以使用一个read模块,大模型使用read查记忆中的信息来回答。
RAG就是用来解决长期记忆的问题,将长期记忆的信息存到数据库中,然后询问的时候去数据库中检索相关信息,然后再进行回答。
RAG的实验说明了,直接告诉大模型正例比反例更有效,这对预训练和prompt有所启发。
write模块也可以做一些工作,比如用一个llm来判断信息是否是有效信息,有效才写入。
还可以有一个reflection模块,可以对记忆中的信息进行反思,推理,整理,获取一些新的想法或者信息。
-
tool调用
-
目标任务规划
plan能力是llm在observation之后对达成目标进行一个规划,将plan内容作为输入喂给llm。
plan的实现是大模型假设了一个虚拟世界,然后假设进行多个步骤进行,搜索最佳的规划。
tool_call
-
用户向agent输入问题
-
llm决策,分析输入判断是否调用tool,选择tool,生成调用tool所需的参数指令
-
agent执行tool,接收tool返回结果
-
llm整合tool结果和用户输入,生成最终答案
llm决策的过程有两种方案:1. llm本身的function calling能力,动态决定是否调用tool。2. 如果llm不支持function calling,可以通过prompt,规则引擎来选择tool

prompt实现tool_call
-
System Prompt:如果你遇到无法回答的问题,使用工具。把使用工具的指令放在<tool>和</tool>中间,使用完工具后你会得到输出,放在<output>和</output>中间。
-
现在你可以使用的工具如下:查询某时某地温度的函数temperature(location,time),使用范例:temperature(‘台北’,‘2025.02.22‘)
-
User Prompt:2025年3月10日,高雄气温如何
-
llm返回的结果中获取对应标签内的结果做对应,例如<tool>是需要调用的工具,需要做调用。<output>是工具结果,需要喂给大模型回答。
System Prompt是一个大模型应用下的prompt,需要放在最前面,相当于给一个大模型应用一个全局的prompt。每次对话都是基于这个prompt。优先级比User Prompt高。
function_call实现tool_call
tools = [ # 工具1 获取当前时刻的时间 { "type": "function", "function": { "name": "get_current_time", "description": "当你想知道现在的时间时非常有用。", # 因为获取当前时间无需输入参数,因此parameters为空字典"parameters": {}, }, }, # 工具2 获取指定城市的天气 { "type": "function", "function": { "name": "get_current_weather", "description": "当你想查询指定城市的天气时非常有用。", "parameters": { "type": "object", "properties": { # 查询天气时需要提供位置,因此参数设置为location"location": { "type": "string", "description": "城市或县区,比如北京市、杭州市、余杭区等。", } }, "required": ["location"], }, }, }, ] def call_with_messages(): print("\n") messages = [ { "content": input( "请输入:" ), # 提问示例:"现在几点了?" "一个小时后几点" "北京天气如何?""role": "user", } ] print("-" * 60) # 模型的第一轮调用 i = 1 first_response = get_response(messages) assistant_output = first_response.choices[0].message print(f"\n第{i}轮大模型输出信息:{first_response}\n") if assistant_output.content is None: assistant_output.content = "" messages.append(assistant_output) # 如果不需要调用工具,则直接返回最终答案if ( assistant_output.tool_calls == None ): # 如果模型判断无需调用工具,则将assistant的回复直接打印出来,无需进行模型的第二轮调用print(f"无需调用工具,我可以直接回复:{assistant_output.content}") return# 如果需要调用工具,则进行模型的多轮调用,直到模型判断无需调用工具while assistant_output.tool_calls != None: # 如果判断需要调用查询天气工具,则运行查询天气工具 tool_info = { "content": "", "role": "tool", "tool_call_id": assistant_output.tool_calls[0].id, } if assistant_output.tool_calls[0].function.name == "get_current_weather": # 提取位置参数信息 argumens = json.loads(assistant_output.tool_calls[0].function.arguments) tool_info["content"] = get_current_weather(argumens) # 如果判断需要调用查询时间工具,则运行查询时间工具elif assistant_output.tool_calls[0].function.name == "get_current_time": tool_info["content"] = get_current_time() tool_output = tool_info["content"] print(f"工具输出信息:{tool_output}\n") print("-" * 60) messages.append(tool_info) assistant_output = get_response(messages).choices[0].message if assistant_output.content is None: assistant_output.content = "" messages.append(assistant_output) i += 1print(f"第{i}轮大模型输出信息:{assistant_output}\n") print(f"最终答案:{assistant_output.content}") if __name__ == "__main__": call_with_messages()
程序缺陷定位Agent
Work flow
-
embedding模型:HuggingFace 嵌入模型,sentence-transformers/all-MiniLM-L6-v2,用于将文本转换为向量,进行检索增强生成(RAG)
-
向量数据库:FAISS
-
文本分割工具:LangChain的
RecursiveCharacterTextSplitter-
递归分割:
-
首先尝试用
\n\n分割,如果块仍大于chunk_size,则用\n分割,依此类推,直到所有块满足大小要求。 -
如果所有分隔符都无法满足,则强制按
chunk_size截断。
-
-
重叠机制:
-
每个块的末尾会包含下一个块的开头部分(
chunk_overlap字符),确保上下文连贯。
-
-
-
LLM客户端:OpenAI客户端,简化了 API 调用流程
-
使用buggy的slice从FAISS获取最相关的上下文
Real Agent
一、 主要组成部分
您可以将这个系统想象成一个由**“大脑”**、**“工具箱”**、**“专家团队”**和**“记忆系统”**组成的团队。
-
大脑 (The Brain) -
**IntelligentOrchestratorAgent**:-
角色: 总调度师,是整个系统的决策核心。
-
职责:
-
执行 **ReAct (思考-行动-观察) 循环**,驱动整个分析流程。
-
维护**工作记忆**,跟踪任务状态和已收集到的信息。
-
与大语言模型(LLM)交互,进行高层次的**思考**和**决策**。
-
根据LLM的决策,选择并调度“工具箱”中的工具。
-
-
-
工作记忆 (Working Memory) -
self.working_memory** 字典**:-
角色: Agent的短期记忆和草稿纸。
-
职责:
-
存储当前分析任务的所有上下文信息,包括初始输入、中间产物和最终结果。
-
为 ReAct 的每一步提供数据支持:为“思考”提供上下文,为“行动”提供参数。
-
-
-
工具箱 (The Toolbox) -
**tools.py**:-
角色: “大脑”可以使用的标准操作接口。
-
职责:
-
将“专家团队”的复杂能力封装成一个个标准化的工具(如
TraceAnalysisTool,SliceGenerationTool)。 -
定义每个工具的**名称、描述和参数**,虽然目前参数的使用是简化的。
-
由
ToolRegistry负责统一管理和执行。
-
-
-
专家团队 (The Specialists) -
agents/** 目录**:-
角色: 真正干活的专家,每个Agent负责一个特定的专业领域。
-
职责:
-
TraceAgent: 负责生成和最小化执行轨迹。 -
SliceAgent: 负责生成智能程序切片和摘要。 -
ContextAgent: 负责代码的索引、存储(向量数据库)和检索(RAG)。 -
AnalysisAgent: 负责调用LLM进行最终的、深入的根本原因分析。 -
ReportingAgent: 负责生成最终的分析报告。
-
-
-
数据模型 (The Language) -
**models.py**:-
角色: 系统内部统一的“沟通语言”。
-
职责: 定义了所有组件之间传递的数据结构,如
AnalysisRequest(任务请求),ExecutionTrace(执行轨迹),SummarizedSlice(摘要切片) 等,确保数据流的规范和一致。
-
二、 大致的工作链路 (ReAct流程)
整个链路是一个**动态的、由LLM驱动的迭代过程**,旨在逐步收集证据,最终定位问题根源。
起点: 用户发起一次分析请求,IntelligentOrchestratorAgent 开始工作。
ReAct 循环开始:
-
第一次迭代:建立认知 (代码索引)
-
🤔 思考: "我接到了一个新任务,但我对代码一无所知。首先,我需要建立对代码的整体认知。"
-
🎬 行动: LLM决策调用
codebase_indexing工具。系统执行CodebaseIndexingTool,该工具背后的ContextAgent会将代码解析、嵌入并存入向量数据库。 -
👀 观察: "代码库索引完成,现在我可以检索代码了。"
-
-
第二次迭代:发现异常 (轨迹分析)
-
🤔 思考: "代码我已经了解了。现在我要看看出错的程序在运行时到底发生了什么,它和正常版本有什么不同。"
-
🎬 行动: LLM决策调用
trace_analysis工具。TraceAgent会模拟运行两个版本的代码,生成并对比执行轨迹。 -
👀 观察: "轨迹分析完成。新旧版本在执行路径上有差异,旧版走了7步,新版走了9步。"
-
-
第三次迭代:聚焦关键 (智能切片)
-
🤔 思考: "执行轨迹太长太啰嗦了,我需要从这些差异中提取出最核心的逻辑变化。"
-
🎬 行动: LLM决策调用
slice_generation工具。SliceAgent会对轨迹进行对齐、切片和摘要,生成一份高度浓缩的“关键逻辑差异报告”。 -
👀 观察: "智能切片已生成,关键变量有2个。"
-
-
第四次迭代:关联上下文 (代码检索)
-
🤔 思考: "这个抽象的‘逻辑差异’很关键,但它具体对应到哪几行原始代码呢?我需要找到相关的代码上下文来理解它。"
-
🎬 行动: LLM决策调用
context_retrieval工具。ContextAgent根据智能切片的内容,从向量数据库中检索出最相关的原始代码片段。 -
👀 观察: "已找到10个相关的代码片段。"
-
-
第五次迭代:最终诊断 (根本原因分析)
-
🤔 思考: "证据收集完毕!我有执行轨迹差异、浓缩的智能切片、以及相关的代码上下文。现在是时候下结论了。"
-
🎬 行动: LLM决策调用
root_cause_analysis工具。AnalysisAgent将所有收集到的证据打包,形成一个超级详细的Prompt,最后一次请求LLM进行最终的根本原因分析。 -
👀 观察: "根本原因分析完成,置信度85%。"
-
终点: root_cause_analysis 完成后,is_task_complete() 返回 True,循环结束。ReportingAgent 根据最终分析结果生成一份详细的报告。

1479

被折叠的 条评论
为什么被折叠?



