Nested handoff history: speaker names in summary records break Chat Completions agents when a handoff_history_mapper forwards the transcript
维护者通常 1 天内回复
还没有人认领这个 Issue。
评估
- 难度
- 2/5
- 预计耗时
- 1-3 小时
- 新手友好度
- 85/100
- Issue 类型
- 缺陷
- 描述清晰度
- 描述清楚
- 活跃度
- 活跃
- 技术栈
- python
调研方向
阅读 src/agents/models/chatcmpl_converter.py,尤其是 Converter.maybe_easy_input_message,并比较它接受的消息字段与 src/agents/handoffs/history.py 中由 _parse_summary_line 恢复的 name 字段。为一个嵌套的 handoff 历史项添加回归测试,其中说话者名称通过 handoff_history_mapper 转发给 Chat Completions 模型。复现成功且现有 converter 测试通过后即为完成。
由索引模型根据 Issue 内容生成。
描述
Please read this first
- Have you read the docs? Agents SDK docs Yes
- Have you searched for related issues? Yes. Related but different: #4230 (keeps empty turns in nested history), #3319 and #3814 (other nested-history parsing issues).
Describe the bug
With RunConfig(nest_handoff_history=True), conversation history is carried in <CONVERSATION HISTORY> summary records, and the next handoff parses those records back into input items. The record format includes an optional speaker name, role (name): content: the summary writer emits it for items that have a name (_format_transcript_item_legacy in src/agents/handoffs/history.py), and _parse_summary_line restores it as a name key, e.g. {'role': 'user', 'name': 'Dana', 'content': '...'}.
When a RunConfig.handoff_history_mapper returns the normalized transcript to the next agent (the documented way to "return the exact list of input items to forward to the next agent"), and that agent uses a Chat Completions model, the run fails before the request is sent:
UserError: Unhandled item type or structure: {'role': 'user', 'name': 'Dana', 'content': 'Hi, I need my subscription history.'}
The same conversation without speaker names works. The cause is that Converter.maybe_easy_input_message (src/agents/models/chatcmpl_converter.py) only accepts the keys content, role, type and phase, so the items produced by the SDK's own parser are rejected, even though Chat Completions messages support a name field.
Debug information
- Agents SDK version: v0.24.0 (also reproduced on
mainat 27c75c5) - Related library versions (optional, e.g.
any-llm,litellm, orpydantic): openai 3.29.0, pydantic 2.12.3 - Python version: 3.12.13
- Operating system: Linux (aarch64)
- Model and model provider: Chat Completions (
OpenAIChatCompletionsModel); the repro stubs the HTTP transport - Does the issue reproduce with the latest Agents SDK release? Yes (v0.24.0, and
mainat 27c75c5) - Does the issue occur consistently or intermittently? Consistently
without speaker names: Your plan changed in May.
Traceback (most recent call last):
...
File "04_named_history_chat_completions.py", line 40, in run
result = await Runner.run(agents(), [{"role": "assistant", "content": summary},
File "agents/run.py", line 352, in run
return await runner.run(
File "agents/run.py", line 610, in run
return await self._run_impl(starting_agent, input, **kwargs)
File "agents/run.py", line 1909, in _run_impl
turn_result = await run_single_turn(
File "agents/run_internal/run_loop.py", line 2883, in run_single_turn
new_response = await get_new_response(
File "agents/run_internal/run_loop.py", line 3046, in get_new_response
new_response = await get_response_with_retry(
File "agents/run_internal/model_retry.py", line 612, in get_response_with_retry
response = await _await_model_attempt(get_response(), timeout)
File "agents/run_internal/model_retry.py", line 296, in _await_model_attempt
return await awaitable
File "agents/models/openai_chatcompletions.py", line 249, in get_response
response = await self._fetch_response(
File "agents/models/openai_chatcompletions.py", line 621, in _fetch_response
converted_messages = Converter.items_to_messages(
File "agents/models/chatcmpl_converter.py", line 1026, in items_to_messages
raise UserError(f"Unhandled item type or structure: {item}")
agents.exceptions.UserError: Unhandled item type or structure: {'role': 'user', 'name': 'Dana', 'content': 'Hi, I need my subscription history.'}
Repro steps
Self-contained; the model is a stubbed Chat Completions HTTP transport, so no API key or network is needed.
import asyncio
import httpx
from openai import AsyncOpenAI
from agents import Agent, OpenAIChatCompletionsModel, RunConfig, Runner, get_conversation_history_wrappers, handoff
from agents import set_tracing_disabled
set_tracing_disabled(True)
def chat_model(*messages):
"""A Chat Completions model whose HTTP endpoint returns the given assistant messages in order."""
replies = iter(messages)
def respond(request):
return httpx.Response(200, json={"id": "chatcmpl-1", "object": "chat.completion", "created": 0, "model": "gpt-4.1",
"choices": [{"index": 0, "finish_reason": "stop", "message": next(replies)}]})
client = AsyncOpenAI(api_key="test", http_client=httpx.AsyncClient(transport=httpx.MockTransport(respond)))
return OpenAIChatCompletionsModel("gpt-4.1", client)
def agents():
billing = Agent(name="Billing", model=chat_model({"role": "assistant", "content": "Your plan changed in May."}))
return Agent(name="Triage", handoffs=[handoff(billing)], model=chat_model(
{"role": "assistant", "content": None, "tool_calls": [{"id": "call_1", "type": "function", "function": {
"name": "transfer_to_billing", "arguments": "{}"}}]}))
def earlier(user, assistant):
"""An earlier conversation carried over in the nested-history summary format."""
start, end = get_conversation_history_wrappers()
return ("For context, here is the conversation so far between the user and the previous agent:\n"
f"{start}\n1. {user}: Hi, I need my subscription history.\n2. {assistant}: Sure, let me look into it.\n{end}")
async def run(summary):
config = RunConfig(nest_handoff_history=True, handoff_history_mapper=lambda transcript: transcript)
result = await Runner.run(agents(), [{"role": "assistant", "content": summary},
{"role": "user", "content": "When did my plan change?"}], run_config=config)
return result.final_output
async def main():
print("without speaker names:", await run(earlier("user", "assistant")))
print("with speaker names: ", await run(earlier("user (Dana)", "assistant (Support)")))
asyncio.run(main())
Expected behavior
Items recovered from nested-history summaries can be sent to any supported model: either the Chat Completions converter maps name to the message's name field, or the parser keeps the speaker name in a form the converters accept. Both lines of the script print Your plan changed in May.
- 主要语言
- Python
- 星标
- 30.5k
- 派生
- 5k
- 平均合并
- 16 小时 15 分钟
- 30 天内合并 PR
- 154
环境准备
- 没有 Dockerfile 或 Docker Compose 文件
- 有 Pull Request 模板
- 阅读贡献指南
从这里开始
- 先读完整个 Issue,再读项目的贡献指南。
- 在 Issue 下留言说明你要接手 —— 这能避免两个人做同样的事。
- Fork 仓库,在一个分支上完成修改。
- 提交 Pull Request,并在描述里引用这个 Issue 编号。
openai/openai-agents-python 的其他 Issue
-
难度 2/5 1-3 小时 新手友好度 82/100
openai/openai-agents-python#5594 ·
维护者通常 1 天内回复
-
Default tool error text tells the model to "try again" after a call that may already have taken effect可能已有人在做 @Anurag07-crypto 今天认领。 未关闭
难度 1/5 1 小时以内 新手友好度 72/100
openai/openai-agents-python#5573 · 3 条评论 ·
维护者通常 1 天内回复
-
难度 2/5 1-3 小时 新手友好度 66/100
openai/openai-agents-python#5550 · 3 条评论 ·
维护者通常 1 天内回复
-
难度 1/5 1 小时以内 新手友好度 88/100
openai/openai-agents-python#5529 ·
维护者通常 1 天内回复
-
难度 1/5 1 小时以内 新手友好度 88/100
openai/openai-agents-python#5498 ·
维护者通常 1 天内回复
查看 openai/openai-agents-python 的全部 Issue
相似的 Issue
-
integration: logger
难度 2/5 1-3 小时 新手友好度 68/100
home-assistant/core#191147 · 1 条评论 ·
维护者通常 1 天内回复
-
难度 2/5 1-3 小时 新手友好度 76/100
维护者通常 1 天内回复
-
documentation p4
难度 1/5 1-3 小时 新手友好度 88/100
MillenniumDawn/focus-tree-creation-tool#417 ·
维护者通常 3 天内回复
-
bug
难度 2/5 1-3 小时 新手友好度 78/100
CharlesP44/Beem_Energy#96 ·
-
难度 2/5 1-3 小时 新手友好度 88/100
rheeloaded/paperpull#153 ·
维护者通常 1 天内回复