阶段4(下):LangChain 1.x、提示词与注入防护、模型侧(路由/vLLM/LoRA)、AI 编程工具与验证、Dify/Coze 专题

This commit is contained in:
Shikui Wang committed 2026-10-23 16:15:33 +08:00
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"]