day16-LangChain高级组件之-中间件-运行时-上下文工程-长期记忆等

今日内容

1 中间件

1.1 介绍

# 1 是什么
## 控制并定制Agent执行的每一个步骤
## 中间件提供了一种方法,可以更精细地控制代理(Agent)内部的执行流程。
	核心的代理循环包括调用模型、让模型选择并执行工具,然后在模型不再调用工具时结束:如下图1
    中间件在这些步骤的之前和之后暴露了钩子 (hooks):如下图2
    
    
# 2 中间件可以做什么?
    -监控 (Monitor)
    	追踪代理行为,包括日志记录、分析和调试。

    -修改 (Modify)
    	转换提示词(prompts)、工具选择和输出格式。

    -控制 (Control)
    	添加重试、回退和提前终止逻辑。

    -强制执行 (Enforce)
    	应用速率限制、安全防护(guardrails)和个人身份信息(PII)检测。
        
        
    
# 3 使用方式
from langchain.agents import create_agent
from langchain.agents.middleware import SummarizationMiddleware, HumanInTheLoopMiddleware
agent = create_agent(
    model=model,
    tools=[...],
    middleware=[SummarizationMiddleware(), HumanInTheLoopMiddleware()],
)

image-20260308011230625

image-20260308011305291

1.2 Built-in middleware[预制中间件]-内置中间件

Middleware 中间件 Description 描述
Summarization 摘要 Automatically summarize conversation history when approaching token limits. 接近代币限制时自动总结对话记录。
Human-in-the-loop 人机参与 Pause execution for human approval of tool calls. 暂停执行以供人工批准工具调用。
Model call limit 模型呼叫限制 Limit the number of model calls to prevent excessive costs. 限制模型调用次数,以防止过高成本。
Tool call limit 工具调用限制 Control tool execution by limiting call counts. 通过限制呼叫次数来控制工具执行。
Model fallback 模型的备选 Automatically fallback to alternative models when primary fails. 当主模式失败时,会自动回退到其他模式。
PII detection PII 检测 Detect and handle Personally Identifiable Information (PII). 检测并处理个人身份信息(PII)。
To-do list 待办事项列表 Equip agents with task planning and tracking capabilities. 为客服人员配备任务规划和跟踪能力。
LLM tool selector LLM 工具选择器 Use an LLM to select relevant tools before calling main model. 在调用主模型之前,先用 LLM 选择相关工具。
Tool retry 工具重试 Automatically retry failed tool calls with exponential backoff. 用指数回撤自动重试失败的工具调用。
Model retry 模型重试 Automatically retry failed model calls with exponential backoff. 自动用指数退回方式重试失败的模型调用。
LLM tool emulator LLM 工具仿真器 Emulate tool execution using an LLM for testing purposes. 用 LLM 模拟工具执行以进行测试。
Context editing 上下文编辑 Manage conversation context by trimming or clearing tool uses. 通过修剪或清理工具使用来管理对话上下文。
Shell tool 壳体工具 Expose a persistent shell session to agents for command execution. 向代理开放一个持久的壳会话以执行命令。
File search 文件搜索 Provide Glob and Grep search tools over filesystem files. 在文件系统文件上提供 Glob 和 Grep 搜索工具。
Filesystem 文件系统 Provide agents with a filesystem for storing context and long-term memories. 为代理提供存储上下文和长期记忆的文件系统。
Subagent 副代理人 Add the ability to spawn subagents. 增加生成子代理人的能力。

1.3 自定义中间件

# 1 通过实现运行在代理执行流程中特定点的钩子(hooks)来构建自定义中间件。

# 2 可以通过两种方式创建中间件
	基于装饰器 (Decorator-based): - 适用于单钩子中间件,快速且简单
    基于类:(Class-based): - 适用于具有多个钩子的复杂中间件,功能更强大

1.3.1 基于装饰器的中间件

# 1 有很多装饰器
# 节点式 (Node-style)(在特定的执行点运行):
    @before_agent - 代理启动前(每次调用一次)
    @before_model - 每次模型调用前
    @after_model -  每次模型响应后
    @after_agent -  代理完成时(每次调用最多一次)
    
# 包装式 (Wrap-style)(拦截并控制执行):
    @wrap_model_call - 每次模型调用周围
    @wrap_tool_call - 每次工具调用周围
    
# 便利装饰器 (Convenience decorators):
    @dynamic_prompt - 生成动态系统提示词(相当于修改提示词的 @wrap_model_call)
from langchain.agents import create_agent, AgentState
from langchain_openai import ChatOpenAI
from langchain.agents.middleware import before_model, after_model, wrap_model_call
from langchain.agents.middleware import AgentState, ModelRequest, ModelResponse, dynamic_prompt
from langchain.messages import AIMessage
from langchain.agents import create_agent
from langgraph.runtime import Runtime
from typing import Any, Callable
from typing import TypedDict

# 1 连接model

model = ChatOpenAI(
    model="qwen-plus",
    api_key="sk-6459007b813946289da12857950c955b",
    base_url="https://dashscope.aliyuncs.com/compatible-mode/v1",
)
############ 定义中间件#################
## 2.1 在模型调用前,记录日志
# 节点式 (Node-style):模型调用前的日志记录
@before_model
def log_before_model(state: AgentState, runtime: Runtime) -> dict[str, Any] | None:
    # 打印出用户用户消息
    print(f"About to call model with {len(state['messages'])} messages")
    return None

## 2.2 模型调用后的验证,如果最后ai返回的消息中有 BLOCKED, 直接返回用户我不能正常回答请求
# 节点式 (Node-style):模型调用后的验证,如果最后ai返回的消息中有 BLOCKED, 直接返回用户我不能正常回答请求
@after_model(can_jump_to=["end"])
def validate_output(state: AgentState, runtime: Runtime) -> dict[str, Any] | None:
    # 拿到最后一条消息,一定是调用模型返回的--》这个模型应用后会触发
    last_message = state["messages"][-1]
    if "BLOCKED" in last_message.content:
        return {
            "messages": [AIMessage("I cannot respond to that request.")],
            "jump_to": "end"
        }
    # 继续后续操作
    return None
## 2.3重试逻辑 model调用周围,失败了就重试
# 包装式 (Wrap-style):重试逻辑 model调用周围,失败了就重试
@wrap_model_call
def retry_model(
    request: ModelRequest,
    handler: Callable[[ModelRequest], ModelResponse],
) -> ModelResponse:
    for attempt in range(3):
        try:
            return handler(request) # 重新调用模型
        except Exception as e:
            if attempt == 2:
                raise
            print(f"Retry {attempt + 1}/3 after error: {e}")

## 2.4 生成 动态提示词,相当于修改提示词
# 便利装饰器:生成 动态提示词,相当于修改提示词
@dynamic_prompt
def personalized_prompt(request: ModelRequest) -> str:
    user_name = request.runtime.context.get("user_name", "guest")
    return f"You are a helpful assistant for user {user_name}. Be concise and friendly."

# 定义上下对象
class CustomContext(TypedDict):
    user_name: str

agent = create_agent(
    model,
    tools=[],
    middleware=[log_before_model, validate_output, retry_model, personalized_prompt],
    context_schema=CustomContext, # 上下文
)

response = agent.invoke({"messages": "hello"},context=CustomContext(user_name="Justin"),)

print(response["messages"][-1].pretty_print())

1.3.2 基于类的中间件

'''
基于类的中间件
两种钩子样式
节点式钩子 (Node-style hooks)
    在特定的执行点顺序运行。用于日志记录、验证和状态更新。
包装式钩子 (Wrap-style hooks)
    拦截执行并对处理程序调用具有完全控制。用于重试、缓存和转换
'''
from langchain_openai import ChatOpenAI
from langchain.agents.middleware import AgentState, ModelRequest, ModelResponse, dynamic_prompt
from langchain.messages import AIMessage
from langchain.agents import create_agent
from langgraph.runtime import Runtime
from typing import Any, Callable
from langchain.agents.middleware import AgentMiddleware, AgentState
import random
# 1 连接千问model
qian_wen = ChatOpenAI(
    model="qwen-plus",
    api_key="sk-6459007b813946289da12857950c955b",
    base_url="https://dashscope.aliyuncs.com/compatible-mode/v1",
)
# 2 连接DeepSeek的model
deep_seek = ChatOpenAI(
    model="deepseek-chat",
    api_key="sk-4a782de375c04ad5b62e08dfac8e69b0",
    base_url="https://api.deepseek.com",
)

'''
节点式钩子 (Node-style hooks)
在执行流程中的特定点运行:
    before_agent - 代理启动前(每次调用一次)
    before_model - 每次模型调用前
    after_model - 每次模型响应后
    after_agent - 代理完成时(每次调用最多一次)
'''
### 3 日志记录
# 写个类,继承 AgentMiddleware---》在类中写方法:before_agent,before_model,after_model,after_agent
class LoggingMiddleware(AgentMiddleware):
    def before_model(self, state: AgentState, runtime: Runtime) -> dict[str, Any] | None:
        print(f"About to call model with {len(state['messages'])} messages")
        return None

    def after_model(self, state: AgentState, runtime: Runtime) -> dict[str, Any] | None:
        print(f"Model returned: {state['messages'][-1].content}")
        return None

# 4 对话长度限制
class MessageLimitMiddleware(AgentMiddleware):
    def __init__(self, max_messages: int = 50):
        super().__init__() # 这个必须要写,才能调用父类的 __init__完成一些初始化操作
        # 在类初始化的时候,假如自己的数据---》后续使用
        self.max_messages = max_messages

    def before_model(self, state: AgentState, runtime: Runtime) -> dict[str, Any] | None:
        # 1 判断用户消息长度如果大于规定的50,直接结束
        if len(state["messages"]) >= self.max_messages:
            return {
                "messages": [AIMessage("Conversation limit reached.")],
                "jump_to": "end"
            }
        return None


'''
包装式钩子 (Wrap-style hooks)
拦截执行并控制何时调用处理程序:
    wrap_model_call - 每次模型调用周围
    wrap_tool_call - 每次工具调用周围
您可以决定处理程序是调用零次(短路)、一次(正常流程)还是多次(重试逻辑)。
'''
# 5 重试中间件:默认重试3次,可以传入
class RetryMiddleware(AgentMiddleware):
    def __init__(self, max_retries: int = 3):
        super().__init__()
        self.max_retries = max_retries

    def wrap_model_call(
        self,
        request: ModelRequest,
        handler: Callable[[ModelRequest], ModelResponse],
    ) -> ModelResponse:
        for attempt in range(self.max_retries):
            try:
                return handler(request)
            except Exception as e:
                if attempt == self.max_retries - 1:
                    raise
                print(f"Retry {attempt + 1}/{self.max_retries} after error: {e}")

class DynamicModelMiddleware(AgentMiddleware):
    def wrap_model_call(
        self,
        request: ModelRequest,
        handler: Callable[[ModelRequest], ModelResponse],
    ) -> ModelResponse:
        # 随机选择模型
        if random.choice([True, False]):
            # 随机选择模型
            model = qian_wen
        else:
            model = deep_seek
        return handler(request.override(model=model))


agent = create_agent(
    qian_wen,
    tools=[],
    middleware=[LoggingMiddleware(),MessageLimitMiddleware(20),RetryMiddleware(),DynamicModelMiddleware()],
)

response = agent.invoke({"messages": "hello"})

print(response["messages"][-1].pretty_print())

# 因为我随机选择了 DeepSeek 或千问--》而第一次使用了DeepSeek,而DeepSeek的sk失效了--》自然掉不通---》中间件重试--》选了千问--》通了,就返回了

2 长期记忆

2.1 概念

# 1 LangChain 智能体使用 LangGraph 持久化来实现长期记忆。这是一个更高级的主题,需要了解 LangGraph 才能使用。

# 2 内存存储 (Memory storage)
LangGraph 将长期记忆作为 JSON 文档存储在存储 (store) 中,具体请参考 LangGraph 内存存储。
每条记忆都组织在一个自定义的 namespace(类似于文件夹)和一个独特的 key(类似于文件名)下。命名空间通常包含用户或组织 ID 或其他标签,以便于组织信息。
这种结构支持记忆的层次化组织。随后可以通过内容过滤器支持跨命名空间搜索


image-20260311204616340

2.1 基本使用

# pip install langgraph
from langgraph.store.memory import InMemoryStore  # 内存存储

# 1 一个函数
def embed(texts: list[str]) -> list[list[float]]:
    # 替换为实际的嵌入函数或 LangChain 嵌入对象
    return [[1.0, 2.0] * len(texts)]

# 2 实例化得到一个 InMemoryStore
# InMemoryStore 将数据保存到内存字典中。在生产环境中使用基于数据库的存储。
store = InMemoryStore(index={"embed": embed, "dims": 2}) #
user_id = "my-user"
application_context = "chitchat"
namespace = (user_id, application_context)
# 3 放到长期记忆中,通过 namespace 区分,内部根据key区分
store.put(
    namespace,
    "a-memory",
    {
        "rules": [
            "User likes short, direct language",
            "User only speaks English & python",
        ],
        "my-key": "my-value",
    },
)

# 4 取出长期记忆
# 通过 namespace取到"文件夹,再根据key取到具体memory内容
item = store.get(namespace, "a-memory")
# 在此命名空间内搜索 "memories",过滤内容等价性,并按向量相似度排序
items = store.search(
    namespace, filter={"my-key": "my-value"}, query="language preferences"
)


### 掌握的:  store.put()      store.get()

2.3 从工具中写入长期记忆

from dataclasses import dataclass
from typing_extensions import TypedDict

from langchain.agents import create_agent
from langchain.tools import tool, ToolRuntime
from langgraph.store.memory import InMemoryStore
from langchain_openai import ChatOpenAI

# 1 连接模型
model = ChatOpenAI(
    model="qwen-plus",
    api_key="sk-6459007b813946289da12857950c955b",
    base_url="https://dashscope.aliyuncs.com/compatible-mode/v1",
)

# 2 创建 store对象
store = InMemoryStore()

# 3 定义一个上下文对象
@dataclass
class Context:
    user_id: str

# TypedDict 定义了供 LLM 使用的用户信息结构
class UserInfo(TypedDict):
    name: str

# 4 定义一个工具--》取出用户传入的用户信息---》存储到长期记忆中
# 允许智能体更新用户信息的工具(适用于聊天应用)
@tool
def save_user_info(user_info: UserInfo, runtime: ToolRuntime[Context]) -> str:
    """Save user info."""
    # 访问 store - 与提供给 `create_agent` 的 store 相同
    store = runtime.store # 运行时中拿长期记忆   还能拿到短期记忆,上下文。。。
    user_id = runtime.context.user_id #  运行时 拿 上下文
    # 在 store 中存储数据 (namespace, key, data)
    store.put(("users",), user_id, user_info) # 向长期记忆中存:namespace,key,具体内容
    return "Successfully saved user info."

# 5 创建智能体
agent = create_agent(
    model=model,
    tools=[save_user_info], # 工具
    store=store,            # 这里传入了store对象,后续能从运行时runtime中取出store
    context_schema=Context  # 传入上下文对象的类型
)

# 6 运行智能体
agent.invoke(
    {"messages": [{"role": "user", "content": "My name is lqz"}]},
    # 在 context 中传入 user_id 以识别正在更新谁的信息
    context=Context(user_id="user_123")
)
# 7 调用model,会触发工具执行--》就会把 user_123:{user_name:lqz}-->存到store长期记忆中


# 后续我们从任意位置,都能取出长期记忆中存的值
# 您可以直接访问 store 来获取该值
print(store.get(("users",), "user_123").value)

2.4 从工具中取出长期记忆


from dataclasses import dataclass
from langchain.agents import create_agent
from langchain.tools import tool, ToolRuntime
from langgraph.store.memory import InMemoryStore
from langchain_openai import ChatOpenAI
# 1 连接模型
model = ChatOpenAI(
    model="qwen-plus",
    api_key="sk-6459007b813946289da12857950c955b",
    base_url="https://dashscope.aliyuncs.com/compatible-mode/v1",
)

# 2 定义 Context
@dataclass
class Context:
    user_id: str

# 3 实例化得到长期记忆对象
store = InMemoryStore()



# # python 基础知识
# # 不可变数据类型,可以作为字典的key
# a=11
# b='lqz'
# c=(1,)
# # 可变数据类型,不能作为字典的key
# l=[1,] # 列表
# d={'name':'lqz'}  # 字典
# s={1,}

# 4 直接写入 :使用 put 方法向 store 写入示例数据
store.put(     # 元组,字符串,数字 是不可变类型
    ("users",),  # 用于将相关数据分组的命名空间(用于用户数据的 users 命名空间)
    "user_123",  # 命名空间内的 Key(用户 ID 作为 Key)
    {
        "name": "lqz",
        "language": "English",
    }  # 为给定用户存储的数据
)

# 工具要不要传参数,是我们写代码的决定的---》来调用它,是大模型决定的
'''
look up user information--->给了大模型---》结合工具:get_user_info---》不需要解析出参数的
look up user information,user_id is user_123--->给了大模型---》结合工具:get_user_info---》不会,工具不需要参数
look up user information--->给了大模型---》大模型决定调工具:get_user_info--》不传参数,我们不知道拿谁的信息
    -工具中:拿到上下文:runtime.context
    -调用模型时,在上下中传入了 user_id
    -工具中就能拿到 user_id
    -根据 user_id 去长期记忆中取出对于的人名---》返回人名了

'''
@tool
def get_user_info(runtime: ToolRuntime[Context]) -> str:
    """Look up user info."""
    # 访问 store - 与提供给 `create_agent` 的 store 是一个,store中有 put进去的数据
    store = runtime.store
    # 从上下中取出:user_id:user_123
    user_id = runtime.context.user_id
    # 从 store 检索数据 - 返回带有 value 和 metadata 的 StoreValue 对象
    user_info = store.get(("users",), user_id) #
    return str(user_info.value) if user_info else "Unknown user"

agent = create_agent(
    model=model,
    tools=[get_user_info],
    # 将 store 传递给智能体 - 使智能体能够在运行工具时访问 store
    store=store,
    context_schema=Context
)

# 运行智能体
res=agent.invoke(
    {"messages": [{"role": "user", "content": "look up user information"}]},
    context=Context(user_id="user_123")
)
print(res['messages'][-1])
# 注意区分长期记忆和短期记忆的概念

3 运行时

3.1 概念

# 1 LangChain 的 create_agent 实际上是在 LangGraph 的运行时环境下运行的。
# 2 LangGraph 暴露了一个 Runtime 对象,其中包含以下信息:
	Context (上下文): 静态信息,例如用户 ID、数据库连接,或代理调用所需的其他依赖项。【上下文对象】
    Store (存储): 一个 BaseStore 实例,用于长期记忆。【长期记忆】
    Stream writer (流写入器): 一个用于通过 "custom" 流模式进行信息流式传输的对象。【讲工具讲过】

3.2 使用

# 1 使用 create_agent 创建代理时,你可以指定一个 context_schema 来定义存储在代理 Runtime 中的 context 结构。【上下文】

# 2 在调用代理时,通过传入 context 参数来提供运行的相关配置
from dataclasses import dataclass

from langchain_core.runnables import RunnableConfig
from langchain.agents import create_agent, AgentState
from langchain.tools import tool, ToolRuntime
from langgraph.store.memory import InMemoryStore
from langgraph.runtime import Runtime
from langchain_openai import ChatOpenAI
from langchain.agents.middleware import dynamic_prompt, ModelRequest, before_model, after_model
model = ChatOpenAI(
    model="qwen-plus",
    api_key="sk-6459007b813946289da12857950c955b",
    base_url="https://dashscope.aliyuncs.com/compatible-mode/v1",
)
@dataclass
class Context:
    user_name: str

### 1 工具中使用
@tool
def fetch_user_name(runtime: ToolRuntime[Context]) -> str:
    """获取用户姓名."""
    user_name = runtime.context.user_name
    return user_name

### 2 在中间件内部 (Inside middleware)
@before_model
def log_before_model(state: AgentState, runtime: Runtime[Context]) -> dict | None:  # [!code highlight]
    print(f"Processing request for user: {runtime.context.user_name}")  # [!code highlight]
    return None
agent = create_agent(
    model=model,
    middleware=[log_before_model],
    tools=[fetch_user_name],
    context_schema=Context
)

res=agent.invoke(
    {"messages": [{"role": "user", "content": "What's my name?"}]},
    context=Context(user_name="lqz")
)
print(res)

4 上下文工程Context

4.1 概念

#1 概述 (Overview)
构建代理(或任何大型语言模型应用程序)的难点在于使其足够可靠。尽管它们可能适用于原型,但在实际用例中却经常失败。

# 2 为什么代理会失败? (Why do agents fail?)
当代理失败时,通常是因为代理内部的大型语言模型(LLM)调用采取了错误的行动或没有达到我们的预期。LLM 失败的原因有两个:
    底层 LLM 的能力不足
    没有将“正确的”上下文传递给 LLM
    
#3  通常情况下,代理不可靠的首要原因实际上是第二个。

#4 上下文工程(Context engineering)就是以正确的格式提供正确的信息和工具,以便 LLM 能够完成任务。这是 AI 工程师的头号工作。这种缺乏“正确”上下文的情况是提高代理可靠性的最大障碍,而 LangChain 的代理抽象设计独特,正是为了促进上下文工程。

###########################
# Context engineering 不仅仅是LangChain中context这个对象
	-上下工程:用户第一次跟智能体交互,一直交互了很多次---》我们智能体是否能完整的记录用户所有的交互,包含用户信息,用户提示词。。---》称之为上文工程
    -能实现上下文工程方式很多
    	-1 短期记忆
        -2 长期记忆
        -3 runtime context
        
   上下文大于提示词,上下文就包含提示词,因为包含历史

   function calling 已死,mcp当立?
    function calling包含mcp
    
    skills--->就是一堆提示词--》提前写好的提示词
    	-使用代码大模型写代码---》
        	第一次写个金字塔:张三写的
            第二次写个99乘法表:李四写的
            -----代码规范,很混乱----
        -skills预设的提示词,限定代码规范
        	第一次写个金字塔:张三写的
            第二次写个99乘法表:张三写的
            -----代码规范统一----
############## 
from dataclasses import dataclass

from langchain.agents import create_agent, AgentState
from langgraph.store.memory import InMemoryStore
from langchain_openai import ChatOpenAI
from langchain.agents.middleware import dynamic_prompt, ModelRequest

# 1 连接model
model = ChatOpenAI(
    model="qwen-plus",
    api_key="sk-6459007b813946289da12857950c955b",
    base_url="https://dashscope.aliyuncs.com/compatible-mode/v1",
)

# 1 中间件中实现上下文
@dynamic_prompt
def state_aware_prompt(request: ModelRequest) -> str:
    # request.messages 是 request.state["messages"] 的快捷方式
    message_count = len(request.messages)
    base = "You are a helpful assistant."
    if message_count > 10:
        base += "\nThis is a long conversation - be extra concise."

    return base
agent = create_agent(
    model=model,
    middleware=[state_aware_prompt]
)



##################### 案例二#############

# # 2 store作为上下文
@dataclass
class Context:
    user_id: str

@dynamic_prompt
def store_aware_prompt(request: ModelRequest) -> str:
    user_id = request.runtime.context.user_id
    # 从 Store 读取:获取用户偏好--》可能是第一次交互时,放入的,后续第3次交互时取出来的
    store = request.runtime.store
    user_prefs = store.get(("preferences",), user_id)

    base = "You are a helpful assistant."

    if user_prefs:
        style = user_prefs.value.get("communication_style", "balanced")
        base += f"\nUser prefers {style} responses."

    return base
agent = create_agent(
    model=model,
    tools=[...],
    middleware=[store_aware_prompt],
    context_schema=Context,
    store=InMemoryStore()
)

# 3 运行时上下文 (Runtime Context)
@dataclass
class Context:
    user_role: str
    deployment_env: str

@dynamic_prompt
def context_aware_prompt(request: ModelRequest) -> str:
    # 从 Runtime Context 读取:用户角色和环境
    user_role = request.runtime.context.user_role
    env = request.runtime.context.deployment_env

    base = "You are a helpful assistant."

    if user_role == "admin":
        base += "\nYou have admin access. You can perform all operations."
    elif user_role == "viewer":
        base += "\nYou have read-only access. Guide users to read operations only."

    if env == "production":
        base += "\nBe extra careful with any data modifications."

    return base

agent = create_agent(
    model=model,
    tools=[...],
    middleware=[context_aware_prompt],
    context_schema=Context
)

5 多智能体

# 1 多智能体系统将复杂的应用拆分成多个协同工作以解决问题的专业化智能体。

# 2 它不依赖于单个智能体来处理每一步,多智能体架构允许您将更小、更专注的智能体组合成一个【协调】的工作流程。

# 3 当出现以下情况时,多智能体系统非常有用:
    单个智能体拥有太多工具,导致其难以决定使用哪个工具。
    上下文或记忆对于单个智能体来说变得过于庞大,无法有效跟踪。
    任务需要专业化(例如,一个规划者、一个研究员、一个数学专家)
    
# 4 举个例子:
	一键生成历史人物讲解视频的智能体
    	-1 输入历史人物:诸葛亮
        -2 使用文本agent--》取出诸葛亮的文字描述
        -3 把文字描述转成语音朗读:工具
        -4 生成视频:使用视频agent
        -5 输出给用户
        -----------多智能体协同工作------------
        

5.1 案例

from langchain.agents import create_agent
from langchain.tools import tool
from langchain_openai import ChatOpenAI

# 1 连接model
model01 = ChatOpenAI(
    model="qwen-plus",
    api_key="sk-6459007b813946289da12857950c955b",
    base_url="https://dashscope.aliyuncs.com/compatible-mode/v1",
)
# 2 创建智能体 agent01
agent01 = create_agent(model=model01)

@tool(
    "agent01",
    description="医疗专家"
)
def doctor(query: str):
    # 写一些逻辑,最终决定使用那个 agent 调用
    result = agent01.invoke({
        "messages": [{"role": "user", "content": query}]
    })
    print('医疗专家回答')
    return result["messages"][-1].content


# 3 连接model2
model2 = ChatOpenAI(
    model="deepseek-chat",
    api_key="sk-c531e222e3674598a2e21338f72722d3",
    base_url="https://api.deepseek.com",
)
# 4 创建智能体2
agent02 = create_agent(model=model2, tools=[doctor])

print(agent02.invoke({"messages": [{"role": "user", "content": '血压80/150正常吗?'}]})['messages'][-1].content)

# 我们触发:是使用agent02,但是最终,是使用了agent01
# 在工具中,根据某种逻辑选择使用那个agent

5.2 案例二:langraph

from typing import Literal

from langchain.agents import AgentState, create_agent
from langchain.messages import AIMessage, ToolMessage
from langchain.tools import tool, ToolRuntime
from langgraph.graph import StateGraph, START, END
from langgraph.types import Command
from typing_extensions import NotRequired
from langchain_openai import ChatOpenAI

# 1. 定义状态
class MultiAgentState(AgentState):
    active_agent: NotRequired[str]


# 2. 定义工具
@tool
def transfer_to_sales(
    runtime: ToolRuntime,
) -> Command:
    """Transfer to the sales agent."""
    last_ai_message = next(
        msg for msg in reversed(runtime.state["messages"]) if isinstance(msg, AIMessage)
    )
    transfer_message = ToolMessage(
        content="Transferred to sales agent from support agent",
        tool_call_id=runtime.tool_call_id,
    )
    return Command(
        goto="sales_agent",
        update={
            "active_agent": "sales_agent",
            "messages": [last_ai_message, transfer_message],
        },
        graph=Command.PARENT,
    )


@tool
def transfer_to_support(
    runtime: ToolRuntime,
) -> Command:
    """Transfer to the support agent."""
    last_ai_message = next(
        msg for msg in reversed(runtime.state["messages"]) if isinstance(msg, AIMessage)
    )
    transfer_message = ToolMessage(
        content="Transferred to support agent from sales agent",
        tool_call_id=runtime.tool_call_id,
    )
    return Command(
        goto="support_agent",
        update={
            "active_agent": "support_agent",
            "messages": [last_ai_message, transfer_message],
        },
        graph=Command.PARENT,
    )
# 3 连接model
model1 = ChatOpenAI(
    model="deepseek-chat",
    api_key="sk-8b83ef66aa604b449bfb3b900405b050",
    base_url="https://api.deepseek.com",
)
# 4. 创建智能体
sales_agent = create_agent(
    model=model1,
    tools=[transfer_to_support],
    system_prompt="You are a sales agent. Help with sales inquiries. If asked about technical issues or support, transfer to the support agent.",
)

# 5 连接model,创建智能体
model02 = ChatOpenAI(
    model="qwen-plus",
    api_key="sk-6459007b813946289da12857950c955b",
    base_url="https://dashscope.aliyuncs.com/compatible-mode/v1",
)
support_agent = create_agent(
    model=model02,
    tools=[transfer_to_sales],
    system_prompt="You are a support agent. Help with technical issues. If asked about pricing or purchasing, transfer to the sales agent.",
)


#  6 触发那个智能体:销售触发销售智能体
def call_sales_agent(state: MultiAgentState) -> Command:
    """Node that calls the sales agent."""
    response = sales_agent.invoke(state)
    return response

 # 7 触发那个智能体:技术支持智触发技术支持智能体
def call_support_agent(state: MultiAgentState) -> Command:
    """Node that calls the support agent."""
    response = support_agent.invoke(state)
    return response


# 8 路由自动选择
def route_after_agent(
    state: MultiAgentState,
) -> Literal["sales_agent", "support_agent", "__end__"]:
    """Route based on active_agent, or END if the agent finished without handoff."""
    messages = state.get("messages", [])

    # Check the last message - if it's an AIMessage without tool calls, we're done
    if messages:
        last_msg = messages[-1]
        if isinstance(last_msg, AIMessage) and not last_msg.tool_calls:
            return "__end__"

    # Otherwise route to the active agent
    active = state.get("active_agent", "sales_agent")
    return active if active else "sales_agent"

# 9 路由初始化
def route_initial(
    state: MultiAgentState,
) -> Literal["sales_agent", "support_agent"]:
    """Route to the active agent based on state, default to sales agent."""
    return state.get("active_agent") or "sales_agent"


# 9 构建Graph
builder = StateGraph(MultiAgentState)
builder.add_node("sales_agent", call_sales_agent)
builder.add_node("support_agent", call_support_agent)

# Start with conditional routing based on initial active_agent
builder.add_conditional_edges(START, route_initial, ["sales_agent", "support_agent"])

# After each agent, check if we should end or route to another agent
builder.add_conditional_edges(
    "sales_agent", route_after_agent, ["sales_agent", "support_agent", END]
)
builder.add_conditional_edges(
    "support_agent", route_after_agent, ["sales_agent", "support_agent", END]
)

graph = builder.compile()

# 10 graph--》内部自动选择使用哪个智能体
result = graph.invoke(
    {
        "messages": [
            {
                "role": "user",
                "content": "Hi, I'm having trouble with my account login. Can you help?",
            }
        ]
    }
)

for msg in result["messages"]:
    print(msg.pretty_print())

6 RAG

# 1 增强检索:
	-公司内部有内部规定,不能作为训练大模型的素材
    -大模型是不知道公司内部规定的
    -问大模型公司规章第三条是什么---》没法准确回答
    -大模型+外挂RAG 方式---》实现 问大模型公司规章第三条是什么--》正确回答
    -RAG叫增强检索

# 2 概念
大型语言模型 (LLM) 功能强大,但它们有两个主要的局限性:
    # 有限的上下文 — 它们无法一次性消化整个语料库。
    # 静态的知识 — 它们的训练数据是固定在某个时间点的。
    # 检索通过在查询时获取相关的外部知识来解决这些问题。这是检索增强生成 (Retrieval-Augmented Generation, RAG) 的基础:用特定于上下文的信息来增强 LLM 的答案
posted @ 2026-03-19 19:58  凫弥  阅读(6)  评论(0)    收藏  举报