
Agent2Agent å ¥é (11) - travel planner example
ãAgent2Agentãã®å ¬åŒãµã³ãã«ãtravel planner exampleãã詊ããã®ã§ãŸãšããŸããã
åå
1. travel planner example
OpenAIã¢ãã«ã®ä»æ§ã«æºæ ããæ è¡ãã©ã³ããŒãšãŒãžã§ã³ãã§ããGoogleå ¬åŒã®ãa2a-python SDKãã«åºã¥ããŠå®è£ ãããããA2AãïŒAgent2AgentïŒãããã³ã«ã«æºæ ããæ è¡ã¢ã·ã¹ã¿ã³ãã®ãã¢ã§ãã
2. ã»ããã¢ãã
ã»ããã¢ããæé ã¯ã次ã®ãšããã§ãã
(1) ãããžã§ã¯ããã©ã«ãã®äœæ
mkdir travel_planner_example
cd travel_planner_example(2) Pythonã®ä»®æ³ç°å¢ã®æºåã
Python 3.11以äžã䜿çšããŸãã
ã»Mac
python -m venv .venv
source .venv/bin/activateã»Windows
python -m venv .venv
.venv\Scripts\activate(3) ããã±ãŒãžã®ã€ã³ã¹ããŒã«ã
pip install a2a-sdk uvicorn
pip install langchain-core langchain-openai3. A2AãšãŒãžã§ã³ãã®å®è£
3-1. ã³ã¢ãšãŒãžã§ã³ãã®å®çŸ©
<OpenAIã®APIããŒ>ã«èªåã®OpenAIã®APIããŒãèšè¿°ããŠãã ããã
ã»agent.py
from collections.abc import AsyncGenerator
from typing import Any
from langchain_core.messages import HumanMessage, SystemMessage
from langchain_openai import ChatOpenAI
# æ
è¡ãã©ã³ããŒãšãŒãžã§ã³ã
class TravelPlannerAgent:
# åæå
def __init__(self):
# ã¢ãã«ã®æºå
self.model = ChatOpenAI(
model='gpt-4o',
api_key='<OpenAIã®APIããŒ>',
temperature=0.7,
)
# LLMã®å¿çãã¯ã©ã€ã¢ã³ãã«ã¹ããªãŒãã³ã°
async def stream(self, query: str) -> AsyncGenerator[dict[str, Any], None]:
try:
messages = [
SystemMessage(
content="""
ããªãã¯ãæ
è¡èšç»ãç®çå°æ
å ±ããããŠæ
è¡ã®ãããããå°éãšãããæ
è¡ã¢ã·ã¹ã¿ã³ãã®ãšãã¹ããŒãã§ãã
ããªãã®ç®æšã¯ããŠãŒã¶ãŒã®å¥œã¿ãå¶çŽã«åºã¥ããŠã楜ããå®å
šã§çŸå®çãªæ
è¡ãèšç»ã§ããããæ¯æŽããããšã§ãã
æ
å ±æäŸã«ããã£ãŠã¯ã以äžã®ç¹ã«çæããŠãã ããã
- ã¢ããã€ã¹ã¯å
·äœçãã€å®è·µçãªãã®ã«ããŠãã ããã
- å£ç¯ãäºç®ãæ
è¡ã®ããžã¹ãã£ã¯ã¹ãèæ
®ããŠãã ããã
- æåäœéšãçŸå°ãªãã§ã¯ã®ã¢ã¯ãã£ããã£ã匷調ããŠãã ããã
- ç®çå°ã«é¢é£ããå®çšçãªæ
è¡ã®ãã³ããèšèŒããŠãã ããã
- å¿
èŠã«å¿ããŠãèŠåºããç®æ¡æžããªã©ãçšããŠãæ
å ±ãåãããããæŽçããŠãã ããã
æ
çšèšç»ã«ã€ããŠ
- 芳å
ã¹ãããéã®ç§»åæéãèæ
®ãããçŸå®çãªæ¥ããšã®ãã©ã³ãäœæããŠãã ããã
- 人æ°ã®èгå
ã¹ããããšã人éé¢ããå Žæã§ã®äœéšããã©ã³ã¹ããåãå
¥ããŠãã ããã
- ããããã®æèŠæéãšå®çšçãªããžã¹ãã£ã¯ã¹ãçã蟌ãã§ãã ããã
- å°å
ã®æçãäžå¿ãšããé£äºã®ãªãã·ã§ã³ãææ¡ããŠãã ããã
- èšç»ã«ã¯ã倩åãå°å
ã®ã€ãã³ããå¶æ¥æéãèæ
®ããŠãã ããã
åžžã«èŠªåã§ç±å¿ã§ãããªããçŸå®çãªå£èª¿ãç¶æããå¿
èŠã«å¿ããŠèªåã®ç¥èã®éçãèªããŸãããã
"""
)
]
# ãŠãŒã¶ãŒã¡ãã»ãŒãžãå±¥æŽã«è¿œå
messages.append(HumanMessage(content=query))
# ã¹ããªãŒãã³ã°ã¢ãŒãã§ã¢ãã«ãåŒã³åºããŠå¿çãçæ
async for chunk in self.model.astream(messages):
if hasattr(chunk, 'content') and chunk.content:
yield {'content': chunk.content, 'done': False}
yield {'content': '', 'done': True}
except Exception as e:
print(f'errorïŒ{e!s}')
yield {
'content': 'ãªã¯ãšã¹ãã®åŠçäžã«ãšã©ãŒãçºçããŸããã',
'done': True,
}3-2. AgentExecutor ã§ã³ã¢ãšãŒãžã§ã³ããã©ãã
ã»agent_executor.py
from typing import override
from a2a.server.agent_execution import AgentExecutor, RequestContext
from a2a.server.events import EventQueue
from a2a.types import (
TaskArtifactUpdateEvent,
TaskState,
TaskStatus,
TaskStatusUpdateEvent,
)
from a2a.utils import new_text_artifact
from agent import TravelPlannerAgent
# AgentExecutorã®äœæ
class TravelPlannerAgentExecutor(AgentExecutor):
# åæå
def __init__(self):
self.agent = TravelPlannerAgent()
# å®è¡
@override
async def execute(
self,
context: RequestContext,
event_queue: EventQueue,
) -> None:
query = context.get_user_input()
if not context.message:
raise Exception('ã¡ãã»ãŒãžã¯æäŸãããŠããŸãã')
# ã¹ããªãŒãã³ã°
async for event in self.agent.stream(query):
message = TaskArtifactUpdateEvent(
contextId=context.context_id, # type: ignore
taskId=context.task_id, # type: ignore
artifact=new_text_artifact(
name='current_result',
text=event['content'],
),
)
await event_queue.enqueue_event(message)
if event['done']:
break
# å®äº
status = TaskStatusUpdateEvent(
contextId=context.context_id, # type: ignore
taskId=context.task_id, # type: ignore
status=TaskStatus(state=TaskState.completed),
final=True
)
await event_queue.enqueue_event(status)
# ãã£ã³ã»ã«
@override
async def cancel(
self, context: RequestContext, event_queue: EventQueue
) -> None:
raise Exception('ãã£ã³ã»ã«ã¯ãµããŒããããŠããŸãã')ã»TaskArtifactUpdateEvent
çæéäžãŸãã¯æçµã¢ãŠãããããæççã«éä¿¡ããããã®ã€ãã³ãã
ã»contextId : ã³ã³ããã¹ãID
ã»taskId : ã¿ã¹ã¯ID
ã»artifact : ã¢ãŒãã£ãã¡ã¯ã
ã»lastChunk : æçµãã£ã³ã¯ãã©ãã
ã»TaskStatusUpdateEvent
ã©ã€ããµã€ã¯ã«ã®å€æŽãã¯ã©ã€ã¢ã³ãã«äŒããããã®ã€ãã³ãã
ã»contextId : ã³ã³ããã¹ãID
ã»taskId : ã¿ã¹ã¯ID
ã»status : çŸåšã®ã¿ã¹ã¯ç¶æ
ã»final : ã¹ããªãŒãã³ã°çµäºã瀺ãçåœå€
ã»TaskState
ã¿ã¹ã¯ç¶æ
ã
ã»auth_required
ã»canceled
ã»completed
ã»failed
ã»input_required
ã»rejected
ã»submitted
ã»unknown
ã»working
3-3. AgentSkill ãš AgentCard ã®æºå
ã»__main__.py
import uvicorn
from a2a.server.apps import A2AStarletteApplication
from a2a.server.request_handlers import DefaultRequestHandler
from a2a.server.tasks import InMemoryTaskStore
from a2a.types import (
AgentCapabilities,
AgentCard,
AgentSkill,
)
from agent_executor import TravelPlannerAgentExecutor
# ã¡ã€ã³
if __name__ == '__main__':
# Agent Skill ã®äœæ
skill = AgentSkill(
id='travel_planner',
name='travel planner agent',
description='travel planner',
tags=['travel planner'],
examples=['hello', 'nice to meet you!'],
)
# Agent Card ã®äœæ
agent_card = AgentCard(
name='travel planner Agent',
description='travel planner',
url='http://localhost:10001/',
version='1.0.0',
defaultInputModes=['text'],
defaultOutputModes=['text'],
capabilities=AgentCapabilities(streaming=True),
skills=[skill],
)
# ãªã¯ãšã¹ããã³ãã©ã®äœæ
request_handler = DefaultRequestHandler(
agent_executor=TravelPlannerAgentExecutor(),
task_store=InMemoryTaskStore(),
)
# A2AãµãŒãã®äœæ
server = A2AStarletteApplication(
agent_card=agent_card, http_handler=request_handler
)
# A2AãµãŒãã®å®è¡
uvicorn.run(server.build(), host='0.0.0.0', port=10001)
3-4. A2AãšãŒãžã§ã³ãã®å®è¡
(1) A2AãšãŒãžã§ã³ãã®å®è¡ã
A2Aãšã³ããã€ã³ããå
¬éãããŸãã
python __main__.pyINFO: Started server process [76037]
INFO: Waiting for application startup.
INFO: Application startup complete.
INFO: Uvicorn running on http://0.0.0.0:10001 (Press CTRL+C to quit)4. A2AãšãŒãžã§ã³ãã®åŒã³åºã
(1) A2AãšãŒãžã§ã³ããåŒã³åºãã¯ã©ã€ã¢ã³ãã®äœæã
ã»loop_client.py
import asyncio
from typing import Any
from uuid import uuid4
import httpx
from a2a.client import A2AClient
from a2a.types import (
MessageSendParams,
SendStreamingMessageRequest,
)
# ãŠã§ã«ã«ã ã¡ãã»ãŒãžã®åºå
def print_welcome_message() -> None:
print('æ±çš A2A ã¯ã©ã€ã¢ã³ããžãããã!')
print("ã¯ãšãªãå
¥åããŠãã ãã (çµäºããã«ã¯ãexitããšå
¥å)")
# ãŠãŒã¶ãŒã¯ãšãªã®ååŸ
def get_user_query() -> str:
return input('\n> ')
# ãµãŒããšã®ã€ã³ã¿ã©ã¯ã·ã§ã³
async def interact_with_server(client: A2AClient) -> None:
while True:
user_input = get_user_query()
if user_input.lower() == 'exit':
break
send_message_payload: dict[str, Any] = {
'message': {
'role': 'user',
'parts': [{'type': 'text', 'text': user_input}],
'messageId': uuid4().hex,
},
}
try:
streaming_request = SendStreamingMessageRequest(
id=uuid4().hex,
params=MessageSendParams(**send_message_payload)
)
stream_response = client.send_message_streaming(streaming_request)
async for chunk in stream_response:
print(get_response_text(chunk), end='', flush=True)
await asyncio.sleep(0.1)
except Exception as e:
print(f'ãšã©ãŒãçºçããŸãã: {e}')
# ã¬ã¹ãã³ã¹ããã¹ãã®ååŸ
def get_response_text(chunk):
data = chunk.model_dump(mode='json', exclude_none=True)
result = data.get('result', {})
# ã¢ãŒãã£ãã¡ã¯ãæŽæ°ã€ãã³ã
if 'artifact' in result:
parts = result['artifact']['parts']
# ã¹ããŒã¿ã¹æŽæ°ã€ãã³ã
elif 'status' in result and result['status'].get('message'):
parts = result['status']['message']['parts']
# ãã®ä»ã®ã€ãã³ã
else:
return ''
# æåã®ããŒãã®ããã¹ããè¿ã
return parts[0].get('text', '')
# ã¡ã€ã³
async def main() -> None:
print_welcome_message()
async with httpx.AsyncClient() as httpx_client:
client = await A2AClient.get_client_from_agent_card_url(
httpx_client, 'http://localhost:10001'
)
await interact_with_server(client)
# ã¡ã€ã³ã®å®è¡
if __name__ == '__main__':
asyncio.run(main())
(2) A2AãšãŒãžã§ã³ããåŒã³åºãã¯ã©ã€ã¢ã³ãã®å®è¡ã
python loop_client.py æ±çš A2A ã¯ã©ã€ã¢ã³ããžãããã!
ã¯ãšãªãå
¥åããŠãã ãã (çµäºããã«ã¯ãexitããšå
¥å)
> (3) 質åã
å¿çã¯ã¹ããªãŒãã³ã°ã§åºåãããŸãã
> ç§èåã®æ
è¡ãã©ã³ãäœã£ãŠãã¡ããã§ãïŒç§èåã¯æ±äº¬ã®äžå¿ã«äœçœ®ãã黿°è¡ãšããŠç¥ãããäžæ¹ã§ãã¢ãã¡ãã²ãŒã ã®æåã®äžå¿å°ã§ããããŸãã以äžã¯ãç§èåã§ã®äžæ¥æ
è¡ãã©ã³ã®ææ¡ã§ãã
### ç§èåäžæ¥æ
è¡ãã©ã³
#### åå: 黿°è¡ãšã·ã§ããã³ã°
- **9:00 AM - ç§èåé§
å°ç**
- JRç§èåé§
ã«å°çãäžå€®æ¹æãåºãããããã«é»æ°è¡ãžã¢ã¯ã»ã¹ã§ããŸãã
- **9:30 AM - ãšããã·ã«ã¡ã© ãã«ãã¡ãã£ã¢ Akiba**
- ææ°ã®å®¶é»è£œåãèŠãŠåãããšãã§ããŸããã«ã¡ã©ãããœã³ã³ãå®¶é»ã®åæããè±å¯ã§ãã
: