
Agnes Free Text
- 45 installs
- 174 repo stars
- Updated June 1, 2026
- kangarooking/agnes-free-model-skills
Call the Agnes-2.0-Flash free text model API for chat completions, streaming, and tool-calling experiments.
About
Wrapper for Sapiens AI's free text generation model with OpenAI-compatible API. Integrates into agent skills for code help, chat, and LLM experiments without API cost.
- OpenAI-compatible SDK
- Free streaming text generation
Agnes Free Text by the numbers
- 45 all-time installs (skills.sh)
- Ranked #7,749 of 16,546 AI & Agent Building skills by installs in the Skillselion catalog
- Data as of Aug 2, 2026 (Skillselion catalog sync)
npx skills add https://github.com/kangarooking/agnes-free-model-skills --skill agnes-free-textAdd your badge
Show developers this skill is listed on Skillselion. Paste this into your README.
| Installs | 45 |
|---|---|
| repo stars | ★ 174 |
| Last updated | June 1, 2026 |
| Repository | kangarooking/agnes-free-model-skills ↗ |
What it does
Call the Agnes-2.0-Flash free text model API for chat completions, streaming, and tool-calling experiments.
Files
Agnes Free Text
Use this skill to call Sapiens AI's Agnes-2.0-Flash text model through the Agnes API.
Credential Rule
Read the API key from the environment. Do not hardcode keys in prompts, scripts, skill files, commits, or shell history.
Preferred variable:
export AGNES_API_KEY="..."The helper also accepts AGNES_TOKEN as a fallback.
Quick Start
Run a normal chat completion:
python3 scripts/agnes_text.py chat \
--prompt "用三句话解释 Agent 为什么需要工具调用"Use a system prompt and save the raw response JSON:
python3 scripts/agnes_text.py chat \
--system "You are a concise technical writer." \
--prompt "Explain tool calling for a beginner." \
--output-json ./outputs/agnes-free-text/response.jsonPass tool definitions from JSON:
python3 scripts/agnes_text.py chat \
--prompt "Decide whether a weather tool is needed." \
--tools-json ./tools.json \
--tool-choice autoValidate the request shape without calling the API:
python3 scripts/agnes_text.py chat \
--prompt "Write a short product intro." \
--max-tokens 300 \
--dry-runWorkflow
1. Clarify whether the user wants a direct answer, JSON-like structured output, coding help, or streaming output. 2. Use scripts/agnes_text.py for repeatable calls. Prefer --dry-run first when parameters are complex. 3. Keep the model fixed to agnes-2.0-flash unless the API docs are updated. 4. For multi-turn context, pass repeated --message role:content values instead of flattening the conversation into one prompt. 5. For tool calling, pass --tools-json as either an inline JSON array or a path to a JSON file.
Notes
- Endpoint:
POST https://apihub.agnes-ai.com/v1/chat/completions - Model:
agnes-2.0-flash - Request shape is compatible with OpenAI Chat Completions.
- Supported features include system prompts, multi-turn messages, streaming, tool definitions, and tool choice.
- If the API returns an authorization error, check
AGNES_API_KEYfirst.
Reference
Read references/api.md when you need endpoint details, response fields, tool-calling shape, or streaming examples.
interface:
display_name: "Agnes Free Text"
short_description: "Free Agnes text API helper"
default_prompt: "Use $agnes-free-text to call the free Agnes text API for a concise answer."
Agnes Free Text API Reference
Source document: outputs/wechat-runs/2026-05-30-agnes-ai-free-api/文本模型文档.md.
Model
- Name:
agnes-2.0-flash - Provider: Sapiens AI
- Use cases: chat completion, multi-turn conversation, tool calling, agentic workflows, coding tasks, reasoning, streaming output, JSON-style output.
Endpoint
POST https://apihub.agnes-ai.com/v1/chat/completions
Authorization: Bearer YOUR_API_KEY
Content-Type: application/jsonRequest Fields
| Field | Type | Required | Notes |
|---|---|---|---|
model | string | yes | Fixed to agnes-2.0-flash. |
messages | array | yes | OpenAI-style messages with system, user, and assistant roles. |
temperature | number | no | Lower values make output more deterministic. |
top_p | number | no | Nucleus sampling. |
max_tokens | number | no | Maximum generated tokens. |
stream | boolean | no | Enable streaming output. |
tools | array | no | Function/tool definitions for agent workflows. |
tool_choice | string/object | no | Controls whether and how tools are used. |
Basic Request
curl https://apihub.agnes-ai.com/v1/chat/completions \
-H "Authorization: Bearer $AGNES_API_KEY" \
-H "Content-Type: application/json" \
-d '{
"model": "agnes-2.0-flash",
"messages": [
{"role": "system", "content": "You are a helpful AI assistant."},
{"role": "user", "content": "Explain how autonomous agents use tools."}
],
"temperature": 0.7,
"max_tokens": 1024
}'Streaming Request
{
"model": "agnes-2.0-flash",
"messages": [
{"role": "user", "content": "Write a short product introduction for an AI assistant app."}
],
"stream": true
}Tool Calling Shape
{
"model": "agnes-2.0-flash",
"messages": [
{"role": "user", "content": "What is the weather like in Singapore today?"}
],
"tools": [
{
"type": "function",
"function": {
"name": "get_weather",
"description": "Get the current weather for a location",
"parameters": {
"type": "object",
"properties": {
"location": {
"type": "string",
"description": "The city and country"
}
},
"required": ["location"]
}
}
}
]
}Response Fields
Expect an OpenAI-style response with:
idobjectcreatedmodelchoices[].message.rolechoices[].message.contentchoices[].finish_reasonusage.prompt_tokensusage.completion_tokensusage.total_tokens
Prompting Notes
- Give clear instructions, context, and desired output format.
- For coding tasks, include language, framework, error message, and expected behavior.
- For agent workflows, include the goal, available tools, constraints, and stopping conditions.
#!/usr/bin/env python3
"""Call the Agnes-2.0-Flash chat completions API."""
from __future__ import annotations
import argparse
import json
import os
from pathlib import Path
import sys
from typing import Any
from urllib import error, request
API_BASE = os.environ.get("AGNES_API_BASE", "https://apihub.agnes-ai.com").rstrip("/")
MODEL = "agnes-2.0-flash"
class ApiError(RuntimeError):
def __init__(self, message: str, status: int | None = None, payload: Any = None):
super().__init__(message)
self.status = status
self.payload = payload
def get_api_key() -> str:
token = os.environ.get("AGNES_API_KEY") or os.environ.get("AGNES_TOKEN")
if not token:
raise SystemExit("Missing API key. Set AGNES_API_KEY, for example: export AGNES_API_KEY='...'")
return token
def parse_json_or_text(raw: str) -> Any:
try:
return json.loads(raw)
except json.JSONDecodeError:
return raw
def extract_error_message(payload: Any) -> str | None:
if isinstance(payload, dict):
err = payload.get("error")
if isinstance(err, dict):
return str(err.get("message") or err.get("type") or err)
if isinstance(err, str):
return err
if payload.get("message"):
return str(payload["message"])
return None
def request_json(url: str, token: str, payload: dict[str, Any]) -> dict[str, Any]:
data = json.dumps(payload, ensure_ascii=False).encode("utf-8")
req = request.Request(
url,
data=data,
headers={
"Authorization": f"Bearer {token}",
"Content-Type": "application/json",
},
method="POST",
)
try:
with request.urlopen(req, timeout=120) as resp:
raw = resp.read().decode("utf-8")
except error.HTTPError as exc:
raw = exc.read().decode("utf-8", errors="replace")
parsed = parse_json_or_text(raw)
message = extract_error_message(parsed) or f"HTTP {exc.code}: {raw}"
raise ApiError(message, status=exc.code, payload=parsed) from exc
except error.URLError as exc:
raise ApiError(f"Network error: {exc}") from exc
parsed = parse_json_or_text(raw)
if not isinstance(parsed, dict):
raise ApiError(f"Expected JSON object, got: {raw[:300]}")
if parsed.get("error"):
raise ApiError(extract_error_message(parsed) or "API returned an error", payload=parsed)
return parsed
def stream_response(url: str, token: str, payload: dict[str, Any]) -> None:
data = json.dumps(payload, ensure_ascii=False).encode("utf-8")
req = request.Request(
url,
data=data,
headers={
"Authorization": f"Bearer {token}",
"Content-Type": "application/json",
},
method="POST",
)
try:
with request.urlopen(req, timeout=120) as resp:
for raw_line in resp:
line = raw_line.decode("utf-8", errors="replace").rstrip()
if line:
print(line)
except error.HTTPError as exc:
raw = exc.read().decode("utf-8", errors="replace")
parsed = parse_json_or_text(raw)
message = extract_error_message(parsed) or f"HTTP {exc.code}: {raw}"
raise ApiError(message, status=exc.code, payload=parsed) from exc
except error.URLError as exc:
raise ApiError(f"Network error: {exc}") from exc
def parse_message(raw: str) -> dict[str, str]:
if ":" not in raw:
raise SystemExit(f"Invalid --message value. Use role:content, got: {raw}")
role, content = raw.split(":", 1)
role = role.strip()
content = content.strip()
if role not in {"system", "user", "assistant"}:
raise SystemExit(f"Invalid message role: {role}")
if not content:
raise SystemExit("Message content cannot be empty.")
return {"role": role, "content": content}
def build_payload(args: argparse.Namespace) -> dict[str, Any]:
messages: list[dict[str, str]] = []
if args.system:
messages.append({"role": "system", "content": args.system})
for raw in args.message or []:
messages.append(parse_message(raw))
if args.prompt:
messages.append({"role": "user", "content": args.prompt})
if not messages:
raise SystemExit("Provide --prompt or at least one --message role:content value.")
payload: dict[str, Any] = {
"model": MODEL,
"messages": messages,
}
optional_values = {
"temperature": args.temperature,
"top_p": args.top_p,
"max_tokens": args.max_tokens,
"stream": args.stream,
}
for key, value in optional_values.items():
if value is not None:
payload[key] = value
if args.tools_json:
payload["tools"] = read_json_value(args.tools_json, "--tools-json")
if args.tool_choice:
payload["tool_choice"] = read_json_value(args.tool_choice, "--tool-choice", allow_plain_string=True)
return payload
def read_json_value(value: str, label: str, allow_plain_string: bool = False) -> Any:
path = Path(value).expanduser()
raw = path.read_text(encoding="utf-8") if path.exists() else value
try:
return json.loads(raw)
except json.JSONDecodeError as exc:
if allow_plain_string and not raw.strip().startswith(("{", "[", '"')):
return raw
raise SystemExit(f"Invalid JSON for {label}: {exc}") from exc
def extract_text(response: dict[str, Any]) -> str | None:
choices = response.get("choices")
if isinstance(choices, list) and choices:
first = choices[0]
if isinstance(first, dict):
message = first.get("message")
if isinstance(message, dict) and isinstance(message.get("content"), str):
return message["content"]
if isinstance(first.get("text"), str):
return first["text"]
return None
def cmd_chat(args: argparse.Namespace) -> int:
payload = build_payload(args)
url = f"{args.api_base.rstrip('/')}/v1/chat/completions"
if args.dry_run:
print(json.dumps({"url": url, "payload": payload}, ensure_ascii=False, indent=2))
return 0
token = get_api_key()
try:
if args.stream:
stream_response(url, token, payload)
return 0
response = request_json(url, token, payload)
except ApiError as exc:
print(f"Agnes API error: {exc}", file=sys.stderr)
if exc.payload is not None:
print(json.dumps(exc.payload, ensure_ascii=False, indent=2), file=sys.stderr)
return 1
if args.output_json:
output_path = Path(args.output_json).expanduser()
output_path.parent.mkdir(parents=True, exist_ok=True)
output_path.write_text(json.dumps(response, ensure_ascii=False, indent=2) + "\n", encoding="utf-8")
text = extract_text(response)
if text:
print(text)
else:
print(json.dumps(response, ensure_ascii=False, indent=2))
return 0
def build_parser() -> argparse.ArgumentParser:
parser = argparse.ArgumentParser(description="Call Agnes-2.0-Flash chat completions.")
subparsers = parser.add_subparsers(dest="command", required=True)
chat = subparsers.add_parser("chat", help="Submit a chat completion request")
chat.add_argument("--prompt", help="User prompt to append as the final user message")
chat.add_argument("--system", help="Optional system prompt")
chat.add_argument("--message", action="append", help="Message as role:content; can be repeated")
chat.add_argument("--temperature", type=float)
chat.add_argument("--top-p", type=float)
chat.add_argument("--max-tokens", type=int)
chat.add_argument("--stream", action="store_true", help="Print raw streaming response lines")
chat.add_argument("--tools-json", help="Tools JSON array or path to a JSON file")
chat.add_argument("--tool-choice", help="Tool choice as a JSON value or plain string such as auto")
chat.add_argument("--output-json", help="Write the raw response JSON to this path")
chat.add_argument("--api-base", default=API_BASE)
chat.add_argument("--dry-run", action="store_true", help="Print request JSON without calling the API")
chat.set_defaults(func=cmd_chat)
return parser
def main() -> int:
parser = build_parser()
args = parser.parse_args()
return args.func(args)
if __name__ == "__main__":
raise SystemExit(main())