流式展示一次工具往返#
把一个 Python 函数变成工具,并在你的应用中显示它的进度。模型请求 add,收到 42,然后给出最终回答。
运行示例#
在仓库根目录下:
uv run python docs/_examples/streaming_tools.py
输出包含:
Answer: 42
这个程序使用确定性的离线模型,并在需要时使用临时存储。它不需要 API 密钥,由文档测试套件执行。
可复用的三个部分#
定义能力。类型注解描述参数,文档字符串告诉模型何时调用它:
@tool
def add(left: int, right: int) -> int:
"""Add two integers exactly.
Args:
left: First operand.
right: Second operand.
Snippet:
add(left=20, right=22)
Guidelines:
- Use this tool for exact integer addition.
"""
return left + right
显示进度。事件回调会收到推理、工具调用、工具结果以及增量的回答文本:
class ConsoleEvents(AgentEventDispatcher):
async def on_reasoning_delta_event(self, event: AgentEvent) -> None:
print(f"Reasoning: {event.delta}")
async def on_tool_started_event(self, event: AgentEvent) -> None:
call = event.tool_calls[0]
print(f"Tool: {call.name} {call.arguments}")
async def on_tool_completed_event(self, event: AgentEvent) -> None:
assert event.message is not None
print(f"Result: {event.message.content}")
async def on_text_delta_event(self, event: AgentEvent) -> None:
print(event.delta)
连接到客户端。当你准备好发出真实请求时,把 MathModel() 替换成你的模型提供商:
async def main() -> None:
client = await create_agent(MathModel(), tools=[add], event_dispatcher=ConsoleEvents())
reply = await client.run("What is 20 plus 22?")
assert reply.content == "Answer: 42"
预设脚本的模型总是调用工具。真实的模型会自行决定是否调用,而且即使内部进行了推理,也可能不会返回可见的推理内容。
完整源码#
1"""Run a typed tool and display reasoning, tool results, and final text."""
2
3import asyncio
4from collections.abc import AsyncIterator
5
6from zett_agent.client import create_agent
7from zett_agent.dispatcher import AgentEventDispatcher
8from zett_agent.events import AgentEvent
9from zett_agent.messages import (
10 AssistantMessage,
11 ToolCall,
12 ToolMessage,
13)
14from zett_agent.model import (
15 ModelEvent,
16 ModelRequest,
17 ModelResponse,
18 RetryOptions,
19)
20from zett_agent.tools.base import tool
21
22
23@tool
24def add(left: int, right: int) -> int:
25 """Add two integers exactly.
26
27 Args:
28 left: First operand.
29 right: Second operand.
30
31 Snippet:
32 add(left=20, right=22)
33
34 Guidelines:
35 - Use this tool for exact integer addition.
36 """
37 return left + right
38
39
40class MathModel:
41 """Script a model/tool/model round trip without making a network request."""
42
43 retry = RetryOptions(max_retries=0)
44
45 async def stream(self, request: ModelRequest) -> AsyncIterator[ModelEvent]:
46 if isinstance(request.messages[-1], ToolMessage):
47 answer = f"Answer: {request.messages[-1].content}"
48 yield ModelEvent.text(answer)
49 yield ModelEvent.completed(ModelResponse(AssistantMessage(content=answer)))
50 else:
51 assert any(definition.name == "add" for definition in request.tools)
52 yield ModelEvent.reasoning("Use exact arithmetic.")
53 yield ModelEvent.completed(
54 ModelResponse(AssistantMessage(tool_calls=(ToolCall("sum-1", "add", {"left": 20, "right": 22}),)))
55 )
56
57
58class ConsoleEvents(AgentEventDispatcher):
59 async def on_reasoning_delta_event(self, event: AgentEvent) -> None:
60 print(f"Reasoning: {event.delta}")
61
62 async def on_tool_started_event(self, event: AgentEvent) -> None:
63 call = event.tool_calls[0]
64 print(f"Tool: {call.name} {call.arguments}")
65
66 async def on_tool_completed_event(self, event: AgentEvent) -> None:
67 assert event.message is not None
68 print(f"Result: {event.message.content}")
69
70 async def on_text_delta_event(self, event: AgentEvent) -> None:
71 print(event.delta)
72
73
74async def main() -> None:
75 client = await create_agent(MathModel(), tools=[add], event_dispatcher=ConsoleEvents())
76 reply = await client.run("What is 20 plus 22?")
77 assert reply.content == "Answer: 42"
78
79
80if __name__ == "__main__":
81 asyncio.run(main())
下一步:阅读把流式输出接入终端或界面了解事件处理和取消,或阅读定义并注册工具了解模式、错误和安全执行。