阶段4(下):LangChain 1.x、提示词与注入防护、模型侧(路由/vLLM/LoRA)、AI 编程工具与验证、Dify/Coze 专题
This commit is contained in:
1 parent
caaec830b3
commit
7a32d2c9a6
38 files changed
+3059
-67
No files matched your search
@@ -0,0 +1,101 @@
|
||||
# requires: langchain, langchain_core, pydantic
|
||||
"""LangChain 1.x 基础:LCEL 链、结构化输出解析 + 重试 + 备用模型、create_agent 工具调用 + 中间件。
|
||||
|
||||
用假模型离线运行(GenericFakeChatModel 按顺序返回预设消息)。换成真实模型:
|
||||
from langchain.chat_models import init_chat_model
|
||||
model = init_chat_model("openai:gpt-4.1-mini") # 模型名和提供方写法以官方文档为准
|
||||
基于 langchain 1.4 / langchain-core 1.7 编写,以官方文档为准:https://docs.langchain.com/oss/python/langchain/overview
|
||||
运行:python lc_basics.py
|
||||
"""
|
||||
from __future__ import annotations
|
||||
|
||||
from langchain.agents import create_agent
|
||||
from langchain.agents.middleware import ModelCallLimitMiddleware
|
||||
from langchain_core.language_models.fake_chat_models import GenericFakeChatModel
|
||||
from langchain_core.messages import AIMessage
|
||||
from langchain_core.output_parsers import PydanticOutputParser, StrOutputParser
|
||||
from langchain_core.prompts import ChatPromptTemplate
|
||||
from langchain_core.runnables import RunnableLambda
|
||||
from langchain_core.tools import tool
|
||||
from pydantic import BaseModel, Field
|
||||
|
||||
|
||||
def fake(*texts_or_msgs) -> GenericFakeChatModel:
|
||||
msgs = [m if isinstance(m, AIMessage) else AIMessage(content=m) for m in texts_or_msgs]
|
||||
return GenericFakeChatModel(messages=iter(msgs))
|
||||
|
||||
|
||||
# ---------- 1. LCEL:提示词 | 模型 | 解析器 ----------
|
||||
def demo_lcel() -> str:
|
||||
prompt = ChatPromptTemplate.from_messages([
|
||||
("system", "你是客服,用一句话回答。"),
|
||||
("human", "{question}"),
|
||||
])
|
||||
chain = prompt | fake("您好,开发票请在订单详情页申请。") | StrOutputParser()
|
||||
return chain.invoke({"question": "怎么开发票"})
|
||||
|
||||
|
||||
# ---------- 2. 结构化输出:解析 + 失败重试 + 备用模型 ----------
|
||||
class Triage(BaseModel):
|
||||
category: str = Field(pattern="^(refund|bug|question)$")
|
||||
urgent: bool
|
||||
|
||||
|
||||
def demo_structured() -> Triage:
|
||||
parser = PydanticOutputParser(pydantic_object=Triage)
|
||||
prompt = ChatPromptTemplate.from_messages([
|
||||
("system", "对工单分类。{format_instructions}"),
|
||||
("human", "{ticket}"),
|
||||
]).partial(format_instructions=parser.get_format_instructions())
|
||||
|
||||
# 主模型第一次返回了不合法的 JSON,第二次正确:with_retry 对解析失败重试
|
||||
primary = prompt | fake("类别是退款", '{"category": "refund", "urgent": true}') | parser
|
||||
robust = primary.with_retry(stop_after_attempt=2)
|
||||
|
||||
# 主链彻底失败时切换到备用链(真实项目中是另一个模型 / 提供方)
|
||||
def broken(_):
|
||||
raise TimeoutError("主模型超时")
|
||||
backup = prompt | fake('{"category": "question", "urgent": false}') | parser
|
||||
with_fallback = RunnableLambda(broken).with_fallbacks([backup])
|
||||
assert with_fallback.invoke({"ticket": "怎么改地址"}).category == "question"
|
||||
|
||||
return robust.invoke({"ticket": "我要退款,很急"})
|
||||
|
||||
|
||||
# ---------- 3. create_agent:工具调用 + 调用次数限制 ----------
|
||||
class ToolCallingFake(GenericFakeChatModel):
|
||||
"""假模型不支持 bind_tools,这里直接返回自身;真实模型会把工具定义发给接口。"""
|
||||
def bind_tools(self, tools, **kwargs):
|
||||
return self
|
||||
|
||||
|
||||
@tool
|
||||
def get_order_status(order_id: str) -> str:
|
||||
"""查询订单的物流状态。order_id 形如 A1001。"""
|
||||
return {"A1001": "已发货,预计明天送达"}.get(order_id, "订单不存在")
|
||||
|
||||
|
||||
def demo_agent() -> list:
|
||||
model = ToolCallingFake(messages=iter([
|
||||
AIMessage(content="", tool_calls=[{"name": "get_order_status", "args": {"order_id": "A1001"}, "id": "c1"}]),
|
||||
AIMessage(content="您的订单 A1001 已发货,预计明天送达。"),
|
||||
]))
|
||||
agent = create_agent(
|
||||
model,
|
||||
tools=[get_order_status],
|
||||
system_prompt="你是售后客服,查询订单要用工具。",
|
||||
middleware=[ModelCallLimitMiddleware(run_limit=5)], # 防止无限循环
|
||||
)
|
||||
out = agent.invoke({"messages": [{"role": "user", "content": "A1001 到哪了"}]})
|
||||
return out["messages"]
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
print("LCEL:", demo_lcel())
|
||||
t = demo_structured()
|
||||
print("结构化输出:", t)
|
||||
assert t.category == "refund" and t.urgent
|
||||
msgs = demo_agent()
|
||||
for m in msgs:
|
||||
print(f"{type(m).__name__:12s}", m.content or getattr(m, "tool_calls", ""))
|
||||
assert [type(m).__name__ for m in msgs] == ["HumanMessage", "AIMessage", "ToolMessage", "AIMessage"]
|
||||
Reference in new issue
Block a user