ARTICLE DETAIL

资讯详情

深耕网站SEO优化与搜索引擎排名提升的一线实战洞察。

Claude Agent SDK实战:从零构建能调用工具的AI智能体

Claude Agent SDK实战:从零构建能调用工具的AI智能体 1. 项目概述为什么我们需要一个“Agent SDK”最近和几个做AI应用开发的朋友聊天发现大家不约而同地遇到了一个瓶颈大模型API调用起来简单但想让它真正“干活”尤其是完成一个需要多步骤、有状态、能调用外部工具的任务就变得异常复杂。你需要在代码里手动维护对话历史、解析模型输出、判断下一步该调用哪个函数、处理可能的错误最后还得把结果整理好再塞回给模型。整个过程写下来代码又臭又长逻辑还容易散落一地。这其实就是“智能体”Agent要解决的核心问题。它不是一个简单的聊天机器人而是一个能够自主理解目标、规划步骤、执行工具调用并持续学习的AI程序。而“Claude Agent SDK”就是Anthropic为自家Claude模型家族量身打造的一套开发框架它把上述那些繁琐的、通用的逻辑封装起来让你能像搭积木一样快速构建出功能强大、逻辑清晰的AI智能体。简单来说它解决的是“如何让Claude从‘健谈的学者’变成‘能干的助手’”这个问题。对于开发者而言这意味着你可以把精力从构建Agent运行引擎上解放出来更专注于设计智能体的“大脑”提示词工程和“手脚”工具函数。无论你是想做一个能自动分析数据并生成报告的分析助手还是一个能理解用户自然语言指令去操作内部系统的运维机器人这个SDK都提供了标准化的“生产线”。2. 核心架构与设计哲学拆解在深入代码之前理解Claude Agent SDK的设计哲学至关重要。它没有重新发明轮子而是基于一套清晰、现代的AI应用架构模式我们可以称之为“会话式工作流引擎”。2.1 核心组件四大支柱整个SDK围绕四个核心概念构建它们共同定义了一个智能体的生命周期智能体Agent这是核心控制器。它封装了Claude模型实例比如Claude 3.5 Sonnet、记忆系统对话历史以及最重要的——决策逻辑。你可以把它想象成项目的总指挥它根据当前对话状态和用户输入决定下一步是思考、说话还是行动。工具Tools这是智能体的“手”和“感官”。一个工具本质上是一个Python函数加上清晰的元数据描述名称、描述、参数JSON Schema。SDK负责自动将这些工具的描述格式化后提供给Claude模型并在模型决定使用时安全地调用对应的Python函数。例如get_weather(location: str)或query_database(sql: str)。运行器Runner这是执行引擎。它负责驱动整个“用户输入 - 模型思考 - 选择工具 - 执行工具 - 更新状态 - 生成回复”的循环。AgentRunner类处理了所有的底层交互细节包括流式输出、工具调用解析、错误处理等。开发者通常与它进行直接交互。状态State这是智能体的“记忆”。SDK维护了一个会话状态对象其中自动包含了完整的对话历史messages。更重要的是它允许你向这个状态中注入任意自定义数据比如用户ID、会话配置、或者之前工具执行的结果摘要。这个状态在每次交互中被传递和更新确保了智能体的连续性。2.2 工作流一次典型的交互是如何发生的理解数据流能帮你更好地调试和定制智能体。下面是一次标准交互的幕后过程初始化你创建一个Agent为它配置Claude客户端、工具列表和系统提示词。然后将其放入AgentRunner。用户输入用户说“帮我查一下北京和上海明天的天气然后对比一下。”状态准备Runner将用户输入追加到当前会话的state.messages中形成最新的对话上下文。模型推理Runner将整个状态包括历史消息和工具描述发送给Claude模型。决策与结构化输出Claude理解任务后它不会直接回复天气对比结果而是输出一个结构化的响应。这个响应可能是纯文本回复如果无需工具直接回答。工具调用请求更可能的情况是它输出一个或多个tool_use块指名要调用get_weather工具并提供了参数{location: 北京}。工具执行Runner拦截到tool_use块在其配置的工具列表中查找匹配的工具函数用指定的参数执行它并获取返回值例如{city: 北京, forecast: 晴15-25°C}。结果回传Runner将工具执行结果封装成一个tool_result消息块追加回状态中。循环Runner将更新后的状态包含了新的tool_result再次发送给Claude。Claude看到天气数据后可能会决定再调用一次工具查询上海天气然后重复步骤5-7。最终回复当Claude认为已经收集到足够信息或达到最大迭代次数它会输出最终的纯文本回复“北京明天晴15-25°C上海明天多云18-28°C。上海温度稍高且为多云天气。”流式输出以上所有来自Claude的文本输出思考过程和最终回复都可以通过流式Streaming方式实时返回给前端实现打字机效果。这个设计的关键在于将决策模型与执行工具分离并通过状态对象来维系会话上下文。这让你的代码变得非常模块化和可测试。3. 从零到一构建你的第一个智能体理论说再多不如动手一试。我们来构建一个最简单的“计算器智能体”它不仅能聊天还能进行数学运算。3.1 环境准备与安装首先确保你的Python环境在3.8以上。然后安装必要的包pip install anthropic agent-protocol注意agent-protocol是Anthropic Agent SDK的包名。你需要一个有效的Anthropic API密钥可以从其官网获取。请妥善保管你的API Key不要将其硬编码在代码中提交到版本库。3.2 定义你的第一个工具工具是智能体能力的延伸。我们先定义一个加法工具。import json from typing import Any from anthropic.types import ToolUseBlock # 这是一个标准的工具函数。重点在于参数的类型提示和清晰的docstring。 def add_numbers(a: float, b: float) - float: 计算两个数字的和。 Args: a: 第一个加数。 b: 第二个加数。 Returns: 两个数字的和。 return a b # 为了让SDK识别这个函数为工具我们需要用tool装饰器来包装它。 # 但更常见的做法是使用Tool类来构建工具列表。 from anthropic.types import Tool # 创建工具对象。SDK会自动从函数签名和docstring中提取参数schema。 add_tool Tool.from_function( funcadd_numbers, nameadd_numbers, # 工具名模型通过这个名字来调用 description计算两个数字的加法运算。, # 给模型看的描述至关重要 )实操心得工具的description字段是提示词工程的一部分。描述要准确、简洁说明工具的用途和输入输出。模糊的描述会导致模型错误调用或不敢调用。例如“处理数字”就比“计算两个数字的和”要差得多。3.3 创建智能体并运行有了工具我们就可以组装智能体了。import asyncio from anthropic import Anthropic from anthropic.agent import Agent, AgentRunner async def main(): # 1. 初始化Claude客户端 client Anthropic(api_keyyour-api-key-here) # 2. 定义系统提示词。这决定了智能体的“人格”和核心行为准则。 system_prompt 你是一个专业的数学计算助手。你的主要职责是帮助用户进行数学运算。 当用户提出数学计算需求时你应该主动使用提供的计算工具来获取精确结果。 如果用户的问题不涉及计算或者工具无法解决你可以进行普通的对话。 请确保你的回答清晰、有条理。 # 3. 创建智能体注入模型、系统和工具 agent Agent( clientclient, systemsystem_prompt, tools[add_tool], # 将工具列表传入 modelclaude-3-5-sonnet-20241022, # 指定使用的模型 ) # 4. 创建运行器 runner AgentRunner(agentagent) # 5. 运行智能体用户输入一个计算请求 user_input 请问123.45和678.9相加等于多少 print(f用户: {user_input}) async for event in runner.run_stream(user_input): # 事件流处理我们可以实时看到模型思考和工具调用的过程 if event.type text-delta: # 文本流式输出 print(event.delta, end, flushTrue) elif event.type tool-call-created: print(f\n[智能体决定调用工具: {event.tool_name}]) elif event.type tool-call-done: print(f\n[工具调用完成结果: {event.result}]\n) if __name__ __main__: asyncio.run(main())运行这段代码你会看到类似以下的输出用户: 请问123.45和678.9相加等于多少 我需要计算这两个数的和让我使用加法工具。 [智能体决定调用工具: add_numbers] [工具调用完成结果: 802.35] 123.45与678.9相加的结果是802.35。恭喜你已经创建了一个具备专业能力的AI智能体。它不再是空谈而是能真正执行任务了。4. 进阶实战构建一个多功能个人助理智能体单一的计算器太简单了。让我们构建一个更实用的“个人助理”智能体它能查天气、查时间、甚至进行简单的网页搜索模拟。4.1 设计工具集我们将为这个智能体装备三个工具。import datetime import pytz # 需要安装: pip install pytz from typing import Dict, Any # 工具1获取指定城市当前时间 def get_current_time(city: str) - str: 根据城市名称获取其当前日期和时间。 Args: city: 城市名称例如 Shanghai, New York。支持主要世界城市。 Returns: 该城市当前的日期和时间字符串格式为 YYYY-MM-DD HH:MM:SS (时区)。 # 一个简单的时区映射实际项目可以使用更完善的库如timezonefinder或地理编码API timezone_map { shanghai: Asia/Shanghai, beijing: Asia/Shanghai, new york: America/New_York, london: Europe/London, tokyo: Asia/Tokyo, } tz_name timezone_map.get(city.lower(), UTC) try: tz pytz.timezone(tz_name) now datetime.datetime.now(tz) return now.strftime(f%Y-%m-%d %H:%M:%S ({tz_name})) except pytz.exceptions.UnknownTimeZoneError: return f抱歉未找到城市 {city} 对应的时区信息。 # 工具2模拟天气查询 def get_weather_forecast(location: str, date: str today) - Dict[str, Any]: 查询指定地点和日期的天气预报模拟数据。 Args: location: 地点名称如城市名。 date: 日期支持 today, tomorrow 或 YYYY-MM-DD 格式。默认为 today。 Returns: 一个包含天气信息的字典例如 {location: Beijing, date: 2023-10-27, condition: Sunny, temp_max: 22, temp_min: 12}。 # 这是一个模拟函数。真实场景应调用如OpenWeatherMap、和风天气等API。 # 为了演示我们返回一些预设数据。 mock_data { (beijing, today): {location: Beijing, date: 2023-10-27, condition: Sunny, temp_max: 22, temp_min: 12}, (shanghai, tomorrow): {location: Shanghai, date: 2023-10-28, condition: Cloudy, temp_max: 25, temp_min: 18}, (new york, today): {location: New York, date: 2023-10-27, condition: Rainy, temp_max: 15, temp_min: 8}, } key (location.lower(), date) return mock_data.get(key, {location: location, date: date, condition: Unknown, temp_max: 0, temp_min: 0}) # 工具3模拟网页搜索 def search_web(query: str, max_results: int 3) - list: 根据查询词进行网页搜索模拟返回摘要结果。 Args: query: 搜索关键词。 max_results: 返回的最大结果数量默认为3。 Returns: 一个包含搜索结果的列表每个结果是一个字典包含 title, snippet, url 字段。 # 模拟搜索返回 mock_results [ { title: f关于 {query} 的官方文档, snippet: f这里是一些关于{query}的摘要信息通常来自权威来源。, url: fhttps://example.com/docs/{query.replace( , _)} }, { title: f教程如何理解 {query}, snippet: 这是一篇社区教程详细解释了相关概念和步骤。, url: fhttps://example-tutorial.com/{query} } ] return mock_results[:max_results] # 使用Tool.from_function创建工具对象 from anthropic.types import Tool time_tool Tool.from_function(get_current_time, nameget_current_time, description获取指定城市的当前时间。) weather_tool Tool.from_function(get_weather_forecast, nameget_weather_forecast, description查询指定地点和日期的天气预报。) search_tool Tool.from_function(search_web, namesearch_web, description根据关键词进行网页搜索返回结果摘要。)4.2 配置智能体与复杂系统提示词一个强大的智能体需要一个好的“大脑”。系统提示词就是它的行为准则和人格设定。def create_personal_assistant_agent(client): system_prompt 你是一个高效、贴心且专业的个人数字助理名叫“Clara”。你的核心目标是准确理解用户需求并利用所有可用的工具来完成任务。 # 核心原则 1. **主动性**如果用户的问题涉及查询信息如天气、时间、事实你应该主动使用相应的工具而不是要求用户提供你已经能获取的信息。 2. **精确性**使用工具获取精确数据。例如当被问到时间时调用get_current_time工具当被问到天气时调用get_weather_forecast工具。 3. **信息整合**如果用户的问题需要多步骤或多源信息例如“对比一下北京和纽约的天气”你应该规划步骤依次调用工具最后将结果整合成一个完整、清晰的回答。 4. **诚实与边界**如果工具无法提供信息或者问题超出你的能力范围如需要实时联网但工具是模拟的请如实告知用户你的局限性不要编造信息。 5. **对话友好**在提供事实信息的同时保持对话的自然和友好。在给出工具获取的数据后可以附加一句相关的建议或评论例如“上海明天多云建议带伞出门”。 # 工具使用指南 - get_current_time: 用于任何关于当前时间、日期、时区的问题。 - get_weather_forecast: 用于查询当前或未来的天气状况。 - search_web: 当用户询问需要最新知识或事实核查的问题时使用注意当前为模拟数据。 现在开始为用户提供帮助吧 agent Agent( clientclient, systemsystem_prompt, tools[time_tool, weather_tool, search_tool], modelclaude-3-5-sonnet-20241022, ) return agent注意事项系统提示词的长度和细节需要平衡。过于冗长可能会占用太多上下文窗口挤占对话历史空间过于简略则可能导致智能体行为不符合预期。通常需要在实际对话中进行多次迭代和调试。4.3 实现带状态管理的持续对话智能体的价值在于持续的、有上下文的交互。AgentRunner会自动管理消息历史但我们也可以介入状态管理。async def run_assistant_conversation(): client Anthropic(api_keyyour-api-key) agent create_personal_assistant_agent(client) runner AgentRunner(agentagent) # 模拟一个多轮对话 conversation [ “我下周要去纽约出差那边的天气怎么样”, “那我从上海出发上海明天天气呢”, # 注意这里包含了“上海”和“明天” “好的顺便告诉我现在纽约是几点钟了。” ] for user_msg in conversation: print(f\n[用户] {user_msg}) print([助理] , end) full_response async for event in runner.run_stream(user_msg): if event.type text-delta: print(event.delta, end, flushTrue) full_response event.delta elif event.type tool-call-created: print(f\n 调用工具: {event.tool_name}) elif event.type tool-call-done: # 可以在这里记录或处理工具结果 pass print() # 换行 # 对话结束后可以查看完整的对话历史 print(\n 完整对话历史 ) for msg in runner.state.messages: print(f{msg.type}: {msg.content[0].text if hasattr(msg.content[0], text) else msg.content})运行这个对话你会看到智能体如何连贯地处理多轮问题它可能会先调用天气工具查纽约天气然后在第二轮直接调用工具查上海明天天气第三轮再调用时间工具。所有历史都自动保存在runner.state中供模型在下一轮参考。5. 高级特性与生产级考量当你准备将智能体投入生产环境时以下几个高级特性和考量点至关重要。5.1 流式输出与用户体验对于前端应用流式输出是必备特性。SDK的run_stream方法已经提供了细粒度的事件流。async def stream_with_ui_events(runner: AgentRunner, user_input: str): 一个更精细的事件处理器适合用于驱动前端UI更新。 async for event in runner.run_stream(user_input): if event.type text-delta: # 发送给前端的文本块 yield {type: text, data: event.delta} elif event.type tool-call-created: # 通知前端智能体开始调用工具了可以显示加载状态 yield {type: tool_call_start, tool_name: event.tool_name} elif event.type tool-call-done: # 通知前端工具调用完成可以更新界面或日志 yield {type: tool_call_end, result: event.result} elif event.type input-required: # 一个高级特性智能体可以主动要求用户输入更多信息 yield {type: need_input, message: event.message}5.2 错误处理与韧性工具调用和模型推理都可能失败一个健壮的智能体必须能妥善处理。from anthropic.agent import ToolError def safe_get_weather(location: str, date: str): 一个带有错误处理的工具函数示例 try: # 模拟可能出错的API调用 if location.lower() nowhere: raise ValueError(无效的地点名称。) # ... 实际调用天气API ... return {condition: Sunny, temp: 25} except Exception as e: # 将异常转化为ToolError模型能接收到清晰的错误信息 raise ToolError( messagef查询天气时出错{str(e)}, data{location: location, date: date} # 可选附带错误上下文 ) # 在Agent层面设置全局错误处理 async def run_with_graceful_degradation(runner, user_input): try: async for event in runner.run_stream(user_input): # 处理事件... pass except Exception as e: # 捕获运行器级别的严重错误如网络错误、鉴权失败 print(f智能体会话发生严重错误: {e}) # 可以在这里进行降级处理例如切换到一个更简单的模型或返回预设的兜底回复 return 抱歉服务暂时不可用请稍后再试。实操心得ToolError非常有用。当工具函数抛出此异常时错误信息会被传递回Claude模型。模型能够理解“工具执行失败了”并可能尝试其他策略或向用户请求澄清。这比让工具静默失败或返回一个模糊错误要好得多。5.3 上下文管理与优化Claude模型有上下文窗口限制例如200K tokens。长对话会导致历史消息消耗大量token增加成本并可能影响模型对最近信息的关注。策略1自动摘要在对话轮数达到一定数量后可以触发一个过程让模型自己总结之前的对话要点然后用这个摘要替换掉旧的历史消息。async def summarize_conversation(client, messages): 调用模型生成对话摘要 summary_prompt f 请将以下对话内容浓缩成一个简洁的摘要保留核心事实、用户的主要需求和已做出的决定。 摘要将用于后续对话的上下文。 对话记录 {messages} response client.messages.create( modelclaude-3-haiku-20240307, # 使用更便宜、更快的模型做摘要 max_tokens500, messages[{role: user, content: summary_prompt}] ) return response.content[0].text # 在runner.state.messages过长时可以调用此函数进行摘要替换。策略2选择性记忆不是所有消息都需要永久保存。可以设计规则只保留最近N轮对话和那些标记为“重要”的消息例如包含关键决策或用户偏好。5.4 工具设计的艺术工具的设计质量直接决定智能体的能力上限。原子性与复用性工具应该尽可能“原子化”每个工具只做一件事。例如将get_user_profile和update_user_profile分成两个工具而不是一个庞大的manage_user工具。原子工具更容易被模型理解和组合调用。描述即契约工具的name和description是模型理解它的唯一途径。使用清晰、无歧义的语言。在描述中说明输入参数的格式例如“日期格式为YYYY-MM-DD”和返回值的结构。参数验证在工具函数内部对输入参数进行严格的验证和清洗。这能防止无效调用和潜在的安全问题。验证失败时抛出清晰的ToolError。模拟与真实开发初期可以使用模拟工具快速验证智能体逻辑。上线前逐步替换为调用真实API、数据库或内部服务的实现。6. 常见问题与排查技巧实录在实际开发中你肯定会遇到各种问题。以下是一些典型场景和解决思路。6.1 模型不调用工具症状用户的问题明显应该使用工具但模型却用文本回复猜测或拒绝使用工具。排查步骤检查工具描述这是最常见的原因。描述是否足够清晰、有吸引力模型是否理解这个工具能解决当前问题尝试重写描述更直接地关联用户可能使用的自然语言。例如将“获取数据”改为“查询用户的订单历史记录”。检查系统提示词系统提示词中是否明确鼓励或指导模型使用工具添加明确的指令如“当用户询问需要实时或外部数据的问题时优先使用提供的工具。”检查参数SchemaSDK从函数签名生成的JSON Schema可能过于复杂或有限制。使用Tool.from_function的parameters参数手动定义一个更简单、更宽松的Schema试试。提供示例在系统提示词中加入一两个工具调用的示例Few-shot Learning展示在什么情境下应该调用哪个工具以及如何调用。6.2 工具调用参数错误症状模型决定调用工具但传入的参数格式不对、缺少必填参数或值无效。排查步骤查看原始输出在tool-call-created事件中可以打印出event.arguments查看模型试图传递什么参数。这有助于判断是模型理解有误还是Schema定义不清。简化参数如果工具需要多个参数模型可能难以同时正确推断所有值。考虑拆分成多个更简单的工具或者设计一个工具让大部分参数有合理的默认值。使用更强大的模型Claude 3.5 Sonnet在工具调用准确性上通常优于Haiku。如果问题复杂升级模型可能是最直接的解决方案。后处理与重试在工具函数内部如果发现参数问题可以尝试进行一些基本的清洗和转换如去除空格、尝试解析日期。如果完全无法处理抛出ToolError模型可能会根据错误信息调整后重试。6.3 会话状态混乱或丢失症状智能体忘记了之前对话中确认过的事情或者行为出现不一致。排查步骤确认状态持久化如果你在每次用户请求时都新建一个AgentRunner那么状态自然是全新的。确保在Web服务或对话应用中将runner.state与用户的会话ID关联并持久化如存入数据库或Redis。检查上下文长度使用client.count_tokens()估算当前state.messages的token数。如果接近模型上限模型性能会下降开始“遗忘”早期内容。此时需要触发上文提到的上下文管理策略摘要或截断。自定义状态注入除了消息历史你可以将重要信息存入runner.state.custom_state一个字典。例如在工具中修改了用户偏好后将其存入custom_state[“user_preferences”]这样在后续的对话中你可以通过系统提示词或工具逻辑来读取这些信息实现更复杂的记忆。6.4 性能与成本优化症状响应速度慢API调用费用高。优化策略工具调用合并如果模型频繁地连续调用多个简单工具如查A地天气再查B地天气可以考虑设计一个“批量查询”工具一次性接受多个查询条件减少模型思考-调用-等待的循环次数。缓存工具结果对于结果变化不频繁的工具如某些数据查询可以在工具内部实现缓存逻辑使用TTL避免重复调用昂贵的外部API。模型分级对于简单的确认、摘要生成或预处理任务可以使用更便宜、更快的模型如Claude 3 Haiku。只在核心的复杂推理和规划环节使用Sonnet或Opus。设置超时与重试为工具调用和模型API调用设置合理的超时时间并实现重试机制特别是对于网络波动提升整体可靠性。构建一个成熟可用的Claude Agent是一个迭代过程。从定义一个清晰的任务开始设计最小可行的工具集编写明确的系统指令然后通过大量的真实对话测试来不断调整和优化。这个SDK提供的是一套强大而灵活的脚手架而真正智能体的“灵魂”则来自于你对业务逻辑的深刻理解和对人机交互细节的精心打磨。
返回列表