DEV Community

Solon Framework
Solon Framework

Posted on

The Solon AI Talent System: Solving Real Business Problems with Composable Agent Capabilities

Every LLM agent library gives you tools. Few give you a coherent system for organizing those tools into reusable, context-aware skill clusters that can adapt to the business problem at hand — not the other way around. Solon AI's Talent system is that organization layer.

In this post I'll walk through what a Talent actually is, how it differs from a plain tool, and how Solon ships over thirty built-in Talents covering everything from code search and memory management to enterprise messaging and file operations. Then I'll show how to compose them to solve real business problems: sending a report email with attachments, pushing a deployment notification to your team chat, and running a self-improving agent that learns from past conversations.

What a Talent actually is

In Solon AI, a Talent is a bundle of three things:

  1. Tools — the FunctionTool methods the LLM can call (annotated with @ToolMapping).
  2. Instructions — a dynamic prompt fragment injected into the system message when the Talent is active, telling the model how to use those tools.
  3. Metadata — name, description, and an enable/disable flag.

The key difference from a plain tool is context awareness. A Talent can decide whether it's relevant to the current conversation via isSupported(Prompt), initialize state when attached via onAttach(Prompt), and even swap its tool set dynamically based on what the user is asking.

public class MyTalent extends AbsTalent {

    @Override
    public boolean isSupported(Prompt prompt) {
        String content = prompt.getUserContent().toLowerCase();
        return content.contains("代码") || content.contains("code");
    }

    @Override
    public String getInstruction(Prompt prompt) {
        return "## 代码搜索指南\n你可以通过 codesearch 工具查找 API 文档和示例代码...\n";
    }

    @ToolMapping(name = "my_tool", description = "Do something useful")
    public String myTool(@Param("param") String param) {
        return doSomething(param);
    }
}
Enter fullscreen mode Exit fullscreen mode

AbsTalent (the convenience base class) handles the boilerplate: it reflects over @ToolMapping methods to auto-register tools, manages the enabled/disabled lifecycle, and exposes getToolMap() and getToolAry() for introspection.

The built-in Talent ecosystem

Solon AI ships a rich set of Talents across dedicated Maven modules. Here's a survey of the ones most relevant to business applications:

Talent Module What it does
TerminalTalent solon-ai-talent-cli File read/write/ls/grep/glob + bash execution with sandbox support
MemoryTalent solon-ai-talent-memory Long-term memory: extract, recall, search, consolidate, prune
WebsearchTalent solon-ai-talent-web Real-time web search via Exa MCP
CodeSearchTalent solon-ai-talent-web Code repo search via Exa MCP
MailTalent solon-ai-talent-mail Send emails with HTML body and file attachments
DingTalkTalent solon-ai-talent-social Push messages to DingTalk (with HMAC signature)
FeishuTalent solon-ai-talent-social Push Lark/Feishu cards (with signature)
WeComTalent solon-ai-talent-social Push messages to WeCom / Enterprise WeChat
Text2SqlTalent solon-ai-talent-text2sql Natural language to SQL with dialect support
PdfTalent solon-ai-talent-pdf PDF reading and extraction
RedisTalent solon-ai-talent-data Redis read/write operations
LspTalent solon-ai-talent-lsp Language Server Protocol integration

All Talents are opt-in. You挂 them onto a ReActAgent or HarnessEngine explicitly:

ReActAgent agent = ReActAgent.of()
    .model(chatModel)
    .defaultTalentAdd(
        new TerminalTalent(mountManager),
        new MemoryTalent(solutionProvider),
        new MailTalent(workDir, host, port, user, pass),
        new DingTalkTalent(webhookUrl, secret)
    )
    .build();
Enter fullscreen mode Exit fullscreen mode

Each Talent's isSupported() method is evaluated at call time. If the current prompt matches, the Talent activates, its getInstruction() is injected into the system message, and its tools become available to the model.

Business scenario 1: automated report delivery

A common enterprise pattern: generate a report, attach it, and email it to stakeholders. With Solon AI, this is two Talents working together.

MailTalent mail = new MailTalent(
    "/path/to/workdir",
    "smtp.company.com", 465,
    "agent@company.com", "smtp-password"
);

DingTalkTalent dingtalk = new DingTalkTalent(
    "https://oapi.dingtalk.com/robot/send?access_token=xxx",
    "SECxxxxxxxx"
);

ReActAgent agent = ReActAgent.of()
    .model(chatModel)
    .defaultTalentAdd(mail, dingtalk)
    .build();
Enter fullscreen mode Exit fullscreen mode

When the user says "Generate the monthly sales report and send it to the team," the agent:

  1. Uses TerminalTalent to generate the report file in the work directory.
  2. Calls send_email on MailTalent, which auto-detects the attachment path, reads the file bytes, and sends via SMTPS (port 465, TLS). The MailTalent resolves attachment paths relative to the work directory and validates them against the root path to prevent path traversal.
  3. Calls send_dingtalk on DingTalkTalent, which packages the summary as a markdown message, computes the HmacSHA256 signature, and pushes to the webhook.

Both operations are synchronous from the agent's perspective. MailTalent uses a connection pool (coreSize=2) for SMTP sessions, so high-frequency report runs don't open a new TCP connection each time.

Business scenario 2: multi-platform team notification

In a multi-office company, different teams use different platforms — DingTalk in China, Feishu in some subsidiaries, WeCom in others. Rather than writing three separate notification integrations, you挂 all three Talents and let the model pick the right one based on context.

ReActAgent agent = ReActAgent.of()
    .model(chatModel)
    .defaultTalentAdd(
        new DingTalkTalent(dingtalkUrl, dingtalkSecret),
        new FeishuTalent(feishuUrl, feishuSecret),
        new WeComTalent(wecomUrl)
    )
    .build();
Enter fullscreen mode Exit fullscreen mode

Each Talent overrides isSupported() to match keywords in the user's message:

  • DingTalkTalent: matches "钉钉" or "ding"
  • FeishuTalent: matches "飞书", "lark", or "feishu"
  • WeComTalent: matches "企微", "企业微信", or "wecom"

This means the model automatically routes to the correct platform. If the user says "Notify the Shanghai team on DingTalk about the deployment," only DingTalkTalent.isSupported() returns true, so its instruction and tools are injected. The other two Talents stay dormant.

FeishuTalent adds a nice touch: if a title is provided, it sends an interactive card (with a blue header and lark_md body); otherwise it falls back to plain text. DingTalkTalent similarly upgrades to markdown mode when a title is present.

Business scenario 3: self-improving agent with long-term memory

Perhaps the most powerful pattern is combining MemoryTalent with a ReActAgent to build an agent that gets smarter over time.

MemorySolutionProvider solutionProvider = /* ... configure ... */;

MemoryTalent memory = new MemoryTalent(solutionProvider)
    .relevanceCount(5)       // semantic matches from current conversation
    .priorityCount(5)        // high-importance fallback memories
    .relevanceInjection(true); // mix semantics + popularity

ReActAgent agent = ReActAgent.of()
    .model(chatModel)
    .defaultTalentAdd(memory)
    .build();
Enter fullscreen mode Exit fullscreen mode

MemoryTalent exposes five tools:

  • memory_extract — store a fact with an importance score (1-10). Returns the old value for comparison if the key already exists.
  • memory_recall — exact lookup by key.
  • memory_search — semantic search by natural language query, or * to list all keys.
  • memory_consolidate — merge multiple low-importance fragments into a single high-importance insight (importance automatically set to 10).
  • memory_prune — delete a stale or incorrect entry.

The getInstruction() method of MemoryTalent does something important: it injects relevant memories into the system prompt at the start of each turn. It fetches up to relevanceCount semantically similar memories and up to priorityCount high-importance memories, deduplicates them, and formats them into the prompt. This means the agent doesn't need to explicitly call memory_search on every turn — the most relevant context is already there.

The agent is also guided by the injected instructions to proactively call memory_extract when it learns something worth remembering, and memory_consolidate when it notices碎片 (fragments) piling up.

The mount system: connecting Talents to the filesystem

TerminalTalent (the file system and bash Talent) works in concert with MountManager, which maps logical paths (@solon-source, @workspace-agents, etc.) to physical directories. When sandbox mode is enabled, TerminalTalent restricts all file operations to the work directory and explicitly listed mounts — no absolute paths, no cross-mount writes unless the mount is marked writeable="true".

The instruction string that TerminalTalent.getInstruction() generates is dynamic: it lists all active mounts with their aliases, types, and write permissions, so the LLM always knows which paths it can safely use.

<mount_list>
  <mount alias="@solon-source" type="FILES" writeable="false" ... />
  <mount alias="@workspace-agents" type="AGENTS" writeable="false" ... />
</mount_list>
Enter fullscreen mode Exit fullscreen mode

This means you can give an agent read-only access to your source code repositories while allowing it to write freely in its work directory — all without hardcoding path restrictions in the prompt.

Why composition matters

The Talent system's real power isn't any single Talent — it's that they compose cleanly. Each Talent is an independent module with no hard dependencies on the others. You can mix and match:

  • Add Text2SqlTalent to let the agent query your database in natural language.
  • Add PdfTalent to let it read and summarize PDF attachments.
  • Add CodeSearchTalent so it can look up API docs before writing code.
  • Add MemoryTalent so it remembers your preferences across sessions.

And when a Talent isn't needed, you simply don't挂 it. There's no global registry to clean up, no configuration file to edit. The isSupported() gate ensures even unused Talents impose zero overhead.

All Talents share the same small vocabulary: @ToolMapping for tools, getInstruction() for context, isSupported() for gating. That uniformity is what makes the system feel cohesive rather than a collection of disjoint utilities.

If you want to explore the full list of built-in Talents and their APIs, the source lives at solon.noear.org.

Top comments (0)