22
33提供与 TableStore Memory 的集成能力,自动存储用户和 Agent 的对话历史。
44
5- Example (基本使用):
6- >>> from agentrun.server import AgentRunServer, AgentRequest
7- >>> from agentrun.memory_collection import MemoryConversation
8- >>>
9- >>> # 初始化 Memory Conversation
10- >>> memory = MemoryConversation(memory_collection_name="my-memory")
11- >>>
12- >>> # 包装 invoke_agent 函数
13- >>> async def invoke_agent(req: AgentRequest):
14- ... async for event in memory.wrap_invoke_agent(req, my_agent_handler):
15- ... yield event
16- >>>
17- >>> server = AgentRunServer(invoke_agent=invoke_agent)
18- >>> server.start()
195"""
206
217import json
228import os
23- from typing import Any , AsyncIterator , Callable , Dict , Optional , TYPE_CHECKING
9+ from typing import (
10+ Any ,
11+ AsyncIterator ,
12+ Callable ,
13+ Dict ,
14+ List ,
15+ Optional ,
16+ TYPE_CHECKING ,
17+ )
2418import uuid
2519
2620import tablestore
@@ -88,13 +82,17 @@ def _default_user_id_extractor(req: Any) -> str:
8882 """默认的 user_id 提取器
8983
9084 优先级:
91- 1. X-User-ID 请求头
85+ 1. X-User-ID 请求头(支持多种格式)
9286 2. user_id 查询参数
9387 3. 默认值 "default_user"
9488 """
9589 if req .raw_request :
9690 # 从请求头获取
97- user_id = req .raw_request .headers .get ("X-User-ID" )
91+ user_id = (
92+ req .raw_request .headers .get ("X-AgentRun-User-ID" )
93+ or req .raw_request .headers .get ("x-agentrun-user-id" )
94+ or req .raw_request .headers .get ("X-Agentrun-User-Id" )
95+ )
9896 if user_id :
9997 return user_id
10098
@@ -110,26 +108,20 @@ def _default_session_id_extractor(req: Any) -> str:
110108 """默认的 session_id 提取器
111109
112110 优先级:
113- 1. X-Session-ID 请求头
114- 2. sessionId 查询参数
115- 3. 从最后一条消息的 id 生成
116- 4. 生成新的 UUID
111+ 1. X-Session-ID 请求头(支持多种格式)
112+ 2. 生成新的 UUID
117113 """
118114 if req .raw_request :
119- # 从请求头获取
120- session_id = req . raw_request . headers . get ( "X-Conversation-ID" )
121- if session_id :
122- return session_id
123-
124- # 从查询参数获取
125- session_id = req . raw_request . query_params . get ( "sessionId" )
115+ # 从请求头获取(兼容多种格式)
116+ # 支持:X-AgentRun-Session-ID, x-agentrun-session-id, X-Agentrun-Session-Id
117+ session_id = (
118+ req . raw_request . headers . get ( "X-AgentRun-Session-ID" )
119+ or req . raw_request . headers . get ( "x-agentrun-session-id" )
120+ or req . raw_request . headers . get ( "X-Agentrun-Session-Id" )
121+ )
126122 if session_id :
127123 return session_id
128124
129- # 从消息 ID 生成(如果有)
130- if req .messages and req .messages [- 1 ].id :
131- return f"session_{ req .messages [- 1 ].id } "
132-
133125 # 生成新的 session_id
134126 return f"session_{ uuid .uuid4 ().hex [:16 ]} "
135127
@@ -138,13 +130,18 @@ def _default_agent_id_extractor(req: Any) -> str:
138130 """默认的 agent_id 提取器
139131
140132 优先级:
141- 1. X-Agent-ID 请求头
133+ 1. X-Agent-ID 请求头(支持多种格式)
142134 2. 从 URL 路径中提取 /agent-runtimes/{agent_id}/... 格式
143135 3. 默认值 "default_agent"
144136 """
145137 if req .raw_request :
146- # 从请求头获取
147- agent_id = req .raw_request .headers .get ("X-Agent-ID" )
138+ # 从请求头获取(兼容多种格式)
139+ # 支持:X-AgentRun-Agent-ID, x-agentrun-agent-id, X-Agentrun-Agent-Id, X-Agent-ID, x-agent-id
140+ agent_id = (
141+ req .raw_request .headers .get ("X-AgentRun-Agent-ID" )
142+ or req .raw_request .headers .get ("x-agentrun-agent-id" )
143+ or req .raw_request .headers .get ("X-Agentrun-Agent-Id" )
144+ )
148145 if agent_id :
149146 return agent_id
150147
@@ -407,8 +404,12 @@ async def wrap_invoke_agent(
407404 "content" : self ._extract_message_content (msg .content ),
408405 })
409406
410- # 收集 Agent 响应
407+ # 收集 Agent 响应(包括文本和工具调用)
411408 agent_response_content = ""
409+ tool_calls : Dict [str , Dict [str , Any ]] = (
410+ {}
411+ ) # tool_call_id -> tool_call_info
412+ tool_results : List [Dict [str , Any ]] = [] # 工具执行结果列表
412413
413414 try :
414415 # 流式处理 Agent 响应
@@ -420,17 +421,80 @@ async def wrap_invoke_agent(
420421 if event .event == EventType .TEXT and "delta" in event .data :
421422 agent_response_content += event .data ["delta" ]
422423
424+ # 收集工具调用信息
425+ elif event .event == EventType .TOOL_CALL :
426+ # 完整的工具调用
427+ tool_id = event .data .get ("id" , "" )
428+ if tool_id :
429+ tool_calls [tool_id ] = {
430+ "id" : tool_id ,
431+ "type" : "function" ,
432+ "function" : {
433+ "name" : event .data .get ("name" , "" ),
434+ "arguments" : event .data .get ("args" , "" ),
435+ },
436+ }
437+
438+ elif event .event == EventType .TOOL_CALL_CHUNK :
439+ # 工具调用片段(流式场景)
440+ tool_id = event .data .get ("id" , "" )
441+ if tool_id :
442+ if tool_id not in tool_calls :
443+ tool_calls [tool_id ] = {
444+ "id" : tool_id ,
445+ "type" : "function" ,
446+ "function" : {
447+ "name" : event .data .get ("name" , "" ),
448+ "arguments" : "" ,
449+ },
450+ }
451+ # 累积参数片段
452+ if "args_delta" in event .data :
453+ tool_calls [tool_id ]["function" ][
454+ "arguments"
455+ ] += event .data ["args_delta" ]
456+
457+ # 收集工具执行结果
458+ elif event .event == EventType .TOOL_RESULT :
459+ tool_id = event .data .get ("id" , "" )
460+ if tool_id :
461+ tool_results .append ({
462+ "role" : "tool" ,
463+ "tool_call_id" : tool_id ,
464+ "content" : str (event .data .get ("result" , "" )),
465+ })
466+
423467 # 透传事件
424468 yield event
425469
426470 # 保存完整的对话轮次(输入 + 输出)
427- if agent_response_content :
471+ # 只有当有文本内容或工具调用时才保存
472+ if agent_response_content or tool_calls or tool_results :
428473 try :
429- # 将助手响应添加到消息列表
430- output_messages = input_messages + [ {
474+ # 构建助手响应消息
475+ assistant_message : Dict [ str , Any ] = {
431476 "role" : "assistant" ,
432- "content" : agent_response_content ,
433- }]
477+ }
478+
479+ # 添加文本内容(如果有)
480+ if agent_response_content :
481+ assistant_message ["content" ] = agent_response_content
482+ else :
483+ # OpenAI 格式要求:如果有 tool_calls,content 可以为 null
484+ assistant_message ["content" ] = None
485+
486+ # 添加工具调用(如果有)
487+ if tool_calls :
488+ assistant_message ["tool_calls" ] = list (
489+ tool_calls .values ()
490+ )
491+
492+ # 构建完整的消息列表
493+ output_messages = input_messages + [assistant_message ]
494+
495+ # 添加工具执行结果(如果有)
496+ if tool_results :
497+ output_messages .extend (tool_results )
434498
435499 # 将完整的对话历史存储为一条消息
436500 # content 字段存储 JSON 格式的消息列表
@@ -446,8 +510,10 @@ async def wrap_invoke_agent(
446510 await memory_store .update_session (session )
447511
448512 logger .debug (
449- f"Saved conversation: { len (output_messages )} messages, "
450- f"response length: { len (agent_response_content )} chars"
513+ f"Saved conversation: { len (output_messages )} messages,"
514+ f" text length: { len (agent_response_content )} chars,"
515+ f" tool_calls: { len (tool_calls )} , tool_results:"
516+ f" { len (tool_results )} "
451517 )
452518 except Exception as e :
453519 logger .error (
0 commit comments