06.1: Build Context
此内容尚不支持你的语言。
The build-context stage constructs the ChatTurnContext closure for a persona turn. It fetches and
simplifies recent message history, translates Discord message IDs to compact prompt identifiers,
resolves participant metadata, and delegates prompt assembly to the context-build pipeline.
Flow and ownership
Section titled “Flow and ownership”The stage is coordinated by buildChatTurnContext() across two modules:
- Intent wrapper (
src/utils/chat/contextPipelineIntent.ts): checks whether autonomous short-term memory (STM) maintenance is due viaisShortTermMemoryMaintenanceDue(). When Deliberate Tool Mode is active and maintenance is due, it temporarily admitsupdate_short_term_memoryinto the tool allowlist so memory updates run without requiring user prompt keywords. - Base builder (
src/utils/chat/contextPipeline.ts): coordinates history retrieval, message simplification, participant resolution, and prompt synthesis.
ChatTurn │ ├─► contextPipelineIntent ──► checks autonomous STM maintenance │ ▼contextPipeline.buildChatTurnContext() │ ├─► readChannelHistory() ──► fetches or reuses the busy channel's history window ├─► buildSimplifiedHistory() ──► builds MessageIdMap, collapses same-author runs, │ extracts embed/CV2 notices, filters privacy ├─► prepareParticipantContext() ──► resolves visible authors, aliases, personas │ ▼buildContext() (context-build pipeline) ──► assembles StructuredContextItem prompt items │ ├─► appendTailDirectives() ──► appends stop, reasoning, and queued directives └─► resolveAssistantPrefill() ──► stores TurnPrefill on context closureHistory fetching and simplification
Section titled “History fetching and simplification”- Message retrieval: the first turn fetches up to
message_fetch_limitmessages from Discord. Within the same busy stretch, later personas and queued replays merge the previous window with cached messages newer than its oldest entry and confirmed deliveries. Cached instances take precedence; deleted IDs are removed, then messages sort by snowflake and trim to the turn’s limit. Delivery recording retains webhook messages before their gateway echoes arrive. A delivery without a full message object drops the window so the next turn reads it from Discord. - History freshness: update, delete, bulk-delete, and raw reaction events record changed IDs on the lock entry. Changes to uncached window messages, an increased limit, a cache with a gap, or a deletion requiring older history take the REST path. A change arriving during a REST read forces another read because discord.js can overwrite a cached edit with the response. Signal overflow drops the window. A cleared cache or one whose oldest entry is newer than the previous window’s newest message requires REST even below capacity, because deletion or sweeping can shrink a cache after earlier messages were evicted. Shard disconnects and reconnects drop all windows; disconnected shards disable reuse. DM turns use REST unless the client subscribes to DM reaction events. The idle channel keeps no window because the message sweeper can evict cache entries.
- Anchored selection: after queued-trigger add-back, proxy filtering, and reset/compact-refresh
slicing,
selectAnchoredHistory()inhistoryWindow.tsretains a start ID until the window would exceed the normalized message limit. It advances byhistoryWindowStride(limit): 30% rounded down to an even number (24 at 80). With enough history, selection holds betweenlimit - strideandlimitDiscord messages before simplification. Resets take precedence; a required queued trigger remains included. ID-only anchors survive idle release for five minutes; gateway reconnects, boundary deletions, changed limits, and unavailable starts can force a new anchor. - Budget truncation:
truncateDialogueHistory()removes whole stride batches of oldest exchanges when over budget, preserving samples, the newest user request, tail-slot facts, and reserved-tail accounting. It reapplies the registry slot, saved STM depth and cacheable-prefix boundary against surviving history. Inspection reads the same channel anchor without changing its state, and both truncation paths receive the configured limit. - History auxiliary reads: proxy identities use the unfiltered history; sprite records, minimal notice bodies, and agendas use visible messages after blacklist and block filtering. These reads run concurrently before simplification.
- Reply targets: missing same-channel targets are deduplicated and fetched with at most four concurrent REST calls before simplification. Failed targets omit their annotations without retrying during that build. Simplification and compact ID registration remain in message order.
- Message ID translation:
MessageIdMapmaps 64-bit Discord snowflake IDs to compact sequential identifiers (ref_1,ref_2). This reduces prompt token usage and provides stable references for tools that target messages. - Same-author merging: consecutive pure-text messages from the same author collapse into a single entry while preserving individual timestamps and IDs for metadata tools. Media-bearing messages, including messages with file attachments, remain distinct to keep media attachments unambiguous.
- File attachments: text-readable files are recorded as
documentAttachmentson the entry. A reply carries its target’s files the same way it carries the target’s images. Theread_filehint is rendered later, once the turn’s tool list is known (see Dialogue history). - Thread starter media: references to parent-channel messages are fetched from the referenced channel, including forwarded snapshots; image references use the starter’s ID so tools can retrieve them from inside the thread.
- Privacy and blocks: messages from full-privacy users and blocked authors are excluded.
- System notice hydration: extracts system notices from Discord embeds and Components V2 containers,
restoring the full text of minimal-verbosity cards via
resolveMinimalNoticeBodies().
Deliberate Tool Mode admission
Section titled “Deliberate Tool Mode admission”Besides keyword triggers, follow-ups, and the autonomous sticker and STM tools, media on the triggering
message’s history entry admits the tool that reads it: a file admits read_file, a non-emoji image
admits analyze_image, a GIF also admits process_gif, and a YouTube link admits
process_youtube_video (getAttachmentDeliberateToolIntentResult()). Only the triggering entry counts,
so older media never keeps a tool exposed. Each tool’s own availability gate still applies.
/tool prompt inspection applies the same rule in buildSnapshotToolFilter().
Participant resolution and prompt delegation
Section titled “Participant resolution and prompt delegation”- Participant context:
prepareParticipantContext()scans visible messages for mentions, aliases, and persona triggers. Results are cached in a request-scoped map (participantRequestScopes) across co-responding persona turns under the same channel lock. The same scope shares member-fetch promises, keyed by guild and user, across history naming, participant hydration, presence, and mention resolution. Failed member fetches remain shared for that invocation; a new invocation can retry. - Hydration: user rows, blacklist status, and privacy use the user cache. Both hydration phases run at most four seeds at once and assemble profiles and proxy namespace notes in seed order. Registration invalidates a cached missing user after the write succeeds. Cache failures use the existing restrictive blacklist and privacy defaults.
- Mention names: each native build memoizes user-name promises by guild and user. Persona builds share member fetches but resolve mention names independently because personalization can differ. Callers outside native assembly can omit the memo and resolve names locally.
- Timing:
context_built.participantMsand the latency metric’sparticipant_msmeasure participant preparation; hydration remains inside the native-build duration. - Prompt construction: delegates to
buildContext()insrc/utils/text/contextBuilder.ts. The Context-Build Pipeline owns persona system prompts, retrieved memories, and RAG document assembly. - Tail directives: applies registry positional inserts, including tail-slot blocks, then appends prompt directives in prioritized order, including emoji usage penalties, reasoning formatting instructions, queued reply guidance, and uncensor rules.
- Prefill resolution: expanded prefill text is stored on
context.assistantPrefillrather than committed directly tocontextItems, allowing individual model attempts to determine how the prefill is formatted.
Streaming context flags
Section titled “Streaming context flags”The stage initializes per-turn flags on StreamingContext:
replyNoticeState: initialized to{ attempted: false, sent: false }for queued turns. This allows subsequent delivery stages to post a “Replying to…” notice before sending alter or sprite webhook messages that cannot use native Discord replies.disableRecentMessageReplyTool: set totrueon queued turns. The active stream already delivers the reply text, so suppressing tool-driven replies avoids duplicate delivery paths.
Constraints and rationale
Section titled “Constraints and rationale”- Stable compact references: translating raw snowflakes into sequential reference IDs prevents token inflation and prevents models from hallucinating 19-digit Discord snowflake numbers.
- Delegated prompt ownership: chat coordination confines itself to channel history and delivery flags, delegating dialogue assembly and memory retrieval to the context-build pipeline.
- Tool suppression on queued replies: blocking the reply tool on queued turns prevents the bot from executing an autonomous tool reply that conflicts with its streamed text.
Source pointers
Section titled “Source pointers”src/utils/chat/contextPipelineIntent.ts: autonomous STM intent preflight.src/utils/chat/contextPipeline.ts:buildChatTurnContext()implementation.src/utils/chat/historyWindow.ts: bounded history reuse and gateway change signals.src/utils/text/contextBuilder.ts:buildContext()entry point to the context-build pipeline.src/utils/text/messageIdMap.ts: Discord snowflake to compact ID translation.