news 2026/9/14 15:31:12

SurfSense 主代理身份系统提示词解析:开放网络研究编排者的角色设计与调度机制

作者头像

张小明

前端开发工程师

1.2k 24
文章封面图
SurfSense 主代理身份系统提示词解析:开放网络研究编排者的角色设计与调度机制

SurfSense 主代理身份系统提示词解析:开放网络研究编排者的角色设计与调度机制

【免费下载链接】SurfSenseOpen-source NotebookLM alternative. Research the open web with live data(Reddit, YT, IG, TikTok, Indeed, Google Search, Maps etc) through one platform, API or MCP server. Join our Discord: https://discord.gg/ejRNvftDp9项目地址: https://gitcode.com/GitHub_Trending/su/SurfSense

导读

本文围绕 SurfSense 后端多代理聊天系统中主代理(main agent)的身份提示词片段 identity/private.md 展开,解析 SurfSense 如何为编排型 Agent 定义"我是谁、我做什么、我如何工作"三层角色框架。你将了解到:主代理如何通过task工具调度 Reddit、YouTube、Google Maps 等平台专业子代理完成实时网络研究,如何将知识库、连接应用与持久记忆纳入调度决策,以及这套身份设计在系统提示词组装管线中的真实落地方式。读完本文,你可以理解 SurfSense 多代理编排系统的设计哲学,并掌握其提示词模块化、可见性感知与路由规则的实现细节。

一、文档定位:一份"身份区块"提示词

identity/private.md是 SurfSense 主代理系统提示词中<agent_identity>区块的内容源。它不是独立运行的指令,而是由构建器在运行时加载并注入的片段。从源码看,身份区块的构建逻辑位于 builder/sections/identity.py,其核心逻辑如下:

def build_identity_section( *, visibility: ChatVisibility, resolved_today: str, ) -> str: variant = "team" if visibility == ChatVisibility.SEARCH_SPACE else "private" fragment = read_prompt_md(f"identity/{variant}.md") if not fragment: return "" return "\n" + fragment.format(resolved_today=resolved_today) + "\n"

两个关键点值得注意:

  • 可见性感知(visibility-aware):同一份身份定义存在privateteam两个变体。当会话属于私人聊天(ChatVisibility.PRIVATE)时加载private.md;当会话属于工作区/团队空间(ChatVisibility.SEARCH_SPACE)时加载team.md。二者的差异集中在一处——team.md额外声明"你处于团队线程中,每条消息带[DisplayName]前缀,相关引用与决策需归属到具名作者"。
  • 模板注入:身份片段末尾的Today (UTC): {resolved_today}占位符,由构建器传入的实际日期填充。

resolved_today的生成与整个提示词的组装在 builder/compose.py 中完成:

resolved_today = (today or datetime.now(UTC)).astimezone(UTC).date().isoformat() visibility = thread_visibility or ChatVisibility.PRIVATE

提示词片段通过 builder/load_md.py 中的read_prompt_md()app.agents.chat.multi_agent_chat.main_agent.system_prompt.prompts包内按文件名读取 Markdown 文本,整个系统提示词由若干区块按固定顺序拼接。根据compose.py的模块 docstring,默认组装顺序为:

<agent_identity> # 本文档所在区块 [用户自定义系统指令,如有] <core_behavior> # 默认正文 <knowledge_base_first> # 默认正文 <dynamic_context> # 始终注入 <routing> # 默认正文 <specialists> # 始终注入(动态名册) <tools> # 始终注入 <memory_protocol> # 默认正文 <citations> # 始终注入 <output_format> # 始终注入 <refusal_and_limits> # 始终注入 <reminder> # 始终注入

这意味着identity/private.md是整套系统提示词的"开场白"——它在最前面确立代理的角色与职责边界,随后才由<routing><specialists><tools>等区块展开具体行为规则。即使用户提供了自定义系统指令(custom_system_instructions),它也只是叠加在身份区块之后而非替代它,以保证平台级的安全兜底(KB-first、路由、引用、输出格式、拒答规则)始终生效。

二、身份核心:一个"开放网络研究平台"的编排者

identity/private.md给出了主代理的身份宣言:

You areSurfSense's main agent, the orchestrator of an open-source open web research platform.

这句话包含了三个限定层次:

  1. 主代理(main agent):SurfSense 多代理系统中负责与用户直接对话、统揽全局的 Agent。
  2. 编排者(orchestrator):它不是所有工作的执行者,而是"路由者"。正如身份片段强调的:"You are an orchestrator — most non-trivial work belongs on a specialist."
  3. 开放网络研究平台(open web research platform):用户来此研究实时网络——社区与受众在说什么、排名/评论/页面如何变化、开放网络上发布了什么,并将研究成果与自己的知识库结合使用。

这一身份设计直接呼应项目定位(Open-source NotebookLM alternative,面向 Reddit、YouTube、Instagram、TikTok、Indeed、Google Search、Maps 等实时数据源),将"研究实时网络"这一核心能力凝练成代理的身份内核。

三、三类调度对象:网络数据、用户上下文、交付物

身份片段明确列出主代理通过task工具调度的三类工作,这是整份文档的骨架:

3.1 实时网络数据(Live web data)

Reddit、YouTube、Instagram、TikTok、Amazon、Walmart、Google Maps、Google Search 以及 Web Crawler 返回结构化、实时的平台数据,包括帖子、评论、字幕、视频、商品、评论、SERP 与完整页面内容。

从仓库的 subagents/builtins 目录结构可以印证这一名册的落地:每个平台对应一个独立子代理包,包含agent.pysystem_prompt.mddescription.mdtools/(如 reddit、youtube、tiktok、google_maps、google_search、amazon、walmart、web_crawler、indeed 等)。每个子代理运行在自己的工具栈与上下文隔离环境中,返回单一的综合结果。

3.2 用户自身上下文(The user's own context)

包括知识库、连接的应用与持久记忆。这部分同样有对应子代理与中间件支撑:

  • knowledge_base 子代理:subagents/builtins/knowledge_base 下提供search_knowledge_base.py工具与ask_knowledge_base_tool.py,并有云/桌面双版本的 system prompt;
  • mcp_discovery 子代理:subagents/builtins/mcp_discovery 通过 MCP 客户端连接 Slack、Notion、Jira、Gmail、日历等第三方服务;
  • memory 子代理:subagents/builtins/memory 维护持久记忆;
  • main_agent 中间件middleware/目录中的memorykb_persistenceknowledge_tree等中间件在请求-响应链路上处理记忆与知识库的持久化。

3.3 交付物(Deliverables)

报告(reports)、播客(podcasts)与演示文稿(presentations),由deliverables子代理基于专家们的研究结果构建。对应实现位于 subagents/builtins/deliverables,其tools/下包含report.pypodcast.pyvideo_presentation.pygenerate_image.pyresume.py等工具。值得注意的是,podcast 这类交付物采用异步链路:deliverables子代理设置好播客后立即返回,生成过程由 Celery 后台任务驱动,聊天中的实时卡片接管进度展示(详见下文路由规则)。

四、编排者哲学:以数据说话,而非凭假设作答

身份片段对编排者的工作方式提出了明确的价值观约束:

Your value is routing each request to the right specialist, synthesizing evidence across sources, and answering with what the data shows rather than what you assume.

翻译过来即三个职责:

  1. 路由(routing):把每个请求分发到正确的专家;
  2. 综合证据(synthesizing evidence):跨来源汇总证据;
  3. 以数据作答(answer with data):回答的依据是"数据展示了什么",而不是"自己假设了什么"。

这一哲学在配套的 routing.md 中被展开为具体的路由规则,例如:

  • 受众情绪(audience sentiment)属于平台:关于品牌/产品/话题的"人们在说什么、感觉如何",应调用task(reddit, …)task(youtube, …)task(tiktok, …)task(google_maps, …)task(amazon, …)/task(walmart, …)去平台取回结构化的实时对话,而不是用网络搜索找"关于这段对话的文章";
  • 搜索负责发现,爬虫负责阅读:搜索结果(摘要、AI 概览)只是线索而非信源,答案在页面里时先用task(web_crawler, …)抓取页面再作答;
  • 地点归 Maps,开放网络归 Search:发现实体店/商户用 Google Maps 专家(返回结构化名称/地址/电话/网站),无实体门店的在线实体用搜索专家;
  • 请求 N 个列表时统计独立实体:同一品牌/母机构旗下的多个分支、地点、子项目只算一个条目,凑不满 N 就继续扩大发现,诚实交付"更小的列表"优于"注水的 15 个";
  • 完整数据集落成文件而非聊天:需要整表数据时指示 web_crawler 用export_runCSV 工具抓取并保存,只转述工作区路径与行数;
  • 主代理没有文件系统工具:对工作区的任何读写改查都必须经task(knowledge_base, …),绝不使用write_filels等直接文件操作。

同时 core_behavior.md 为身份补充了沟通风格约束:简洁直接、不念开场白、不叙述意图、模棱两可时先问再做、准确优先于附和、坚持到任务完成或真正受阻。

五、task工具:编排的指挥棒

身份片段反复强调"通过task工具调度专业子代理",因此理解task工具的契约是理解这份身份的关键。其完整规格见 prompts/tools/task/description.md,要点如下:

5.1 单模式(single mode)参数

  • subagent_type:要调用的专家名称,必须匹配<specialists>名册中的条目;
  • description:完整的任务提示词。专家看不到当前线程,所以必须把所有上下文、约束与期望返回内容全部写进description;专家会用自己的格式作答,不要强行规定其输出格式。

5.2 批处理模式(batch mode)参数

  • tasks{description, subagent_type}对象数组,用于并发扇出(fan-out)。当单个请求拆分成3 个或更多独立的专家调用时使用(例如"从这份清单创建五个 issue")。子任务在小型并发上限下运行,运行时为每个子任务返回一条以[task <index>]前缀标识的 ToolMessage。

批处理模式有一个关键限制:批处理子任务不支持人类介入(human-in-the-loop)中断——若某个子任务需要审批,会报错,此时必须将该任务改为非批处理的单次task(...)调用重新调度。1~2 个独立调用则直接发出两次单独的task(...)即可。

5.3 验证机制(verification teaching)

description.md末尾附有<verification>教学块,这是身份"以数据说话"哲学在工具层的具体化:子代理的自然语言回复是"自我报告",不是证据——它可能声称 Slack 消息已发出、Jira issue 已创建,但底层工具调用实际静默失败或被限流。主代理必须把"Done""Posted to #general"这类成功表述当作假设而非事实,并通过两个真实信号交叉验证:

  1. state['receipts']:每个变更型工具都会向这个追加型列表写入结构化Receipt(包含 route、type、operation、status、external_id、verifiable_url、preview)。子代理的<output_contract>会在evidence.receipts中携带匹配的 Receipt。若子代理声称成功却没有任何status="success"的 Receipt(异步交付物如播客/视频为"pending"),则该操作实际上没有发生——按失败处理,原样告知用户,不要盲目重试;
  2. task(web_crawler, …)外部确认:当 Receipt 带verifiable_url(Notion 页面 URL、Slack 永久链接、Jira issue URL 等)时,可以爬取该 URL 从外部确认操作已生效,适用于用户明确点名的高风险变更。

Receipt 状态语义:

  • status="success":变更已在后端提交。带verifiable_url且高风险时可用爬虫外部确认,否则信任 Receipt 并告知用户完成;Celery 支撑的交付物(播客、视频演示)也落在这里,因为子代理已等待 worker 完成;
  • status="failed":Receipt 的error字段携带后端错误,原样转述给用户,仅在用户明确要求时才重路由或重试;
  • status="pending":当前罕见(现有变更工具都会等待后端返回)。若遇到,告知用户工作已启动(引用external_id/preview供后续查找),不要爬取,也不要重新派发同样的task(...)

5.4 task 示例

prompts/tools/task/example.md 给出三个最小示例,其中前两个也内嵌在description.md中:

user: "Save these meeting notes to my KB: …" → task(subagent_type="knowledge_base", description="Save the notes below to a new document under /documents/notes/. Pick a sensible title and folder; tell me the path you used.\n\n<notes>…</notes>") user: "What did Maya say about the Q2 roadmap in Slack last week?" → task(subagent_type="mcp_discovery", description="In Slack, find messages from Maya about the Q2 roadmap from the past week. Return the most relevant quotes with channel and timestamp.") user: "Find my Q2 roadmap and summarise the milestones." → task(subagent_type="knowledge_base", description="Locate the Q2 roadmap document under /documents and summarise its milestones. Use glob or grep if the path isn't obvious from the workspace tree.")

六、<specialists>动态名册:路由的对象从哪来

<agent_identity>区块提到的"specialist subagents"在提示词中以<specialists>区块动态呈现。其构建逻辑在 builder/sections/specialists.py:

def build_specialists_section(specialist_lines: list[tuple[str, str]]) -> str: bullets = "\n".join(f"- **{name}** — {desc}" for name, desc in specialist_lines) return f"\n<specialists>\n{bullets}\n</specialists>\n"

名册是动态的,取决于当前工作区的连接器配置。从 subagents/registry.py 的源码可以看到,注册表依据SUBAGENT_TO_REQUIRED_CONNECTOR_MAP做连接器过滤——某专家若依赖特定连接器而当前空间未配置,则会被移出名册。但按specialists.pydocstring 的说明,名册按约定永不为空deliverablesknowledge_base两个专家在映射中声明了空集合frozenset(),因此在任何基于连接器的剔除中都能存活。

七、持久记忆:身份中"用户自身上下文"的一部分

身份片段将"persistent memory"列为用户自身上下文的一类。对应的运行协议见 prompts/memory_protocol/private.md:主代理在理解每条用户消息后,判断其是否揭示了关于用户的持久事实——角色、兴趣、偏好、项目、背景或长期指令;若是,则在正常回复同时调用update_memory,不推迟到后续回合;一次性问答、问候、会话琐事等临时聊天噪音则跳过。

记忆的存储格式为基于标题的 Markdown:新条目放在## Facts## Preferences## Instructions##标题下,条目形如- YYYY-MM-DD: text。若旧记忆存在(YYYY-MM-DD) [fact|pref|instr]旧格式标记,需保留信息但按新格式写入。该协议同样有 private/team 变体,与身份区块的可见性选择一致。

八、private 与 team 变体:同一身份的两副面孔

身份片段以private.md/team.md双变体存在,二者核心内容完全一致,差异仅体现在团队语境上。对比两个文件可以总结出:

维度private(私人线程)team(团队线程)
服务对象用户个人的知识库与记忆团队的共享知识库与持久团队记忆
消息格式无前缀每条消息带[DisplayName]前缀
引用归属不涉及相关引用与决策归属到具名作者

这一变体机制由build_identity_section依据ChatVisibility自动选择,ChatVisibility.SEARCH_SPACE映射到 team 变体,其余(含默认值PRIVATE)映射到 private 变体。同理,dynamic_contextmemory_protocol区块也遵循相同的 private/team 双轨设计,保证整个提示词体系在个人/团队两种场景下语义自洽。

九、时间注入:让编排者感知"今天"

身份片段最后一行Today (UTC): {resolved_today}的作用不可小觑。在compose.py中,resolved_today以 UTC 日期(ISO 格式YYYY-MM-DD)注入,且同样注入到用户自定义系统指令custom_system_instructions.format(resolved_today=resolved_today))。这为代理提供了时间锚点,使其能够:

  • 正确理解"上周""最近一个月"等相对时间表述;
  • 感知研究任务中对时效性的要求(例如 Reddit 搜索中 "past month, sort by top" 类指令的落地)。

此外,仓库中还有一个专门的时间处理插件 plugins/year_substituter.py,可见团队对时间感知的重视——身份片段中的这一行是整个时间感知链路的一部分。

十、从身份到行为:一份提示词如何撑起整个编排系统

综合以上分析,identity/private.md虽然只有短短 24 行,但它以高度凝练的方式定义了 SurfSense 主代理的完整工作模型,并与提示词管线的其他区块形成闭环:

  1. 角色定义<agent_identity>):我是谁——开放网络研究平台的编排者;
  2. 能力边界<routing>+<specialists>):我有什么——调度实时网络专家、知识库与记忆、交付物三类子代理,且名册随工作区连接器动态变化;
  3. 工作方式<tools>+<memory_protocol>+<citations>):我怎么做——通过task的 single/batch 双模式调度、用 Receipt 验证子代理自报、边回答边沉淀持久记忆、按<output_format>输出;
  4. 价值观约束<core_behavior>+<refusal_and_limits>):我如何自持——以数据作答、简洁直接、必要时拒答。

这种"身份先行、区块化组装、可见性感知、动态名册"的设计,使得 SurfSense 能够在保持统一主代理体验的同时,弹性适配不同工作区连接器组合与个人/团队两种协作场景。对于希望构建多代理编排系统的开发者而言,identity/private.md及其周边 compose.py、routing.md、task 工具说明 构成了一套值得借鉴的"角色定义 → 行为规则 → 工具契约 → 验证兜底"提示词工程范本。

【免费下载链接】SurfSenseOpen-source NotebookLM alternative. Research the open web with live data(Reddit, YT, IG, TikTok, Indeed, Google Search, Maps etc) through one platform, API or MCP server. Join our Discord: https://discord.gg/ejRNvftDp9项目地址: https://gitcode.com/GitHub_Trending/su/SurfSense

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

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

民办本科考生银行校招报班指南:学历认可度低如何选机构精准突围

前几天有个学妹找我吐槽&#xff0c;说自己备考银行走了好多弯路&#xff0c;浪费了很多时间。聊下来发现&#xff0c;她踩的坑&#xff0c;其实很多人都在踩。今天就借这个机会&#xff0c;跟大家好好说说民办本科考银行报班选什么机构那些事。一、民办本科考生考银行的处境民…

作者头像 李华
网站建设 2026/9/14 15:26:14

顶级域(TLD)全解析:从DNS原理到域名选型与排错实战

先问一个特别基础的问题&#xff1a;你在浏览器地址栏里敲下www.example.com的时候&#xff0c;有没有想过最后那一段.com到底是什么&#xff1f;我认真研究域名系统&#xff0c;就是从第一次注册域名开始的。当时什么都不懂&#xff0c;看到首年只要几块钱的后缀就冲动入手&am…

作者头像 李华
网站建设 2026/9/14 15:25:45

自举开关深度解析:突破ADC采样精度瓶颈的核心技术

1. 自举开关不是“加个电容就完事”&#xff1a;为什么ADC采样精度卡在12位再也上不去 你有没有遇到过这样的情况&#xff1a;明明选了16位SAR ADC芯片&#xff0c;参考电压用的是低温漂基准源&#xff0c;PCB也做了四层板独立模拟地&#xff0c;可实测有效位数&#xff08;ENO…

作者头像 李华