news 2026/10/10 5:32:16

TaskWeaver 核心数据概念解析:Post 消息在角色通信、附件管理与提示词生命周期中的设计

作者头像

张小明

前端开发工程师

1.2k 24
文章封面图
TaskWeaver 核心数据概念解析:Post 消息在角色通信、附件管理与提示词生命周期中的设计
  • AI Agent
  • 数据分析

【免费下载链接】TaskWeaver

The first "code-first" agent framework for seamlessly planning and executing data analytics tasks.

项目地址:https://gitcode.com/gh_mirrors/ta/TaskWeaver
点击查看免费下载

Post(消息)是 TaskWeaver 会话系统中连接各角色(Role)的最小数据单元:无论是用户的提问、Planner 的规划指令,还是 CodeInterpreter 的执行结果,最终都以 Post 的形式在角色之间传递,并被组织进 Round 与 Conversation 供 LLM 提示词构造和历史记忆使用。本篇以 Post 官方概念文档 为骨架,结合 post.py 等核心源码,从数据结构、附件体系、消息持久性到提示词中的呈现方式,完整剖析 Post 的设计原理与实战用法。

一、Post 在 TaskWeaver 会话数据模型中的定位

TaskWeaver 的记忆体系采用“会话(Conversation)→ 轮次(Round)→ 消息(Post)”三层递进结构,Post 是最底层的消息载体:

  • Conversation:一次与用户的完整交互,包含若干 Round,定义见 conversation.py;
  • Round:一次对话回合的基本单元,是 Post 的有序集合,同时记录user_query与state(finished/failed/created),定义见 round.py;
  • Post:单条消息,承载角色间的文本内容与非文本附件。

用源码中的类注释可以概括 Post 的语义:"A post is the message used to communicate between two roles."(Post 是用于在两个角色之间通信的消息)。它应当始终携带一个文本message作为字符串消息,而其他数据格式则应放入附件(Attachment)中,发送角色可以是 User、Planner 或其他扩展角色。

从数据关系看,Post 是唯一直接面向角色(Role)的数据对象——角色的reply方法返回的就是一个 Post(见 role.py 中def reply(self, memory: Memory, **kwargs) -> Post),因此理解 Post 是理解整个 TaskWeaver 消息流转的起点。

二、Post 数据结构与字段详解

Post 在 post.py 中定义为@dataclass,包含五个字段:

@dataclass class Post: id: str # Post 的唯一 ID send_from: RoleName # 发送方角色 send_to: RoleName # 接收方角色 message: str # 文本消息内容 attachment_list: List[Attachment] # 附件列表

各字段含义如下:

字段类型说明
idstrPost 的唯一标识,形如post-xxx,由create_id()生成
send_fromRoleName发送方角色名,RoleName在 type_vars.py 中定义为str类型别名
send_toRoleName接收方角色名
messagestr文本消息,始终存在(允许为空字符串)
attachment_listList[Attachment]附件列表,承载文本之外的结构化数据

关于send_from与send_to,官方文档特别指出:在某些场景下两者可以相同,用于表示角色的自我通信(self-communication)。例如 Planner 在自我思考、整理计划时会向自己发送 Post,这是 TaskWeaver 规划流程中的常见用法。

2.1 消息的创建:Post.create

post.py 提供了静态工厂方法Post.create:

@staticmethod def create( message: Optional[str], send_from: RoleName, send_to: RoleName = "Unknown", attachment_list: Optional[List[Attachment]] = None, ) -> Post: return Post( id="post-" + create_id(), message=message is not None and message or "", send_from=send_from, send_to=send_to, attachment_list=attachment_list if attachment_list is not None else [], )

要点:message为None时会被规范化为空字符串,attachment_list缺省时为空列表,send_to默认值为"Unknown",方便在不确定接收者时先创建再填充。

2.2 序列化:to_dict / from_dict

Post 需要持久化到 YAML 文件中(会话记录、经验沉淀),因此实现了双向转换:

  • to_dict()将 Post 转为字典,附件列表通过每个Attachment.to_dict()递归序列化;
  • from_dict()从字典还原 Post,并重新生成新的id(使用secrets.token_hex(6)),保证从文件加载后的对象与原始对象区分开。

2.3 附件操作接口

Post 提供了三个附件操作接口(post.py):

def add_attachment(self, attachment: Attachment) -> None: ... # 追加附件 def get_attachment(self, type: AttachmentType) -> List[Attachment]: ... # 按类型取出全部附件 def del_attachment(self, type_list: List[AttachmentType]) -> None: ... # 按类型列表批量删除附件

其中get_attachment是角色消费附件的主要入口,例如 Planner 通过post.get_attachment(type=AttachmentType.plan)[0]读取计划内容(见 planner.py),CodeInterpreter 通过get_attachment(type=AttachmentType.reply_content)[0].content提取生成的代码。

三、附件(Attachment)体系:文本之外的载体

Post 的attachment_list存放的是 Attachment 对象。官方文档强调:附件用于存储文本消息之外的各种数据,例如代码片段或工件(artifact)文件路径;附件可能只被发送方角色使用,也可能被接收方角色使用。

Attachment 定义在 attachment.py,核心字段为id、type、content和可选的extra。其中type是AttachmentType枚举,完整枚举值可以从源码中看到(该枚举即 TaskWeaver 各角色结构化输出的"协议"):

class AttachmentType(Enum): # Planner 相关 init_plan = "init_plan" # 初始计划 plan = "plan" # 计划 current_plan_step = "current_plan_step" # 当前计划步骤 plan_reasoning = "plan_reasoning" # 计划推理 stop = "stop" # 停止信号 # CodeInterpreter 生成代码 thought = "thought" # 思考过程 reply_type = "reply_type" # 回复类型 reply_content = "reply_content" # 生成的代码/回复内容 # 代码验证 verification = "verification" # 代码执行 code_error = "code_error" execution_status = "execution_status" execution_result = "execution_result" # 执行结果 artifact_paths = "artifact_paths" # 工件文件路径 # 代码修订 revise_message = "revise_message" # 函数调用 function = "function" # WebExplorer web_exploring_plan = "web_exploring_plan" web_exploring_screenshot = "web_exploring_screenshot" web_exploring_link = "web_exploring_link" # 其他 invalid_response = "invalid_response" text = "text" shared_memory_entry = "shared_memory_entry" # 共享记忆条目 image_url = "image_url" # 视觉输入图片

可见附件类型覆盖了 Planner 计划、代码生成与验证、执行结果、函数调用、网页探索、视觉输入等全部角色间的结构化数据。附件通过Attachment.create(type, content, id, extra)创建;反序列化时(from_dict),源码会显式拒绝python、sample、text这类已废弃的类型名并抛出ValueError,提示用户参考官方博客修复旧数据。

四、消息的持久性控制:Temporal 分隔符机制

这是 Post 设计中最重要的机制之一。官方文档指出:通常情况下,message会作为历史对话轮次出现在提示词中;但有时消息过长,只应在当前轮次保留,下一轮起便从提示词中删除,以避免提示词膨胀。典型例子是CodeInterpreter 生成的长执行结果——它只在当前轮次有意义。

TaskWeaver 为此提供了"时间性标注"(temporal annotation)方式,将消息(或消息的一部分)标记为"仅当前轮次保留":

message = PromptUtil.wrap_text_with_delimiter(message, delimiter=PromptUtil.DELIMITER_TEMPORAL)

4.1 底层实现:PromptUtil

PromptUtil位于 prompt_util.py,其核心定义如下:

class PromptUtil: DELIMITER_TEMPORAL: Tuple[str, str] = ("{{DELIMITER_START_TEMPORAL}}", "{{DELIMITER_END_TEMPORAL}}") @staticmethod def wrap_text_with_delimiter(text, delimiter: Tuple[str, str]) -> str: return f"{delimiter[0]}{text}{delimiter[1]}"

被标注的内容会被包裹在一对特殊标记{{DELIMITER_START_TEMPORAL}} ... {{DELIMITER_END_TEMPORAL}}之间。get_all_delimiters()通过反射收集所有以DELIMITER_开头的分隔符对,为后续清理做准备。

4.2 提示词构造中的清理规则:Memory.get_role_rounds

Post 的"当前轮次保留"语义在 memory.py 的get_role_rounds中真正落地。该方法为某个角色提取其参与过的所有轮次以构造提示词,并执行如下清理:

# 对除最后一轮之外的所有轮次,删除被 temporal 标记包裹的内容 for round in rounds_from_role[:-1]: for post in round.post_list: post.message = PromptUtil.remove_parts( post.message, delimiter=PromptUtil.DELIMITER_TEMPORAL, ) # 对最后一轮,仅剥离分隔符标记本身,保留完整内容 for post in rounds_from_role[-1].post_list: post.message = PromptUtil.remove_all_delimiters(post.message)

remove_parts会循环查找起始标记与结束标记,把二者之间的整段内容连同标记一起从文本中删除(找不到配对标记时安全退出);remove_all_delimiters则只移除所有分隔符标记而保留文本。这一设计保证了:

  • 历史轮次中,被 temporal 标注的冗长内容(如长执行结果)不再进入提示词,控制上下文长度;
  • 当前轮次中,该内容仍完整可见,供角色基于最新结果继续推理。

五、Post 与提示词、结构化输出的双向转换

Post 不仅在记忆层存储,还承担着"与 LLM 结构化输出互转"的职责,核心实现在 translator.py 的PostTranslator:

  • raw_text_to_post:解析 LLM 输出的流式 JSON({"response": {"message": ..., "send_to": ..., "thought": ..., "plan": ...}}),将各键值分别映射到 Post 的message、send_to及各类 Attachment,通过post_proxy.update_attachment(...)边解析边组装;
  • post_to_raw_text:反向将 Post 序列化为 LLM 输入格式的结构化文本,附件按类型聚合后与send_to、message一起包装进{"response": ...}。

这说明 Post 的字段集合(文本 + 结构化附件)正是 TaskWeaver 与 LLM 交互的"线协议":角色输出的每一个结构化字段最终都会落到某个 Post 的附件中,再作为下一角色的提示词上下文。

此外,会话执行过程中,session.py 会在 Post 进出时记录in.attachments/out.attachments等 trace 属性,便于链路追踪与观测(详见 tracing.py)。

六、实战示例:一个完整轮次中的 Post 流转

结合仓库自带的示例 example-planner.yaml,可以直观看到 Post 在真实会话中的形态(该示例由 Conversation 加载,rounds[0].post_list即为该轮次的所有 Post):

enabled: True rounds: - user_query: count the rows of /home/data.csv state: created post_list: - message: count the rows of /home/data.csv send_from: User send_to: Planner attachment_list: - message: Please load the data file /home/data.csv and count the rows of the loaded data send_from: Planner send_to: CodeInterpreter attachment_list: - type: init_plan content: |- 1. load the data file 2. count the rows of the loaded data <narrow depend on 1> 3. report the result to the user <wide depend on 2> - type: plan content: |- 1. instruct CodeInterpreter to load the data file and count the rows of the loaded data 2. report the result to the user - type: current_plan_step content: 1. instruct CodeInterpreter to load the data file and count the rows of the loaded data - message: Load the data file /home/data.csv successfully and there are 100 rows in the data file send_from: CodeInterpreter send_to: Planner attachment_list: - message: The data file /home/data.csv is loaded and there are 100 rows in the data file send_from: Planner send_to: User attachment_list: - type: init_plan content: |- 1. load the data file 2. count the rows of the loaded data <narrow depend on 1> 3. report the result to the user <wide depend on 2> - type: plan content: |- 1. instruct CodeInterpreter to load the data file and count the rows of the loaded data 2. report the result to the user - type: current_plan_step content: 2. report the result to the user

该示例完整呈现了 Post 的几个典型特征:

  1. 消息链:User → Planner → CodeInterpreter → Planner → User的 Post 序列构成一轮完整对话,每个 Post 都带send_from与send_to;
  2. 文本为主、附件为辅:Planner 发送给 CodeInterpreter 的 Post 在message中给出指令文本,同时通过init_plan、plan、current_plan_step三类附件携带结构化计划;
  3. 附件更新随轮次推进:最后一跳 Planner 回复用户时,current_plan_step已推进到第 2 步,反映规划状态的变化;
  4. 无需附件的 Post:User 提问、CodeInterpreter 汇报结果等 Post 的attachment_list为空列表。

七、结语

Post 是 TaskWeaver 消息体系的基石:它以"始终携带文本消息 + 可选结构化附件"的简洁设计,统一了 User、Planner、CodeInterpreter 以及 WebExplorer、DocumentRetriever 等扩展角色之间的通信协议;配合 Round 与 Conversation 的组织层级、temporal 分隔符的上下文裁剪机制、以及PostTranslator与 LLM 结构化输出的双向转换,构成了整套会话记忆与提示词构造的闭环。无论是阅读源码、编写自定义角色,还是调试多角色协作流程,从 Post 入手都是最快的切入路径。

  • AI Agent
  • 数据分析

【免费下载链接】TaskWeaver

The first "code-first" agent framework for seamlessly planning and executing data analytics tasks.

项目地址:https://gitcode.com/gh_mirrors/ta/TaskWeaver
点击查看免费下载

相关推荐

上一篇:XHS-Downloader终极问题排查指南:3分钟解决90%的使用难题
下一篇:小红书资源下载神器:3步解锁无水印高清内容

创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考

版权声明: 本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若内容造成侵权/违法违规/事实不符,请联系邮箱:809451989@qq.com进行投诉反馈,一经查实,立即删除!
网站建设 2026/10/10 5:30:37

Python shlex 完全指南:从词法原理到命令行安全解析

第一次在别人的工具源码里看到import shlex时&#xff0c;我第一反应是&#xff1a;这名字是故意的吧&#xff1f;后来翻了文档才知道&#xff0c;它全称是shell lexical analyzer&#xff0c;也就是“Shell 词法分析器”。当时我正好在写一个需要解析命令行字符串的工具&#…

作者头像 李华