☰
strands-agents Python SDK v0.1.8:摘要式对话管理、工具调用健壮性与可观测性增强
2026/9/28 20:14:14 网站建设 项目流程
  • 人工智能
  • 大模型
  • AI Agent
  • Agent 框架
  • 多智能体
  • 工具调用
  • MCP 服务

【免费下载链接】harness-sdk

Build an agent harness and control it end-to-end. Open-source SDK for production AI agents in Python & TypeScript - any model, any cloud.

项目地址:https://gitcode.com/GitHub_Trending/sdkpython13/harness-sdk
点击查看免费下载

本篇文章以 strands-agents(harness-sdk 仓库中的 Python SDK,即strands-py)v0.1.8 版本发布说明为核心,系统梳理该版本在对话历史管理、直接工具调用、工具注册表约束、节流重试与 OpenTelemetry 遥测等方面的关键变更,并结合仓库源码逐一展开原理级解读,帮助读者快速理解该版本引入的新能力与底层实现,直接用于生产级 Agent 的上下文管理、工具编排与监控配置。

版本概览:v0.1.8 带来了什么

strands-agents Python SDK 的 v0.1.8(发布于 2025-06-18)是一次以「对话管理」与「工程健壮性」为主题的迭代,核心变更可归纳为四条主线:

  1. 新增SummarizingConversationManager(摘要式对话管理器):上下文溢出时不再粗暴丢弃旧消息,而是让模型生成摘要以保留关键信息(PR 112,首个核心功能合并);
  2. 直接工具调用体验修复:agent.tool.xxx()方法式调用支持用下划线匹配带连字符的工具名(PR 178);
  3. 工具注册表更严格的名称约束:禁止注册仅以-/_区分的相似工具名(PR 193);
  4. 对话管理与遥测的底层重构:截断逻辑迁入对话管理器并新增should_truncate_results配置(PR 192)、节流重试改为指数退避(PR 223)、Agent 支持自定义 tracer provider(PR 207)、OTLP exporter 不可用时改为抛出异常(PR 234)。

此外该版本还包含大量工程改进:为 litellm 测试补充 inference profile(PR 209)、添加 A2A 依赖并缓解 OpenTelemetry 依赖冲突(PR 232)、移除未使用的 swagger-parser 依赖(PR 220)、引入 docstring parser(PR 239)、新增集成测试工作流(PR 201/237)等。该版本同时迎来了四位新贡献者:stefanoamorelli、poshinchen、jer96 与 AdnaneKhan。

下文将按主题深入每个核心变更的源码实现,帮助读者理解这些特性「为什么这样设计、在代码里如何落地」。

一、摘要式对话管理器:上下文溢出时「摘要」而非「丢弃」

1.1 设计动机:滑动窗口的局限

在 v0.1.8 之前,上下文管理主要依赖滑动窗口策略(SlidingWindowConversationManager),它只保留最近 N 条消息,更早的消息被直接裁掉。这种方式简单高效,但对长会话不友好——早期用户目标、关键约束与已完成的工具执行结果一旦被裁掉,Agent 就会「失忆」,导致后续任务偏离原始意图。

SummarizingConversationManager正是为解决这一问题而引入:当上下文溢出时,它调用模型把最旧的若干条消息压缩成一条结构化摘要,再以「受保护消息 + 摘要 + 剩余消息」的形式重组历史,从而在受控的上下文预算内保留关键信息。其核心实现位于 summarizing_conversation_manager.py,类定义如下:

class SummarizingConversationManager(ConversationManager): def __init__( self, summary_ratio: float = 0.3, preserve_recent_messages: int = 10, summarization_agent: Optional["Agent"] = None, summarization_system_prompt: str | None = None, *, pin_first: int | None = None, proactive_compression: bool | ProactiveCompressionConfig | None = None, ): ...

1.2 核心参数详解

参数默认值说明
summary_ratio0.3发生上下文溢出时,被摘要的旧消息占全部消息的比例。有效区间为 0.1~0.8,越界值会被自动钳制(max(0.1, min(0.8, summary_ratio))),例如 0.05 会被钳制为 0.1、0.95 会被钳制为 0.8
preserve_recent_messages10始终保留的最近消息条数,保证最近上下文不被摘要,让 Agent 能延续当前任务状态
summarization_agentNone可选:指定一个专用 Agent 来执行摘要。若提供,该 Agent 在摘要过程中可以使用工具,走完整 Agent 管线
summarization_system_promptNone可选:覆盖默认摘要提示词。注意:与summarization_agent互斥,两者同时传入会抛出ValueError,因为 Agent 自带系统提示词
pin_firstNone可选:永久固定会话开头的 N 条消息(如系统指令、初始目标),固定消息受摘要保护并前移压缩
proactive_compressionNone可选:是否在模型调用前进行主动压缩。True表示在上下文窗口使用率达到 70% 时触发;传{"compression_threshold": 0.8}可自定义阈值;False/None表示关闭,仅保留溢出后的被动恢复

从测试 test_summarizing_conversation_manager.py 可以印证默认值与钳制行为:

def test_init_default_values(): manager = SummarizingConversationManager() assert manager.summarization_agent is None assert manager.summary_ratio == 0.3 assert manager.preserve_recent_messages == 10 def test_init_clamps_summary_ratio(): manager = SummarizingConversationManager(summary_ratio=0.05) assert manager.summary_ratio == 0.1 # 下界钳制 manager = SummarizingConversationManager(summary_ratio=0.95) assert manager.summary_ratio == 0.8 # 上界钳制

1.3 摘要流程的源码级拆解

摘要的触发与执行链路在 summarizing_conversation_manager.py 的_summarize_oldest方法中,大致分为六步:

  1. 计算待摘要数量:max(1, int(len(agent.messages) * self.summary_ratio)),再与len(messages) - preserve_recent_messages取最小值,保证不动最近消息;
  2. 调整分割点:调用_adjust_split_point_for_tool_pairs,将分割点向后推进,避免切断toolUse/toolResult配对消息——这是保证模型理解不混乱的关键细节(详见下文 1.4);
  3. 应用 pin 机制:首次缩减时执行apply_pin_first(agent.messages, self.pin_first),把开头 N 条消息标记为固定;
  4. 划分消息:通过partition_pinned把待摘要区间拆成「保留的固定消息」与「可摘要消息」,若可摘要列表为空则抛出ContextWindowOverflowException;
  5. 生成摘要:调用_generate_summary得到摘要消息,并为其分配 tracking id(因为摘要消息绕过了常规的 append 方法);
  6. 重组历史:agent.messages[:] = protected_to_preserve + [summary_message] + remaining_messages,原地替换消息列表。

1.4 摘要生成的两条路径

_generate_summary根据是否配置了summarization_agent走两条路径:

  • 路径一(专用摘要 Agent):_generate_summary_with_agent把待摘要消息注入专用 Agent 并让其执行agent("Please summarize this conversation.")。实现上会临时关闭 structured output(摘要需要纯文本,structured output 会生成toolUse块,而这些块在 user 消息中非法)、为无工具 Agent 注册noop_tool以满足工具规范要求,并在finally中完整还原 Agent 的 system prompt、消息、工具注册表与 structured output 配置,确保副作用不外泄;
  • 路径二(默认,直接调用模型):_generate_summary_with_model通过run_async包装共享助手generate_summary,直接调用父 Agent 的model.stream(),绕过完整 Agent 管线。这一点非常关键——若默认路径也走完整 Agent 管线,会重入_invocation_lock造成死锁,并污染指标、trace 与中断状态。

默认摘要提示词定义在 context_compression.py 的DEFAULT_SUMMARIZATION_PROMPT中,它要求摘要必须:

  • 使用要点列表格式输出结构化摘要,不得对话式回复、不得直接对用户说话、不得评论工具可用性;
  • 除非明确说明,否则不得假设工具执行失败;
  • 必须覆盖关键主题与问题、所有重要工具的执行与结果、共享的代码或技术信息、以及关键洞察小节;
  • 以第三人称书写。

示例格式为:

## Conversation Summary * Topic 1: Key information * Topic 2: Key information ## Tools Executed * Tool X: Result Y

生成完成后,as_user_summary会把模型的回复重新标注为user角色的纯文本消息(只保留 text 块,丢弃 reasoning 与 tool-use 块——Bedrock 等提供商会拒绝 user 消息中包含 reasoning 内容),从而作为历史中的「摘要消息」参与后续轮次。

1.5 主动压缩(Proactive Compression)与溢出恢复(Reactive)

v0.1.8 之前,截断逻辑散落在 Agent 主循环中;v0.1.8 将其统一迁入对话管理器基类ConversationManager(见 conversation_manager.py),并定义了两种压缩场景:

  • 被动恢复(Reactive):模型调用抛出ContextWindowOverflowException后,调用reduce_context(agent, e),此时必须把历史缩减到下一次模型调用能成功,否则重抛异常;
  • 主动压缩(Proactive):ConversationManager基类在构造时接受proactive_compression,并在register_hooks中注册BeforeModelCallEvent回调。回调里用agent.model.estimate_utilization(projected_input_tokens)估算上下文占用率,超过阈值(默认DEFAULT_COMPRESSION_THRESHOLD = 0.7)就调用reduce_context(agent)——这是尽力而为操作,失败仅记录日志,模型调用照常继续。

SummarizingConversationManager.reduce_context的差异化处理正是围绕这两类场景设计的:

def reduce_context(self, agent, e=None, **kwargs): try: self._summarize_oldest(agent) except Exception as summarization_error: if e is not None: logger.error("Summarization failed: %s", summarization_error) raise summarization_error from e # 被动:必须重抛,保证溢出被处理 logger.warning("Proactive summarization failed, continuing: %s", summarization_error) # 主动:吞掉错误,让模型调用继续

值得一提的还有remove_context中removed_message_count的维护:被摘要的旧消息会计入计数,但已存在的摘要消息本身不计入,从而保证会话指标(如已移除消息数)不被摘要过程污染。会话持久化方面,get_state/restore_from_session会保存并恢复summary_message,让摘要能跨会话延续。

1.6 分割点保护:不拆散工具调用配对

无论摘要还是截断,都需要避免在toolUse与紧随其后的toolResult之间切开。共享助手adjust_split_point_for_tool_pairs(位于 context_compression.py)负责把分割点向前推进,直到该位置的旧消息既不是孤立的toolResult(它缺少前置的toolUse),也不是没有紧随toolResult的toolUse;若找不到合法分割点则抛出ContextWindowOverflowException。同类助手find_valid_trim_point还要求裁切起点必须是 user 消息(多数模型供应商的硬性要求)。这些细节保证了压缩后的历史对模型始终「语法合法、语义连贯」。

二、直接工具调用:下划线统一匹配连字符

strands-agents 支持把工具当作 Agent 的方法直接调用:agent.tool.my_tool(param="value")。底层由 _caller.py 的_ToolCaller.__getattr__实现——由于 Python 标识符不能包含连字符,而工具名(如web-search)往往含连字符,v0.1.8 之前的版本里用户只能写getattr(agent.tool, "web-search")这类别扭的代码。

v0.1.8 修复(PR 178)后,__getattr__会先按原名查工具注册表,若未命中且属性名含下划线,则扫描所有tool_name.replace("-", "_") == name的工具进行归一化匹配,即agent.tool.web_search(...)可以正确命中名为web-search的工具:

def _find_normalized_tool_name(self, name: str) -> str: tool_registry = self._agent.tool_registry.registry if tool_registry.get(name): return name if "_" in name: filtered_tools = [ tool_name for (tool_name, tool) in tool_registry.items() if tool_name.replace("-", "_") == name ] if filtered_tools: return filtered_tools[0] # 注册表已防御相似名,直接取第一个 raise AttributeError(f"Tool '{name}' not found")

测试 test_caller.py 验证了直接调用不仅返回正确结果,还会在调用后触发conversation_manager.apply_management(agent)对消息历史做管理(见test_agent_tool中断言conversation_manager_spy.apply_management.assert_called_with(agent))。

直接调用还有两个值得注意的行为:

  • 并发保护:若record_direct_tool_call=True(默认),直接调用会尝试获取 Agent 的并发锁;若 Agent 正在执行调用,会抛出ConcurrencyException,提示「Set record_direct_tool_call=False to allow direct tool calls during agent invocation」;
  • 历史记录:默认会在消息历史中记录完整的工具执行序列(用户消息描述调用、assistant 消息含 toolUse、用户消息含 toolResult、assistant 消息确认调用),且只记录工具 spec 中声明的参数,避免把非序列化对象塞进历史。

三、工具注册表:禁止相似名称冲突

与上一项修复配套,v0.1.8 的 PR 193 在工具注册表(registry.py)中新增了归一化名称冲突检测:注册工具时,除了检查完全重名,还会检查是否存在「仅以-/_区分」的已注册工具。例如已有my-tool时再注册my_tool会抛出ValueError:

if self.registry.get(tool.tool_name) is None: normalized_name = tool.tool_name.replace("-", "_") matching_tools = [ tool_name for (tool_name, tool) in self.registry.items() if tool_name.replace("-", "_") == normalized_name ] if matching_tools: raise ValueError( f"Tool name '{tool.tool_name}' already exists as '{matching_tools[0]}'." " Cannot add a duplicate tool which differs by a '-' or '_'" )

这一约束与第二节的归一化匹配形成了闭环:正因为注册表从源头杜绝了归一化后同名的工具,_find_normalized_tool_name才敢放心地「直接取第一个匹配」而无需处理歧义。需要注意,该检查对支持热重载(supports_hot_reload)的工具会放行,以兼容热更新场景。

四、截断逻辑迁移与should_truncate_results

PR 192 将原本散落在 Agent 主循环中的截断逻辑整体迁入对话管理器,并在滑动窗口管理器(sliding_window_conversation_manager.py)中新增should_truncate_results配置项:

def __init__( self, window_size: int = 40, should_truncate_results: bool = True, *, per_turn: bool | int = False, pin_first: int | None = None, proactive_compression: bool | ProactiveCompressionConfig | None = None, ): ...

各参数含义如下:

参数默认值说明
window_size40历史中最多保留的消息条数;设为 0 表示每次缩减清空全部消息
should_truncate_resultsTrue某条消息过大(超出模型上下文窗口)时,是否尝试截断其中的 tool result 内容
per_turnFalse管理时机:False仅在每次调用结束时管理;True在每次模型调用前管理;正整数表示每 N 次模型调用前管理一次。适合工具密集循环场景,防止上下文在长循环中失控
pin_firstNone固定会话开头 N 条消息,与摘要管理器语义一致
proactive_compressionNone主动压缩开关,语义与SummarizingConversationManager相同

在被动溢出恢复(reduce_context的e非空)时,滑动窗口管理器会先尝试找到最旧的含 tool result 的消息并截断其内容,以「不动窗口」的方式腾出空间;只有当截断仍不够时才真正滑动窗口。这正是「截断(truncate)→ 滑动(slide)」的两级降级策略,测试覆盖于 test_sliding_window_conversation_manager.py(可对照仓库 tests 目录查看)。

五、节流重试:指数退避(Exponential Backoff)

PR 223 将模型节流(throttling)重试策略升级为指数退避。实现位于 _retry.py 的ModelRetryStrategy:

  • 每次重试延迟翻倍:initial_delay → initial_delay*2 → initial_delay*4 …,并受max_delay上限约束;
  • 默认参数max_attempts=6、initial_delay=4、max_delay=240,即延迟序列为4s → 8s → 16s → 32s → 64s(前 5 次重试,第 6 次尝试失败后放弃并重抛原始异常);
  • 成功调用后状态自动重置;
  • 通过is_retryable判定可重试异常集合,子类可只覆写该方法来定制重试范围,而不必重写整个策略。

相比固定间隔重试,指数退避能显著降低限流场景下的「惊群效应」,对依赖第三方模型 API 的生产 Agent 尤为重要。

六、遥测与可观测性:自定义 Tracer Provider 与 Exporter 异常

6.1 Agent 支持自定义 tracer provider(PR 207)

v0.1.8 允许把预配置的SDKTracerProvider注入 Agent 的遥测链路。相关配置入口在 config.py 的StrandsTelemetry类:

StrandsTelemetry(tracer_provider=my_provider).setup_console_exporter().setup_otlp_exporter()

要点如下:

  • 传入tracer_provider时直接复用该 provider;未传入时自动创建新的SDKTracerProvider并设为全局 provider,同时配置 W3C baggage 与 trace context 传播器;
  • exporter 需通过setup_console_exporter()/setup_otlp_exporter()显式配置(支持方法链式调用),环境变量如OTEL_EXPORTER_OTLP_ENDPOINT、OTEL_EXPORTER_OTLP_HEADERS、OTEL_SERVICE_NAME由底层 OpenTelemetry SDK 处理;
  • setup_otlp_exporter()内部使用OTLPSpanExporter+BatchSpanProcessor组合。

在 Agent 侧(agent.py),构造时通过self.tracer = get_tracer()获取全局 tracer 单例(无配置时为 no-op),随后start_agent_span会携带 agent 名称、模型 ID 与工具列表开启整轮调用的 trace span。

6.2 Exporter 不可用时抛出异常(PR 234)

v0.1.8 对 OTel exporter 的错误处理策略做了收紧:当 exporter 不可用(如 OTLP endpoint 配置错误、依赖缺失)时,配置过程不再静默吞掉错误,而是抛出异常让调用方尽早感知。这是对「遥测配置失败应尽早暴露」的工程化改进——避免生产环境中 trace 数据「悄悄丢失」而无人知晓。注意这与 exporter 配置成功后的异步导出失败仍以日志形式记录(Failed exporter configurations are logged but do not raise exceptions的旧行为只适用于普通配置失败路径)的设计形成互补:配置期严格、运行期容忍。

6.3 缓解 OpenTelemetry 依赖冲突(PR 232)

A2A(Agent-to-Agent)支持在引入新依赖的同时带来了 OTel 依赖版本冲突,v0.1.8 通过调整 A2A 依赖声明(add a2a deps and mitigate otel conflict)解决了这一问题。从源码结构看,A2A 相关能力位于 strands/experimental 目录(含bidi、a2a等子模块),属于实验性功能区,读者可按需引入。

七、工程与质量改进

v0.1.8 还包含若干面向工程效率与代码质量的变更:

  • 集成测试工作流(PR 201/237):新增并修正 PR 集成测试工作流,为 SDK 的跨版本回归提供了自动化保障;仓库中strands-py/tests_integ目录即存放各类集成测试(模型、工具、内存、沙箱等);
  • 移除未使用依赖 swagger-parser(PR 220):清理依赖树,减小安装体积并降低供应链面;
  • docstring parser(PR 239):引入 docstring 解析能力(涉及 tools/decorator.py 等处的工具 schema 构建逻辑),为从 docstring 自动生成工具描述与参数 schema 奠定基础;
  • litellm 测试补充 inference profile(PR 209):修正模型集成测试,移除所有权检查,适配 litellm 的 inference profile 用法;
  • 贡献流程简化(PR 221):简化贡献模板与 PR 脚本,降低社区贡献门槛——该版本新增的四位贡献者正是这一开放协作的直接体现。

八、小结与升级建议

v0.1.8 的定位非常清晰:把「上下文如何被管理」从 Agent 主循环中彻底解耦出来,同时打磨工具调用的边界行为与遥测的可靠性。对于生产使用者,升级后建议重点验证以下场景:

  1. 长会话:将ConversationManager切换为SummarizingConversationManager(summary_ratio=0.3, preserve_recent_messages=10),观察上下文溢出时摘要质量与 token 占用;如需自定义摘要格式,可传入summarization_system_prompt或专用summarization_agent;
  2. 工具命名:确认工具名统一使用连字符或下划线其一,避免在注册表归一化冲突检查下注册失败;同时可利用agent.tool.xxx()的下划线调用方式简化代码;
  3. 节流与遥测:确认模型 API 限流下的指数退避参数(max_attempts/initial_delay/max_delay)符合预期;配置 OTel 后验证 endpoint 可达性,避免 exporter 初始化异常在运行时才暴露。

相关源码与测试均可在当前仓库的 strands-py/src/strands/agent/conversation_manager 目录、strands-py/src/strands/tools 目录与 strands-py/tests/strands/agent 测试目录中进一步研读。

  • 人工智能
  • 大模型
  • AI Agent
  • Agent 框架
  • 多智能体
  • 工具调用
  • MCP 服务

【免费下载链接】harness-sdk

Build an agent harness and control it end-to-end. Open-source SDK for production AI agents in Python & TypeScript - any model, any cloud.

项目地址:https://gitcode.com/GitHub_Trending/sdkpython13/harness-sdk
点击查看免费下载

相关推荐

创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考

需要专业的网站建设服务?

联系我们获取免费的网站建设咨询和方案报价,让我们帮助您实现业务目标

立即咨询