From 359c8923edda382d7342de330c2db30b8f5497d2 Mon Sep 17 00:00:00 2001 From: wehub-skill-sync Date: Mon, 13 Jul 2026 21:37:11 +0800 Subject: [PATCH] chore: import zh skill langchain-architecture --- README.wehub.md | 9 ++ SKILL.md | 273 +++++++++++++++++++++++++++++++ references/details.md | 366 ++++++++++++++++++++++++++++++++++++++++++ 3 files changed, 648 insertions(+) create mode 100644 README.wehub.md create mode 100644 SKILL.md create mode 100644 references/details.md diff --git a/README.wehub.md b/README.wehub.md new file mode 100644 index 0000000..ec99278 --- /dev/null +++ b/README.wehub.md @@ -0,0 +1,9 @@ +# WeHub 来源说明 + +- Skill 名称:`langchain-architecture` +- 中文类目:LangChain/LangGraph 智能体架构设计 +- 上游仓库:`wshobson__agents` +- 上游路径:`plugins/llm-application-dev/skills/langchain-architecture/SKILL.md` +- 上游链接:https://github.com/wshobson/agents/blob/HEAD/plugins/llm-application-dev/skills/langchain-architecture/SKILL.md +- 本仓库为 WeHub 中文 Skill 汉化包,基于 skill 市场筛选 Top200 清单整理 +- 原作者、版权和许可证信息以上游仓库为准 diff --git a/SKILL.md b/SKILL.md new file mode 100644 index 0000000..3a918e4 --- /dev/null +++ b/SKILL.md @@ -0,0 +1,273 @@ +--- +name: langchain-architecture +description: 使用 LangChain 1.x 和 LangGraph 设计包含代理、记忆和工具集成的 LLM 应用程序。适用于构建 LangChain 应用、实现 AI 代理或创建复杂的 LLM 工作流。 +--- + +# LangChain 与 LangGraph 架构 + +掌握现代 LangChain 1.x 和 LangGraph,用于构建包含代理、状态管理、记忆和工具集成的高级 LLM 应用程序。 + +## 何时使用此技能 + +- 构建具有工具访问权限的自主 AI 代理 +- 实现复杂的多步骤 LLM 工作流 +- 管理对话记忆和状态 +- 将 LLM 与外部数据源和 API 集成 +- 创建模块化、可复用的 LLM 应用组件 +- 实现文档处理管道 +- 构建生产级 LLM 应用 + +## 包结构(LangChain 1.x) + +``` +langchain (1.2.x) # 高层编排 +langchain-core (1.2.x) # 核心抽象(消息、提示词、工具) +langchain-community # 第三方集成 +langgraph # 代理编排与状态管理 +langchain-openai # OpenAI 集成 +langchain-anthropic # Anthropic/Claude 集成 +langchain-voyageai # Voyage AI 嵌入向量 +langchain-pinecone # Pinecone 向量存储 +``` + +## 核心概念 + +### 1. LangGraph 代理 + +LangGraph 是 2026 年构建代理的标准方案。它提供: + +**关键特性:** + +- **StateGraph**:带类型状态的显式状态管理 +- **持久化执行**:代理在故障后仍可恢复 +- **人机协同**:可在任意节点检查和修改状态 +- **记忆**:跨会话的短期和长期记忆 +- **检查点**:保存和恢复代理状态 + +**代理模式:** + +- **ReAct**:通过 `create_react_agent` 实现推理与行动 +- **规划与执行**:分离规划节点和执行节点 +- **多代理**:在专用代理之间进行监督路由 +- **工具调用**:基于 Pydantic 模式的工具调用 + +### 2. 状态管理 + +LangGraph 使用 TypedDict 实现显式状态: + +```python +from typing import Annotated, TypedDict +from langgraph.graph import MessagesState + +# 简单的基于消息的状态 +class AgentState(MessagesState): + """扩展 MessagesState,添加自定义字段。""" + context: Annotated[list, "检索到的文档"] + +# 用于复杂代理的自定义状态 +class CustomState(TypedDict): + messages: Annotated[list, "对话历史"] + context: Annotated[dict, "检索到的上下文"] + current_step: str + results: list +``` + +### 3. 记忆系统 + +现代记忆实现: + +- **ConversationBufferMemory**:存储所有消息(短对话) +- **ConversationSummaryMemory**:对较旧消息进行摘要(长对话) +- **ConversationTokenBufferMemory**:基于令牌的窗口 +- **VectorStoreRetrieverMemory**:语义相似度检索 +- **LangGraph 检查点**:跨会话的持久化状态 + +### 4. 文档处理 + +加载、转换和存储文档: + +**组件:** + +- **文档加载器**:从多种来源加载 +- **文本分割器**:智能分块文档 +- **向量存储**:存储和检索嵌入向量 +- **检索器**:获取相关文档 + +### 5. 回调与追踪 + +LangSmith 是可观测性的标准方案: + +- 请求/响应日志记录 +- 令牌用量追踪 +- 延迟监控 +- 错误追踪 +- 追踪可视化 + +## 快速开始 + +### 使用 LangGraph 的现代 ReAct 代理 + +```python +from langgraph.prebuilt import create_react_agent +from langgraph.checkpoint.memory import MemorySaver +from langchain_anthropic import ChatAnthropic +from langchain_core.tools import tool +import ast +import operator + +# 初始化 LLM(推荐 Claude Sonnet 4.6) +llm = ChatAnthropic(model="claude-sonnet-4-6", temperature=0) + +# 使用 Pydantic 模式定义工具 +@tool +def search_database(query: str) -> str: + """搜索内部数据库以获取信息。""" + # 你的数据库搜索逻辑 + return f"结果:{query}" + +@tool +def calculate(expression: str) -> str: + """安全计算数学表达式。 + + 支持:+、-、*、/、**、%、括号 + 示例:'(2 + 3) * 4' 返回 '20' + """ + # 使用 ast 进行安全数学计算 + allowed_operators = { + ast.Add: operator.add, + ast.Sub: operator.sub, + ast.Mult: operator.mul, + ast.Div: operator.truediv, + ast.Pow: operator.pow, + ast.Mod: operator.mod, + ast.USub: operator.neg, + } + + def _eval(node): + if isinstance(node, ast.Constant): + return node.value + elif isinstance(node, ast.BinOp): + left = _eval(node.left) + right = _eval(node.right) + return allowed_operators[type(node.op)](left, right) + elif isinstance(node, ast.UnaryOp): + operand = _eval(node.operand) + return allowed_operators[type(node.op)](operand) + else: + raise ValueError(f"不支持的操作:{type(node)}") + + try: + tree = ast.parse(expression, mode='eval') + return str(_eval(tree.body)) + except Exception as e: + return f"错误:{e}" + +tools = [search_database, calculate] + +# 创建用于记忆持久化的检查点 +checkpointer = MemorySaver() + +# 创建 ReAct 代理 +agent = create_react_agent( + llm, + tools, + checkpointer=checkpointer +) + +# 使用线程 ID 运行代理以实现记忆 +config = {"configurable": {"thread_id": "user-123"}} +result = await agent.ainvoke( + {"messages": [("user", "搜索 Python 教程并计算 25 * 4")]}, + config=config +) +``` + +## 详细模式与完整示例 + +详细模式文档位于 `references/details.md`。当上述导航层级不足以满足需求时,请阅读该文件。 + +## 测试策略 + +```python +import pytest +from unittest.mock import AsyncMock, patch + +@pytest.mark.asyncio +async def test_agent_tool_selection(): + """测试代理是否正确选择工具。""" + with patch.object(llm, 'ainvoke') as mock_llm: + mock_llm.return_value = AsyncMock(content="使用 search_database") + + result = await agent.ainvoke({ + "messages": [("user", "搜索文档")] + }) + + # 验证工具被调用 + assert "search_database" in str(result) + +@pytest.mark.asyncio +async def test_memory_persistence(): + """测试记忆是否在多次调用间保持持久化。""" + config = {"configurable": {"thread_id": "test-thread"}} + + # 第一条消息 + await agent.ainvoke( + {"messages": [("user", "记住:代码是 12345")]}, + config + ) + + # 第二条消息应记住 + result = await agent.ainvoke( + {"messages": [("user", "代码是什么?")]}, + config + ) + + assert "12345" in result["messages"][-1].content +``` + +## 性能优化 + +### 1. 使用 Redis 进行缓存 + +```python +from langchain_community.cache import RedisCache +from langchain_core.globals import set_llm_cache +import redis + +redis_client = redis.Redis.from_url("redis://localhost:6379") +set_llm_cache(RedisCache(redis_client)) +``` + +### 2. 异步批处理 + +```python +import asyncio +from langchain_core.documents import Document + +async def process_documents(documents: list[Document]) -> list: + """并行处理文档。""" + tasks = [process_single(doc) for doc in documents] + return await asyncio.gather(*tasks) + +async def process_single(doc: Document) -> dict: + """处理单个文档。""" + chunks = text_splitter.split_documents([doc]) + embeddings = await embeddings_model.aembed_documents( + [c.page_content for c in chunks] + ) + return {"doc_id": doc.metadata.get("id"), "embeddings": embeddings} +``` + +### 3. 连接池 + +```python +from langchain_pinecone import PineconeVectorStore +from pinecone import Pinecone + +# 复用 Pinecone 客户端 +pc = Pinecone(api_key=os.environ["PINECONE_API_KEY"]) +index = pc.Index("my-index") + +# 使用现有索引创建向量存储 +vectorstore = PineconeVectorStore(index=index, embedding=embeddings) +``` diff --git a/references/details.md b/references/details.md new file mode 100644 index 0000000..1d69973 --- /dev/null +++ b/references/details.md @@ -0,0 +1,366 @@ +# langchain-architecture — 详细模式与实战示例 + +## 架构模式 + +### 模式 1:基于 LangGraph 的 RAG + +```python +from langgraph.graph import StateGraph, START, END +from langchain_anthropic import ChatAnthropic +from langchain_voyageai import VoyageAIEmbeddings +from langchain_pinecone import PineconeVectorStore +from langchain_core.documents import Document +from langchain_core.prompts import ChatPromptTemplate +from typing import TypedDict, Annotated + +class RAGState(TypedDict): + question: str + context: Annotated[list[Document], "检索到的文档"] + answer: str + +# 初始化组件 +llm = ChatAnthropic(model="claude-sonnet-4-6") +embeddings = VoyageAIEmbeddings(model="voyage-3-large") +vectorstore = PineconeVectorStore(index_name="docs", embedding=embeddings) +retriever = vectorstore.as_retriever(search_kwargs={"k": 4}) + +# 定义节点 +async def retrieve(state: RAGState) -> RAGState: + """检索相关文档。""" + docs = await retriever.ainvoke(state["question"]) + return {"context": docs} + +async def generate(state: RAGState) -> RAGState: + """根据上下文生成回答。""" + prompt = ChatPromptTemplate.from_template( + """请根据以下上下文回答问题。如果你无法回答,请如实说明。 + + 上下文:{context} + + 问题:{question} + + 回答:""" + ) + context_text = "\n\n".join(doc.page_content for doc in state["context"]) + response = await llm.ainvoke( + prompt.format(context=context_text, question=state["question"]) + ) + return {"answer": response.content} + +# 构建图 +builder = StateGraph(RAGState) +builder.add_node("retrieve", retrieve) +builder.add_node("generate", generate) +builder.add_edge(START, "retrieve") +builder.add_edge("retrieve", "generate") +builder.add_edge("generate", END) + +rag_chain = builder.compile() + +# 使用该链 +result = await rag_chain.ainvoke({"question": "主要主题是什么?"}) +``` + +### 模式 2:带结构化工具的自定义 Agent + +```python +from langchain_core.tools import StructuredTool +from pydantic import BaseModel, Field + +class SearchInput(BaseModel): + """数据库搜索的输入。""" + query: str = Field(description="搜索查询") + filters: dict = Field(default={}, description="可选筛选条件") + +class EmailInput(BaseModel): + """发送邮件的输入。""" + recipient: str = Field(description="邮件收件人") + subject: str = Field(description="邮件主题") + content: str = Field(description="邮件正文") + +async def search_database(query: str, filters: dict = {}) -> str: + """搜索内部数据库以获取信息。""" + # 你的数据库搜索逻辑 + return f"针对 '{query}' 的查询结果,筛选条件为 {filters}" + +async def send_email(recipient: str, subject: str, content: str) -> str: + """向指定收件人发送邮件。""" + # 邮件发送逻辑 + return f"邮件已发送至 {recipient}" + +tools = [ + StructuredTool.from_function( + coroutine=search_database, + name="search_database", + description="搜索内部数据库", + args_schema=SearchInput + ), + StructuredTool.from_function( + coroutine=send_email, + name="send_email", + description="发送邮件", + args_schema=EmailInput + ) +] + +agent = create_react_agent(llm, tools) +``` + +### 模式 3:基于 StateGraph 的多步骤工作流 + +```python +from langgraph.graph import StateGraph, START, END +from typing import TypedDict, Literal + +class WorkflowState(TypedDict): + text: str + entities: list + analysis: str + summary: str + current_step: str + +async def extract_entities(state: WorkflowState) -> WorkflowState: + """从文本中提取关键实体。""" + prompt = f"从以下文本中提取关键实体:{state['text']}\n\n以 JSON 列表形式返回。" + response = await llm.ainvoke(prompt) + return {"entities": response.content, "current_step": "analyze"} + +async def analyze_entities(state: WorkflowState) -> WorkflowState: + """分析提取出的实体。""" + prompt = f"分析以下实体:{state['entities']}\n\n提供洞察见解。" + response = await llm.ainvoke(prompt) + return {"analysis": response.content, "current_step": "summarize"} + +async def generate_summary(state: WorkflowState) -> WorkflowState: + """生成最终摘要。""" + prompt = f"""总结: + 实体:{state['entities']} + 分析:{state['analysis']} + + 提供一份简洁的摘要。""" + response = await llm.ainvoke(prompt) + return {"summary": response.content, "current_step": "complete"} + +def route_step(state: WorkflowState) -> Literal["analyze", "summarize", "end"]: + """根据当前状态路由到下一步。""" + step = state.get("current_step", "extract") + if step == "analyze": + return "analyze" + elif step == "summarize": + return "summarize" + return "end" + +# 构建工作流 +builder = StateGraph(WorkflowState) +builder.add_node("extract", extract_entities) +builder.add_node("analyze", analyze_entities) +builder.add_node("summarize", generate_summary) + +builder.add_edge(START, "extract") +builder.add_conditional_edges("extract", route_step, { + "analyze": "analyze", + "summarize": "summarize", + "end": END +}) +builder.add_conditional_edges("analyze", route_step, { + "summarize": "summarize", + "end": END +}) +builder.add_edge("summarize", END) + +workflow = builder.compile() +``` + +### 模式 4:多 Agent 编排 + +```python +from langgraph.graph import StateGraph, START, END +from langgraph.prebuilt import create_react_agent +from langchain_core.messages import HumanMessage +from typing import Literal + +class MultiAgentState(TypedDict): + messages: list + next_agent: str + +# 创建专用 agent +researcher = create_react_agent(llm, research_tools) +writer = create_react_agent(llm, writing_tools) +reviewer = create_react_agent(llm, review_tools) + +async def supervisor(state: MultiAgentState) -> MultiAgentState: + """根据任务路由到合适的 agent。""" + prompt = f"""根据对话内容,应由哪个 agent 处理此任务? + + 选项: + - researcher:用于查找信息 + - writer:用于创作内容 + - reviewer:用于审阅和编辑 + - FINISH:任务已完成 + + 消息:{state['messages']} + + 请只回复 agent 名称。""" + + response = await llm.ainvoke(prompt) + return {"next_agent": response.content.strip().lower()} + +def route_to_agent(state: MultiAgentState) -> Literal["researcher", "writer", "reviewer", "end"]: + """根据 supervisor 的决策进行路由。""" + next_agent = state.get("next_agent", "").lower() + if next_agent == "finish": + return "end" + return next_agent if next_agent in ["researcher", "writer", "reviewer"] else "end" + +# 构建多 agent 图 +builder = StateGraph(MultiAgentState) +builder.add_node("supervisor", supervisor) +builder.add_node("researcher", researcher) +builder.add_node("writer", writer) +builder.add_node("reviewer", reviewer) + +builder.add_edge(START, "supervisor") +builder.add_conditional_edges("supervisor", route_to_agent, { + "researcher": "researcher", + "writer": "writer", + "reviewer": "reviewer", + "end": END +}) + +# 每个 agent 返回 supervisor +for agent in ["researcher", "writer", "reviewer"]: + builder.add_edge(agent, "supervisor") + +multi_agent = builder.compile() +``` + +## 记忆管理 + +### 基于 LangGraph 的令牌级记忆 + +```python +from langgraph.checkpoint.memory import MemorySaver +from langgraph.prebuilt import create_react_agent + +# 内存检查点器(开发环境) +checkpointer = MemorySaver() + +# 创建带持久化记忆的 agent +agent = create_react_agent(llm, tools, checkpointer=checkpointer) + +# 每个 thread_id 维护独立的对话 +config = {"configurable": {"thread_id": "session-abc123"}} + +# 使用相同 thread_id 时,消息在多次调用间持久化 +result1 = await agent.ainvoke({"messages": [("user", "我叫 Alice")]}, config) +result2 = await agent.ainvoke({"messages": [("user", "我叫什么名字?")]}, config) +# Agent 记住了:"你的名字是 Alice" +``` + +### 基于 PostgreSQL 的生产级记忆 + +```python +from langgraph.checkpoint.postgres import PostgresSaver + +# 生产环境检查点器 +checkpointer = PostgresSaver.from_conn_string( + "postgresql://user:pass@localhost/langgraph" +) + +agent = create_react_agent(llm, tools, checkpointer=checkpointer) +``` + +### 用于长期上下文的向量存储记忆 + +```python +from langchain_community.vectorstores import Chroma +from langchain_voyageai import VoyageAIEmbeddings + +embeddings = VoyageAIEmbeddings(model="voyage-3-large") +memory_store = Chroma( + collection_name="conversation_memory", + embedding_function=embeddings, + persist_directory="./memory_db" +) + +async def retrieve_relevant_memory(query: str, k: int = 5) -> list: + """检索相关的历史对话。""" + docs = await memory_store.asimilarity_search(query, k=k) + return [doc.page_content for doc in docs] + +async def store_memory(content: str, metadata: dict = {}): + """将对话存入长期记忆。""" + await memory_store.aadd_texts([content], metadatas=[metadata]) +``` + +## 回调系统与 LangSmith + +### LangSmith 追踪 + +```python +import os +from langchain_anthropic import ChatAnthropic + +# 启用 LangSmith 追踪 +os.environ["LANGCHAIN_TRACING_V2"] = "true" +os.environ["LANGCHAIN_API_KEY"] = "your-api-key" +os.environ["LANGCHAIN_PROJECT"] = "my-project" + +# 所有 LangChain/LangGraph 操作将被自动追踪 +llm = ChatAnthropic(model="claude-sonnet-4-6") +``` + +### 自定义回调处理器 + +```python +from langchain_core.callbacks import BaseCallbackHandler +from typing import Any, Dict, List + +class CustomCallbackHandler(BaseCallbackHandler): + def on_llm_start( + self, serialized: Dict[str, Any], prompts: List[str], **kwargs + ) -> None: + print(f"LLM 开始,共 {len(prompts)} 条提示") + + def on_llm_end(self, response, **kwargs) -> None: + print(f"LLM 完成:共 {len(response.generations)} 个生成结果") + + def on_llm_error(self, error: Exception, **kwargs) -> None: + print(f"LLM 错误:{error}") + + def on_tool_start( + self, serialized: Dict[str, Any], input_str: str, **kwargs + ) -> None: + print(f"工具开始:{serialized.get('name')}") + + def on_tool_end(self, output: str, **kwargs) -> None: + print(f"工具完成:{output[:100]}...") + +# 使用回调 +result = await agent.ainvoke( + {"messages": [("user", "query")]}, + config={"callbacks": [CustomCallbackHandler()]} +) +``` + +## 流式响应 + +```python +from langchain_anthropic import ChatAnthropic + +llm = ChatAnthropic(model="claude-sonnet-4-6", streaming=True) + +# 流式输出令牌 +async for chunk in llm.astream("给我讲个故事"): + print(chunk.content, end="", flush=True) + +# 流式输出 agent 事件 +async for event in agent.astream_events( + {"messages": [("user", "搜索并总结")]}, + version="v2" +): + if event["event"] == "on_chat_model_stream": + print(event["data"]["chunk"].content, end="") + elif event["event"] == "on_tool_start": + print(f"\n[正在使用工具:{event['name']}]") +```