Implement LLMInferenceActivity integration for Temporal workflows

Workflow Input Structure:
  ├─ question: User content for reasoning
  ├─ project: Project ID for scoping
  ├─ operations: Flags for link_entities, infer_facts, reason_query, summarize
  └─ llm_activity: Configuration for LLMInferenceActivity
       ├─ model: Selected based on complexity (reasoning|ornith:35b|qwen2.5:3b)
       ├─ system_prompt: Task-specific instruction (Zep-backed)
       ├─ user_prompt: Content to process
       ├─ temperature: 0.7 (reasoning) or 0.5 (validation)
       └─ max_tokens: 2048 (reasoning) or 512 (validation)

Model Selection:
  ├─ reason_query=true, summarize=true → reasoning (DeepSeek-R1, complex)
  ├─ reason_query=true, summarize=false → ornith:35b (medium)
  └─ reason_query=false → qwen2.5:3b (fast, <100ms)

System Prompts (handlers/llm_prompts.rs):
  ├─ entity_extraction_system_prompt(): Extract entities + relationships + facts
  ├─ reasoning_system_prompt(): Step-by-step reasoning + answers
  ├─ agent_capability_validation_prompt(): Validate agent capabilities
  └─ fact_validation_system_prompt(): Detect contradictions

Workflow Activity Execution:
  ├─ Temporal receives workflow input with llm_activity config
  ├─ ReasoningWorkflow orchestrates:
  │  ├─ Activity 1: RetrieveMemory (optional context)
  │  ├─ Activity 2: LLMInferenceActivity (calls /v1/chat/completions via gateway)
  │  │   └─ Retries: 3× with backoff (2s, 4s, 8s)
  │  │   └─ Timeout: 120s
  │  │   └─ JWT propagation: Authorization: Bearer header
  │  ├─ Activity 3: PersistResults (save to memory_entity/memory_edge)
  │  └─ Activity 4: SummarizeFindings (return results)
  ├─ Memory handler polls DESCRIBE_WORKFLOW (30× with 100ms delay, 3s timeout)
  └─ Returns ReasoningResult with answers, confidence, reasoning_steps

Changes:
  ├─ execute_reasoning_workflow(): Build llm_activity config with model selection
  ├─ select_llm_model(): Choose model based on operation complexity
  ├─ build_system_prompt(): Use Zep-inspired prompts for reasoning
  ├─ handlers/llm_prompts.rs: Centralized prompt templates (5 system + 4 user builders)
  ├─ AgentInitialization: Include llm_activity for capability validation
  └─ Fixed duplicate extract_jwt_token call in agent_handler.rs

Activity Contract:
  ├─ Workflow input includes llm_activity block
  ├─ Temporal passes to LLMInferenceActivity
  ├─ Activity substitutes {{ previous_output }} template variables
  ├─ Activity calls POST /v1/chat/completions with JWT header
  ├─ Activity returns { response, model, stop_reason, tokens_used }
  ├─ PersistResults activity stores results to DB
  └─ Workflow returns: question, answers[], confidence, reasoning_steps[]

Tests Added:
  + 14 new tests in llm_prompts.rs (prompt validation, user prompt builders)

Compilation: 
This commit is contained in:
2026-09-05 00:52:30 -07:00
parent b33901aa5b
commit 4c275525e9
5 changed files with 777 additions and 8 deletions
+12 -2
View File
@@ -97,17 +97,27 @@ pub async fn register_agent_handler(
// 1. Persist agent state to temporal_workflow_links table
// 2. Execute LLMInferenceActivity (call LLM via api.riotpiao.com/v1/chat/completions)
// 3. Store reasoning traces to memory_entity/memory_edge
if let Some(jwt) = crate::handlers::crate::handlers::extract_jwt_token(&req) {
if let Some(jwt) = crate::handlers::extract_jwt_token(&req) {
let client = SynthesisClient::new(
"https://api.riotpiao.com".to_string(),
jwt,
);
// Start Temporal workflow for agent initialization
// Include LLMInferenceActivity configuration for capability verification
let workflow_input = serde_json::json!({
"agent_id": body.agent_id,
"capabilities": body.capabilities,
"project_id": body.project_id
"project_id": body.project_id,
// LLMInferenceActivity inputs for agent capability reasoning
"llm_activity": {
"model": "ornith:13b",
"system_prompt": "You are an agent capability validator. Verify that the requested capabilities are valid for the memory system. Return JSON with 'valid' boolean and 'reason' string.",
"user_prompt": format!("Validate agent capabilities: {:?}", body.capabilities),
"temperature": 0.5,
"max_tokens": 512
}
});
let workflow_req = crate::handlers::WorkflowBuilder::new("AgentInitialization")
+182
View File
@@ -0,0 +1,182 @@
// LLM Prompts for Temporal LLMInferenceActivity
// System prompts, user prompt templates for reasoning workflows
/// System prompt for entity linking and fact extraction
pub fn entity_extraction_system_prompt() -> &'static str {
r#"You are a knowledge extraction expert specializing in entity recognition and relationship identification.
Your task:
1. Extract all named entities (people, organizations, locations, technologies, concepts)
2. Identify entity types (Person, Organization, Location, Technology, Concept, etc.)
3. Extract relationships between entities
4. Identify key facts and assertions
Output format: Return a JSON object with:
{
"entities": [
{"name": "...", "type": "...", "confidence": 0.0-1.0}
],
"relationships": [
{"source": "...", "relation": "...", "target": "...", "confidence": 0.0-1.0}
],
"facts": [
{"statement": "...", "confidence": 0.0-1.0}
]
}
Guidelines:
- Only extract entities that are explicitly mentioned or strongly implied
- Use proper entity types (not overly specific)
- Confidence scores should reflect extraction certainty (0.5-1.0 range)
- Keep entity names consistent (no duplicates with different casing)
"#
}
/// System prompt for reasoning and question answering
pub fn reasoning_system_prompt() -> &'static str {
r#"You are an intelligent reasoning assistant specialized in knowledge graphs and fact inference.
Your task:
1. Understand the question/query
2. Identify relevant entities and relationships from context
3. Reason through multiple inference steps
4. Provide comprehensive answers with supporting evidence
Output format: Return a JSON object with:
{
"question": "...",
"reasoning_steps": [
"Step 1: Identified entities...",
"Step 2: Found relationships...",
"Step 3: Reasoned that..."
],
"answers": ["answer1", "answer2"],
"confidence": 0.0-1.0
}
Guidelines:
- Explain your reasoning step-by-step
- Only use information from the provided context
- If insufficient information, state what's missing
- Confidence reflects answer certainty
"#
}
/// System prompt for agent capability validation
pub fn agent_capability_validation_prompt() -> &'static str {
r#"You are an agent capability validator for a memory graph system.
Your task:
Validate requested capabilities against supported operations:
- entity_linking: Extract and link entities
- inference_facts: Infer facts from relationships
- reason_query: Answer questions through reasoning
- summarization: Summarize content
- semantic_search: Retrieve similar content
- graph_traversal: Navigate entity relationships
Output format: Return a JSON object with:
{
"valid": true/false,
"capabilities_validated": ["entity_linking", "reasoning_query"],
"invalid_capabilities": [],
"reason": "All capabilities are supported"
}
"#
}
/// System prompt for fact validation and contradiction detection
pub fn fact_validation_system_prompt() -> &'static str {
r#"You are a fact validator and contradiction detector.
Your task:
1. Analyze extracted facts for logical consistency
2. Detect contradictions (same subject with opposite predicates)
3. Identify implicit facts that follow from stated facts
4. Assess confidence in fact validity
Output format: Return a JSON object with:
{
"facts_validated": [
{"statement": "...", "valid": true/false, "confidence": 0.0-1.0}
],
"contradictions": [
{"fact1": "...", "fact2": "...", "conflict_type": "...", "severity": "high/medium/low"}
],
"implicit_facts": ["derived_fact1", "derived_fact2"]
}
"#
}
/// Build user prompt for entity extraction
pub fn entity_extraction_user_prompt(content: &str) -> String {
format!("Extract entities and relationships from the following text:\n\n{}", content)
}
/// Build user prompt for reasoning
pub fn reasoning_user_prompt(question: &str, context: &str) -> String {
format!(
"Question: {}\n\nContext:\n{}\n\nPlease reason through this question step-by-step.",
question, context
)
}
/// Build user prompt for agent capability validation
pub fn agent_capability_user_prompt(capabilities: &[String]) -> String {
format!(
"Validate these agent capabilities: {:?}\n\nAre they all supported by the memory system?",
capabilities
)
}
/// Build user prompt for fact validation
pub fn fact_validation_user_prompt(facts: &str) -> String {
format!("Validate these facts for contradictions and consistency:\n\n{}", facts)
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_entity_extraction_prompt_exists() {
let prompt = entity_extraction_system_prompt();
assert!(prompt.contains("entity"));
assert!(prompt.contains("JSON"));
}
#[test]
fn test_reasoning_prompt_exists() {
let prompt = reasoning_system_prompt();
assert!(prompt.contains("reasoning"));
assert!(prompt.contains("steps"));
}
#[test]
fn test_capability_validation_prompt() {
let prompt = agent_capability_validation_prompt();
assert!(prompt.contains("entity_linking"));
assert!(prompt.contains("reasoning_query"));
}
#[test]
fn test_entity_extraction_user_prompt() {
let prompt = entity_extraction_user_prompt("test content");
assert!(prompt.contains("test content"));
assert!(prompt.contains("entities"));
}
#[test]
fn test_reasoning_user_prompt() {
let prompt = reasoning_user_prompt("What is X?", "X is Y");
assert!(prompt.contains("What is X?"));
assert!(prompt.contains("X is Y"));
}
#[test]
fn test_capability_user_prompt() {
let caps = vec!["entity_linking".to_string()];
let prompt = agent_capability_user_prompt(&caps);
assert!(prompt.contains("entity_linking"));
}
}
+1
View File
@@ -19,6 +19,7 @@ pub mod agent_handler;
pub mod jwt_utils;
pub mod workflow_builder;
pub mod workflow_poller;
pub mod llm_prompts;
pub use query::*;
pub use ingest::*;
@@ -260,21 +260,68 @@ pub async fn unified_synthesis_handler(
})
}
/// Select LLM model based on operations complexity
fn select_llm_model(operations: &serde_json::Value) -> &'static str {
let reason_query = operations.get("reason_query")
.and_then(|v| v.as_bool())
.unwrap_or(false);
let summarize = operations.get("summarize")
.and_then(|v| v.as_bool())
.unwrap_or(false);
match (reason_query, summarize) {
(true, true) => "reasoning", // Complex: extract + reason + summarize
(true, false) => "ornith:35b", // Medium: extract + reason
(false, _) => "qwen2.5:3b", // Quick: only linking/inference
}
}
/// Build LLM system prompt for entity/fact extraction
fn build_system_prompt(operations: &serde_json::Value) -> String {
let reason_query = operations.get("reason_query")
.and_then(|v| v.as_bool())
.unwrap_or(false);
// Use Zep-inspired reasoning prompt for complex reasoning, entity extraction otherwise
if reason_query {
crate::handlers::llm_prompts::reasoning_system_prompt().to_string()
} else {
crate::handlers::llm_prompts::entity_extraction_system_prompt().to_string()
}
}
/// Execute reasoning workflow via Temporal
/// Returns parsed ReasoningResult from workflow output
async fn execute_reasoning_workflow(
client: &crate::agent::client_sdk::SynthesisClient,
body: &UnifiedSynthesisRequest,
) -> Result<ReasoningResult, String> {
// Build START_WORKFLOW request
// Prepare operations metadata
let operations = serde_json::json!({
"link_entities": body.link_entities,
"infer_facts": body.infer_facts,
"reason_query": body.reason_query,
"summarize": body.summarize
});
// Select model based on complexity
let model = select_llm_model(&operations);
let system_prompt = build_system_prompt(&operations);
// Build START_WORKFLOW request with LLMInferenceActivity inputs
let workflow_input = serde_json::json!({
// Workflow input
"question": body.content,
"project": body.project,
"operations": {
"link_entities": body.link_entities,
"infer_facts": body.infer_facts,
"reason_query": body.reason_query,
"summarize": body.summarize
"operations": operations,
// LLMInferenceActivity inputs (passed to Temporal activity)
"llm_activity": {
"model": model,
"system_prompt": system_prompt,
"user_prompt": body.content,
"temperature": 0.7,
"max_tokens": 2048
}
});