Solon v3.8.3

agent - 认识智能体接口

</> markdown
2026年1月14日 上午11:36:16

在 Solon AI 框架中,Agent(智能体) 是执行任务的核心单元。它不仅是对大语言模型(LLM)的封装,更是集成了 状态管理(Session)、工具调用(Tools)、长短记忆(History) 和 工作流控制(Flow) 的自治实体。

1、智能体接口(Agent)

智能体(Agent)是 Solon AI Agent 的最小协作单元。在设计上,它具有“多重身份”以满足不同层级的交互需求:

维度属性描述视角
对外身份name唯一标识:智能体在团队中的名字同伴视角(你是谁)
description职责描述:用于语义路由与任务分发的参考依据主管视角(你能干什么)
profile交互契约:定义能力画像、输入限制等约束条件团队视角(如何用你)
对内身份systemPrompt系统提示词:赋予底层 LLM 角色灵魂与执行标准核心视角(我该怎么做)
行为实现call核心逻辑:具体的推理与工具执行过程执行视角
run节点驱动:由团队协议(TeamProtocol)驱动的生命周期管理框架视角

Agent 接口继承自 AgentHandler 和 NamedTaskComponent,支持在 Solon Flow 工作流中自动化运行。

2、快速开始:定制一个智能体

您可以直接实现 Agent 接口来定义特定的业务逻辑:

Agent coder = new Agent() {
    @Override public String name() { return "Coder"; }
    @Override public String description() { return "负责编写核心业务代码"; }
    @Override public AssistantMessage call(Prompt prompt, AgentSession session) {
        return ChatMessage.ofAssistant("代码已提交: login.java");
    }
};

框架内置实现:

  • SimpleAgent: 基础智能体,适用于简单的指令响应。
  • ReActAgent: 具备“思考-行动-观察”循环的自反思智能体,支持工具调用。
  • TeamAgent: 复合智能体,负责指挥成员按协议(如 A2A)进行协作。

3、智能体构建样例

SimpleAgent resumeAgent = SimpleAgent.of(chatModel)
                .name("ResumeExtractor")
                .title("简历信息提取器")
                .systemPrompt(SimpleSystemPrompt.builder()
                        .role("你是一个专业的人事助理")
                        .instruction("请从用户提供的文本中提取关键信息")
                        .build())
                .outputSchema(ResumeInfo.class)
                .build();


ReActAgent illustrator = ReActAgent.of(chatModel)
        .name("illustrator")
        .description("负责视觉风格定义")
        .profile(p -> p.skillAdd("矢量插画", "色彩心理学")
                .modeAdd("text", "image") 
                .metaPut("engine", "Nano Banana")
                .style("极简主义"))
        .systemPrompt(ReActSystemPrompt.builder()
                .role("首席矢量插画专家")
                .instruction("1. 擅长极简主义风格,通过‘色彩心理学’选择能引起情感共鸣的主色调。\n" +
                             "2. 针对用户需求,先构思视觉隐喻(Thought),再定义具体的视觉规格。\n" +
                             "3. 如果需要生成图片,请输出具体的 Prompt 给底层图像引擎(Nano Banana)。\n" +
                             "4. 最终输出应包含:风格描述、配色方案、圆角/线条规范,以及可选的图片描述。")
                .build())
        .build();
        
        
TeamAgent team = TeamAgent.of(chatModel)
                .name("sequential_pipeline")
                .protocol(TeamProtocols.SEQUENTIAL)
                .agentAdd(extractor, converter, polisher)
                .build();

4、具体接口参考

public interface Agent extends AgentHandler, NamedTaskComponent {
    static final Logger LOG = LoggerFactory.getLogger(Agent.class);

    /**
     * 智能体名称(唯一标识)
     */
    String name();

    /**
     * 智能体职责描述(用于语义路由与任务分发参考)
     */
    String description();

    /**
     * 智能体档案(能力画像与交互契约)
     */
    default AgentProfile profile() {
        return null;
    }

    /**
     * 生成动态职责描述(支持模板渲染)
     */
    default String descriptionFor(FlowContext context) {
        if (context == null) {
            return description();
        }

        return SnelUtil.render(description(), context.model());
    }

    /**
     * 响应式任务执行(继续上次的任务)
     *
     * @param session 会话上下文
     */
    default AssistantMessage call(AgentSession session) throws Throwable {
        return call(null, session);
    }

    /**
     * 指定指令的任务执行(开始新任务)
     *
     * @param prompt  显式指令
     * @param session 会话上下文
     */
    AssistantMessage call(Prompt prompt, AgentSession session) throws Throwable;

    /**
     * Solon Flow 节点运行实现
     * <p>处理 Session 初始化、协议注入、推理执行及轨迹同步。</p>
     */
    @Override
    default void run(FlowContext context, Node node) throws Throwable {
        // 1. 获取或初始化会话
        AgentSession session = context.computeIfAbsent(KEY_SESSION, k -> new InMemoryAgentSession("tmp"));

        // 2. 处理团队协作轨迹与拦截
        String traceKey = context.getAs(KEY_CURRENT_TRACE_KEY);
        TeamTrace trace = (traceKey != null) ? context.getAs(traceKey) : null;

        if (trace != null) {
            trace.setLastAgentName(this.name());
            for (RankEntity<TeamInterceptor> item : trace.getOptions().getInterceptorList()) {
                if (item.target.shouldAgentContinue(trace, this) == false) {
                    trace.addStep(ChatRole.ASSISTANT, name(),
                            "[Skipped] Cancelled by " + item.target.getClass().getSimpleName(), 0);

                    if (LOG.isDebugEnabled()) {
                        LOG.debug("Agent [{}] execution skipped by interceptor: {}", name(), item.target.getClass().getSimpleName());
                    }
                    return;
                }
            }
        }

        // 3. 准备提示词并执行推理
        Prompt effectivePrompt = null;
        if (trace != null) {
            effectivePrompt = trace.getProtocol().prepareAgentPrompt(trace, this, trace.getPrompt(), trace.getConfig().getLocale());
        }

        if (LOG.isDebugEnabled()) {
            LOG.debug("Agent [{}] start calling...", name());
        }

        long start = System.currentTimeMillis();
        AssistantMessage msg = call(effectivePrompt, session);
        long duration = System.currentTimeMillis() - start;

        // 4. 同步执行轨迹与结果处理
        if (trace != null) {
            String rawContent = (msg.getContent() == null) ? "" : msg.getContent().trim();
            String finalResult = trace.getProtocol().resolveAgentOutput(trace, this, rawContent);

            if (finalResult == null || finalResult.isEmpty()) {
                finalResult = "Agent [" + name() + "] processed but returned no textual content.";
            }

            trace.addStep(ChatRole.ASSISTANT, name(), finalResult, duration);

            // 执行后置回调
            trace.getProtocol().onAgentEnd(trace, this);
            trace.getOptions().getInterceptorList().forEach(item -> item.target.onAgentEnd(trace, this));
        }

        if (LOG.isDebugEnabled()) {
            LOG.debug("Agent [{}] call completed in {}ms", name(), duration);
        }
    }

    // --- Context Keys ---
    static String KEY_CURRENT_TRACE_KEY = "_current_trace_key_";
    static String KEY_SESSION = "_SESSION_";
    static String KEY_PROTOCOL = "_PROTOCOL_";

    // --- Node IDs ---
    static String ID_START = "start";
    static String ID_END = "end";
    static String ID_REASON_BEF = "reason_bef";
    static String ID_REASON = "reason";
    static String ID_ACTION_BEF = "action_bef";
    static String ID_ACTION = "action";
    static String ID_SYSTEM = "system";
    static String ID_SUPERVISOR = "supervisor";
    static String ID_BIDDING = "bidding";
}