TheDocumentation Index
Fetch the complete documentation index at: https://mintlify.com/GuaiZai233/FrostAgent/llms.txt
Use this file to discover all available pages before exploring further.
Engine is the central orchestration component of FrostAgent. It owns the agentic loop that repeatedly calls the LLM, inspects the response for tool calls, executes those tools, and feeds the results back into the conversation until the model produces a plain-text final answer or the iteration ceiling is hit. Every other subsystem — the session manager, the tool registry, the LLM provider, the message dispatcher — is reached through the Engine.
Engine Struct Fields
TheEngine struct (defined in internal/llm/agent.go) bundles everything the agentic loop needs into one place:
| Field | Purpose |
|---|---|
MaxIterations | Maximum number of LLM ↔ tool-call rounds before giving up. Set to 5 in main.go. |
ToolRegistry | Map of tool name → ToolExecutor. Populated at startup from internal/tools. |
Provider | The core.LLMProvider implementation (openai.Client). Receives ChatRequest, returns ChatResponse. |
BaseURL | The OpenAI-compatible API endpoint, from UPSTREAM_ENDPOINT. |
APIKey | Bearer token for the upstream LLM API, from UPSTREAM_API_KEY. |
ModelName | Model identifier sent in every ChatRequest, from MODEL_NAME. |
SessionManager | Manages all active SessionContext objects for per-user history. |
Dispatcher | Routes OutgoingMessage to the right platform adapter by platform ID. |
SendHook | Optional callback fired when send_message executes. The adapter sets this to deliver rich messages mid-loop. |
StartedAt | Timestamp recorded when the Engine is initialised; exposed through BotStatusService. |
Version | Semantic version string ("0.1.0"); reported in status and log headers. |
TotalMessagesProcessed | Monotonically incremented counter (one per runLoop iteration). Useful for observability. |
MaxIterations is initialised to 5 in cmd/app/main.go. Raising it allows more complex, multi-step reasoning but also increases worst-case latency and LLM token cost.Running the Engine
The Engine exposes three entry points. Choose based on whether you need stateless inference, direct history control, or automatic session management.Run — Single stateless call
Run constructs a minimal two-message conversation (system prompt + user input) and runs the loop once. It is useful for one-shot queries where conversation history is not required.
SYSTEM_PROMPT environment variable. If the variable is empty, the system message will have an empty content string.
RunMessages — Pass a full message history
RunMessages accepts an already-assembled []ChatMessage slice and feeds it directly into the loop. If the first message is not a system message, a system prompt is prepended automatically.
session.Snapshot() and passes it here to keep session locking outside the loop.
RunWithSession — Stateful session-based call
RunWithSession handles the full lifecycle: it loads (or creates) the session, prepends the system prompt for new sessions, appends the user message, runs the loop, trims the history, and writes it back — all under the session mutex.
RunWithSession holds the session lock (session.Lock() / session.Unlock()) for the entire duration of the LLM call. For high-throughput scenarios consider using RunMessages with externally managed locking, as the OneBot adapter does.The Agentic Loop
runLoop is the private method that all three entry points converge on. It implements the standard ReAct-style observe → think → act cycle:
- Tool schema injection — All registered tools are serialised into
core.Toolstructs and sent with everyChatRequestso the model can choose any of them. - Sequential tool execution — If the LLM requests multiple tool calls in a single turn, they are executed sequentially in the order returned.
- Iteration ceiling — If
MaxIterationsrounds complete without a plain-text response, the loop returns a fallback string indicating the limit was reached.
SendHook
SendHook is a func(toolResultJSON string) field on the Engine. When the send_message tool fires successfully, the Engine calls SendHook with the raw JSON result before reporting back to the LLM. This lets the platform adapter deliver rich messages to the user mid-loop, without waiting for the model to finish its final turn.
In the OneBot adapter, SendHook is configured per-request inside the reply function:
SendHook returns, the Engine substitutes the result with a short confirmation string ("消息已发送") so the LLM knows the delivery succeeded without receiving the full payload again.
Context Trimming
AfterRunWithSession finishes, it calls trimMessagesForSession to keep the session history within bounds:
system message at index 0 and then keeps the most recent MaxHistory messages. Crucially, if the trim boundary lands in the middle of a tool-call chain (i.e., a tool role message would appear before its corresponding assistant message), the start index is walked backwards until it sits on a non-tool message. This prevents the OpenAI API error: “tool message must follow an assistant message with tool_calls”.