跳转至

Java 独立消费者

在自己的应用中使用 Maven Central 已发布的 com.axisvela:praxo-core:0.9.0, 要求 Java 21 或更新版本、Maven 3.9 或更新版本。无需检出 PRAXO-CORE 源码、API Key、 模型服务或 AxisVela 产品。安装需要 Maven Central 访问或已有本地仓库缓存。

选择边界

适用于需要显式工具范围与 Policy 的嵌入式模型/工具循环。应用仍负责模型访问、真实身份、 持久审批、外部效果和隔离。它不是托管 Agent 平台、持久图引擎或沙箱服务。 选型前阅读何时不应选择 Core。

创建应用

在 pom.xml 中声明精确依赖:

<dependency>
  <groupId>com.axisvela</groupId>
  <artifactId>praxo-core</artifactId>
  <version>0.9.0</version>
</dependency>

不要使用 system scope、本地 JAR 路径或框架源码替代构建。测试会验证 SDK 版本, 并确认 Version 来自已发布的 praxo-core-0.9.0.jar。

应用源码

以下为 src/main/java/starter/App.java,直接渲染自 受测源码。

package starter;

import com.axisvela.praxo.PraxoException;
import com.axisvela.praxo.agent.Agent;
import com.axisvela.praxo.agent.AgentOptions;
import com.axisvela.praxo.agent.Session;
import com.axisvela.praxo.model.Message;
import com.axisvela.praxo.model.Model;
import com.axisvela.praxo.model.Request;
import com.axisvela.praxo.model.Response;
import com.axisvela.praxo.model.ToolCall;
import com.axisvela.praxo.model.ToolSpec;
import com.axisvela.praxo.policy.Action;
import com.axisvela.praxo.policy.Decision;
import com.axisvela.praxo.tool.Registry;
import com.axisvela.praxo.tool.Tool;

import java.io.PrintStream;
import java.util.List;
import java.util.Map;

public final class App {
    private App() {
    }

    static final class FixtureModel implements Model {
        private final String target;

        FixtureModel(String target) {
            this.target = target;
        }

        @Override
        public Response generate(Request request) {
            Message last = request.messages().get(request.messages().size() - 1);
            if (Message.TOOL.equals(last.role())) {
                return Response.of(Message.assistant("recorded", List.of()));
            }
            ToolCall call = new ToolCall("record-1", "record", Map.of("target", target));
            return Response.of(Message.assistant("", List.of(call)));
        }
    }

    static final class RecordTool implements Tool {
        private int calls;

        @Override
        public String name() {
            return "record";
        }

        @Override
        public String description() {
            return "Record a target in an in-memory counter.";
        }

        @Override
        public Object invoke(Map<String, Object> args) {
            calls += 1;
            return "recorded";
        }

        public ToolSpec spec() {
            return new ToolSpec(
                    "record",
                    description(),
                    Map.of(
                            "type", "object",
                            "required", List.of("target"),
                            "properties", Map.of("target", Map.of("type", "string"))),
                    "write");
        }

        int calls() {
            return calls;
        }
    }

    public record Built(Agent agent, RecordTool tool) {
    }

    static Built buildAgent(String target) {
        Registry tools = new Registry();
        RecordTool tool = new RecordTool();
        tools.register(tool);
        AgentOptions options = new AgentOptions(new FixtureModel(target));
        options.tools = tools;
        options.maxSteps = 3;
        options.allowedTools = List.of("record");
        options.policy = (Action action) -> {
            if ("model.generate".equals(action.kind())) {
                return Decision.allow("model generation");
            }
            if ("tool.invoke".equals(action.kind())) {
                Object value = action.arguments() == null
                        ? null
                        : action.arguments().get("target");
                return "record".equals(action.toolName()) && "public".equals(value)
                        ? Decision.allow("public target")
                        : Decision.deny("only the public target is permitted");
            }
            return Decision.deny("unsupported action");
        };
        return new Built(new Agent(options), tool);
    }

    public static void run(PrintStream out) throws Exception {
        Built allowed = buildAgent("public");
        var result = allowed.agent().run(
                new Session("starter-public"), "Record the requested target.");
        if (!"recorded".equals(result.output) || allowed.tool().calls != 1) {
            throw new IllegalStateException(
                    "allow verification failed: output=" + result.output
                            + " effects=" + allowed.tool().calls);
        }
        out.println("allow: recorded; effects=1");

        Built denied = buildAgent("private");
        try {
            denied.agent().run(
                    new Session("starter-private"), "Record the requested target.");
        } catch (PraxoException error) {
            if (!"AGENT_POLICY_DENIED".equals(error.code)) {
                throw error;
            }
            if (denied.tool().calls != 0) {
                throw new IllegalStateException(
                        "denied call produced effects=" + denied.tool().calls);
            }
            out.println("deny: policy rejected; effects=0");
            return;
        }
        throw new IllegalStateException("private target was not denied");
    }

    public static void main(String[] args) throws Exception {
        run(System.out);
    }
}

验收测试

以下为 src/test/java/starter/AppTest.java,直接渲染自 受测源码。

package starter;

import com.axisvela.praxo.PraxoException;
import com.axisvela.praxo.Version;
import com.axisvela.praxo.agent.Session;
import org.junit.jupiter.api.Test;

import java.io.ByteArrayOutputStream;
import java.io.PrintStream;
import java.net.URL;
import java.nio.charset.StandardCharsets;
import java.nio.file.Path;

import static org.junit.jupiter.api.Assertions.assertEquals;
import static org.junit.jupiter.api.Assertions.assertThrows;
import static org.junit.jupiter.api.Assertions.assertTrue;

class AppTest {
    @Test
    void allowedToolRunsOnce() throws Exception {
        var built = App.buildAgent("public");
        var result = built.agent().run(
                new Session("test-public"), "Record the requested target.");
        assertEquals("recorded", result.output);
        assertEquals(1, built.tool().calls());
    }

    @Test
    void deniedToolHasNoEffect() {
        var built = App.buildAgent("private");
        PraxoException error = assertThrows(PraxoException.class, () -> built.agent().run(
                new Session("test-private"), "Record the requested target."));
        assertEquals("AGENT_POLICY_DENIED", error.code);
        assertEquals(0, built.tool().calls());
    }

    @Test
    void starterOutputIsDeterministic() throws Exception {
        ByteArrayOutputStream buffer = new ByteArrayOutputStream();
        try (PrintStream out = new PrintStream(buffer, true, StandardCharsets.UTF_8)) {
            App.run(out);
        }
        String expected = "allow: recorded; effects=1" + System.lineSeparator()
                + "deny: policy rejected; effects=0" + System.lineSeparator();
        assertEquals(expected, buffer.toString(StandardCharsets.UTF_8));
    }

    @Test
    void dependencyComesFromPublishedJar() throws Exception {
        assertEquals("0.9.0", Version.SDK_VERSION);
        URL location = Version.class.getProtectionDomain().getCodeSource().getLocation();
        String filename = Path.of(location.toURI()).getFileName().toString();
        assertEquals("praxo-core-0.9.0.jar", filename);
        assertTrue(location.toURI().toString().contains("/com/axisvela/praxo-core/0.9.0/"));
    }
}

验证

mvn -B test
mvn -q -B exec:java

程序预期输出:

allow: recorded; effects=1
deny: policy rejected; effects=0

模型是确定性 fixture,不是真实 LLM。allowedTools 限制工具名,Policy 额外检查目标参数。 被拒绝的调用抛出 PraxoException,错误码为 AGENT_POLICY_DENIED,不得增加内存效果计数。 当前 API 的空允许列表不表示 deny-all。替换为真实模型适配器后仍需保留这些测试。

治理验收

GovernedAcceptanceTest 增加五项负向检查:

mvn -B -Dtest=GovernedAcceptanceTest test
检查 必须结果
缺少必需 Policy 应用守卫在运行前拒绝;工具零副作用
工具范围为空 应用守卫拒绝;工具零副作用
Middleware 改变最终参数 最终操作重新授权;拒绝阻止副作用
审批编辑改变参数 编辑后的操作重新授权;拒绝阻止副作用
Policy 服务错误 运行 fail closed;工具零副作用

使用受测源码。 守卫属于应用层:Core 保留普通 nil-policy 行为。这些检查不证明真实身份、持久审批、 效果对账或通用安全 Profile。

这些检查仅证明指定发布产物与 fixture 行为,不证明真实模型质量、身份真实性、隔离、持久效果 或生产安全。后续参考Java 实现指南及安全模型。