1、api_key可以为空
2、环境命令补充
This commit is contained in:
parent
05ece1bffc
commit
0cd43c37a7
32
README.md
32
README.md
@ -68,7 +68,7 @@ packaging/
|
|||||||
- 实现人工确认入口:`confirm --decision approve|reject` 只处理待确认回滚。
|
- 实现人工确认入口:`confirm --decision approve|reject` 只处理待确认回滚。
|
||||||
- 实现 checkpoint 自动保存和 `resume` 续跑:全局步骤、成功 IP、单 IP 已完成 action 会跳过。
|
- 实现 checkpoint 自动保存和 `resume` 续跑:全局步骤、成功 IP、单 IP 已完成 action 会跳过。
|
||||||
- 实现 LLM structured output 骨架:意图识别、参数抽取、部署计划生成。
|
- 实现 LLM structured output 骨架:意图识别、参数抽取、部署计划生成。
|
||||||
- 实现 OpenAI-compatible 真实 LLM client,支持 `base_url` / `api_key` / `model` 配置。
|
- 实现 OpenAI-compatible 真实 LLM client,支持 `base_url` / `model` 配置,`api_key` 可为空。
|
||||||
- 固化真实 LLM 提示词:意图识别、参数抽取、部署计划生成均要求 JSON structured output。
|
- 固化真实 LLM 提示词:意图识别、参数抽取、部署计划生成均要求 JSON structured output。
|
||||||
- 增加规则 fallback `RuleBasedLlmClient`,用于本地开发和测试。
|
- 增加规则 fallback `RuleBasedLlmClient`,用于本地开发和测试。
|
||||||
- 增加 LLM 输出 guardrails,禁止计划中出现可执行脚本命令和非法 action。
|
- 增加 LLM 输出 guardrails,禁止计划中出现可执行脚本命令和非法 action。
|
||||||
@ -90,11 +90,12 @@ packaging/
|
|||||||
|
|
||||||
## LLM 配置
|
## LLM 配置
|
||||||
|
|
||||||
默认不配置 LLM 时,`analyze` 使用本地规则 fallback。配置真实 LLM 后,会走 OpenAI-compatible `/chat/completions`:
|
默认不配置 LLM 时,`analyze` 使用本地规则 fallback。配置真实 LLM 后,会走 OpenAI-compatible `/chat/completions`。
|
||||||
|
|
||||||
|
`base_url` 和 `model` 必填;`api_key` 可为空。如果模型服务不需要鉴权,不配置 `PAM_LLM_API_KEY` 或不传 `--llm-api-key` 即可,Agent 不会发送 `Authorization` 请求头。
|
||||||
|
|
||||||
```powershell
|
```powershell
|
||||||
$env:PAM_LLM_BASE_URL="https://your-llm.example.com/v1"
|
$env:PAM_LLM_BASE_URL="https://your-llm.example.com/v1"
|
||||||
$env:PAM_LLM_API_KEY="your-api-key"
|
|
||||||
$env:PAM_LLM_MODEL="your-model-name"
|
$env:PAM_LLM_MODEL="your-model-name"
|
||||||
|
|
||||||
python -m pam_deploy_graph.cli analyze --config doc_scripts/config.txt.example --text "请用 MCP 预演部署 HET PAM Node 版本 2.0.5,不要动环境"
|
python -m pam_deploy_graph.cli analyze --config doc_scripts/config.txt.example --text "请用 MCP 预演部署 HET PAM Node 版本 2.0.5,不要动环境"
|
||||||
@ -107,12 +108,17 @@ python -m pam_deploy_graph.cli analyze \
|
|||||||
--config doc_scripts/config.txt.example \
|
--config doc_scripts/config.txt.example \
|
||||||
--text "请用 MCP 预演部署 HET PAM Node 版本 2.0.5,不要动环境" \
|
--text "请用 MCP 预演部署 HET PAM Node 版本 2.0.5,不要动环境" \
|
||||||
--llm-base-url https://your-llm.example.com/v1 \
|
--llm-base-url https://your-llm.example.com/v1 \
|
||||||
--llm-api-key your-api-key \
|
|
||||||
--llm-model your-model-name
|
--llm-model your-model-name
|
||||||
```
|
```
|
||||||
|
|
||||||
真实 LLM 调用位置在 `pam_deploy_graph/llm/openai_compatible.py`,提示词在 `pam_deploy_graph/llm/prompts.py`。发送给 LLM 的 `base_params` 会脱敏,`CLIENT_SECRET` 不会进入 prompt;本地生成计划后仍会执行 guardrails 校验。
|
真实 LLM 调用位置在 `pam_deploy_graph/llm/openai_compatible.py`,提示词在 `pam_deploy_graph/llm/prompts.py`。发送给 LLM 的 `base_params` 会脱敏,`CLIENT_SECRET` 不会进入 prompt;本地生成计划后仍会执行 guardrails 校验。
|
||||||
|
|
||||||
|
如果服务需要鉴权,再补充:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
export PAM_LLM_API_KEY="your-api-key"
|
||||||
|
```
|
||||||
|
|
||||||
## MCP Client 配置
|
## MCP Client 配置
|
||||||
|
|
||||||
CLI/chat 已支持通过 `--mcp-config` 直接加载 MCP 配置。常用场景只需要配置 MCP `server_url` 和独立鉴权信息;Agent 会连接 MCP server,调用 `list_tools` 自动发现 server 暴露的 tools,再按 action 名自动匹配。
|
CLI/chat 已支持通过 `--mcp-config` 直接加载 MCP 配置。常用场景只需要配置 MCP `server_url` 和独立鉴权信息;Agent 会连接 MCP server,调用 `list_tools` 自动发现 server 暴露的 tools,再按 action 名自动匹配。
|
||||||
@ -183,6 +189,24 @@ agent = PamDeployAgent(mcp_runner=runner)
|
|||||||
|
|
||||||
## 使用方式
|
## 使用方式
|
||||||
|
|
||||||
|
开发项目迁移到新环境后,推荐先安装完整开发依赖:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
python -m venv .venv
|
||||||
|
source .venv/bin/activate
|
||||||
|
python -m pip install --upgrade pip setuptools wheel
|
||||||
|
python -m pip install -e ".[mcp,chat,test]"
|
||||||
|
pytest -q
|
||||||
|
```
|
||||||
|
|
||||||
|
Windows PowerShell 激活虚拟环境使用:
|
||||||
|
|
||||||
|
```powershell
|
||||||
|
.\.venv\Scripts\Activate.ps1
|
||||||
|
```
|
||||||
|
|
||||||
|
基础要求:Python 3.11+。如果要执行 Linux 脚本 action,运行环境还需要 `bash` 和 `curl`;如果要构建 Linux 解压即用包,请在 Linux x86_64 构建机上执行打包脚本。
|
||||||
|
|
||||||
整体逻辑结构流程图:
|
整体逻辑结构流程图:
|
||||||
|
|
||||||
```text
|
```text
|
||||||
|
|||||||
@ -26,8 +26,6 @@ def build_llm_client(
|
|||||||
missing = []
|
missing = []
|
||||||
if not actual_base_url:
|
if not actual_base_url:
|
||||||
missing.append("base_url")
|
missing.append("base_url")
|
||||||
if not actual_api_key:
|
|
||||||
missing.append("api_key")
|
|
||||||
if not actual_model:
|
if not actual_model:
|
||||||
missing.append("model")
|
missing.append("model")
|
||||||
if missing:
|
if missing:
|
||||||
|
|||||||
@ -43,8 +43,6 @@ class OpenAICompatibleLlmClient:
|
|||||||
"""保存连接参数、模型参数和可替换的 HTTP transport。"""
|
"""保存连接参数、模型参数和可替换的 HTTP transport。"""
|
||||||
if not base_url:
|
if not base_url:
|
||||||
raise ValueError("必须配置 LLM base_url")
|
raise ValueError("必须配置 LLM base_url")
|
||||||
if not api_key:
|
|
||||||
raise ValueError("必须配置 LLM api_key")
|
|
||||||
if not model:
|
if not model:
|
||||||
raise ValueError("必须配置 LLM model")
|
raise ValueError("必须配置 LLM model")
|
||||||
self.base_url = base_url.rstrip("/")
|
self.base_url = base_url.rstrip("/")
|
||||||
@ -178,12 +176,12 @@ class OpenAICompatibleLlmClient:
|
|||||||
},
|
},
|
||||||
],
|
],
|
||||||
}
|
}
|
||||||
|
headers = {"Content-Type": "application/json"}
|
||||||
|
if self.api_key:
|
||||||
|
headers["Authorization"] = f"Bearer {self.api_key}"
|
||||||
response = self.transport(
|
response = self.transport(
|
||||||
_chat_completions_url(self.base_url),
|
_chat_completions_url(self.base_url),
|
||||||
{
|
headers,
|
||||||
"Authorization": f"Bearer {self.api_key}",
|
|
||||||
"Content-Type": "application/json",
|
|
||||||
},
|
|
||||||
request_payload,
|
request_payload,
|
||||||
self.timeout_sec,
|
self.timeout_sec,
|
||||||
)
|
)
|
||||||
|
|||||||
@ -2,6 +2,7 @@ from dataclasses import asdict
|
|||||||
|
|
||||||
from pam_deploy_graph.agent import PamDeployAgent
|
from pam_deploy_graph.agent import PamDeployAgent
|
||||||
from pam_deploy_graph.checkpoint_store import redact_mapping
|
from pam_deploy_graph.checkpoint_store import redact_mapping
|
||||||
|
from pam_deploy_graph.llm.factory import build_llm_client
|
||||||
from pam_deploy_graph.llm.openai_compatible import OpenAICompatibleLlmClient
|
from pam_deploy_graph.llm.openai_compatible import OpenAICompatibleLlmClient
|
||||||
from pam_deploy_graph.llm.rule_based import RuleBasedLlmClient
|
from pam_deploy_graph.llm.rule_based import RuleBasedLlmClient
|
||||||
from pam_deploy_graph.llm.validators import validate_deploy_plan
|
from pam_deploy_graph.llm.validators import validate_deploy_plan
|
||||||
@ -110,6 +111,45 @@ def test_openai_compatible_client_uses_base_url_api_key_and_prompt():
|
|||||||
assert "只输出一个 JSON 对象" in calls[0][2]["messages"][0]["content"]
|
assert "只输出一个 JSON 对象" in calls[0][2]["messages"][0]["content"]
|
||||||
|
|
||||||
|
|
||||||
|
def test_openai_compatible_client_allows_empty_api_key():
|
||||||
|
calls = []
|
||||||
|
|
||||||
|
def transport(url, headers, payload, timeout_sec):
|
||||||
|
calls.append((url, headers, payload, timeout_sec))
|
||||||
|
return {
|
||||||
|
"choices": [
|
||||||
|
{
|
||||||
|
"message": {
|
||||||
|
"content": (
|
||||||
|
'{"intent":"deploy","mode_preference":"未指定",'
|
||||||
|
'"strategy_preference":"fake","confidence":0.8}'
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
|
||||||
|
client = OpenAICompatibleLlmClient(
|
||||||
|
base_url="https://llm.example/v1",
|
||||||
|
api_key="",
|
||||||
|
model="model-a",
|
||||||
|
transport=transport,
|
||||||
|
)
|
||||||
|
|
||||||
|
result = client.understand_request("部署")
|
||||||
|
|
||||||
|
assert result.intent == "deploy"
|
||||||
|
assert "Authorization" not in calls[0][1]
|
||||||
|
assert calls[0][1]["Content-Type"] == "application/json"
|
||||||
|
|
||||||
|
|
||||||
|
def test_llm_factory_allows_empty_api_key_with_base_url_and_model():
|
||||||
|
client = build_llm_client(base_url="https://llm.example/v1", model="model-a")
|
||||||
|
|
||||||
|
assert isinstance(client, OpenAICompatibleLlmClient)
|
||||||
|
assert client.api_key == ""
|
||||||
|
|
||||||
|
|
||||||
def test_openai_compatible_client_does_not_send_base_secret():
|
def test_openai_compatible_client_does_not_send_base_secret():
|
||||||
calls = []
|
calls = []
|
||||||
|
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user