学习COZE编程 / LangGraph 通用工作流项目 提示词模板

COZE / LangGraph 通用工作流项目模

阅读说明

本文档中的规范内容分为三类:

【硬约束】

指当前项目代码、运行时适配层或平台调用方式已锁定的内容。

这类内容不建议擅自修改,否则可能导致运行、导入、参数导出或部署失败。

【默认规范】

指当前模板已采用、且推荐继续沿用的工程组织方式。

这类内容通常与项目实际一致,并且适合脚手架生成器直接采用。

【v1 新增】

指项目原模板未提供,但为提升生产可维护性、测试性、开箱即用能力而补充的规范。

这类内容已按"强制 / 推荐"区分,避免生成无效或空洞目录。


1. 文档定位

本文档用于定义一套面向 COZE 运行时 、基于 LangGraph 的通用工作流项目模板规范。

V1.4.2 的目标只有一个:生成一套可运行、可复制、可验收、可直接作为新项目起点的工程模板

这份规范面向:

  • 团队内部统一开发
  • 新项目脚手架生成
  • 外包 / 新人接入
  • LLM / 代码生成模型约束输入
  • 代码评审与项目对照检查

2. V1.4.2 的核心原则

  1. 以可行为第一原则

    不引入明知可能导致失败、歧义或高概率踩坑的默认实现。

  2. 以开箱即用为第一目标

    生成出来的项目应至少能:

    • 导入成功
    • 启动成功
    • 跑通一条 happy path
    • 通过最小测试
    • 导出 schema
  3. 以平台绑定点最小变更为前提

    入口、主图、导出变量、节点签名、schema 机制等不做发散设计。

  4. 以新人和生成器都能稳定理解为标准

    不依赖隐式约定,不把关键机制藏在实现细节里。


3. 适用项目范围

本规范覆盖以下项目类型:

  • 纯 workflow 项目
  • agent 项目
  • workflow + agent 共存项目
  • 带文件处理的项目
  • 带数据库 / checkpoint 的项目

4. 约束分层

4.1 【硬约束】

以下内容视为平台适配层和当前模板实现的锁定行为:

  • 入口文件固定为 src/main.py
  • 主图模块固定为 src/graphs/graph.py
  • 主图导出变量固定为 main_graph
  • 工作流输入输出通过 GraphInput / GraphOutput 声明
  • 状态模型使用 Pydantic BaseModel
  • 节点函数签名采用统一形式:stateconfigruntime
  • 节点函数中的 configruntime 参数必须保留,不得删除
  • .coze 中必须声明 project.entrypoint
  • scripts/ 目录路径不应随意改动
  • 运行时对 graphs.graphagents.agent 的识别和装载机制由 main.pygraph_helper 锁定
  • 修改节点、子图、工具、目录结构、配置、状态模型或工作流行为后,必须同步更新对应 AGENTS.md
  • 反射导入相关目录必须保留 __init__.py

4.2 【默认规范】

以下为推荐直接用于脚手架生成的默认值:

  • graph.py 只编排,不写业务逻辑
  • 节点默认返回自身完整 OutputModel
  • 图的最终对外输出由 output_schema=GraphOutput 从全局状态提取
  • 节点异常直接抛出,由入口层统一分类处理
  • 文件类入参默认使用统一 File 模型
  • 测试默认按 unit / integration / smoke 三层组织
  • checkpoint 默认可配置启用,失败时降级到内存
  • shell 脚本默认避免 eval + 字符串拼接 JSON
  • main.py 顶层初始化保持轻量,不在导入阶段执行高风险外部连接或重计算逻辑

4.3 【v1 新增 - 强制】

脚手架生成器或新建项目必须产出:

  • tests/unittests/integrationtests/smoke
  • .env.example
  • pytest 配置样例
  • 最小验收标准
  • 最小 AGENTS.md
  • 最小可运行代码骨架

4.4 【v1 新增 - 推荐】

以下为推荐扩展,不作为最小脚手架强制项:

  • docs/ 治理文档目录
  • observability/ 目录
  • routing/conditions.py
  • subgraphs/

5. 推荐目录结构

复制代码
project_root/
├── .coze
├── pyproject.toml
├── requirements.txt
├── README.md
├── AGENTS.md
├── .env.example
├── scripts/
│   ├── setup.sh
│   ├── pack.sh
│   ├── http_run.sh
│   ├── local_run.sh
│   ├── load_env.sh
│   └── load_env.py
├── src/
│   ├── __init__.py
│   ├── main.py
│   ├── graphs/
│   │   ├── __init__.py
│   │   ├── graph.py
│   │   ├── state.py
│   │   ├── routing/
│   │   │   ├── __init__.py
│   │   │   └── conditions.py
│   │   ├── subgraphs/
│   │   │   └── __init__.py
│   │   └── nodes/
│   │       ├── __init__.py
│   │       ├── input_node.py
│   │       ├── process_node.py
│   │       └── output_node.py
│   ├── agents/
│   │   ├── __init__.py
│   │   └── agent.py
│   ├── tools/
│   │   ├── __init__.py
│   │   └── example_tool.py
│   ├── storage/
│   │   ├── __init__.py
│   │   ├── memory/
│   │   │   ├── __init__.py
│   │   │   └── memory_saver.py
│   │   ├── database/
│   │   │   ├── __init__.py
│   │   │   └── db.py
│   │   └── s3/
│   │       ├── __init__.py
│   │       └── s3_storage.py
│   ├── utils/
│   │   ├── __init__.py
│   │   ├── errors.py
│   │   └── file/
│   │       ├── __init__.py
│   │       └── file.py
│   └── observability/
│       ├── __init__.py
│       └── tracing.py
├── tests/
│   ├── conftest.py
│   ├── unit/
│   │   └── test_nodes.py
│   ├── integration/
│   │   └── test_graph.py
│   └── smoke/
│       └── test_service.py
└── docs/
    ├── architecture.md
    ├── node_conventions.md
    ├── state_schema.md
    └── release_checklist.md

6. 目录职责说明

6.1 src/main.py 【硬约束】

作为平台统一入口与运行时适配层,负责:

  • HTTP 服务暴露
  • graph / agent 模式加载
  • 请求上下文创建
  • 同步 / 流式执行
  • schema 导出
  • 错误统一处理

6.2 src/graphs/ 【默认规范】

用于工作流图实现:

  • graph.py:主图编排
  • state.py:状态模型
  • nodes/:节点实现
  • routing/:条件路由
  • subgraphs/:子图

6.3 src/agents/ 【硬约束 + 默认规范】

用于 agent 模式实现。

可与 graphs/ 共存,但运行时只会选择一种模式执行。

6.4 src/tools/ 【默认规范】

工具目录不是自动发现目录。

必须显式导入、显式注册、显式调用。

6.5 src/storage/s3/ 命名兼容说明

src/storage/s3/ 下的实现文件推荐语义明确命名。

以下命名均可视为等价有效实现:

  • s3_storage.py
  • client.py

若项目已有 s3_storage.py,生成器不得重复创建同职责的 client.py

6.6 src/observability/ 【v1 新增 - 推荐】

不是最小模板强制项。

若项目无 tracing / metrics / logging 扩展需求,可不启用。

6.7 docs/ 【v1 新增 - 推荐】

用于治理文档。

不是最小可运行模板的必需项,但建议保留目录入口。


7. .coze 规范

7.1 .coze 的最小推荐配置

复制代码
[project]
entrypoint = "src/main.py"
requires = ["python-3.12"]

[dev]
build = ["bash", "scripts/setup.sh"]
run = ["bash", "scripts/http_run.sh", "-p", "5000"]
pack = ["bash", "scripts/pack.sh"]
deps = ["git"]

[deploy]
build = ["bash", "scripts/setup.sh"]
run = ["bash", "scripts/http_run.sh", "-p", "5000"]
deps = ["git"]

7.2 路径原则

模板规范中,脚本路径统一推荐使用相对路径

若现有项目中存在绝对路径写法,应视为历史实现,后续应逐步收敛。

7.3 必填项

  • project.entrypoint

8. 依赖与打包规范

8.1 依赖管理方式

推荐优先使用 uv,同时保留 pip 回退能力。

8.2 文件职责

  • pyproject.toml:主依赖声明
  • requirements.txt:兼容性依赖清单
  • pack.sh:导出 requirements.txt

8.3 双模式原则

模板应支持:

  • uv 为首选安装方式
  • pip 为兜底回退方式

8.4 测试依赖要求 【v1 新增 - 强制】

若测试使用异步接口(如 ainvoke()@pytest.mark.asyncio),测试环境必须安装:

  • pytest
  • pytest-asyncio
  • httpx

推荐在 pyproject.toml 中声明:

复制代码
[project.optional-dependencies]
test = [
    "pytest>=8,<9",
    "pytest-asyncio>=0.23,<1",
    "httpx>=0.27,<1",
]

9. 状态建模规范

9.1 状态分层

推荐采用以下状态层级:

状态类型 命名约定 作用
全局状态 GlobalState 图运行时全量业务状态
图输入 GraphInput 工作流对外输入 Schema
图输出 GraphOutput 工作流对外输出 Schema
节点输入 {NodeName}Input 节点消费的数据契约
节点输出 {NodeName}Output 节点产出的数据契约

9.2 基本要求 【硬约束】

  • 所有状态模型使用 Pydantic BaseModel
  • GraphInput / GraphOutput 必须支持 model_json_schema()
  • 图的输出通过 output_schema=GraphOutput 对外暴露

9.3 输出映射约定 【默认规范】

  • 节点优先返回自身的 OutputModel
  • 节点输出会写入运行时状态
  • 图的最终对外输出由 output_schema=GraphOutput 从全局状态中提取

9.4 GraphOutput 返回模式说明

当前模板默认模式是:

  • 最后一个业务节点返回自身的 OutputModel
  • LangGraph 通过 output_schema=GraphOutput 从全局状态中自动提取最终输出字段

在简单场景下,也可以让最后一个节点直接返回 GraphOutput,但这不是默认推荐示例。

9.5 节点间状态合并机制说明 【默认规范】

LangGraph 的执行机制如下:

  • 节点返回的 OutputModel 会被转换为字典
  • 返回字段会合并进当前运行时状态
  • 下一个节点看到的是已合并后的状态视图

因此:

  • 节点 A 返回的字段,节点 B 之所以能读取,是因为这些字段已被合并进状态
  • 节点的 InputModel 所需字段,应存在于 GlobalState 中,或具备默认值
  • 若节点声明了必填字段,但前序节点未将其写入状态,则运行时会因字段缺失而报错

9.6 状态合并风险警示 【默认规范】

若节点输出的字段未在 GlobalState 中声明,且状态模型未显式允许额外字段,则在状态合并时可能触发校验失败。

因此:

  • 节点输出字段应与 GlobalState 保持一致
  • 尤其是 raw_datadebug_infointermediate_result 等辅助字段,不能随意返回而不建模

示例:

  • read_excel_node 返回 raw_data
  • GlobalState 中应显式声明 raw_data
  • 否则后续状态更新可能失败

9.7 辅助字段聚合兜底方案 【v1 新增 - 推荐】

为减少 GlobalState 因辅助字段频繁扩容,推荐增加一个通用聚合字段:

复制代码
metadata: dict = Field(
    default_factory=dict,
    description="节点间透传的辅助数据、调试信息、中间结果等"
)

使用原则:

  • 非核心辅助字段优先写入 metadata
  • 核心业务字段仍应显式声明在 GlobalState 顶层
  • metadata 是兜底容器,不替代显式建模

例如:

  • raw_data
  • debug_info
  • intermediate_result

这类字段若不适合作为顶层字段,可统一写入 metadata

9.8 metadata 与最终输出的关系

仅在 GlobalState 中声明 metadata,不会让它自动出现在最终输出中。

若希望 metadata 进入最终返回结果,则必须同时在 GraphOutput 中声明该字段。

9.9 文件类型规范 【默认规范】

当节点需要处理 Excel、PDF、图片等文件时,应优先使用 utils.file.file.File 作为字段类型,而不是原始字符串路径。

File 模型统一表达:

  • 本地路径
  • 远程 URL
  • 文件类型推断
  • 下载与本地化处理能力

9.10 最小状态示例

复制代码
from typing import Optional, Literal
from pydantic import BaseModel, Field


class GraphInput(BaseModel):
    query: str = Field(..., description="用户输入")
    debug: bool = Field(default=False, description="调试开关")


class GraphOutput(BaseModel):
    answer: str = Field(..., description="输出结果")
    status: Literal["success", "failed"] = Field(..., description="执行状态")
    metadata: Optional[dict] = None


class GlobalState(BaseModel):
    query: str
    debug: bool = False
    normalized_query: Optional[str] = None
    answer: Optional[str] = None
    status: Optional[Literal["success", "failed"]] = None
    metadata: dict = Field(default_factory=dict)
    error_code: Optional[str] = None
    error_message: Optional[str] = None


class InputNodeInput(BaseModel):
    query: str


class InputNodeOutput(BaseModel):
    normalized_query: str


class ProcessNodeInput(BaseModel):
    normalized_query: str
    metadata: dict = Field(default_factory=dict)


class ProcessNodeOutput(BaseModel):
    answer: str
    status: Literal["success", "failed"]
    metadata: dict = Field(default_factory=dict)

10. 图编排规范

10.1 graph.py 职责 【默认规范】

src/graphs/graph.py 只负责:

  • 创建 StateGraph
  • 绑定输入输出 Schema
  • 注册节点
  • 设置入口点
  • 添加顺序边
  • 按需添加条件边 / 子图 / 循环
  • 编译并导出 main_graph

不应在 graph.py 中编写具体业务处理逻辑。

10.2 模块约束 【硬约束】

  • 文件名固定为 graph.py
  • 模块路径固定为 graphs.graph
  • 必须导出 main_graph

10.3 主图最小示例

复制代码
from langgraph.graph import StateGraph, END
from graphs.state import GlobalState, GraphInput, GraphOutput
from graphs.nodes.input_node import input_node
from graphs.nodes.process_node import process_node
from graphs.nodes.output_node import output_node

builder = StateGraph(
    GlobalState,
    input_schema=GraphInput,
    output_schema=GraphOutput,
)

builder.add_node("input_node", input_node)
builder.add_node("process_node", process_node)
builder.add_node("output_node", output_node)

builder.set_entry_point("input_node")
builder.add_edge("input_node", "process_node")
builder.add_edge("process_node", "output_node")
builder.add_edge("output_node", END)

main_graph = builder.compile()

10.4 条件边规范 【v1 新增 - 推荐】

条件边不是基础模板强制项,但底层支持。

推荐将条件函数放在 src/graphs/routing/conditions.py

最小示例:

复制代码
from typing import Literal
from graphs.state import GlobalState


def decide_next(state: GlobalState) -> Literal["process_node", "output_node"]:
    if state.query.strip():
        return "process_node"
    return "output_node"

10.5 子图与并行说明

  • 子图支持,但不作为最小模板默认形态
  • 并行支持,但不建议作为最小脚手架默认实现
  • 若启用子图或并行,需额外补充状态合并策略

11. 节点开发规范

11.1 节点职责

节点是最小执行单元,应满足:

  • 单一职责
  • 输入输出明确
  • 尽量纯函数化
  • 便于单元测试

11.2 统一函数签名 【硬约束】

复制代码
from langchain_core.runnables import RunnableConfig
from langgraph.runtime import Runtime
from coze_coding_utils.runtime_ctx.context import Context

def node_id_node(
    state: NodeInput,
    config: RunnableConfig,
    runtime: Runtime[Context],
) -> NodeOutput:
    ...

11.3 节点参数要求

【硬约束】
  • config 必须保留
  • runtime 必须保留
【默认规范】
  • 支持 defasync def
  • 默认返回完整 OutputModel
  • 不在模块导入阶段执行副作用代码

11.4 命名规范

推荐命名为:

  • 文件名:{node_id}_node.py
  • 函数名:{node_id}_node

11.5 DocString 元数据

推荐在节点函数中维护结构化 DocString:

复制代码
def process_node(...):
    """
    title: 数据处理节点
    desc: 对输入数据进行标准化和计算
    integrations: pandas, openpyxl
    """

11.6 异常处理规范

节点默认直接抛出异常,不建议吞错。

推荐做法:

  • 参数问题抛 ValueError
  • 资源不存在抛 FileNotFoundError
  • 外部依赖失败抛语义明确的异常
  • 统一在 main.py 中做错误分类与返回格式化

11.7 最小节点示例

复制代码
from langchain_core.runnables import RunnableConfig
from langgraph.runtime import Runtime
from coze_coding_utils.runtime_ctx.context import Context
from graphs.state import InputNodeInput, InputNodeOutput


def input_node(
    state: InputNodeInput,
    config: RunnableConfig,
    runtime: Runtime[Context],
) -> InputNodeOutput:
    """
    title: 输入节点
    desc: 对输入进行清洗和标准化
    integrations:
    """
    query = state.query.strip()
    if not query:
        raise ValueError("query 不能为空")

    return InputNodeOutput(normalized_query=query)

12. 服务入口与运行时规范

12.1 main.py 职责 【硬约束】

src/main.py 负责:

  • 创建 HTTP 服务
  • 装载 graph 或 agent
  • 创建请求上下文
  • 执行同步与流式运行
  • 暴露 schema 查询
  • 统一错误处理

12.2 main.py 修改原则

main.py 作为平台适配层,开发者通常不应修改。

仅在以下场景下,才可谨慎变更:

  • 新增全局 HTTP 端点
  • 调整统一错误处理策略
  • 修改全局超时、执行包装或服务初始化逻辑

12.3 main.py 延迟初始化建议 【默认规范】

推荐 main.py 在模块顶层仅执行轻量初始化,例如:

  • app = FastAPI()
  • 轻量对象装配
  • 常量级配置读取

将可能触发以下行为的逻辑尽量延迟到首次请求或首次调用时执行:

  • 外部数据库连接
  • 环境变量重度校验
  • 模型加载
  • 大计算量初始化
  • 远程依赖探测

该建议用于降低:

  • from main import app 的导入失败概率
  • 烟雾测试失败概率
  • 本地开发首次接入门槛

12.4 模式识别与装载机制 【硬约束】

项目中 graphs/agents/ 可共存,但运行时只会选择一种模式执行。

运行时通过 graph_helper.is_agent_proj() 自动判断项目类型:

  • 若识别为 Agent 项目,则加载 agents.agent
  • 否则加载 graphs.graph

12.5 推荐端点

推荐暴露:

  • POST /run
  • POST /stream_run
  • POST /cancel/{run_id}
  • POST /node_run/{node_id}
  • GET /graph_parameter
  • GET /health

12.6 上下文规范

【硬约束】

当前已验证的核心上下文字段为:

  • run_id
【默认规范】

推荐要求:

  • 从 Header x-run-id 读取
  • 若不存在,由服务端生成
  • 在整个请求链路中保持透传一致

12.7 流式输出规范 【硬约束】

推荐采用 SSE:

复制代码
id: {run_id}
event: message
data: {json}

并通过 Header 区分:

  • 常规流式
  • debug 流式(x-workflow-stream-mode: debug

12.8 本地运行模式

建议保留以下启动模式:

复制代码
python src/main.py -m http
python src/main.py -m flow
python src/main.py -m node -n process_node
python src/main.py -m agent

13. 持久化与存储规范

13.1 Checkpointer 策略

checkpoint 不是平台自动注入能力,应由模板代码显式管理。

推荐实现:

  • 优先使用 Postgres checkpointer
  • 失败时自动降级到 MemorySaver
  • 使用单例管理生命周期

13.2 原则

  • checkpoint 为推荐能力,不是所有项目的强制要求
  • 生产项目建议启用
  • 本地开发可仅用内存模式

13.3 存储层职责

storage/memory/
  • checkpointer 初始化
  • 降级逻辑
  • 生命周期管理
storage/database/
  • 连接管理
  • 凭据与连接字符串装配
  • 重试逻辑
storage/s3/
  • 文件上传下载
  • 对象存储抽象

14. 工具层规范

14.1 基本原则

src/tools/ 不是自动发现目录。

模板要求:显式导入、显式注册、显式调用

14.2 工具适用场景

适合封装:

  • 外部 API 调用
  • 文件读取
  • 数据库查询
  • 通用文本处理
  • 多节点复用逻辑

14.3 最小示例

复制代码
from pydantic import BaseModel


class ExampleToolInput(BaseModel):
    text: str


class ExampleToolOutput(BaseModel):
    result: str


def example_tool(payload: ExampleToolInput) -> ExampleToolOutput:
    return ExampleToolOutput(result=payload.text.strip().lower())

15. 错误处理规范

15.1 分层原则 【v1 新增】

错误建议分为:

  1. 用户输入错误
  2. 节点执行错误
  3. 外部依赖错误
  4. 系统配置错误
  5. 未知错误

15.2 推荐做法

  • 节点层抛出语义清晰的异常
  • main.py 统一捕获与分类
  • debug 模式可附加诊断信息
  • 生产模式避免泄露内部堆栈

15.3 最小错误模型 【v1 新增】

复制代码
from typing import Optional
from pydantic import BaseModel


class ErrorResponse(BaseModel):
    code: str
    message: str
    detail: Optional[str] = None
    run_id: Optional[str] = None

16. 测试规范

16.1 测试分层 【v1 新增 - 强制】

单元测试

针对单个节点:

  • 构造 Input Model
  • mock config
  • mock runtime
  • 断言 Output Model 或异常
集成测试

针对 main_graph

  • 导入 main_graph
  • 调用 invoke()ainvoke()
  • 断言主要状态流转与输出
烟雾测试

针对 HTTP 服务:

  • /health
  • /graph_parameter

16.2 最小测试文件清单 【v1 新增 - 强制】

脚手架生成器必须产出以下最小测试文件:

复制代码
tests/
├── conftest.py
├── unit/
│   └── test_nodes.py
├── integration/
│   └── test_graph.py
└── smoke/
    └── test_service.py

每个文件都必须至少包含一个可运行测试函数,不得只生成空目录。

16.3 文件职责说明

文件 强制要求 覆盖目标
tests/conftest.py 提供 mock runtime / mock config 等公共 fixture
tests/unit/test_nodes.py 至少覆盖 1 个核心节点的 success / error 分支
tests/integration/test_graph.py 至少覆盖 main_graph 的 1 条 happy path
tests/smoke/test_service.py 覆盖 /health/graph_parameter

16.4 pytest 配置建议 【v1 新增 - 强制】

建议在 pyproject.toml 中补充:

复制代码
[tool.pytest.ini_options]
testpaths = ["tests"]
python_files = ["test_*.py"]
python_classes = ["Test*"]
python_functions = ["test_*"]
addopts = "-ra -q"

16.5 最小测试示例

tests/conftest.py
复制代码
import pytest
from types import SimpleNamespace


@pytest.fixture
def mock_config():
    return {}


@pytest.fixture
def mock_runtime():
    return SimpleNamespace(context=SimpleNamespace(run_id="test-run"))
tests/unit/test_nodes.py
复制代码
import pytest
from graphs.nodes.input_node import input_node
from graphs.state import InputNodeInput


def test_input_node_success(mock_config, mock_runtime):
    result = input_node(
        InputNodeInput(query="  hello  "),
        mock_config,
        mock_runtime,
    )
    assert result.normalized_query == "hello"


def test_input_node_empty_raises(mock_config, mock_runtime):
    with pytest.raises(ValueError, match="query 不能为空"):
        input_node(
            InputNodeInput(query="   "),
            mock_config,
            mock_runtime,
        )
tests/integration/test_graph.py
复制代码
import pytest
from graphs.graph import main_graph
from graphs.state import GraphInput


@pytest.mark.asyncio
async def test_main_graph_happy_path():
    result = await main_graph.ainvoke(
        GraphInput(query="hello", debug=False)
    )
    assert result["status"] == "success"
    assert "answer" in result
tests/smoke/test_service.py
复制代码
from fastapi.testclient import TestClient
from main import app


client = TestClient(app)


def test_health():
    resp = client.get("/health")
    assert resp.status_code == 200


def test_graph_parameter():
    resp = client.get("/graph_parameter")
    assert resp.status_code == 200

16.6 烟雾测试导入风险提醒

main.py 在模块顶层执行环境变量读取、数据库连接或 graph 初始化,from main import app 可能在导入阶段失败。

因此建议:

  • 避免在 main.py 顶层执行不可控外部资源初始化
  • 必要时在测试中使用 monkeypatch 或测试专用环境变量
  • 保证 test_service.py 在无生产依赖的本地环境中也可导入并运行

17. 最小验收标准 【v1 新增 - 强制】

每个新建项目至少满足以下条件:

  1. scripts/setup.sh 可执行
  2. python src/main.py -m flow 可跑通一条 happy path
  3. GET /health 正常返回
  4. GET /graph_parameter 可返回合法 schema
  5. 至少一个节点单测通过
  6. 至少一个主图集成测试通过
  7. 若启用 checkpoint,需验证其可初始化
  8. pack.sh 可成功导出依赖

18. AGENTS.md 规范

18.1 作用说明

AGENTS.md 用于记录项目结构、节点职责、工具能力、依赖与约束信息。

该文件既服务团队成员,也服务 LLM / 代码生成模型。

18.2 推荐维护内容

建议在 AGENTS.md 中维护:

  • 节点清单
  • 节点职责
  • 子图清单
  • 工具清单
  • 输入输出摘要
  • 外部依赖
  • 风险点与注意事项

18.3 同步维护要求 【硬约束】

当以下内容发生变化时,必须同步更新对应 AGENTS.md

  • 节点新增、删除或重命名
  • 子图新增、删除或重构
  • 工具新增、删除或接口变更
  • 目录结构调整
  • 配置与工作流行为变化
  • 节点职责、输入输出、依赖关系变化
  • 节点的 InputModelOutputModel 发生字段新增、删除、重命名或类型变更

18.4 格式兼容说明

AGENTS.md 的格式不限于列表或表格,以清晰表达以下信息为准:

  • 节点清单
  • 职责边界
  • 输入输出摘要
  • 约束规则

建议:

  • 节点较少的项目可使用列表
  • 节点较多的项目可使用表格

生成器不应因列表 / 表格格式差异而重复改写内容。

18.5 最小模板

复制代码
# AGENTS.md

## Project Overview
- 项目类型:workflow / agent / hybrid
- 主入口:src/main.py
- 主图模块:src/graphs/graph.py
- 主图导出:main_graph

## Nodes
- input_node: 输入清洗与标准化
- process_node: 核心业务处理
- output_node: 输出整理

## State Models
- GraphInput: 外部输入
- GraphOutput: 外部输出
- GlobalState: 运行时全局状态

## Tools
- example_tool: 示例工具

## Constraints
- 节点签名必须保留 state/config/runtime
- 修改节点、工具、目录结构、状态模型后必须同步更新本文件
- graph.py 只负责编排,不写业务逻辑

19. 环境变量规范

19.1 .env.example 【v1 新增 - 强制】

建议提供最小示例:

复制代码
# 数据库连接(用于 checkpoint 持久化)
PGDATABASE_URL=postgresql://user:password@host:port/dbname

# 可选:开发环境标识
COZE_PROJECT_ENV=DEV

19.2 使用原则

  • .env.example 只放占位示例,不放真实凭证
  • 生产敏感信息应通过安全配置系统注入
  • README 或 AGENTS.md 中应说明哪些变量是必需的,哪些是可选的

20. 治理文档建议 【v1 新增 - 推荐】

建议保留最小治理文档入口:

  • architecture.md
  • node_conventions.md
  • state_schema.md
  • release_checklist.md

不是最小可运行模板的强制项。


21. 脚本实现规范

21.1 基本原则

脚本必须以稳定传参为优先,不得默认采用易出错的 eval + 拼接 JSON 方案。

21.2 local_run.sh 安全要求 【默认规范】

禁止默认采用如下高风险模式:

复制代码
cmd="$cmd -i '$input'"
eval "$cmd"

原因:

  • JSON 中若包含单引号、换行或特殊字符,极易失败
  • 导致本地调试行为与真实输入不一致

21.3 推荐做法

应优先选择以下方案之一:

  • 使用 "$@" 原样透传参数
  • 将 JSON 写入临时文件再读取
  • 采用 base64 编码传输再在程序内解码

模板默认建议使用"临时文件"或"参数原样透传"方式,不使用 eval

21.4 安全版 local_run.sh 参考实现 【v1 新增 - 强制示例】

复制代码
#!/bin/bash
set -e

mode=""
node=""
input=""
SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
WORK_DIR="${COZE_WORKSPACE_PATH:-$(dirname "$SCRIPT_DIR")}"

usage() {
  echo "用法: $0 -m <模式> [-n <节点ID>] [-i <输入JSON>]"
}

while getopts "m:n:i:h" opt; do
  case "$opt" in
    m) mode="$OPTARG" ;;
    n) node="$OPTARG" ;;
    i) input="$OPTARG" ;;
    h) usage; exit 0 ;;
    \?) usage; exit 1 ;;
  esac
done

[ -z "$mode" ] && { echo "错误: 必须指定 -m 参数"; usage; exit 1; }

args=("-m" "$mode")
[ -n "$node" ] && args+=("-n" "$node")

if [ -n "$input" ]; then
  tmpfile=$(mktemp)
  printf '%s' "$input" > "$tmpfile"
  args+=("-i" "@$tmpfile")
  trap 'rm -f "$tmpfile"' EXIT
fi

if [ -f "${SCRIPT_DIR}/load_env.sh" ]; then
  source "${SCRIPT_DIR}/load_env.sh"
fi

python "${WORK_DIR}/src/main.py" "${args[@]}"

说明:

  • 该方案避免 eval
  • 通过临时文件规避 JSON 引号逃逸问题
  • 若采用 -i "@file" 约定,则 main.py 的输入解析逻辑必须支持从文件读取内容

22. 最小可运行代码骨架

22.1 .coze

复制代码
[project]
entrypoint = "src/main.py"
requires = ["python-3.12"]

[dev]
build = ["bash", "scripts/setup.sh"]
run = ["bash", "scripts/http_run.sh", "-p", "5000"]
pack = ["bash", "scripts/pack.sh"]
deps = ["git"]

[deploy]
build = ["bash", "scripts/setup.sh"]
run = ["bash", "scripts/http_run.sh", "-p", "5000"]
deps = ["git"]

22.2 src/graphs/state.py

复制代码
from typing import Optional, Literal
from pydantic import BaseModel, Field


class GraphInput(BaseModel):
    query: str = Field(..., description="用户输入")
    debug: bool = Field(default=False, description="调试开关")


class GraphOutput(BaseModel):
    answer: str = Field(..., description="输出结果")
    status: Literal["success", "failed"] = Field(..., description="执行状态")
    metadata: Optional[dict] = None


class GlobalState(BaseModel):
    query: str
    debug: bool = False
    normalized_query: Optional[str] = None
    answer: Optional[str] = None
    status: Optional[Literal["success", "failed"]] = None
    metadata: dict = Field(default_factory=dict)
    error_code: Optional[str] = None
    error_message: Optional[str] = None


class InputNodeInput(BaseModel):
    query: str


class InputNodeOutput(BaseModel):
    normalized_query: str


class ProcessNodeInput(BaseModel):
    normalized_query: str
    metadata: dict = Field(default_factory=dict)


class ProcessNodeOutput(BaseModel):
    answer: str
    status: Literal["success", "failed"]
    metadata: dict = Field(default_factory=dict)

22.3 src/graphs/nodes/input_node.py

复制代码
from langchain_core.runnables import RunnableConfig
from langgraph.runtime import Runtime
from coze_coding_utils.runtime_ctx.context import Context
from graphs.state import InputNodeInput, InputNodeOutput


def input_node(
    state: InputNodeInput,
    config: RunnableConfig,
    runtime: Runtime[Context],
) -> InputNodeOutput:
    """
    title: 输入节点
    desc: 对输入进行清洗和标准化
    integrations:
    """
    query = state.query.strip()
    if not query:
        raise ValueError("query 不能为空")

    return InputNodeOutput(normalized_query=query)

22.4 src/graphs/nodes/process_node.py

复制代码
from langchain_core.runnables import RunnableConfig
from langgraph.runtime import Runtime
from coze_coding_utils.runtime_ctx.context import Context
from graphs.state import ProcessNodeInput, ProcessNodeOutput


def process_node(
    state: ProcessNodeInput,
    config: RunnableConfig,
    runtime: Runtime[Context],
) -> ProcessNodeOutput:
    """
    title: 处理节点
    desc: 生成最终回答
    integrations:
    """
    return ProcessNodeOutput(
        answer=f"processed: {state.normalized_query}",
        status="success",
        metadata={"source": "process_node"},
    )

22.5 src/graphs/nodes/output_node.py

复制代码
from langchain_core.runnables import RunnableConfig
from langgraph.runtime import Runtime
from coze_coding_utils.runtime_ctx.context import Context
from graphs.state import GlobalState, GraphOutput


def output_node(
    state: GlobalState,
    config: RunnableConfig,
    runtime: Runtime[Context],
) -> GraphOutput:
    """
    title: 输出整理节点
    desc: 从全局状态整理最终输出
    integrations:
    """
    if not state.answer or not state.status:
        raise ValueError("输出字段缺失")

    return GraphOutput(
        answer=state.answer,
        status=state.status,
        metadata=state.metadata or None,
    )

22.6 src/graphs/graph.py

复制代码
from langgraph.graph import StateGraph, END
from graphs.state import GlobalState, GraphInput, GraphOutput
from graphs.nodes.input_node import input_node
from graphs.nodes.process_node import process_node
from graphs.nodes.output_node import output_node

builder = StateGraph(
    GlobalState,
    input_schema=GraphInput,
    output_schema=GraphOutput,
)

builder.add_node("input_node", input_node)
builder.add_node("process_node", process_node)
builder.add_node("output_node", output_node)

builder.set_entry_point("input_node")
builder.add_edge("input_node", "process_node")
builder.add_edge("process_node", "output_node")
builder.add_edge("output_node", END)

main_graph = builder.compile()

23. 适用于代码生成模型的规则

为保证模板生成稳定性,LLM 或代码生成器在基于本文档生成项目时,应严格遵守以下规则:

  1. 不修改 src/main.pysrc/graphs/graph.pymain_graph.coze 的关键绑定关系
  2. 所有状态模型使用 Pydantic
  3. 节点签名必须包含 state/config/runtime
  4. 节点默认返回 OutputModel
  5. 文件入参优先使用统一 File 模型
  6. 新增节点、工具、目录、状态模型后同步更新 AGENTS.md
  7. 必须生成最小测试文件清单,而不是只生成空目录
  8. 必须生成 .env.example 与 pytest 配置
  9. graph.py 只编排,不写业务实现
  10. 不默认引入复杂子图、并行编排和不必要扩展目录
  11. 不生成使用 eval 处理 JSON 入参的脚本
  12. 对辅助字段默认使用 metadata 兜底,除非用户明确要求单独顶层建模
  13. 对异步测试默认补齐 pytest-asyncio 依赖
  14. 不因 AGENTS.md 使用列表或表格而进行无意义改写

24. V1.4.2 的最小落地标准

若一个项目声称"符合 V1.4.2 模板",则至少应满足:

  • 可导入 graphs.graph
  • 可导入 main
  • main_graph 存在
  • GraphInput / GraphOutput 存在
  • 节点签名为 state/config/runtime
  • 本地可执行最小 happy path
  • /health/graph_parameter 可访问
  • tests/unittests/integrationtests/smoke 存在且含可运行测试
  • .env.example 存在
  • AGENTS.md 存在且与代码一致

25. 边界说明

V1.4.2 的重点不是"写得最全",而是"默认生成后尽量不出错"。

因此,以下内容被有意弱化或不作为默认项:

  • 不强制引入复杂错误处理节点
  • 不默认引入并行分支
  • 不默认引入子图
  • 不默认引入重型 observability 设计
  • 不使用高风险 shell 拼接方式
  • 不推荐返回未建模的辅助字段

26. 结论

本规范适合作为 正式脚手架生成标准、项目验收清单与代码评审基线

其中:

  • 平台入口、主图模块、主图导出、Pydantic 状态、节点签名、config/runtime 参数保留、脚本路径、agent/workflow 自动识别、AGENTS.md 同步要求、__init__.py 保留等属于【硬约束】
  • OutputModel 返回、output_schema 映射、File 类型、状态合并机制、metadata 兜底、工具显式注册、相对路径优先、测试三层结构、轻量顶层初始化等属于【默认规范】
  • 最小测试文件清单、.env.example、pytest 配置、最小 AGENTS.md、治理文档目录、路由目录、子图目录、安全版 local_run.sh 参考实现等属于【v1 新增】

V1.4.2 追求的不是理论完备,而是:
生成即可用,运行不踩坑,验收有标准,扩展有边界。

附录 经验补充

  1. 先跑通最小闭环:先让基础工作流能端到端运行,再逐步叠加分支和外部依赖,避免一开始就陷入复杂调优。
  2. 数据与代码分离:配置和常亮数据等结构化数据单独放置在 JSON 配置文件,更新时只改数据文件,不动业务逻辑代码。需要外部用户更改的参数配置,可以使用.toml文件,更易于人类阅读。
  3. 分支兜底防崩溃:用条件路由拦截非法或无关输入,闲聊走对话分支,查询走工作流,杜绝原始报错抛给用户。
  4. LLM 一箭双雕:让入口节点同时完成"回复用户"和"判断是否触发工作流",把两次模型调用压缩为一次。
  5. Prompt 边界约束:严格限制 LLM 只输出简短确认语,禁止其编造具体日期,所有事实数据交由后端结构化节点生成。
  6. 流式+耗时透视 :开启 --stream 逐节点追踪,配合 elapsed_ms 统计,一眼看清耗时到底卡在哪个环节。
  7. 诊断脚本科学定位:写专门脚本拆分 TTFB、连接建立和内容生成时间,避免凭感觉猜,精准定位是网络慢还是模型慢。
  8. 换模型有时比改代码更有效:当代码逻辑已最优仍很慢时,果断换更快端点或更轻量模型,收益可能远超代码层面优化。
  9. 将工作流生成workflow.md在根目录创建workflow.md,用严格mermaid规范格式来描述工作流包括:## flowchart LR、 ## 节点说明、## 输入输出、## 关键设计
相关推荐
菜鸟小九2 小时前
JUC(共享模型之管程、synchronized、wait、park、活跃性、renetrantlock、条件变量)
java·开发语言·juc
程序员阿明2 小时前
spring boot3识别PDF图纸
java·spring boot·后端·pdf
研究点啥好呢2 小时前
Github热榜项目推荐 | 学习与贡献是开源的意义
学习·开源·github
Java成神之路-2 小时前
深度解析TCP连接管理:三次握手、四次挥手与保活机制
网络·网络协议·tcp/ip
blxr_2 小时前
Spring AI自定义Advisor
java·spring
kisloy2 小时前
【反爬虫】极验4 W参数逆向分析
java·javascript·爬虫
每天吃饭的羊2 小时前
性能优化学习
学习
-Rane2 小时前
【C++】红黑树
java·开发语言
浮槎来2 小时前
光伏组件的PID学习
运维·学习·硬件工程·光伏