Planning Poker Agent 案例中的核心设计决策——逻辑代码 vs LLM 推理的权衡标准。Agent 架构的实战指南。
如果你正在构建 AI Agent,多半经常需要权衡:究竟应该让 LLM 处理多少逻辑,又该让传统代码处理多少逻辑。今天早上,我想分享一下自己在这方面的实践经历,抛砖引玉,也听听大家的看法!
平时,我会花大量时间收集用户反馈。以前,我会把这些洞察整理成 Linear 中的工单,然后耗费大量脑力评估投入产出比,为任务优先级提供依据。在这个大胆的 AI 新世界里,我决定换一种方式:编写一个 planning poker Agent,帮助我对 Linear 中的部分工单进行 T-shirt sizing。
这个 Agent 基于 Portia SDK 构建,将执行以下任务:
使用 Linear 的远程 MCP server 获取相关 Linear 工单。它是我们提供内置身份验证的数千种工具之一。
模拟多个开发者 persona 给出的规模估算,并针对每个工单的工作量规模达成共识。为此,我想通过继承我们的 LLM tool,创建一个能够以 structured output 形式返回估算结果的工单估算工具。这个工具会读取一个 context.md 文件,其中保存了 Portia SDK 的架构摘要及其核心抽象,从而帮助 LLM 估算工作量。
结果我发现,原来自己之前已经把这项任务交给了一位开发者——姑且称他为 Ethan——只是后来忘得一干二净!于是,我们两个人在同一时间分别实现了这个功能,只不过……我在很大程度上依赖 LLM 来处理任务,而他则更多地依赖代码。接下来,让我们详细看看这两种方法有何不同。
完整代码可以在我们的 Agent 示例代码仓库中查看。
我依赖一个足够健壮的 prompt 和 Portia planning agent,让它自行推断出需要执行的完整步骤:先从 Linear 获取并筛选工单,再以每个开发者 persona 的视角估算工单规模,最后计算平均结果。
本质上,我依靠 LLM 自身完成以下工作:
下面这段代码就是魔法发生的地方:
# Get tickets from Linear and estimate the size of the tickets
project = "Async Portia"
query = f"""Get the tickets i'm working on from Linear with a limit of 3 on the tool call. Then filter specifically for those regarding the {project} project.
For each combination of the tickets above and the following personas, estimate the size of the ticket.
{personas}
Return the estimates in a list of PlanningPokerEstimate objects, with estimate sizes averaged across the personas for each ticket.
"""
estimates = portia.run(
query=query,
structured_output_schema=PlanningPokerEstimateList,
).outputs.final_output.value.estimates
另一方面,Ethan 认为,无论是规划,还是对估算结果进行索引、聚合和迭代,我们其实都没有必要依赖 LLM。于是,他使用 Portia 的声明式 PlanBuilder 接口,明确列出了需要执行的步骤和 tool call。
他首先执行了一次 Portia plan,从 Linear 获取工单,并通过 structured output 将其转换为 LinearTicket 对象。接着,为了生成规模估算结果,他使用传统代码遍历每个开发者 persona,以及上一次 plan run 返回列表中的每个工单元素。每次迭代都会在一个只有单个步骤的 Portia plan run 中调用工单估算工具。
下面这段代码同时包含了获取工单的 plan run 和工单规模估算的迭代过程:
# Fetch Linear tickets
project = "Async SDK"
query = f"Get the tickets i'm working on from linear regarding the {project} project"
plan = PlanBuilder(
query, structured_output_schema=LinearTicketList
).step(
query + " and only call the tool with a limit of 3", tool_id="portia:mcp:mcp.linear.app:list_my_issues"
).step(
f"Filter the tickets to only include specifically the ones related to {project}", tool_id="llm_tool"
).build()
plan_run = portia.run_plan(plan)
tickets = plan_run.outputs.final_output.value.tickets
# Iterate over tickets and persona to generate estimates
for ticket in tickets:
estimates = []
estimate_plan = PlanBuilder(
"estimate the size of the ticket", structured_output_schema=PlanningPokerEstimate
).step(f"Estimate the size of the ticket: {ticket.title}\n\n{ticket.description}", tool_id="ticket_estimator_tool").build()
for persona in personas:
context = f"""
{persona}
{tool_context}
"""
estimate_tool.tool_context = context
portia.tool_registry.with_tool(estimate_tool, overwrite=True)
estimate = portia.run_plan(estimate_plan)
if estimate.state == PlanRunState.COMPLETE:
estimates.append(estimate.outputs.final_output.value)
让我们把这两种方法放在一起比较,并从中得出一些结论。我将 LangSmith 接入了 Portia,以便获得可观测性,并由此收集到下文所展示的各项指标。
你可以相信 Portia Agent 能够推断出正确的执行步骤顺序,也能正确地展开并迭代 tool call,因此它确实可以简化开发,有点像某种形式的 vibe coding。
不过,与 vibe coding 类似,它也需要一定程度的「LLM-whispering」——也就是 prompt engineering——同时还要选择正确的底层模型。特别是对于预期需要执行大量迭代的 plan run,你必须准备健壮的 eval sets,持续监控其可靠性,否则你原本想得到的是《蒙娜丽莎》,最终到手的却可能是一幅毕加索。
像我这样高度依赖 LLM 来处理规划和执行,确实会让追踪过程变得格外简单。Portia dashboard 中只需要一个 PlanRunState 实例,就能向我展示底层 subagent 完成的全部工作。当然,这也让后续重新查看 plan run 的输出变得更加容易。
相比之下,Ethan 最终产生了大量 plan run,因此审计和调试都会更加困难。
正如你所预料的,LLM-heavy 方法速度更慢,成本也更高。归根结底,我们处理的上下文总量应该是相同的——工单数量和估算次数都一样——但在 plan run 期间,需要在所有执行 Agent 之间持续传递不断增长的 context window,这部分额外开销意味着 LLM-heavy 方法不可避免地更慢、更昂贵。
此外,当代码本身就能完成任务时,采用这种方法也意味着你要承受 LLM 的随机性。
在上面的比较中,还有一个方面没有被纳入考虑,那就是自主性。
由于这个示例中的任务范围定义得非常清晰——planning poker Agent 等于获取并筛选工单、按 persona 分别进行估算,再汇总共识——因此完全可以认为,在生产规模下,应该只让 LLM 处理传统代码难以轻松完成的任务,例如自然语言处理。
但是,当来自环境的输入会发生变化,或者任务范围本身具有流动性时,LLM-heavy 方法才真正能够发挥优势。我会尝试在后续文章中更直观地展现这一点。👉🏼 如果你对此感兴趣,请在下方评论区告诉我!
Portia AI 是一个开源框架,用于构建可预测、有状态且经过身份验证的 Agent 工作流。
我们允许开发者根据自己的需要,对 multi-agent 部署保留任意程度的监督和控制,同时我们也极度专注于生产就绪能力。
欢迎大家体验我们的 SDK,大胆把东西折腾坏,并在 Discord 中告诉我们你的使用感受。
部分评论可能只有登录后的访客才能看到。登录即可查看全部评论。
如果需要采取进一步措施,你可以考虑屏蔽此人和/或举报滥用行为。