Repository navigation
Replies: 1 comment
|
很喜欢这里“小而稳定的 system prompt + 异步物化 skill overlay”的思路,尤其是 hot path 只同步读 actor state、失败时保留 last-known-good,以及 DM / channel 双 seam 注入。 |
0 replies
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Uh oh!
There was an error while loading. Please reload this page.
背景与目标
当前 aevatar NyxidChat agent 的系统提示词偏重:基础
system-prompt.md约 255 行 / ~5,961 tokens,里面混了大量"能力相关"的 playbook / 能力清单。这类内容变动频繁,每次调整都要改代码、重新部署。目标:
aevatar-system),归 ChronoAI 组织所有;每次 aevatar agent 处理任务时,都强制把这些 tagged 技能作为 system prompt 的补充——不交给模型自觉,而是由代码无条件注入。一、现状 review
1. 基础 system prompt
NyxIdChatSystemPrompt.cs(GetManifestResourceStream,懒加载静态)作为嵌入资源加载(.csproj:63<EmbeddedResource Include="Skills/**/*"/>)。2. Lark bot 拼接
每轮在
ConversationReplyGenerator.BuildSystemPrompt()(:1030-1051,同步)按序拼接:LoadBaseSystemPrompt()(嵌入资源:1053)## Channel Runtime ConfigurationNyxIdRelayPromptConfiguration.BuildChannelRuntimeConfigurationSection()<channel-context>块ChannelContextMiddleware.BuildChannelContextSection()## Available Skills(仅本地 skill)LocalSkillCatalog.BuildSystemPromptSection()3. Lark relay 追加
relay 没有独立的一段 prompt,复用同一条
ChannelContextMiddleware,把 bot-owner 身份(operator_*)和发送者(subject_*)塞进同一个<channel-context>块(ChannelConversationTurnRunner.cs:1818、NyxIdRelayTransport.cs:1030)。即"relay 追加" = runtime 配置段 + 带 owner/sender 身份的 context 块,和 bot 路径同一套代码。4. 现有 Ornn skill 拉取机制("按需",非注入)
OrnnSkillClient.SearchSkillsAsync(token, query, scope="mixed")→OrnnSkillSummary[];GetSkillJsonAsync(token, idOrName)→OrnnSkillJson(含Files)。ornn_search_skills(scope 硬编码 mixed)、use_skill(本地优先,否则OrnnRemoteSkillFetcher拉远端、解析 SKILL.md frontmatter/workflows/scripts)。Tags/Category/IsPrivate。SkillRegistry+TTL 缓存已被刻意删除,iter27)。二、决定方案形态的硬约束
注入 hook 是同步的:
AIGAgentBase.DecorateSystemPrompt(string)→string(:354)、BuildRequest、ChatRuntime._requestBuilder(Func<LLMRequest>)、ConversationReplyGenerator.BuildSystemPrompt(:1030) 全同步 → 不能在轮内同步发 HTTP。CI 禁
GetAwaiter().GetResult()(tools/ci/architecture_guards.sh:322)→ overlay 必须离线异步物化、同步读。🔑 存在两条独立的 prompt builder(最关键的发现):
AgentRunReplyGenerationExecutor→ConversationReplyGeneratorLoadBaseSystemPrompt()嵌入 mdBuildSystemPrompt(:1030)NyxIdChatGAgent→RoleGAgent.BuildRequestEffectiveConfig.SystemPromptDecorateSystemPrompt(:278)→ 要"每轮强制",必须两处都注入;只改一处会漏掉 Lark 或 DM。
无 org 字段 → 组织 scoping 只能靠"用谁的 token 拉(服务端可见性)+ client 侧按 tag 过滤"。
host-fact 规则(FI-002 / CLAUDE.md:22):tag 值、org token 必须 host config 注入,生产代码/类型/prompt 不得出现具体 skill/tag 名。iter27 删缓存的疤就在
DecorateSystemPrompt注释里——任何进程内缓存都会被 review 挑战。三、推荐方案:Actor-state Overlay + 双 seam 强制注入
把
aevatar-systemtag overlay 建模为NyxIdChatGAgent(RoleGAgent) 自身 config 身份的一部分:离线异步物化进 Protobuf 状态,每轮同步读 in-memory mirror,事件驱动 self-refresh,两条 seam 都注入。无 read-model/projector 塔,无进程内缓存;fetch/render 由无状态 builder 完成。数据归属与存储
NyxIdChatGAgent(它本就拥有RoleGAgentState.config_overrides.system_prompt)。ai_messages.proto新增SystemSkillOverlay { string overlay_markdown; string source_watermark; int64 materialized_at; }作为RoleGAgentState字段;经SystemSkillOverlayMaterializedEvent+ reducer 提交(仿ApplyInitializeRoleAgentRoleGAgent.cs:1296)。committed event + actor state = 唯一事实源。string? _systemSkillOverlaymirror,激活时从已提交状态 hydrate——同构于已验证的SkillRunnerGAgent._systemPromptOverride(离线写、同步读)。刷新(事件驱动,无轮询,I/O 全离线)
OnActivateAsync:若 watermark 空或超 TTL,用ScheduleSelfDurableTimeoutAsync(GAgentBase.cs:322) 排一次 self-refresh。HandleRemoteApprovalStatusCheckRoleGAgent.cs:351)离线调 builder → 算 watermark → 变了才PersistDomainEventAsync(幂等)。组织/标签 scoping(无 org 字段下如何落地)
SkillRunnerGAgent取OutboundConfig.NyxApiKey)调SearchSkillsAsync(serviceToken, scope:"mixed")。OrnnSkillSummary.Tags/Metadata.Tags(无需改 Ornn server)。GetSkillJsonAsync(serviceToken, name)取 body。配置面(全部 host 注入,零字面量)
SystemSkillOverlayOptions { Tag; OrgServiceToken; RefreshTtl; MaxSkills; Enabled; }(仿OrnnOptions.cs)。Aevatar:SystemSkills:{Tag, OrgServiceToken(secret-ref), RefreshTtl, MaxSkills, Enabled}。AevatarAIFeatureOptions加字段 →AevatarPlatformHostBuilderExtensions.cs:51绑定 →RegisterSystemSkillOverlay(仿RegisterOrnnSkills)→AddSystemSkillOverlay(仿AddOrnnSkills)。Enabled=false或无 tag → 整个特性 no-op,不影响非 ChronoAI host。降级(永不阻塞/失败一轮)
OperationCanceledException重抛(仿ActorBackedUserMemoryStore:147)。system-prompt.md 瘦身
aevatar-systemtag skillsuse_skill/ornn_search_skills机制说明四、已定决策
MaxSkills以内全文 body注入(强制),超出转"名+一句话"目录、可use_skill展开DecorateSystemPrompt) + channel (BuildSystemPrompt) 都注入,真正"每轮强制"OrgServiceToken),后续如多组织再加 per-org resolver五、分阶段实施 + 测试 + guard
SystemSkillOverlay/事件、Options、DI、host 绑定ISystemSkillOverlayBuilder(Search→tag 过滤→fetch→render)OnActivatearming、mirror hydrateDecorateSystemPrompt(DM) +BuildEffectiveReplyPlanAsync→BuildSystemPrompt传参(channel)bash tools/ci/architecture_guards.sh+test_stability_guards.shGetAwaiter().GetResult()六、为什么这套设计合规
GetAwaiter().GetResult():所有 Ornn I/O 在离线 self-handler,热路径只做同步字符串拼接。SearchSkillsAsync/GetSkillJsonAsync+ client 侧 tag 过滤。待讨论
aevatar-system这个 tag 字面值、以及 ChronoAI org/service token 的来源(secret 管理方式)——这是 host fact,需要确认。MaxSkills上限取值、overlay 字节预算。All reactions