LangChain 动态 prompt
flyfish
版本
Name: langchain
Version: 1.2.13
@dynamic_prompt 装饰器 实现
文末有源码
expert的回答
Agent 启动成功 (输入 exit/quit 退出)
支持角色:默认(user) / 专家(expert) / 新手(beginner)
User: 解释一下什么是异步编程
Assistant FINAL:
用户询问的是关于异步编程的概念解释。这是一个技术问题,我需要使用专业的技术术语来详细解释异步编程。
异步编程是计算机科学中的一个重要概念,我应该从以下几个方面来解释:
- 基本定义
- 与同步编程的对比
- 核心概念(回调、Promise、async/await等)
- 应用场景
- 优缺点
这个问题不需要使用任何工具函数,因为这是一个纯概念性的技术解释问题。
异步编程详解
一、基本定义
异步编程(Asynchronous Programming) 是一种编程范式,允许程序在执行某些耗时操作(如 I/O 操作、网络请求、文件读写等)时,不阻塞主线程,而是继续执行其他任务,待耗时操作完成后通过回调、事件或 Promise 等方式通知程序。
二、同步 vs 异步对比
| 特性 | 同步编程 | 异步编程 |
|---|---|---|
| 执行方式 | 顺序执行,阻塞等待 | 非阻塞,并发执行 |
| 线程占用 | 长时间占用线程 | 释放线程资源 |
| 响应性 | 较差 | 较好 |
| 代码复杂度 | 较低 | 较高 |
| 适用场景 | 简单任务 | I/O 密集型、网络请求 |
三、核心概念
1. 回调函数(Callback)
javascript
// 异步操作完成后执行的函数
fs.readFile('file.txt', (err, data) => {
if (err) throw err;
console.log(data);
});
2. Promise
javascript
// 表示异步操作最终完成或失败的对象
fetch('/api/data')
.then(response => response.json())
.then(data => console.log(data))
.catch(error => console.error(error));
3. async/await
javascript
// 基于 Promise 的语法糖,使异步代码看起来像同步
async function fetchData() {
try {
const response = await fetch('/api/data');
const data = await response.json();
console.log(data);
} catch (error) {
console.error(error);
}
}
4. 事件循环(Event Loop)
┌─────────────────────────────────────┐
│ 调用栈 (Call Stack) │
├─────────────────────────────────────┤
│ 任务队列 (Task Queue) │
│ ┌─────────────────────────────┐ │
│ │ 回调函数 1 → 回调函数 2 → ... │ │
│ └─────────────────────────────┘ │
├─────────────────────────────────────┤
│ 微任务队列 (Microtask Queue) │
│ (Promise.then, process.nextTick) │
└─────────────────────────────────────┘
四、典型应用场景
| 场景 | 说明 |
|---|---|
| 网络请求 | HTTP 请求、API 调用 |
| 文件 I/O | 读写文件、数据库操作 |
| 定时器 | setTimeout、setInterval |
| 事件监听 | 用户交互、消息队列 |
| 流式处理 | 大数据流、实时数据 |
五、优缺点分析
✅ 优点
- 提高性能:不阻塞主线程,充分利用系统资源
- 增强响应性:UI 不会因后台任务而卡顿
- 可扩展性:支持高并发场景
- 资源效率:减少线程创建和切换开销
❌ 缺点
- 代码复杂度:回调地狱、错误处理困难
- 调试困难:调用栈不清晰,难以追踪
- 内存泄漏风险:事件监听器未正确清理
- 执行顺序控制:需要额外机制保证顺序
六、最佳实践
javascript
// 1. 使用 async/await 替代嵌套回调
// 2. 统一错误处理机制
// 3. 避免内存泄漏(及时取消订阅)
// 4. 使用 Promise.all 并行处理多个异步任务
// 5. 合理设置超时和重试机制
异步编程是现代 Web 开发、Node.js、移动应用等领域的核心技术,理解其原理对于构建高性能、响应式应用至关重要。
beginner的回答
Agent 启动成功 (输入 exit/quit 退出)
支持角色:默认(user) / 专家(expert) / 新手(beginner)
User: 解释一下什么是异步编程
Assistant FINAL:
用户想了解什么是异步编程。这是一个编程概念,我需要用简单易懂的语言来解释,避免使用太多技术术语。
异步编程是编程中的一个重要概念,我应该用生活中的类比来帮助理解。让我用简单的方式解释:
- 什么是异步编程
- 为什么需要它
- 用生活中的例子说明
- 与同步编程的对比
我不需要使用任何工具,因为这是一个概念解释问题,不需要计算或获取时间。
什么是异步编程?
让我用一个生活中的例子来解释:
🍳 同步编程 vs 异步编程
同步编程(排队等待)
想象你在餐厅点餐:
- 你点餐后,必须站在柜台前等待
- 厨师做好一道菜,你才能继续点下一道菜
- 如果一道菜需要10分钟,你就得等10分钟
异步编程(并行处理)
现在想象另一种方式:
- 你点餐后,拿到一个号码牌
- 你可以去旁边玩手机、聊天
- 等菜做好了,号码牌会响,你再去取
- 在等待期间,你可以做其他事情
💡 核心概念
异步编程 就是让程序在等待某些操作(比如读取文件、网络请求)完成时,不卡住等待,而是继续做其他事情。
🌰 实际例子
javascript
// 同步:一个接一个
下载图片1 → 等待完成 → 下载图片2 → 等待完成 → 下载图片3
// 异步:同时开始
下载图片1 → 继续下载图片2 → 继续下载图片3 → 等所有完成
✅ 异步编程的好处
| 好处 | 说明 |
|---|---|
| 不卡顿 | 用户界面不会卡住 |
| 效率高 | 可以同时处理多个任务 |
| 响应快 | 用户不用长时间等待 |
⚠️ 需要注意
- 代码逻辑可能更复杂
- 需要处理"回调"或"Promise"
- 错误处理要更小心
简单总结:异步编程就像"一边等水烧开,一边切菜",而不是"等水烧开了再切菜"。这样能更高效地利用时间!
完整代码
python
from datetime import datetime
import json
from dataclasses import dataclass
from langchain.agents.middleware import dynamic_prompt, ModelRequest
from langchain_openai import ChatOpenAI
from langchain.agents import create_agent
from langchain.tools import tool
# =====================================
# vLLM 配置
# =====================================
VLLM_BASE_URL = "http://192.168.1.3:8000/v1"
VLLM_API_KEY = "empty"
VLLM_MODEL_NAME = "Qwen3.5-35B-A3B"
# =====================================
# LLM 初始化
# =====================================
llm = ChatOpenAI(
model=VLLM_MODEL_NAME,
base_url=VLLM_BASE_URL,
api_key=VLLM_API_KEY,
temperature=0,
)
# =====================================
# 定义上下文数据类(存储动态参数)
# =====================================
@dataclass
class Context:
"""Agent 上下文:用于动态提示词的参数传递"""
user_role: str = "user" # 角色:user(默认)/expert(专家)/beginner(新手)
# =====================================
# 动态提示词中间件
# =====================================
@dynamic_prompt
def dynamic_system_prompt(request: ModelRequest) -> str:
"""
根据上下文的 user_role 动态生成系统提示词
"""
# 从运行时上下文获取用户角色
user_role = request.runtime.context.user_role
base_prompt = "You are a helpful AI assistant."
# 专家模式:技术细节、专业术语
if user_role == "expert":
prompt = f"{base_prompt} Provide detailed technical responses, use professional terminology."
# 新手模式:简单解释、无术语、通俗易懂
elif user_role == "beginner":
prompt = f"{base_prompt} Explain concepts simply and avoid jargon, use easy-to-understand language."
# 默认模式:通用助手
else:
prompt = base_prompt
return prompt
# =====================================
# 定义工具(Tools)
# =====================================
@tool
def multiply(a: int, b: int) -> int:
"""Multiply two numbers"""
print("\n[TOOL EXECUTE] multiply 被调用")
print(f"[TOOL INPUT] a={a}, b={b}")
result = a * b
print(f"[TOOL RESULT] {result}\n")
return result
@tool
def add(a: int, b: int) -> int:
"""Add two numbers"""
print("\n[TOOL EXECUTE] add 被调用")
print(f"[TOOL INPUT] a={a}, b={b}")
result = a + b
print(f"[TOOL RESULT] {result}\n")
return result
@tool
def get_time() -> str:
"""Get current system time"""
print("\n[TOOL EXECUTE] get_time 被调用")
now = str(datetime.now())
print(f"[TOOL RESULT] {now}\n")
return now
tools = [multiply, add, get_time]
# =====================================
# 创建 Agent:使用动态提示词+上下文
# =====================================
agent = create_agent(
model=llm,
tools=tools,
# 移除静态 system_prompt,使用动态中间件
middleware=[dynamic_system_prompt],
# 绑定上下文数据结构
context_schema=Context
)
# =====================================
# 调试函数
# =====================================
def print_messages(messages):
print("\n========== 发送给大模型的 Messages ==========")
for m in messages:
role = m["role"]
content = m["content"]
print(f"\nROLE: {role}")
print("CONTENT:")
print(content)
print("\n============================================\n")
def print_response(resp):
print("\n========== 大模型返回完整结果 ==========")
for msg in resp["messages"]:
print("\nROLE:", msg.type)
print("CONTENT:")
print(msg.content)
print("\n=======================================\n")
# =====================================
# CLI:支持传入角色上下文(动态提示词生效)
# =====================================
def main():
print("\nAgent 启动成功 (输入 exit/quit 退出)")
print("支持角色:默认(user) / 专家(expert) / 新手(beginner)\n")
while True:
query = input("User: ")
if query.lower() in ["exit", "quit"]:
break
messages = [{"role": "user", "content": query}]
# ========================
# 调用时传入上下文角色
# 可自由切换:expert / beginner / user
# ========================
result = agent.invoke(
{"messages": messages},
# 动态传递角色,这里默认用 expert,可以改成 beginner 测试
context=Context(user_role="expert")
)
# 输出结果
print("Assistant FINAL:")
print(result["messages"][-1].content)
print()
if __name__ == "__main__":
main()