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:
@@ -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")
|
||||
|
||||
@@ -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"));
|
||||
}
|
||||
}
|
||||
@@ -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
|
||||
}
|
||||
});
|
||||
|
||||
|
||||
Reference in New Issue
Block a user