Skip to content

Tactics

Tactic is LLLM's center model. A tactic is a small, typed, service-ready unit that does one thing well.

The boundary is intentionally narrower than an agent framework. It answers:

  • what input shape the tactic accepts,
  • what output shape it returns,
  • whether it supports streaming,
  • how to call it locally or remotely,
  • how to describe it to services and packages.

Design Lineage

The Tactic boundary was originally developed from the reusable reasoning units needed by Analytica. Analytica decomposes complex societal, economic, political, and scientific questions into grounded subpropositions, runs specialized agents or tools over those subquestions, and synthesizes the results into a more stable analysis.

LLLM generalizes that pattern. A tactic is not tied to soft propositional reasoning, forecasting, or any one agent architecture. It keeps the useful boundary: a typed unit of work that can be validated, streamed, served, packaged, composed, audited, and reused by other systems.

Applied Systems

LLLM has also been applied in autonomous software-development systems such as Apeiron. Apeiron uses agentic components for full-lifecycle application synthesis, including demand modeling, computer-use agent evaluation, activity tracing, and locality-controlled iteration. That is the other side of the tactic design: the same small callable boundary can serve analysis agents, software-building agents, evaluators, tracers, tools, and deployment services without forcing them into one runtime.

from pydantic import BaseModel
from lllm import Tactic


class EchoInput(BaseModel):
    text: str


class EchoOutput(BaseModel):
    text: str


class EchoTactic(Tactic[EchoInput, EchoOutput]):
    name = "echo"
    input_type = EchoInput
    output_type = EchoOutput

    def _run(self, input_value, *, context=None):
        return EchoOutput(text=input_value.text.upper())

Input may arrive as a model instance or a JSON-compatible mapping. The tactic normalizes the value before _run() sees it, so service clients, test code, and in-process callers can share the same behavior.

Call Paths

Method Use
run(input_value, context=None) Synchronous local call.
arun(input_value, context=None) Async local call.
stream(...) / astream(...) Data chunks when the tactic supports streaming.
events(...) / aevents(...) Full TacticEvent envelopes for status/error streams.
info() TacticInfo metadata for services, packages, and agents.

CallContext carries request ids, trace ids, caller identity, and metadata. It is optional for small scripts and useful for services that need auditability or cross-service correlation.

Metadata

TacticInfo is the public description of the tactic:

  • name and description,
  • input and output JSON schemas,
  • examples and metadata,
  • package and service refs,
  • capability flags such as streaming support.

This is what /info returns from a service and what PsiHub can render into package cards. Keep info() data descriptive and portable; do not put raw secrets or environment-specific credentials in metadata.

Runtime-Agnostic By Design

A tactic can hide:

  • a Pydantic AI agent,
  • a native prompt/dialog workflow,
  • a plain Python function,
  • a guardrailed proxy,
  • a remote HTTP service.

The caller still sees the same typed boundary. That is why LLLM composes well with PsiHub and SSSN: the package or channel layer can point at a tactic without inheriting the implementation runtime.

Reference

  • Cheng, Junyan, Kyle Richardson, and Peter Chin. "Analytica: Soft Propositional Reasoning for Robust and Scalable LLM-Driven Analysis." The Fourteenth International Conference on Learning Representations (ICLR), 2026.
  • Cheng, Junyan, Ankit Srivastava, Jessie Zeng, Milenko Drinic, and Jack W. Stokes. "Apeiron: A Scalable LLM-agentic Framework for Autonomous Full-lifecycle Demand-optimized Application Synthesis." Findings of the Association for Computational Linguistics: ACL 2026, 2026, pp. 3868-3899.