外观
LangChain 系统学习文档
适合人群:已经掌握前端或 Node.js 基础,想进入 AI 应用开发、智能体开发、RAG 问答、工具调用和 LLM 全栈项目的学习者。
学习目标:从大模型应用的基本概念入门,逐步掌握 LangChain 的模型接口、消息、提示词、工具、结构化输出、检索增强生成、Agent、流式输出、观测与生产实践。
核对时间:2026-06-25。本文以 LangChain 官方文档当前信息为基础整理。官方当前将 LangChain 定位为可配置的 agent harness,用于组合模型、工具、提示词和中间件;LangChain agents 构建在 LangGraph 之上。
官方资料:
- LangChain Python 文档:https://docs.langchain.com/oss/python/langchain/overview
- LangChain Tools:https://docs.langchain.com/oss/python/langchain/tools
- LangChain Retrieval:https://docs.langchain.com/oss/python/langchain/retrieval
- LangChain Structured Output:https://docs.langchain.com/oss/python/langchain/structured-output
- LangChain JS 文档:https://docs.langchain.com/oss/javascript/langchain/overview
目录
第一篇:基础认知
- LangChain 是什么
- LLM 应用和传统应用的区别
- LangChain、LangGraph、LangSmith 的关系
- 前端开发者为什么要学 LangChain
- 安装与第一个 Agent
第二篇:核心组件
- Chat Model
- Message
- Prompt
- Tool
- Structured Output
- Streaming
- Middleware
第三篇:RAG 与知识库
- RAG 是什么
- Document Loader
- Text Splitter
- Embedding
- Vector Store
- Retriever
- RAG 问答链路
第四篇:Agent 应用
- Agent 是什么
- 工具调用流程
- create_agent
- 多轮上下文和短期记忆
- Human-in-the-loop
- Guardrails
第五篇:工程化与生产
- LangChain 项目结构
- 前端 + 后端 + LangChain 架构
- 观测、追踪与评估
- 成本、延迟和稳定性
- 常见误区
- 学习路线和项目实战
第一篇:基础认知
1. LangChain 是什么
LangChain 是一个用于构建 LLM 应用和 Agent 应用的框架。它把大模型应用中常见的能力抽象成组件,例如:
- 模型调用
- 消息结构
- 提示词
- 工具调用
- 结构化输出
- 检索增强生成
- Agent 执行循环
- 中间件
- 流式输出
- 观测与调试
普通调用大模型可能只是:
python
response = model.invoke("请解释什么是 RAG")LangChain 更关注完整应用:
text
用户问题
-> 提示词组装
-> 检索相关文档
-> 调用模型
-> 必要时调用工具
-> 返回结构化结果或流式文本
-> 记录 trace,便于调试和评估如果你从前端转 AI 全栈,可以把 LangChain 理解成“AI 应用层的框架”。它不像 Vue/React 负责页面渲染,而是负责把模型、工具、数据和业务流程组织起来。
2. LLM 应用和传统应用的区别
传统应用更确定:
text
输入 -> 业务逻辑 -> 数据库 -> 输出同样输入通常得到同样输出。
LLM 应用更不确定:
text
输入 -> Prompt -> 模型推理 -> 可能调用工具 -> 生成输出它的特点:
- 输出具有概率性。
- Prompt 会影响行为。
- 模型可能误解用户意图。
- 工具调用可能失败。
- 成本和延迟不可忽视。
- 需要观测和评估。
因此 AI 全栈工程师不只是会调 API,还要会设计:
- 输入边界
- Prompt
- 工具权限
- 数据检索
- 结构化输出
- 错误兜底
- 评估指标
- 用户体验
3. LangChain、LangGraph、LangSmith 的关系
可以这样理解:
text
LangChain:Agent 和 LLM 应用开发框架,提供模型、工具、Agent harness 等抽象。
LangGraph:底层有状态编排运行时,适合复杂、长期运行、可恢复的 Agent。
LangSmith:观测、追踪、评估、调试和部署相关平台。选择:
- 只想快速做一个工具调用 Agent:先用 LangChain。
- 需要复杂状态流、人类审批、可中断、可恢复:使用 LangGraph。
- 需要调试、评估、观测:使用 LangSmith。
LangChain 官方当前文档强调:LangChain 的 agents 构建在 LangGraph 之上,因此学习顺序可以是:
text
LLM 基础 -> LangChain 组件 -> LangChain Agent -> LangGraph 状态编排4. 前端开发者为什么要学 LangChain
前端开发者转 AI 全栈有天然优势:
- 擅长产品交互和用户体验。
- 理解异步请求、状态管理、流式渲染。
- 熟悉 TypeScript,适合写类型安全的 AI 前端和 API 层。
- 能把 AI 能力包装成可用产品,而不是停留在脚本。
LangChain 对你有三类价值:
- 学会 AI 后端如何组织模型、工具和数据。
- 理解前端聊天 UI 背后的 Agent 运行机制。
- 能做 RAG、智能客服、文档问答、代码助手、业务自动化工具。
5. 安装与第一个 Agent
Python 安装:
bash
pip install -U langchain如果使用特定模型提供商,需要安装对应集成包。例如:
bash
pip install -U "langchain[openai]"示例:
python
from langchain.agents import create_agent
def get_weather(city: str) -> str:
"""Get weather for a given city."""
return f"{city} 今天适合学习 AI 全栈。"
agent = create_agent(
model="openai:gpt-5.5",
tools=[get_weather],
system_prompt="你是一个简洁可靠的助手。",
)
result = agent.invoke({
"messages": [
{"role": "user", "content": "上海天气怎么样?"}
]
})
print(result["messages"][-1].content_blocks)注意:本文使用的 create_agent、content_blocks、middleware 等 API 属于 LangChain 1.0,请安装 langchain>=1.0(0.x 版本没有这些 API)。示例中的模型名称要根据你实际使用的模型提供商配置;工程中不要把 API Key 写进代码,应使用环境变量。
5.1 深入:create_agent 的完整签名与内部循环
create_agent(langchain.agents,Since v1.0)返回的不是黑盒对象,而是一个编译好的 LangGraph 图(CompiledStateGraph)。完整参数:
| 参数 | 类型 | 作用 |
|---|---|---|
model | str | BaseChatModel | 模型字符串(如 "openai:gpt-5.5")或 chat model 实例 |
tools | Sequence[...] | None | 工具列表;为空时退化成纯模型节点(无 tool loop) |
system_prompt | str | SystemMessage | None | 字符串会被自动转成 SystemMessage 放到消息列表开头 |
middleware | Sequence[AgentMiddleware] | 中间件序列(见第 12 章) |
response_format | strategy / type / dict | 结构化输出配置(见第 10 章) |
state_schema | type[AgentState] | 扩展默认状态字段 |
context_schema | type[ContextT] | 运行时 context 的 schema |
checkpointer / store | - | 短期(thread 内)/ 长期(跨 thread)记忆 |
interrupt_before / interrupt_after | list[str] | None | 静态断点节点名(调试用) |
name | str | None | 作为子图嵌入大图时的节点名 |
cache | BaseCache | None | 图执行缓存 |
模型字符串写法是 "<provider>:<model>"(例如 "openai:gpt-5.5"),需要安装对应集成包;想显式控制初始化参数,用 init_chat_model("openai:gpt-5.5", ...) 拿到实例再传给 model。
官方文档描述的循环:
text
agent 节点:应用 system prompt 后调用模型
-> AIMessage 含 tool_calls?
是 -> tools 节点执行工具,结果作为 ToolMessage 追加进 messages
-> 回到 agent 节点再次调用模型
否 -> 循环结束,返回完整 messages 列表💡 因为返回值就是 LangGraph 编译图,invoke / stream / astream / get_state_history / checkpointer 等图级 API 全部可用——这就是"LangChain agents 构建在 LangGraph 之上"的具体含义。
第二篇:核心组件
6. Chat Model
Chat Model 是聊天模型接口。它接收消息列表,返回模型消息。
概念上类似:
python
messages = [
{"role": "system", "content": "你是一个后端架构师"},
{"role": "user", "content": "如何设计 RAG 系统?"}
]模型接口要关注:
- 模型提供商
- 模型名称
- temperature
- max tokens
- timeout
- retry
- streaming
- tool calling 支持情况
在 AI 全栈项目中,模型层最好封装,避免业务代码到处直接依赖某个供应商。
6.1 补充:init_chat_model 与 provider:model 字符串
跨供应商统一入口是 init_chat_model:init_chat_model("openai:gpt-5.5") 形式的 provider:model 字符串会自动解析到对应集成包(需已安装 langchain-openai 等)。工程建议:在 services/ 层集中一处把配置(模型名、temperature、timeout)转成 chat model 实例,业务代码只接收实例;这样换供应商或加 ModelFallbackMiddleware 都不用改调用点。⚠️ temperature 等参数是否生效取决于具体模型(部分推理模型不接受该参数),以提供商文档为准。
7. Message
Message 是 LLM 应用的基本上下文单位。
常见角色:
- system:系统指令,定义助手身份、规则和边界。
- user:用户输入。
- assistant:模型回复。
- tool:工具执行结果。
多轮对话不是简单字符串拼接,而是一组消息:
python
[
{"role": "system", "content": "你是客服助手"},
{"role": "user", "content": "我想退款"},
{"role": "assistant", "content": "请提供订单号"},
{"role": "user", "content": "订单 123"}
]前端聊天 UI 中的消息状态,最终也会转换成后端模型可理解的消息格式。
8. Prompt
Prompt 不只是“提示词”,而是模型行为的配置入口。
好的 Prompt 包含:
- 角色
- 任务目标
- 输入上下文
- 输出格式
- 约束条件
- 示例
- 异常处理方式
示例:
text
你是一个技术文档助手。
任务:根据给定资料回答用户问题。
要求:
1. 只基于资料回答。
2. 不确定时说不知道。
3. 输出 Markdown。
4. 最后列出引用来源。Prompt 设计要避免:
- 指令过长但没有结构。
- 输出格式不明确。
- 要求模型做精确计算。
- 没有说明无法回答时怎么办。
9. Tool
Tool 让模型能调用外部能力,例如:
- 查询数据库
- 搜索文档
- 调用 API
- 执行计算
- 创建工单
- 查询订单状态
工具本质上是有输入和输出的函数。模型决定何时调用工具,应用负责真正执行工具。
示例:
python
def get_order_status(order_id: str) -> str:
"""查询订单状态。"""
return "订单已发货"工具设计原则:
- 名称清晰。
- 描述准确。
- 参数结构明确。
- 返回结果简洁。
- 权限边界严格。
- 不把危险操作直接暴露给模型。
例如删除订单、退款、发送邮件这类操作要加入人工确认或审批。
9.1 底层机制:tool calling 在 API 层发生了什么
模型拿到的不是函数本身,而是函数的 JSON Schema 描述。完整链路:
text
1. 你定义 @tool 函数 -> LangChain 转成工具 schema(name / description / args JSON Schema)
2. 请求模型 API:schema 列表随 messages 一起发给模型提供商
3. 模型决定调用与否:返回带 tool_calls 字段的 AIMessage(name、args、id)
4. 应用侧(ToolNode)真正执行:按 name 找到本地函数、校验并填入 args
5. 结果包装为 ToolMessage(带上对应 tool_call_id)追加进 messages
6. 再次把消息列表发给模型,直到某轮响应不再出现 tool_calls,循环结束关键认知:
- 模型从不执行任何代码,它只是“写出调用申请”。执行、校验、错误处理都是应用的责任——这也是工具错误消息要写成“可恢复提示”而不是裸
failed的原因。 tool_calls/ToolMessage是 LangChain 统一抽象后的消息字段,不同提供商(OpenAI 的 tools、Anthropic 的 tool_use)由集成包在底层做格式翻译。- 模型可以一次返回多个 tool_calls(并行工具调用)。⚠️ 每个
tool_call_id都必须有对应的 ToolMessage 返回,缺一个就可能在提供商侧报错或被模型误读为工具丢失。
9.2 @tool 装饰器与 ToolRuntime
python
from langchain.tools import tool
@tool
def search_database(query: str, limit: int = 10) -> str:
"""Search the customer database for records matching the query."""
return f"Found {limit} results for '{query}'"要点:
- docstring 自动成为工具描述,类型注解生成输入 schema——这两者是模型选择和调用工具的核心依据,要像写 API 文档一样写它们。
- 工具名建议
snake_case:部分提供商会拒绝或异常处理含空格、特殊字符的名字。可用@tool("web_search")覆盖名字、description=覆盖描述。 - 复杂参数用
@tool(args_schema=WeatherInput)配 Pydantic 模型 +Field(description=...)。 - ⚠️ 参数名
config和runtime是保留名,用作工具入参会在运行时报错。 - 工具内访问运行时信息的推荐方式是声明
runtime: ToolRuntime参数(取代旧的InjectedState、InjectedStore、get_runtime())。该参数由 ToolNode 注入,对模型不可见:runtime.state读当前会话状态、runtime.context读本次调用的只读配置(如 user_id)、runtime.store读写跨会话长期记忆。
python
from langchain.tools import tool, ToolRuntime
@tool
def get_user_preference(pref_name: str, runtime: ToolRuntime) -> str:
"""Get a user preference value."""
preferences = runtime.state.get("user_preferences", {})
return preferences.get(pref_name, "Not set")- 返回值类型决定行为:返回
str→ 转成人读的 ToolMessage;返回dict→ 序列化为结构化工具输出,模型可按字段推理;返回Command→ 直接写图状态(记得同时带上ToolMessage并填tool_call_id=runtime.tool_call_id,让模型看到结果)。
10. Structured Output
结构化输出让模型返回可解析的数据,而不是自由文本。
场景:
- 表单抽取
- 分类
- 评分
- 路由
- 生成数据库字段
- 前端组件渲染数据
示例目标:
json
{
"intent": "refund",
"order_id": "123",
"confidence": 0.92
}结构化输出对前端很重要,因为 UI 通常需要稳定字段,而不是一段不可控文本。
建议:
- 用 schema 描述输出结构。
- 对枚举值做约束。
- 对模型输出做校验。
- 校验失败要有重试或兜底。
10.1 内部机制:两种实现策略
create_agent 用 response_format 参数处理结构化输出,结果放在最终 state 的 structured_response 键里。底层有两条路径(langchain.agents.structured_output):
| 策略 | 机制 | 适用 |
|---|---|---|
ProviderStrategy | 走提供商原生 structured output(OpenAI、Anthropic、Gemini、xAI 等支持),由提供商侧保证 schema 合规 | 模型支持时最可靠,优先用 |
ToolStrategy | 把 schema 包装成一个“伪工具”注入 tool calling,诱导模型调用它来“提交”结果,本地再做 Pydantic 校验 | 其余所有支持 tool calling 的模型 |
直接传 schema 类型时,LangChain 根据模型能力自动选策略;⚠️ 裸 JSON Schema dict 必须显式包一层 strategy,不会被自动识别。
python
from pydantic import BaseModel, Field
from typing import Literal
from langchain.agents import create_agent
from langchain.agents.structured_output import ToolStrategy
class ProductReview(BaseModel):
"""Analysis of a product review."""
rating: int | None = Field(description="The rating of the product", ge=1, le=5)
sentiment: Literal["positive", "negative"] = Field(description="The sentiment of the review")
agent = create_agent(
model="gpt-5.5",
tools=tools,
response_format=ToolStrategy(ProductReview),
)
result = agent.invoke({"messages": [...]})
result["structured_response"] # ProductReview 实例(Pydantic)或 dict(dataclass/TypedDict)Schema 类型支持:Pydantic 模型(返回校验后的实例)、dataclass / TypedDict(返回 dict)、JSON Schema(返回 dict)。ToolStrategy 还有 handle_errors 参数控制校验失败后的重试行为(True 默认用错误模板消息让模型重试 / 自定义 str / 异常类型 / callable / False 直接抛异常),对应异常如 StructuredOutputValidationError、MultipleStructuredOutputsError。
⚠️ 注意事项:
- 同时给了 tools 和 response_format 时,模型必须支持 tools 与 structured output 并用,个别提供商会冲突。
ToolStrategy的结果本质仍是“模型调了一个工具”,不是提供商级硬约束,务必保留本地校验 + 重试。- 结构化输出约束的是最终提交格式,不保证中间推理正确——字段内容质量仍要靠评测。
11. Streaming
Streaming 是流式输出。它让用户不必等待完整回复生成完才看到结果。
前端体验:
text
模型一边生成,页面一边显示。常见实现:
- Server-Sent Events
- WebSocket
- fetch readable stream
流式输出要处理:
- loading 状态
- 中断请求
- token 增量渲染
- 工具调用中间状态
- 错误提示
- 最终消息落库
AI 产品的体验差异,很大一部分来自流式交互是否顺滑。
12. Middleware
Middleware 用于在 Agent 执行过程中插入横切逻辑,例如:
- 日志
- 重试
- 输入过滤
- 输出校验
- 工具调用限制
- 模型路由
- 成本统计
- 安全策略
前端开发者可以类比:
- Express/NestJS 中间件
- Vue Router guard
- Axios interceptor
AI 应用中的中间件能让你把业务逻辑和治理逻辑分开,避免 Agent 主流程越来越乱。
12.1 内部机制:两类钩子
中间件不是独立运行时——钩子跑在 create_agent 编译出的那张 LangGraph 图内部。官方把钩子分成两种风格:
Node-style(顺序执行,在固定切点):
| 钩子 | 触发时机 |
|---|---|
before_agent | 每次调用开始前一次 |
before_model | 每次模型调用前 |
after_model | 每次模型响应后 |
after_agent | 每次调用结束后一次 |
Wrap-style(环绕执行,自己决定 handler 调 0/1/N 次):
| 钩子 | 触发时机 |
|---|---|
wrap_model_call | 包住每次模型调用(可做重试、缓存、改写请求) |
wrap_tool_call | 包住每次工具调用(可做监控、动态工具) |
用装饰器或类实现,节点式钩子返回 dict 直接经 reducer 合入 state:
python
from langchain.agents.middleware import before_model, AgentState
from langchain.messages import AIMessage
from langgraph.runtime import Runtime
from typing import Any
@before_model(can_jump_to=["end"])
def check_message_limit(state: AgentState, runtime: Runtime) -> dict[str, Any] | None:
if len(state["messages"]) >= 50:
return {"messages": [AIMessage("Conversation limit reached.")], "jump_to": "end"}
return Nonewrap 式示例(模型调用重试):
python
from langchain.agents.middleware import wrap_model_call, ModelRequest, ModelResponse
from typing import Callable
@wrap_model_call
def retry_model(request: ModelRequest, handler: Callable[[ModelRequest], ModelResponse]) -> ModelResponse:
for attempt in range(3):
try:
return handler(request)
except Exception:
if attempt == 2:
raise12.2 内置中间件清单
官方提供一批生产可用的 prebuilt middleware(langchain.agents.middleware),常用:
| 中间件 | 用途 |
|---|---|
ToolRetryMiddleware / ModelRetryMiddleware | 工具/模型调用指数退避重试 |
ModelFallbackMiddleware | 主模型失败自动切换备用模型 |
SummarizationMiddleware | 接近 token 上限时自动摘要历史 |
HumanInTheLoopMiddleware | 指定工具调用前暂停等人工审批(interrupt_on={"send_email": True},按工具 .name 匹配) |
PIIMiddleware | PII 检测与脱敏 |
ModelCallLimitMiddleware / ToolCallLimitMiddleware | 限制模型/工具调用次数防失控 |
TodoListMiddleware | 给 Agent 加任务规划清单 |
LLMToolSelectorMiddleware | 先用一次 LLM 挑选相关工具再进主调用(大工具集省 token) |
💡 中间件让 create_agent 生成的整个 Agent(含全部钩子)可以作为节点/子图嵌入更大的 StateGraph,HITL 暂停、摘要、PII 脱敏都会跟着子图走——这是 LangChain 与 LangGraph 组合使用的官方姿势。
⚠️ 注意:多个中间件都改同一非 reducer 状态字段时,最外层(outer)的值胜出;messages 这类带 reducer 的字段则是叠加。若外层中间件重试导致 handler() 被调多次,之前那次调用产生的状态更新会被丢弃。
第三篇:RAG 与知识库
13. RAG 是什么
RAG 是 Retrieval-Augmented Generation,检索增强生成。
它解决的问题:
text
模型不知道你的私有文档、公司制度、项目代码和数据库内容。RAG 流程:
text
用户问题
-> 向量化
-> 检索相关文档
-> 把文档塞进 Prompt
-> 模型基于文档回答RAG 不是“把所有资料都丢给模型”。它的关键是检索出足够相关、足够少、足够可信的上下文。
14. Document Loader
Document Loader 负责读取资料来源:
- Markdown
- Word
- HTML
- Notion
- 数据库
- Git 仓库
- 网页
读取后通常得到 Document:
text
page_content:正文
metadata:来源、标题、路径、页码、更新时间metadata 很重要。没有来源信息,回答就很难追溯。
15. Text Splitter
Text Splitter 负责切分文档。
为什么要切分:
- 文档太长,不能全部放入上下文。
- 检索需要更小粒度。
- 模型需要集中上下文。
切分策略:
- 按标题切分。
- 按段落切分。
- 按固定 token 长度切分。
- 保留 overlap,避免上下文断裂。
切分过小会丢上下文,切分过大又会降低检索精度。工程中需要根据文档类型调参。
15.1 内部机制与常见陷阱
- chunk 大小和 overlap 都以 token 计(
RecursiveCharacterTextSplitter按分隔符列表递归尝试,默认["\n\n", "\n", " ", ""]——从段落、换行到字符逐级退化,优先保住语义边界),而不是按字符数硬切——这是它比朴素定长切分好的原因。 - ⚠️ 表格、代码块、列表被拦腰切断是 RAG 最常见的静默故障:切片本身 embedding 得分不低,检索也能召回,但模型拿到的是残缺结构,答非所问。Markdown 文档要按标题层级切(保留 heading 路径作为前缀或 metadata),代码按函数/类边界切。
- embedding 模型对输入长度有截断上限;chunk 太大不会报错,只会“悄悄只编码前半段”,检索质量无声下降。⚠️ 入库和查询必须用同一个 embedding 模型和维度——换模型 = 全量重建索引,否则新旧向量不在同一空间。
- 把标题、来源、更新时间拼进 chunk 文本(而不只放 metadata),往往比调 overlap 更有效,因为 embedding 只“看得见”文本。
16. Embedding
Embedding 把文本变成向量。
语义相似的文本,向量距离更近。
例如:
text
"如何退款" 和 "我要申请退货退款" 语义接近Embedding 模型选择要考虑:
- 中文效果
- 多语言效果
- 向量维度
- 成本
- 速度
- 是否支持本地部署
17. Vector Store
Vector Store 用于存储和检索向量。
常见选择:
- Chroma
- FAISS
- Milvus
- Qdrant
- Weaviate
- pgvector
- Elasticsearch/OpenSearch 向量检索
前端 AI 全栈路线中,建议先用轻量方案:
text
本地学习:Chroma 或 FAISS
全栈项目:PostgreSQL + pgvector 或托管向量库18. Retriever
Retriever 负责根据用户问题取回相关文档。
常见策略:
- 相似度检索
- MMR 去冗余
- metadata 过滤
- hybrid search
- rerank
示例:
text
用户问:“如何配置 Docker Compose?”
Retriever 返回 Docker 文档中最相关的几个片段。检索质量直接决定 RAG 回答质量。很多 RAG 项目失败不是模型不行,而是检索上下文错了。
18.1 策略的底层逻辑与选择
| 策略 | 解决什么问题 | 机制 |
|---|---|---|
| 相似度(vector search) | 语义改写、同义词 | 查询向量与文档向量的距离排序;对专有名词/ID/罕见词不敏感 |
| BM25 / keyword | 精确词命中 | 词频统计,纯字面匹配;不懂同义 |
| hybrid search | 上面两者的盲区互补 | 两路召回后融合排序(如 RRF),需要向量库支持 |
| MMR | top-k 里全是近重复片段 | 相关性 × 多样性联合选择,牺牲一点精度换覆盖 |
| metadata filter | 全库检索噪音大、权限过滤 | 先用结构化条件缩小候选集,属于 pre-filter,效果稳定 |
| rerank | 召回快但排序粗 | 两阶段:向量粗召回 N 条 -> cross-encoder 精排取 top-k,延迟换精度 |
⚠️ 常见陷阱:
- “相似度分数高”不等于“对回答有用”:向量检索对数字、代码符号、专有名词区分力弱,这类查询优先 hybrid。
- 盲目加大 top-k 会让上下文膨胀,模型在长上下文中间部分注意力反而下降(lost in the middle),常见最优区间其实很小(几条到十几条),要靠评测集找拐点,不要拍脑袋。
- filter 条件和 embedding 是两个正交机制:权限、时效这类硬约束必须走 metadata filter,绝不能指望模型“自己注意”。
19. RAG 问答链路
一个最小 RAG 系统包含:
text
文档导入
-> 切分
-> embedding
-> 入库
-> 用户提问
-> 检索
-> 组装 Prompt
-> 模型回答
-> 前端展示引用来源生产级 RAG 还要考虑:
- 文档增量更新
- 权限过滤
- 引用来源
- 幻觉控制
- 评估集
- 召回率和准确率
- 用户反馈
第四篇:Agent 应用
20. Agent 是什么
Agent 是能根据目标自主选择步骤、调用工具并完成任务的系统。
普通聊天:
text
用户问 -> 模型答Agent:
text
用户目标
-> 模型判断需要什么工具
-> 调用工具
-> 观察结果
-> 继续推理
-> 最终回答或执行动作例如:
text
“帮我查订单 123 的物流,并判断是否需要安抚用户”Agent 可能会:
- 调用订单查询工具。
- 调用物流查询工具。
- 判断物流延迟。
- 生成客服回复。
21. 工具调用流程
工具调用通常包含:
- 模型根据用户意图选择工具。
- 模型生成工具参数。
- 应用执行工具。
- 工具结果返回模型。
- 模型基于结果继续回答。
风险点:
- 参数错误。
- 工具失败。
- 工具返回太长。
- 工具结果被模型误解。
- 工具权限过大。
工程中要对工具调用做:
- 参数校验
- 超时控制
- 错误处理
- 权限控制
- 日志追踪
- 高风险操作确认
22. create_agent
LangChain 当前提供 create_agent 用于创建可配置 Agent。
核心输入:
- model
- tools
- system_prompt
- middleware
示例结构:
python
agent = create_agent(
model=model,
tools=[search_docs, query_order],
system_prompt="你是一个企业知识库助手。",
)建议先用 LangChain Agent 做快速验证。如果流程变得复杂,例如需要状态图、可中断审批、持久化恢复,再切到 LangGraph。
23. 多轮上下文和短期记忆
多轮对话需要保存上下文。
最简单方式:
text
把最近 N 轮消息传给模型问题:
- 上下文越来越长。
- 成本越来越高。
- 无关历史干扰当前回答。
优化方式:
- 只保留最近窗口。
- 摘要历史。
- 结构化保存用户偏好。
- 把长期记忆放入数据库或向量库。
前端聊天系统要区分:
- UI 消息历史
- 模型上下文
- 数据库存档
- 长期记忆
它们不是同一个东西。
23.1 上下文工程(context engineering):LangChain 官方框架
官方文档把“Agent 为什么不可靠”归结为两点:模型能力不够,或喂给模型的上下文不对——实践里绝大多数失败是后者,因此上下文工程被称为 AI 工程师的第一职责。LangChain 把可控对象分成三类:
| 上下文类型 | 控制什么 | 暂态/持久 |
|---|---|---|
| Model Context | 进入模型调用的内容:指令、历史、工具列表、response format | 暂态(只影响本次调用,不改存档) |
| Tool Context | 工具能读写什么:state、store、runtime context | 持久(写入状态) |
| Life-cycle Context | 步骤之间发生什么:摘要、guardrails、日志 | 持久 |
对应三个数据源,别混淆:
| 数据源 | 别名 | 作用域 | 例子 |
|---|---|---|---|
| Runtime Context | 静态配置 | 会话内只读 | user_id、权限、连接配置 |
| State | 短期记忆 | 会话内可写 | messages、工具结果、计数器 |
| Store | 长期记忆 | 跨会话 | 用户偏好、抽取的事实 |
LangChain 的实现抓手正是第 12 章的 middleware:wrap_model_call 里 request.override(system_message=..., tools=...) 修改暂态上下文而不污染 state;要持久修改则返回带 Command 的 ExtendedModelResponse 走 reducer 合入。常见陷阱:
- ⚠️ 把检索到的大段文档、工具原始 JSON 全量塞进 messages 并留在 state 里:每一轮都重复计费,还会挤掉真正重要的指令。正确做法是暂态注入(本轮可见)或摘要/落盘引用。
- ⚠️ 用
SummarizationMiddleware时,摘要是有损压缩——工具调用对(AIMessage tool_calls + ToolMessage)不能被拆散摘要,否则消息历史非法、提供商直接报错。 - 长上下文不等于更多信息利用:官方建议控制进入模型的 token 总量(context editing / trimming 中间件就是干这个的),质量优先于数量。
24. Human-in-the-loop
Human-in-the-loop 指人类参与 Agent 流程,常见于:
- 审批退款
- 确认发送邮件
- 修改自动生成内容
- 审核数据库变更
- 确认高风险工具调用
前端要提供:
- 暂停状态展示
- 审批按钮
- 修改输入框
- 操作记录
- 继续执行入口
复杂 HITL 流程更适合 LangGraph,因为它更擅长保存状态、暂停和恢复。
24.1 LangChain 层的现成方案:HumanInTheLoopMiddleware
在 create_agent 里不用手写暂停逻辑——内置的 HumanInTheLoopMiddleware 按工具名拦截,命中即触发 LangGraph interrupt 等待外部决策:
python
from langchain.agents import create_agent
from langchain.agents.middleware import HumanInTheLoopMiddleware
agent = create_agent(
model="gpt-5.5",
tools=[read_email, send_email],
middleware=[HumanInTheLoopMiddleware(interrupt_on={"send_email": True})],
)机制上它就是第 12 章 wrap_tool_call 类钩子的官方预置实现:interrupt_on 的 key 匹配工具的 .name(@tool 装饰的函数默认取函数名)。调用方看到 interrupt payload 后,用 Command(resume=...) 传回 approve/edit/reject 决策。⚠️ 前提与 LangGraph 相同:必须配 checkpointer 和稳定的 thread_id,否则暂停后无法恢复。
25. Guardrails
Guardrails 是保护栏,用于限制模型行为。
常见方式:
- 输入过滤
- 输出校验
- 工具权限控制
- 敏感信息脱敏
- 结构化输出 schema
- 人工审批
- 策略提示词
不要只依赖 Prompt 安全。真正可靠的系统要在代码层、工具层、权限层和产品层共同限制风险。
25.1 分层实施与 prompt injection
Guardrails 按拦截位置分层,各层能力不同,缺一不可:
| 层 | 时机 | 手段 | 特点 |
|---|---|---|---|
| 输入层 | 模型调用前 | before_model 钩子:内容过滤、PII 检测(PIIMiddleware)、长度/语言校验 | 便宜,但挡不住语义级攻击 |
| 决策层 | 工具调用前 | HumanInTheLoopMiddleware、ToolCallLimitMiddleware、工具参数 schema 校验 | 唯一能拦“合法格式、非法意图”的层 |
| 输出层 | 模型响应后 | after_model / 结构化输出校验(ToolStrategy + handle_errors)、敏感词与泄露检查 | 防不合规内容到达用户 |
| 权限层 | 工具执行时 | API Key 最小权限、只读凭证、写操作 dry-run | 模型被说服也越不过去 |
💡 prompt injection 的核心认识:检索到的文档、网页、用户文件里的“指令”会进入模型上下文,模型无法从根本上区分“老板的指示”和“恶意网页里藏的一句话”。⚠️ 所以防线不可能全在 prompt 层(写“忽略注入指令”只是提高成本,不是保证),真正可靠的是让被注入的模型手里没有危险能力:写操作走 HITL 审批、凭证按工具粒度下发、外部内容永远不解锁新工具。这也是第 9 章“权限边界严格、不把危险操作直接暴露给模型”的工程理由。
第五篇:工程化与生产
26. LangChain 项目结构
推荐结构:
text
ai-service/
├── app/
│ ├── agents/
│ ├── tools/
│ ├── retrievers/
│ ├── prompts/
│ ├── schemas/
│ └── services/
├── data/
├── tests/
└── pyproject.toml职责:
agents:Agent 组装。tools:工具函数。retrievers:检索器。prompts:提示词模板。schemas:结构化输入输出。services:业务服务和数据库访问。
27. 前端 + 后端 + LangChain 架构
适合你的方向:
text
Vue/Nuxt 前端
-> NestJS API 网关
-> Python FastAPI AI 服务
-> LangChain/LangGraph
-> PostgreSQL + pgvector
-> Redis/队列为什么不一定把 AI 全写在 Node.js:
- Python AI 生态更完整。
- 很多模型、向量库、数据处理工具优先支持 Python。
- 前端/Node 仍然负责产品体验、鉴权、业务 API 和系统集成。
但 TypeScript 也有 LangChain JS,可用于轻量 AI 能力、前端原型、Node API 中的简单 Agent。
28. 观测、追踪与评估
LLM 应用必须可观测。
要记录:
- 输入
- Prompt
- 检索到的文档
- 模型名称
- 工具调用
- 输出
- token 成本
- 延迟
- 错误
评估:
- 回答是否正确。
- 是否引用了来源。
- 是否遵守输出格式。
- 是否拒绝了不该回答的问题。
- 工具调用是否合理。
没有评估的 Agent 很难稳定迭代。
29. 成本、延迟和稳定性
成本来源:
- 输入 token
- 输出 token
- embedding
- rerank
- 工具/API 调用
- 向量库和服务器
优化:
- 缩短 Prompt。
- 检索更少但更准。
- 缓存 embedding。
- 常见问题缓存答案。
- 小任务用便宜模型。
- 复杂任务再用强模型。
稳定性:
- 超时重试。
- 降级模型。
- 工具失败兜底。
- 输出校验。
- 结构化错误返回。
30. 常见误区
误区一:LangChain 会让模型更聪明。
纠正:LangChain 负责组织应用流程,不会神奇提升模型能力。
误区二:所有 AI 应用都要 Agent。
纠正:简单分类、抽取、问答不一定需要 Agent。能用确定流程解决,就不要过度 Agent 化。
误区三:RAG 就是向量库。
纠正:RAG 包括文档处理、切分、检索、重排、Prompt、回答、引用和评估。
误区四:Prompt 写好了就能上线。
纠正:上线还需要日志、评估、错误处理、权限、安全和成本控制。
31. 学习路线和项目实战
第一阶段:LLM 调用
- 学消息格式。
- 学模型参数。
- 写简单聊天接口。
- 实现流式输出。
第二阶段:工具调用
- 写查询天气/订单/数据库工具。
- 学参数 schema。
- 学工具错误处理。
第三阶段:RAG
- 导入 Markdown 文档。
- 切分和 embedding。
- 用向量库检索。
- 前端展示引用来源。
第四阶段:Agent
- 用
create_agent组合模型和工具。 - 加入短期记忆。
- 加入人工确认。
第五阶段:生产
- 加 trace。
- 建评估集。
- 统计 token 成本。
- 做降级和超时。
项目建议:
text
个人知识库 AI 助手
- 前端:Nuxt/Vue + Tailwind
- 后端:NestJS
- AI 服务:Python + LangChain
- 数据库:PostgreSQL + pgvector
- 功能:文档上传、RAG 问答、引用来源、流式输出、反馈评分快速记忆清单
- LangChain 是 LLM/Agent 应用框架,不是模型。
- 核心组件是 model、message、prompt、tool、structured output、retriever、agent。
- RAG 的关键不是向量库,而是检索质量和上下文控制。
- Tool 要有明确输入输出和权限边界。
- 简单流程先用 LangChain,复杂状态编排再用 LangGraph。
- AI 应用必须做观测、评估、成本控制和错误兜底。