【AI】AI学习笔记:翻译langGraph 中断 human-in-loop

中断

中断允许您在特定点暂停图的执行,并在继续之前等待外部输入。这使您能够实现需要外部输入才能继续的人机协同模式。当中断被触发时,LangGraph 会使用其持久化层保存图状态,并无限期等待,直到您恢复执行。

中断通过在图的节点中的任意位置调用 interrupt() 函数来工作。该函数接受任何可 JSON 序列化的值,并将其暴露给调用者。当您准备好继续时,通过使用 Command 重新调用图来恢复执行,然后该值将成为节点内部 interrupt() 调用的返回值。

与静态断点(在特定节点之前或之后暂停)不同,中断是动态的------它们可以放置在代码中的任何位置,并且可以根据应用程序逻辑有条件地触发。

  • 检查点保持您的位置: 检查点写入确切的图状态,以便您稍后恢复,即使在错误状态下也是如此。
  • thread_id 是您的指针: 设置 config={"configurable": {"thread_id": ...}} 告诉检查点要加载哪个状态。
  • 中断负载以 __interrupt__ 形式暴露: 传递给 interrupt() 的值会在 __interrupt__ 字段中返回给调用者,以便您知道图正在等待什么。

您选择的 thread_id 实际上是您的持久化游标。重复使用它会恢复同一个检查点;使用新值则会启动一个具有空状态的全新线程。

使用 interrupt 暂停

interrupt 函数暂停图执行并向调用者返回一个值。当您在节点内调用 interrupt 时,LangGraph 会保存当前图状态并等待您输入以恢复执行。

要使用 interrupt,您需要:

  1. 一个检查点来持久化图状态(在生产环境中使用持久的检查点)
  2. 配置中的线程 ID,以便运行时知道从哪个状态恢复
  3. 在要暂停的地方调用 interrupt()(负载必须是可 JSON 序列化的)
python 复制代码
from langgraph.types import interrupt

def approval_node(state: State):
    # 暂停并请求批准
    approved = interrupt("Do you approve this action?")

    # 当您恢复时,Command(resume=...) 将值返回到这里
    return {"approved": approved}

当您调用 interrupt 时,会发生以下情况:

  1. 图执行被挂起 在调用 interrupt 的确切位置
  2. 状态被保存到检查点,以便以后可以恢复执行。在生产环境中,这应该是一个持久的检查点(例如,由数据库支持)
  3. 值被返回 给调用者,放在 __interrupt__ 下;它可以是任何可 JSON 序列化的值(字符串、对象、数组等)
  4. 图无限期等待直到您用响应恢复执行
  5. 响应被传递回 节点,成为 interrupt() 调用的返回值

恢复中断

中断暂停执行后,您可以通过使用包含恢复值的 Command 再次调用图来恢复它。恢复值被传递回 interrupt 调用,允许节点使用外部输入继续执行。

python 复制代码
from langgraph.types import Command

# 初始运行 - 遇到中断并暂停
# thread_id 是持久化指针(在生产环境中存储稳定的 ID)
config = {"configurable": {"thread_id": "thread-1"}}
result = graph.invoke({"input": "data"}, config=config)

# 检查中断了什么
# __interrupt__ 包含传递给 interrupt() 的负载
print(result["__interrupt__"])
# > [Interrupt(value='Do you approve this action?')]

# 用人类的响应恢复
# 恢复负载成为节点内部 interrupt() 的返回值
graph.invoke(Command(resume=True), config=config)

关于恢复的要点:

  • 恢复时必须使用与中断发生时相同的线程 ID
  • 传递给 Command(resume=...) 的值将成为 interrupt 调用的返回值
  • 节点在恢复时从调用 interrupt 的节点开头重新开始,因此 interrupt 之前的任何代码都会再次运行
  • 您可以传递任何可 JSON 序列化的值作为恢复值

常见模式

中断的关键作用是能够暂停执行并等待外部输入。这对于各种用例非常有用,包括:

批准或拒绝

中断最常见的用途之一是在关键操作之前暂停并请求批准。例如,您可能希望让人类批准一个 API 调用、数据库更改或任何其他重要决定。

python 复制代码
from typing import Literal
from langgraph.types import interrupt, Command

def approval_node(state: State) -> Command[Literal["proceed", "cancel"]]:
    # 暂停执行;负载出现在 result["__interrupt__"] 中
    is_approved = interrupt({
        "question": "Do you want to proceed with this action?",
        "details": state["action_details"]
    })

    # 根据响应路由
    if is_approved:
        return Command(goto="proceed")  # 在提供恢复负载后运行
    else:
        return Command(goto="cancel")

当您恢复图时,传递 true 以批准或 false 以拒绝:

python 复制代码
# 批准
graph.invoke(Command(resume=True), config=config)

# 拒绝
graph.invoke(Command(resume=False), config=config)

完整示例

python 复制代码
  from typing import Literal, Optional, TypedDict

  from langgraph.checkpoint.memory import MemorySaver
  from langgraph.graph import StateGraph, START, END
  from langgraph.types import Command, interrupt


  class ApprovalState(TypedDict):
      action_details: str
      status: Optional[Literal["pending", "approved", "rejected"]]


  def approval_node(state: ApprovalState) -> Command[Literal["proceed", "cancel"]]:
      # 暴露详细信息,以便调用者可以在 UI 中呈现它们
      decision = interrupt({
          "question": "Approve this action?",
          "details": state["action_details"],
      })

      # 恢复后路由到适当的节点
      return Command(goto="proceed" if decision else "cancel")


  def proceed_node(state: ApprovalState):
      return {"status": "approved"}


  def cancel_node(state: ApprovalState):
      return {"status": "rejected"}


  builder = StateGraph(ApprovalState)
  builder.add_node("approval", approval_node)
  builder.add_node("proceed", proceed_node)
  builder.add_node("cancel", cancel_node)
  builder.add_edge(START, "approval")
  builder.add_edge("proceed", END)
  builder.add_edge("cancel", END)

  # 在生产中使用更持久的检查点
  checkpointer = MemorySaver()
  graph = builder.compile(checkpointer=checkpointer)

  config = {"configurable": {"thread_id": "approval-123"}}
  initial = graph.invoke(
      {"action_details": "Transfer $500", "status": "pending"},
      config=config,
  )
  print(initial["__interrupt__"])  # -> [Interrupt(value={'question': ..., 'details': ...})]

  # 用决定恢复;True 路由到 proceed,False 路由到 cancel
  resumed = graph.invoke(Command(resume=True), config=config)
  print(resumed["status"])  # -> "approved"

审查和编辑状态

有时,您希望让人类在继续之前审查和编辑图状态的一部分。这对于纠正 LLM、添加缺失信息或进行调整非常有用。

python 复制代码
from langgraph.types import interrupt

def review_node(state: State):
    # 暂停并显示当前内容以供审查(出现在 result["__interrupt__"] 中)
    edited_content = interrupt({
        "instruction": "Review and edit this content",
        "content": state["generated_text"]
    })

    # 用编辑后的版本更新状态
    return {"generated_text": edited_content}

恢复时,提供编辑后的内容:

python 复制代码
graph.invoke(
    Command(resume="The edited and improved text"),  # 值成为 interrupt() 的返回
    config=config
)

完整示例"

python 复制代码
  import sqlite3
  from typing import TypedDict

  from langgraph.checkpoint.memory import MemorySaver
  from langgraph.graph import StateGraph, START, END
  from langgraph.types import Command, interrupt


  class ReviewState(TypedDict):
      generated_text: str


  def review_node(state: ReviewState):
      # 请审阅者编辑生成的内容
      updated = interrupt({
          "instruction": "Review and edit this content",
          "content": state["generated_text"],
      })
      return {"generated_text": updated}


  builder = StateGraph(ReviewState)
  builder.add_node("review", review_node)
  builder.add_edge(START, "review")
  builder.add_edge("review", END)

  checkpointer = MemorySaver()
  graph = builder.compile(checkpointer=checkpointer)

  config = {"configurable": {"thread_id": "review-42"}}
  initial = graph.invoke({"generated_text": "Initial draft"}, config=config)
  print(initial["__interrupt__"])  # -> [Interrupt(value={'instruction': ..., 'content': ...})]

  # 用审阅者编辑后的文本恢复
  final_state = graph.invoke(
      Command(resume="Improved draft after review"),
      config=config,
  )
  print(final_state["generated_text"])  # -> "Improved draft after review"

在工具中使用中断

您也可以将中断直接放在工具函数内部。这使得工具本身在每次被调用时都会暂停等待批准,并允许在工具执行之前由人类审查和编辑工具调用。

首先,定义一个使用 interrupt 的工具:

python 复制代码
from langchain.tools import tool
from langgraph.types import interrupt

@tool
def send_email(to: str, subject: str, body: str):
    """Send an email to a recipient."""

    # 发送前暂停;负载出现在 result["__interrupt__"] 中
    response = interrupt({
        "action": "send_email",
        "to": to,
        "subject": subject,
        "body": body,
        "message": "Approve sending this email?"
    })

    if response.get("action") == "approve":
        # 恢复值可以在执行前覆盖输入
        final_to = response.get("to", to)
        final_subject = response.get("subject", subject)
        final_body = response.get("body", body)
        return f"Email sent to {final_to} with subject '{final_subject}'"
    return "Email cancelled by user"

当您希望批准逻辑与工具本身共存时,这种方法非常有用,使得它可以在图的不同部分重复使用。LLM 可以自然地调用该工具,并且中断会在工具被调用时暂停执行,允许您批准、编辑或取消操作。

完整示例

python 复制代码
  import sqlite3
  from typing import TypedDict

  from langchain.tools import tool
  from langchain_anthropic import ChatAnthropic
  from langgraph.checkpoint.sqlite import SqliteSaver
  from langgraph.graph import StateGraph, START, END
  from langgraph.types import Command, interrupt


  class AgentState(TypedDict):
      messages: list[dict]


  @tool
  def send_email(to: str, subject: str, body: str):
      """Send an email to a recipient."""

      # 发送前暂停;负载出现在 result["__interrupt__"] 中
      response = interrupt({
          "action": "send_email",
          "to": to,
          "subject": subject,
          "body": body,
          "message": "Approve sending this email?",
      })

      if response.get("action") == "approve":
          final_to = response.get("to", to)
          final_subject = response.get("subject", subject)
          final_body = response.get("body", body)

          # 实际发送电子邮件(此处为您的实现)
          print(f"[send_email] to={final_to} subject={final_subject} body={final_body}")
          return f"Email sent to {final_to}"

      return "Email cancelled by user"


  model = ChatAnthropic(model="claude-sonnet-4-5-20250929").bind_tools([send_email])


  def agent_node(state: AgentState):
      # LLM 可能决定调用工具;中断在发送前暂停
      result = model.invoke(state["messages"])
      return {"messages": state["messages"] + [result]}


  builder = StateGraph(AgentState)
  builder.add_node("agent", agent_node)
  builder.add_edge(START, "agent")
  builder.add_edge("agent", END)

  checkpointer = SqliteSaver(sqlite3.connect("tool-approval.db"))
  graph = builder.compile(checkpointer=checkpointer)

  config = {"configurable": {"thread_id": "email-workflow"}}
  initial = graph.invoke(
      {
          "messages": [
              {"role": "user", "content": "Send an email to alice@example.com about the meeting"}
          ]
      },
      config=config,
  )
  print(initial["__interrupt__"])  # -> [Interrupt(value={'action': 'send_email', ...})]

  # 用批准和可选的编辑参数恢复
  resumed = graph.invoke(
      Command(resume={"action": "approve", "subject": "Updated subject"}),
      config=config,
  )
  print(resumed["messages"][-1])  # -> send_email 返回的工具结果

验证人类输入

有时您需要验证来自人类的输入,并在无效时再次询问。您可以使用循环中的多个 interrupt 调用来实现这一点。

python 复制代码
from langgraph.types import interrupt

def get_age_node(state: State):
    prompt = "What is your age?"

    while True:
        answer = interrupt(prompt)  # 负载出现在 result["__interrupt__"] 中

        # 验证输入
        if isinstance(answer, int) and answer > 0:
            # 有效输入 - 继续
            break
        else:
            # 无效输入 - 用更具体的提示再次询问
            prompt = f"'{answer}' is not a valid age. Please enter a positive number."

    return {"age": answer}

每次您用无效输入恢复图时,它都会用更清晰的消息再次询问。一旦提供了有效输入,节点完成,图继续。

完整示例

python 复制代码
  import sqlite3
  from typing import TypedDict

  from langgraph.checkpoint.sqlite import SqliteSaver
  from langgraph.graph import StateGraph, START, END
  from langgraph.types import Command, interrupt


  class FormState(TypedDict):
      age: int | None


  def get_age_node(state: FormState):
      prompt = "What is your age?"

      while True:
          answer = interrupt(prompt)  # 负载出现在 result["__interrupt__"] 中

          if isinstance(answer, int) and answer > 0:
              return {"age": answer}

          prompt = f"'{answer}' is not a valid age. Please enter a positive number."


  builder = StateGraph(FormState)
  builder.add_node("collect_age", get_age_node)
  builder.add_edge(START, "collect_age")
  builder.add_edge("collect_age", END)

  checkpointer = SqliteSaver(sqlite3.connect("forms.db"))
  graph = builder.compile(checkpointer=checkpointer)

  config = {"configurable": {"thread_id": "form-1"}}
  first = graph.invoke({"age": None}, config=config)
  print(first["__interrupt__"])  # -> [Interrupt(value='What is your age?', ...)]

  # 提供无效数据;节点重新提示
  retry = graph.invoke(Command(resume="thirty"), config=config)
  print(retry["__interrupt__"])  # -> [Interrupt(value="'thirty' is not a valid age...", ...)]

  # 提供有效数据;循环退出并更新状态
  final = graph.invoke(Command(resume=30), config=config)
  print(final["age"])  # -> 30

中断的规则

当您在节点内调用 interrupt 时,LangGraph 通过引发一个异常来暂停执行,该异常通知运行时暂停。此异常通过调用堆栈向上传播,并被运行时捕获,然后通知图保存当前状态并等待外部输入。

当执行恢复时(在您提供请求的输入之后),运行时从节点开头 重新启动整个节点------而不是从调用 interrupt 的确切行恢复。这意味着在 interrupt 之前运行的任何代码都将再次执行。因此,在使用中断时,需要遵循一些重要规则,以确保它们按预期工作。

不要将 interrupt 调用包装在 try/except 中

interrupt 通过抛出特殊异常来在调用点暂停执行。如果您将 interrupt 调用包装在 try/except 块中,您将捕获此异常,并且中断不会传递回图。

  • ✅ 将 interrupt 调用与容易出错的代码分开
  • ✅ 在 try/except 块中使用特定的异常类型

```python 分离逻辑 theme={null} def node_a(state: State): # ✅ 良好:先中断,然后单独处理错误情况 interrupt("What's your name?") try: fetch_data() # 这可能会失败 except Exception as e: print(e) return state ```

python 复制代码
def node_a(state: State):
    # ✅ 良好:捕获特定的异常类型不会捕获中断异常
    try:
        name = interrupt("What's your name?")
        fetch_data()  # 这可能会失败
    except NetworkException as e:
        print(e)
    return state
  • 🔴 不要将 interrupt 调用包装在裸露的 try/except 块中
python 复制代码
def node_a(state: State):
    # ❌ 错误:将中断包装在裸露的 try/except 中会捕获中断异常
    try:
        interrupt("What's your name?")
    except Exception as e:
        print(e)
    return state

不要重新排序节点内的 interrupt 调用

在单个节点中使用多个中断很常见,但如果不小心处理,可能会导致意外行为。

当节点包含多个中断调用时,LangGraph 会为执行节点的任务维护一个特定的恢复值列表。每当执行恢复时,它从节点的开头开始。对于遇到的每个中断,LangGraph 检查任务恢复列表中是否存在匹配的值。匹配是严格基于索引的,因此节点内中断调用的顺序很重要。

  • ✅ 保持节点执行间 interrupt 调用的一致性
python 复制代码
def node_a(state: State):
    # ✅ 良好:中断调用每次都以相同顺序发生
    name = interrupt("What's your name?")
    age = interrupt("What's your age?")
    city = interrupt("What's your city?")

    return {
        "name": name,
        "age": age,
        "city": city
    }
  • 🔴 不要有条件地跳过节点内的 interrupt 调用
  • 🔴 不要使用在执行间非确定性的逻辑来循环 interrupt 调用
python 复制代码
  def node_a(state: State):
      # ❌ 错误:有条件地跳过中断会改变顺序
      name = interrupt("What's your name?")

      # 第一次运行时,可能会跳过这个中断
      # 恢复时,可能不会跳过它 - 导致索引不匹配
      if state.get("needs_age"):
          age = interrupt("What's your age?")

      city = interrupt("What's your city?")

      return {"name": name, "city": city}
python 复制代码
def node_a(state: State):
    # ❌ 错误:基于非确定性数据循环
    # 中断的数量在执行间变化
    results = []
    for item in state.get("dynamic_list", []):  # 列表可能在运行之间变化
        result = interrupt(f"Approve {item}?")
        results.append(result)

    return {"results": results}

不要在 interrupt 调用中返回复杂值

根据所使用的检查点,复杂值可能无法序列化(例如,您不能序列化函数)。为了使您的图适应任何部署,最佳实践是只使用可以合理序列化的值。

  • ✅ 将简单的、可 JSON 序列化的类型传递给 interrupt
  • ✅ 传递具有简单值的字典/对象
python 复制代码
  def node_a(state: State):
      # ✅ 良好:传递可序列化的简单类型
      name = interrupt("What's your name?")
      count = interrupt(42)
      approved = interrupt(True)

      return {"name": name, "count": count, "approved": approved}
python 复制代码
  def node_a(state: State):
      # ✅ 良好:传递具有简单值的字典
      response = interrupt({
          "question": "Enter user details",
          "fields": ["name", "email", "age"],
          "current_values": state.get("user", {})
      })

      return {"user": response}
  • 🔴 不要将函数、类实例或其他复杂对象传递给 interrupt
python 复制代码
  def validate_input(value):
      return len(value) > 0

  def node_a(state: State):
      # ❌ 错误:将函数传递给中断,函数无法序列化
      response = interrupt({
          "question": "What's your name?",
          "validator": validate_input  # 这会失败
      })
      return {"name": response}
python 复制代码
  class DataProcessor:
      def __init__(self, config):
          self.config = config

  def node_a(state: State):
      processor = DataProcessor({"mode": "strict"})

      # ❌ 错误:将类实例传递给中断,实例无法序列化
      response = interrupt({
          "question": "Enter data to process",
          "processor": processor  # 这会失败
      })
      return {"result": response}

interrupt 之前调用的副作用必须是幂等的

因为中断通过重新调用它们被调用的节点来工作,所以在 interrupt 之前调用的副作用应该(理想情况下)是幂等的。上下文中的幂等性意味着相同的操作可以多次应用,而不会改变初始执行之外的结果。

例如,您可能有一个更新记录的 API 调用在一个节点内部。如果在进行该调用之后调用 interrupt,则每次恢复节点时都会重新运行该调用,可能会覆盖初始更新或创建重复记录。

  • ✅ 在 interrupt 之前使用幂等操作
  • ✅ 将副作用放在 interrupt 调用之后
  • ✅ 尽可能将副作用分离到单独的节点中
python 复制代码
  def node_a(state: State):
      # ✅ 良好:使用幂等的 upsert 操作,多次运行将具有相同的结果
      db.upsert_user(
          user_id=state["user_id"],
          status="pending_approval"
      )

      approved = interrupt("Approve this change?")

      return {"approved": approved}
python 复制代码
  def node_a(state: State):
      # ✅ 良好:将副作用放在中断之后,确保只在收到批准后运行一次
      approved = interrupt("Approve this change?")

      if approved:
          db.create_audit_log(
              user_id=state["user_id"],
              action="approved"
          )

      return {"approved": approved}
python 复制代码
  def approval_node(state: State):
      # ✅ 良好:只在此节点中处理中断
      approved = interrupt("Approve this change?")

      return {"approved": approved}

  def notification_node(state: State):
      # ✅ 良好:副作用发生在单独的节点中,这在批准后运行,因此只执行一次
      if (state.approved):
          send_notification(
              user_id=state["user_id"],
              status="approved"
          )

      return state
  • 🔴 不要在 interrupt 之前执行非幂等操作
  • 🔴 不要在不检查是否存在的情况下创建新记录
python 复制代码
  def node_a(state: State):
      # ❌ 错误:在中断前创建新记录,这将在每次恢复时创建重复记录
      audit_id = db.create_audit_log({
          "user_id": state["user_id"],
          "action": "pending_approval",
          "timestamp": datetime.now()
      })

      approved = interrupt("Approve this change?")

      return {"approved": approved, "audit_id": audit_id}
python 复制代码
  def node_a(state: State):
      # ❌ 错误:在中断前追加到列表,这将在每次恢复时添加重复条目
      db.append_to_history(state["user_id"], "approval_requested")

      approved = interrupt("Approve this change?")

      return {"approved": approved}

与作为函数调用的子图一起使用

在节点内调用子图时,父图将从调用子图并触发 interrupt 的节点开头 恢复执行。类似地,子图 也将从调用 interrupt 的节点开头恢复。

python 复制代码
def node_in_parent_graph(state: State):
    some_code()  # <-- 这将在恢复时重新执行
    # 以函数形式调用子图,子图包含一个 `interrupt` 调用
    subgraph_result = subgraph.invoke(some_input)
    # ...

def node_in_subgraph(state: State):
    some_other_code()  # <-- 这将在恢复时重新执行
    result = interrupt("What's your name?")
    # ...

使用中断调试

要调试和测试图,您可以使用静态中断作为断点,一次一个节点地步进图执行。静态中断在定义的点触发,要么在节点执行之前,要么在之后。您可以通过在编译图时指定 interrupt_beforeinterrupt_after 来设置这些。
静态中断**不**推荐用于人机协同工作流。请使用 [`interrupt`](https://reference.langchain.com/python/langgraph/types/#langgraph.types.interrupt) 函数。

python 复制代码
    graph = builder.compile(
        interrupt_before=["node_a"],  # [!code highlight]
        interrupt_after=["node_b", "node_c"],  # [!code highlight]
        checkpointer=checkpointer,
    )

    # 将线程 ID 传递给图
    config = {
        "configurable": {
            "thread_id": "some_thread"
        }
    }

    # 运行图直到断点
    graph.invoke(inputs, config=config)  # [!code highlight]

    # 恢复图
    graph.invoke(None, config=config)  # [!code highlight]
复制代码
1. 断点在 `compile` 时设置。
2. `interrupt_before` 指定在节点执行前应暂停的节点。
3. `interrupt_after` 指定在节点执行后应暂停的节点。
4. 需要检查点来启用断点。
5. 运行图直到第一个断点。
6. 通过传入 `None` 作为输入来恢复图,这将运行图直到下一个断点。
python 复制代码
    config = {
        "configurable": {
            "thread_id": "some_thread"
        }
    }

    # 运行图直到断点
    graph.invoke(
        inputs,
        interrupt_before=["node_a"],  # [!code highlight]
        interrupt_after=["node_b", "node_c"],  # [!code highlight]
        config=config,
    )

    # 恢复图
    graph.invoke(None, config=config)  # [!code highlight]
复制代码
1. 调用 `graph.invoke` 时带有 `interrupt_before` 和 `interrupt_after` 参数,这是运行时配置,每次调用都可以更改。
2. `interrupt_before` 指定在节点执行前应暂停的节点。
3. `interrupt_after` 指定在节点执行后应暂停的节点。
4. 运行图直到第一个断点。
5. 通过传入 `None` 作为输入来恢复图,这将运行图直到下一个断点。

使用 LangSmith Studio

您可以使用 LangSmith Studio 在运行图之前在 UI 中为图设置静态中断。您还可以使用 UI 检查执行中任意点的图状态。


在 GitHub 上编辑此页\](https://github.com/langchain-ai/docs/edit/main/src/oss/langgraph/interrupts.mdx) 或 \[提交问题\](https://github.com/langchain-ai/docs/issues/new/choose)。 通过 MCP \[连接这些文档\](/use-these-docs) 到 Claude、VSCode 等,以获取实时答案。 *** ** * ** *** > 要查找此文档中的导航和其他页面,请获取 llms.txt 文件:https://docs.langchain.com/llms.txt

相关推荐
星幻元宇VR1 小时前
青少年法治展厅设备【青少年法律知识学习系统】
学习·安全·虚拟现实
Engineer邓祥浩2 小时前
设计模式学习(19) 23-17 观察者模式
学习·观察者模式·设计模式
如果你想拥有什么先让自己配得上拥有2 小时前
向师傅学习的黄金和斐波总结二
学习
云边散步2 小时前
godot2D游戏教程系列一(8)
笔记·学习·音视频
楼田莉子2 小时前
CMake学习:入门及其下载配置
开发语言·c++·vscode·后端·学习
宵时待雨2 小时前
数据结构(初阶)笔记归纳7:链表OJ
c语言·开发语言·数据结构·笔记·算法·链表
2501_901147832 小时前
组合总和IV——动态规划与高性能优化学习笔记
学习·算法·面试·职场和发展·性能优化·动态规划·求职招聘
Jan123.2 小时前
深入理解数据库事务与锁机制:InnoDB实战指南
数据库·学习
爱喝可乐的老王2 小时前
机器学习监督学习模型--决策树
学习·决策树·机器学习