The Kotlin-native AI agent harness for the JVM.
Bring your own LLM. Ship an agent in a few lines, not three weeks. Open source, Apache 2.0.
suspend fun main() {
val runtime = Sophi.runtime {
provider = buildClaudeProvider(System.getenv("ANTHROPIC_API_KEY"))
contextWindowTokens(200_000)
}
println(runtime.turn(runtime.newSession(), "What changed since the last tag?"))
}
Apache 2.0 · Kotlin on JDK 21+ · 17 Maven modules · Any LLM provider
What you get that a plain LLM client doesn't
One agent loop, with the parts real agents need already built in. Each is a line in the builder.
Any model, local or cloud
Claude, or any OpenAI-compatible server — including local ones like Ollama and LM Studio. Switching is one line, not a rewrite.
- No framework lock-in: one small provider interface
- Run fully offline against a local model
- You state the context window; SoPhi compacts at 80%
val runtime = Sophi.runtime {
provider = buildOpenAiCompatProvider(
baseUrl = "http://localhost:11434/v1",
apiKey = null,
model = "qwen3:14b",
)
model = "qwen3:14b"
contextWindowTokens(32_768)
}
Learns from use, plans and replans
Tool reliability stats and lessons distilled at the end of each session. Explicit multi-step goals, with every plan version logged.
- Lessons recalled in the next session
- /good and /bad feedback that sticks
- Plan progress as events your UI can draw
val runtime = Sophi.runtime {
// provider, contextWindowTokens …
learning(LearningConfig())
goalDecomposition(Path.of(".sophi/plans")) { event ->
println(event)
}
}
val session = runtime.newSession()
runtime.streamTurn(session, "Plan a release for Friday") { e ->
if (e is TurnEvent.Token) print(e.text)
}
Your tools, MCP and skills
Implement one interface for your own tools, call any MCP server, and load capabilities from plain Markdown skill files.
- Risk levels and confirmation built in
- MCP servers connect and disconnect at runtime
- Skills the agent can install, and write itself
class WeatherTool : Tool {
override val name = "weather"
override val description = "Current weather for a city"
override val parametersJson =
"""{"type":"object","properties":{"city":{"type":"string"}}}"""
override suspend fun execute(argumentsJson: String) =
"Sunny, 21°C"
}
val runtime = Sophi.runtime {
// provider, contextWindowTokens …
tool(WeatherTool())
mcpConfig(Path.of("mcp.json"))
skillTools()
}
- MemoryLong-term memory in threads, places and a profile. Experimental.
- Crash-safe sessionsAppend-only, branchable session files. Resume anywhere.
- Streaming eventsTokens, tool calls and confirmations as they happen.
- SubagentsDelegate work to agents you define in plain files.
Three ways to run it
Same core every time, so switching later is a one-line change, not a rewrite.
val runtime = Sophi.runtime {
provider = buildClaudeProvider(System.getenv("ANTHROPIC_API_KEY"))
contextWindowTokens(200_000)
systemPrompt = "You are a build assistant."
tool(MyCustomTool())
}
val session = runtime.newSession(title = "release-check")
val reply = runtime.turn(session, "What changed since the last tag?")
mvn -pl sophi-cli -am package
alias sophi='java -jar sophi-cli/target/sophi-cli-1.0.0-SNAPSHOT.jar'
sophi --model claude-sonnet-4-5 --context-window-tokens 200000
# a local model, a resumed session, a goal with a check
sophi --provider openai-compat --base-url http://localhost:11434/v1 --model qwen3:14b --context-window-tokens 32768
sophi --session <id>
/goal --check "mvn test" fix the failing build
mvn -pl sophi-web -am spring-boot:run
curl -X POST localhost:8080/api/sessions
curl -X POST localhost:8080/api/sessions/<id>/turn \
-H 'Content-Type: application/json' -d '{"input": "hello"}'
curl -N "localhost:8080/api/sessions/<id>/stream?input=hello" # SSE
Build it from source.
Needs JDK 21+ and Maven. Maven Central coming soon.
git clone https://github.com/shukriev/SoPhi
cd SoPhi
mvn -q -DskipTests install
export ANTHROPIC_API_KEY=sk-ant-...
See it in a real product.
SoPhi Companion is a personal agent built on this harness: it remembers, sits in on your meetings and gets things done — and keeps your life on your machine.
Meet Companion →
Building SoPhi
The engineering behind the harness, one decision at a time.
- Why we built a Kotlin agent harness instead of using oneThe case for owning the loop on the JVM.
- The agent loop: a first-principles designThe core every other module builds on.
- From inert files to self-writing skills: evolving an AI agent's skill systemSkills that start as Markdown and end up writing themselves.
All articles → How SoPhi compares to Koog, LangChain4j and Spring AI →