Skip to content

身份、可靠性、路由与版本维护:练习答案 ​

练习解答 ​

python
from urllib.parse import urlsplit
def accept(url):
    p=urlsplit(url)
    return p.scheme=='https' and p.hostname in {'agents.example.com','backup.example.com'} and p.port in (None,443) and not p.username
assert accept('https://backup.example.com/rpc')
assert not accept('https://backup.example.com.evil.test')
assert not accept('https://agents.example.com:8080')
print('alternate route passed')

首跳合法不代表 Location 目标合法;每次重定向都需重新验证并应用网络限制。

旧版任务维护 ​

在旧环境保存以下完整答案为 answer_old_task.py。两个任务分开保存和查询,断言读取身份与状态;未知 ID 仍走错误联合。

python
import asyncio,httpx
from a2a.client import A2AClient
from a2a.server.apps import A2AStarletteApplication
from a2a.server.agent_execution import AgentExecutor
from a2a.server.tasks import InMemoryTaskStore
from a2a.server.request_handlers import DefaultRequestHandler
from a2a.types import AgentCard,AgentCapabilities,Task,TaskStatus,TaskState,GetTaskRequest,TaskQueryParams,GetTaskSuccessResponse,JSONRPCErrorResponse
class ReadOnly(AgentExecutor):
    async def execute(self,context,event_queue):raise NotImplementedError('maintenance read only')
    async def cancel(self,context,event_queue):raise NotImplementedError('maintenance read only')
async def main():
    store=InMemoryTaskStore()
    card=AgentCard(name='Archive',description='legacy task lookup',version='demo',url='http://old/',capabilities=AgentCapabilities(),default_input_modes=['text'],default_output_modes=['text'],skills=[])
    handler=DefaultRequestHandler(agent_executor=ReadOnly(),task_store=store)
    app=A2AStarletteApplication(agent_card=card,http_handler=handler).build()
    async with httpx.AsyncClient(transport=httpx.ASGITransport(app=app)) as http:
        client=A2AClient(httpx_client=http,agent_card=card)
        for state in (TaskState.working,TaskState.completed):
            await store.save(Task(id='order-A',context_id='c-A',status=TaskStatus(state=state)))
            response=await client.get_task(GetTaskRequest(id='query',params=TaskQueryParams(id='order-A')))
            assert isinstance(response.root,GetTaskSuccessResponse)
            task=response.root.result
            assert task.id=='order-A' and task.status.state==state
            archive=task.model_dump_json(by_alias=True)
            assert Task.model_validate_json(archive).status.state==state
            print(task.id,task.status.state.value)
        await store.save(Task(id='order-B',context_id='c-B',status=TaskStatus(state=TaskState.failed)))
        for task_id,state in [('order-A',TaskState.completed),('order-B',TaskState.failed)]:
            r=await client.get_task(GetTaskRequest(id=task_id,params=TaskQueryParams(id=task_id)))
            assert isinstance(r.root,GetTaskSuccessResponse) and r.root.result.status.state==state
        missing=await client.get_task(GetTaskRequest(id='absent',params=TaskQueryParams(id='missing')))
        assert isinstance(missing.root,JSONRPCErrorResponse)
        assert missing.root.error.code==-32001
        print('missing task rejected')
asyncio.run(main())

输出保留 working、completed 和 missing task rejected;新增断言证明 order-B 的失败不会覆盖 order-A。

用心记录每一步成长