迭代逼近目标#
goal_mode 外部事件会选定接下来的某一次请求。随后一个私有评估器先报告未完成,最终报告已达成。这里演示的是控制流,而不是真实模型的评估质量。
运行#
在仓库根目录下执行:
uv run python docs/_examples/goal.py
输出包含:
Implementation and tests are complete.
这个程序在需要时使用确定性的离线模型和临时存储。它不需要 API 密钥,并由文档测试套件执行。
把执行者与审查者分开#
应用为某个即将发出的请求选择目标模式。审查者检查执行者的结果,并可以要求再试一次:
async def main() -> None:
reviewer = ReviewModel()
definition = SubAgentDefinition(
name="review",
description="verify goal completion",
system_prompt="Inspect evidence and call report_goal_evaluation.",
model=reviewer,
)
config = AgentRunConfig(session_id="goal-example", request_id="request-1")
client = await create_agent(
DraftModel(),
config=config,
extensions=[GoalExtension(definition, max_iterations=2)],
event_dispatcher=GoalEvents(),
)
accepted = client.agent.emit_external_event(ExternalEvent(GOAL_MODE_EVENT_NAME, {}), config=config)
assert accepted == ["GoalExtension"]
result = await client.run("Implement the change with tests", config=config)
assert reviewer.reviews == 2
print(result.content)
这里审查者故意只报告一次缺少测试,然后接受第二次作答。在真实的工作流中,应让它检查具体的证据,而不是接受执行者的说法。请用迭代次数上限来限制继续执行,并为用户提供取消的途径;评估器并不能保证结果正确。
完整源码#
1"""Run a bounded goal review using the normal Agent as a private evaluator."""
2
3import asyncio
4from collections.abc import AsyncIterator
5
6from zett_agent.agent import AgentRunConfig
7from zett_agent.client import create_agent
8from zett_agent.dispatcher import AgentEventDispatcher
9from zett_agent.events import AgentEvent
10from zett_agent.extensions.external import ExternalEvent
11from zett_agent.extensions.goal import (
12 GOAL_MODE_EVENT_NAME,
13 GoalExtension,
14)
15from zett_agent.extensions.subagent import SubAgentDefinition
16from zett_agent.messages import (
17 AssistantMessage,
18 ToolCall,
19 ToolMessage,
20)
21from zett_agent.model import (
22 ModelEvent,
23 ModelRequest,
24 ModelResponse,
25 RetryOptions,
26)
27
28
29class DraftModel:
30 retry = RetryOptions(max_retries=0)
31
32 def __init__(self) -> None:
33 self.attempt = 0
34
35 async def stream(self, request: ModelRequest) -> AsyncIterator[ModelEvent]:
36 self.attempt += 1
37 answer = "A draft without tests." if self.attempt == 1 else "Implementation and tests are complete."
38 yield ModelEvent.text(answer)
39 yield ModelEvent.completed(ModelResponse(AssistantMessage(content=answer)))
40
41
42class ReviewModel:
43 """Script review decisions; real evaluators must inspect actual evidence."""
44
45 retry = RetryOptions(max_retries=0)
46
47 def __init__(self) -> None:
48 self.reviews = 0
49
50 async def stream(self, request: ModelRequest) -> AsyncIterator[ModelEvent]:
51 if isinstance(request.messages[-1], ToolMessage):
52 message = AssistantMessage(content="Decision submitted.")
53 else:
54 self.reviews += 1
55 achieved = self.reviews > 1
56 message = AssistantMessage(
57 tool_calls=(
58 ToolCall(
59 "review-1",
60 "report_goal_evaluation",
61 {
62 "achieved": achieved,
63 "summary": "Verified" if achieved else "Tests missing",
64 "remaining_work": [] if achieved else ["Add tests"],
65 "next_instruction": None if achieved else "Add and run the tests.",
66 },
67 ),
68 )
69 )
70 yield ModelEvent.completed(ModelResponse(message))
71
72
73class GoalEvents(AgentEventDispatcher):
74 async def on_internal_message_started_event(self, event: AgentEvent) -> None:
75 print("Continuing after private review")
76
77
78async def main() -> None:
79 reviewer = ReviewModel()
80 definition = SubAgentDefinition(
81 name="review",
82 description="verify goal completion",
83 system_prompt="Inspect evidence and call report_goal_evaluation.",
84 model=reviewer,
85 )
86 config = AgentRunConfig(session_id="goal-example", request_id="request-1")
87 client = await create_agent(
88 DraftModel(),
89 config=config,
90 extensions=[GoalExtension(definition, max_iterations=2)],
91 event_dispatcher=GoalEvents(),
92 )
93 accepted = client.agent.emit_external_event(ExternalEvent(GOAL_MODE_EVENT_NAME, {}), config=config)
94 assert accepted == ["GoalExtension"]
95 result = await client.run("Implement the change with tests", config=config)
96 assert reviewer.reviews == 2
97 print(result.content)
98
99
100if __name__ == "__main__":
101 asyncio.run(main())
下一步:普通的工作委派见运行一个隔离的子智能体,目标模式的选择和限制见GoalExtension。