跳转至

TypeScript 独立消费者

在自己的 Node.js 应用中使用已发布的 @axisvela/praxo-core@0.7.1,要求 Node.js 20 或更新版本。无需检出 PRAXO-CORE 源码、API Key、模型服务或 AxisVela 产品。 安装需要 npm Registry 访问或已有缓存。

选择边界

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

创建应用

在新的应用目录中创建精确依赖的 package.json,再由锁文件安装:

npm install --save-exact @axisvela/praxo-core@0.7.1

提交生成的 package-lock.json,重复验收使用 npm ci。不要使用 file:、link:、 仓库 URL 或本地路径替换发布包。测试会验证固定版本以及模块是否解析到应用内部。

应用源码

以下为 starter.js,直接渲染自受测源码。

const {
  Agent,
  FuncHook,
  Registry,
  ROLE_ASSISTANT,
  ROLE_TOOL,
  PolicyDeniedError,
} = require("@axisvela/praxo-core");

class FixtureModel {
  constructor(target) {
    this.target = target;
  }

  async generate(request) {
    const last = request.messages[request.messages.length - 1];
    if (last.role === ROLE_TOOL) {
      return { message: { role: ROLE_ASSISTANT, content: "recorded" } };
    }
    return {
      message: {
        role: ROLE_ASSISTANT,
        toolCalls: [
          {
            id: "record-1",
            name: "record",
            arguments: { target: this.target },
          },
        ],
      },
    };
  }
}

class RecordTool {
  constructor() {
    this.name = "record";
    this.description = "Record a target in an in-memory counter.";
    this.risk = "write";
    this.parameters = {
      type: "object",
      required: ["target"],
      properties: { target: { type: "string" } },
    };
    this.calls = 0;
  }

  async invoke() {
    this.calls += 1;
    return "recorded";
  }
}

function buildAgent(target) {
  const tool = new RecordTool();
  const tools = new Registry();
  tools.register(tool);
  const policy = new FuncHook((action) => {
    if (action.kind === "model.generate") {
      return { allowed: true };
    }
    if (action.kind === "tool.invoke") {
      return {
        allowed:
          action.toolName === "record" &&
          action.arguments?.target === "public",
        reason: "only the public target is permitted",
      };
    }
    return { allowed: false, reason: "unsupported action" };
  });
  const agent = new Agent({
    model: new FixtureModel(target),
    tools,
    maxSteps: 3,
    allowedTools: ["record"],
    policy,
  });
  return { agent, tool };
}

async function run(write = (line) => console.log(line)) {
  const allowed = buildAgent("public");
  const result = await allowed.agent.run(
    { id: "starter-public" },
    "Record the requested target."
  );
  if (result.output !== "recorded" || allowed.tool.calls !== 1) {
    throw new Error(
      `allow verification failed: output=${JSON.stringify(result.output)} effects=${allowed.tool.calls}`
    );
  }
  write("allow: recorded; effects=1");

  const denied = buildAgent("private");
  try {
    await denied.agent.run(
      { id: "starter-private" },
      "Record the requested target."
    );
  } catch (error) {
    if (!(error instanceof PolicyDeniedError)) {
      throw error;
    }
    if (denied.tool.calls !== 0) {
      throw new Error(`denied call produced effects=${denied.tool.calls}`);
    }
    write("deny: policy rejected; effects=0");
    return;
  }
  throw new Error("private target was not denied");
}

if (require.main === module) {
  run().catch((error) => {
    console.error(error);
    process.exitCode = 1;
  });
}

module.exports = { buildAgent, run };

验收测试

以下为 test_starter.test.js,直接渲染自受测源码。

const assert = require("node:assert/strict");
const path = require("node:path");
const test = require("node:test");

const praxo = require("@axisvela/praxo-core");
const pkg = require("@axisvela/praxo-core/package.json");

const { buildAgent, run } = require("./starter");

const expectedVersion = process.env.PRAXO_EXPECTED_VERSION || "0.7.1";

test("allowed tool executes once", async () => {
  const { agent, tool } = buildAgent("public");
  const result = await agent.run(
    { id: "test-public" },
    "Record the requested target."
  );
  assert.equal(result.output, "recorded");
  assert.equal(tool.calls, 1);
});

test("denied tool has no effect", async () => {
  const { agent, tool } = buildAgent("private");
  await assert.rejects(
    agent.run({ id: "test-private" }, "Record the requested target."),
    praxo.PolicyDeniedError
  );
  assert.equal(tool.calls, 0);
});

test("starter output is deterministic", async () => {
  const lines = [];
  await run((line) => lines.push(line));
  assert.deepEqual(lines, [
    "allow: recorded; effects=1",
    "deny: policy rejected; effects=0",
  ]);
});

test("dependency comes from the published package", () => {
  assert.equal(pkg.version, expectedVersion);
  assert.equal(praxo.SDK_VERSION, pkg.version);
  const resolved = require.resolve("@axisvela/praxo-core");
  const root = path.resolve(__dirname);
  assert.ok(
    resolved.startsWith(root + path.sep),
    `dependency resolved outside the application: ${resolved}`
  );
  const dependency = require("./package.json").dependencies["@axisvela/praxo-core"];
  assert.equal(dependency, expectedVersion);
  assert.ok(!/^(file:|link:|git|https?:)/.test(dependency));
});

验证

npm ci --no-audit --no-fund
npm test
node starter.js

程序预期输出:

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

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

治理验收

governed.test.js 增加五项负向检查:

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

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

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