【LangChain】LangChain 学习笔记

学习项目 Github 地址: github.com/huhaochi221...

智扫通 Agent 地址: github.com/huhaochi221...

LangChain 简介

LangChain 由 Harrison Chase 创建于2022年10月,它是围绕LLMs(大语言模型)建立的一个框架。

LangChain自身并不开发LLMs,它的核心理念是为各种LLMs实现通用的接口,把LLMs相关的组件"链接"在一起, 简化LLMs应用的开发难度,方便开发者快速地开发复杂的LLMs应用。

LangChain是后续学习RAG开发的主力框架

安装

本质上是 Python 的 SDK (第三方包)

一行命令: pip install langchain langchain-community langchain-ollama dashscope chromadb

  • langchain:核心包
  • langchain-community:社区支持包,提供了更多的第三方模型调用(我们用的阿里云干问模型就需要这个包)
  • langchain-ollama:0llama支持包,支持调用ollama托管部署的本地模型
  • dashscope:阿里云通义千问的Python SDK
  • chromadb:轻量向量数据库(后续使用)

RAG 介绍

通用的基础大模型存在一些问题:

  • LLM的知识不是实时的,模型训练好后不具备自动更新知识的能力,会导致部分信息滞后
  • LLM领域知识是缺乏的,大模型的知识来源于训练数据,这些数据主要来自公开的互联网和开源数据集,无法覆盖特定领域或高度专业化的内部知识
  • 幻觉问题,LLM有时会在回答中生成看似合理但实际上是错误的信息
  • 数据安全性

RAG (Retrieval-Augmented Generation)即检索增强生成,为大模型提供了从特定数据源检索到的信息,以此来修正和补充生成的答案。可以总结为一个公式:RAG = 检索技术 + LLM 提示。

向量的基础概念

RAG流程中,向量库是一个重要的节点。

  • 离线流程:知识和信息→向量嵌入(向量化)→ 存入向量库
  • 在线流程:用户的提问→向量嵌入(向量化)→ 在向量库中匹配

向量(Vector)就是文本的"数学身份证":它把一段文字的语义信息 ,转换成一串固定长度的数字列表,让计算机能"看懂"文字的含义并做相似度计算。

在向量匹配的过程中,如何识别2段文本是否表述相似的含义,主要可以通过如余弦相似度等算法来完成 比如(下列案例中向量为示例,仅描述概念,非真实向量):

如text-embedding-v1模型,可以生成1536维的向量(一段文本固定得到1536个数字序列),比较实用。 1536个数字表示,这段文本在1536个主题(抽象的语义特征)方向上的得分(强度)。

余弦相似度

向量的数字序列,共同决定了向量在高维空间中的方向和长度.而余弦相似度主要就是撇除长度的影响,得到方向的夹角。夹角越小越相似,即方向相同。

LangChainRAG 组件

LangChain目前支持三种类型的模型:LLMs(大语言模型)、Chat ModeLs(聊天模型)、Embeddings ModeLs(嵌入模型)。

  • LLMs:是技术范畴的统称,指基于大参数量、海量文本训练的 Transformer 架构模型,核心能力是理解和生成自然语言,主要服务于文本生成场景
  • 聊天模型:是应用范畴的细分,是专为对话场景优化的 LLMs,核心能力是模拟人类对话的轮次交互,主要服务于聊天场景
  • 文本嵌入模型:文本嵌入模型接收文本作为输入,得到文本的向量。

LangChain支持的三类模型,它们的使用场景不同,输入和输出不同,开发者需要根据项目需要选择相应。

LangChain 访问大模型

Python 复制代码
from langchain_community.llms.tongyi import Tongyi

model = Tongyi(model="qwen-max")

# 调用invoke向模型提问

res =model.invoke(input="你是谁?")
print(res)

模型的流式输出

Python 复制代码
from langchain_community.llms.tongyi import Tongyi

model = Tongyi(model="qwen-max")

# 调用invoke向模型提问

res =model.stream(input="你是谁?")

for chunk in res:
    print(chunk,end="", flush=True)

LangChain 调用聊天模型

Python 复制代码
from langchain_community.chat_models.tongyi import ChatTongyi
from langchain_core.messages import HumanMessage, SystemMessage, AIMessage

model = ChatTongyi(model="qwen3-max", streaming=True)

messages = [
    SystemMessage(content="你是李白"),
    HumanMessage(content="给我写一首唐诗"),
    AIMessage(content="""
    《月下独酌·其二》
    
    金樽对月饮长空,
    万里江天一色同。
    醉踏青崖云作伴,
    笑邀白鹿访仙翁。
    星垂大野浮银汉,
    浪卷千峰入玉虹。
    莫问明朝何处去,
    且将肝胆照苍穹!
    """),
    HumanMessage(content="就按你上一个回复的格式,输出诗句")
]

res = model.stream(input=messages)

for chunk in res:
    print(chunk.content, end="", flush=True)

LangChain 的消息简写

Python 复制代码
from langchain_community.chat_models.tongyi import ChatTongyi
from langchain_core.messages import HumanMessage, SystemMessage, AIMessage

model = ChatTongyi(model="qwen3-max", streaming=True)

messages = [
    ("system", "你是李白"),
    ("human", "给我写一首唐诗"),
    ("ai", """
    《月下独酌·其二》

    金樽对月饮长空,
    万里江天一色同。
    醉踏青崖云作伴,
    笑邀白鹿访仙翁。
    星垂大野浮银汉,
    浪卷千峰入玉虹。
    莫问明朝何处去,
    且将肝胆照苍穹!
    """ ),
    ("human", "就按你上一个回复的格式,输出诗句")
]

res = model.stream(input=messages)

for chunk in res:
    print(chunk.content, end="", flush=True)

LangChain 调用嵌入模型

Embeddings ModeLs嵌入模型的特点:将字符串作为输入,返回一个浮点数的列表(向量)。 在NLP中,Embedding的作用就是将数据进行文本向量化

Python 复制代码
from langchain_community.embeddings import DashScopeEmbeddings

model = DashScopeEmbeddings()

print(model.embed_query("我喜欢你"))

print(model.embed_documents(["我喜欢你", "我稀饭你", "晚上吃啥"]))

LangChain 通用提示词模板

Python 复制代码
from langchain_core.prompts import PromptTemplate
from langchain_community.llms.tongyi import Tongyi
# zero-shot
prompt_template = PromptTemplate.from_template(
    "我的邻居姓{lastname},刚生了{gender},你帮我取个名字,简单回答"
)
model = Tongyi(model="qwen-max")
# 调用format方法
# prompt_text = prompt_template.format(lastname="胡",gender="男孩")
# print(prompt_text)

# res = model.invoke(input=prompt_text)
# print(res)

# 构建执行链条的方法

chain = prompt_template | model

res = chain.invoke(input={"lastname": "胡", "gender": "女儿"})
print(res)

FewShot 提示词模板

ini 复制代码
from langchain_community.llms.tongyi import Tongyi
from langchain_core.prompts import FewShotPromptTemplate, PromptTemplate

# 示例模板
example_template = PromptTemplate.from_template("单词:{word}, 反义词:{antonym}")
# 示例的数据
example_data = [
    {"word": "大", "antonym": "小"},
    {"word": "高", "antonym": "低"}
]


few_shot_template = FewShotPromptTemplate(
    example_prompt=example_template,   # 示例数据的模板
    examples=example_data,         # 示例数据(动态注入),list内嵌字典
    prefix="告诉我单词的反义词,我提供如下示例:",           # 示例之前的提示词
    suffix="基于前面的示例告诉我:{input_word}的反义词是?",           # 示例之后的提示词
    input_variables=['input_word']     # 声明在前缀或后缀中需要注入的变量名
)

prompt_text =  few_shot_template.invoke(input={"input_word": "左"}).to_string()

print(prompt_text)

model = Tongyi(model="qwen-max")
print(model.invoke(input=prompt_text))

模板类的 format 和 invoke 方法

Python 复制代码
from langchain_core.prompts import PromptTemplate

template = PromptTemplate.from_template("我的邻居是:{name}, 最喜欢:{hobby}")

res = template.format(name="狗蛋", hobby="看电影")
print(res,type(res))

res = template.invoke({"name": "乔奕磊", "hobby": "旅游"})
print(res,type(res))

输出

arduino 复制代码
我的邻居是:狗蛋, 最喜欢:看电影 <class 'str'>
text='我的邻居是:乔奕磊, 最喜欢:旅游' <class 'langchain_core.prompt_values.StringPromptValue'>

ChatPromptTemplate 的使用

PromptTemplate:通用提示词模板,支持动态注入信息。 FewShotPromptTempLate:支持基于模板注入任意数量的示例信息。 ChatPromptTemplate:支持注入任意数量的历史会话信息。

Python 复制代码
from langchain_core.prompts import ChatPromptTemplate, MessagesPlaceholder
from langchain_deepseek import ChatDeepSeek

chat_prompt_template = ChatPromptTemplate.from_messages(
    [
        ("system","你是一个边塞诗人,可以作诗"),
        MessagesPlaceholder("history"),
        ("human","请再来一首唐诗")
    ]
)

history_data = [
    ("human","你来写一首唐诗"),
    ("system", "床前明月光,疑是地上霜.举头望明月,低头思故乡.")
]

#StringPrompt Value
prompt_text = chat_prompt_template.invoke({"history": history_data}).to_string()
print(prompt_text)

model = ChatDeepSeek(model="deepseek-v4-flash")
res = model.invoke(prompt_text)
print(res.content)

chain 的基础使用

「将组件串联,上一个组件的输出作为下一个组件的输入!是LangChain链(尤其是丨管道链)的核心工作 原理,这也是链式调用的核心价值:实现数据的自动化流转与组件的协同工作,如下。

chain = prompt_template l model 核心前提:即Runnable子类对象才能入链(以及callable、Mapping接口子类对象也可加入(后续了解用的不多))。

我们目前所学习到的组件,均是Runnable接口的子类,如下类的继承关系:

组成的链在执行上有:上一个组件的输出作为下一个组件的输入的特性。

所以有如下执行流程:

简单理解 Runnable 接口

LangChain 中的绝大多数核心组件都继承了 Runnable 抽象基类(位于 langchain_core.runnables.base)。 代码:

chain = prompt | model

chain变量是RunnableSequence (RunnableSerializable子类) 类型

而得到这个类型的原因就是Runnable基类内部对__or__魔术方法的改写。

同时,在后面继续使用「添加新的组件,依旧会得到RunnableSequence,这就是链的基础架构.

StrOutputParser 字符串输出解析器

StrOutputParser是LangChain内置的简单字符串解析器

  • 可以将AIMessage解析为简单的字符串,符合了模型invoke方法要求(可传入字符串,不接收 AIMessage类型)
  • 是Runnable接口的子类(可以加入链)
Python 复制代码
chain = prompt | model | model

会报错,错误的主要原因是:

  • prompt的结果是PromptValue类型,输入给了model
  • modeL的输出结果是AIMessage

所以需要做类型转换,可以借助LangChain 内置的解析器

Python 复制代码
parser = StroutputParser()
chain = prompt | model 丨 parser 丨 model

JsonOutputParser&多模型执行链

在前面我们完成了这样的需求去构建多模型链,不过这种做法并不标准,因为:

上一个模型的输出,没有被处理就输入下一个模型。

正常情况下我们应该有如下处理逻辑:

我们要把 AIMessage 对象转化为dict 对象,StrOutputParser就不能使用了,这时候就要用到 JsonOutputParser

Python 复制代码
from langchain_core.output_parsers import JsonOutputParser
from langchain_deepseek.chat_models import ChatDeepSeek
from langchain_core.prompts import PromptTemplate

json_paser = JsonOutputParser()

model = ChatDeepSeek(model="deepseek-v4-flash")

first_prompt = PromptTemplate.from_template(
    "我邻居姓:{lastname},刚生了{gender},请帮忙起名字,并封装为 JSON 格式。要求key是name,value是你你起的名字"
)
second_prompt = PromptTemplate.from_template(
    "姓名:{name},请帮我解析含义。"
)

chain = first_prompt | model | json_paser | second_prompt | model

for chunk in chain.stream({"lastname": "王", "gender": "男"}):
    print(chunk.content, end="", flush=True)

自定义函数加入链

前文我们根据JsonoutputParser完成了多模型执行链条的构建。

除了JsonOutputParser这类固定功能的解析器之外我们也可以自己编写Lambda匿名函数来完成自定义逻辑的数据转换,想怎么转换就怎么转换,更自由。

想要完成这个功能,可以基于RunnableLambda类实现。

RunnableLambda类是LangChain内置的,将普通函数等转换为Runnable接口实例,方便自定义函数加入chain。 语法:

RunnabLeLambda(函数对象或Lambda匿名函数)

Python 复制代码
from langchain_core.output_parsers import JsonOutputParser, StrOutputParser
from langchain_core.runnables import  RunnableLambda
from langchain_deepseek.chat_models import ChatDeepSeek
from langchain_core.prompts import PromptTemplate

json_paser = JsonOutputParser()

model = ChatDeepSeek(model="deepseek-v4-flash")

str_parser = StrOutputParser()

first_prompt = PromptTemplate.from_template(
    "我邻居姓:{lastname},刚生了{gender},请帮忙起名字,并封装为 JSON 格式。要求仅输出姓名,不要其他内容"
)
second_prompt = PromptTemplate.from_template(
    "姓名:{name},请帮我解析含义。"
)

my_func = RunnableLambda(lambda ai_msg : {"name": ai_msg.content})

chain = first_prompt | model | my_func | second_prompt | model | str_parser

for chunk in chain.stream({"lastname": "王", "gender": "男"}):
    print(chunk, end="", flush=True)

Memory 临时会话记忆

如果想要封装历史记录,除了自行维护历史消息外,也可以借助LangChain内置的历史记录附加功能。

LangChain提供了History功能,帮助模型在有历史记忆的情况下回答。

  • 基于RunnableWithMessageHistory在原有链的基础上创建带有历史记录功能的新链(新Runnable实例)
  • 基于InMemoryChatMessageHistory为历史记录提供内存存储(临时用)
Python 复制代码
from langchain_core.chat_history import InMemoryChatMessageHistory
from langchain_core.output_parsers import StrOutputParser
from langchain_core.prompts import PromptTemplate
from langchain_core.runnables import RunnableWithMessageHistory
from langchain_deepseek import ChatDeepSeek

model = ChatDeepSeek(model="deepseek-v4-flash")
prompt = PromptTemplate.from_template(
    "你需要根据会话历史回应用户问题。对话历史:{chat_history},用户提问:{input},请回答"
)

str_parser = StrOutputParser()

base_chain = prompt | model | str_parser

store = {}
# 实现通过会话 id 获取 InMemoryChatMessageHistory 类对象
def get_history(session_id):
    if session_id not in store:
        store[session_id] = InMemoryChatMessageHistory()

    return store[session_id]

# 创建一个新的链,对原有的链增强功能:自动附加历史消息

conversion_chain = RunnableWithMessageHistory(
    base_chain,     # 原有链
    get_history,    # 通过会话id获取历史消息
    input_messages_key="input",     #表示用户输入在模板中的占位符
    history_messages_key="chat_history"
)

if __name__ == '__main__':
    session_config = {
        "configurable": {
            "session_id": "user_001"
        }
    }

    res = conversion_chain.invoke({"input": "这里有一只猫"}, session_config)
    print("第1次执行:", res )

    res = conversion_chain.invoke({"input": "这里有两只狗"}, session_config)
    print("第2次执行:", res)

    res = conversion_chain.invoke({"input": "这里有多少个宠物"}, session_config)
    print("第3次执行:", res)

Memory 长期会话

使用InMemoryChatMessageHistory仅可以在内存中临时存储会话记忆,一旦程序退出,则记忆丢失。 InMemoryChatMessageHistory 类继承自 BaseChatMessageHistory

我们可以自行实现一个基于Json格式和本地文件的会话数据保存

FileChatMessageHistory类实现,核心思路:

  • 基于文件存储会话记录,以session_id为文件名,不同session_id有不同文件存储消息

继承BaseChatMessageHistory实现如下3个方法:

  • add_messages:同步模式,添加消息
  • messages:同步模式,获取消息
  • clear:同步模式,清除消息
Python 复制代码
import os, json
from typing import Sequence

from langchain_core.chat_history import BaseChatMessageHistory
from langchain_core.messages import message_to_dict, messages_from_dict, BaseMessage
from langchain_core.chat_history import InMemoryChatMessageHistory
from langchain_core.output_parsers import StrOutputParser
from langchain_core.prompts import PromptTemplate
from langchain_core.runnables import RunnableWithMessageHistory
from langchain_deepseek import ChatDeepSeek


# message_to_dict: 将message对象转换为字典
# message_from_dict: 将字典转换为message对象

class FileChatMessageHistory(BaseChatMessageHistory):
    def __init__(self,session_id, storage_path):
        self.session_id = session_id    # 会话id
        self.storage_path = storage_path    # 不同会话id的存储文件路径
        self.file_path = os.path.join(self.storage_path, session_id)

        os.makedirs(os.path.dirname(self.file_path), exist_ok=True)

    def add_messages(self, messages: Sequence[BaseMessage]) -> None:
        all_messages = list(self.messages)      # 已有的消息列表
        all_messages.extend(messages)            # 新的和已有的融合成一个list

        # 将数据同步写入本地文件
        # 类对象写入文件 -> 一堆二进制
        # 为了方便, 可以将BaseMessage消息转换为字典(借助JSON模块以json字符串写入文件)
        new_messages = []
        for message in all_messages:
            d = message_to_dict(message)
            new_messages.append(d)
        # 将数据写入文件
        with open(self.file_path, "w", encoding="utf-8") as f:
            json.dump(new_messages, f)

    @property #@property 装饰器将message方法变成成员属性用
    def messages(self) -> list[BaseMessage]:
        try:
            with open(self.file_path, "r", encoding="utf-8") as f:
                messages_data = json.load(f)    # 返回值就是:list[字典]
                return messages_from_dict(messages_data)

        except FileNotFoundError:
            return []

    def clear(self) -> None:
        with open(self.file_path, "w", encoding="utf-8") as f:
            json.dump([], f)




model = ChatDeepSeek(model="deepseek-v4-flash")
prompt = PromptTemplate.from_template(
    "你需要根据会话历史回应用户问题。对话历史:{chat_history},用户提问:{input},请回答"
)

str_parser = StrOutputParser()

base_chain = prompt | model | str_parser

store = {}
# 实现通过会话 id 获取 InMemoryChatMessageHistory 类对象
def get_history(session_id):
    return FileChatMessageHistory(session_id, "./chat_history")


# 创建一个新的链,对原有的链增强功能:自动附加历史消息

conversion_chain = RunnableWithMessageHistory(
    base_chain,     # 原有链
    get_history,    # 通过会话id获取历史消息
    input_messages_key="input",     #表示用户输入在模板中的占位符
    history_messages_key="chat_history"
)

if __name__ == '__main__':
    session_config = {
        "configurable": {
            "session_id": "user_001"
        }
    }

    # res = conversion_chain.invoke({"input": "这里有一只猫"}, session_config)
    # print("第1次执行:", res )
    #
    # res = conversion_chain.invoke({"input": "这里有两只狗"}, session_config)
    # print("第2次执行:", res)

    res = conversion_chain.invoke({"input": "这里有多少个宠物"}, session_config)
    print("第3次执行:", res)

CSVLoader 的使用

Python 复制代码
from langchain_community.document_loaders import CSVLoader

loader = CSVLoader(
    file_path="./data/stu.csv",
    csv_args={
        "delimiter": ",",    # 指定分隔符
        "quotechar": '"',    # 指定带有分隔符文本的引号包围是单引号还是双引号
        # 如果数据有表头,就不要下方代码
        "fieldnames": ['a','b','c','d']
    },
    encoding="utf-8"
)

documents = loader.load()

print(documents)

# 懒加载
for document in loader.lazy_load():
    print(document)

JSONLoader

JSONLoader用于将JSON数据加载为Document类型对象。 使用JSONLoader需要额外安装:pip install jq

Python 复制代码
from langchain_community.document_loaders import JSONLoader
loader = JSONLoader(
    file_path="./data/stu_json_lines.json",
    jq_schema=".name",
    text_content=False,
    json_lines=True
)

document =  loader.load()
print(document)
JSON 复制代码
{"name": "周杰伦", "age": 11, "gender": "男"}
{"name": "蔡依林", "age": 12, "gender": "女"}
{"name": "王力鸿", "age": 11, "gender": "男"}

TextLoader 和文档分割器

Python 复制代码
from langchain_community.document_loaders import TextLoader
from langchain_text_splitters import RecursiveCharacterTextSplitter

loader = TextLoader("./data/我的学习笔记.txt", encoding="utf-8")

docs = loader.load()

splitter = RecursiveCharacterTextSplitter(
    chunk_size=500,     # 片段长度
    chunk_overlap=50,   # 片段重叠长度
    # 文本自然段落分隔的依据符号
    separators=["\n\n", "\n", " ", "", "", "。", "?", "!", ",", "、"],
    length_function=len
)

split_docs = splitter.split_documents(docs)
print(len(split_docs))

for doc in split_docs:
    print(doc)
    print("=" * 80)

PyPDFLoader

LangChain内支持许多PDF的加载器,我们选择其中的PyPDFLoader使用。

PyPDFLoader加载器,依赖PyPDF库,所以,需要安装它:pip install pypdf

Python 复制代码
from langchain_community.document_loaders import PyPDFLoader

loader = PyPDFLoader(
    file_path="./data/咏鹅.pdf",
    mode="single",   # single 只返回一个Document对象
    password="123456"
)
i = 0
for doc in loader.lazy_load():
    i += 1
    print(doc)
    print("=" * 80)

Vector Store 向量存储

基于LangChain的向量存储,存储嵌入数据,并执行相似性搜索。

1. 内存向量存储

Python 复制代码
from langchain_core.vectorstores import InMemoryVectorStore
from langchain_community.embeddings import DashScopeEmbeddings
from langchain_community.document_loaders import CSVLoader


vector_store = InMemoryVectorStore(
    embedding=DashScopeEmbeddings()
)

loader = CSVLoader(
    file_path="./data/info.csv",
    encoding="utf-8",
    source_column="source",     # 指定本条数据来源是哪路
)

documents = loader.load()

print(documents[1])

# 向量存储的新增,删除,检索

vector_store.add_documents(
    documents=documents,    # 添加的文档, 类型为list[Document]
    ids=["id" + str(i) for i in range(1, len(documents) + 1)]   # 给添加的文档提供id
)

vector_store.delete(["id1", "id2"])

result = vector_store.similarity_search(
    "Python是不是简单易学",
    3
)

print(result)

2.外部向量持久化存储

Python 复制代码
from langchain_chroma import Chroma
from langchain_community.embeddings import DashScopeEmbeddings
from langchain_community.document_loaders import CSVLoader

# Chroma 轻量级向量数据库
# 下载LangChain-chroma chromaDB


vector_store = Chroma(
    collection_name="test",     # 表名
    embedding_function=DashScopeEmbeddings(),   # 嵌入模型
    persist_directory="./chroma_db"     # 文件夹
)

# loader = CSVLoader(
#     file_path="./data/info.csv",
#     encoding="utf-8",
#     source_column="source",     # 指定本条数据来源是哪路
# )
#
# documents = loader.load()
#
# print(documents[1])
#
# # 向量存储的新增,删除,检索
#
# vector_store.add_documents(
#     documents=documents,    # 添加的文档, 类型为list[Document]
#     ids=["id" + str(i) for i in range(1, len(documents) + 1)]   # 给添加的文档提供id
# )
#
# vector_store.delete(["id1", "id2"])

result = vector_store.similarity_search(
    "Python是不是简单易学",
    3,
    filter={"source": "黑马程序员"}
)

print(result)

基于向量检索构建提示词

Python 复制代码
from langchain_community.embeddings import DashScopeEmbeddings
from langchain_core.output_parsers import StrOutputParser
from langchain_core.prompts import ChatPromptTemplate
from langchain_core.vectorstores import InMemoryVectorStore
from langchain_deepseek import ChatDeepSeek

model = ChatDeepSeek(model="deepseek-v4-flash")
prompt = ChatPromptTemplate.from_messages(
    [
        ("system", "以我提供的已知参考资料为主,简洁和专业的回答用户问题。参考资料:{context}。"),
        ("user", "用户提问:{input}")
    ]
)

vector_store = InMemoryVectorStore(embedding=DashScopeEmbeddings(model="text-embedding-v1"))

vector_store.add_texts(["减肥就是要少吃多练","在减脂期间吃东西很重要,清淡少油控制卡路里摄入并运动起来","跑步是很好的运动哦"])

input_text = "怎么减肥?"

result = vector_store.similarity_search(input_text, 2)

reference_text = "["

for doc in result:
    reference_text += doc.page_content
reference_text += "]"

print(reference_text)

def print_prompt(prompt):
    print(prompt.to_string())
    print("=" * 20)
    return prompt

chain = prompt | print_prompt | model | StrOutputParser()
res = chain.invoke({"input": input_text, "context": reference_text})
print(res)

RunnablePassThrought 的使用

向量检索加入链

Python 复制代码
from langchain_community.embeddings import DashScopeEmbeddings
from langchain_core.documents import Document
from langchain_core.output_parsers import StrOutputParser
from langchain_core.prompts import ChatPromptTemplate
from langchain_core.runnables import RunnablePassthrough
from langchain_core.vectorstores import InMemoryVectorStore
from langchain_deepseek import ChatDeepSeek


def print_prompt(prompt):
    print(prompt.to_string())
    print("=" * 20)
    return prompt

model = ChatDeepSeek(model="deepseek-v4-flash")
prompt = ChatPromptTemplate.from_messages(
    [
        ("system", "以我提供的已知参考资料为主,简洁和专业的回答用户问题。参考资料:{context}。"),
        ("user", "用户提问:{input}")
    ]
)

vector_store = InMemoryVectorStore(embedding=DashScopeEmbeddings(model="text-embedding-v1"))

vector_store.add_texts(["减肥就是要少吃多练","在减脂期间吃东西很重要,清淡少油控制卡路里摄入并运动起来","跑步是很好的运动哦"])

input_text = "怎么减肥?"


retriever = vector_store.as_retriever(search_kwargs={"k": 2})

def format_func(docs: list[Document]):
    if not docs:
        return "无相关参考资料"
    formatted_str = "["
    for doc in docs:
        formatted_str += doc.page_content
    formatted_str += "]"

    return formatted_str


chain = (
    {"input": RunnablePassthrough(), "context": retriever | format_func} | prompt | print_prompt | model | StrOutputParser()
)

res = chain.invoke(input_text)
print(res)

RAG 项目

RAG即检索、增强和生成,其主要分为2条线:

  1. 离线处理:向私有知识库(向量存储)源源不断添加私有知识文档
  • 向知识库添加来自未来的知识文档(基于模型训练完成时间)
  • 向模型添加私有知识文档
  • 给出模型参考资料,规避模型幻觉(一本正经的胡说八道)
  1. 在线处理:用户提问会先基于私有知识库做检索,获取参考资料,同步组装新提示词询问大模型获取结果。

本次项目以"某东商品衣服"为例,以衣服属性构建本地知识。使用者可以自由更新本地知识,用户问题的答案也是基于本地知识生成的。

文本上传 WEB 服务

使用 streamlit 包快速构建简单的 web 服务

Python 复制代码
import streamlit as st

st.title("知识库更新服务")

uploader_file = st.file_uploader(
    "请上传txt文件",
     type=["txt"],
    accept_multiple_files=False
)

if uploader_file is not None:
    file_name = uploader_file.name
    file_type = uploader_file.type
    file_size = uploader_file.size / 1024


    st.subheader(f"文件名: {file_name}")
    st.write(f"格式: {file_type} | 大小 {file_size:.2f}KB")

    # get_value -> byte -> decode('utf-8')
    text = uploader_file.getvalue().decode('utf-8')
    st.write(text)

md5 工具函数开发

Python 复制代码
import os
import config_data as config
import hashlib

def check_md5(md5_str: str):
    """检查传入的MD5字符是否已经被传入过了
        return False表示md5未处理过  True表示md5已处理过
    """
    if not os.path.exists(config.md5_path):
        # if 进入表示文件不存在
        open(config.md5_path, 'w', encoding="utf-8").close()
        return False
    else:
        for line in open(config.md5_path, 'r', encoding="utf-8").readlines():
            line = line.strip()     # 处理字符串前后的空格和回车
            if line == md5_str:
                return True

        return False


def save_md5(md5_str: str):
    """保存传入的MD5字符"""
    with open(config.md5_path, "a", encoding="utf-8") as f:
        f.write(md5_str + '\n')

def get_string_md5(input_str: str, encoding="utf-8"):
    """获取传入的字符串的MD5"""
    # 将字符串转换为byte字节数组
    str_bytes = input_str.encode(encoding= encoding)

    # 创建MD5对象
    md5_obj = hashlib.md5()
    md5_obj.update(str_bytes)
    return md5_obj.hexdigest()


class KnowledgeBaseService(object):
    def __init__(self):
        self.chroma = None
        self.spliter = None

    def upload_by_str(self, data, filename):
        """将传入的字符串进行向量化,存入向量数据库中"""
        pass

if __name__ == '__main__':
    # save_md5("7a8941058aaf4df5147042ce104568da")
    print(check_md5("7a8941058aaf4df5147042ce104568da"))

知识库更新服务

Python 复制代码
import os

from langchain_community.embeddings import DashScopeEmbeddings
from langchain_text_splitters import RecursiveCharacterTextSplitter
from datetime import datetime
import config_data as config
import hashlib
from langchain_chroma import Chroma

def check_md5(md5_str: str):
    """检查传入的MD5字符是否已经被传入过了
        return False表示md5未处理过  True表示md5已处理过
    """
    if not os.path.exists(config.md5_path):
        # if 进入表示文件不存在
        open(config.md5_path, 'w', encoding="utf-8").close()
        return False
    else:
        for line in open(config.md5_path, 'r', encoding="utf-8").readlines():
            line = line.strip()     # 处理字符串前后的空格和回车
            if line == md5_str:
                return True

        return False


def save_md5(md5_str: str):
    """保存传入的MD5字符"""
    with open(config.md5_path, "a", encoding="utf-8") as f:
        f.write(md5_str + '\n')

def get_string_md5(input_str: str, encoding="utf-8"):
    """获取传入的字符串的MD5"""
    # 将字符串转换为byte字节数组
    str_bytes = input_str.encode(encoding= encoding)

    # 创建MD5对象
    md5_obj = hashlib.md5()
    md5_obj.update(str_bytes)
    return md5_obj.hexdigest()


class KnowledgeBaseService(object):
    def __init__(self):
        # 创建保存向量数据库的目录,已存在则跳过
        os.makedirs(config.persist_directory, exist_ok=True)

        self.chroma = Chroma(
            collection_name=config.collection_name,     # 表名
            embedding_function=DashScopeEmbeddings(model="text-embedding-v1"),
            persist_directory=config.persist_directory  # 指定向量数据库的保存目录
        )
        self.spliter = RecursiveCharacterTextSplitter(
            chunk_size=config.chunk_size,   # 块大小
            chunk_overlap=config.chunk_overlap, # 块重叠
            separators=config.separators,   # 文本分隔符
            length_function=len             # 获取文本长度的函数
        )

    def upload_by_str(self, data: str, filename):
        """将传入的字符串进行向量化,存入向量数据库中"""
        md5_hax = get_string_md5(data)

        if check_md5(md5_hax):
            return "[跳过]数据已处理过"


        if len(data) > config.max_splite_char_number:
            knowledge_chunks: list[str] = self.spliter.split_text(data)

        else:
            knowledge_chunks = [data]

        metadata = {
            "source": filename,
            "create_time": datetime.now().strftime("%Y-%m-%d %H:%M:%S"),
            "operator": "hhc",
        }

        self.chroma.add_texts(
            knowledge_chunks,
            metadatas=[metadata for _ in knowledge_chunks]
        )

        save_md5(md5_hax)

        return "[成功]数据处理完成"





if __name__ == '__main__':
    service = KnowledgeBaseService()
    r = service.upload_by_str("周杰伦", "textfile")
    print(r)

完成离线流程开发

python 复制代码
"""
Streamlit: 当WEB页面元素改变时, 会自动触发页面更新
"""
import time

import streamlit as st
from knowledge_base import KnowledgeBaseService

st.title("知识库更新服务")

uploader_file = st.file_uploader(
    "请上传txt文件",
     type=["txt"],
    accept_multiple_files=False
)
service = KnowledgeBaseService()

if "service" not in st.session_state:
    st.session_state["service"] = KnowledgeBaseService()

if uploader_file is not None:
    file_name = uploader_file.name
    file_type = uploader_file.type
    file_size = uploader_file.size / 1024


    st.subheader(f"文件名: {file_name}")
    st.write(f"格式: {file_type} | 大小 {file_size:.2f}KB")

    # get_value -> byte -> decode('utf-8')
    text = uploader_file.getvalue().decode('utf-8')

    with st.spinner("正在处理数据..."):
        time.sleep(1)
        result = st.session_state["service"].upload_by_str(text, file_name)
        st.write(result)

在线流程开发

Python 复制代码
from re import search

from langchain_chroma import Chroma
from langchain_community.embeddings import DashScopeEmbeddings

import config_data as config

class VectorStoreService(object):

    def __init__(self, embedding):

        self.embedding = embedding

        self.vector_store = Chroma(
            collection_name=config.collection_name,
            embedding_function=embedding,
            persist_directory=config.persist_directory,

        )

    def get_retriever(self):
        return self.vector_store.as_retriever(search_kwargs={"k": config.similarity_threshold})


if __name__ == '__main__':
    service = VectorStoreService(DashScopeEmbeddings(model="text-embedding-v1"))
    retriever = service.get_retriever()

    result = retriever.invoke("我的体重150斤,尺码推荐")
    print(result)

rag 核心服务开发

Python 复制代码
from langchain_community.embeddings import DashScopeEmbeddings
from langchain_core.documents import Document
from langchain_core.output_parsers import StrOutputParser
from langchain_core.prompts import ChatPromptTemplate, format_document
from langchain_core.runnables import RunnablePassthrough
from langchain_deepseek import ChatDeepSeek

import config_data as config

from vector_store import VectorStoreService

def print_prompt(prompt):
    print(prompt.to_string())
    print("=" * 20)
    return prompt


class RagService(object):
    def __init__(self):
        self.vector_service = VectorStoreService(
            embedding=DashScopeEmbeddings(model=config.embeddings_model_name)
        )
        self.prompt_template = ChatPromptTemplate(
            [
                ("system", "以我提供的已知参考资料为主,简洁和专业的回答用户问题。参考资料:{context}。"),
                ("user", "请回答用户提问: {input}")
            ]
        )
        self.chat_model = ChatDeepSeek(model=config.chat_model_name)
        self.chain = self.__get_chain()

    def __get_chain(self):
        retriever = self.vector_service.get_retriever()

        def format_document(docs: list[Document]):
            if not docs:
                return "无相关参考资料"
            formatted_str = ""

            for doc in docs:
                formatted_str += f"文档片段:{doc.page_content}\n文档元数据:{doc.metadata}\n\n"
            return formatted_str

        chain = (
                {
                    "input": RunnablePassthrough(),
                    "context": retriever | format_document
                } | self.prompt_template | print_prompt | self.chat_model | StrOutputParser()
        )

        return chain


if __name__ == '__main__':
    res = RagService().chain.invoke("我的体重150斤,尺码推荐")
    print(res)

历史会话记录功能

Python 复制代码
import json
import os
from typing import Sequence

from langchain_core.chat_history import BaseChatMessageHistory
from langchain_core.messages import BaseMessage, message_to_dict, messages_from_dict

def get_history(session_id):
    return FileChatMessageHistory(session_id, "./chat_history")


class FileChatMessageHistory(BaseChatMessageHistory):
    def __init__(self,session_id, storage_path):
        self.session_id = session_id    # 会话id
        self.storage_path = storage_path    # 不同会话id的存储文件路径
        self.file_path = os.path.join(self.storage_path, session_id)

        os.makedirs(os.path.dirname(self.file_path), exist_ok=True)

    def add_messages(self, messages: Sequence[BaseMessage]) -> None:
        all_messages = list(self.messages)      # 已有的消息列表
        all_messages.extend(messages)            # 新的和已有的融合成一个list

        # 将数据同步写入本地文件
        # 类对象写入文件 -> 一堆二进制
        # 为了方便, 可以将BaseMessage消息转换为字典(借助JSON模块以json字符串写入文件)
        new_messages = []
        for message in all_messages:
            d = message_to_dict(message)
            new_messages.append(d)
        # 将数据写入文件
        with open(self.file_path, "w", encoding="utf-8") as f:
            json.dump(new_messages, f)

    @property #@property 装饰器将message方法变成成员属性用
    def messages(self) -> list[BaseMessage]:
        try:
            with open(self.file_path, "r", encoding="utf-8") as f:
                messages_data = json.load(f)    # 返回值就是:list[字典]
                return messages_from_dict(messages_data)

        except FileNotFoundError:
            return []

    def clear(self) -> None:
        with open(self.file_path, "w", encoding="utf-8") as f:
            json.dump([], f)

这个 chain 太狗屎了, 第48行开始, 全是为了拼成链写了很多冗余代码

Python 复制代码
from langchain_community.embeddings import DashScopeEmbeddings
from langchain_core.documents import Document
from langchain_core.output_parsers import StrOutputParser
from langchain_core.prompts import ChatPromptTemplate, format_document, MessagesPlaceholder
from langchain_core.runnables import RunnablePassthrough, RunnableWithMessageHistory, RunnableLambda
from langchain_deepseek import ChatDeepSeek

import config_data as config
from RAG项目案例.file_history_store import get_history

from vector_store import VectorStoreService


def print_prompt(prompt):
    print(prompt.to_string())
    print("=" * 20)
    return prompt


class RagService(object):
    def __init__(self):
        self.vector_service = VectorStoreService(
            embedding=DashScopeEmbeddings(model=config.embeddings_model_name)
        )
        self.prompt_template = ChatPromptTemplate(
            [
                ("system", "以我提供的已知参考资料为主,简洁和专业的回答用户问题。参考资料:{context}。"),
                ("system", "并且我提供用户的对话历史记录,如下"),
                MessagesPlaceholder("history"),
                ("user", "请回答用户提问: {input}")
            ]
        )
        self.chat_model = ChatDeepSeek(model=config.chat_model_name)
        self.chain = self.__get_chain()

    def __get_chain(self):
        retriever = self.vector_service.get_retriever()

        def format_document(docs: list[Document]):
            if not docs:
                return "无相关参考资料"
            formatted_str = ""

            for doc in docs:
                formatted_str += f"文档片段:{doc.page_content}\n文档元数据:{doc.metadata}\n\n"
            return formatted_str

        def temp1(value: dict) -> str:
            return value["input"]

        def temp2(value):
            # {input, context, history}
            new_value = {}
            new_value["input"] = value["input"]["input"]
            new_value["context"] = value["context"]
            new_value["history"] = value["input"]["history"]
            return new_value

        chain = (
                {
                    "input": RunnablePassthrough(),
                    "context": RunnableLambda(temp1) | retriever | format_document
                } | RunnableLambda(temp2) | self.prompt_template | print_prompt | self.chat_model | StrOutputParser()
        )

        conversation_chain = RunnableWithMessageHistory(
            chain,
            get_history,
            input_messages_key="input",
            history_messages_key="history"
        )

        return conversation_chain


if __name__ == '__main__':
    # sessionid 配置
    session_config = {
        "configurable":{
            "session_id": "user_001",
        }
    }
    res = RagService().chain.invoke({"input": "我的身高185cm,尺码推荐"}, session_config)
    print(res)

聊天页面开发

Python 复制代码
import time
from rag import RagService
import streamlit as st
import config_data as config


st.title("智能客服")
st.divider()

rag_service = RagService()

if "message" not in st.session_state:
    st.session_state["message"] = [{"role": "assistant", "content": "你好,我是智能客服,请开始提问吧!"}]

if "rag" not in st.session_state:
    st.session_state["rag"] = RagService()

for message in st.session_state["message"]:
    st.chat_message(message["role"]).write(message["content"])

prompt = st.chat_input()



if prompt:

    st.chat_message("user").write(prompt)

    st.session_state["message"].append({"role": "user", "content": prompt})

    ai_res_list = []
    with st.spinner("AI 思考中..."):
        res_stream = st.session_state["rag"].chain.invoke({"input": prompt}, config.session_config)

        def capture(generator, cache_list):
            for chunk in generator:
                cache_list.append(chunk)
                yield chunk

        st.chat_message("assistant").write_stream(capture(res_stream, ai_res_list))
        st.session_state["message"].append({"role": "assistant", "content": "".join(ai_res_list)})

Agent 智能体

智能体(Agent)是一种能够自主规划、决策、执行任务的组件,核心是让大语言模型(LLM)根据任务需求,选择并调用 工具,完成单靠模型自身无法解决的复杂问题。

  • 没有Agent时,LLM 只能基于自身训练数据回答问题,遇到需要实时数据、复杂计算、外部工具调用的场景就会卡壳。
  • 有了Agent后,LLM 就像一个"指挥官",能思考任务步骤>选择合适工具>执行工具调用>根据结果调整策略,直到完成任务

初体验

Python 复制代码
from langchain.agents import create_agent
from langchain_deepseek import ChatDeepSeek
from langchain_core.tools import tool


@tool(description="查询天气")
def get_weather() -> str:
    return "晴天"

agent = create_agent(
    model=ChatDeepSeek(model="deepseek-v4-flash"),
    tools=[get_weather],
    system_prompt="你是一个聊天助手,可以回答用户问题"
)



res = agent.invoke(
    {
        "messages": [
            {"role": "user", "content": "明天太原天气如何?"}
        ]
    }
)

for msg in res["messages"]:
    print(type(msg).__name__, msg.content)

agent 的流式输出

Python 复制代码
from langchain.agents import create_agent
from langchain_deepseek import ChatDeepSeek
from langchain_core.tools import tool

@tool(description="获取股票价格")
def get_price(name: str) -> str:
    return f"股票{name}价格为20元"

@tool(description="查询股票信息")
def get_info(name: str) -> str:
    return f"{name}是一家A股上市的公司,专注于教育"

agent = create_agent(
    model=ChatDeepSeek(model="deepseek-v4-flash"),
    tools=[get_info, get_price],
    system_prompt="""
    你是一个股票查询助手,你需要根据用户的指令进行股票查询
    """,
)

for chunk in agent.stream(
    {"messages":[{"role": "user", "content": "传智教育股价多少?并介绍一下"}]},
    stream_mode="values"
):
    latest_message = chunk['messages'][-1]
    print(latest_message)

ReAct

Agent ReAct 是大模型智能体的核心思考与行动框架,全称 Reasoning+Acting(推理+行动),是让Agent 像人 类一样「思考问题→制定策略→执行行动→验证结果」的关键逻辑。

简单来说:ReAct让Agent 不再是"直接回答问题",而是通过"自然语言思考过程"指导工具调用,一步步解决复杂问题,完美适配需要多步推理、工具协作的场景(如智能客服、报告生成、任务规划等)

一个典型的ReAct范式的Agent如图所示:

  • 思考Reasoning:分析问题,判断现有信息是否足够,明确下一步

即模型决策是否需要调用外部工具获取更多信息用来回答

  • 行动Action:执行思考阶段指定的策略

即基于模型决策结果,调用工具获取信息

  • 观察observation:获取行动的结果,提取有效信息

即获取工具返回值即判断工具是否正常工作为下一轮思考提供信息

(再)思考→(再)行动→(再)观察→循环往复直到结束

LangChain的Agent对象遵循ReAct框架要求,在执行的过程中会持续的自我思考、自我行动、自我观察。

middleware 中间件

中间件的作用是对智能体的每一步工作进行控制和自定义的执行

作用场景:

  • 日志记录、分析、调试
  • 转换提示词、工具选择
  • 重试、备用、提前终止等逻辑控制
  • 安全防护、个人身份检测等

LangChain中内置了一些基础的中间件,参见:docs.langchain.com/oss/python/... 中间件通过Hooks钩子来实现拦截,自定义中间件可以简单的使用装饰器来定义

节点式钩子(执行点顺序拦截):

  • before_agent:agent执行之前拦截
  • after_agent:agent执行后拦截
  • before_model:模型执行前拦截
  • after_model:模型执行后拦截

针对工具和模型的包装式钩子

  • wrap_model_call:每个模型调用时候拦截
  • wrap_tool_call:每个工具调用时候拦截
Python 复制代码
from langchain.agents import create_agent, AgentState
from langchain_deepseek import ChatDeepSeek
from langchain_core.tools import tool
from langchain.agents.middleware import before_agent, after_agent, before_model, wrap_model_call, after_model, \
    wrap_tool_call
from langgraph.runtime import Runtime


@tool(description="查询天气")
def get_weather() -> str:
    return "晴天"


"""
agent执行前
agent执行后
model执行前
model执行后
工具执行中
模型执行中

"""


@before_agent
def log_before_agent(state: AgentState, runtime: Runtime) -> None:
    # agent执行前会调用这个函数并传入state和runtime两个对象
    print(f"[before agent]agent启动,并附带{len(state['messages'])}消息")


@after_agent
def log_after_agent(state: AgentState, runtime: Runtime) -> None:
    print(f"[after agent]agent结束,并附带{len(state['messages'])}消息")


@before_model
def log_before_model(state: AgentState, runtime: Runtime) -> None:
    print(f"[before_model]模型即将调用,并附带{len(state['messages'])}消息")


@after_model
def log_after_model(state: AgentState, runtime: Runtime) -> None:
    print(f"[after_model]模型调用结束,并附带{len(state['messages'])}消息")


@wrap_model_call
def model_call_hook(request, handler):
    print("模型调用啦")
    return handler(request)


@wrap_tool_call
def monitor_tool(request, handler):
    print(f"工具执行:{request.tool_call['name']}")
    print(f"工具执行传入参数:{request.tool_call['args']}")

    return handler(request)


agent = create_agent(
    model=ChatDeepSeek(model="deepseek-v4-flash"),
    tools=[get_weather],
    middleware=[log_before_agent, log_after_agent, log_before_model, log_after_model, model_call_hook, monitor_tool]
)

res = agent.invoke({"messages": [{"role": "user", "content": "深圳今天的天气如何呀,如何穿衣"}]})
print("**********\n", res)

智扫通 Agent 项目

智扫通Agent项目是一个面向消费者(toC)的智能客服系统,旨在为用户提供全周期的扫地机器人相关服务。

(1)智能问答服务:

  • 处理购买前的产品咨询(如功能、价格、对比等)。
  • 解决购买后的使用问题(如操作指导、故障处理、维护建议等)。
  • 基于RAG技术,从知识库中检索准确信息并生成自然语言回答,确保响应及时且可靠。

(2)使用报告与优化建议生成:

  • 针对已购买用户,自动分析扫地机器人的使用数据(如清洁频率、耗材状态、错误日志等)。
  • 生成个性化报告,总结使用情况并提供优化建议(如清洁计划调整、部件更换提醒等)。
  • 支持用户主动查询报告或系统定期推送,帮助用户最大化产品价值。

工具类

路径工具

Python 复制代码
"""
为整个工程提供统一的绝对路径
"""

import os

def get_project_root() -> str:
    """
    获取项目根目录
    :return: 项目根目录
    """
    current_file = os.path.abspath(__file__)
    # 获取工程根目录
    current_dir = os.path.dirname(current_file)
    # 获取项目根目录
    project_root = os.path.dirname(current_dir)

    return project_root

def get_abs_path(relative_path: str) -> str:
    """
    获取绝对路径
    :param relative_path: 相对路径
    :return: 绝对路径
    """

    project_root = get_project_root()
    return os.path.join(project_root, relative_path)

if __name__ == '__main__':
    print(get_abs_path("config/config.txt"))

日志工具类

Python 复制代码
from datetime import datetime
import logging
import os


from utils.path_tool import get_abs_path

# 日志保存的根目录
LOG_ROOT = get_abs_path("logs")
# 确保日志的目录存在
os.makedirs(LOG_ROOT, exist_ok=True)
# 创建日志目录
DEFAULT_LOG_FORMAT = logging.Formatter(
    "%(asctime)s - %(name)s - %(levelname)s - %(filename)s:%(lineno)d - %(message)s"
)

def get_logger(
        name: str = "agent",
        console_level: int = logging.INFO,
        file_level: int = logging.DEBUG,
        log_file: str = "agent.log",
) -> logging.Logger:
    logger = logging.getLogger(name)
    logger.setLevel(logging.DEBUG)

    # 避免重复添加Handler
    if logger.handlers:
        return logger

    # 创建控制台Handler
    console_handler = logging.StreamHandler()
    console_handler.setLevel(console_level)
    console_handler.setFormatter(DEFAULT_LOG_FORMAT)

    logger.addHandler(console_handler)

    # 文件handler
    if not log_file:
        log_file = os.path.join(LOG_ROOT, f"{name}_{datetime.now().strftime('%Y-%m-%d')}.log")

    file_handler = logging.FileHandler(log_file, encoding="utf-8")
    file_handler.setLevel(file_level)
    file_handler.setFormatter(DEFAULT_LOG_FORMAT)

    logger.addHandler(file_handler)

    return logger

# 快捷获取日志器
logger = get_logger()


if __name__ == "__main__":
    logger.debug("调试日志")
    logger.info("信息日志")
    logger.warning("警告日志")
    logger.error("错误日志")
相关推荐
lhxcc_fly1 小时前
4.LangChain--Prompt提示词
langchain·llm·prompt
阿里云云原生1 小时前
告别 Agent“黑箱”!阿里云上线 AI Agent 可观测方案,支持 Multi-Agent 全链路透视
agent·监控
lz18341 小时前
Agent Skills 也需要 lockfile
agent
兆。1 小时前
LangChain实验跟踪集成指南:面向ML研究员
数据库·langchain
沉默王二2 小时前
用Codex+Step 3.7Flash开发Agent工作流,198B激活11B参数,实测结果真有东西
agent·ai编程·编程语言
糖果店的幽灵2 小时前
LangChain 1.3 完全教程:从入门到精通-Part 7: Documents(文档处理)
java·python·langchain
Jing_jing_X2 小时前
我用 Claude Code 搭了一个远程 Claude web:手机发指令,本地电脑自己写代码
ai·agent·个人开发·ai应用开发
_未完待续2 小时前
从零打造 AI Agent (三)—— Plan Mode:复杂任务的安全执行
agent·ai编程
FelixBitSoul2 小时前
面试必考!RAG 知识库全链路深度解析:父子分块 × Rerank × 查询重写 × 标准化改写
人工智能·langchain·aigc