Environment setup
Xcode 27. iOS 27 or macOS 27 SwiftUI App. Foundation Models. Optional Core AI if a state routes to a custom language model.
PackThis state's facts
RespondActive Profile
ToolsMaybe change mode
CapStop or continue
@MainActor
final class AgentOrchestrator {
let state = AgentState()
var log: [String] = []
let maxSteps = 6
func run(userText: String) async throws -> String {
state.mode = .gather
var last = userText
var steps = 0
while state.mode != .done, steps < maxSteps {
steps += 1
let session = LanguageModelSession(profile: AgentProfile(state: state))
let response = try await session.respond(to: last)
last = String(describing: response)
log.append("step \(steps) mode=\(state.mode.rawValue)")
}
if state.mode != .done {
log.append("stopped at cap")
}
return last
}
}
Creating a new session each step is simple and honest. If the SDK keeps transcript across Profile changes on one session, prefer that and pass the same session in. Confirm which pattern the installed docs use. Do not copy a LangGraph checkpointer.
Pack state-specific facts before respond using the context packer. Gather gets search hits. Act gets the chosen action only.
Cap is mandatory. A loop without a max step count is a battery leak.
Next, make the log something support can read.
Key concepts
- The orchestrator owns state, session, and a
maxStepscap. - The loop is pack, respond, maybe change mode, stop at done or cap.
- Pack state-specific facts before
respond. - A loop without a step cap is a battery leak.
Takeaways
- The cap is mandatory.
- Confirm whether the SDK keeps transcript across Profile changes on one session.
- Do not copy a LangGraph checkpointer.