Agent 功能的架构约定图放 services/agent/graphs/,必须定义显式 TypedDict 输入/输出 state schema;prompt 一律放 services/agent/prompts/,不许内联在图代码里。

为什么要这么设计?

这套 Agent 架构约定的设计意图

你描述的这条规范本质上是在做关注点分离(Separation of Concerns),把 Agent 系统拆成了三个正交的维度:流程编排、数据契约、提示词工程。下面逐条拆解为什么要这么做。


1. Graph 放 services/agent/graphs/ + 显式 TypedDict State Schema

为什么不直接用 dictTypedDict 内联在函数签名里?

# ❌ 松散做法 — 上下游节点靠"默契"传字段
def node_a(state: dict) -> dict:
    state["foo"] = "bar"
    return state

# ✅ 规范做法 — 显式契约
class AgentState(TypedDict):
    messages: list[BaseMessage]
    plan: str
    tool_results: list[dict]
    is_complete: bool
好处 说明
编译期可检查 Mypy / Pyright 能在 CI 里直接报出字段拼写错误、类型不匹配,而不是等到运行时 KeyError
节点间契约清晰 每个 node 的 input → output 一目了然,新人不用顺着整个 graph 追数据流
Graph 可视化/序列化 LangGraph 等框架依赖显式 state 做 checkpoint、time-travel、状态恢复,隐式 dict 做不到
可测试性 单测一个节点时,TypedDict 就是 mock 数据的 schema,不用猜该传什么

2. Prompt 放 services/agent/prompts/,禁止内联

这是整条规范里最实用的一条,原因至少有四层:

① 角色分离:写 Prompt 的 ≠ 写 Graph 的

Prompt 工程师 / 产品人员 → 改 prompts/xxx.txt
后端工程师             → 改 graphs/xxx.py

Prompt 调优是高频迭代,如果 prompt 写在 Python 代码里,每次改个措辞都要触碰业务代码、跑完整 CI、有合并冲突风险

② 版本管理 & A/B 实验

prompts/
  planner_v1.txt
  planner_v2.txt        ← 灰度实验,直接切文件
  tool_router.txt
  summarizer.txt

放在独立文件里,可以:

  • 用 Git 对 prompt 单独做 diff / blame / 回滚
  • 按版本命名做 A/B test
  • 未来迁移到 Prompt 管理平台(LangSmith、Helicone 等)零成本

③ 可观测性 & 成本追踪

集中管理后,很容易加一层统一的 loader,顺带做:

  • Token 计数 / 成本预估
  • Prompt 注入检测
  • 变量注入审计(哪些 {variable} 被填充了什么值)

如果 prompt 散落在各个 .py 文件里,这些横切逻辑就没地方挂。

④ 防止 Prompt 与逻辑耦合

# ❌ 内联 — prompt 和流程控制混在一起,改 prompt 可能误改逻辑
def planner_node(state):
    response = llm.invoke(f"""你是一个规划助手。
    用户的请求是: {state['input']}
    如果涉及代码,请调用 code_tool...   # ← 这是 prompt 还是业务规则?
    """)
    ...

# ✅ 分离 — 各管各的
# prompts/planner.txt
# graphs/planner.py
def planner_node(state: PlannerInput) -> PlannerOutput:
    prompt = load_prompt("planner", input=state["input"])
    response = llm.invoke(prompt)
    ...

3. 整体目录结构的意图

services/agent/
├── graphs/            ← 流程编排("怎么走")
│   ├── main_agent.py
│   └── sub_graphs/
├── prompts/           ← 提示词("怎么说")
│   ├── planner.txt
│   └── tool_router.txt
├── tools/             ← 工具实现("用什么做")
└── schemas.py         ← TypedDict 定义("数据长什么样")

这四个目录对应了 Agent 系统的四个独立变化频率

目录 变化频率 改动人
graphs/ 低频(架构定下来很少改) 后端
schemas.py 中频(新增字段时改) 后端
prompts/ 高频(持续调优) Prompt 工程师 / 产品
tools/ 中频(新增能力时改) 后端

变化频率不同的东西不应该放在同一个文件里——这是软件设计里最朴素也最重要的原则之一。


一句话总结

这套约定的核心目的是:让 prompt 调优、图编排、数据契约三件事可以独立演进、独立测试、独立 review,互不拖累。 在 Agent 系统这种 prompt 改动频率远高于代码的系统里,这种分离不是洁癖,而是生存需要。

Logo

Agent 垂直技术社区,欢迎活跃、内容共建。

更多推荐