Python 独立消费者¶
在自己的应用中使用已发布的 praxo-core==0.8.1,要求 Python 3.11 或更新版本。
无需检出 PRAXO-CORE 源码、API Key、模型服务或 AxisVela 产品。安装需要 PyPI 访问或已有 wheel 缓存。
选择边界¶
适用于需要异步模型/工具循环、显式工具范围和 Policy 的嵌入式应用。应用仍负责模型访问、 真实身份、持久审批、外部效果和隔离。它不是托管 Agent 平台、持久图引擎或沙箱服务。 选型前阅读何时不应选择 Core。
在新应用安装¶
在新的应用目录创建虚拟环境,直接使用其中的 Python,无需激活。
POSIX:
python3 -m venv .venv
.venv/bin/python -m pip install --index-url https://pypi.org/simple praxo-core==0.8.1
PowerShell:
python -m venv .venv
.venv\Scripts\python.exe -m pip install --index-url https://pypi.org/simple praxo-core==0.8.1
不要使用 pip install -e、仓库路径或 PYTHONPATH 替换 SDK。验收测试检查安装版本、
包位于虚拟环境内,以及不存在直接路径安装记录。
应用源码¶
以下为 starter.py。文档站直接渲染受测应用源码,
在 GitHub 阅读时可通过源码链接查看。
import asyncio
import sys
from typing import TextIO
from praxo.agent import Agent, AgentOptions, Session
from praxo.errors import PolicyDeniedError
from praxo.model import Message, Request, Response, ToolCall
from praxo.policy import Action, Decision, FuncHook
from praxo.tool import Registry
class FixtureModel:
def __init__(self, target: str):
self.target = target
async def generate(self, request: Request) -> Response:
if request.messages[-1].role == "tool":
return Response(message=Message(role="assistant", content="recorded"))
return Response(message=Message(
role="assistant",
tool_calls=[ToolCall(id="record-1", name="record", arguments={"target": self.target})],
))
class RecordTool:
name = "record"
description = "Record a target in an in-memory counter."
risk = "write"
parameters = {
"type": "object",
"required": ["target"],
"properties": {"target": {"type": "string"}},
}
def __init__(self):
self.calls = 0
async def invoke(self, args):
self.calls += 1
return "recorded"
def build_agent(target: str) -> tuple[Agent, RecordTool]:
impl = RecordTool()
tools = Registry()
tools.register(impl)
def authorize(action: Action) -> Decision:
if action.kind == "model.generate":
return Decision(allowed=True)
if action.kind == "tool.invoke":
return Decision(
allowed=action.tool_name == "record" and action.arguments.get("target") == "public",
reason="only the public target is permitted",
)
return Decision(allowed=False, reason="unsupported action")
return Agent(AgentOptions(
model=FixtureModel(target), tools=tools, max_steps=3,
allowed_tools=["record"], policy=FuncHook(authorize),
)), impl
async def run(output: TextIO) -> None:
agent, impl = build_agent("public")
result = await agent.run(Session(id="starter-public"), "Record the requested target.")
if result.output != "recorded" or impl.calls != 1:
raise RuntimeError(f"allow verification failed: output={result.output!r} effects={impl.calls}")
print("allow: recorded; effects=1", file=output)
agent, impl = build_agent("private")
try:
await agent.run(Session(id="starter-private"), "Record the requested target.")
except PolicyDeniedError:
if impl.calls != 0:
raise RuntimeError(f"denied call produced effects={impl.calls}")
else:
raise RuntimeError("private target was not denied")
print("deny: policy rejected; effects=0", file=output)
if __name__ == "__main__":
asyncio.run(run(sys.stdout))
验收测试¶
以下为 test_starter.py,直接来自受测源码。
import importlib.metadata
import io
from pathlib import Path
import sys
import unittest
import praxo
from praxo.agent import Session
from praxo.errors import PolicyDeniedError
from starter import build_agent, run
class StarterTests(unittest.IsolatedAsyncioTestCase):
async def test_allowed_tool(self):
agent, impl = build_agent("public")
result = await agent.run(Session(id="test-public"), "Record the requested target.")
self.assertEqual(result.output, "recorded")
self.assertEqual(impl.calls, 1)
async def test_denied_tool_has_no_effect(self):
agent, impl = build_agent("private")
with self.assertRaises(PolicyDeniedError):
await agent.run(Session(id="test-private"), "Record the requested target.")
self.assertEqual(impl.calls, 0)
async def test_starter_output(self):
output = io.StringIO()
await run(output)
self.assertEqual(output.getvalue(), "allow: recorded; effects=1\ndeny: policy rejected; effects=0\n")
def test_published_dependency(self):
self.assertNotEqual(sys.prefix, sys.base_prefix, "use a dedicated virtual environment")
distribution = importlib.metadata.distribution("praxo-core")
self.assertEqual(distribution.version, "0.8.1")
self.assertEqual(praxo.__version__, "0.8.1")
self.assertTrue(Path(praxo.__file__).resolve().is_relative_to(Path(sys.prefix).resolve()))
self.assertIsNone(distribution.read_text("direct_url.json"), "install from the registry, not a local path")
if __name__ == "__main__":
unittest.main()
验证¶
POSIX:
.venv/bin/python -m pip check
.venv/bin/python -I -m unittest discover -s . -v
.venv/bin/python -I starter.py
PowerShell:
.venv\Scripts\python.exe -m pip check
.venv\Scripts\python.exe -I -m unittest discover -s . -v
.venv\Scripts\python.exe -I starter.py
程序预期输出:
allow: recorded; effects=1
deny: policy rejected; effects=0
模型是确定性 fixture,不是真实 LLM。allowed_tools 限制工具名,Policy 额外检查目标
参数。当前 API 的空范围不表示 deny-all。被拒绝的 private 目标不得到达内存效果计数器。
替换为真实模型适配器后仍需保留这些测试。
治理验收¶
test_governed.py 增加五项负向检查:
.venv/bin/python -I -m unittest discover -s . -p "test_governed.py" -v
| 检查 | 必须结果 |
|---|---|
| 缺少必需 Policy | 应用守卫在运行前拒绝;工具零副作用 |
| 工具范围为空 | 应用守卫拒绝;工具零副作用 |
| Middleware 改变最终参数 | 最终操作重新授权;拒绝阻止副作用 |
| 审批编辑改变参数 | 编辑后的操作重新授权;拒绝阻止副作用 |
| Policy 服务错误 | 运行 fail closed;工具零副作用 |
使用受测源码。 守卫属于应用层:Core 保留普通 nil-policy 行为。这些检查不证明真实身份、持久审批、 效果对账或通用安全 Profile。
这些检查仅证明指定发布包与 fixture 行为,不证明真实模型质量、生产安全或持久执行保证。 后续参考Python API 与实现指南及安全模型。