加载中

实现多轮对话

第 27 课15 分钟

构建一个带有工具的对话系统需要实现一个循环,该循环持续调用 Claude,直到它不再请求使用工具。当 Claude 不再请求工具时,这表明它已经为用户准备好了最终响应。

检测工具请求

判断 Claude 是否想要使用工具的关键在于响应消息的 stop_reason 字段。当 Claude 决定需要调用工具时,该字段会被设置为 "tool_use"。这为我们提供了一种简洁的方式来检查是否需要继续对话循环:

python
if response.stop_reason != "tool_use":
    break  # Claude is done, no more tools needed

对话循环

主对话函数遵循一个简单的模式:

python
def run_conversation(messages):
    while True:
        response = chat(messages, tools=[get_current_datetime_schema])
        add_assistant_message(messages, response)
        print(text_from_message(response))
        
        if response.stop_reason != "tool_use":
            break
            
        tool_results = run_tools(response)
        add_user_message(messages, tool_results)
    
    return messages

这个循环会持续进行,直到 Claude 提供最终答案而不再请求任何工具。

处理多个工具调用

Claude 可以在单个响应中请求多个工具。消息内容包含一个块列表,我们需要分别处理每个工具使用块:

run_tools 函数通过筛选工具使用块并逐一处理来实现这一点:

python
def run_tools(message):
    tool_requests = [
        block for block in message.content if block.type == "tool_use"
    ]
    tool_result_blocks = []
    
    for tool_request in tool_requests:
        # 处理每个工具请求……

工具结果块

每个工具使用块都必须用相应的工具结果块来回应。它们之间的连接通过匹配的 ID 来维持:

工具结果块的结构包括:

python
tool_result_block = {
    "type": "tool_result",
    "tool_use_id": tool_request.id,
    "content": json.dumps(tool_output),
    "is_error": False
}

错误处理

稳健的工具执行需要处理潜在的错误。当工具失败时,我们仍然需要向 Claude 提供一个结果块:

python
try:
    tool_output = run_tool(tool_request.name, tool_request.input)
    tool_result_block = {
        "type": "tool_result",
        "tool_use_id": tool_request.id,
        "content": json.dumps(tool_output),
        "is_error": False
    }
except Exception as e:
    tool_result_block = {
        "type": "tool_result", 
        "tool_use_id": tool_request.id,
        "content": f"Error: {e}",
        "is_error": True
    }

可扩展的工具路由

为了支持多个工具,创建一个将工具名称映射到其实现的路由函数:

python
def run_tool(tool_name, tool_input):
    if tool_name == "get_current_datetime":
        return get_current_datetime(**tool_input)
    elif tool_name == "another_tool":
        return another_tool(**tool_input)
    # 根据需要添加更多工具

这种方法使得添加新工具变得容易,而无需修改核心对话逻辑。

完整工作流程

完整的多轮对话工作方式如下:

  • 将用户消息与可用工具一起发送给 Claude
  • Claude 以文本和/或工具请求进行响应
  • 执行所有请求的工具并创建结果块
  • 将工具结果作为用户消息发送回去
  • 重复此过程,直到 Claude 提供最终答案

这创造了一种无缝的体验,Claude 可以在多个轮次中使用多个工具,以全面回答复杂的用户请求。对话历史保留了完整的上下文,使 Claude 能够基于之前的工具结果构建,从而提供全面的响应。